1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
use nohash_hasher::IntSet;

use re_entity_db::EntityDb;
use re_log_types::EntityPath;
use re_types::{ComponentName, SpaceViewClassIdentifier};

use crate::{
    ApplicableEntities, IndicatedEntities, PerVisualizer, QueryRange, SmallVisualizerSet,
    SpaceViewClassRegistryError, SpaceViewId, SpaceViewSpawnHeuristics,
    SpaceViewSystemExecutionError, SpaceViewSystemRegistrator, SystemExecutionOutput, ViewQuery,
    ViewerContext, VisualizableEntities,
};

#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Ord, Eq)]
pub enum SpaceViewClassLayoutPriority {
    /// This space view can share space with others
    ///
    /// Used for boring things like text and plots.
    Low,

    #[default]
    Medium,

    /// Give this space view lots of space.
    /// Used for spatial views (2D/3D).
    High,
}

/// Context object returned by [`crate::SpaceViewClass::visualizable_filter_context`].
pub trait VisualizableFilterContext {
    fn as_any(&self) -> &dyn std::any::Any;
}

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

/// Defines a class of space view without any concrete types making it suitable for storage and interfacing.
///
/// Each Space View in the viewer's viewport has a single class assigned immutable at its creation time.
/// The class defines all aspects of its behavior.
/// It determines which entities are queried, how they are rendered, and how the user can interact with them.
//
// TODO(andreas): Consider formulating a space view instance context object that is passed to all
// methods that operate on concrete space views as opposed to be about general information on the class.
pub trait SpaceViewClass: Send + Sync {
    /// Identifier string of this space view class.
    ///
    /// By convention we use `PascalCase`.
    fn identifier() -> SpaceViewClassIdentifier
    where
        Self: Sized;

    /// User-facing name of this space view class.
    ///
    /// Used for UI display.
    fn display_name(&self) -> &'static str;

    /// Icon used to identify this space view class.
    fn icon(&self) -> &'static re_ui::Icon {
        &re_ui::icons::SPACE_VIEW_GENERIC
    }

    /// Help text describing how to interact with this space view in the ui.
    fn help_markdown(&self, egui_ctx: &egui::Context) -> String;

    /// Called once upon registration of the class
    ///
    /// This can be used to register all built-in [`crate::ViewContextSystem`] and [`crate::VisualizerSystem`].
    fn on_register(
        &self,
        system_registry: &mut SpaceViewSystemRegistrator<'_>,
    ) -> Result<(), SpaceViewClassRegistryError>;

    /// Called once for every new space view instance of this class.
    ///
    /// The state is *not* persisted across viewer sessions, only shared frame-to-frame.
    fn new_state(&self) -> Box<dyn SpaceViewState>;

    /// Optional archetype of the Space View's blueprint properties.
    ///
    /// Blueprint components that only apply to the space view itself, not to the entities it displays.
    fn blueprint_archetype(&self) -> Option<Vec<ComponentName>> {
        None
    }

    /// Preferred aspect ratio for the ui tiles of this space view.
    fn preferred_tile_aspect_ratio(&self, _state: &dyn SpaceViewState) -> Option<f32> {
        None
    }

    /// Controls how likely this space view will get a large tile in the ui.
    fn layout_priority(&self) -> SpaceViewClassLayoutPriority;

    /// Default query range for this space view.
    //TODO(#6918): also provide ViewerContext and SpaceViewId, to enable reading view properties.
    fn default_query_range(&self, _state: &dyn SpaceViewState) -> QueryRange {
        QueryRange::LatestAt
    }

    /// Determines a suitable origin given the provided set of entities.
    ///
    /// This function only considers the transform topology, disregarding the actual visualizability
    /// of the entities (for this, use [`Self::visualizable_filter_context`]).
    fn recommended_root_for_entities(
        &self,
        _entities: &IntSet<EntityPath>,
        _entity_db: &re_entity_db::EntityDb,
    ) -> Option<EntityPath> {
        Some(EntityPath::root())
    }

    /// Create context object that is passed to all of this classes visualizers
    /// to determine whether they can be visualized
    ///
    /// See [`crate::VisualizerSystem::filter_visualizable_entities`].
    fn visualizable_filter_context(
        &self,
        _space_origin: &EntityPath,
        _entity_db: &re_entity_db::EntityDb,
    ) -> Box<dyn VisualizableFilterContext> {
        Box::new(())
    }

    /// Choose the default visualizers to enable for this entity.
    ///
    /// Helpful for customizing fallback behavior for types that are insufficient
    /// to determine indicated on their own.
    ///
    /// Will only be called for entities where the selected visualizers have not
    /// been overridden by the blueprint.
    ///
    /// This interface provides a default implementation which will return all visualizers
    /// which are both visualizable and indicated for the given entity.
    fn choose_default_visualizers(
        &self,
        entity_path: &EntityPath,
        _applicable_entities_per_visualizer: &PerVisualizer<ApplicableEntities>,
        visualizable_entities_per_visualizer: &PerVisualizer<VisualizableEntities>,
        indicated_entities_per_visualizer: &PerVisualizer<IndicatedEntities>,
    ) -> SmallVisualizerSet {
        let available_visualizers =
            visualizable_entities_per_visualizer
                .iter()
                .filter_map(|(visualizer, ents)| {
                    if ents.contains(entity_path) {
                        Some(visualizer)
                    } else {
                        None
                    }
                });

        available_visualizers
            .filter_map(|visualizer| {
                if indicated_entities_per_visualizer
                    .get(visualizer)
                    .map_or(false, |matching_list| matching_list.contains(entity_path))
                {
                    Some(*visualizer)
                } else {
                    None
                }
            })
            .collect()
    }

    /// Determines which space views should be spawned by default for this class.
    fn spawn_heuristics(&self, ctx: &ViewerContext<'_>) -> SpaceViewSpawnHeuristics;

    /// Ui shown when the user selects a space view of this class.
    fn selection_ui(
        &self,
        _ctx: &ViewerContext<'_>,
        _ui: &mut egui::Ui,
        _state: &mut dyn SpaceViewState,
        _space_origin: &EntityPath,
        _space_view_id: SpaceViewId,
    ) -> Result<(), SpaceViewSystemExecutionError> {
        Ok(())
    }

    /// Additional UI displayed in the tab title bar, between the "maximize" and "help" buttons.
    ///
    /// Note: this is a right-to-left layout.
    fn extra_title_bar_ui(
        &self,
        _ctx: &ViewerContext<'_>,
        _ui: &mut egui::Ui,
        _state: &mut dyn SpaceViewState,
        _space_origin: &EntityPath,
        _space_view_id: SpaceViewId,
    ) -> Result<(), SpaceViewSystemExecutionError> {
        Ok(())
    }

    /// Draws the ui for this space view class and handles ui events.
    ///
    /// The passed state is kept frame-to-frame.
    ///
    /// TODO(wumpf): Right now the ui methods control when and how to create [`re_renderer::ViewBuilder`]s.
    ///              In the future, we likely want to move view builder handling to `re_viewport` with
    ///              minimal configuration options exposed via [`crate::SpaceViewClass`].
    fn ui(
        &self,
        ctx: &ViewerContext<'_>,
        ui: &mut egui::Ui,
        state: &mut dyn SpaceViewState,
        query: &ViewQuery<'_>,
        system_output: SystemExecutionOutput,
    ) -> Result<(), SpaceViewSystemExecutionError>;
}

