Skip to content

Commit

Permalink
Add example for adding custom Space Views (#2328)
Browse files Browse the repository at this point in the history
<!--
Open the PR up as a draft until you feel it is ready for a proper
review.

Do not make PR:s from your own `main` branch, as that makes it difficult
for reviewers to add their own fixes.

Add any improvements to the branch as new commits to make it easier for
reviewers to follow the progress. All commits will be squashed to a
single commit once the PR is merged into `main`.

Make sure you mention any issues that this PR closes in the description,
as well as any other related issues.

To get an auto-generated PR description you can put "copilot:summary" or
"copilot:walkthrough" anywhere.
-->

### What

Adds an example for custom Space Views.


https://github.com/rerun-io/rerun/assets/1220815/50033dfd-fdba-44b6-9b6d-ad4a1071ccd6

Simplified handling of empty context & empty state.

Significant part of #2249 
* #2249 

### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)

<!-- This line will get updated when the PR build summary job finishes.
-->
PR Build Summary: https://build.rerun.io/pr/2328

<!-- pr-link-docs:start -->
Docs preview: https://rerun.io/preview/33ca881/docs
Examples preview: https://rerun.io/preview/33ca881/examples
<!-- pr-link-docs:end -->
  • Loading branch information
Wumpf authored Jun 8, 2023
1 parent f9d8a99 commit 141c64d
Show file tree
Hide file tree
Showing 23 changed files with 568 additions and 48 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/re_data_ui/src/item_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ pub fn select_hovered_on_click(
if response.ctx.input(|i| i.modifiers.command) {
selection_state.toggle_selection(selection_state.hovered().to_vec());
} else {
selection_state.set_multi_selection(selection_state.hovered().clone().into_iter());
selection_state.set_selection(selection_state.hovered().clone().into_iter());
}
}
}
19 changes: 0 additions & 19 deletions crates/re_space_view/src/empty_scene_context.rs

This file was deleted.

4 changes: 0 additions & 4 deletions crates/re_space_view/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@
pub mod controls;
mod data_blueprint;
mod empty_scene_context;
mod empty_space_view_state;
mod screenshot;
mod unreachable_transform_reason;

pub use data_blueprint::{DataBlueprintGroup, DataBlueprintTree};
pub use empty_scene_context::EmptySceneContext;
pub use empty_space_view_state::EmptySpaceViewState;
pub use screenshot::ScreenshotMode;
pub use unreachable_transform_reason::UnreachableTransformReason;
6 changes: 3 additions & 3 deletions crates/re_space_view_bar_chart/src/space_view_class.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use egui::util::hash;
use re_components::TensorData;
use re_log_types::EntityPath;
use re_space_view::{controls, EmptySpaceViewState};
use re_space_view::controls;
use re_viewer_context::{
auto_color, SpaceViewClass, SpaceViewClassName, SpaceViewId, TypedScene, ViewerContext,
};
Expand All @@ -12,8 +12,8 @@ use super::scene_part::SceneBarChart;
pub struct BarChartSpaceView;

