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 {}