From 5a10357d2120302120140dd257783c4ce0ca661b Mon Sep 17 00:00:00 2001 From: Jeremy Leibs Date: Thu, 6 Jun 2024 23:00:55 +0200 Subject: [PATCH] Introduce new archetype for Axes3D (#6510) ### What - Builds on top of: https://github.com/rerun-io/rerun/pull/6505 (merge that first and rebase) This replaces the axes-related entity properties with a new Axes3D archetype. The archetype allows overriding the axis-length from code. Controlling whether the axes are visible now happens via enabling of the visualizer. We use similar heuristics to before to enable axes3d visualizers on transforms in certain situations. The legacy UI has been remapped to the new indicator/heuristics implementation. ![image](https://github.com/rerun-io/rerun/assets/3312232/e24bbe52-5470-41e1-ada6-79d0d8059cef) TODO: - [] Still need to implement the example in rust / cpp. ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using examples from latest `main` build: [rerun.io/viewer](https://rerun.io/viewer/pr/6510?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [rerun.io/viewer](https://rerun.io/viewer/pr/6510?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! - [PR Build Summary](https://build.rerun.io/pr/6510) - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) To run all checks from `main`, comment on the PR with `@rerun-bot full-check`. --- .../src/datatype_editors/float_drag.rs | 12 +- crates/re_edit_ui/src/datatype_editors/mod.rs | 2 +- crates/re_edit_ui/src/lib.rs | 13 +- crates/re_entity_db/src/entity_properties.rs | 29 --- crates/re_selection_panel/src/override_ui.rs | 78 ++++--- .../re_selection_panel/src/selection_panel.rs | 182 ++++++++-------- crates/re_space_view/src/results_ext.rs | 1 + crates/re_space_view/src/view_property_ui.rs | 1 + .../re_space_view_spatial/src/heuristics.rs | 76 +------ crates/re_space_view_spatial/src/view_2d.rs | 10 +- crates/re_space_view_spatial/src/view_3d.rs | 68 +++++- .../src/visualizers/mod.rs | 4 +- .../src/visualizers/transform3d_arrows.rs | 129 +++++++++-- .../src/space_view_class.rs | 1 + .../src/line_visualizer_system.rs | 9 +- .../src/point_visualizer_system.rs | 9 +- .../re_types/definitions/rerun/archetypes.fbs | 1 + .../definitions/rerun/archetypes/axes3d.fbs | 30 +++ .../re_types/definitions/rerun/components.fbs | 1 + .../rerun/components/axis_length.fbs | 21 ++ crates/re_types/src/archetypes/.gitattributes | 1 + crates/re_types/src/archetypes/axes3d.rs | 204 ++++++++++++++++++ crates/re_types/src/archetypes/mod.rs | 2 + crates/re_types/src/components/.gitattributes | 1 + crates/re_types/src/components/axis_length.rs | 185 ++++++++++++++++ .../src/components/axis_length_ext.rs | 15 ++ crates/re_types/src/components/mod.rs | 3 + .../re_viewer/src/component_defaults/mod.rs | 4 + crates/re_viewer_context/src/query_context.rs | 6 +- .../src/space_view/view_context.rs | 91 +++++++- .../src/space_view/view_query.rs | 16 ++ crates/re_viewport/src/system_execution.rs | 18 +- .../re_viewport_blueprint/src/space_view.rs | 59 ++++- .../src/view_properties.rs | 1 + docs/content/reference/types/archetypes.md | 1 + .../reference/types/archetypes/.gitattributes | 1 + .../reference/types/archetypes/axes3d.md | 34 +++ docs/content/reference/types/components.md | 1 + .../reference/types/components/.gitattributes | 1 + .../reference/types/components/axis_length.md | 20 ++ .../reference/types/datatypes/float32.md | 1 + .../reference/types/views/spatial2d_view.md | 1 + .../reference/types/views/spatial3d_view.md | 1 + .../all/archetypes/transform3d_axes.cpp | 29 +++ .../all/archetypes/transform3d_axes.py | 30 +++ .../all/archetypes/transform3d_axes.rs | 29 +++ rerun_cpp/src/rerun/archetypes.hpp | 1 + rerun_cpp/src/rerun/archetypes/.gitattributes | 2 + rerun_cpp/src/rerun/archetypes/axes3d.cpp | 33 +++ rerun_cpp/src/rerun/archetypes/axes3d.hpp | 92 ++++++++ rerun_cpp/src/rerun/c/rerun.h | 2 +- rerun_cpp/src/rerun/components.hpp | 1 + rerun_cpp/src/rerun/components/.gitattributes | 1 + .../src/rerun/components/axis_length.hpp | 61 ++++++ rerun_py/docs/gen_common_index.py | 1 + rerun_py/rerun_sdk/rerun/__init__.py | 1 + .../rerun_sdk/rerun/archetypes/.gitattributes | 1 + .../rerun_sdk/rerun/archetypes/__init__.py | 2 + rerun_py/rerun_sdk/rerun/archetypes/axes3d.py | 111 ++++++++++ .../rerun_sdk/rerun/components/.gitattributes | 1 + .../rerun_sdk/rerun/components/__init__.py | 4 + .../rerun_sdk/rerun/components/axis_length.py | 28 +++ .../check_all_components_ui.py | 1 + 63 files changed, 1461 insertions(+), 314 deletions(-) create mode 100644 crates/re_types/definitions/rerun/archetypes/axes3d.fbs create mode 100644 crates/re_types/definitions/rerun/components/axis_length.fbs create mode 100644 crates/re_types/src/archetypes/axes3d.rs create mode 100644 crates/re_types/src/components/axis_length.rs create mode 100644 crates/re_types/src/components/axis_length_ext.rs create mode 100644 docs/content/reference/types/archetypes/axes3d.md create mode 100644 docs/content/reference/types/components/axis_length.md create mode 100644 docs/snippets/all/archetypes/transform3d_axes.cpp create mode 100644 docs/snippets/all/archetypes/transform3d_axes.py create mode 100644 docs/snippets/all/archetypes/transform3d_axes.rs create mode 100644 rerun_cpp/src/rerun/archetypes/axes3d.cpp create mode 100644 rerun_cpp/src/rerun/archetypes/axes3d.hpp create mode 100644 rerun_cpp/src/rerun/components/axis_length.hpp create mode 100644 rerun_py/rerun_sdk/rerun/archetypes/axes3d.py create mode 100644 rerun_py/rerun_sdk/rerun/components/axis_length.py diff --git a/crates/re_edit_ui/src/datatype_editors/float_drag.rs b/crates/re_edit_ui/src/datatype_editors/float_drag.rs index a0712b8d4dc2..0c60c5d06c55 100644 --- a/crates/re_edit_ui/src/datatype_editors/float_drag.rs +++ b/crates/re_edit_ui/src/datatype_editors/float_drag.rs @@ -1,7 +1,17 @@ use egui::NumExt as _; +use re_types::datatypes; -/// Generic editor for a f32 value from zero to infinity. +/// Generic editor for a [`re_types::datatypes::Float32`] value from zero to infinity. pub fn edit_f32_zero_to_inf( + _ctx: &re_viewer_context::ViewerContext<'_>, + ui: &mut egui::Ui, + value: &mut impl std::ops::DerefMut, +) -> egui::Response { + edit_f32_zero_to_inf_impl(ui, &mut value.deref_mut().0) +} + +/// Generic editor for a raw f32 value from zero to infinity. +pub fn edit_f32_zero_to_inf_raw( _ctx: &re_viewer_context::ViewerContext<'_>, ui: &mut egui::Ui, value: &mut impl std::ops::DerefMut, diff --git a/crates/re_edit_ui/src/datatype_editors/mod.rs b/crates/re_edit_ui/src/datatype_editors/mod.rs index 07adc2c2e153..efe0bee01306 100644 --- a/crates/re_edit_ui/src/datatype_editors/mod.rs +++ b/crates/re_edit_ui/src/datatype_editors/mod.rs @@ -5,5 +5,5 @@ mod singleline_string; pub use bool_toggle::{edit_bool, edit_bool_raw}; pub use enum_combobox::edit_enum; -pub use float_drag::edit_f32_zero_to_inf; +pub use float_drag::{edit_f32_zero_to_inf, edit_f32_zero_to_inf_raw}; pub use singleline_string::edit_singleline_string; diff --git a/crates/re_edit_ui/src/lib.rs b/crates/re_edit_ui/src/lib.rs index fe0414ce2220..778442b10a69 100644 --- a/crates/re_edit_ui/src/lib.rs +++ b/crates/re_edit_ui/src/lib.rs @@ -12,7 +12,9 @@ mod visual_bounds2d; use datatype_editors::edit_enum; use re_types::{ blueprint::components::{BackgroundKind, Corner2D, LockRangeDuringZoom, Visible}, - components::{Color, MarkerSize, Name, Radius, StrokeWidth, Text}, + components::{ + AxisLength, Color, ImagePlaneDistance, MarkerSize, Name, Radius, StrokeWidth, Text, + }, }; use re_viewer_context::ViewerContext; @@ -42,15 +44,18 @@ pub fn register_editors(registry: &mut re_viewer_context::ComponentUiRegistry) { registry.add_singleline_editor_ui(marker_shape::edit_marker_shape_ui); registry.add_singleline_editor_ui(range1d::edit_range1d); + registry.add_singleline_editor_ui::(datatype_editors::edit_f32_zero_to_inf); + registry.add_singleline_editor_ui::(datatype_editors::edit_f32_zero_to_inf); + registry.add_singleline_editor_ui::(datatype_editors::edit_bool_raw); registry.add_singleline_editor_ui::(datatype_editors::edit_bool); registry.add_singleline_editor_ui::(datatype_editors::edit_singleline_string); registry.add_singleline_editor_ui::(datatype_editors::edit_singleline_string); - registry.add_singleline_editor_ui::(datatype_editors::edit_f32_zero_to_inf); - registry.add_singleline_editor_ui::(datatype_editors::edit_f32_zero_to_inf); - registry.add_singleline_editor_ui::(datatype_editors::edit_f32_zero_to_inf); + registry.add_singleline_editor_ui::(datatype_editors::edit_f32_zero_to_inf_raw); + registry.add_singleline_editor_ui::(datatype_editors::edit_f32_zero_to_inf_raw); + registry.add_singleline_editor_ui::(datatype_editors::edit_f32_zero_to_inf_raw); registry.add_singleline_editor_ui(|_ctx, ui, value| { edit_enum(ui, "corner2d", value, &Corner2D::ALL) diff --git a/crates/re_entity_db/src/entity_properties.rs b/crates/re_entity_db/src/entity_properties.rs index 9518682af83e..63fb8d54501a 100644 --- a/crates/re_entity_db/src/entity_properties.rs +++ b/crates/re_entity_db/src/entity_properties.rs @@ -114,17 +114,6 @@ pub struct EntityProperties { /// Used to scale the radii of the points in the resulting point cloud. pub backproject_radius_scale: EditableAutoValue, // TODO(andreas): should be a component on the DepthImage archetype. - /// Whether to show the 3D transform visualization at all. - // TODO(andreas): should go away once we can disable visualizer. Revisit how to collectively enable/disable these on an entire view. - // To consider: Make a TransformAxis archetype whose indicator is what enables the visualizer - // -> size etc. are now part of this archetype, not the `Transform` archetype - // -> `TransformAxis` itself doesn't have a required component, but the visualizer has. Just like in SeriesLines & Scalar. - // TODO(andreas)/TODO(jleibs): There's a pattern here that we should capture & formalize in the API / codegen / definitions. - pub transform_3d_visible: EditableAutoValue, - - /// The length of the arrows in the entity's own coordinate system (space). - pub transform_3d_size: EditableAutoValue, // TODO(andreas): should be a component on the Transform3D/TransformAxis archetype. - /// Should the legend be shown (for plot space views). pub show_legend: EditableAutoValue, // TODO(andreas): BarChart is still using it, we already have the legend archteype! @@ -147,8 +136,6 @@ impl Default for EntityProperties { backproject_depth: EditableAutoValue::Auto(true), depth_from_world_scale: EditableAutoValue::Auto(1.0), backproject_radius_scale: EditableAutoValue::Auto(1.0), - transform_3d_visible: EditableAutoValue::Auto(false), - transform_3d_size: EditableAutoValue::Auto(1.0), show_legend: EditableAutoValue::Auto(true), legend_location: None, time_series_aggregator: EditableAutoValue::Auto(TimeSeriesAggregator::default()), @@ -175,12 +162,6 @@ impl EntityProperties { .or(&child.backproject_radius_scale) .clone(), - transform_3d_visible: self - .transform_3d_visible - .or(&child.transform_3d_visible) - .clone(), - transform_3d_size: self.transform_3d_size.or(&child.transform_3d_size).clone(), - show_legend: self.show_legend.or(&child.show_legend).clone(), legend_location: self.legend_location.or(child.legend_location), time_series_aggregator: self @@ -213,12 +194,6 @@ impl EntityProperties { .or(&self.backproject_radius_scale) .clone(), - transform_3d_visible: other - .transform_3d_visible - .or(&self.transform_3d_visible) - .clone(), - transform_3d_size: self.transform_3d_size.or(&other.transform_3d_size).clone(), - show_legend: other.show_legend.or(&self.show_legend).clone(), legend_location: other.legend_location.or(self.legend_location), time_series_aggregator: other @@ -236,8 +211,6 @@ impl EntityProperties { backproject_depth, depth_from_world_scale, backproject_radius_scale, - transform_3d_visible, - transform_3d_size, show_legend, legend_location, time_series_aggregator, @@ -248,8 +221,6 @@ impl EntityProperties { || backproject_depth.has_edits(&other.backproject_depth) || depth_from_world_scale.has_edits(&other.depth_from_world_scale) || backproject_radius_scale.has_edits(&other.backproject_radius_scale) - || transform_3d_visible.has_edits(&other.transform_3d_visible) - || transform_3d_size.has_edits(&other.transform_3d_size) || show_legend.has_edits(&other.show_legend) || *legend_location != other.legend_location || time_series_aggregator.has_edits(&other.time_series_aggregator) diff --git a/crates/re_selection_panel/src/override_ui.rs b/crates/re_selection_panel/src/override_ui.rs index a1d5c0e665c4..7b385c49b44e 100644 --- a/crates/re_selection_panel/src/override_ui.rs +++ b/crates/re_selection_panel/src/override_ui.rs @@ -8,15 +8,14 @@ use re_log_types::{DataCell, DataRow, RowId, StoreKind}; use re_types_core::{components::VisualizerOverrides, ComponentName}; use re_ui::{ContextExt as _, UiExt as _}; use re_viewer_context::{ - ComponentUiTypes, DataResult, OverridePath, QueryContext, SpaceViewClassExt as _, - SystemCommand, SystemCommandSender as _, ViewSystemIdentifier, ViewerContext, + ComponentUiTypes, DataResult, OverridePath, SpaceViewClassExt as _, SystemCommand, + SystemCommandSender as _, ViewContext, ViewSystemIdentifier, ViewerContext, }; use re_viewport_blueprint::SpaceViewBlueprint; pub fn override_ui( - ctx: &ViewerContext<'_>, + ctx: &ViewContext<'_>, space_view: &SpaceViewBlueprint, - view_state: &dyn re_viewer_context::SpaceViewState, instance_path: &InstancePath, ui: &mut egui::Ui, ) { @@ -47,10 +46,6 @@ pub fn override_ui( .map(|props| props.resolved_component_overrides.keys().copied().collect()) .unwrap_or_default(); - 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 @@ -60,7 +55,8 @@ pub fn override_ui( // 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 + let Some(queried) = ctx + .visualizer_collection .get_by_identifier(*vis) .ok() .map(|vis| vis.visualizer_query_info().queried) @@ -78,14 +74,14 @@ pub fn override_ui( &query, ctx.recording(), ui, - &view_systems, &component_to_vis, &active_overrides, &data_result, - view_state, ); - let Some(overrides) = data_result.property_overrides else { + // TODO(jleibs): This clone is annoying, but required because QueryContext wants + // a reference to the EntityPath. We could probably refactor this to avoid the clone. + let Some(overrides) = data_result.property_overrides.clone() else { return; }; @@ -94,6 +90,8 @@ pub fn override_ui( .into_iter() .sorted_by_key(|(c, _)| *c); + let query_context = ctx.query_context(&data_result, &query); + re_ui::list_item::list_item_scope(ui, "overrides", |ui| { ui.spacing_mut().item_spacing.y = 0.0; for ( @@ -107,7 +105,10 @@ pub fn override_ui( let Some(visualizer_identifier) = component_to_vis.get(component_name) else { continue; }; - let Ok(visualizer) = view_systems.get_by_identifier(*visualizer_identifier) else { + let Ok(visualizer) = ctx + .visualizer_collection + .get_by_identifier(*visualizer_identifier) + else { re_log::warn!( "Failed to resolve visualizer identifier {visualizer_identifier}, to a visualizer implementation" ); @@ -117,7 +118,7 @@ pub fn override_ui( let value_fn = |ui: &mut egui::Ui| { let (origin_db, query) = match store_kind { StoreKind::Blueprint => { - (ctx.store_context.blueprint, ctx.blueprint_query.clone()) + (ctx.blueprint_db(), ctx.viewer_ctx.blueprint_query.clone()) } StoreKind::Recording => (ctx.recording(), ctx.current_query()), }; @@ -134,14 +135,8 @@ pub fn override_ui( .cloned(); /* arc */ if let Some(results) = component_data { - ctx.component_ui_registry.singleline_edit_ui( - &QueryContext { - viewer_ctx: ctx, - target_entity_path: &instance_path.entity_path, - archetype_name: None, - query: &query, - view_state, - }, + ctx.viewer_ctx.component_ui_registry.singleline_edit_ui( + &query_context, ui, origin_db, entity_path_overridden, @@ -177,15 +172,13 @@ pub fn override_ui( #[allow(clippy::too_many_arguments)] pub fn add_new_override( - ctx: &ViewerContext<'_>, + ctx: &ViewContext<'_>, query: &LatestAtQuery, db: &EntityDb, ui: &mut egui::Ui, - view_systems: &re_viewer_context::VisualizerCollection, component_to_vis: &BTreeMap, active_overrides: &BTreeSet, data_result: &DataResult, - view_state: &dyn re_viewer_context::SpaceViewState, ) { let remaining_components = component_to_vis .keys() @@ -201,13 +194,7 @@ pub fn add_new_override( opened = true; ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - let query_context = QueryContext { - viewer_ctx: ctx, - target_entity_path: &data_result.entity_path, - archetype_name: None, - query, - view_state, - }; + let query_context = ctx.query_context(data_result, query); // Present the option to add new components for each component that doesn't // already have an active override. @@ -227,6 +214,7 @@ pub fn add_new_override( // If there is no registered editor, don't let the user create an override // TODO(andreas): Can only handle single line editors right now. if !ctx + .viewer_ctx .component_ui_registry .registered_ui_types(*component) .contains(ComponentUiTypes::SingleLineEditor) @@ -247,11 +235,16 @@ pub fn add_new_override( .latest_at(query, &data_result.entity_path, *component, &components) .and_then(|result| result.2[0].clone()) .or_else(|| { - view_systems.get_by_identifier(*viz).ok().and_then(|sys| { - sys.fallback_for(&query_context, *component) - .map(|fallback| DataCell::from_arrow(*component, fallback)) - .ok() - }) + ctx.visualizer_collection + .get_by_identifier(*viz) + .ok() + .and_then(|sys| { + sys.fallback_for(&query_context, *component) + .map(|fallback| { + DataCell::from_arrow(*component, fallback) + }) + .ok() + }) }) else { re_log::warn!("Could not identify an initial value for: {}", component); @@ -262,16 +255,17 @@ pub fn add_new_override( match DataRow::from_cells( RowId::new(), - ctx.store_context.blueprint_timepoint_for_writes(), + ctx.blueprint_timepoint_for_writes(), override_path.clone(), [initial_data], ) { Ok(row) => { - ctx.command_sender - .send_system(SystemCommand::UpdateBlueprint( - ctx.store_context.blueprint.store_id().clone(), + ctx.viewer_ctx.command_sender.send_system( + SystemCommand::UpdateBlueprint( + ctx.blueprint_db().store_id().clone(), vec![row], - )); + ), + ); } Err(err) => { re_log::warn!( diff --git a/crates/re_selection_panel/src/selection_panel.rs b/crates/re_selection_panel/src/selection_panel.rs index 009c786d32a4..e39169908605 100644 --- a/crates/re_selection_panel/src/selection_panel.rs +++ b/crates/re_selection_panel/src/selection_panel.rs @@ -14,8 +14,10 @@ use re_log_types::EntityPathFilter; use re_space_view::DataResultQuery as _; use re_space_view_time_series::TimeSeriesSpaceView; use re_types::{ - archetypes::Pinhole, - components::{ImagePlaneDistance, PinholeProjection, Transform3D}, + archetypes::{Axes3D, Pinhole}, + components::{ + AxisLength, ImagePlaneDistance, PinholeProjection, Transform3D, VisualizerOverrides, + }, tensor_data::TensorDataMeaning, }; use re_ui::{icons, list_item, ContextExt as _, DesignTokens, SyntaxHighlighting as _, UiExt as _}; @@ -173,17 +175,10 @@ impl SelectionPanel { override_visualizer_ui(ctx, view, instance_path, ui); }); - let view_state = view_states - .get_mut( - ctx.space_view_class_registry, - *view_id, - view.class_identifier(), - ) - .view_state - .as_ref(); + let view_ctx = view.bundle_context_with_states(ctx, view_states); ui.large_collapsing_header("Component Overrides", true, |ui| { - override_ui(ctx, view, view_state, instance_path, ui); + override_ui(&view_ctx, view, instance_path, ui); }); } } @@ -225,34 +220,13 @@ impl SelectionPanel { } Item::DataResult(space_view_id, instance_path) => { - let Some(space_view) = blueprint.space_view(space_view_id) else { + let Some(view) = blueprint.space_view(space_view_id) else { return; }; - let visualizer_collection = ctx - .space_view_class_registry - .new_visualizer_collection(space_view.class_identifier()); + let view_ctx = view.bundle_context_with_states(ctx, view_states); - let Some(view_state) = view_states - .get(*space_view_id) - .map(|states| states.view_state.as_ref()) - else { - return; - }; - - let view_ctx = ViewContext { - viewer_ctx: ctx, - view_state, - visualizer_collection: &visualizer_collection, - }; - - blueprint_ui_for_data_result( - &view_ctx, - space_view, - ui, - *space_view_id, - instance_path, - ); + blueprint_ui_for_data_result(&view_ctx, view, ui, *space_view_id, instance_path); } } } @@ -1247,7 +1221,7 @@ fn entity_props_ui( // if *view_state.state_spatial.nav_mode.get() == SpatialNavigationMode::ThreeD { pinhole_props_ui(ctx, ui, data_result); depth_props_ui(ctx.viewer_ctx, ui, entity_path, entity_props); - transform3d_visualization_ui(ctx.viewer_ctx, ui, entity_path, entity_props); + transform3d_visualization_ui(ctx, ui, data_result); }); } @@ -1314,6 +1288,84 @@ fn pinhole_props_ui(ctx: &ViewContext<'_>, ui: &mut egui::Ui, data_result: &Data } } +fn transform3d_visualization_ui( + ctx: &ViewContext<'_>, + ui: &mut egui::Ui, + data_result: &DataResult, +) { + re_tracing::profile_function!(); + + let (query, store) = + guess_query_and_db_for_selected_entity(ctx.viewer_ctx, &data_result.entity_path); + + if store + .latest_at_component::(&data_result.entity_path, &query) + .is_none() + { + return; + } + + let arrow_viz = "Transform3DArrows".into(); + + let mut show_arrows = data_result.visualizers.contains(&arrow_viz); + + let results = data_result.latest_at_with_overrides::(ctx, &query); + + let mut arrow_length: f32 = results.get_mono_with_fallback::().into(); + + { + let response = ui.re_checkbox( &mut show_arrows, "Show transform").on_hover_text( + "Enables/disables the display of three arrows to visualize the (accumulated) transform at this entity. Red/green/blue show the x/y/z axis respectively."); + if response.changed() { + let component = if show_arrows { + VisualizerOverrides::from( + data_result + .visualizers + .iter() + .chain(std::iter::once(&arrow_viz)) + .map(|v| re_types_core::ArrowString::from(v.as_str())) + .collect::>(), + ) + } else { + VisualizerOverrides::from( + data_result + .visualizers + .iter() + .filter(|v| **v != arrow_viz) + .map(|v| re_types_core::ArrowString::from(v.as_str())) + .collect::>(), + ) + }; + + data_result.save_individual_override(ctx.viewer_ctx, &component); + } + } + + if show_arrows { + ui.end_row(); + ui.label("Transform-arrow length"); + let speed = (arrow_length * 0.05).at_least(0.001); + let response = ui + .add( + egui::DragValue::new(&mut arrow_length) + .clamp_range(0.0..=1.0e8) + .speed(speed), + ) + .on_hover_text( + "How long the arrows should be in the entity's own coordinate system. Double-click to reset to auto.", + ); + if response.double_clicked() { + data_result.clear_individual_override::(ctx.viewer_ctx); + response.surrender_focus(); + } else if response.changed() { + data_result + .save_individual_override::(ctx.viewer_ctx, &arrow_length.into()); + } + } + + ui.end_row(); +} + fn depth_props_ui( ctx: &ViewerContext<'_>, ui: &mut egui::Ui, @@ -1413,61 +1465,3 @@ fn backproject_radius_scale_ui(ui: &mut egui::Ui, property: &mut EditableAutoVal } ui.end_row(); } - -fn transform3d_visualization_ui( - ctx: &ViewerContext<'_>, - ui: &mut egui::Ui, - entity_path: &EntityPath, - entity_props: &mut EntityProperties, -) { - re_tracing::profile_function!(); - - let (query, store) = guess_query_and_db_for_selected_entity(ctx, entity_path); - - if store - .latest_at_component::(entity_path, &query) - .is_none() - { - return; - } - - let show_arrows = &mut entity_props.transform_3d_visible; - let arrow_length = &mut entity_props.transform_3d_size; - - { - let mut checked = *show_arrows.get(); - let response = ui.re_checkbox( &mut checked, "Show transform").on_hover_text( - "Enables/disables the display of three arrows to visualize the (accumulated) transform at this entity. Red/green/blue show the x/y/z axis respectively."); - if response.changed() { - *show_arrows = EditableAutoValue::UserEdited(checked); - } - if response.double_clicked() { - *show_arrows = EditableAutoValue::Auto(checked); - } - } - - if *show_arrows.get() { - ui.end_row(); - ui.label("Transform-arrow length"); - let mut value = *arrow_length.get(); - let speed = (value * 0.05).at_least(0.001); - let response = ui - .add( - egui::DragValue::new(&mut value) - .clamp_range(0.0..=1.0e8) - .speed(speed), - ) - .on_hover_text( - "How long the arrows should be in the entity's own coordinate system. Double-click to reset to auto.", - ); - if response.double_clicked() { - // reset to auto - the exact value will be restored somewhere else - *arrow_length = EditableAutoValue::Auto(value); - response.surrender_focus(); - } else if response.changed() { - *arrow_length = EditableAutoValue::UserEdited(value); - } - } - - ui.end_row(); -} diff --git a/crates/re_space_view/src/results_ext.rs b/crates/re_space_view/src/results_ext.rs index 026bb5624b05..fa7302b45c6b 100644 --- a/crates/re_space_view/src/results_ext.rs +++ b/crates/re_space_view/src/results_ext.rs @@ -95,6 +95,7 @@ impl<'a> HybridLatestAtResults<'a> { archetype_name: None, // TODO(jleibs): Do we need this? query: &self.query, view_state: self.ctx.view_state, + view_ctx: Some(self.ctx), }; fallback_provider diff --git a/crates/re_space_view/src/view_property_ui.rs b/crates/re_space_view/src/view_property_ui.rs index 124bd5ec4384..9fb83ba619d4 100644 --- a/crates/re_space_view/src/view_property_ui.rs +++ b/crates/re_space_view/src/view_property_ui.rs @@ -36,6 +36,7 @@ fn view_property_ui_impl( archetype_name: Some(archetype.name), query: ctx.blueprint_query, view_state, + view_ctx: None, }; let component_results = ctx.blueprint_db().latest_at( diff --git a/crates/re_space_view_spatial/src/heuristics.rs b/crates/re_space_view_spatial/src/heuristics.rs index 0ab8490d1c13..f14cf394d23e 100644 --- a/crates/re_space_view_spatial/src/heuristics.rs +++ b/crates/re_space_view_spatial/src/heuristics.rs @@ -9,20 +9,18 @@ use re_log_types::EntityPath; use re_types::{ components::{DepthMeter, TensorData}, tensor_data::TensorDataMeaning, - Archetype as _, SpaceViewClassIdentifier, + SpaceViewClassIdentifier, }; use re_viewer_context::{IdentifiedViewSystem, PerSystemEntities, ViewerContext}; use crate::{ - query_pinhole_legacy, view_kind::SpatialSpaceViewKind, - visualizers::{ImageVisualizer, SpatialViewVisualizerData, Transform3DArrowsVisualizer}, + visualizers::{ImageVisualizer, SpatialViewVisualizerData}, }; pub fn generate_auto_legacy_properties( ctx: &ViewerContext<'_>, per_system_entities: &PerSystemEntities, - scene_bbox_accum: &macaw::BoundingBox, spatial_kind: SpatialSpaceViewKind, ) -> re_entity_db::EntityPropertyMap { re_tracing::profile_function!(); @@ -36,12 +34,6 @@ pub fn generate_auto_legacy_properties( &mut auto_properties, spatial_kind, ); - update_transform3d_lines_heuristics( - ctx, - per_system_entities, - &mut auto_properties, - scene_bbox_accum, - ); auto_properties } @@ -120,70 +112,6 @@ fn update_depth_cloud_property_heuristics( } } -fn update_transform3d_lines_heuristics( - ctx: &ViewerContext<'_>, - per_system_entities: &PerSystemEntities, - auto_properties: &mut re_entity_db::EntityPropertyMap, - scene_bbox_accum: &macaw::BoundingBox, -) { - for ent_path in per_system_entities - .get(&Transform3DArrowsVisualizer::identifier()) - .unwrap_or(&BTreeSet::new()) - { - fn is_pinhole_extrinsics_of<'a>( - ent_path: &'a EntityPath, - ctx: &'a ViewerContext<'_>, - ) -> Option<&'a EntityPath> { - if query_pinhole_legacy(ctx.recording(), &ctx.current_query(), ent_path).is_some() { - return Some(ent_path); - } else { - // Any direct child has a pinhole camera? - if let Some(child_tree) = ctx.recording().tree().subtree(ent_path) { - for child in child_tree.children.values() { - if query_pinhole_legacy(ctx.recording(), &ctx.current_query(), &child.path) - .is_some() - { - return Some(&child.path); - } - } - } - } - - None - } - - let mut properties = auto_properties.get(ent_path); - - // By default show the transform if it is a camera extrinsic, - // or if this entity only contains Transform3D components. - let only_has_transform_components = ctx - .recording_store() - .all_components(&ctx.current_query().timeline(), ent_path) - .map_or(false, |c| { - c.iter() - .all(|c| re_types::archetypes::Transform3D::all_components().contains(c)) - }); - properties.transform_3d_visible = EditableAutoValue::Auto( - only_has_transform_components || is_pinhole_extrinsics_of(ent_path, ctx).is_some(), - ); - - // TODO(jleibs): This should be an independent heuristic. - /* - if let Some(pinhole_path) = is_pinhole_extrinsics_of(ent_path, ctx) { - // If there's a pinhole, we orient ourselves on its image plane distance - //let pinhole_path_props = auto_properties.get(pinhole_path); - //properties.transform_3d_size = - // EditableAutoValue::Auto(*pinhole_path_props.pinhole_image_plane_distance * 0.25); - } else { - */ - // Size should be proportional to the scene extent, here covered by its diagonal - let diagonal_length = (scene_bbox_accum.max - scene_bbox_accum.min).length(); - properties.transform_3d_size = EditableAutoValue::Auto(diagonal_length * 0.05); - - auto_properties.overwrite_properties(ent_path.clone(), properties); - } -} - /// Returns all entities for which a visualizer of the given kind would be picked. /// /// I.e. all entities for which at least one visualizer of the specified kind is applicable diff --git a/crates/re_space_view_spatial/src/view_2d.rs b/crates/re_space_view_spatial/src/view_2d.rs index f1ea7ca96225..76ece67ef3ae 100644 --- a/crates/re_space_view_spatial/src/view_2d.rs +++ b/crates/re_space_view_spatial/src/view_2d.rs @@ -159,15 +159,11 @@ impl SpaceViewClass for SpatialSpaceView2D { ent_paths: &PerSystemEntities, auto_properties: &mut re_entity_db::EntityPropertyMap, ) { - let Ok(state) = state.downcast_mut::() else { + let Ok(_state) = state.downcast_mut::() else { return; }; - *auto_properties = generate_auto_legacy_properties( - ctx, - ent_paths, - &state.bounding_boxes.accumulated, - SpatialSpaceViewKind::TwoD, - ); + *auto_properties = + generate_auto_legacy_properties(ctx, ent_paths, SpatialSpaceViewKind::TwoD); } fn spawn_heuristics( diff --git a/crates/re_space_view_spatial/src/view_3d.rs b/crates/re_space_view_spatial/src/view_3d.rs index 005df589caeb..4c3b7c84c545 100644 --- a/crates/re_space_view_spatial/src/view_3d.rs +++ b/crates/re_space_view_spatial/src/view_3d.rs @@ -1,3 +1,4 @@ +use ahash::HashSet; use itertools::Itertools; use nohash_hasher::IntSet; @@ -11,11 +12,14 @@ use re_types::{ }; use re_ui::UiExt as _; use re_viewer_context::{ - PerSystemEntities, RecommendedSpaceView, SpaceViewClass, SpaceViewClassRegistryError, + IdentifiedViewSystem, IndicatedEntities, PerSystemEntities, PerVisualizer, + RecommendedSpaceView, SmallVisualizerSet, SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId, SpaceViewSpawnHeuristics, SpaceViewState, SpaceViewStateExt as _, - SpaceViewSystemExecutionError, ViewQuery, ViewerContext, VisualizableFilterContext, + SpaceViewSystemExecutionError, ViewQuery, ViewSystemIdentifier, ViewerContext, + VisualizableEntities, VisualizableFilterContext, }; +use crate::visualizers::{CamerasVisualizer, Transform3DArrowsVisualizer, Transform3DDetector}; use crate::{ contexts::{register_spatial_contexts, PrimitiveCounter}, heuristics::{ @@ -184,6 +188,56 @@ impl SpaceViewClass for SpatialSpaceView3D { Box::new(context.unwrap_or_default()) } + /// Choose the default visualizers to enable for this entity. + fn choose_default_visualizers( + &self, + entity_path: &EntityPath, + visualizable_entities_per_visualizer: &PerVisualizer, + indicated_entities_per_visualizer: &PerVisualizer, + ) -> SmallVisualizerSet { + let available_visualizers: HashSet<&ViewSystemIdentifier> = + visualizable_entities_per_visualizer + .iter() + .filter_map(|(visualizer, ents)| { + if ents.contains(entity_path) { + Some(visualizer) + } else { + None + } + }) + .collect(); + + let mut visualizers: SmallVisualizerSet = available_visualizers + .iter() + .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(); + + if available_visualizers.contains(&Transform3DArrowsVisualizer::identifier()) { + // There are two cases where we want to activate the [`Transform3DArrowVisualizer`]: + // - If we have no visualizers, but [`Transform3DDetector`] indicates there is a transform here. + // - If we have the [`CamerasVisualizer`] active. + if (visualizers.is_empty() + && available_visualizers.contains(&Transform3DDetector::identifier())) + || visualizers.contains(&CamerasVisualizer::identifier()) + { + visualizers.push(Transform3DArrowsVisualizer::identifier()); + } + } + + // If there were no other visualizers, or this is a camera, then we will include axes. + + visualizers + } + fn spawn_heuristics( &self, ctx: &ViewerContext<'_>, @@ -285,15 +339,11 @@ impl SpaceViewClass for SpatialSpaceView3D { ent_paths: &PerSystemEntities, auto_properties: &mut re_entity_db::EntityPropertyMap, ) { - let Ok(state) = state.downcast_mut::() else { + let Ok(_state) = state.downcast_mut::() else { return; }; - *auto_properties = generate_auto_legacy_properties( - ctx, - ent_paths, - &state.bounding_boxes.accumulated, - SpatialSpaceViewKind::ThreeD, - ); + *auto_properties = + generate_auto_legacy_properties(ctx, ent_paths, SpatialSpaceViewKind::ThreeD); } fn selection_ui( diff --git a/crates/re_space_view_spatial/src/visualizers/mod.rs b/crates/re_space_view_spatial/src/visualizers/mod.rs index 16e39d243e55..71607564b50c 100644 --- a/crates/re_space_view_spatial/src/visualizers/mod.rs +++ b/crates/re_space_view_spatial/src/visualizers/mod.rs @@ -19,7 +19,7 @@ mod transform3d_arrows; pub use cameras::CamerasVisualizer; pub use images::{ImageVisualizer, ViewerImage}; pub use spatial_view_visualizer::SpatialViewVisualizerData; -pub use transform3d_arrows::{add_axis_arrows, Transform3DArrowsVisualizer}; +pub use transform3d_arrows::{add_axis_arrows, Transform3DArrowsVisualizer, Transform3DDetector}; // --- @@ -65,6 +65,7 @@ pub fn register_2d_spatial_visualizers( system_registry.register_visualizer::()?; system_registry.register_visualizer::()?; system_registry.register_visualizer::()?; + system_registry.register_visualizer::()?; Ok(()) } @@ -84,6 +85,7 @@ pub fn register_3d_spatial_visualizers( system_registry.register_visualizer::()?; system_registry.register_visualizer::()?; system_registry.register_visualizer::()?; + system_registry.register_visualizer::()?; Ok(()) } 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 981fac556e53..ab2a7e2e4e77 100644 --- a/crates/re_space_view_spatial/src/visualizers/transform3d_arrows.rs +++ b/crates/re_space_view_spatial/src/visualizers/transform3d_arrows.rs @@ -1,18 +1,23 @@ use egui::Color32; use re_log_types::{EntityPath, Instance}; -use re_types::components::Transform3D; +use re_space_view::DataResultQuery; +use re_types::{ + archetypes::{Axes3D, Pinhole, Transform3D}, + components::{AxisLength, ImagePlaneDistance}, +}; use re_viewer_context::{ - ApplicableEntities, IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContext, + ApplicableEntities, IdentifiedViewSystem, QueryContext, SpaceViewStateExt, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ - contexts::TransformContext, view_kind::SpatialSpaceViewKind, + contexts::TransformContext, ui::SpatialSpaceViewState, view_kind::SpatialSpaceViewKind, visualizers::SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, }; -use super::{filter_visualizable_3d_entities, SpatialViewVisualizerData}; +use super::{filter_visualizable_3d_entities, CamerasVisualizer, SpatialViewVisualizerData}; pub struct Transform3DArrowsVisualizer(SpatialViewVisualizerData); @@ -32,7 +37,7 @@ impl IdentifiedViewSystem for Transform3DArrowsVisualizer { impl VisualizerSystem for Transform3DArrowsVisualizer { fn visualizer_query_info(&self) -> VisualizerQueryInfo { - VisualizerQueryInfo::from_archetype::() + VisualizerQueryInfo::from_archetype::() } fn filter_visualizable_entities( @@ -63,18 +68,6 @@ impl VisualizerSystem for Transform3DArrowsVisualizer { line_builder.radius_boost_in_ui_points_for_outlines(SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES); for data_result in query.iter_visible_data_results(ctx, Self::identifier()) { - if !*data_result.accumulated_properties().transform_3d_visible { - continue; - } - - if ctx - .recording() - .latest_at_component::(&data_result.entity_path, &latest_at_query) - .is_none() - { - continue; - } - // Use transform without potential pinhole, since we don't want to visualize image-space coordinates. let Some(world_from_obj) = transforms.reference_from_entity_ignoring_pinhole( &data_result.entity_path, @@ -91,11 +84,14 @@ impl VisualizerSystem for Transform3DArrowsVisualizer { world_from_obj, ); + let results = data_result.latest_at_with_overrides::(ctx, &latest_at_query); + let axis_length = results.get_mono_with_fallback::().into(); + add_axis_arrows( &mut line_builder, world_from_obj, Some(&data_result.entity_path), - *data_result.accumulated_properties().transform_3d_size, + axis_length, query .highlights .entity_outline_mask(data_result.entity_path.hash()) @@ -167,4 +163,99 @@ pub fn add_axis_arrows( .picking_instance_id(picking_instance_id); } -re_viewer_context::impl_component_fallback_provider!(Transform3DArrowsVisualizer => []); +impl TypedComponentFallbackProvider for Transform3DArrowsVisualizer { + fn fallback_for(&self, ctx: &QueryContext<'_>) -> AxisLength { + if let Some(view_ctx) = ctx.view_ctx { + let query_result = ctx.viewer_ctx.lookup_query_result(view_ctx.view_id); + + // If there is a camera in the scene and it has a pinhole, use the image plane distance to determine the axis length. + if let Some(length) = query_result + .tree + .lookup_result_by_path(ctx.target_entity_path) + .cloned() + .and_then(|data_result| { + if data_result + .visualizers + .contains(&CamerasVisualizer::identifier()) + { + let results = + data_result.latest_at_with_overrides::(view_ctx, ctx.query); + + Some(results.get_mono_with_fallback::()) + } else { + None + } + }) + { + let length: f32 = length.into(); + return (length * 0.5).into(); + } + } + + // If there is a finite bounding box, use the scene size to determine the axis length. + if let Ok(state) = ctx.view_state.downcast_ref::() { + let scene_size = state.bounding_boxes.accumulated.size().length(); + + if scene_size.is_finite() && scene_size > 0.0 { + return (scene_size * 0.05).into(); + }; + } + + // Otherwise 0.3 is a reasonable default. + + // This value somewhat arbitrary. In almost all cases where the scene has defined bounds + // the heuristic will change it or it will be user edited. In the case of non-defined bounds + // this value works better with the default camera setup. + 0.3.into() + } +} + +re_viewer_context::impl_component_fallback_provider!(Transform3DArrowsVisualizer => [AxisLength]); + +/// The `Transform3DDetector` doesn't actually visualize anything, but it allows us to detect +/// when a transform should otherwise be visualized. +/// +/// See the logic in [`crate::SpatialSpaceView3D`]`::choose_default_visualizers`. +#[derive(Default)] +pub struct Transform3DDetector(); + +impl IdentifiedViewSystem for Transform3DDetector { + fn identifier() -> re_viewer_context::ViewSystemIdentifier { + "Transform3DDetector".into() + } +} + +impl VisualizerSystem for Transform3DDetector { + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() + } + + fn execute( + &mut self, + _ctx: &ViewContext<'_>, + _query: &ViewQuery<'_>, + _context_systems: &ViewContextCollection, + ) -> Result, SpaceViewSystemExecutionError> { + Ok(vec![]) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_fallback_provider(&self) -> &dyn re_viewer_context::ComponentFallbackProvider { + self + } + + #[inline] + fn filter_visualizable_entities( + &self, + _entities: ApplicableEntities, + _context: &dyn VisualizableFilterContext, + ) -> VisualizableEntities { + // Never actually visualize this detector + Default::default() + } +} + +re_viewer_context::impl_component_fallback_provider!(Transform3DDetector => []); diff --git a/crates/re_space_view_text_log/src/space_view_class.rs b/crates/re_space_view_text_log/src/space_view_class.rs index c189705ba400..1b5ffe282105 100644 --- a/crates/re_space_view_text_log/src/space_view_class.rs +++ b/crates/re_space_view_text_log/src/space_view_class.rs @@ -108,6 +108,7 @@ impl SpaceViewClass for TextSpaceView { _root_entity_properties: &mut EntityProperties, ) -> Result<(), SpaceViewSystemExecutionError> { let state = state.downcast_mut::()?; + let ViewTextFilters { col_timelines, col_entity_path, diff --git a/crates/re_space_view_time_series/src/line_visualizer_system.rs b/crates/re_space_view_time_series/src/line_visualizer_system.rs index 43a5c5e3c609..b70d0ee96b3b 100644 --- a/crates/re_space_view_time_series/src/line_visualizer_system.rs +++ b/crates/re_space_view_time_series/src/line_visualizer_system.rs @@ -149,13 +149,8 @@ impl SeriesLineSystem { let resolver = ctx.recording().resolver(); - let query_ctx = QueryContext { - viewer_ctx: ctx.viewer_ctx, - archetype_name: Some(SeriesLine::name()), - query: &ctx.current_query(), - target_entity_path: &data_result.entity_path, - view_state: ctx.view_state, - }; + let current_query = ctx.current_query(); + let query_ctx = ctx.query_context(data_result, ¤t_query); let fallback_color = re_viewer_context::TypedComponentFallbackProvider::::fallback_for( diff --git a/crates/re_space_view_time_series/src/point_visualizer_system.rs b/crates/re_space_view_time_series/src/point_visualizer_system.rs index 81858f5fee21..c6060f797559 100644 --- a/crates/re_space_view_time_series/src/point_visualizer_system.rs +++ b/crates/re_space_view_time_series/src/point_visualizer_system.rs @@ -99,13 +99,8 @@ impl SeriesPointSystem { // TODO(cmc): this should be thread-pooled in case there are a gazillon series in the same plot… for data_result in view_query.iter_visible_data_results(ctx, Self::identifier()) { - let query_ctx = QueryContext { - viewer_ctx: ctx.viewer_ctx, - archetype_name: Some(SeriesPoint::name()), - query: &ctx.current_query(), - target_entity_path: &data_result.entity_path, - view_state: ctx.view_state, - }; + let current_query = ctx.current_query(); + let query_ctx = ctx.query_context(data_result, ¤t_query); let fallback_color = re_viewer_context::TypedComponentFallbackProvider::::fallback_for( diff --git a/crates/re_types/definitions/rerun/archetypes.fbs b/crates/re_types/definitions/rerun/archetypes.fbs index 4ddccbed02f3..35a533031f6d 100644 --- a/crates/re_types/definitions/rerun/archetypes.fbs +++ b/crates/re_types/definitions/rerun/archetypes.fbs @@ -2,6 +2,7 @@ include "./archetypes/annotation_context.fbs"; include "./archetypes/arrows2d.fbs"; include "./archetypes/arrows3d.fbs"; include "./archetypes/asset3d.fbs"; +include "./archetypes/axes3d.fbs"; include "./archetypes/bar_chart.fbs"; include "./archetypes/boxes2d.fbs"; include "./archetypes/boxes3d.fbs"; diff --git a/crates/re_types/definitions/rerun/archetypes/axes3d.fbs b/crates/re_types/definitions/rerun/archetypes/axes3d.fbs new file mode 100644 index 000000000000..693cc7ceca42 --- /dev/null +++ b/crates/re_types/definitions/rerun/archetypes/axes3d.fbs @@ -0,0 +1,30 @@ +include "fbs/attributes.fbs"; +include "rust/attributes.fbs"; + +include "rerun/components.fbs"; + +namespace rerun.archetypes; + +// --- + +/// This archetype shows a set of orthogonal coordinate axes such as for representing a transform. +/// +/// \py See [`Transform3D`][rerun.archetypes.Transform3D] +/// \rs See [`Transform3D`][crate::archetypes.ScTransform3Dalar] +/// \cpp See `rerun::archetypes::Transform3D` +/// +/// \example archetypes/transform3d_axes title="Transform with axes" image="https://static.rerun.io/transform3d_axes/35cd6a68cce0cd582231984be4e2628d1627540b/1200w.png" +table Axes3D ( + "attr.docs.category": "Plotting", + "attr.docs.view_types": "Spatial3DView, Spatial2DView: if logged above active projection", + "attr.docs.unreleased" +) { + // --- Required --- + + // --- Optional --- + + /// Length of the 3 axes. + length: rerun.components.AxisLength ("attr.rerun.component_optional", nullable, order: 1000); + + /// TODO(jleibs): Abillity to set color for the axes. +} diff --git a/crates/re_types/definitions/rerun/components.fbs b/crates/re_types/definitions/rerun/components.fbs index f1b5a5374e78..0a2c4a77c198 100644 --- a/crates/re_types/definitions/rerun/components.fbs +++ b/crates/re_types/definitions/rerun/components.fbs @@ -1,4 +1,5 @@ include "./components/annotation_context.fbs"; +include "./components/axis_length.fbs"; include "./components/blob.fbs"; include "./components/class_id.fbs"; include "./components/clear_is_recursive.fbs"; diff --git a/crates/re_types/definitions/rerun/components/axis_length.fbs b/crates/re_types/definitions/rerun/components/axis_length.fbs new file mode 100644 index 000000000000..7ee6e026299d --- /dev/null +++ b/crates/re_types/definitions/rerun/components/axis_length.fbs @@ -0,0 +1,21 @@ +include "arrow/attributes.fbs"; +include "python/attributes.fbs"; +include "rust/attributes.fbs"; + +include "rerun/datatypes.fbs"; +include "rerun/attributes.fbs"; + +namespace rerun.components; + +// --- + +/// The length of an axis in local units of the space. +struct AxisLength ( + "attr.python.aliases": "float", + "attr.python.array_aliases": "float, npt.ArrayLike", + "attr.rust.derive": "Copy, PartialEq, PartialOrd", + "attr.rust.repr": "transparent", + "attr.docs.unreleased" +) { + length: rerun.datatypes.Float32 (order: 100); +} diff --git a/crates/re_types/src/archetypes/.gitattributes b/crates/re_types/src/archetypes/.gitattributes index cc96164742f4..4f5061652788 100644 --- a/crates/re_types/src/archetypes/.gitattributes +++ b/crates/re_types/src/archetypes/.gitattributes @@ -5,6 +5,7 @@ annotation_context.rs linguist-generated=true arrows2d.rs linguist-generated=true arrows3d.rs linguist-generated=true asset3d.rs linguist-generated=true +axes3d.rs linguist-generated=true bar_chart.rs linguist-generated=true boxes2d.rs linguist-generated=true boxes3d.rs linguist-generated=true diff --git a/crates/re_types/src/archetypes/axes3d.rs b/crates/re_types/src/archetypes/axes3d.rs new file mode 100644 index 000000000000..080aba86f25d --- /dev/null +++ b/crates/re_types/src/archetypes/axes3d.rs @@ -0,0 +1,204 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/rust/api.rs +// Based on "crates/re_types/definitions/rerun/archetypes/axes3d.fbs". + +#![allow(trivial_numeric_casts)] +#![allow(unused_imports)] +#![allow(unused_parens)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::cloned_instead_of_copied)] +#![allow(clippy::iter_on_single_items)] +#![allow(clippy::map_flatten)] +#![allow(clippy::match_wildcard_for_single_variants)] +#![allow(clippy::needless_question_mark)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::unnecessary_cast)] + +use ::re_types_core::external::arrow2; +use ::re_types_core::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Archetype**: This archetype shows a set of orthogonal coordinate axes such as for representing a transform. +/// +/// See [`Transform3D`][crate::archetypes.ScTransform3Dalar] +/// +/// ## Example +/// +/// ### Transform with axes +/// ```ignore +/// fn main() -> Result<(), Box> { +/// let rec = rerun::RecordingStreamBuilder::new("rerun_example_transform3d_axes").spawn()?; +/// +/// let base_axes = rerun::Axes3D::new().with_length(1.0); +/// let other_axes = rerun::Axes3D::new().with_length(0.5); +/// +/// rec.log_static("base", &base_axes)?; +/// rec.log_static("base/rotated", &other_axes)?; +/// rec.log_static("base/rotated/translated", &other_axes)?; +/// +/// for deg in 0..360 { +/// rec.set_time_sequence("step", deg); +/// rec.log( +/// "base/rotated", +/// &rerun::Transform3D::from_rotation(rerun::RotationAxisAngle::new( +/// [1.0, 1.0, 1.0], +/// rerun::Angle::Degrees(deg as f32), +/// )), +/// )?; +/// rec.log( +/// "base/rotated/translated", +/// &rerun::Transform3D::from_translation([2.0, 0.0, 0.0]), +/// )?; +/// } +/// +/// Ok(()) +/// } +/// ``` +///
+/// +/// +/// +/// +/// +/// +/// +///
+#[derive(Clone, Debug)] +pub struct Axes3D { + /// Length of the 3 axes. + pub length: Option, +} + +impl ::re_types_core::SizeBytes for Axes3D { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.length.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + >::is_pod() + } +} + +static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 0usize]> = + once_cell::sync::Lazy::new(|| []); + +static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = + once_cell::sync::Lazy::new(|| ["rerun.components.Axes3DIndicator".into()]); + +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = + once_cell::sync::Lazy::new(|| ["rerun.components.AxisLength".into()]); + +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 2usize]> = + once_cell::sync::Lazy::new(|| { + [ + "rerun.components.Axes3DIndicator".into(), + "rerun.components.AxisLength".into(), + ] + }); + +impl Axes3D { + /// The total number of components in the archetype: 0 required, 1 recommended, 1 optional + pub const NUM_COMPONENTS: usize = 2usize; +} + +/// Indicator component for the [`Axes3D`] [`::re_types_core::Archetype`] +pub type Axes3DIndicator = ::re_types_core::GenericIndicatorComponent; + +impl ::re_types_core::Archetype for Axes3D { + type Indicator = Axes3DIndicator; + + #[inline] + fn name() -> ::re_types_core::ArchetypeName { + "rerun.archetypes.Axes3D".into() + } + + #[inline] + fn display_name() -> &'static str { + "Axes 3D" + } + + #[inline] + fn indicator() -> MaybeOwnedComponentBatch<'static> { + static INDICATOR: Axes3DIndicator = Axes3DIndicator::DEFAULT; + MaybeOwnedComponentBatch::Ref(&INDICATOR) + } + + #[inline] + fn required_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + REQUIRED_COMPONENTS.as_slice().into() + } + + #[inline] + fn recommended_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + RECOMMENDED_COMPONENTS.as_slice().into() + } + + #[inline] + fn optional_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + OPTIONAL_COMPONENTS.as_slice().into() + } + + #[inline] + fn all_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + ALL_COMPONENTS.as_slice().into() + } + + #[inline] + fn from_arrow_components( + arrow_data: impl IntoIterator)>, + ) -> DeserializationResult { + re_tracing::profile_function!(); + use ::re_types_core::{Loggable as _, ResultExt as _}; + let arrays_by_name: ::std::collections::HashMap<_, _> = arrow_data + .into_iter() + .map(|(name, array)| (name.full_name(), array)) + .collect(); + let length = if let Some(array) = arrays_by_name.get("rerun.components.AxisLength") { + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Axes3D#length")? + .into_iter() + .next() + .flatten() + } else { + None + }; + Ok(Self { length }) + } +} + +impl ::re_types_core::AsComponents for Axes3D { + fn as_component_batches(&self) -> Vec> { + re_tracing::profile_function!(); + use ::re_types_core::Archetype as _; + [ + Some(Self::indicator()), + self.length + .as_ref() + .map(|comp| (comp as &dyn ComponentBatch).into()), + ] + .into_iter() + .flatten() + .collect() + } +} + +impl Axes3D { + /// Create a new `Axes3D`. + #[inline] + pub fn new() -> Self { + Self { length: None } + } + + /// Length of the 3 axes. + #[inline] + pub fn with_length(mut self, length: impl Into) -> Self { + self.length = Some(length.into()); + self + } +} diff --git a/crates/re_types/src/archetypes/mod.rs b/crates/re_types/src/archetypes/mod.rs index 6ce5a6ebc872..b34937d09375 100644 --- a/crates/re_types/src/archetypes/mod.rs +++ b/crates/re_types/src/archetypes/mod.rs @@ -7,6 +7,7 @@ mod arrows3d; mod arrows3d_ext; mod asset3d; mod asset3d_ext; +mod axes3d; mod bar_chart; mod boxes2d; mod boxes2d_ext; @@ -45,6 +46,7 @@ pub use self::annotation_context::AnnotationContext; pub use self::arrows2d::Arrows2D; pub use self::arrows3d::Arrows3D; pub use self::asset3d::Asset3D; +pub use self::axes3d::Axes3D; pub use self::bar_chart::BarChart; pub use self::boxes2d::Boxes2D; pub use self::boxes3d::Boxes3D; diff --git a/crates/re_types/src/components/.gitattributes b/crates/re_types/src/components/.gitattributes index fa15c2b5ca1d..6d4308f5d00e 100644 --- a/crates/re_types/src/components/.gitattributes +++ b/crates/re_types/src/components/.gitattributes @@ -2,6 +2,7 @@ .gitattributes linguist-generated=true annotation_context.rs linguist-generated=true +axis_length.rs linguist-generated=true blob.rs linguist-generated=true class_id.rs linguist-generated=true color.rs linguist-generated=true diff --git a/crates/re_types/src/components/axis_length.rs b/crates/re_types/src/components/axis_length.rs new file mode 100644 index 000000000000..5c4eb7358ee7 --- /dev/null +++ b/crates/re_types/src/components/axis_length.rs @@ -0,0 +1,185 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/rust/api.rs +// Based on "crates/re_types/definitions/rerun/components/axis_length.fbs". + +#![allow(trivial_numeric_casts)] +#![allow(unused_imports)] +#![allow(unused_parens)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::cloned_instead_of_copied)] +#![allow(clippy::iter_on_single_items)] +#![allow(clippy::map_flatten)] +#![allow(clippy::match_wildcard_for_single_variants)] +#![allow(clippy::needless_question_mark)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::unnecessary_cast)] + +use ::re_types_core::external::arrow2; +use ::re_types_core::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Component**: The length of an axis in local units of the space. +#[derive(Clone, Debug, Copy, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct AxisLength(pub crate::datatypes::Float32); + +impl ::re_types_core::SizeBytes for AxisLength { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.0.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + ::is_pod() + } +} + +impl> From for AxisLength { + fn from(v: T) -> Self { + Self(v.into()) + } +} + +impl std::borrow::Borrow for AxisLength { + #[inline] + fn borrow(&self) -> &crate::datatypes::Float32 { + &self.0 + } +} + +impl std::ops::Deref for AxisLength { + type Target = crate::datatypes::Float32; + + #[inline] + fn deref(&self) -> &crate::datatypes::Float32 { + &self.0 + } +} + +impl std::ops::DerefMut for AxisLength { + #[inline] + fn deref_mut(&mut self) -> &mut crate::datatypes::Float32 { + &mut self.0 + } +} + +::re_types_core::macros::impl_into_cow!(AxisLength); + +impl ::re_types_core::Loggable for AxisLength { + type Name = ::re_types_core::ComponentName; + + #[inline] + fn name() -> Self::Name { + "rerun.components.AxisLength".into() + } + + #[allow(clippy::wildcard_imports)] + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + use arrow2::datatypes::*; + DataType::Float32 + } + + #[allow(clippy::wildcard_imports)] + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + use ::re_types_core::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, datatypes::*}; + Ok({ + let (somes, data0): (Vec<_>, Vec<_>) = data + .into_iter() + .map(|datum| { + let datum: Option<::std::borrow::Cow<'a, Self>> = datum.map(Into::into); + let datum = datum.map(|datum| datum.into_owned().0); + (datum.is_some(), datum) + }) + .unzip(); + let data0_bitmap: Option = { + let any_nones = somes.iter().any(|some| !*some); + any_nones.then(|| somes.into()) + }; + PrimitiveArray::new( + Self::arrow_datatype(), + data0 + .into_iter() + .map(|datum| datum.map(|datum| datum.0).unwrap_or_default()) + .collect(), + data0_bitmap, + ) + .boxed() + }) + } + + #[allow(clippy::wildcard_imports)] + fn from_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + use ::re_types_core::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, buffer::*, datatypes::*}; + Ok(arrow_data + .as_any() + .downcast_ref::() + .ok_or_else(|| { + let expected = Self::arrow_datatype(); + let actual = arrow_data.data_type().clone(); + DeserializationError::datatype_mismatch(expected, actual) + }) + .with_context("rerun.components.AxisLength#length")? + .into_iter() + .map(|opt| opt.copied()) + .map(|res_or_opt| res_or_opt.map(|v| crate::datatypes::Float32(v))) + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .map(|res| res.map(|v| Some(Self(v)))) + .collect::>>>() + .with_context("rerun.components.AxisLength#length") + .with_context("rerun.components.AxisLength")?) + } + + #[allow(clippy::wildcard_imports)] + #[inline] + fn from_arrow(arrow_data: &dyn arrow2::array::Array) -> DeserializationResult> + where + Self: Sized, + { + use ::re_types_core::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, buffer::*, datatypes::*}; + if let Some(validity) = arrow_data.validity() { + if validity.unset_bits() != 0 { + return Err(DeserializationError::missing_data()); + } + } + Ok({ + let slice = arrow_data + .as_any() + .downcast_ref::() + .ok_or_else(|| { + let expected = DataType::Float32; + let actual = arrow_data.data_type().clone(); + DeserializationError::datatype_mismatch(expected, actual) + }) + .with_context("rerun.components.AxisLength#length")? + .values() + .as_slice(); + { + slice + .iter() + .copied() + .map(|v| crate::datatypes::Float32(v)) + .map(|v| Self(v)) + .collect::>() + } + }) + } +} diff --git a/crates/re_types/src/components/axis_length_ext.rs b/crates/re_types/src/components/axis_length_ext.rs new file mode 100644 index 000000000000..6c1ceba9c0e8 --- /dev/null +++ b/crates/re_types/src/components/axis_length_ext.rs @@ -0,0 +1,15 @@ +use super::AxisLength; + +impl Default for AxisLength { + #[inline] + fn default() -> Self { + 1.0.into() + } +} + +impl From for f32 { + #[inline] + fn from(val: AxisLength) -> Self { + val.0.into() + } +} diff --git a/crates/re_types/src/components/mod.rs b/crates/re_types/src/components/mod.rs index 51ef9ca0fdc0..9e94543a0a9a 100644 --- a/crates/re_types/src/components/mod.rs +++ b/crates/re_types/src/components/mod.rs @@ -1,6 +1,8 @@ // DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/rust/api.rs mod annotation_context; +mod axis_length; +mod axis_length_ext; mod blob; mod blob_ext; mod class_id; @@ -73,6 +75,7 @@ mod view_coordinates; mod view_coordinates_ext; pub use self::annotation_context::AnnotationContext; +pub use self::axis_length::AxisLength; pub use self::blob::Blob; pub use self::class_id::ClassId; pub use self::color::Color; diff --git a/crates/re_viewer/src/component_defaults/mod.rs b/crates/re_viewer/src/component_defaults/mod.rs index 9279b5a49c08..e73c7045b887 100644 --- a/crates/re_viewer/src/component_defaults/mod.rs +++ b/crates/re_viewer/src/component_defaults/mod.rs @@ -111,6 +111,10 @@ pub fn list_default_components( ::name(), AnnotationContext::default().to_arrow()?, ), + ( + ::name(), + AxisLength::default().to_arrow()?, + ), (::name(), Blob::default().to_arrow()?), ( ::name(), diff --git a/crates/re_viewer_context/src/query_context.rs b/crates/re_viewer_context/src/query_context.rs index 556069562e2f..c692901d9401 100644 --- a/crates/re_viewer_context/src/query_context.rs +++ b/crates/re_viewer_context/src/query_context.rs @@ -5,7 +5,7 @@ use smallvec::SmallVec; use re_log_types::{EntityPath, EntityPathHash}; -use crate::{DataResult, SpaceViewId, SpaceViewState, ViewerContext}; +use crate::{DataResult, SpaceViewId, SpaceViewState, ViewContext, ViewerContext}; slotmap::new_key_type! { /// Identifier for a [`DataResultNode`] @@ -35,6 +35,10 @@ pub struct QueryContext<'a> { /// The view state of the view in which the query is executed. pub view_state: &'a dyn SpaceViewState, + + /// The view context, if available. + // TODO(jleibs): Make this non-optional. + pub view_ctx: Option<&'a ViewContext<'a>>, } /// The result of executing a single data query diff --git a/crates/re_viewer_context/src/space_view/view_context.rs b/crates/re_viewer_context/src/space_view/view_context.rs index 1e79c556f18c..0b99118c5915 100644 --- a/crates/re_viewer_context/src/space_view/view_context.rs +++ b/crates/re_viewer_context/src/space_view/view_context.rs @@ -1,4 +1,10 @@ -use crate::{DataQueryResult, SpaceViewId}; +use std::sync::Arc; + +use re_data_store::LatestAtQuery; +use re_log_types::{DataCell, EntityPath, TimePoint}; +use re_types::{AsComponents, ComponentBatch, ComponentName}; + +use crate::{DataQueryResult, DataResult, QueryContext, SpaceViewId}; /// The context associated with a view. /// @@ -9,11 +15,28 @@ use crate::{DataQueryResult, SpaceViewId}; /// information to resolve a query with possible overrides and fallback values. pub struct ViewContext<'a> { pub viewer_ctx: &'a crate::ViewerContext<'a>, + pub view_id: SpaceViewId, pub view_state: &'a dyn crate::SpaceViewState, - pub visualizer_collection: &'a crate::VisualizerCollection, + pub visualizer_collection: Arc, } impl<'a> ViewContext<'a> { + #[inline] + pub fn query_context( + &'a self, + data_result: &'a DataResult, + query: &'a LatestAtQuery, + ) -> QueryContext<'a> { + QueryContext { + viewer_ctx: self.viewer_ctx, + target_entity_path: &data_result.entity_path, + archetype_name: None, + query, + view_state: self.view_state, + view_ctx: Some(self), + } + } + /// The active recording. #[inline] pub fn recording(&self) -> &re_entity_db::EntityDb { @@ -39,25 +62,30 @@ impl<'a> ViewContext<'a> { } /// Returns the current selection. + #[inline] pub fn selection(&self) -> &crate::ItemCollection { self.viewer_ctx.selection() } /// Returns the currently hovered objects. + #[inline] pub fn hovered(&self) -> &crate::ItemCollection { self.viewer_ctx.hovered() } + #[inline] pub fn selection_state(&self) -> &crate::ApplicationSelectionState { self.viewer_ctx.selection_state() } /// The current time query, based on the current time control. + #[inline] pub fn current_query(&self) -> re_data_store::LatestAtQuery { self.viewer_ctx.current_query() } /// Set hover/select/focus for a given selection based on an egui response. + #[inline] pub fn select_hovered_on_click( &self, response: &egui::Response, @@ -66,7 +94,66 @@ impl<'a> ViewContext<'a> { self.viewer_ctx.select_hovered_on_click(response, selection); } + #[inline] pub fn lookup_query_result(&self, id: SpaceViewId) -> &DataQueryResult { self.viewer_ctx.lookup_query_result(id) } + + #[inline] + pub fn save_blueprint_archetype(&self, entity_path: EntityPath, components: &dyn AsComponents) { + self.viewer_ctx + .save_blueprint_archetype(entity_path, components); + } + + #[inline] + pub fn save_blueprint_component( + &self, + entity_path: &EntityPath, + components: &dyn ComponentBatch, + ) { + self.viewer_ctx + .save_blueprint_component(entity_path, components); + } + + #[inline] + pub fn save_blueprint_data_cell(&self, entity_path: &EntityPath, data_cell: DataCell) { + self.viewer_ctx + .save_blueprint_data_cell(entity_path, data_cell); + } + + #[inline] + pub fn save_empty_blueprint_component(&self, entity_path: &EntityPath) + where + C: re_types::Component + 'a, + { + self.viewer_ctx + .save_empty_blueprint_component::(entity_path); + } + + #[inline] + pub fn reset_blueprint_component_by_name( + &self, + entity_path: &EntityPath, + component_name: ComponentName, + ) { + self.viewer_ctx + .reset_blueprint_component_by_name(entity_path, component_name); + } + + #[inline] + pub fn save_empty_blueprint_component_by_name( + &self, + entity_path: &EntityPath, + component_name: ComponentName, + ) { + self.viewer_ctx + .save_empty_blueprint_component_by_name(entity_path, component_name); + } + + #[inline] + pub fn blueprint_timepoint_for_writes(&self) -> TimePoint { + self.viewer_ctx + .store_context + .blueprint_timepoint_for_writes() + } } 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 69e97373716b..ea1914c32111 100644 --- a/crates/re_viewer_context/src/space_view/view_query.rs +++ b/crates/re_viewer_context/src/space_view/view_query.rs @@ -236,6 +236,22 @@ impl DataResult { ctx.save_empty_blueprint_component::(recursive_override_path); } + /// Clears the recursive override for a given component + pub fn clear_individual_override(&self, ctx: &ViewerContext<'_>) { + // TODO(jleibs): Make it impossible for this to happen with different type structure + // This should never happen unless we're doing something with a partially processed + // query. + let Some(individual_override_path) = self.individual_override_path() else { + re_log::warn!( + "Tried to save override for {:?} but it has no override path", + self.entity_path + ); + return; + }; + + ctx.save_empty_blueprint_component::(individual_override_path); + } + /// Write the [`EntityProperties`] for this result back to the Blueprint store on the recursive override. /// /// Setting `new_recursive_props` to `None` will always clear the override. diff --git a/crates/re_viewport/src/system_execution.rs b/crates/re_viewport/src/system_execution.rs index 524cf911e74f..0bbdc0469078 100644 --- a/crates/re_viewport/src/system_execution.rs +++ b/crates/re_viewport/src/system_execution.rs @@ -4,7 +4,6 @@ use ahash::HashMap; use rayon::prelude::*; use re_log_types::TimeInt; -use re_types::SpaceViewClassIdentifier; use re_viewer_context::{ PerSystemDataResults, SpaceViewId, SpaceViewState, SystemExecutionOutput, ViewContextCollection, ViewQuery, ViewStates, ViewerContext, VisualizerCollection, @@ -15,24 +14,15 @@ use re_viewport_blueprint::SpaceViewBlueprint; fn run_space_view_systems( ctx: &ViewerContext<'_>, - space_view_class: SpaceViewClassIdentifier, + view: &SpaceViewBlueprint, query: &ViewQuery<'_>, view_state: &dyn SpaceViewState, context_systems: &mut ViewContextCollection, view_systems: &mut VisualizerCollection, ) -> Vec { - re_tracing::profile_function!(space_view_class.as_str()); - - // TODO(jleibs): This is weird. Most of the time we don't need this. - let visualizer_collection = ctx - .space_view_class_registry - .new_visualizer_collection(space_view_class); + re_tracing::profile_function!(view.class_identifier().as_str()); - let view_ctx = re_viewer_context::ViewContext { - viewer_ctx: ctx, - view_state, - visualizer_collection: &visualizer_collection, - }; + let view_ctx = view.bundle_context_with_state(ctx, view_state); { re_tracing::profile_wait!("ViewContextSystem::execute"); @@ -109,7 +99,7 @@ pub fn execute_systems_for_space_view<'a>( let draw_data = if let Some(view_state) = view_states.get(view.id) { run_space_view_systems( ctx, - view.class_identifier(), + view, &query, view_state.view_state.as_ref(), &mut context_systems, diff --git a/crates/re_viewport_blueprint/src/space_view.rs b/crates/re_viewport_blueprint/src/space_view.rs index b81c7b480b07..7650009c6ddd 100644 --- a/crates/re_viewport_blueprint/src/space_view.rs +++ b/crates/re_viewport_blueprint/src/space_view.rs @@ -1,4 +1,8 @@ +use std::sync::Arc; + +use ahash::HashMap; use itertools::{FoldWhile, Itertools}; +use parking_lot::Mutex; use re_entity_db::EntityProperties; use re_types::SpaceViewClassIdentifier; @@ -18,7 +22,8 @@ use re_types_core::Archetype as _; use re_viewer_context::{ ContentsName, DataResult, PerSystemEntities, QueryRange, RecommendedSpaceView, SpaceViewClass, SpaceViewClassRegistry, SpaceViewId, SpaceViewState, StoreContext, SystemCommand, - SystemCommandSender as _, SystemExecutionOutput, ViewQuery, ViewerContext, + SystemCommandSender as _, SystemExecutionOutput, ViewContext, ViewQuery, ViewStates, + ViewerContext, VisualizerCollection, }; /// A view of a space. @@ -462,6 +467,58 @@ impl SpaceViewBlueprint { |time_range| QueryRange::TimeRange(time_range.clone()), ) } + + pub fn bundle_context_with_states<'a>( + &self, + ctx: &'a ViewerContext<'a>, + view_states: &'a mut ViewStates, + ) -> ViewContext<'a> { + let view_state = view_states + .get_mut( + ctx.space_view_class_registry, + self.id, + self.class_identifier(), + ) + .view_state + .as_ref(); + + ViewContext { + viewer_ctx: ctx, + view_id: self.id, + view_state, + visualizer_collection: self.visualizer_collection(ctx), + } + } + + pub fn bundle_context_with_state<'a>( + &self, + ctx: &'a ViewerContext<'a>, + view_state: &'a dyn SpaceViewState, + ) -> ViewContext<'a> { + ViewContext { + viewer_ctx: ctx, + view_id: self.id, + view_state, + visualizer_collection: self.visualizer_collection(ctx), + } + } + + fn visualizer_collection(&self, ctx: &ViewerContext<'_>) -> Arc { + static VISUALIZER_FOR_CONTEXT: once_cell::sync::Lazy< + Mutex>>, + > = once_cell::sync::Lazy::new(Default::default); + + VISUALIZER_FOR_CONTEXT + .lock() + .entry(self.class_identifier()) + .or_insert_with(|| { + Arc::new( + ctx.space_view_class_registry + .new_visualizer_collection(self.class_identifier()), + ) + }) + .clone() + } } #[cfg(test)] diff --git a/crates/re_viewport_blueprint/src/view_properties.rs b/crates/re_viewport_blueprint/src/view_properties.rs index 2eb79f2186c7..48111a3378a0 100644 --- a/crates/re_viewport_blueprint/src/view_properties.rs +++ b/crates/re_viewport_blueprint/src/view_properties.rs @@ -162,6 +162,7 @@ impl<'a> ViewProperty<'a> { archetype_name: Some(self.archetype_name), query: self.blueprint_query, view_state, + view_ctx: None, } } } diff --git a/docs/content/reference/types/archetypes.md b/docs/content/reference/types/archetypes.md index 01feee9e3455..f01751fccc19 100644 --- a/docs/content/reference/types/archetypes.md +++ b/docs/content/reference/types/archetypes.md @@ -15,6 +15,7 @@ Archetypes are bundles of components. This page lists all built-in components. ## Plotting +* [`Axes3D`](archetypes/axes3d.md): This archetype shows a set of orthogonal coordinate axes such as for representing a transform. * [`BarChart`](archetypes/bar_chart.md): A bar chart. * [`Scalar`](archetypes/scalar.md): Log a double-precision scalar. * [`SeriesLine`](archetypes/series_line.md): Define the style properties for a line series in a chart. diff --git a/docs/content/reference/types/archetypes/.gitattributes b/docs/content/reference/types/archetypes/.gitattributes index 5035912305cd..7dd234417708 100644 --- a/docs/content/reference/types/archetypes/.gitattributes +++ b/docs/content/reference/types/archetypes/.gitattributes @@ -5,6 +5,7 @@ annotation_context.md linguist-generated=true arrows2d.md linguist-generated=true arrows3d.md linguist-generated=true asset3d.md linguist-generated=true +axes3d.md linguist-generated=true bar_chart.md linguist-generated=true boxes2d.md linguist-generated=true boxes3d.md linguist-generated=true diff --git a/docs/content/reference/types/archetypes/axes3d.md b/docs/content/reference/types/archetypes/axes3d.md new file mode 100644 index 000000000000..aa0935e1446e --- /dev/null +++ b/docs/content/reference/types/archetypes/axes3d.md @@ -0,0 +1,34 @@ +--- +title: "Axes3D" +--- + + +This archetype shows a set of orthogonal coordinate axes such as for representing a transform. + +## Components + +**Optional**: [`AxisLength`](../components/axis_length.md) + +## Shown in +* [Spatial3DView](../views/spatial3d_view.md) +* [Spatial2DView](../views/spatial2d_view.md) (if logged above active projection) + +## API reference links + * 🌊 [C++ API docs for `Axes3D`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1archetypes_1_1Axes3D.html?speculative-link) + * 🐍 [Python API docs for `Axes3D`](https://ref.rerun.io/docs/python/stable/common/archetypes?speculative-link#rerun.archetypes.Axes3D) + * 🦀 [Rust API docs for `Axes3D`](https://docs.rs/rerun/latest/rerun/archetypes/struct.Axes3D.html?speculative-link) + +## Example + +### Transform with axes + +snippet: archetypes/transform3d_axes + + + + + + + + + diff --git a/docs/content/reference/types/components.md b/docs/content/reference/types/components.md index 41cb9de0d931..7c2547c2a381 100644 --- a/docs/content/reference/types/components.md +++ b/docs/content/reference/types/components.md @@ -14,6 +14,7 @@ on [Entities and Components](../../concepts/entity-component.md). * [`AnnotationContext`](components/annotation_context.md): The `AnnotationContext` provides additional information on how to display entities. +* [`AxisLength`](components/axis_length.md): The length of an axis in local units of the space. * [`Blob`](components/blob.md): A binary blob of data. * [`ClassId`](components/class_id.md): A 16-bit ID representing a type of semantic class. * [`ClearIsRecursive`](components/clear_is_recursive.md): Configures how a clear operation should behave - recursive or not. diff --git a/docs/content/reference/types/components/.gitattributes b/docs/content/reference/types/components/.gitattributes index a9818981d6b9..0dea75b762ce 100644 --- a/docs/content/reference/types/components/.gitattributes +++ b/docs/content/reference/types/components/.gitattributes @@ -2,6 +2,7 @@ .gitattributes linguist-generated=true annotation_context.md linguist-generated=true +axis_length.md linguist-generated=true blob.md linguist-generated=true class_id.md linguist-generated=true clear_is_recursive.md linguist-generated=true diff --git a/docs/content/reference/types/components/axis_length.md b/docs/content/reference/types/components/axis_length.md new file mode 100644 index 000000000000..eb7807c2afcb --- /dev/null +++ b/docs/content/reference/types/components/axis_length.md @@ -0,0 +1,20 @@ +--- +title: "AxisLength" +--- + + +The length of an axis in local units of the space. + +## Fields + +* length: [`Float32`](../datatypes/float32.md) + +## API reference links + * 🌊 [C++ API docs for `AxisLength`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1AxisLength.html?speculative-link) + * 🐍 [Python API docs for `AxisLength`](https://ref.rerun.io/docs/python/stable/common/components?speculative-link#rerun.components.AxisLength) + * 🦀 [Rust API docs for `AxisLength`](https://docs.rs/rerun/latest/rerun/components/struct.AxisLength.html?speculative-link) + + +## Used by + +* [`Axes3D`](../archetypes/axes3d.md?speculative-link) diff --git a/docs/content/reference/types/datatypes/float32.md b/docs/content/reference/types/datatypes/float32.md index fbd8738f83e0..6bfe194b9012 100644 --- a/docs/content/reference/types/datatypes/float32.md +++ b/docs/content/reference/types/datatypes/float32.md @@ -17,4 +17,5 @@ A single-precision 32-bit IEEE 754 floating point number. ## Used by +* [`AxisLength`](../components/axis_length.md?speculative-link) * [`ImagePlaneDistance`](../components/image_plane_distance.md?speculative-link) diff --git a/docs/content/reference/types/views/spatial2d_view.md b/docs/content/reference/types/views/spatial2d_view.md index f496568cfab6..1c793966c01f 100644 --- a/docs/content/reference/types/views/spatial2d_view.md +++ b/docs/content/reference/types/views/spatial2d_view.md @@ -57,6 +57,7 @@ snippet: views/spatial2d * [`SegmentationImage`](../archetypes/segmentation_image.md) * [`Arrows3D`](../archetypes/arrows3d.md) (if logged above active projection) * [`Asset3D`](../archetypes/asset3d.md) (if logged above active projection) +* [`Axes3D`](../archetypes/axes3d.md) (if logged above active projection) * [`Boxes3D`](../archetypes/boxes3d.md) (if logged above active projection) * [`LineStrips3D`](../archetypes/line_strips3d.md) (if logged above active projection) * [`Mesh3D`](../archetypes/mesh3d.md) (if logged above active projection) diff --git a/docs/content/reference/types/views/spatial3d_view.md b/docs/content/reference/types/views/spatial3d_view.md index 0d28a2be43db..5a6662d85483 100644 --- a/docs/content/reference/types/views/spatial3d_view.md +++ b/docs/content/reference/types/views/spatial3d_view.md @@ -41,6 +41,7 @@ snippet: views/spatial3d * [`AnnotationContext`](../archetypes/annotation_context.md) * [`Arrows3D`](../archetypes/arrows3d.md) * [`Asset3D`](../archetypes/asset3d.md) +* [`Axes3D`](../archetypes/axes3d.md) * [`Boxes3D`](../archetypes/boxes3d.md) * [`Clear`](../archetypes/clear.md) * [`DisconnectedSpace`](../archetypes/disconnected_space.md) diff --git a/docs/snippets/all/archetypes/transform3d_axes.cpp b/docs/snippets/all/archetypes/transform3d_axes.cpp new file mode 100644 index 000000000000..9cd9543ff5a5 --- /dev/null +++ b/docs/snippets/all/archetypes/transform3d_axes.cpp @@ -0,0 +1,29 @@ +// Log different transforms with visualized coordinates axes. + +#include + +int main() { + const auto rec = rerun::RecordingStream("rerun_example_transform3d_axes"); + rec.spawn().exit_on_failure(); + + auto base_axes = rerun::Axes3D().with_length(1.0); + auto other_axes = rerun::Axes3D().with_length(0.5); + + rec.log_static("base", base_axes); + rec.log_static("base/rotated", other_axes); + rec.log_static("base/rotated/translated", other_axes); + + for (int deg = 0; deg < 360; deg++) { + rec.set_time_sequence("step", deg); + + rec.log( + "base/rotated", + rerun::Transform3D(rerun::RotationAxisAngle( + {1.0f, 1.0f, 1.0f}, + rerun::Angle::degrees(static_cast(deg)) + )) + ); + + rec.log("base/rotated/translated", rerun::Transform3D({2.0f, 0.0f, 0.0f})); + } +} diff --git a/docs/snippets/all/archetypes/transform3d_axes.py b/docs/snippets/all/archetypes/transform3d_axes.py new file mode 100644 index 000000000000..df55c0d5c4a4 --- /dev/null +++ b/docs/snippets/all/archetypes/transform3d_axes.py @@ -0,0 +1,30 @@ +"""Log different transforms with visualized coordinates axes.""" + +import rerun as rr + +rr.init("rerun_example_transform3d_axes", spawn=True) + +# Make the base axes longer +# Log all axes markers as static first +rr.log("base", rr.Axes3D(length=1), static=True) +rr.log("base/rotated", rr.Axes3D(length=0.5), static=True) +rr.log("base/rotated/translated", rr.Axes3D(length=0.5), static=True) + +# Now sweep out a rotation relative to the base +for deg in range(360): + rr.set_time_sequence("step", deg) + rr.log( + "base/rotated", + rr.Transform3D( + rotation=rr.RotationAxisAngle( + axis=[1.0, 1.0, 1.0], + degrees=deg, + ) + ), + ) + rr.log( + "base/rotated/translated", + rr.Transform3D( + translation=[2.0, 0, 0], + ), + ) diff --git a/docs/snippets/all/archetypes/transform3d_axes.rs b/docs/snippets/all/archetypes/transform3d_axes.rs new file mode 100644 index 000000000000..e1198186eda6 --- /dev/null +++ b/docs/snippets/all/archetypes/transform3d_axes.rs @@ -0,0 +1,29 @@ +//! Log different transforms with visualized coordinates axes. + +fn main() -> Result<(), Box> { + let rec = rerun::RecordingStreamBuilder::new("rerun_example_transform3d_axes").spawn()?; + + let base_axes = rerun::Axes3D::new().with_length(1.0); + let other_axes = rerun::Axes3D::new().with_length(0.5); + + rec.log_static("base", &base_axes)?; + rec.log_static("base/rotated", &other_axes)?; + rec.log_static("base/rotated/translated", &other_axes)?; + + for deg in 0..360 { + rec.set_time_sequence("step", deg); + rec.log( + "base/rotated", + &rerun::Transform3D::from_rotation(rerun::RotationAxisAngle::new( + [1.0, 1.0, 1.0], + rerun::Angle::Degrees(deg as f32), + )), + )?; + rec.log( + "base/rotated/translated", + &rerun::Transform3D::from_translation([2.0, 0.0, 0.0]), + )?; + } + + Ok(()) +} diff --git a/rerun_cpp/src/rerun/archetypes.hpp b/rerun_cpp/src/rerun/archetypes.hpp index f001bdc6d371..5e0f02b47dd6 100644 --- a/rerun_cpp/src/rerun/archetypes.hpp +++ b/rerun_cpp/src/rerun/archetypes.hpp @@ -6,6 +6,7 @@ #include "archetypes/arrows2d.hpp" #include "archetypes/arrows3d.hpp" #include "archetypes/asset3d.hpp" +#include "archetypes/axes3d.hpp" #include "archetypes/bar_chart.hpp" #include "archetypes/boxes2d.hpp" #include "archetypes/boxes3d.hpp" diff --git a/rerun_cpp/src/rerun/archetypes/.gitattributes b/rerun_cpp/src/rerun/archetypes/.gitattributes index 395fe533c718..549d638dca58 100644 --- a/rerun_cpp/src/rerun/archetypes/.gitattributes +++ b/rerun_cpp/src/rerun/archetypes/.gitattributes @@ -9,6 +9,8 @@ arrows3d.cpp linguist-generated=true arrows3d.hpp linguist-generated=true asset3d.cpp linguist-generated=true asset3d.hpp linguist-generated=true +axes3d.cpp linguist-generated=true +axes3d.hpp linguist-generated=true bar_chart.cpp linguist-generated=true bar_chart.hpp linguist-generated=true boxes2d.cpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/archetypes/axes3d.cpp b/rerun_cpp/src/rerun/archetypes/axes3d.cpp new file mode 100644 index 000000000000..335c211eac74 --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/axes3d.cpp @@ -0,0 +1,33 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/re_types/definitions/rerun/archetypes/axes3d.fbs". + +#include "axes3d.hpp" + +#include "../collection_adapter_builtins.hpp" + +namespace rerun::archetypes {} + +namespace rerun { + + Result> AsComponents::serialize( + const archetypes::Axes3D& archetype + ) { + using namespace archetypes; + std::vector cells; + cells.reserve(2); + + if (archetype.length.has_value()) { + auto result = DataCell::from_loggable(archetype.length.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + { + auto indicator = Axes3D::IndicatorComponent(); + auto result = DataCell::from_loggable(indicator); + RR_RETURN_NOT_OK(result.error); + cells.emplace_back(std::move(result.value)); + } + + return cells; + } +} // namespace rerun diff --git a/rerun_cpp/src/rerun/archetypes/axes3d.hpp b/rerun_cpp/src/rerun/archetypes/axes3d.hpp new file mode 100644 index 000000000000..521f223bd9fd --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/axes3d.hpp @@ -0,0 +1,92 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/re_types/definitions/rerun/archetypes/axes3d.fbs". + +#pragma once + +#include "../collection.hpp" +#include "../compiler_utils.hpp" +#include "../components/axis_length.hpp" +#include "../data_cell.hpp" +#include "../indicator_component.hpp" +#include "../result.hpp" + +#include +#include +#include +#include + +namespace rerun::archetypes { + /// **Archetype**: This archetype shows a set of orthogonal coordinate axes such as for representing a transform. + /// + /// See `rerun::archetypes::Transform3D` + /// + /// ## Example + /// + /// ### Transform with axes + /// ![image](https://static.rerun.io/transform3d_axes/35cd6a68cce0cd582231984be4e2628d1627540b/full.png) + /// + /// ```cpp + /// #include + /// + /// int main() { + /// const auto rec = rerun::RecordingStream("rerun_example_transform3d_axes"); + /// rec.spawn().exit_on_failure(); + /// + /// auto base_axes = rerun::Axes3D().with_length(1.0); + /// auto other_axes = rerun::Axes3D().with_length(0.5); + /// + /// rec.log_static("base", base_axes); + /// rec.log_static("base/rotated", other_axes); + /// rec.log_static("base/rotated/translated", other_axes); + /// + /// for (int deg = 0; deg <360; deg++) { + /// rec.set_time_sequence("step", deg); + /// + /// rec.log( + /// "base/rotated", + /// rerun::Transform3D(rerun::RotationAxisAngle( + /// {1.0f, 1.0f, 1.0f}, + /// rerun::Angle::degrees(static_cast(deg)) + /// )) + /// ); + /// + /// rec.log("base/rotated/translated", rerun::Transform3D({2.0f, 0.0f, 0.0f})); + /// } + /// } + /// ``` + struct Axes3D { + /// Length of the 3 axes. + std::optional length; + + public: + static constexpr const char IndicatorComponentName[] = "rerun.components.Axes3DIndicator"; + + /// Indicator component, used to identify the archetype when converting to a list of components. + using IndicatorComponent = rerun::components::IndicatorComponent; + + public: + Axes3D() = default; + Axes3D(Axes3D&& other) = default; + + /// Length of the 3 axes. + Axes3D with_length(rerun::components::AxisLength _length) && { + length = std::move(_length); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + }; + +} // namespace rerun::archetypes + +namespace rerun { + /// \private + template + struct AsComponents; + + /// \private + template <> + struct AsComponents { + /// Serialize all set component batches. + static Result> serialize(const archetypes::Axes3D& archetype); + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/c/rerun.h b/rerun_cpp/src/rerun/c/rerun.h index 992799ff024c..06e6303c50cd 100644 --- a/rerun_cpp/src/rerun/c/rerun.h +++ b/rerun_cpp/src/rerun/c/rerun.h @@ -266,7 +266,7 @@ typedef struct rr_error { /// /// This should match the string returned by `rr_version_string`. /// If not, the SDK's binary and the C header are out of sync. -#define RERUN_SDK_HEADER_VERSION "0.17.0-alpha.2" +#define RERUN_SDK_HEADER_VERSION "0.17.0-alpha.3" /// Returns a human-readable version string of the Rerun C SDK. /// diff --git a/rerun_cpp/src/rerun/components.hpp b/rerun_cpp/src/rerun/components.hpp index d56a35af8ca7..cc69ede2b29a 100644 --- a/rerun_cpp/src/rerun/components.hpp +++ b/rerun_cpp/src/rerun/components.hpp @@ -3,6 +3,7 @@ #pragma once #include "components/annotation_context.hpp" +#include "components/axis_length.hpp" #include "components/blob.hpp" #include "components/class_id.hpp" #include "components/clear_is_recursive.hpp" diff --git a/rerun_cpp/src/rerun/components/.gitattributes b/rerun_cpp/src/rerun/components/.gitattributes index d4f61adb5784..9b7176bee6fd 100644 --- a/rerun_cpp/src/rerun/components/.gitattributes +++ b/rerun_cpp/src/rerun/components/.gitattributes @@ -3,6 +3,7 @@ .gitattributes linguist-generated=true annotation_context.cpp linguist-generated=true annotation_context.hpp linguist-generated=true +axis_length.hpp linguist-generated=true blob.cpp linguist-generated=true blob.hpp linguist-generated=true class_id.hpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/components/axis_length.hpp b/rerun_cpp/src/rerun/components/axis_length.hpp new file mode 100644 index 000000000000..558f93188caa --- /dev/null +++ b/rerun_cpp/src/rerun/components/axis_length.hpp @@ -0,0 +1,61 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/re_types/definitions/rerun/components/axis_length.fbs". + +#pragma once + +#include "../datatypes/float32.hpp" +#include "../result.hpp" + +#include +#include + +namespace rerun::components { + /// **Component**: The length of an axis in local units of the space. + struct AxisLength { + rerun::datatypes::Float32 length; + + public: + AxisLength() = default; + + AxisLength(rerun::datatypes::Float32 length_) : length(length_) {} + + AxisLength& operator=(rerun::datatypes::Float32 length_) { + length = length_; + return *this; + } + + AxisLength(float value_) : length(value_) {} + + AxisLength& operator=(float value_) { + length = value_; + return *this; + } + + /// Cast to the underlying Float32 datatype + operator rerun::datatypes::Float32() const { + return length; + } + }; +} // namespace rerun::components + +namespace rerun { + static_assert(sizeof(rerun::datatypes::Float32) == sizeof(components::AxisLength)); + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.components.AxisLength"; + + /// Returns the arrow data type this type corresponds to. + static const std::shared_ptr& arrow_datatype() { + return Loggable::arrow_datatype(); + } + + /// Serializes an array of `rerun::components::AxisLength` into an arrow array. + static Result> to_arrow( + const components::AxisLength* instances, size_t num_instances + ) { + return Loggable::to_arrow(&instances->length, num_instances); + } + }; +} // namespace rerun diff --git a/rerun_py/docs/gen_common_index.py b/rerun_py/docs/gen_common_index.py index 819f8093cea2..1773519b4ebb 100755 --- a/rerun_py/docs/gen_common_index.py +++ b/rerun_py/docs/gen_common_index.py @@ -181,6 +181,7 @@ class Section: "archetypes.Arrows3D", "archetypes.Arrows2D", "archetypes.Asset3D", + "archetypes.Axes3D", "archetypes.Boxes2D", "archetypes.Boxes3D", "archetypes.LineStrips2D", diff --git a/rerun_py/rerun_sdk/rerun/__init__.py b/rerun_py/rerun_sdk/rerun/__init__.py index eb8ceb495603..088c6bb7e0ab 100644 --- a/rerun_py/rerun_sdk/rerun/__init__.py +++ b/rerun_py/rerun_sdk/rerun/__init__.py @@ -41,6 +41,7 @@ Arrows2D as Arrows2D, Arrows3D as Arrows3D, Asset3D as Asset3D, + Axes3D as Axes3D, BarChart as BarChart, Boxes2D as Boxes2D, Boxes3D as Boxes3D, diff --git a/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes b/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes index d249edf2df81..fe89b89524b3 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes @@ -6,6 +6,7 @@ annotation_context.py linguist-generated=true arrows2d.py linguist-generated=true arrows3d.py linguist-generated=true asset3d.py linguist-generated=true +axes3d.py linguist-generated=true bar_chart.py linguist-generated=true boxes2d.py linguist-generated=true boxes3d.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/archetypes/__init__.py b/rerun_py/rerun_sdk/rerun/archetypes/__init__.py index 6b6b4e518c5f..147860ee7670 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/__init__.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/__init__.py @@ -6,6 +6,7 @@ from .arrows2d import Arrows2D from .arrows3d import Arrows3D from .asset3d import Asset3D +from .axes3d import Axes3D from .bar_chart import BarChart from .boxes2d import Boxes2D from .boxes3d import Boxes3D @@ -34,6 +35,7 @@ "Arrows2D", "Arrows3D", "Asset3D", + "Axes3D", "BarChart", "Boxes2D", "Boxes3D", diff --git a/rerun_py/rerun_sdk/rerun/archetypes/axes3d.py b/rerun_py/rerun_sdk/rerun/archetypes/axes3d.py new file mode 100644 index 000000000000..e4c1d13ffb5a --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/archetypes/axes3d.py @@ -0,0 +1,111 @@ +# DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/python/mod.rs +# Based on "crates/re_types/definitions/rerun/archetypes/axes3d.fbs". + +# You can extend this class by creating a "Axes3DExt" class in "axes3d_ext.py". + +from __future__ import annotations + +from typing import Any + +from attrs import define, field + +from .. import components, datatypes +from .._baseclasses import Archetype +from ..error_utils import catch_and_log_exceptions + +__all__ = ["Axes3D"] + + +@define(str=False, repr=False, init=False) +class Axes3D(Archetype): + """ + **Archetype**: This archetype shows a set of orthogonal coordinate axes such as for representing a transform. + + See [`Transform3D`][rerun.archetypes.Transform3D] + + Example + ------- + ### Transform with axes: + ```python + import rerun as rr + + rr.init("rerun_example_transform3d_axes", spawn=True) + + # Make the base axes longer + # Log all axes markers as static first + rr.log("base", rr.Axes3D(length=1), static=True) + rr.log("base/rotated", rr.Axes3D(length=0.5), static=True) + rr.log("base/rotated/translated", rr.Axes3D(length=0.5), static=True) + + # Now sweep out a rotation relative to the base + for deg in range(360): + rr.set_time_sequence("step", deg) + rr.log( + "base/rotated", + rr.Transform3D( + rotation=rr.RotationAxisAngle( + axis=[1.0, 1.0, 1.0], + degrees=deg, + ) + ), + ) + rr.log( + "base/rotated/translated", + rr.Transform3D( + translation=[2.0, 0, 0], + ), + ) + ``` +
+ + + + + + + +
+ + """ + + def __init__(self: Any, *, length: datatypes.Float32Like | None = None): + """ + Create a new instance of the Axes3D archetype. + + Parameters + ---------- + length: + Length of the 3 axes. + + """ + + # You can define your own __init__ function as a member of Axes3DExt in axes3d_ext.py + with catch_and_log_exceptions(context=self.__class__.__name__): + self.__attrs_init__(length=length) + return + self.__attrs_clear__() + + def __attrs_clear__(self) -> None: + """Convenience method for calling `__attrs_init__` with all `None`s.""" + self.__attrs_init__( + length=None, # type: ignore[arg-type] + ) + + @classmethod + def _clear(cls) -> Axes3D: + """Produce an empty Axes3D, bypassing `__init__`.""" + inst = cls.__new__(cls) + inst.__attrs_clear__() + return inst + + length: components.AxisLengthBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.AxisLengthBatch._optional, # type: ignore[misc] + ) + # Length of the 3 axes. + # + # (Docstring intentionally commented out to hide this field from the docs) + + __str__ = Archetype.__str__ + __repr__ = Archetype.__repr__ # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/components/.gitattributes b/rerun_py/rerun_sdk/rerun/components/.gitattributes index 370009cd9b0b..ca3b0f59b8d8 100644 --- a/rerun_py/rerun_sdk/rerun/components/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/components/.gitattributes @@ -3,6 +3,7 @@ .gitattributes linguist-generated=true __init__.py linguist-generated=true annotation_context.py linguist-generated=true +axis_length.py linguist-generated=true blob.py linguist-generated=true class_id.py linguist-generated=true clear_is_recursive.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/components/__init__.py b/rerun_py/rerun_sdk/rerun/components/__init__.py index 1788b743be0a..e36042969005 100644 --- a/rerun_py/rerun_sdk/rerun/components/__init__.py +++ b/rerun_py/rerun_sdk/rerun/components/__init__.py @@ -9,6 +9,7 @@ AnnotationContextLike, AnnotationContextType, ) +from .axis_length import AxisLength, AxisLengthBatch, AxisLengthType from .blob import Blob, BlobArrayLike, BlobBatch, BlobLike, BlobType from .class_id import ClassId, ClassIdBatch, ClassIdType from .clear_is_recursive import ( @@ -78,6 +79,9 @@ "AnnotationContextBatch", "AnnotationContextLike", "AnnotationContextType", + "AxisLength", + "AxisLengthBatch", + "AxisLengthType", "Blob", "BlobArrayLike", "BlobBatch", diff --git a/rerun_py/rerun_sdk/rerun/components/axis_length.py b/rerun_py/rerun_sdk/rerun/components/axis_length.py new file mode 100644 index 000000000000..9e652f949aff --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/axis_length.py @@ -0,0 +1,28 @@ +# DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/python/mod.rs +# Based on "crates/re_types/definitions/rerun/components/axis_length.fbs". + +# You can extend this class by creating a "AxisLengthExt" class in "axis_length_ext.py". + +from __future__ import annotations + +from .. import datatypes +from .._baseclasses import ComponentBatchMixin + +__all__ = ["AxisLength", "AxisLengthBatch", "AxisLengthType"] + + +class AxisLength(datatypes.Float32): + """**Component**: The length of an axis in local units of the space.""" + + # You can define your own __init__ function as a member of AxisLengthExt in axis_length_ext.py + + # Note: there are no fields here because AxisLength delegates to datatypes.Float32 + pass + + +class AxisLengthType(datatypes.Float32Type): + _TYPE_NAME: str = "rerun.components.AxisLength" + + +class AxisLengthBatch(datatypes.Float32Batch, ComponentBatchMixin): + _ARROW_TYPE = AxisLengthType() diff --git a/tests/python/release_checklist/check_all_components_ui.py b/tests/python/release_checklist/check_all_components_ui.py index 353d1c8fdf91..887568a125a2 100644 --- a/tests/python/release_checklist/check_all_components_ui.py +++ b/tests/python/release_checklist/check_all_components_ui.py @@ -89,6 +89,7 @@ def alternatives(self) -> list[Any] | None: ), ) ]), + "AxisLengthBatch": TestCase(batch=[100.0, 200.0, 300.0]), "BlobBatch": TestCase( alternatives=[ b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09",