pub trait SpaceViewClassExt<'a>: SpaceViewClass + 'a {
    /// Determines the set of visible entities for a given space view.
    // TODO(andreas): This should be part of the SpaceView's (non-blueprint) state.
    // Updated whenever `applicable_entities_per_visualizer` or the space view blueprint changes.
    fn determine_visualizable_entities(
        &self,
        applicable_entities_per_visualizer: &PerVisualizer<ApplicableEntities>,
        entity_db: &EntityDb,
        visualizers: &crate::VisualizerCollection,
        space_origin: &EntityPath,
    ) -> PerVisualizer<VisualizableEntities> {
        re_tracing::profile_function!();

        let filter_ctx = self.visualizable_filter_context(space_origin, entity_db);

        PerVisualizer::<VisualizableEntities>(
            visualizers
                .iter_with_identifiers()
                .map(|(visualizer_identifier, visualizer_system)| {
                    let entities = if let Some(applicable_entities) =
                        applicable_entities_per_visualizer.get(&visualizer_identifier)
                    {
                        visualizer_system.filter_visualizable_entities(
                            applicable_entities.clone(),
                            filter_ctx.as_ref(),
                        )
                    } else {
                        VisualizableEntities::default()
                    };

                    (visualizer_identifier, entities)
                })
                .collect(),
        )
    }
}

impl<'a> SpaceViewClassExt<'a> for dyn SpaceViewClass + 'a {}

/// Unserialized frame to frame state of a space view.
///
/// For any state that should be persisted, use the Blueprint!
/// This state is used for transient state, such as animation or uncommitted ui state like dragging a camera.
/// (on mouse release, the camera would be committed to the blueprint).
pub trait SpaceViewState: std::any::Any + Sync + Send {
    /// Converts itself to a reference of [`std::any::Any`], which enables downcasting to concrete types.
    fn as_any(&self) -> &dyn 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
    }
}

pub trait SpaceViewStateExt: SpaceViewState {
    /// Downcasts this state to a reference of a concrete type.
    fn downcast_ref<T: SpaceViewState>(&self) -> Result<&T, SpaceViewSystemExecutionError> {
        self.as_any()
            .downcast_ref()
            .ok_or(SpaceViewSystemExecutionError::StateCastError(
                std::any::type_name::<T>(),
            ))
    }

    /// Downcasts this state to a mutable reference of a concrete type.
    fn downcast_mut<T: SpaceViewState>(&mut self) -> Result<&mut T, SpaceViewSystemExecutionError> {
        self.as_any_mut()
            .downcast_mut()
            .ok_or(SpaceViewSystemExecutionError::StateCastError(
                std::any::type_name::<T>(),
            ))
    }
}

impl SpaceViewStateExt for dyn SpaceViewState {}