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