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 299 300 301 302
use nohash_hasher::IntSet;
use re_entity_db::EntityDb;
use re_log_types::EntityPath;
use re_types::{ComponentName, ViewClassIdentifier};
use crate::{
ApplicableEntities, IndicatedEntities, PerVisualizer, QueryRange, SmallVisualizerSet,
SystemExecutionOutput, ViewClassRegistryError, ViewId, ViewQuery, ViewSpawnHeuristics,
ViewSystemExecutionError, ViewSystemRegistrator, ViewerContext, VisualizableEntities,
};
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Ord, Eq)]
pub enum ViewClassLayoutPriority {
/// This view can share space with others
///
/// Used for boring things like text and plots.
Low,
#[default]
Medium,
/// Give this view lots of space.
/// Used for spatial views (2D/3D).
High,
}
/// Context object returned by [`crate::ViewClass::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 view without any concrete types making it suitable for storage and interfacing.
///
/// Each 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 view instance context object that is passed to all
// methods that operate on concrete views as opposed to be about general information on the class.
pub trait ViewClass: Send + Sync {
/// Identifier string of this view class.
///
/// By convention we use `PascalCase`.
fn identifier() -> ViewClassIdentifier
where
Self: Sized;
/// User-facing name of this view class.
///
/// Used for UI display.
fn display_name(&self) -> &'static str;
/// Icon used to identify this view class.
fn icon(&self) -> &'static re_ui::Icon {
&re_ui::icons::VIEW_GENERIC
}
/// Help text describing how to interact with this 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 ViewSystemRegistrator<'_>,
) -> Result<(), ViewClassRegistryError>;
/// Called once for every new view instance of this class.
///
/// The state is *not* persisted across viewer sessions, only shared frame-to-frame.
fn new_state(&self) -> Box<dyn ViewState>;
/// Optional archetype of the View's blueprint properties.
///
/// Blueprint components that only apply to the 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 view.
fn preferred_tile_aspect_ratio(&self, _state: &dyn ViewState) -> Option<f32> {
None
}
/// Controls how likely this view will get a large tile in the ui.
fn layout_priority(&self) -> ViewClassLayoutPriority;
/// Controls whether the visible time range UI should be displayed for this view.
fn supports_visible_time_range(&self) -> bool {
false
}
/// Default query range for this view.
//TODO(#6918): also provide ViewerContext and ViewId, to enable reading view properties.
fn default_query_range(&self, _state: &dyn ViewState) -> 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 views should be spawned by default for this class.
fn spawn_heuristics(&self, ctx: &ViewerContext<'_>) -> ViewSpawnHeuristics;
/// Ui shown when the user selects a view of this class.
fn selection_ui(
&self,
_ctx: &ViewerContext<'_>,
_ui: &mut egui::Ui,
_state: &mut dyn ViewState,
_space_origin: &EntityPath,
_view_id: ViewId,
) -> Result<(), ViewSystemExecutionError> {
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 ViewState,
_space_origin: &EntityPath,
_view_id: ViewId,
) -> Result<(), ViewSystemExecutionError> {
Ok(())
}
/// Draws the ui for this 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::ViewClass`].
fn ui(
&self,
ctx: &ViewerContext<'_>,
ui: &mut egui::Ui,
state: &mut dyn ViewState,
query: &ViewQuery<'_>,
system_output: SystemExecutionOutput,
) -> Result<(), ViewSystemExecutionError>;
}
pub trait ViewClassExt<'a>: ViewClass + 'a {
/// Determines the set of visible entities for a given view.
// TODO(andreas): This should be part of the View's (non-blueprint) state.
// Updated whenever `applicable_entities_per_visualizer` or the 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> ViewClassExt<'a> for dyn ViewClass + 'a {}
/// Unserialized frame to frame state of a 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 ViewState: 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 view state.
impl ViewState for () {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
pub trait ViewStateExt: ViewState {
/// Downcasts this state to a reference of a concrete type.
fn downcast_ref<T: ViewState>(&self) -> Result<&T, ViewSystemExecutionError> {
self.as_any()
.downcast_ref()
.ok_or(ViewSystemExecutionError::StateCastError(
std::any::type_name::<T>(),
))
}
/// Downcasts this state to a mutable reference of a concrete type.
fn downcast_mut<T: ViewState>(&mut self) -> Result<&mut T, ViewSystemExecutionError> {
self.as_any_mut()
.downcast_mut()
.ok_or(ViewSystemExecutionError::StateCastError(
std::any::type_name::<T>(),
))
}
}
impl ViewStateExt for dyn ViewState {}