impl SpaceViewClass for BarChartSpaceView {
type State = EmptySpaceViewState;
type Context = re_space_view::EmptySceneContext;
type State = ();
type Context = ();
type SceneParts = SceneBarChart;
type ScenePartData = ();

Expand Down
2 changes: 1 addition & 1 deletion crates/re_space_view_text/src/space_view_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub struct TextSpaceView;

impl SpaceViewClass for TextSpaceView {
type State = TextSpaceViewState;
type Context = re_space_view::EmptySceneContext;
type Context = ();
type SceneParts = SceneText;
type ScenePartData = ();

Expand Down
2 changes: 1 addition & 1 deletion crates/re_space_view_text_box/src/space_view_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ pub struct TextBoxSpaceView;

impl SpaceViewClass for TextBoxSpaceView {
type State = TextBoxSpaceViewState;
type Context = ();
type SceneParts = SceneTextBox;
type Context = re_space_view::EmptySceneContext;
type ScenePartData = ();

fn name(&self) -> SpaceViewClassName {
Expand Down
6 changes: 3 additions & 3 deletions crates/re_space_view_time_series/src/space_view_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use egui::{
use re_arrow_store::TimeType;
use re_format::next_grid_tick_magnitude_ns;
use re_log_types::EntityPath;
use re_space_view::{controls, EmptySpaceViewState};
use re_space_view::controls;
use re_viewer_context::{
SpaceViewClass, SpaceViewClassName, SpaceViewId, TypedScene, ViewerContext,
};
Expand All @@ -17,8 +17,8 @@ use crate::scene_part::{PlotSeriesKind, SceneTimeSeries};
pub struct TimeSeriesSpaceView;

impl SpaceViewClass for TimeSeriesSpaceView {
type State = EmptySpaceViewState;
type Context = re_space_view::EmptySceneContext;
type State = ();
type Context = ();
type SceneParts = SceneTimeSeries;
type ScenePartData = ();

Expand Down
6 changes: 3 additions & 3 deletions crates/re_viewer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ pub use store_hub::StoreHub;
pub mod external {
pub use {eframe, egui};
pub use {
re_arrow_store, re_arrow_store::external::arrow2, re_data_store, re_log, re_log_types,
re_memory, re_renderer, re_ui, re_viewer_context, re_viewer_context::external::re_query,
re_viewport,
re_arrow_store, re_arrow_store::external::arrow2, re_components, re_data_store, re_log,
re_log_types, re_memory, re_renderer, re_ui, re_viewer_context,
re_viewer_context::external::re_query, re_viewport, re_viewport::external::re_space_view,
};
}

Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/src/ui/selection_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl SelectionPanel {
&mut ctx.selection_state_mut().history,
) {
ctx.selection_state_mut()
.set_multi_selection(selection.iter().cloned());
.set_selection(selection.iter().cloned());
}
});

Expand Down
6 changes: 3 additions & 3 deletions crates/re_viewer_context/src/selection_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,13 @@ impl SelectionState {
///
/// Returns the previous selection.
pub fn set_single_selection(&mut self, item: Item) -> ItemCollection {
self.set_multi_selection(std::iter::once(item))
self.set_selection(std::iter::once(item))
}

/// Sets several objects to be selected, updating history as needed.
///
/// Returns the previous selection.
pub fn set_multi_selection(&mut self, items: impl Iterator<Item = Item>) -> ItemCollection {
pub fn set_selection(&mut self, items: impl Iterator<Item = Item>) -> ItemCollection {
let new_selection = ItemCollection::new(items);
self.history.update_selection(&new_selection);
std::mem::replace(&mut self.selection, new_selection)
Expand Down Expand Up @@ -190,7 +190,7 @@ impl SelectionState {
.filter(|item| toggle_items_set.contains(item)),
);

self.set_multi_selection(new_selection.into_iter());
self.set_selection(new_selection.into_iter());
}

pub fn hovered_space(&self) -> &HoveredSpace {
Expand Down
13 changes: 13 additions & 0 deletions crates/re_viewer_context/src/space_view/dyn_space_view_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pub trait DynSpaceViewClass {
///
/// Used for both ui display and identification.
/// Must be unique within a viewer session.
///
/// TODO(#2336): Display name and identifier should be separate.
fn name(&self) -> SpaceViewClassName;

/// Icon used to identify this space view class.
Expand Down Expand Up @@ -105,3 +107,14 @@ pub trait SpaceViewState: std::any::Any {
/// Converts itself to a reference of [`std::any::Any`], which enables downcasting to concrete types.
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}

/// Implementation of an empty space view state.
impl SpaceViewState for () {
fn as_any(&self) -> &dyn std::any::Any {
self
}

fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
15 changes: 15 additions & 0 deletions crates/re_viewer_context/src/space_view/scene_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@ pub trait SceneContext {
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}

/// Implementation of an empty scene context.
impl SceneContext for () {
fn vec_mut(&mut self) -> Vec<&mut dyn SceneContextPart> {
Vec::new()
}

fn as_any(&self) -> &dyn std::any::Any {
self
}

fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}

/// Scene context that can be used by scene elements and ui methods to retrieve information about the scene as a whole.
///
/// Is always populated before scene elements.
Expand Down
4 changes: 4 additions & 0 deletions crates/re_viewport/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub use space_view::{SpaceViewBlueprint, SpaceViewState};
pub use view_category::ViewCategory;
pub use viewport::{Viewport, ViewportState};

pub mod external {
pub use re_space_view;
}

// ---------------------------------------------------------------------------

// TODO(andreas): This should be part of re_data_ui::item_ui.
Expand Down
38 changes: 34 additions & 4 deletions crates/re_viewport/src/space_view_heuristics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ pub fn all_possible_space_views(
.chain(root_children.values().map(|sub_tree| &sub_tree.path))
.unique();

// TODO(andreas): Only needed for workaround for custom space views.
// This should go away together with ViewCategory.
let root_space = spaces_info.get_first_parent_with_info(&EntityPath::root());
let root_entities = root_space
.descendants_without_transform
.iter()
.cloned()
.collect::<Vec<_>>();

// For each candidate, create space views for all possible categories.
candidate_space_paths
.flat_map(|candidate_space_path| {
Expand All @@ -46,10 +55,19 @@ pub fn all_possible_space_views(
})
.collect::<Vec<_>>()
})
// TODO(andreas): Hack to get in custom space views.
// .chain(ctx.space_view_class_registry.iter().map(|class| {
// SpaceViewBlueprint::new(class.name(), ViewCategory::Text, &EntityPath::root(), &[])
// }))
// TODO(wumpf): Workaround to add custom space views.
.chain(ctx.space_view_class_registry.iter().filter_map(|class| {
if category_from_class_name(class.name()).is_none() {
Some(SpaceViewBlueprint::new(
class.name(),
ViewCategory::Text,
&EntityPath::root(),
&root_entities,
))
} else {
None
}
}))
.collect()
}

Expand Down Expand Up @@ -370,3 +388,15 @@ fn class_name_from_category(category: ViewCategory) -> SpaceViewClassName {
}
.into()
}

fn category_from_class_name(name: SpaceViewClassName) -> Option<ViewCategory> {
match name.as_str() {
"Text" => Some(ViewCategory::Text),
"Text Box" => Some(ViewCategory::TextBox),
"Time Series" => Some(ViewCategory::TimeSeries),
"Bar Chart" => Some(ViewCategory::BarChart),
"Spatial" => Some(ViewCategory::Spatial),
"Tensor" => Some(ViewCategory::Tensor),
_ => None,
}
}
8 changes: 6 additions & 2 deletions crates/re_viewport/src/viewport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use itertools::Itertools as _;

use re_data_store::EntityPath;
use re_data_ui::item_ui;
use re_space_view::{DataBlueprintGroup, EmptySpaceViewState};
use re_space_view::DataBlueprintGroup;
use re_viewer_context::{
DataBlueprintGroupHandle, Item, SpaceViewClassName, SpaceViewClassRegistry,
SpaceViewHighlights, SpaceViewId, ViewerContext,
Expand Down Expand Up @@ -229,6 +229,10 @@ impl Viewport {
let group_is_visible = group.properties_projected.visible && space_view_visible;

for entity_path in &entities {
if entity_path.is_root() {
continue;
}

ui.horizontal(|ui| {
let mut properties = space_view
.data_blueprint
Expand Down Expand Up @@ -567,7 +571,7 @@ impl ViewportState {
// "Space View class \"{}\" is not registered.",
// space_view_class
// );
Box::<EmptySpaceViewState>::default()
Box::<()>::default()
};
SpaceViewState {
state,
Expand Down
19 changes: 16 additions & 3 deletions docs/content/howto/extend-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ order: 4
description: How to extend the Rerun Viewer UI using Rust and egui
---

### What you can build
# What you can build

## Custom UI embedding the Viewer

![The Rerun Viewer, extended with a custom panel to the right](https://github.com/rerun-io/rerun/assets/1148717/cbbad63e-9b18-4e54-bafe-b6ffd723f63e)

Expand All @@ -17,5 +19,16 @@ The Rerun Viewer is defined by the crate [`re_viewer`](https://github.com/rerun-
The best way to get started is by reading [the source code of the `extend_viewer_ui` example](https://github.com/rerun-io/rerun/tree/main/examples/rust/extend_viewer_ui).


### Future work
We plan to also support embedding your own GUI widgets and views inside the viewer.
## Custom Space Views classes

![The Rerun Viewer, extended with a custom Space View that is shown three times, each time showing points on a colored plane](https://static.rerun.io/9c04c0140552ff9ddd526f98381765382a71e86c_custom_space_view.jpeg)

Above screenshot shows the [`custom_space_view`](https://github.com/rerun-io/rerun/tree/main/examples/rust/custom_space_view) example.
This example demonstrates how to add a fully custom Space View class to Rerun on startup.
Space Views that are added this way have access to the exact same interfaces as all other Space Views,
meaning that any of the built-in Space Views serves can serve as an additional example on how to implement Space Views.

**⚠️ Note that the interface for adding Space Views are very far from stable.** Expect code implementing custom Space Views to break with every release of Rerun.

# Future work
We plan to also support embedding your own GUI widgets inside existing space views.
26 changes: 26 additions & 0 deletions examples/rust/custom_space_view/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "custom_space_view"
version = "0.7.0"
edition = "2021"
rust-version = "1.69"
license = "MIT OR Apache-2.0"
publish = false

[features]
default = []

# Turn on if you want to share analytics with Rerun (e.g. callstacks on crashes).
analytics = ["re_crash_handler/analytics", "re_viewer/analytics"]

[dependencies]
re_crash_handler = { path = "../../../crates/re_crash_handler" }
re_viewer = { path = "../../../crates/re_viewer", default-features = false }

# We need re_sdk_comms to receive log events from and SDK:
re_sdk_comms = { path = "../../../crates/re_sdk_comms", features = ["server"] }

# mimalloc is a much faster allocator:
mimalloc = "0.1"

# We need tokio for re_sdk_comms:
tokio = { version = "1.24", features = ["macros", "rt-multi-thread"] }
17 changes: 17 additions & 0 deletions examples/rust/custom_space_view/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Custom Space View UI
Example showing how to add custom Space View classes to extend the Rerun Viewer.

The example is really basic, but should be something you can build upon.

The example starts an SDK server which the Python or Rust logging SDK can connect to.

![image](https://static.rerun.io/9c04c0140552ff9ddd526f98381765382a71e86c_custom_space_view.jpeg)

[#2337](https://github.com/rerun-io/rerun/issues/2337): Note that in order to spawn a web viewer with these customizations applied,
you have to build the web viewer of the version yourself.
This is currently not supported outside of the Rerun repository.

## Testing it
Start it with `cargo run -p custom_space_view`.

Then put some data into it with: `cargo run -p minimal_options -- --connect`
Loading

0 comments on commit 141c64d

Please sign in to comment.