diff --git a/crates/store/re_types/definitions/rerun/archetypes/boxes3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/boxes3d.fbs index 97b430877b37..5698436f09e4 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/boxes3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/boxes3d.fbs @@ -4,6 +4,9 @@ namespace rerun.archetypes; /// 3D boxes with half-extents and optional center, rotations, colors etc. /// +/// Note that orienting and placing the box is handled via `[archetypes.LeafTransforms3D]`. +/// Some of its component are repeated here for convenience. +/// /// \example archetypes/box3d_simple !api title="Simple 3D boxes" image="https://static.rerun.io/box3d_simple/d6a3f38d2e3360fbacac52bb43e44762635be9c8/1200w.png" /// \example archetypes/box3d_batch title="Batch of 3D boxes" image="https://static.rerun.io/box3d_batch/6d3e453c3a0201ae42bbae9de941198513535f1d/1200w.png" table Boxes3D ( @@ -21,13 +24,25 @@ table Boxes3D ( // --- Recommended --- /// Optional center positions of the boxes. - centers: [rerun.components.Position3D] ("attr.rerun.component_recommended", nullable, order: 2000); + /// + /// If not specified, the centers will be at (0, 0, 0). + /// Note that this uses a [components.LeafTranslation3D] which is also used by [archetypes.LeafTransforms3D]. + centers: [rerun.components.LeafTranslation3D] ("attr.rerun.component_recommended", nullable, order: 2000); - /// Optional rotations of the boxes. - rotations: [rerun.components.Rotation3D] ("attr.rerun.component_recommended", nullable, order: 2100); + /// Rotations via axis + angle. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a [components.LeafRotationAxisAngle] which is also used by [archetypes.LeafTransforms3D]. + rotation_axis_angles: [rerun.components.LeafRotationAxisAngle] ("attr.rerun.component_optional", nullable, order: 2100); + + /// Rotations via quaternion. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a [components.LeafRotationQuat] which is also used by [archetypes.LeafTransforms3D]. + quaternions: [rerun.components.LeafRotationQuat] ("attr.rerun.component_optional", nullable, order: 2200); /// Optional colors for the boxes. - colors: [rerun.components.Color] ("attr.rerun.component_recommended", nullable, order: 2200); + colors: [rerun.components.Color] ("attr.rerun.component_recommended", nullable, order: 2300); // --- Optional --- diff --git a/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs b/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs index 5e1e7b5a990f..cd002b615891 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs @@ -8,8 +8,8 @@ namespace rerun.archetypes; /// (e.g. a bounding sphere). /// For points whose radii are for the sake of visualization, use `Points3D` instead. /// -/// Currently, ellipsoids are always rendered as wireframes. -/// Opaque and transparent rendering will be supported later. +/// Note that orienting and placing the ellipsoids/spheres is handled via `[archetypes.LeafTransforms3D]`. +/// Some of its component are repeated here for convenience. /// /// \example archetypes/ellipsoid_batch !api title="Batch of ellipsoids" table Ellipsoids ( @@ -32,15 +32,23 @@ table Ellipsoids ( /// Optional center positions of the ellipsoids. /// /// If not specified, the centers will be at (0, 0, 0). - centers: [rerun.components.Position3D] ("attr.rerun.component_recommended", nullable, order: 2000); + /// Note that this uses a [components.LeafTranslation3D] which is also used by [archetypes.LeafTransforms3D]. + centers: [rerun.components.LeafTranslation3D] ("attr.rerun.component_recommended", nullable, order: 2000); - /// Optional rotations of the ellipsoids. + /// Rotations via axis + angle. /// - /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. - rotations: [rerun.components.Rotation3D] ("attr.rerun.component_recommended", nullable, order: 2100); + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a [components.LeafRotationAxisAngle] which is also used by [archetypes.LeafTransforms3D]. + rotation_axis_angles: [rerun.components.LeafRotationAxisAngle] ("attr.rerun.component_optional", nullable, order: 2100); + + /// Rotations via quaternion. + /// + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a [components.LeafRotationQuat] which is also used by [archetypes.LeafTransforms3D]. + quaternions: [rerun.components.LeafRotationQuat] ("attr.rerun.component_optional", nullable, order: 2200); /// Optional colors for the ellipsoids. - colors: [rerun.components.Color] ("attr.rerun.component_recommended", nullable, order: 2200); + colors: [rerun.components.Color] ("attr.rerun.component_recommended", nullable, order: 2300); // --- Optional --- 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 eb07bb5da210..9ff2cdb8a1c2 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/leaf_transforms3d.fbs @@ -8,7 +8,7 @@ namespace rerun.archetypes; /// 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. +/// Currently, many visualizers support only a single leaf transform per entity. /// 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, diff --git a/crates/store/re_types/src/archetypes/boxes3d.rs b/crates/store/re_types/src/archetypes/boxes3d.rs index 2797a7840720..87d9753e8756 100644 --- a/crates/store/re_types/src/archetypes/boxes3d.rs +++ b/crates/store/re_types/src/archetypes/boxes3d.rs @@ -20,6 +20,9 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// **Archetype**: 3D boxes with half-extents and optional center, rotations, colors etc. /// +/// Note that orienting and placing the box is handled via `[archetypes.LeafTransforms3D]`. +/// Some of its component are repeated here for convenience. +/// /// ## Example /// /// ### Batch of 3D boxes @@ -33,10 +36,9 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// [(2.0, 0.0, 0.0), (-2.0, 0.0, 0.0), (0.0, 0.0, 2.0)], /// [(2.0, 2.0, 1.0), (1.0, 1.0, 0.5), (2.0, 0.5, 1.0)], /// ) -/// .with_rotations([ -/// rerun::Rotation3D::IDENTITY, -/// rerun::Quaternion::from_xyzw([0.0, 0.0, 0.382683, 0.923880]).into(), // 45 degrees around Z -/// rerun::RotationAxisAngle::new((0.0, 1.0, 0.0), rerun::Angle::from_degrees(30.0)).into(), +/// .with_quaternions([ +/// rerun::Quaternion::IDENTITY, +/// rerun::Quaternion::from_xyzw([0.0, 0.0, 0.382683, 0.923880]), // 45 degrees around Z /// ]) /// .with_radii([0.025]) /// .with_colors([ @@ -66,10 +68,22 @@ pub struct Boxes3D { pub half_sizes: Vec, /// Optional center positions of the boxes. - pub centers: Option>, + /// + /// If not specified, the centers will be at (0, 0, 0). + /// Note that this uses a [`components::LeafTranslation3D`][crate::components::LeafTranslation3D] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. + pub centers: Option>, + + /// Rotations via axis + angle. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a [`components::LeafRotationAxisAngle`][crate::components::LeafRotationAxisAngle] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. + pub rotation_axis_angles: Option>, - /// Optional rotations of the boxes. - pub rotations: Option>, + /// Rotations via quaternion. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a [`components::LeafRotationQuat`][crate::components::LeafRotationQuat] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. + pub quaternions: Option>, /// Optional colors for the boxes. pub colors: Option>, @@ -97,7 +111,8 @@ impl ::re_types_core::SizeBytes for Boxes3D { fn heap_size_bytes(&self) -> u64 { self.half_sizes.heap_size_bytes() + self.centers.heap_size_bytes() - + self.rotations.heap_size_bytes() + + self.rotation_axis_angles.heap_size_bytes() + + self.quaternions.heap_size_bytes() + self.colors.heap_size_bytes() + self.radii.heap_size_bytes() + self.fill_mode.heap_size_bytes() @@ -108,8 +123,9 @@ impl ::re_types_core::SizeBytes for Boxes3D { #[inline] fn is_pod() -> bool { >::is_pod() - && >>::is_pod() - && >>::is_pod() + && >>::is_pod() + && >>::is_pod() + && >>::is_pod() && >>::is_pod() && >>::is_pod() && >::is_pod() @@ -121,19 +137,20 @@ impl ::re_types_core::SizeBytes for Boxes3D { static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = once_cell::sync::Lazy::new(|| ["rerun.components.HalfSize3D".into()]); -static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = +static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 3usize]> = once_cell::sync::Lazy::new(|| { [ - "rerun.components.Position3D".into(), - "rerun.components.Rotation3D".into(), + "rerun.components.LeafTranslation3D".into(), "rerun.components.Color".into(), "rerun.components.Boxes3DIndicator".into(), ] }); -static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 6usize]> = once_cell::sync::Lazy::new(|| { [ + "rerun.components.LeafRotationAxisAngle".into(), + "rerun.components.LeafRotationQuat".into(), "rerun.components.Radius".into(), "rerun.components.FillMode".into(), "rerun.components.Text".into(), @@ -141,14 +158,15 @@ static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = ] }); -static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 9usize]> = +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 10usize]> = once_cell::sync::Lazy::new(|| { [ "rerun.components.HalfSize3D".into(), - "rerun.components.Position3D".into(), - "rerun.components.Rotation3D".into(), + "rerun.components.LeafTranslation3D".into(), "rerun.components.Color".into(), "rerun.components.Boxes3DIndicator".into(), + "rerun.components.LeafRotationAxisAngle".into(), + "rerun.components.LeafRotationQuat".into(), "rerun.components.Radius".into(), "rerun.components.FillMode".into(), "rerun.components.Text".into(), @@ -157,8 +175,8 @@ static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 9usize]> = }); impl Boxes3D { - /// The total number of components in the archetype: 1 required, 4 recommended, 4 optional - pub const NUM_COMPONENTS: usize = 9usize; + /// The total number of components in the archetype: 1 required, 3 recommended, 6 optional + pub const NUM_COMPONENTS: usize = 10usize; } /// Indicator component for the [`Boxes3D`] [`::re_types_core::Archetype`] @@ -225,9 +243,10 @@ impl ::re_types_core::Archetype for Boxes3D { .collect::>>() .with_context("rerun.archetypes.Boxes3D#half_sizes")? }; - let centers = if let Some(array) = arrays_by_name.get("rerun.components.Position3D") { + let centers = if let Some(array) = arrays_by_name.get("rerun.components.LeafTranslation3D") + { Some({ - ::from_arrow_opt(&**array) + ::from_arrow_opt(&**array) .with_context("rerun.archetypes.Boxes3D#centers")? .into_iter() .map(|v| v.ok_or_else(DeserializationError::missing_data)) @@ -237,18 +256,32 @@ impl ::re_types_core::Archetype for Boxes3D { } else { None }; - let rotations = if let Some(array) = arrays_by_name.get("rerun.components.Rotation3D") { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Boxes3D#rotations")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Boxes3D#rotations")? - }) - } else { - None - }; + let rotation_axis_angles = + if let Some(array) = arrays_by_name.get("rerun.components.LeafRotationAxisAngle") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Boxes3D#rotation_axis_angles")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Boxes3D#rotation_axis_angles")? + }) + } else { + None + }; + let quaternions = + if let Some(array) = arrays_by_name.get("rerun.components.LeafRotationQuat") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Boxes3D#quaternions")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Boxes3D#quaternions")? + }) + } else { + None + }; let colors = if let Some(array) = arrays_by_name.get("rerun.components.Color") { Some({ ::from_arrow_opt(&**array) @@ -309,7 +342,8 @@ impl ::re_types_core::Archetype for Boxes3D { Ok(Self { half_sizes, centers, - rotations, + rotation_axis_angles, + quaternions, colors, radii, fill_mode, @@ -329,7 +363,10 @@ impl ::re_types_core::AsComponents for Boxes3D { self.centers .as_ref() .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), - self.rotations + self.rotation_axis_angles + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.quaternions .as_ref() .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), self.colors @@ -363,7 +400,8 @@ impl Boxes3D { Self { half_sizes: half_sizes.into_iter().map(Into::into).collect(), centers: None, - rotations: None, + rotation_axis_angles: None, + quaternions: None, colors: None, radii: None, fill_mode: None, @@ -373,22 +411,44 @@ impl Boxes3D { } /// Optional center positions of the boxes. + /// + /// If not specified, the centers will be at (0, 0, 0). + /// Note that this uses a [`components::LeafTranslation3D`][crate::components::LeafTranslation3D] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. #[inline] pub fn with_centers( mut self, - centers: impl IntoIterator>, + centers: impl IntoIterator>, ) -> Self { self.centers = Some(centers.into_iter().map(Into::into).collect()); self } - /// Optional rotations of the boxes. + /// Rotations via axis + angle. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a [`components::LeafRotationAxisAngle`][crate::components::LeafRotationAxisAngle] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. + #[inline] + pub fn with_rotation_axis_angles( + mut self, + rotation_axis_angles: impl IntoIterator< + Item = impl Into, + >, + ) -> Self { + self.rotation_axis_angles = + Some(rotation_axis_angles.into_iter().map(Into::into).collect()); + self + } + + /// Rotations via quaternion. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a [`components::LeafRotationQuat`][crate::components::LeafRotationQuat] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. #[inline] - pub fn with_rotations( + pub fn with_quaternions( mut self, - rotations: impl IntoIterator>, + quaternions: impl IntoIterator>, ) -> Self { - self.rotations = Some(rotations.into_iter().map(Into::into).collect()); + self.quaternions = Some(quaternions.into_iter().map(Into::into).collect()); self } diff --git a/crates/store/re_types/src/archetypes/boxes3d_ext.rs b/crates/store/re_types/src/archetypes/boxes3d_ext.rs index d0f53c5c7967..f326feaf0f63 100644 --- a/crates/store/re_types/src/archetypes/boxes3d_ext.rs +++ b/crates/store/re_types/src/archetypes/boxes3d_ext.rs @@ -1,5 +1,5 @@ use crate::{ - components::{HalfSize3D, Position3D}, + components::{HalfSize3D, LeafTranslation3D}, datatypes::Vec3D, }; @@ -15,7 +15,7 @@ impl Boxes3D { /// Creates new [`Boxes3D`] with [`Self::centers`] and [`Self::half_sizes`]. #[inline] pub fn from_centers_and_half_sizes( - centers: impl IntoIterator>, + centers: impl IntoIterator>, half_sizes: impl IntoIterator>, ) -> Self { Self::new(half_sizes).with_centers(centers) @@ -37,7 +37,7 @@ impl Boxes3D { /// TODO(#3285): Does *not* preserve data as-is and instead creates half-sizes from the input data. #[inline] pub fn from_centers_and_sizes( - centers: impl IntoIterator>, + centers: impl IntoIterator>, sizes: impl IntoIterator>, ) -> Self { Self::from_sizes(sizes).with_centers(centers) @@ -56,7 +56,7 @@ impl Boxes3D { .zip(boxes.half_sizes.iter()) .map(|(min, half_size)| { let min = min.into(); - Position3D::new( + LeafTranslation3D::new( min.x() + half_size.x(), min.y() + half_size.y(), min.z() + half_size.z(), diff --git a/crates/store/re_types/src/archetypes/ellipsoids.rs b/crates/store/re_types/src/archetypes/ellipsoids.rs index f0842e113556..7eb84c9642a0 100644 --- a/crates/store/re_types/src/archetypes/ellipsoids.rs +++ b/crates/store/re_types/src/archetypes/ellipsoids.rs @@ -24,8 +24,8 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// (e.g. a bounding sphere). /// For points whose radii are for the sake of visualization, use `Points3D` instead. /// -/// Currently, ellipsoids are always rendered as wireframes. -/// Opaque and transparent rendering will be supported later. +/// Note that orienting and placing the ellipsoids/spheres is handled via `[archetypes.LeafTransforms3D]`. +/// Some of its component are repeated here for convenience. #[derive(Clone, Debug, PartialEq)] pub struct Ellipsoids { /// For each ellipsoid, half of its size on its three axes. @@ -36,12 +36,20 @@ pub struct Ellipsoids { /// Optional center positions of the ellipsoids. /// /// If not specified, the centers will be at (0, 0, 0). - pub centers: Option>, + /// Note that this uses a [`components::LeafTranslation3D`][crate::components::LeafTranslation3D] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. + pub centers: Option>, - /// Optional rotations of the ellipsoids. + /// Rotations via axis + angle. /// - /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. - pub rotations: Option>, + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a [`components::LeafRotationAxisAngle`][crate::components::LeafRotationAxisAngle] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. + pub rotation_axis_angles: Option>, + + /// Rotations via quaternion. + /// + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a [`components::LeafRotationQuat`][crate::components::LeafRotationQuat] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. + pub quaternions: Option>, /// Optional colors for the ellipsoids. pub colors: Option>, @@ -66,7 +74,8 @@ impl ::re_types_core::SizeBytes for Ellipsoids { fn heap_size_bytes(&self) -> u64 { self.half_sizes.heap_size_bytes() + self.centers.heap_size_bytes() - + self.rotations.heap_size_bytes() + + self.rotation_axis_angles.heap_size_bytes() + + self.quaternions.heap_size_bytes() + self.colors.heap_size_bytes() + self.line_radii.heap_size_bytes() + self.fill_mode.heap_size_bytes() @@ -77,8 +86,9 @@ impl ::re_types_core::SizeBytes for Ellipsoids { #[inline] fn is_pod() -> bool { >::is_pod() - && >>::is_pod() - && >>::is_pod() + && >>::is_pod() + && >>::is_pod() + && >>::is_pod() && >>::is_pod() && >>::is_pod() && >::is_pod() @@ -90,19 +100,20 @@ impl ::re_types_core::SizeBytes for Ellipsoids { static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = once_cell::sync::Lazy::new(|| ["rerun.components.HalfSize3D".into()]); -static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = +static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 3usize]> = once_cell::sync::Lazy::new(|| { [ - "rerun.components.Position3D".into(), - "rerun.components.Rotation3D".into(), + "rerun.components.LeafTranslation3D".into(), "rerun.components.Color".into(), "rerun.components.EllipsoidsIndicator".into(), ] }); -static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 6usize]> = once_cell::sync::Lazy::new(|| { [ + "rerun.components.LeafRotationAxisAngle".into(), + "rerun.components.LeafRotationQuat".into(), "rerun.components.Radius".into(), "rerun.components.FillMode".into(), "rerun.components.Text".into(), @@ -110,14 +121,15 @@ static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = ] }); -static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 9usize]> = +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 10usize]> = once_cell::sync::Lazy::new(|| { [ "rerun.components.HalfSize3D".into(), - "rerun.components.Position3D".into(), - "rerun.components.Rotation3D".into(), + "rerun.components.LeafTranslation3D".into(), "rerun.components.Color".into(), "rerun.components.EllipsoidsIndicator".into(), + "rerun.components.LeafRotationAxisAngle".into(), + "rerun.components.LeafRotationQuat".into(), "rerun.components.Radius".into(), "rerun.components.FillMode".into(), "rerun.components.Text".into(), @@ -126,8 +138,8 @@ static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 9usize]> = }); impl Ellipsoids { - /// The total number of components in the archetype: 1 required, 4 recommended, 4 optional - pub const NUM_COMPONENTS: usize = 9usize; + /// The total number of components in the archetype: 1 required, 3 recommended, 6 optional + pub const NUM_COMPONENTS: usize = 10usize; } /// Indicator component for the [`Ellipsoids`] [`::re_types_core::Archetype`] @@ -194,9 +206,10 @@ impl ::re_types_core::Archetype for Ellipsoids { .collect::>>() .with_context("rerun.archetypes.Ellipsoids#half_sizes")? }; - let centers = if let Some(array) = arrays_by_name.get("rerun.components.Position3D") { + let centers = if let Some(array) = arrays_by_name.get("rerun.components.LeafTranslation3D") + { Some({ - ::from_arrow_opt(&**array) + ::from_arrow_opt(&**array) .with_context("rerun.archetypes.Ellipsoids#centers")? .into_iter() .map(|v| v.ok_or_else(DeserializationError::missing_data)) @@ -206,18 +219,32 @@ impl ::re_types_core::Archetype for Ellipsoids { } else { None }; - let rotations = if let Some(array) = arrays_by_name.get("rerun.components.Rotation3D") { - Some({ - ::from_arrow_opt(&**array) - .with_context("rerun.archetypes.Ellipsoids#rotations")? - .into_iter() - .map(|v| v.ok_or_else(DeserializationError::missing_data)) - .collect::>>() - .with_context("rerun.archetypes.Ellipsoids#rotations")? - }) - } else { - None - }; + let rotation_axis_angles = + if let Some(array) = arrays_by_name.get("rerun.components.LeafRotationAxisAngle") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Ellipsoids#rotation_axis_angles")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Ellipsoids#rotation_axis_angles")? + }) + } else { + None + }; + let quaternions = + if let Some(array) = arrays_by_name.get("rerun.components.LeafRotationQuat") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Ellipsoids#quaternions")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Ellipsoids#quaternions")? + }) + } else { + None + }; let colors = if let Some(array) = arrays_by_name.get("rerun.components.Color") { Some({ ::from_arrow_opt(&**array) @@ -278,7 +305,8 @@ impl ::re_types_core::Archetype for Ellipsoids { Ok(Self { half_sizes, centers, - rotations, + rotation_axis_angles, + quaternions, colors, line_radii, fill_mode, @@ -298,7 +326,10 @@ impl ::re_types_core::AsComponents for Ellipsoids { self.centers .as_ref() .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), - self.rotations + self.rotation_axis_angles + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.quaternions .as_ref() .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), self.colors @@ -332,7 +363,8 @@ impl Ellipsoids { Self { half_sizes: half_sizes.into_iter().map(Into::into).collect(), centers: None, - rotations: None, + rotation_axis_angles: None, + quaternions: None, colors: None, line_radii: None, fill_mode: None, @@ -344,24 +376,42 @@ impl Ellipsoids { /// Optional center positions of the ellipsoids. /// /// If not specified, the centers will be at (0, 0, 0). + /// Note that this uses a [`components::LeafTranslation3D`][crate::components::LeafTranslation3D] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. #[inline] pub fn with_centers( mut self, - centers: impl IntoIterator>, + centers: impl IntoIterator>, ) -> Self { self.centers = Some(centers.into_iter().map(Into::into).collect()); self } - /// Optional rotations of the ellipsoids. + /// Rotations via axis + angle. + /// + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a [`components::LeafRotationAxisAngle`][crate::components::LeafRotationAxisAngle] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. + #[inline] + pub fn with_rotation_axis_angles( + mut self, + rotation_axis_angles: impl IntoIterator< + Item = impl Into, + >, + ) -> Self { + self.rotation_axis_angles = + Some(rotation_axis_angles.into_iter().map(Into::into).collect()); + self + } + + /// Rotations via quaternion. /// - /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a [`components::LeafRotationQuat`][crate::components::LeafRotationQuat] which is also used by [`archetypes::LeafTransforms3D`][crate::archetypes::LeafTransforms3D]. #[inline] - pub fn with_rotations( + pub fn with_quaternions( mut self, - rotations: impl IntoIterator>, + quaternions: impl IntoIterator>, ) -> Self { - self.rotations = Some(rotations.into_iter().map(Into::into).collect()); + self.quaternions = Some(quaternions.into_iter().map(Into::into).collect()); self } diff --git a/crates/store/re_types/src/archetypes/ellipsoids_ext.rs b/crates/store/re_types/src/archetypes/ellipsoids_ext.rs index b4b152d532b2..91952a8134ca 100644 --- a/crates/store/re_types/src/archetypes/ellipsoids_ext.rs +++ b/crates/store/re_types/src/archetypes/ellipsoids_ext.rs @@ -1,4 +1,4 @@ -use crate::components::{HalfSize3D, Position3D}; +use crate::components::{HalfSize3D, LeafTranslation3D}; use super::Ellipsoids; @@ -19,7 +19,7 @@ impl Ellipsoids { #[doc(alias = "sphere")] #[inline] pub fn from_centers_and_radii( - centers: impl IntoIterator>, + centers: impl IntoIterator>, radii: impl IntoIterator, ) -> Self { Self::new(radii.into_iter().map(HalfSize3D::splat)).with_centers(centers) @@ -34,7 +34,7 @@ impl Ellipsoids { /// Creates a new [`Ellipsoids`] with [`Self::centers`] and [`Self::half_sizes`]. #[inline] pub fn from_centers_and_half_sizes( - centers: impl IntoIterator>, + centers: impl IntoIterator>, half_sizes: impl IntoIterator>, ) -> Self { Self::new(half_sizes).with_centers(centers) diff --git a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs index 8802f630cd9e..256e1200ce19 100644 --- a/crates/store/re_types/src/archetypes/leaf_transforms3d.rs +++ b/crates/store/re_types/src/archetypes/leaf_transforms3d.rs @@ -25,7 +25,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// 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. +/// Currently, many visualizers support only a single leaf transform per entity. /// 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, 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 index 02b86240949d..329755a1411d 100644 --- 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 @@ -3,6 +3,9 @@ use crate::datatypes; use super::LeafRotationAxisAngle; impl LeafRotationAxisAngle { + /// The identity rotation, representing no rotation. + pub const IDENTITY: Self = Self(datatypes::RotationAxisAngle::IDENTITY); + /// Create a new rotation from an axis and an angle. #[inline] pub fn new(axis: impl Into, angle: impl Into) -> Self { diff --git a/crates/store/re_types/src/components/leaf_translation3d_ext.rs b/crates/store/re_types/src/components/leaf_translation3d_ext.rs index 9faa18ee4719..0f6cd50d98f7 100644 --- a/crates/store/re_types/src/components/leaf_translation3d_ext.rs +++ b/crates/store/re_types/src/components/leaf_translation3d_ext.rs @@ -1,5 +1,5 @@ -#[cfg(feature = "glam")] use super::LeafTranslation3D; +use crate::datatypes::Vec3D; // This is intentionally not implemented for `Vec3`: // The transform semantic is expressed here, `Vec3` on the other hand implements conversion to `glam::Vec3A`. @@ -13,3 +13,32 @@ impl From for glam::Affine3A { } } } + +impl LeafTranslation3D { + /// No translation. + pub const ZERO: Self = Self::new(0.0, 0.0, 0.0); + + /// Create a new translation. + #[inline] + pub const fn new(x: f32, y: f32, z: f32) -> Self { + Self(Vec3D::new(x, y, z)) + } + + /// The x coordinate, i.e. index 0 + #[inline] + pub fn x(&self) -> f32 { + self.0.x() + } + + /// The y coordinate, i.e. index 1 + #[inline] + pub fn y(&self) -> f32 { + self.0.y() + } + + /// The z coordinate, i.e. index 2 + #[inline] + pub fn z(&self) -> f32 { + self.0.z() + } +} diff --git a/crates/store/re_types/src/components/rotation_axis_angle_ext.rs b/crates/store/re_types/src/components/rotation_axis_angle_ext.rs index 76737ba8f10d..f8b9ea14c3f9 100644 --- a/crates/store/re_types/src/components/rotation_axis_angle_ext.rs +++ b/crates/store/re_types/src/components/rotation_axis_angle_ext.rs @@ -3,6 +3,9 @@ use crate::datatypes; use super::RotationAxisAngle; impl RotationAxisAngle { + /// The identity rotation, representing no rotation. + pub const IDENTITY: Self = Self(datatypes::RotationAxisAngle::IDENTITY); + /// Create a new rotation from an axis and an angle. #[inline] pub fn new(axis: impl Into, angle: impl Into) -> Self { diff --git a/crates/store/re_types/src/components/translation3d_ext.rs b/crates/store/re_types/src/components/translation3d_ext.rs index 6d55deee950e..6d0f24e483a4 100644 --- a/crates/store/re_types/src/components/translation3d_ext.rs +++ b/crates/store/re_types/src/components/translation3d_ext.rs @@ -1,5 +1,5 @@ -#[cfg(feature = "glam")] use super::Translation3D; +use crate::datatypes::Vec3D; // This is intentionally not implemented for `Vec3`: // The transform semantic is expressed here, `Vec3` on the other hand implements conversion to `glam::Vec3A`. @@ -13,3 +13,32 @@ impl From for glam::Affine3A { } } } + +impl Translation3D { + /// No translation. + pub const ZERO: Self = Self::new(0.0, 0.0, 0.0); + + /// Create a new translation. + #[inline] + pub const fn new(x: f32, y: f32, z: f32) -> Self { + Self(Vec3D::new(x, y, z)) + } + + /// The x coordinate, i.e. index 0 + #[inline] + pub fn x(&self) -> f32 { + self.0.x() + } + + /// The y coordinate, i.e. index 1 + #[inline] + pub fn y(&self) -> f32 { + self.0.y() + } + + /// The z coordinate, i.e. index 2 + #[inline] + pub fn z(&self) -> f32 { + self.0.z() + } +} diff --git a/crates/store/re_types/src/datatypes/angle_ext.rs b/crates/store/re_types/src/datatypes/angle_ext.rs index b6a98f9f8f4f..d91e813f0a70 100644 --- a/crates/store/re_types/src/datatypes/angle_ext.rs +++ b/crates/store/re_types/src/datatypes/angle_ext.rs @@ -2,6 +2,9 @@ use super::Angle; use std::fmt::Formatter; impl Angle { + /// Zero angle, often used for representing no rotation. + pub const ZERO: Self = Self { radians: 0.0 }; + /// Angle in radians. #[inline] pub fn radians(&self) -> f32 { diff --git a/crates/store/re_types/src/datatypes/rotation3d_ext.rs b/crates/store/re_types/src/datatypes/rotation3d_ext.rs index 27d27485b55c..f0ba1deafc0f 100644 --- a/crates/store/re_types/src/datatypes/rotation3d_ext.rs +++ b/crates/store/re_types/src/datatypes/rotation3d_ext.rs @@ -65,14 +65,3 @@ impl Default for Rotation3D { Self::IDENTITY } } - -// TODO(#6831): Transitional, to be removed with the rest of this file. -impl From for Rotation3D { - #[inline] - fn from(r: crate::Rotation3D) -> Self { - match r { - crate::Rotation3D::Quaternion(q) => Self::Quaternion(q.0), - crate::Rotation3D::AxisAngle(a) => Self::AxisAngle(a.0), - } - } -} diff --git a/crates/store/re_types/src/datatypes/rotation_axis_angle_ext.rs b/crates/store/re_types/src/datatypes/rotation_axis_angle_ext.rs index 8a7a7b40f2cd..27ca8e3057c2 100644 --- a/crates/store/re_types/src/datatypes/rotation_axis_angle_ext.rs +++ b/crates/store/re_types/src/datatypes/rotation_axis_angle_ext.rs @@ -3,6 +3,12 @@ use super::RotationAxisAngle; use crate::datatypes::{Angle, Vec3D}; impl RotationAxisAngle { + /// The identity rotation, representing no rotation. + pub const IDENTITY: Self = Self { + axis: Vec3D([1.0, 0.0, 0.0]), // Might as well use a zero vector here, but putting in the X axis is less error prone. + angle: Angle::ZERO, + }; + /// Create a new rotation from an axis and an angle. #[inline] pub fn new(axis: impl Into, angle: impl Into) -> Self { @@ -42,9 +48,6 @@ impl From for mint::Quaternion { impl Default for RotationAxisAngle { #[inline] fn default() -> Self { - Self { - axis: Vec3D::new(1.0, 0.0, 0.0), - angle: Angle::from_radians(0.0), - } + Self::IDENTITY } } diff --git a/crates/store/re_types/tests/box3d.rs b/crates/store/re_types/tests/box3d.rs index fe4d431dc89a..e810151b0632 100644 --- a/crates/store/re_types/tests/box3d.rs +++ b/crates/store/re_types/tests/box3d.rs @@ -10,16 +10,17 @@ fn roundtrip() { components::HalfSize3D::new(4.0, 5.0, 6.0), ], centers: Some(vec![ - components::Position3D::new(1.0, 2.0, 3.0), // - components::Position3D::new(4.0, 5.0, 6.0), + components::LeafTranslation3D::new(1.0, 2.0, 3.0), // + components::LeafTranslation3D::new(4.0, 5.0, 6.0), ]), - rotations: Some(vec![ - components::Rotation3D::from(datatypes::Quaternion::from_xyzw([1.0, 2.0, 3.0, 4.0])), - components::Rotation3D::from(datatypes::RotationAxisAngle::new( - [1.0, 2.0, 3.0], - datatypes::Angle::from_radians(4.0), - )), + quaternions: Some(vec![ + datatypes::Quaternion::from_xyzw([1.0, 2.0, 3.0, 4.0]).into() ]), + rotation_axis_angles: Some(vec![datatypes::RotationAxisAngle::new( + [1.0, 2.0, 3.0], + datatypes::Angle::from_radians(4.0), + ) + .into()]), colors: Some(vec![ components::Color::from_unmultiplied_rgba(0xAA, 0x00, 0x00, 0xCC), // components::Color::from_unmultiplied_rgba(0x00, 0xBB, 0x00, 0xDD), @@ -41,13 +42,11 @@ fn roundtrip() { let arch = Boxes3D::from_half_sizes([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]) .with_centers([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]) - .with_rotations([ - components::Rotation3D::from(datatypes::Quaternion::from_xyzw([1.0, 2.0, 3.0, 4.0])), - components::Rotation3D::from(datatypes::RotationAxisAngle::new( - [1.0, 2.0, 3.0], - datatypes::Angle::from_radians(4.0), - )), - ]) + .with_quaternions([datatypes::Quaternion::from_xyzw([1.0, 2.0, 3.0, 4.0])]) + .with_rotation_axis_angles([datatypes::RotationAxisAngle::new( + [1.0, 2.0, 3.0], + datatypes::Angle::from_radians(4.0), + )]) .with_colors([0xAA0000CC, 0x00BB00DD]) .with_radii([42.0, 43.0]) .with_fill_mode(components::FillMode::Solid) 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 c7d5f3610527..438bbb93b444 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 @@ -95,6 +95,15 @@ impl TransformInfo { pub fn single_entity_transform_silent(&self) -> glam::Affine3A { *self.reference_from_instances.first() } + + /// Returns reference from instance transforms, repeating the last value indefinitely. + #[inline] + pub fn clamped_reference_from_instances(&self) -> impl Iterator + '_ { + self.reference_from_instances + .iter() + .chain(std::iter::repeat(self.reference_from_instances.last())) + .copied() + } } #[derive(Clone, Copy)] 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 295410444b50..2da0827840c8 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs @@ -5,9 +5,7 @@ use re_renderer::{ }; use re_types::{ archetypes::Boxes3D, - components::{ - ClassId, Color, FillMode, HalfSize3D, KeypointId, Position3D, Radius, Rotation3D, Text, - }, + components::{ClassId, Color, FillMode, HalfSize3D, KeypointId, Radius, Text}, }; use re_viewer_context::{ auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, @@ -23,9 +21,9 @@ use crate::{ }; use super::{ - entity_iterator::clamped_or, filter_visualizable_3d_entities, - process_annotation_and_keypoint_slices, process_color_slice, process_labels_3d, - process_radius_slice, SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, + filter_visualizable_3d_entities, process_annotation_and_keypoint_slices, process_color_slice, + process_labels_3d, process_radius_slice, SpatialViewVisualizerData, + SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, }; // --- @@ -78,47 +76,36 @@ 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(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); - let mut obj_space_bounding_box = re_math::BoundingBox::NOTHING; + let mut world_space_bounding_box = re_math::BoundingBox::NOTHING; + + let world_from_instances = ent_context + .transform_info + .clamped_reference_from_instances(); - let centers = clamped_or(data.centers, &Position3D::ZERO); - let rotations = clamped_or(data.rotations, &Rotation3D::IDENTITY); - for (instance_index, (half_size, ¢er, rotation, radius, &color)) in - itertools::izip!(data.half_sizes, centers, rotations, radii, &colors).enumerate() + for (instance_index, (half_size, world_from_instance, radius, &color)) in + itertools::izip!(data.half_sizes, world_from_instances, radii, &colors).enumerate() { let instance = Instance::from(instance_index as u64); - // Transform from a centered unit cube to this box in the entity's - // coordinate system. - let entity_from_mesh = glam::Affine3A::from_scale_rotation_translation( - glam::Vec3::from(*half_size) * 2.0, - rotation.0.into(), - center.into(), - ); - let proc_mesh_key = proc_mesh::ProcMeshKey::Cube; - obj_space_bounding_box = obj_space_bounding_box.union( - // We must perform this transform to fully account for the per-instance - // transform, which is separate from the entity's transform. + let world_from_instance = world_from_instance + * glam::Affine3A::from_scale(glam::Vec3::from(*half_size) * 2.0); + world_space_bounding_box = world_space_bounding_box.union( proc_mesh_key .simple_bounding_box() - .transform_affine3(&entity_from_mesh), + .transform_affine3(&world_from_instance), ); match data.fill_mode { FillMode::Wireframe => { let box3d = line_batch - .add_box_outline_from_transform(entity_from_mesh) + .add_box_outline_from_transform(world_from_instance) .color(color) .radius(radius) .picking_instance_id(PickingLayerInstanceId(instance_index as _)); @@ -143,7 +130,7 @@ impl Boxes3DVisualizer { mesh_instances.push(MeshInstance { gpu_mesh: solid_mesh.gpu_mesh, mesh: None, - world_from_mesh: entity_from_mesh, + world_from_mesh: world_from_instance, outline_mask_ids: ent_context.highlight.index_outline_mask(instance), picking_layer_id: picking_layer_id_from_instance_path_hash( InstancePathHash::instance(entity_path, instance), @@ -155,17 +142,21 @@ impl Boxes3DVisualizer { } self.0 - .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); + .bounding_boxes + .push((entity_path.hash(), world_space_bounding_box)); 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. let label_positions = if data.labels.len() == 1 && num_instances > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - itertools::Either::Left(std::iter::once(obj_space_bounding_box.center())) + itertools::Either::Left(std::iter::once(world_space_bounding_box.center())) } else { // Take center point of every box. itertools::Either::Right( - clamped_or(data.centers, &Position3D::ZERO).map(|&c| c.into()), + ent_context + .transform_info + .clamped_reference_from_instances() + .map(|t| t.translation.into()), ) }; @@ -175,7 +166,7 @@ impl Boxes3DVisualizer { data.labels, &colors, &annotation_infos, - world_from_obj, + glam::Affine3A::IDENTITY, )); } } @@ -191,8 +182,6 @@ struct Boxes3DComponentData<'a> { half_sizes: &'a [HalfSize3D], // Clamped to edge - centers: &'a [Position3D], - rotations: &'a [Rotation3D], colors: &'a [Color], radii: &'a [Radius], labels: &'a [Text], @@ -265,8 +254,6 @@ impl VisualizerSystem for Boxes3DVisualizer { return Ok(()); } - let centers = results.get_or_empty_dense(resolver)?; - let rotations = results.get_or_empty_dense(resolver)?; let colors = results.get_or_empty_dense(resolver)?; let fill_mode = results.get_or_empty_dense(resolver)?; let radii = results.get_or_empty_dense(resolver)?; @@ -292,10 +279,8 @@ impl VisualizerSystem for Boxes3DVisualizer { } } - let data = re_query::range_zip_1x7( + let data = re_query::range_zip_1x5( half_sizes.range_indexed(), - centers.range_indexed(), - rotations.range_indexed(), colors.range_indexed(), radii.range_indexed(), labels.range_indexed(), @@ -303,21 +288,9 @@ impl VisualizerSystem for Boxes3DVisualizer { keypoint_ids.range_indexed(), ) .map( - |( - _index, - half_sizes, - centers, - rotations, - colors, - radii, - labels, - class_ids, - keypoint_ids, - )| { + |(_index, half_sizes, colors, radii, labels, class_ids, keypoint_ids)| { Boxes3DComponentData { half_sizes, - centers: centers.unwrap_or_default(), - rotations: rotations.unwrap_or_default(), colors: colors.unwrap_or_default(), radii: radii.unwrap_or_default(), // fill mode is currently a non-repeated component 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 fbc853a1396d..8bfaeff79b03 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs @@ -1,33 +1,29 @@ -use re_entity_db::{EntityPath, InstancePathHash}; +use re_entity_db::InstancePathHash; use re_log_types::Instance; use re_renderer::{ renderer::MeshInstance, LineDrawableBuilder, PickingLayerInstanceId, RenderContext, }; use re_types::{ archetypes::Ellipsoids, - components::{ - ClassId, Color, FillMode, HalfSize3D, KeypointId, Position3D, Radius, Rotation3D, Text, - }, + components::{ClassId, Color, FillMode, HalfSize3D, KeypointId, Radius, Text}, }; use re_viewer_context::{ auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, - ResolvedAnnotationInfos, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, - ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, + ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ contexts::SpatialSceneEntityContext, - instance_hash_conversions::picking_layer_id_from_instance_path_hash, - proc_mesh, + instance_hash_conversions::picking_layer_id_from_instance_path_hash, proc_mesh, view_kind::SpatialSpaceViewKind, - visualizers::{UiLabel, UiLabelTarget}, }; use super::{ - entity_iterator::clamped_or, filter_visualizable_3d_entities, - process_annotation_and_keypoint_slices, process_color_slice, process_radius_slice, - SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, + filter_visualizable_3d_entities, process_annotation_and_keypoint_slices, process_color_slice, + process_labels_3d, process_radius_slice, SpatialViewVisualizerData, + SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, }; // --- @@ -45,36 +41,6 @@ impl Default for EllipsoidsVisualizer { // NOTE: Do not put profile scopes in these methods. They are called for all entities and all // timestamps within a time range -- it's _a lot_. impl EllipsoidsVisualizer { - fn process_labels<'a>( - entity_path: &'a EntityPath, - centers: &'a [Position3D], - labels: &'a [Text], - colors: &'a [egui::Color32], - annotation_infos: &'a ResolvedAnnotationInfos, - world_from_entity: glam::Affine3A, - ) -> impl Iterator + 'a { - let labels = annotation_infos - .iter() - .zip(labels.iter().map(Some).chain(std::iter::repeat(None))) - .map(|(annotation_info, label)| annotation_info.label(label.map(|l| l.as_str()))); - - itertools::izip!(centers, labels, colors) - .enumerate() - .filter_map(move |(i, (center, label, color))| { - label.map(|label| UiLabel { - text: label, - color: *color, - target: UiLabelTarget::Position3D( - world_from_entity.transform_point3(center.0.into()), - ), - labeled_instance: InstancePathHash::instance( - entity_path, - Instance::from(i as u64), - ), - }) - }) - } - #[allow(clippy::too_many_arguments)] fn process_data<'a>( &mut self, @@ -114,48 +80,37 @@ impl EllipsoidsVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - 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, - world_from_obj, - )); - let mut line_batch = line_builder .batch("ellipsoids") .depth_offset(ent_context.depth_offset) - .world_from_obj(world_from_obj) .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); - let mut obj_space_bounding_box = re_math::BoundingBox::NOTHING; + let mut world_space_bounding_box = re_math::BoundingBox::NOTHING; + + let world_from_instances = ent_context + .transform_info + .clamped_reference_from_instances(); - for (instance_index, (half_size, ¢er, rotation, radius, color)) in - itertools::izip!(data.half_sizes, centers, rotations, radii, colors).enumerate() + for (instance_index, (half_size, world_from_instance, radius, &color)) in + itertools::izip!(data.half_sizes, world_from_instances, radii, &colors).enumerate() { let instance = Instance::from(instance_index as u64); - // Transform from a centered unit sphere to this ellipsoid in the entity's - // coordinate system. - let entity_from_mesh = glam::Affine3A::from_scale_rotation_translation( - glam::Vec3::from(*half_size), - rotation.0.into(), - center.into(), - ); // TODO(kpreid): subdivisions should be configurable, and possibly dynamic based on // either world size or screen size (depending on application). let subdivisions = 2; let proc_mesh_key = proc_mesh::ProcMeshKey::Sphere { subdivisions }; + // No need to take half_size times 2 since the mesh we're using is already scaled accordingly. + let world_from_instance = + world_from_instance * glam::Affine3A::from_scale(glam::Vec3::from(*half_size)); + world_space_bounding_box = world_space_bounding_box.union( + proc_mesh_key + .simple_bounding_box() + .transform_affine3(&world_from_instance), + ); + match data.fill_mode { FillMode::Wireframe => { let Some(wireframe_mesh) = @@ -170,15 +125,12 @@ impl EllipsoidsVisualizer { )); }; - obj_space_bounding_box = obj_space_bounding_box - .union(wireframe_mesh.bbox.transform_affine3(&entity_from_mesh)); - for strip in &wireframe_mesh.line_strips { let strip_builder = line_batch .add_strip( strip .iter() - .map(|&point| entity_from_mesh.transform_point3(point)), + .map(|&point| world_from_instance.transform_point3(point)), ) .color(color) .radius(radius) @@ -206,13 +158,10 @@ impl EllipsoidsVisualizer { )); }; - obj_space_bounding_box = obj_space_bounding_box - .union(solid_mesh.bbox.transform_affine3(&entity_from_mesh)); - mesh_instances.push(MeshInstance { gpu_mesh: solid_mesh.gpu_mesh, mesh: None, - world_from_mesh: entity_from_mesh, + world_from_mesh: world_from_instance, outline_mask_ids: ent_context.highlight.index_outline_mask(instance), picking_layer_id: picking_layer_id_from_instance_path_hash( InstancePathHash::instance(entity_path, instance), @@ -224,7 +173,33 @@ impl EllipsoidsVisualizer { } self.0 - .add_bounding_box(entity_path.hash(), obj_space_bounding_box, world_from_obj); + .bounding_boxes + .push((entity_path.hash(), world_space_bounding_box)); + + 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. + let label_positions = if data.labels.len() == 1 && num_instances > 1 { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + itertools::Either::Left(std::iter::once(world_space_bounding_box.center())) + } else { + // Take center point of every box. + itertools::Either::Right( + ent_context + .transform_info + .clamped_reference_from_instances() + .map(|t| t.translation.into()), + ) + }; + + self.0.ui_labels.extend(process_labels_3d( + entity_path, + label_positions, + data.labels, + &colors, + &annotation_infos, + glam::Affine3A::IDENTITY, + )); + } } Ok(()) @@ -238,8 +213,6 @@ struct EllipsoidsComponentData<'a> { half_sizes: &'a [HalfSize3D], // Clamped to edge - centers: &'a [Position3D], - rotations: &'a [Rotation3D], colors: &'a [Color], line_radii: &'a [Radius], labels: &'a [Text], @@ -315,8 +288,6 @@ impl VisualizerSystem for EllipsoidsVisualizer { // line_builder.reserve_strips(num_ellipsoids * sphere_mesh.line_strips.len())?; // line_builder.reserve_vertices(num_ellipsoids * sphere_mesh.vertex_count)?; - let centers = results.get_or_empty_dense(resolver)?; - let rotations = results.get_or_empty_dense(resolver)?; let colors = results.get_or_empty_dense(resolver)?; let line_radii = results.get_or_empty_dense(resolver)?; let fill_mode = results.get_or_empty_dense(resolver)?; @@ -324,10 +295,8 @@ impl VisualizerSystem for EllipsoidsVisualizer { let class_ids = results.get_or_empty_dense(resolver)?; let keypoint_ids = results.get_or_empty_dense(resolver)?; - let data = re_query::range_zip_1x8( + let data = re_query::range_zip_1x6( half_sizes.range_indexed(), - centers.range_indexed(), - rotations.range_indexed(), colors.range_indexed(), line_radii.range_indexed(), fill_mode.range_indexed(), @@ -339,8 +308,6 @@ impl VisualizerSystem for EllipsoidsVisualizer { |( _index, half_sizes, - centers, - rotations, colors, line_radii, fill_mode, @@ -350,8 +317,6 @@ impl VisualizerSystem for EllipsoidsVisualizer { )| { EllipsoidsComponentData { half_sizes, - centers: centers.unwrap_or_default(), - rotations: rotations.unwrap_or_default(), colors: colors.unwrap_or_default(), line_radii: line_radii.unwrap_or_default(), // fill mode is currently a non-repeated component diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index 2335020c5e1f..a89756395cc7 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -679,7 +679,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, 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
", + 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, many 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/migration/migration-0-18.md b/docs/content/reference/migration/migration-0-18.md index eee404ea5e77..e25f6b3d2486 100644 --- a/docs/content/reference/migration/migration-0-18.md +++ b/docs/content/reference/migration/migration-0-18.md @@ -210,3 +210,12 @@ After: rec.log("world/mesh", &rerun::Asset3D::from_file(path)?)?; rec.log("world/mesh", &rerun::LeafTransform3D::default().with_translations([translation]))?; ``` + +### [`Boxes3D`](https://rerun.io/docs/reference/types/archetypes/boxes3d) changes + +`centers` is now a [`LeafTranslation3D`](https://rerun.io/docs/reference/types/components/leaf_translation3d#speculative-link) instead of a [`Position3D`](https://rerun.io/docs/reference/types/components/position3d) component. +The main difference in behavior is that this means it overlaps with the newly introduced [`LeafTransforms3D`](https://rerun.io/docs/reference/types/archetypes/leaf_transform3d#speculative-link) archetype. + +`rotation` was removed in favor of `rotation_axis_angles` and `quaternions` which are +[`LeafRotationAxisAngle`](https://rerun.io/docs/reference/types/components/leaf_rotation_axis_angle#speculative-link) and `LeafRotationQuat`(https://rerun.io/docs/reference/types/components/leaf_rotation_quat#speculative-link) components. +Consequently, instead of using `with_rotations` (C++/Rust) or `rotation=` (Python) you'll need to use `with_quaternions`/`quaternions=` or `with_rotation_axis_angles`/`rotation_axis_angles=` respectively. diff --git a/docs/content/reference/types/archetypes/boxes3d.md b/docs/content/reference/types/archetypes/boxes3d.md index 344704145f9e..1a9fb68ad9d0 100644 --- a/docs/content/reference/types/archetypes/boxes3d.md +++ b/docs/content/reference/types/archetypes/boxes3d.md @@ -5,13 +5,16 @@ title: "Boxes3D" 3D boxes with half-extents and optional center, rotations, colors etc. +Note that orienting and placing the box is handled via `[archetypes.LeafTransforms3D]`. +Some of its component are repeated here for convenience. + ## Components **Required**: [`HalfSize3D`](../components/half_size3d.md) -**Recommended**: [`Position3D`](../components/position3d.md), [`Rotation3D`](../components/rotation3d.md), [`Color`](../components/color.md) +**Recommended**: [`LeafTranslation3D`](../components/leaf_translation3d.md), [`Color`](../components/color.md) -**Optional**: [`Radius`](../components/radius.md), [`FillMode`](../components/fill_mode.md), [`Text`](../components/text.md), [`ClassId`](../components/class_id.md) +**Optional**: [`LeafRotationAxisAngle`](../components/leaf_rotation_axis_angle.md), [`LeafRotationQuat`](../components/leaf_rotation_quat.md), [`Radius`](../components/radius.md), [`FillMode`](../components/fill_mode.md), [`Text`](../components/text.md), [`ClassId`](../components/class_id.md) ## Shown in * [Spatial3DView](../views/spatial3d_view.md) diff --git a/docs/content/reference/types/archetypes/ellipsoids.md b/docs/content/reference/types/archetypes/ellipsoids.md index 196cf8a60b62..a61f2bc858e1 100644 --- a/docs/content/reference/types/archetypes/ellipsoids.md +++ b/docs/content/reference/types/archetypes/ellipsoids.md @@ -9,16 +9,16 @@ This archetype is for ellipsoids or spheres whose size is a key part of the data (e.g. a bounding sphere). For points whose radii are for the sake of visualization, use `Points3D` instead. -Currently, ellipsoids are always rendered as wireframes. -Opaque and transparent rendering will be supported later. +Note that orienting and placing the ellipsoids/spheres is handled via `[archetypes.LeafTransforms3D]`. +Some of its component are repeated here for convenience. ## Components **Required**: [`HalfSize3D`](../components/half_size3d.md) -**Recommended**: [`Position3D`](../components/position3d.md), [`Rotation3D`](../components/rotation3d.md), [`Color`](../components/color.md) +**Recommended**: [`LeafTranslation3D`](../components/leaf_translation3d.md), [`Color`](../components/color.md) -**Optional**: [`Radius`](../components/radius.md), [`FillMode`](../components/fill_mode.md), [`Text`](../components/text.md), [`ClassId`](../components/class_id.md) +**Optional**: [`LeafRotationAxisAngle`](../components/leaf_rotation_axis_angle.md), [`LeafRotationQuat`](../components/leaf_rotation_quat.md), [`Radius`](../components/radius.md), [`FillMode`](../components/fill_mode.md), [`Text`](../components/text.md), [`ClassId`](../components/class_id.md) ## Shown in * [Spatial3DView](../views/spatial3d_view.md) diff --git a/docs/content/reference/types/archetypes/leaf_transforms3d.md b/docs/content/reference/types/archetypes/leaf_transforms3d.md index 6bd8c93acd7e..d576a760511e 100644 --- a/docs/content/reference/types/archetypes/leaf_transforms3d.md +++ b/docs/content/reference/types/archetypes/leaf_transforms3d.md @@ -10,7 +10,7 @@ For transforms that are propagated in the transform hierarchy, see [`archetypes. 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. +Currently, many visualizers support only a single leaf transform per entity. 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, diff --git a/docs/content/reference/types/components/leaf_rotation_axis_angle.md b/docs/content/reference/types/components/leaf_rotation_axis_angle.md index 887479537466..58548141623c 100644 --- a/docs/content/reference/types/components/leaf_rotation_axis_angle.md +++ b/docs/content/reference/types/components/leaf_rotation_axis_angle.md @@ -17,4 +17,6 @@ title: "LeafRotationAxisAngle" ## Used by +* [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md?speculative-link) * [`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 index f514ed44518c..3566c77ddae5 100644 --- a/docs/content/reference/types/components/leaf_rotation_quat.md +++ b/docs/content/reference/types/components/leaf_rotation_quat.md @@ -20,4 +20,6 @@ datastore as provided, when used in the Viewer, quaternions will always be norma ## Used by +* [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md?speculative-link) * [`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 index 83896d17052f..262696323cd4 100644 --- a/docs/content/reference/types/components/leaf_translation3d.md +++ b/docs/content/reference/types/components/leaf_translation3d.md @@ -17,4 +17,6 @@ A translation vector in 3D space that doesn't propagate in the transform hierarc ## Used by +* [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md?speculative-link) * [`LeafTransforms3D`](../archetypes/leaf_transforms3d.md) diff --git a/docs/content/reference/types/components/position3d.md b/docs/content/reference/types/components/position3d.md index ea58c1fcaede..8ea86bf73f2a 100644 --- a/docs/content/reference/types/components/position3d.md +++ b/docs/content/reference/types/components/position3d.md @@ -18,7 +18,5 @@ A position in 3D space. ## Used by * [`Arrows3D`](../archetypes/arrows3d.md) -* [`Boxes3D`](../archetypes/boxes3d.md) -* [`Ellipsoids`](../archetypes/ellipsoids.md?speculative-link) * [`Mesh3D`](../archetypes/mesh3d.md) * [`Points3D`](../archetypes/points3d.md) diff --git a/docs/content/reference/types/components/rotation3d.md b/docs/content/reference/types/components/rotation3d.md index e2f33d22a352..19443a215bc1 100644 --- a/docs/content/reference/types/components/rotation3d.md +++ b/docs/content/reference/types/components/rotation3d.md @@ -15,7 +15,3 @@ A 3D rotation, represented either by a quaternion or a rotation around axis. * 🦀 [Rust API docs for `Rotation3D`](https://docs.rs/rerun/latest/rerun/components/struct.Rotation3D.html) -## Used by - -* [`Boxes3D`](../archetypes/boxes3d.md) -* [`Ellipsoids`](../archetypes/ellipsoids.md?speculative-link) diff --git a/docs/snippets/all/archetypes/box3d_batch.cpp b/docs/snippets/all/archetypes/box3d_batch.cpp index f54144ea472a..a904699a379b 100644 --- a/docs/snippets/all/archetypes/box3d_batch.cpp +++ b/docs/snippets/all/archetypes/box3d_batch.cpp @@ -12,11 +12,10 @@ int main() { {{2.0f, 0.0f, 0.0f}, {-2.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 2.0f}}, {{2.0f, 2.0f, 1.0f}, {1.0f, 1.0f, 0.5f}, {2.0f, 0.5f, 1.0f}} ) - .with_rotations({ + .with_quaternions({ rerun::Quaternion::IDENTITY, // 45 degrees around Z rerun::Quaternion::from_xyzw(0.0f, 0.0f, 0.382683f, 0.923880f), - rerun::RotationAxisAngle({0.0f, 1.0f, 0.0f}, rerun::Angle::degrees(30.0f)), }) .with_radii({0.025f}) .with_colors({ diff --git a/docs/snippets/all/archetypes/box3d_batch.py b/docs/snippets/all/archetypes/box3d_batch.py index ae6964e5a59c..1b4e8d85ba7c 100644 --- a/docs/snippets/all/archetypes/box3d_batch.py +++ b/docs/snippets/all/archetypes/box3d_batch.py @@ -1,7 +1,6 @@ """Log a batch of oriented bounding boxes.""" import rerun as rr -from rerun.datatypes import Angle, Quaternion, Rotation3D, RotationAxisAngle rr.init("rerun_example_box3d_batch", spawn=True) @@ -10,10 +9,9 @@ rr.Boxes3D( centers=[[2, 0, 0], [-2, 0, 0], [0, 0, 2]], half_sizes=[[2.0, 2.0, 1.0], [1.0, 1.0, 0.5], [2.0, 0.5, 1.0]], - rotations=[ - Rotation3D.identity(), - Quaternion(xyzw=[0.0, 0.0, 0.382683, 0.923880]), # 45 degrees around Z - RotationAxisAngle(axis=[0, 1, 0], angle=Angle(deg=30)), + quaternions=[ + rr.Quaternion.identity(), + rr.Quaternion(xyzw=[0.0, 0.0, 0.382683, 0.923880]), # 45 degrees around Z ], radii=0.025, colors=[(255, 0, 0), (0, 255, 0), (0, 0, 255)], diff --git a/docs/snippets/all/archetypes/box3d_batch.rs b/docs/snippets/all/archetypes/box3d_batch.rs index 54c6e4e07dda..554e23a3cc1e 100644 --- a/docs/snippets/all/archetypes/box3d_batch.rs +++ b/docs/snippets/all/archetypes/box3d_batch.rs @@ -9,10 +9,9 @@ fn main() -> Result<(), Box> { [(2.0, 0.0, 0.0), (-2.0, 0.0, 0.0), (0.0, 0.0, 2.0)], [(2.0, 2.0, 1.0), (1.0, 1.0, 0.5), (2.0, 0.5, 1.0)], ) - .with_rotations([ - rerun::Rotation3D::IDENTITY, - rerun::Quaternion::from_xyzw([0.0, 0.0, 0.382683, 0.923880]).into(), // 45 degrees around Z - rerun::RotationAxisAngle::new((0.0, 1.0, 0.0), rerun::Angle::from_degrees(30.0)).into(), + .with_quaternions([ + rerun::Quaternion::IDENTITY, + rerun::Quaternion::from_xyzw([0.0, 0.0, 0.382683, 0.923880]), // 45 degrees around Z ]) .with_radii([0.025]) .with_colors([ diff --git a/examples/python/arkit_scenes/README.md b/examples/python/arkit_scenes/README.md index a948185d0d42..195b7cc1e8fe 100644 --- a/examples/python/arkit_scenes/README.md +++ b/examples/python/arkit_scenes/README.md @@ -66,10 +66,10 @@ for i, label_info in enumerate(annotation["data"]): rr.Boxes3D( half_sizes=half_size, centers=centroid, - rotations=rr.Quaternion(xyzw=rot.as_quat()), labels=label, colors=colors[i], ), + rr.LeafTransforms3D(mat3x3=mat3x3), timeless=True, ) ``` diff --git a/examples/python/arkit_scenes/arkit_scenes/__main__.py b/examples/python/arkit_scenes/arkit_scenes/__main__.py index d6940e959d62..dc458b3b6cdc 100755 --- a/examples/python/arkit_scenes/arkit_scenes/__main__.py +++ b/examples/python/arkit_scenes/arkit_scenes/__main__.py @@ -67,18 +67,16 @@ def log_annotated_bboxes(annotation: dict[str, Any]) -> None: half_size = 0.5 * np.array(label_info["segments"]["obbAligned"]["axesLengths"]).reshape(-1, 3)[0] centroid = np.array(label_info["segments"]["obbAligned"]["centroid"]).reshape(-1, 3)[0] - rotation = np.array(label_info["segments"]["obbAligned"]["normalizedAxes"]).reshape(3, 3) - - rot = R.from_matrix(rotation).inv() + mat3x3 = np.array(label_info["segments"]["obbAligned"]["normalizedAxes"]).reshape(3, 3) rr.log( f"world/annotations/box-{uid}-{label}", rr.Boxes3D( half_sizes=half_size, centers=centroid, - rotations=rr.Quaternion(xyzw=rot.as_quat()), labels=label, ), + rr.LeafTransforms3D(mat3x3=mat3x3), static=True, ) diff --git a/examples/python/nuscenes_dataset/README.md b/examples/python/nuscenes_dataset/README.md index 943a9ae7bed9..1daf1a1a56f8 100644 --- a/examples/python/nuscenes_dataset/README.md +++ b/examples/python/nuscenes_dataset/README.md @@ -110,7 +110,7 @@ rr.log( rr.Boxes3D( sizes=sizes, centers=centers, - rotations=rotations, + quaternions=quaternions, class_ids=class_ids, fill_mode=rr.components.FillMode.Solid, ), diff --git a/examples/python/nuscenes_dataset/nuscenes_dataset/__main__.py b/examples/python/nuscenes_dataset/nuscenes_dataset/__main__.py index 42f0a5e4637a..97b025d52fd0 100755 --- a/examples/python/nuscenes_dataset/nuscenes_dataset/__main__.py +++ b/examples/python/nuscenes_dataset/nuscenes_dataset/__main__.py @@ -193,7 +193,7 @@ def log_annotations(first_sample_token: str, nusc: nuscenes.NuScenes, max_timest ann_tokens = sample_data["anns"] sizes = [] centers = [] - rotations = [] + quaternions = [] class_ids = [] for ann_token in ann_tokens: ann = nusc.get("sample_annotation", ann_token) @@ -202,7 +202,7 @@ def log_annotations(first_sample_token: str, nusc: nuscenes.NuScenes, max_timest width, length, height = ann["size"] sizes.append((length, width, height)) # x, y, z sizes centers.append(ann["translation"]) - rotations.append(rr.Quaternion(xyzw=rotation_xyzw)) + quaternions.append(rr.Quaternion(xyzw=rotation_xyzw)) if ann["category_name"] not in label2id: label2id[ann["category_name"]] = len(label2id) class_ids.append(label2id[ann["category_name"]]) @@ -212,7 +212,7 @@ def log_annotations(first_sample_token: str, nusc: nuscenes.NuScenes, max_timest rr.Boxes3D( sizes=sizes, centers=centers, - rotations=rotations, + quaternions=quaternions, class_ids=class_ids, fill_mode=rr.components.FillMode.Solid, ), diff --git a/rerun_cpp/src/rerun/archetypes/boxes3d.cpp b/rerun_cpp/src/rerun/archetypes/boxes3d.cpp index 18d0e9695954..0fad25a8511d 100644 --- a/rerun_cpp/src/rerun/archetypes/boxes3d.cpp +++ b/rerun_cpp/src/rerun/archetypes/boxes3d.cpp @@ -14,7 +14,7 @@ namespace rerun { ) { using namespace archetypes; std::vector cells; - cells.reserve(9); + cells.reserve(10); { auto result = DataCell::from_loggable(archetype.half_sizes); @@ -26,8 +26,13 @@ namespace rerun { RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } - if (archetype.rotations.has_value()) { - auto result = DataCell::from_loggable(archetype.rotations.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.quaternions.has_value()) { + auto result = DataCell::from_loggable(archetype.quaternions.value()); RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } diff --git a/rerun_cpp/src/rerun/archetypes/boxes3d.hpp b/rerun_cpp/src/rerun/archetypes/boxes3d.hpp index 0a40990d5a74..ece33c988a70 100644 --- a/rerun_cpp/src/rerun/archetypes/boxes3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/boxes3d.hpp @@ -9,9 +9,10 @@ #include "../components/color.hpp" #include "../components/fill_mode.hpp" #include "../components/half_size3d.hpp" -#include "../components/position3d.hpp" +#include "../components/leaf_rotation_axis_angle.hpp" +#include "../components/leaf_rotation_quat.hpp" +#include "../components/leaf_translation3d.hpp" #include "../components/radius.hpp" -#include "../components/rotation3d.hpp" #include "../components/text.hpp" #include "../data_cell.hpp" #include "../indicator_component.hpp" @@ -25,6 +26,9 @@ namespace rerun::archetypes { /// **Archetype**: 3D boxes with half-extents and optional center, rotations, colors etc. /// + /// Note that orienting and placing the box is handled via `[archetypes.LeafTransforms3D]`. + /// Some of its component are repeated here for convenience. + /// /// ## Example /// /// ### Batch of 3D boxes @@ -43,11 +47,10 @@ namespace rerun::archetypes { /// {{2.0f, 0.0f, 0.0f}, {-2.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 2.0f}}, /// {{2.0f, 2.0f, 1.0f}, {1.0f, 1.0f, 0.5f}, {2.0f, 0.5f, 1.0f}} /// ) - /// .with_rotations({ + /// .with_quaternions({ /// rerun::Quaternion::IDENTITY, /// // 45 degrees around Z /// rerun::Quaternion::from_xyzw(0.0f, 0.0f, 0.382683f, 0.923880f), - /// rerun::RotationAxisAngle({0.0f, 1.0f, 0.0f}, rerun::Angle::degrees(30.0f)), /// }) /// .with_radii({0.025f}) /// .with_colors({ @@ -65,10 +68,22 @@ namespace rerun::archetypes { Collection half_sizes; /// Optional center positions of the boxes. - std::optional> centers; + /// + /// If not specified, the centers will be at (0, 0, 0). + /// Note that this uses a `components::LeafTranslation3D` which is also used by `archetypes::LeafTransforms3D`. + std::optional> centers; + + /// Rotations via axis + angle. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a `components::LeafRotationAxisAngle` which is also used by `archetypes::LeafTransforms3D`. + std::optional> rotation_axis_angles; - /// Optional rotations of the boxes. - std::optional> rotations; + /// Rotations via quaternion. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a `components::LeafRotationQuat` which is also used by `archetypes::LeafTransforms3D`. + std::optional> quaternions; /// Optional colors for the boxes. std::optional> colors; @@ -108,7 +123,7 @@ namespace rerun::archetypes { /// Creates new `Boxes3D` with `centers` and `half_sizes`. static Boxes3D from_centers_and_half_sizes( - Collection centers, + Collection centers, Collection half_sizes ) { Boxes3D boxes; @@ -131,7 +146,8 @@ namespace rerun::archetypes { /// from the input data. /// TODO(andreas): This should not take an std::vector. static Boxes3D from_centers_and_sizes( - Collection centers, const std::vector& sizes + Collection centers, + const std::vector& sizes ) { Boxes3D boxes = from_sizes(std::move(sizes)); boxes.centers = std::move(centers); @@ -153,15 +169,33 @@ namespace rerun::archetypes { Boxes3D(Boxes3D&& other) = default; /// Optional center positions of the boxes. - Boxes3D with_centers(Collection _centers) && { + /// + /// If not specified, the centers will be at (0, 0, 0). + /// Note that this uses a `components::LeafTranslation3D` which is also used by `archetypes::LeafTransforms3D`. + Boxes3D with_centers(Collection _centers) && { centers = std::move(_centers); // See: https://github.com/rerun-io/rerun/issues/4027 RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } - /// Optional rotations of the boxes. - Boxes3D with_rotations(Collection _rotations) && { - rotations = std::move(_rotations); + /// Rotations via axis + angle. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a `components::LeafRotationAxisAngle` which is also used by `archetypes::LeafTransforms3D`. + Boxes3D with_rotation_axis_angles( + Collection _rotation_axis_angles + ) && { + 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. + /// + /// If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + /// Note that this uses a `components::LeafRotationQuat` which is also used by `archetypes::LeafTransforms3D`. + Boxes3D with_quaternions(Collection _quaternions) && { + quaternions = std::move(_quaternions); // See: https://github.com/rerun-io/rerun/issues/4027 RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } diff --git a/rerun_cpp/src/rerun/archetypes/boxes3d_ext.cpp b/rerun_cpp/src/rerun/archetypes/boxes3d_ext.cpp index 6cdf8bb5ffb5..c368807dd084 100644 --- a/rerun_cpp/src/rerun/archetypes/boxes3d_ext.cpp +++ b/rerun_cpp/src/rerun/archetypes/boxes3d_ext.cpp @@ -19,7 +19,7 @@ namespace rerun { /// Creates new `Boxes3D` with `centers` and `half_sizes`. static Boxes3D from_centers_and_half_sizes( - Collection centers, + Collection centers, Collection half_sizes ) { Boxes3D boxes; @@ -42,7 +42,8 @@ namespace rerun { /// from the input data. /// TODO(andreas): This should not take an std::vector. static Boxes3D from_centers_and_sizes( - Collection centers, const std::vector& sizes + Collection centers, + const std::vector& sizes ) { Boxes3D boxes = from_sizes(std::move(sizes)); boxes.centers = std::move(centers); @@ -78,7 +79,7 @@ namespace rerun { auto num_components = std::min(mins.size(), sizes.size()); std::vector half_sizes; - std::vector centers; + std::vector centers; half_sizes.reserve(num_components); centers.reserve(num_components); diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids.cpp b/rerun_cpp/src/rerun/archetypes/ellipsoids.cpp index cc4109b1a12b..c3dc313e92e5 100644 --- a/rerun_cpp/src/rerun/archetypes/ellipsoids.cpp +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids.cpp @@ -14,7 +14,7 @@ namespace rerun { ) { using namespace archetypes; std::vector cells; - cells.reserve(9); + cells.reserve(10); { auto result = DataCell::from_loggable(archetype.half_sizes); @@ -26,8 +26,13 @@ namespace rerun { RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } - if (archetype.rotations.has_value()) { - auto result = DataCell::from_loggable(archetype.rotations.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.quaternions.has_value()) { + auto result = DataCell::from_loggable(archetype.quaternions.value()); RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp index 6451374d5a3d..69d2bc781bb1 100644 --- a/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp @@ -9,9 +9,10 @@ #include "../components/color.hpp" #include "../components/fill_mode.hpp" #include "../components/half_size3d.hpp" -#include "../components/position3d.hpp" +#include "../components/leaf_rotation_axis_angle.hpp" +#include "../components/leaf_rotation_quat.hpp" +#include "../components/leaf_translation3d.hpp" #include "../components/radius.hpp" -#include "../components/rotation3d.hpp" #include "../components/text.hpp" #include "../data_cell.hpp" #include "../indicator_component.hpp" @@ -29,8 +30,8 @@ namespace rerun::archetypes { /// (e.g. a bounding sphere). /// For points whose radii are for the sake of visualization, use `Points3D` instead. /// - /// Currently, ellipsoids are always rendered as wireframes. - /// Opaque and transparent rendering will be supported later. + /// Note that orienting and placing the ellipsoids/spheres is handled via `[archetypes.LeafTransforms3D]`. + /// Some of its component are repeated here for convenience. struct Ellipsoids { /// For each ellipsoid, half of its size on its three axes. /// @@ -40,12 +41,20 @@ namespace rerun::archetypes { /// Optional center positions of the ellipsoids. /// /// If not specified, the centers will be at (0, 0, 0). - std::optional> centers; + /// Note that this uses a `components::LeafTranslation3D` which is also used by `archetypes::LeafTransforms3D`. + std::optional> centers; - /// Optional rotations of the ellipsoids. + /// Rotations via axis + angle. /// - /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. - std::optional> rotations; + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a `components::LeafRotationAxisAngle` which is also used by `archetypes::LeafTransforms3D`. + std::optional> rotation_axis_angles; + + /// Rotations via quaternion. + /// + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a `components::LeafRotationQuat` which is also used by `archetypes::LeafTransforms3D`. + std::optional> quaternions; /// Optional colors for the ellipsoids. std::optional> colors; @@ -96,7 +105,7 @@ namespace rerun::archetypes { /// Creates new `Ellipsoids` with `centers` and `half_sizes`. static Ellipsoids from_centers_and_half_sizes( - Collection centers, + Collection centers, Collection half_sizes ) { Ellipsoids ellipsoids; @@ -112,17 +121,32 @@ namespace rerun::archetypes { /// Optional center positions of the ellipsoids. /// /// If not specified, the centers will be at (0, 0, 0). - Ellipsoids with_centers(Collection _centers) && { + /// Note that this uses a `components::LeafTranslation3D` which is also used by `archetypes::LeafTransforms3D`. + Ellipsoids with_centers(Collection _centers) && { centers = std::move(_centers); // See: https://github.com/rerun-io/rerun/issues/4027 RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } - /// Optional rotations of the ellipsoids. + /// Rotations via axis + angle. + /// + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a `components::LeafRotationAxisAngle` which is also used by `archetypes::LeafTransforms3D`. + Ellipsoids with_rotation_axis_angles( + Collection _rotation_axis_angles + ) && { + 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. /// - /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. - Ellipsoids with_rotations(Collection _rotations) && { - rotations = std::move(_rotations); + /// If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + /// Note that this uses a `components::LeafRotationQuat` which is also used by `archetypes::LeafTransforms3D`. + Ellipsoids with_quaternions(Collection _quaternions + ) && { + quaternions = std::move(_quaternions); // See: https://github.com/rerun-io/rerun/issues/4027 RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp index 7fd7c8a4126a..b1bb6cb05c2d 100644 --- a/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp @@ -32,7 +32,7 @@ namespace rerun { /// Creates new `Ellipsoids` with `centers` and `half_sizes`. static Ellipsoids from_centers_and_half_sizes( - Collection centers, + Collection centers, Collection half_sizes ) { Ellipsoids ellipsoids; diff --git a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp index 30bf26d0d7f2..ddddd9629d1c 100644 --- a/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/leaf_transforms3d.hpp @@ -27,7 +27,7 @@ namespace rerun::archetypes { /// 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. + /// Currently, many visualizers support only a single leaf transform per entity. /// 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, diff --git a/rerun_cpp/tests/archetypes/boxes3d.cpp b/rerun_cpp/tests/archetypes/boxes3d.cpp index f1e04e2bd9d9..07bcece08b1a 100644 --- a/rerun_cpp/tests/archetypes/boxes3d.cpp +++ b/rerun_cpp/tests/archetypes/boxes3d.cpp @@ -15,9 +15,8 @@ SCENARIO( GIVEN("Constructed from builder via from_half_sizes and manually") { auto from_builder = Boxes3D::from_half_sizes({{10.f, 9.f, 8.f}, {5.f, -5.f, 5.f}}) .with_centers({{0.f, 0.f, 0.f}, {-1.f, 1.f, -2.f}}) - .with_rotations({ + .with_quaternions({ Quaternion::from_xyzw(0.f, 1.f, 2.f, 3.f), - RotationAxisAngle({0.f, 1.f, 2.f}, Angle::radians(45.f)), }) .with_colors({0xAA0000CC, 0x00BB00DD}) .with_labels({"hello", "friend"}) @@ -27,9 +26,8 @@ SCENARIO( Boxes3D from_manual; from_manual.half_sizes = {{10.f, 9.f, 8.f}, {5.f, -5.f, 5.f}}; from_manual.centers = {{0.f, 0.f, 0.f}, {-1.f, 1.f, -2.f}}; - from_manual.rotations = { + from_manual.quaternions = { Quaternion::from_xyzw(0.f, 1.f, 2.f, 3.f), - RotationAxisAngle({0.f, 1.f, 2.f}, Angle::radians(45.f)), }; from_manual.colors = {{0xAA, 0x00, 0x00, 0xCC}, {0x00, 0xBB, 0x00, 0xDD}}; from_manual.labels = {"hello", "friend"}; diff --git a/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py b/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py index babb131aab04..32527e0bbaad 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py @@ -21,12 +21,14 @@ class Boxes3D(Boxes3DExt, Archetype): """ **Archetype**: 3D boxes with half-extents and optional center, rotations, colors etc. + Note that orienting and placing the box is handled via `[archetypes.LeafTransforms3D]`. + Some of its component are repeated here for convenience. + Example ------- ### Batch of 3D boxes: ```python import rerun as rr - from rerun.datatypes import Angle, Quaternion, Rotation3D, RotationAxisAngle rr.init("rerun_example_box3d_batch", spawn=True) @@ -35,10 +37,9 @@ class Boxes3D(Boxes3DExt, Archetype): rr.Boxes3D( centers=[[2, 0, 0], [-2, 0, 0], [0, 0, 2]], half_sizes=[[2.0, 2.0, 1.0], [1.0, 1.0, 0.5], [2.0, 0.5, 1.0]], - rotations=[ - Rotation3D.identity(), - Quaternion(xyzw=[0.0, 0.0, 0.382683, 0.923880]), # 45 degrees around Z - RotationAxisAngle(axis=[0, 1, 0], angle=Angle(deg=30)), + quaternions=[ + rr.Quaternion.identity(), + rr.Quaternion(xyzw=[0.0, 0.0, 0.382683, 0.923880]), # 45 degrees around Z ], radii=0.025, colors=[(255, 0, 0), (0, 255, 0), (0, 0, 255)], @@ -66,7 +67,8 @@ def __attrs_clear__(self) -> None: self.__attrs_init__( half_sizes=None, # type: ignore[arg-type] centers=None, # type: ignore[arg-type] - rotations=None, # type: ignore[arg-type] + rotation_axis_angles=None, # type: ignore[arg-type] + quaternions=None, # type: ignore[arg-type] colors=None, # type: ignore[arg-type] radii=None, # type: ignore[arg-type] fill_mode=None, # type: ignore[arg-type] @@ -89,21 +91,39 @@ def _clear(cls) -> Boxes3D: # # (Docstring intentionally commented out to hide this field from the docs) - centers: components.Position3DBatch | None = field( + centers: components.LeafTranslation3DBatch | None = field( metadata={"component": "optional"}, default=None, - converter=components.Position3DBatch._optional, # type: ignore[misc] + converter=components.LeafTranslation3DBatch._optional, # type: ignore[misc] ) # Optional center positions of the boxes. # + # If not specified, the centers will be at (0, 0, 0). + # Note that this uses a [`components.LeafTranslation3D`][rerun.components.LeafTranslation3D] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. + # + # (Docstring intentionally commented out to hide this field from the docs) + + rotation_axis_angles: components.LeafRotationAxisAngleBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.LeafRotationAxisAngleBatch._optional, # type: ignore[misc] + ) + # Rotations via axis + angle. + # + # If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + # Note that this uses a [`components.LeafRotationAxisAngle`][rerun.components.LeafRotationAxisAngle] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. + # # (Docstring intentionally commented out to hide this field from the docs) - rotations: components.Rotation3DBatch | None = field( + quaternions: components.LeafRotationQuatBatch | None = field( metadata={"component": "optional"}, default=None, - converter=components.Rotation3DBatch._optional, # type: ignore[misc] + converter=components.LeafRotationQuatBatch._optional, # type: ignore[misc] ) - # Optional rotations of the boxes. + # Rotations via quaternion. + # + # If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + # Note that this uses a [`components.LeafRotationQuat`][rerun.components.LeafRotationQuat] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. # # (Docstring intentionally commented out to hide this field from the docs) diff --git a/rerun_py/rerun_sdk/rerun/archetypes/boxes3d_ext.py b/rerun_py/rerun_sdk/rerun/archetypes/boxes3d_ext.py index ce48e1a2bc76..b231514f6c82 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/boxes3d_ext.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/boxes3d_ext.py @@ -18,7 +18,8 @@ def __init__( mins: datatypes.Vec3DArrayLike | None = None, half_sizes: datatypes.Vec3DArrayLike | None = None, centers: datatypes.Vec3DArrayLike | None = None, - rotations: datatypes.Rotation3DArrayLike | None = None, + rotation_axis_angles: datatypes.RotationAxisAngleArrayLike | None = None, + quaternions: datatypes.QuaternionArrayLike | None = None, colors: datatypes.Rgba32ArrayLike | None = None, radii: datatypes.Float32ArrayLike | None = None, fill_mode: components.FillMode | None = None, @@ -40,8 +41,19 @@ def __init__( Only valid when used together with either `sizes` or `half_sizes`. centers: Optional center positions of the boxes. - rotations: - Optional rotations of the boxes. + + If not specified, the centers will be at (0, 0, 0). + Note that this uses a [`components.LeafTranslation3D`][rerun.components.LeafTranslation3D] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. + rotation_axis_angles: + Rotations via axis + angle. + + If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + Note that this uses a [`components.LeafRotationAxisAngle`][rerun.components.LeafRotationAxisAngle] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. + quaternions: + Rotations via quaternion. + + If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + Note that this uses a [`components.LeafRotationQuat`][rerun.components.LeafRotationQuat] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. colors: Optional colors for the boxes. radii: @@ -81,7 +93,8 @@ def __init__( self.__attrs_init__( half_sizes=half_sizes, centers=centers, - rotations=rotations, + rotation_axis_angles=rotation_axis_angles, + quaternions=quaternions, colors=colors, radii=radii, fill_mode=fill_mode, diff --git a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py index 953c72fa7838..78d59b3dd64d 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py @@ -25,8 +25,8 @@ class Ellipsoids(EllipsoidsExt, Archetype): (e.g. a bounding sphere). For points whose radii are for the sake of visualization, use `Points3D` instead. - Currently, ellipsoids are always rendered as wireframes. - Opaque and transparent rendering will be supported later. + Note that orienting and placing the ellipsoids/spheres is handled via `[archetypes.LeafTransforms3D]`. + Some of its component are repeated here for convenience. """ # __init__ can be found in ellipsoids_ext.py @@ -36,7 +36,8 @@ def __attrs_clear__(self) -> None: self.__attrs_init__( half_sizes=None, # type: ignore[arg-type] centers=None, # type: ignore[arg-type] - rotations=None, # type: ignore[arg-type] + rotation_axis_angles=None, # type: ignore[arg-type] + quaternions=None, # type: ignore[arg-type] colors=None, # type: ignore[arg-type] line_radii=None, # type: ignore[arg-type] fill_mode=None, # type: ignore[arg-type] @@ -61,25 +62,39 @@ def _clear(cls) -> Ellipsoids: # # (Docstring intentionally commented out to hide this field from the docs) - centers: components.Position3DBatch | None = field( + centers: components.LeafTranslation3DBatch | None = field( metadata={"component": "optional"}, default=None, - converter=components.Position3DBatch._optional, # type: ignore[misc] + converter=components.LeafTranslation3DBatch._optional, # type: ignore[misc] ) # Optional center positions of the ellipsoids. # # If not specified, the centers will be at (0, 0, 0). + # Note that this uses a [`components.LeafTranslation3D`][rerun.components.LeafTranslation3D] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. # # (Docstring intentionally commented out to hide this field from the docs) - rotations: components.Rotation3DBatch | None = field( + rotation_axis_angles: components.LeafRotationAxisAngleBatch | None = field( metadata={"component": "optional"}, default=None, - converter=components.Rotation3DBatch._optional, # type: ignore[misc] + converter=components.LeafRotationAxisAngleBatch._optional, # type: ignore[misc] ) - # Optional rotations of the ellipsoids. + # Rotations via axis + angle. # - # If not specified, the axes of the ellipsoid align with the axes of the coordinate system. + # If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + # Note that this uses a [`components.LeafRotationAxisAngle`][rerun.components.LeafRotationAxisAngle] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. + # + # (Docstring intentionally commented out to hide this field from the docs) + + quaternions: components.LeafRotationQuatBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.LeafRotationQuatBatch._optional, # type: ignore[misc] + ) + # Rotations via quaternion. + # + # If no rotation is specified, the axes of the ellipsoid align with the axes of the local coordinate system. + # Note that this uses a [`components.LeafRotationQuat`][rerun.components.LeafRotationQuat] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. # # (Docstring intentionally commented out to hide this field from the docs) diff --git a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids_ext.py b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids_ext.py index 750e6a2c6927..58932377ca75 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids_ext.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids_ext.py @@ -17,7 +17,8 @@ def __init__( half_sizes: datatypes.Vec3DArrayLike | None = None, radii: datatypes.Float32ArrayLike | None = None, centers: datatypes.Vec3DArrayLike | None = None, - rotations: datatypes.Rotation3DArrayLike | None = None, + rotation_axis_angles: datatypes.RotationAxisAngleArrayLike | None = None, + quaternions: datatypes.QuaternionArrayLike | None = None, colors: datatypes.Rgba32ArrayLike | None = None, line_radii: datatypes.Float32ArrayLike | None = None, fill_mode: components.FillMode | None = None, @@ -37,8 +38,16 @@ def __init__( Specify this instead of `half_sizes` centers: Optional center positions of the ellipsoids. - rotations: - Optional rotations of the ellipsoids. + rotation_axis_angles: + Rotations via axis + angle. + + If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + Note that this uses a [`components.LeafRotationAxisAngle`][rerun.components.LeafRotationAxisAngle] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. + quaternions: + Rotations via quaternion. + + If no rotation is specified, the axes of the boxes align with the axes of the local coordinate system. + Note that this uses a [`components.LeafRotationQuat`][rerun.components.LeafRotationQuat] which is also used by [`archetypes.LeafTransforms3D`][rerun.archetypes.LeafTransforms3D]. colors: Optional colors for the ellipsoids. line_radii: @@ -66,7 +75,8 @@ def __init__( self.__attrs_init__( half_sizes=half_sizes, centers=centers, - rotations=rotations, + rotation_axis_angles=rotation_axis_angles, + quaternions=quaternions, colors=colors, line_radii=line_radii, fill_mode=fill_mode, diff --git a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py index 17c1a84b3780..1a501a607496 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/leaf_transforms3d.py @@ -28,7 +28,7 @@ class LeafTransforms3D(Archetype): 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. + Currently, many visualizers support only a single leaf transform per entity. 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, diff --git a/rerun_py/tests/unit/common_arrays.py b/rerun_py/tests/unit/common_arrays.py index 90d096f3870a..d70344ad787c 100644 --- a/rerun_py/tests/unit/common_arrays.py +++ b/rerun_py/tests/unit/common_arrays.py @@ -21,10 +21,10 @@ Angle, Float32ArrayLike, Quaternion, + QuaternionArrayLike, Rgba32ArrayLike, - Rotation3D, - Rotation3DArrayLike, RotationAxisAngle, + RotationAxisAngleArrayLike, Utf8, Utf8ArrayLike, Uuid, @@ -215,46 +215,55 @@ def uvec3ds_expected(obj: Any, type_: Any | None = None) -> Any: return type_._optional(expected) -rotations_arrays: list[Rotation3DArrayLike] = [ +quaternion_arrays: list[QuaternionArrayLike] = [ [], - # Rotation3D - Rotation3D(Quaternion(xyzw=[1, 2, 3, 4])), - Rotation3D(Quaternion(xyzw=torch.tensor([1, 2, 3, 4]))), - Rotation3D(RotationAxisAngle([1.0, 2.0, 3.0], Angle(4))), - # Quaternion Quaternion(xyzw=[1, 2, 3, 4]), Quaternion(xyzw=[1.0, 2.0, 3.0, 4.0]), Quaternion(xyzw=np.array([1, 2, 3, 4])), Quaternion(xyzw=torch.tensor([1, 2, 3, 4])), - # RotationAxisAngle + [ + Quaternion(xyzw=np.array([1, 2, 3, 4])), + Quaternion(xyzw=[1, 2, 3, 4]), + ], +] + + +def expected_quaternions(rotations: QuaternionArrayLike, type_: Any) -> Any: + if rotations is None: + return type_._optional(None) + elif hasattr(rotations, "__len__") and len(rotations) == 0: + return type_._optional(rotations) + elif isinstance(rotations, Quaternion): + return type_._optional(Quaternion(xyzw=[1, 2, 3, 4])) + else: # sequence of Rotation3DLike + return type_._optional([Quaternion(xyzw=[1, 2, 3, 4])] * 2) + + +rotation_axis_angle_arrays: list[RotationAxisAngleArrayLike] = [ + [], RotationAxisAngle([1, 2, 3], 4), RotationAxisAngle([1.0, 2.0, 3.0], Angle(4)), RotationAxisAngle(Vec3D([1, 2, 3]), Angle(4)), RotationAxisAngle(np.array([1, 2, 3], dtype=np.uint8), Angle(rad=4)), RotationAxisAngle(torch.tensor([1, 2, 3]), Angle(rad=4)), - # Sequence[Rotation3DBatch] [ - Rotation3D(Quaternion(xyzw=[1, 2, 3, 4])), - [1, 2, 3, 4], - Quaternion(xyzw=[1, 2, 3, 4]), + RotationAxisAngle([1, 2, 3], 4), RotationAxisAngle([1, 2, 3], 4), ], ] -def expected_rotations(rotations: Rotation3DArrayLike, type_: Any) -> Any: +def expected_rotation_axis_angles(rotations: RotationAxisAngleArrayLike, type_: Any) -> Any: if rotations is None: return type_._optional(None) elif hasattr(rotations, "__len__") and len(rotations) == 0: return type_._optional(rotations) - elif isinstance(rotations, Rotation3D): - return type_._optional(rotations) elif isinstance(rotations, RotationAxisAngle): return type_._optional(RotationAxisAngle([1, 2, 3], 4)) elif isinstance(rotations, Quaternion): return type_._optional(Quaternion(xyzw=[1, 2, 3, 4])) else: # sequence of Rotation3DLike - return type_._optional([Quaternion(xyzw=[1, 2, 3, 4])] * 3 + [RotationAxisAngle([1, 2, 3], 4)]) + return type_._optional([RotationAxisAngle([1, 2, 3], 4)] * 2) radii_arrays: list[Float32ArrayLike | None] = [ diff --git a/rerun_py/tests/unit/test_box3d.py b/rerun_py/tests/unit/test_box3d.py index f988c71afb17..37bf9c43ebb6 100644 --- a/rerun_py/tests/unit/test_box3d.py +++ b/rerun_py/tests/unit/test_box3d.py @@ -4,21 +4,30 @@ from typing import Optional, cast import rerun as rr -from rerun.components import HalfSize3DBatch, Position3DBatch, Rotation3DBatch -from rerun.datatypes import ClassIdArrayLike, Float32ArrayLike, Rgba32ArrayLike, Rotation3DArrayLike, Utf8ArrayLike -from rerun.datatypes.vec3d import Vec3DArrayLike +from rerun.components import HalfSize3DBatch, LeafRotationAxisAngleBatch, LeafRotationQuatBatch, LeafTranslation3DBatch +from rerun.datatypes import ( + ClassIdArrayLike, + Float32ArrayLike, + QuaternionArrayLike, + Rgba32ArrayLike, + RotationAxisAngleArrayLike, + Utf8ArrayLike, + Vec3DArrayLike, +) from .common_arrays import ( class_ids_arrays, class_ids_expected, colors_arrays, colors_expected, - expected_rotations, + expected_quaternions, + expected_rotation_axis_angles, labels_arrays, labels_expected, + quaternion_arrays, radii_arrays, radii_expected, - rotations_arrays, + rotation_axis_angle_arrays, vec3ds_arrays as centers_arrays, vec3ds_arrays as half_sizes_arrays, vec3ds_expected as centers_expected, @@ -27,55 +36,79 @@ def test_boxes3d() -> None: + fill_mode_arrays = [None, rr.components.FillMode.Solid, rr.components.FillMode.Wireframe] + all_arrays = itertools.zip_longest( half_sizes_arrays, centers_arrays, - rotations_arrays, + rotation_axis_angle_arrays, + quaternion_arrays, colors_arrays, radii_arrays, + fill_mode_arrays, labels_arrays, class_ids_arrays, ) - for half_sizes, centers, rotations, colors, radii, labels, class_ids in all_arrays: + for ( + half_sizes, + centers, + rotation_axis_angles, + quaternions, + colors, + radii, + fill_mode, + labels, + class_ids, + ) in all_arrays: half_sizes = half_sizes if half_sizes is not None else half_sizes_arrays[-1] # make Pyright happy as it's apparently not able to track typing info trough zip_longest half_sizes = cast(Vec3DArrayLike, half_sizes) centers = cast(Vec3DArrayLike, centers) - rotations = cast(Rotation3DArrayLike, rotations) + rotation_axis_angles = cast(RotationAxisAngleArrayLike, rotation_axis_angles) + quaternions = cast(QuaternionArrayLike, quaternions) radii = cast(Optional[Float32ArrayLike], radii) colors = cast(Optional[Rgba32ArrayLike], colors) labels = cast(Optional[Utf8ArrayLike], labels) class_ids = cast(Optional[ClassIdArrayLike], class_ids) + fill_mode = cast(Optional[rr.components.FillMode], fill_mode) print( f"rr.Boxes3D(\n" f" half_sizes={half_sizes}\n" - f" rotations={rotations}\n" + f" rotation_axis_angles={rotation_axis_angles}\n" + f" quaternions={quaternions}\n" f" centers={centers}\n" f" radii={radii!r}\n" f" colors={colors!r}\n" f" labels={labels!r}\n" + f" fill_mode={fill_mode!r}\n" f" class_ids={class_ids!r}\n" f")" ) arch = rr.Boxes3D( half_sizes=half_sizes, centers=centers, - rotations=rotations, + rotation_axis_angles=rotation_axis_angles, + quaternions=quaternions, radii=radii, colors=colors, labels=labels, + fill_mode=fill_mode, class_ids=class_ids, ) print(f"{arch}\n") assert arch.half_sizes == half_sizes_expected(half_sizes, HalfSize3DBatch) - assert arch.centers == centers_expected(centers, Position3DBatch) - assert arch.rotations == expected_rotations(rotations, Rotation3DBatch) + assert arch.centers == centers_expected(centers, LeafTranslation3DBatch) + assert arch.rotation_axis_angles == expected_rotation_axis_angles( + rotation_axis_angles, LeafRotationAxisAngleBatch + ) + assert arch.quaternions == expected_quaternions(quaternions, LeafRotationQuatBatch) assert arch.colors == colors_expected(colors) assert arch.radii == radii_expected(radii) + assert arch.fill_mode == rr.components.FillModeBatch._optional(fill_mode) assert arch.labels == labels_expected(labels) assert arch.class_ids == class_ids_expected(class_ids) @@ -90,10 +123,3 @@ def test_with_centers_and_sizes() -> None: def test_with_mins_and_sizes() -> None: assert rr.Boxes3D(mins=[-1, -1, -1], sizes=[2, 4, 2]) == rr.Boxes3D(centers=[0, 1, 0], half_sizes=[1, 2, 1]) - - -if __name__ == "__main__": - test_boxes3d() - test_with_sizes() - test_with_centers_and_sizes() - test_with_mins_and_sizes() diff --git a/rerun_py/tests/unit/test_ellipsoids3d.py b/rerun_py/tests/unit/test_ellipsoids3d.py new file mode 100644 index 000000000000..79b0b36efcc6 --- /dev/null +++ b/rerun_py/tests/unit/test_ellipsoids3d.py @@ -0,0 +1,113 @@ +from __future__ import annotations + +import itertools +from typing import Optional, cast + +import rerun as rr +from rerun.components import HalfSize3DBatch, LeafRotationAxisAngleBatch, LeafRotationQuatBatch, LeafTranslation3DBatch +from rerun.datatypes import ( + ClassIdArrayLike, + Float32ArrayLike, + QuaternionArrayLike, + Rgba32ArrayLike, + RotationAxisAngleArrayLike, + Utf8ArrayLike, + Vec3DArrayLike, +) + +from .common_arrays import ( + class_ids_arrays, + class_ids_expected, + colors_arrays, + colors_expected, + expected_quaternions, + expected_rotation_axis_angles, + labels_arrays, + labels_expected, + quaternion_arrays, + radii_arrays, + radii_expected, + rotation_axis_angle_arrays, + vec3ds_arrays as centers_arrays, + vec3ds_arrays as half_sizes_arrays, + vec3ds_expected as centers_expected, + vec3ds_expected as half_sizes_expected, +) + + +def test_ellipsoids() -> None: + fill_mode_arrays = [None, rr.components.FillMode.Solid, rr.components.FillMode.Wireframe] + + all_arrays = itertools.zip_longest( + half_sizes_arrays, + centers_arrays, + rotation_axis_angle_arrays, + quaternion_arrays, + colors_arrays, + radii_arrays, + fill_mode_arrays, + labels_arrays, + class_ids_arrays, + ) + + for ( + half_sizes, + centers, + rotation_axis_angles, + quaternions, + colors, + line_radii, + fill_mode, + labels, + class_ids, + ) in all_arrays: + half_sizes = half_sizes if half_sizes is not None else half_sizes_arrays[-1] + + # make Pyright happy as it's apparently not able to track typing info trough zip_longest + half_sizes = cast(Vec3DArrayLike, half_sizes) + centers = cast(Vec3DArrayLike, centers) + rotation_axis_angles = cast(RotationAxisAngleArrayLike, rotation_axis_angles) + quaternions = cast(QuaternionArrayLike, quaternions) + line_radii = cast(Optional[Float32ArrayLike], line_radii) + colors = cast(Optional[Rgba32ArrayLike], colors) + labels = cast(Optional[Utf8ArrayLike], labels) + class_ids = cast(Optional[ClassIdArrayLike], class_ids) + fill_mode = cast(Optional[rr.components.FillMode], fill_mode) + + print( + f"rr.Ellipsoids(\n" + f" half_sizes={half_sizes}\n" + f" rotation_axis_angles={rotation_axis_angles}\n" + f" quaternions={quaternions}\n" + f" centers={centers}\n" + f" line_radii={line_radii!r}\n" + f" colors={colors!r}\n" + f" labels={labels!r}\n" + f" fill_mode={fill_mode!r}\n" + f" class_ids={class_ids!r}\n" + f")" + ) + arch = rr.Ellipsoids( + half_sizes=half_sizes, + centers=centers, + rotation_axis_angles=rotation_axis_angles, + quaternions=quaternions, + line_radii=line_radii, + colors=colors, + labels=labels, + fill_mode=fill_mode, + class_ids=class_ids, + ) + print(f"{arch}\n") + + assert arch.half_sizes == half_sizes_expected(half_sizes, HalfSize3DBatch) + assert arch.centers == centers_expected(centers, LeafTranslation3DBatch) + assert arch.rotation_axis_angles == expected_rotation_axis_angles( + rotation_axis_angles, LeafRotationAxisAngleBatch + ) + assert arch.quaternions == expected_quaternions(quaternions, LeafRotationQuatBatch) + assert arch.colors == colors_expected(colors) + assert arch.line_radii == radii_expected(line_radii) + assert arch.fill_mode == rr.components.FillModeBatch._optional(fill_mode) + assert arch.labels == labels_expected(labels) + assert arch.class_ids == class_ids_expected(class_ids) diff --git a/rerun_py/tests/unit/test_rotation3d.py b/rerun_py/tests/unit/test_rotation3d.py deleted file mode 100644 index c47e9b241fa3..000000000000 --- a/rerun_py/tests/unit/test_rotation3d.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -from rerun.datatypes import Rotation3DBatch - -from .common_arrays import ( - expected_rotations, - rotations_arrays, -) - - -def test_rotation3d() -> None: - for rotations in rotations_arrays: - print(f"rrd.Rotation3DBatch({rotations})") - datatype = Rotation3DBatch(rotations) - print(f"{datatype}\n") - - assert datatype == expected_rotations(rotations, Rotation3DBatch) - - -if __name__ == "__main__": - test_rotation3d() diff --git a/tests/cpp/roundtrips/boxes3d/main.cpp b/tests/cpp/roundtrips/boxes3d/main.cpp index b4a3f655f21c..383cdc64692c 100644 --- a/tests/cpp/roundtrips/boxes3d/main.cpp +++ b/tests/cpp/roundtrips/boxes3d/main.cpp @@ -10,13 +10,6 @@ int main(int, char** argv) { "boxes3d", rerun::archetypes::Boxes3D::from_half_sizes({{10.f, 9.f, 8.f}, {5.f, -5.f, 5.f}}) .with_centers({{0.f, 0.f, 0.f}, {-1.f, 1.f, -2.f}}) - .with_rotations({ - rerun::datatypes::Quaternion::from_xyzw(0.f, 1.f, 2.f, 3.f), - rerun::datatypes::RotationAxisAngle( - {0.f, 1.f, 2.f}, - rerun::datatypes::Angle::degrees(45.f) - ), - }) .with_colors({0xAA0000CC, 0x00BB00DD}) .with_labels({"hello", "friend"}) .with_radii({0.1f, 0.01f}) diff --git a/tests/python/roundtrips/boxes3d/main.py b/tests/python/roundtrips/boxes3d/main.py index c0d9569ef683..7dfd113c0ea0 100755 --- a/tests/python/roundtrips/boxes3d/main.py +++ b/tests/python/roundtrips/boxes3d/main.py @@ -8,13 +8,11 @@ import numpy as np import rerun as rr -from rerun.datatypes import Angle, Quaternion, RotationAxisAngle def main() -> None: half_sizes = np.array([[10, 9, 8], [5, -5, 5]]) centers = np.array([[0, 0, 0], [-1, 1, -2]]) - rotations = [Quaternion(xyzw=[0, 1, 2, 3]), RotationAxisAngle([0, 1, 2], Angle(deg=45))] colors = np.array( [ 0xAA0000CC, @@ -29,7 +27,6 @@ def main() -> None: boxes3d = rr.Boxes3D( half_sizes=half_sizes, centers=centers, - rotations=rotations, colors=colors, labels=labels, radii=radii, diff --git a/tests/python/test_api/test_api.py b/tests/python/test_api/test_api.py index 4603dd25c1e1..9a7b89144249 100755 --- a/tests/python/test_api/test_api.py +++ b/tests/python/test_api/test_api.py @@ -251,7 +251,7 @@ def run_bounding_box() -> None: "bbox_test/bbox", rr.Boxes3D( half_sizes=[1.0, 0.5, 0.25], - rotations=rr.Quaternion(xyzw=[0, 0, np.sin(np.pi / 4), np.cos(np.pi / 4)]), + quaternions=rr.Quaternion(xyzw=[0, 0, np.sin(np.pi / 4), np.cos(np.pi / 4)]), colors=[0, 255, 0], radii=0.01, labels="box/t0", @@ -264,7 +264,7 @@ def run_bounding_box() -> None: rr.Boxes3D( centers=np.array([1.0, 0.0, 0.0]), half_sizes=[1.0, 0.5, 0.25], - rotations=rr.Quaternion(xyzw=[0, 0, np.sin(np.pi / 4), np.cos(np.pi / 4)]), + quaternions=rr.Quaternion(xyzw=[0, 0, np.sin(np.pi / 4), np.cos(np.pi / 4)]), colors=[255, 255, 0], radii=0.02, labels="box/t1", diff --git a/tests/rust/roundtrips/boxes3d/src/main.rs b/tests/rust/roundtrips/boxes3d/src/main.rs index b6664b0518fa..41841cf914d5 100644 --- a/tests/rust/roundtrips/boxes3d/src/main.rs +++ b/tests/rust/roundtrips/boxes3d/src/main.rs @@ -1,12 +1,6 @@ //! Logs a `Box3D` archetype for roundtrip checks. -use rerun::{ - archetypes::Boxes3D, - components::Rotation3D, - datatypes::{Quaternion, RotationAxisAngle}, - transform::Angle, - RecordingStream, -}; +use rerun::{archetypes::Boxes3D, RecordingStream}; #[derive(Debug, clap::Parser)] #[clap(author, version, about)] @@ -20,13 +14,6 @@ fn run(rec: &RecordingStream, _args: &Args) -> anyhow::Result<()> { "boxes3d", &Boxes3D::from_half_sizes([[10., 9., 8.], [5., -5., 5.]]) .with_centers([[0., 0., 0.], [-1., 1., -2.]]) - .with_rotations([ - Rotation3D::from(Quaternion::from_xyzw([0., 1., 2., 3.])), - Rotation3D::from(RotationAxisAngle::new( - [0., 1., 2.], - Angle::from_degrees(45.), - )), - ]) .with_colors([0xAA0000CC, 0x00BB00DD]) .with_labels(["hello", "friend"]) .with_radii([0.1, 0.01]) diff --git a/tests/rust/test_api/src/main.rs b/tests/rust/test_api/src/main.rs index 0c48dbc6bd09..622f05c18ba5 100644 --- a/tests/rust/test_api/src/main.rs +++ b/tests/rust/test_api/src/main.rs @@ -32,7 +32,7 @@ fn test_bbox(rec: &RecordingStream) -> anyhow::Result<()> { "bbox_test/bbox", &Boxes3D::from_half_sizes([(1.0, 0.5, 0.25)]) .with_colors([0x00FF00FF]) - .with_rotations([Quaternion::from_xyzw([ + .with_quaternions([Quaternion::from_xyzw([ 0.0, 0.0, (TAU / 8.0).sin(), @@ -47,7 +47,7 @@ fn test_bbox(rec: &RecordingStream) -> anyhow::Result<()> { "bbox_test/bbox", &Boxes3D::from_centers_and_half_sizes([(1.0, 0.0, 0.0)], [(1.0, 0.5, 0.25)]) .with_colors([Color::from_rgb(255, 255, 0)]) - .with_rotations([Quaternion::from_xyzw([ + .with_quaternions([Quaternion::from_xyzw([ 0.0, 0.0, (TAU / 8.0).sin(),