re_viewer_context/view/
view_class.rs

1use nohash_hasher::IntSet;
2
3use re_entity_db::EntityDb;
4use re_log_types::{EntityPath, ResolvedEntityPathFilter};
5use re_types::{ComponentName, ViewClassIdentifier};
6
7use crate::{
8    IndicatedEntities, MaybeVisualizableEntities, PerVisualizer, QueryRange, SmallVisualizerSet,
9    SystemExecutionOutput, ViewClassRegistryError, ViewId, ViewQuery, ViewSpawnHeuristics,
10    ViewSystemExecutionError, ViewSystemRegistrator, ViewerContext, VisualizableEntities,
11};
12
13#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Ord, Eq)]
14pub enum ViewClassLayoutPriority {
15    /// This view can share space with others
16    ///
17    /// Used for boring things like text and plots.
18    Low,
19
20    #[default]
21    Medium,
22
23    /// Give this view lots of space.
24    /// Used for spatial views (2D/3D).
25    High,
26}
27
28/// Context object returned by [`crate::ViewClass::visualizable_filter_context`].
29pub trait VisualizableFilterContext {
30    fn as_any(&self) -> &dyn std::any::Any;
31}
32
33impl VisualizableFilterContext for () {
34    fn as_any(&self) -> &dyn std::any::Any {
35        self
36    }
37}
38
39/// Defines a class of view without any concrete types making it suitable for storage and interfacing.
40///
41/// Each View in the viewer's viewport has a single class assigned immutable at its creation time.
42/// The class defines all aspects of its behavior.
43/// It determines which entities are queried, how they are rendered, and how the user can interact with them.
44//
45// TODO(andreas): Consider formulating a view instance context object that is passed to all
46// methods that operate on concrete views as opposed to be about general information on the class.
47pub trait ViewClass: Send + Sync {
48    /// Identifier string of this view class.
49    ///
50    /// By convention we use `PascalCase`.
51    fn identifier() -> ViewClassIdentifier
52    where
53        Self: Sized;
54
55    /// User-facing name of this view class.
56    ///
57    /// Used for UI display.
58    fn display_name(&self) -> &'static str;
59
60    /// Icon used to identify this view class.
61    fn icon(&self) -> &'static re_ui::Icon {
62        &re_ui::icons::VIEW_GENERIC
63    }
64
65    fn help(&self, egui_ctx: &egui::Context) -> re_ui::Help;
66
67    /// Called once upon registration of the class
68    ///
69    /// This can be used to register all built-in [`crate::ViewContextSystem`] and [`crate::VisualizerSystem`].
70    fn on_register(
71        &self,
72        system_registry: &mut ViewSystemRegistrator<'_>,
73    ) -> Result<(), ViewClassRegistryError>;
74
75    /// Called once for every new view instance of this class.
76    ///
77    /// The state is *not* persisted across viewer sessions, only shared frame-to-frame.
78    fn new_state(&self) -> Box<dyn ViewState>;
79
80    /// Optional archetype of the View's blueprint properties.
81    ///
82    /// Blueprint components that only apply to the view itself, not to the entities it displays.
83    fn blueprint_archetype(&self) -> Option<Vec<ComponentName>> {
84        None
85    }
86
87    /// Preferred aspect ratio for the ui tiles of this view.
88    fn preferred_tile_aspect_ratio(&self, _state: &dyn ViewState) -> Option<f32> {
89        None
90    }
91
92    /// Controls how likely this view will get a large tile in the ui.
93    fn layout_priority(&self) -> ViewClassLayoutPriority;
94
95    /// Controls whether the visible time range UI should be displayed for this view.
96    fn supports_visible_time_range(&self) -> bool {
97        false
98    }
99
100    /// Default query range for this view.
101    //TODO(#6918): also provide ViewerContext and ViewId, to enable reading view properties.
102    fn default_query_range(&self, _state: &dyn ViewState) -> QueryRange {
103        QueryRange::LatestAt
104    }
105
106    /// Determines a suitable origin given the provided set of entities.
107    ///
108    /// This function only considers the transform topology, disregarding the actual visualizability
109    /// of the entities (for this, use [`Self::visualizable_filter_context`]).
110    fn recommended_root_for_entities(
111        &self,
112        _entities: &IntSet<EntityPath>,
113        _entity_db: &re_entity_db::EntityDb,
114    ) -> Option<EntityPath> {
115        Some(EntityPath::root())
116    }
117
118    /// Create context object that is passed to all of this classes visualizers
119    /// to determine whether they can be visualized
120    ///
121    /// See [`crate::VisualizerSystem::filter_visualizable_entities`].
122    fn visualizable_filter_context(
123        &self,
124        _space_origin: &EntityPath,
125        _entity_db: &re_entity_db::EntityDb,
126    ) -> Box<dyn VisualizableFilterContext> {
127        Box::new(())
128    }
129
130    /// Choose the default visualizers to enable for this entity.
131    ///
132    /// Helpful for customizing fallback behavior for types that are insufficient
133    /// to determine indicated on their own.
134    ///
135    /// Will only be called for entities where the selected visualizers have not
136    /// been overridden by the blueprint.
137    ///
138    /// This interface provides a default implementation which will return all visualizers
139    /// which are both visualizable and indicated for the given entity.
140    fn choose_default_visualizers(
141        &self,
142        entity_path: &EntityPath,
143        _maybe_visualizable_entities_per_visualizer: &PerVisualizer<MaybeVisualizableEntities>,
144        visualizable_entities_per_visualizer: &PerVisualizer<VisualizableEntities>,
145        indicated_entities_per_visualizer: &PerVisualizer<IndicatedEntities>,
146    ) -> SmallVisualizerSet {
147        let available_visualizers =
148            visualizable_entities_per_visualizer
149                .iter()
150                .filter_map(|(visualizer, ents)| {
151                    if ents.contains(entity_path) {
152                        Some(visualizer)
153                    } else {
154                        None
155                    }
156                });
157
158        available_visualizers
159            .filter_map(|visualizer| {
160                if indicated_entities_per_visualizer
161                    .get(visualizer)
162                    .is_some_and(|matching_list| matching_list.contains(entity_path))
163                {
164                    Some(*visualizer)
165                } else {
166                    None
167                }
168            })
169            .collect()
170    }
171
172    /// Determines which views should be spawned by default for this class.
173    ///
174    /// As the name implies, `suggested_filter` is only a suggestion and can be,
175    /// overwritten if a view decides to display more data.
176    fn spawn_heuristics(
177        &self,
178        ctx: &ViewerContext<'_>,
179        suggested_filter: &ResolvedEntityPathFilter,
180    ) -> ViewSpawnHeuristics;
181
182    /// Ui shown when the user selects a view of this class.
183    fn selection_ui(
184        &self,
185        _ctx: &ViewerContext<'_>,
186        _ui: &mut egui::Ui,
187        _state: &mut dyn ViewState,
188        _space_origin: &EntityPath,
189        _view_id: ViewId,
190    ) -> Result<(), ViewSystemExecutionError> {
191        Ok(())
192    }
193
194    /// Additional UI displayed in the tab title bar, between the "maximize" and "help" buttons.
195    ///
196    /// Note: this is a right-to-left layout.
197    fn extra_title_bar_ui(
198        &self,
199        _ctx: &ViewerContext<'_>,
200        _ui: &mut egui::Ui,
201        _state: &mut dyn ViewState,
202        _space_origin: &EntityPath,
203        _view_id: ViewId,
204    ) -> Result<(), ViewSystemExecutionError> {
205        Ok(())
206    }
207
208    /// Draws the ui for this view class and handles ui events.
209    ///
210    /// The passed state is kept frame-to-frame.
211    ///
212    /// TODO(wumpf): Right now the ui methods control when and how to create [`re_renderer::ViewBuilder`]s.
213    ///              In the future, we likely want to move view builder handling to `re_viewport` with
214    ///              minimal configuration options exposed via [`crate::ViewClass`].
215    fn ui(
216        &self,
217        ctx: &ViewerContext<'_>,
218        ui: &mut egui::Ui,
219        state: &mut dyn ViewState,
220        query: &ViewQuery<'_>,
221        system_output: SystemExecutionOutput,
222    ) -> Result<(), ViewSystemExecutionError>;
223}
224
225pub trait ViewClassExt<'a>: ViewClass + 'a {
226    /// Determines the set of visible entities for a given view.
227    // TODO(andreas): This should be part of the View's (non-blueprint) state.
228    // Updated whenever `maybe_visualizable_entities_per_visualizer` or the view blueprint changes.
229    fn determine_visualizable_entities(
230        &self,
231        maybe_visualizable_entities_per_visualizer: &PerVisualizer<MaybeVisualizableEntities>,
232        entity_db: &EntityDb,
233        visualizers: &crate::VisualizerCollection,
234        space_origin: &EntityPath,
235    ) -> PerVisualizer<VisualizableEntities> {
236        re_tracing::profile_function!();
237
238        let filter_ctx = self.visualizable_filter_context(space_origin, entity_db);
239
240        PerVisualizer::<VisualizableEntities>(
241            visualizers
242                .iter_with_identifiers()
243                .map(|(visualizer_identifier, visualizer_system)| {
244                    let entities = if let Some(maybe_visualizable_entities) =
245                        maybe_visualizable_entities_per_visualizer.get(&visualizer_identifier)
246                    {
247                        visualizer_system.filter_visualizable_entities(
248                            maybe_visualizable_entities.clone(),
249                            filter_ctx.as_ref(),
250                        )
251                    } else {
252                        VisualizableEntities::default()
253                    };
254
255                    (visualizer_identifier, entities)
256                })
257                .collect(),
258        )
259    }
260}
261
262impl<'a> ViewClassExt<'a> for dyn ViewClass + 'a {}
263
264/// Unserialized frame to frame state of a view.
265///
266/// For any state that should be persisted, use the Blueprint!
267/// This state is used for transient state, such as animation or uncommitted ui state like dragging a camera.
268/// (on mouse release, the camera would be committed to the blueprint).
269pub trait ViewState: std::any::Any + Sync + Send {
270    /// Converts itself to a reference of [`std::any::Any`], which enables downcasting to concrete types.
271    fn as_any(&self) -> &dyn std::any::Any;
272
273    /// Converts itself to a reference of [`std::any::Any`], which enables downcasting to concrete types.
274    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
275}
276
277/// Implementation of an empty view state.
278impl ViewState for () {
279    fn as_any(&self) -> &dyn std::any::Any {
280        self
281    }
282
283    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
284        self
285    }
286}
287
288pub trait ViewStateExt: ViewState {
289    /// Downcasts this state to a reference of a concrete type.
290    fn downcast_ref<T: ViewState>(&self) -> Result<&T, ViewSystemExecutionError> {
291        self.as_any()
292            .downcast_ref()
293            .ok_or(ViewSystemExecutionError::StateCastError(
294                std::any::type_name::<T>(),
295            ))
296    }
297
298    /// Downcasts this state to a mutable reference of a concrete type.
299    fn downcast_mut<T: ViewState>(&mut self) -> Result<&mut T, ViewSystemExecutionError> {
300        self.as_any_mut()
301            .downcast_mut()
302            .ok_or(ViewSystemExecutionError::StateCastError(
303                std::any::type_name::<T>(),
304            ))
305    }
306}
307
308impl ViewStateExt for dyn ViewState {}