From bb3ee816e124b0122af4c4609bab6361293e30a2 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Mon, 1 Jul 2024 11:44:11 -0700 Subject: [PATCH 01/19] Add `Ellipsoids` archetype, visualizer, and snippets. Currently, the visualizer is inefficient, in that it pipes everything through the `LineDrawableBuilder` every frame unnecessarily. However, this should not be worse than if the user were to create a similar number of lines directly. --- Cargo.lock | 54 ++- Cargo.toml | 1 + .../re_types/definitions/rerun/archetypes.fbs | 1 + .../rerun/archetypes/ellipsoids.fbs | 63 +++ .../re_types/src/archetypes/.gitattributes | 1 + .../re_types/src/archetypes/ellipsoids.rs | 388 ++++++++++++++++++ .../re_types/src/archetypes/ellipsoids_ext.rs | 44 ++ crates/store/re_types/src/archetypes/mod.rs | 3 + .../viewer/re_space_view_spatial/Cargo.toml | 1 + .../viewer/re_space_view_spatial/src/lib.rs | 1 + .../re_space_view_spatial/src/proc_mesh.rs | 136 ++++++ .../src/visualizers/ellipsoids.rs | 346 ++++++++++++++++ .../src/visualizers/mod.rs | 2 + docs/content/reference/types/archetypes.md | 1 + .../reference/types/archetypes/.gitattributes | 1 + .../reference/types/archetypes/ellipsoids.md | 37 ++ .../reference/types/components/class_id.md | 1 + .../reference/types/components/color.md | 1 + .../reference/types/components/half_size3d.md | 1 + .../reference/types/components/position3d.md | 1 + .../reference/types/components/radius.md | 1 + .../reference/types/components/rotation3d.md | 1 + .../reference/types/components/text.md | 1 + .../reference/types/views/spatial2d_view.md | 1 + .../reference/types/views/spatial3d_view.md | 1 + .../all/archetypes/ellipsoid_batch.cpp | 38 ++ .../all/archetypes/ellipsoid_batch.py | 35 ++ .../all/archetypes/ellipsoid_batch.rs | 37 ++ rerun_cpp/src/rerun/archetypes.hpp | 1 + rerun_cpp/src/rerun/archetypes/.gitattributes | 2 + rerun_cpp/src/rerun/archetypes/ellipsoids.cpp | 63 +++ rerun_cpp/src/rerun/archetypes/ellipsoids.hpp | 169 ++++++++ .../src/rerun/archetypes/ellipsoids_ext.cpp | 76 ++++ .../rerun_sdk/rerun/archetypes/.gitattributes | 1 + .../rerun_sdk/rerun/archetypes/__init__.py | 2 + .../rerun_sdk/rerun/archetypes/ellipsoids.py | 124 ++++++ .../rerun/archetypes/ellipsoids_ext.py | 74 ++++ 37 files changed, 1700 insertions(+), 11 deletions(-) create mode 100644 crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs create mode 100644 crates/store/re_types/src/archetypes/ellipsoids.rs create mode 100644 crates/store/re_types/src/archetypes/ellipsoids_ext.rs create mode 100644 crates/viewer/re_space_view_spatial/src/proc_mesh.rs create mode 100644 crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs create mode 100644 docs/content/reference/types/archetypes/ellipsoids.md create mode 100644 docs/snippets/all/archetypes/ellipsoid_batch.cpp create mode 100644 docs/snippets/all/archetypes/ellipsoid_batch.py create mode 100644 docs/snippets/all/archetypes/ellipsoid_batch.rs create mode 100644 rerun_cpp/src/rerun/archetypes/ellipsoids.cpp create mode 100644 rerun_cpp/src/rerun/archetypes/ellipsoids.hpp create mode 100644 rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp create mode 100644 rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py create mode 100644 rerun_py/rerun_sdk/rerun/archetypes/ellipsoids_ext.py diff --git a/Cargo.lock b/Cargo.lock index 33ad8c00262a..5742439725bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1177,6 +1177,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const_soft_float" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ca1caa64ef4ed453e68bb3db612e51cf1b2f5b871337f0fcab1c8f87cc3dff" + +[[package]] +name = "constgebra" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1aaf9b65849a68662ac6c0810c8893a765c960b907dd7cfab9c4a50bf764fbc" +dependencies = [ + "const_soft_float", +] + [[package]] name = "convert_case" version = "0.6.0" @@ -2280,6 +2295,12 @@ dependencies = [ "serde", ] +[[package]] +name = "glam" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" + [[package]] name = "glob" version = "0.3.1" @@ -2488,6 +2509,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hexasphere" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64d70ed6295005e2bc5a6f624300cfd6d49908da76b3654c4cbdb1d4222705" +dependencies = [ + "constgebra", + "glam 0.28.0", +] + [[package]] name = "hexf-parse" version = "0.2.1" @@ -2935,7 +2966,7 @@ version = "0.18.0-alpha.1+dev" dependencies = [ "anyhow", "clap", - "glam", + "glam 0.22.0", "re_tracing", "rerun", ] @@ -2964,7 +2995,7 @@ version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e4daa18df16fd196b955e4bb16574641f39141fb3ea81b493a84b9439d17e6" dependencies = [ - "glam", + "glam 0.22.0", "num-traits", "serde", ] @@ -3093,7 +3124,7 @@ version = "0.18.0-alpha.1+dev" dependencies = [ "anyhow", "clap", - "glam", + "glam 0.22.0", "rerun", ] @@ -3543,7 +3574,7 @@ version = "0.18.0-alpha.1+dev" dependencies = [ "anyhow", "clap", - "glam", + "glam 0.22.0", "prost", "prost-build", "protoc-prebuilt", @@ -4705,7 +4736,7 @@ dependencies = [ "ecolor", "enumset", "getrandom", - "glam", + "glam 0.22.0", "gltf", "half 2.3.1", "itertools 0.13.0", @@ -4744,7 +4775,7 @@ dependencies = [ "anyhow", "bytemuck", "console_error_panic_hook", - "glam", + "glam 0.22.0", "image", "itertools 0.13.0", "macaw", @@ -4918,7 +4949,8 @@ dependencies = [ "bytemuck", "criterion", "egui", - "glam", + "glam 0.22.0", + "hexasphere", "itertools 0.13.0", "macaw", "mimalloc", @@ -5094,7 +5126,7 @@ dependencies = [ "ecolor", "egui_plot", "emath", - "glam", + "glam 0.22.0", "half 2.3.1", "image", "infer", @@ -5295,7 +5327,7 @@ dependencies = [ "egui-wgpu", "egui_extras", "egui_tiles", - "glam", + "glam 0.22.0", "half 2.3.1", "indexmap 2.1.0", "itertools 0.13.0", @@ -5336,7 +5368,7 @@ dependencies = [ "ahash", "egui", "egui_tiles", - "glam", + "glam 0.22.0", "image", "itertools 0.13.0", "nohash-hasher", @@ -6450,7 +6482,7 @@ version = "0.18.0-alpha.1+dev" dependencies = [ "anyhow", "clap", - "glam", + "glam 0.22.0", "itertools 0.13.0", "ndarray", "ndarray-rand", diff --git a/Cargo.toml b/Cargo.toml index b92803cd66ca..8cd61ddd4543 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -169,6 +169,7 @@ glam = "0.22" # glam update blocked by macaw glob = "0.3" gltf = "1.1" half = "2.3.1" +hexasphere = "14.0.0" image = { version = "0.25", default-features = false } indent = "0.1" indexmap = "2.1" # Version chosen to align with other dependencies diff --git a/crates/store/re_types/definitions/rerun/archetypes.fbs b/crates/store/re_types/definitions/rerun/archetypes.fbs index 4ddccbed02f3..c529f04c4652 100644 --- a/crates/store/re_types/definitions/rerun/archetypes.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes.fbs @@ -8,6 +8,7 @@ include "./archetypes/boxes3d.fbs"; include "./archetypes/clear.fbs"; include "./archetypes/depth_image.fbs"; include "./archetypes/disconnected_space.fbs"; +include "./archetypes/ellipsoids.fbs"; include "./archetypes/image.fbs"; include "./archetypes/line_strips2d.fbs"; include "./archetypes/line_strips3d.fbs"; diff --git a/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs b/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs new file mode 100644 index 000000000000..d94d1797c301 --- /dev/null +++ b/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs @@ -0,0 +1,63 @@ +include "fbs/attributes.fbs"; +include "rust/attributes.fbs"; +include "cpp/attributes.fbs"; + +include "rerun/datatypes.fbs"; +include "rerun/components.fbs"; + +namespace rerun.archetypes; + +// --- + +/// 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. +/// +/// 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. +/// +/// \example archetypes/ellipsoid_batch !api title="Batch of ellipsoids" +table Ellipsoids ( + "attr.rust.derive": "PartialEq", + "attr.rust.new_pub_crate", + "attr.cpp.no_field_ctors", + "attr.docs.category": "Spatial 3D", + "attr.docs.view_types": "Spatial3DView, Spatial2DView: if logged above active projection" +) { + // --- Required --- + + /// For each ellipsoid, half of its size on its three axes. + /// + /// If all components are equal, then it is a sphere with that radius. + half_sizes: [rerun.components.HalfSize3D] ("attr.rerun.component_required", order: 1000); + + // --- Recommended --- + + /// 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); + + /// Optional rotations of the boxes. + /// + /// 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); + + /// Optional colors for the ellipsoids. + colors: [rerun.components.Color] ("attr.rerun.component_recommended", nullable, order: 2200); + + // --- Optional --- + + /// Optional radii for the lines used when the ellipsoid is rendered as a wireframe. + line_radii: [rerun.components.Radius] ("attr.rerun.component_optional", nullable, order: 3000); + + /// Optional text labels for the ellipsoids. + labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3100); + + /// Optional `ClassId`s for the ellipsoids. + /// + /// The class ID provides colors and labels if not specified explicitly. + class_ids: [rerun.components.ClassId] ("attr.rerun.component_optional", nullable, order: 3200); +} diff --git a/crates/store/re_types/src/archetypes/.gitattributes b/crates/store/re_types/src/archetypes/.gitattributes index 56544ae96577..b299bfcf0b66 100644 --- a/crates/store/re_types/src/archetypes/.gitattributes +++ b/crates/store/re_types/src/archetypes/.gitattributes @@ -10,6 +10,7 @@ boxes2d.rs linguist-generated=true boxes3d.rs linguist-generated=true depth_image.rs linguist-generated=true disconnected_space.rs linguist-generated=true +ellipsoids.rs linguist-generated=true image.rs linguist-generated=true line_strips2d.rs linguist-generated=true line_strips3d.rs linguist-generated=true diff --git a/crates/store/re_types/src/archetypes/ellipsoids.rs b/crates/store/re_types/src/archetypes/ellipsoids.rs new file mode 100644 index 000000000000..52bad1ffe285 --- /dev/null +++ b/crates/store/re_types/src/archetypes/ellipsoids.rs @@ -0,0 +1,388 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/rust/api.rs +// Based on "crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs". + +#![allow(unused_imports)] +#![allow(unused_parens)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::cloned_instead_of_copied)] +#![allow(clippy::map_flatten)] +#![allow(clippy::needless_question_mark)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::too_many_lines)] + +use ::re_types_core::external::arrow2; +use ::re_types_core::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Archetype**: 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. +/// +/// 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. +#[derive(Clone, Debug, PartialEq)] +pub struct Ellipsoids { + /// For each ellipsoid, half of its size on its three axes. + /// + /// If all components are equal, then it is a sphere with that radius. + pub half_sizes: Vec, + + /// Optional center positions of the ellipsoids. + /// + /// If not specified, the centers will be at (0, 0, 0). + pub centers: Option>, + + /// Optional rotations of the boxes. + /// + /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. + pub rotations: Option>, + + /// Optional colors for the ellipsoids. + pub colors: Option>, + + /// Optional radii for the lines used when the ellipsoid is rendered as a wireframe. + pub line_radii: Option>, + + /// Optional text labels for the ellipsoids. + pub labels: Option>, + + /// Optional `ClassId`s for the ellipsoids. + /// + /// The class ID provides colors and labels if not specified explicitly. + pub class_ids: Option>, +} + +impl ::re_types_core::SizeBytes for Ellipsoids { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.half_sizes.heap_size_bytes() + + self.centers.heap_size_bytes() + + self.rotations.heap_size_bytes() + + self.colors.heap_size_bytes() + + self.line_radii.heap_size_bytes() + + self.labels.heap_size_bytes() + + self.class_ids.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + >::is_pod() + && >>::is_pod() + && >>::is_pod() + && >>::is_pod() + && >>::is_pod() + && >>::is_pod() + && >>::is_pod() + } +} + +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]> = + once_cell::sync::Lazy::new(|| { + [ + "rerun.components.Position3D".into(), + "rerun.components.Rotation3D".into(), + "rerun.components.Color".into(), + "rerun.components.EllipsoidsIndicator".into(), + ] + }); + +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 3usize]> = + once_cell::sync::Lazy::new(|| { + [ + "rerun.components.Radius".into(), + "rerun.components.Text".into(), + "rerun.components.ClassId".into(), + ] + }); + +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 8usize]> = + once_cell::sync::Lazy::new(|| { + [ + "rerun.components.HalfSize3D".into(), + "rerun.components.Position3D".into(), + "rerun.components.Rotation3D".into(), + "rerun.components.Color".into(), + "rerun.components.EllipsoidsIndicator".into(), + "rerun.components.Radius".into(), + "rerun.components.Text".into(), + "rerun.components.ClassId".into(), + ] + }); + +impl Ellipsoids { + /// The total number of components in the archetype: 1 required, 4 recommended, 3 optional + pub const NUM_COMPONENTS: usize = 8usize; +} + +/// Indicator component for the [`Ellipsoids`] [`::re_types_core::Archetype`] +pub type EllipsoidsIndicator = ::re_types_core::GenericIndicatorComponent; + +impl ::re_types_core::Archetype for Ellipsoids { + type Indicator = EllipsoidsIndicator; + + #[inline] + fn name() -> ::re_types_core::ArchetypeName { + "rerun.archetypes.Ellipsoids".into() + } + + #[inline] + fn display_name() -> &'static str { + "Ellipsoids" + } + + #[inline] + fn indicator() -> MaybeOwnedComponentBatch<'static> { + static INDICATOR: EllipsoidsIndicator = EllipsoidsIndicator::DEFAULT; + MaybeOwnedComponentBatch::Ref(&INDICATOR) + } + + #[inline] + fn required_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + REQUIRED_COMPONENTS.as_slice().into() + } + + #[inline] + fn recommended_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + RECOMMENDED_COMPONENTS.as_slice().into() + } + + #[inline] + fn optional_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + OPTIONAL_COMPONENTS.as_slice().into() + } + + #[inline] + fn all_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + ALL_COMPONENTS.as_slice().into() + } + + #[inline] + fn from_arrow_components( + arrow_data: impl IntoIterator)>, + ) -> DeserializationResult { + re_tracing::profile_function!(); + use ::re_types_core::{Loggable as _, ResultExt as _}; + let arrays_by_name: ::std::collections::HashMap<_, _> = arrow_data + .into_iter() + .map(|(name, array)| (name.full_name(), array)) + .collect(); + let half_sizes = { + let array = arrays_by_name + .get("rerun.components.HalfSize3D") + .ok_or_else(DeserializationError::missing_data) + .with_context("rerun.archetypes.Ellipsoids#half_sizes")?; + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Ellipsoids#half_sizes")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Ellipsoids#half_sizes")? + }; + let centers = if let Some(array) = arrays_by_name.get("rerun.components.Position3D") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Ellipsoids#centers")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Ellipsoids#centers")? + }) + } 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 colors = if let Some(array) = arrays_by_name.get("rerun.components.Color") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Ellipsoids#colors")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Ellipsoids#colors")? + }) + } else { + None + }; + let line_radii = if let Some(array) = arrays_by_name.get("rerun.components.Radius") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Ellipsoids#line_radii")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Ellipsoids#line_radii")? + }) + } else { + None + }; + let labels = if let Some(array) = arrays_by_name.get("rerun.components.Text") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Ellipsoids#labels")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Ellipsoids#labels")? + }) + } else { + None + }; + let class_ids = if let Some(array) = arrays_by_name.get("rerun.components.ClassId") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.Ellipsoids#class_ids")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.Ellipsoids#class_ids")? + }) + } else { + None + }; + Ok(Self { + half_sizes, + centers, + rotations, + colors, + line_radii, + labels, + class_ids, + }) + } +} + +impl ::re_types_core::AsComponents for Ellipsoids { + fn as_component_batches(&self) -> Vec> { + re_tracing::profile_function!(); + use ::re_types_core::Archetype as _; + [ + Some(Self::indicator()), + Some((&self.half_sizes as &dyn ComponentBatch).into()), + self.centers + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.rotations + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.colors + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.line_radii + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.labels + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.class_ids + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + ] + .into_iter() + .flatten() + .collect() + } +} + +impl Ellipsoids { + /// Create a new `Ellipsoids`. + #[inline] + pub(crate) fn new( + half_sizes: impl IntoIterator>, + ) -> Self { + Self { + half_sizes: half_sizes.into_iter().map(Into::into).collect(), + centers: None, + rotations: None, + colors: None, + line_radii: None, + labels: None, + class_ids: None, + } + } + + /// Optional center positions of the ellipsoids. + /// + /// If not specified, the centers will be at (0, 0, 0). + #[inline] + pub fn with_centers( + mut self, + centers: impl IntoIterator>, + ) -> Self { + self.centers = Some(centers.into_iter().map(Into::into).collect()); + self + } + + /// Optional rotations of the boxes. + /// + /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. + #[inline] + pub fn with_rotations( + mut self, + rotations: impl IntoIterator>, + ) -> Self { + self.rotations = Some(rotations.into_iter().map(Into::into).collect()); + self + } + + /// Optional colors for the ellipsoids. + #[inline] + pub fn with_colors( + mut self, + colors: impl IntoIterator>, + ) -> Self { + self.colors = Some(colors.into_iter().map(Into::into).collect()); + self + } + + /// Optional radii for the lines used when the ellipsoid is rendered as a wireframe. + #[inline] + pub fn with_line_radii( + mut self, + line_radii: impl IntoIterator>, + ) -> Self { + self.line_radii = Some(line_radii.into_iter().map(Into::into).collect()); + self + } + + /// Optional text labels for the ellipsoids. + #[inline] + pub fn with_labels( + mut self, + labels: impl IntoIterator>, + ) -> Self { + self.labels = Some(labels.into_iter().map(Into::into).collect()); + self + } + + /// Optional `ClassId`s for the ellipsoids. + /// + /// The class ID provides colors and labels if not specified explicitly. + #[inline] + pub fn with_class_ids( + mut self, + class_ids: impl IntoIterator>, + ) -> Self { + self.class_ids = Some(class_ids.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 new file mode 100644 index 000000000000..ac5b06b3c2f2 --- /dev/null +++ b/crates/store/re_types/src/archetypes/ellipsoids_ext.rs @@ -0,0 +1,44 @@ +use crate::components::{HalfSize3D, Position3D}; + +use super::Ellipsoids; + +impl Ellipsoids { + /// Creates a new [`Ellipsoids`] with [`Self::half_sizes`]. + #[inline] + pub fn from_half_sizes(half_sizes: impl IntoIterator>) -> Self { + Self::new(half_sizes) + } + + /// Creates a new [`Ellipsoids`] for spheres with the given radii. + // Note: This is not a `Radius` component because the `Radius` component is for + // the on-screen sizes of lines and points. + #[inline] + pub fn from_radii(radii: impl IntoIterator) -> Self { + Self::new(radii.into_iter().map(sphere_size)) + } + + /// Creates a new [`Ellipsoids`] with [`Self::centers`] and [`Self::half_sizes`]. + #[inline] + pub fn from_centers_and_half_sizes( + centers: impl IntoIterator>, + half_sizes: impl IntoIterator>, + ) -> Self { + Self::new(half_sizes).with_centers(centers) + } + + /// Creates a new [`Ellipsoids`] for spheres with the given [`Self::centers`], and + /// [`Self::half_sizes`] all equal to the given radii. + // Note: This is not a `Radius` component because the `Radius` component is for + // the on-screen sizes of lines and points. + #[inline] + pub fn from_centers_and_radii( + centers: impl IntoIterator>, + radii: impl IntoIterator, + ) -> Self { + Self::new(radii.into_iter().map(sphere_size)).with_centers(centers) + } +} + +fn sphere_size(radius: f32) -> HalfSize3D { + HalfSize3D::new(radius, radius, radius) +} diff --git a/crates/store/re_types/src/archetypes/mod.rs b/crates/store/re_types/src/archetypes/mod.rs index a29b3b0e4e24..aa035642ddd9 100644 --- a/crates/store/re_types/src/archetypes/mod.rs +++ b/crates/store/re_types/src/archetypes/mod.rs @@ -15,6 +15,8 @@ mod boxes3d_ext; mod depth_image; mod depth_image_ext; mod disconnected_space; +mod ellipsoids; +mod ellipsoids_ext; mod image; mod image_ext; mod line_strips2d; @@ -50,6 +52,7 @@ pub use self::boxes2d::Boxes2D; pub use self::boxes3d::Boxes3D; pub use self::depth_image::DepthImage; pub use self::disconnected_space::DisconnectedSpace; +pub use self::ellipsoids::Ellipsoids; pub use self::image::Image; pub use self::line_strips2d::LineStrips2D; pub use self::line_strips3d::LineStrips3D; diff --git a/crates/viewer/re_space_view_spatial/Cargo.toml b/crates/viewer/re_space_view_spatial/Cargo.toml index 795211046c76..2f96432beacd 100644 --- a/crates/viewer/re_space_view_spatial/Cargo.toml +++ b/crates/viewer/re_space_view_spatial/Cargo.toml @@ -45,6 +45,7 @@ bitflags.workspace = true bytemuck.workspace = true egui = { workspace = true, features = ["serde"] } glam.workspace = true +hexasphere.workspace = true itertools.workspace = true macaw = { workspace = true, features = ["with_serde"] } nohash-hasher.workspace = true diff --git a/crates/viewer/re_space_view_spatial/src/lib.rs b/crates/viewer/re_space_view_spatial/src/lib.rs index b6c609b35cfa..7d81a4653562 100644 --- a/crates/viewer/re_space_view_spatial/src/lib.rs +++ b/crates/viewer/re_space_view_spatial/src/lib.rs @@ -14,6 +14,7 @@ mod mesh_cache; mod mesh_loader; mod pickable_image; mod picking; +mod proc_mesh; mod scene_bounding_boxes; mod space_camera_3d; mod spatial_topology; diff --git a/crates/viewer/re_space_view_spatial/src/proc_mesh.rs b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs new file mode 100644 index 000000000000..bb3ad4bc5a49 --- /dev/null +++ b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs @@ -0,0 +1,136 @@ +use std::sync::Arc; + +use ahash::HashSet; +use glam::Vec3; + +use re_renderer::RenderContext; +use re_viewer_context::Cache; + +// ---------------------------------------------------------------------------- + +/// Description of a mesh that can be procedurally generated. +/// +/// Obtain the actual mesh by passing this to [`WireframeCache`]. +/// In the future, it will be possible to produce solid triangle meshes too. +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub enum ProcMeshKey { + /// A sphere of unit radius. + Sphere { subdivisions: usize }, +} + +/// A renderable mesh generated from a [`ProcMeshKey`] by the [`WireframeCache`], +/// which is to be drawn as lines rather than triangles. +pub struct WireframeMesh { + pub bbox: macaw::BoundingBox, + + pub vertex_count: usize, + + /// Collection of line strips making up the wireframe. + /// + /// TODO(kpreid): This should instead be a GPU buffer, but we don’t yet have a + /// `re_renderer::Renderer` implementation that takes instanced meshes and applies + /// the line shader to them, instead of doing immediate-mode accumulation of line strips. + pub line_strips: Vec>, +} + +// ---------------------------------------------------------------------------- + +/// Cache for the computation of wireframe meshes from [`SynthMeshKey`]s. +/// These meshes may then be rendered as instances of the cached +/// mesh. +#[derive(Default)] +pub struct WireframeCache(ahash::HashMap>>); + +impl WireframeCache { + pub fn entry( + &mut self, + key: ProcMeshKey, + render_ctx: &RenderContext, + ) -> Option> { + re_tracing::profile_function!(); + + self.0 + .entry(key) + .or_insert_with(|| { + re_log::debug!("Generating mesh {key:?}…"); + + let mesh = generate_wireframe(&key, render_ctx); + + // Right now, this can never return None, but in the future + // it will perform GPU allocations which can fail. + + Some(Arc::new(mesh)) + }) + .clone() + } +} + +impl Cache for WireframeCache { + fn begin_frame(&mut self) {} + + fn purge_memory(&mut self) { + self.0.clear(); + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } +} + +/// Generate a wireframe mesh without caching. +fn generate_wireframe(key: &ProcMeshKey, render_ctx: &RenderContext) -> WireframeMesh { + // In the future, render_ctx will be used to allocate GPU memory for the mesh. + _ = render_ctx; + + match *key { + ProcMeshKey::Sphere { subdivisions } => { + let subdiv = hexasphere::shapes::IcoSphere::new(subdivisions, |other_glam_vec| { + // `hexasphere` uses a different version of `glam` than we do. + // + <[f32; 3]>::from(other_glam_vec).into() + }); + + let sphere_points = subdiv.raw_data(); + + let line_strips: Vec> = if false { + // TODO(kpreid): There seems to be a bug in `hexasphere` where it fails to + // return lines which reach the original corners of the shape. + // Until that’s fixed, this code is skipped and we use the kludge of extracting + // edges from the triangles, below. + subdiv + .get_all_line_indices(1, |v| v.push(0)) + .split(|&i| i == 0) + .map(|strip| -> Vec { + strip + .iter() + .map(|&i| sphere_points[i as usize - 1]) + .collect() + }) + .collect() + } else { + // Gather edges from the triangles, deduplicating. + let lines: HashSet<(u32, u32)> = subdiv + .get_all_indices() + .chunks(3) + .flat_map(|triangle| { + let [i1, i2, i3] = <[u32; 3]>::try_from(triangle).unwrap(); + [(i1, i2), (i2, i3), (i3, i1)] + }) + .map(|(i1, i2)| if i1 > i2 { (i2, i1) } else { (i1, i2) }) + .collect(); + + lines + .into_iter() + .map(|(i1, i2)| vec![sphere_points[i1 as usize], sphere_points[i2 as usize]]) + .collect() + }; + WireframeMesh { + bbox: macaw::BoundingBox::from_center_size(Vec3::splat(0.0), Vec3::splat(1.0)), + vertex_count: line_strips.iter().map(|v| v.len()).sum(), + line_strips, + } + } + } +} + +// TODO(kpreid): A solid (rather than wireframe) mesh cache implementation should live here. diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs b/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs new file mode 100644 index 000000000000..8cc6fa9fa391 --- /dev/null +++ b/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs @@ -0,0 +1,346 @@ +use re_entity_db::{EntityPath, InstancePathHash}; +use re_log_types::Instance; +use re_query::range_zip_1x7; +use re_renderer::{LineDrawableBuilder, PickingLayerInstanceId, RenderContext}; +use re_types::{ + archetypes::Ellipsoids, + components::{ClassId, Color, HalfSize3D, KeypointId, Position3D, Radius, Rotation3D, Text}, +}; +use re_viewer_context::{ + auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, + ResolvedAnnotationInfos, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, + ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, + VisualizerQueryInfo, VisualizerSystem, +}; + +use crate::{ + contexts::SpatialSceneEntityContext, + proc_mesh::{ProcMeshKey, WireframeCache}, + view_kind::SpatialSpaceViewKind, + visualizers::{UiLabel, UiLabelTarget}, +}; + +use super::{ + entity_iterator::clamped, filter_visualizable_3d_entities, + process_annotation_and_keypoint_slices, process_color_slice, process_radius_slice, + SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, +}; + +// --- + +pub struct EllipsoidsVisualizer(SpatialViewVisualizerData); + +impl Default for EllipsoidsVisualizer { + fn default() -> Self { + Self(SpatialViewVisualizerData::new(Some( + SpatialSpaceViewKind::ThreeD, + ))) + } +} + +// 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, + half_sizes: &'a [HalfSize3D], + centers: impl Iterator + 'a, + labels: &'a [Text], + colors: &'a [egui::Color32], + annotation_infos: &'a ResolvedAnnotationInfos, + world_from_entity: glam::Affine3A, + ) -> impl Iterator + 'a { + let labels = clamped(labels, half_sizes.len()); + let centers = centers.chain(std::iter::repeat(&Position3D::ZERO)); + itertools::izip!(annotation_infos.iter(), centers, labels, colors) + .enumerate() + .filter_map(move |(i, (annotation_info, center, label, color))| { + let label = annotation_info.label(Some(label.as_str())); + 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), + ), + }) + }) + } + + fn process_data<'a>( + &mut self, + ctx: &QueryContext<'_>, + line_builder: &mut LineDrawableBuilder<'_>, + query: &ViewQuery<'_>, + ent_context: &SpatialSceneEntityContext<'_>, + data: impl Iterator>, + render_ctx: &RenderContext, + ) -> Result<(), SpaceViewSystemExecutionError> { + let entity_path = ctx.target_entity_path; + + for data in data { + let num_instances = data.half_sizes.len(); + if num_instances == 0 { + continue; + } + + let (annotation_infos, _) = process_annotation_and_keypoint_slices( + query.latest_at, + num_instances, + data.half_sizes.iter().map(|_| glam::Vec3::ZERO), + data.keypoint_ids, + data.class_ids, + &ent_context.annotations, + ); + + // Has not custom fallback for radius, so we use the default. + // TODO(andreas): It would be nice to have this handle this fallback as part of the query. + let radii = process_radius_slice( + entity_path, + num_instances, + data.line_radii, + Radius::default(), + ); + let colors = + process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); + + let centers = clamped(data.centers, num_instances); + self.0.ui_labels.extend(Self::process_labels( + entity_path, + data.half_sizes, + centers, + data.labels, + &colors, + &annotation_infos, + ent_context.world_from_entity, + )); + + let mut line_batch = line_builder + .batch("ellipsoids") + .depth_offset(ent_context.depth_offset) + .world_from_obj(ent_context.world_from_entity) + .outline_mask_ids(ent_context.highlight.overall) + .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); + + let mut bounding_box = macaw::BoundingBox::nothing(); + + let centers = + clamped(data.centers, num_instances).chain(std::iter::repeat(&Position3D::ZERO)); + let rotations = clamped(data.rotations, num_instances) + .chain(std::iter::repeat(&Rotation3D::IDENTITY)); + for (i, (half_size, ¢er, rotation, radius, color)) in + itertools::izip!(data.half_sizes, centers, rotations, radii, colors).enumerate() + { + let half_size_vec = glam::Vec3::from(*half_size); + let transform = glam::Affine3A::from_scale_rotation_translation( + glam::Vec3::from(*half_size), + rotation.0.into(), + center.into(), + ); + + // TODO(kpreid): subdivisions should be affected by on-screen size, not world size, + // and also be configurable. + let subdivisions = ((half_size_vec.length() * 2.5).round() as usize).clamp(1, 20); + + let Some(sphere_mesh) = ctx.viewer_ctx.cache.entry(|c: &mut WireframeCache| { + c.entry(ProcMeshKey::Sphere { subdivisions }, render_ctx) + }) else { + // TODO(kpreid): Should this be just returning nothing instead? + // If we do, there won't be any error report, just missing data. + + return Err(SpaceViewSystemExecutionError::DrawDataCreationError( + "Failed to allocate wireframe mesh".into(), + )); + }; + + bounding_box = bounding_box.union(sphere_mesh.bbox.transform_affine3(&transform)); + + for strip in &sphere_mesh.line_strips { + let box3d = line_batch + .add_strip(strip.iter().map(|&point| transform.transform_point3(point))) + .color(color) + .radius(radius) + .picking_instance_id(PickingLayerInstanceId(i as _)); + + if let Some(outline_mask_ids) = ent_context + .highlight + .instances + .get(&Instance::from(i as u64)) + { + box3d.outline_mask_ids(*outline_mask_ids); + } + } + } + + self.0.add_bounding_box( + entity_path.hash(), + bounding_box, + ent_context.world_from_entity, + ); + } + + Ok(()) + } +} + +// --- + +struct EllipsoidsComponentData<'a> { + // Point of views + half_sizes: &'a [HalfSize3D], + + // Clamped to edge + centers: &'a [Position3D], + rotations: &'a [Rotation3D], + colors: &'a [Color], + line_radii: &'a [Radius], + labels: &'a [Text], + keypoint_ids: &'a [KeypointId], + class_ids: &'a [ClassId], +} + +impl IdentifiedViewSystem for EllipsoidsVisualizer { + fn identifier() -> re_viewer_context::ViewSystemIdentifier { + "Ellipsoids".into() + } +} + +impl VisualizerSystem for EllipsoidsVisualizer { + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() + } + + fn filter_visualizable_entities( + &self, + entities: ApplicableEntities, + context: &dyn VisualizableFilterContext, + ) -> VisualizableEntities { + re_tracing::profile_function!(); + filter_visualizable_3d_entities(entities, context) + } + + fn execute( + &mut self, + ctx: &ViewContext<'_>, + view_query: &ViewQuery<'_>, + context_systems: &ViewContextCollection, + ) -> Result, SpaceViewSystemExecutionError> { + let Some(render_ctx) = ctx.viewer_ctx.render_ctx else { + return Err(SpaceViewSystemExecutionError::NoRenderContextError); + }; + + // TODO(kpreid): Should be using instanced meshes kept in GPU buffers + // instead of this immediate-mode strategy that copies every vertex every frame. + let mut line_builder = LineDrawableBuilder::new(render_ctx); + line_builder.radius_boost_in_ui_points_for_outlines(SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES); + + super::entity_iterator::process_archetype::( + ctx, + view_query, + context_systems, + |ctx, spatial_ctx, results| { + use re_space_view::RangeResultsExt as _; + + let resolver = ctx.recording().resolver(); + + let half_sizes = match results.get_required_component_dense::(resolver) + { + Some(vectors) => vectors?, + _ => return Ok(()), + }; + + let num_ellipsoids = half_sizes + .range_indexed() + .map(|(_, vectors)| vectors.len()) + .sum::(); + if num_ellipsoids == 0 { + return Ok(()); + } + + // Ideally we would reserve space here, but we don't know the mesh subdivision yet, + // and this will become moot when we switch to instanced meshes. + // 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 labels = results.get_or_empty_dense(resolver)?; + let class_ids = results.get_or_empty_dense(resolver)?; + let keypoint_ids = results.get_or_empty_dense(resolver)?; + + let data = range_zip_1x7( + half_sizes.range_indexed(), + centers.range_indexed(), + rotations.range_indexed(), + colors.range_indexed(), + line_radii.range_indexed(), + labels.range_indexed(), + class_ids.range_indexed(), + keypoint_ids.range_indexed(), + ) + .map( + |( + _index, + half_sizes, + centers, + rotations, + colors, + line_radii, + labels, + class_ids, + keypoint_ids, + )| { + 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(), + labels: labels.unwrap_or_default(), + class_ids: class_ids.unwrap_or_default(), + keypoint_ids: keypoint_ids.unwrap_or_default(), + } + }, + ); + + self.process_data( + ctx, + &mut line_builder, + view_query, + spatial_ctx, + data, + render_ctx, + )?; + + Ok(()) + }, + )?; + + Ok(vec![(line_builder.into_draw_data()?.into())]) + } + + fn data(&self) -> Option<&dyn std::any::Any> { + Some(self.0.as_any()) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_fallback_provider(&self) -> &dyn re_viewer_context::ComponentFallbackProvider { + self + } +} + +impl TypedComponentFallbackProvider for EllipsoidsVisualizer { + fn fallback_for(&self, ctx: &QueryContext<'_>) -> Color { + auto_color_for_entity_path(ctx.target_entity_path) + } +} + +re_viewer_context::impl_component_fallback_provider!(EllipsoidsVisualizer => [Color]); diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/mod.rs b/crates/viewer/re_space_view_spatial/src/visualizers/mod.rs index 9df2935a88cd..2c8b488de97c 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/mod.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/mod.rs @@ -7,6 +7,7 @@ mod boxes2d; mod boxes3d; mod cameras; mod depth_images; +mod ellipsoids; mod images; mod lines2d; mod lines3d; @@ -100,6 +101,7 @@ pub fn register_3d_spatial_visualizers( system_registry.register_visualizer::()?; system_registry.register_visualizer::()?; system_registry.register_visualizer::()?; + system_registry.register_visualizer::()?; system_registry.register_visualizer::()?; system_registry.register_visualizer::()?; Ok(()) diff --git a/docs/content/reference/types/archetypes.md b/docs/content/reference/types/archetypes.md index 9b4628bd4942..8708f2b52146 100644 --- a/docs/content/reference/types/archetypes.md +++ b/docs/content/reference/types/archetypes.md @@ -38,6 +38,7 @@ This page lists all built-in archetypes. * [`Arrows3D`](archetypes/arrows3d.md): 3D arrows with optional colors, radii, labels, etc. * [`Asset3D`](archetypes/asset3d.md): A prepacked 3D asset (`.gltf`, `.glb`, `.obj`, `.stl`, etc.). * [`Boxes3D`](archetypes/boxes3d.md): 3D boxes with half-extents and optional center, rotations, rotations, colors etc. +* [`Ellipsoids`](archetypes/ellipsoids.md): 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. * [`LineStrips3D`](archetypes/line_strips3d.md): 3D line strips with positions and optional colors, radii, labels, etc. * [`Mesh3D`](archetypes/mesh3d.md): A 3D triangle mesh as specified by its per-mesh and per-vertex properties. * [`Pinhole`](archetypes/pinhole.md): Camera perspective projection (a.k.a. intrinsics). diff --git a/docs/content/reference/types/archetypes/.gitattributes b/docs/content/reference/types/archetypes/.gitattributes index c8dbda543c26..a548f7dc5a01 100644 --- a/docs/content/reference/types/archetypes/.gitattributes +++ b/docs/content/reference/types/archetypes/.gitattributes @@ -11,6 +11,7 @@ boxes3d.md linguist-generated=true clear.md linguist-generated=true depth_image.md linguist-generated=true disconnected_space.md linguist-generated=true +ellipsoids.md linguist-generated=true image.md linguist-generated=true line_strips2d.md linguist-generated=true line_strips3d.md linguist-generated=true diff --git a/docs/content/reference/types/archetypes/ellipsoids.md b/docs/content/reference/types/archetypes/ellipsoids.md new file mode 100644 index 000000000000..d6dcf69bfe7b --- /dev/null +++ b/docs/content/reference/types/archetypes/ellipsoids.md @@ -0,0 +1,37 @@ +--- +title: "Ellipsoids" +--- + + +3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. + +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. + +## Components + +**Required**: [`HalfSize3D`](../components/half_size3d.md) + +**Recommended**: [`Position3D`](../components/position3d.md), [`Rotation3D`](../components/rotation3d.md), [`Color`](../components/color.md) + +**Optional**: [`Radius`](../components/radius.md), [`Text`](../components/text.md), [`ClassId`](../components/class_id.md) + +## Shown in +* [Spatial3DView](../views/spatial3d_view.md) +* [Spatial2DView](../views/spatial2d_view.md) (if logged above active projection) + +## API reference links + * 🌊 [C++ API docs for `Ellipsoids`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1archetypes_1_1Ellipsoids.html) + * 🐍 [Python API docs for `Ellipsoids`](https://ref.rerun.io/docs/python/stable/common/archetypes#rerun.archetypes.Ellipsoids) + * 🦀 [Rust API docs for `Ellipsoids`](https://docs.rs/rerun/latest/rerun/archetypes/struct.Ellipsoids.html) + +## Example + +### Batch of ellipsoids + +snippet: archetypes/ellipsoid_batch + diff --git a/docs/content/reference/types/components/class_id.md b/docs/content/reference/types/components/class_id.md index 5163b88d1498..e282465c550b 100644 --- a/docs/content/reference/types/components/class_id.md +++ b/docs/content/reference/types/components/class_id.md @@ -21,6 +21,7 @@ A 16-bit ID representing a type of semantic class. * [`Arrows3D`](../archetypes/arrows3d.md) * [`Boxes2D`](../archetypes/boxes2d.md) * [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md) * [`LineStrips2D`](../archetypes/line_strips2d.md) * [`LineStrips3D`](../archetypes/line_strips3d.md) * [`Mesh3D`](../archetypes/mesh3d.md) diff --git a/docs/content/reference/types/components/color.md b/docs/content/reference/types/components/color.md index c27b2a2ff556..0fdb7bff4283 100644 --- a/docs/content/reference/types/components/color.md +++ b/docs/content/reference/types/components/color.md @@ -25,6 +25,7 @@ byte is `R` and the least significant byte is `A`. * [`BarChart`](../archetypes/bar_chart.md) * [`Boxes2D`](../archetypes/boxes2d.md) * [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md) * [`LineStrips2D`](../archetypes/line_strips2d.md) * [`LineStrips3D`](../archetypes/line_strips3d.md) * [`Mesh3D`](../archetypes/mesh3d.md) diff --git a/docs/content/reference/types/components/half_size3d.md b/docs/content/reference/types/components/half_size3d.md index 1b915a394103..5db4e0f80b7f 100644 --- a/docs/content/reference/types/components/half_size3d.md +++ b/docs/content/reference/types/components/half_size3d.md @@ -23,3 +23,4 @@ Negative sizes indicate that the box is flipped along the respective axis, but t ## Used by * [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md) diff --git a/docs/content/reference/types/components/position3d.md b/docs/content/reference/types/components/position3d.md index 94461a066f08..286d6f7b7fd3 100644 --- a/docs/content/reference/types/components/position3d.md +++ b/docs/content/reference/types/components/position3d.md @@ -19,5 +19,6 @@ A position in 3D space. * [`Arrows3D`](../archetypes/arrows3d.md) * [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md) * [`Mesh3D`](../archetypes/mesh3d.md) * [`Points3D`](../archetypes/points3d.md) diff --git a/docs/content/reference/types/components/radius.md b/docs/content/reference/types/components/radius.md index 1e903fb0a93a..0b023869e1d8 100644 --- a/docs/content/reference/types/components/radius.md +++ b/docs/content/reference/types/components/radius.md @@ -28,6 +28,7 @@ The Viewer's UI scaling defaults to the OS scaling which typically is 100% for f * [`Arrows3D`](../archetypes/arrows3d.md) * [`Boxes2D`](../archetypes/boxes2d.md) * [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md) * [`LineStrips2D`](../archetypes/line_strips2d.md) * [`LineStrips3D`](../archetypes/line_strips3d.md) * [`Points2D`](../archetypes/points2d.md) diff --git a/docs/content/reference/types/components/rotation3d.md b/docs/content/reference/types/components/rotation3d.md index be0011da2848..a4491bd3c67f 100644 --- a/docs/content/reference/types/components/rotation3d.md +++ b/docs/content/reference/types/components/rotation3d.md @@ -18,3 +18,4 @@ A 3D rotation, represented either by a quaternion or a rotation around axis. ## Used by * [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md) diff --git a/docs/content/reference/types/components/text.md b/docs/content/reference/types/components/text.md index 7224b79fddcb..384ae791ecf2 100644 --- a/docs/content/reference/types/components/text.md +++ b/docs/content/reference/types/components/text.md @@ -21,6 +21,7 @@ A string of text, e.g. for labels and text documents. * [`Arrows3D`](../archetypes/arrows3d.md) * [`Boxes2D`](../archetypes/boxes2d.md) * [`Boxes3D`](../archetypes/boxes3d.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md) * [`LineStrips2D`](../archetypes/line_strips2d.md) * [`LineStrips3D`](../archetypes/line_strips3d.md) * [`Points2D`](../archetypes/points2d.md) diff --git a/docs/content/reference/types/views/spatial2d_view.md b/docs/content/reference/types/views/spatial2d_view.md index 48774402817e..16754e3fc6a0 100644 --- a/docs/content/reference/types/views/spatial2d_view.md +++ b/docs/content/reference/types/views/spatial2d_view.md @@ -58,6 +58,7 @@ snippet: views/spatial2d * [`Arrows3D`](../archetypes/arrows3d.md) (if logged above active projection) * [`Asset3D`](../archetypes/asset3d.md) (if logged above active projection) * [`Boxes3D`](../archetypes/boxes3d.md) (if logged above active projection) +* [`Ellipsoids`](../archetypes/ellipsoids.md) (if logged above active projection) * [`LineStrips3D`](../archetypes/line_strips3d.md) (if logged above active projection) * [`Mesh3D`](../archetypes/mesh3d.md) (if logged above active projection) * [`Points3D`](../archetypes/points3d.md) (if logged above active projection) diff --git a/docs/content/reference/types/views/spatial3d_view.md b/docs/content/reference/types/views/spatial3d_view.md index 90ab22d654ef..7741ae401efe 100644 --- a/docs/content/reference/types/views/spatial3d_view.md +++ b/docs/content/reference/types/views/spatial3d_view.md @@ -44,6 +44,7 @@ snippet: views/spatial3d * [`Boxes3D`](../archetypes/boxes3d.md) * [`Clear`](../archetypes/clear.md) * [`DisconnectedSpace`](../archetypes/disconnected_space.md) +* [`Ellipsoids`](../archetypes/ellipsoids.md) * [`LineStrips3D`](../archetypes/line_strips3d.md) * [`Mesh3D`](../archetypes/mesh3d.md) * [`Points3D`](../archetypes/points3d.md) diff --git a/docs/snippets/all/archetypes/ellipsoid_batch.cpp b/docs/snippets/all/archetypes/ellipsoid_batch.cpp new file mode 100644 index 000000000000..dd0340520810 --- /dev/null +++ b/docs/snippets/all/archetypes/ellipsoid_batch.cpp @@ -0,0 +1,38 @@ +// Log a batch of ellipsoids. + +#include + +int main() { + const auto rec = rerun::RecordingStream("rerun_example_ellipsoid_batch"); + rec.spawn().exit_on_failure(); + + // Let's build a snowman! + float belly_z = 2.5; + float head_z = 4.5; + rec.log( + "batch", + rerun::Ellipsoids::from_centers_and_half_sizes( + { + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, belly_z}, + {0.0f, 0.0f, head_z}, + {-0.6f, -0.77f, head_z}, + {0.6f, -0.77f, head_z}, + }, + { + {2.0f, 2.0f, 2.0f}, + {1.5f, 1.5f, 1.5f}, + {1.0f, 1.0f, 1.0f}, + {0.15f, 0.15f, 0.15f}, + {0.15f, 0.15f, 0.15f}, + } + ) + .with_colors({ + rerun::Rgba32(255, 255, 255), + rerun::Rgba32(255, 255, 255), + rerun::Rgba32(255, 255, 255), + rerun::Rgba32(0, 0, 0), + rerun::Rgba32(0, 0, 0), + }) + ); +} diff --git a/docs/snippets/all/archetypes/ellipsoid_batch.py b/docs/snippets/all/archetypes/ellipsoid_batch.py new file mode 100644 index 000000000000..b8c1aa3db06b --- /dev/null +++ b/docs/snippets/all/archetypes/ellipsoid_batch.py @@ -0,0 +1,35 @@ +"""Log a batch of ellipsoids.""" + +import rerun as rr + +rr.init("rerun_example_ellipsoid_batch", spawn=True) + +# Let's build a snowman! +belly_z = 2.5 +head_z = 4.5 +rr.log( + "batch", + rr.Ellipsoids( + centers=[ + [0.0, 0.0, 0.0], + [0.0, 0.0, belly_z], + [0.0, 0.0, head_z], + [-0.6, -0.77, head_z], + [0.6, -0.77, head_z], + ], + half_sizes=[ + [2.0, 2.0, 2.0], + [1.5, 1.5, 1.5], + [1.0, 1.0, 1.0], + [0.15, 0.15, 0.15], + [0.15, 0.15, 0.15], + ], + colors=[ + (255, 255, 255), + (255, 255, 255), + (255, 255, 255), + (0, 0, 0), + (0, 0, 0), + ], + ), +) diff --git a/docs/snippets/all/archetypes/ellipsoid_batch.rs b/docs/snippets/all/archetypes/ellipsoid_batch.rs new file mode 100644 index 000000000000..b6f02af7e1d0 --- /dev/null +++ b/docs/snippets/all/archetypes/ellipsoid_batch.rs @@ -0,0 +1,37 @@ +//! Log a batch of `Ellipsoids`. + +fn main() -> Result<(), Box> { + let rec = rerun::RecordingStreamBuilder::new("rerun_example_ellipsoid_batch").spawn()?; + + // Let's build a snowman! + let belly_z = 2.5; + let head_z = 4.5; + rec.log( + "batch", + &rerun::Ellipsoids::from_centers_and_half_sizes( + [ + (0.0, 0.0, 0.0), + (0.0, 0.0, belly_z), + (0.0, 0.0, head_z), + (-0.6, -0.77, head_z), + (0.6, -0.77, head_z), + ], + [ + (2.0, 2.0, 2.0), + (1.5, 1.5, 1.5), + (1.0, 1.0, 1.0), + (0.15, 0.15, 0.15), + (0.15, 0.15, 0.15), + ], + ) + .with_colors([ + rerun::Color::from_rgb(255, 255, 255), + rerun::Color::from_rgb(255, 255, 255), + rerun::Color::from_rgb(255, 255, 255), + rerun::Color::from_rgb(0, 0, 0), + rerun::Color::from_rgb(0, 0, 0), + ]), + )?; + + Ok(()) +} diff --git a/rerun_cpp/src/rerun/archetypes.hpp b/rerun_cpp/src/rerun/archetypes.hpp index 1075260a8335..2a160744ff06 100644 --- a/rerun_cpp/src/rerun/archetypes.hpp +++ b/rerun_cpp/src/rerun/archetypes.hpp @@ -12,6 +12,7 @@ #include "archetypes/clear.hpp" #include "archetypes/depth_image.hpp" #include "archetypes/disconnected_space.hpp" +#include "archetypes/ellipsoids.hpp" #include "archetypes/image.hpp" #include "archetypes/line_strips2d.hpp" #include "archetypes/line_strips3d.hpp" diff --git a/rerun_cpp/src/rerun/archetypes/.gitattributes b/rerun_cpp/src/rerun/archetypes/.gitattributes index bb3c86280a87..8ab0c080f3e2 100644 --- a/rerun_cpp/src/rerun/archetypes/.gitattributes +++ b/rerun_cpp/src/rerun/archetypes/.gitattributes @@ -21,6 +21,8 @@ depth_image.cpp linguist-generated=true depth_image.hpp linguist-generated=true disconnected_space.cpp linguist-generated=true disconnected_space.hpp linguist-generated=true +ellipsoids.cpp linguist-generated=true +ellipsoids.hpp linguist-generated=true image.cpp linguist-generated=true image.hpp linguist-generated=true line_strips2d.cpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids.cpp b/rerun_cpp/src/rerun/archetypes/ellipsoids.cpp new file mode 100644 index 000000000000..6baf7232881c --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids.cpp @@ -0,0 +1,63 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs". + +#include "ellipsoids.hpp" + +#include "../collection_adapter_builtins.hpp" + +namespace rerun::archetypes {} + +namespace rerun { + + Result> AsComponents::serialize( + const archetypes::Ellipsoids& archetype + ) { + using namespace archetypes; + std::vector cells; + cells.reserve(8); + + { + auto result = DataCell::from_loggable(archetype.half_sizes); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.centers.has_value()) { + auto result = DataCell::from_loggable(archetype.centers.value()); + 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()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.colors.has_value()) { + auto result = DataCell::from_loggable(archetype.colors.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.line_radii.has_value()) { + auto result = DataCell::from_loggable(archetype.line_radii.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.labels.has_value()) { + auto result = DataCell::from_loggable(archetype.labels.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.class_ids.has_value()) { + auto result = DataCell::from_loggable(archetype.class_ids.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + { + auto indicator = Ellipsoids::IndicatorComponent(); + auto result = DataCell::from_loggable(indicator); + RR_RETURN_NOT_OK(result.error); + cells.emplace_back(std::move(result.value)); + } + + return cells; + } +} // namespace rerun diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp new file mode 100644 index 000000000000..2dd007cc54e3 --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp @@ -0,0 +1,169 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs". + +#pragma once + +#include "../collection.hpp" +#include "../compiler_utils.hpp" +#include "../components/class_id.hpp" +#include "../components/color.hpp" +#include "../components/half_size3d.hpp" +#include "../components/position3d.hpp" +#include "../components/radius.hpp" +#include "../components/rotation3d.hpp" +#include "../components/text.hpp" +#include "../data_cell.hpp" +#include "../indicator_component.hpp" +#include "../result.hpp" + +#include +#include +#include +#include + +namespace rerun::archetypes { + /// **Archetype**: 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. + /// + /// 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. + struct Ellipsoids { + /// For each ellipsoid, half of its size on its three axes. + /// + /// If all components are equal, then it is a sphere with that radius. + Collection half_sizes; + + /// Optional center positions of the ellipsoids. + /// + /// If not specified, the centers will be at (0, 0, 0). + std::optional> centers; + + /// Optional rotations of the boxes. + /// + /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. + std::optional> rotations; + + /// Optional colors for the ellipsoids. + std::optional> colors; + + /// Optional radii for the lines used when the ellipsoid is rendered as a wireframe. + std::optional> line_radii; + + /// Optional text labels for the ellipsoids. + std::optional> labels; + + /// Optional `ClassId`s for the ellipsoids. + /// + /// The class ID provides colors and labels if not specified explicitly. + std::optional> class_ids; + + public: + static constexpr const char IndicatorComponentName[] = + "rerun.components.EllipsoidsIndicator"; + + /// Indicator component, used to identify the archetype when converting to a list of components. + using IndicatorComponent = rerun::components::IndicatorComponent; + + public: + // Extensions to generated type defined in 'ellipsoids_ext.cpp' + + /// Creates new `Ellipsoids` with `half_sizes` centered around the local origin. + static Ellipsoids from_half_sizes(Collection half_sizes) { + Ellipsoids ellipsoids; + ellipsoids.half_sizes = std::move(half_sizes); + return ellipsoids; + } + + /// Creates new `Ellipsoids` with `half_sizes` created from radii. + /// + /// TODO(andreas): This should not take an std::vector. + static Boxes3D from_radii(const std::vector& sizes); + + /// Creates new `Ellipsoids` with `centers` and `half_sizes`. + static Ellipsoids from_centers_and_half_sizes( + Collection centers, + Collection half_sizes + ) { + Ellipsoids ellipsoids; + ellipsoids.half_sizes = std::move(half_sizes); + ellipsoids.centers = std::move(centers); + return ellipsoids; + } + + /// Creates new `Ellipsoids` with `half_sizes` and `centers` created from centers and radii. + /// + /// TODO(andreas): This should not take an std::vector. + static Boxes3D from_centers_and_radii( + const std::vector& centers, const std::vector& radii + ); + + public: + Ellipsoids() = default; + Ellipsoids(Ellipsoids&& other) = default; + + /// Optional center positions of the ellipsoids. + /// + /// If not specified, the centers will be at (0, 0, 0). + 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 boxes. + /// + /// 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); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + + /// Optional colors for the ellipsoids. + Ellipsoids with_colors(Collection _colors) && { + colors = std::move(_colors); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + + /// Optional radii for the lines used when the ellipsoid is rendered as a wireframe. + Ellipsoids with_line_radii(Collection _line_radii) && { + line_radii = std::move(_line_radii); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + + /// Optional text labels for the ellipsoids. + Ellipsoids with_labels(Collection _labels) && { + labels = std::move(_labels); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + + /// Optional `ClassId`s for the ellipsoids. + /// + /// The class ID provides colors and labels if not specified explicitly. + Ellipsoids with_class_ids(Collection _class_ids) && { + class_ids = std::move(_class_ids); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + }; + +} // namespace rerun::archetypes + +namespace rerun { + /// \private + template + struct AsComponents; + + /// \private + template <> + struct AsComponents { + /// Serialize all set component batches. + static Result> serialize(const archetypes::Ellipsoids& archetype); + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp new file mode 100644 index 000000000000..80381dd97004 --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp @@ -0,0 +1,76 @@ +#include "ellipsoids.hpp" + +#include "../collection_adapter_builtins.hpp" + +// #define EDIT_EXTENSION + +namespace rerun { + namespace archetypes { + +#ifdef EDIT_EXTENSION + // + + /// Creates new `Ellipsoids` with `half_sizes` centered around the local origin. + static Ellipsoids from_half_sizes(Collection half_sizes) { + Ellipsoids ellipsoids; + ellipsoids.half_sizes = std::move(half_sizes); + return ellipsoids; + } + + /// Creates new `Ellipsoids` with `half_sizes` created from radii. + /// + /// TODO(andreas): This should not take an std::vector. + static Boxes3D from_radii(const std::vector& sizes); + + /// Creates new `Ellipsoids` with `centers` and `half_sizes`. + static Ellipsoids from_centers_and_half_sizes( + Collection centers, + Collection half_sizes + ) { + Ellipsoids ellipsoids; + ellipsoids.half_sizes = std::move(half_sizes); + ellipsoids.centers = std::move(centers); + return ellipsoids; + } + + /// Creates new `Ellipsoids` with `half_sizes` and `centers` created from centers and radii. + /// + /// TODO(andreas): This should not take an std::vector. + static Boxes3D from_centers_and_radii( + const std::vector& centers, const std::vector& radii + ); + + // +#endif + Ellipsoids Ellipsoids::from_radii(const std::vector& radii) { + std::vector half_sizes; + half_sizes.reserve(radii.size()); + for (const auto& radius : radii) { + half_sizes.emplace_back(radius, radius, radius); + } + + // Move the vector into a component batch. + return Ellipsoids::from_half_sizes(std::move(half_sizes)); + } + + Ellipsoids Ellipsoids::from_centers_and_radii( + const std::vector& centers, const std::vector& radii + ) { + auto num_components = std::min(centers.size(), radii.size()); + + std::vector half_sizes; + half_sizes.reserve(num_components); + + for (size_t i = 0; i < num_components; ++i) { + float radius = radii[i]; + half_sizes.emplace_back(radius, radius, radius); + } + + // We only transformed the radii; the centers are good as-is. + Ellipsoids ellipsoids; + ellipsoids.half_sizes = std::move(half_sizes); + ellipsoids.centers = std::move(centers); + return ellipsoids; + } + } // namespace archetypes +} // namespace rerun diff --git a/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes b/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes index 689720b6cbf0..a12d51c77170 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes @@ -12,6 +12,7 @@ boxes3d.py linguist-generated=true clear.py linguist-generated=true depth_image.py linguist-generated=true disconnected_space.py linguist-generated=true +ellipsoids.py linguist-generated=true image.py linguist-generated=true line_strips2d.py linguist-generated=true line_strips3d.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/archetypes/__init__.py b/rerun_py/rerun_sdk/rerun/archetypes/__init__.py index 890f04d766bb..9547a265e712 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/__init__.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/__init__.py @@ -12,6 +12,7 @@ from .clear import Clear from .depth_image import DepthImage from .disconnected_space import DisconnectedSpace +from .ellipsoids import Ellipsoids from .image import Image from .line_strips2d import LineStrips2D from .line_strips3d import LineStrips3D @@ -40,6 +41,7 @@ "Clear", "DepthImage", "DisconnectedSpace", + "Ellipsoids", "Image", "LineStrips2D", "LineStrips3D", diff --git a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py new file mode 100644 index 000000000000..526065593e14 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py @@ -0,0 +1,124 @@ +# DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/python/mod.rs +# Based on "crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs". + +# You can extend this class by creating a "EllipsoidsExt" class in "ellipsoids_ext.py". + +from __future__ import annotations + +from attrs import define, field + +from .. import components +from .._baseclasses import ( + Archetype, +) +from .ellipsoids_ext import EllipsoidsExt + +__all__ = ["Ellipsoids"] + + +@define(str=False, repr=False, init=False) +class Ellipsoids(EllipsoidsExt, Archetype): + """ + **Archetype**: 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. + + 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. + """ + + # __init__ can be found in ellipsoids_ext.py + + def __attrs_clear__(self) -> None: + """Convenience method for calling `__attrs_init__` with all `None`s.""" + self.__attrs_init__( + half_sizes=None, # type: ignore[arg-type] + centers=None, # type: ignore[arg-type] + rotations=None, # type: ignore[arg-type] + colors=None, # type: ignore[arg-type] + line_radii=None, # type: ignore[arg-type] + labels=None, # type: ignore[arg-type] + class_ids=None, # type: ignore[arg-type] + ) + + @classmethod + def _clear(cls) -> Ellipsoids: + """Produce an empty Ellipsoids, bypassing `__init__`.""" + inst = cls.__new__(cls) + inst.__attrs_clear__() + return inst + + half_sizes: components.HalfSize3DBatch = field( + metadata={"component": "required"}, + converter=components.HalfSize3DBatch._required, # type: ignore[misc] + ) + # For each ellipsoid, half of its size on its three axes. + # + # If all components are equal, then it is a sphere with that radius. + # + # (Docstring intentionally commented out to hide this field from the docs) + + centers: components.Position3DBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.Position3DBatch._optional, # type: ignore[misc] + ) + # Optional center positions of the ellipsoids. + # + # If not specified, the centers will be at (0, 0, 0). + # + # (Docstring intentionally commented out to hide this field from the docs) + + rotations: components.Rotation3DBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.Rotation3DBatch._optional, # type: ignore[misc] + ) + # Optional rotations of the boxes. + # + # If not specified, the axes of the ellipsoid align with the axes of the coordinate system. + # + # (Docstring intentionally commented out to hide this field from the docs) + + colors: components.ColorBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.ColorBatch._optional, # type: ignore[misc] + ) + # Optional colors for the ellipsoids. + # + # (Docstring intentionally commented out to hide this field from the docs) + + line_radii: components.RadiusBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.RadiusBatch._optional, # type: ignore[misc] + ) + # Optional radii for the lines used when the ellipsoid is rendered as a wireframe. + # + # (Docstring intentionally commented out to hide this field from the docs) + + labels: components.TextBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.TextBatch._optional, # type: ignore[misc] + ) + # Optional text labels for the ellipsoids. + # + # (Docstring intentionally commented out to hide this field from the docs) + + class_ids: components.ClassIdBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.ClassIdBatch._optional, # type: ignore[misc] + ) + # Optional `ClassId`s for the ellipsoids. + # + # The class ID provides colors and labels if not specified explicitly. + # + # (Docstring intentionally commented out to hide this field from the docs) + + __str__ = Archetype.__str__ + __repr__ = Archetype.__repr__ # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids_ext.py b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids_ext.py new file mode 100644 index 000000000000..aa8a9e9b0da5 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids_ext.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np + +from .. import datatypes +from ..error_utils import _send_warning_or_raise, catch_and_log_exceptions + + +class EllipsoidsExt: + """Extension for [Ellipsoids][rerun.archetypes.Ellipsoids].""" + + def __init__( + self: Any, + *, + half_sizes: datatypes.Vec3DArrayLike | None = None, + radii: datatypes.Float32ArrayLike | None = None, + centers: datatypes.Vec3DArrayLike | None = None, + rotations: datatypes.Rotation3DArrayLike | None = None, + colors: datatypes.Rgba32ArrayLike | None = None, + line_radii: datatypes.Float32ArrayLike | None = None, + labels: datatypes.Utf8ArrayLike | None = None, + class_ids: datatypes.ClassIdArrayLike | None = None, + ) -> None: + """ + Create a new instance of the Ellipsoids archetype. + + Parameters + ---------- + half_sizes: + All half-extents that make up the batch of ellipsoids. + Specify this instead of `radii` + radii: + All radii that make up this batch of spheres. + Specify this instead of `half_sizes` + centers: + Optional center positions of the ellipsoids. + rotations: + Optional rotations of the ellipsoids. + colors: + Optional colors for the ellipsoids. + line_radii: + Optional radii for the lines that make up the ellipsoids. + labels: + Optional text labels for the ellipsoids. + class_ids: + Optional `ClassId`s for the ellipsoids. + + The class ID provides colors and labels if not specified explicitly. + + """ + + with catch_and_log_exceptions(context=self.__class__.__name__): + if radii is not None: + if half_sizes is not None: + _send_warning_or_raise("Cannot specify both `radii` and `half_sizes` at the same time.", 1) + + radii = np.asarray(radii, dtype=np.float32) + # Duplicate [r1, r2, ...] to [[r1, r1, r1], [r2, r2, r2], ...] + half_sizes = np.repeat(np.expand_dims(radii, axis=1), 3, axis=1) + + self.__attrs_init__( + half_sizes=half_sizes, + centers=centers, + rotations=rotations, + colors=colors, + line_radii=line_radii, + labels=labels, + class_ids=class_ids, + ) + return + + self.__attrs_clear__() From 2f04451c8b7ce0d1f46a9a4cb9211196390e741b Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 10 Jul 2024 17:40:14 -0700 Subject: [PATCH 02/19] Ack cargo-deny duplicate detection. --- deny.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/deny.toml b/deny.toml index 072a8afa43a0..e8012aad92cd 100644 --- a/deny.toml +++ b/deny.toml @@ -52,6 +52,7 @@ skip = [ { name = "block2" }, # Old version via winit 0.29 -> icrate 0.0.4 { name = "cargo_metadata" }, # Older version used by ply-rs. It's small, and it's build-time only! { name = "cfg_aliases" }, # Tiny macro-only crate. Winit and other use older version than us. + { name = "glam" }, # Old version used by hexasphere { name = "hashbrown" }, # Old version used by polar-rs { name = "libloading" }, # Old version used by ash (vulkan binding), newer version used by khronos-egl { name = "memoffset" }, # Small crate From a69cf298daecb38450f6f4043fbfbf8dcce67390 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 10 Jul 2024 17:40:35 -0700 Subject: [PATCH 03/19] Add `Ellipsoids` to docs sections. --- rerun_py/docs/gen_common_index.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rerun_py/docs/gen_common_index.py b/rerun_py/docs/gen_common_index.py index 92aeaa0e2267..9e3d919ed3bd 100755 --- a/rerun_py/docs/gen_common_index.py +++ b/rerun_py/docs/gen_common_index.py @@ -183,6 +183,7 @@ class Section: "archetypes.Asset3D", "archetypes.Boxes2D", "archetypes.Boxes3D", + "archetypes.Ellipsoids", "archetypes.LineStrips2D", "archetypes.LineStrips3D", "archetypes.Mesh3D", From 2581b2123397944d6df21c04b39dc85b6abc22ba Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 10 Jul 2024 18:59:52 -0700 Subject: [PATCH 04/19] Fix doc link. --- crates/viewer/re_space_view_spatial/src/proc_mesh.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/viewer/re_space_view_spatial/src/proc_mesh.rs b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs index bb3ad4bc5a49..8b28031de2b6 100644 --- a/crates/viewer/re_space_view_spatial/src/proc_mesh.rs +++ b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs @@ -35,7 +35,7 @@ pub struct WireframeMesh { // ---------------------------------------------------------------------------- -/// Cache for the computation of wireframe meshes from [`SynthMeshKey`]s. +/// Cache for the computation of wireframe meshes from [`ProcMeshKey`]s. /// These meshes may then be rendered as instances of the cached /// mesh. #[derive(Default)] From ab7080ce30f1315afa5d7102538b9bf6fe1af02c Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 10 Jul 2024 19:00:46 -0700 Subject: [PATCH 05/19] Fix C++ paste-o. --- rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp index 80381dd97004..24f029e31b3a 100644 --- a/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp @@ -20,7 +20,7 @@ namespace rerun { /// Creates new `Ellipsoids` with `half_sizes` created from radii. /// /// TODO(andreas): This should not take an std::vector. - static Boxes3D from_radii(const std::vector& sizes); + static Ellipsoids from_radii(const std::vector& sizes); /// Creates new `Ellipsoids` with `centers` and `half_sizes`. static Ellipsoids from_centers_and_half_sizes( @@ -36,7 +36,7 @@ namespace rerun { /// Creates new `Ellipsoids` with `half_sizes` and `centers` created from centers and radii. /// /// TODO(andreas): This should not take an std::vector. - static Boxes3D from_centers_and_radii( + static Ellipsoids from_centers_and_radii( const std::vector& centers, const std::vector& radii ); From 117b22645d04f5ceb09dbef379ff5d65d15cfb55 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 10 Jul 2024 21:30:29 -0700 Subject: [PATCH 06/19] Re-codegen. --- rerun_cpp/src/rerun/archetypes/ellipsoids.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp index 2dd007cc54e3..de1b0030478a 100644 --- a/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp @@ -80,7 +80,7 @@ namespace rerun::archetypes { /// Creates new `Ellipsoids` with `half_sizes` created from radii. /// /// TODO(andreas): This should not take an std::vector. - static Boxes3D from_radii(const std::vector& sizes); + static Ellipsoids from_radii(const std::vector& sizes); /// Creates new `Ellipsoids` with `centers` and `half_sizes`. static Ellipsoids from_centers_and_half_sizes( @@ -96,7 +96,7 @@ namespace rerun::archetypes { /// Creates new `Ellipsoids` with `half_sizes` and `centers` created from centers and radii. /// /// TODO(andreas): This should not take an std::vector. - static Boxes3D from_centers_and_radii( + static Ellipsoids from_centers_and_radii( const std::vector& centers, const std::vector& radii ); From d29ea51a80d6650b197708ba84353a52cbf304e9 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 10:55:23 -0700 Subject: [PATCH 07/19] =?UTF-8?q?Fix=20mention=20of=20=E2=80=9Cboxes?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Emil Ernerfeldt --- .../store/re_types/definitions/rerun/archetypes/ellipsoids.fbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs b/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs index d94d1797c301..61431b85e7a2 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs @@ -40,7 +40,7 @@ table Ellipsoids ( /// If not specified, the centers will be at (0, 0, 0). centers: [rerun.components.Position3D] ("attr.rerun.component_recommended", nullable, order: 2000); - /// Optional rotations of the boxes. + /// Optional rotations of the ellipsoids. /// /// 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); From 3c3b0dd6224eaebffad4dc63f291ffd1ce7ba4eb Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 11:27:01 -0700 Subject: [PATCH 08/19] Use fixed subdivisions as a placeholder. --- .../re_space_view_spatial/src/visualizers/ellipsoids.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 8cc6fa9fa391..2dff69d8f1c7 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs @@ -134,16 +134,15 @@ impl EllipsoidsVisualizer { for (i, (half_size, ¢er, rotation, radius, color)) in itertools::izip!(data.half_sizes, centers, rotations, radii, colors).enumerate() { - let half_size_vec = glam::Vec3::from(*half_size); let transform = glam::Affine3A::from_scale_rotation_translation( glam::Vec3::from(*half_size), rotation.0.into(), center.into(), ); - // TODO(kpreid): subdivisions should be affected by on-screen size, not world size, - // and also be configurable. - let subdivisions = ((half_size_vec.length() * 2.5).round() as usize).clamp(1, 20); + // TODO(kpreid): subdivisions should be configurable, and possibly dynamic based on + // either world size or screen size (depending on application). + let subdivisions = 4; let Some(sphere_mesh) = ctx.viewer_ctx.cache.entry(|c: &mut WireframeCache| { c.entry(ProcMeshKey::Sphere { subdivisions }, render_ctx) From 4c2d341d5af22d4f29756b4e750bb199dc410de3 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 11:34:02 -0700 Subject: [PATCH 09/19] Hide TODO from docs and re-codegen --- crates/store/re_types/src/archetypes/ellipsoids.rs | 4 ++-- rerun_cpp/src/rerun/archetypes/ellipsoids.hpp | 4 ++-- rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp | 8 ++++---- rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/store/re_types/src/archetypes/ellipsoids.rs b/crates/store/re_types/src/archetypes/ellipsoids.rs index 52bad1ffe285..826eeeff65a1 100644 --- a/crates/store/re_types/src/archetypes/ellipsoids.rs +++ b/crates/store/re_types/src/archetypes/ellipsoids.rs @@ -38,7 +38,7 @@ pub struct Ellipsoids { /// If not specified, the centers will be at (0, 0, 0). pub centers: Option>, - /// Optional rotations of the boxes. + /// Optional rotations of the ellipsoids. /// /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. pub rotations: Option>, @@ -332,7 +332,7 @@ impl Ellipsoids { self } - /// Optional rotations of the boxes. + /// Optional rotations of the ellipsoids. /// /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. #[inline] diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp index de1b0030478a..3a6b3a9eba10 100644 --- a/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp @@ -41,7 +41,7 @@ namespace rerun::archetypes { /// If not specified, the centers will be at (0, 0, 0). std::optional> centers; - /// Optional rotations of the boxes. + /// Optional rotations of the ellipsoids. /// /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. std::optional> rotations; @@ -113,7 +113,7 @@ namespace rerun::archetypes { RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } - /// Optional rotations of the boxes. + /// Optional rotations of the ellipsoids. /// /// If not specified, the axes of the ellipsoid align with the axes of the coordinate system. Ellipsoids with_rotations(Collection _rotations) && { diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp index 24f029e31b3a..784c1c76799a 100644 --- a/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp @@ -18,8 +18,8 @@ namespace rerun { } /// Creates new `Ellipsoids` with `half_sizes` created from radii. - /// - /// TODO(andreas): This should not take an std::vector. + // + // TODO(andreas): This should not take an std::vector. static Ellipsoids from_radii(const std::vector& sizes); /// Creates new `Ellipsoids` with `centers` and `half_sizes`. @@ -34,8 +34,8 @@ namespace rerun { } /// Creates new `Ellipsoids` with `half_sizes` and `centers` created from centers and radii. - /// - /// TODO(andreas): This should not take an std::vector. + // + // TODO(andreas): This should not take an std::vector. static Ellipsoids from_centers_and_radii( const std::vector& centers, const std::vector& radii ); diff --git a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py index 526065593e14..adf0d2c41239 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py @@ -76,7 +76,7 @@ def _clear(cls) -> Ellipsoids: default=None, converter=components.Rotation3DBatch._optional, # type: ignore[misc] ) - # Optional rotations of the boxes. + # Optional rotations of the ellipsoids. # # If not specified, the axes of the ellipsoid align with the axes of the coordinate system. # From aecdd4d86736c227f1dddc0359ee6e9e8c414a8a Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 11:36:43 -0700 Subject: [PATCH 10/19] Apply suggestions from code review Co-authored-by: Emil Ernerfeldt --- .../store/re_types/definitions/rerun/archetypes/ellipsoids.fbs | 2 +- crates/store/re_types/src/archetypes/ellipsoids_ext.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs b/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs index 61431b85e7a2..6bd5b04cf8a1 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/ellipsoids.fbs @@ -9,7 +9,7 @@ namespace rerun.archetypes; // --- -/// 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. +/// 3D ellipsoids or spheres. /// /// This archetype is for ellipsoids or spheres whose size is a key part of the data /// (e.g. a bounding sphere). diff --git a/crates/store/re_types/src/archetypes/ellipsoids_ext.rs b/crates/store/re_types/src/archetypes/ellipsoids_ext.rs index ac5b06b3c2f2..cc2f0066f713 100644 --- a/crates/store/re_types/src/archetypes/ellipsoids_ext.rs +++ b/crates/store/re_types/src/archetypes/ellipsoids_ext.rs @@ -13,6 +13,7 @@ impl Ellipsoids { // Note: This is not a `Radius` component because the `Radius` component is for // the on-screen sizes of lines and points. #[inline] + #[doc(alias = "sphere")] pub fn from_radii(radii: impl IntoIterator) -> Self { Self::new(radii.into_iter().map(sphere_size)) } @@ -30,6 +31,7 @@ impl Ellipsoids { /// [`Self::half_sizes`] all equal to the given radii. // Note: This is not a `Radius` component because the `Radius` component is for // the on-screen sizes of lines and points. + #[doc(alias = "sphere")] #[inline] pub fn from_centers_and_radii( centers: impl IntoIterator>, From 52298f6753f9801bac16bc411dbb672104686cb8 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 11:39:22 -0700 Subject: [PATCH 11/19] Update note on hexasphere lines bug. --- crates/viewer/re_space_view_spatial/src/proc_mesh.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/proc_mesh.rs b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs index c0a9dffcba99..6b0693977e0b 100644 --- a/crates/viewer/re_space_view_spatial/src/proc_mesh.rs +++ b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs @@ -92,11 +92,13 @@ fn generate_wireframe(key: &ProcMeshKey, render_ctx: &RenderContext) -> Wirefram let sphere_points = subdiv.raw_data(); + // TODO(kpreid): There is a bug in `hexasphere` where it fails to return lines which + // reach the original corners of the shape. This will be fixed as part of + // , + // which is merged but not yet published on crates.io. + // When hexasphere 15.0 or 14.0.1 is available, update, then keep the first branch + // of this `if` only. let line_strips: Vec> = if false { - // TODO(kpreid): There seems to be a bug in `hexasphere` where it fails to - // return lines which reach the original corners of the shape. - // Until that’s fixed, this code is skipped and we use the kludge of extracting - // edges from the triangles, below. subdiv .get_all_line_indices(1, |v| v.push(0)) .split(|&i| i == 0) From 4f003aba6e17b294d5503bcb343f11c3082002a1 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 11:44:49 -0700 Subject: [PATCH 12/19] Remove glam multi-version workaround --- .../re_space_view_spatial/src/proc_mesh.rs | 17 +++++++++-------- deny.toml | 1 - 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/proc_mesh.rs b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs index 6b0693977e0b..274bb795d232 100644 --- a/crates/viewer/re_space_view_spatial/src/proc_mesh.rs +++ b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs @@ -84,13 +84,9 @@ fn generate_wireframe(key: &ProcMeshKey, render_ctx: &RenderContext) -> Wirefram match *key { ProcMeshKey::Sphere { subdivisions } => { - let subdiv = hexasphere::shapes::IcoSphere::new(subdivisions, |other_glam_vec| { - // `hexasphere` uses a different version of `glam` than we do. - // - <[f32; 3]>::from(other_glam_vec).into() - }); + let subdiv = hexasphere::shapes::IcoSphere::new(subdivisions, |_| ()); - let sphere_points = subdiv.raw_data(); + let sphere_points = subdiv.raw_points(); // TODO(kpreid): There is a bug in `hexasphere` where it fails to return lines which // reach the original corners of the shape. This will be fixed as part of @@ -105,7 +101,7 @@ fn generate_wireframe(key: &ProcMeshKey, render_ctx: &RenderContext) -> Wirefram .map(|strip| -> Vec { strip .iter() - .map(|&i| sphere_points[i as usize - 1]) + .map(|&i| sphere_points[i as usize - 1].into()) .collect() }) .collect() @@ -123,7 +119,12 @@ fn generate_wireframe(key: &ProcMeshKey, render_ctx: &RenderContext) -> Wirefram lines .into_iter() - .map(|(i1, i2)| vec![sphere_points[i1 as usize], sphere_points[i2 as usize]]) + .map(|(i1, i2)| { + vec![ + sphere_points[i1 as usize].into(), + sphere_points[i2 as usize].into(), + ] + }) .collect() }; WireframeMesh { diff --git a/deny.toml b/deny.toml index e8012aad92cd..072a8afa43a0 100644 --- a/deny.toml +++ b/deny.toml @@ -52,7 +52,6 @@ skip = [ { name = "block2" }, # Old version via winit 0.29 -> icrate 0.0.4 { name = "cargo_metadata" }, # Older version used by ply-rs. It's small, and it's build-time only! { name = "cfg_aliases" }, # Tiny macro-only crate. Winit and other use older version than us. - { name = "glam" }, # Old version used by hexasphere { name = "hashbrown" }, # Old version used by polar-rs { name = "libloading" }, # Old version used by ash (vulkan binding), newer version used by khronos-egl { name = "memoffset" }, # Small crate From 9ad178d5935f009292183303f80da4c7ceab67b0 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 11:59:20 -0700 Subject: [PATCH 13/19] Add and use `HalfSize3D::splat`. --- crates/store/re_types/src/archetypes/ellipsoids_ext.rs | 8 ++------ crates/store/re_types/src/components/half_size3d_ext.rs | 6 ++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/store/re_types/src/archetypes/ellipsoids_ext.rs b/crates/store/re_types/src/archetypes/ellipsoids_ext.rs index cc2f0066f713..a96093e3a9f2 100644 --- a/crates/store/re_types/src/archetypes/ellipsoids_ext.rs +++ b/crates/store/re_types/src/archetypes/ellipsoids_ext.rs @@ -15,7 +15,7 @@ impl Ellipsoids { #[inline] #[doc(alias = "sphere")] pub fn from_radii(radii: impl IntoIterator) -> Self { - Self::new(radii.into_iter().map(sphere_size)) + Self::new(radii.into_iter().map(HalfSize3D::splat)) } /// Creates a new [`Ellipsoids`] with [`Self::centers`] and [`Self::half_sizes`]. @@ -37,10 +37,6 @@ impl Ellipsoids { centers: impl IntoIterator>, radii: impl IntoIterator, ) -> Self { - Self::new(radii.into_iter().map(sphere_size)).with_centers(centers) + Self::new(radii.into_iter().map(HalfSize3D::splat)).with_centers(centers) } } - -fn sphere_size(radius: f32) -> HalfSize3D { - HalfSize3D::new(radius, radius, radius) -} diff --git a/crates/store/re_types/src/components/half_size3d_ext.rs b/crates/store/re_types/src/components/half_size3d_ext.rs index b63c909bc98c..905c127c5064 100644 --- a/crates/store/re_types/src/components/half_size3d_ext.rs +++ b/crates/store/re_types/src/components/half_size3d_ext.rs @@ -9,6 +9,12 @@ impl HalfSize3D { Self(Vec3D::new(half_width, half_height, half_depth)) } + /// Create a new half-extent with all the same sizes (a radius, perhaps). + #[inline] + pub const fn splat(half_size: f32) -> Self { + Self(Vec3D::new(half_size, half_size, half_size)) + } + /// Width of a box using this half-extent. #[inline] pub fn width(self) -> f32 { From d672869d370f924a33f6bacac84be14051059c3f Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 12:02:29 -0700 Subject: [PATCH 14/19] Correct bounding box --- crates/viewer/re_space_view_spatial/src/proc_mesh.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/viewer/re_space_view_spatial/src/proc_mesh.rs b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs index 274bb795d232..2551d15f04b4 100644 --- a/crates/viewer/re_space_view_spatial/src/proc_mesh.rs +++ b/crates/viewer/re_space_view_spatial/src/proc_mesh.rs @@ -128,7 +128,8 @@ fn generate_wireframe(key: &ProcMeshKey, render_ctx: &RenderContext) -> Wirefram .collect() }; WireframeMesh { - bbox: re_math::BoundingBox::from_center_size(Vec3::splat(0.0), Vec3::splat(1.0)), + // sphere’s radius is 1, so its size is 2 + bbox: re_math::BoundingBox::from_center_size(Vec3::splat(0.0), Vec3::splat(2.0)), vertex_count: line_strips.iter().map(|v| v.len()).sum(), line_strips, } From dd054ab169b631d4444926311a5f7c907296b1d1 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 15:06:49 -0700 Subject: [PATCH 15/19] Don't pass `half_sizes` to `process_labels`. --- .../src/visualizers/ellipsoids.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) 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 5c9ab83f6e63..d80703a6393e 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs @@ -43,15 +43,15 @@ impl Default for EllipsoidsVisualizer { impl EllipsoidsVisualizer { fn process_labels<'a>( entity_path: &'a EntityPath, - half_sizes: &'a [HalfSize3D], - centers: impl Iterator + 'a, + num_instances: usize, + centers: &'a [Position3D], labels: &'a [Text], colors: &'a [egui::Color32], annotation_infos: &'a ResolvedAnnotationInfos, world_from_entity: glam::Affine3A, ) -> impl Iterator + 'a { - let labels = clamped(labels, half_sizes.len()); - let centers = centers.chain(std::iter::repeat(&Position3D::ZERO)); + let labels = clamped(labels, num_instances); + let centers = clamped(centers, num_instances).chain(std::iter::repeat(&Position3D::ZERO)); itertools::izip!(annotation_infos.iter(), centers, labels, colors) .enumerate() .filter_map(move |(i, (annotation_info, center, label, color))| { @@ -107,11 +107,10 @@ impl EllipsoidsVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - let centers = clamped(data.centers, num_instances); self.0.ui_labels.extend(Self::process_labels( entity_path, - data.half_sizes, - centers, + num_instances, + data.centers, data.labels, &colors, &annotation_infos, From 93576975a01e17cff8d0b6434bc9ebff4b2ad069 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 15:13:21 -0700 Subject: [PATCH 16/19] Put radii constructors first. Add "sphere" keyword. --- .../re_types/src/archetypes/ellipsoids_ext.rs | 30 +++++++++---------- .../src/rerun/archetypes/ellipsoids_ext.cpp | 25 ++++++++-------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/crates/store/re_types/src/archetypes/ellipsoids_ext.rs b/crates/store/re_types/src/archetypes/ellipsoids_ext.rs index a96093e3a9f2..b4b152d532b2 100644 --- a/crates/store/re_types/src/archetypes/ellipsoids_ext.rs +++ b/crates/store/re_types/src/archetypes/ellipsoids_ext.rs @@ -3,12 +3,6 @@ use crate::components::{HalfSize3D, Position3D}; use super::Ellipsoids; impl Ellipsoids { - /// Creates a new [`Ellipsoids`] with [`Self::half_sizes`]. - #[inline] - pub fn from_half_sizes(half_sizes: impl IntoIterator>) -> Self { - Self::new(half_sizes) - } - /// Creates a new [`Ellipsoids`] for spheres with the given radii. // Note: This is not a `Radius` component because the `Radius` component is for // the on-screen sizes of lines and points. @@ -18,15 +12,6 @@ impl Ellipsoids { Self::new(radii.into_iter().map(HalfSize3D::splat)) } - /// Creates a new [`Ellipsoids`] with [`Self::centers`] and [`Self::half_sizes`]. - #[inline] - pub fn from_centers_and_half_sizes( - centers: impl IntoIterator>, - half_sizes: impl IntoIterator>, - ) -> Self { - Self::new(half_sizes).with_centers(centers) - } - /// Creates a new [`Ellipsoids`] for spheres with the given [`Self::centers`], and /// [`Self::half_sizes`] all equal to the given radii. // Note: This is not a `Radius` component because the `Radius` component is for @@ -39,4 +24,19 @@ impl Ellipsoids { ) -> Self { Self::new(radii.into_iter().map(HalfSize3D::splat)).with_centers(centers) } + + /// Creates a new [`Ellipsoids`] with [`Self::half_sizes`]. + #[inline] + pub fn from_half_sizes(half_sizes: impl IntoIterator>) -> Self { + Self::new(half_sizes) + } + + /// Creates a new [`Ellipsoids`] with [`Self::centers`] and [`Self::half_sizes`]. + #[inline] + pub fn from_centers_and_half_sizes( + centers: impl IntoIterator>, + half_sizes: impl IntoIterator>, + ) -> Self { + Self::new(half_sizes).with_centers(centers) + } } diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp index 784c1c76799a..7fd7c8a4126a 100644 --- a/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids_ext.cpp @@ -10,6 +10,19 @@ namespace rerun { #ifdef EDIT_EXTENSION // + /// Creates new `Ellipsoids` that are spheres, with `half_sizes` created from radii. + // + // TODO(andreas): This should not take an std::vector. + static Ellipsoids from_radii(const std::vector& sizes); + + /// Creates new `Ellipsoids` that are spheres, with `half_sizes` and `centers` created + /// from centers and radii. + // + // TODO(andreas): This should not take an std::vector. + static Ellipsoids from_centers_and_radii( + const std::vector& centers, const std::vector& radii + ); + /// Creates new `Ellipsoids` with `half_sizes` centered around the local origin. static Ellipsoids from_half_sizes(Collection half_sizes) { Ellipsoids ellipsoids; @@ -17,11 +30,6 @@ namespace rerun { return ellipsoids; } - /// Creates new `Ellipsoids` with `half_sizes` created from radii. - // - // TODO(andreas): This should not take an std::vector. - static Ellipsoids from_radii(const std::vector& sizes); - /// Creates new `Ellipsoids` with `centers` and `half_sizes`. static Ellipsoids from_centers_and_half_sizes( Collection centers, @@ -33,13 +41,6 @@ namespace rerun { return ellipsoids; } - /// Creates new `Ellipsoids` with `half_sizes` and `centers` created from centers and radii. - // - // TODO(andreas): This should not take an std::vector. - static Ellipsoids from_centers_and_radii( - const std::vector& centers, const std::vector& radii - ); - // #endif Ellipsoids Ellipsoids::from_radii(const std::vector& radii) { From 6aa20e57e04b0b9667e247e4e56ce607aec7bcb5 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 12 Jul 2024 15:15:50 -0700 Subject: [PATCH 17/19] Re-codegen. --- .../re_types/src/archetypes/ellipsoids.rs | 2 +- docs/content/reference/types/archetypes.md | 2 +- .../reference/types/archetypes/ellipsoids.md | 2 +- rerun_cpp/src/rerun/archetypes/ellipsoids.hpp | 27 ++++++++++--------- .../rerun_sdk/rerun/archetypes/ellipsoids.py | 2 +- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/crates/store/re_types/src/archetypes/ellipsoids.rs b/crates/store/re_types/src/archetypes/ellipsoids.rs index 826eeeff65a1..8f3fc89f3920 100644 --- a/crates/store/re_types/src/archetypes/ellipsoids.rs +++ b/crates/store/re_types/src/archetypes/ellipsoids.rs @@ -18,7 +18,7 @@ use ::re_types_core::SerializationResult; use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; use ::re_types_core::{DeserializationError, DeserializationResult}; -/// **Archetype**: 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. +/// **Archetype**: 3D ellipsoids or spheres. /// /// This archetype is for ellipsoids or spheres whose size is a key part of the data /// (e.g. a bounding sphere). diff --git a/docs/content/reference/types/archetypes.md b/docs/content/reference/types/archetypes.md index 45fac43c6afc..9e6dc545a532 100644 --- a/docs/content/reference/types/archetypes.md +++ b/docs/content/reference/types/archetypes.md @@ -38,7 +38,7 @@ This page lists all built-in archetypes. * [`Arrows3D`](archetypes/arrows3d.md): 3D arrows with optional colors, radii, labels, etc. * [`Asset3D`](archetypes/asset3d.md): A prepacked 3D asset (`.gltf`, `.glb`, `.obj`, `.stl`, etc.). * [`Boxes3D`](archetypes/boxes3d.md): 3D boxes with half-extents and optional center, rotations, colors etc. -* [`Ellipsoids`](archetypes/ellipsoids.md): 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. +* [`Ellipsoids`](archetypes/ellipsoids.md): 3D ellipsoids or spheres. * [`LineStrips3D`](archetypes/line_strips3d.md): 3D line strips with positions and optional colors, radii, labels, etc. * [`Mesh3D`](archetypes/mesh3d.md): A 3D triangle mesh as specified by its per-mesh and per-vertex properties. * [`Pinhole`](archetypes/pinhole.md): Camera perspective projection (a.k.a. intrinsics). diff --git a/docs/content/reference/types/archetypes/ellipsoids.md b/docs/content/reference/types/archetypes/ellipsoids.md index d6dcf69bfe7b..a43b72d9c378 100644 --- a/docs/content/reference/types/archetypes/ellipsoids.md +++ b/docs/content/reference/types/archetypes/ellipsoids.md @@ -3,7 +3,7 @@ title: "Ellipsoids" --- -3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. +3D ellipsoids or spheres. This archetype is for ellipsoids or spheres whose size is a key part of the data (e.g. a bounding sphere). diff --git a/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp index 3a6b3a9eba10..e22d516be6bd 100644 --- a/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp +++ b/rerun_cpp/src/rerun/archetypes/ellipsoids.hpp @@ -22,7 +22,7 @@ #include namespace rerun::archetypes { - /// **Archetype**: 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. + /// **Archetype**: 3D ellipsoids or spheres. /// /// This archetype is for ellipsoids or spheres whose size is a key part of the data /// (e.g. a bounding sphere). @@ -70,6 +70,19 @@ namespace rerun::archetypes { public: // Extensions to generated type defined in 'ellipsoids_ext.cpp' + /// Creates new `Ellipsoids` that are spheres, with `half_sizes` created from radii. + // + // TODO(andreas): This should not take an std::vector. + static Ellipsoids from_radii(const std::vector& sizes); + + /// Creates new `Ellipsoids` that are spheres, with `half_sizes` and `centers` created + /// from centers and radii. + // + // TODO(andreas): This should not take an std::vector. + static Ellipsoids from_centers_and_radii( + const std::vector& centers, const std::vector& radii + ); + /// Creates new `Ellipsoids` with `half_sizes` centered around the local origin. static Ellipsoids from_half_sizes(Collection half_sizes) { Ellipsoids ellipsoids; @@ -77,11 +90,6 @@ namespace rerun::archetypes { return ellipsoids; } - /// Creates new `Ellipsoids` with `half_sizes` created from radii. - /// - /// TODO(andreas): This should not take an std::vector. - static Ellipsoids from_radii(const std::vector& sizes); - /// Creates new `Ellipsoids` with `centers` and `half_sizes`. static Ellipsoids from_centers_and_half_sizes( Collection centers, @@ -93,13 +101,6 @@ namespace rerun::archetypes { return ellipsoids; } - /// Creates new `Ellipsoids` with `half_sizes` and `centers` created from centers and radii. - /// - /// TODO(andreas): This should not take an std::vector. - static Ellipsoids from_centers_and_radii( - const std::vector& centers, const std::vector& radii - ); - public: Ellipsoids() = default; Ellipsoids(Ellipsoids&& other) = default; diff --git a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py index adf0d2c41239..7a228344874d 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids.py @@ -19,7 +19,7 @@ @define(str=False, repr=False, init=False) class Ellipsoids(EllipsoidsExt, Archetype): """ - **Archetype**: 3D ellipsoids with half-extents and optional center, rotations, rotations, colors etc. + **Archetype**: 3D ellipsoids or spheres. This archetype is for ellipsoids or spheres whose size is a key part of the data (e.g. a bounding sphere). From 6c7c1d0ce195fe99f8e4cb175b5e93fefc60704b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 14 Jul 2024 15:58:02 +0200 Subject: [PATCH 18/19] Python: Export Ellipsoids --- rerun_py/rerun_sdk/rerun/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rerun_py/rerun_sdk/rerun/__init__.py b/rerun_py/rerun_sdk/rerun/__init__.py index fb91da4e20b7..c3fb2a806ad8 100644 --- a/rerun_py/rerun_sdk/rerun/__init__.py +++ b/rerun_py/rerun_sdk/rerun/__init__.py @@ -48,6 +48,7 @@ Clear as Clear, DepthImage as DepthImage, DisconnectedSpace as DisconnectedSpace, + Ellipsoids as Ellipsoids, Image as Image, LineStrips2D as LineStrips2D, LineStrips3D as LineStrips3D, From 080dd4a9c03304f515b799d978d4c2babefd3e88 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 14 Jul 2024 15:59:15 +0200 Subject: [PATCH 19/19] Use a much more coarse mesh by default --- .../viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b58adc21a66b..48b7bc3f7d70 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/ellipsoids.rs @@ -140,7 +140,7 @@ impl EllipsoidsVisualizer { // TODO(kpreid): subdivisions should be configurable, and possibly dynamic based on // either world size or screen size (depending on application). - let subdivisions = 4; + let subdivisions = 2; let Some(sphere_mesh) = ctx.viewer_ctx.cache.entry(|c: &mut WireframeCache| { c.entry(ProcMeshKey::Sphere { subdivisions }, render_ctx)