From 6a151d2ee492c51949aedeb5b914ebd132c5a42e Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 26 Jul 2024 16:54:11 +0200 Subject: [PATCH 01/29] Introduce LeafTransform3D type + associated components --- .../re_types/definitions/rerun/archetypes.fbs | 1 + .../rerun/archetypes/leaf_transforms3d.fbs | 36 ++ .../rerun/components/rotation_axis_angle.fbs | 9 + .../rerun/components/rotation_quat.fbs | 14 +- .../definitions/rerun/components/scale3d.fbs | 13 + .../rerun/components/transform_mat3x3.fbs | 43 +++ .../rerun/components/translation3d.fbs | 9 + .../re_types/src/archetypes/.gitattributes | 1 + .../src/archetypes/leaf_transforms3d.rs | 321 ++++++++++++++++++ crates/store/re_types/src/archetypes/mod.rs | 2 + .../re_types/src/components/.gitattributes | 5 + .../components/leaf_rotation_axis_angle.rs | 105 ++++++ .../src/components/leaf_rotation_quat.rs | 116 +++++++ .../re_types/src/components/leaf_scale3d.rs | 117 +++++++ .../src/components/leaf_transform_mat3x3.rs | 125 +++++++ .../src/components/leaf_translation3d.rs | 113 ++++++ crates/store/re_types/src/components/mod.rs | 10 + .../re_types/src/components/rotation_quat.rs | 2 +- crates/viewer/re_viewer/src/reflection/mod.rs | 61 +++- docs/content/reference/types/archetypes.md | 1 + .../reference/types/archetypes/.gitattributes | 1 + .../types/archetypes/leaf_transforms3d.md | 27 ++ docs/content/reference/types/components.md | 5 + .../reference/types/components/.gitattributes | 5 + .../components/leaf_rotation_axis_angle.md | 20 ++ .../types/components/leaf_rotation_quat.md | 23 ++ .../types/components/leaf_scale3d.md | 24 ++ .../types/components/leaf_transform_mat3x3.md | 32 ++ .../types/components/leaf_translation3d.md | 20 ++ .../types/components/rotation_quat.md | 2 +- .../reference/types/datatypes/mat3x3.md | 1 + .../reference/types/datatypes/quaternion.md | 1 + .../types/datatypes/rotation_axis_angle.md | 1 + .../reference/types/datatypes/vec3d.md | 2 + .../reference/types/views/spatial2d_view.md | 1 + .../reference/types/views/spatial3d_view.md | 1 + rerun_cpp/src/rerun/archetypes.hpp | 1 + rerun_cpp/src/rerun/archetypes/.gitattributes | 2 + .../rerun/archetypes/leaf_transforms3d.cpp | 53 +++ .../rerun/archetypes/leaf_transforms3d.hpp | 114 +++++++ rerun_cpp/src/rerun/components.hpp | 5 + rerun_cpp/src/rerun/components/.gitattributes | 5 + .../components/leaf_rotation_axis_angle.hpp | 60 ++++ .../rerun/components/leaf_rotation_quat.hpp | 60 ++++ .../src/rerun/components/leaf_scale3d.hpp | 66 ++++ .../components/leaf_transform_mat3x3.hpp | 74 ++++ .../rerun/components/leaf_translation3d.hpp | 62 ++++ .../src/rerun/components/rotation_quat.hpp | 2 +- .../rerun_sdk/rerun/archetypes/.gitattributes | 1 + .../rerun_sdk/rerun/archetypes/__init__.py | 2 + .../rerun/archetypes/leaf_transforms3d.py | 136 ++++++++ .../rerun_sdk/rerun/components/.gitattributes | 5 + .../rerun_sdk/rerun/components/__init__.py | 20 ++ .../components/leaf_rotation_axis_angle.py | 36 ++ .../rerun/components/leaf_rotation_quat.py | 41 +++ .../rerun/components/leaf_scale3d.py | 42 +++ .../rerun/components/leaf_transform_mat3x3.py | 72 ++++ .../rerun/components/leaf_translation3d.py | 36 ++ .../rerun/components/rotation_quat.py | 2 +- 59 files changed, 2161 insertions(+), 6 deletions(-) create mode 100644 crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs create mode 100644 crates/store/re_types/src/archetypes/leaf_transforms3d.rs create mode 100644 crates/store/re_types/src/components/leaf_rotation_axis_angle.rs create mode 100644 crates/store/re_types/src/components/leaf_rotation_quat.rs create mode 100644 crates/store/re_types/src/components/leaf_scale3d.rs create mode 100644 crates/store/re_types/src/components/leaf_transform_mat3x3.rs create mode 100644 crates/store/re_types/src/components/leaf_translation3d.rs create mode 100644 docs/content/reference/types/archetypes/leaf_transforms3d.md create mode 100644 docs/content/reference/types/components/leaf_rotation_axis_angle.md create mode 100644 docs/content/reference/types/components/leaf_rotation_quat.md create mode 100644 docs/content/reference/types/components/leaf_scale3d.md create mode 100644 docs/content/reference/types/components/leaf_transform_mat3x3.md create mode 100644 docs/content/reference/types/components/leaf_translation3d.md create mode 100644 rerun_cpp/src/rerun/archetypes/leaf_transforms3d.cpp create mode 100644 rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp create mode 100644 rerun_cpp/src/rerun/components/leaf_rotation_axis_angle.hpp create mode 100644 rerun_cpp/src/rerun/components/leaf_rotation_quat.hpp create mode 100644 rerun_cpp/src/rerun/components/leaf_scale3d.hpp create mode 100644 rerun_cpp/src/rerun/components/leaf_transform_mat3x3.hpp create mode 100644 rerun_cpp/src/rerun/components/leaf_translation3d.hpp create mode 100644 rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py create mode 100644 rerun_py/rerun_sdk/rerun/components/leaf_rotation_axis_angle.py create mode 100644 rerun_py/rerun_sdk/rerun/components/leaf_rotation_quat.py create mode 100644 rerun_py/rerun_sdk/rerun/components/leaf_scale3d.py create mode 100644 rerun_py/rerun_sdk/rerun/components/leaf_transform_mat3x3.py create mode 100644 rerun_py/rerun_sdk/rerun/components/leaf_translation3d.py diff --git a/crates/store/re_types/definitions/rerun/archetypes.fbs b/crates/store/re_types/definitions/rerun/archetypes.fbs index 07af568d23d5..eb5e6c105bb2 100644 --- a/crates/store/re_types/definitions/rerun/archetypes.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes.fbs @@ -13,6 +13,7 @@ include "./archetypes/disconnected_space.fbs"; include "./archetypes/ellipsoids.fbs"; include "./archetypes/image.fbs"; include "./archetypes/image_encoded.fbs"; +include "./archetypes/leaf_transforms3d.fbs"; include "./archetypes/line_strips2d.fbs"; include "./archetypes/line_strips3d.fbs"; include "./archetypes/mesh3d.fbs"; diff --git a/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs new file mode 100644 index 000000000000..5af972ea864e --- /dev/null +++ b/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs @@ -0,0 +1,36 @@ +namespace rerun.archetypes; + + +/// One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. +/// +/// For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. +/// +/// From the point of view of the entity's coordinate system, +/// all components are applied in the inverse order they are listed here. +/// E.g. if both a translation and a max3x3 transform are present, +/// the 3x3 matrix is applied first, followed by the translation. +// TODO(andreas): Add example. +table LeafTransforms3D ( + "attr.rust.derive": "Default, PartialEq", + "attr.rust.generate_field_info", + "attr.docs.category": "Spatial 3D", + "attr.docs.view_types": "Spatial3DView, Spatial2DView: if logged above active projection" +) { + /// Translation vectors. + translation: [rerun.components.LeafTranslation3D] ("attr.rerun.component_optional", nullable, order: 1100); + + /// Rotations via axis + angle. + rotation_axis_angle: [rerun.components.LeafRotationAxisAngle] ("attr.rerun.component_optional", nullable, order: 1200); + + /// Rotations via quaternion. + quaternion: [rerun.components.LeafRotationQuat] ("attr.rerun.component_optional", nullable, order: 1300); + + /// Scaling factor. + scale: [rerun.components.LeafScale3D] ("attr.rerun.component_optional", nullable, order: 1400); + + /// 3x3 transformation matrix. + mat3x3: [rerun.components.LeafTransformMat3x3] ("attr.rerun.component_optional", nullable, order: 1500); + + // TODO(andreas): Support TransformRelation? + // TODO(andreas): Support axis_length? +} diff --git a/crates/store/re_types/definitions/rerun/components/rotation_axis_angle.fbs b/crates/store/re_types/definitions/rerun/components/rotation_axis_angle.fbs index b807a746dd86..20c956535084 100644 --- a/crates/store/re_types/definitions/rerun/components/rotation_axis_angle.fbs +++ b/crates/store/re_types/definitions/rerun/components/rotation_axis_angle.fbs @@ -8,3 +8,12 @@ table RotationAxisAngle ( ) { rotation: rerun.datatypes.RotationAxisAngle (order: 100); } + +/// 3D rotation represented by a rotation around a given axis that doesn't propagate in the transform hierarchy. +table LeafRotationAxisAngle ( + "attr.docs.unreleased", + "attr.rust.derive": "Default, Copy, PartialEq", + "attr.rust.repr": "transparent" +) { + rotation: rerun.datatypes.RotationAxisAngle (order: 100); +} diff --git a/crates/store/re_types/definitions/rerun/components/rotation_quat.fbs b/crates/store/re_types/definitions/rerun/components/rotation_quat.fbs index b88f69cce51f..b657f7db88e7 100644 --- a/crates/store/re_types/definitions/rerun/components/rotation_quat.fbs +++ b/crates/store/re_types/definitions/rerun/components/rotation_quat.fbs @@ -3,7 +3,7 @@ namespace rerun.components; /// A 3D rotation expressed as a quaternion. /// /// Note: although the x,y,z,w components of the quaternion will be passed through to the -/// datastore as provided, when used in the Viewer Quaternions will always be normalized. +/// datastore as provided, when used in the Viewer, quaternions will always be normalized. struct RotationQuat ( "attr.docs.unreleased", "attr.rust.derive": "Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable", @@ -11,3 +11,15 @@ struct RotationQuat ( ) { quaternion: rerun.datatypes.Quaternion (order: 100); } + +/// A 3D rotation expressed as a quaternion that doesn't propagate in the transform hierarchy. +/// +/// Note: although the x,y,z,w components of the quaternion will be passed through to the +/// datastore as provided, when used in the Viewer, quaternions will always be normalized. +struct LeafRotationQuat ( + "attr.docs.unreleased", + "attr.rust.derive": "Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable", + "attr.rust.repr": "transparent" +) { + quaternion: rerun.datatypes.Quaternion (order: 100); +} diff --git a/crates/store/re_types/definitions/rerun/components/scale3d.fbs b/crates/store/re_types/definitions/rerun/components/scale3d.fbs index 43acd0905fff..6ccf568ddb18 100644 --- a/crates/store/re_types/definitions/rerun/components/scale3d.fbs +++ b/crates/store/re_types/definitions/rerun/components/scale3d.fbs @@ -12,3 +12,16 @@ struct Scale3D ( ) { scale: rerun.datatypes.Vec3D (order: 100); } + +/// A 3D scale factor that doesn't propagate in the transform hierarchy. +/// +/// A scale of 1.0 means no scaling. +/// A scale of 2.0 means doubling the size. +/// Each component scales along the corresponding axis. +struct LeafScale3D ( + "attr.docs.unreleased", + "attr.rust.derive": "Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable", + "attr.rust.repr": "transparent" +) { + scale: rerun.datatypes.Vec3D (order: 100); +} diff --git a/crates/store/re_types/definitions/rerun/components/transform_mat3x3.fbs b/crates/store/re_types/definitions/rerun/components/transform_mat3x3.fbs index 9e502264c336..a8bc8c0a6194 100644 --- a/crates/store/re_types/definitions/rerun/components/transform_mat3x3.fbs +++ b/crates/store/re_types/definitions/rerun/components/transform_mat3x3.fbs @@ -43,3 +43,46 @@ struct TransformMat3x3 ( matrix: rerun.datatypes.Mat3x3 (order: 100); } + +/// A 3x3 transformation matrix Matrix that doesn't propagate in the transform hierarchy. +/// +/// 3x3 matrixes are able to represent any affine transformation in 3D space, +/// i.e. rotation, scaling, shearing, reflection etc. +/// +/// Matrices in Rerun are stored as flat list of coefficients in column-major order: +/// ```text +/// column 0 column 1 column 2 +/// ------------------------------------------------- +/// row 0 | flat_columns[0] flat_columns[3] flat_columns[6] +/// row 1 | flat_columns[1] flat_columns[4] flat_columns[7] +/// row 2 | flat_columns[2] flat_columns[5] flat_columns[8] +/// ``` +/// +/// \py However, construction is done from a list of rows, which follows NumPy's convention: +/// \py ```python +/// \py np.testing.assert_array_equal( +/// \py rr.components.LeafTransformMat3x3([1, 2, 3, 4, 5, 6, 7, 8, 9]).flat_columns, np.array([1, 4, 7, 2, 5, 8, 3, 6, 9], dtype=np.float32) +/// \py ) +/// \py np.testing.assert_array_equal( +/// \py rr.components.LeafTransformMat3x3([[1, 2, 3], [4, 5, 6], [7, 8, 9]]).flat_columns, +/// \py np.array([1, 4, 7, 2, 5, 8, 3, 6, 9], dtype=np.float32), +/// \py ) +/// \py ``` +/// \py If you want to construct a matrix from a list of columns instead, use the named `columns` parameter: +/// \py ```python +/// \py np.testing.assert_array_equal( +/// \py rr.components.LeafTransformMat3x3(columns=[1, 2, 3, 4, 5, 6, 7, 8, 9]).flat_columns, +/// \py np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=np.float32), +/// \py ) +/// \py np.testing.assert_array_equal( +/// \py rr.components.LeafTransformMat3x3(columns=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]).flat_columns, +/// \py np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=np.float32), +/// \py ) +/// \py ``` +struct LeafTransformMat3x3 ( + "attr.docs.unreleased", + "attr.rust.derive": "Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable", + "attr.rust.repr": "transparent" +) { + matrix: rerun.datatypes.Mat3x3 (order: 100); +} diff --git a/crates/store/re_types/definitions/rerun/components/translation3d.fbs b/crates/store/re_types/definitions/rerun/components/translation3d.fbs index cd31895e01b7..61deaf3cbfa2 100644 --- a/crates/store/re_types/definitions/rerun/components/translation3d.fbs +++ b/crates/store/re_types/definitions/rerun/components/translation3d.fbs @@ -8,3 +8,12 @@ struct Translation3D ( ) { vector: rerun.datatypes.Vec3D (order: 100); } + +/// A translation vector in 3D space that doesn't propagate in the transform hierarchy. +struct LeafTranslation3D ( + "attr.docs.unreleased", + "attr.rust.derive": "Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable", + "attr.rust.repr": "transparent" +) { + vector: rerun.datatypes.Vec3D (order: 100); +} diff --git a/crates/store/re_types/src/archetypes/.gitattributes b/crates/store/re_types/src/archetypes/.gitattributes index a11fab28307e..e06018ef07f6 100644 --- a/crates/store/re_types/src/archetypes/.gitattributes +++ b/crates/store/re_types/src/archetypes/.gitattributes @@ -13,6 +13,7 @@ disconnected_space.rs linguist-generated=true ellipsoids.rs linguist-generated=true image.rs linguist-generated=true image_encoded.rs linguist-generated=true +leaf_transforms3d.rs linguist-generated=true line_strips2d.rs linguist-generated=true line_strips3d.rs linguist-generated=true mesh3d.rs linguist-generated=true diff --git a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs new file mode 100644 index 000000000000..20b4c10d8dc7 --- /dev/null +++ b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs @@ -0,0 +1,321 @@ +// 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/archetypes/leaf_transforms3d.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::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Archetype**: One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. +/// +/// For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. +/// +/// From the point of view of the entity's coordinate system, +/// all components are applied in the inverse order they are listed here. +/// E.g. if both a translation and a max3x3 transform are present, +/// the 3x3 matrix is applied first, followed by the translation. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct LeafTransforms3D { + /// Translation vectors. + pub translation: Option>, + + /// Rotations via axis + angle. + pub rotation_axis_angle: Option>, + + /// Rotations via quaternion. + pub quaternion: Option>, + + /// Scaling factor. + pub scale: Option>, + + /// 3x3 transformation matrix. + pub mat3x3: Option>, +} + +impl ::re_types_core::SizeBytes for LeafTransforms3D { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.translation.heap_size_bytes() + + self.rotation_axis_angle.heap_size_bytes() + + self.quaternion.heap_size_bytes() + + self.scale.heap_size_bytes() + + self.mat3x3.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + >>::is_pod() + && >>::is_pod() + && >>::is_pod() + && >>::is_pod() + && >>::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.LeafTransforms3DIndicator".into()]); + +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 5usize]> = + once_cell::sync::Lazy::new(|| { + [ + "rerun.components.LeafTranslation3D".into(), + "rerun.components.LeafRotationAxisAngle".into(), + "rerun.components.LeafRotationQuat".into(), + "rerun.components.LeafScale3D".into(), + "rerun.components.LeafTransformMat3x3".into(), + ] + }); + +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 6usize]> = + once_cell::sync::Lazy::new(|| { + [ + "rerun.components.LeafTransforms3DIndicator".into(), + "rerun.components.LeafTranslation3D".into(), + "rerun.components.LeafRotationAxisAngle".into(), + "rerun.components.LeafRotationQuat".into(), + "rerun.components.LeafScale3D".into(), + "rerun.components.LeafTransformMat3x3".into(), + ] + }); + +impl LeafTransforms3D { + /// The total number of components in the archetype: 0 required, 1 recommended, 5 optional + pub const NUM_COMPONENTS: usize = 6usize; +} + +/// Indicator component for the [`LeafTransforms3D`] [`::re_types_core::Archetype`] +pub type LeafTransforms3DIndicator = ::re_types_core::GenericIndicatorComponent; + +impl ::re_types_core::Archetype for LeafTransforms3D { + type Indicator = LeafTransforms3DIndicator; + + #[inline] + fn name() -> ::re_types_core::ArchetypeName { + "rerun.archetypes.LeafTransforms3D".into() + } + + #[inline] + fn display_name() -> &'static str { + "Leaf transforms 3D" + } + + #[inline] + fn indicator() -> MaybeOwnedComponentBatch<'static> { + static INDICATOR: LeafTransforms3DIndicator = LeafTransforms3DIndicator::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 translation = + if let Some(array) = arrays_by_name.get("rerun.components.LeafTranslation3D") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.LeafTransforms3D#translation")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.LeafTransforms3D#translation")? + }) + } else { + None + }; + let rotation_axis_angle = + if let Some(array) = arrays_by_name.get("rerun.components.LeafRotationAxisAngle") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.LeafTransforms3D#rotation_axis_angle")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.LeafTransforms3D#rotation_axis_angle")? + }) + } else { + None + }; + let quaternion = + if let Some(array) = arrays_by_name.get("rerun.components.LeafRotationQuat") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.LeafTransforms3D#quaternion")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.LeafTransforms3D#quaternion")? + }) + } else { + None + }; + let scale = if let Some(array) = arrays_by_name.get("rerun.components.LeafScale3D") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.LeafTransforms3D#scale")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.LeafTransforms3D#scale")? + }) + } else { + None + }; + let mat3x3 = if let Some(array) = arrays_by_name.get("rerun.components.LeafTransformMat3x3") + { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.LeafTransforms3D#mat3x3")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.LeafTransforms3D#mat3x3")? + }) + } else { + None + }; + Ok(Self { + translation, + rotation_axis_angle, + quaternion, + scale, + mat3x3, + }) + } +} + +impl ::re_types_core::AsComponents for LeafTransforms3D { + fn as_component_batches(&self) -> Vec> { + re_tracing::profile_function!(); + use ::re_types_core::Archetype as _; + [ + Some(Self::indicator()), + self.translation + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.rotation_axis_angle + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.quaternion + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.scale + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.mat3x3 + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + ] + .into_iter() + .flatten() + .collect() + } +} + +impl ::re_types_core::ArchetypeReflectionMarker for LeafTransforms3D {} + +impl LeafTransforms3D { + /// Create a new `LeafTransforms3D`. + #[inline] + pub fn new() -> Self { + Self { + translation: None, + rotation_axis_angle: None, + quaternion: None, + scale: None, + mat3x3: None, + } + } + + /// Translation vectors. + #[inline] + pub fn with_translation( + mut self, + translation: impl IntoIterator>, + ) -> Self { + self.translation = Some(translation.into_iter().map(Into::into).collect()); + self + } + + /// Rotations via axis + angle. + #[inline] + pub fn with_rotation_axis_angle( + mut self, + rotation_axis_angle: impl IntoIterator< + Item = impl Into, + >, + ) -> Self { + self.rotation_axis_angle = Some(rotation_axis_angle.into_iter().map(Into::into).collect()); + self + } + + /// Rotations via quaternion. + #[inline] + pub fn with_quaternion( + mut self, + quaternion: impl IntoIterator>, + ) -> Self { + self.quaternion = Some(quaternion.into_iter().map(Into::into).collect()); + self + } + + /// Scaling factor. + #[inline] + pub fn with_scale( + mut self, + scale: impl IntoIterator>, + ) -> Self { + self.scale = Some(scale.into_iter().map(Into::into).collect()); + self + } + + /// 3x3 transformation matrix. + #[inline] + pub fn with_mat3x3( + mut self, + mat3x3: impl IntoIterator>, + ) -> Self { + self.mat3x3 = Some(mat3x3.into_iter().map(Into::into).collect()); + self + } +} diff --git a/crates/store/re_types/src/archetypes/mod.rs b/crates/store/re_types/src/archetypes/mod.rs index dcada0933ea3..1daaf00c374d 100644 --- a/crates/store/re_types/src/archetypes/mod.rs +++ b/crates/store/re_types/src/archetypes/mod.rs @@ -21,6 +21,7 @@ mod image; mod image_encoded; mod image_encoded_ext; mod image_ext; +mod leaf_transforms3d; mod line_strips2d; mod line_strips3d; mod mesh3d; @@ -57,6 +58,7 @@ pub use self::disconnected_space::DisconnectedSpace; pub use self::ellipsoids::Ellipsoids; pub use self::image::Image; pub use self::image_encoded::ImageEncoded; +pub use self::leaf_transforms3d::LeafTransforms3D; pub use self::line_strips2d::LineStrips2D; pub use self::line_strips3d::LineStrips3D; pub use self::mesh3d::Mesh3D; diff --git a/crates/store/re_types/src/components/.gitattributes b/crates/store/re_types/src/components/.gitattributes index ab6662dfba01..bac7129fe59b 100644 --- a/crates/store/re_types/src/components/.gitattributes +++ b/crates/store/re_types/src/components/.gitattributes @@ -21,6 +21,11 @@ half_size2d.rs linguist-generated=true half_size3d.rs linguist-generated=true image_plane_distance.rs linguist-generated=true keypoint_id.rs linguist-generated=true +leaf_rotation_axis_angle.rs linguist-generated=true +leaf_rotation_quat.rs linguist-generated=true +leaf_scale3d.rs linguist-generated=true +leaf_transform_mat3x3.rs linguist-generated=true +leaf_translation3d.rs linguist-generated=true line_strip2d.rs linguist-generated=true line_strip3d.rs linguist-generated=true magnification_filter.rs linguist-generated=true diff --git a/crates/store/re_types/src/components/leaf_rotation_axis_angle.rs b/crates/store/re_types/src/components/leaf_rotation_axis_angle.rs new file mode 100644 index 000000000000..62ca19ca7490 --- /dev/null +++ b/crates/store/re_types/src/components/leaf_rotation_axis_angle.rs @@ -0,0 +1,105 @@ +// 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/components/rotation_axis_angle.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::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Component**: 3D rotation represented by a rotation around a given axis that doesn't propagate in the transform hierarchy. +#[derive(Clone, Debug, Default, Copy, PartialEq)] +#[repr(transparent)] +pub struct LeafRotationAxisAngle(pub crate::datatypes::RotationAxisAngle); + +impl ::re_types_core::SizeBytes for LeafRotationAxisAngle { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.0.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + ::is_pod() + } +} + +impl> From for LeafRotationAxisAngle { + fn from(v: T) -> Self { + Self(v.into()) + } +} + +impl std::borrow::Borrow for LeafRotationAxisAngle { + #[inline] + fn borrow(&self) -> &crate::datatypes::RotationAxisAngle { + &self.0 + } +} + +impl std::ops::Deref for LeafRotationAxisAngle { + type Target = crate::datatypes::RotationAxisAngle; + + #[inline] + fn deref(&self) -> &crate::datatypes::RotationAxisAngle { + &self.0 + } +} + +impl std::ops::DerefMut for LeafRotationAxisAngle { + #[inline] + fn deref_mut(&mut self) -> &mut crate::datatypes::RotationAxisAngle { + &mut self.0 + } +} + +::re_types_core::macros::impl_into_cow!(LeafRotationAxisAngle); + +impl ::re_types_core::Loggable for LeafRotationAxisAngle { + type Name = ::re_types_core::ComponentName; + + #[inline] + fn name() -> Self::Name { + "rerun.components.LeafRotationAxisAngle".into() + } + + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + crate::datatypes::RotationAxisAngle::arrow_datatype() + } + + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + crate::datatypes::RotationAxisAngle::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_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + crate::datatypes::RotationAxisAngle::from_arrow_opt(arrow_data) + .map(|v| v.into_iter().map(|v| v.map(Self)).collect()) + } +} diff --git a/crates/store/re_types/src/components/leaf_rotation_quat.rs b/crates/store/re_types/src/components/leaf_rotation_quat.rs new file mode 100644 index 000000000000..e063765e73f7 --- /dev/null +++ b/crates/store/re_types/src/components/leaf_rotation_quat.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/components/rotation_quat.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::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Component**: A 3D rotation expressed as a quaternion that doesn't propagate in the transform hierarchy. +/// +/// Note: although the x,y,z,w components of the quaternion will be passed through to the +/// datastore as provided, when used in the Viewer, quaternions will always be normalized. +#[derive(Clone, Debug, Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(transparent)] +pub struct LeafRotationQuat(pub crate::datatypes::Quaternion); + +impl ::re_types_core::SizeBytes for LeafRotationQuat { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.0.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + ::is_pod() + } +} + +impl> From for LeafRotationQuat { + fn from(v: T) -> Self { + Self(v.into()) + } +} + +impl std::borrow::Borrow for LeafRotationQuat { + #[inline] + fn borrow(&self) -> &crate::datatypes::Quaternion { + &self.0 + } +} + +impl std::ops::Deref for LeafRotationQuat { + type Target = crate::datatypes::Quaternion; + + #[inline] + fn deref(&self) -> &crate::datatypes::Quaternion { + &self.0 + } +} + +impl std::ops::DerefMut for LeafRotationQuat { + #[inline] + fn deref_mut(&mut self) -> &mut crate::datatypes::Quaternion { + &mut self.0 + } +} + +::re_types_core::macros::impl_into_cow!(LeafRotationQuat); + +impl ::re_types_core::Loggable for LeafRotationQuat { + type Name = ::re_types_core::ComponentName; + + #[inline] + fn name() -> Self::Name { + "rerun.components.LeafRotationQuat".into() + } + + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + crate::datatypes::Quaternion::arrow_datatype() + } + + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + crate::datatypes::Quaternion::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_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + crate::datatypes::Quaternion::from_arrow_opt(arrow_data) + .map(|v| v.into_iter().map(|v| v.map(Self)).collect()) + } + + #[inline] + fn from_arrow(arrow_data: &dyn arrow2::array::Array) -> DeserializationResult> + where + Self: Sized, + { + crate::datatypes::Quaternion::from_arrow(arrow_data).map(bytemuck::cast_vec) + } +} diff --git a/crates/store/re_types/src/components/leaf_scale3d.rs b/crates/store/re_types/src/components/leaf_scale3d.rs new file mode 100644 index 000000000000..c27856446135 --- /dev/null +++ b/crates/store/re_types/src/components/leaf_scale3d.rs @@ -0,0 +1,117 @@ +// 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/components/scale3d.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::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Component**: A 3D scale factor that doesn't propagate in the transform hierarchy. +/// +/// A scale of 1.0 means no scaling. +/// A scale of 2.0 means doubling the size. +/// Each component scales along the corresponding axis. +#[derive(Clone, Debug, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(transparent)] +pub struct LeafScale3D(pub crate::datatypes::Vec3D); + +impl ::re_types_core::SizeBytes for LeafScale3D { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.0.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + ::is_pod() + } +} + +impl> From for LeafScale3D { + fn from(v: T) -> Self { + Self(v.into()) + } +} + +impl std::borrow::Borrow for LeafScale3D { + #[inline] + fn borrow(&self) -> &crate::datatypes::Vec3D { + &self.0 + } +} + +impl std::ops::Deref for LeafScale3D { + type Target = crate::datatypes::Vec3D; + + #[inline] + fn deref(&self) -> &crate::datatypes::Vec3D { + &self.0 + } +} + +impl std::ops::DerefMut for LeafScale3D { + #[inline] + fn deref_mut(&mut self) -> &mut crate::datatypes::Vec3D { + &mut self.0 + } +} + +::re_types_core::macros::impl_into_cow!(LeafScale3D); + +impl ::re_types_core::Loggable for LeafScale3D { + type Name = ::re_types_core::ComponentName; + + #[inline] + fn name() -> Self::Name { + "rerun.components.LeafScale3D".into() + } + + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + crate::datatypes::Vec3D::arrow_datatype() + } + + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + crate::datatypes::Vec3D::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_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + crate::datatypes::Vec3D::from_arrow_opt(arrow_data) + .map(|v| v.into_iter().map(|v| v.map(Self)).collect()) + } + + #[inline] + fn from_arrow(arrow_data: &dyn arrow2::array::Array) -> DeserializationResult> + where + Self: Sized, + { + crate::datatypes::Vec3D::from_arrow(arrow_data).map(bytemuck::cast_vec) + } +} diff --git a/crates/store/re_types/src/components/leaf_transform_mat3x3.rs b/crates/store/re_types/src/components/leaf_transform_mat3x3.rs new file mode 100644 index 000000000000..322d91ca8c8f --- /dev/null +++ b/crates/store/re_types/src/components/leaf_transform_mat3x3.rs @@ -0,0 +1,125 @@ +// 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/components/transform_mat3x3.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::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Component**: A 3x3 transformation matrix Matrix that doesn't propagate in the transform hierarchy. +/// +/// 3x3 matrixes are able to represent any affine transformation in 3D space, +/// i.e. rotation, scaling, shearing, reflection etc. +/// +/// Matrices in Rerun are stored as flat list of coefficients in column-major order: +/// ```text +/// column 0 column 1 column 2 +/// ------------------------------------------------- +/// row 0 | flat_columns[0] flat_columns[3] flat_columns[6] +/// row 1 | flat_columns[1] flat_columns[4] flat_columns[7] +/// row 2 | flat_columns[2] flat_columns[5] flat_columns[8] +/// ``` +#[derive(Clone, Debug, Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(transparent)] +pub struct LeafTransformMat3x3(pub crate::datatypes::Mat3x3); + +impl ::re_types_core::SizeBytes for LeafTransformMat3x3 { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.0.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + ::is_pod() + } +} + +impl> From for LeafTransformMat3x3 { + fn from(v: T) -> Self { + Self(v.into()) + } +} + +impl std::borrow::Borrow for LeafTransformMat3x3 { + #[inline] + fn borrow(&self) -> &crate::datatypes::Mat3x3 { + &self.0 + } +} + +impl std::ops::Deref for LeafTransformMat3x3 { + type Target = crate::datatypes::Mat3x3; + + #[inline] + fn deref(&self) -> &crate::datatypes::Mat3x3 { + &self.0 + } +} + +impl std::ops::DerefMut for LeafTransformMat3x3 { + #[inline] + fn deref_mut(&mut self) -> &mut crate::datatypes::Mat3x3 { + &mut self.0 + } +} + +::re_types_core::macros::impl_into_cow!(LeafTransformMat3x3); + +impl ::re_types_core::Loggable for LeafTransformMat3x3 { + type Name = ::re_types_core::ComponentName; + + #[inline] + fn name() -> Self::Name { + "rerun.components.LeafTransformMat3x3".into() + } + + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + crate::datatypes::Mat3x3::arrow_datatype() + } + + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + crate::datatypes::Mat3x3::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_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + crate::datatypes::Mat3x3::from_arrow_opt(arrow_data) + .map(|v| v.into_iter().map(|v| v.map(Self)).collect()) + } + + #[inline] + fn from_arrow(arrow_data: &dyn arrow2::array::Array) -> DeserializationResult> + where + Self: Sized, + { + crate::datatypes::Mat3x3::from_arrow(arrow_data).map(bytemuck::cast_vec) + } +} diff --git a/crates/store/re_types/src/components/leaf_translation3d.rs b/crates/store/re_types/src/components/leaf_translation3d.rs new file mode 100644 index 000000000000..928b4470c762 --- /dev/null +++ b/crates/store/re_types/src/components/leaf_translation3d.rs @@ -0,0 +1,113 @@ +// 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/components/translation3d.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::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Component**: A translation vector in 3D space that doesn't propagate in the transform hierarchy. +#[derive(Clone, Debug, Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(transparent)] +pub struct LeafTranslation3D(pub crate::datatypes::Vec3D); + +impl ::re_types_core::SizeBytes for LeafTranslation3D { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.0.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + ::is_pod() + } +} + +impl> From for LeafTranslation3D { + fn from(v: T) -> Self { + Self(v.into()) + } +} + +impl std::borrow::Borrow for LeafTranslation3D { + #[inline] + fn borrow(&self) -> &crate::datatypes::Vec3D { + &self.0 + } +} + +impl std::ops::Deref for LeafTranslation3D { + type Target = crate::datatypes::Vec3D; + + #[inline] + fn deref(&self) -> &crate::datatypes::Vec3D { + &self.0 + } +} + +impl std::ops::DerefMut for LeafTranslation3D { + #[inline] + fn deref_mut(&mut self) -> &mut crate::datatypes::Vec3D { + &mut self.0 + } +} + +::re_types_core::macros::impl_into_cow!(LeafTranslation3D); + +impl ::re_types_core::Loggable for LeafTranslation3D { + type Name = ::re_types_core::ComponentName; + + #[inline] + fn name() -> Self::Name { + "rerun.components.LeafTranslation3D".into() + } + + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + crate::datatypes::Vec3D::arrow_datatype() + } + + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + crate::datatypes::Vec3D::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_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + crate::datatypes::Vec3D::from_arrow_opt(arrow_data) + .map(|v| v.into_iter().map(|v| v.map(Self)).collect()) + } + + #[inline] + fn from_arrow(arrow_data: &dyn arrow2::array::Array) -> DeserializationResult> + where + Self: Sized, + { + crate::datatypes::Vec3D::from_arrow(arrow_data).map(bytemuck::cast_vec) + } +} diff --git a/crates/store/re_types/src/components/mod.rs b/crates/store/re_types/src/components/mod.rs index d52e2550b0f0..cc7cf957aa46 100644 --- a/crates/store/re_types/src/components/mod.rs +++ b/crates/store/re_types/src/components/mod.rs @@ -35,6 +35,11 @@ mod image_plane_distance; mod image_plane_distance_ext; mod keypoint_id; mod keypoint_id_ext; +mod leaf_rotation_axis_angle; +mod leaf_rotation_quat; +mod leaf_scale3d; +mod leaf_transform_mat3x3; +mod leaf_translation3d; mod line_strip2d; mod line_strip2d_ext; mod line_strip3d; @@ -124,6 +129,11 @@ pub use self::half_size2d::HalfSize2D; pub use self::half_size3d::HalfSize3D; pub use self::image_plane_distance::ImagePlaneDistance; pub use self::keypoint_id::KeypointId; +pub use self::leaf_rotation_axis_angle::LeafRotationAxisAngle; +pub use self::leaf_rotation_quat::LeafRotationQuat; +pub use self::leaf_scale3d::LeafScale3D; +pub use self::leaf_transform_mat3x3::LeafTransformMat3x3; +pub use self::leaf_translation3d::LeafTranslation3D; pub use self::line_strip2d::LineStrip2D; pub use self::line_strip3d::LineStrip3D; pub use self::magnification_filter::MagnificationFilter; diff --git a/crates/store/re_types/src/components/rotation_quat.rs b/crates/store/re_types/src/components/rotation_quat.rs index 8f9629497b8e..1de2470e79ad 100644 --- a/crates/store/re_types/src/components/rotation_quat.rs +++ b/crates/store/re_types/src/components/rotation_quat.rs @@ -21,7 +21,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// **Component**: A 3D rotation expressed as a quaternion. /// /// Note: although the x,y,z,w components of the quaternion will be passed through to the -/// datastore as provided, when used in the Viewer Quaternions will always be normalized. +/// datastore as provided, when used in the Viewer, quaternions will always be normalized. #[derive(Clone, Debug, Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] #[repr(transparent)] pub struct RotationQuat(pub crate::datatypes::Quaternion); diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index 546aedc943b5..f491a2ce0d85 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -384,6 +384,41 @@ fn generate_component_reflection() -> Result::name(), + ComponentReflection { + docstring_md: "3D rotation represented by a rotation around a given axis that doesn't propagate in the transform hierarchy.", + placeholder: Some(LeafRotationAxisAngle::default().to_arrow()?), + }, + ), + ( + ::name(), + ComponentReflection { + docstring_md: "A 3D rotation expressed as a quaternion that doesn't propagate in the transform hierarchy.\n\nNote: although the x,y,z,w components of the quaternion will be passed through to the\ndatastore as provided, when used in the Viewer, quaternions will always be normalized.", + placeholder: Some(LeafRotationQuat::default().to_arrow()?), + }, + ), + ( + ::name(), + ComponentReflection { + docstring_md: "A 3D scale factor that doesn't propagate in the transform hierarchy.\n\nA scale of 1.0 means no scaling.\nA scale of 2.0 means doubling the size.\nEach component scales along the corresponding axis.", + placeholder: Some(LeafScale3D::default().to_arrow()?), + }, + ), + ( + ::name(), + ComponentReflection { + docstring_md: "A 3x3 transformation matrix Matrix that doesn't propagate in the transform hierarchy.\n\n3x3 matrixes are able to represent any affine transformation in 3D space,\ni.e. rotation, scaling, shearing, reflection etc.\n\nMatrices in Rerun are stored as flat list of coefficients in column-major order:\n```text\n column 0 column 1 column 2\n -------------------------------------------------\nrow 0 | flat_columns[0] flat_columns[3] flat_columns[6]\nrow 1 | flat_columns[1] flat_columns[4] flat_columns[7]\nrow 2 | flat_columns[2] flat_columns[5] flat_columns[8]\n```", + placeholder: Some(LeafTransformMat3x3::default().to_arrow()?), + }, + ), + ( + ::name(), + ComponentReflection { + docstring_md: "A translation vector in 3D space that doesn't propagate in the transform hierarchy.", + placeholder: Some(LeafTranslation3D::default().to_arrow()?), + }, + ), ( ::name(), ComponentReflection { @@ -513,7 +548,7 @@ fn generate_component_reflection() -> Result::name(), ComponentReflection { - docstring_md: "A 3D rotation expressed as a quaternion.\n\nNote: although the x,y,z,w components of the quaternion will be passed through to the\ndatastore as provided, when used in the Viewer Quaternions will always be normalized.", + docstring_md: "A 3D rotation expressed as a quaternion.\n\nNote: although the x,y,z,w components of the quaternion will be passed through to the\ndatastore as provided, when used in the Viewer, quaternions will always be normalized.", placeholder: Some(RotationQuat::default().to_arrow()?), }, ), @@ -654,6 +689,30 @@ fn generate_component_reflection() -> Result ArchetypeReflectionMap { re_tracing::profile_function!(); let array = [ + ( + ArchetypeName::new("rerun.archetypes.LeafTransforms3D"), + ArchetypeReflection { + display_name: "Leaf transforms 3D", + docstring_md: "One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy.\n\nFor transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`].\n\nFrom the point of view of the entity's coordinate system,\nall components are applied in the inverse order they are listed here.\nE.g. if both a translation and a max3x3 transform are present,\nthe 3x3 matrix is applied first, followed by the translation.", + fields: vec![ + ArchetypeFieldReflection { component_name : + "rerun.components.LeafTranslation3D".into(), display_name : + "Translation", docstring_md : "Translation vectors.", }, + ArchetypeFieldReflection { component_name : + "rerun.components.LeafRotationAxisAngle".into(), display_name : + "Rotation axis angle", docstring_md : "Rotations via axis + angle.", + }, ArchetypeFieldReflection { component_name : + "rerun.components.LeafRotationQuat".into(), display_name : + "Quaternion", docstring_md : "Rotations via quaternion.", }, + ArchetypeFieldReflection { component_name : + "rerun.components.LeafScale3D".into(), display_name : "Scale", + docstring_md : "Scaling factor.", }, ArchetypeFieldReflection { + component_name : "rerun.components.LeafTransformMat3x3".into(), + display_name : "Mat 3x 3", docstring_md : + "3x3 transformation matrix.", }, + ], + }, + ), ( ArchetypeName::new("rerun.archetypes.Transform3D"), ArchetypeReflection { diff --git a/docs/content/reference/types/archetypes.md b/docs/content/reference/types/archetypes.md index 916c9205a652..0ceb8ac66911 100644 --- a/docs/content/reference/types/archetypes.md +++ b/docs/content/reference/types/archetypes.md @@ -40,6 +40,7 @@ This page lists all built-in archetypes. * [`Asset3D`](archetypes/asset3d.md): A prepacked 3D asset (`.gltf`, `.glb`, `.obj`, `.stl`, etc.). * [`Boxes3D`](archetypes/boxes3d.md): 3D boxes with half-extents and optional center, rotations, colors etc. * [`Ellipsoids`](archetypes/ellipsoids.md): 3D ellipsoids or spheres. +* [`LeafTransforms3D`](archetypes/leaf_transforms3d.md): One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. * [`LineStrips3D`](archetypes/line_strips3d.md): 3D line strips with positions and optional colors, radii, labels, etc. * [`Mesh3D`](archetypes/mesh3d.md): A 3D triangle mesh as specified by its per-mesh and per-vertex properties. * [`Pinhole`](archetypes/pinhole.md): Camera perspective projection (a.k.a. intrinsics). diff --git a/docs/content/reference/types/archetypes/.gitattributes b/docs/content/reference/types/archetypes/.gitattributes index a47310c9498b..de7c9ad2068a 100644 --- a/docs/content/reference/types/archetypes/.gitattributes +++ b/docs/content/reference/types/archetypes/.gitattributes @@ -14,6 +14,7 @@ disconnected_space.md linguist-generated=true ellipsoids.md linguist-generated=true image.md linguist-generated=true image_encoded.md linguist-generated=true +leaf_transforms3d.md linguist-generated=true line_strips2d.md linguist-generated=true line_strips3d.md linguist-generated=true mesh3d.md linguist-generated=true diff --git a/docs/content/reference/types/archetypes/leaf_transforms3d.md b/docs/content/reference/types/archetypes/leaf_transforms3d.md new file mode 100644 index 000000000000..a00274cecdbb --- /dev/null +++ b/docs/content/reference/types/archetypes/leaf_transforms3d.md @@ -0,0 +1,27 @@ +--- +title: "LeafTransforms3D" +--- + + +One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. + +For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. + +From the point of view of the entity's coordinate system, +all components are applied in the inverse order they are listed here. +E.g. if both a translation and a max3x3 transform are present, +the 3x3 matrix is applied first, followed by the translation. + +## Components + +**Optional**: [`LeafTranslation3D`](../components/leaf_translation3d.md), [`LeafRotationAxisAngle`](../components/leaf_rotation_axis_angle.md), [`LeafRotationQuat`](../components/leaf_rotation_quat.md), [`LeafScale3D`](../components/leaf_scale3d.md), [`LeafTransformMat3x3`](../components/leaf_transform_mat3x3.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 `LeafTransforms3D`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1archetypes_1_1LeafTransforms3D.html) + * 🐍 [Python API docs for `LeafTransforms3D`](https://ref.rerun.io/docs/python/stable/common/archetypes#rerun.archetypes.LeafTransforms3D) + * 🦀 [Rust API docs for `LeafTransforms3D`](https://docs.rs/rerun/latest/rerun/archetypes/struct.LeafTransforms3D.html) + diff --git a/docs/content/reference/types/components.md b/docs/content/reference/types/components.md index c7e5090e2211..f8f43dbb7e89 100644 --- a/docs/content/reference/types/components.md +++ b/docs/content/reference/types/components.md @@ -34,6 +34,11 @@ on [Entities and Components](../../concepts/entity-component.md). * [`HalfSize3D`](components/half_size3d.md): Half-size (radius) of a 3D box. * [`ImagePlaneDistance`](components/image_plane_distance.md): The distance from the camera origin to the image plane when the projection is shown in a 3D viewer. * [`KeypointId`](components/keypoint_id.md): A 16-bit ID representing a type of semantic keypoint within a class. +* [`LeafRotationAxisAngle`](components/leaf_rotation_axis_angle.md): 3D rotation represented by a rotation around a given axis that doesn't propagate in the transform hierarchy. +* [`LeafRotationQuat`](components/leaf_rotation_quat.md): A 3D rotation expressed as a quaternion that doesn't propagate in the transform hierarchy. +* [`LeafScale3D`](components/leaf_scale3d.md): A 3D scale factor that doesn't propagate in the transform hierarchy. +* [`LeafTransformMat3x3`](components/leaf_transform_mat3x3.md): A 3x3 transformation matrix Matrix that doesn't propagate in the transform hierarchy. +* [`LeafTranslation3D`](components/leaf_translation3d.md): A translation vector in 3D space that doesn't propagate in the transform hierarchy. * [`LineStrip2D`](components/line_strip2d.md): A line strip in 2D space. * [`LineStrip3D`](components/line_strip3d.md): A line strip in 3D space. * [`MagnificationFilter`](components/magnification_filter.md): Filter used when magnifying an image/texture such that a single pixel/texel is displayed as multiple pixels on screen. diff --git a/docs/content/reference/types/components/.gitattributes b/docs/content/reference/types/components/.gitattributes index a54341eb5dbf..c6754973864d 100644 --- a/docs/content/reference/types/components/.gitattributes +++ b/docs/content/reference/types/components/.gitattributes @@ -22,6 +22,11 @@ half_size2d.md linguist-generated=true half_size3d.md linguist-generated=true image_plane_distance.md linguist-generated=true keypoint_id.md linguist-generated=true +leaf_rotation_axis_angle.md linguist-generated=true +leaf_rotation_quat.md linguist-generated=true +leaf_scale3d.md linguist-generated=true +leaf_transform_mat3x3.md linguist-generated=true +leaf_translation3d.md linguist-generated=true line_strip2d.md linguist-generated=true line_strip3d.md linguist-generated=true magnification_filter.md linguist-generated=true diff --git a/docs/content/reference/types/components/leaf_rotation_axis_angle.md b/docs/content/reference/types/components/leaf_rotation_axis_angle.md new file mode 100644 index 000000000000..887479537466 --- /dev/null +++ b/docs/content/reference/types/components/leaf_rotation_axis_angle.md @@ -0,0 +1,20 @@ +--- +title: "LeafRotationAxisAngle" +--- + + +3D rotation represented by a rotation around a given axis that doesn't propagate in the transform hierarchy. + +## Fields + +* rotation: [`RotationAxisAngle`](../datatypes/rotation_axis_angle.md) + +## API reference links + * 🌊 [C++ API docs for `LeafRotationAxisAngle`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1LeafRotationAxisAngle.html?speculative-link) + * 🐍 [Python API docs for `LeafRotationAxisAngle`](https://ref.rerun.io/docs/python/stable/common/components?speculative-link#rerun.components.LeafRotationAxisAngle) + * 🦀 [Rust API docs for `LeafRotationAxisAngle`](https://docs.rs/rerun/latest/rerun/components/struct.LeafRotationAxisAngle.html?speculative-link) + + +## Used by + +* [`LeafTransforms3D`](../archetypes/leaf_transforms3d.md) diff --git a/docs/content/reference/types/components/leaf_rotation_quat.md b/docs/content/reference/types/components/leaf_rotation_quat.md new file mode 100644 index 000000000000..f514ed44518c --- /dev/null +++ b/docs/content/reference/types/components/leaf_rotation_quat.md @@ -0,0 +1,23 @@ +--- +title: "LeafRotationQuat" +--- + + +A 3D rotation expressed as a quaternion that doesn't propagate in the transform hierarchy. + +Note: although the x,y,z,w components of the quaternion will be passed through to the +datastore as provided, when used in the Viewer, quaternions will always be normalized. + +## Fields + +* quaternion: [`Quaternion`](../datatypes/quaternion.md) + +## API reference links + * 🌊 [C++ API docs for `LeafRotationQuat`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1LeafRotationQuat.html?speculative-link) + * 🐍 [Python API docs for `LeafRotationQuat`](https://ref.rerun.io/docs/python/stable/common/components?speculative-link#rerun.components.LeafRotationQuat) + * 🦀 [Rust API docs for `LeafRotationQuat`](https://docs.rs/rerun/latest/rerun/components/struct.LeafRotationQuat.html?speculative-link) + + +## Used by + +* [`LeafTransforms3D`](../archetypes/leaf_transforms3d.md) diff --git a/docs/content/reference/types/components/leaf_scale3d.md b/docs/content/reference/types/components/leaf_scale3d.md new file mode 100644 index 000000000000..1e72a5ad0d92 --- /dev/null +++ b/docs/content/reference/types/components/leaf_scale3d.md @@ -0,0 +1,24 @@ +--- +title: "LeafScale3D" +--- + + +A 3D scale factor that doesn't propagate in the transform hierarchy. + +A scale of 1.0 means no scaling. +A scale of 2.0 means doubling the size. +Each component scales along the corresponding axis. + +## Fields + +* scale: [`Vec3D`](../datatypes/vec3d.md) + +## API reference links + * 🌊 [C++ API docs for `LeafScale3D`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1LeafScale3D.html?speculative-link) + * 🐍 [Python API docs for `LeafScale3D`](https://ref.rerun.io/docs/python/stable/common/components?speculative-link#rerun.components.LeafScale3D) + * 🦀 [Rust API docs for `LeafScale3D`](https://docs.rs/rerun/latest/rerun/components/struct.LeafScale3D.html?speculative-link) + + +## Used by + +* [`LeafTransforms3D`](../archetypes/leaf_transforms3d.md) diff --git a/docs/content/reference/types/components/leaf_transform_mat3x3.md b/docs/content/reference/types/components/leaf_transform_mat3x3.md new file mode 100644 index 000000000000..0fe2e9baf4eb --- /dev/null +++ b/docs/content/reference/types/components/leaf_transform_mat3x3.md @@ -0,0 +1,32 @@ +--- +title: "LeafTransformMat3x3" +--- + + +A 3x3 transformation matrix Matrix that doesn't propagate in the transform hierarchy. + +3x3 matrixes are able to represent any affine transformation in 3D space, +i.e. rotation, scaling, shearing, reflection etc. + +Matrices in Rerun are stored as flat list of coefficients in column-major order: +```text + column 0 column 1 column 2 + ------------------------------------------------- +row 0 | flat_columns[0] flat_columns[3] flat_columns[6] +row 1 | flat_columns[1] flat_columns[4] flat_columns[7] +row 2 | flat_columns[2] flat_columns[5] flat_columns[8] +``` + +## Fields + +* matrix: [`Mat3x3`](../datatypes/mat3x3.md) + +## API reference links + * 🌊 [C++ API docs for `LeafTransformMat3x3`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1LeafTransformMat3x3.html?speculative-link) + * 🐍 [Python API docs for `LeafTransformMat3x3`](https://ref.rerun.io/docs/python/stable/common/components?speculative-link#rerun.components.LeafTransformMat3x3) + * 🦀 [Rust API docs for `LeafTransformMat3x3`](https://docs.rs/rerun/latest/rerun/components/struct.LeafTransformMat3x3.html?speculative-link) + + +## Used by + +* [`LeafTransforms3D`](../archetypes/leaf_transforms3d.md) diff --git a/docs/content/reference/types/components/leaf_translation3d.md b/docs/content/reference/types/components/leaf_translation3d.md new file mode 100644 index 000000000000..83896d17052f --- /dev/null +++ b/docs/content/reference/types/components/leaf_translation3d.md @@ -0,0 +1,20 @@ +--- +title: "LeafTranslation3D" +--- + + +A translation vector in 3D space that doesn't propagate in the transform hierarchy. + +## Fields + +* vector: [`Vec3D`](../datatypes/vec3d.md) + +## API reference links + * 🌊 [C++ API docs for `LeafTranslation3D`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1LeafTranslation3D.html?speculative-link) + * 🐍 [Python API docs for `LeafTranslation3D`](https://ref.rerun.io/docs/python/stable/common/components?speculative-link#rerun.components.LeafTranslation3D) + * 🦀 [Rust API docs for `LeafTranslation3D`](https://docs.rs/rerun/latest/rerun/components/struct.LeafTranslation3D.html?speculative-link) + + +## Used by + +* [`LeafTransforms3D`](../archetypes/leaf_transforms3d.md) diff --git a/docs/content/reference/types/components/rotation_quat.md b/docs/content/reference/types/components/rotation_quat.md index 1422195afdec..34960614834b 100644 --- a/docs/content/reference/types/components/rotation_quat.md +++ b/docs/content/reference/types/components/rotation_quat.md @@ -6,7 +6,7 @@ title: "RotationQuat" A 3D rotation expressed as a quaternion. Note: although the x,y,z,w components of the quaternion will be passed through to the -datastore as provided, when used in the Viewer Quaternions will always be normalized. +datastore as provided, when used in the Viewer, quaternions will always be normalized. ## Fields diff --git a/docs/content/reference/types/datatypes/mat3x3.md b/docs/content/reference/types/datatypes/mat3x3.md index cc9a9c416948..ea81160ddcdd 100644 --- a/docs/content/reference/types/datatypes/mat3x3.md +++ b/docs/content/reference/types/datatypes/mat3x3.md @@ -26,5 +26,6 @@ row 2 | flat_columns[2] flat_columns[5] flat_columns[8] ## Used by +* [`LeafTransformMat3x3`](../components/leaf_transform_mat3x3.md?speculative-link) * [`PinholeProjection`](../components/pinhole_projection.md) * [`TransformMat3x3`](../components/transform_mat3x3.md?speculative-link) diff --git a/docs/content/reference/types/datatypes/quaternion.md b/docs/content/reference/types/datatypes/quaternion.md index 2dee7995e377..d806ad25507d 100644 --- a/docs/content/reference/types/datatypes/quaternion.md +++ b/docs/content/reference/types/datatypes/quaternion.md @@ -20,5 +20,6 @@ datastore as provided, when used in the Viewer Quaternions will always be normal ## Used by +* [`LeafRotationQuat`](../components/leaf_rotation_quat.md?speculative-link) * [`Rotation3D`](../datatypes/rotation3d.md) * [`RotationQuat`](../components/rotation_quat.md?speculative-link) diff --git a/docs/content/reference/types/datatypes/rotation_axis_angle.md b/docs/content/reference/types/datatypes/rotation_axis_angle.md index d758088d2df4..df548704dd1e 100644 --- a/docs/content/reference/types/datatypes/rotation_axis_angle.md +++ b/docs/content/reference/types/datatypes/rotation_axis_angle.md @@ -18,5 +18,6 @@ title: "RotationAxisAngle" ## Used by +* [`LeafRotationAxisAngle`](../components/leaf_rotation_axis_angle.md?speculative-link) * [`Rotation3D`](../datatypes/rotation3d.md) * [`RotationAxisAngle`](../components/rotation_axis_angle.md?speculative-link) diff --git a/docs/content/reference/types/datatypes/vec3d.md b/docs/content/reference/types/datatypes/vec3d.md index ddd38d6f02f4..38a2abe96f46 100644 --- a/docs/content/reference/types/datatypes/vec3d.md +++ b/docs/content/reference/types/datatypes/vec3d.md @@ -18,6 +18,8 @@ A vector in 3D space. ## Used by * [`HalfSize3D`](../components/half_size3d.md) +* [`LeafScale3D`](../components/leaf_scale3d.md?speculative-link) +* [`LeafTranslation3D`](../components/leaf_translation3d.md?speculative-link) * [`LineStrip3D`](../components/line_strip3d.md) * [`Position3D`](../components/position3d.md) * [`RotationAxisAngle`](../datatypes/rotation_axis_angle.md) diff --git a/docs/content/reference/types/views/spatial2d_view.md b/docs/content/reference/types/views/spatial2d_view.md index dd65b44530c8..d51b649394e0 100644 --- a/docs/content/reference/types/views/spatial2d_view.md +++ b/docs/content/reference/types/views/spatial2d_view.md @@ -60,6 +60,7 @@ snippet: views/spatial2d * [`Asset3D`](../archetypes/asset3d.md) (if logged above active projection) * [`Boxes3D`](../archetypes/boxes3d.md) (if logged above active projection) * [`Ellipsoids`](../archetypes/ellipsoids.md) (if logged above active projection) +* [`LeafTransforms3D`](../archetypes/leaf_transforms3d.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) * [`Points3D`](../archetypes/points3d.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 db564bdb56a3..2896d2ef47ae 100644 --- a/docs/content/reference/types/views/spatial3d_view.md +++ b/docs/content/reference/types/views/spatial3d_view.md @@ -45,6 +45,7 @@ snippet: views/spatial3d * [`Clear`](../archetypes/clear.md) * [`DisconnectedSpace`](../archetypes/disconnected_space.md) * [`Ellipsoids`](../archetypes/ellipsoids.md) +* [`LeafTransforms3D`](../archetypes/leaf_transforms3d.md) * [`LineStrips3D`](../archetypes/line_strips3d.md) * [`Mesh3D`](../archetypes/mesh3d.md) * [`Points3D`](../archetypes/points3d.md) diff --git a/rerun_cpp/src/rerun/archetypes.hpp b/rerun_cpp/src/rerun/archetypes.hpp index d568c39f5943..01e040445bad 100644 --- a/rerun_cpp/src/rerun/archetypes.hpp +++ b/rerun_cpp/src/rerun/archetypes.hpp @@ -15,6 +15,7 @@ #include "archetypes/ellipsoids.hpp" #include "archetypes/image.hpp" #include "archetypes/image_encoded.hpp" +#include "archetypes/leaf_transforms3d.hpp" #include "archetypes/line_strips2d.hpp" #include "archetypes/line_strips3d.hpp" #include "archetypes/mesh3d.hpp" diff --git a/rerun_cpp/src/rerun/archetypes/.gitattributes b/rerun_cpp/src/rerun/archetypes/.gitattributes index b3776f43140a..98d5359e2cee 100644 --- a/rerun_cpp/src/rerun/archetypes/.gitattributes +++ b/rerun_cpp/src/rerun/archetypes/.gitattributes @@ -27,6 +27,8 @@ image.cpp linguist-generated=true image.hpp linguist-generated=true image_encoded.cpp linguist-generated=true image_encoded.hpp linguist-generated=true +leaf_transforms3d.cpp linguist-generated=true +leaf_transforms3d.hpp linguist-generated=true line_strips2d.cpp linguist-generated=true line_strips2d.hpp linguist-generated=true line_strips3d.cpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.cpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.cpp new file mode 100644 index 000000000000..f6aca770ac0d --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.cpp @@ -0,0 +1,53 @@ +// 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/archetypes/leaf_transforms3d.fbs". + +#include "leaf_transforms3d.hpp" + +#include "../collection_adapter_builtins.hpp" + +namespace rerun::archetypes {} + +namespace rerun { + + Result> AsComponents::serialize( + const archetypes::LeafTransforms3D& archetype + ) { + using namespace archetypes; + std::vector cells; + cells.reserve(6); + + if (archetype.translation.has_value()) { + auto result = DataCell::from_loggable(archetype.translation.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.rotation_axis_angle.has_value()) { + auto result = DataCell::from_loggable(archetype.rotation_axis_angle.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.quaternion.has_value()) { + auto result = DataCell::from_loggable(archetype.quaternion.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.scale.has_value()) { + auto result = DataCell::from_loggable(archetype.scale.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.mat3x3.has_value()) { + auto result = DataCell::from_loggable(archetype.mat3x3.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + { + auto indicator = LeafTransforms3D::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/leaf_transforms3d.hpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp new file mode 100644 index 000000000000..6dd2de5b121d --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp @@ -0,0 +1,114 @@ +// 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/archetypes/leaf_transforms3d.fbs". + +#pragma once + +#include "../collection.hpp" +#include "../compiler_utils.hpp" +#include "../components/leaf_rotation_axis_angle.hpp" +#include "../components/leaf_rotation_quat.hpp" +#include "../components/leaf_scale3d.hpp" +#include "../components/leaf_transform_mat3x3.hpp" +#include "../components/leaf_translation3d.hpp" +#include "../data_cell.hpp" +#include "../indicator_component.hpp" +#include "../result.hpp" + +#include +#include +#include +#include + +namespace rerun::archetypes { + /// **Archetype**: One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. + /// + /// For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. + /// + /// From the point of view of the entity's coordinate system, + /// all components are applied in the inverse order they are listed here. + /// E.g. if both a translation and a max3x3 transform are present, + /// the 3x3 matrix is applied first, followed by the translation. + struct LeafTransforms3D { + /// Translation vectors. + std::optional> translation; + + /// Rotations via axis + angle. + std::optional> rotation_axis_angle; + + /// Rotations via quaternion. + std::optional> quaternion; + + /// Scaling factor. + std::optional> scale; + + /// 3x3 transformation matrix. + std::optional> mat3x3; + + public: + static constexpr const char IndicatorComponentName[] = + "rerun.components.LeafTransforms3DIndicator"; + + /// Indicator component, used to identify the archetype when converting to a list of components. + using IndicatorComponent = rerun::components::IndicatorComponent; + + public: + LeafTransforms3D() = default; + LeafTransforms3D(LeafTransforms3D&& other) = default; + + /// Translation vectors. + LeafTransforms3D with_translation( + Collection _translation + ) && { + translation = std::move(_translation); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + + /// Rotations via axis + angle. + LeafTransforms3D with_rotation_axis_angle( + Collection _rotation_axis_angle + ) && { + rotation_axis_angle = std::move(_rotation_axis_angle); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + + /// Rotations via quaternion. + LeafTransforms3D with_quaternion(Collection _quaternion + ) && { + quaternion = std::move(_quaternion); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + + /// Scaling factor. + LeafTransforms3D with_scale(Collection _scale) && { + scale = std::move(_scale); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + + /// 3x3 transformation matrix. + LeafTransforms3D with_mat3x3(Collection _mat3x3 + ) && { + mat3x3 = std::move(_mat3x3); + // 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::LeafTransforms3D& archetype + ); + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components.hpp b/rerun_cpp/src/rerun/components.hpp index fd92e24cdec3..fe3940f822da 100644 --- a/rerun_cpp/src/rerun/components.hpp +++ b/rerun_cpp/src/rerun/components.hpp @@ -23,6 +23,11 @@ #include "components/half_size3d.hpp" #include "components/image_plane_distance.hpp" #include "components/keypoint_id.hpp" +#include "components/leaf_rotation_axis_angle.hpp" +#include "components/leaf_rotation_quat.hpp" +#include "components/leaf_scale3d.hpp" +#include "components/leaf_transform_mat3x3.hpp" +#include "components/leaf_translation3d.hpp" #include "components/line_strip2d.hpp" #include "components/line_strip3d.hpp" #include "components/magnification_filter.hpp" diff --git a/rerun_cpp/src/rerun/components/.gitattributes b/rerun_cpp/src/rerun/components/.gitattributes index 3b125ebb1e59..f3ca1a95432a 100644 --- a/rerun_cpp/src/rerun/components/.gitattributes +++ b/rerun_cpp/src/rerun/components/.gitattributes @@ -28,6 +28,11 @@ half_size2d.hpp linguist-generated=true half_size3d.hpp linguist-generated=true image_plane_distance.hpp linguist-generated=true keypoint_id.hpp linguist-generated=true +leaf_rotation_axis_angle.hpp linguist-generated=true +leaf_rotation_quat.hpp linguist-generated=true +leaf_scale3d.hpp linguist-generated=true +leaf_transform_mat3x3.hpp linguist-generated=true +leaf_translation3d.hpp linguist-generated=true line_strip2d.cpp linguist-generated=true line_strip2d.hpp linguist-generated=true line_strip3d.cpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/components/leaf_rotation_axis_angle.hpp b/rerun_cpp/src/rerun/components/leaf_rotation_axis_angle.hpp new file mode 100644 index 000000000000..ed72a67e78dc --- /dev/null +++ b/rerun_cpp/src/rerun/components/leaf_rotation_axis_angle.hpp @@ -0,0 +1,60 @@ +// 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/components/rotation_axis_angle.fbs". + +#pragma once + +#include "../datatypes/rotation_axis_angle.hpp" +#include "../result.hpp" + +#include +#include + +namespace rerun::components { + /// **Component**: 3D rotation represented by a rotation around a given axis that doesn't propagate in the transform hierarchy. + struct LeafRotationAxisAngle { + rerun::datatypes::RotationAxisAngle rotation; + + public: + LeafRotationAxisAngle() = default; + + LeafRotationAxisAngle(rerun::datatypes::RotationAxisAngle rotation_) + : rotation(rotation_) {} + + LeafRotationAxisAngle& operator=(rerun::datatypes::RotationAxisAngle rotation_) { + rotation = rotation_; + return *this; + } + + /// Cast to the underlying RotationAxisAngle datatype + operator rerun::datatypes::RotationAxisAngle() const { + return rotation; + } + }; +} // namespace rerun::components + +namespace rerun { + static_assert( + sizeof(rerun::datatypes::RotationAxisAngle) == sizeof(components::LeafRotationAxisAngle) + ); + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.components.LeafRotationAxisAngle"; + + /// 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::LeafRotationAxisAngle` into an arrow array. + static Result> to_arrow( + const components::LeafRotationAxisAngle* instances, size_t num_instances + ) { + return Loggable::to_arrow( + &instances->rotation, + num_instances + ); + } + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components/leaf_rotation_quat.hpp b/rerun_cpp/src/rerun/components/leaf_rotation_quat.hpp new file mode 100644 index 000000000000..f1fed9de518a --- /dev/null +++ b/rerun_cpp/src/rerun/components/leaf_rotation_quat.hpp @@ -0,0 +1,60 @@ +// 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/components/rotation_quat.fbs". + +#pragma once + +#include "../datatypes/quaternion.hpp" +#include "../result.hpp" + +#include +#include + +namespace rerun::components { + /// **Component**: A 3D rotation expressed as a quaternion that doesn't propagate in the transform hierarchy. + /// + /// Note: although the x,y,z,w components of the quaternion will be passed through to the + /// datastore as provided, when used in the Viewer, quaternions will always be normalized. + struct LeafRotationQuat { + rerun::datatypes::Quaternion quaternion; + + public: + LeafRotationQuat() = default; + + LeafRotationQuat(rerun::datatypes::Quaternion quaternion_) : quaternion(quaternion_) {} + + LeafRotationQuat& operator=(rerun::datatypes::Quaternion quaternion_) { + quaternion = quaternion_; + return *this; + } + + /// Cast to the underlying Quaternion datatype + operator rerun::datatypes::Quaternion() const { + return quaternion; + } + }; +} // namespace rerun::components + +namespace rerun { + static_assert(sizeof(rerun::datatypes::Quaternion) == sizeof(components::LeafRotationQuat)); + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.components.LeafRotationQuat"; + + /// 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::LeafRotationQuat` into an arrow array. + static Result> to_arrow( + const components::LeafRotationQuat* instances, size_t num_instances + ) { + return Loggable::to_arrow( + &instances->quaternion, + num_instances + ); + } + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components/leaf_scale3d.hpp b/rerun_cpp/src/rerun/components/leaf_scale3d.hpp new file mode 100644 index 000000000000..5974916fff6f --- /dev/null +++ b/rerun_cpp/src/rerun/components/leaf_scale3d.hpp @@ -0,0 +1,66 @@ +// 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/components/scale3d.fbs". + +#pragma once + +#include "../datatypes/vec3d.hpp" +#include "../result.hpp" + +#include +#include +#include + +namespace rerun::components { + /// **Component**: A 3D scale factor that doesn't propagate in the transform hierarchy. + /// + /// A scale of 1.0 means no scaling. + /// A scale of 2.0 means doubling the size. + /// Each component scales along the corresponding axis. + struct LeafScale3D { + rerun::datatypes::Vec3D scale; + + public: + LeafScale3D() = default; + + LeafScale3D(rerun::datatypes::Vec3D scale_) : scale(scale_) {} + + LeafScale3D& operator=(rerun::datatypes::Vec3D scale_) { + scale = scale_; + return *this; + } + + LeafScale3D(std::array xyz_) : scale(xyz_) {} + + LeafScale3D& operator=(std::array xyz_) { + scale = xyz_; + return *this; + } + + /// Cast to the underlying Vec3D datatype + operator rerun::datatypes::Vec3D() const { + return scale; + } + }; +} // namespace rerun::components + +namespace rerun { + static_assert(sizeof(rerun::datatypes::Vec3D) == sizeof(components::LeafScale3D)); + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.components.LeafScale3D"; + + /// 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::LeafScale3D` into an arrow array. + static Result> to_arrow( + const components::LeafScale3D* instances, size_t num_instances + ) { + return Loggable::to_arrow(&instances->scale, num_instances); + } + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components/leaf_transform_mat3x3.hpp b/rerun_cpp/src/rerun/components/leaf_transform_mat3x3.hpp new file mode 100644 index 000000000000..729fff7bc896 --- /dev/null +++ b/rerun_cpp/src/rerun/components/leaf_transform_mat3x3.hpp @@ -0,0 +1,74 @@ +// 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/components/transform_mat3x3.fbs". + +#pragma once + +#include "../datatypes/mat3x3.hpp" +#include "../result.hpp" + +#include +#include +#include + +namespace rerun::components { + /// **Component**: A 3x3 transformation matrix Matrix that doesn't propagate in the transform hierarchy. + /// + /// 3x3 matrixes are able to represent any affine transformation in 3D space, + /// i.e. rotation, scaling, shearing, reflection etc. + /// + /// Matrices in Rerun are stored as flat list of coefficients in column-major order: + /// ```text + /// column 0 column 1 column 2 + /// ------------------------------------------------- + /// row 0 | flat_columns[0] flat_columns[3] flat_columns[6] + /// row 1 | flat_columns[1] flat_columns[4] flat_columns[7] + /// row 2 | flat_columns[2] flat_columns[5] flat_columns[8] + /// ``` + struct LeafTransformMat3x3 { + rerun::datatypes::Mat3x3 matrix; + + public: + LeafTransformMat3x3() = default; + + LeafTransformMat3x3(rerun::datatypes::Mat3x3 matrix_) : matrix(matrix_) {} + + LeafTransformMat3x3& operator=(rerun::datatypes::Mat3x3 matrix_) { + matrix = matrix_; + return *this; + } + + LeafTransformMat3x3(std::array flat_columns_) : matrix(flat_columns_) {} + + LeafTransformMat3x3& operator=(std::array flat_columns_) { + matrix = flat_columns_; + return *this; + } + + /// Cast to the underlying Mat3x3 datatype + operator rerun::datatypes::Mat3x3() const { + return matrix; + } + }; +} // namespace rerun::components + +namespace rerun { + static_assert(sizeof(rerun::datatypes::Mat3x3) == sizeof(components::LeafTransformMat3x3)); + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.components.LeafTransformMat3x3"; + + /// 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::LeafTransformMat3x3` into an arrow array. + static Result> to_arrow( + const components::LeafTransformMat3x3* instances, size_t num_instances + ) { + return Loggable::to_arrow(&instances->matrix, num_instances); + } + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components/leaf_translation3d.hpp b/rerun_cpp/src/rerun/components/leaf_translation3d.hpp new file mode 100644 index 000000000000..d7732757e2eb --- /dev/null +++ b/rerun_cpp/src/rerun/components/leaf_translation3d.hpp @@ -0,0 +1,62 @@ +// 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/components/translation3d.fbs". + +#pragma once + +#include "../datatypes/vec3d.hpp" +#include "../result.hpp" + +#include +#include +#include + +namespace rerun::components { + /// **Component**: A translation vector in 3D space that doesn't propagate in the transform hierarchy. + struct LeafTranslation3D { + rerun::datatypes::Vec3D vector; + + public: + LeafTranslation3D() = default; + + LeafTranslation3D(rerun::datatypes::Vec3D vector_) : vector(vector_) {} + + LeafTranslation3D& operator=(rerun::datatypes::Vec3D vector_) { + vector = vector_; + return *this; + } + + LeafTranslation3D(std::array xyz_) : vector(xyz_) {} + + LeafTranslation3D& operator=(std::array xyz_) { + vector = xyz_; + return *this; + } + + /// Cast to the underlying Vec3D datatype + operator rerun::datatypes::Vec3D() const { + return vector; + } + }; +} // namespace rerun::components + +namespace rerun { + static_assert(sizeof(rerun::datatypes::Vec3D) == sizeof(components::LeafTranslation3D)); + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.components.LeafTranslation3D"; + + /// 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::LeafTranslation3D` into an arrow array. + static Result> to_arrow( + const components::LeafTranslation3D* instances, size_t num_instances + ) { + return Loggable::to_arrow(&instances->vector, num_instances); + } + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components/rotation_quat.hpp b/rerun_cpp/src/rerun/components/rotation_quat.hpp index a34d7dc62429..4bdfc6d06ac6 100644 --- a/rerun_cpp/src/rerun/components/rotation_quat.hpp +++ b/rerun_cpp/src/rerun/components/rotation_quat.hpp @@ -13,7 +13,7 @@ namespace rerun::components { /// **Component**: A 3D rotation expressed as a quaternion. /// /// Note: although the x,y,z,w components of the quaternion will be passed through to the - /// datastore as provided, when used in the Viewer Quaternions will always be normalized. + /// datastore as provided, when used in the Viewer, quaternions will always be normalized. struct RotationQuat { rerun::datatypes::Quaternion quaternion; diff --git a/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes b/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes index 9485c225adf1..5ed3b7834283 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes @@ -15,6 +15,7 @@ disconnected_space.py linguist-generated=true ellipsoids.py linguist-generated=true image.py linguist-generated=true image_encoded.py linguist-generated=true +leaf_transforms3d.py linguist-generated=true line_strips2d.py linguist-generated=true line_strips3d.py linguist-generated=true mesh3d.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 f5d60e03a309..70acdc671da6 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/__init__.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/__init__.py @@ -15,6 +15,7 @@ from .ellipsoids import Ellipsoids from .image import Image from .image_encoded import ImageEncoded +from .leaf_transforms3d import LeafTransforms3D from .line_strips2d import LineStrips2D from .line_strips3d import LineStrips3D from .mesh3d import Mesh3D @@ -45,6 +46,7 @@ "Ellipsoids", "Image", "ImageEncoded", + "LeafTransforms3D", "LineStrips2D", "LineStrips3D", "Mesh3D", diff --git a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py new file mode 100644 index 000000000000..b921c673459b --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py @@ -0,0 +1,136 @@ +# 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/archetypes/leaf_transforms3d.fbs". + +# You can extend this class by creating a "LeafTransforms3DExt" class in "leaf_transforms3d_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__ = ["LeafTransforms3D"] + + +@define(str=False, repr=False, init=False) +class LeafTransforms3D(Archetype): + """ + **Archetype**: One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. + + For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. + + From the point of view of the entity's coordinate system, + all components are applied in the inverse order they are listed here. + E.g. if both a translation and a max3x3 transform are present, + the 3x3 matrix is applied first, followed by the translation. + """ + + def __init__( + self: Any, + *, + translation: datatypes.Vec3DArrayLike | None = None, + rotation_axis_angle: datatypes.RotationAxisAngleArrayLike | None = None, + quaternion: datatypes.QuaternionArrayLike | None = None, + scale: datatypes.Vec3DArrayLike | None = None, + mat3x3: datatypes.Mat3x3ArrayLike | None = None, + ): + """ + Create a new instance of the LeafTransforms3D archetype. + + Parameters + ---------- + translation: + Translation vectors. + rotation_axis_angle: + Rotations via axis + angle. + quaternion: + Rotations via quaternion. + scale: + Scaling factor. + mat3x3: + 3x3 transformation matrix. + + """ + + # You can define your own __init__ function as a member of LeafTransforms3DExt in leaf_transforms3d_ext.py + with catch_and_log_exceptions(context=self.__class__.__name__): + self.__attrs_init__( + translation=translation, + rotation_axis_angle=rotation_axis_angle, + quaternion=quaternion, + scale=scale, + mat3x3=mat3x3, + ) + return + self.__attrs_clear__() + + def __attrs_clear__(self) -> None: + """Convenience method for calling `__attrs_init__` with all `None`s.""" + self.__attrs_init__( + translation=None, # type: ignore[arg-type] + rotation_axis_angle=None, # type: ignore[arg-type] + quaternion=None, # type: ignore[arg-type] + scale=None, # type: ignore[arg-type] + mat3x3=None, # type: ignore[arg-type] + ) + + @classmethod + def _clear(cls) -> LeafTransforms3D: + """Produce an empty LeafTransforms3D, bypassing `__init__`.""" + inst = cls.__new__(cls) + inst.__attrs_clear__() + return inst + + translation: components.LeafTranslation3DBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.LeafTranslation3DBatch._optional, # type: ignore[misc] + ) + # Translation vectors. + # + # (Docstring intentionally commented out to hide this field from the docs) + + rotation_axis_angle: components.LeafRotationAxisAngleBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.LeafRotationAxisAngleBatch._optional, # type: ignore[misc] + ) + # Rotations via axis + angle. + # + # (Docstring intentionally commented out to hide this field from the docs) + + quaternion: components.LeafRotationQuatBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.LeafRotationQuatBatch._optional, # type: ignore[misc] + ) + # Rotations via quaternion. + # + # (Docstring intentionally commented out to hide this field from the docs) + + scale: components.LeafScale3DBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.LeafScale3DBatch._optional, # type: ignore[misc] + ) + # Scaling factor. + # + # (Docstring intentionally commented out to hide this field from the docs) + + mat3x3: components.LeafTransformMat3x3Batch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.LeafTransformMat3x3Batch._optional, # type: ignore[misc] + ) + # 3x3 transformation matrix. + # + # (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 97a701b83707..85ea8ef2bc96 100644 --- a/rerun_py/rerun_sdk/rerun/components/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/components/.gitattributes @@ -23,6 +23,11 @@ half_size2d.py linguist-generated=true half_size3d.py linguist-generated=true image_plane_distance.py linguist-generated=true keypoint_id.py linguist-generated=true +leaf_rotation_axis_angle.py linguist-generated=true +leaf_rotation_quat.py linguist-generated=true +leaf_scale3d.py linguist-generated=true +leaf_transform_mat3x3.py linguist-generated=true +leaf_translation3d.py linguist-generated=true line_strip2d.py linguist-generated=true line_strip3d.py linguist-generated=true magnification_filter.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 85d82926cc28..9ea13fe6eb6d 100644 --- a/rerun_py/rerun_sdk/rerun/components/__init__.py +++ b/rerun_py/rerun_sdk/rerun/components/__init__.py @@ -41,6 +41,11 @@ from .half_size3d import HalfSize3D, HalfSize3DBatch, HalfSize3DType from .image_plane_distance import ImagePlaneDistance, ImagePlaneDistanceBatch, ImagePlaneDistanceType from .keypoint_id import KeypointId, KeypointIdBatch, KeypointIdType +from .leaf_rotation_axis_angle import LeafRotationAxisAngle, LeafRotationAxisAngleBatch, LeafRotationAxisAngleType +from .leaf_rotation_quat import LeafRotationQuat, LeafRotationQuatBatch, LeafRotationQuatType +from .leaf_scale3d import LeafScale3D, LeafScale3DBatch, LeafScale3DType +from .leaf_transform_mat3x3 import LeafTransformMat3x3, LeafTransformMat3x3Batch, LeafTransformMat3x3Type +from .leaf_translation3d import LeafTranslation3D, LeafTranslation3DBatch, LeafTranslation3DType from .line_strip2d import LineStrip2D, LineStrip2DArrayLike, LineStrip2DBatch, LineStrip2DLike, LineStrip2DType from .line_strip3d import LineStrip3D, LineStrip3DArrayLike, LineStrip3DBatch, LineStrip3DLike, LineStrip3DType from .magnification_filter import ( @@ -171,6 +176,21 @@ "KeypointId", "KeypointIdBatch", "KeypointIdType", + "LeafRotationAxisAngle", + "LeafRotationAxisAngleBatch", + "LeafRotationAxisAngleType", + "LeafRotationQuat", + "LeafRotationQuatBatch", + "LeafRotationQuatType", + "LeafScale3D", + "LeafScale3DBatch", + "LeafScale3DType", + "LeafTransformMat3x3", + "LeafTransformMat3x3Batch", + "LeafTransformMat3x3Type", + "LeafTranslation3D", + "LeafTranslation3DBatch", + "LeafTranslation3DType", "LineStrip2D", "LineStrip2DArrayLike", "LineStrip2DBatch", diff --git a/rerun_py/rerun_sdk/rerun/components/leaf_rotation_axis_angle.py b/rerun_py/rerun_sdk/rerun/components/leaf_rotation_axis_angle.py new file mode 100644 index 000000000000..04464c38fcf1 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/leaf_rotation_axis_angle.py @@ -0,0 +1,36 @@ +# 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/components/rotation_axis_angle.fbs". + +# You can extend this class by creating a "LeafRotationAxisAngleExt" class in "leaf_rotation_axis_angle_ext.py". + +from __future__ import annotations + +from .. import datatypes +from .._baseclasses import ( + ComponentBatchMixin, + ComponentMixin, +) + +__all__ = ["LeafRotationAxisAngle", "LeafRotationAxisAngleBatch", "LeafRotationAxisAngleType"] + + +class LeafRotationAxisAngle(datatypes.RotationAxisAngle, ComponentMixin): + """**Component**: 3D rotation represented by a rotation around a given axis that doesn't propagate in the transform hierarchy.""" + + _BATCH_TYPE = None + # You can define your own __init__ function as a member of LeafRotationAxisAngleExt in leaf_rotation_axis_angle_ext.py + + # Note: there are no fields here because LeafRotationAxisAngle delegates to datatypes.RotationAxisAngle + pass + + +class LeafRotationAxisAngleType(datatypes.RotationAxisAngleType): + _TYPE_NAME: str = "rerun.components.LeafRotationAxisAngle" + + +class LeafRotationAxisAngleBatch(datatypes.RotationAxisAngleBatch, ComponentBatchMixin): + _ARROW_TYPE = LeafRotationAxisAngleType() + + +# This is patched in late to avoid circular dependencies. +LeafRotationAxisAngle._BATCH_TYPE = LeafRotationAxisAngleBatch # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/components/leaf_rotation_quat.py b/rerun_py/rerun_sdk/rerun/components/leaf_rotation_quat.py new file mode 100644 index 000000000000..860693c48637 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/leaf_rotation_quat.py @@ -0,0 +1,41 @@ +# 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/components/rotation_quat.fbs". + +# You can extend this class by creating a "LeafRotationQuatExt" class in "leaf_rotation_quat_ext.py". + +from __future__ import annotations + +from .. import datatypes +from .._baseclasses import ( + ComponentBatchMixin, + ComponentMixin, +) + +__all__ = ["LeafRotationQuat", "LeafRotationQuatBatch", "LeafRotationQuatType"] + + +class LeafRotationQuat(datatypes.Quaternion, ComponentMixin): + """ + **Component**: A 3D rotation expressed as a quaternion that doesn't propagate in the transform hierarchy. + + Note: although the x,y,z,w components of the quaternion will be passed through to the + datastore as provided, when used in the Viewer, quaternions will always be normalized. + """ + + _BATCH_TYPE = None + # You can define your own __init__ function as a member of LeafRotationQuatExt in leaf_rotation_quat_ext.py + + # Note: there are no fields here because LeafRotationQuat delegates to datatypes.Quaternion + pass + + +class LeafRotationQuatType(datatypes.QuaternionType): + _TYPE_NAME: str = "rerun.components.LeafRotationQuat" + + +class LeafRotationQuatBatch(datatypes.QuaternionBatch, ComponentBatchMixin): + _ARROW_TYPE = LeafRotationQuatType() + + +# This is patched in late to avoid circular dependencies. +LeafRotationQuat._BATCH_TYPE = LeafRotationQuatBatch # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/components/leaf_scale3d.py b/rerun_py/rerun_sdk/rerun/components/leaf_scale3d.py new file mode 100644 index 000000000000..c02a492d1779 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/leaf_scale3d.py @@ -0,0 +1,42 @@ +# 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/components/scale3d.fbs". + +# You can extend this class by creating a "LeafScale3DExt" class in "leaf_scale3d_ext.py". + +from __future__ import annotations + +from .. import datatypes +from .._baseclasses import ( + ComponentBatchMixin, + ComponentMixin, +) + +__all__ = ["LeafScale3D", "LeafScale3DBatch", "LeafScale3DType"] + + +class LeafScale3D(datatypes.Vec3D, ComponentMixin): + """ + **Component**: A 3D scale factor that doesn't propagate in the transform hierarchy. + + A scale of 1.0 means no scaling. + A scale of 2.0 means doubling the size. + Each component scales along the corresponding axis. + """ + + _BATCH_TYPE = None + # You can define your own __init__ function as a member of LeafScale3DExt in leaf_scale3d_ext.py + + # Note: there are no fields here because LeafScale3D delegates to datatypes.Vec3D + pass + + +class LeafScale3DType(datatypes.Vec3DType): + _TYPE_NAME: str = "rerun.components.LeafScale3D" + + +class LeafScale3DBatch(datatypes.Vec3DBatch, ComponentBatchMixin): + _ARROW_TYPE = LeafScale3DType() + + +# This is patched in late to avoid circular dependencies. +LeafScale3D._BATCH_TYPE = LeafScale3DBatch # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/components/leaf_transform_mat3x3.py b/rerun_py/rerun_sdk/rerun/components/leaf_transform_mat3x3.py new file mode 100644 index 000000000000..3dfd11033cee --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/leaf_transform_mat3x3.py @@ -0,0 +1,72 @@ +# 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/components/transform_mat3x3.fbs". + +# You can extend this class by creating a "LeafTransformMat3x3Ext" class in "leaf_transform_mat3x3_ext.py". + +from __future__ import annotations + +from .. import datatypes +from .._baseclasses import ( + ComponentBatchMixin, + ComponentMixin, +) + +__all__ = ["LeafTransformMat3x3", "LeafTransformMat3x3Batch", "LeafTransformMat3x3Type"] + + +class LeafTransformMat3x3(datatypes.Mat3x3, ComponentMixin): + """ + **Component**: A 3x3 transformation matrix Matrix that doesn't propagate in the transform hierarchy. + + 3x3 matrixes are able to represent any affine transformation in 3D space, + i.e. rotation, scaling, shearing, reflection etc. + + Matrices in Rerun are stored as flat list of coefficients in column-major order: + ```text + column 0 column 1 column 2 + ------------------------------------------------- + row 0 | flat_columns[0] flat_columns[3] flat_columns[6] + row 1 | flat_columns[1] flat_columns[4] flat_columns[7] + row 2 | flat_columns[2] flat_columns[5] flat_columns[8] + ``` + + However, construction is done from a list of rows, which follows NumPy's convention: + ```python + np.testing.assert_array_equal( + rr.components.LeafTransformMat3x3([1, 2, 3, 4, 5, 6, 7, 8, 9]).flat_columns, np.array([1, 4, 7, 2, 5, 8, 3, 6, 9], dtype=np.float32) + ) + np.testing.assert_array_equal( + rr.components.LeafTransformMat3x3([[1, 2, 3], [4, 5, 6], [7, 8, 9]]).flat_columns, + np.array([1, 4, 7, 2, 5, 8, 3, 6, 9], dtype=np.float32), + ) + ``` + If you want to construct a matrix from a list of columns instead, use the named `columns` parameter: + ```python + np.testing.assert_array_equal( + rr.components.LeafTransformMat3x3(columns=[1, 2, 3, 4, 5, 6, 7, 8, 9]).flat_columns, + np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=np.float32), + ) + np.testing.assert_array_equal( + rr.components.LeafTransformMat3x3(columns=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]).flat_columns, + np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=np.float32), + ) + ``` + """ + + _BATCH_TYPE = None + # You can define your own __init__ function as a member of LeafTransformMat3x3Ext in leaf_transform_mat3x3_ext.py + + # Note: there are no fields here because LeafTransformMat3x3 delegates to datatypes.Mat3x3 + pass + + +class LeafTransformMat3x3Type(datatypes.Mat3x3Type): + _TYPE_NAME: str = "rerun.components.LeafTransformMat3x3" + + +class LeafTransformMat3x3Batch(datatypes.Mat3x3Batch, ComponentBatchMixin): + _ARROW_TYPE = LeafTransformMat3x3Type() + + +# This is patched in late to avoid circular dependencies. +LeafTransformMat3x3._BATCH_TYPE = LeafTransformMat3x3Batch # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/components/leaf_translation3d.py b/rerun_py/rerun_sdk/rerun/components/leaf_translation3d.py new file mode 100644 index 000000000000..cac6ea63d266 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/leaf_translation3d.py @@ -0,0 +1,36 @@ +# 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/components/translation3d.fbs". + +# You can extend this class by creating a "LeafTranslation3DExt" class in "leaf_translation3d_ext.py". + +from __future__ import annotations + +from .. import datatypes +from .._baseclasses import ( + ComponentBatchMixin, + ComponentMixin, +) + +__all__ = ["LeafTranslation3D", "LeafTranslation3DBatch", "LeafTranslation3DType"] + + +class LeafTranslation3D(datatypes.Vec3D, ComponentMixin): + """**Component**: A translation vector in 3D space that doesn't propagate in the transform hierarchy.""" + + _BATCH_TYPE = None + # You can define your own __init__ function as a member of LeafTranslation3DExt in leaf_translation3d_ext.py + + # Note: there are no fields here because LeafTranslation3D delegates to datatypes.Vec3D + pass + + +class LeafTranslation3DType(datatypes.Vec3DType): + _TYPE_NAME: str = "rerun.components.LeafTranslation3D" + + +class LeafTranslation3DBatch(datatypes.Vec3DBatch, ComponentBatchMixin): + _ARROW_TYPE = LeafTranslation3DType() + + +# This is patched in late to avoid circular dependencies. +LeafTranslation3D._BATCH_TYPE = LeafTranslation3DBatch # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/components/rotation_quat.py b/rerun_py/rerun_sdk/rerun/components/rotation_quat.py index b042f6000e77..52588d8443a1 100644 --- a/rerun_py/rerun_sdk/rerun/components/rotation_quat.py +++ b/rerun_py/rerun_sdk/rerun/components/rotation_quat.py @@ -19,7 +19,7 @@ class RotationQuat(datatypes.Quaternion, ComponentMixin): **Component**: A 3D rotation expressed as a quaternion. Note: although the x,y,z,w components of the quaternion will be passed through to the - datastore as provided, when used in the Viewer Quaternions will always be normalized. + datastore as provided, when used in the Viewer, quaternions will always be normalized. """ _BATCH_TYPE = None From 189f26ba8d17fddb8a14c2f3a3e1cfe43a74addd Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 26 Jul 2024 16:58:24 +0200 Subject: [PATCH 02/29] Rust extensions for leaf transform component types --- .../leaf_rotation_axis_angle_ext.rs | 19 ++++++++++ .../src/components/leaf_rotation_quat_ext.rs | 14 ++++++++ .../src/components/leaf_scale3d_ext.rs | 36 +++++++++++++++++++ .../components/leaf_transform_mat3x3_ext.rs | 15 ++++++++ .../src/components/leaf_translation3d_ext.rs | 15 ++++++++ crates/store/re_types/src/components/mod.rs | 5 +++ 6 files changed, 104 insertions(+) create mode 100644 crates/store/re_types/src/components/leaf_rotation_axis_angle_ext.rs create mode 100644 crates/store/re_types/src/components/leaf_rotation_quat_ext.rs create mode 100644 crates/store/re_types/src/components/leaf_scale3d_ext.rs create mode 100644 crates/store/re_types/src/components/leaf_transform_mat3x3_ext.rs create mode 100644 crates/store/re_types/src/components/leaf_translation3d_ext.rs diff --git a/crates/store/re_types/src/components/leaf_rotation_axis_angle_ext.rs b/crates/store/re_types/src/components/leaf_rotation_axis_angle_ext.rs new file mode 100644 index 000000000000..02b86240949d --- /dev/null +++ b/crates/store/re_types/src/components/leaf_rotation_axis_angle_ext.rs @@ -0,0 +1,19 @@ +use crate::datatypes; + +use super::LeafRotationAxisAngle; + +impl LeafRotationAxisAngle { + /// Create a new rotation from an axis and an angle. + #[inline] + pub fn new(axis: impl Into, angle: impl Into) -> Self { + Self(datatypes::RotationAxisAngle::new(axis, angle)) + } +} + +#[cfg(feature = "glam")] +impl From for glam::Affine3A { + #[inline] + fn from(val: LeafRotationAxisAngle) -> Self { + Self::from_axis_angle(val.0.axis.into(), val.0.angle.radians()) + } +} diff --git a/crates/store/re_types/src/components/leaf_rotation_quat_ext.rs b/crates/store/re_types/src/components/leaf_rotation_quat_ext.rs new file mode 100644 index 000000000000..769f4753df9b --- /dev/null +++ b/crates/store/re_types/src/components/leaf_rotation_quat_ext.rs @@ -0,0 +1,14 @@ +use super::LeafRotationQuat; + +impl LeafRotationQuat { + /// The identity rotation, representing no rotation. + pub const IDENTITY: Self = Self(crate::datatypes::Quaternion::IDENTITY); +} + +#[cfg(feature = "glam")] +impl From for glam::Affine3A { + #[inline] + fn from(val: LeafRotationQuat) -> Self { + Self::from_quat(val.0.into()) + } +} diff --git a/crates/store/re_types/src/components/leaf_scale3d_ext.rs b/crates/store/re_types/src/components/leaf_scale3d_ext.rs new file mode 100644 index 000000000000..55a82498495f --- /dev/null +++ b/crates/store/re_types/src/components/leaf_scale3d_ext.rs @@ -0,0 +1,36 @@ +use crate::datatypes::Vec3D; + +use super::LeafScale3D; + +impl LeafScale3D { + /// Scale the same amount along all axis. + #[inline] + pub fn uniform(value: f32) -> Self { + Self(Vec3D([value, value, value])) + } +} + +impl From for LeafScale3D { + #[inline] + fn from(value: f32) -> Self { + Self(crate::datatypes::Vec3D([value, value, value])) + } +} + +#[cfg(feature = "glam")] +impl From for glam::Affine3A { + #[inline] + fn from(v: LeafScale3D) -> Self { + Self { + matrix3: glam::Mat3A::from_diagonal(v.0.into()), + translation: glam::Vec3A::ZERO, + } + } +} + +impl Default for LeafScale3D { + #[inline] + fn default() -> Self { + Self(crate::datatypes::Vec3D([1.0, 1.0, 1.0])) + } +} diff --git a/crates/store/re_types/src/components/leaf_transform_mat3x3_ext.rs b/crates/store/re_types/src/components/leaf_transform_mat3x3_ext.rs new file mode 100644 index 000000000000..73f81b81e6ea --- /dev/null +++ b/crates/store/re_types/src/components/leaf_transform_mat3x3_ext.rs @@ -0,0 +1,15 @@ +#[cfg(feature = "glam")] +use super::LeafTransformMat3x3; + +// This is intentionally not implemented for `Mat3x3`: +// The transform semantic is expressed here, `Mat3x3` on the other hand implements conversion to `glam::Mat3A`. +#[cfg(feature = "glam")] +impl From for glam::Affine3A { + #[inline] + fn from(v: LeafTransformMat3x3) -> Self { + Self { + matrix3: glam::Mat3A::from_cols_slice(&v.0 .0), + translation: glam::Vec3A::ZERO, + } + } +} diff --git a/crates/store/re_types/src/components/leaf_translation3d_ext.rs b/crates/store/re_types/src/components/leaf_translation3d_ext.rs new file mode 100644 index 000000000000..9faa18ee4719 --- /dev/null +++ b/crates/store/re_types/src/components/leaf_translation3d_ext.rs @@ -0,0 +1,15 @@ +#[cfg(feature = "glam")] +use super::LeafTranslation3D; + +// This is intentionally not implemented for `Vec3`: +// The transform semantic is expressed here, `Vec3` on the other hand implements conversion to `glam::Vec3A`. +#[cfg(feature = "glam")] +impl From for glam::Affine3A { + #[inline] + fn from(v: LeafTranslation3D) -> Self { + Self { + matrix3: glam::Mat3A::IDENTITY, + translation: glam::Vec3A::from_slice(&v.0 .0), + } + } +} diff --git a/crates/store/re_types/src/components/mod.rs b/crates/store/re_types/src/components/mod.rs index cc7cf957aa46..4609ff285314 100644 --- a/crates/store/re_types/src/components/mod.rs +++ b/crates/store/re_types/src/components/mod.rs @@ -36,10 +36,15 @@ mod image_plane_distance_ext; mod keypoint_id; mod keypoint_id_ext; mod leaf_rotation_axis_angle; +mod leaf_rotation_axis_angle_ext; mod leaf_rotation_quat; +mod leaf_rotation_quat_ext; mod leaf_scale3d; +mod leaf_scale3d_ext; mod leaf_transform_mat3x3; +mod leaf_transform_mat3x3_ext; mod leaf_translation3d; +mod leaf_translation3d_ext; mod line_strip2d; mod line_strip2d_ext; mod line_strip3d; From cbdb67d019808ac0f6dd6bc32166d319ef47a1b0 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 26 Jul 2024 18:12:23 +0200 Subject: [PATCH 03/29] Refactor transform context to work with leaf transforms --- .../re_space_view_spatial/src/contexts/mod.rs | 2 + .../src/contexts/transform_context.rs | 496 ++++++++++++------ .../src/visualizers/depth_images.rs | 28 +- .../src/visualizers/transform3d_arrows.rs | 9 +- .../visualizers/utilities/entity_iterator.rs | 30 +- 5 files changed, 367 insertions(+), 198 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/contexts/mod.rs b/crates/viewer/re_space_view_spatial/src/contexts/mod.rs index 709b93c2dd53..8ab9523761f2 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/mod.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/mod.rs @@ -11,10 +11,12 @@ pub use transform_context::TransformContext; use re_renderer::DepthOffset; use re_viewer_context::{Annotations, SpaceViewClassRegistryError}; +use transform_context::TransformInfo; /// Context objects for a single entity in a spatial scene. pub struct SpatialSceneEntityContext<'a> { pub world_from_entity: glam::Affine3A, + pub transform_info: &'a TransformInfo, pub depth_offset: DepthOffset, pub annotations: std::sync::Arc, diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index 2fe2ba8ba73c..643b0ac9d5e1 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -1,3 +1,4 @@ +use itertools::Either; use nohash_hasher::IntMap; use re_chunk_store::LatestAtQuery; @@ -6,8 +7,10 @@ use re_space_view::DataResultQuery as _; use re_types::{ archetypes::Pinhole, components::{ - DisconnectedSpace, ImagePlaneDistance, PinholeProjection, RotationAxisAngle, RotationQuat, - Scale3D, Transform3D, TransformMat3x3, TransformRelation, Translation3D, ViewCoordinates, + DisconnectedSpace, ImagePlaneDistance, LeafRotationAxisAngle, LeafRotationQuat, + LeafScale3D, LeafTransformMat3x3, LeafTranslation3D, PinholeProjection, RotationAxisAngle, + RotationQuat, Scale3D, Transform3D, TransformMat3x3, TransformRelation, Translation3D, + ViewCoordinates, }, ComponentNameSet, Loggable as _, }; @@ -15,11 +18,30 @@ use re_viewer_context::{IdentifiedViewSystem, ViewContext, ViewContextSystem}; use crate::visualizers::image_view_coordinates; -#[derive(Clone)] -struct TransformInfo { +#[derive(Clone, Debug)] +pub struct TransformInfo { /// The transform from the entity to the reference space. + /// + /// Does not include per instance leaf transforms! + /// *Does* include 3D from 2D pinhole transform if present. pub reference_from_entity: glam::Affine3A, + /// Like [`TransformInfo::reference_from_entity`], but if this entity has a pinhole camera, it won't affect the transform. + /// + /// Normally, the transform we compute for an entity with a pinhole transform places all objects + /// in front (defined by view coordinates) of the camera with a given image plane distance. + /// In some cases like drawing the lines for a frustum or arrows for the 3D transform, this is not the desired transformation. + /// + /// TODO(andreas): This a lot of overhead for something used very rarely. + /// + /// TODO(#2663, #1025): Going forward we should have separate transform hierarchies for 2D (i.e. projected) and 3D, + /// which would remove the need for this. + pub reference_from_entity_ignoring_3d_from_2d_pinhole: glam::Affine3A, + + /// Optional list of out of leaf transforms that are applied to the instances of this entity. + // TODO(andreas): not very widely supported yet. We may likely want to make `reference_from_instance` a thing and encapsulate stuff further. + pub entity_from_instance_leaf_transforms: Vec, + /// The pinhole camera ancestor of this entity if any. /// /// None indicates that this entity is under the eye camera with no Pinhole camera in-between. @@ -27,6 +49,17 @@ struct TransformInfo { pub parent_pinhole: Option, } +impl Default for TransformInfo { + fn default() -> Self { + Self { + reference_from_entity: glam::Affine3A::IDENTITY, + reference_from_entity_ignoring_3d_from_2d_pinhole: glam::Affine3A::IDENTITY, + entity_from_instance_leaf_transforms: Vec::new(), + parent_pinhole: None, + } + } +} + #[derive(Clone, Copy)] enum UnreachableTransformReason { /// More than one pinhole camera between this and the reference space. @@ -103,7 +136,7 @@ impl ViewContextSystem for TransformContext { self.space_origin = query.space_origin.clone(); // Find the entity path tree for the root. - let Some(mut current_tree) = &entity_tree.subtree(query.space_origin) else { + let Some(current_tree) = &entity_tree.subtree(query.space_origin) else { // It seems the space path is not part of the object tree! // This happens frequently when the viewer remembers space views from a previous run that weren't shown yet. // Naturally, in this case we don't have any transforms yet. @@ -119,11 +152,32 @@ impl ViewContextSystem for TransformContext { current_tree, ctx.recording(), &time_query, - glam::Affine3A::IDENTITY, - &None, // Ignore potential pinhole camera at the root of the space view, since it regarded as being "above" this root. + // Ignore potential pinhole camera at the root of the space view, since it regarded as being "above" this root. + TransformInfo::default(), ); // Walk up from the reference to the highest reachable parent. + self.gather_parent_transforms(ctx, query, current_tree, &time_query); + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl TransformContext { + /// Gather transforms for everything _above_ the root. + fn gather_parent_transforms<'a>( + &mut self, + ctx: &'a ViewContext<'a>, + query: &re_viewer_context::ViewQuery<'_>, + mut current_tree: &'a EntityTree, + time_query: &LatestAtQuery, + ) { + re_tracing::profile_function!(); + + let entity_tree = ctx.recording().tree(); + let mut encountered_pinhole = None; let mut reference_from_ancestor = glam::Affine3A::IDENTITY; while let Some(parent_path) = current_tree.path.parent() { @@ -139,10 +193,10 @@ impl ViewContextSystem for TransformContext { // Note that the transform at the reference is the first that needs to be inverted to "break out" of its hierarchy. // Generally, the transform _at_ a node isn't relevant to it's children, but only to get to its parent in turn! - match transform_at( + let new_transform = match transforms_at( current_tree, ctx.recording(), - &time_query, + time_query, // TODO(#1025): See comment in transform_at. This is a workaround for precision issues // and the fact that there is no meaningful image plane distance for 3D->2D views. |_| 500.0, @@ -153,33 +207,59 @@ impl ViewContextSystem for TransformContext { Some((parent_tree.path.clone(), unreachable_reason)); break; } - Ok(None) => {} - Ok(Some(parent_from_child)) => { - reference_from_ancestor *= parent_from_child.inverse(); + Ok(transforms_at_entity) => { + let mut new_transform = TransformInfo { + reference_from_entity: reference_from_ancestor, + reference_from_entity_ignoring_3d_from_2d_pinhole: reference_from_ancestor, + entity_from_instance_leaf_transforms: transforms_at_entity + .parent_from_entity_leaf_transforms + .iter() + .map(|&t| t.inverse()) + .collect(), + parent_pinhole: encountered_pinhole.clone(), + }; + + // Need to take care of the fact that we're walking the other direction of the tree here compared to `gather_descendants_transforms`! + if let Some(entity_from_2d_pinhole_content) = + transforms_at_entity.instance_from_pinhole_image_plane + { + debug_assert!(encountered_pinhole.as_ref() == Some(¤t_tree.path)); + new_transform.reference_from_entity *= + entity_from_2d_pinhole_content.inverse(); + } + if let Some(parent_from_entity_tree_transform) = + transforms_at_entity.parent_from_entity_tree_transform + { + new_transform.reference_from_entity *= + parent_from_entity_tree_transform.inverse(); + } + + // If we're going up the tree and encounter a pinhole, we need to apply it, it means that our reference is 2D. + // So it's not acutally a "3d_from_2d" but rather a "2d_from_3d", consequently + // `reference_from_entity_ignoring_3d_from_2d_pinhole`is always the same as `reference_from_entity`. + new_transform.reference_from_entity_ignoring_3d_from_2d_pinhole = + new_transform.reference_from_entity; + + new_transform } - } + }; + + reference_from_ancestor = new_transform.reference_from_entity; - // (skip over everything at and under `current_tree` automatically) + // (this skips over everything at and under `current_tree` automatically) self.gather_descendants_transforms( ctx, query, parent_tree, ctx.recording(), - &time_query, - reference_from_ancestor, - &encountered_pinhole, + time_query, + new_transform, ); current_tree = parent_tree; } } - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl TransformContext { #[allow(clippy::too_many_arguments)] fn gather_descendants_transforms( &mut self, @@ -188,24 +268,22 @@ impl TransformContext { subtree: &EntityTree, entity_db: &EntityDb, query: &LatestAtQuery, - reference_from_entity: glam::Affine3A, - encountered_pinhole: &Option, + transform: TransformInfo, ) { + let encountered_pinhole = transform.parent_pinhole.clone(); + let reference_from_parent = transform.reference_from_entity; + let reference_from_parent_ignoring_3d_from_2d_pinhole = + transform.reference_from_entity_ignoring_3d_from_2d_pinhole; match self.transform_per_entity.entry(subtree.path.clone()) { std::collections::hash_map::Entry::Occupied(_) => { return; } std::collections::hash_map::Entry::Vacant(e) => { - e.insert(TransformInfo { - reference_from_entity, - parent_pinhole: encountered_pinhole.clone(), - }); + e.insert(transform); } } for child_tree in subtree.children.values() { - let mut encountered_pinhole = encountered_pinhole.clone(); - let lookup_image_plane = |p: &_| { let query_result = ctx.viewer_ctx.lookup_query_result(view_query.space_view_id); @@ -223,7 +301,8 @@ impl TransformContext { .into() }; - let reference_from_child = match transform_at( + let mut encountered_pinhole = encountered_pinhole.clone(); + let new_transform = match transforms_at( child_tree, entity_db, query, @@ -235,17 +314,41 @@ impl TransformContext { .push((child_tree.path.clone(), unreachable_reason)); continue; } - Ok(None) => reference_from_entity, - Ok(Some(child_from_parent)) => reference_from_entity * child_from_parent, + + Ok(transforms_at_entity) => { + let mut new_transform = TransformInfo { + reference_from_entity: reference_from_parent, + reference_from_entity_ignoring_3d_from_2d_pinhole: + reference_from_parent_ignoring_3d_from_2d_pinhole, + entity_from_instance_leaf_transforms: transforms_at_entity + .parent_from_entity_leaf_transforms, + parent_pinhole: encountered_pinhole.clone(), + }; + + if let Some(parent_from_entity_tree_transform) = + transforms_at_entity.parent_from_entity_tree_transform + { + new_transform.reference_from_entity *= parent_from_entity_tree_transform; + new_transform.reference_from_entity_ignoring_3d_from_2d_pinhole *= + parent_from_entity_tree_transform; + } + if let Some(entity_from_2d_pinhole_content) = + transforms_at_entity.instance_from_pinhole_image_plane + { + new_transform.reference_from_entity *= entity_from_2d_pinhole_content; + } + + new_transform + } }; + self.gather_descendants_transforms( ctx, view_query, child_tree, entity_db, query, - reference_from_child, - &encountered_pinhole, + new_transform, ); } } @@ -254,51 +357,20 @@ impl TransformContext { &self.space_origin } - /// Retrieves the transform of on entity from its local system to the space of the reference. + /// Retrives transform information for a given entity. /// - /// Returns None if the path is not reachable. - pub fn reference_from_entity(&self, ent_path: &EntityPath) -> Option { - self.transform_per_entity - .get(ent_path) - .map(|i| i.reference_from_entity) + /// Returns `None` if it's not reachable from the view's origin. + pub fn transform_info_for_entity(&self, ent_path: &EntityPath) -> Option<&TransformInfo> { + self.transform_per_entity.get(ent_path) } - /// Like [`Self::reference_from_entity`], but if `ent_path` has a pinhole camera, it won't affect the transform. + /// Retrieves the transform of on entity from its local system to the space of the reference. /// - /// Normally, the transform we compute for an entity with a pinhole transform places all objects - /// in front (defined by view coordinates) of the camera with a given image plane distance. - /// In some cases like drawing the lines for a frustum or arrows for the 3D transform, this is not the desired transformation. /// Returns None if the path is not reachable. - /// - /// TODO(#2663, #1025): Going forward we should have separate transform hierarchies for 2D (i.e. projected) and 3D, - /// which would remove the need for this. - pub fn reference_from_entity_ignoring_pinhole( - &self, - ent_path: &EntityPath, - entity_db: &EntityDb, - query: &LatestAtQuery, - ) -> Option { - let transform_info = self.transform_per_entity.get(ent_path)?; - if let (true, Some(parent)) = ( - transform_info.parent_pinhole.as_ref() == Some(ent_path), - ent_path.parent(), - ) { - self.reference_from_entity(&parent).map(|t| { - t * get_parent_from_child_transform(ent_path, entity_db, query).unwrap_or_default() - }) - } else { - Some(transform_info.reference_from_entity) - } - } - - /// Retrieves the ancestor (or self) pinhole under which this entity sits. - /// - /// None indicates either that the entity does not exist in this hierarchy or that this entity is under the eye camera with no Pinhole camera in-between. - /// Some indicates that the entity is under a pinhole camera at the given entity path that is not at the root of the space view. - pub fn parent_pinhole(&self, ent_path: &EntityPath) -> Option<&EntityPath> { + pub fn reference_from_entity(&self, ent_path: &EntityPath) -> Option { self.transform_per_entity .get(ent_path) - .and_then(|i| i.parent_pinhole.as_ref()) + .map(|i| i.reference_from_entity) } } @@ -341,7 +413,7 @@ But they are instead ordered like this:\n{actual_order:?}" #[cfg(not(debug_assertions))] fn debug_assert_transform_field_order(_: &re_types::reflection::Reflection) {} -fn get_parent_from_child_transform( +fn query_and_resolve_tree_transform_at_entity( entity_path: &EntityPath, entity_db: &EntityDb, query: &LatestAtQuery, @@ -351,7 +423,6 @@ fn get_parent_from_child_transform( query, entity_path, [ - Transform3D::name(), Translation3D::name(), RotationAxisAngle::name(), RotationQuat::name(), @@ -364,17 +435,17 @@ fn get_parent_from_child_transform( return None; } - // Order is specified by order of components in the Transform3D archetype. - // See `has_transform_expected_order` let mut transform = glam::Affine3A::IDENTITY; + + // Order see `debug_assert_transform_field_order` if let Some(translation) = result.component_instance::(0) { - transform *= glam::Affine3A::from(translation); + transform = glam::Affine3A::from(translation); } - if let Some(rotation) = result.component_instance::(0) { - transform *= glam::Affine3A::from(rotation); + if let Some(rotation_quat) = result.component_instance::(0) { + transform *= glam::Affine3A::from(rotation_quat); } - if let Some(rotation) = result.component_instance::(0) { - transform *= glam::Affine3A::from(rotation); + if let Some(rotation_axis_angle) = result.component_instance::(0) { + transform *= glam::Affine3A::from(rotation_axis_angle); } if let Some(scale) = result.component_instance::(0) { transform *= glam::Affine3A::from(scale); @@ -382,25 +453,122 @@ fn get_parent_from_child_transform( if let Some(mat3x3) = result.component_instance::(0) { transform *= glam::Affine3A::from(mat3x3); } + if result.component_instance::(0) == Some(TransformRelation::ChildFromParent) + { + transform = transform.inverse(); + } - let transform_relation = result - .component_instance::(0) - .unwrap_or_default(); - if transform_relation == TransformRelation::ChildFromParent { - Some(transform.inverse()) - } else { - Some(transform) + Some(transform) +} + +fn query_and_resolve_leaf_transform_at_entity( + entity_path: &EntityPath, + entity_db: &EntityDb, + query: &LatestAtQuery, +) -> Vec { + // TODO(#6743): Doesn't take into account overrides. + let result = entity_db.latest_at( + query, + entity_path, + [ + LeafTranslation3D::name(), + LeafRotationAxisAngle::name(), + LeafRotationQuat::name(), + LeafScale3D::name(), + LeafTransformMat3x3::name(), + ], + ); + + let max_count = result + .components + .values() + .map(|comp| comp.num_instances()) + .max() + .unwrap_or(0) as usize; + if max_count == 0 { + return Vec::new(); } - // TODO(#6831): Should add a unit test to this method once all variants are in. - // (Should test correct order being applied etc.. Might require splitting) + #[inline] + pub fn clamped_or_nothing( + values: Vec, + clamped_len: usize, + ) -> impl Iterator { + let Some(last) = values.last() else { + return Either::Left(std::iter::empty()); + }; + let last = last.clone(); + Either::Right( + values + .into_iter() + .chain(std::iter::repeat(last)) + .take(clamped_len), + ) + } + + let mut iter_translation = clamped_or_nothing( + result + .component_batch::() + .unwrap_or_default(), + max_count, + ); + let mut iter_rotation_quat = clamped_or_nothing( + result + .component_batch::() + .unwrap_or_default(), + max_count, + ); + let mut iter_rotation_axis_angle = clamped_or_nothing( + result + .component_batch::() + .unwrap_or_default(), + max_count, + ); + let mut iter_scale = clamped_or_nothing( + result + .component_batch::() + .unwrap_or_default(), + max_count, + ); + let mut iter_mat3x3 = clamped_or_nothing( + result + .component_batch::() + .unwrap_or_default(), + max_count, + ); + + let mut transforms = Vec::with_capacity(max_count); + for _ in 0..max_count { + // Order see `debug_assert_transform_field_order` + let mut transform = glam::Affine3A::IDENTITY; + if let Some(translation) = iter_translation.next() { + transform = glam::Affine3A::from(translation); + } + if let Some(rotation_quat) = iter_rotation_quat.next() { + transform *= glam::Affine3A::from(rotation_quat); + } + if let Some(rotation_axis_angle) = iter_rotation_axis_angle.next() { + transform *= glam::Affine3A::from(rotation_axis_angle); + } + if let Some(scale) = iter_scale.next() { + transform *= glam::Affine3A::from(scale); + } + if let Some(mat3x3) = iter_mat3x3.next() { + transform *= glam::Affine3A::from(mat3x3); + } + + transforms.push(transform); + } + + transforms } -fn get_cached_pinhole( - entity_path: &re_log_types::EntityPath, +fn query_and_resolve_obj_from_pinhole_image_plane( + entity_path: &EntityPath, entity_db: &EntityDb, - query: &re_chunk_store::LatestAtQuery, -) -> Option<(PinholeProjection, ViewCoordinates)> { + query: &LatestAtQuery, + pinhole_image_plane_distance: impl Fn(&EntityPath) -> f32, +) -> Option { entity_db .latest_at_component::(entity_path, query) .map(|(_index, image_from_camera)| { @@ -411,21 +579,85 @@ fn get_cached_pinhole( .map_or(ViewCoordinates::RDF, |(_index, res)| res), ) }) + .map(|(image_from_camera, view_coordinates)| { + // Everything under a pinhole camera is a 2D projection, thus doesn't actually have a proper 3D representation. + // Our visualization interprets this as looking at a 2D image plane from a single point (the pinhole). + + // Center the image plane and move it along z, scaling the further the image plane is. + let distance = pinhole_image_plane_distance(entity_path); + let focal_length = image_from_camera.focal_length_in_pixels(); + let focal_length = glam::vec2(focal_length.x(), focal_length.y()); + let scale = distance / focal_length; + let translation = (-image_from_camera.principal_point() * scale).extend(distance); + + let image_plane3d_from_2d_content = glam::Affine3A::from_translation(translation) + // We want to preserve any depth that might be on the pinhole image. + // Use harmonic mean of x/y scale for those. + * glam::Affine3A::from_scale( + scale.extend(2.0 / (1.0 / scale.x + 1.0 / scale.y)), + ); + + // Our interpretation of the pinhole camera implies that the axis semantics, i.e. ViewCoordinates, + // determine how the image plane is oriented. + // (see also `CamerasPart` where the frustum lines are set up) + let obj_from_image_plane3d = view_coordinates.from_other(&image_view_coordinates()); + + glam::Affine3A::from_mat3(obj_from_image_plane3d) * image_plane3d_from_2d_content + + // Above calculation is nice for a certain kind of visualizing a projected image plane, + // but the image plane distance is arbitrary and there might be other, better visualizations! + + // TODO(#1025): + // As such we don't ever want to invert this matrix! + // However, currently our 2D views require do to exactly that since we're forced to + // build a relationship between the 2D plane and the 3D world, when actually the 2D plane + // should have infinite depth! + // The inverse of this matrix *is* working for this, but quickly runs into precision issues. + // See also `ui_2d.rs#setup_target_config` + }) +} + +/// Resolved transforms at an entity. +struct TransformsAtEntity { + parent_from_entity_tree_transform: Option, + parent_from_entity_leaf_transforms: Vec, + instance_from_pinhole_image_plane: Option, } -fn transform_at( +fn transforms_at( subtree: &EntityTree, entity_db: &EntityDb, query: &LatestAtQuery, pinhole_image_plane_distance: impl Fn(&EntityPath) -> f32, encountered_pinhole: &mut Option, -) -> Result, UnreachableTransformReason> { +) -> Result { re_tracing::profile_function!(); let entity_path = &subtree.path; + let transforms_at_entity = TransformsAtEntity { + parent_from_entity_tree_transform: query_and_resolve_tree_transform_at_entity( + entity_path, + entity_db, + query, + ), + parent_from_entity_leaf_transforms: query_and_resolve_leaf_transform_at_entity( + entity_path, + entity_db, + query, + ), + instance_from_pinhole_image_plane: query_and_resolve_obj_from_pinhole_image_plane( + entity_path, + entity_db, + query, + pinhole_image_plane_distance, + ), + }; - let pinhole = get_cached_pinhole(entity_path, entity_db, query); - if pinhole.is_some() { + // Handle pinhole encounters. + if transforms_at_entity + .instance_from_pinhole_image_plane + .is_some() + { if encountered_pinhole.is_some() { return Err(UnreachableTransformReason::NestedPinholeCameras); } else { @@ -433,60 +665,22 @@ fn transform_at( } } - let transform3d = get_parent_from_child_transform(entity_path, entity_db, query); - - let pinhole = pinhole.map(|(image_from_camera, camera_xyz)| { - // Everything under a pinhole camera is a 2D projection, thus doesn't actually have a proper 3D representation. - // Our visualization interprets this as looking at a 2D image plane from a single point (the pinhole). - - // Center the image plane and move it along z, scaling the further the image plane is. - let distance = pinhole_image_plane_distance(entity_path); - let focal_length = image_from_camera.focal_length_in_pixels(); - let focal_length = glam::vec2(focal_length.x(), focal_length.y()); - let scale = distance / focal_length; - let translation = (-image_from_camera.principal_point() * scale).extend(distance); - - let image_plane3d_from_2d_content = glam::Affine3A::from_translation(translation) - // We want to preserve any depth that might be on the pinhole image. - // Use harmonic mean of x/y scale for those. - * glam::Affine3A::from_scale( - scale.extend(2.0 / (1.0 / scale.x + 1.0 / scale.y)), - ); - - // Our interpretation of the pinhole camera implies that the axis semantics, i.e. ViewCoordinates, - // determine how the image plane is oriented. - // (see also `CamerasPart` where the frustum lines are set up) - let world_from_image_plane3d = camera_xyz.from_other(&image_view_coordinates()); - - glam::Affine3A::from_mat3(world_from_image_plane3d) * image_plane3d_from_2d_content - - // Above calculation is nice for a certain kind of visualizing a projected image plane, - // but the image plane distance is arbitrary and there might be other, better visualizations! - - // TODO(#1025): - // As such we don't ever want to invert this matrix! - // However, currently our 2D views require do to exactly that since we're forced to - // build a relationship between the 2D plane and the 3D world, when actually the 2D plane - // should have infinite depth! - // The inverse of this matrix *is* working for this, but quickly runs into precision issues. - // See also `ui_2d.rs#setup_target_config` - }); - - let is_disconnect_space = || { - entity_db + // If there is any other transform, we ignore `DisconnectedSpace`. + if transforms_at_entity + .parent_from_entity_tree_transform + .is_none() + && transforms_at_entity + .parent_from_entity_leaf_transforms + .is_empty() + && transforms_at_entity + .instance_from_pinhole_image_plane + .is_none() + && entity_db .latest_at_component::(entity_path, query) .map_or(false, |(_index, res)| **res) - }; - - // If there is any other transform, we ignore `DisconnectedSpace`. - if transform3d.is_some() || pinhole.is_some() { - Ok(Some( - transform3d.unwrap_or(glam::Affine3A::IDENTITY) - * pinhole.unwrap_or(glam::Affine3A::IDENTITY), - )) - } else if is_disconnect_space() { + { Err(UnreachableTransformReason::DisconnectedSpace) } else { - Ok(None) + Ok(transforms_at_entity) } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs b/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs index e02ec82d72db..97c5ea554aa3 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs @@ -17,7 +17,7 @@ use re_viewer_context::{ }; use crate::{ - contexts::{SpatialSceneEntityContext, TransformContext}, + contexts::SpatialSceneEntityContext, query_pinhole_legacy, view_kind::SpatialSpaceViewKind, visualizers::{filter_visualizable_2d_entities, SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES}, @@ -53,7 +53,6 @@ impl DepthImageVisualizer { &mut self, ctx: &QueryContext<'_>, depth_clouds: &mut Vec, - transforms: &TransformContext, ent_context: &SpatialSceneEntityContext<'_>, images: impl Iterator, ) { @@ -76,7 +75,7 @@ impl DepthImageVisualizer { image.colormap = Some(image.colormap.unwrap_or_else(|| self.fallback_for(ctx))); if is_3d_view { - if let Some(parent_pinhole_path) = transforms.parent_pinhole(entity_path) { + if let Some(parent_pinhole_path) = &ent_context.transform_info.parent_pinhole { let fill_ratio = fill_ratio.unwrap_or_default(); // NOTE: we don't pass in `world_from_obj` because this corresponds to the @@ -84,7 +83,6 @@ impl DepthImageVisualizer { // What we want are the extrinsics of the depth camera! match Self::process_entity_view_as_depth_cloud( ctx, - transforms, ent_context, &image, entity_path, @@ -142,10 +140,8 @@ impl DepthImageVisualizer { } } - #[allow(clippy::too_many_arguments)] fn process_entity_view_as_depth_cloud( ctx: &QueryContext<'_>, - transforms: &TransformContext, ent_context: &SpatialSceneEntityContext<'_>, image: &ImageInfo, ent_path: &EntityPath, @@ -162,14 +158,9 @@ impl DepthImageVisualizer { }; // Place the cloud at the pinhole's location. Note that this means we ignore any 2D transforms that might be there. - let world_from_view = transforms.reference_from_entity_ignoring_pinhole( - parent_pinhole_path, - ctx.recording(), - ctx.query, - ); - let Some(world_from_view) = world_from_view else { - anyhow::bail!("Couldn't fetch pinhole extrinsics at {parent_pinhole_path:?}"); - }; + let world_from_view = ent_context + .transform_info + .reference_from_entity_ignoring_3d_from_2d_pinhole; let world_from_rdf = world_from_view * glam::Affine3A::from_mat3( intrinsics @@ -255,7 +246,6 @@ impl VisualizerSystem for DepthImageVisualizer { }; let mut depth_clouds = Vec::new(); - let transforms = context_systems.get::()?; super::entity_iterator::process_archetype::( ctx, @@ -314,13 +304,7 @@ impl VisualizerSystem for DepthImageVisualizer { }, ); - self.process_depth_image_data( - ctx, - &mut depth_clouds, - transforms, - spatial_ctx, - &mut data, - ); + self.process_depth_image_data(ctx, &mut depth_clouds, spatial_ctx, &mut data); Ok(()) }, diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs b/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs index e25cc399ec18..da408d4b5e8c 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs @@ -70,13 +70,12 @@ impl VisualizerSystem for Transform3DArrowsVisualizer { for data_result in query.iter_visible_data_results(ctx, Self::identifier()) { // 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, - ctx.recording(), - &latest_at_query, - ) else { + let Some(transform_info) = + transforms.transform_info_for_entity(&data_result.entity_path) + else { continue; }; + let world_from_obj = transform_info.reference_from_entity_ignoring_3d_from_2d_pinhole; let results = data_result .latest_at_with_blueprint_resolved_data::(ctx, &latest_at_query); diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs index facb42d77ebf..5ea09e277f5b 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs @@ -6,15 +6,12 @@ use re_space_view::{ }; use re_types::Archetype; use re_viewer_context::{ - IdentifiedViewSystem, QueryContext, QueryRange, SpaceViewClass, SpaceViewSystemExecutionError, - ViewContext, ViewContextCollection, ViewQuery, + IdentifiedViewSystem, QueryContext, QueryRange, SpaceViewSystemExecutionError, ViewContext, + ViewContextCollection, ViewQuery, }; -use crate::{ - contexts::{ - AnnotationSceneContext, EntityDepthOffsets, SpatialSceneEntityContext, TransformContext, - }, - SpatialSpaceView3D, +use crate::contexts::{ + AnnotationSceneContext, EntityDepthOffsets, SpatialSceneEntityContext, TransformContext, }; // --- @@ -160,24 +157,17 @@ where let system_identifier = System::identifier(); for data_result in query.iter_visible_data_results(ctx, system_identifier) { - // The transform that considers pinholes only makes sense if this is a 3D space-view - let world_from_entity = - if view_ctx.space_view_class_identifier() == SpatialSpaceView3D::identifier() { - transforms.reference_from_entity(&data_result.entity_path) - } else { - transforms.reference_from_entity_ignoring_pinhole( - &data_result.entity_path, - ctx.recording(), - &latest_at, - ) - }; - - let Some(world_from_entity) = world_from_entity else { + let Some(transform_info) = transforms.transform_info_for_entity(&data_result.entity_path) + else { continue; }; + + let world_from_entity = transform_info.reference_from_entity; + let depth_offset_key = (system_identifier, data_result.entity_path.hash()); let entity_context = SpatialSceneEntityContext { world_from_entity, + transform_info, depth_offset: depth_offsets .per_entity_and_visualizer .get(&depth_offset_key) From 570206e4a5b1413899b0afb426d305e734447b68 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Thu, 25 Jul 2024 15:44:28 +0200 Subject: [PATCH 04/29] fix broken camera transform handling --- .../src/contexts/transform_context.rs | 9 ---- .../src/visualizers/cameras.rs | 44 +++++-------------- 2 files changed, 11 insertions(+), 42 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index 643b0ac9d5e1..b2f411d9b16e 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -363,15 +363,6 @@ impl TransformContext { pub fn transform_info_for_entity(&self, ent_path: &EntityPath) -> Option<&TransformInfo> { self.transform_per_entity.get(ent_path) } - - /// Retrieves the transform of on entity from its local system to the space of the reference. - /// - /// Returns None if the path is not reachable. - pub fn reference_from_entity(&self, ent_path: &EntityPath) -> Option { - self.transform_per_entity - .get(ent_path) - .map(|i| i.reference_from_entity) - } } #[cfg(debug_assertions)] diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs b/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs index 5ac201daa5de..fef514f071f8 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs @@ -3,7 +3,7 @@ use re_log_types::Instance; use re_renderer::renderer::LineStripFlags; use re_types::{ archetypes::Pinhole, - components::{ImagePlaneDistance, Transform3D, ViewCoordinates}, + components::{ImagePlaneDistance, ViewCoordinates}, }; use re_viewer_context::{ ApplicableEntities, DataResult, IdentifiedViewSystem, QueryContext, SpaceViewOutlineMasks, @@ -52,7 +52,6 @@ impl CamerasVisualizer { transforms: &TransformContext, data_result: &DataResult, pinhole: &Pinhole, - transform_at_entity: Option, pinhole_view_coordinates: ViewCoordinates, entity_highlight: &SpaceViewOutlineMasks, ) { @@ -74,42 +73,26 @@ impl CamerasVisualizer { return; } - // We need special handling to find the 3D transform for drawing the - // frustum itself. The transform that would otherwise be in the - // transform context might include both a rigid transform and a pinhole. This - // makes sense, since if there's an image logged here one would expect - // both the rigid and the pinhole to apply, but here we're only interested - // in the rigid transform at this entity path, excluding the pinhole - // portion (we handle the pinhole separately later). - let world_from_camera_rigid = { - // Start with the transform to the entity parent, if it exists - let world_from_parent = ent_path - .parent() - .and_then(|parent_path| transforms.reference_from_entity(&parent_path)) - .unwrap_or(glam::Affine3A::IDENTITY); - - // Then combine it with the transform at the entity itself, if there is one. - if let Some(transform_at_entity) = transform_at_entity { - world_from_parent * transform_at_entity.into_parent_from_child_transform() - } else { - world_from_parent - } + // The camera transform does not include the pinhole transform. + let Some(transform_info) = transforms.transform_info_for_entity(ent_path) else { + return; }; + let world_from_camera = transform_info.reference_from_entity_ignoring_3d_from_2d_pinhole; // If this transform is not representable as an `IsoTransform` we can't display it yet. // This would happen if the camera is under another camera or under a transform with non-uniform scale. - let Some(world_from_camera_rigid_iso) = - re_math::IsoTransform::from_mat4(&world_from_camera_rigid.into()) + let Some(world_from_camera_iso) = + re_math::IsoTransform::from_mat4(&world_from_camera.into()) else { return; }; - debug_assert!(world_from_camera_rigid_iso.is_finite()); + debug_assert!(world_from_camera_iso.is_finite()); self.space_cameras.push(SpaceCamera3D { ent_path: ent_path.clone(), pinhole_view_coordinates, - world_from_camera: world_from_camera_rigid_iso, + world_from_camera: world_from_camera_iso, pinhole: Some(pinhole.clone()), picture_plane_distance: frustum_length, }); @@ -163,8 +146,7 @@ impl CamerasVisualizer { // The frustum is setup as a RDF frustum, but if the view coordinates are not RDF, // we need to reorient the displayed frustum so that we indicate the correct orientation in the 3D world space. .world_from_obj( - world_from_camera_rigid - * glam::Affine3A::from_mat3(pinhole_view_coordinates.from_rdf()), + world_from_camera * glam::Affine3A::from_mat3(pinhole_view_coordinates.from_rdf()), ) .outline_mask_ids(entity_highlight.overall) .picking_object_id(instance_layer_id.object); @@ -183,7 +165,7 @@ impl CamerasVisualizer { self.data.add_bounding_box_from_points( ent_path.hash(), std::iter::once(glam::Vec3::ZERO), - world_from_camera_rigid, + world_from_camera, ); } } @@ -232,10 +214,6 @@ impl VisualizerSystem for CamerasVisualizer { transforms, data_result, &pinhole, - // TODO(#5607): what should happen if the promise is still pending? - ctx.recording() - .latest_at_component::(&data_result.entity_path, &time_query) - .map(|(_index, c)| c), pinhole.camera_xyz.unwrap_or(ViewCoordinates::RDF), // TODO(#2641): This should come from archetype entity_highlight, ); From 90a64cf0d3ac179ff45d237dc906b2d27ff183c7 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 26 Jul 2024 18:53:50 +0200 Subject: [PATCH 05/29] improve 2D in 3D handling --- .../re_space_view_spatial/src/contexts/mod.rs | 5 +- .../src/contexts/transform_context.rs | 70 ++++++++++--------- .../src/visualizers/cameras.rs | 13 +++- .../src/visualizers/depth_images.rs | 24 ++++--- .../src/visualizers/transform3d_arrows.rs | 15 +++- 5 files changed, 78 insertions(+), 49 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/contexts/mod.rs b/crates/viewer/re_space_view_spatial/src/contexts/mod.rs index 8ab9523761f2..4b420befb7b4 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/mod.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/mod.rs @@ -5,17 +5,16 @@ mod transform_context; pub use annotation_context::AnnotationSceneContext; pub use depth_offsets::EntityDepthOffsets; use re_types::SpaceViewClassIdentifier; -pub use transform_context::TransformContext; +pub use transform_context::{TransformContext, TransformInfo, TwoDInThreeDTransformInfo}; // ----------------------------------------------------------------------------- use re_renderer::DepthOffset; use re_viewer_context::{Annotations, SpaceViewClassRegistryError}; -use transform_context::TransformInfo; /// Context objects for a single entity in a spatial scene. pub struct SpatialSceneEntityContext<'a> { - pub world_from_entity: glam::Affine3A, + pub world_from_entity: glam::Affine3A, // TODO(andreas): replace usage with `transform_info` accessors. pub transform_info: &'a TransformInfo, pub depth_offset: DepthOffset, pub annotations: std::sync::Arc, diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index b2f411d9b16e..1a9e4439b7d5 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -23,39 +23,38 @@ pub struct TransformInfo { /// The transform from the entity to the reference space. /// /// Does not include per instance leaf transforms! - /// *Does* include 3D from 2D pinhole transform if present. + /// Include 3D-from-2D / 2D-from-3D pinhole transform if present. pub reference_from_entity: glam::Affine3A, - /// Like [`TransformInfo::reference_from_entity`], but if this entity has a pinhole camera, it won't affect the transform. - /// - /// Normally, the transform we compute for an entity with a pinhole transform places all objects - /// in front (defined by view coordinates) of the camera with a given image plane distance. - /// In some cases like drawing the lines for a frustum or arrows for the 3D transform, this is not the desired transformation. - /// - /// TODO(andreas): This a lot of overhead for something used very rarely. - /// - /// TODO(#2663, #1025): Going forward we should have separate transform hierarchies for 2D (i.e. projected) and 3D, - /// which would remove the need for this. - pub reference_from_entity_ignoring_3d_from_2d_pinhole: glam::Affine3A, - /// Optional list of out of leaf transforms that are applied to the instances of this entity. // TODO(andreas): not very widely supported yet. We may likely want to make `reference_from_instance` a thing and encapsulate stuff further. pub entity_from_instance_leaf_transforms: Vec, - /// The pinhole camera ancestor of this entity if any. + /// If this entity is under (!) a pinhole camera, this contains additional information. + /// + /// TODO(#2663, #1025): Going forward we should have separate transform hierarchies for 2D (i.e. projected) and 3D, + /// which would remove the need for this. + pub twod_in_threed_info: Option, +} + +#[derive(Clone, Debug)] +pub struct TwoDInThreeDTransformInfo { + /// Pinhole camera ancestor (may be this entity itself). /// /// None indicates that this entity is under the eye camera with no Pinhole camera in-between. /// Some indicates that the entity is under a pinhole camera at the given entity path that is not at the root of the space view. - pub parent_pinhole: Option, + pub parent_pinhole: EntityPath, + + /// The last 3D from 3D transform at the pinhole camera, before the pinhole transformation itself. + pub reference_from_pinhole_entity: glam::Affine3A, } impl Default for TransformInfo { fn default() -> Self { Self { reference_from_entity: glam::Affine3A::IDENTITY, - reference_from_entity_ignoring_3d_from_2d_pinhole: glam::Affine3A::IDENTITY, entity_from_instance_leaf_transforms: Vec::new(), - parent_pinhole: None, + twod_in_threed_info: None, } } } @@ -210,19 +209,23 @@ impl TransformContext { Ok(transforms_at_entity) => { let mut new_transform = TransformInfo { reference_from_entity: reference_from_ancestor, - reference_from_entity_ignoring_3d_from_2d_pinhole: reference_from_ancestor, entity_from_instance_leaf_transforms: transforms_at_entity .parent_from_entity_leaf_transforms .iter() .map(|&t| t.inverse()) .collect(), - parent_pinhole: encountered_pinhole.clone(), + + // Going up the tree, we can only encounter 2d->3d transforms. + // 3d->2d transforms can't happen because `Pinhole` represents 3d->2d (and we're walking backwards!) + twod_in_threed_info: None, }; // Need to take care of the fact that we're walking the other direction of the tree here compared to `gather_descendants_transforms`! if let Some(entity_from_2d_pinhole_content) = transforms_at_entity.instance_from_pinhole_image_plane { + // If we're going up the tree and encounter a pinhole, we still to apply it. + // This is what handles "3D in 2D". debug_assert!(encountered_pinhole.as_ref() == Some(¤t_tree.path)); new_transform.reference_from_entity *= entity_from_2d_pinhole_content.inverse(); @@ -234,12 +237,6 @@ impl TransformContext { parent_from_entity_tree_transform.inverse(); } - // If we're going up the tree and encounter a pinhole, we need to apply it, it means that our reference is 2D. - // So it's not acutally a "3d_from_2d" but rather a "2d_from_3d", consequently - // `reference_from_entity_ignoring_3d_from_2d_pinhole`is always the same as `reference_from_entity`. - new_transform.reference_from_entity_ignoring_3d_from_2d_pinhole = - new_transform.reference_from_entity; - new_transform } }; @@ -270,10 +267,8 @@ impl TransformContext { query: &LatestAtQuery, transform: TransformInfo, ) { - let encountered_pinhole = transform.parent_pinhole.clone(); + let twod_in_threed_info = transform.twod_in_threed_info.clone(); let reference_from_parent = transform.reference_from_entity; - let reference_from_parent_ignoring_3d_from_2d_pinhole = - transform.reference_from_entity_ignoring_3d_from_2d_pinhole; match self.transform_per_entity.entry(subtree.path.clone()) { std::collections::hash_map::Entry::Occupied(_) => { return; @@ -301,7 +296,9 @@ impl TransformContext { .into() }; - let mut encountered_pinhole = encountered_pinhole.clone(); + let mut encountered_pinhole = twod_in_threed_info + .as_ref() + .map(|info| info.parent_pinhole.clone()); let new_transform = match transforms_at( child_tree, entity_db, @@ -318,23 +315,28 @@ impl TransformContext { Ok(transforms_at_entity) => { let mut new_transform = TransformInfo { reference_from_entity: reference_from_parent, - reference_from_entity_ignoring_3d_from_2d_pinhole: - reference_from_parent_ignoring_3d_from_2d_pinhole, entity_from_instance_leaf_transforms: transforms_at_entity .parent_from_entity_leaf_transforms, - parent_pinhole: encountered_pinhole.clone(), + twod_in_threed_info: twod_in_threed_info.clone(), }; if let Some(parent_from_entity_tree_transform) = transforms_at_entity.parent_from_entity_tree_transform { new_transform.reference_from_entity *= parent_from_entity_tree_transform; - new_transform.reference_from_entity_ignoring_3d_from_2d_pinhole *= - parent_from_entity_tree_transform; } if let Some(entity_from_2d_pinhole_content) = transforms_at_entity.instance_from_pinhole_image_plane { + // This should be an unreachable transform. + debug_assert!(twod_in_threed_info.is_none()); + // There has to be a pinhole to get here. + debug_assert!(encountered_pinhole.is_some()); + + new_transform.twod_in_threed_info = Some(TwoDInThreeDTransformInfo { + parent_pinhole: child_tree.path.clone(), + reference_from_pinhole_entity: new_transform.reference_from_entity, + }); new_transform.reference_from_entity *= entity_from_2d_pinhole_content; } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs b/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs index fef514f071f8..8b42bac3fc6c 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs @@ -77,7 +77,18 @@ impl CamerasVisualizer { let Some(transform_info) = transforms.transform_info_for_entity(ent_path) else { return; }; - let world_from_camera = transform_info.reference_from_entity_ignoring_3d_from_2d_pinhole; + let Some(twod_in_threed_info) = &transform_info.twod_in_threed_info else { + // This implies that the transform context didn't see the pinhole transform. + // Should be impossible! + re_log::error_once!("Transform context didn't register the pinhole transform, but `CamerasVisualizer` is trying to display it!"); + return; + }; + if &twod_in_threed_info.parent_pinhole != ent_path { + // This implies that the camera is under another camera. + // This should be reported already as an error at this point. + return; + } + let world_from_camera = twod_in_threed_info.reference_from_pinhole_entity; // If this transform is not representable as an `IsoTransform` we can't display it yet. // This would happen if the camera is under another camera or under a transform with non-uniform scale. diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs b/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs index 97c5ea554aa3..7dea211e987e 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs @@ -18,6 +18,7 @@ use re_viewer_context::{ use crate::{ contexts::SpatialSceneEntityContext, + contexts::TwoDInThreeDTransformInfo, query_pinhole_legacy, view_kind::SpatialSpaceViewKind, visualizers::{filter_visualizable_2d_entities, SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES}, @@ -75,7 +76,7 @@ impl DepthImageVisualizer { image.colormap = Some(image.colormap.unwrap_or_else(|| self.fallback_for(ctx))); if is_3d_view { - if let Some(parent_pinhole_path) = &ent_context.transform_info.parent_pinhole { + if let Some(twod_in_threed_info) = &ent_context.transform_info.twod_in_threed_info { let fill_ratio = fill_ratio.unwrap_or_default(); // NOTE: we don't pass in `world_from_obj` because this corresponds to the @@ -86,7 +87,7 @@ impl DepthImageVisualizer { ent_context, &image, entity_path, - parent_pinhole_path, + twod_in_threed_info, depth_meter, fill_ratio, ) { @@ -145,22 +146,25 @@ impl DepthImageVisualizer { ent_context: &SpatialSceneEntityContext<'_>, image: &ImageInfo, ent_path: &EntityPath, - parent_pinhole_path: &EntityPath, + twod_in_threed_info: &TwoDInThreeDTransformInfo, depth_meter: DepthMeter, radius_scale: FillRatio, ) -> anyhow::Result { re_tracing::profile_function!(); - let Some(intrinsics) = - query_pinhole_legacy(ctx.recording(), ctx.query, parent_pinhole_path) - else { - anyhow::bail!("Couldn't fetch pinhole intrinsics at {parent_pinhole_path:?}"); + let Some(intrinsics) = query_pinhole_legacy( + ctx.recording(), + ctx.query, + &twod_in_threed_info.parent_pinhole, + ) else { + anyhow::bail!( + "Couldn't fetch pinhole intrinsics at {:?}", + twod_in_threed_info.parent_pinhole + ); }; // Place the cloud at the pinhole's location. Note that this means we ignore any 2D transforms that might be there. - let world_from_view = ent_context - .transform_info - .reference_from_entity_ignoring_3d_from_2d_pinhole; + let world_from_view = twod_in_threed_info.reference_from_pinhole_entity; let world_from_rdf = world_from_view * glam::Affine3A::from_mat3( intrinsics diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs b/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs index da408d4b5e8c..2771842af2de 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs @@ -75,7 +75,20 @@ impl VisualizerSystem for Transform3DArrowsVisualizer { else { continue; }; - let world_from_obj = transform_info.reference_from_entity_ignoring_3d_from_2d_pinhole; + let world_from_obj = + if let Some(twod_in_threed_info) = &transform_info.twod_in_threed_info { + if twod_in_threed_info.parent_pinhole != data_result.entity_path { + // We're inside a 2D space. But this is a 3D transform. + // Something is wrong here and this is not the right place to report it. + // Better just don't draw the axis! + continue; + } else { + // Don't apply the from-2D transform, stick with the last known 3D. + twod_in_threed_info.reference_from_pinhole_entity + } + } else { + transform_info.reference_from_entity + }; let results = data_result .latest_at_with_blueprint_resolved_data::(ctx, &latest_at_query); From 2e7b9488f1f93335f9eb8ea440794fa24449390f Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 29 Jul 2024 10:41:12 +0200 Subject: [PATCH 06/29] rerun-lint typo fixes --- .../re_space_view_spatial/src/contexts/transform_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index 1a9e4439b7d5..d67236ac5a4f 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -215,8 +215,8 @@ impl TransformContext { .map(|&t| t.inverse()) .collect(), - // Going up the tree, we can only encounter 2d->3d transforms. - // 3d->2d transforms can't happen because `Pinhole` represents 3d->2d (and we're walking backwards!) + // Going up the tree, we can only encounter 2D->3D transforms. + // 3D->2D transforms can't happen because `Pinhole` represents 3D->2D (and we're walking backwards!) twod_in_threed_info: None, }; From 0c540203f285e5aac96191f9dd65ac7ba64cef27 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 29 Jul 2024 10:57:05 +0200 Subject: [PATCH 07/29] Remove OutOfTreeTransform3D --- .../definitions/rerun/archetypes/asset3d.fbs | 10 +- .../re_types/definitions/rerun/components.fbs | 1 - .../components/out_of_tree_transform3d.fbs | 13 --- .../store/re_types/src/archetypes/asset3d.rs | 58 ++-------- .../re_types/src/archetypes/asset3d_ext.rs | 1 - .../re_types/src/components/.gitattributes | 1 - crates/store/re_types/src/components/mod.rs | 2 - .../src/components/out_of_tree_transform3d.rs | 109 ------------------ crates/store/re_types/tests/asset3d.rs | 30 +---- crates/top/rerun/src/sdk.rs | 4 +- .../re_data_ui/src/component_ui_registry.rs | 1 - crates/viewer/re_data_ui/src/transform3d.rs | 14 --- .../re_space_view_spatial/src/mesh_loader.rs | 6 +- .../src/visualizers/assets3d.rs | 38 +++--- crates/viewer/re_viewer/src/reflection/mod.rs | 7 -- .../reference/types/archetypes/asset3d.md | 2 - docs/content/reference/types/components.md | 1 - .../reference/types/components/.gitattributes | 1 - .../components/out_of_tree_transform3d.md | 22 ---- .../reference/types/datatypes/transform3d.md | 1 - .../all/archetypes/asset3d_out_of_tree.cpp | 39 ------- .../all/archetypes/asset3d_out_of_tree.py | 31 ----- .../all/archetypes/asset3d_out_of_tree.rs | 40 ------- .../python/signed_distance_fields/README.md | 7 +- .../signed_distance_fields/__main__.py | 7 +- rerun_cpp/src/rerun.hpp | 1 - rerun_cpp/src/rerun/archetypes/asset3d.cpp | 7 +- rerun_cpp/src/rerun/archetypes/asset3d.hpp | 15 --- rerun_cpp/src/rerun/components.hpp | 1 - rerun_cpp/src/rerun/components/.gitattributes | 1 - .../components/out_of_tree_transform3d.hpp | 73 ------------ rerun_py/rerun_sdk/rerun/__init__.py | 2 - .../rerun_sdk/rerun/archetypes/asset3d.py | 12 -- .../rerun_sdk/rerun/archetypes/asset3d_ext.py | 8 +- .../rerun_sdk/rerun/components/.gitattributes | 1 - .../rerun_sdk/rerun/components/__init__.py | 4 - .../components/out_of_tree_transform3d.py | 40 ------- rerun_py/tests/unit/test_asset3d.py | 13 --- .../check_all_components_ui.py | 7 -- 39 files changed, 38 insertions(+), 593 deletions(-) delete mode 100644 crates/store/re_types/definitions/rerun/components/out_of_tree_transform3d.fbs delete mode 100644 crates/store/re_types/src/components/out_of_tree_transform3d.rs delete mode 100644 docs/content/reference/types/components/out_of_tree_transform3d.md delete mode 100644 docs/snippets/all/archetypes/asset3d_out_of_tree.cpp delete mode 100644 docs/snippets/all/archetypes/asset3d_out_of_tree.py delete mode 100644 docs/snippets/all/archetypes/asset3d_out_of_tree.rs delete mode 100644 rerun_cpp/src/rerun/components/out_of_tree_transform3d.hpp delete mode 100644 rerun_py/rerun_sdk/rerun/components/out_of_tree_transform3d.py diff --git a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs index 974c1b4ea43b..72bbd5d306a4 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs @@ -7,9 +7,8 @@ namespace rerun.archetypes; /// See also [archetypes.Mesh3D]. /// /// \example archetypes/asset3d_simple title="Simple 3D asset" image="https://static.rerun.io/asset3d_simple/af238578188d3fd0de3e330212120e2842a8ddb2/1200w.png" -/// \example archetypes/asset3d_out_of_tree !api title="3D asset with out-of-tree transform" table Asset3D ( - "attr.rust.derive": "PartialEq", + "attr.rust.derive": "PartialEq, Eq", "attr.docs.category": "Spatial 3D", "attr.docs.view_types": "Spatial3DView, Spatial2DView: if logged above active projection" ) { @@ -31,11 +30,4 @@ table Asset3D ( /// If omitted, the viewer will try to guess from the data blob. /// If it cannot guess, it won't be able to render the asset. media_type: rerun.components.MediaType ("attr.rerun.component_recommended", nullable, order: 2000); - - // --- Optional --- - - /// An out-of-tree transform. - /// - /// Applies a transformation to the asset itself without impacting its children. - transform: rerun.components.OutOfTreeTransform3D ("attr.rerun.component_optional", nullable, order: 3000); } diff --git a/crates/store/re_types/definitions/rerun/components.fbs b/crates/store/re_types/definitions/rerun/components.fbs index 06093a49364f..5140b0b39b4b 100644 --- a/crates/store/re_types/definitions/rerun/components.fbs +++ b/crates/store/re_types/definitions/rerun/components.fbs @@ -29,7 +29,6 @@ include "./components/marker_size.fbs"; include "./components/media_type.fbs"; include "./components/name.fbs"; include "./components/opacity.fbs"; -include "./components/out_of_tree_transform3d.fbs"; include "./components/pinhole_projection.fbs"; include "./components/position2d.fbs"; include "./components/position3d.fbs"; diff --git a/crates/store/re_types/definitions/rerun/components/out_of_tree_transform3d.fbs b/crates/store/re_types/definitions/rerun/components/out_of_tree_transform3d.fbs deleted file mode 100644 index 79c5a80f5508..000000000000 --- a/crates/store/re_types/definitions/rerun/components/out_of_tree_transform3d.fbs +++ /dev/null @@ -1,13 +0,0 @@ -namespace rerun.components; - -// --- - -/// An out-of-tree affine transform between two 3D spaces, represented in a given direction. -/// -/// "Out-of-tree" means that the transform only affects its own entity: children don't inherit from it. -table OutOfTreeTransform3D ( - "attr.rust.derive": "Default, PartialEq" -) { - /// Representation of the transform. - repr: rerun.datatypes.Transform3D (order: 100); -} diff --git a/crates/store/re_types/src/archetypes/asset3d.rs b/crates/store/re_types/src/archetypes/asset3d.rs index 1c34dd174190..dcbf5293849b 100644 --- a/crates/store/re_types/src/archetypes/asset3d.rs +++ b/crates/store/re_types/src/archetypes/asset3d.rs @@ -51,7 +51,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// /// -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Asset3D { /// The asset's bytes. pub blob: crate::components::Blob, @@ -67,26 +67,17 @@ pub struct Asset3D { /// If omitted, the viewer will try to guess from the data blob. /// If it cannot guess, it won't be able to render the asset. pub media_type: Option, - - /// An out-of-tree transform. - /// - /// Applies a transformation to the asset itself without impacting its children. - pub transform: Option, } impl ::re_types_core::SizeBytes for Asset3D { #[inline] fn heap_size_bytes(&self) -> u64 { - self.blob.heap_size_bytes() - + self.media_type.heap_size_bytes() - + self.transform.heap_size_bytes() + self.blob.heap_size_bytes() + self.media_type.heap_size_bytes() } #[inline] fn is_pod() -> bool { - ::is_pod() - && >::is_pod() - && >::is_pod() + ::is_pod() && >::is_pod() } } @@ -101,22 +92,21 @@ static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 2usize]> = ] }); -static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = - once_cell::sync::Lazy::new(|| ["rerun.components.OutOfTreeTransform3D".into()]); +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 0usize]> = + once_cell::sync::Lazy::new(|| []); -static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 3usize]> = once_cell::sync::Lazy::new(|| { [ "rerun.components.Blob".into(), "rerun.components.MediaType".into(), "rerun.components.Asset3DIndicator".into(), - "rerun.components.OutOfTreeTransform3D".into(), ] }); impl Asset3D { - /// The total number of components in the archetype: 1 required, 2 recommended, 1 optional - pub const NUM_COMPONENTS: usize = 4usize; + /// The total number of components in the archetype: 1 required, 2 recommended, 0 optional + pub const NUM_COMPONENTS: usize = 3usize; } /// Indicator component for the [`Asset3D`] [`::re_types_core::Archetype`] @@ -193,21 +183,7 @@ impl ::re_types_core::Archetype for Asset3D { } else { None }; - let transform = - if let Some(array) = arrays_by_name.get("rerun.components.OutOfTreeTransform3D") { - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Asset3D#transform")? - .into_iter() - .next() - .flatten() - } else { - None - }; - Ok(Self { - blob, - media_type, - transform, - }) + Ok(Self { blob, media_type }) } } @@ -221,9 +197,6 @@ impl ::re_types_core::AsComponents for Asset3D { self.media_type .as_ref() .map(|comp| (comp as &dyn ComponentBatch).into()), - self.transform - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch).into()), ] .into_iter() .flatten() @@ -238,7 +211,6 @@ impl Asset3D { Self { blob: blob.into(), media_type: None, - transform: None, } } @@ -257,16 +229,4 @@ impl Asset3D { self.media_type = Some(media_type.into()); self } - - /// An out-of-tree transform. - /// - /// Applies a transformation to the asset itself without impacting its children. - #[inline] - pub fn with_transform( - mut self, - transform: impl Into, - ) -> Self { - self.transform = Some(transform.into()); - self - } } diff --git a/crates/store/re_types/src/archetypes/asset3d_ext.rs b/crates/store/re_types/src/archetypes/asset3d_ext.rs index ad11edb9ba5b..78fd8a78f998 100644 --- a/crates/store/re_types/src/archetypes/asset3d_ext.rs +++ b/crates/store/re_types/src/archetypes/asset3d_ext.rs @@ -36,7 +36,6 @@ impl Asset3D { Self { blob: contents.into(), media_type, - transform: None, } } } diff --git a/crates/store/re_types/src/components/.gitattributes b/crates/store/re_types/src/components/.gitattributes index bac7129fe59b..7b321253b632 100644 --- a/crates/store/re_types/src/components/.gitattributes +++ b/crates/store/re_types/src/components/.gitattributes @@ -35,7 +35,6 @@ media_type.rs linguist-generated=true mod.rs linguist-generated=true name.rs linguist-generated=true opacity.rs linguist-generated=true -out_of_tree_transform3d.rs linguist-generated=true pinhole_projection.rs linguist-generated=true position2d.rs linguist-generated=true position3d.rs linguist-generated=true diff --git a/crates/store/re_types/src/components/mod.rs b/crates/store/re_types/src/components/mod.rs index 4609ff285314..47b442724b8c 100644 --- a/crates/store/re_types/src/components/mod.rs +++ b/crates/store/re_types/src/components/mod.rs @@ -60,7 +60,6 @@ mod name; mod name_ext; mod opacity; mod opacity_ext; -mod out_of_tree_transform3d; mod pinhole_projection; mod pinhole_projection_ext; mod position2d; @@ -147,7 +146,6 @@ pub use self::marker_size::MarkerSize; pub use self::media_type::MediaType; pub use self::name::Name; pub use self::opacity::Opacity; -pub use self::out_of_tree_transform3d::OutOfTreeTransform3D; pub use self::pinhole_projection::PinholeProjection; pub use self::position2d::Position2D; pub use self::position3d::Position3D; diff --git a/crates/store/re_types/src/components/out_of_tree_transform3d.rs b/crates/store/re_types/src/components/out_of_tree_transform3d.rs deleted file mode 100644 index ab1ca3b068cb..000000000000 --- a/crates/store/re_types/src/components/out_of_tree_transform3d.rs +++ /dev/null @@ -1,109 +0,0 @@ -// 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/components/out_of_tree_transform3d.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::ComponentName; -use ::re_types_core::SerializationResult; -use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; -use ::re_types_core::{DeserializationError, DeserializationResult}; - -/// **Component**: An out-of-tree affine transform between two 3D spaces, represented in a given direction. -/// -/// "Out-of-tree" means that the transform only affects its own entity: children don't inherit from it. -#[derive(Clone, Debug, Default, PartialEq)] -pub struct OutOfTreeTransform3D( - /// Representation of the transform. - pub crate::datatypes::Transform3D, -); - -impl ::re_types_core::SizeBytes for OutOfTreeTransform3D { - #[inline] - fn heap_size_bytes(&self) -> u64 { - self.0.heap_size_bytes() - } - - #[inline] - fn is_pod() -> bool { - ::is_pod() - } -} - -impl> From for OutOfTreeTransform3D { - fn from(v: T) -> Self { - Self(v.into()) - } -} - -impl std::borrow::Borrow for OutOfTreeTransform3D { - #[inline] - fn borrow(&self) -> &crate::datatypes::Transform3D { - &self.0 - } -} - -impl std::ops::Deref for OutOfTreeTransform3D { - type Target = crate::datatypes::Transform3D; - - #[inline] - fn deref(&self) -> &crate::datatypes::Transform3D { - &self.0 - } -} - -impl std::ops::DerefMut for OutOfTreeTransform3D { - #[inline] - fn deref_mut(&mut self) -> &mut crate::datatypes::Transform3D { - &mut self.0 - } -} - -::re_types_core::macros::impl_into_cow!(OutOfTreeTransform3D); - -impl ::re_types_core::Loggable for OutOfTreeTransform3D { - type Name = ::re_types_core::ComponentName; - - #[inline] - fn name() -> Self::Name { - "rerun.components.OutOfTreeTransform3D".into() - } - - #[inline] - fn arrow_datatype() -> arrow2::datatypes::DataType { - crate::datatypes::Transform3D::arrow_datatype() - } - - fn to_arrow_opt<'a>( - data: impl IntoIterator>>>, - ) -> SerializationResult> - where - Self: Clone + 'a, - { - crate::datatypes::Transform3D::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_arrow_opt( - arrow_data: &dyn arrow2::array::Array, - ) -> DeserializationResult>> - where - Self: Sized, - { - crate::datatypes::Transform3D::from_arrow_opt(arrow_data) - .map(|v| v.into_iter().map(|v| v.map(Self)).collect()) - } -} diff --git a/crates/store/re_types/tests/asset3d.rs b/crates/store/re_types/tests/asset3d.rs index 113b6c6e34f0..0b697446340c 100644 --- a/crates/store/re_types/tests/asset3d.rs +++ b/crates/store/re_types/tests/asset3d.rs @@ -1,11 +1,7 @@ -use std::f32::consts::TAU; - use re_types::{ archetypes::Asset3D, - components::{Blob, MediaType, OutOfTreeTransform3D}, - datatypes::{ - Angle, Rotation3D, RotationAxisAngle, Scale3D, TranslationRotationScale3D, Utf8, Vec3D, - }, + components::{Blob, MediaType}, + datatypes::Utf8, Archetype as _, AsComponents as _, }; @@ -16,29 +12,9 @@ fn roundtrip() { let expected = Asset3D { blob: Blob(BYTES.to_vec().into()), media_type: Some(MediaType(Utf8(MediaType::GLTF.into()))), - transform: Some(OutOfTreeTransform3D( - re_types::datatypes::Transform3D::TranslationRotationScale( - TranslationRotationScale3D { - translation: Some(Vec3D([1.0, 2.0, 3.0])), - rotation: Some(Rotation3D::AxisAngle(RotationAxisAngle { - axis: Vec3D([0.2, 0.2, 0.8]), - angle: Angle::from_radians(0.5 * TAU), - })), - scale: Some(Scale3D::Uniform(42.0)), - from_parent: true, - }, - ), - )), // }; - let arch = Asset3D::from_file_contents(BYTES.to_vec(), Some(MediaType::gltf())).with_transform( - re_types::datatypes::Transform3D::from_translation_rotation_scale( - [1.0, 2.0, 3.0], - RotationAxisAngle::new([0.2, 0.2, 0.8], Angle::from_radians(0.5 * TAU)), - 42.0, - ) - .from_parent(), - ); + let arch = Asset3D::from_file_contents(BYTES.to_vec(), Some(MediaType::gltf())); similar_asserts::assert_eq!(expected, arch); // let expected_extensions: HashMap<_, _> = [ diff --git a/crates/top/rerun/src/sdk.rs b/crates/top/rerun/src/sdk.rs index 8c91db5388a0..fa335708472f 100644 --- a/crates/top/rerun/src/sdk.rs +++ b/crates/top/rerun/src/sdk.rs @@ -27,8 +27,8 @@ mod prelude { pub use re_chunk::ChunkTimeline; pub use re_types::components::{ AlbedoFactor, Color, FillMode, HalfSize2D, HalfSize3D, LineStrip2D, LineStrip3D, MediaType, - OutOfTreeTransform3D, Position2D, Position3D, Radius, Scale3D, Text, TextLogLevel, - TransformRelation, TriangleIndices, Vector2D, Vector3D, + Position2D, Position3D, Radius, Scale3D, Text, TextLogLevel, TransformRelation, + TriangleIndices, Vector2D, Vector3D, }; pub use re_types::datatypes::{ Angle, AnnotationInfo, ClassDescription, Float32, KeypointPair, Mat3x3, Quaternion, Rgba32, diff --git a/crates/viewer/re_data_ui/src/component_ui_registry.rs b/crates/viewer/re_data_ui/src/component_ui_registry.rs index c3bb679819e1..79a185606d3d 100644 --- a/crates/viewer/re_data_ui/src/component_ui_registry.rs +++ b/crates/viewer/re_data_ui/src/component_ui_registry.rs @@ -22,7 +22,6 @@ pub fn create_component_ui_registry() -> ComponentUiRegistry { add_to_registry::(&mut registry); add_to_registry::(&mut registry); add_to_registry::(&mut registry); - add_to_registry::(&mut registry); add_to_registry::(&mut registry); add_to_registry::(&mut registry); diff --git a/crates/viewer/re_data_ui/src/transform3d.rs b/crates/viewer/re_data_ui/src/transform3d.rs index ac62199d3097..65e89ca80d4a 100644 --- a/crates/viewer/re_data_ui/src/transform3d.rs +++ b/crates/viewer/re_data_ui/src/transform3d.rs @@ -45,20 +45,6 @@ impl DataUi for re_types::components::Transform3D { } } -impl DataUi for re_types::components::OutOfTreeTransform3D { - #[inline] - fn data_ui( - &self, - ctx: &ViewerContext<'_>, - ui: &mut egui::Ui, - ui_layout: UiLayout, - query: &re_chunk_store::LatestAtQuery, - db: &re_entity_db::EntityDb, - ) { - re_types::components::Transform3D(self.0).data_ui(ctx, ui, ui_layout, query, db); - } -} - impl DataUi for Transform3D { #[allow(clippy::only_used_in_recursion)] fn data_ui( diff --git a/crates/viewer/re_space_view_spatial/src/mesh_loader.rs b/crates/viewer/re_space_view_spatial/src/mesh_loader.rs index df0a01b68c11..1949cdbe4b1b 100644 --- a/crates/viewer/re_space_view_spatial/src/mesh_loader.rs +++ b/crates/viewer/re_space_view_spatial/src/mesh_loader.rs @@ -73,11 +73,7 @@ impl LoadedMesh { ) -> anyhow::Result { re_tracing::profile_function!(); - let Asset3D { - blob, - media_type, - transform: _, - } = asset3d; + let Asset3D { blob, media_type } = asset3d; let media_type = MediaType::or_guess_from_data(media_type.clone(), blob.as_slice()) .ok_or_else(|| anyhow::anyhow!("couldn't guess media type"))?; diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs index 2c7904873d99..45cd9fe28b1b 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs @@ -1,11 +1,11 @@ use re_chunk_store::RowId; use re_log_types::{hash::Hash64, Instance, TimeInt}; -use re_query::range_zip_1x2; +use re_query::range_zip_1x1; use re_renderer::renderer::MeshInstance; use re_renderer::RenderContext; use re_types::{ archetypes::Asset3D, - components::{Blob, MediaType, OutOfTreeTransform3D}, + components::{Blob, MediaType}, }; use re_viewer_context::{ ApplicableEntities, IdentifiedViewSystem, QueryContext, SpaceViewSystemExecutionError, @@ -37,7 +37,6 @@ struct Asset3DComponentData<'a> { blob: &'a Blob, media_type: Option<&'a MediaType>, - transform: Option<&'a OutOfTreeTransform3D>, } // NOTE: Do not put profile scopes in these methods. They are called for all entities and all @@ -57,9 +56,6 @@ impl Asset3DVisualizer { let mesh = Asset3D { blob: data.blob.clone(), media_type: data.media_type.cloned(), - - // NOTE: Don't even try to cache the transform! - transform: None, }; let primary_row_id = data.index.1; @@ -86,9 +82,11 @@ impl Asset3DVisualizer { re_tracing::profile_scope!("mesh instances"); let world_from_pose = ent_context.world_from_entity - * data - .transform - .map_or(glam::Affine3A::IDENTITY, |t| t.0.into()); + * *ent_context + .transform_info + .entity_from_instance_leaf_transforms + .first() + .unwrap_or(&glam::Affine3A::IDENTITY); instances.extend(mesh.mesh_instances.iter().map(move |mesh_instance| { let pose_from_mesh = mesh_instance.world_from_mesh; @@ -159,21 +157,15 @@ impl VisualizerSystem for Asset3DVisualizer { }; let media_types = results.get_or_empty_dense(resolver)?; - let transforms = results.get_or_empty_dense(resolver)?; - let data = range_zip_1x2( - blobs.range_indexed(), - media_types.range_indexed(), - transforms.range_indexed(), - ) - .filter_map(|(&index, blobs, media_types, transforms)| { - blobs.first().map(|blob| Asset3DComponentData { - index, - blob, - media_type: media_types.and_then(|media_types| media_types.first()), - transform: transforms.and_then(|transforms| transforms.first()), - }) - }); + let data = range_zip_1x1(blobs.range_indexed(), media_types.range_indexed()) + .filter_map(|(&index, blobs, media_types)| { + blobs.first().map(|blob| Asset3DComponentData { + index, + blob, + media_type: media_types.and_then(|media_types| media_types.first()), + }) + }); self.process_data(ctx, render_ctx, &mut instances, spatial_ctx, data); Ok(()) diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index f491a2ce0d85..e8a30916df29 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -475,13 +475,6 @@ fn generate_component_reflection() -> Result::name(), - ComponentReflection { - docstring_md: "An out-of-tree affine transform between two 3D spaces, represented in a given direction.\n\n\"Out-of-tree\" means that the transform only affects its own entity: children don't inherit from it.", - placeholder: Some(OutOfTreeTransform3D::default().to_arrow()?), - }, - ), ( ::name(), ComponentReflection { diff --git a/docs/content/reference/types/archetypes/asset3d.md b/docs/content/reference/types/archetypes/asset3d.md index 880727f1cfd1..8208b63d581e 100644 --- a/docs/content/reference/types/archetypes/asset3d.md +++ b/docs/content/reference/types/archetypes/asset3d.md @@ -13,8 +13,6 @@ See also [`archetypes.Mesh3D`](https://rerun.io/docs/reference/types/archetypes/ **Recommended**: [`MediaType`](../components/media_type.md) -**Optional**: [`OutOfTreeTransform3D`](../components/out_of_tree_transform3d.md) - ## Shown in * [Spatial3DView](../views/spatial3d_view.md) * [Spatial2DView](../views/spatial2d_view.md) (if logged above active projection) diff --git a/docs/content/reference/types/components.md b/docs/content/reference/types/components.md index f8f43dbb7e89..3a2e31e2e8b0 100644 --- a/docs/content/reference/types/components.md +++ b/docs/content/reference/types/components.md @@ -47,7 +47,6 @@ on [Entities and Components](../../concepts/entity-component.md). * [`MediaType`](components/media_type.md): A standardized media type (RFC2046, formerly known as MIME types), encoded as a string. * [`Name`](components/name.md): A display name, typically for an entity or a item like a plot series. * [`Opacity`](components/opacity.md): Degree of transparency ranging from 0.0 (fully transparent) to 1.0 (fully opaque). -* [`OutOfTreeTransform3D`](components/out_of_tree_transform3d.md): An out-of-tree affine transform between two 3D spaces, represented in a given direction. * [`PinholeProjection`](components/pinhole_projection.md): Camera projection, from image coordinates to view coordinates. * [`Position2D`](components/position2d.md): A position in 2D space. * [`Position3D`](components/position3d.md): A position in 3D space. diff --git a/docs/content/reference/types/components/.gitattributes b/docs/content/reference/types/components/.gitattributes index c6754973864d..b4fe052cf41f 100644 --- a/docs/content/reference/types/components/.gitattributes +++ b/docs/content/reference/types/components/.gitattributes @@ -35,7 +35,6 @@ marker_size.md linguist-generated=true media_type.md linguist-generated=true name.md linguist-generated=true opacity.md linguist-generated=true -out_of_tree_transform3d.md linguist-generated=true pinhole_projection.md linguist-generated=true position2d.md linguist-generated=true position3d.md linguist-generated=true diff --git a/docs/content/reference/types/components/out_of_tree_transform3d.md b/docs/content/reference/types/components/out_of_tree_transform3d.md deleted file mode 100644 index ca8ec12b7174..000000000000 --- a/docs/content/reference/types/components/out_of_tree_transform3d.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: "OutOfTreeTransform3D" ---- - - -An out-of-tree affine transform between two 3D spaces, represented in a given direction. - -"Out-of-tree" means that the transform only affects its own entity: children don't inherit from it. - -## Fields - -* repr: [`Transform3D`](../datatypes/transform3d.md) - -## API reference links - * 🌊 [C++ API docs for `OutOfTreeTransform3D`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1OutOfTreeTransform3D.html) - * 🐍 [Python API docs for `OutOfTreeTransform3D`](https://ref.rerun.io/docs/python/stable/common/components#rerun.components.OutOfTreeTransform3D) - * 🦀 [Rust API docs for `OutOfTreeTransform3D`](https://docs.rs/rerun/latest/rerun/components/struct.OutOfTreeTransform3D.html) - - -## Used by - -* [`Asset3D`](../archetypes/asset3d.md) diff --git a/docs/content/reference/types/datatypes/transform3d.md b/docs/content/reference/types/datatypes/transform3d.md index 970ccd641ce2..443132c49da3 100644 --- a/docs/content/reference/types/datatypes/transform3d.md +++ b/docs/content/reference/types/datatypes/transform3d.md @@ -17,5 +17,4 @@ Representation of a 3D affine transform. ## Used by -* [`OutOfTreeTransform3D`](../components/out_of_tree_transform3d.md) * [`Transform3D`](../components/transform3d.md) diff --git a/docs/snippets/all/archetypes/asset3d_out_of_tree.cpp b/docs/snippets/all/archetypes/asset3d_out_of_tree.cpp deleted file mode 100644 index 4158d9364e8e..000000000000 --- a/docs/snippets/all/archetypes/asset3d_out_of_tree.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Log a simple 3D asset with an out-of-tree transform which will not affect its children. - -#include -#include - -#include -#include - -int main(int argc, char** argv) { - if (argc < 2) { - throw std::runtime_error("Usage: "); - } - const auto path = argv[1]; - - const auto rec = rerun::RecordingStream("rerun_example_asset3d_out_of_tree"); - rec.spawn().exit_on_failure(); - - rec.log_static("world", rerun::ViewCoordinates::RIGHT_HAND_Z_UP); // Set an up-axis - - rec.set_time_sequence("frame", 0); - rec.log("world/asset", rerun::Asset3D::from_file(path).value_or_throw()); - // Those points will not be affected by their parent's out-of-tree transform! - rec.log( - "world/asset/points", - rerun::Points3D(rerun::demo::grid3d(-10.0f, 10.0f, 10)) - ); - - for (int64_t i = 1; i < 20; ++i) { - rec.set_time_sequence("frame", i); - - // Modify the asset's out-of-tree transform: this will not affect its children (i.e. the points)! - const auto translation = - rerun::TranslationRotationScale3D({0.0, 0.0, static_cast(i) - 10.0f}); - rec.log( - "world/asset", - rerun::Collection(rerun::OutOfTreeTransform3D(translation)) - ); - } -} diff --git a/docs/snippets/all/archetypes/asset3d_out_of_tree.py b/docs/snippets/all/archetypes/asset3d_out_of_tree.py deleted file mode 100644 index 35dd272dfcb6..000000000000 --- a/docs/snippets/all/archetypes/asset3d_out_of_tree.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Log a simple 3D asset with an out-of-tree transform which will not affect its children.""" - -import sys - -import numpy as np -import rerun as rr -from rerun.components import OutOfTreeTransform3DBatch -from rerun.datatypes import TranslationRotationScale3D - -if len(sys.argv) < 2: - print(f"Usage: {sys.argv[0]} ") - sys.exit(1) - -rr.init("rerun_example_asset3d_out_of_tree", spawn=True) - -rr.log("world", rr.ViewCoordinates.RIGHT_HAND_Z_UP, static=True) # Set an up-axis - -rr.set_time_sequence("frame", 0) -rr.log("world/asset", rr.Asset3D(path=sys.argv[1])) -# Those points will not be affected by their parent's out-of-tree transform! -rr.log( - "world/asset/points", - rr.Points3D(np.vstack([xyz.ravel() for xyz in np.mgrid[3 * [slice(-10, 10, 10j)]]]).T), -) - -asset = rr.Asset3D(path=sys.argv[1]) -for i in range(1, 20): - rr.set_time_sequence("frame", i) - - translation = TranslationRotationScale3D(translation=[0, 0, i - 10.0]) - rr.log_components("world/asset", [OutOfTreeTransform3DBatch(translation)]) diff --git a/docs/snippets/all/archetypes/asset3d_out_of_tree.rs b/docs/snippets/all/archetypes/asset3d_out_of_tree.rs deleted file mode 100644 index 8673eeb4a73d..000000000000 --- a/docs/snippets/all/archetypes/asset3d_out_of_tree.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Log a simple 3D asset with an out-of-tree transform which will not affect its children. - -use rerun::{ - demo_util::grid, - external::{anyhow, glam}, -}; - -fn main() -> anyhow::Result<()> { - let args = std::env::args().collect::>(); - let Some(path) = args.get(1) else { - anyhow::bail!("Usage: {} ", args[0]); - }; - - let rec = rerun::RecordingStreamBuilder::new("rerun_example_asset3d_out_of_tree").spawn()?; - - rec.log_static("world", &rerun::ViewCoordinates::RIGHT_HAND_Z_UP)?; // Set an up-axis - - rec.set_time_sequence("frame", 0); - rec.log("world/asset", &rerun::Asset3D::from_file(path)?)?; - // Those points will not be affected by their parent's out-of-tree transform! - rec.log( - "world/asset/points", - &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)), - )?; - - for i in 1..20 { - rec.set_time_sequence("frame", i); - - // Modify the asset's out-of-tree transform: this will not affect its children (i.e. the points)! - let translation = - rerun::TranslationRotationScale3D::from_translation([0.0, 0.0, i as f32 - 10.0]); - rec.log_component_batches( - "world/asset", - false, - [&rerun::OutOfTreeTransform3D::from(translation) as _], - )?; - } - - Ok(()) -} diff --git a/examples/python/signed_distance_fields/README.md b/examples/python/signed_distance_fields/README.md index 8589a6b210ad..df9a256b2135 100644 --- a/examples/python/signed_distance_fields/README.md +++ b/examples/python/signed_distance_fields/README.md @@ -40,10 +40,9 @@ center = bs2.center - bs1.center * scale ``` ```python -# Logging the 3D asset with the unit sphere -mesh3d = rr.Asset3D(path=path) -mesh3d.transform = rr.OutOfTreeTransform3DBatch(rr.TranslationRotationScale3D(translation=center, scale=scale)) -rr.log("world/mesh", mesh3d) +# Logging the 3D asset within the unit sphere +rr.log("world/mesh", rr.Asset3D(path=path)) +rr.log("world/mesh", rr.Transform3D(translation=center, scale=scale)) ``` ### Sample SDF diff --git a/examples/python/signed_distance_fields/signed_distance_fields/__main__.py b/examples/python/signed_distance_fields/signed_distance_fields/__main__.py index 108ab74e3b75..17adac1a5357 100755 --- a/examples/python/signed_distance_fields/signed_distance_fields/__main__.py +++ b/examples/python/signed_distance_fields/signed_distance_fields/__main__.py @@ -89,11 +89,8 @@ def log_mesh(path: Path, mesh: Trimesh) -> None: scale = bs2.scale / bs1.scale center = bs2.center - bs1.center * scale - mesh3d = rr.Asset3D(path=path) - mesh3d.transform = rr.OutOfTreeTransform3DBatch( - rr.datatypes.TranslationRotationScale3D(translation=center, scale=scale) - ) - rr.log("world/mesh", mesh3d) + rr.log("world/mesh", rr.Asset3D(path=path)) + rr.log("world/mesh", rr.Transform3D(translation=center, scale=scale)) def log_sampled_sdf(points: npt.NDArray[np.float32], sdf: npt.NDArray[np.float32]) -> None: diff --git a/rerun_cpp/src/rerun.hpp b/rerun_cpp/src/rerun.hpp index 261d1c7ef43e..5a82929634e9 100644 --- a/rerun_cpp/src/rerun.hpp +++ b/rerun_cpp/src/rerun.hpp @@ -38,7 +38,6 @@ namespace rerun { using components::LineStrip2D; using components::LineStrip3D; using components::MediaType; - using components::OutOfTreeTransform3D; using components::Position2D; using components::Position3D; using components::Radius; diff --git a/rerun_cpp/src/rerun/archetypes/asset3d.cpp b/rerun_cpp/src/rerun/archetypes/asset3d.cpp index 0a02b9aec381..55bd9196dc2a 100644 --- a/rerun_cpp/src/rerun/archetypes/asset3d.cpp +++ b/rerun_cpp/src/rerun/archetypes/asset3d.cpp @@ -14,7 +14,7 @@ namespace rerun { ) { using namespace archetypes; std::vector cells; - cells.reserve(4); + cells.reserve(3); { auto result = DataCell::from_loggable(archetype.blob); @@ -26,11 +26,6 @@ namespace rerun { RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } - if (archetype.transform.has_value()) { - auto result = DataCell::from_loggable(archetype.transform.value()); - RR_RETURN_NOT_OK(result.error); - cells.push_back(std::move(result.value)); - } { auto indicator = Asset3D::IndicatorComponent(); auto result = DataCell::from_loggable(indicator); diff --git a/rerun_cpp/src/rerun/archetypes/asset3d.hpp b/rerun_cpp/src/rerun/archetypes/asset3d.hpp index 22131f95f6e9..c8ef4757bdb3 100644 --- a/rerun_cpp/src/rerun/archetypes/asset3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/asset3d.hpp @@ -7,7 +7,6 @@ #include "../compiler_utils.hpp" #include "../components/blob.hpp" #include "../components/media_type.hpp" -#include "../components/out_of_tree_transform3d.hpp" #include "../data_cell.hpp" #include "../indicator_component.hpp" #include "../result.hpp" @@ -66,11 +65,6 @@ namespace rerun::archetypes { /// If it cannot guess, it won't be able to render the asset. std::optional media_type; - /// An out-of-tree transform. - /// - /// Applies a transformation to the asset itself without impacting its children. - std::optional transform; - public: static constexpr const char IndicatorComponentName[] = "rerun.components.Asset3DIndicator"; @@ -127,15 +121,6 @@ namespace rerun::archetypes { // See: https://github.com/rerun-io/rerun/issues/4027 RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } - - /// An out-of-tree transform. - /// - /// Applies a transformation to the asset itself without impacting its children. - Asset3D with_transform(rerun::components::OutOfTreeTransform3D _transform) && { - transform = std::move(_transform); - // See: https://github.com/rerun-io/rerun/issues/4027 - RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) - } }; } // namespace rerun::archetypes diff --git a/rerun_cpp/src/rerun/components.hpp b/rerun_cpp/src/rerun/components.hpp index fe3940f822da..a579c12e7828 100644 --- a/rerun_cpp/src/rerun/components.hpp +++ b/rerun_cpp/src/rerun/components.hpp @@ -36,7 +36,6 @@ #include "components/media_type.hpp" #include "components/name.hpp" #include "components/opacity.hpp" -#include "components/out_of_tree_transform3d.hpp" #include "components/pinhole_projection.hpp" #include "components/position2d.hpp" #include "components/position3d.hpp" diff --git a/rerun_cpp/src/rerun/components/.gitattributes b/rerun_cpp/src/rerun/components/.gitattributes index f3ca1a95432a..943e213436a7 100644 --- a/rerun_cpp/src/rerun/components/.gitattributes +++ b/rerun_cpp/src/rerun/components/.gitattributes @@ -45,7 +45,6 @@ marker_size.hpp linguist-generated=true media_type.hpp linguist-generated=true name.hpp linguist-generated=true opacity.hpp linguist-generated=true -out_of_tree_transform3d.hpp linguist-generated=true pinhole_projection.hpp linguist-generated=true position2d.hpp linguist-generated=true position3d.hpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/components/out_of_tree_transform3d.hpp b/rerun_cpp/src/rerun/components/out_of_tree_transform3d.hpp deleted file mode 100644 index 131fb9dfe56a..000000000000 --- a/rerun_cpp/src/rerun/components/out_of_tree_transform3d.hpp +++ /dev/null @@ -1,73 +0,0 @@ -// 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/components/out_of_tree_transform3d.fbs". - -#pragma once - -#include "../datatypes/transform3d.hpp" -#include "../datatypes/translation_rotation_scale3d.hpp" -#include "../result.hpp" - -#include -#include - -namespace rerun::components { - /// **Component**: An out-of-tree affine transform between two 3D spaces, represented in a given direction. - /// - /// "Out-of-tree" means that the transform only affects its own entity: children don't inherit from it. - struct OutOfTreeTransform3D { - /// Representation of the transform. - rerun::datatypes::Transform3D repr; - - public: - OutOfTreeTransform3D() = default; - - OutOfTreeTransform3D(rerun::datatypes::Transform3D repr_) : repr(repr_) {} - - OutOfTreeTransform3D& operator=(rerun::datatypes::Transform3D repr_) { - repr = repr_; - return *this; - } - - OutOfTreeTransform3D(rerun::datatypes::TranslationRotationScale3D TranslationRotationScale_) - : repr(TranslationRotationScale_) {} - - OutOfTreeTransform3D& operator=( - rerun::datatypes::TranslationRotationScale3D TranslationRotationScale_ - ) { - repr = TranslationRotationScale_; - return *this; - } - - /// Cast to the underlying Transform3D datatype - operator rerun::datatypes::Transform3D() const { - return repr; - } - }; -} // namespace rerun::components - -namespace rerun { - static_assert( - sizeof(rerun::datatypes::Transform3D) == sizeof(components::OutOfTreeTransform3D) - ); - - /// \private - template <> - struct Loggable { - static constexpr const char Name[] = "rerun.components.OutOfTreeTransform3D"; - - /// 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::OutOfTreeTransform3D` into an arrow array. - static Result> to_arrow( - const components::OutOfTreeTransform3D* instances, size_t num_instances - ) { - return Loggable::to_arrow( - &instances->repr, - num_instances - ); - } - }; -} // namespace rerun diff --git a/rerun_py/rerun_sdk/rerun/__init__.py b/rerun_py/rerun_sdk/rerun/__init__.py index 906fc4db983c..2a63264cc313 100644 --- a/rerun_py/rerun_sdk/rerun/__init__.py +++ b/rerun_py/rerun_sdk/rerun/__init__.py @@ -76,8 +76,6 @@ from .components import ( AlbedoFactor as AlbedoFactor, MediaType as MediaType, - OutOfTreeTransform3D as OutOfTreeTransform3D, - OutOfTreeTransform3DBatch as OutOfTreeTransform3DBatch, Radius as Radius, Scale3D as Scale3D, TensorDimensionIndexSelection as TensorDimensionIndexSelection, diff --git a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py index abe22e53df63..04c7330b53c2 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py @@ -59,7 +59,6 @@ def __attrs_clear__(self) -> None: self.__attrs_init__( blob=None, # type: ignore[arg-type] media_type=None, # type: ignore[arg-type] - transform=None, # type: ignore[arg-type] ) @classmethod @@ -95,16 +94,5 @@ def _clear(cls) -> Asset3D: # # (Docstring intentionally commented out to hide this field from the docs) - transform: components.OutOfTreeTransform3DBatch | None = field( - metadata={"component": "optional"}, - default=None, - converter=components.OutOfTreeTransform3DBatch._optional, # type: ignore[misc] - ) - # An out-of-tree transform. - # - # Applies a transformation to the asset itself without impacting its children. - # - # (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/archetypes/asset3d_ext.py b/rerun_py/rerun_sdk/rerun/archetypes/asset3d_ext.py index 92e7882d510d..d47ba735620e 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/asset3d_ext.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/asset3d_ext.py @@ -35,7 +35,6 @@ def __init__( path: str | pathlib.Path | None = None, contents: datatypes.BlobLike | None = None, media_type: datatypes.Utf8Like | None = None, - transform: datatypes.Transform3DLike | None = None, ): """ Create a new instance of the Asset3D archetype. @@ -63,11 +62,6 @@ def __init__( or the viewer will try to guess from the contents (magic header). If the media type cannot be guessed, the viewer won't be able to render the asset. - transform: - An out-of-tree transform. - - Applies a transformation to the asset itself without impacting its children. - """ with catch_and_log_exceptions(context=self.__class__.__name__): @@ -81,7 +75,7 @@ def __init__( if media_type is None: media_type = guess_media_type(str(path)) - self.__attrs_init__(blob=blob, media_type=media_type, transform=transform) + self.__attrs_init__(blob=blob, media_type=media_type) return self.__attrs_clear__() diff --git a/rerun_py/rerun_sdk/rerun/components/.gitattributes b/rerun_py/rerun_sdk/rerun/components/.gitattributes index 85ea8ef2bc96..5240df29aa9e 100644 --- a/rerun_py/rerun_sdk/rerun/components/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/components/.gitattributes @@ -36,7 +36,6 @@ marker_size.py linguist-generated=true media_type.py linguist-generated=true name.py linguist-generated=true opacity.py linguist-generated=true -out_of_tree_transform3d.py linguist-generated=true pinhole_projection.py linguist-generated=true position2d.py linguist-generated=true position3d.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 9ea13fe6eb6d..1f1a7da35b77 100644 --- a/rerun_py/rerun_sdk/rerun/components/__init__.py +++ b/rerun_py/rerun_sdk/rerun/components/__init__.py @@ -60,7 +60,6 @@ from .media_type import MediaType, MediaTypeBatch, MediaTypeType from .name import Name, NameBatch, NameType from .opacity import Opacity, OpacityBatch, OpacityType -from .out_of_tree_transform3d import OutOfTreeTransform3D, OutOfTreeTransform3DBatch, OutOfTreeTransform3DType from .pinhole_projection import PinholeProjection, PinholeProjectionBatch, PinholeProjectionType from .position2d import Position2D, Position2DBatch, Position2DType from .position3d import Position3D, Position3DBatch, Position3DType @@ -223,9 +222,6 @@ "Opacity", "OpacityBatch", "OpacityType", - "OutOfTreeTransform3D", - "OutOfTreeTransform3DBatch", - "OutOfTreeTransform3DType", "PinholeProjection", "PinholeProjectionBatch", "PinholeProjectionType", diff --git a/rerun_py/rerun_sdk/rerun/components/out_of_tree_transform3d.py b/rerun_py/rerun_sdk/rerun/components/out_of_tree_transform3d.py deleted file mode 100644 index 26de7e765666..000000000000 --- a/rerun_py/rerun_sdk/rerun/components/out_of_tree_transform3d.py +++ /dev/null @@ -1,40 +0,0 @@ -# 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/components/out_of_tree_transform3d.fbs". - -# You can extend this class by creating a "OutOfTreeTransform3DExt" class in "out_of_tree_transform3d_ext.py". - -from __future__ import annotations - -from .. import datatypes -from .._baseclasses import ( - ComponentBatchMixin, - ComponentMixin, -) - -__all__ = ["OutOfTreeTransform3D", "OutOfTreeTransform3DBatch", "OutOfTreeTransform3DType"] - - -class OutOfTreeTransform3D(datatypes.Transform3D, ComponentMixin): - """ - **Component**: An out-of-tree affine transform between two 3D spaces, represented in a given direction. - - "Out-of-tree" means that the transform only affects its own entity: children don't inherit from it. - """ - - _BATCH_TYPE = None - # You can define your own __init__ function as a member of OutOfTreeTransform3DExt in out_of_tree_transform3d_ext.py - - # Note: there are no fields here because OutOfTreeTransform3D delegates to datatypes.Transform3D - pass - - -class OutOfTreeTransform3DType(datatypes.Transform3DType): - _TYPE_NAME: str = "rerun.components.OutOfTreeTransform3D" - - -class OutOfTreeTransform3DBatch(datatypes.Transform3DBatch, ComponentBatchMixin): - _ARROW_TYPE = OutOfTreeTransform3DType() - - -# This is patched in late to avoid circular dependencies. -OutOfTreeTransform3D._BATCH_TYPE = OutOfTreeTransform3DBatch # type: ignore[assignment] diff --git a/rerun_py/tests/unit/test_asset3d.py b/rerun_py/tests/unit/test_asset3d.py index 24ed86b448a2..e50beae90f03 100644 --- a/rerun_py/tests/unit/test_asset3d.py +++ b/rerun_py/tests/unit/test_asset3d.py @@ -26,16 +26,3 @@ def test_asset3d() -> None: for asset in assets: assert asset.blob.as_arrow_array() == rr.components.BlobBatch(blob_comp).as_arrow_array() assert asset.media_type == rr.components.MediaTypeBatch(rr.components.MediaType.GLB) - assert asset.transform is None - - -def test_asset3d_transform() -> None: - asset = rr.Asset3D(path=CUBE_FILEPATH, transform=rr.datatypes.TranslationRotationScale3D(translation=[1, 2, 3])) - - assert asset.transform is not None - assert ( - asset.transform.as_arrow_array() - == rr.components.OutOfTreeTransform3DBatch( - rr.datatypes.TranslationRotationScale3D(translation=[1, 2, 3]) - ).as_arrow_array() - ) diff --git a/tests/python/release_checklist/check_all_components_ui.py b/tests/python/release_checklist/check_all_components_ui.py index 3bf6b106d497..d51ba681ae9a 100644 --- a/tests/python/release_checklist/check_all_components_ui.py +++ b/tests/python/release_checklist/check_all_components_ui.py @@ -122,13 +122,6 @@ def alternatives(self) -> list[Any] | None: "MarkerSizeBatch": TestCase(batch=[5.0, 1.0, 2.0]), "MediaTypeBatch": TestCase("application/jpg"), "NameBatch": TestCase(batch=["Hello World", "Foo Bar", "Baz Qux"]), - "OutOfTreeTransform3DBatch": TestCase( - alternatives=[ - rr.datatypes.TranslationRotationScale3D( - translation=(1, 2, 3), rotation=rr.datatypes.Quaternion(xyzw=[0, 0, 0, 1]), scale=(1, 1, 1) - ), - ] - ), "OpacityBatch": TestCase(0.5), "PinholeProjectionBatch": TestCase([(0, 1, 2), (3, 4, 5), (6, 7, 8)]), "Position2DBatch": TestCase(batch=[(0, 1), (2, 3), (4, 5)]), From 692d72a531a5a9b38ff52ea985eec8d1d436063d Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 29 Jul 2024 16:18:11 +0200 Subject: [PATCH 08/29] Always make use of leaf transforms now, use them to instantiate mesh/asset3d more often in the scene if there's several --- Cargo.lock | 4 + .../definitions/rerun/archetypes/asset3d.fbs | 2 + .../definitions/rerun/archetypes/mesh3d.fbs | 2 + .../store/re_types/src/archetypes/asset3d.rs | 2 + .../store/re_types/src/archetypes/mesh3d.rs | 2 + .../viewer/re_space_view_spatial/Cargo.toml | 1 + .../re_space_view_spatial/src/contexts/mod.rs | 1 - .../src/contexts/transform_context.rs | 251 ++++++++++++------ .../src/visualizers/arrows2d.rs | 15 +- .../src/visualizers/arrows3d.rs | 15 +- .../src/visualizers/assets3d.rs | 44 ++- .../src/visualizers/boxes2d.rs | 15 +- .../src/visualizers/boxes3d.rs | 15 +- .../src/visualizers/depth_images.rs | 21 +- .../src/visualizers/ellipsoids.rs | 15 +- .../src/visualizers/image_encoded.rs | 21 +- .../src/visualizers/images.rs | 31 +-- .../src/visualizers/lines2d.rs | 15 +- .../src/visualizers/lines3d.rs | 15 +- .../src/visualizers/meshes.rs | 40 +-- .../src/visualizers/mod.rs | 11 +- .../src/visualizers/points2d.rs | 14 +- .../src/visualizers/points3d.rs | 15 +- .../src/visualizers/segmentation_images.rs | 22 +- .../src/visualizers/transform3d_arrows.rs | 28 +- .../visualizers/utilities/entity_iterator.rs | 3 - .../src/visualizers/utilities/mod.rs | 4 +- .../visualizers/utilities/textured_rect.rs | 60 ++++- .../reference/types/archetypes/asset3d.md | 8 +- .../reference/types/archetypes/mesh3d.md | 2 + rerun_cpp/src/rerun/archetypes/asset3d.hpp | 2 + rerun_cpp/src/rerun/archetypes/mesh3d.hpp | 2 + .../rerun_sdk/rerun/archetypes/asset3d.py | 2 + rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py | 2 + 34 files changed, 412 insertions(+), 290 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b2e73ed14a2..f3f720127a82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5008,6 +5008,7 @@ dependencies = [ "re_viewport_blueprint", "serde", "smallvec", + "vec1", "web-time", ] @@ -6971,6 +6972,9 @@ name = "vec1" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bda7c41ca331fe9a1c278a9e7ee055f4be7f5eb1c2b72f079b4ff8b5fce9d5c" +dependencies = [ + "smallvec", +] [[package]] name = "version_check" diff --git a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs index 72bbd5d306a4..a00832f6daac 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs @@ -6,6 +6,8 @@ namespace rerun.archetypes; /// /// See also [archetypes.Mesh3D]. /// +/// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +/// /// \example archetypes/asset3d_simple title="Simple 3D asset" image="https://static.rerun.io/asset3d_simple/af238578188d3fd0de3e330212120e2842a8ddb2/1200w.png" table Asset3D ( "attr.rust.derive": "PartialEq, Eq", diff --git a/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs index c12a36f8d543..c342d5a2171e 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs @@ -6,6 +6,8 @@ namespace rerun.archetypes; /// /// See also [archetypes.Asset3D]. /// +/// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +/// /// \example archetypes/mesh3d_indexed title="Simple indexed 3D mesh" image="https://static.rerun.io/mesh3d_simple/e1e5fd97265daf0d0bc7b782d862f19086fd6975/1200w.png" /// \example archetypes/mesh3d_partial_updates !api title="3D mesh with partial updates" image="https://static.rerun.io/mesh3d_partial_updates/a11e4accb0257dcd9531867b7e1d6fd5e3bee5c3/1200w.png" table Mesh3D ( diff --git a/crates/store/re_types/src/archetypes/asset3d.rs b/crates/store/re_types/src/archetypes/asset3d.rs index dcbf5293849b..f93f7ec97802 100644 --- a/crates/store/re_types/src/archetypes/asset3d.rs +++ b/crates/store/re_types/src/archetypes/asset3d.rs @@ -22,6 +22,8 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// See also [`archetypes::Mesh3D`][crate::archetypes::Mesh3D]. /// +/// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +/// /// ## Example /// /// ### Simple 3D asset diff --git a/crates/store/re_types/src/archetypes/mesh3d.rs b/crates/store/re_types/src/archetypes/mesh3d.rs index 83ab561562d7..f24a13a210eb 100644 --- a/crates/store/re_types/src/archetypes/mesh3d.rs +++ b/crates/store/re_types/src/archetypes/mesh3d.rs @@ -22,6 +22,8 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// See also [`archetypes::Asset3D`][crate::archetypes::Asset3D]. /// +/// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +/// /// ## Example /// /// ### Simple indexed 3D mesh diff --git a/crates/viewer/re_space_view_spatial/Cargo.toml b/crates/viewer/re_space_view_spatial/Cargo.toml index 10d53674e49d..ff210b3a7f1e 100644 --- a/crates/viewer/re_space_view_spatial/Cargo.toml +++ b/crates/viewer/re_space_view_spatial/Cargo.toml @@ -52,6 +52,7 @@ nohash-hasher.workspace = true once_cell.workspace = true serde.workspace = true smallvec = { workspace = true, features = ["serde"] } +vec1 = { workspace = true, features = ["smallvec-v1"] } web-time.workspace = true diff --git a/crates/viewer/re_space_view_spatial/src/contexts/mod.rs b/crates/viewer/re_space_view_spatial/src/contexts/mod.rs index 4b420befb7b4..d7a425f20ccc 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/mod.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/mod.rs @@ -14,7 +14,6 @@ use re_viewer_context::{Annotations, SpaceViewClassRegistryError}; /// Context objects for a single entity in a spatial scene. pub struct SpatialSceneEntityContext<'a> { - pub world_from_entity: glam::Affine3A, // TODO(andreas): replace usage with `transform_info` accessors. pub transform_info: &'a TransformInfo, pub depth_offset: DepthOffset, pub annotations: std::sync::Arc, diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index d67236ac5a4f..a261a0d5721e 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -15,6 +15,7 @@ use re_types::{ ComponentNameSet, Loggable as _, }; use re_viewer_context::{IdentifiedViewSystem, ViewContext, ViewContextSystem}; +use vec1::smallvec_v1::SmallVec1; use crate::visualizers::image_view_coordinates; @@ -22,13 +23,16 @@ use crate::visualizers::image_view_coordinates; pub struct TransformInfo { /// The transform from the entity to the reference space. /// - /// Does not include per instance leaf transforms! + /// ⚠️ Does not include per instance leaf transforms! ⚠️ /// Include 3D-from-2D / 2D-from-3D pinhole transform if present. - pub reference_from_entity: glam::Affine3A, + reference_from_entity: glam::Affine3A, - /// Optional list of out of leaf transforms that are applied to the instances of this entity. - // TODO(andreas): not very widely supported yet. We may likely want to make `reference_from_instance` a thing and encapsulate stuff further. - pub entity_from_instance_leaf_transforms: Vec, + /// List of transforms per instance including leaf transforms. + /// + /// If no leaf transforms are present, this is always the same as `reference_from_entity`. + /// (also implying that in this case there is only a single element). + /// If there are leaf transforms there may be more than one element. + pub reference_from_instances: SmallVec1<[glam::Affine3A; 1]>, /// If this entity is under (!) a pinhole camera, this contains additional information. /// @@ -53,12 +57,45 @@ impl Default for TransformInfo { fn default() -> Self { Self { reference_from_entity: glam::Affine3A::IDENTITY, - entity_from_instance_leaf_transforms: Vec::new(), + reference_from_instances: SmallVec1::new(glam::Affine3A::IDENTITY), twod_in_threed_info: None, } } } +impl TransformInfo { + /// Warns that multiple transforms within the the entity are not supported. + #[inline] + pub fn warn_on_per_instance_transform( + &self, + entity_name: &EntityPath, + visualizer_name: &'static str, + ) { + if self.reference_from_instances.len() > 1 { + re_log::warn_once!( + "There are multiple leaf transforms for entity {entity_name:?}. Visualizer {visualizer_name:?} supports only one transform per entity. Using the first one." + ); + } + } + + /// Returns the first instance transform and warns if there are multiple (via [`Self::warn_on_per_instance_transform`]). + #[inline] + pub fn single_entity_transform_required( + &self, + entity_name: &EntityPath, + visualizer_name: &'static str, + ) -> glam::Affine3A { + self.warn_on_per_instance_transform(entity_name, visualizer_name); + *self.reference_from_instances.first() + } + + /// Returns the first instance transform and does not warn if there are multiple. + #[inline] + pub fn single_entity_transform_silent(&self) -> glam::Affine3A { + *self.reference_from_instances.first() + } +} + #[derive(Clone, Copy)] enum UnreachableTransformReason { /// More than one pinhole camera between this and the reference space. @@ -193,7 +230,7 @@ impl TransformContext { // Note that the transform at the reference is the first that needs to be inverted to "break out" of its hierarchy. // Generally, the transform _at_ a node isn't relevant to it's children, but only to get to its parent in turn! let new_transform = match transforms_at( - current_tree, + ¤t_tree.path, ctx.recording(), time_query, // TODO(#1025): See comment in transform_at. This is a workaround for precision issues @@ -206,39 +243,10 @@ impl TransformContext { Some((parent_tree.path.clone(), unreachable_reason)); break; } - Ok(transforms_at_entity) => { - let mut new_transform = TransformInfo { - reference_from_entity: reference_from_ancestor, - entity_from_instance_leaf_transforms: transforms_at_entity - .parent_from_entity_leaf_transforms - .iter() - .map(|&t| t.inverse()) - .collect(), - - // Going up the tree, we can only encounter 2D->3D transforms. - // 3D->2D transforms can't happen because `Pinhole` represents 3D->2D (and we're walking backwards!) - twod_in_threed_info: None, - }; - - // Need to take care of the fact that we're walking the other direction of the tree here compared to `gather_descendants_transforms`! - if let Some(entity_from_2d_pinhole_content) = - transforms_at_entity.instance_from_pinhole_image_plane - { - // If we're going up the tree and encounter a pinhole, we still to apply it. - // This is what handles "3D in 2D". - debug_assert!(encountered_pinhole.as_ref() == Some(¤t_tree.path)); - new_transform.reference_from_entity *= - entity_from_2d_pinhole_content.inverse(); - } - if let Some(parent_from_entity_tree_transform) = - transforms_at_entity.parent_from_entity_tree_transform - { - new_transform.reference_from_entity *= - parent_from_entity_tree_transform.inverse(); - } - - new_transform - } + Ok(transforms_at_entity) => transform_info_for_upward_propagation( + reference_from_ancestor, + transforms_at_entity, + ), }; reference_from_ancestor = new_transform.reference_from_entity; @@ -279,6 +287,8 @@ impl TransformContext { } for child_tree in subtree.children.values() { + let child_path = &child_tree.path; + let lookup_image_plane = |p: &_| { let query_result = ctx.viewer_ctx.lookup_query_result(view_query.space_view_id); @@ -300,7 +310,7 @@ impl TransformContext { .as_ref() .map(|info| info.parent_pinhole.clone()); let new_transform = match transforms_at( - child_tree, + child_path, entity_db, query, lookup_image_plane, @@ -308,40 +318,16 @@ impl TransformContext { ) { Err(unreachable_reason) => { self.unreachable_descendants - .push((child_tree.path.clone(), unreachable_reason)); + .push((child_path.clone(), unreachable_reason)); continue; } - Ok(transforms_at_entity) => { - let mut new_transform = TransformInfo { - reference_from_entity: reference_from_parent, - entity_from_instance_leaf_transforms: transforms_at_entity - .parent_from_entity_leaf_transforms, - twod_in_threed_info: twod_in_threed_info.clone(), - }; - - if let Some(parent_from_entity_tree_transform) = - transforms_at_entity.parent_from_entity_tree_transform - { - new_transform.reference_from_entity *= parent_from_entity_tree_transform; - } - if let Some(entity_from_2d_pinhole_content) = - transforms_at_entity.instance_from_pinhole_image_plane - { - // This should be an unreachable transform. - debug_assert!(twod_in_threed_info.is_none()); - // There has to be a pinhole to get here. - debug_assert!(encountered_pinhole.is_some()); - - new_transform.twod_in_threed_info = Some(TwoDInThreeDTransformInfo { - parent_pinhole: child_tree.path.clone(), - reference_from_pinhole_entity: new_transform.reference_from_entity, - }); - new_transform.reference_from_entity *= entity_from_2d_pinhole_content; - } - - new_transform - } + Ok(transforms_at_entity) => transform_info_for_downward_propagation( + child_path, + reference_from_parent, + twod_in_threed_info.clone(), + transforms_at_entity, + ), }; self.gather_descendants_transforms( @@ -367,6 +353,118 @@ impl TransformContext { } } +/// Compute transform info for when we walk up the tree from the reference. +fn transform_info_for_upward_propagation( + reference_from_ancestor: glam::Affine3A, + transforms_at_entity: TransformsAtEntity, +) -> TransformInfo { + let mut reference_from_entity = reference_from_ancestor; + + // Need to take care of the fact that we're walking the other direction of the tree here compared to `transform_info_for_downward_propagation`! + // Apply inverse transforms in flipped order! + + // Apply 2D->3D transform if present. + if let Some(entity_from_2d_pinhole_content) = + transforms_at_entity.instance_from_pinhole_image_plane + { + // If we're going up the tree and encounter a pinhole, we still to apply it. + // This is what handles "3D in 2D". + reference_from_entity *= entity_from_2d_pinhole_content.inverse(); + } + + // Collect & compute leaf transforms. + let reference_from_instances = if let Ok(mut entity_from_instances) = + SmallVec1::<[glam::Affine3A; 1]>::try_from_vec( + transforms_at_entity.entity_from_instance_leaf_transforms, + ) { + for entity_from_instance in &mut entity_from_instances { + *entity_from_instance = reference_from_entity * entity_from_instance.inverse(); + // Now this is actually `reference_from_instance`. + } + entity_from_instances + } else { + SmallVec1::new(reference_from_entity) + }; + + // Apply tree transform if any. + if let Some(parent_from_entity_tree_transform) = + transforms_at_entity.parent_from_entity_tree_transform + { + reference_from_entity *= parent_from_entity_tree_transform.inverse(); + } + + TransformInfo { + reference_from_entity, + reference_from_instances, + + // Going up the tree, we can only encounter 2D->3D transforms. + // 3D->2D transforms can't happen because `Pinhole` represents 3D->2D (and we're walking backwards!) + twod_in_threed_info: None, + } +} + +/// Compute transform info for when we walk down the tree from the reference. +fn transform_info_for_downward_propagation( + current_path: &EntityPath, + reference_from_parent: glam::Affine3A, + mut twod_in_threed_info: Option, + transforms_at_entity: TransformsAtEntity, +) -> TransformInfo { + let mut reference_from_entity = reference_from_parent; + + // Apply tree transform. + if let Some(parent_from_entity_tree_transform) = + transforms_at_entity.parent_from_entity_tree_transform + { + reference_from_entity *= parent_from_entity_tree_transform; + } + + // Collect & compute leaf transforms. + let (mut reference_from_instances, has_leaf_transforms) = if let Ok(mut entity_from_instances) = + SmallVec1::try_from_vec(transforms_at_entity.entity_from_instance_leaf_transforms) + { + for entity_from_instance in &mut entity_from_instances { + *entity_from_instance = reference_from_entity * (*entity_from_instance); + // Now this is actually `reference_from_instance`. + } + (entity_from_instances, true) + } else { + (SmallVec1::new(reference_from_entity), false) + }; + + // Apply 2D->3D transform if present. + if let Some(entity_from_2d_pinhole_content) = + transforms_at_entity.instance_from_pinhole_image_plane + { + // Should have bailed out already earlier. + debug_assert!( + twod_in_threed_info.is_none(), + "2D->3D transform already set, this should be unreachable." + ); + + twod_in_threed_info = Some(TwoDInThreeDTransformInfo { + parent_pinhole: current_path.clone(), + reference_from_pinhole_entity: reference_from_entity, + }); + reference_from_entity *= entity_from_2d_pinhole_content; + + // Need to update per instance transforms as well if there are leaf transforms! + if has_leaf_transforms { + *reference_from_instances.first_mut() = reference_from_entity; + } else { + for reference_from_instance in &mut reference_from_instances { + *reference_from_instance *= entity_from_2d_pinhole_content; + } + } + } + + TransformInfo { + reference_from_entity, + reference_from_instances, + twod_in_threed_info, + } +} + #[cfg(debug_assertions)] fn debug_assert_transform_field_order(reflection: &re_types::reflection::Reflection) { let expected_order = vec![ @@ -613,12 +711,12 @@ fn query_and_resolve_obj_from_pinhole_image_plane( /// Resolved transforms at an entity. struct TransformsAtEntity { parent_from_entity_tree_transform: Option, - parent_from_entity_leaf_transforms: Vec, + entity_from_instance_leaf_transforms: Vec, instance_from_pinhole_image_plane: Option, } fn transforms_at( - subtree: &EntityTree, + entity_path: &EntityPath, entity_db: &EntityDb, query: &LatestAtQuery, pinhole_image_plane_distance: impl Fn(&EntityPath) -> f32, @@ -626,14 +724,13 @@ fn transforms_at( ) -> Result { re_tracing::profile_function!(); - let entity_path = &subtree.path; let transforms_at_entity = TransformsAtEntity { parent_from_entity_tree_transform: query_and_resolve_tree_transform_at_entity( entity_path, entity_db, query, ), - parent_from_entity_leaf_transforms: query_and_resolve_leaf_transform_at_entity( + entity_from_instance_leaf_transforms: query_and_resolve_leaf_transform_at_entity( entity_path, entity_db, query, @@ -663,7 +760,7 @@ fn transforms_at( .parent_from_entity_tree_transform .is_none() && transforms_at_entity - .parent_from_entity_leaf_transforms + .entity_from_instance_leaf_transforms .is_empty() && transforms_at_entity .instance_from_pinhole_image_plane diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/arrows2d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/arrows2d.rs index f0c50c25ff0a..3b2a5d4495cc 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/arrows2d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/arrows2d.rs @@ -72,9 +72,13 @@ impl Arrows2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); + let world_from_obj = ent_context + .transform_info + .single_entity_transform_required(entity_path, "Arrows2D"); + let mut line_batch = line_builder .batch(entity_path.to_string()) - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); @@ -112,11 +116,8 @@ impl Arrows2DVisualizer { obj_space_bounding_box.extend(end.extend(0.0)); } - self.data.add_bounding_box( - entity_path.hash(), - obj_space_bounding_box, - ent_context.world_from_entity, - ); + self.data + .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many arrows but only a single label, place the single label at the middle of the visualization. @@ -142,7 +143,7 @@ impl Arrows2DVisualizer { data.labels, &colors, &annotation_infos, - ent_context.world_from_entity, + world_from_obj, )); } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/arrows3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/arrows3d.rs index 0242fc0ff2cf..291ddf5d34d0 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/arrows3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/arrows3d.rs @@ -72,9 +72,13 @@ impl Arrows3DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); + let world_from_obj = ent_context + .transform_info + .single_entity_transform_required(entity_path, "Arrows3D"); + let mut line_batch = line_builder .batch(entity_path.to_string()) - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); @@ -113,11 +117,8 @@ impl Arrows3DVisualizer { obj_space_bounding_box.extend(end); } - self.data.add_bounding_box( - entity_path.hash(), - obj_space_bounding_box, - ent_context.world_from_entity, - ); + self.data + .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many arrows but only a single label, place the single label at the middle of the visualization. @@ -142,7 +143,7 @@ impl Arrows3DVisualizer { data.labels, &colors, &annotation_infos, - ent_context.world_from_entity, + world_from_obj, )); } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs index 45cd9fe28b1b..e96a8b016482 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs @@ -81,30 +81,26 @@ impl Asset3DVisualizer { if let Some(mesh) = mesh { re_tracing::profile_scope!("mesh instances"); - let world_from_pose = ent_context.world_from_entity - * *ent_context - .transform_info - .entity_from_instance_leaf_transforms - .first() - .unwrap_or(&glam::Affine3A::IDENTITY); - - instances.extend(mesh.mesh_instances.iter().map(move |mesh_instance| { - let pose_from_mesh = mesh_instance.world_from_mesh; - let world_from_mesh = world_from_pose * pose_from_mesh; - - MeshInstance { - gpu_mesh: mesh_instance.gpu_mesh.clone(), - world_from_mesh, - outline_mask_ids, - picking_layer_id: picking_layer_id_from_instance_path_hash( - picking_instance_hash, - ), - ..Default::default() - } - })); - - self.0 - .add_bounding_box(entity_path.hash(), mesh.bbox(), world_from_pose); + // Let's draw the mesh once for every instance transform. Because why not! + for &world_from_pose in &ent_context.transform_info.reference_from_instances { + instances.extend(mesh.mesh_instances.iter().map(move |mesh_instance| { + let pose_from_mesh = mesh_instance.world_from_mesh; + let world_from_mesh = world_from_pose * pose_from_mesh; + + MeshInstance { + gpu_mesh: mesh_instance.gpu_mesh.clone(), + world_from_mesh, + outline_mask_ids, + picking_layer_id: picking_layer_id_from_instance_path_hash( + picking_instance_hash, + ), + ..Default::default() + } + })); + + self.0 + .add_bounding_box(entity_path.hash(), mesh.bbox(), world_from_pose); + } }; } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/boxes2d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/boxes2d.rs index 70844766741e..86e4d41f843f 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/boxes2d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/boxes2d.rs @@ -121,10 +121,14 @@ impl Boxes2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); + let world_from_obj = ent_context + .transform_info + .single_entity_transform_required(entity_path, "Boxes2D"); + let mut line_batch = line_builder .batch("boxes2d") .depth_offset(ent_context.depth_offset) - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); @@ -158,11 +162,8 @@ impl Boxes2DVisualizer { } } - self.data.add_bounding_box( - entity_path.hash(), - obj_space_bounding_box, - ent_context.world_from_entity, - ); + self.data + .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { if data.labels.len() == 1 && num_instances > 1 { @@ -174,7 +175,7 @@ impl Boxes2DVisualizer { data.labels, &colors, &annotation_infos, - ent_context.world_from_entity, + world_from_obj, )); } else { let centers = clamped_or(data.centers, &Position2D::ZERO); diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs index bcbd75e16a46..295410444b50 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs @@ -78,10 +78,14 @@ impl Boxes3DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); + let world_from_obj = ent_context + .transform_info + .single_entity_transform_required(entity_path, "Boxes3D"); + let mut line_batch = line_builder .batch("boxes3d") .depth_offset(ent_context.depth_offset) - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); @@ -150,11 +154,8 @@ impl Boxes3DVisualizer { } } - self.0.add_bounding_box( - entity_path.hash(), - obj_space_bounding_box, - ent_context.world_from_entity, - ); + self.0 + .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many boxes but only a single label, place the single label at the middle of the visualization. @@ -174,7 +175,7 @@ impl Boxes3DVisualizer { data.labels, &colors, &annotation_infos, - ent_context.world_from_entity, + world_from_obj, )); } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs b/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs index 7dea211e987e..5aacc564fb7c 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/depth_images.rs @@ -22,10 +22,10 @@ use crate::{ query_pinhole_legacy, view_kind::SpatialSpaceViewKind, visualizers::{filter_visualizable_2d_entities, SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES}, - PickableImageRect, SpatialSpaceView2D, SpatialSpaceView3D, + PickableImageRect, SpatialSpaceView3D, }; -use super::{bounding_box_for_textured_rect, textured_rect_from_image, SpatialViewVisualizerData}; +use super::{textured_rect_from_image, SpatialViewVisualizerData}; pub struct DepthImageVisualizer { pub data: SpatialViewVisualizerData, @@ -59,6 +59,9 @@ impl DepthImageVisualizer { ) { let is_3d_view = ent_context.space_view_class_identifier == SpatialSpaceView3D::identifier(); + ent_context + .transform_info + .warn_on_per_instance_transform(ctx.target_entity_path, "DepthImage"); let entity_path = ctx.target_entity_path; let meaning = TensorDataMeaning::Depth; @@ -115,19 +118,9 @@ impl DepthImageVisualizer { &image, meaning, re_renderer::Rgba::WHITE, + "DepthImage", + &mut self.data, ) { - // Only update the bounding box if this is a 2D space view. - // This is avoids a cyclic relationship where the image plane grows - // the bounds which in turn influence the size of the image plane. - // See: https://github.com/rerun-io/rerun/issues/3728 - if ent_context.space_view_class_identifier == SpatialSpaceView2D::identifier() { - self.data.add_bounding_box( - entity_path.hash(), - bounding_box_for_textured_rect(&textured_rect), - ent_context.world_from_entity, - ); - } - self.images.push(PickableImageRect { ent_path: entity_path.clone(), row_id: image.blob_row_id, diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs b/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs index b5a6fbdcd9ac..fbc853a1396d 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs @@ -117,19 +117,23 @@ impl EllipsoidsVisualizer { let centers = clamped_or(data.centers, &Position3D::ZERO); let rotations = clamped_or(data.rotations, &Rotation3D::IDENTITY); + let world_from_obj = ent_context + .transform_info + .single_entity_transform_required(entity_path, "Ellipsoids"); + self.0.ui_labels.extend(Self::process_labels( entity_path, data.centers, data.labels, &colors, &annotation_infos, - ent_context.world_from_entity, + world_from_obj, )); let mut line_batch = line_builder .batch("ellipsoids") .depth_offset(ent_context.depth_offset) - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); @@ -219,11 +223,8 @@ impl EllipsoidsVisualizer { } } - self.0.add_bounding_box( - entity_path.hash(), - obj_space_bounding_box, - ent_context.world_from_entity, - ); + self.0 + .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); } Ok(()) diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/image_encoded.rs b/crates/viewer/re_space_view_spatial/src/visualizers/image_encoded.rs index 0f1aa1fe078f..82afabded9ac 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/image_encoded.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/image_encoded.rs @@ -8,7 +8,7 @@ use re_types::{ tensor_data::TensorDataMeaning, }; use re_viewer_context::{ - ApplicableEntities, IdentifiedViewSystem, ImageDecodeCache, QueryContext, SpaceViewClass, + ApplicableEntities, IdentifiedViewSystem, ImageDecodeCache, QueryContext, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, @@ -16,12 +16,11 @@ use re_viewer_context::{ use crate::{ contexts::SpatialSceneEntityContext, view_kind::SpatialSpaceViewKind, - visualizers::filter_visualizable_2d_entities, PickableImageRect, SpatialSpaceView2D, + visualizers::filter_visualizable_2d_entities, PickableImageRect, }; use super::{ - bounding_box_for_textured_rect, entity_iterator::process_archetype, textured_rect_from_tensor, - SpatialViewVisualizerData, + entity_iterator::process_archetype, textured_rect_from_tensor, SpatialViewVisualizerData, }; pub struct ImageEncodedVisualizer { @@ -188,19 +187,9 @@ impl ImageEncodedVisualizer { meaning, multiplicative_tint, colormap, + "ImageEncoded", + &mut self.data, ) { - // Only update the bounding box if this is a 2D space view. - // This is avoids a cyclic relationship where the image plane grows - // the bounds which in turn influence the size of the image plane. - // See: https://github.com/rerun-io/rerun/issues/3728 - if spatial_ctx.space_view_class_identifier == SpatialSpaceView2D::identifier() { - self.data.add_bounding_box( - entity_path.hash(), - bounding_box_for_textured_rect(&textured_rect), - spatial_ctx.world_from_entity, - ); - } - self.images.push(PickableImageRect { ent_path: entity_path.clone(), row_id: tensor_data_row_id, diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/images.rs b/crates/viewer/re_space_view_spatial/src/visualizers/images.rs index 67d8bfa8bad7..8ffd7bd2a2f3 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/images.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/images.rs @@ -10,20 +10,19 @@ use re_types::{ tensor_data::TensorDataMeaning, }; use re_viewer_context::{ - ApplicableEntities, IdentifiedViewSystem, QueryContext, SpaceViewClass, - SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, - ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, - VisualizerAdditionalApplicabilityFilter, VisualizerQueryInfo, VisualizerSystem, + ApplicableEntities, IdentifiedViewSystem, QueryContext, SpaceViewSystemExecutionError, + TypedComponentFallbackProvider, ViewContext, ViewContextCollection, ViewQuery, + VisualizableEntities, VisualizableFilterContext, VisualizerAdditionalApplicabilityFilter, + VisualizerQueryInfo, VisualizerSystem, }; use crate::{ contexts::SpatialSceneEntityContext, view_kind::SpatialSpaceViewKind, - visualizers::filter_visualizable_2d_entities, PickableImageRect, SpatialSpaceView2D, + visualizers::filter_visualizable_2d_entities, PickableImageRect, }; use super::{ - bounding_box_for_textured_rect, entity_iterator::process_archetype, textured_rect_from_tensor, - SpatialViewVisualizerData, + entity_iterator::process_archetype, textured_rect_from_tensor, SpatialViewVisualizerData, }; pub struct ImageVisualizer { @@ -178,6 +177,10 @@ impl ImageVisualizer { // TODO(jleibs): Make this more explicit let meaning = TensorDataMeaning::Unknown; + spatial_ctx + .transform_info + .warn_on_per_instance_transform(ctx.target_entity_path, "DepthImage"); + for data in data { if !data.tensor.is_shaped_like_an_image() { continue; @@ -205,19 +208,9 @@ impl ImageVisualizer { meaning, multiplicative_tint, colormap, + "Image", + &mut self.data, ) { - // Only update the bounding box if this is a 2D space view. - // This is avoids a cyclic relationship where the image plane grows - // the bounds which in turn influence the size of the image plane. - // See: https://github.com/rerun-io/rerun/issues/3728 - if spatial_ctx.space_view_class_identifier == SpatialSpaceView2D::identifier() { - self.data.add_bounding_box( - entity_path.hash(), - bounding_box_for_textured_rect(&textured_rect), - spatial_ctx.world_from_entity, - ); - } - self.images.push(PickableImageRect { ent_path: entity_path.clone(), row_id: tensor_data_row_id, diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs index 0a261f91a32c..fa33b888ded1 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs @@ -69,10 +69,14 @@ impl Lines2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); + let world_from_obj = ent_context + .transform_info + .single_entity_transform_required(entity_path, "Lines2D"); + let mut line_batch = line_builder .batch(entity_path.to_string()) .depth_offset(ent_context.depth_offset) - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); @@ -99,11 +103,8 @@ impl Lines2DVisualizer { } } - self.data.add_bounding_box( - entity_path.hash(), - obj_space_bounding_box, - ent_context.world_from_entity, - ); + self.data + .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many strips but only a single label, place the single label at the middle of the visualization. @@ -131,7 +132,7 @@ impl Lines2DVisualizer { data.labels, &colors, &annotation_infos, - ent_context.world_from_entity, + world_from_obj, )); } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs index c533b72f79cf..4ace87adeb25 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs @@ -71,10 +71,14 @@ impl Lines3DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); + let world_from_obj = ent_context + .transform_info + .single_entity_transform_required(entity_path, "Lines2D"); + let mut line_batch = line_builder .batch(entity_path.to_string()) .depth_offset(ent_context.depth_offset) - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); @@ -106,11 +110,8 @@ impl Lines3DVisualizer { } debug_assert_eq!(data.strips.len(), num_rendered_strips, "the number of renderer strips after all post-processing is done should be equal to {} (got {num_rendered_strips} instead)", data.strips.len()); - self.data.add_bounding_box( - entity_path.hash(), - obj_space_bounding_box, - ent_context.world_from_entity, - ); + self.data + .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many strips but only a single label, place the single label at the middle of the visualization. @@ -136,7 +137,7 @@ impl Lines3DVisualizer { data.labels, &colors, &annotation_infos, - ent_context.world_from_entity, + world_from_obj, )); } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/meshes.rs b/crates/viewer/re_space_view_spatial/src/visualizers/meshes.rs index 7c25c36147e8..94b3b191df2e 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/meshes.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/meshes.rs @@ -117,26 +117,26 @@ impl Mesh3DVisualizer { }); if let Some(mesh) = mesh { - instances.extend(mesh.mesh_instances.iter().map(move |mesh_instance| { - let entity_from_mesh = mesh_instance.world_from_mesh; - let world_from_mesh = ent_context.world_from_entity * entity_from_mesh; - - MeshInstance { - gpu_mesh: mesh_instance.gpu_mesh.clone(), - world_from_mesh, - outline_mask_ids, - picking_layer_id: picking_layer_id_from_instance_path_hash( - picking_instance_hash, - ), - ..Default::default() - } - })); - - self.0.add_bounding_box( - entity_path.hash(), - mesh.bbox(), - ent_context.world_from_entity, - ); + // Let's draw the mesh once for every instance transform. Because why not! + for &world_from_instance in &ent_context.transform_info.reference_from_instances { + instances.extend(mesh.mesh_instances.iter().map(move |mesh_instance| { + let entity_from_mesh = mesh_instance.world_from_mesh; + let world_from_mesh = world_from_instance * entity_from_mesh; + + MeshInstance { + gpu_mesh: mesh_instance.gpu_mesh.clone(), + world_from_mesh, + outline_mask_ids, + picking_layer_id: picking_layer_id_from_instance_path_hash( + picking_instance_hash, + ), + ..Default::default() + } + })); + + self.0 + .add_bounding_box(entity_path.hash(), mesh.bbox(), world_from_instance); + } }; } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/mod.rs b/crates/viewer/re_space_view_spatial/src/visualizers/mod.rs index 5f4c19ce1021..d33a82ceb809 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/mod.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/mod.rs @@ -26,9 +26,9 @@ pub use images::ImageVisualizer; pub use segmentation_images::SegmentationImageVisualizer; pub use transform3d_arrows::{add_axis_arrows, AxisLengthDetector, Transform3DArrowsVisualizer}; pub use utilities::{ - bounding_box_for_textured_rect, entity_iterator, process_labels_2d, process_labels_3d, - textured_rect_from_image, textured_rect_from_tensor, SpatialViewVisualizerData, UiLabel, - UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY, + entity_iterator, process_labels_2d, process_labels_3d, textured_rect_from_image, + textured_rect_from_tensor, SpatialViewVisualizerData, UiLabel, UiLabelTarget, + MAX_NUM_LABELS_PER_ENTITY, }; // --- @@ -320,9 +320,12 @@ pub fn load_keypoint_connections( line_builder.reserve_strips(max_num_connections)?; line_builder.reserve_vertices(max_num_connections * 2)?; + // The calling visualizer has the same issue of not knowing what do with per instance transforms + // and should have warned already if there are multiple transforms. + let world_from_obj = ent_context.transform_info.single_entity_transform_silent(); let mut line_batch = line_builder .batch("keypoint connections") - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .picking_object_id(re_renderer::PickingLayerObjectId(ent_path.hash64())); // TODO(andreas): Make configurable. Should we pick up the point's radius and make this proportional? diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/points2d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/points2d.rs index 0734d69cdf05..d763ed719548 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/points2d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/points2d.rs @@ -84,6 +84,9 @@ impl Points2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); + let world_from_obj = ent_context + .transform_info + .single_entity_transform_required(entity_path, "Points2D"); { let point_batch = point_builder .batch(entity_path.to_string()) @@ -92,7 +95,7 @@ impl Points2DVisualizer { re_renderer::renderer::PointCloudBatchFlags::FLAG_DRAW_AS_CIRCLES | re_renderer::renderer::PointCloudBatchFlags::FLAG_ENABLE_SHADING, ) - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); @@ -120,11 +123,8 @@ impl Points2DVisualizer { let obj_space_bounding_box = re_math::BoundingBox::from_points(positions.iter().copied()); - self.data.add_bounding_box( - entity_path.hash(), - obj_space_bounding_box, - ent_context.world_from_entity, - ); + self.data + .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); load_keypoint_connections(line_builder, ent_context, entity_path, &keypoints)?; @@ -147,7 +147,7 @@ impl Points2DVisualizer { data.labels, &colors, &annotation_infos, - ent_context.world_from_entity, + world_from_obj, )); } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/points3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/points3d.rs index 50873b163273..c20b20ca927e 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/points3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/points3d.rs @@ -95,10 +95,14 @@ impl Points3DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); + let world_from_obj = ent_context + .transform_info + .single_entity_transform_required(entity_path, "Points3D"); + { let point_batch = point_builder .batch(entity_path.to_string()) - .world_from_obj(ent_context.world_from_entity) + .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); @@ -125,11 +129,8 @@ impl Points3DVisualizer { let obj_space_bounding_box = re_math::BoundingBox::from_points(positions.iter().copied()); - self.data.add_bounding_box( - entity_path.hash(), - obj_space_bounding_box, - ent_context.world_from_entity, - ); + self.data + .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); load_keypoint_connections(line_builder, ent_context, entity_path, &keypoints)?; @@ -150,7 +151,7 @@ impl Points3DVisualizer { data.labels, &colors, &annotation_infos, - ent_context.world_from_entity, + world_from_obj, )); } } diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/segmentation_images.rs b/crates/viewer/re_space_view_spatial/src/visualizers/segmentation_images.rs index e1d571116c81..3354a55f66b8 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/segmentation_images.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/segmentation_images.rs @@ -6,7 +6,7 @@ use re_types::{ tensor_data::TensorDataMeaning, }; use re_viewer_context::{ - ApplicableEntities, IdentifiedViewSystem, ImageInfo, QueryContext, SpaceViewClass, + ApplicableEntities, IdentifiedViewSystem, ImageInfo, QueryContext, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, @@ -16,10 +16,10 @@ use crate::{ ui::SpatialSpaceViewState, view_kind::SpatialSpaceViewKind, visualizers::{filter_visualizable_2d_entities, textured_rect_from_image}, - PickableImageRect, SpatialSpaceView2D, + PickableImageRect, }; -use super::{bounding_box_for_textured_rect, SpatialViewVisualizerData}; +use super::SpatialViewVisualizerData; pub struct SegmentationImageVisualizer { pub data: SpatialViewVisualizerData, @@ -137,21 +137,9 @@ impl VisualizerSystem for SegmentationImageVisualizer { &image, meaning, multiplicative_tint, + "SegmentationImage", + &mut self.data, ) { - // Only update the bounding box if this is a 2D space view. - // This is avoids a cyclic relationship where the image plane grows - // the bounds which in turn influence the size of the image plane. - // See: https://github.com/rerun-io/rerun/issues/3728 - if spatial_ctx.space_view_class_identifier - == SpatialSpaceView2D::identifier() - { - self.data.add_bounding_box( - entity_path.hash(), - bounding_box_for_textured_rect(&textured_rect), - spatial_ctx.world_from_entity, - ); - } - self.images.push(PickableImageRect { ent_path: entity_path.clone(), row_id: image.blob_row_id, diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs b/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs index 2771842af2de..ee97433c8da0 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/transform3d_arrows.rs @@ -75,20 +75,22 @@ impl VisualizerSystem for Transform3DArrowsVisualizer { else { continue; }; - let world_from_obj = - if let Some(twod_in_threed_info) = &transform_info.twod_in_threed_info { - if twod_in_threed_info.parent_pinhole != data_result.entity_path { - // We're inside a 2D space. But this is a 3D transform. - // Something is wrong here and this is not the right place to report it. - // Better just don't draw the axis! - continue; - } else { - // Don't apply the from-2D transform, stick with the last known 3D. - twod_in_threed_info.reference_from_pinhole_entity - } + let world_from_obj = if let Some(twod_in_threed_info) = + &transform_info.twod_in_threed_info + { + if twod_in_threed_info.parent_pinhole != data_result.entity_path { + // We're inside a 2D space. But this is a 3D transform. + // Something is wrong here and this is not the right place to report it. + // Better just don't draw the axis! + continue; } else { - transform_info.reference_from_entity - }; + // Don't apply the from-2D transform, stick with the last known 3D. + twod_in_threed_info.reference_from_pinhole_entity + } + } else { + transform_info + .single_entity_transform_required(&data_result.entity_path, "Transform3DArrows") + }; let results = data_result .latest_at_with_blueprint_resolved_data::(ctx, &latest_at_query); diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs index 5ea09e277f5b..2a287270c726 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs @@ -162,11 +162,8 @@ where continue; }; - let world_from_entity = transform_info.reference_from_entity; - let depth_offset_key = (system_identifier, data_result.entity_path.hash()); let entity_context = SpatialSceneEntityContext { - world_from_entity, transform_info, depth_offset: depth_offsets .per_entity_and_visualizer diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/mod.rs b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/mod.rs index 57bca95c769a..5ac76c7f0d8b 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/mod.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/mod.rs @@ -7,6 +7,4 @@ pub use labels::{ process_labels_2d, process_labels_3d, UiLabel, UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY, }; pub use spatial_view_visualizer::SpatialViewVisualizerData; -pub use textured_rect::{ - bounding_box_for_textured_rect, textured_rect_from_image, textured_rect_from_tensor, -}; +pub use textured_rect::{textured_rect_from_image, textured_rect_from_tensor}; diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/textured_rect.rs b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/textured_rect.rs index ac591e9a69d3..9845842b0a0f 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/textured_rect.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/textured_rect.rs @@ -3,9 +3,13 @@ use re_chunk_store::RowId; use re_log_types::EntityPath; use re_renderer::renderer; use re_types::{components::Colormap, datatypes::TensorData, tensor_data::TensorDataMeaning}; -use re_viewer_context::{gpu_bridge, ImageInfo, ImageStatsCache, TensorStatsCache, ViewerContext}; +use re_viewer_context::{ + gpu_bridge, ImageInfo, ImageStatsCache, SpaceViewClass as _, TensorStatsCache, ViewerContext, +}; -use crate::contexts::SpatialSceneEntityContext; +use crate::{contexts::SpatialSceneEntityContext, SpatialSpaceView2D}; + +use super::SpatialViewVisualizerData; #[allow(clippy::too_many_arguments)] pub fn textured_rect_from_image( @@ -15,6 +19,8 @@ pub fn textured_rect_from_image( image: &ImageInfo, meaning: TensorDataMeaning, multiplicative_tint: egui::Rgba, + visualizer_name: &'static str, + visualizer_data: &mut SpatialViewVisualizerData, ) -> Option { let render_ctx = ctx.render_ctx?; @@ -47,9 +53,11 @@ pub fn textured_rect_from_image( renderer::TextureFilterMin::Linear }; - let world_from_entity = ent_context.world_from_entity; + let world_from_entity = ent_context + .transform_info + .single_entity_transform_required(ent_path, visualizer_name); - Some(renderer::TexturedRect { + let textured_rect = renderer::TexturedRect { top_left_corner_position: world_from_entity.transform_point3(Vec3::ZERO), extent_u: world_from_entity.transform_vector3(Vec3::X * image.width() as f32), extent_v: world_from_entity.transform_vector3(Vec3::Y * image.height() as f32), @@ -63,7 +71,21 @@ pub fn textured_rect_from_image( depth_offset: ent_context.depth_offset, outline_mask: ent_context.highlight.overall, }, - }) + }; + + // Only update the bounding box if this is a 2D space view. + // This is avoids a cyclic relationship where the image plane grows + // the bounds which in turn influence the size of the image plane. + // See: https://github.com/rerun-io/rerun/issues/3728 + if ent_context.space_view_class_identifier == SpatialSpaceView2D::identifier() { + visualizer_data.add_bounding_box( + ent_path.hash(), + bounding_box_for_textured_rect(&textured_rect), + world_from_entity, + ); + } + + Some(textured_rect) } Err(err) => { @@ -83,6 +105,8 @@ pub fn textured_rect_from_tensor( meaning: TensorDataMeaning, multiplicative_tint: egui::Rgba, colormap: Option, + visualizer_name: &'static str, + visualizer_data: &mut SpatialViewVisualizerData, ) -> Option { let render_ctx = ctx.render_ctx?; @@ -121,9 +145,11 @@ pub fn textured_rect_from_tensor( renderer::TextureFilterMin::Linear }; - let world_from_entity = ent_context.world_from_entity; + let world_from_entity = ent_context + .transform_info + .single_entity_transform_required(ent_path, visualizer_name); - Some(renderer::TexturedRect { + let textured_rect = renderer::TexturedRect { top_left_corner_position: world_from_entity.transform_point3(Vec3::ZERO), extent_u: world_from_entity.transform_vector3(Vec3::X * width as f32), extent_v: world_from_entity.transform_vector3(Vec3::Y * height as f32), @@ -135,7 +161,21 @@ pub fn textured_rect_from_tensor( depth_offset: ent_context.depth_offset, outline_mask: ent_context.highlight.overall, }, - }) + }; + + // Only update the bounding box if this is a 2D space view. + // This is avoids a cyclic relationship where the image plane grows + // the bounds which in turn influence the size of the image plane. + // See: https://github.com/rerun-io/rerun/issues/3728 + if ent_context.space_view_class_identifier == SpatialSpaceView2D::identifier() { + visualizer_data.add_bounding_box( + ent_path.hash(), + bounding_box_for_textured_rect(&textured_rect), + world_from_entity, + ); + } + + Some(textured_rect) } Err(err) => { re_log::error_once!("Failed to create texture for {debug_name:?}: {err}"); @@ -144,9 +184,7 @@ pub fn textured_rect_from_tensor( } } -pub fn bounding_box_for_textured_rect( - textured_rect: &renderer::TexturedRect, -) -> re_math::BoundingBox { +fn bounding_box_for_textured_rect(textured_rect: &renderer::TexturedRect) -> re_math::BoundingBox { let left_top = textured_rect.top_left_corner_position; let extent_u = textured_rect.extent_u; let extent_v = textured_rect.extent_v; diff --git a/docs/content/reference/types/archetypes/asset3d.md b/docs/content/reference/types/archetypes/asset3d.md index 8208b63d581e..6bddd34b2b18 100644 --- a/docs/content/reference/types/archetypes/asset3d.md +++ b/docs/content/reference/types/archetypes/asset3d.md @@ -7,6 +7,8 @@ A prepacked 3D asset (`.gltf`, `.glb`, `.obj`, `.stl`, etc.). See also [`archetypes.Mesh3D`](https://rerun.io/docs/reference/types/archetypes/mesh3d). +If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + ## Components **Required**: [`Blob`](../components/blob.md) @@ -22,7 +24,7 @@ See also [`archetypes.Mesh3D`](https://rerun.io/docs/reference/types/archetypes/ * 🐍 [Python API docs for `Asset3D`](https://ref.rerun.io/docs/python/stable/common/archetypes#rerun.archetypes.Asset3D) * 🦀 [Rust API docs for `Asset3D`](https://docs.rs/rerun/latest/rerun/archetypes/struct.Asset3D.html) -## Examples +## Example ### Simple 3D asset @@ -36,7 +38,3 @@ snippet: archetypes/asset3d_simple -### 3D asset with out-of-tree transform - -snippet: archetypes/asset3d_out_of_tree - diff --git a/docs/content/reference/types/archetypes/mesh3d.md b/docs/content/reference/types/archetypes/mesh3d.md index 37461ed89904..d347a58a103e 100644 --- a/docs/content/reference/types/archetypes/mesh3d.md +++ b/docs/content/reference/types/archetypes/mesh3d.md @@ -7,6 +7,8 @@ A 3D triangle mesh as specified by its per-mesh and per-vertex properties. See also [`archetypes.Asset3D`](https://rerun.io/docs/reference/types/archetypes/asset3d). +If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + ## Components **Required**: [`Position3D`](../components/position3d.md) diff --git a/rerun_cpp/src/rerun/archetypes/asset3d.hpp b/rerun_cpp/src/rerun/archetypes/asset3d.hpp index c8ef4757bdb3..3f6290276c79 100644 --- a/rerun_cpp/src/rerun/archetypes/asset3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/asset3d.hpp @@ -22,6 +22,8 @@ namespace rerun::archetypes { /// /// See also `archetypes::Mesh3D`. /// + /// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + /// /// ## Example /// /// ### Simple 3D asset diff --git a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp index cff8d26153e1..1f2724fd605e 100644 --- a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp @@ -27,6 +27,8 @@ namespace rerun::archetypes { /// /// See also `archetypes::Asset3D`. /// + /// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + /// /// ## Example /// /// ### Simple indexed 3D mesh diff --git a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py index 04c7330b53c2..e8eb82e1d479 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py @@ -23,6 +23,8 @@ class Asset3D(Asset3DExt, Archetype): See also [`archetypes.Mesh3D`][rerun.archetypes.Mesh3D]. + If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + Example ------- ### Simple 3D asset: diff --git a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py index 1b7e7ab53308..db2768a20d85 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py @@ -23,6 +23,8 @@ class Mesh3D(Mesh3DExt, Archetype): See also [`archetypes.Asset3D`][rerun.archetypes.Asset3D]. + If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + Example ------- ### Simple indexed 3D mesh: From 4a38c07538674c16a50ce2ce2423eb27b166cfb3 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 29 Jul 2024 16:33:23 +0200 Subject: [PATCH 09/29] improve leaf transform3d docs and make fields more consequently plural --- .../definitions/rerun/archetypes/asset3d.fbs | 2 +- .../rerun/archetypes/leaf_transforms3d.fbs | 20 ++-- .../definitions/rerun/archetypes/mesh3d.fbs | 2 +- .../rerun/components/transform_relation.fbs | 4 +- .../store/re_types/src/archetypes/asset3d.rs | 2 +- .../src/archetypes/leaf_transforms3d.rs | 105 ++++++++++-------- .../store/re_types/src/archetypes/mesh3d.rs | 2 +- .../src/components/transform_relation.rs | 8 +- crates/viewer/re_viewer/src/reflection/mod.rs | 14 +-- .../reference/types/archetypes/asset3d.md | 2 +- .../types/archetypes/leaf_transforms3d.md | 8 +- .../reference/types/archetypes/mesh3d.md | 2 +- rerun_cpp/src/rerun/archetypes/asset3d.hpp | 2 +- .../rerun/archetypes/leaf_transforms3d.cpp | 16 +-- .../rerun/archetypes/leaf_transforms3d.hpp | 45 ++++---- rerun_cpp/src/rerun/archetypes/mesh3d.hpp | 2 +- .../rerun/components/transform_relation.hpp | 4 +- rerun_py/rerun_sdk/rerun/__init__.py | 1 + .../rerun_sdk/rerun/archetypes/asset3d.py | 2 +- .../rerun/archetypes/leaf_transforms3d.py | 56 +++++----- rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py | 2 +- .../rerun/components/transform_relation.py | 4 +- 22 files changed, 169 insertions(+), 136 deletions(-) diff --git a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs index a00832f6daac..eca67013d9ed 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs @@ -6,7 +6,7 @@ namespace rerun.archetypes; /// /// See also [archetypes.Mesh3D]. /// -/// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +/// If there are multiple [archetypes.LeafTransforms3D], the mesh will be drawn for each transform. /// /// \example archetypes/asset3d_simple title="Simple 3D asset" image="https://static.rerun.io/asset3d_simple/af238578188d3fd0de3e330212120e2842a8ddb2/1200w.png" table Asset3D ( diff --git a/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs index 5af972ea864e..a80342d0d390 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs @@ -3,7 +3,13 @@ namespace rerun.archetypes; /// One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. /// -/// For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. +/// For transforms that are propagated in the transform hierarchy, see [archetypes.Transform3D]. +/// +/// If both [archetypes.LeafTransforms3D] and [archetypes.Transform3D] are present, +/// first the tree propagating [archetypes.Transform3D] is applied, then [archetypes.LeafTransforms3D]. +/// +/// Currently, most visualizers support only a single leaf transform per entity. +/// Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. /// /// From the point of view of the entity's coordinate system, /// all components are applied in the inverse order they are listed here. @@ -17,18 +23,18 @@ table LeafTransforms3D ( "attr.docs.view_types": "Spatial3DView, Spatial2DView: if logged above active projection" ) { /// Translation vectors. - translation: [rerun.components.LeafTranslation3D] ("attr.rerun.component_optional", nullable, order: 1100); + translations: [rerun.components.LeafTranslation3D] ("attr.rerun.component_optional", nullable, order: 1100); /// Rotations via axis + angle. - rotation_axis_angle: [rerun.components.LeafRotationAxisAngle] ("attr.rerun.component_optional", nullable, order: 1200); + rotation_axis_angles: [rerun.components.LeafRotationAxisAngle] ("attr.rerun.component_optional", nullable, order: 1200); /// Rotations via quaternion. - quaternion: [rerun.components.LeafRotationQuat] ("attr.rerun.component_optional", nullable, order: 1300); + quaternions: [rerun.components.LeafRotationQuat] ("attr.rerun.component_optional", nullable, order: 1300); - /// Scaling factor. - scale: [rerun.components.LeafScale3D] ("attr.rerun.component_optional", nullable, order: 1400); + /// Scaling factors. + scales: [rerun.components.LeafScale3D] ("attr.rerun.component_optional", nullable, order: 1400); - /// 3x3 transformation matrix. + /// 3x3 transformation matrices. mat3x3: [rerun.components.LeafTransformMat3x3] ("attr.rerun.component_optional", nullable, order: 1500); // TODO(andreas): Support TransformRelation? diff --git a/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs index c342d5a2171e..b910d00dee02 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs @@ -6,7 +6,7 @@ namespace rerun.archetypes; /// /// See also [archetypes.Asset3D]. /// -/// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +/// If there are multiple [archetypes.LeafTransforms3D], the mesh will be drawn for each transform. /// /// \example archetypes/mesh3d_indexed title="Simple indexed 3D mesh" image="https://static.rerun.io/mesh3d_simple/e1e5fd97265daf0d0bc7b782d862f19086fd6975/1200w.png" /// \example archetypes/mesh3d_partial_updates !api title="3D mesh with partial updates" image="https://static.rerun.io/mesh3d_partial_updates/a11e4accb0257dcd9531867b7e1d6fd5e3bee5c3/1200w.png" diff --git a/crates/store/re_types/definitions/rerun/components/transform_relation.fbs b/crates/store/re_types/definitions/rerun/components/transform_relation.fbs index 94543ceb28ab..2deaef624dca 100644 --- a/crates/store/re_types/definitions/rerun/components/transform_relation.fbs +++ b/crates/store/re_types/definitions/rerun/components/transform_relation.fbs @@ -6,14 +6,14 @@ enum TransformRelation: byte ( ) { /// The transform describes how to transform into the parent entity's space. /// - /// E.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means + /// E.g. a translation of (0, 1, 0) with this [components.TransformRelation] logged at `parent/child` means /// that from the point of view of `parent`, `parent/child` is translated 1 unit along `parent`'s Y axis. /// From perspective of `parent/child`, the `parent` entity is translated -1 unit along `parent/child`'s Y axis. ParentFromChild(default), /// The transform describes how to transform into the child entity's space. /// - /// E.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means + /// E.g. a translation of (0, 1, 0) with this [components.TransformRelation] logged at `parent/child` means /// that from the point of view of `parent`, `parent/child` is translated -1 unit along `parent`'s Y axis. /// From perspective of `parent/child`, the `parent` entity is translated 1 unit along `parent/child`'s Y axis. ChildFromParent, diff --git a/crates/store/re_types/src/archetypes/asset3d.rs b/crates/store/re_types/src/archetypes/asset3d.rs index f93f7ec97802..15d4f92eef82 100644 --- a/crates/store/re_types/src/archetypes/asset3d.rs +++ b/crates/store/re_types/src/archetypes/asset3d.rs @@ -22,7 +22,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// See also [`archetypes::Mesh3D`][crate::archetypes::Mesh3D]. /// -/// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +/// If there are multiple [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D], the mesh will be drawn for each transform. /// /// ## Example /// diff --git a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs index 20b4c10d8dc7..ca7109d3d7f8 100644 --- a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs +++ b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs @@ -20,7 +20,13 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// **Archetype**: One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. /// -/// For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. +/// For transforms that are propagated in the transform hierarchy, see [`archetypes::Transform3D`][crate::archetypes::Transform3D]. +/// +/// If both [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D] and [`archetypes::Transform3D`][crate::archetypes::Transform3D] are present, +/// first the tree propagating [`archetypes::Transform3D`][crate::archetypes::Transform3D] is applied, then [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. +/// +/// Currently, most visualizers support only a single leaf transform per entity. +/// Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. /// /// From the point of view of the entity's coordinate system, /// all components are applied in the inverse order they are listed here. @@ -29,28 +35,28 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; #[derive(Clone, Debug, Default, PartialEq)] pub struct LeafTransforms3D { /// Translation vectors. - pub translation: Option>, + pub translations: Option>, /// Rotations via axis + angle. - pub rotation_axis_angle: Option>, + pub rotation_axis_angles: Option>, /// Rotations via quaternion. - pub quaternion: Option>, + pub quaternions: Option>, - /// Scaling factor. - pub scale: Option>, + /// Scaling factors. + pub scales: Option>, - /// 3x3 transformation matrix. + /// 3x3 transformation matrices. pub mat3x3: Option>, } impl ::re_types_core::SizeBytes for LeafTransforms3D { #[inline] fn heap_size_bytes(&self) -> u64 { - self.translation.heap_size_bytes() - + self.rotation_axis_angle.heap_size_bytes() - + self.quaternion.heap_size_bytes() - + self.scale.heap_size_bytes() + self.translations.heap_size_bytes() + + self.rotation_axis_angles.heap_size_bytes() + + self.quaternions.heap_size_bytes() + + self.scales.heap_size_bytes() + self.mat3x3.heap_size_bytes() } @@ -150,53 +156,53 @@ impl ::re_types_core::Archetype for LeafTransforms3D { .into_iter() .map(|(name, array)| (name.full_name(), array)) .collect(); - let translation = + let translations = if let Some(array) = arrays_by_name.get("rerun.components.LeafTranslation3D") { Some({ ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.LeafTransforms3D#translation")? + .with_context("rerun.archetypes.LeafTransforms3D#translations")? .into_iter() .map(|v| v.ok_or_else(DeserializationError::missing_data)) .collect::>>() - .with_context("rerun.archetypes.LeafTransforms3D#translation")? + .with_context("rerun.archetypes.LeafTransforms3D#translations")? }) } else { None }; - let rotation_axis_angle = + let rotation_axis_angles = if let Some(array) = arrays_by_name.get("rerun.components.LeafRotationAxisAngle") { Some({ ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.LeafTransforms3D#rotation_axis_angle")? + .with_context("rerun.archetypes.LeafTransforms3D#rotation_axis_angles")? .into_iter() .map(|v| v.ok_or_else(DeserializationError::missing_data)) .collect::>>() - .with_context("rerun.archetypes.LeafTransforms3D#rotation_axis_angle")? + .with_context("rerun.archetypes.LeafTransforms3D#rotation_axis_angles")? }) } else { None }; - let quaternion = + let quaternions = if let Some(array) = arrays_by_name.get("rerun.components.LeafRotationQuat") { Some({ ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.LeafTransforms3D#quaternion")? + .with_context("rerun.archetypes.LeafTransforms3D#quaternions")? .into_iter() .map(|v| v.ok_or_else(DeserializationError::missing_data)) .collect::>>() - .with_context("rerun.archetypes.LeafTransforms3D#quaternion")? + .with_context("rerun.archetypes.LeafTransforms3D#quaternions")? }) } else { None }; - let scale = if let Some(array) = arrays_by_name.get("rerun.components.LeafScale3D") { + let scales = if let Some(array) = arrays_by_name.get("rerun.components.LeafScale3D") { Some({ ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.LeafTransforms3D#scale")? + .with_context("rerun.archetypes.LeafTransforms3D#scales")? .into_iter() .map(|v| v.ok_or_else(DeserializationError::missing_data)) .collect::>>() - .with_context("rerun.archetypes.LeafTransforms3D#scale")? + .with_context("rerun.archetypes.LeafTransforms3D#scales")? }) } else { None @@ -215,10 +221,10 @@ impl ::re_types_core::Archetype for LeafTransforms3D { None }; Ok(Self { - translation, - rotation_axis_angle, - quaternion, - scale, + translations, + rotation_axis_angles, + quaternions, + scales, mat3x3, }) } @@ -230,16 +236,16 @@ impl ::re_types_core::AsComponents for LeafTransforms3D { use ::re_types_core::Archetype as _; [ Some(Self::indicator()), - self.translation + self.translations .as_ref() .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), - self.rotation_axis_angle + self.rotation_axis_angles .as_ref() .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), - self.quaternion + self.quaternions .as_ref() .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), - self.scale + self.scales .as_ref() .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), self.mat3x3 @@ -259,57 +265,58 @@ impl LeafTransforms3D { #[inline] pub fn new() -> Self { Self { - translation: None, - rotation_axis_angle: None, - quaternion: None, - scale: None, + translations: None, + rotation_axis_angles: None, + quaternions: None, + scales: None, mat3x3: None, } } /// Translation vectors. #[inline] - pub fn with_translation( + pub fn with_translations( mut self, - translation: impl IntoIterator>, + translations: impl IntoIterator>, ) -> Self { - self.translation = Some(translation.into_iter().map(Into::into).collect()); + self.translations = Some(translations.into_iter().map(Into::into).collect()); self } /// Rotations via axis + angle. #[inline] - pub fn with_rotation_axis_angle( + pub fn with_rotation_axis_angles( mut self, - rotation_axis_angle: impl IntoIterator< + rotation_axis_angles: impl IntoIterator< Item = impl Into, >, ) -> Self { - self.rotation_axis_angle = Some(rotation_axis_angle.into_iter().map(Into::into).collect()); + self.rotation_axis_angles = + Some(rotation_axis_angles.into_iter().map(Into::into).collect()); self } /// Rotations via quaternion. #[inline] - pub fn with_quaternion( + pub fn with_quaternions( mut self, - quaternion: impl IntoIterator>, + quaternions: impl IntoIterator>, ) -> Self { - self.quaternion = Some(quaternion.into_iter().map(Into::into).collect()); + self.quaternions = Some(quaternions.into_iter().map(Into::into).collect()); self } - /// Scaling factor. + /// Scaling factors. #[inline] - pub fn with_scale( + pub fn with_scales( mut self, - scale: impl IntoIterator>, + scales: impl IntoIterator>, ) -> Self { - self.scale = Some(scale.into_iter().map(Into::into).collect()); + self.scales = Some(scales.into_iter().map(Into::into).collect()); self } - /// 3x3 transformation matrix. + /// 3x3 transformation matrices. #[inline] pub fn with_mat3x3( mut self, diff --git a/crates/store/re_types/src/archetypes/mesh3d.rs b/crates/store/re_types/src/archetypes/mesh3d.rs index f24a13a210eb..1c51a846a67e 100644 --- a/crates/store/re_types/src/archetypes/mesh3d.rs +++ b/crates/store/re_types/src/archetypes/mesh3d.rs @@ -22,7 +22,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// See also [`archetypes::Asset3D`][crate::archetypes::Asset3D]. /// -/// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +/// If there are multiple [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D], the mesh will be drawn for each transform. /// /// ## Example /// diff --git a/crates/store/re_types/src/components/transform_relation.rs b/crates/store/re_types/src/components/transform_relation.rs index 5af660a8ecc0..aa14638e7873 100644 --- a/crates/store/re_types/src/components/transform_relation.rs +++ b/crates/store/re_types/src/components/transform_relation.rs @@ -23,7 +23,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; pub enum TransformRelation { /// The transform describes how to transform into the parent entity's space. /// - /// E.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means + /// E.g. a translation of (0, 1, 0) with this [`components::TransformRelation`][crate::components::TransformRelation] logged at `parent/child` means /// that from the point of view of `parent`, `parent/child` is translated 1 unit along `parent`'s Y axis. /// From perspective of `parent/child`, the `parent` entity is translated -1 unit along `parent/child`'s Y axis. #[default] @@ -31,7 +31,7 @@ pub enum TransformRelation { /// The transform describes how to transform into the child entity's space. /// - /// E.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means + /// E.g. a translation of (0, 1, 0) with this [`components::TransformRelation`][crate::components::TransformRelation] logged at `parent/child` means /// that from the point of view of `parent`, `parent/child` is translated -1 unit along `parent`'s Y axis. /// From perspective of `parent/child`, the `parent` entity is translated 1 unit along `parent/child`'s Y axis. ChildFromParent = 2, @@ -47,10 +47,10 @@ impl ::re_types_core::reflection::Enum for TransformRelation { fn docstring_md(self) -> &'static str { match self { Self::ParentFromChild => { - "The transform describes how to transform into the parent entity's space.\n\nE.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means\nthat from the point of view of `parent`, `parent/child` is translated 1 unit along `parent`'s Y axis.\nFrom perspective of `parent/child`, the `parent` entity is translated -1 unit along `parent/child`'s Y axis." + "The transform describes how to transform into the parent entity's space.\n\nE.g. a translation of (0, 1, 0) with this [`components::TransformRelation`][crate::components::TransformRelation] logged at `parent/child` means\nthat from the point of view of `parent`, `parent/child` is translated 1 unit along `parent`'s Y axis.\nFrom perspective of `parent/child`, the `parent` entity is translated -1 unit along `parent/child`'s Y axis." } Self::ChildFromParent => { - "The transform describes how to transform into the child entity's space.\n\nE.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means\nthat from the point of view of `parent`, `parent/child` is translated -1 unit along `parent`'s Y axis.\nFrom perspective of `parent/child`, the `parent` entity is translated 1 unit along `parent/child`'s Y axis." + "The transform describes how to transform into the child entity's space.\n\nE.g. a translation of (0, 1, 0) with this [`components::TransformRelation`][crate::components::TransformRelation] logged at `parent/child` means\nthat from the point of view of `parent`, `parent/child` is translated -1 unit along `parent`'s Y axis.\nFrom perspective of `parent/child`, the `parent` entity is translated 1 unit along `parent/child`'s Y axis." } } } diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index e8a30916df29..50db839fdbcd 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -686,23 +686,23 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { ArchetypeName::new("rerun.archetypes.LeafTransforms3D"), ArchetypeReflection { display_name: "Leaf transforms 3D", - docstring_md: "One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy.\n\nFor transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`].\n\nFrom the point of view of the entity's coordinate system,\nall components are applied in the inverse order they are listed here.\nE.g. if both a translation and a max3x3 transform are present,\nthe 3x3 matrix is applied first, followed by the translation.", + docstring_md: "One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy.\n\nFor transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d).\n\nIf both [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) and [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) are present,\nfirst the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d).\n\nCurrently, most visualizers support only a single leaf transform per entity.\nCheck archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied.\n\nFrom the point of view of the entity's coordinate system,\nall components are applied in the inverse order they are listed here.\nE.g. if both a translation and a max3x3 transform are present,\nthe 3x3 matrix is applied first, followed by the translation.", fields: vec![ ArchetypeFieldReflection { component_name : "rerun.components.LeafTranslation3D".into(), display_name : - "Translation", docstring_md : "Translation vectors.", }, + "Translations", docstring_md : "Translation vectors.", }, ArchetypeFieldReflection { component_name : "rerun.components.LeafRotationAxisAngle".into(), display_name : - "Rotation axis angle", docstring_md : "Rotations via axis + angle.", + "Rotation axis angles", docstring_md : "Rotations via axis + angle.", }, ArchetypeFieldReflection { component_name : "rerun.components.LeafRotationQuat".into(), display_name : - "Quaternion", docstring_md : "Rotations via quaternion.", }, + "Quaternions", docstring_md : "Rotations via quaternion.", }, ArchetypeFieldReflection { component_name : - "rerun.components.LeafScale3D".into(), display_name : "Scale", - docstring_md : "Scaling factor.", }, ArchetypeFieldReflection { + "rerun.components.LeafScale3D".into(), display_name : "Scales", + docstring_md : "Scaling factors.", }, ArchetypeFieldReflection { component_name : "rerun.components.LeafTransformMat3x3".into(), display_name : "Mat 3x 3", docstring_md : - "3x3 transformation matrix.", }, + "3x3 transformation matrices.", }, ], }, ), diff --git a/docs/content/reference/types/archetypes/asset3d.md b/docs/content/reference/types/archetypes/asset3d.md index 6bddd34b2b18..989bddfb4ea3 100644 --- a/docs/content/reference/types/archetypes/asset3d.md +++ b/docs/content/reference/types/archetypes/asset3d.md @@ -7,7 +7,7 @@ A prepacked 3D asset (`.gltf`, `.glb`, `.obj`, `.stl`, etc.). See also [`archetypes.Mesh3D`](https://rerun.io/docs/reference/types/archetypes/mesh3d). -If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +If there are multiple [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d), the mesh will be drawn for each transform. ## Components diff --git a/docs/content/reference/types/archetypes/leaf_transforms3d.md b/docs/content/reference/types/archetypes/leaf_transforms3d.md index a00274cecdbb..6c9306222a66 100644 --- a/docs/content/reference/types/archetypes/leaf_transforms3d.md +++ b/docs/content/reference/types/archetypes/leaf_transforms3d.md @@ -5,7 +5,13 @@ title: "LeafTransforms3D" One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. -For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. +For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d). + +If both [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) and [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) are present, +first the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d). + +Currently, most visualizers support only a single leaf transform per entity. +Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. From the point of view of the entity's coordinate system, all components are applied in the inverse order they are listed here. diff --git a/docs/content/reference/types/archetypes/mesh3d.md b/docs/content/reference/types/archetypes/mesh3d.md index d347a58a103e..7183b207f301 100644 --- a/docs/content/reference/types/archetypes/mesh3d.md +++ b/docs/content/reference/types/archetypes/mesh3d.md @@ -7,7 +7,7 @@ A 3D triangle mesh as specified by its per-mesh and per-vertex properties. See also [`archetypes.Asset3D`](https://rerun.io/docs/reference/types/archetypes/asset3d). -If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. +If there are multiple [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d), the mesh will be drawn for each transform. ## Components diff --git a/rerun_cpp/src/rerun/archetypes/asset3d.hpp b/rerun_cpp/src/rerun/archetypes/asset3d.hpp index 3f6290276c79..5b10825de8ad 100644 --- a/rerun_cpp/src/rerun/archetypes/asset3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/asset3d.hpp @@ -22,7 +22,7 @@ namespace rerun::archetypes { /// /// See also `archetypes::Mesh3D`. /// - /// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + /// If there are multiple `archetypes::LeafTransforms3D`, the mesh will be drawn for each transform. /// /// ## Example /// diff --git a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.cpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.cpp index f6aca770ac0d..48423d9677e3 100644 --- a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.cpp +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.cpp @@ -16,23 +16,23 @@ namespace rerun { std::vector cells; cells.reserve(6); - if (archetype.translation.has_value()) { - auto result = DataCell::from_loggable(archetype.translation.value()); + if (archetype.translations.has_value()) { + auto result = DataCell::from_loggable(archetype.translations.value()); RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } - if (archetype.rotation_axis_angle.has_value()) { - auto result = DataCell::from_loggable(archetype.rotation_axis_angle.value()); + if (archetype.rotation_axis_angles.has_value()) { + auto result = DataCell::from_loggable(archetype.rotation_axis_angles.value()); RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } - if (archetype.quaternion.has_value()) { - auto result = DataCell::from_loggable(archetype.quaternion.value()); + if (archetype.quaternions.has_value()) { + auto result = DataCell::from_loggable(archetype.quaternions.value()); RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } - if (archetype.scale.has_value()) { - auto result = DataCell::from_loggable(archetype.scale.value()); + if (archetype.scales.has_value()) { + auto result = DataCell::from_loggable(archetype.scales.value()); RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } diff --git a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp index 6dd2de5b121d..ad8fc4cd136d 100644 --- a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp @@ -22,7 +22,13 @@ namespace rerun::archetypes { /// **Archetype**: One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. /// - /// For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. + /// For transforms that are propagated in the transform hierarchy, see `archetypes::Transform3D`. + /// + /// If both `archetypes::LeafTransforms3D` and `archetypes::Transform3D` are present, + /// first the tree propagating `archetypes::Transform3D` is applied, then `archetypes::LeafTransforms3D`. + /// + /// Currently, most visualizers support only a single leaf transform per entity. + /// Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. /// /// From the point of view of the entity's coordinate system, /// all components are applied in the inverse order they are listed here. @@ -30,18 +36,18 @@ namespace rerun::archetypes { /// the 3x3 matrix is applied first, followed by the translation. struct LeafTransforms3D { /// Translation vectors. - std::optional> translation; + std::optional> translations; /// Rotations via axis + angle. - std::optional> rotation_axis_angle; + std::optional> rotation_axis_angles; /// Rotations via quaternion. - std::optional> quaternion; + std::optional> quaternions; - /// Scaling factor. - std::optional> scale; + /// Scaling factors. + std::optional> scales; - /// 3x3 transformation matrix. + /// 3x3 transformation matrices. std::optional> mat3x3; public: @@ -56,39 +62,40 @@ namespace rerun::archetypes { LeafTransforms3D(LeafTransforms3D&& other) = default; /// Translation vectors. - LeafTransforms3D with_translation( - Collection _translation + LeafTransforms3D with_translations( + Collection _translations ) && { - translation = std::move(_translation); + translations = std::move(_translations); // See: https://github.com/rerun-io/rerun/issues/4027 RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } /// Rotations via axis + angle. - LeafTransforms3D with_rotation_axis_angle( - Collection _rotation_axis_angle + LeafTransforms3D with_rotation_axis_angles( + Collection _rotation_axis_angles ) && { - rotation_axis_angle = std::move(_rotation_axis_angle); + rotation_axis_angles = std::move(_rotation_axis_angles); // See: https://github.com/rerun-io/rerun/issues/4027 RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } /// Rotations via quaternion. - LeafTransforms3D with_quaternion(Collection _quaternion + LeafTransforms3D with_quaternions( + Collection _quaternions ) && { - quaternion = std::move(_quaternion); + quaternions = std::move(_quaternions); // See: https://github.com/rerun-io/rerun/issues/4027 RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } - /// Scaling factor. - LeafTransforms3D with_scale(Collection _scale) && { - scale = std::move(_scale); + /// Scaling factors. + LeafTransforms3D with_scales(Collection _scales) && { + scales = std::move(_scales); // See: https://github.com/rerun-io/rerun/issues/4027 RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } - /// 3x3 transformation matrix. + /// 3x3 transformation matrices. LeafTransforms3D with_mat3x3(Collection _mat3x3 ) && { mat3x3 = std::move(_mat3x3); diff --git a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp index 1f2724fd605e..3538b70f01fe 100644 --- a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp @@ -27,7 +27,7 @@ namespace rerun::archetypes { /// /// See also `archetypes::Asset3D`. /// - /// If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + /// If there are multiple `archetypes::LeafTransforms3D`, the mesh will be drawn for each transform. /// /// ## Example /// diff --git a/rerun_cpp/src/rerun/components/transform_relation.hpp b/rerun_cpp/src/rerun/components/transform_relation.hpp index 1a76de63a617..87ceb3e9b731 100644 --- a/rerun_cpp/src/rerun/components/transform_relation.hpp +++ b/rerun_cpp/src/rerun/components/transform_relation.hpp @@ -20,14 +20,14 @@ namespace rerun::components { /// The transform describes how to transform into the parent entity's space. /// - /// E.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means + /// E.g. a translation of (0, 1, 0) with this `components::TransformRelation` logged at `parent/child` means /// that from the point of view of `parent`, `parent/child` is translated 1 unit along `parent`'s Y axis. /// From perspective of `parent/child`, the `parent` entity is translated -1 unit along `parent/child`'s Y axis. ParentFromChild = 1, /// The transform describes how to transform into the child entity's space. /// - /// E.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means + /// E.g. a translation of (0, 1, 0) with this `components::TransformRelation` logged at `parent/child` means /// that from the point of view of `parent`, `parent/child` is translated -1 unit along `parent`'s Y axis. /// From perspective of `parent/child`, the `parent` entity is translated 1 unit along `parent/child`'s Y axis. ChildFromParent = 2, diff --git a/rerun_py/rerun_sdk/rerun/__init__.py b/rerun_py/rerun_sdk/rerun/__init__.py index 2a63264cc313..b93b056dc3f2 100644 --- a/rerun_py/rerun_sdk/rerun/__init__.py +++ b/rerun_py/rerun_sdk/rerun/__init__.py @@ -51,6 +51,7 @@ Ellipsoids as Ellipsoids, Image as Image, ImageEncoded as ImageEncoded, + LeafTransforms3D as LeafTransforms3D, LineStrips2D as LineStrips2D, LineStrips3D as LineStrips3D, Mesh3D as Mesh3D, diff --git a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py index e8eb82e1d479..a4b753aee4e6 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py @@ -23,7 +23,7 @@ class Asset3D(Asset3DExt, Archetype): See also [`archetypes.Mesh3D`][rerun.archetypes.Mesh3D]. - If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + If there are multiple [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D], the mesh will be drawn for each transform. Example ------- diff --git a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py index b921c673459b..8009196f7667 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py @@ -23,7 +23,13 @@ class LeafTransforms3D(Archetype): """ **Archetype**: One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy. - For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`]. + For transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`][rerun.archetypes.Transform3D]. + + If both [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D] and [`archetypes.Transform3D`][rerun.archetypes.Transform3D] are present, + first the tree propagating [`archetypes.Transform3D`][rerun.archetypes.Transform3D] is applied, then [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. + + Currently, most visualizers support only a single leaf transform per entity. + Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. From the point of view of the entity's coordinate system, all components are applied in the inverse order they are listed here. @@ -34,10 +40,10 @@ class LeafTransforms3D(Archetype): def __init__( self: Any, *, - translation: datatypes.Vec3DArrayLike | None = None, - rotation_axis_angle: datatypes.RotationAxisAngleArrayLike | None = None, - quaternion: datatypes.QuaternionArrayLike | None = None, - scale: datatypes.Vec3DArrayLike | None = None, + translations: datatypes.Vec3DArrayLike | None = None, + rotation_axis_angles: datatypes.RotationAxisAngleArrayLike | None = None, + quaternions: datatypes.QuaternionArrayLike | None = None, + scales: datatypes.Vec3DArrayLike | None = None, mat3x3: datatypes.Mat3x3ArrayLike | None = None, ): """ @@ -45,26 +51,26 @@ def __init__( Parameters ---------- - translation: + translations: Translation vectors. - rotation_axis_angle: + rotation_axis_angles: Rotations via axis + angle. - quaternion: + quaternions: Rotations via quaternion. - scale: - Scaling factor. + scales: + Scaling factors. mat3x3: - 3x3 transformation matrix. + 3x3 transformation matrices. """ # You can define your own __init__ function as a member of LeafTransforms3DExt in leaf_transforms3d_ext.py with catch_and_log_exceptions(context=self.__class__.__name__): self.__attrs_init__( - translation=translation, - rotation_axis_angle=rotation_axis_angle, - quaternion=quaternion, - scale=scale, + translations=translations, + rotation_axis_angles=rotation_axis_angles, + quaternions=quaternions, + scales=scales, mat3x3=mat3x3, ) return @@ -73,10 +79,10 @@ def __init__( def __attrs_clear__(self) -> None: """Convenience method for calling `__attrs_init__` with all `None`s.""" self.__attrs_init__( - translation=None, # type: ignore[arg-type] - rotation_axis_angle=None, # type: ignore[arg-type] - quaternion=None, # type: ignore[arg-type] - scale=None, # type: ignore[arg-type] + translations=None, # type: ignore[arg-type] + rotation_axis_angles=None, # type: ignore[arg-type] + quaternions=None, # type: ignore[arg-type] + scales=None, # type: ignore[arg-type] mat3x3=None, # type: ignore[arg-type] ) @@ -87,7 +93,7 @@ def _clear(cls) -> LeafTransforms3D: inst.__attrs_clear__() return inst - translation: components.LeafTranslation3DBatch | None = field( + translations: components.LeafTranslation3DBatch | None = field( metadata={"component": "optional"}, default=None, converter=components.LeafTranslation3DBatch._optional, # type: ignore[misc] @@ -96,7 +102,7 @@ def _clear(cls) -> LeafTransforms3D: # # (Docstring intentionally commented out to hide this field from the docs) - rotation_axis_angle: components.LeafRotationAxisAngleBatch | None = field( + rotation_axis_angles: components.LeafRotationAxisAngleBatch | None = field( metadata={"component": "optional"}, default=None, converter=components.LeafRotationAxisAngleBatch._optional, # type: ignore[misc] @@ -105,7 +111,7 @@ def _clear(cls) -> LeafTransforms3D: # # (Docstring intentionally commented out to hide this field from the docs) - quaternion: components.LeafRotationQuatBatch | None = field( + quaternions: components.LeafRotationQuatBatch | None = field( metadata={"component": "optional"}, default=None, converter=components.LeafRotationQuatBatch._optional, # type: ignore[misc] @@ -114,12 +120,12 @@ def _clear(cls) -> LeafTransforms3D: # # (Docstring intentionally commented out to hide this field from the docs) - scale: components.LeafScale3DBatch | None = field( + scales: components.LeafScale3DBatch | None = field( metadata={"component": "optional"}, default=None, converter=components.LeafScale3DBatch._optional, # type: ignore[misc] ) - # Scaling factor. + # Scaling factors. # # (Docstring intentionally commented out to hide this field from the docs) @@ -128,7 +134,7 @@ def _clear(cls) -> LeafTransforms3D: default=None, converter=components.LeafTransformMat3x3Batch._optional, # type: ignore[misc] ) - # 3x3 transformation matrix. + # 3x3 transformation matrices. # # (Docstring intentionally commented out to hide this field from the docs) diff --git a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py index db2768a20d85..d3fa38bb1ac2 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py @@ -23,7 +23,7 @@ class Mesh3D(Mesh3DExt, Archetype): See also [`archetypes.Asset3D`][rerun.archetypes.Asset3D]. - If there are multiple `LeafTransforms3D`, the mesh will be drawn for each transform. + If there are multiple [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D], the mesh will be drawn for each transform. Example ------- diff --git a/rerun_py/rerun_sdk/rerun/components/transform_relation.py b/rerun_py/rerun_sdk/rerun/components/transform_relation.py index 71ec11127c6c..e9bead75f441 100644 --- a/rerun_py/rerun_sdk/rerun/components/transform_relation.py +++ b/rerun_py/rerun_sdk/rerun/components/transform_relation.py @@ -34,7 +34,7 @@ class TransformRelation(Enum): """ The transform describes how to transform into the parent entity's space. - E.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means + E.g. a translation of (0, 1, 0) with this [`components.TransformRelation`][rerun.components.TransformRelation] logged at `parent/child` means that from the point of view of `parent`, `parent/child` is translated 1 unit along `parent`'s Y axis. From perspective of `parent/child`, the `parent` entity is translated -1 unit along `parent/child`'s Y axis. """ @@ -43,7 +43,7 @@ class TransformRelation(Enum): """ The transform describes how to transform into the child entity's space. - E.g. a translation of (0, 1, 0) with this `TransformRelation` logged at `parent/child` means + E.g. a translation of (0, 1, 0) with this [`components.TransformRelation`][rerun.components.TransformRelation] logged at `parent/child` means that from the point of view of `parent`, `parent/child` is translated -1 unit along `parent`'s Y axis. From perspective of `parent/child`, the `parent` entity is translated 1 unit along `parent/child`'s Y axis. """ From 539529d03f45ed016776954e2ce6a3629868a414 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 29 Jul 2024 16:56:41 +0200 Subject: [PATCH 10/29] add missing python leafscale3d extension --- .../rerun/components/leaf_scale3d.py | 5 +-- .../rerun/components/leaf_scale3d_ext.py | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 rerun_py/rerun_sdk/rerun/components/leaf_scale3d_ext.py diff --git a/rerun_py/rerun_sdk/rerun/components/leaf_scale3d.py b/rerun_py/rerun_sdk/rerun/components/leaf_scale3d.py index c02a492d1779..99b9aae4502a 100644 --- a/rerun_py/rerun_sdk/rerun/components/leaf_scale3d.py +++ b/rerun_py/rerun_sdk/rerun/components/leaf_scale3d.py @@ -10,11 +10,12 @@ ComponentBatchMixin, ComponentMixin, ) +from .leaf_scale3d_ext import LeafScale3DExt __all__ = ["LeafScale3D", "LeafScale3DBatch", "LeafScale3DType"] -class LeafScale3D(datatypes.Vec3D, ComponentMixin): +class LeafScale3D(LeafScale3DExt, datatypes.Vec3D, ComponentMixin): """ **Component**: A 3D scale factor that doesn't propagate in the transform hierarchy. @@ -24,7 +25,7 @@ class LeafScale3D(datatypes.Vec3D, ComponentMixin): """ _BATCH_TYPE = None - # You can define your own __init__ function as a member of LeafScale3DExt in leaf_scale3d_ext.py + # __init__ can be found in leaf_scale3d_ext.py # Note: there are no fields here because LeafScale3D delegates to datatypes.Vec3D pass diff --git a/rerun_py/rerun_sdk/rerun/components/leaf_scale3d_ext.py b/rerun_py/rerun_sdk/rerun/components/leaf_scale3d_ext.py new file mode 100644 index 000000000000..cd8fe78ca338 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/leaf_scale3d_ext.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Union + +if TYPE_CHECKING: + from rerun.datatypes import Float32Like, Vec3DLike + + +class LeafScale3DExt: + """Extension for [LeafScale3D][rerun.components.LeafScale3D].""" + + def __init__( + self: Any, + uniform_or_per_axis: Union[Vec3DLike, Float32Like] = True, + ): + """ + 3D scaling factor. + + A scale of 1.0 means no scaling. + A scale of 2.0 means doubling the size. + Each component scales along the corresponding axis. + + Parameters + ---------- + uniform_or_per_axis: + If a single value is given, it is applied the same to all three axis (uniform scaling). + + """ + if not hasattr(uniform_or_per_axis, "__len__") or len(uniform_or_per_axis) == 1: # type: ignore[arg-type] + self.__attrs_init__([uniform_or_per_axis, uniform_or_per_axis, uniform_or_per_axis]) + else: + self.__attrs_init__(uniform_or_per_axis) From 73d9a2a094a9bce63c602d2003774d442adf0b7b Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 29 Jul 2024 16:56:49 +0200 Subject: [PATCH 11/29] add leat transform3d test --- rerun_py/tests/unit/test_leaf_transforms3d.py | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 rerun_py/tests/unit/test_leaf_transforms3d.py diff --git a/rerun_py/tests/unit/test_leaf_transforms3d.py b/rerun_py/tests/unit/test_leaf_transforms3d.py new file mode 100644 index 000000000000..4307ded32056 --- /dev/null +++ b/rerun_py/tests/unit/test_leaf_transforms3d.py @@ -0,0 +1,87 @@ +from __future__ import annotations + +import itertools +from fractions import Fraction +from typing import Optional, cast + +import rerun as rr +from rerun.datatypes import ( + Angle, + Quaternion, + RotationAxisAngle, +) + +from .common_arrays import none_empty_or_value +from .test_matnxn import MAT_3X3_INPUT +from .test_vecnd import VEC_3D_INPUT + +SCALE_3D_INPUT = [ + # Uniform + 4, + 4.0, + Fraction(8, 2), + # ThreeD + *VEC_3D_INPUT, +] + + +def test_leaf_transform3d() -> None: + rotation_axis_angle_arrays = [ + None, + RotationAxisAngle([1, 2, 3], rr.Angle(deg=10)), + [RotationAxisAngle([1, 2, 3], rr.Angle(deg=10)), RotationAxisAngle([3, 2, 1], rr.Angle(rad=1))], + ] + quaternion_arrays = [ + None, + Quaternion(xyzw=[1, 2, 3, 4]), + [Quaternion(xyzw=[4, 3, 2, 1]), Quaternion(xyzw=[1, 2, 3, 4])], + ] + + # TODO(andreas): It would be nice to support scalar values here + scale_arrays = [None, [1.0, 2.0, 3.0], [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]] + + # TODO(#6831): repopulate this list with all transform variants + all_arrays = itertools.zip_longest( + VEC_3D_INPUT + [None], + rotation_axis_angle_arrays, + quaternion_arrays, + scale_arrays, + MAT_3X3_INPUT + [None], + ) + + for ( + translation, + rotation_axis_angle, + quaternion, + scale, + mat3x3, + ) in all_arrays: + translations = cast(Optional[rr.datatypes.Vec3DArrayLike], translation) + rotation_axis_angles = cast(Optional[rr.datatypes.RotationAxisAngleArrayLike], rotation_axis_angle) + quaternions = cast(Optional[rr.datatypes.QuaternionArrayLike], quaternion) + scales = cast(Optional[rr.datatypes.Vec3DArrayLike | rr.datatypes.Float32Like], scale) + mat3x3 = cast(Optional[rr.datatypes.Mat3x3ArrayLike], mat3x3) + + print( + f"rr.LeafTransform3D(\n" + f" translations={translations!r}\n" # + f" rotation_axis_angles={rotation_axis_angles!r}\n" # + f" quaternions={quaternions!r}\n" # + f" scales={scales!r}\n" # + f" mat3x3={mat3x3!r}\n" # + f")" + ) + arch = rr.LeafTransforms3D( + translations=translations, + rotation_axis_angles=rotation_axis_angles, + quaternions=quaternions, + scales=scales, + mat3x3=mat3x3, + ) + print(f"{arch}\n") + + assert arch.translations == rr.components.LeafTranslation3DBatch._optional(translations) + assert arch.rotation_axis_angles == rr.components.LeafRotationAxisAngleBatch._optional(rotation_axis_angles) + assert arch.quaternions == rr.components.LeafRotationQuatBatch._optional(quaternions) + assert arch.scales == rr.components.LeafScale3DBatch._optional(scales) + assert arch.mat3x3 == rr.components.LeafTransformMat3x3Batch._optional(mat3x3) From a5bc96f61bc5a59947a3a96590adeb1d0e951307 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 29 Jul 2024 17:46:48 +0200 Subject: [PATCH 12/29] fix leaf transform query being broken --- .../src/contexts/transform_context.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index a261a0d5721e..0a3909b49127 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -599,13 +599,13 @@ fn query_and_resolve_leaf_transform_at_entity( let mut iter_translation = clamped_or_nothing( result - .component_batch::() + .component_batch::() .unwrap_or_default(), max_count, ); let mut iter_rotation_quat = clamped_or_nothing( result - .component_batch::() + .component_batch::() .unwrap_or_default(), max_count, ); @@ -616,14 +616,12 @@ fn query_and_resolve_leaf_transform_at_entity( max_count, ); let mut iter_scale = clamped_or_nothing( - result - .component_batch::() - .unwrap_or_default(), + result.component_batch::().unwrap_or_default(), max_count, ); let mut iter_mat3x3 = clamped_or_nothing( result - .component_batch::() + .component_batch::() .unwrap_or_default(), max_count, ); From 6bebb1359e084663d054263a62960af70434f3b5 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 29 Jul 2024 18:08:59 +0200 Subject: [PATCH 13/29] Add missing extensions and example for leaf transforms --- .../rerun/archetypes/leaf_transforms3d.fbs | 3 +- .../src/archetypes/leaf_transforms3d.rs | 58 +++++++++++++++++++ crates/viewer/re_viewer/src/reflection/mod.rs | 2 +- .../types/archetypes/leaf_transforms3d.md | 14 +++++ .../archetypes/leaf_transforms3d_combined.cpp | 37 ++++++++++++ .../archetypes/leaf_transforms3d_combined.py | 21 +++++++ .../archetypes/leaf_transforms3d_combined.rs | 45 ++++++++++++++ docs/snippets/snippets.toml | 5 ++ .../rerun/archetypes/leaf_transforms3d.hpp | 39 +++++++++++++ .../src/rerun/components/leaf_scale3d.hpp | 23 ++++++++ .../src/rerun/components/leaf_scale3d_ext.cpp | 27 +++++++++ .../rerun/components/leaf_translation3d.hpp | 21 +++++++ .../components/leaf_translation3d_ext.cpp | 25 ++++++++ .../rerun/archetypes/leaf_transforms3d.py | 35 +++++++++++ rerun_py/tests/unit/test_leaf_transforms3d.py | 2 - 15 files changed, 353 insertions(+), 4 deletions(-) create mode 100644 docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp create mode 100644 docs/snippets/all/archetypes/leaf_transforms3d_combined.py create mode 100644 docs/snippets/all/archetypes/leaf_transforms3d_combined.rs create mode 100644 rerun_cpp/src/rerun/components/leaf_scale3d_ext.cpp create mode 100644 rerun_cpp/src/rerun/components/leaf_translation3d_ext.cpp diff --git a/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs index a80342d0d390..d0fa014b141d 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs @@ -15,7 +15,8 @@ namespace rerun.archetypes; /// all components are applied in the inverse order they are listed here. /// E.g. if both a translation and a max3x3 transform are present, /// the 3x3 matrix is applied first, followed by the translation. -// TODO(andreas): Add example. +/// +/// \example archetypes/leaf_transforms3d_combined title="Regular & leaf transform in tandom" image="https://static.rerun.io/leaf_transform3d/41674f0082d6de489f8a1cd1583f60f6b5820ddf/1200w.png" table LeafTransforms3D ( "attr.rust.derive": "Default, PartialEq", "attr.rust.generate_field_info", diff --git a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs index ca7109d3d7f8..41ab8d2f2433 100644 --- a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs +++ b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs @@ -32,6 +32,64 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// all components are applied in the inverse order they are listed here. /// E.g. if both a translation and a max3x3 transform are present, /// the 3x3 matrix is applied first, followed by the translation. +/// +/// ## Example +/// +/// ### Regular & leaf transform in tandom +/// ```ignore +/// use rerun::{ +/// demo_util::grid, +/// external::{anyhow, glam}, +/// }; +/// +/// fn main() -> anyhow::Result<()> { +/// let rec = +/// rerun::RecordingStreamBuilder::new("rerun_example_leaf_transform3d_combined").spawn()?; +/// +/// rec.set_time_sequence("frame", 0); +/// +/// // Log a box and points further down in the hierarchy. +/// rec.log( +/// "world/box", +/// &rerun::Boxes3D::from_half_sizes([[1.0, 1.0, 1.0]]), +/// )?; +/// rec.log( +/// "world/box/points", +/// &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)), +/// )?; +/// +/// for i in 1..100 { +/// rec.set_time_sequence("frame", i); +/// +/// // Log a regular transform which affects both the box and the points. +/// rec.log( +/// "world/box", +/// &rerun::Transform3D::from_rotation(rerun::RotationAxisAngle { +/// axis: [0.0, 0.0, 1.0].into(), +/// angle: rerun::Angle::from_degrees(i as f32 * 2.0), +/// }), +/// )?; +/// +/// // Log an leaf transform which affects only the box. +/// let translation = [0.0, 0.0, (i as f32 * 0.1 - 5.0).abs() - 5.0]; +/// rec.log( +/// "world/box", +/// &rerun::LeafTransforms3D::new().with_translations([translation]), +/// )?; +/// } +/// +/// Ok(()) +/// } +/// ``` +///
+/// +/// +/// +/// +/// +/// +/// +///
#[derive(Clone, Debug, Default, PartialEq)] pub struct LeafTransforms3D { /// Translation vectors. diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index 50db839fdbcd..bf5a7905a63e 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -686,7 +686,7 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { ArchetypeName::new("rerun.archetypes.LeafTransforms3D"), ArchetypeReflection { display_name: "Leaf transforms 3D", - docstring_md: "One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy.\n\nFor transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d).\n\nIf both [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) and [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) are present,\nfirst the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d).\n\nCurrently, most visualizers support only a single leaf transform per entity.\nCheck archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied.\n\nFrom the point of view of the entity's coordinate system,\nall components are applied in the inverse order they are listed here.\nE.g. if both a translation and a max3x3 transform are present,\nthe 3x3 matrix is applied first, followed by the translation.", + docstring_md: "One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy.\n\nFor transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d).\n\nIf both [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) and [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) are present,\nfirst the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d).\n\nCurrently, most visualizers support only a single leaf transform per entity.\nCheck archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied.\n\nFrom the point of view of the entity's coordinate system,\nall components are applied in the inverse order they are listed here.\nE.g. if both a translation and a max3x3 transform are present,\nthe 3x3 matrix is applied first, followed by the translation.\n\n## Example\n\n### Regular & leaf transform in tandom\n```ignore\nuse rerun::{\n demo_util::grid,\n external::{anyhow, glam},\n};\n\nfn main() -> anyhow::Result<()> {\n let rec =\n rerun::RecordingStreamBuilder::new(\"rerun_example_leaf_transform3d_combined\").spawn()?;\n\n rec.set_time_sequence(\"frame\", 0);\n\n // Log a box and points further down in the hierarchy.\n rec.log(\n \"world/box\",\n &rerun::Boxes3D::from_half_sizes([[1.0, 1.0, 1.0]]),\n )?;\n rec.log(\n \"world/box/points\",\n &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)),\n )?;\n\n for i in 1..100 {\n rec.set_time_sequence(\"frame\", i);\n\n // Log a regular transform which affects both the box and the points.\n rec.log(\n \"world/box\",\n &rerun::Transform3D::from_rotation(rerun::RotationAxisAngle {\n axis: [0.0, 0.0, 1.0].into(),\n angle: rerun::Angle::from_degrees(i as f32 * 2.0),\n }),\n )?;\n\n // Log an leaf transform which affects only the box.\n let translation = [0.0, 0.0, (i as f32 * 0.1 - 5.0).abs() - 5.0];\n rec.log(\n \"world/box\",\n &rerun::LeafTransforms3D::new().with_translations([translation]),\n )?;\n }\n\n Ok(())\n}\n```\n
\n\n \n \n \n \n \n\n
", fields: vec![ ArchetypeFieldReflection { component_name : "rerun.components.LeafTranslation3D".into(), display_name : diff --git a/docs/content/reference/types/archetypes/leaf_transforms3d.md b/docs/content/reference/types/archetypes/leaf_transforms3d.md index 6c9306222a66..3ab96c7ad772 100644 --- a/docs/content/reference/types/archetypes/leaf_transforms3d.md +++ b/docs/content/reference/types/archetypes/leaf_transforms3d.md @@ -31,3 +31,17 @@ the 3x3 matrix is applied first, followed by the translation. * 🐍 [Python API docs for `LeafTransforms3D`](https://ref.rerun.io/docs/python/stable/common/archetypes#rerun.archetypes.LeafTransforms3D) * 🦀 [Rust API docs for `LeafTransforms3D`](https://docs.rs/rerun/latest/rerun/archetypes/struct.LeafTransforms3D.html) +## Example + +### Regular & leaf transform in tandom + +snippet: archetypes/leaf_transforms3d_combined + + + + + + + + + diff --git a/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp b/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp new file mode 100644 index 000000000000..da8afb1570a5 --- /dev/null +++ b/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp @@ -0,0 +1,37 @@ +// Log a simple 3D box with a regular & leaf transform. + +#include +#include + +int main() { + const auto rec = rerun::RecordingStream("rerun_example_leaf_transform3d_combined"); + rec.set_time_sequence("frame", 0); + + // Log a box and points further down in the hierarchy. + rec.log("world/box", rerun::Boxes3D::from_half_sizes({{1.0, 1.0, 1.0}})); + rec.log( + "world/box/points", + rerun::Points3D(rerun::demo::grid3d(-10.0f, 10.0f, 10)) + ); + + for (int i = 1; i < 20; ++i) { + rec.set_time_sequence("frame", i); + + // Log a regular transform which affects both the box and the points. + rec.log( + "world/box", + rerun::Transform3D::from_rotation(rerun::RotationAxisAngle{ + {0.0f, 0.0f, 1.0f}, + rerun::Angle::degrees(static_cast(i) * 2.0f) + }) + ); + + // Log an leaf transform which affects only the box. + rec.log( + "world/box", + rerun::LeafTransforms3D().with_translations( + {{0, 0, fabs(static_cast(i) * 0.1f - 5.0f) - 5.0f}} + ) + ); + } +} diff --git a/docs/snippets/all/archetypes/leaf_transforms3d_combined.py b/docs/snippets/all/archetypes/leaf_transforms3d_combined.py new file mode 100644 index 000000000000..f58e6131dc02 --- /dev/null +++ b/docs/snippets/all/archetypes/leaf_transforms3d_combined.py @@ -0,0 +1,21 @@ +"""Log a simple 3D box with a regular & leaf transform.""" + +import numpy as np +import rerun as rr + +rr.init("rerun_example_leaf_transform3d_combined", spawn=True) + +rr.set_time_sequence("frame", 0) + +# Log a box and points further down in the hierarchy. +rr.log("world/box", rr.Boxes3D(half_sizes=[[1.0, 1.0, 1.0]])) +rr.log("world/box/points", rr.Points3D(np.vstack([xyz.ravel() for xyz in np.mgrid[3 * [slice(-10, 10, 10j)]]]).T)) + +for i in range(1, 100): + rr.set_time_sequence("frame", i) + + # Log a regular transform which affects both the box and the points. + rr.log("world/box", rr.Transform3D(rotation_axis_angle=rr.RotationAxisAngle([0, 0, 1], angle=rr.Angle(deg=i * 2)))) + + # Log an leaf transform which affects only the box. + rr.log("world/box", rr.LeafTransforms3D(translations=[0, 0, abs(i * 0.1 - 5.0) - 5.0])) diff --git a/docs/snippets/all/archetypes/leaf_transforms3d_combined.rs b/docs/snippets/all/archetypes/leaf_transforms3d_combined.rs new file mode 100644 index 000000000000..7acfbfbfa92d --- /dev/null +++ b/docs/snippets/all/archetypes/leaf_transforms3d_combined.rs @@ -0,0 +1,45 @@ +//! Log a simple 3D box with a regular & leaf transform. + +use rerun::{ + demo_util::grid, + external::{anyhow, glam}, +}; + +fn main() -> anyhow::Result<()> { + let rec = + rerun::RecordingStreamBuilder::new("rerun_example_leaf_transform3d_combined").spawn()?; + + rec.set_time_sequence("frame", 0); + + // Log a box and points further down in the hierarchy. + rec.log( + "world/box", + &rerun::Boxes3D::from_half_sizes([[1.0, 1.0, 1.0]]), + )?; + rec.log( + "world/box/points", + &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)), + )?; + + for i in 1..100 { + rec.set_time_sequence("frame", i); + + // Log a regular transform which affects both the box and the points. + rec.log( + "world/box", + &rerun::Transform3D::from_rotation(rerun::RotationAxisAngle { + axis: [0.0, 0.0, 1.0].into(), + angle: rerun::Angle::from_degrees(i as f32 * 2.0), + }), + )?; + + // Log an leaf transform which affects only the box. + let translation = [0.0, 0.0, (i as f32 * 0.1 - 5.0).abs() - 5.0]; + rec.log( + "world/box", + &rerun::LeafTransforms3D::new().with_translations([translation]), + )?; + } + + Ok(()) +} diff --git a/docs/snippets/snippets.toml b/docs/snippets/snippets.toml index c77ef5e06b0f..1678295fd3b9 100644 --- a/docs/snippets/snippets.toml +++ b/docs/snippets/snippets.toml @@ -163,6 +163,11 @@ quick_start = [ # These examples don't have exactly the same implementation. "archetypes/transform3d_hierarchy" = [ # Uses a lot of trigonometry which is surprisingly easy to get the same on Rust & C++, but not on Python/Numpy "py", ] +"archetypes/leaf_transforms3d_combined" = [ # TODO(#3235): Slight floating point differences in point grid. + "cpp", + "py", + "rust", +] # `$config_dir` will be replaced with the absolute path of `docs/snippets`. [extra_args] diff --git a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp index ad8fc4cd136d..c2ac8fe49b89 100644 --- a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp @@ -34,6 +34,45 @@ namespace rerun::archetypes { /// all components are applied in the inverse order they are listed here. /// E.g. if both a translation and a max3x3 transform are present, /// the 3x3 matrix is applied first, followed by the translation. + /// + /// ## Example + /// + /// ### Regular & leaf transform in tandom + /// ![image](https://static.rerun.io/leaf_transform3d/41674f0082d6de489f8a1cd1583f60f6b5820ddf/full.png) + /// + /// ```cpp + /// #include + /// #include + /// + /// int main() { + /// const auto rec = rerun::RecordingStream("rerun_example_asset3d_out_of_tree"); + /// rec.set_time_sequence("frame", 0); + /// + /// // Log a box and points further down in the hierarchy. + /// rec.log("world/box", rerun::Boxes3D::from_half_sizes({{1.0, 1.0, 1.0}})); + /// rec.log( + /// "world/box/points", + /// rerun::Points3D(rerun::demo::grid3d(-10.0f, 10.0f, 10)) + /// ); + /// + /// for (int i = 1; i <20; ++i) { + /// rec.set_time_sequence("frame", i); + /// + /// // Log a regular transform which affects both the box and the points. + /// rec.log( + /// "world/box", + /// rerun::Transform3D::from_rotation(rerun::RotationAxisAngle{ + /// {0.0f, 0.0f, 1.0f}, + /// {rerun::Angle::degrees(static_cast(i) * 2.0f)} + /// }) + /// ); + /// + /// // Log an leaf transform which affects only the box. + /// float translation[] = {0, 0, fabs(static_cast(i) * 0.1f - 5.0f) - 5.0f}; + /// rec.log("world/box", rerun::LeafTransforms3D().with_translations({translation})); + /// } + /// } + /// ``` struct LeafTransforms3D { /// Translation vectors. std::optional> translations; diff --git a/rerun_cpp/src/rerun/components/leaf_scale3d.hpp b/rerun_cpp/src/rerun/components/leaf_scale3d.hpp index 5974916fff6f..805f9bba0084 100644 --- a/rerun_cpp/src/rerun/components/leaf_scale3d.hpp +++ b/rerun_cpp/src/rerun/components/leaf_scale3d.hpp @@ -19,6 +19,29 @@ namespace rerun::components { struct LeafScale3D { rerun::datatypes::Vec3D scale; + public: + // Extensions to generated type defined in 'leaf_scale3d_ext.cpp' + + /// Construct `LeafScale3D` from x/y/z values. + LeafScale3D(float x, float y, float z) : scale{x, y, z} {} + + /// Construct `LeafScale3D` from x/y/z float pointer. + explicit LeafScale3D(const float* xyz) : scale{xyz[0], xyz[1], xyz[2]} {} + + /// Construct a `LeafScale3D` from a uniform scale factor. + explicit LeafScale3D(float uniform_scale) + : LeafScale3D(datatypes::Vec3D{uniform_scale, uniform_scale, uniform_scale}) {} + + /// Explicitly construct a `LeafScale3D` from a uniform scale factor. + static LeafScale3D uniform(float uniform_scale) { + return LeafScale3D(uniform_scale); + } + + /// Explicitly construct a `LeafScale3D` from a 3D scale factor. + static LeafScale3D three_d(datatypes::Vec3D scale) { + return LeafScale3D(scale); + } + public: LeafScale3D() = default; diff --git a/rerun_cpp/src/rerun/components/leaf_scale3d_ext.cpp b/rerun_cpp/src/rerun/components/leaf_scale3d_ext.cpp new file mode 100644 index 000000000000..67f5706b06c8 --- /dev/null +++ b/rerun_cpp/src/rerun/components/leaf_scale3d_ext.cpp @@ -0,0 +1,27 @@ +namespace rerun::components { +#if 0 + // + + /// Construct `LeafScale3D` from x/y/z values. + LeafScale3D(float x, float y, float z) : scale{x, y, z} {} + + /// Construct `LeafScale3D` from x/y/z float pointer. + explicit LeafScale3D(const float* xyz) : scale{xyz[0], xyz[1], xyz[2]} {} + + /// Construct a `LeafScale3D` from a uniform scale factor. + explicit LeafScale3D(float uniform_scale) : LeafScale3D(datatypes::Vec3D{uniform_scale, uniform_scale, uniform_scale}) {} + + /// Explicitly construct a `LeafScale3D` from a uniform scale factor. + static LeafScale3D uniform(float uniform_scale) { + return LeafScale3D(uniform_scale); + } + + /// Explicitly construct a `LeafScale3D` from a 3D scale factor. + static LeafScale3D three_d(datatypes::Vec3D scale) { + return LeafScale3D(scale); + } + + // + +#endif +} // namespace rerun::components diff --git a/rerun_cpp/src/rerun/components/leaf_translation3d.hpp b/rerun_cpp/src/rerun/components/leaf_translation3d.hpp index d7732757e2eb..47c39bef9d64 100644 --- a/rerun_cpp/src/rerun/components/leaf_translation3d.hpp +++ b/rerun_cpp/src/rerun/components/leaf_translation3d.hpp @@ -15,6 +15,27 @@ namespace rerun::components { struct LeafTranslation3D { rerun::datatypes::Vec3D vector; + public: + // Extensions to generated type defined in 'leaf_translation3d_ext.cpp' + + /// Construct `LeafTranslation3D` from x/y/z values. + LeafTranslation3D(float x, float y, float z) : vector{x, y, z} {} + + /// Construct `LeafTranslation3D` from x/y/z float pointer. + explicit LeafTranslation3D(const float* xyz) : vector{xyz[0], xyz[1], xyz[2]} {} + + float x() const { + return vector.x(); + } + + float y() const { + return vector.y(); + } + + float z() const { + return vector.z(); + } + public: LeafTranslation3D() = default; diff --git a/rerun_cpp/src/rerun/components/leaf_translation3d_ext.cpp b/rerun_cpp/src/rerun/components/leaf_translation3d_ext.cpp new file mode 100644 index 000000000000..2ec728c70ce3 --- /dev/null +++ b/rerun_cpp/src/rerun/components/leaf_translation3d_ext.cpp @@ -0,0 +1,25 @@ +namespace rerun::components { +#if 0 + // + + /// Construct `LeafTranslation3D` from x/y/z values. + LeafTranslation3D(float x, float y, float z) : vector{x, y, z} {} + + /// Construct `LeafTranslation3D` from x/y/z float pointer. + explicit LeafTranslation3D(const float* xyz) : vector{xyz[0], xyz[1], xyz[2]} {} + + float x() const { + return vector.x(); + } + + float y() const { + return vector.y(); + } + + float z() const { + return vector.z(); + } + + // +#endif +} // namespace rerun::components diff --git a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py index 8009196f7667..3bef923c9460 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py @@ -35,6 +35,41 @@ class LeafTransforms3D(Archetype): all components are applied in the inverse order they are listed here. E.g. if both a translation and a max3x3 transform are present, the 3x3 matrix is applied first, followed by the translation. + + Example + ------- + ### Regular & leaf transform in tandom: + ```python + import numpy as np + import rerun as rr + + rr.init("rerun_example_leaf_transform3d_combined", spawn=True) + + rr.set_time_sequence("frame", 0) + + # Log a box and points further down in the hierarchy. + rr.log("world/box", rr.Boxes3D(half_sizes=[[1.0, 1.0, 1.0]])) + rr.log("world/box/points", rr.Points3D(np.vstack([xyz.ravel() for xyz in np.mgrid[3 * [slice(-10, 10, 10j)]]]).T)) + + for i in range(1, 100): + rr.set_time_sequence("frame", i) + + # Log a regular transform which affects both the box and the points. + rr.log("world/box", rr.Transform3D(rotation_axis_angle=rr.RotationAxisAngle([0, 0, 1], angle=rr.Angle(deg=i * 2)))) + + # Log an leaf transform which affects only the box. + rr.log("world/box", rr.LeafTransforms3D(translations=[0, 0, abs(i * 0.1 - 5.0) - 5.0])) + ``` +
+ + + + + + + +
+ """ def __init__( diff --git a/rerun_py/tests/unit/test_leaf_transforms3d.py b/rerun_py/tests/unit/test_leaf_transforms3d.py index 4307ded32056..f0df1d4d57a7 100644 --- a/rerun_py/tests/unit/test_leaf_transforms3d.py +++ b/rerun_py/tests/unit/test_leaf_transforms3d.py @@ -6,12 +6,10 @@ import rerun as rr from rerun.datatypes import ( - Angle, Quaternion, RotationAxisAngle, ) -from .common_arrays import none_empty_or_value from .test_matnxn import MAT_3X3_INPUT from .test_vecnd import VEC_3D_INPUT From 392ba3ab043ca810dba99d5f8473f4d4ef19402f Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 10:21:51 +0200 Subject: [PATCH 14/29] fix transform upward propagation --- .../src/contexts/transform_context.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index 99ed4160ea54..9b28779ed1a3 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -375,7 +375,7 @@ fn transform_info_for_upward_propagation( } // Collect & compute leaf transforms. - let reference_from_instances = if let Ok(mut entity_from_instances) = + let (mut reference_from_instances, has_leaf_transforms) = if let Ok(mut entity_from_instances) = SmallVec1::<[glam::Affine3A; 1]>::try_from_vec( transforms_at_entity.entity_from_instance_leaf_transforms, ) { @@ -383,9 +383,9 @@ fn transform_info_for_upward_propagation( *entity_from_instance = reference_from_entity * entity_from_instance.inverse(); // Now this is actually `reference_from_instance`. } - entity_from_instances + (entity_from_instances, true) } else { - SmallVec1::new(reference_from_entity) + (SmallVec1::new(reference_from_entity), false) }; // Apply tree transform if any. @@ -393,6 +393,13 @@ fn transform_info_for_upward_propagation( transforms_at_entity.parent_from_entity_tree_transform { reference_from_entity *= parent_from_entity_tree_transform.inverse(); + if has_leaf_transforms { + for reference_from_instance in &mut reference_from_instances { + *reference_from_instance = reference_from_entity * (*reference_from_instance); + } + } else { + *reference_from_instances.first_mut() = reference_from_entity; + } } TransformInfo { From dfefab8d071cb724d8f22b2245e14cca9bcadd4b Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 11:18:58 +0200 Subject: [PATCH 15/29] fix num_instances on `UnitChunkShared` --- crates/store/re_chunk/src/helpers.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/store/re_chunk/src/helpers.rs b/crates/store/re_chunk/src/helpers.rs index f9969587ae9c..d26b7fca88c8 100644 --- a/crates/store/re_chunk/src/helpers.rs +++ b/crates/store/re_chunk/src/helpers.rs @@ -235,11 +235,13 @@ impl UnitChunkShared { /// The maximum value amongst all components is what's returned. #[inline] pub fn num_instances(&self) -> u64 { + debug_assert!(self.num_rows() == 1); self.components .values() .map(|list_array| { - list_array.validity().map_or_else( - || list_array.len(), + let array = list_array.value(0); + array.validity().map_or_else( + || array.len(), |validity| validity.len() - validity.unset_bits(), ) }) From 9167a316eff4ebe5faa39a632f8284dae903f375 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 11:37:09 +0200 Subject: [PATCH 16/29] fix transform_component_tracker query --- .../re_space_view_spatial/src/transform_component_tracker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/viewer/re_space_view_spatial/src/transform_component_tracker.rs b/crates/viewer/re_space_view_spatial/src/transform_component_tracker.rs index e11e1d0380ea..a4fbb12c1848 100644 --- a/crates/viewer/re_space_view_spatial/src/transform_component_tracker.rs +++ b/crates/viewer/re_space_view_spatial/src/transform_component_tracker.rs @@ -47,7 +47,7 @@ impl TransformComponentTracker { #[inline] pub fn is_potentially_transformed_leaf_transform3d(&self, entity_path: &EntityPath) -> bool { - self.transform3d_entities.contains(&entity_path.hash()) + self.leaf_transforms3d_entities.contains(&entity_path.hash()) } } From 7f44b5af77c061ebe0c8e24969e92677e52e48ec Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 11:39:18 +0200 Subject: [PATCH 17/29] Add new snippet for leaf transforms on mesh --- .../definitions/rerun/archetypes/mesh3d.fbs | 1 + .../store/re_types/src/archetypes/mesh3d.rs | 57 ++++++++++++++++++- .../reference/types/archetypes/mesh3d.md | 12 ++++ .../archetypes/mesh3d_leaf_transforms3d.cpp | 38 +++++++++++++ .../archetypes/mesh3d_leaf_transforms3d.py | 30 ++++++++++ .../archetypes/mesh3d_leaf_transforms3d.rs | 44 ++++++++++++++ .../rerun/archetypes/leaf_transforms3d.hpp | 12 ++-- rerun_cpp/src/rerun/archetypes/mesh3d.hpp | 44 +++++++++++++- rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py | 45 ++++++++++++++- 9 files changed, 275 insertions(+), 8 deletions(-) create mode 100644 docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.cpp create mode 100644 docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.py create mode 100644 docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.rs diff --git a/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs index b910d00dee02..8251897994a6 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs @@ -10,6 +10,7 @@ namespace rerun.archetypes; /// /// \example archetypes/mesh3d_indexed title="Simple indexed 3D mesh" image="https://static.rerun.io/mesh3d_simple/e1e5fd97265daf0d0bc7b782d862f19086fd6975/1200w.png" /// \example archetypes/mesh3d_partial_updates !api title="3D mesh with partial updates" image="https://static.rerun.io/mesh3d_partial_updates/a11e4accb0257dcd9531867b7e1d6fd5e3bee5c3/1200w.png" +/// \example archetypes/mesh3d_leaf_transforms3d title="3D mesh with leaf transforms" image="https://static.rerun.io/mesh3d_leaf_transforms3d/c2d0ee033129da53168f5705625a9b033f3a3d61/1200w.png" table Mesh3D ( "attr.rust.derive": "PartialEq", "attr.docs.category": "Spatial 3D", diff --git a/crates/store/re_types/src/archetypes/mesh3d.rs b/crates/store/re_types/src/archetypes/mesh3d.rs index 1c51a846a67e..178f5a18d679 100644 --- a/crates/store/re_types/src/archetypes/mesh3d.rs +++ b/crates/store/re_types/src/archetypes/mesh3d.rs @@ -24,7 +24,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// If there are multiple [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D], the mesh will be drawn for each transform. /// -/// ## Example +/// ## Examples /// /// ### Simple indexed 3D mesh /// ```ignore @@ -51,6 +51,61 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// /// +/// +/// ### 3D mesh with leaf transforms +/// ```ignore +/// fn main() -> Result<(), Box> { +/// let rec = +/// rerun::RecordingStreamBuilder::new("rerun_example_mesh3d_leaf_transforms3d").spawn()?; +/// +/// rec.set_time_sequence("frame", 0); +/// rec.log( +/// "shape", +/// &rerun::Mesh3D::new([ +/// [1.0, 1.0, 1.0], +/// [-1.0, -1.0, 1.0], +/// [-1.0, 1.0, -1.0], +/// [1.0, -1.0, -1.0], +/// ]) +/// .with_triangle_indices([[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]) +/// .with_vertex_colors([0xFF0000FF, 0x00FF00FF, 0x00000FFFF, 0xFFFF00FF]), +/// )?; +/// // This box will not be affected by its parent's leaf transforms! +/// rec.log( +/// "shape/box", +/// &rerun::Boxes3D::from_half_sizes([[5.0, 5.0, 5.0]]), +/// )?; +/// +/// for i in 0..100 { +/// rec.set_time_sequence("frame", i); +/// rec.log( +/// "shape", +/// &rerun::LeafTransforms3D::default() +/// .with_translations([ +/// [2.0, 0.0, 0.0], +/// [0.0, 2.0, 0.0], +/// [0.0, -2.0, 0.0], +/// [-2.0, 0.0, 0.0], +/// ]) +/// .with_rotation_axis_angles([rerun::RotationAxisAngle::new( +/// [0.0, 0.0, 1.0], +/// rerun::Angle::from_degrees(i as f32 * 2.0), +/// )]), +/// )?; +/// } +/// +/// Ok(()) +/// } +/// ``` +///
+/// +/// +/// +/// +/// +/// +/// +///
#[derive(Clone, Debug, PartialEq)] pub struct Mesh3D { /// The positions of each vertex. diff --git a/docs/content/reference/types/archetypes/mesh3d.md b/docs/content/reference/types/archetypes/mesh3d.md index 7183b207f301..1fef4f950c76 100644 --- a/docs/content/reference/types/archetypes/mesh3d.md +++ b/docs/content/reference/types/archetypes/mesh3d.md @@ -52,3 +52,15 @@ snippet: archetypes/mesh3d_partial_updates +### 3D mesh with leaf transforms + +snippet: archetypes/mesh3d_leaf_transforms3d + + + + + + + + + diff --git a/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.cpp b/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.cpp new file mode 100644 index 000000000000..6523b09714a7 --- /dev/null +++ b/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.cpp @@ -0,0 +1,38 @@ +// Log a simple 3D mesh with several leaf-transforms which instantiate the mesh several times and will not affect its children. + +#include + +int main() { + const auto rec = rerun::RecordingStream("rerun_example_mesh3d_leaf_transforms3d"); + rec.spawn().exit_on_failure(); + + rec.set_time_sequence("frame", 0); + rec.log( + "shape", + rerun::Mesh3D( + {{1.0f, 1.0f, 1.0f}, {-1.0f, -1.0f, 1.0f}, {-1.0f, 1.0f, -1.0f}, {1.0f, -1.0f, -1.0f}} + ) + .with_triangle_indices({{0, 1, 2}, {0, 1, 3}, {0, 2, 3}, {1, 2, 3}}) + .with_vertex_colors({0xFF0000FF, 0x00FF00FF, 0x00000FFFF, 0xFFFF00FF}) + ); + // This box will not be affected by its parent's leaf transforms! + rec.log("shape/box", rerun::Boxes3D::from_half_sizes({{5.0f, 5.0f, 5.0f}})); + + for (int i = 0; i < 100; ++i) { + rec.set_time_sequence("frame", i); + rec.log( + "shape", + rerun::LeafTransforms3D() + .with_translations( + {{2.0f, 0.0f, 0.0f}, + {0.0f, 2.0f, 0.0f}, + {0.0f, -2.0f, 0.0f}, + {-2.0f, 0.0f, 0.0f}} + ) + .with_rotation_axis_angles({rerun::RotationAxisAngle( + {0.0f, 0.0f, 1.0f}, + rerun::Angle::degrees(static_cast(i) * 2.0f) + )}), + ); + } +} diff --git a/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.py b/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.py new file mode 100644 index 000000000000..4e17ad107a6a --- /dev/null +++ b/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.py @@ -0,0 +1,30 @@ +"""Log a simple 3D mesh with several leaf-transforms which instantiate the mesh several times and will not affect its children.""" + +import rerun as rr + +rr.init("rerun_example_mesh3d_leaf_transforms3d", spawn=True) +rr.set_time_sequence("frame", 0) + +rr.log( + "shape", + rr.Mesh3D( + vertex_positions=[[1, 1, 1], [-1, -1, 1], [-1, 1, -1], [1, -1, -1]], + triangle_indices=[[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]], + vertex_colors=[[255, 0, 0], [0, 255, 0], [0, 0, 255], [255, 255, 0]], + ), +) +# This box will not be affected by its parent's leaf transforms! +rr.log( + "shape/box", + rr.Boxes3D(half_sizes=[[5.0, 5.0, 5.0]]), +) + +for i in range(0, 100): + rr.set_time_sequence("frame", i) + rr.log( + "shape", + rr.LeafTransforms3D( + translations=[[2, 0, 0], [0, 2, 0], [0, -2, 0], [-2, 0, 0]], + rotation_axis_angles=rr.RotationAxisAngle([0, 0, 1], rr.Angle(deg=i * 2)), + ), + ) diff --git a/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.rs b/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.rs new file mode 100644 index 000000000000..90fc38e66db8 --- /dev/null +++ b/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.rs @@ -0,0 +1,44 @@ +// Log a simple 3D mesh with several leaf-transforms which instantiate the mesh several times and will not affect its children. + +fn main() -> Result<(), Box> { + let rec = + rerun::RecordingStreamBuilder::new("rerun_example_mesh3d_leaf_transforms3d").spawn()?; + + rec.set_time_sequence("frame", 0); + rec.log( + "shape", + &rerun::Mesh3D::new([ + [1.0, 1.0, 1.0], + [-1.0, -1.0, 1.0], + [-1.0, 1.0, -1.0], + [1.0, -1.0, -1.0], + ]) + .with_triangle_indices([[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]) + .with_vertex_colors([0xFF0000FF, 0x00FF00FF, 0x00000FFFF, 0xFFFF00FF]), + )?; + // This box will not be affected by its parent's leaf transforms! + rec.log( + "shape/box", + &rerun::Boxes3D::from_half_sizes([[5.0, 5.0, 5.0]]), + )?; + + for i in 0..100 { + rec.set_time_sequence("frame", i); + rec.log( + "shape", + &rerun::LeafTransforms3D::default() + .with_translations([ + [2.0, 0.0, 0.0], + [0.0, 2.0, 0.0], + [0.0, -2.0, 0.0], + [-2.0, 0.0, 0.0], + ]) + .with_rotation_axis_angles([rerun::RotationAxisAngle::new( + [0.0, 0.0, 1.0], + rerun::Angle::from_degrees(i as f32 * 2.0), + )]), + )?; + } + + Ok(()) +} diff --git a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp index c2ac8fe49b89..b604b1a5d496 100644 --- a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp @@ -45,7 +45,7 @@ namespace rerun::archetypes { /// #include /// /// int main() { - /// const auto rec = rerun::RecordingStream("rerun_example_asset3d_out_of_tree"); + /// const auto rec = rerun::RecordingStream("rerun_example_leaf_transform3d_combined"); /// rec.set_time_sequence("frame", 0); /// /// // Log a box and points further down in the hierarchy. @@ -63,13 +63,17 @@ namespace rerun::archetypes { /// "world/box", /// rerun::Transform3D::from_rotation(rerun::RotationAxisAngle{ /// {0.0f, 0.0f, 1.0f}, - /// {rerun::Angle::degrees(static_cast(i) * 2.0f)} + /// rerun::Angle::degrees(static_cast(i) * 2.0f) /// }) /// ); /// /// // Log an leaf transform which affects only the box. - /// float translation[] = {0, 0, fabs(static_cast(i) * 0.1f - 5.0f) - 5.0f}; - /// rec.log("world/box", rerun::LeafTransforms3D().with_translations({translation})); + /// rec.log( + /// "world/box", + /// rerun::LeafTransforms3D().with_translations( + /// {{0, 0, fabs(static_cast(i) * 0.1f - 5.0f) - 5.0f}} + /// ) + /// ); /// } /// } /// ``` diff --git a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp index 3538b70f01fe..e3a8e491b38b 100644 --- a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp @@ -29,7 +29,7 @@ namespace rerun::archetypes { /// /// If there are multiple `archetypes::LeafTransforms3D`, the mesh will be drawn for each transform. /// - /// ## Example + /// ## Examples /// /// ### Simple indexed 3D mesh /// ![image](https://static.rerun.io/mesh3d_simple/e1e5fd97265daf0d0bc7b782d862f19086fd6975/full.png) @@ -63,6 +63,48 @@ namespace rerun::archetypes { /// ); /// } /// ``` + /// + /// ### 3D mesh with leaf transforms + /// ![image](https://static.rerun.io/mesh3d_leaf_transforms3d/c2d0ee033129da53168f5705625a9b033f3a3d61/full.png) + /// + /// ```cpp + /// #include + /// + /// int main() { + /// const auto rec = rerun::RecordingStream("rerun_example_mesh3d_leaf_transforms3d"); + /// rec.spawn().exit_on_failure(); + /// + /// rec.set_time_sequence("frame", 0); + /// rec.log( + /// "shape", + /// rerun::Mesh3D( + /// {{1.0f, 1.0f, 1.0f}, {-1.0f, -1.0f, 1.0f}, {-1.0f, 1.0f, -1.0f}, {1.0f, -1.0f, -1.0f}} + /// ) + /// .with_triangle_indices({{0, 1, 2}, {0, 1, 3}, {0, 2, 3}, {1, 2, 3}}) + /// .with_vertex_colors({0xFF0000FF, 0x00FF00FF, 0x00000FFFF, 0xFFFF00FF}) + /// ); + /// // This box will not be affected by its parent's leaf transforms! + /// rec.log("shape/box", rerun::Boxes3D::from_half_sizes({{5.0f, 5.0f, 5.0f}})); + /// + /// for (int i = 0; i <100; ++i) { + /// rec.set_time_sequence("frame", i); + /// rec.log( + /// "shape", + /// rerun::LeafTransforms3D() + /// .with_translations( + /// {{2.0f, 0.0f, 0.0f}, + /// {0.0f, 2.0f, 0.0f}, + /// {0.0f, -2.0f, 0.0f}, + /// {-2.0f, 0.0f, 0.0f}} + /// ) + /// .with_rotation_axis_angles({rerun::RotationAxisAngle( + /// {0.0f, 0.0f, 1.0f}, + /// rerun::Angle::degrees(static_cast(i) * 2.0f) + /// )}), + /// ); + /// } + /// } + /// ``` struct Mesh3D { /// The positions of each vertex. /// diff --git a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py index d3fa38bb1ac2..01a864b48930 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py @@ -25,8 +25,8 @@ class Mesh3D(Mesh3DExt, Archetype): If there are multiple [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D], the mesh will be drawn for each transform. - Example - ------- + Examples + -------- ### Simple indexed 3D mesh: ```python import rerun as rr @@ -53,6 +53,47 @@ class Mesh3D(Mesh3DExt, Archetype): + ### 3D mesh with leaf transforms: + ```python + import rerun as rr + + rr.init("rerun_example_mesh3d_leaf_transforms3d", spawn=True) + rr.set_time_sequence("frame", 0) + + rr.log( + "shape", + rr.Mesh3D( + vertex_positions=[[1, 1, 1], [-1, -1, 1], [-1, 1, -1], [1, -1, -1]], + triangle_indices=[[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]], + vertex_colors=[[255, 0, 0], [0, 255, 0], [0, 0, 255], [255, 255, 0]], + ), + ) + # This box will not be affected by its parent's leaf transforms! + rr.log( + "shape/box", + rr.Boxes3D(half_sizes=[[5.0, 5.0, 5.0]]), + ) + + for i in range(0, 100): + rr.set_time_sequence("frame", i) + rr.log( + "shape", + rr.LeafTransforms3D( + translations=[[2, 0, 0], [0, 2, 0], [0, -2, 0], [-2, 0, 0]], + rotation_axis_angles=rr.RotationAxisAngle([0, 0, 1], rr.Angle(deg=i * 2)), + ), + ) + ``` +
+ + + + + + + +
+ """ # __init__ can be found in mesh3d_ext.py From dd9d505398f8e34170af8ec3130c14e1cd401297 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 11:39:48 +0200 Subject: [PATCH 18/29] fix the the! --- .../re_space_view_spatial/src/contexts/transform_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index 9b28779ed1a3..714fe92dd038 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -66,7 +66,7 @@ impl Default for TransformInfo { } impl TransformInfo { - /// Warns that multiple transforms within the the entity are not supported. + /// Warns that multiple transforms within the entity are not supported. #[inline] pub fn warn_on_per_instance_transform( &self, From ea1c85b230a5031e1bc13c8b0ca759a482ff9c1c Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 11:40:59 +0200 Subject: [PATCH 19/29] typo fix --- .../re_space_view_spatial/src/contexts/transform_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index 714fe92dd038..d20bfa5649c0 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -347,7 +347,7 @@ impl TransformContext { &self.space_origin } - /// Retrives transform information for a given entity. + /// Retrieves transform information for a given entity. /// /// Returns `None` if it's not reachable from the view's origin. pub fn transform_info_for_entity(&self, ent_path: &EntityPath) -> Option<&TransformInfo> { From 9864884a24dcaf427f6f981e6da64dd665c4c644 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 12:01:18 +0200 Subject: [PATCH 20/29] upgrade migration guide and python docs --- .../reference/migration/migration-0-18.md | 69 +++++++++++++------ rerun_py/docs/gen_common_index.py | 3 +- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/docs/content/reference/migration/migration-0-18.md b/docs/content/reference/migration/migration-0-18.md index 830732fbd581..eee404ea5e77 100644 --- a/docs/content/reference/migration/migration-0-18.md +++ b/docs/content/reference/migration/migration-0-18.md @@ -66,13 +66,12 @@ Other changes in data representation: * Angles (as used in `RotationAxisAngle`) are now always stored in radians, conversion functions for degrees are provided. Scaling no longer distinguishes uniform and 3D scaling in its data representation. Uniform scaling is now always expressed as 3 floats with the same value. -TODO(andreas): Write about LeafTransforms3D +`OutOfTreeTransform3D` got removed. Instead, there is now a new [`LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transform3d#speculative-link). archetype which fulfills the same role, but works more similar to the `Transform3D` archetype and is supported by all 3D spatial primitives. #### Python The `Transform3D` archetype no longer has a `transform` argument. Use one of the other arguments instead. -TODO(andreas): Not true as of writing. but should be true at the time or release! Before: ```python @@ -83,13 +82,21 @@ After: rr.log("myentity", rr.Transform3D(translation=Vec3D([1, 2, 3]), relation=rr.TransformRelation.ChildFromParent)) ``` - -TODO(andreas): code example - - -TODO(andreas): Talk about LeafTransforms3D -TODO(andreas): … and Asset3D specifically - +Asset3D previously had a `transform` argument, now you have to log either a `LeafTransform3D` or a `Transform3D` on the same entity: +Before: +```python +rr.log("world/mesh", rr.Asset3D( + path=path, + transform=rr.OutOfTreeTransform3DBatch( + rr.TranslationRotationScale3D(translation=center, scale=scale) + ) + )) +``` +After: +```python +rr.log("world/mesh", rr.Asset3D(path=path)) +rr.log("world/mesh", rr.LeafTransform3D(translation=center, scale=scale)) +``` #### C++ @@ -119,18 +126,28 @@ an empty archetype instead that you can populate (e.g. `rerun::Transform3D().wit Scale is no longer an enum datatype but a component with a 3D vec: Before: -```rust -let scale_uniform = rerun::Scale3D::Uniform(2.0); -let scale_y = rerun::Scale3D::ThreeD([1.0, 2.0, 1.0]); +```cpp +auto scale_uniform = rerun::Scale3D::Uniform(2.0); +auto scale_y = rerun::Scale3D::ThreeD([1.0, 2.0, 1.0]); ``` After: -```rust -let scale_uniform = rerun::Scale3D::uniform(2.0); -let scale_y = rerun::Scale3D::from([1.0, 2.0, 1.0]); +```cpp +auto scale_uniform = rerun::Scale3D::uniform(2.0); +auto scale_y = rerun::Scale3D::from([1.0, 2.0, 1.0]); ``` -TODO(andreas): Talk about LeafTransforms3D -TODO(andreas): … and Asset3D specifically +Asset3D previously had a `transform` field, now you have to log either a `LeafTransform3D` or a `Transform3D` on the same entity: +Before: +```cpp +rec.log("world/asset", rerun::Asset3D::from_file(path).value_or_throw() + .with_transform(rerun::OutOfTreeTransform3D(translation)) +); +``` +After: +```cpp +rec.log("world/asset", rerun::Asset3D::from_file(path).value_or_throw()); +rec.log("world/mesh", &rerun::archetypes::LeafTransform3D().with_translations(translation)); +``` #### Rust `rerun::archetypes::Transform3D` no longer has a `new`, use other factory methods instead, e.g. `from_translation_rotation_scale` or `from_mat3x3` @@ -164,13 +181,12 @@ impl From for rerun::Transform3D { fn from(transform: GltfTransform) -> Self { rerun::Transform3D::from_translation_rotation_scale( transform.t, - rerun::datatypes::Quaternion::from_xyzw(transform.r), + rerun::Quaternion::from_xyzw(transform.r), transform.s, ) } } ``` -TODO(andreas): Quaternion in above snippet is likely to change as well. Since all aspects of the transform archetypes are now granular, they can be chained with `with_` functions: ```rust @@ -181,5 +197,16 @@ Note that the order of the method calls does _not_ affect the order in which tra `rerun::Transform3D::IDENTITY` has been removed, sue `rerun::Transform3D::default()` to start out with an empty archetype instead that you can populate (e.g. `rerun::Transform3D::default().with_mat3x3(rerun::datatypes::Mat3x3::IDENTITY)`). -TODO(andreas): Talk about LeafTransforms3D -TODO(andreas): … and Asset3D specifically + +Asset3D previously had a `transform` field, now you have to log either a `LeafTransform3D` or a `Transform3D` on the same entity: +Before: +```rust +rec.log("world/mesh", &rerun::Asset3D::from_file(path)? + .with_transform(rerun::OutOfTreeTransform3D::from(rerun::TranslationRotationScale3D(translation))) +)?; +``` +After: +```rust +rec.log("world/mesh", &rerun::Asset3D::from_file(path)?)?; +rec.log("world/mesh", &rerun::LeafTransform3D::default().with_translations([translation]))?; +``` diff --git a/rerun_py/docs/gen_common_index.py b/rerun_py/docs/gen_common_index.py index 6ba6564a7e76..fa54666c9ecb 100755 --- a/rerun_py/docs/gen_common_index.py +++ b/rerun_py/docs/gen_common_index.py @@ -209,9 +209,8 @@ class Section: "archetypes.DisconnectedSpace", "archetypes.Pinhole", "archetypes.Transform3D", + "archetypes.LeafTransform3D", "archetypes.ViewCoordinates", - "components.TransformMat3x3", - "components.Translation3D", "datatypes.Quaternion", "datatypes.RotationAxisAngle", "datatypes.Scale3D", From 6e06051d4664e73d8ba539e0519522e5a47da01c Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 12:17:06 +0200 Subject: [PATCH 21/29] snippet fixes --- crates/store/re_types/src/archetypes/leaf_transforms3d.rs | 2 +- crates/viewer/re_viewer/src/reflection/mod.rs | 2 +- docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp | 2 +- docs/snippets/all/archetypes/leaf_transforms3d_combined.py | 2 +- docs/snippets/all/archetypes/leaf_transforms3d_combined.rs | 2 +- docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.cpp | 2 +- rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp | 2 +- rerun_cpp/src/rerun/archetypes/mesh3d.hpp | 2 +- rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs index 41ab8d2f2433..cffab5b3bc08 100644 --- a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs +++ b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs @@ -58,7 +58,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)), /// )?; /// -/// for i in 1..100 { +/// for i in 0..180 { /// rec.set_time_sequence("frame", i); /// /// // Log a regular transform which affects both the box and the points. diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index bf5a7905a63e..d883b5f7dc6c 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -686,7 +686,7 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { ArchetypeName::new("rerun.archetypes.LeafTransforms3D"), ArchetypeReflection { display_name: "Leaf transforms 3D", - docstring_md: "One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy.\n\nFor transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d).\n\nIf both [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) and [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) are present,\nfirst the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d).\n\nCurrently, most visualizers support only a single leaf transform per entity.\nCheck archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied.\n\nFrom the point of view of the entity's coordinate system,\nall components are applied in the inverse order they are listed here.\nE.g. if both a translation and a max3x3 transform are present,\nthe 3x3 matrix is applied first, followed by the translation.\n\n## Example\n\n### Regular & leaf transform in tandom\n```ignore\nuse rerun::{\n demo_util::grid,\n external::{anyhow, glam},\n};\n\nfn main() -> anyhow::Result<()> {\n let rec =\n rerun::RecordingStreamBuilder::new(\"rerun_example_leaf_transform3d_combined\").spawn()?;\n\n rec.set_time_sequence(\"frame\", 0);\n\n // Log a box and points further down in the hierarchy.\n rec.log(\n \"world/box\",\n &rerun::Boxes3D::from_half_sizes([[1.0, 1.0, 1.0]]),\n )?;\n rec.log(\n \"world/box/points\",\n &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)),\n )?;\n\n for i in 1..100 {\n rec.set_time_sequence(\"frame\", i);\n\n // Log a regular transform which affects both the box and the points.\n rec.log(\n \"world/box\",\n &rerun::Transform3D::from_rotation(rerun::RotationAxisAngle {\n axis: [0.0, 0.0, 1.0].into(),\n angle: rerun::Angle::from_degrees(i as f32 * 2.0),\n }),\n )?;\n\n // Log an leaf transform which affects only the box.\n let translation = [0.0, 0.0, (i as f32 * 0.1 - 5.0).abs() - 5.0];\n rec.log(\n \"world/box\",\n &rerun::LeafTransforms3D::new().with_translations([translation]),\n )?;\n }\n\n Ok(())\n}\n```\n
\n\n \n \n \n \n \n\n
", + docstring_md: "One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy.\n\nFor transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d).\n\nIf both [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) and [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) are present,\nfirst the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d).\n\nCurrently, most visualizers support only a single leaf transform per entity.\nCheck archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied.\n\nFrom the point of view of the entity's coordinate system,\nall components are applied in the inverse order they are listed here.\nE.g. if both a translation and a max3x3 transform are present,\nthe 3x3 matrix is applied first, followed by the translation.\n\n## Example\n\n### Regular & leaf transform in tandom\n```ignore\nuse rerun::{\n demo_util::grid,\n external::{anyhow, glam},\n};\n\nfn main() -> anyhow::Result<()> {\n let rec =\n rerun::RecordingStreamBuilder::new(\"rerun_example_leaf_transform3d_combined\").spawn()?;\n\n rec.set_time_sequence(\"frame\", 0);\n\n // Log a box and points further down in the hierarchy.\n rec.log(\n \"world/box\",\n &rerun::Boxes3D::from_half_sizes([[1.0, 1.0, 1.0]]),\n )?;\n rec.log(\n \"world/box/points\",\n &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)),\n )?;\n\n for i in 0..180 {\n rec.set_time_sequence(\"frame\", i);\n\n // Log a regular transform which affects both the box and the points.\n rec.log(\n \"world/box\",\n &rerun::Transform3D::from_rotation(rerun::RotationAxisAngle {\n axis: [0.0, 0.0, 1.0].into(),\n angle: rerun::Angle::from_degrees(i as f32 * 2.0),\n }),\n )?;\n\n // Log an leaf transform which affects only the box.\n let translation = [0.0, 0.0, (i as f32 * 0.1 - 5.0).abs() - 5.0];\n rec.log(\n \"world/box\",\n &rerun::LeafTransforms3D::new().with_translations([translation]),\n )?;\n }\n\n Ok(())\n}\n```\n
\n\n \n \n \n \n \n\n
", fields: vec![ ArchetypeFieldReflection { component_name : "rerun.components.LeafTranslation3D".into(), display_name : diff --git a/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp b/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp index da8afb1570a5..1983d3f13e77 100644 --- a/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp +++ b/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp @@ -14,7 +14,7 @@ int main() { rerun::Points3D(rerun::demo::grid3d(-10.0f, 10.0f, 10)) ); - for (int i = 1; i < 20; ++i) { + for (int i = 0; i < 180; ++i) { rec.set_time_sequence("frame", i); // Log a regular transform which affects both the box and the points. diff --git a/docs/snippets/all/archetypes/leaf_transforms3d_combined.py b/docs/snippets/all/archetypes/leaf_transforms3d_combined.py index f58e6131dc02..0c5dcca64523 100644 --- a/docs/snippets/all/archetypes/leaf_transforms3d_combined.py +++ b/docs/snippets/all/archetypes/leaf_transforms3d_combined.py @@ -11,7 +11,7 @@ rr.log("world/box", rr.Boxes3D(half_sizes=[[1.0, 1.0, 1.0]])) rr.log("world/box/points", rr.Points3D(np.vstack([xyz.ravel() for xyz in np.mgrid[3 * [slice(-10, 10, 10j)]]]).T)) -for i in range(1, 100): +for i in range(0, 180): rr.set_time_sequence("frame", i) # Log a regular transform which affects both the box and the points. diff --git a/docs/snippets/all/archetypes/leaf_transforms3d_combined.rs b/docs/snippets/all/archetypes/leaf_transforms3d_combined.rs index 7acfbfbfa92d..24b08cdffde4 100644 --- a/docs/snippets/all/archetypes/leaf_transforms3d_combined.rs +++ b/docs/snippets/all/archetypes/leaf_transforms3d_combined.rs @@ -21,7 +21,7 @@ fn main() -> anyhow::Result<()> { &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)), )?; - for i in 1..100 { + for i in 0..180 { rec.set_time_sequence("frame", i); // Log a regular transform which affects both the box and the points. diff --git a/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.cpp b/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.cpp index 6523b09714a7..1c9751a1bed8 100644 --- a/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.cpp +++ b/docs/snippets/all/archetypes/mesh3d_leaf_transforms3d.cpp @@ -32,7 +32,7 @@ int main() { .with_rotation_axis_angles({rerun::RotationAxisAngle( {0.0f, 0.0f, 1.0f}, rerun::Angle::degrees(static_cast(i) * 2.0f) - )}), + )}) ); } } diff --git a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp index b604b1a5d496..aec284d5f7f9 100644 --- a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp @@ -55,7 +55,7 @@ namespace rerun::archetypes { /// rerun::Points3D(rerun::demo::grid3d(-10.0f, 10.0f, 10)) /// ); /// - /// for (int i = 1; i <20; ++i) { + /// for (int i = 0; i <180; ++i) { /// rec.set_time_sequence("frame", i); /// /// // Log a regular transform which affects both the box and the points. diff --git a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp index e3a8e491b38b..ad6f7c8e272b 100644 --- a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp @@ -100,7 +100,7 @@ namespace rerun::archetypes { /// .with_rotation_axis_angles({rerun::RotationAxisAngle( /// {0.0f, 0.0f, 1.0f}, /// rerun::Angle::degrees(static_cast(i) * 2.0f) - /// )}), + /// )}) /// ); /// } /// } diff --git a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py index 3bef923c9460..44b487c04e73 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py @@ -51,7 +51,7 @@ class LeafTransforms3D(Archetype): rr.log("world/box", rr.Boxes3D(half_sizes=[[1.0, 1.0, 1.0]])) rr.log("world/box/points", rr.Points3D(np.vstack([xyz.ravel() for xyz in np.mgrid[3 * [slice(-10, 10, 10j)]]]).T)) - for i in range(1, 100): + for i in range(0, 180): rr.set_time_sequence("frame", i) # Log a regular transform which affects both the box and the points. From 02cd0bc9816f6713f8f649189738dde118be88f9 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 12:40:07 +0200 Subject: [PATCH 22/29] typo fix in python docgen --- rerun_py/docs/gen_common_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rerun_py/docs/gen_common_index.py b/rerun_py/docs/gen_common_index.py index fa54666c9ecb..73edd33519ca 100755 --- a/rerun_py/docs/gen_common_index.py +++ b/rerun_py/docs/gen_common_index.py @@ -209,7 +209,7 @@ class Section: "archetypes.DisconnectedSpace", "archetypes.Pinhole", "archetypes.Transform3D", - "archetypes.LeafTransform3D", + "archetypes.LeafTransforms3D", "archetypes.ViewCoordinates", "datatypes.Quaternion", "datatypes.RotationAxisAngle", From 5baf54a48dfb7e3e2c0a21c59767400bc053874d Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 12:42:47 +0200 Subject: [PATCH 23/29] C++ sample formatting and float shenanigans --- docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp b/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp index 1983d3f13e77..931d9d52c965 100644 --- a/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp +++ b/docs/snippets/all/archetypes/leaf_transforms3d_combined.cpp @@ -22,15 +22,14 @@ int main() { "world/box", rerun::Transform3D::from_rotation(rerun::RotationAxisAngle{ {0.0f, 0.0f, 1.0f}, - rerun::Angle::degrees(static_cast(i) * 2.0f) - }) + rerun::Angle::degrees(static_cast(i) * 2.0f)}) ); // Log an leaf transform which affects only the box. rec.log( "world/box", rerun::LeafTransforms3D().with_translations( - {{0, 0, fabs(static_cast(i) * 0.1f - 5.0f) - 5.0f}} + {{0.0f, 0.0f, std::abs(static_cast(i) * 0.1f - 5.0f) - 5.0f}} ) ); } From 8ffab403a31144cef697f462fa8dee08457be7f1 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 14:38:26 +0200 Subject: [PATCH 24/29] cargo fmt --- .../re_space_view_spatial/src/transform_component_tracker.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/viewer/re_space_view_spatial/src/transform_component_tracker.rs b/crates/viewer/re_space_view_spatial/src/transform_component_tracker.rs index a4fbb12c1848..de0879efe830 100644 --- a/crates/viewer/re_space_view_spatial/src/transform_component_tracker.rs +++ b/crates/viewer/re_space_view_spatial/src/transform_component_tracker.rs @@ -47,7 +47,8 @@ impl TransformComponentTracker { #[inline] pub fn is_potentially_transformed_leaf_transform3d(&self, entity_path: &EntityPath) -> bool { - self.leaf_transforms3d_entities.contains(&entity_path.hash()) + self.leaf_transforms3d_entities + .contains(&entity_path.hash()) } } From a7cf0206e5288aaac784eab2c2b19514bb9471ed Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jul 2024 14:43:30 +0200 Subject: [PATCH 25/29] codegen --- rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp index aec284d5f7f9..28caf975ba13 100644 --- a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp @@ -63,15 +63,14 @@ namespace rerun::archetypes { /// "world/box", /// rerun::Transform3D::from_rotation(rerun::RotationAxisAngle{ /// {0.0f, 0.0f, 1.0f}, - /// rerun::Angle::degrees(static_cast(i) * 2.0f) - /// }) + /// rerun::Angle::degrees(static_cast(i) * 2.0f)}) /// ); /// /// // Log an leaf transform which affects only the box. /// rec.log( /// "world/box", /// rerun::LeafTransforms3D().with_translations( - /// {{0, 0, fabs(static_cast(i) * 0.1f - 5.0f) - 5.0f}} + /// {{0.0f, 0.0f, std::abs(static_cast(i) * 0.1f - 5.0f) - 5.0f}} /// ) /// ); /// } From bff1eb0773573f7f0ac6c60e01ece321059b4ab8 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 31 Jul 2024 10:46:14 +0200 Subject: [PATCH 26/29] don't run mesh3d_leaf_transforms3d --- docs/snippets/snippets.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/snippets/snippets.toml b/docs/snippets/snippets.toml index 1678295fd3b9..ea368b3854a4 100644 --- a/docs/snippets/snippets.toml +++ b/docs/snippets/snippets.toml @@ -168,6 +168,12 @@ quick_start = [ # These examples don't have exactly the same implementation. "py", "rust", ] +"archetypes/mesh3d_leaf_transforms3d" = [ # TODO(#3235): Slight floating point differences in deg to rad conversion. + "cpp", + "py", + "rust", +] + # `$config_dir` will be replaced with the absolute path of `docs/snippets`. [extra_args] From d978da3814ee7c6c1878fcab89d6e8092d09cc47 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 31 Jul 2024 15:58:56 +0200 Subject: [PATCH 27/29] doc fixes fbs --- crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs | 3 ++- .../definitions/rerun/archetypes/leaf_transforms3d.fbs | 2 +- crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs | 3 ++- crates/store/re_types/src/archetypes/asset3d.rs | 3 ++- crates/store/re_types/src/archetypes/leaf_transforms3d.rs | 2 +- crates/store/re_types/src/archetypes/mesh3d.rs | 3 ++- crates/viewer/re_viewer/src/reflection/mod.rs | 2 +- docs/content/reference/types/archetypes/asset3d.md | 3 ++- docs/content/reference/types/archetypes/leaf_transforms3d.md | 2 +- docs/content/reference/types/archetypes/mesh3d.md | 3 ++- rerun_cpp/src/rerun/archetypes/asset3d.hpp | 3 ++- rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp | 2 +- rerun_cpp/src/rerun/archetypes/mesh3d.hpp | 3 ++- rerun_py/rerun_sdk/rerun/archetypes/asset3d.py | 3 ++- rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py | 2 +- rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py | 3 ++- 16 files changed, 26 insertions(+), 16 deletions(-) diff --git a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs index eca67013d9ed..c2942a1bbee9 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/asset3d.fbs @@ -6,7 +6,8 @@ namespace rerun.archetypes; /// /// See also [archetypes.Mesh3D]. /// -/// If there are multiple [archetypes.LeafTransforms3D], the mesh will be drawn for each transform. +/// If there are multiple [archetypes.LeafTransforms3D] instances logged to the same entity as a mesh, +/// an instance of the mesh will be drawn for each transform. /// /// \example archetypes/asset3d_simple title="Simple 3D asset" image="https://static.rerun.io/asset3d_simple/af238578188d3fd0de3e330212120e2842a8ddb2/1200w.png" table Asset3D ( diff --git a/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs index d0fa014b141d..eb07bb5da210 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs @@ -9,7 +9,7 @@ namespace rerun.archetypes; /// first the tree propagating [archetypes.Transform3D] is applied, then [archetypes.LeafTransforms3D]. /// /// Currently, most visualizers support only a single leaf transform per entity. -/// Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. +/// Check archetype documentations for details - if not otherwise specified, only the first leaf transform is applied. /// /// From the point of view of the entity's coordinate system, /// all components are applied in the inverse order they are listed here. diff --git a/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs index 8251897994a6..aa9fa48bd160 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/mesh3d.fbs @@ -6,7 +6,8 @@ namespace rerun.archetypes; /// /// See also [archetypes.Asset3D]. /// -/// If there are multiple [archetypes.LeafTransforms3D], the mesh will be drawn for each transform. +/// If there are multiple [archetypes.LeafTransforms3D] instances logged to the same entity as a mesh, +/// an instance of the mesh will be drawn for each transform. /// /// \example archetypes/mesh3d_indexed title="Simple indexed 3D mesh" image="https://static.rerun.io/mesh3d_simple/e1e5fd97265daf0d0bc7b782d862f19086fd6975/1200w.png" /// \example archetypes/mesh3d_partial_updates !api title="3D mesh with partial updates" image="https://static.rerun.io/mesh3d_partial_updates/a11e4accb0257dcd9531867b7e1d6fd5e3bee5c3/1200w.png" diff --git a/crates/store/re_types/src/archetypes/asset3d.rs b/crates/store/re_types/src/archetypes/asset3d.rs index 15d4f92eef82..da567d13824a 100644 --- a/crates/store/re_types/src/archetypes/asset3d.rs +++ b/crates/store/re_types/src/archetypes/asset3d.rs @@ -22,7 +22,8 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// See also [`archetypes::Mesh3D`][crate::archetypes::Mesh3D]. /// -/// If there are multiple [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D], the mesh will be drawn for each transform. +/// If there are multiple [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D] instances logged to the same entity as a mesh, +/// an instance of the mesh will be drawn for each transform. /// /// ## Example /// diff --git a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs index cffab5b3bc08..8802f630cd9e 100644 --- a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs +++ b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs @@ -26,7 +26,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// first the tree propagating [`archetypes::Transform3D`][crate::archetypes::Transform3D] is applied, then [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. /// /// Currently, most visualizers support only a single leaf transform per entity. -/// Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. +/// Check archetype documentations for details - if not otherwise specified, only the first leaf transform is applied. /// /// From the point of view of the entity's coordinate system, /// all components are applied in the inverse order they are listed here. diff --git a/crates/store/re_types/src/archetypes/mesh3d.rs b/crates/store/re_types/src/archetypes/mesh3d.rs index 178f5a18d679..624c98957d3a 100644 --- a/crates/store/re_types/src/archetypes/mesh3d.rs +++ b/crates/store/re_types/src/archetypes/mesh3d.rs @@ -22,7 +22,8 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// See also [`archetypes::Asset3D`][crate::archetypes::Asset3D]. /// -/// If there are multiple [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D], the mesh will be drawn for each transform. +/// If there are multiple [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D] instances logged to the same entity as a mesh, +/// an instance of the mesh will be drawn for each transform. /// /// ## Examples /// diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index d883b5f7dc6c..7e3e463182fa 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -686,7 +686,7 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { ArchetypeName::new("rerun.archetypes.LeafTransforms3D"), ArchetypeReflection { display_name: "Leaf transforms 3D", - docstring_md: "One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy.\n\nFor transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d).\n\nIf both [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) and [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) are present,\nfirst the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d).\n\nCurrently, most visualizers support only a single leaf transform per entity.\nCheck archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied.\n\nFrom the point of view of the entity's coordinate system,\nall components are applied in the inverse order they are listed here.\nE.g. if both a translation and a max3x3 transform are present,\nthe 3x3 matrix is applied first, followed by the translation.\n\n## Example\n\n### Regular & leaf transform in tandom\n```ignore\nuse rerun::{\n demo_util::grid,\n external::{anyhow, glam},\n};\n\nfn main() -> anyhow::Result<()> {\n let rec =\n rerun::RecordingStreamBuilder::new(\"rerun_example_leaf_transform3d_combined\").spawn()?;\n\n rec.set_time_sequence(\"frame\", 0);\n\n // Log a box and points further down in the hierarchy.\n rec.log(\n \"world/box\",\n &rerun::Boxes3D::from_half_sizes([[1.0, 1.0, 1.0]]),\n )?;\n rec.log(\n \"world/box/points\",\n &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)),\n )?;\n\n for i in 0..180 {\n rec.set_time_sequence(\"frame\", i);\n\n // Log a regular transform which affects both the box and the points.\n rec.log(\n \"world/box\",\n &rerun::Transform3D::from_rotation(rerun::RotationAxisAngle {\n axis: [0.0, 0.0, 1.0].into(),\n angle: rerun::Angle::from_degrees(i as f32 * 2.0),\n }),\n )?;\n\n // Log an leaf transform which affects only the box.\n let translation = [0.0, 0.0, (i as f32 * 0.1 - 5.0).abs() - 5.0];\n rec.log(\n \"world/box\",\n &rerun::LeafTransforms3D::new().with_translations([translation]),\n )?;\n }\n\n Ok(())\n}\n```\n
\n\n \n \n \n \n \n\n
", + docstring_md: "One or more transforms between the parent and the current entity which are *not* propagated in the transform hierarchy.\n\nFor transforms that are propagated in the transform hierarchy, see [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d).\n\nIf both [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) and [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) are present,\nfirst the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d).\n\nCurrently, most visualizers support only a single leaf transform per entity.\nCheck archetype documentations for details - if not otherwise specified, only the first leaf transform is applied.\n\nFrom the point of view of the entity's coordinate system,\nall components are applied in the inverse order they are listed here.\nE.g. if both a translation and a max3x3 transform are present,\nthe 3x3 matrix is applied first, followed by the translation.\n\n## Example\n\n### Regular & leaf transform in tandom\n```ignore\nuse rerun::{\n demo_util::grid,\n external::{anyhow, glam},\n};\n\nfn main() -> anyhow::Result<()> {\n let rec =\n rerun::RecordingStreamBuilder::new(\"rerun_example_leaf_transform3d_combined\").spawn()?;\n\n rec.set_time_sequence(\"frame\", 0);\n\n // Log a box and points further down in the hierarchy.\n rec.log(\n \"world/box\",\n &rerun::Boxes3D::from_half_sizes([[1.0, 1.0, 1.0]]),\n )?;\n rec.log(\n \"world/box/points\",\n &rerun::Points3D::new(grid(glam::Vec3::splat(-10.0), glam::Vec3::splat(10.0), 10)),\n )?;\n\n for i in 0..180 {\n rec.set_time_sequence(\"frame\", i);\n\n // Log a regular transform which affects both the box and the points.\n rec.log(\n \"world/box\",\n &rerun::Transform3D::from_rotation(rerun::RotationAxisAngle {\n axis: [0.0, 0.0, 1.0].into(),\n angle: rerun::Angle::from_degrees(i as f32 * 2.0),\n }),\n )?;\n\n // Log an leaf transform which affects only the box.\n let translation = [0.0, 0.0, (i as f32 * 0.1 - 5.0).abs() - 5.0];\n rec.log(\n \"world/box\",\n &rerun::LeafTransforms3D::new().with_translations([translation]),\n )?;\n }\n\n Ok(())\n}\n```\n
\n\n \n \n \n \n \n\n
", fields: vec![ ArchetypeFieldReflection { component_name : "rerun.components.LeafTranslation3D".into(), display_name : diff --git a/docs/content/reference/types/archetypes/asset3d.md b/docs/content/reference/types/archetypes/asset3d.md index 989bddfb4ea3..17a991d3290e 100644 --- a/docs/content/reference/types/archetypes/asset3d.md +++ b/docs/content/reference/types/archetypes/asset3d.md @@ -7,7 +7,8 @@ A prepacked 3D asset (`.gltf`, `.glb`, `.obj`, `.stl`, etc.). See also [`archetypes.Mesh3D`](https://rerun.io/docs/reference/types/archetypes/mesh3d). -If there are multiple [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d), the mesh will be drawn for each transform. +If there are multiple [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) instances logged to the same entity as a mesh, +an instance of the mesh will be drawn for each transform. ## Components diff --git a/docs/content/reference/types/archetypes/leaf_transforms3d.md b/docs/content/reference/types/archetypes/leaf_transforms3d.md index 3ab96c7ad772..6bd8c93acd7e 100644 --- a/docs/content/reference/types/archetypes/leaf_transforms3d.md +++ b/docs/content/reference/types/archetypes/leaf_transforms3d.md @@ -11,7 +11,7 @@ If both [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/ar first the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d). Currently, most visualizers support only a single leaf transform per entity. -Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. +Check archetype documentations for details - if not otherwise specified, only the first leaf transform is applied. From the point of view of the entity's coordinate system, all components are applied in the inverse order they are listed here. diff --git a/docs/content/reference/types/archetypes/mesh3d.md b/docs/content/reference/types/archetypes/mesh3d.md index 1fef4f950c76..e9dace74ced1 100644 --- a/docs/content/reference/types/archetypes/mesh3d.md +++ b/docs/content/reference/types/archetypes/mesh3d.md @@ -7,7 +7,8 @@ A 3D triangle mesh as specified by its per-mesh and per-vertex properties. See also [`archetypes.Asset3D`](https://rerun.io/docs/reference/types/archetypes/asset3d). -If there are multiple [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d), the mesh will be drawn for each transform. +If there are multiple [`archetypes.LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transforms3d) instances logged to the same entity as a mesh, +an instance of the mesh will be drawn for each transform. ## Components diff --git a/rerun_cpp/src/rerun/archetypes/asset3d.hpp b/rerun_cpp/src/rerun/archetypes/asset3d.hpp index 5b10825de8ad..88141b6b0ab8 100644 --- a/rerun_cpp/src/rerun/archetypes/asset3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/asset3d.hpp @@ -22,7 +22,8 @@ namespace rerun::archetypes { /// /// See also `archetypes::Mesh3D`. /// - /// If there are multiple `archetypes::LeafTransforms3D`, the mesh will be drawn for each transform. + /// If there are multiple `archetypes::LeafTransforms3D` instances logged to the same entity as a mesh, + /// an instance of the mesh will be drawn for each transform. /// /// ## Example /// diff --git a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp index 28caf975ba13..30bf26d0d7f2 100644 --- a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp @@ -28,7 +28,7 @@ namespace rerun::archetypes { /// first the tree propagating `archetypes::Transform3D` is applied, then `archetypes::LeafTransforms3D`. /// /// Currently, most visualizers support only a single leaf transform per entity. - /// Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. + /// Check archetype documentations for details - if not otherwise specified, only the first leaf transform is applied. /// /// From the point of view of the entity's coordinate system, /// all components are applied in the inverse order they are listed here. diff --git a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp index ad6f7c8e272b..934e9f8c6948 100644 --- a/rerun_cpp/src/rerun/archetypes/mesh3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/mesh3d.hpp @@ -27,7 +27,8 @@ namespace rerun::archetypes { /// /// See also `archetypes::Asset3D`. /// - /// If there are multiple `archetypes::LeafTransforms3D`, the mesh will be drawn for each transform. + /// If there are multiple `archetypes::LeafTransforms3D` instances logged to the same entity as a mesh, + /// an instance of the mesh will be drawn for each transform. /// /// ## Examples /// diff --git a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py index a4b753aee4e6..f442198762b6 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py @@ -23,7 +23,8 @@ class Asset3D(Asset3DExt, Archetype): See also [`archetypes.Mesh3D`][rerun.archetypes.Mesh3D]. - If there are multiple [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D], the mesh will be drawn for each transform. + If there are multiple [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D] instances logged to the same entity as a mesh, + an instance of the mesh will be drawn for each transform. Example ------- diff --git a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py index 44b487c04e73..17c1a84b3780 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py @@ -29,7 +29,7 @@ class LeafTransforms3D(Archetype): first the tree propagating [`archetypes.Transform3D`][rerun.archetypes.Transform3D] is applied, then [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. Currently, most visualizers support only a single leaf transform per entity. - Check archetype documentations for details - if not otherwise specified, onlyt the first leaf transform is applied. + Check archetype documentations for details - if not otherwise specified, only the first leaf transform is applied. From the point of view of the entity's coordinate system, all components are applied in the inverse order they are listed here. diff --git a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py index 01a864b48930..3aa2a3d5c043 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py @@ -23,7 +23,8 @@ class Mesh3D(Mesh3DExt, Archetype): See also [`archetypes.Asset3D`][rerun.archetypes.Asset3D]. - If there are multiple [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D], the mesh will be drawn for each transform. + If there are multiple [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D] instances logged to the same entity as a mesh, + an instance of the mesh will be drawn for each transform. Examples -------- From 111d601717f56018300d5e90d11fc3ba1b7d3bfa Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 31 Jul 2024 16:19:29 +0200 Subject: [PATCH 28/29] note on simpler transform hierarchy --- .../re_space_view_spatial/src/contexts/transform_context.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs index d20bfa5649c0..f202a828d7c6 100644 --- a/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs +++ b/crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs @@ -114,6 +114,8 @@ enum UnreachableTransformReason { /// making world and reference space equivalent for a given space view. /// /// Should be recomputed every frame. +/// +/// TODO(#7025): Alternative proposal to not have to deal with tree upwards walking & per-origin tree walking. #[derive(Clone)] pub struct TransformContext { /// All transforms provided are relative to this reference path. From 0e22047138b922720978088a8183ac5a7719d48e Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 31 Jul 2024 16:26:31 +0200 Subject: [PATCH 29/29] better point out the nature of hybrid join --- .../viewer/re_space_view_spatial/src/visualizers/assets3d.rs | 3 ++- crates/viewer/re_space_view_spatial/src/visualizers/meshes.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs index 69ab1d85ab62..4b71591cf8d9 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs @@ -81,7 +81,8 @@ impl Asset3DVisualizer { if let Some(mesh) = mesh { re_tracing::profile_scope!("mesh instances"); - // Let's draw the mesh once for every instance transform. Because why not! + // Let's draw the mesh once for every instance transform. + // TODO(#7026): This a rare form of hybrid joining. for &world_from_pose in &ent_context.transform_info.reference_from_instances { instances.extend(mesh.mesh_instances.iter().map(move |mesh_instance| { let pose_from_mesh = mesh_instance.world_from_mesh; diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/meshes.rs b/crates/viewer/re_space_view_spatial/src/visualizers/meshes.rs index 36044df46e2d..defea630efed 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/meshes.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/meshes.rs @@ -119,7 +119,8 @@ impl Mesh3DVisualizer { }); if let Some(mesh) = mesh { - // Let's draw the mesh once for every instance transform. Because why not! + // Let's draw the mesh once for every instance transform. + // TODO(#7026): This a rare form of hybrid joining. for &world_from_instance in &ent_context.transform_info.reference_from_instances { instances.extend(mesh.mesh_instances.iter().map(move |mesh_instance| { let entity_from_mesh = mesh_instance.world_from_mesh;