From 29c1f83b6be831b88bf3fafc2c2fdfea913437bc Mon Sep 17 00:00:00 2001 From: Jeremy Leibs Date: Mon, 9 Dec 2024 19:31:32 +0100 Subject: [PATCH] Make the near clipping plane editable in 2D views (#8348) ### What This adjusts the value to now be in physical scene units. I.e. for most 3d scenes in meters, the clip plane will be 10cm, which is a very reasonable default. Additionally, this gives users an escape hatch for scenes where the default clipping plane causes their data to not be visible. Test script: ```python import rerun as rr import rerun.blueprint as rrb import numpy as np rr.init("rerun_example_pinhole_perspective", spawn=True) img = np.zeros((300, 300, 3), dtype=np.uint8) cam_proj = [152.34006, 0, 175.5, 0, 152.34006, 175.5, 0, 0, 1] rr.log( "world/cam", rr.Pinhole( image_from_camera=cam_proj, width=300, height=300, image_plane_distance=0.1 ), ) rr.log("world/cam", rr.Image(img)) rr.log( "world/points", rr.Points3D([(0.0, 0.5, 0.1), (0.1, 0.1, 0.3), (-0.1, -0.1, 0.5)], radii=0.025), ) rr.log_file_from_path("examples/assets/example.gltf") rr.log("examples/assets", rr.Transform3D(translation=[0, 0, 0.4], scale=0.05)) rr.send_blueprint( rrb.Horizontal( rrb.Spatial3DView(), rrb.Spatial2DView( origin="world/cam", contents="/**", visual_bounds=rrb.VisualBounds2D( near_clip_plane=0.01, ), ), ) ) ``` ### New issues: - https://github.com/rerun-io/rerun/issues/8373 --- .../blueprint/archetypes/visual_bounds2d.fbs | 5 + .../rerun/blueprint/components.fbs | 1 + .../blueprint/components/near_clip_plane.fbs | 14 +++ .../blueprint/archetypes/visual_bounds2d.rs | 63 ++++++++-- .../src/blueprint/components/.gitattributes | 1 + .../re_types/src/blueprint/components/mod.rs | 3 + .../blueprint/components/near_clip_plane.rs | 116 ++++++++++++++++++ .../components/near_clip_plane_ext.rs | 11 ++ crates/viewer/re_component_ui/src/lib.rs | 4 +- .../viewer/re_space_view_spatial/src/ui_2d.rs | 36 ++++-- .../src/blueprint/validation_gen/mod.rs | 2 + crates/viewer/re_viewer/src/reflection/mod.rs | 14 ++- .../reference/migration/migration-0-21.md | 27 ++++ .../reference/types/views/graph_view.md | 3 + .../reference/types/views/spatial2d_view.md | 3 + .../blueprint/archetypes/visual_bounds2d.cpp | 14 ++- .../blueprint/archetypes/visual_bounds2d.hpp | 13 +- rerun_cpp/src/rerun/blueprint/components.hpp | 1 + .../rerun/blueprint/components/.gitattributes | 1 + .../blueprint/components/near_clip_plane.hpp | 79 ++++++++++++ .../blueprint/archetypes/visual_bounds2d.py | 11 ++ .../archetypes/visual_bounds2d_ext.py | 19 ++- .../rerun/blueprint/components/.gitattributes | 1 + .../rerun/blueprint/components/__init__.py | 3 + .../blueprint/components/near_clip_plane.py | 33 +++++ 25 files changed, 449 insertions(+), 29 deletions(-) create mode 100644 crates/store/re_types/definitions/rerun/blueprint/components/near_clip_plane.fbs create mode 100644 crates/store/re_types/src/blueprint/components/near_clip_plane.rs create mode 100644 crates/store/re_types/src/blueprint/components/near_clip_plane_ext.rs create mode 100644 docs/content/reference/migration/migration-0-21.md create mode 100644 rerun_cpp/src/rerun/blueprint/components/near_clip_plane.hpp create mode 100644 rerun_py/rerun_sdk/rerun/blueprint/components/near_clip_plane.py diff --git a/crates/store/re_types/definitions/rerun/blueprint/archetypes/visual_bounds2d.fbs b/crates/store/re_types/definitions/rerun/blueprint/archetypes/visual_bounds2d.fbs index 0e9dc2719a57..21b8599e5383 100644 --- a/crates/store/re_types/definitions/rerun/blueprint/archetypes/visual_bounds2d.fbs +++ b/crates/store/re_types/definitions/rerun/blueprint/archetypes/visual_bounds2d.fbs @@ -16,4 +16,9 @@ table VisualBounds2D ( /// /// Use this to control pan & zoom of the view. range: rerun.blueprint.components.VisualBounds2D ("attr.rerun.component_required", order: 1000); + + /// Controls the distance to the near clip plane in 3D scene units. + /// + /// Content closer than this distance will not be visible. + near_clip_plane: rerun.blueprint.components.NearClipPlane ("attr.rerun.component_optional", order: 2000); } diff --git a/crates/store/re_types/definitions/rerun/blueprint/components.fbs b/crates/store/re_types/definitions/rerun/blueprint/components.fbs index 1ee15c8acbe8..4f216593296d 100644 --- a/crates/store/re_types/definitions/rerun/blueprint/components.fbs +++ b/crates/store/re_types/definitions/rerun/blueprint/components.fbs @@ -17,6 +17,7 @@ include "./components/included_content.fbs"; include "./components/interactive.fbs"; include "./components/lock_range_during_zoom.fbs"; include "./components/map_provider.fbs"; +include "./components/near_clip_plane.fbs"; include "./components/panel_state.fbs"; include "./components/query_expression.fbs"; include "./components/root_container.fbs"; diff --git a/crates/store/re_types/definitions/rerun/blueprint/components/near_clip_plane.fbs b/crates/store/re_types/definitions/rerun/blueprint/components/near_clip_plane.fbs new file mode 100644 index 000000000000..200fc64ef33b --- /dev/null +++ b/crates/store/re_types/definitions/rerun/blueprint/components/near_clip_plane.fbs @@ -0,0 +1,14 @@ +namespace rerun.blueprint.components; + +// --- + +/// Distance to the near clip plane used for `Spatial2DView`. +struct NearClipPlane ( + "attr.rerun.scope": "blueprint", + "attr.rust.derive": "Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable", + "attr.rust.repr": "transparent", + "attr.docs.unreleased" +) { + /// Distance to the near clip plane in 3D scene units. + near_clip_plane: rerun.datatypes.Float32 (order: 100); +} diff --git a/crates/store/re_types/src/blueprint/archetypes/visual_bounds2d.rs b/crates/store/re_types/src/blueprint/archetypes/visual_bounds2d.rs index 3de813191ef9..7cf84881ea80 100644 --- a/crates/store/re_types/src/blueprint/archetypes/visual_bounds2d.rs +++ b/crates/store/re_types/src/blueprint/archetypes/visual_bounds2d.rs @@ -31,6 +31,11 @@ pub struct VisualBounds2D { /// /// Use this to control pan & zoom of the view. pub range: crate::blueprint::components::VisualBounds2D, + + /// Controls the distance to the near clip plane in 3D scene units. + /// + /// Content closer than this distance will not be visible. + pub near_clip_plane: crate::blueprint::components::NearClipPlane, } static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentDescriptor; 1usize]> = @@ -51,10 +56,16 @@ static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentDescriptor; 1usiz }] }); -static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentDescriptor; 0usize]> = - once_cell::sync::Lazy::new(|| []); +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentDescriptor; 1usize]> = + once_cell::sync::Lazy::new(|| { + [ComponentDescriptor { + archetype_name: Some("rerun.blueprint.archetypes.VisualBounds2D".into()), + component_name: "rerun.blueprint.components.NearClipPlane".into(), + archetype_field_name: Some("near_clip_plane".into()), + }] + }); -static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentDescriptor; 2usize]> = +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentDescriptor; 3usize]> = once_cell::sync::Lazy::new(|| { [ ComponentDescriptor { @@ -67,12 +78,17 @@ static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentDescriptor; 2usize]> = component_name: "VisualBounds2DIndicator".into(), archetype_field_name: None, }, + ComponentDescriptor { + archetype_name: Some("rerun.blueprint.archetypes.VisualBounds2D".into()), + component_name: "rerun.blueprint.components.NearClipPlane".into(), + archetype_field_name: Some("near_clip_plane".into()), + }, ] }); impl VisualBounds2D { - /// The total number of components in the archetype: 1 required, 1 recommended, 0 optional - pub const NUM_COMPONENTS: usize = 2usize; + /// The total number of components in the archetype: 1 required, 1 recommended, 1 optional + pub const NUM_COMPONENTS: usize = 3usize; } /// Indicator component for the [`VisualBounds2D`] [`::re_types_core::Archetype`] @@ -140,7 +156,23 @@ impl ::re_types_core::Archetype for VisualBounds2D { .ok_or_else(DeserializationError::missing_data) .with_context("rerun.blueprint.archetypes.VisualBounds2D#range")? }; - Ok(Self { range }) + let near_clip_plane = { + let array = arrays_by_name + .get("rerun.blueprint.components.NearClipPlane") + .ok_or_else(DeserializationError::missing_data) + .with_context("rerun.blueprint.archetypes.VisualBounds2D#near_clip_plane")?; + ::from_arrow2_opt(&**array) + .with_context("rerun.blueprint.archetypes.VisualBounds2D#near_clip_plane")? + .into_iter() + .next() + .flatten() + .ok_or_else(DeserializationError::missing_data) + .with_context("rerun.blueprint.archetypes.VisualBounds2D#near_clip_plane")? + }; + Ok(Self { + range, + near_clip_plane, + }) } } @@ -160,6 +192,16 @@ impl ::re_types_core::AsComponents for VisualBounds2D { }), } }), + (Some(&self.near_clip_plane as &dyn ComponentBatch)).map(|batch| { + ::re_types_core::ComponentBatchCowWithDescriptor { + batch: batch.into(), + descriptor_override: Some(ComponentDescriptor { + archetype_name: Some("rerun.blueprint.archetypes.VisualBounds2D".into()), + archetype_field_name: Some(("near_clip_plane").into()), + component_name: ("rerun.blueprint.components.NearClipPlane").into(), + }), + } + }), ] .into_iter() .flatten() @@ -172,9 +214,13 @@ impl ::re_types_core::ArchetypeReflectionMarker for VisualBounds2D {} impl VisualBounds2D { /// Create a new `VisualBounds2D`. #[inline] - pub fn new(range: impl Into) -> Self { + pub fn new( + range: impl Into, + near_clip_plane: impl Into, + ) -> Self { Self { range: range.into(), + near_clip_plane: near_clip_plane.into(), } } } @@ -182,11 +228,12 @@ impl VisualBounds2D { impl ::re_types_core::SizeBytes for VisualBounds2D { #[inline] fn heap_size_bytes(&self) -> u64 { - self.range.heap_size_bytes() + self.range.heap_size_bytes() + self.near_clip_plane.heap_size_bytes() } #[inline] fn is_pod() -> bool { ::is_pod() + && ::is_pod() } } diff --git a/crates/store/re_types/src/blueprint/components/.gitattributes b/crates/store/re_types/src/blueprint/components/.gitattributes index 238359328a19..65eeb1377804 100644 --- a/crates/store/re_types/src/blueprint/components/.gitattributes +++ b/crates/store/re_types/src/blueprint/components/.gitattributes @@ -15,6 +15,7 @@ interactive.rs linguist-generated=true lock_range_during_zoom.rs linguist-generated=true map_provider.rs linguist-generated=true mod.rs linguist-generated=true +near_clip_plane.rs linguist-generated=true panel_state.rs linguist-generated=true query_expression.rs linguist-generated=true row_share.rs linguist-generated=true diff --git a/crates/store/re_types/src/blueprint/components/mod.rs b/crates/store/re_types/src/blueprint/components/mod.rs index ca655d4e56b3..15647fbf1138 100644 --- a/crates/store/re_types/src/blueprint/components/mod.rs +++ b/crates/store/re_types/src/blueprint/components/mod.rs @@ -19,6 +19,8 @@ mod interactive; mod interactive_ext; mod lock_range_during_zoom; mod map_provider; +mod near_clip_plane; +mod near_clip_plane_ext; mod panel_state; mod panel_state_ext; mod query_expression; @@ -55,6 +57,7 @@ pub use self::included_content::IncludedContent; pub use self::interactive::Interactive; pub use self::lock_range_during_zoom::LockRangeDuringZoom; pub use self::map_provider::MapProvider; +pub use self::near_clip_plane::NearClipPlane; pub use self::panel_state::PanelState; pub use self::query_expression::QueryExpression; pub use self::row_share::RowShare; diff --git a/crates/store/re_types/src/blueprint/components/near_clip_plane.rs b/crates/store/re_types/src/blueprint/components/near_clip_plane.rs new file mode 100644 index 000000000000..b9caf19b944d --- /dev/null +++ b/crates/store/re_types/src/blueprint/components/near_clip_plane.rs @@ -0,0 +1,116 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/rust/api.rs +// Based on "crates/store/re_types/definitions/rerun/blueprint/components/near_clip_plane.fbs". + +#![allow(unused_imports)] +#![allow(unused_parens)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::cloned_instead_of_copied)] +#![allow(clippy::map_flatten)] +#![allow(clippy::needless_question_mark)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::too_many_lines)] + +use ::re_types_core::external::arrow2; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, ComponentBatchCowWithDescriptor}; +use ::re_types_core::{ComponentDescriptor, ComponentName}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Component**: Distance to the near clip plane used for `Spatial2DView`. +#[derive(Clone, Debug, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(transparent)] +pub struct NearClipPlane( + /// Distance to the near clip plane in 3D scene units. + pub crate::datatypes::Float32, +); + +impl ::re_types_core::Component for NearClipPlane { + #[inline] + fn descriptor() -> ComponentDescriptor { + ComponentDescriptor::new("rerun.blueprint.components.NearClipPlane") + } +} + +::re_types_core::macros::impl_into_cow!(NearClipPlane); + +impl ::re_types_core::Loggable for NearClipPlane { + #[inline] + fn arrow_datatype() -> arrow::datatypes::DataType { + crate::datatypes::Float32::arrow_datatype() + } + + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult + where + Self: Clone + 'a, + { + crate::datatypes::Float32::to_arrow_opt(data.into_iter().map(|datum| { + datum.map(|datum| match datum.into() { + ::std::borrow::Cow::Borrowed(datum) => ::std::borrow::Cow::Borrowed(&datum.0), + ::std::borrow::Cow::Owned(datum) => ::std::borrow::Cow::Owned(datum.0), + }) + })) + } + + fn from_arrow2_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + crate::datatypes::Float32::from_arrow2_opt(arrow_data) + .map(|v| v.into_iter().map(|v| v.map(Self)).collect()) + } + + #[inline] + fn from_arrow2(arrow_data: &dyn arrow2::array::Array) -> DeserializationResult> + where + Self: Sized, + { + crate::datatypes::Float32::from_arrow2(arrow_data).map(bytemuck::cast_vec) + } +} + +impl> From for NearClipPlane { + fn from(v: T) -> Self { + Self(v.into()) + } +} + +impl std::borrow::Borrow for NearClipPlane { + #[inline] + fn borrow(&self) -> &crate::datatypes::Float32 { + &self.0 + } +} + +impl std::ops::Deref for NearClipPlane { + type Target = crate::datatypes::Float32; + + #[inline] + fn deref(&self) -> &crate::datatypes::Float32 { + &self.0 + } +} + +impl std::ops::DerefMut for NearClipPlane { + #[inline] + fn deref_mut(&mut self) -> &mut crate::datatypes::Float32 { + &mut self.0 + } +} + +impl ::re_types_core::SizeBytes for NearClipPlane { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.0.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + ::is_pod() + } +} diff --git a/crates/store/re_types/src/blueprint/components/near_clip_plane_ext.rs b/crates/store/re_types/src/blueprint/components/near_clip_plane_ext.rs new file mode 100644 index 000000000000..89f46e4246e6 --- /dev/null +++ b/crates/store/re_types/src/blueprint/components/near_clip_plane_ext.rs @@ -0,0 +1,11 @@ +use re_types_core::datatypes::Float32; + +use super::NearClipPlane; + +impl Default for NearClipPlane { + #[inline] + fn default() -> Self { + // Default near clip plane to reasonable distance for common cameras + Self(Float32(0.1)) + } +} diff --git a/crates/viewer/re_component_ui/src/lib.rs b/crates/viewer/re_component_ui/src/lib.rs index 7460795c9fc5..abe649c8b3a7 100644 --- a/crates/viewer/re_component_ui/src/lib.rs +++ b/crates/viewer/re_component_ui/src/lib.rs @@ -35,7 +35,8 @@ use datatype_uis::{ use re_types::{ blueprint::components::{ - BackgroundKind, Corner2D, GridSpacing, LockRangeDuringZoom, MapProvider, ViewFit, Visible, + BackgroundKind, Corner2D, GridSpacing, LockRangeDuringZoom, MapProvider, NearClipPlane, + ViewFit, Visible, }, components::{ AggregationPolicy, AlbedoFactor, AxisLength, Color, DepthMeter, DrawOrder, FillMode, @@ -76,6 +77,7 @@ pub fn create_component_ui_registry() -> re_viewer_context::ComponentUiRegistry registry.add_singleline_edit_or_view::(edit_f32_zero_to_max); registry.add_singleline_edit_or_view::(edit_ui_points); registry.add_singleline_edit_or_view::(edit_ui_points); + registry.add_singleline_edit_or_view::(edit_f32_zero_to_max); // float min-max components: registry.add_singleline_edit_or_view::(edit_f32_min_to_max_float); diff --git a/crates/viewer/re_space_view_spatial/src/ui_2d.rs b/crates/viewer/re_space_view_spatial/src/ui_2d.rs index 54d3a64c89b3..6f5ae55cfd15 100644 --- a/crates/viewer/re_space_view_spatial/src/ui_2d.rs +++ b/crates/viewer/re_space_view_spatial/src/ui_2d.rs @@ -15,8 +15,7 @@ use re_types::{ }; use re_ui::{ContextExt as _, ModifiersMarkdown, MouseButtonMarkdown}; use re_viewer_context::{ - gpu_bridge, ItemSpaceContext, SpaceViewId, SpaceViewSystemExecutionError, ViewQuery, - ViewerContext, + gpu_bridge, ItemSpaceContext, SpaceViewSystemExecutionError, ViewQuery, ViewerContext, }; use re_viewport_blueprint::ViewProperty; @@ -31,16 +30,11 @@ use crate::{ /// Pan and zoom, and return the current transform. fn ui_from_scene( ctx: &ViewerContext<'_>, - view_id: SpaceViewId, response: &egui::Response, view_class: &SpatialSpaceView2D, view_state: &mut SpatialSpaceViewState, + bounds_property: &ViewProperty, ) -> RectTransform { - let bounds_property = ViewProperty::from_archetype::( - ctx.blueprint_db(), - ctx.blueprint_query, - view_id, - ); let bounds: blueprint_components::VisualBounds2D = bounds_property .component_or_fallback(ctx, view_class, view_state) .ok_or_log_error() @@ -169,20 +163,35 @@ impl SpatialSpaceView2D { let (response, painter) = ui.allocate_painter(ui.available_size(), egui::Sense::click_and_drag()); + let bounds_property = ViewProperty::from_archetype::( + ctx.blueprint_db(), + ctx.blueprint_query, + query.space_view_id, + ); + // Convert ui coordinates to/from scene coordinates. - let ui_from_scene = ui_from_scene(ctx, query.space_view_id, &response, self, state); + let ui_from_scene = ui_from_scene(ctx, &response, self, state, &bounds_property); let scene_from_ui = ui_from_scene.inverse(); + let near_clip_plane: blueprint_components::NearClipPlane = bounds_property + .component_or_fallback(ctx, self, state) + .ok_or_log_error() + .unwrap_or_default(); + // TODO(andreas): Use the same eye & transformations as in `setup_target_config`. let eye = Eye { world_from_rub_view: IsoTransform::IDENTITY, fov_y: None, }; + // Don't let clipping plane become zero + let near_clip_plane = f32::max(f32::MIN_POSITIVE, *near_clip_plane.0); + let scene_bounds = *scene_from_ui.to(); let Ok(target_config) = setup_target_config( &painter, scene_bounds, + near_clip_plane, &query.space_origin.to_string(), query.highlights.any_outlines(), &state.pinhole_at_origin, @@ -287,6 +296,7 @@ impl SpatialSpaceView2D { fn setup_target_config( egui_painter: &egui::Painter, scene_bounds: Rect, + near_clip_plane: f32, space_name: &str, any_outlines: bool, scene_pinhole: &Option, @@ -348,17 +358,17 @@ fn setup_target_config( } let pinhole_rect = Rect::from_min_size(Pos2::ZERO, egui::vec2(resolution.x, resolution.y)); + let focal_length = pinhole.focal_length_in_pixels(); + let focal_length = 2.0 / (1.0 / focal_length.x() + 1.0 / focal_length.y()); // harmonic mean (lack of anamorphic support) + let projection_from_view = re_renderer::view_builder::Projection::Perspective { vertical_fov: pinhole.fov_y().unwrap_or(Eye::DEFAULT_FOV_Y), - near_plane_distance: 0.1, + near_plane_distance: near_clip_plane * focal_length / 500.0, // TODO(#8373): The need to scale this by 500 is quite hacky. aspect_ratio: pinhole .aspect_ratio() .unwrap_or(scene_bounds_size.x / scene_bounds_size.y), // only happens if the pinhole lacks resolution }; - let focal_length = pinhole.focal_length_in_pixels(); - let focal_length = 2.0 / (1.0 / focal_length.x() + 1.0 / focal_length.y()); // harmonic mean (lack of anamorphic support) - // Position the camera looking straight at the principal point: let view_from_world = re_math::IsoTransform::look_at_rh( pinhole.principal_point().extend(-focal_length), diff --git a/crates/viewer/re_viewer/src/blueprint/validation_gen/mod.rs b/crates/viewer/re_viewer/src/blueprint/validation_gen/mod.rs index 85bc964e87a8..e2ffaf50eb40 100644 --- a/crates/viewer/re_viewer/src/blueprint/validation_gen/mod.rs +++ b/crates/viewer/re_viewer/src/blueprint/validation_gen/mod.rs @@ -14,6 +14,7 @@ pub use re_types::blueprint::components::IncludedContent; pub use re_types::blueprint::components::Interactive; pub use re_types::blueprint::components::LockRangeDuringZoom; pub use re_types::blueprint::components::MapProvider; +pub use re_types::blueprint::components::NearClipPlane; pub use re_types::blueprint::components::PanelState; pub use re_types::blueprint::components::QueryExpression; pub use re_types::blueprint::components::RowShare; @@ -57,6 +58,7 @@ pub fn is_valid_blueprint(blueprint: &EntityDb) -> bool { && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) + && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index a32288da7a61..2a4902df555e 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -172,6 +172,14 @@ fn generate_component_reflection() -> Result::name(), + ComponentReflection { + docstring_md: "Distance to the near clip plane used for `Spatial2DView`.", + custom_placeholder: Some(NearClipPlane::default().to_arrow2()?), + datatype: NearClipPlane::arrow2_datatype(), + }, + ), ( ::name(), ComponentReflection { @@ -2202,7 +2210,11 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { "rerun.blueprint.components.VisualBounds2D".into(), display_name : "Range", docstring_md : "Controls the visible range of a 2D view.\n\nUse this to control pan & zoom of the view.", - is_required : true, }, + is_required : true, }, ArchetypeFieldReflection { component_name : + "rerun.blueprint.components.NearClipPlane".into(), display_name : + "Near clip plane", docstring_md : + "Controls the distance to the near clip plane in 3D scene units.\n\nContent closer than this distance will not be visible.", + is_required : false, }, ], }, ), diff --git a/docs/content/reference/migration/migration-0-21.md b/docs/content/reference/migration/migration-0-21.md new file mode 100644 index 000000000000..e0b0a5d693a7 --- /dev/null +++ b/docs/content/reference/migration/migration-0-21.md @@ -0,0 +1,27 @@ +--- +title: Migrating from 0.20 to 0.21 +order: 989 +--- + +### Near clip plane for `Spatial2D` views now defaults to `0.1` in 3D scene units. + +Previously, the clip plane was set an arbitrary value that worked reasonably for +cameras with large focal lengths, but become problematic for cameras with smaller +focal length values. This value is now normalized based on the focal length of +the camera. + +If the default value of `0.1` is still too large for your use-case it can be configured +using the new `near_clip_plane` of the `VisualBounds2D` blueprint property, either +through the UI, or through the SDK in Python: +```python +rr.send_blueprint( + rrb.Spatial2DView( + origin="world/cam", + contents="/**", + visual_bounds=rrb.VisualBounds2D( + near_clip_plane=0.01, + ), + ) +) +``` + diff --git a/docs/content/reference/types/views/graph_view.md b/docs/content/reference/types/views/graph_view.md index f6830c618ed6..58432cb123b8 100644 --- a/docs/content/reference/types/views/graph_view.md +++ b/docs/content/reference/types/views/graph_view.md @@ -12,6 +12,9 @@ Everything within these bounds is guaranteed to be visible. Somethings outside of these bounds may also be visible due to letterboxing. +* `range`: Controls the visible range of a 2D view. +* `near_clip_plane`: Controls the distance to the near clip plane in 3D scene units. + ## API reference links * 🐍 [Python API docs for `GraphView`](https://ref.rerun.io/docs/python/stable/common/blueprint_views?speculative-link#rerun.blueprint.views.GraphView) diff --git a/docs/content/reference/types/views/spatial2d_view.md b/docs/content/reference/types/views/spatial2d_view.md index d7a752d0212a..7125701ab1b9 100644 --- a/docs/content/reference/types/views/spatial2d_view.md +++ b/docs/content/reference/types/views/spatial2d_view.md @@ -17,6 +17,9 @@ The visible parts of the scene, in the coordinate space of the scene. Everything within these bounds are guaranteed to be visible. Somethings outside of these bounds may also be visible due to letterboxing. + +* `range`: Controls the visible range of a 2D view. +* `near_clip_plane`: Controls the distance to the near clip plane in 3D scene units. ### `time_ranges` Configures which range on each timeline is shown by this view (unless specified differently per entity). diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/visual_bounds2d.cpp b/rerun_cpp/src/rerun/blueprint/archetypes/visual_bounds2d.cpp index 9326c22b3548..d5756b46e0ef 100644 --- a/rerun_cpp/src/rerun/blueprint/archetypes/visual_bounds2d.cpp +++ b/rerun_cpp/src/rerun/blueprint/archetypes/visual_bounds2d.cpp @@ -15,7 +15,7 @@ namespace rerun { ) { using namespace blueprint::archetypes; std::vector cells; - cells.reserve(2); + cells.reserve(3); { auto result = ComponentBatch::from_loggable( @@ -29,6 +29,18 @@ namespace rerun { RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } + { + auto result = ComponentBatch::from_loggable( + archetype.near_clip_plane, + ComponentDescriptor( + "rerun.blueprint.archetypes.VisualBounds2D", + "near_clip_plane", + "rerun.blueprint.components.NearClipPlane" + ) + ); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } { auto indicator = VisualBounds2D::IndicatorComponent(); auto result = ComponentBatch::from_loggable(indicator); diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/visual_bounds2d.hpp b/rerun_cpp/src/rerun/blueprint/archetypes/visual_bounds2d.hpp index fc0f11e560f1..0cf72032e464 100644 --- a/rerun_cpp/src/rerun/blueprint/archetypes/visual_bounds2d.hpp +++ b/rerun_cpp/src/rerun/blueprint/archetypes/visual_bounds2d.hpp @@ -3,6 +3,7 @@ #pragma once +#include "../../blueprint/components/near_clip_plane.hpp" #include "../../blueprint/components/visual_bounds2d.hpp" #include "../../collection.hpp" #include "../../component_batch.hpp" @@ -27,6 +28,11 @@ namespace rerun::blueprint::archetypes { /// Use this to control pan & zoom of the view. rerun::blueprint::components::VisualBounds2D range; + /// Controls the distance to the near clip plane in 3D scene units. + /// + /// Content closer than this distance will not be visible. + rerun::blueprint::components::NearClipPlane near_clip_plane; + public: static constexpr const char IndicatorComponentName[] = "rerun.blueprint.components.VisualBounds2DIndicator"; @@ -38,8 +44,11 @@ namespace rerun::blueprint::archetypes { VisualBounds2D() = default; VisualBounds2D(VisualBounds2D&& other) = default; - explicit VisualBounds2D(rerun::blueprint::components::VisualBounds2D _range) - : range(std::move(_range)) {} + explicit VisualBounds2D( + rerun::blueprint::components::VisualBounds2D _range, + rerun::blueprint::components::NearClipPlane _near_clip_plane + ) + : range(std::move(_range)), near_clip_plane(std::move(_near_clip_plane)) {} }; } // namespace rerun::blueprint::archetypes diff --git a/rerun_cpp/src/rerun/blueprint/components.hpp b/rerun_cpp/src/rerun/blueprint/components.hpp index 6dfe128cab3c..e5ed33415971 100644 --- a/rerun_cpp/src/rerun/blueprint/components.hpp +++ b/rerun_cpp/src/rerun/blueprint/components.hpp @@ -19,6 +19,7 @@ #include "blueprint/components/interactive.hpp" #include "blueprint/components/lock_range_during_zoom.hpp" #include "blueprint/components/map_provider.hpp" +#include "blueprint/components/near_clip_plane.hpp" #include "blueprint/components/panel_state.hpp" #include "blueprint/components/query_expression.hpp" #include "blueprint/components/root_container.hpp" diff --git a/rerun_cpp/src/rerun/blueprint/components/.gitattributes b/rerun_cpp/src/rerun/blueprint/components/.gitattributes index 50a1a60a3463..01c332e58b9c 100644 --- a/rerun_cpp/src/rerun/blueprint/components/.gitattributes +++ b/rerun_cpp/src/rerun/blueprint/components/.gitattributes @@ -22,6 +22,7 @@ interactive.hpp linguist-generated=true lock_range_during_zoom.hpp linguist-generated=true map_provider.cpp linguist-generated=true map_provider.hpp linguist-generated=true +near_clip_plane.hpp linguist-generated=true panel_state.cpp linguist-generated=true panel_state.hpp linguist-generated=true query_expression.hpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/blueprint/components/near_clip_plane.hpp b/rerun_cpp/src/rerun/blueprint/components/near_clip_plane.hpp new file mode 100644 index 000000000000..b400a9c9c22f --- /dev/null +++ b/rerun_cpp/src/rerun/blueprint/components/near_clip_plane.hpp @@ -0,0 +1,79 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/store/re_types/definitions/rerun/blueprint/components/near_clip_plane.fbs". + +#pragma once + +#include "../../component_descriptor.hpp" +#include "../../datatypes/float32.hpp" +#include "../../result.hpp" + +#include +#include + +namespace rerun::blueprint::components { + /// **Component**: Distance to the near clip plane used for `Spatial2DView`. + struct NearClipPlane { + /// Distance to the near clip plane in 3D scene units. + rerun::datatypes::Float32 near_clip_plane; + + public: + NearClipPlane() = default; + + NearClipPlane(rerun::datatypes::Float32 near_clip_plane_) + : near_clip_plane(near_clip_plane_) {} + + NearClipPlane& operator=(rerun::datatypes::Float32 near_clip_plane_) { + near_clip_plane = near_clip_plane_; + return *this; + } + + NearClipPlane(float value_) : near_clip_plane(value_) {} + + NearClipPlane& operator=(float value_) { + near_clip_plane = value_; + return *this; + } + + /// Cast to the underlying Float32 datatype + operator rerun::datatypes::Float32() const { + return near_clip_plane; + } + }; +} // namespace rerun::blueprint::components + +namespace rerun { + static_assert( + sizeof(rerun::datatypes::Float32) == sizeof(blueprint::components::NearClipPlane) + ); + + /// \private + template <> + struct Loggable { + static constexpr ComponentDescriptor Descriptor = + "rerun.blueprint.components.NearClipPlane"; + + /// 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::blueprint:: components::NearClipPlane` into an arrow array. + static Result> to_arrow( + const blueprint::components::NearClipPlane* instances, size_t num_instances + ) { + if (num_instances == 0) { + return Loggable::to_arrow(nullptr, 0); + } else if (instances == nullptr) { + return rerun::Error( + ErrorCode::UnexpectedNullArgument, + "Passed array instances is null when num_elements> 0." + ); + } else { + return Loggable::to_arrow( + &instances->near_clip_plane, + num_instances + ); + } + } + }; +} // namespace rerun diff --git a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visual_bounds2d.py b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visual_bounds2d.py index 7a6a5159dc0b..110ebd5c2d69 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visual_bounds2d.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visual_bounds2d.py @@ -34,6 +34,7 @@ def __attrs_clear__(self) -> None: """Convenience method for calling `__attrs_init__` with all `None`s.""" self.__attrs_init__( range=None, # type: ignore[arg-type] + near_clip_plane=None, # type: ignore[arg-type] ) @classmethod @@ -53,5 +54,15 @@ def _clear(cls) -> VisualBounds2D: # # (Docstring intentionally commented out to hide this field from the docs) + near_clip_plane: blueprint_components.NearClipPlaneBatch = field( + metadata={"component": "required"}, + converter=blueprint_components.NearClipPlaneBatch._required, # type: ignore[misc] + ) + # Controls the distance to the near clip plane in 3D scene units. + # + # Content closer than this distance will not be visible. + # + # (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/blueprint/archetypes/visual_bounds2d_ext.py b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visual_bounds2d_ext.py index 78cc1158bbfc..f28c2ae1f359 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visual_bounds2d_ext.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visual_bounds2d_ext.py @@ -13,8 +13,9 @@ class VisualBounds2DExt: def __init__( self: Any, *, - x_range: datatypes.Range1DLike, - y_range: datatypes.Range1DLike, + x_range: datatypes.Range1DLike | None = None, + y_range: datatypes.Range1DLike | None = None, + near_clip_plane: datatypes.Float32Like | None = None, ): """ Create a new instance of the VisualBounds2D archetype. @@ -25,10 +26,22 @@ def __init__( The minimum visible range of the X-axis (usually left and right bounds). y_range: The minimum visible range of the Y-axis (usually left and right bounds). + near_clip_plane: + The distance to the near clipping plane. """ + if x_range is not None and y_range is not None: + range = blueprint_components.VisualBounds2D(x_range=x_range, y_range=y_range) + elif x_range is not None or y_range is not None: + raise ValueError("Both x_range and y_range must be specified.") + else: + range = None + with catch_and_log_exceptions(context=self.__class__.__name__): - self.__attrs_init__(range=blueprint_components.VisualBounds2D(x_range=x_range, y_range=y_range)) + self.__attrs_init__( + range=range, + near_clip_plane=near_clip_plane, + ) return self.__attrs_clear__() diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes b/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes index 320328225835..4fdcc51e8c50 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes @@ -19,6 +19,7 @@ included_content.py linguist-generated=true interactive.py linguist-generated=true lock_range_during_zoom.py linguist-generated=true map_provider.py linguist-generated=true +near_clip_plane.py linguist-generated=true panel_state.py linguist-generated=true query_expression.py linguist-generated=true root_container.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py b/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py index 8f7f6182b390..18d213e617d7 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py @@ -19,6 +19,7 @@ from .interactive import Interactive, InteractiveBatch from .lock_range_during_zoom import LockRangeDuringZoom, LockRangeDuringZoomBatch from .map_provider import MapProvider, MapProviderArrayLike, MapProviderBatch, MapProviderLike +from .near_clip_plane import NearClipPlane, NearClipPlaneBatch from .panel_state import PanelState, PanelStateArrayLike, PanelStateBatch, PanelStateLike from .query_expression import QueryExpression, QueryExpressionBatch from .root_container import RootContainer, RootContainerBatch @@ -80,6 +81,8 @@ "MapProviderArrayLike", "MapProviderBatch", "MapProviderLike", + "NearClipPlane", + "NearClipPlaneBatch", "PanelState", "PanelStateArrayLike", "PanelStateBatch", diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/near_clip_plane.py b/rerun_py/rerun_sdk/rerun/blueprint/components/near_clip_plane.py new file mode 100644 index 000000000000..c2693d691431 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/near_clip_plane.py @@ -0,0 +1,33 @@ +# DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/python/mod.rs +# Based on "crates/store/re_types/definitions/rerun/blueprint/components/near_clip_plane.fbs". + +# You can extend this class by creating a "NearClipPlaneExt" class in "near_clip_plane_ext.py". + +from __future__ import annotations + +from ... import datatypes +from ..._baseclasses import ( + ComponentBatchMixin, + ComponentDescriptor, + ComponentMixin, +) + +__all__ = ["NearClipPlane", "NearClipPlaneBatch"] + + +class NearClipPlane(datatypes.Float32, ComponentMixin): + """**Component**: Distance to the near clip plane used for `Spatial2DView`.""" + + _BATCH_TYPE = None + # You can define your own __init__ function as a member of NearClipPlaneExt in near_clip_plane_ext.py + + # Note: there are no fields here because NearClipPlane delegates to datatypes.Float32 + pass + + +class NearClipPlaneBatch(datatypes.Float32Batch, ComponentBatchMixin): + _COMPONENT_DESCRIPTOR: ComponentDescriptor = ComponentDescriptor("rerun.blueprint.components.NearClipPlane") + + +# This is patched in late to avoid circular dependencies. +NearClipPlane._BATCH_TYPE = NearClipPlaneBatch # type: ignore[assignment]