diff --git a/Cargo.lock b/Cargo.lock index 3088ca7f2607..f1f04e3618c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4649,6 +4649,7 @@ dependencies = [ name = "re_space_view" version = "0.13.0-alpha.2" dependencies = [ + "ahash", "egui", "itertools 0.12.0", "nohash-hasher", @@ -4987,6 +4988,7 @@ dependencies = [ "eframe", "egui", "egui-wgpu", + "egui_extras", "egui_plot", "egui_tiles", "ehttp", diff --git a/crates/re_data_ui/src/component_ui_registry.rs b/crates/re_data_ui/src/component_ui_registry.rs index 25901d6a135e..7c5caeb05022 100644 --- a/crates/re_data_ui/src/component_ui_registry.rs +++ b/crates/re_data_ui/src/component_ui_registry.rs @@ -4,6 +4,8 @@ use re_query::ComponentWithInstances; use re_types::external::arrow2::array::Utf8Array; use re_viewer_context::{ComponentUiRegistry, UiVerbosity, ViewerContext}; +use crate::editors::register_editors; + use super::EntityDataUi; pub fn create_component_ui_registry() -> ComponentUiRegistry { @@ -29,6 +31,8 @@ pub fn create_component_ui_registry() -> ComponentUiRegistry { add_to_registry::(&mut registry); + register_editors(&mut registry); + registry } diff --git a/crates/re_data_ui/src/editors.rs b/crates/re_data_ui/src/editors.rs new file mode 100644 index 000000000000..2f42b02f6e1f --- /dev/null +++ b/crates/re_data_ui/src/editors.rs @@ -0,0 +1,221 @@ +// TODO(jleibs): Turn this into a trait + +use egui::NumExt as _; +use re_data_store::{DataStore, LatestAtQuery}; +use re_log_types::EntityPath; +use re_query::ComponentWithInstances; +use re_types::{ + components::{Color, Radius, ScalarScattering, Text}, + Component, Loggable, +}; +use re_viewer_context::{UiVerbosity, ViewerContext}; + +#[allow(clippy::too_many_arguments)] +fn edit_color_ui( + ctx: &ViewerContext<'_>, + ui: &mut egui::Ui, + _verbosity: UiVerbosity, + query: &LatestAtQuery, + store: &DataStore, + entity_path: &EntityPath, + override_path: &EntityPath, + component: &ComponentWithInstances, + instance_key: &re_types::components::InstanceKey, +) { + let current_color = component + .lookup::(instance_key) + .ok() + .unwrap_or_else(|| default_color(ctx, query, store, entity_path)); + + let [r, g, b, a] = current_color.to_array(); + let current_color = egui::Color32::from_rgba_unmultiplied(r, g, b, a); + let mut edit_color = current_color; + + egui::color_picker::color_edit_button_srgba( + ui, + &mut edit_color, + egui::color_picker::Alpha::Opaque, + ); + + if edit_color != current_color { + let [r, g, b, a] = edit_color.to_array(); + let new_color = Color::from_unmultiplied_rgba(r, g, b, a); + + ctx.save_blueprint_component(override_path, new_color); + } +} + +#[inline] +fn default_color( + _ctx: &ViewerContext<'_>, + _query: &LatestAtQuery, + _store: &DataStore, + _entity_path: &EntityPath, +) -> Color { + Color::from_rgb(255, 255, 255) +} + +#[allow(clippy::too_many_arguments)] +fn edit_text_ui( + ctx: &ViewerContext<'_>, + ui: &mut egui::Ui, + _verbosity: UiVerbosity, + query: &LatestAtQuery, + store: &DataStore, + entity_path: &EntityPath, + override_path: &EntityPath, + component: &ComponentWithInstances, + instance_key: &re_types::components::InstanceKey, +) { + let current_text = component + .lookup::(instance_key) + .ok() + .unwrap_or_else(|| default_text(ctx, query, store, entity_path)); + + let current_text = current_text.to_string(); + let mut edit_text = current_text.clone(); + + egui::TextEdit::singleline(&mut edit_text).show(ui); + + if edit_text != current_text { + let new_text = Text::from(edit_text); + + ctx.save_blueprint_component(override_path, new_text); + } +} + +#[inline] +fn default_text( + _ctx: &ViewerContext<'_>, + _query: &LatestAtQuery, + _store: &DataStore, + entity_path: &EntityPath, +) -> Text { + Text::from(entity_path.to_string()) +} + +#[allow(clippy::too_many_arguments)] +fn edit_scatter_ui( + ctx: &ViewerContext<'_>, + ui: &mut egui::Ui, + _verbosity: UiVerbosity, + query: &LatestAtQuery, + store: &DataStore, + entity_path: &EntityPath, + override_path: &EntityPath, + component: &ComponentWithInstances, + instance_key: &re_types::components::InstanceKey, +) { + let current_scatter = component + .lookup::(instance_key) + .ok() + .unwrap_or_else(|| default_scatter(ctx, query, store, entity_path)); + + let current_scatter = current_scatter.0; + let mut edit_scatter = current_scatter; + + let scattered_text = if current_scatter { "Scattered" } else { "Line" }; + + egui::ComboBox::from_id_source("scatter") + .selected_text(scattered_text) + .show_ui(ui, |ui| { + ui.style_mut().wrap = Some(false); + ui.selectable_value(&mut edit_scatter, false, "Line"); + ui.selectable_value(&mut edit_scatter, true, "Scattered"); + }); + + if edit_scatter != current_scatter { + let new_scatter = ScalarScattering::from(edit_scatter); + + ctx.save_blueprint_component(override_path, new_scatter); + } +} + +#[inline] +fn default_scatter( + _ctx: &ViewerContext<'_>, + _query: &LatestAtQuery, + _store: &DataStore, + _entity_path: &EntityPath, +) -> ScalarScattering { + ScalarScattering::from(false) +} + +#[allow(clippy::too_many_arguments)] +fn edit_radius_ui( + ctx: &ViewerContext<'_>, + ui: &mut egui::Ui, + _verbosity: UiVerbosity, + query: &LatestAtQuery, + store: &DataStore, + entity_path: &EntityPath, + override_path: &EntityPath, + component: &ComponentWithInstances, + instance_key: &re_types::components::InstanceKey, +) { + let current_radius = component + .lookup::(instance_key) + .ok() + .unwrap_or_else(|| default_radius(ctx, query, store, entity_path)); + + let current_radius = current_radius.0; + let mut edit_radius = current_radius; + + let speed = (current_radius * 0.01).at_least(0.001); + + ui.add( + egui::DragValue::new(&mut edit_radius) + .clamp_range(0.0..=f64::INFINITY) + .speed(speed), + ); + + if edit_radius != current_radius { + let new_radius = Radius::from(edit_radius); + + ctx.save_blueprint_component(override_path, new_radius); + } +} + +#[inline] +fn default_radius( + _ctx: &ViewerContext<'_>, + _query: &LatestAtQuery, + _store: &DataStore, + _entity_path: &EntityPath, +) -> Radius { + Radius::from(1.0) +} + +fn register_editor<'a, C: Component + Loggable + 'static>( + registry: &mut re_viewer_context::ComponentUiRegistry, + default: fn(&ViewerContext<'_>, &LatestAtQuery, &DataStore, &EntityPath) -> C, + edit: fn( + &ViewerContext<'_>, + &mut egui::Ui, + UiVerbosity, + &LatestAtQuery, + &DataStore, + &EntityPath, + &EntityPath, + &ComponentWithInstances, + &re_types::components::InstanceKey, + ), +) where + C: Into<::std::borrow::Cow<'a, C>>, +{ + registry.add_editor( + C::name(), + Box::new(move |ctx, query, store, entity_path| { + let c = default(ctx, query, store, entity_path); + [c].into() + }), + Box::new(edit), + ); +} + +pub fn register_editors(registry: &mut re_viewer_context::ComponentUiRegistry) { + register_editor::(registry, default_color, edit_color_ui); + register_editor::(registry, default_text, edit_text_ui); + register_editor::(registry, default_scatter, edit_scatter_ui); + register_editor::(registry, default_radius, edit_radius_ui); +} diff --git a/crates/re_data_ui/src/lib.rs b/crates/re_data_ui/src/lib.rs index d27ea982e9a5..77089b26490a 100644 --- a/crates/re_data_ui/src/lib.rs +++ b/crates/re_data_ui/src/lib.rs @@ -14,6 +14,7 @@ mod component; mod component_path; mod component_ui_registry; mod data; +mod editors; mod entity_db; mod entity_path; mod image; @@ -30,6 +31,7 @@ pub use crate::image::{ show_zoomed_image_region, show_zoomed_image_region_area_outline, tensor_summary_ui_grid_contents, }; +pub use component::EntityComponentWithInstances; pub use component_ui_registry::{add_to_registry, create_component_ui_registry}; pub use image_meaning::image_meaning_for_entity; @@ -50,7 +52,7 @@ pub fn ui_visible_components<'a>( } /// Show this component in the UI. -fn is_component_visible_in_ui(component_name: &ComponentName) -> bool { +pub fn is_component_visible_in_ui(component_name: &ComponentName) -> bool { const HIDDEN_COMPONENTS: &[&str] = &["rerun.components.InstanceKey"]; !HIDDEN_COMPONENTS.contains(&component_name.as_ref()) } diff --git a/crates/re_space_view/Cargo.toml b/crates/re_space_view/Cargo.toml index 56ff4613fe96..d01dca9ce16a 100644 --- a/crates/re_space_view/Cargo.toml +++ b/crates/re_space_view/Cargo.toml @@ -25,6 +25,7 @@ re_tracing.workspace = true re_types_core.workspace = true re_viewer_context.workspace = true +ahash.workspace = true egui.workspace = true itertools.workspace = true nohash-hasher.workspace = true diff --git a/crates/re_space_view/src/data_query_blueprint.rs b/crates/re_space_view/src/data_query_blueprint.rs index e544510e48df..00eac3529531 100644 --- a/crates/re_space_view/src/data_query_blueprint.rs +++ b/crates/re_space_view/src/data_query_blueprint.rs @@ -1,3 +1,4 @@ +use ahash::HashMap; use nohash_hasher::IntMap; use slotmap::SlotMap; use smallvec::SmallVec; @@ -7,9 +8,9 @@ use re_entity_db::{ EntityPropertyMap, EntityTree, }; use re_log_types::{ - path::RuleEffect, DataRow, EntityPath, EntityPathFilter, EntityPathRule, RowId, + path::RuleEffect, DataRow, EntityPath, EntityPathFilter, EntityPathRule, RowId, StoreKind, }; -use re_types_core::archetypes::Clear; +use re_types_core::{archetypes::Clear, ComponentName}; use re_viewer_context::{ blueprint_timepoint_for_writes, DataQueryId, DataQueryResult, DataResult, DataResultHandle, DataResultNode, DataResultTree, IndicatorMatchingEntities, PerVisualizer, PropertyOverrides, @@ -368,6 +369,9 @@ impl DataQueryPropertyResolver<'_> { } } + // TODO(jleibs): Should pass through an initial `ComponentOverrides` here + // if we were to support incrementally inheriting overrides from parent + // contexts such as the `SpaceView` or `Container`. EntityOverrideContext { root, individual: self.resolve_entity_overrides_for_path( @@ -421,6 +425,8 @@ impl DataQueryPropertyResolver<'_> { /// with individual overrides at the leafs. fn update_overrides_recursive( &self, + ctx: &StoreContext<'_>, + query: &LatestAtQuery, query_result: &mut DataQueryResult, override_context: &EntityOverrideContext, accumulated: &EntityProperties, @@ -442,6 +448,7 @@ impl DataQueryPropertyResolver<'_> { node.data_result.property_overrides = Some(PropertyOverrides { individual_properties: overridden_properties.cloned(), accumulated_properties: accumulated_properties.clone(), + component_overrides: Default::default(), override_path: self .recursive_override_root .join(&node.data_result.entity_path), @@ -459,12 +466,36 @@ impl DataQueryPropertyResolver<'_> { accumulated.clone() }; + let override_path = self + .individual_override_root + .join(&node.data_result.entity_path); + + let mut component_overrides: HashMap = + Default::default(); + + if let Some(override_subtree) = ctx.blueprint.tree().subtree(&override_path) { + for component in override_subtree.entity.components.keys() { + if let Some(component_data) = ctx + .blueprint + .store() + .latest_at(query, &override_path, *component, &[*component]) + .and_then(|result| result.2[0].clone()) + { + if !component_data.is_empty() { + component_overrides.insert( + *component, + (StoreKind::Blueprint, override_path.clone()), + ); + } + } + } + } + node.data_result.property_overrides = Some(PropertyOverrides { individual_properties: overridden_properties.cloned(), accumulated_properties: accumulated_properties.clone(), - override_path: self - .individual_override_root - .join(&node.data_result.entity_path), + component_overrides, + override_path, }); None @@ -473,6 +504,8 @@ impl DataQueryPropertyResolver<'_> { { for child in child_handles { self.update_overrides_recursive( + ctx, + query, query_result, override_context, &accumulated, @@ -496,6 +529,8 @@ impl<'a> PropertyResolver for DataQueryPropertyResolver<'a> { if let Some(root) = query_result.tree.root_handle() { self.update_overrides_recursive( + ctx, + query, query_result, &entity_overrides, &entity_overrides.root, diff --git a/crates/re_space_view_bar_chart/src/visualizer_system.rs b/crates/re_space_view_bar_chart/src/visualizer_system.rs index 7e9a854ad819..834a29dd3c9e 100644 --- a/crates/re_space_view_bar_chart/src/visualizer_system.rs +++ b/crates/re_space_view_bar_chart/src/visualizer_system.rs @@ -3,12 +3,10 @@ use std::collections::BTreeMap; use re_data_store::LatestAtQuery; use re_entity_db::EntityPath; use re_space_view::diff_component_filter; -use re_types::{ - archetypes::BarChart, components::Color, datatypes::TensorData, Archetype, ComponentNameSet, -}; +use re_types::{archetypes::BarChart, components::Color, datatypes::TensorData}; use re_viewer_context::{ IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, - ViewerContext, VisualizerAdditionalApplicabilityFilter, VisualizerSystem, + ViewerContext, VisualizerAdditionalApplicabilityFilter, VisualizerQueryInfo, VisualizerSystem, }; /// A bar chart system, with everything needed to render it. @@ -34,15 +32,8 @@ impl VisualizerAdditionalApplicabilityFilter for BarChartVisualizerEntityFilter } impl VisualizerSystem for BarChartVisualizerSystem { - fn required_components(&self) -> ComponentNameSet { - BarChart::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(BarChart::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn applicability_filter(&self) -> Option> { diff --git a/crates/re_space_view_dataframe/src/visualizer_system.rs b/crates/re_space_view_dataframe/src/visualizer_system.rs index a2a872cafcaf..3d98a5bcadd8 100644 --- a/crates/re_space_view_dataframe/src/visualizer_system.rs +++ b/crates/re_space_view_dataframe/src/visualizer_system.rs @@ -1,7 +1,6 @@ -use re_types::ComponentNameSet; use re_viewer_context::{ IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, - ViewerContext, VisualizerSystem, + ViewerContext, VisualizerQueryInfo, VisualizerSystem, }; /// An empty system to accept all entities in the space view @@ -15,12 +14,8 @@ impl IdentifiedViewSystem for EmptySystem { } impl VisualizerSystem for EmptySystem { - fn required_components(&self) -> ComponentNameSet { - std::iter::empty().collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::empty().collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::empty() } fn execute( diff --git a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs index cd1b06ed1c13..bb2228642760 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs @@ -4,12 +4,11 @@ use re_renderer::renderer::LineStripFlags; use re_types::{ archetypes::Arrows2D, components::{Position2D, Text, Vector2D}, - Archetype as _, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, ResolvedAnnotationInfos, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, ViewerContext, - VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use super::{picking_id_from_instance_key, process_annotations, SpatialViewVisualizerData}; @@ -169,15 +168,8 @@ impl IdentifiedViewSystem for Arrows2DVisualizer { } impl VisualizerSystem for Arrows2DVisualizer { - fn required_components(&self) -> ComponentNameSet { - Arrows2D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(Arrows2D::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs index c14afacb1335..e87ee8d97d2e 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs @@ -4,12 +4,11 @@ use re_renderer::renderer::LineStripFlags; use re_types::{ archetypes::Arrows3D, components::{Position3D, Text, Vector3D}, - Archetype as _, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, ResolvedAnnotationInfos, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, ViewerContext, - VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use super::{picking_id_from_instance_key, process_annotations, SpatialViewVisualizerData}; @@ -170,15 +169,8 @@ impl IdentifiedViewSystem for Arrows3DVisualizer { } impl VisualizerSystem for Arrows3DVisualizer { - fn required_components(&self) -> ComponentNameSet { - Arrows3D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(Arrows3D::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/assets3d.rs b/crates/re_space_view_spatial/src/visualizers/assets3d.rs index ec5b6a6d3893..45d201d481a1 100644 --- a/crates/re_space_view_spatial/src/visualizers/assets3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/assets3d.rs @@ -4,11 +4,11 @@ use re_renderer::renderer::MeshInstance; use re_types::{ archetypes::Asset3D, components::{Blob, InstanceKey, MediaType, OutOfTreeTransform3D}, - Archetype, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, - ViewQuery, ViewerContext, VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + ViewQuery, ViewerContext, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, + VisualizerSystem, }; use super::{ @@ -106,15 +106,8 @@ impl IdentifiedViewSystem for Asset3DVisualizer { } impl VisualizerSystem for Asset3DVisualizer { - fn required_components(&self) -> ComponentNameSet { - Asset3D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(Asset3D::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs index 32c1941ea879..ef08f624b645 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs @@ -3,12 +3,11 @@ use re_query::{ArchetypeView, QueryError}; use re_types::{ archetypes::Boxes2D, components::{HalfSizes2D, Position2D, Text}, - Archetype, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, ResolvedAnnotationInfos, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, ViewerContext, - VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ @@ -167,15 +166,8 @@ impl IdentifiedViewSystem for Boxes2DVisualizer { } impl VisualizerSystem for Boxes2DVisualizer { - fn required_components(&self) -> ComponentNameSet { - Boxes2D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(Boxes2D::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/boxes3d.rs b/crates/re_space_view_spatial/src/visualizers/boxes3d.rs index c39ae17c8310..2c065bfa4adc 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes3d.rs @@ -3,11 +3,11 @@ use re_query::{ArchetypeView, QueryError}; use re_types::{ archetypes::Boxes3D, components::{HalfSizes3D, Position3D, Rotation3D}, - Archetype, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, - ViewQuery, ViewerContext, VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + ViewQuery, ViewerContext, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, + VisualizerSystem, }; use crate::{ @@ -127,15 +127,8 @@ impl IdentifiedViewSystem for Boxes3DVisualizer { } impl VisualizerSystem for Boxes3DVisualizer { - fn required_components(&self) -> ComponentNameSet { - Boxes3D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(Boxes3D::indicator().as_ref().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/cameras.rs b/crates/re_space_view_spatial/src/visualizers/cameras.rs index d7911ba4d135..52c30f6188a2 100644 --- a/crates/re_space_view_spatial/src/visualizers/cameras.rs +++ b/crates/re_space_view_spatial/src/visualizers/cameras.rs @@ -4,12 +4,11 @@ use re_renderer::renderer::LineStripFlags; use re_types::{ archetypes::Pinhole, components::{InstanceKey, Transform3D, ViewCoordinates}, - Archetype as _, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, SpaceViewOutlineMasks, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, ViewerContext, VisualizableEntities, - VisualizableFilterContext, VisualizerSystem, + VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use super::{filter_visualizable_3d_entities, SpatialViewVisualizerData}; @@ -189,15 +188,8 @@ impl CamerasVisualizer { } impl VisualizerSystem for CamerasVisualizer { - fn required_components(&self) -> ComponentNameSet { - re_types::archetypes::Pinhole::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(re_types::archetypes::Pinhole::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/images.rs b/crates/re_space_view_spatial/src/visualizers/images.rs index 530cd4219565..a2746a4a4237 100644 --- a/crates/re_space_view_spatial/src/visualizers/images.rs +++ b/crates/re_space_view_spatial/src/visualizers/images.rs @@ -22,7 +22,7 @@ use re_viewer_context::{ gpu_bridge, ApplicableEntities, DefaultColor, IdentifiedViewSystem, SpaceViewClass, SpaceViewSystemExecutionError, TensorDecodeCache, TensorStatsCache, ViewContextCollection, ViewQuery, ViewerContext, VisualizableEntities, VisualizableFilterContext, - VisualizerAdditionalApplicabilityFilter, VisualizerSystem, + VisualizerAdditionalApplicabilityFilter, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ @@ -673,7 +673,15 @@ impl VisualizerAdditionalApplicabilityFilter for ImageVisualizerEntityFilter { } impl VisualizerSystem for ImageVisualizer { - fn required_components(&self) -> ComponentNameSet { + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + let indicators = [ + Image::indicator().name(), + DepthImage::indicator().name(), + SegmentationImage::indicator().name(), + ] + .into_iter() + .collect(); + let image: ComponentNameSet = Image::required_components() .iter() .map(ToOwned::to_owned) @@ -687,23 +695,26 @@ impl VisualizerSystem for ImageVisualizer { .map(ToOwned::to_owned) .collect(); - image + let required = image .intersection(&depth_image) .map(ToOwned::to_owned) .collect::() .intersection(&segmentation_image) .map(ToOwned::to_owned) - .collect() - } + .collect(); - fn indicator_components(&self) -> ComponentNameSet { - [ - Image::indicator().name(), - DepthImage::indicator().name(), - SegmentationImage::indicator().name(), - ] - .into_iter() - .collect() + let queried = Image::all_components() + .iter() + .chain(DepthImage::all_components().iter()) + .chain(SegmentationImage::all_components().iter()) + .map(ToOwned::to_owned) + .collect(); + + VisualizerQueryInfo { + indicators, + required, + queried, + } } fn applicability_filter(&self) -> Option> { diff --git a/crates/re_space_view_spatial/src/visualizers/lines2d.rs b/crates/re_space_view_spatial/src/visualizers/lines2d.rs index 5909a60bdb0f..218b64fac7ea 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines2d.rs @@ -3,12 +3,11 @@ use re_query::{ArchetypeView, QueryError}; use re_types::{ archetypes::LineStrips2D, components::{LineStrip2D, Text}, - Archetype as _, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, ResolvedAnnotationInfos, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, ViewerContext, - VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ @@ -165,15 +164,8 @@ impl IdentifiedViewSystem for Lines2DVisualizer { } impl VisualizerSystem for Lines2DVisualizer { - fn required_components(&self) -> ComponentNameSet { - LineStrips2D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(LineStrips2D::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/lines3d.rs b/crates/re_space_view_spatial/src/visualizers/lines3d.rs index b8e28d9a56eb..9fbd35881202 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines3d.rs @@ -3,12 +3,11 @@ use re_query::{ArchetypeView, QueryError}; use re_types::{ archetypes::LineStrips3D, components::{LineStrip3D, Text}, - Archetype as _, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, ResolvedAnnotationInfos, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, ViewerContext, - VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ @@ -172,15 +171,8 @@ impl IdentifiedViewSystem for Lines3DVisualizer { } impl VisualizerSystem for Lines3DVisualizer { - fn required_components(&self) -> ComponentNameSet { - LineStrips3D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(LineStrips3D::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/meshes.rs b/crates/re_space_view_spatial/src/visualizers/meshes.rs index 05f79d3e0c4a..cccc3daae17b 100644 --- a/crates/re_space_view_spatial/src/visualizers/meshes.rs +++ b/crates/re_space_view_spatial/src/visualizers/meshes.rs @@ -4,11 +4,11 @@ use re_renderer::renderer::MeshInstance; use re_types::{ archetypes::Mesh3D, components::{Color, InstanceKey, Material, MeshProperties, Position3D, Vector3D}, - Archetype, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, - ViewQuery, ViewerContext, VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + ViewQuery, ViewerContext, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, + VisualizerSystem, }; use super::{ @@ -137,15 +137,8 @@ impl IdentifiedViewSystem for Mesh3DVisualizer { } impl VisualizerSystem for Mesh3DVisualizer { - fn required_components(&self) -> ComponentNameSet { - Mesh3D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(Mesh3D::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/points2d.rs b/crates/re_space_view_spatial/src/visualizers/points2d.rs index 498fede1d010..3c2f297681a3 100644 --- a/crates/re_space_view_spatial/src/visualizers/points2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points2d.rs @@ -3,12 +3,11 @@ use re_renderer::PickingLayerInstanceId; use re_types::{ archetypes::Points2D, components::{ClassId, Color, InstanceKey, KeypointId, Position2D, Radius, Text}, - Archetype, ComponentNameSet, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, ResolvedAnnotationInfos, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, ViewerContext, - VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ @@ -225,15 +224,8 @@ impl IdentifiedViewSystem for Points2DVisualizer { } impl VisualizerSystem for Points2DVisualizer { - fn required_components(&self) -> ComponentNameSet { - Points2D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(Points2D::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/points3d.rs b/crates/re_space_view_spatial/src/visualizers/points3d.rs index 2c9ebfddb8d7..2c202ad7a672 100644 --- a/crates/re_space_view_spatial/src/visualizers/points3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points3d.rs @@ -4,12 +4,11 @@ use re_renderer::PickingLayerInstanceId; use re_types::{ archetypes::Points3D, components::{ClassId, Color, InstanceKey, KeypointId, Position3D, Radius, Text}, - Archetype as _, ComponentNameSet, }; use re_viewer_context::{ Annotations, ApplicableEntities, IdentifiedViewSystem, ResolvedAnnotationInfos, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, ViewerContext, - VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ @@ -166,15 +165,8 @@ impl IdentifiedViewSystem for Points3DVisualizer { } impl VisualizerSystem for Points3DVisualizer { - fn required_components(&self) -> ComponentNameSet { - Points3D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(Points3D::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_spatial/src/visualizers/transform3d_arrows.rs b/crates/re_space_view_spatial/src/visualizers/transform3d_arrows.rs index 45ae4eafebf2..6fa7d160ef8b 100644 --- a/crates/re_space_view_spatial/src/visualizers/transform3d_arrows.rs +++ b/crates/re_space_view_spatial/src/visualizers/transform3d_arrows.rs @@ -1,13 +1,11 @@ use egui::Color32; use re_log_types::EntityPath; use re_renderer::LineStripSeriesBuilder; -use re_types::{ - components::{InstanceKey, Transform3D}, - Archetype, ComponentNameSet, -}; +use re_types::components::{InstanceKey, Transform3D}; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, - ViewQuery, ViewerContext, VisualizableEntities, VisualizableFilterContext, VisualizerSystem, + ViewQuery, ViewerContext, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, + VisualizerSystem, }; use crate::{ @@ -34,20 +32,8 @@ impl IdentifiedViewSystem for Transform3DArrowsVisualizer { } impl VisualizerSystem for Transform3DArrowsVisualizer { - fn required_components(&self) -> ComponentNameSet { - re_types::archetypes::Transform3D::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once( - re_types::archetypes::Transform3D::indicator() - .as_ref() - .name(), - ) - .collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( diff --git a/crates/re_space_view_tensor/src/visualizer_system.rs b/crates/re_space_view_tensor/src/visualizer_system.rs index af409659ee63..e690df2e84d1 100644 --- a/crates/re_space_view_tensor/src/visualizer_system.rs +++ b/crates/re_space_view_tensor/src/visualizer_system.rs @@ -2,13 +2,11 @@ use re_data_store::{LatestAtQuery, VersionedComponent}; use re_entity_db::EntityPath; use re_log_types::RowId; use re_space_view::diff_component_filter; -use re_types::{ - archetypes::Tensor, components::TensorData, tensor_data::DecodedTensor, Archetype, - ComponentNameSet, -}; +use re_types::{archetypes::Tensor, components::TensorData, tensor_data::DecodedTensor}; use re_viewer_context::{ IdentifiedViewSystem, SpaceViewSystemExecutionError, TensorDecodeCache, ViewContextCollection, - ViewQuery, ViewerContext, VisualizerAdditionalApplicabilityFilter, VisualizerSystem, + ViewQuery, ViewerContext, VisualizerAdditionalApplicabilityFilter, VisualizerQueryInfo, + VisualizerSystem, }; #[derive(Default)] @@ -33,15 +31,8 @@ impl VisualizerAdditionalApplicabilityFilter for TensorVisualizerEntityFilter { } impl VisualizerSystem for TensorSystem { - fn required_components(&self) -> ComponentNameSet { - Tensor::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(Tensor::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn applicability_filter(&self) -> Option> { diff --git a/crates/re_space_view_text_document/src/visualizer_system.rs b/crates/re_space_view_text_document/src/visualizer_system.rs index ca83f1f5e662..f121ff40fcab 100644 --- a/crates/re_space_view_text_document/src/visualizer_system.rs +++ b/crates/re_space_view_text_document/src/visualizer_system.rs @@ -2,11 +2,11 @@ use re_data_store::LatestAtQuery; use re_query::{query_archetype, QueryError}; use re_types::{ archetypes::{self, TextDocument}, - components, Archetype as _, ComponentNameSet, + components, }; use re_viewer_context::{ IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, - ViewerContext, VisualizerSystem, + ViewerContext, VisualizerQueryInfo, VisualizerSystem, }; // --- @@ -30,15 +30,8 @@ impl IdentifiedViewSystem for TextDocumentSystem { } impl VisualizerSystem for TextDocumentSystem { - fn required_components(&self) -> ComponentNameSet { - TextDocument::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(TextDocument::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn execute( diff --git a/crates/re_space_view_text_log/src/visualizer_system.rs b/crates/re_space_view_text_log/src/visualizer_system.rs index 6d172ba1d69f..4b807d6993eb 100644 --- a/crates/re_space_view_text_log/src/visualizer_system.rs +++ b/crates/re_space_view_text_log/src/visualizer_system.rs @@ -5,11 +5,10 @@ use re_query_cache::MaybeCachedComponentData; use re_types::{ archetypes::TextLog, components::{Color, Text, TextLogLevel}, - Archetype as _, ComponentNameSet, }; use re_viewer_context::{ IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, - ViewerContext, VisualizerSystem, + ViewerContext, VisualizerQueryInfo, VisualizerSystem, }; #[derive(Debug, Clone)] @@ -42,15 +41,8 @@ impl IdentifiedViewSystem for TextLogSystem { } impl VisualizerSystem for TextLogSystem { - fn required_components(&self) -> ComponentNameSet { - TextLog::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(TextLog::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn execute( diff --git a/crates/re_space_view_time_series/src/visualizer_system.rs b/crates/re_space_view_time_series/src/visualizer_system.rs index fae3e3635a9c..bf83c3449988 100644 --- a/crates/re_space_view_time_series/src/visualizer_system.rs +++ b/crates/re_space_view_time_series/src/visualizer_system.rs @@ -1,15 +1,15 @@ use re_data_store::TimeRange; -use re_log_types::TimeInt; +use re_log_types::{StoreKind, TimeInt}; use re_query_cache::{MaybeCachedComponentData, QueryError}; use re_types::{ archetypes::TimeSeriesScalar, components::{Color, Radius, Scalar, ScalarScattering, Text}, - Archetype, ComponentNameSet, + Component, Loggable, }; use re_viewer_context::{ external::re_entity_db::TimeSeriesAggregator, AnnotationMap, DefaultColor, - IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewQuery, ViewerContext, - VisualizerSystem, + IdentifiedViewSystem, ResolvedAnnotationInfo, SpaceViewSystemExecutionError, ViewQuery, + ViewerContext, VisualizerQueryInfo, VisualizerSystem, }; // --- @@ -84,15 +84,8 @@ impl IdentifiedViewSystem for TimeSeriesSystem { } impl VisualizerSystem for TimeSeriesSystem { - fn required_components(&self) -> ComponentNameSet { - TimeSeriesScalar::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() - } - - fn indicator_components(&self) -> ComponentNameSet { - std::iter::once(TimeSeriesScalar::indicator().name()).collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } fn execute( @@ -120,6 +113,29 @@ impl VisualizerSystem for TimeSeriesSystem { fn as_any(&self) -> &dyn std::any::Any { self } + + fn initial_override_value( + &self, + _ctx: &ViewerContext<'_>, + _query: &re_data_store::LatestAtQuery, + _store: &re_data_store::DataStore, + entity_path: &re_log_types::EntityPath, + component: &re_types::ComponentName, + ) -> Option { + if *component == Color::name() { + let default_color = DefaultColor::EntityPath(entity_path); + + let annotation_info = ResolvedAnnotationInfo::default(); + + let color = annotation_info.color(None, default_color); + + let [r, g, b, a] = color.to_array(); + + Some([Color::from_unmultiplied_rgba(r, g, b, a)].into()) + } else { + None + } + } } impl TimeSeriesSystem { @@ -190,6 +206,19 @@ impl TimeSeriesSystem { .annotation_info(); let default_color = DefaultColor::EntityPath(&data_result.entity_path); + let override_color = lookup_override::(data_result, ctx).map(|c| { + let arr = c.to_array(); + egui::Color32::from_rgba_unmultiplied(arr[0], arr[1], arr[2], arr[3]) + }); + + let override_label = + lookup_override::(data_result, ctx).map(|t| t.to_string()); + + let override_scattered = + lookup_override::(data_result, ctx).map(|s| s.0); + + let override_radius = lookup_override::(data_result, ctx).map(|r| r.0); + let query = re_data_store::RangeQuery::new(query.timeline, TimeRange::new(from, to)); @@ -218,9 +247,16 @@ impl TimeSeriesSystem { MaybeCachedComponentData::iter_or_repeat_opt(&radii, scalars.len()), MaybeCachedComponentData::iter_or_repeat_opt(&labels, scalars.len()), ) { - let color = - annotation_info.color(color.map(|c| c.to_array()), default_color); - let label = annotation_info.label(label.as_ref().map(|l| l.as_str())); + let color = override_color.unwrap_or_else(|| { + annotation_info.color(color.map(|c| c.to_array()), default_color) + }); + let label = override_label.clone().or_else(|| { + annotation_info.label(label.as_ref().map(|l| l.as_str())) + }); + let scattered = override_scattered + .unwrap_or_else(|| scattered.map_or(false, |s| s.0)); + let radius = override_radius + .unwrap_or_else(|| radius.map_or(DEFAULT_RADIUS, |r| r.0)); const DEFAULT_RADIUS: f32 = 0.75; @@ -230,8 +266,8 @@ impl TimeSeriesSystem { attrs: PlotPointAttrs { label, color, - radius: radius.map_or(DEFAULT_RADIUS, |r| r.0), - scattered: scattered.map_or(false, |s| s.0), + radius, + scattered, }, }); } @@ -406,6 +442,28 @@ impl TimeSeriesSystem { } } +fn lookup_override( + data_result: &re_viewer_context::DataResult, + ctx: &ViewerContext<'_>, +) -> Option { + data_result + .property_overrides + .as_ref() + .and_then(|p| p.component_overrides.get(&C::name())) + .and_then(|(store_kind, path)| match store_kind { + StoreKind::Blueprint => ctx + .store_context + .blueprint + .store() + .query_latest_component::(path, ctx.blueprint_query), + StoreKind::Recording => ctx + .entity_db + .store() + .query_latest_component::(path, &ctx.current_query()), + }) + .map(|c| c.value) +} + // --- /// Implements aggregation behavior corresponding to [`TimeSeriesAggregator::Average`]. diff --git a/crates/re_viewer/Cargo.toml b/crates/re_viewer/Cargo.toml index 5cb35f9443aa..611ef0558fae 100644 --- a/crates/re_viewer/Cargo.toml +++ b/crates/re_viewer/Cargo.toml @@ -85,6 +85,7 @@ eframe = { workspace = true, default-features = false, features = [ "puffin", "wgpu", ] } +egui_extras.workspace = true egui_plot.workspace = true egui-wgpu.workspace = true egui.workspace = true diff --git a/crates/re_viewer/src/ui/mod.rs b/crates/re_viewer/src/ui/mod.rs index 2cd9a6f30d63..d32fa1d682b2 100644 --- a/crates/re_viewer/src/ui/mod.rs +++ b/crates/re_viewer/src/ui/mod.rs @@ -1,5 +1,6 @@ mod blueprint_panel; mod mobile_warning_ui; +mod override_ui; mod recordings_panel; mod rerun_menu; mod selection_history_ui; diff --git a/crates/re_viewer/src/ui/override_ui.rs b/crates/re_viewer/src/ui/override_ui.rs new file mode 100644 index 000000000000..20ad7a9463ac --- /dev/null +++ b/crates/re_viewer/src/ui/override_ui.rs @@ -0,0 +1,284 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use itertools::Itertools; +use re_data_store::{DataStore, LatestAtQuery}; +use re_data_ui::{is_component_visible_in_ui, item_ui, temporary_style_ui_for_component}; +use re_entity_db::InstancePath; +use re_log_types::{ComponentPath, DataCell, DataRow, RowId, StoreKind}; +use re_query_cache::external::re_query::get_component_with_instances; +use re_types_core::{components::InstanceKey, ComponentName}; +use re_viewer_context::{ + blueprint_timepoint_for_writes, DataResult, SystemCommand, SystemCommandSender as _, + UiVerbosity, ViewSystemIdentifier, ViewerContext, +}; +use re_viewport::SpaceViewBlueprint; + +pub fn override_ui( + ctx: &ViewerContext<'_>, + space_view: &SpaceViewBlueprint, + instance_path: &InstancePath, + ui: &mut egui::Ui, +) { + let InstancePath { + entity_path, + instance_key, + } = instance_path; + + // Because of how overrides are implemented the overridden-data must be an entity + // in the real store. We would never show an override UI for a selected blueprint + // entity from the blueprint-inspector since it isn't "part" of a space-view to provide + // the overrides. + let query = ctx.current_query(); + let store = ctx.entity_db.store(); + + let query_result = ctx.lookup_query_result(space_view.query_id()); + let Some(data_result) = query_result + .tree + .lookup_result_by_path_and_group(entity_path, false) + .cloned() + else { + ui.label(ctx.re_ui.error_text("Entity not found in view.")); + return; + }; + + let active_overrides: BTreeSet = data_result + .property_overrides + .as_ref() + .map(|props| props.component_overrides.keys().cloned().collect()) + .unwrap_or_default(); + + add_new_override( + ctx, + &query, + store, + ui, + space_view, + &data_result, + &active_overrides, + ); + + ui.end_row(); + + let Some(overrides) = data_result.property_overrides else { + return; + }; + + let components: Vec<_> = overrides + .component_overrides + .into_iter() + .sorted_by_key(|(c, _)| *c) + .filter(|(c, _)| is_component_visible_in_ui(c)) + .collect(); + + egui_extras::TableBuilder::new(ui) + .cell_layout(egui::Layout::left_to_right(egui::Align::Center)) + .auto_shrink([false, true]) + .column(egui_extras::Column::auto()) + .column(egui_extras::Column::auto()) + .column(egui_extras::Column::remainder()) + .body(|mut body| { + re_ui::ReUi::setup_table_body(&mut body); + let row_height = re_ui::ReUi::table_line_height(); + body.rows(row_height, components.len(), |mut row| { + if let Some((component_name, (store_kind, entity_path))) = + components.get(row.index()) + { + // Remove button + row.col(|ui| { + if ctx + .re_ui + .small_icon_button(ui, &re_ui::icons::CLOSE) + .clicked() + { + // Note: need to use the blueprint store since the data might + // not exist in the recording store. + ctx.save_empty_blueprint_component_name( + ctx.store_context.blueprint.store(), + &overrides.override_path, + *component_name, + ); + } + }); + // Component label + row.col(|ui| { + temporary_style_ui_for_component(ui, component_name, |ui| { + item_ui::component_path_button( + ctx, + ui, + &ComponentPath::new(entity_path.clone(), *component_name), + ); + }); + }); + // Editor last to take up remainder of space + row.col(|ui| { + let component_data = match store_kind { + StoreKind::Blueprint => { + let store = ctx.store_context.blueprint.store(); + let query = ctx.blueprint_query; + get_component_with_instances( + store, + query, + entity_path, + *component_name, + ) + } + StoreKind::Recording => get_component_with_instances( + store, + &query, + entity_path, + *component_name, + ), + }; + + if let Some((_, _, component_data)) = component_data { + ctx.component_ui_registry.edit_ui( + ctx, + ui, + UiVerbosity::Small, + &query, + store, + entity_path, + &overrides.override_path, + &component_data, + instance_key, + ); + } else { + // TODO(jleibs): Is it possible to set an override to empty and not confuse + // the situation with "not-overridden?". Maybe we hit this in cases of `[]` vs `[null]`. + ui.weak("(empty)"); + } + }); + } + }); + }); +} + +pub fn add_new_override( + ctx: &ViewerContext<'_>, + query: &LatestAtQuery, + store: &DataStore, + ui: &mut egui::Ui, + space_view: &SpaceViewBlueprint, + data_result: &DataResult, + active_overrides: &BTreeSet, +) { + ui.menu_button("Add new override", |ui| { + ui.style_mut().wrap = Some(false); + + let view_systems = ctx + .space_view_class_registry + .new_visualizer_collection(*space_view.class_identifier()); + + let mut component_to_vis: BTreeMap = + Default::default(); + + // Accumulate the components across all visualizers and track which visualizer + // each component came from so we can use it for defaults later. + // + // If two visualizers have the same component, the first one wins. + // TODO(jleibs): We can do something fancier in the future such as presenting both + // options once we have a motivating use-case. + for vis in &data_result.visualizers { + let Some(queried) = view_systems + .get_by_identifier(*vis) + .ok() + .map(|vis| vis.visualizer_query_info().queried) + else { + continue; + }; + + for component in queried.difference(active_overrides) { + component_to_vis.entry(*component).or_insert_with(|| *vis); + } + } + + // Present the option to add new components for each component that doesn't + // already have an active override. + for (component, viz) in component_to_vis { + // If we don't have an override_path we can't set up an initial override + // this shouldn't happen if the `DataResult` is valid. + let Some(override_path) = data_result.override_path() else { + if cfg!(debug_assertions) { + re_log::error!("No override path for: {}", component); + } + continue; + }; + + // If there is no registered editor, don't let the user create an override + if !ctx.component_ui_registry.has_registered_editor(&component) { + continue; + } + + if ui.button(component.as_str()).clicked() { + // We are creating a new override. We need to decide what initial value to give it. + // - First see if there's an existing splat in the recording. + // - Next see if visualizer system wants to provide a value. + // - Finally, fall back on the default value from the component registry. + + let components = [component]; + + let mut splat_cell: DataCell = [InstanceKey::SPLAT].into(); + splat_cell.compute_size_bytes(); + + let Some(mut initial_data) = store + .latest_at(query, &data_result.entity_path, component, &components) + .and_then(|result| result.2[0].clone()) + .and_then(|cell| { + if cell.num_instances() == 1 { + Some(cell) + } else { + None + } + }) + .or_else(|| { + view_systems.get_by_identifier(viz).ok().and_then(|sys| { + sys.initial_override_value( + ctx, + query, + store, + &data_result.entity_path, + &component, + ) + }) + }) + .or_else(|| { + ctx.component_ui_registry.default_value( + ctx, + query, + store, + &data_result.entity_path, + &component, + ) + }) + else { + re_log::warn!("Could not identify an initial value for: {}", component); + return; + }; + + initial_data.compute_size_bytes(); + + match DataRow::from_cells( + RowId::new(), + blueprint_timepoint_for_writes(), + override_path.clone(), + 1, + [splat_cell, initial_data], + ) { + Ok(row) => ctx + .command_sender + .send_system(SystemCommand::UpdateBlueprint( + ctx.store_context.blueprint.store_id().clone(), + vec![row], + )), + Err(err) => { + re_log::warn!("Failed to create DataRow for blueprint component: {}", err); + } + } + + ui.close_menu(); + } + } + }) + .response + .on_hover_text("Choose a component to specify an override value.".to_owned()); +} diff --git a/crates/re_viewer/src/ui/selection_panel.rs b/crates/re_viewer/src/ui/selection_panel.rs index 0fb59459e476..d42c5b537e00 100644 --- a/crates/re_viewer/src/ui/selection_panel.rs +++ b/crates/re_viewer/src/ui/selection_panel.rs @@ -10,6 +10,7 @@ use re_types::{ components::{PinholeProjection, Transform3D}, tensor_data::TensorDataMeaning, }; +use re_types_core::components::InstanceKey; use re_ui::list_item::ListItem; use re_ui::ReUi; use re_ui::SyntaxHighlighting as _; @@ -23,8 +24,10 @@ use re_viewport::{ Contents, SpaceInfoCollection, Viewport, ViewportBlueprint, }; -use crate::ui::add_space_view_or_container_modal::AddSpaceViewOrContainerModal; use crate::ui::visible_history::visible_history_ui; +use crate::ui::{ + add_space_view_or_container_modal::AddSpaceViewOrContainerModal, override_ui::override_ui, +}; use super::selection_history_ui::SelectionHistoryUi; @@ -190,6 +193,31 @@ impl SelectionPanel { }); } + // Special override section for space-view-entities + if let Item::InstancePath(Some(space_view_id), instance_path) = item { + // Note: for now we only support overriding as a splat, rather than per-instance. So we + // only show the UI when we've selected a full entity (indicated by splat) so as not to + // me ambiguous as to what the override applies to. + if instance_path.instance_key == InstanceKey::SPLAT { + if let Some(space_view) = viewport.blueprint.space_views.get(space_view_id) + { + // TODO(jleibs): Overrides still require special handling inside the visualizers. + // For now, only show the override section for TimeSeries until support is implemented + // generically. + if space_view.class_identifier() == TimeSeriesSpaceView::IDENTIFIER { + ctx.re_ui.large_collapsing_header( + ui, + "Component Overrides", + true, + |ui| { + override_ui(ctx, space_view, instance_path, ui); + }, + ); + } + } + } + } + if has_blueprint_section(item) { ctx.re_ui .large_collapsing_header(ui, "Blueprint", true, |ui| { diff --git a/crates/re_viewer_context/src/blueprint_helpers.rs b/crates/re_viewer_context/src/blueprint_helpers.rs index a1ad754d1452..9bebf295c6e6 100644 --- a/crates/re_viewer_context/src/blueprint_helpers.rs +++ b/crates/re_viewer_context/src/blueprint_helpers.rs @@ -1,4 +1,8 @@ -use re_log_types::{DataCell, DataRow, EntityPath, RowId, Time, TimePoint, Timeline}; +use re_log_types::{ + external::arrow2::datatypes::DataType, DataCell, DataRow, EntityPath, RowId, Time, TimePoint, + Timeline, +}; +use re_types::{components::InstanceKey, ComponentName}; use crate::{SystemCommand, SystemCommandSender as _, ViewerContext}; @@ -22,12 +26,18 @@ impl ViewerContext<'_> { { let timepoint = blueprint_timepoint_for_writes(); - match DataRow::from_cells1_sized( + let mut splat_cell: DataCell = [InstanceKey::SPLAT].into(); + splat_cell.compute_size_bytes(); + + let mut component: DataCell = [component].into(); + component.compute_size_bytes(); + + match DataRow::from_cells( RowId::new(), - entity_path.clone(), timepoint.clone(), + entity_path.clone(), 1, - [component], + [splat_cell, component], ) { Ok(row) => self .command_sender @@ -42,16 +52,15 @@ impl ViewerContext<'_> { } } - /// Helper to save a component to the blueprint store. - pub fn save_empty_blueprint_component<'a, C>(&self, entity_path: &EntityPath) - where - C: re_types::Component + 'a, - { + /// Helper for `save_empty_blueprint_component` and `save_empty_blueprint_component_name`. + fn save_empty_blueprint_component_impl( + &self, + entity_path: &EntityPath, + component_name: ComponentName, + datatype: DataType, + ) { let timepoint = blueprint_timepoint_for_writes(); - - let datatype = C::arrow_datatype(); - - let cell = DataCell::from_arrow_empty(C::name(), datatype); + let cell = DataCell::from_arrow_empty(component_name, datatype); match DataRow::from_cells1( RowId::new(), @@ -72,4 +81,33 @@ impl ViewerContext<'_> { } } } + + /// Helper to save a component to the blueprint store. + pub fn save_empty_blueprint_component<'a, C>(&self, entity_path: &EntityPath) + where + C: re_types::Component + 'a, + { + self.save_empty_blueprint_component_impl( + entity_path, + C::name(), + C::arrow_datatype().clone(), + ); + } + + /// Helper to save a component to the blueprint store. + pub fn save_empty_blueprint_component_name( + &self, + store: &re_data_store::DataStore, + entity_path: &EntityPath, + component_name: ComponentName, + ) { + if let Some(datatype) = store.lookup_datatype(&component_name) { + self.save_empty_blueprint_component_impl(entity_path, component_name, datatype.clone()); + } else { + re_log::error_once!( + "Tried to clear a component with unknown type: {}", + component_name + ); + } + } } diff --git a/crates/re_viewer_context/src/component_ui_registry.rs b/crates/re_viewer_context/src/component_ui_registry.rs index 7e0d1107ca2a..3392d89fcff8 100644 --- a/crates/re_viewer_context/src/component_ui_registry.rs +++ b/crates/re_viewer_context/src/component_ui_registry.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use re_data_store::{DataStore, LatestAtQuery}; use re_entity_db::EntityPath; +use re_log_types::DataCell; use re_query::ComponentWithInstances; use re_types::{components::InstanceKey, ComponentName, Loggable as _}; @@ -46,18 +47,39 @@ type ComponentUiCallback = Box< + Sync, >; +type ComponentEditCallback = Box< + dyn Fn( + &ViewerContext<'_>, + &mut egui::Ui, + UiVerbosity, + &LatestAtQuery, + &DataStore, + &EntityPath, + &EntityPath, + &ComponentWithInstances, + &InstanceKey, + ) + Send + + Sync, +>; + +type DefaultValueCallback = Box< + dyn Fn(&ViewerContext<'_>, &LatestAtQuery, &DataStore, &EntityPath) -> DataCell + Send + Sync, +>; + /// How to display components in a Ui. pub struct ComponentUiRegistry { /// Ui method to use if there was no specific one registered for a component. fallback_ui: ComponentUiCallback, - components: BTreeMap, + component_uis: BTreeMap, + component_editors: BTreeMap, } impl ComponentUiRegistry { pub fn new(fallback_ui: ComponentUiCallback) -> Self { Self { fallback_ui, - components: Default::default(), + component_uis: Default::default(), + component_editors: Default::default(), } } @@ -65,7 +87,28 @@ impl ComponentUiRegistry { /// /// If the component was already registered, the new callback replaces the old one. pub fn add(&mut self, name: ComponentName, callback: ComponentUiCallback) { - self.components.insert(name, callback); + self.component_uis.insert(name, callback); + } + + /// Registers how to edit a given component in the ui. + /// + /// Requires two callbacks: one to provided an initial default value, and one to show the editor + /// UI and save the updated value. + /// + /// If the component was already registered, the new callback replaces the old one. + pub fn add_editor( + &mut self, + name: ComponentName, + default_value: DefaultValueCallback, + editor_callback: ComponentEditCallback, + ) { + self.component_editors + .insert(name, (default_value, editor_callback)); + } + + /// Check if there is a registered editor for a given component + pub fn has_registered_editor(&self, name: &ComponentName) -> bool { + self.component_editors.contains_key(name) } /// Show a ui for this instance of this component. @@ -90,7 +133,7 @@ impl ComponentUiRegistry { } let ui_callback = self - .components + .component_uis .get(&component.name()) .unwrap_or(&self.fallback_ui); (*ui_callback)( @@ -104,4 +147,64 @@ impl ComponentUiRegistry { instance_key, ); } + + /// Show an editor for this instance of this component. + #[allow(clippy::too_many_arguments)] + pub fn edit_ui( + &self, + ctx: &ViewerContext<'_>, + ui: &mut egui::Ui, + verbosity: UiVerbosity, + query: &LatestAtQuery, + store: &DataStore, + entity_path: &EntityPath, + override_path: &EntityPath, + component: &ComponentWithInstances, + instance_key: &InstanceKey, + ) { + re_tracing::profile_function!(component.name().full_name()); + + if let Some((_, edit_callback)) = self.component_editors.get(&component.name()) { + (*edit_callback)( + ctx, + ui, + verbosity, + query, + store, + entity_path, + override_path, + component, + instance_key, + ); + } else { + // Even if we can't edit the component, it's still helpful to show what the value is. + self.ui( + ctx, + ui, + verbosity, + query, + store, + entity_path, + component, + instance_key, + ); + } + } + + /// Return a default value for this component. + #[allow(clippy::too_many_arguments)] + pub fn default_value( + &self, + ctx: &ViewerContext<'_>, + query: &LatestAtQuery, + store: &DataStore, + entity_path: &EntityPath, + component: &ComponentName, + ) -> Option { + re_tracing::profile_function!(component); + + self.component_editors + .get(component) + .map(|(default_value, _)| (*default_value)(ctx, query, store, entity_path)) + } } diff --git a/crates/re_viewer_context/src/lib.rs b/crates/re_viewer_context/src/lib.rs index ca06e1043663..70185349523c 100644 --- a/crates/re_viewer_context/src/lib.rs +++ b/crates/re_viewer_context/src/lib.rs @@ -50,7 +50,7 @@ pub use space_view::{ SpaceViewState, SpaceViewSystemExecutionError, SpaceViewSystemRegistrator, SystemExecutionOutput, ViewContextCollection, ViewContextSystem, ViewQuery, ViewSystemIdentifier, VisualizableFilterContext, VisualizerAdditionalApplicabilityFilter, - VisualizerCollection, VisualizerSystem, + VisualizerCollection, VisualizerQueryInfo, VisualizerSystem, }; pub use store_context::StoreContext; pub use tensor::{TensorDecodeCache, TensorStats, TensorStatsCache}; diff --git a/crates/re_viewer_context/src/space_view/mod.rs b/crates/re_viewer_context/src/space_view/mod.rs index d2948629a2a7..a7cce5cf4c6e 100644 --- a/crates/re_viewer_context/src/space_view/mod.rs +++ b/crates/re_viewer_context/src/space_view/mod.rs @@ -32,7 +32,7 @@ pub use system_execution_output::SystemExecutionOutput; pub use view_context_system::{ViewContextCollection, ViewContextSystem}; pub use view_query::{DataResult, PerSystemDataResults, PropertyOverrides, ViewQuery}; pub use visualizer_entity_subscriber::VisualizerAdditionalApplicabilityFilter; -pub use visualizer_system::{VisualizerCollection, VisualizerSystem}; +pub use visualizer_system::{VisualizerCollection, VisualizerQueryInfo, VisualizerSystem}; // --------------------------------------------------------------------------- diff --git a/crates/re_viewer_context/src/space_view/view_query.rs b/crates/re_viewer_context/src/space_view/view_query.rs index ed0731c95807..47fc781e34e6 100644 --- a/crates/re_viewer_context/src/space_view/view_query.rs +++ b/crates/re_viewer_context/src/space_view/view_query.rs @@ -1,11 +1,12 @@ use std::collections::BTreeMap; +use ahash::HashMap; use itertools::Itertools; use once_cell::sync::Lazy; use re_data_store::LatestAtQuery; use re_entity_db::{EntityPath, EntityProperties, EntityPropertiesComponent, TimeInt, Timeline}; -use re_log_types::{DataCell, DataRow, RowId}; -use re_types::Loggable; +use re_log_types::{DataCell, DataRow, RowId, StoreKind}; +use re_types::{ComponentName, Loggable}; use smallvec::SmallVec; use crate::{ @@ -23,6 +24,12 @@ pub struct PropertyOverrides { /// The individual property set in this `DataResult`, if any. pub individual_properties: Option, + /// An alternative store and entity path to use for the specified component. + // NOTE: StoreKind is easier to work with than a `StoreId`` or full `DataStore` but + // might still be ambiguous when we have multiple stores active at a time. + // TODO(jleibs): Consider something like `tinymap` for this. + pub component_overrides: HashMap, + /// `EntityPath` in the Blueprint store where updated overrides should be written back. pub override_path: EntityPath, } diff --git a/crates/re_viewer_context/src/space_view/visualizer_entity_subscriber.rs b/crates/re_viewer_context/src/space_view/visualizer_entity_subscriber.rs index 446dd17d177f..a118a9288b06 100644 --- a/crates/re_viewer_context/src/space_view/visualizer_entity_subscriber.rs +++ b/crates/re_viewer_context/src/space_view/visualizer_entity_subscriber.rs @@ -28,7 +28,7 @@ pub struct VisualizerEntitySubscriber { /// Visualizer type this subscriber is associated with. visualizer: ViewSystemIdentifier, - /// See [`VisualizerSystem::indicator_components`] + /// See [`crate::VisualizerQueryInfo::indicators`] indicator_components: ComponentNameSet, /// Assigns each required component an index. @@ -84,11 +84,13 @@ struct VisualizerEntityMapping { impl VisualizerEntitySubscriber { pub fn new(visualizer: &T) -> Self { + let visualizer_query_info = visualizer.visualizer_query_info(); + Self { visualizer: T::identifier(), - indicator_components: visualizer.indicator_components(), - required_components_indices: visualizer - .required_components() + indicator_components: visualizer_query_info.indicators, + required_components_indices: visualizer_query_info + .required .into_iter() .enumerate() .map(|(i, name)| (name, i)) diff --git a/crates/re_viewer_context/src/space_view/visualizer_system.rs b/crates/re_viewer_context/src/space_view/visualizer_system.rs index 78d4bdb66367..e328c66d03e2 100644 --- a/crates/re_viewer_context/src/space_view/visualizer_system.rs +++ b/crates/re_viewer_context/src/space_view/visualizer_system.rs @@ -1,6 +1,6 @@ use ahash::HashMap; -use re_types::ComponentNameSet; +use re_types::{Archetype, ComponentNameSet}; use crate::{ ApplicableEntities, IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, @@ -8,23 +8,50 @@ use crate::{ VisualizableFilterContext, VisualizerAdditionalApplicabilityFilter, }; -/// Element of a scene derived from a single archetype query. -/// -/// Is populated after scene contexts and has access to them. -pub trait VisualizerSystem: Send + Sync + 'static { - // TODO(andreas): This should be able to list out the ContextSystems it needs. +pub struct VisualizerQueryInfo { + /// These are not required, but if _any_ of these are found, it is a strong indication that this + /// system should be active (if also the `required_components` are found). + pub indicators: ComponentNameSet, /// Returns the minimal set of components that the system _requires_ in order to be instantiated. /// /// This does not include indicator components. - fn required_components(&self) -> ComponentNameSet; + pub required: ComponentNameSet, - /// These are not required, but if _any_ of these are found, it is a strong indication that this - /// system should be active (if also the `required_components` are found). - #[inline] - fn indicator_components(&self) -> ComponentNameSet { - Default::default() + /// Returns the set of components that the system _queries_. + /// Must include required, usually excludes indicators + pub queried: ComponentNameSet, +} + +impl VisualizerQueryInfo { + pub fn from_archetype() -> Self { + Self { + indicators: std::iter::once(T::indicator().name()).collect(), + required: T::required_components() + .iter() + .map(ToOwned::to_owned) + .collect(), + queried: T::all_components().iter().map(ToOwned::to_owned).collect(), + } + } + + pub fn empty() -> Self { + Self { + indicators: ComponentNameSet::new(), + required: ComponentNameSet::new(), + queried: ComponentNameSet::new(), + } } +} + +/// Element of a scene derived from a single archetype query. +/// +/// Is populated after scene contexts and has access to them. +pub trait VisualizerSystem: Send + Sync + 'static { + // TODO(andreas): This should be able to list out the ContextSystems it needs. + + /// Information about which components are queried by the visualizer. + fn visualizer_query_info(&self) -> VisualizerQueryInfo; /// Filters a set of applicable entities (entities that have all required components), /// into to a set of visualizable entities. @@ -66,6 +93,19 @@ pub trait VisualizerSystem: Send + Sync + 'static { } fn as_any(&self) -> &dyn std::any::Any; + + /// Returns an initial value to use when creating an override for a component for this + /// visualizer. This is used as a fallback if the component doesn't already have data. + fn initial_override_value( + &self, + _ctx: &ViewerContext<'_>, + _query: &re_data_store::LatestAtQuery, + _store: &re_data_store::DataStore, + _entity_path: &re_log_types::EntityPath, + _component: &re_types::ComponentName, + ) -> Option { + None + } } pub struct VisualizerCollection { diff --git a/crates/re_viewport/src/space_view.rs b/crates/re_viewport/src/space_view.rs index f86ca23b6fff..0633eff8dddf 100644 --- a/crates/re_viewport/src/space_view.rs +++ b/crates/re_viewport/src/space_view.rs @@ -518,6 +518,7 @@ impl SpaceViewBlueprint { property_overrides: Some(PropertyOverrides { accumulated_properties, individual_properties, + component_overrides: Default::default(), override_path: entity_path, }), } diff --git a/examples/rust/custom_space_view/src/color_coordinates_visualizer_system.rs b/examples/rust/custom_space_view/src/color_coordinates_visualizer_system.rs index c7a6a1f43ea3..690af975e98a 100644 --- a/examples/rust/custom_space_view/src/color_coordinates_visualizer_system.rs +++ b/examples/rust/custom_space_view/src/color_coordinates_visualizer_system.rs @@ -3,12 +3,10 @@ use re_viewer::external::{ re_log_types::EntityPath, re_query::query_archetype, re_renderer, - re_types::{ - self, components::InstanceKey, Archetype, ComponentName, ComponentNameSet, Loggable as _, - }, + re_types::{self, components::InstanceKey, ComponentName, Loggable as _}, re_viewer_context::{ IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, - ViewSystemIdentifier, ViewerContext, VisualizerSystem, + ViewSystemIdentifier, ViewerContext, VisualizerQueryInfo, VisualizerSystem, }, }; @@ -44,11 +42,8 @@ impl IdentifiedViewSystem for InstanceColorSystem { } impl VisualizerSystem for InstanceColorSystem { - fn required_components(&self) -> ComponentNameSet { - ColorArchetype::required_components() - .iter() - .map(ToOwned::to_owned) - .collect() + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() } /// Populates the scene part with data from the store.