re_viewer_context/view/
view_class.rs

1use nohash_hasher::IntSet;
2
3use re_entity_db::EntityDb;
4use re_log_types::EntityPath;
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    fn spawn_heuristics(&self, ctx: &ViewerContext<'_>) -> ViewSpawnHeuristics;
174
175    /// Ui shown when the user selects a view of this class.
176    fn selection_ui(
177        &self,
178        _ctx: &ViewerContext<'_>,
179        _ui: &mut egui::Ui,
180        _state: &mut dyn ViewState,
181        _space_origin: &EntityPath,
182        _view_id: ViewId,
183    ) -> Result<(), ViewSystemExecutionError> {
184        Ok(())
185    }
186
187    /// Additional UI displayed in the tab title bar, between the "maximize" and "help" buttons.
188    ///
189    /// Note: this is a right-to-left layout.
190    fn extra_title_bar_ui(
191        &self,
192        _ctx: &ViewerContext<'_>,
193        _ui: &mut egui::Ui,
194        _state: &mut dyn ViewState,
195        _space_origin: &EntityPath,
196        _view_id: ViewId,
197    ) -> Result<(), ViewSystemExecutionError> {
198        Ok(())
199    }
200
201    /// Draws the ui for this view class and handles ui events.
202    ///
203    /// The passed state is kept frame-to-frame.
204    ///
205    /// TODO(wumpf): Right now the ui methods control when and how to create [`re_renderer::ViewBuilder`]s.
206    ///              In the future, we likely want to move view builder handling to `re_viewport` with
207    ///              minimal configuration options exposed via [`crate::ViewClass`].
208    fn ui(
209        &self,
210        ctx: &ViewerContext<'_>,
211        ui: &mut egui::Ui,
212        state: &mut dyn ViewState,
213        query: &ViewQuery<'_>,
214        system_output: SystemExecutionOutput,
215    ) -> Result<(), ViewSystemExecutionError>;
216}
217
218pub trait ViewClassExt<'a>: ViewClass + 'a {
219    /// Determines the set of visible entities for a given view.
220    // TODO(andreas): This should be part of the View's (non-blueprint) state.
221    // Updated whenever `maybe_visualizable_entities_per_visualizer` or the view blueprint changes.
222    fn determine_visualizable_entities(
223        &self,
224        maybe_visualizable_entities_per_visualizer: &PerVisualizer<MaybeVisualizableEntities>,
225        entity_db: &EntityDb,
226        visualizers: &crate::VisualizerCollection,
227        space_origin: &EntityPath,
228    ) -> PerVisualizer<VisualizableEntities> {
229        re_tracing::profile_function!();
230
231        let filter_ctx = self.visualizable_filter_context(space_origin, entity_db);
232
233        PerVisualizer::<VisualizableEntities>(
234            visualizers
235                .iter_with_identifiers()
236                .map(|(visualizer_identifier, visualizer_system)| {
237                    let entities = if let Some(maybe_visualizable_entities) =
238                        maybe_visualizable_entities_per_visualizer.get(&visualizer_identifier)
239                    {
240                        visualizer_system.filter_visualizable_entities(
241                            maybe_visualizable_entities.clone(),
242                            filter_ctx.as_ref(),
243                        )
244                    } else {
245                        VisualizableEntities::default()
246                    };
247
248                    (visualizer_identifier, entities)
249                })
250                .collect(),
251        )
252    }
253}
254
255impl<'a> ViewClassExt<'a> for dyn ViewClass + 'a {}
256
257/// Unserialized frame to frame state of a view.
258///
259/// For any state that should be persisted, use the Blueprint!
260/// This state is used for transient state, such as animation or uncommitted ui state like dragging a camera.
261/// (on mouse release, the camera would be committed to the blueprint).
262pub trait ViewState: std::any::Any + Sync + Send {
263    /// Converts itself to a reference of [`std::any::Any`], which enables downcasting to concrete types.
264    fn as_any(&self) -> &dyn std::any::Any;
265
266    /// Converts itself to a reference of [`std::any::Any`], which enables downcasting to concrete types.
267    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
268}
269
270/// Implementation of an empty view state.
271impl ViewState for () {
272    fn as_any(&self) -> &dyn std::any::Any {
273        self
274    }
275
276    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
277        self
278    }
279}
280
281pub trait ViewStateExt: ViewState {
282    /// Downcasts this state to a reference of a concrete type.
283    fn downcast_ref<T: ViewState>(&self) -> Result<&T, ViewSystemExecutionError> {
284        self.as_any()
285            .downcast_ref()
286            .ok_or(ViewSystemExecutionError::StateCastError(
287                std::any::type_name::<T>(),
288            ))
289    }
290
291    /// Downcasts this state to a mutable reference of a concrete type.
292    fn downcast_mut<T: ViewState>(&mut self) -> Result<&mut T, ViewSystemExecutionError> {
293        self.as_any_mut()
294            .downcast_mut()
295            .ok_or(ViewSystemExecutionError::StateCastError(
296                std::any::type_name::<T>(),
297            ))
298    }
299}
300
301impl ViewStateExt for dyn ViewState {}