From 50b9182aa2c87f106f842804aaaf438139d65778 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 11:05:08 +0200 Subject: [PATCH 01/16] centralize max labels limit. not raising it for the moment --- crates/re_space_view_spatial/src/visualizers/arrows2d.rs | 5 +---- crates/re_space_view_spatial/src/visualizers/arrows3d.rs | 5 +---- crates/re_space_view_spatial/src/visualizers/boxes2d.rs | 2 +- crates/re_space_view_spatial/src/visualizers/lines2d.rs | 5 +---- crates/re_space_view_spatial/src/visualizers/lines3d.rs | 5 +---- crates/re_space_view_spatial/src/visualizers/mod.rs | 3 +++ crates/re_space_view_spatial/src/visualizers/points2d.rs | 7 ++----- crates/re_space_view_spatial/src/visualizers/points3d.rs | 7 ++----- 8 files changed, 12 insertions(+), 27 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs index 1a9f1190d68d..c115e253c83e 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs @@ -27,15 +27,12 @@ use super::{ // --- pub struct Arrows2DVisualizer { - /// If the number of arrows in the batch is > max_labels, don't render point labels. - pub max_labels: usize, pub data: SpatialViewVisualizerData, } impl Default for Arrows2DVisualizer { fn default() -> Self { Self { - max_labels: 10, data: SpatialViewVisualizerData::new(Some(SpatialSpaceViewKind::TwoD)), } } @@ -115,7 +112,7 @@ impl Arrows2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= self.max_labels { + if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { let origins = clamped(data.origins, num_instances); self.data.ui_labels.extend(Self::process_labels( entity_path, diff --git a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs index 277ea7156ec3..709196c4e92a 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs @@ -27,15 +27,12 @@ use super::{ // --- pub struct Arrows3DVisualizer { - /// If the number of arrows in the batch is > max_labels, don't render point labels. - pub max_labels: usize, pub data: SpatialViewVisualizerData, } impl Default for Arrows3DVisualizer { fn default() -> Self { Self { - max_labels: 10, data: SpatialViewVisualizerData::new(Some(SpatialSpaceViewKind::ThreeD)), } } @@ -117,7 +114,7 @@ impl Arrows3DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= self.max_labels { + if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { let origins = clamped(data.origins, num_instances); self.data.ui_labels.extend(Self::process_labels( entity_path, diff --git a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs index 369c24782c09..629159657d21 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs @@ -115,7 +115,7 @@ impl Boxes2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= self.max_labels { + if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { let centers = clamped(data.centers, num_instances); self.data.ui_labels.extend(Self::process_labels( entity_path, diff --git a/crates/re_space_view_spatial/src/visualizers/lines2d.rs b/crates/re_space_view_spatial/src/visualizers/lines2d.rs index ac025fbbc2a4..679a9606d8f1 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines2d.rs @@ -28,15 +28,12 @@ use super::{ // --- pub struct Lines2DVisualizer { - /// If the number of arrows in the batch is > max_labels, don't render point labels. - pub max_labels: usize, pub data: SpatialViewVisualizerData, } impl Default for Lines2DVisualizer { fn default() -> Self { Self { - max_labels: 10, data: SpatialViewVisualizerData::new(Some(SpatialSpaceViewKind::TwoD)), } } @@ -114,7 +111,7 @@ impl Lines2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= self.max_labels { + if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { self.data.ui_labels.extend(Self::process_labels( entity_path, data.strips, diff --git a/crates/re_space_view_spatial/src/visualizers/lines3d.rs b/crates/re_space_view_spatial/src/visualizers/lines3d.rs index 7a351ee739e1..2cf3d0e35a0c 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines3d.rs @@ -28,15 +28,12 @@ use super::{ // --- pub struct Lines3DVisualizer { - /// If the number of arrows in the batch is > max_labels, don't render point labels. - pub max_labels: usize, pub data: SpatialViewVisualizerData, } impl Default for Lines3DVisualizer { fn default() -> Self { Self { - max_labels: 10, data: SpatialViewVisualizerData::new(Some(SpatialSpaceViewKind::ThreeD)), } } @@ -117,7 +114,7 @@ impl Lines3DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= self.max_labels { + if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { self.data.ui_labels.extend(Self::process_labels( entity_path, data.strips, diff --git a/crates/re_space_view_spatial/src/visualizers/mod.rs b/crates/re_space_view_spatial/src/visualizers/mod.rs index 309faa10a34e..031f7134150b 100644 --- a/crates/re_space_view_spatial/src/visualizers/mod.rs +++ b/crates/re_space_view_spatial/src/visualizers/mod.rs @@ -59,6 +59,9 @@ pub const SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES: f32 = 1.0; /// Gap between points and their outline. pub const SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES: f32 = 2.5; +/// Maximum number of labels after which we stop displaying labels for that entity all together. +pub const MAX_NUM_LABELS_PER_ENTITY: usize = 10; + pub fn register_2d_spatial_visualizers( system_registry: &mut SpaceViewSystemRegistrator<'_>, ) -> Result<(), SpaceViewClassRegistryError> { diff --git a/crates/re_space_view_spatial/src/visualizers/points2d.rs b/crates/re_space_view_spatial/src/visualizers/points2d.rs index 1ec63c315666..1bfe645eb8a4 100644 --- a/crates/re_space_view_spatial/src/visualizers/points2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points2d.rs @@ -26,21 +26,18 @@ use crate::{ use super::{ entity_iterator::clamped, filter_visualizable_2d_entities, SpatialViewVisualizerData, - SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES, + MAX_NUM_LABELS_PER_ENTITY, SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES, }; // --- pub struct Points2DVisualizer { - /// If the number of points in the batch is > max_labels, don't render point labels. - pub max_labels: usize, pub data: SpatialViewVisualizerData, } impl Default for Points2DVisualizer { fn default() -> Self { Self { - max_labels: 10, data: SpatialViewVisualizerData::new(Some(SpatialSpaceViewKind::TwoD)), } } @@ -158,7 +155,7 @@ impl Points2DVisualizer { load_keypoint_connections(line_builder, ent_context, entity_path, &keypoints)?; - if num_instances <= self.max_labels { + if num_instances <= MAX_NUM_LABELS_PER_ENTITY { self.data.ui_labels.extend(Self::process_labels( entity_path, &positions, diff --git a/crates/re_space_view_spatial/src/visualizers/points3d.rs b/crates/re_space_view_spatial/src/visualizers/points3d.rs index 2f06d005eea8..c0a40312a32b 100644 --- a/crates/re_space_view_spatial/src/visualizers/points3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points3d.rs @@ -26,21 +26,18 @@ use crate::{ use super::{ entity_iterator::clamped, filter_visualizable_3d_entities, SpatialViewVisualizerData, - SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES, + MAX_NUM_LABELS_PER_ENTITY, SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES, }; // --- pub struct Points3DVisualizer { - /// If the number of points in the batch is > max_labels, don't render point labels. - pub max_labels: usize, pub data: SpatialViewVisualizerData, } impl Default for Points3DVisualizer { fn default() -> Self { Self { - max_labels: 10, data: SpatialViewVisualizerData::new(Some(SpatialSpaceViewKind::ThreeD)), } } @@ -164,7 +161,7 @@ impl Points3DVisualizer { load_keypoint_connections(line_builder, ent_context, entity_path, &keypoints)?; - if num_instances <= self.max_labels { + if num_instances <= MAX_NUM_LABELS_PER_ENTITY { self.data.ui_labels.extend(Self::process_labels( entity_path, positions, From 64e57347641db3b76134746342f3023bd7318977 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 11:40:10 +0200 Subject: [PATCH 02/16] center labels on point clouds --- .../src/visualizers/points2d.rs | 19 +++++++++++++++---- .../src/visualizers/points3d.rs | 19 +++++++++++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/points2d.rs b/crates/re_space_view_spatial/src/visualizers/points2d.rs index 1bfe645eb8a4..07356ef646a5 100644 --- a/crates/re_space_view_spatial/src/visualizers/points2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points2d.rs @@ -147,18 +147,29 @@ impl Points2DVisualizer { } } - self.data.add_bounding_box_from_points( + let obj_space_bounding_box = macaw::BoundingBox::from_points(positions.iter().copied()); + self.data.add_bounding_box( entity_path.hash(), - positions.iter().copied(), + obj_space_bounding_box, ent_context.world_from_entity, ); load_keypoint_connections(line_builder, ent_context, entity_path, &keypoints)?; - if num_instances <= MAX_NUM_LABELS_PER_ENTITY { + if data.labels.len() == 1 || num_instances <= MAX_NUM_LABELS_PER_ENTITY { + // If there's many points but only a single label, place the single label at the middle of the visualization. + let obj_space_bbox_center; + let label_positions = if data.labels.len() == 1 && positions.len() > 1 { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + obj_space_bbox_center = [obj_space_bounding_box.center()]; + &obj_space_bbox_center + } else { + positions.as_slice() + }; + self.data.ui_labels.extend(Self::process_labels( entity_path, - &positions, + label_positions, data.labels, &colors, &annotation_infos, diff --git a/crates/re_space_view_spatial/src/visualizers/points3d.rs b/crates/re_space_view_spatial/src/visualizers/points3d.rs index c0a40312a32b..152be87dbd20 100644 --- a/crates/re_space_view_spatial/src/visualizers/points3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points3d.rs @@ -153,18 +153,29 @@ impl Points3DVisualizer { } } - self.data.add_bounding_box_from_points( + let obj_space_bounding_box = macaw::BoundingBox::from_points(positions.iter().copied()); + self.data.add_bounding_box( entity_path.hash(), - positions.iter().copied(), + obj_space_bounding_box, ent_context.world_from_entity, ); load_keypoint_connections(line_builder, ent_context, entity_path, &keypoints)?; - if num_instances <= MAX_NUM_LABELS_PER_ENTITY { + if data.labels.len() == 1 || num_instances <= MAX_NUM_LABELS_PER_ENTITY { + // If there's many points but only a single label, place the single label at the middle of the visualization. + let obj_space_bbox_center; + let label_positions = if data.labels.len() == 1 && positions.len() > 1 { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + obj_space_bbox_center = [obj_space_bounding_box.center()]; + &obj_space_bbox_center + } else { + positions + }; + self.data.ui_labels.extend(Self::process_labels( entity_path, - positions, + label_positions, data.labels, &colors, &annotation_infos, From 049e15555c5e6da310255f595c903377e6ce3977 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 11:51:46 +0200 Subject: [PATCH 03/16] move modules that don't implement visualizers in view_spatial/src/visualizers into subfolder --- .../src/visualizers/depth_images.rs | 5 +-- .../src/visualizers/images.rs | 5 +-- .../src/visualizers/mod.rs | 39 +++---------------- .../src/visualizers/segmentation_images.rs | 5 +-- .../{ => utilities}/entity_iterator.rs | 0 .../src/visualizers/utilities/labels.rs | 28 +++++++++++++ .../src/visualizers/utilities/mod.rs | 8 ++++ .../spatial_view_visualizer.rs | 3 +- .../textured_rect.rs} | 0 9 files changed, 47 insertions(+), 46 deletions(-) rename crates/re_space_view_spatial/src/visualizers/{ => utilities}/entity_iterator.rs (100%) create mode 100644 crates/re_space_view_spatial/src/visualizers/utilities/labels.rs create mode 100644 crates/re_space_view_spatial/src/visualizers/utilities/mod.rs rename crates/re_space_view_spatial/src/visualizers/{ => utilities}/spatial_view_visualizer.rs (95%) rename crates/re_space_view_spatial/src/visualizers/{textured_rect_utils.rs => utilities/textured_rect.rs} (100%) diff --git a/crates/re_space_view_spatial/src/visualizers/depth_images.rs b/crates/re_space_view_spatial/src/visualizers/depth_images.rs index 4c1520f277f6..907922b7b5b4 100644 --- a/crates/re_space_view_spatial/src/visualizers/depth_images.rs +++ b/crates/re_space_view_spatial/src/visualizers/depth_images.rs @@ -27,10 +27,7 @@ use crate::{ PickableImageRect, SpatialSpaceView2D, SpatialSpaceView3D, }; -use super::{ - tensor_to_textured_rect, textured_rect_utils::bounding_box_for_textured_rect, - SpatialViewVisualizerData, -}; +use super::{bounding_box_for_textured_rect, tensor_to_textured_rect, SpatialViewVisualizerData}; pub struct DepthImageVisualizer { pub data: SpatialViewVisualizerData, diff --git a/crates/re_space_view_spatial/src/visualizers/images.rs b/crates/re_space_view_spatial/src/visualizers/images.rs index 7fedce26172f..543cedf4048c 100644 --- a/crates/re_space_view_spatial/src/visualizers/images.rs +++ b/crates/re_space_view_spatial/src/visualizers/images.rs @@ -20,10 +20,7 @@ use crate::{ PickableImageRect, SpatialSpaceView2D, }; -use super::{ - tensor_to_textured_rect, textured_rect_utils::bounding_box_for_textured_rect, - SpatialViewVisualizerData, -}; +use super::{bounding_box_for_textured_rect, tensor_to_textured_rect, SpatialViewVisualizerData}; pub struct ImageVisualizer { pub data: SpatialViewVisualizerData, diff --git a/crates/re_space_view_spatial/src/visualizers/mod.rs b/crates/re_space_view_spatial/src/visualizers/mod.rs index 031f7134150b..a6e5f76f9a96 100644 --- a/crates/re_space_view_spatial/src/visualizers/mod.rs +++ b/crates/re_space_view_spatial/src/visualizers/mod.rs @@ -7,7 +7,6 @@ mod boxes2d; mod boxes3d; mod cameras; mod depth_images; -mod entity_iterator; mod images; mod lines2d; mod lines3d; @@ -15,23 +14,24 @@ mod meshes; mod points2d; mod points3d; mod segmentation_images; -mod spatial_view_visualizer; -mod textured_rect_utils; mod transform3d_arrows; +mod utilities; pub use cameras::CamerasVisualizer; pub use depth_images::DepthImageVisualizer; pub use images::ImageVisualizer; pub use segmentation_images::SegmentationImageVisualizer; -pub use spatial_view_visualizer::SpatialViewVisualizerData; -pub use textured_rect_utils::tensor_to_textured_rect; pub use transform3d_arrows::{add_axis_arrows, AxisLengthDetector, Transform3DArrowsVisualizer}; +pub use utilities::{ + bounding_box_for_textured_rect, entity_iterator, tensor_to_textured_rect, + SpatialViewVisualizerData, UiLabel, UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY, +}; // --- use ahash::HashMap; -use re_entity_db::{EntityPath, InstancePathHash}; +use re_entity_db::EntityPath; use re_types::{ components::Color, datatypes::{KeypointId, KeypointPair}, @@ -59,9 +59,6 @@ pub const SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES: f32 = 1.0; /// Gap between points and their outline. pub const SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES: f32 = 2.5; -/// Maximum number of labels after which we stop displaying labels for that entity all together. -pub const MAX_NUM_LABELS_PER_ENTITY: usize = 10; - pub fn register_2d_spatial_visualizers( system_registry: &mut SpaceViewSystemRegistrator<'_>, ) -> Result<(), SpaceViewClassRegistryError> { @@ -280,30 +277,6 @@ fn process_annotation_and_keypoint_slices( } } -#[derive(Clone)] -pub enum UiLabelTarget { - /// Labels a given rect (in scene coordinates) - Rect(egui::Rect), - - /// Labels a given point (in scene coordinates) - Point2D(egui::Pos2), - - /// A point in space. - Position3D(glam::Vec3), -} - -#[derive(Clone)] -pub struct UiLabel { - pub text: String, - pub color: egui::Color32, - - /// The shape/position being labeled. - pub target: UiLabelTarget, - - /// What is hovered if this label is hovered. - pub labeled_instance: InstancePathHash, -} - pub fn load_keypoint_connections( line_builder: &mut re_renderer::LineDrawableBuilder<'_>, ent_context: &SpatialSceneEntityContext<'_>, diff --git a/crates/re_space_view_spatial/src/visualizers/segmentation_images.rs b/crates/re_space_view_spatial/src/visualizers/segmentation_images.rs index e8284e1db594..9a0b63e1d6fa 100644 --- a/crates/re_space_view_spatial/src/visualizers/segmentation_images.rs +++ b/crates/re_space_view_spatial/src/visualizers/segmentation_images.rs @@ -20,10 +20,7 @@ use crate::{ visualizers::filter_visualizable_2d_entities, PickableImageRect, SpatialSpaceView2D, }; -use super::{ - tensor_to_textured_rect, textured_rect_utils::bounding_box_for_textured_rect, - SpatialViewVisualizerData, -}; +use super::{bounding_box_for_textured_rect, tensor_to_textured_rect, SpatialViewVisualizerData}; pub struct SegmentationImageVisualizer { pub data: SpatialViewVisualizerData, diff --git a/crates/re_space_view_spatial/src/visualizers/entity_iterator.rs b/crates/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs similarity index 100% rename from crates/re_space_view_spatial/src/visualizers/entity_iterator.rs rename to crates/re_space_view_spatial/src/visualizers/utilities/entity_iterator.rs diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs new file mode 100644 index 000000000000..fbeab5a7828e --- /dev/null +++ b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs @@ -0,0 +1,28 @@ +use re_entity_db::InstancePathHash; + +#[derive(Clone)] +pub enum UiLabelTarget { + /// Labels a given rect (in scene coordinates) + Rect(egui::Rect), + + /// Labels a given point (in scene coordinates) + Point2D(egui::Pos2), + + /// A point in space. + Position3D(glam::Vec3), +} + +#[derive(Clone)] +pub struct UiLabel { + pub text: String, + pub color: egui::Color32, + + /// The shape/position being labeled. + pub target: UiLabelTarget, + + /// What is hovered if this label is hovered. + pub labeled_instance: InstancePathHash, +} + +/// Maximum number of labels after which we stop displaying labels for that entity all together. +pub const MAX_NUM_LABELS_PER_ENTITY: usize = 10; diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs b/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs new file mode 100644 index 000000000000..040c9968aa0b --- /dev/null +++ b/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs @@ -0,0 +1,8 @@ +pub mod entity_iterator; +mod labels; +mod spatial_view_visualizer; +mod textured_rect; + +pub use labels::{UiLabel, UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY}; +pub use spatial_view_visualizer::SpatialViewVisualizerData; +pub use textured_rect::{bounding_box_for_textured_rect, tensor_to_textured_rect}; diff --git a/crates/re_space_view_spatial/src/visualizers/spatial_view_visualizer.rs b/crates/re_space_view_spatial/src/visualizers/utilities/spatial_view_visualizer.rs similarity index 95% rename from crates/re_space_view_spatial/src/visualizers/spatial_view_visualizer.rs rename to crates/re_space_view_spatial/src/visualizers/utilities/spatial_view_visualizer.rs index 59f46bbe226d..0cd0fbb02ed8 100644 --- a/crates/re_space_view_spatial/src/visualizers/spatial_view_visualizer.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/spatial_view_visualizer.rs @@ -1,6 +1,7 @@ use re_log_types::EntityPathHash; -use crate::{view_kind::SpatialSpaceViewKind, visualizers::UiLabel}; +use super::UiLabel; +use crate::view_kind::SpatialSpaceViewKind; /// Common data struct for all spatial scene elements. /// diff --git a/crates/re_space_view_spatial/src/visualizers/textured_rect_utils.rs b/crates/re_space_view_spatial/src/visualizers/utilities/textured_rect.rs similarity index 100% rename from crates/re_space_view_spatial/src/visualizers/textured_rect_utils.rs rename to crates/re_space_view_spatial/src/visualizers/utilities/textured_rect.rs From e57cc1ab882fd28a132d7c14a200ed1ebfa70bc0 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 11:55:14 +0200 Subject: [PATCH 04/16] move process_labels_3d out --- .../src/visualizers/mod.rs | 2 +- .../src/visualizers/points3d.rs | 42 +++---------------- .../src/visualizers/utilities/labels.rs | 33 +++++++++++++++ .../src/visualizers/utilities/mod.rs | 2 +- 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/mod.rs b/crates/re_space_view_spatial/src/visualizers/mod.rs index a6e5f76f9a96..4cf1412ce12c 100644 --- a/crates/re_space_view_spatial/src/visualizers/mod.rs +++ b/crates/re_space_view_spatial/src/visualizers/mod.rs @@ -23,7 +23,7 @@ pub use images::ImageVisualizer; pub use segmentation_images::SegmentationImageVisualizer; pub use transform3d_arrows::{add_axis_arrows, AxisLengthDetector, Transform3DArrowsVisualizer}; pub use utilities::{ - bounding_box_for_textured_rect, entity_iterator, tensor_to_textured_rect, + bounding_box_for_textured_rect, entity_iterator, process_labels_3d, tensor_to_textured_rect, SpatialViewVisualizerData, UiLabel, UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY, }; diff --git a/crates/re_space_view_spatial/src/visualizers/points3d.rs b/crates/re_space_view_spatial/src/visualizers/points3d.rs index 152be87dbd20..268f83f1f082 100644 --- a/crates/re_space_view_spatial/src/visualizers/points3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points3d.rs @@ -1,7 +1,5 @@ use itertools::Itertools as _; -use re_entity_db::{EntityPath, InstancePathHash}; -use re_log_types::Instance; use re_query::range_zip_1x5; use re_renderer::{LineDrawableBuilder, PickingLayerInstanceId, PointCloudBuilder}; use re_types::{ @@ -10,8 +8,8 @@ use re_types::{ }; use re_viewer_context::{ auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, - ResolvedAnnotationInfos, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, - ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, + ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; @@ -20,12 +18,12 @@ use crate::{ view_kind::SpatialSpaceViewKind, visualizers::{ load_keypoint_connections, process_annotation_and_keypoint_slices, process_color_slice, - process_radius_slice, UiLabel, UiLabelTarget, + process_radius_slice, }, }; use super::{ - entity_iterator::clamped, filter_visualizable_3d_entities, SpatialViewVisualizerData, + filter_visualizable_3d_entities, process_labels_3d, SpatialViewVisualizerData, MAX_NUM_LABELS_PER_ENTITY, SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES, }; @@ -58,34 +56,6 @@ struct Points3DComponentData<'a> { // 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 Points3DVisualizer { - fn process_labels<'a>( - entity_path: &'a EntityPath, - positions: &'a [glam::Vec3], - labels: &'a [Text], - colors: &'a [egui::Color32], - annotation_infos: &'a ResolvedAnnotationInfos, - world_from_obj: glam::Affine3A, - ) -> impl Iterator + 'a { - let labels = clamped(labels, positions.len()); - itertools::izip!(annotation_infos.iter(), positions, labels, colors) - .enumerate() - .filter_map(move |(i, (annotation_info, point, label, color))| { - let label = annotation_info.label(Some(label.as_str())); - match (point, label) { - (point, Some(label)) => Some(UiLabel { - text: label, - color: *color, - target: UiLabelTarget::Position3D(world_from_obj.transform_point3(*point)), - labeled_instance: InstancePathHash::instance( - entity_path, - Instance::from(i as u64), - ), - }), - _ => None, - } - }) - } - fn process_data<'a>( &mut self, ctx: &QueryContext<'_>, @@ -173,9 +143,9 @@ impl Points3DVisualizer { positions }; - self.data.ui_labels.extend(Self::process_labels( + self.data.ui_labels.extend(process_labels_3d( entity_path, - label_positions, + label_positions.iter(), data.labels, &colors, &annotation_infos, diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs index fbeab5a7828e..4f289a95f20a 100644 --- a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs @@ -1,4 +1,8 @@ use re_entity_db::InstancePathHash; +use re_log_types::{EntityPath, Instance}; +use re_viewer_context::ResolvedAnnotationInfos; + +use super::entity_iterator::clamped; #[derive(Clone)] pub enum UiLabelTarget { @@ -26,3 +30,32 @@ pub struct UiLabel { /// Maximum number of labels after which we stop displaying labels for that entity all together. pub const MAX_NUM_LABELS_PER_ENTITY: usize = 10; + +pub fn process_labels_3d<'a>( + entity_path: &'a EntityPath, + positions: impl ExactSizeIterator + 'a, + labels: &'a [re_types::components::Text], + colors: &'a [egui::Color32], + annotation_infos: &'a ResolvedAnnotationInfos, + world_from_obj: glam::Affine3A, +) -> impl Iterator + 'a { + let labels = clamped(labels, positions.len()); + + itertools::izip!(annotation_infos.iter(), positions, labels, colors) + .enumerate() + .filter_map(move |(i, (annotation_info, point, label, color))| { + let label = annotation_info.label(Some(label.as_str())); + match (point, label) { + (point, Some(label)) => Some(UiLabel { + text: label, + color: *color, + target: UiLabelTarget::Position3D(world_from_obj.transform_point3(*point)), + labeled_instance: InstancePathHash::instance( + entity_path, + Instance::from(i as u64), + ), + }), + _ => None, + } + }) +} diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs b/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs index 040c9968aa0b..b05c940d9e92 100644 --- a/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs @@ -3,6 +3,6 @@ mod labels; mod spatial_view_visualizer; mod textured_rect; -pub use labels::{UiLabel, UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY}; +pub use labels::{process_labels_3d, UiLabel, UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY}; pub use spatial_view_visualizer::SpatialViewVisualizerData; pub use textured_rect::{bounding_box_for_textured_rect, tensor_to_textured_rect}; From fb0d9061be2a2fe8669cd4ed267fbea9c3496fcf Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 11:59:11 +0200 Subject: [PATCH 05/16] center single label on line strips --- .../src/visualizers/lines3d.rs | 106 +++++++----------- .../src/visualizers/points3d.rs | 2 +- .../src/visualizers/utilities/labels.rs | 4 +- 3 files changed, 44 insertions(+), 68 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/lines3d.rs b/crates/re_space_view_spatial/src/visualizers/lines3d.rs index 2cf3d0e35a0c..c96d1c50ec68 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines3d.rs @@ -1,4 +1,3 @@ -use re_entity_db::{EntityPath, InstancePathHash}; use re_log_types::Instance; use re_query::range_zip_1x5; use re_renderer::PickingLayerInstanceId; @@ -8,21 +7,19 @@ use re_types::{ }; use re_viewer_context::{ auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, - ResolvedAnnotationInfos, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, - ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, + ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ - contexts::SpatialSceneEntityContext, - view_kind::SpatialSpaceViewKind, - visualizers::{UiLabel, UiLabelTarget}, + contexts::SpatialSceneEntityContext, view_kind::SpatialSpaceViewKind, + visualizers::process_labels_3d, }; 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, + filter_visualizable_3d_entities, process_annotation_and_keypoint_slices, process_color_slice, + process_radius_slice, SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, }; // --- @@ -42,46 +39,6 @@ impl Default for Lines3DVisualizer { // 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 Lines3DVisualizer { - fn process_labels<'a>( - entity_path: &'a EntityPath, - strips: &'a [LineStrip3D], - labels: &'a [Text], - colors: &'a [egui::Color32], - annotation_infos: &'a ResolvedAnnotationInfos, - world_from_obj: glam::Affine3A, - ) -> impl Iterator + 'a { - let labels = clamped(labels, strips.len()); - itertools::izip!(annotation_infos.iter(), strips, labels, colors,) - .enumerate() - .filter_map(move |(i, (annotation_info, strip, label, color))| { - let label = annotation_info.label(Some(label.as_str())); - match (strip, label) { - (strip, Some(label)) => { - let midpoint = strip - .0 - .iter() - .copied() - .map(glam::Vec3::from) - .sum::() - / (strip.0.len() as f32); - - Some(UiLabel { - text: label, - color: *color, - target: UiLabelTarget::Position3D( - world_from_obj.transform_point3(midpoint), - ), - labeled_instance: InstancePathHash::instance( - entity_path, - Instance::from(i as u64), - ), - }) - } - _ => None, - } - }) - } - fn process_data<'a>( &mut self, ctx: &QueryContext<'_>, @@ -114,17 +71,6 @@ impl Lines3DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { - self.data.ui_labels.extend(Self::process_labels( - entity_path, - data.strips, - data.labels, - &colors, - &annotation_infos, - ent_context.world_from_entity, - )); - } - let mut line_batch = line_builder .batch(entity_path.to_string()) .depth_offset(ent_context.depth_offset) @@ -132,15 +78,15 @@ impl Lines3DVisualizer { .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); - let mut bounding_box = macaw::BoundingBox::nothing(); + let mut obj_space_bounding_box = macaw::BoundingBox::nothing(); let mut num_rendered_strips = 0usize; for (i, (strip, radius, color)) in - itertools::izip!(data.strips, radii, colors).enumerate() + itertools::izip!(data.strips, radii, &colors).enumerate() { let lines = line_batch .add_strip(strip.0.iter().copied().map(Into::into)) - .color(color) + .color(*color) .radius(radius) .picking_instance_id(PickingLayerInstanceId(i as _)); @@ -153,7 +99,7 @@ impl Lines3DVisualizer { } for p in &strip.0 { - bounding_box.extend((*p).into()); + obj_space_bounding_box.extend((*p).into()); } num_rendered_strips += 1; @@ -162,9 +108,39 @@ impl Lines3DVisualizer { self.data.add_bounding_box( entity_path.hash(), - bounding_box, + obj_space_bounding_box, ent_context.world_from_entity, ); + + if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { + // If there's many strips but only a single label, place the single label at the middle of the visualization. + let obj_space_bbox_center; + let label_positions = if data.labels.len() == 1 && data.strips.len() > 1 { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + obj_space_bbox_center = obj_space_bounding_box.center(); + itertools::Either::Left(std::iter::once(obj_space_bbox_center)) + } else { + // Take middle point of every strip. + itertools::Either::Right(data.strips.iter().map(|strip| { + strip + .0 + .iter() + .copied() + .map(glam::Vec3::from) + .sum::() + / (strip.0.len() as f32) + })) + }; + + self.data.ui_labels.extend(process_labels_3d( + entity_path, + label_positions, + data.labels, + &colors, + &annotation_infos, + ent_context.world_from_entity, + )); + } } } } diff --git a/crates/re_space_view_spatial/src/visualizers/points3d.rs b/crates/re_space_view_spatial/src/visualizers/points3d.rs index 268f83f1f082..a63ac0d03753 100644 --- a/crates/re_space_view_spatial/src/visualizers/points3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points3d.rs @@ -145,7 +145,7 @@ impl Points3DVisualizer { self.data.ui_labels.extend(process_labels_3d( entity_path, - label_positions.iter(), + label_positions.iter().copied(), data.labels, &colors, &annotation_infos, diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs index 4f289a95f20a..998e349a74ed 100644 --- a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs @@ -33,7 +33,7 @@ pub const MAX_NUM_LABELS_PER_ENTITY: usize = 10; pub fn process_labels_3d<'a>( entity_path: &'a EntityPath, - positions: impl ExactSizeIterator + 'a, + positions: impl ExactSizeIterator + 'a, labels: &'a [re_types::components::Text], colors: &'a [egui::Color32], annotation_infos: &'a ResolvedAnnotationInfos, @@ -49,7 +49,7 @@ pub fn process_labels_3d<'a>( (point, Some(label)) => Some(UiLabel { text: label, color: *color, - target: UiLabelTarget::Position3D(world_from_obj.transform_point3(*point)), + target: UiLabelTarget::Position3D(world_from_obj.transform_point3(point)), labeled_instance: InstancePathHash::instance( entity_path, Instance::from(i as u64), From eea7247ac7627b0bd4dbf983f02057d33213ccfb Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 12:16:23 +0200 Subject: [PATCH 06/16] share 2d label processing, center line label if only single one is present --- .../src/visualizers/lines2d.rs | 104 +++++++----------- .../src/visualizers/mod.rs | 5 +- .../src/visualizers/points2d.rs | 54 +++------ .../src/visualizers/points3d.rs | 4 +- .../src/visualizers/utilities/labels.rs | 34 +++++- .../src/visualizers/utilities/mod.rs | 4 +- 6 files changed, 91 insertions(+), 114 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/lines2d.rs b/crates/re_space_view_spatial/src/visualizers/lines2d.rs index 679a9606d8f1..27bc1390e614 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines2d.rs @@ -1,4 +1,3 @@ -use re_entity_db::{EntityPath, InstancePathHash}; use re_log_types::Instance; use re_query::range_zip_1x5; use re_renderer::{LineDrawableBuilder, PickingLayerInstanceId}; @@ -8,21 +7,17 @@ use re_types::{ }; use re_viewer_context::{ auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, - ResolvedAnnotationInfos, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, - ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, + ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; -use crate::{ - contexts::SpatialSceneEntityContext, - view_kind::SpatialSpaceViewKind, - visualizers::{UiLabel, UiLabelTarget}, -}; +use crate::{contexts::SpatialSceneEntityContext, view_kind::SpatialSpaceViewKind}; use super::{ - entity_iterator::clamped, filter_visualizable_2d_entities, - process_annotation_and_keypoint_slices, process_color_slice, process_radius_slice, - SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, + filter_visualizable_2d_entities, process_annotation_and_keypoint_slices, process_color_slice, + process_labels_2d, process_radius_slice, SpatialViewVisualizerData, + SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, }; // --- @@ -42,43 +37,6 @@ impl Default for Lines2DVisualizer { // 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 Lines2DVisualizer { - fn process_labels<'a>( - entity_path: &'a EntityPath, - strips: &'a [LineStrip2D], - labels: &'a [Text], - colors: &'a [egui::Color32], - annotation_infos: &'a ResolvedAnnotationInfos, - ) -> impl Iterator + 'a { - let labels = clamped(labels, strips.len()); - itertools::izip!(annotation_infos.iter(), strips, labels, colors,) - .enumerate() - .filter_map(move |(i, (annotation_info, strip, label, color))| { - let label = annotation_info.label(Some(label.as_str())); - match (strip, label) { - (strip, Some(label)) => { - let midpoint = strip - .0 - .iter() - .copied() - .map(glam::Vec2::from) - .sum::() - / (strip.0.len() as f32); - - Some(UiLabel { - text: label, - color: *color, - target: UiLabelTarget::Point2D(egui::pos2(midpoint.x, midpoint.y)), - labeled_instance: InstancePathHash::instance( - entity_path, - Instance::from(i as u64), - ), - }) - } - _ => None, - } - }) - } - fn process_data<'a>( &mut self, ctx: &QueryContext<'_>, @@ -111,16 +69,6 @@ impl Lines2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { - self.data.ui_labels.extend(Self::process_labels( - entity_path, - data.strips, - data.labels, - &colors, - &annotation_infos, - )); - } - let mut line_batch = line_builder .batch(entity_path.to_string()) .depth_offset(ent_context.depth_offset) @@ -128,13 +76,13 @@ impl Lines2DVisualizer { .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); - let mut bounding_box = macaw::BoundingBox::nothing(); + let mut obj_space_bounding_box = macaw::BoundingBox::nothing(); for (i, (strip, radius, color)) in - itertools::izip!(data.strips, radii, colors).enumerate() + itertools::izip!(data.strips, radii, &colors).enumerate() { let lines = line_batch .add_strip_2d(strip.0.iter().copied().map(Into::into)) - .color(color) + .color(*color) .radius(radius) .picking_instance_id(PickingLayerInstanceId(i as _)); @@ -147,15 +95,45 @@ impl Lines2DVisualizer { } for p in &strip.0 { - bounding_box.extend(glam::vec3(p.x(), p.y(), 0.0)); + obj_space_bounding_box.extend(glam::vec3(p.x(), p.y(), 0.0)); } } self.data.add_bounding_box( entity_path.hash(), - bounding_box, + obj_space_bounding_box, ent_context.world_from_entity, ); + + if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { + // If there's many strips but only a single label, place the single label at the middle of the visualization. + let obj_space_bbox_center; + let label_positions = if data.labels.len() == 1 && data.strips.len() > 1 { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + obj_space_bbox_center = obj_space_bounding_box.center().truncate(); + itertools::Either::Left(std::iter::once(obj_space_bbox_center)) + } else { + // Take middle point of every strip. + itertools::Either::Right(data.strips.iter().map(|strip| { + strip + .0 + .iter() + .copied() + .map(glam::Vec2::from) + .sum::() + / (strip.0.len() as f32) + })) + }; + + self.data.ui_labels.extend(process_labels_2d( + entity_path, + label_positions, + data.labels, + &colors, + &annotation_infos, + ent_context.world_from_entity, + )); + } } } } diff --git a/crates/re_space_view_spatial/src/visualizers/mod.rs b/crates/re_space_view_spatial/src/visualizers/mod.rs index 4cf1412ce12c..b1c1f247599d 100644 --- a/crates/re_space_view_spatial/src/visualizers/mod.rs +++ b/crates/re_space_view_spatial/src/visualizers/mod.rs @@ -23,8 +23,9 @@ pub use images::ImageVisualizer; pub use segmentation_images::SegmentationImageVisualizer; pub use transform3d_arrows::{add_axis_arrows, AxisLengthDetector, Transform3DArrowsVisualizer}; pub use utilities::{ - bounding_box_for_textured_rect, entity_iterator, process_labels_3d, tensor_to_textured_rect, - SpatialViewVisualizerData, UiLabel, UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY, + bounding_box_for_textured_rect, entity_iterator, process_labels_2d, process_labels_3d, + tensor_to_textured_rect, SpatialViewVisualizerData, UiLabel, UiLabelTarget, + MAX_NUM_LABELS_PER_ENTITY, }; // --- diff --git a/crates/re_space_view_spatial/src/visualizers/points2d.rs b/crates/re_space_view_spatial/src/visualizers/points2d.rs index 07356ef646a5..2e57d359eece 100644 --- a/crates/re_space_view_spatial/src/visualizers/points2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points2d.rs @@ -1,7 +1,5 @@ use itertools::Itertools as _; -use re_entity_db::{EntityPath, InstancePathHash}; -use re_log_types::Instance; use re_query::range_zip_1x5; use re_renderer::{LineDrawableBuilder, PickingLayerInstanceId, PointCloudBuilder}; use re_types::{ @@ -10,8 +8,8 @@ use re_types::{ }; use re_viewer_context::{ auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, - ResolvedAnnotationInfos, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, - ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, + ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; @@ -20,13 +18,13 @@ use crate::{ view_kind::SpatialSpaceViewKind, visualizers::{ load_keypoint_connections, process_annotation_and_keypoint_slices, process_color_slice, - process_radius_slice, UiLabel, UiLabelTarget, + process_radius_slice, }, }; use super::{ - entity_iterator::clamped, filter_visualizable_2d_entities, SpatialViewVisualizerData, - MAX_NUM_LABELS_PER_ENTITY, SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES, + filter_visualizable_2d_entities, process_labels_2d, SpatialViewVisualizerData, + SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES, }; // --- @@ -46,33 +44,6 @@ impl Default for Points2DVisualizer { // 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 Points2DVisualizer { - fn process_labels<'a>( - entity_path: &'a EntityPath, - positions: &'a [glam::Vec3], - labels: &'a [Text], - colors: &'a [egui::Color32], - annotation_infos: &'a ResolvedAnnotationInfos, - ) -> impl Iterator + 'a { - let labels = clamped(labels, positions.len()); - itertools::izip!(annotation_infos.iter(), positions, labels, colors) - .enumerate() - .filter_map(move |(i, (annotation_info, point, label, color))| { - let label = annotation_info.label(Some(label.as_str())); - match (point, label) { - (point, Some(label)) => Some(UiLabel { - text: label, - color: *color, - target: UiLabelTarget::Point2D(egui::pos2(point.x, point.y)), - labeled_instance: InstancePathHash::instance( - entity_path, - Instance::from(i as u64), - ), - }), - _ => None, - } - }) - } - fn process_data<'a>( &mut self, ctx: &QueryContext<'_>, @@ -156,23 +127,26 @@ impl Points2DVisualizer { load_keypoint_connections(line_builder, ent_context, entity_path, &keypoints)?; - if data.labels.len() == 1 || num_instances <= MAX_NUM_LABELS_PER_ENTITY { + if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many points but only a single label, place the single label at the middle of the visualization. let obj_space_bbox_center; - let label_positions = if data.labels.len() == 1 && positions.len() > 1 { + let label_positions = if data.labels.len() == 1 && data.positions.len() > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - obj_space_bbox_center = [obj_space_bounding_box.center()]; - &obj_space_bbox_center + obj_space_bbox_center = obj_space_bounding_box.center().truncate(); + itertools::Either::Left(std::iter::once(obj_space_bbox_center)) } else { - positions.as_slice() + itertools::Either::Right( + data.positions.iter().map(|p| glam::vec2(p.x(), p.y())), + ) }; - self.data.ui_labels.extend(Self::process_labels( + self.data.ui_labels.extend(process_labels_2d( entity_path, label_positions, data.labels, &colors, &annotation_infos, + ent_context.world_from_entity, )); } } diff --git a/crates/re_space_view_spatial/src/visualizers/points3d.rs b/crates/re_space_view_spatial/src/visualizers/points3d.rs index a63ac0d03753..669221cac753 100644 --- a/crates/re_space_view_spatial/src/visualizers/points3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points3d.rs @@ -24,7 +24,7 @@ use crate::{ use super::{ filter_visualizable_3d_entities, process_labels_3d, SpatialViewVisualizerData, - MAX_NUM_LABELS_PER_ENTITY, SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES, + SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES, }; // --- @@ -132,7 +132,7 @@ impl Points3DVisualizer { load_keypoint_connections(line_builder, ent_context, entity_path, &keypoints)?; - if data.labels.len() == 1 || num_instances <= MAX_NUM_LABELS_PER_ENTITY { + if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many points but only a single label, place the single label at the middle of the visualization. let obj_space_bbox_center; let label_positions = if data.labels.len() == 1 && positions.len() > 1 { diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs index 998e349a74ed..633efcc504dd 100644 --- a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs @@ -45,17 +45,39 @@ pub fn process_labels_3d<'a>( .enumerate() .filter_map(move |(i, (annotation_info, point, label, color))| { let label = annotation_info.label(Some(label.as_str())); - match (point, label) { - (point, Some(label)) => Some(UiLabel { + label.map(|label| UiLabel { + text: label, + color: *color, + target: UiLabelTarget::Position3D(world_from_obj.transform_point3(point)), + labeled_instance: InstancePathHash::instance(entity_path, Instance::from(i as u64)), + }) + }) +} + +pub fn process_labels_2d<'a>( + entity_path: &'a EntityPath, + positions: impl ExactSizeIterator + 'a, + labels: &'a [re_types::components::Text], + colors: &'a [egui::Color32], + annotation_infos: &'a ResolvedAnnotationInfos, + world_from_obj: glam::Affine3A, +) -> impl Iterator + 'a { + let labels = clamped(labels, positions.len()); + itertools::izip!(annotation_infos.iter(), positions, labels, colors) + .enumerate() + .filter_map(move |(i, (annotation_info, point, label, color))| { + let label = annotation_info.label(Some(label.as_str())); + label.map(|label| { + let point = world_from_obj.transform_point3(glam::Vec3::new(point.x, point.y, 0.0)); + UiLabel { text: label, color: *color, - target: UiLabelTarget::Position3D(world_from_obj.transform_point3(point)), + target: UiLabelTarget::Point2D(egui::pos2(point.x, point.y)), labeled_instance: InstancePathHash::instance( entity_path, Instance::from(i as u64), ), - }), - _ => None, - } + } + }) }) } diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs b/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs index b05c940d9e92..176f925cd2cd 100644 --- a/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/mod.rs @@ -3,6 +3,8 @@ mod labels; mod spatial_view_visualizer; mod textured_rect; -pub use labels::{process_labels_3d, UiLabel, UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY}; +pub use labels::{ + process_labels_2d, process_labels_3d, UiLabel, UiLabelTarget, MAX_NUM_LABELS_PER_ENTITY, +}; pub use spatial_view_visualizer::SpatialViewVisualizerData; pub use textured_rect::{bounding_box_for_textured_rect, tensor_to_textured_rect}; From 4f7336ab3ecd31822172b7d25f75a8b9bb4c6451 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 12:47:49 +0200 Subject: [PATCH 07/16] center single labels for arrows --- .../src/visualizers/arrows2d.rs | 115 ++++++++--------- .../src/visualizers/arrows3d.rs | 117 ++++++++---------- .../src/visualizers/lines2d.rs | 5 +- .../src/visualizers/lines3d.rs | 5 +- .../src/visualizers/points2d.rs | 1 + .../src/visualizers/points3d.rs | 1 + .../src/visualizers/utilities/labels.rs | 13 +- 7 files changed, 117 insertions(+), 140 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs index c115e253c83e..2bc0d851c737 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs @@ -1,4 +1,3 @@ -use re_entity_db::{EntityPath, InstancePathHash}; use re_log_types::Instance; use re_query::range_zip_1x6; use re_renderer::{renderer::LineStripFlags, LineDrawableBuilder, PickingLayerInstanceId}; @@ -8,20 +7,20 @@ use re_types::{ }; use re_viewer_context::{ auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, - ResolvedAnnotationInfos, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, - ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, + ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ - contexts::SpatialSceneEntityContext, - view_kind::SpatialSpaceViewKind, - visualizers::{filter_visualizable_2d_entities, UiLabel, UiLabelTarget}, + contexts::SpatialSceneEntityContext, view_kind::SpatialSpaceViewKind, + visualizers::filter_visualizable_2d_entities, }; use super::{ entity_iterator::clamped, process_annotation_and_keypoint_slices, process_color_slice, - process_radius_slice, SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, + process_labels_2d, process_radius_slice, SpatialViewVisualizerData, + SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, }; // --- @@ -41,45 +40,6 @@ impl Default for Arrows2DVisualizer { // 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 Arrows2DVisualizer { - fn process_labels<'a>( - entity_path: &'a EntityPath, - vectors: &'a [Vector2D], - origins: impl Iterator + 'a, - labels: &'a [Text], - colors: &'a [egui::Color32], - annotation_infos: &'a ResolvedAnnotationInfos, - world_from_obj: glam::Affine3A, - ) -> impl Iterator + 'a { - let labels = clamped(labels, vectors.len()); - let origins = origins.chain(std::iter::repeat(&Position2D::ZERO)); - itertools::izip!(annotation_infos.iter(), vectors, origins, labels, colors) - .enumerate() - .filter_map( - move |(i, (annotation_info, vector, origin, label, color))| { - let label = annotation_info.label(Some(label.as_str())); - match (vector, label) { - (vector, Some(label)) => { - let midpoint = - // `0.45` rather than `0.5` to account for cap and such - glam::Vec2::from(origin.0) + glam::Vec2::from(vector.0) * 0.45; - let midpoint = world_from_obj.transform_point3(midpoint.extend(0.0)); - - Some(UiLabel { - text: label, - color: *color, - target: UiLabelTarget::Point2D(egui::pos2(midpoint.x, midpoint.y)), - labeled_instance: InstancePathHash::instance( - entity_path, - Instance::from(i as u64), - ), - }) - } - _ => None, - } - }, - ) - } - fn process_data<'a>( &mut self, ctx: &QueryContext<'_>, @@ -112,31 +72,18 @@ impl Arrows2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { - let origins = clamped(data.origins, num_instances); - self.data.ui_labels.extend(Self::process_labels( - entity_path, - data.vectors, - origins, - data.labels, - &colors, - &annotation_infos, - ent_context.world_from_entity, - )); - } - let mut line_batch = line_builder .batch(entity_path.to_string()) .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 mut obj_space_bounding_box = macaw::BoundingBox::nothing(); let origins = clamped(data.origins, num_instances).chain(std::iter::repeat(&Position2D::ZERO)); - for (i, (vector, origin, radius, color)) in - itertools::izip!(data.vectors, origins, radii, colors).enumerate() + for (i, (vector, origin, radius, &color)) in + itertools::izip!(data.vectors, origins, radii, &colors).enumerate() { let vector: glam::Vec2 = vector.0.into(); let origin: glam::Vec2 = origin.0.into(); @@ -161,15 +108,53 @@ impl Arrows2DVisualizer { segment.outline_mask_ids(*outline_mask_ids); } - bounding_box.extend(origin.extend(0.0)); - bounding_box.extend(end.extend(0.0)); + obj_space_bounding_box.extend(origin.extend(0.0)); + obj_space_bounding_box.extend(end.extend(0.0)); } self.data.add_bounding_box( entity_path.hash(), - bounding_box, + obj_space_bounding_box, ent_context.world_from_entity, ); + + if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { + // If there's many arrows but only a single label, place the single label at the middle of the visualization. + let obj_space_bbox_center; + let (num_positions, label_positions) = + if data.labels.len() == 1 && data.vectors.len() > 1 { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + obj_space_bbox_center = obj_space_bounding_box.center().truncate(); + ( + 1, + itertools::Either::Left(std::iter::once(obj_space_bbox_center)), + ) + } else { + // Take middle point of every arrow. + let origins = clamped(data.origins, num_instances) + .chain(std::iter::repeat(&Position2D::ZERO)); + // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. + ( + num_instances, + itertools::Either::Right(data.vectors.iter().zip(origins).map( + |(vector, origin)| { + // `0.45` rather than `0.5` to account for cap and such + (glam::Vec2::from(origin.0) + glam::Vec2::from(vector.0)) * 0.45 + }, + )), + ) + }; + + self.data.ui_labels.extend(process_labels_2d( + entity_path, + num_positions, + label_positions, + data.labels, + &colors, + &annotation_infos, + ent_context.world_from_entity, + )); + } } } } diff --git a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs index 709196c4e92a..f82c5b600da6 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs @@ -1,4 +1,3 @@ -use re_entity_db::{EntityPath, InstancePathHash}; use re_log_types::Instance; use re_query::range_zip_1x6; use re_renderer::{renderer::LineStripFlags, LineDrawableBuilder, PickingLayerInstanceId}; @@ -8,20 +7,20 @@ use re_types::{ }; use re_viewer_context::{ auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, - ResolvedAnnotationInfos, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, - ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, + ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; use crate::{ - contexts::SpatialSceneEntityContext, - view_kind::SpatialSpaceViewKind, - visualizers::{filter_visualizable_3d_entities, UiLabel, UiLabelTarget}, + contexts::SpatialSceneEntityContext, view_kind::SpatialSpaceViewKind, + visualizers::filter_visualizable_3d_entities, }; use super::{ entity_iterator::clamped, process_annotation_and_keypoint_slices, process_color_slice, - process_radius_slice, SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, + process_labels_3d, process_radius_slice, SpatialViewVisualizerData, + SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, }; // --- @@ -41,47 +40,6 @@ impl Default for Arrows3DVisualizer { // 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 Arrows3DVisualizer { - fn process_labels<'a>( - entity_path: &'a EntityPath, - vectors: &'a [Vector3D], - origins: impl Iterator + 'a, - labels: &'a [Text], - colors: &'a [egui::Color32], - annotation_infos: &'a ResolvedAnnotationInfos, - world_from_obj: glam::Affine3A, - ) -> impl Iterator + 'a { - let labels = clamped(labels, vectors.len()); - let origins = origins.chain(std::iter::repeat(&Position3D::ZERO)); - itertools::izip!(annotation_infos.iter(), vectors, origins, labels, colors) - .enumerate() - .filter_map( - move |(i, (annotation_info, vector, origin, label, color))| { - let label = annotation_info.label(Some(label.as_str())); - match (vector, label) { - (vector, Some(label)) => { - let midpoint = - // `0.45` rather than `0.5` to account for cap and such - (glam::Vec3::from(origin.0) + glam::Vec3::from(vector.0)) * 0.45; - let midpoint = world_from_obj.transform_point3(midpoint); - - Some(UiLabel { - text: label, - color: *color, - target: UiLabelTarget::Position3D( - world_from_obj.transform_point3(midpoint), - ), - labeled_instance: InstancePathHash::instance( - entity_path, - Instance::from(i as u64), - ), - }) - } - _ => None, - } - }, - ) - } - fn process_data<'a>( &mut self, ctx: &QueryContext<'_>, @@ -114,31 +72,18 @@ impl Arrows3DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { - let origins = clamped(data.origins, num_instances); - self.data.ui_labels.extend(Self::process_labels( - entity_path, - data.vectors, - origins, - data.labels, - &colors, - &annotation_infos, - ent_context.world_from_entity, - )); - } - let mut line_batch = line_builder .batch(entity_path.to_string()) .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 mut obj_space_bounding_box = macaw::BoundingBox::nothing(); let origins = clamped(data.origins, num_instances).chain(std::iter::repeat(&Position3D::ZERO)); - for (i, (vector, origin, radius, color)) in - itertools::izip!(data.vectors, origins, radii, colors).enumerate() + for (i, (vector, origin, radius, &color)) in + itertools::izip!(data.vectors, origins, radii, &colors).enumerate() { let vector: glam::Vec3 = vector.0.into(); let origin: glam::Vec3 = origin.0.into(); @@ -164,15 +109,53 @@ impl Arrows3DVisualizer { segment.outline_mask_ids(*outline_mask_ids); } - bounding_box.extend(origin); - bounding_box.extend(end); + obj_space_bounding_box.extend(origin); + obj_space_bounding_box.extend(end); } self.data.add_bounding_box( entity_path.hash(), - bounding_box, + obj_space_bounding_box, ent_context.world_from_entity, ); + + if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { + // If there's many arrows but only a single label, place the single label at the middle of the visualization. + let obj_space_bbox_center; + let (num_positions, label_positions) = + if data.labels.len() == 1 && data.vectors.len() > 1 { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + obj_space_bbox_center = obj_space_bounding_box.center(); + ( + 1, + itertools::Either::Left(std::iter::once(obj_space_bbox_center)), + ) + } else { + // Take middle point of every arrow. + let origins = clamped(data.origins, num_instances) + .chain(std::iter::repeat(&Position3D::ZERO)); + // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. + ( + num_instances, + itertools::Either::Right(data.vectors.iter().zip(origins).map( + |(vector, origin)| { + // `0.45` rather than `0.5` to account for cap and such + (glam::Vec3::from(origin.0) + glam::Vec3::from(vector.0)) * 0.45 + }, + )), + ) + }; + + self.data.ui_labels.extend(process_labels_3d( + entity_path, + num_positions, + label_positions, + data.labels, + &colors, + &annotation_infos, + ent_context.world_from_entity, + )); + } } } } diff --git a/crates/re_space_view_spatial/src/visualizers/lines2d.rs b/crates/re_space_view_spatial/src/visualizers/lines2d.rs index 27bc1390e614..dcbb98ffe086 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines2d.rs @@ -77,12 +77,12 @@ impl Lines2DVisualizer { .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); let mut obj_space_bounding_box = macaw::BoundingBox::nothing(); - for (i, (strip, radius, color)) in + for (i, (strip, radius, &color)) in itertools::izip!(data.strips, radii, &colors).enumerate() { let lines = line_batch .add_strip_2d(strip.0.iter().copied().map(Into::into)) - .color(*color) + .color(color) .radius(radius) .picking_instance_id(PickingLayerInstanceId(i as _)); @@ -127,6 +127,7 @@ impl Lines2DVisualizer { self.data.ui_labels.extend(process_labels_2d( entity_path, + label_positions.len(), label_positions, data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/lines3d.rs b/crates/re_space_view_spatial/src/visualizers/lines3d.rs index c96d1c50ec68..fae04b36a1ad 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines3d.rs @@ -81,12 +81,12 @@ impl Lines3DVisualizer { let mut obj_space_bounding_box = macaw::BoundingBox::nothing(); let mut num_rendered_strips = 0usize; - for (i, (strip, radius, color)) in + for (i, (strip, radius, &color)) in itertools::izip!(data.strips, radii, &colors).enumerate() { let lines = line_batch .add_strip(strip.0.iter().copied().map(Into::into)) - .color(*color) + .color(color) .radius(radius) .picking_instance_id(PickingLayerInstanceId(i as _)); @@ -134,6 +134,7 @@ impl Lines3DVisualizer { self.data.ui_labels.extend(process_labels_3d( entity_path, + label_positions.len(), label_positions, data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/points2d.rs b/crates/re_space_view_spatial/src/visualizers/points2d.rs index 2e57d359eece..cfa7df798d0c 100644 --- a/crates/re_space_view_spatial/src/visualizers/points2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points2d.rs @@ -142,6 +142,7 @@ impl Points2DVisualizer { self.data.ui_labels.extend(process_labels_2d( entity_path, + label_positions.len(), label_positions, data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/points3d.rs b/crates/re_space_view_spatial/src/visualizers/points3d.rs index 669221cac753..b5be3c22ba25 100644 --- a/crates/re_space_view_spatial/src/visualizers/points3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points3d.rs @@ -145,6 +145,7 @@ impl Points3DVisualizer { self.data.ui_labels.extend(process_labels_3d( entity_path, + label_positions.len(), label_positions.iter().copied(), data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs index 633efcc504dd..8c88f35384f6 100644 --- a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs @@ -33,13 +33,15 @@ pub const MAX_NUM_LABELS_PER_ENTITY: usize = 10; pub fn process_labels_3d<'a>( entity_path: &'a EntityPath, - positions: impl ExactSizeIterator + 'a, + num_positions: usize, + positions: impl Iterator + 'a, labels: &'a [re_types::components::Text], colors: &'a [egui::Color32], annotation_infos: &'a ResolvedAnnotationInfos, world_from_obj: glam::Affine3A, ) -> impl Iterator + 'a { - let labels = clamped(labels, positions.len()); + let labels = clamped(labels, num_positions); + let colors = clamped(colors, num_positions); itertools::izip!(annotation_infos.iter(), positions, labels, colors) .enumerate() @@ -56,13 +58,16 @@ pub fn process_labels_3d<'a>( pub fn process_labels_2d<'a>( entity_path: &'a EntityPath, - positions: impl ExactSizeIterator + 'a, + num_positions: usize, + positions: impl Iterator + 'a, labels: &'a [re_types::components::Text], colors: &'a [egui::Color32], annotation_infos: &'a ResolvedAnnotationInfos, world_from_obj: glam::Affine3A, ) -> impl Iterator + 'a { - let labels = clamped(labels, positions.len()); + let labels = clamped(labels, num_positions); + let colors = clamped(colors, num_positions); + itertools::izip!(annotation_infos.iter(), positions, labels, colors) .enumerate() .filter_map(move |(i, (annotation_info, point, label, color))| { From 48ec23f87179e6463d0aec319c23c5725a92b82d Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 12:55:26 +0200 Subject: [PATCH 08/16] center label for single label on box3d --- .../src/visualizers/arrows2d.rs | 2 +- .../src/visualizers/arrows3d.rs | 2 +- .../src/visualizers/boxes3d.rs | 102 ++++++++---------- 3 files changed, 48 insertions(+), 58 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs index 2bc0d851c737..356897df5ca1 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs @@ -122,7 +122,7 @@ impl Arrows2DVisualizer { // If there's many arrows but only a single label, place the single label at the middle of the visualization. let obj_space_bbox_center; let (num_positions, label_positions) = - if data.labels.len() == 1 && data.vectors.len() > 1 { + if data.labels.len() == 1 && num_instances > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. obj_space_bbox_center = obj_space_bounding_box.center().truncate(); ( diff --git a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs index f82c5b600da6..3c18f3e88826 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs @@ -123,7 +123,7 @@ impl Arrows3DVisualizer { // If there's many arrows but only a single label, place the single label at the middle of the visualization. let obj_space_bbox_center; let (num_positions, label_positions) = - if data.labels.len() == 1 && data.vectors.len() > 1 { + if data.labels.len() == 1 && num_instances > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. obj_space_bbox_center = obj_space_bounding_box.center(); ( diff --git a/crates/re_space_view_spatial/src/visualizers/boxes3d.rs b/crates/re_space_view_spatial/src/visualizers/boxes3d.rs index fe39677789ed..b2aceecf5e9a 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes3d.rs @@ -1,4 +1,3 @@ -use re_entity_db::{EntityPath, InstancePathHash}; use re_log_types::Instance; use re_query::range_zip_1x7; use re_renderer::{LineDrawableBuilder, PickingLayerInstanceId}; @@ -8,21 +7,17 @@ use re_types::{ }; use re_viewer_context::{ auto_color_for_entity_path, ApplicableEntities, IdentifiedViewSystem, QueryContext, - ResolvedAnnotationInfos, SpaceViewSystemExecutionError, TypedComponentFallbackProvider, - ViewContext, ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, + ViewContextCollection, ViewQuery, VisualizableEntities, VisualizableFilterContext, VisualizerQueryInfo, VisualizerSystem, }; -use crate::{ - contexts::SpatialSceneEntityContext, - view_kind::SpatialSpaceViewKind, - visualizers::{UiLabel, UiLabelTarget}, -}; +use crate::{contexts::SpatialSceneEntityContext, view_kind::SpatialSpaceViewKind}; 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, + process_annotation_and_keypoint_slices, process_color_slice, process_labels_3d, + process_radius_slice, SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, }; // --- @@ -40,35 +35,6 @@ impl Default for Boxes3DVisualizer { // 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 Boxes3DVisualizer { - fn process_labels<'a>( - entity_path: &'a EntityPath, - half_sizes: &'a [HalfSizes3D], - 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<'_>, @@ -101,17 +67,6 @@ impl Boxes3DVisualizer { 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("boxes3d") .depth_offset(ent_context.depth_offset) @@ -119,17 +74,17 @@ impl Boxes3DVisualizer { .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); - let mut bounding_box = macaw::BoundingBox::nothing(); + let mut obj_space_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() + for (i, (half_size, ¢er, rotation, radius, &color)) in + itertools::izip!(data.half_sizes, centers, rotations, radii, &colors).enumerate() { - bounding_box.extend(half_size.box_min(center)); - bounding_box.extend(half_size.box_max(center)); + obj_space_bounding_box.extend(half_size.box_min(center)); + obj_space_bounding_box.extend(half_size.box_max(center)); let center = center.into(); @@ -156,9 +111,44 @@ impl Boxes3DVisualizer { self.0.add_bounding_box( entity_path.hash(), - bounding_box, + obj_space_bounding_box, ent_context.world_from_entity, ); + + if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { + // If there's many boxes but only a single label, place the single label at the middle of the visualization. + let obj_space_bbox_center; + let (num_positions, label_positions) = + if data.labels.len() == 1 && num_instances > 1 { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + obj_space_bbox_center = obj_space_bounding_box.center(); + ( + 1, + itertools::Either::Left(std::iter::once(obj_space_bbox_center)), + ) + } else { + // Take center point of every box. + // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. + ( + num_instances, + itertools::Either::Right( + clamped(data.centers, num_instances) + .chain(std::iter::repeat(&Position3D::ZERO)) + .map(|&c| c.into()), + ), + ) + }; + + self.0.ui_labels.extend(process_labels_3d( + entity_path, + num_positions, + label_positions, + data.labels, + &colors, + &annotation_infos, + ent_context.world_from_entity, + )); + } } } } From c371b10e3707551bb6d3fb62791be652d3354683 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 13:26:58 +0200 Subject: [PATCH 09/16] 2d box single label centering --- .../src/visualizers/boxes2d.rs | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs index 629159657d21..b26093c52e83 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs @@ -21,22 +21,19 @@ use crate::{ use super::{ entity_iterator::clamped, filter_visualizable_2d_entities, - process_annotation_and_keypoint_slices, process_color_slice, process_radius_slice, - SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, + process_annotation_and_keypoint_slices, process_color_slice, process_labels_2d, + process_radius_slice, SpatialViewVisualizerData, SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, }; // --- pub struct Boxes2DVisualizer { - /// If the number of points in the batch is > max_labels, don't render box labels. - pub max_labels: usize, pub data: SpatialViewVisualizerData, } impl Default for Boxes2DVisualizer { fn default() -> Self { Self { - max_labels: 20, data: SpatialViewVisualizerData::new(Some(SpatialSpaceViewKind::TwoD)), } } @@ -115,18 +112,6 @@ impl Boxes2DVisualizer { let colors = process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors); - if num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { - let centers = clamped(data.centers, num_instances); - self.data.ui_labels.extend(Self::process_labels( - entity_path, - data.half_sizes, - centers, - data.labels, - &colors, - &annotation_infos, - )); - } - let mut line_batch = line_builder .batch("boxes2d") .depth_offset(ent_context.depth_offset) @@ -134,17 +119,17 @@ impl Boxes2DVisualizer { .outline_mask_ids(ent_context.highlight.overall) .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())); - let mut bounding_box = macaw::BoundingBox::nothing(); + let mut obj_space_bounding_box = macaw::BoundingBox::nothing(); let centers = clamped(data.centers, num_instances).chain(std::iter::repeat(&Position2D::ZERO)); - for (i, (half_size, center, radius, color)) in - itertools::izip!(data.half_sizes, centers, radii, colors).enumerate() + for (i, (half_size, center, radius, &color)) in + itertools::izip!(data.half_sizes, centers, radii, &colors).enumerate() { let min = half_size.box_min(*center); let max = half_size.box_max(*center); - bounding_box.extend(min.extend(0.0)); - bounding_box.extend(max.extend(0.0)); + obj_space_bounding_box.extend(min.extend(0.0)); + obj_space_bounding_box.extend(max.extend(0.0)); let rectangle = line_batch .add_rectangle_outline_2d( @@ -166,9 +151,36 @@ impl Boxes2DVisualizer { self.data.add_bounding_box( entity_path.hash(), - bounding_box, + obj_space_bounding_box, ent_context.world_from_entity, ); + + if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { + if data.labels.len() == 1 && num_instances > 1 { + // If there's many boxes but only a single label, place the single label at the middle of the visualization. + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + self.data.ui_labels.extend(process_labels_2d( + entity_path, + 1, + std::iter::once(obj_space_bounding_box.center().truncate()), + data.labels, + &colors, + &annotation_infos, + ent_context.world_from_entity, + )); + } else { + let centers = clamped(data.centers, num_instances) + .chain(std::iter::repeat(&Position2D::ZERO)); + self.data.ui_labels.extend(Self::process_labels( + entity_path, + data.half_sizes, + centers, + data.labels, + &colors, + &annotation_infos, + )); + } + } } } } From 2f6435fa023dfc790fb516404f25ef36eeaf2426 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 13:27:10 +0200 Subject: [PATCH 10/16] cleanup unnecessary temporary --- .../src/visualizers/arrows2d.rs | 6 +-- .../src/visualizers/arrows3d.rs | 47 +++++++++---------- .../src/visualizers/boxes3d.rs | 41 ++++++++-------- .../src/visualizers/lines2d.rs | 6 +-- .../src/visualizers/lines3d.rs | 4 +- .../src/visualizers/points2d.rs | 6 +-- 6 files changed, 53 insertions(+), 57 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs index 356897df5ca1..fde1c88ffb32 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs @@ -120,14 +120,14 @@ impl Arrows2DVisualizer { if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many arrows but only a single label, place the single label at the middle of the visualization. - let obj_space_bbox_center; let (num_positions, label_positions) = if data.labels.len() == 1 && num_instances > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - obj_space_bbox_center = obj_space_bounding_box.center().truncate(); ( 1, - itertools::Either::Left(std::iter::once(obj_space_bbox_center)), + itertools::Either::Left(std::iter::once( + obj_space_bounding_box.center().truncate(), + )), ) } else { // Take middle point of every arrow. diff --git a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs index 3c18f3e88826..9b9290e321f8 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs @@ -121,30 +121,29 @@ impl Arrows3DVisualizer { if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many arrows but only a single label, place the single label at the middle of the visualization. - let obj_space_bbox_center; - let (num_positions, label_positions) = - if data.labels.len() == 1 && num_instances > 1 { - // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - obj_space_bbox_center = obj_space_bounding_box.center(); - ( - 1, - itertools::Either::Left(std::iter::once(obj_space_bbox_center)), - ) - } else { - // Take middle point of every arrow. - let origins = clamped(data.origins, num_instances) - .chain(std::iter::repeat(&Position3D::ZERO)); - // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. - ( - num_instances, - itertools::Either::Right(data.vectors.iter().zip(origins).map( - |(vector, origin)| { - // `0.45` rather than `0.5` to account for cap and such - (glam::Vec3::from(origin.0) + glam::Vec3::from(vector.0)) * 0.45 - }, - )), - ) - }; + let (num_positions, label_positions) = if data.labels.len() == 1 + && num_instances > 1 + { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + ( + 1, + itertools::Either::Left(std::iter::once(obj_space_bounding_box.center())), + ) + } else { + // Take middle point of every arrow. + let origins = clamped(data.origins, num_instances) + .chain(std::iter::repeat(&Position3D::ZERO)); + // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. + ( + num_instances, + itertools::Either::Right(data.vectors.iter().zip(origins).map( + |(vector, origin)| { + // `0.45` rather than `0.5` to account for cap and such + (glam::Vec3::from(origin.0) + glam::Vec3::from(vector.0)) * 0.45 + }, + )), + ) + }; self.data.ui_labels.extend(process_labels_3d( entity_path, diff --git a/crates/re_space_view_spatial/src/visualizers/boxes3d.rs b/crates/re_space_view_spatial/src/visualizers/boxes3d.rs index b2aceecf5e9a..89474d3ecfa3 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes3d.rs @@ -117,27 +117,26 @@ impl Boxes3DVisualizer { if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many boxes but only a single label, place the single label at the middle of the visualization. - let obj_space_bbox_center; - let (num_positions, label_positions) = - if data.labels.len() == 1 && num_instances > 1 { - // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - obj_space_bbox_center = obj_space_bounding_box.center(); - ( - 1, - itertools::Either::Left(std::iter::once(obj_space_bbox_center)), - ) - } else { - // Take center point of every box. - // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. - ( - num_instances, - itertools::Either::Right( - clamped(data.centers, num_instances) - .chain(std::iter::repeat(&Position3D::ZERO)) - .map(|&c| c.into()), - ), - ) - }; + let (num_positions, label_positions) = if data.labels.len() == 1 + && num_instances > 1 + { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + ( + 1, + itertools::Either::Left(std::iter::once(obj_space_bounding_box.center())), + ) + } else { + // Take center point of every box. + // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. + ( + num_instances, + itertools::Either::Right( + clamped(data.centers, num_instances) + .chain(std::iter::repeat(&Position3D::ZERO)) + .map(|&c| c.into()), + ), + ) + }; self.0.ui_labels.extend(process_labels_3d( entity_path, diff --git a/crates/re_space_view_spatial/src/visualizers/lines2d.rs b/crates/re_space_view_spatial/src/visualizers/lines2d.rs index dcbb98ffe086..ccc03e198b55 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines2d.rs @@ -107,11 +107,11 @@ impl Lines2DVisualizer { if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many strips but only a single label, place the single label at the middle of the visualization. - let obj_space_bbox_center; let label_positions = if data.labels.len() == 1 && data.strips.len() > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - obj_space_bbox_center = obj_space_bounding_box.center().truncate(); - itertools::Either::Left(std::iter::once(obj_space_bbox_center)) + itertools::Either::Left(std::iter::once( + obj_space_bounding_box.center().truncate(), + )) } else { // Take middle point of every strip. itertools::Either::Right(data.strips.iter().map(|strip| { diff --git a/crates/re_space_view_spatial/src/visualizers/lines3d.rs b/crates/re_space_view_spatial/src/visualizers/lines3d.rs index fae04b36a1ad..00f7b1965d31 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines3d.rs @@ -114,11 +114,9 @@ impl Lines3DVisualizer { if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many strips but only a single label, place the single label at the middle of the visualization. - let obj_space_bbox_center; let label_positions = if data.labels.len() == 1 && data.strips.len() > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - obj_space_bbox_center = obj_space_bounding_box.center(); - itertools::Either::Left(std::iter::once(obj_space_bbox_center)) + itertools::Either::Left(std::iter::once(obj_space_bounding_box.center())) } else { // Take middle point of every strip. itertools::Either::Right(data.strips.iter().map(|strip| { diff --git a/crates/re_space_view_spatial/src/visualizers/points2d.rs b/crates/re_space_view_spatial/src/visualizers/points2d.rs index cfa7df798d0c..f893ed9e6cdd 100644 --- a/crates/re_space_view_spatial/src/visualizers/points2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points2d.rs @@ -129,11 +129,11 @@ impl Points2DVisualizer { if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many points but only a single label, place the single label at the middle of the visualization. - let obj_space_bbox_center; let label_positions = if data.labels.len() == 1 && data.positions.len() > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - obj_space_bbox_center = obj_space_bounding_box.center().truncate(); - itertools::Either::Left(std::iter::once(obj_space_bbox_center)) + itertools::Either::Left(std::iter::once( + obj_space_bounding_box.center().truncate(), + )) } else { itertools::Either::Right( data.positions.iter().map(|p| glam::vec2(p.x(), p.y())), From fbeae1f6a8fff836cc13f20c0e39b791c9b9e85d Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 14:11:26 +0200 Subject: [PATCH 11/16] Raise max num labels per entity --- .../re_space_view_spatial/src/visualizers/utilities/labels.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs index 8c88f35384f6..df70b0ab4187 100644 --- a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs @@ -29,7 +29,9 @@ pub struct UiLabel { } /// Maximum number of labels after which we stop displaying labels for that entity all together. -pub const MAX_NUM_LABELS_PER_ENTITY: usize = 10; +/// +/// TODO(#4451): Hiding of labels should be configurable. This can be the heuristic for it. +pub const MAX_NUM_LABELS_PER_ENTITY: usize = 30; pub fn process_labels_3d<'a>( entity_path: &'a EntityPath, From 33cb892b43cc2c1edb972207fb3ed6e17ecb5123 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 14:29:25 +0200 Subject: [PATCH 12/16] Fix label processing num propagation --- .../src/visualizers/arrows2d.rs | 41 ++++++++----------- .../src/visualizers/arrows3d.rs | 27 ++++-------- .../src/visualizers/boxes2d.rs | 38 ++++++++--------- .../src/visualizers/boxes3d.rs | 22 +++------- .../src/visualizers/lines2d.rs | 1 - .../src/visualizers/lines3d.rs | 1 - .../src/visualizers/points2d.rs | 1 - .../src/visualizers/points3d.rs | 1 - .../src/visualizers/utilities/labels.rs | 10 ++--- crates/re_viewer_context/src/annotations.rs | 15 +++++++ 10 files changed, 68 insertions(+), 89 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs index fde1c88ffb32..54c8a2d1fa2a 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows2d.rs @@ -120,34 +120,25 @@ impl Arrows2DVisualizer { if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many arrows but only a single label, place the single label at the middle of the visualization. - let (num_positions, label_positions) = - if data.labels.len() == 1 && num_instances > 1 { - // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - ( - 1, - itertools::Either::Left(std::iter::once( - obj_space_bounding_box.center().truncate(), - )), - ) - } else { - // Take middle point of every arrow. - let origins = clamped(data.origins, num_instances) - .chain(std::iter::repeat(&Position2D::ZERO)); - // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. - ( - num_instances, - itertools::Either::Right(data.vectors.iter().zip(origins).map( - |(vector, origin)| { - // `0.45` rather than `0.5` to account for cap and such - (glam::Vec2::from(origin.0) + glam::Vec2::from(vector.0)) * 0.45 - }, - )), - ) - }; + let label_positions = if data.labels.len() == 1 && num_instances > 1 { + // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. + itertools::Either::Left(std::iter::once( + obj_space_bounding_box.center().truncate(), + )) + } else { + // Take middle point of every arrow. + let origins = clamped(data.origins, num_instances) + .chain(std::iter::repeat(&Position2D::ZERO)); + itertools::Either::Right(data.vectors.iter().zip(origins).map( + |(vector, origin)| { + // `0.45` rather than `0.5` to account for cap and such + (glam::Vec2::from(origin.0) + glam::Vec2::from(vector.0)) * 0.45 + }, + )) + }; self.data.ui_labels.extend(process_labels_2d( entity_path, - num_positions, label_positions, data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs index 9b9290e321f8..1ec600e3847d 100644 --- a/crates/re_space_view_spatial/src/visualizers/arrows3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/arrows3d.rs @@ -121,33 +121,24 @@ impl Arrows3DVisualizer { if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many arrows but only a single label, place the single label at the middle of the visualization. - let (num_positions, label_positions) = if data.labels.len() == 1 - && num_instances > 1 - { + let label_positions = if data.labels.len() == 1 && num_instances > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - ( - 1, - itertools::Either::Left(std::iter::once(obj_space_bounding_box.center())), - ) + itertools::Either::Left(std::iter::once(obj_space_bounding_box.center())) } else { // Take middle point of every arrow. let origins = clamped(data.origins, num_instances) .chain(std::iter::repeat(&Position3D::ZERO)); - // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. - ( - num_instances, - itertools::Either::Right(data.vectors.iter().zip(origins).map( - |(vector, origin)| { - // `0.45` rather than `0.5` to account for cap and such - (glam::Vec3::from(origin.0) + glam::Vec3::from(vector.0)) * 0.45 - }, - )), - ) + + itertools::Either::Right(data.vectors.iter().zip(origins).map( + |(vector, origin)| { + // `0.45` rather than `0.5` to account for cap and such + (glam::Vec3::from(origin.0) + glam::Vec3::from(vector.0)) * 0.45 + }, + )) }; self.data.ui_labels.extend(process_labels_3d( entity_path, - num_positions, label_positions, data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs index b26093c52e83..df681ca13996 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs @@ -50,32 +50,31 @@ impl Boxes2DVisualizer { colors: &'a [egui::Color32], annotation_infos: &'a ResolvedAnnotationInfos, ) -> impl Iterator + 'a { - let labels = clamped(labels, half_sizes.len()); + let labels = clamped(labels, annotation_infos.len()); + let colors = clamped(colors, annotation_infos.len()); // Assumes colors is already populated with at least one color. let centers = centers.chain(std::iter::repeat(&Position2D::ZERO)); + itertools::izip!(annotation_infos.iter(), half_sizes, centers, labels, colors) .enumerate() .filter_map( move |(i, (annotation_info, half_size, center, label, color))| { let label = annotation_info.label(Some(label.as_str())); - match (half_size, label) { - (half_size, Some(label)) => { - let min = half_size.box_min(*center); - let max = half_size.box_max(*center); - Some(UiLabel { - text: label, - color: *color, - target: UiLabelTarget::Rect(egui::Rect::from_min_max( - egui::pos2(min.x, min.y), - egui::pos2(max.x, max.y), - )), - labeled_instance: InstancePathHash::instance( - entity_path, - Instance::from(i as u64), - ), - }) + label.map(|label| { + let min = half_size.box_min(*center); + let max = half_size.box_max(*center); + UiLabel { + text: label, + color: *color, + target: UiLabelTarget::Rect(egui::Rect::from_min_max( + egui::pos2(min.x, min.y), + egui::pos2(max.x, max.y), + )), + labeled_instance: InstancePathHash::instance( + entity_path, + Instance::from(i as u64), + ), } - _ => None, - } + }) }, ) } @@ -161,7 +160,6 @@ impl Boxes2DVisualizer { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. self.data.ui_labels.extend(process_labels_2d( entity_path, - 1, std::iter::once(obj_space_bounding_box.center().truncate()), data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/boxes3d.rs b/crates/re_space_view_spatial/src/visualizers/boxes3d.rs index 89474d3ecfa3..abe15cd09979 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes3d.rs @@ -117,30 +117,20 @@ impl Boxes3DVisualizer { if data.labels.len() == 1 || num_instances <= super::MAX_NUM_LABELS_PER_ENTITY { // If there's many boxes but only a single label, place the single label at the middle of the visualization. - let (num_positions, label_positions) = if data.labels.len() == 1 - && num_instances > 1 - { + let label_positions = if data.labels.len() == 1 && num_instances > 1 { // TODO(andreas): A smoothed over time (+ discontinuity detection) bounding box would be great. - ( - 1, - itertools::Either::Left(std::iter::once(obj_space_bounding_box.center())), - ) + itertools::Either::Left(std::iter::once(obj_space_bounding_box.center())) } else { // Take center point of every box. - // Note that this is unfortunately not a FixedSizeIterator, which is why we have to pass along the number of positions. - ( - num_instances, - itertools::Either::Right( - clamped(data.centers, num_instances) - .chain(std::iter::repeat(&Position3D::ZERO)) - .map(|&c| c.into()), - ), + itertools::Either::Right( + clamped(data.centers, num_instances) + .chain(std::iter::repeat(&Position3D::ZERO)) + .map(|&c| c.into()), ) }; self.0.ui_labels.extend(process_labels_3d( entity_path, - num_positions, label_positions, data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/lines2d.rs b/crates/re_space_view_spatial/src/visualizers/lines2d.rs index ccc03e198b55..6f59df26369c 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines2d.rs @@ -127,7 +127,6 @@ impl Lines2DVisualizer { self.data.ui_labels.extend(process_labels_2d( entity_path, - label_positions.len(), label_positions, data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/lines3d.rs b/crates/re_space_view_spatial/src/visualizers/lines3d.rs index 00f7b1965d31..89971cf5b0e6 100644 --- a/crates/re_space_view_spatial/src/visualizers/lines3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/lines3d.rs @@ -132,7 +132,6 @@ impl Lines3DVisualizer { self.data.ui_labels.extend(process_labels_3d( entity_path, - label_positions.len(), label_positions, data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/points2d.rs b/crates/re_space_view_spatial/src/visualizers/points2d.rs index f893ed9e6cdd..f313fd99d35a 100644 --- a/crates/re_space_view_spatial/src/visualizers/points2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points2d.rs @@ -142,7 +142,6 @@ impl Points2DVisualizer { self.data.ui_labels.extend(process_labels_2d( entity_path, - label_positions.len(), label_positions, data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/points3d.rs b/crates/re_space_view_spatial/src/visualizers/points3d.rs index b5be3c22ba25..669221cac753 100644 --- a/crates/re_space_view_spatial/src/visualizers/points3d.rs +++ b/crates/re_space_view_spatial/src/visualizers/points3d.rs @@ -145,7 +145,6 @@ impl Points3DVisualizer { self.data.ui_labels.extend(process_labels_3d( entity_path, - label_positions.len(), label_positions.iter().copied(), data.labels, &colors, diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs index df70b0ab4187..4608aee3f4df 100644 --- a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs @@ -35,15 +35,14 @@ pub const MAX_NUM_LABELS_PER_ENTITY: usize = 30; pub fn process_labels_3d<'a>( entity_path: &'a EntityPath, - num_positions: usize, positions: impl Iterator + 'a, labels: &'a [re_types::components::Text], colors: &'a [egui::Color32], annotation_infos: &'a ResolvedAnnotationInfos, world_from_obj: glam::Affine3A, ) -> impl Iterator + 'a { - let labels = clamped(labels, num_positions); - let colors = clamped(colors, num_positions); + let labels = clamped(labels, annotation_infos.len()); + let colors = clamped(colors, annotation_infos.len()); // Assumes colors is already populated with at least one color. itertools::izip!(annotation_infos.iter(), positions, labels, colors) .enumerate() @@ -60,15 +59,14 @@ pub fn process_labels_3d<'a>( pub fn process_labels_2d<'a>( entity_path: &'a EntityPath, - num_positions: usize, positions: impl Iterator + 'a, labels: &'a [re_types::components::Text], colors: &'a [egui::Color32], annotation_infos: &'a ResolvedAnnotationInfos, world_from_obj: glam::Affine3A, ) -> impl Iterator + 'a { - let labels = clamped(labels, num_positions); - let colors = clamped(colors, num_positions); + let labels = clamped(labels, annotation_infos.len()); + let colors = clamped(colors, annotation_infos.len()); // Assumes colors is already populated with at least one color. itertools::izip!(annotation_infos.iter(), positions, labels, colors) .enumerate() diff --git a/crates/re_viewer_context/src/annotations.rs b/crates/re_viewer_context/src/annotations.rs index 8951e017cc31..d43ee938625f 100644 --- a/crates/re_viewer_context/src/annotations.rs +++ b/crates/re_viewer_context/src/annotations.rs @@ -197,6 +197,21 @@ impl ResolvedAnnotationInfos { Self::Many(infos) => Either::Right(infos.iter()), } } + + pub fn len(&self) -> usize { + match self { + Self::Same(n, _) => *n, + Self::Many(infos) => infos.len(), + } + } + + #[must_use] + pub fn is_empty(&self) -> bool { + match self { + Self::Same(n, _) => *n == 0, + Self::Many(infos) => infos.is_empty(), + } + } } // ---------------------------------------------------------------------------- From ef08a8a34f920d2a45b15f42448df3f4cae4b2aa Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 3 Jul 2024 14:49:12 +0200 Subject: [PATCH 13/16] Document new label centering behavior on archetypes --- crates/re_types/definitions/rerun/archetypes/arrows2d.fbs | 3 +++ crates/re_types/definitions/rerun/archetypes/arrows3d.fbs | 3 +++ crates/re_types/definitions/rerun/archetypes/boxes2d.fbs | 3 +++ crates/re_types/definitions/rerun/archetypes/boxes3d.fbs | 3 +++ .../re_types/definitions/rerun/archetypes/line_strips2d.fbs | 3 +++ .../re_types/definitions/rerun/archetypes/line_strips3d.fbs | 3 +++ crates/re_types/definitions/rerun/archetypes/points2d.fbs | 3 +++ crates/re_types/definitions/rerun/archetypes/points3d.fbs | 3 +++ crates/re_types/src/archetypes/arrows2d.rs | 6 ++++++ crates/re_types/src/archetypes/arrows3d.rs | 6 ++++++ crates/re_types/src/archetypes/boxes2d.rs | 6 ++++++ crates/re_types/src/archetypes/boxes3d.rs | 6 ++++++ crates/re_types/src/archetypes/line_strips2d.rs | 6 ++++++ crates/re_types/src/archetypes/line_strips3d.rs | 6 ++++++ crates/re_types/src/archetypes/points2d.rs | 6 ++++++ crates/re_types/src/archetypes/points3d.rs | 6 ++++++ rerun_cpp/src/rerun/archetypes/arrows2d.hpp | 6 ++++++ rerun_cpp/src/rerun/archetypes/arrows3d.hpp | 6 ++++++ rerun_cpp/src/rerun/archetypes/boxes2d.hpp | 6 ++++++ rerun_cpp/src/rerun/archetypes/boxes3d.hpp | 6 ++++++ rerun_cpp/src/rerun/archetypes/line_strips2d.hpp | 6 ++++++ rerun_cpp/src/rerun/archetypes/line_strips3d.hpp | 6 ++++++ rerun_cpp/src/rerun/archetypes/points2d.hpp | 6 ++++++ rerun_cpp/src/rerun/archetypes/points3d.hpp | 6 ++++++ rerun_py/rerun_sdk/rerun/archetypes/arrows2d.py | 3 +++ rerun_py/rerun_sdk/rerun/archetypes/arrows3d.py | 3 +++ rerun_py/rerun_sdk/rerun/archetypes/boxes2d.py | 3 +++ rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py | 3 +++ rerun_py/rerun_sdk/rerun/archetypes/line_strips2d.py | 6 ++++++ rerun_py/rerun_sdk/rerun/archetypes/line_strips3d.py | 6 ++++++ rerun_py/rerun_sdk/rerun/archetypes/points2d.py | 3 +++ rerun_py/rerun_sdk/rerun/archetypes/points3d.py | 3 +++ 32 files changed, 150 insertions(+) diff --git a/crates/re_types/definitions/rerun/archetypes/arrows2d.fbs b/crates/re_types/definitions/rerun/archetypes/arrows2d.fbs index fb14e4c7bdfa..9058caf84c47 100644 --- a/crates/re_types/definitions/rerun/archetypes/arrows2d.fbs +++ b/crates/re_types/definitions/rerun/archetypes/arrows2d.fbs @@ -43,6 +43,9 @@ table Arrows2D ( colors: [rerun.components.Color] ("attr.rerun.component_optional", nullable, order: 3100); /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3200); /// An optional floating point value that specifies the 2D drawing order. diff --git a/crates/re_types/definitions/rerun/archetypes/arrows3d.fbs b/crates/re_types/definitions/rerun/archetypes/arrows3d.fbs index 6168e0497e69..9efcdba20ba3 100644 --- a/crates/re_types/definitions/rerun/archetypes/arrows3d.fbs +++ b/crates/re_types/definitions/rerun/archetypes/arrows3d.fbs @@ -43,6 +43,9 @@ table Arrows3D ( colors: [rerun.components.Color] ("attr.rerun.component_optional", nullable, order: 3100); /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3200); /// Optional class Ids for the points. diff --git a/crates/re_types/definitions/rerun/archetypes/boxes2d.fbs b/crates/re_types/definitions/rerun/archetypes/boxes2d.fbs index 2f5fe36574e4..042a259f9f61 100644 --- a/crates/re_types/definitions/rerun/archetypes/boxes2d.fbs +++ b/crates/re_types/definitions/rerun/archetypes/boxes2d.fbs @@ -42,6 +42,9 @@ table Boxes2D ( radii: [rerun.components.Radius] ("attr.rerun.component_optional", nullable, order: 2500); /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3000); /// An optional floating point value that specifies the 2D drawing order. diff --git a/crates/re_types/definitions/rerun/archetypes/boxes3d.fbs b/crates/re_types/definitions/rerun/archetypes/boxes3d.fbs index a7f6141c5ad6..2d20345f8d21 100644 --- a/crates/re_types/definitions/rerun/archetypes/boxes3d.fbs +++ b/crates/re_types/definitions/rerun/archetypes/boxes3d.fbs @@ -42,6 +42,9 @@ table Boxes3D ( radii: [rerun.components.Radius] ("attr.rerun.component_optional", nullable, order: 3000); /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3100); /// Optional `ClassId`s for the boxes. diff --git a/crates/re_types/definitions/rerun/archetypes/line_strips2d.fbs b/crates/re_types/definitions/rerun/archetypes/line_strips2d.fbs index 5179dfe4697e..05d30ef7e364 100644 --- a/crates/re_types/definitions/rerun/archetypes/line_strips2d.fbs +++ b/crates/re_types/definitions/rerun/archetypes/line_strips2d.fbs @@ -34,6 +34,9 @@ table LineStrips2D ( // --- Optional --- /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3000); /// An optional floating point value that specifies the 2D drawing order of each line strip. diff --git a/crates/re_types/definitions/rerun/archetypes/line_strips3d.fbs b/crates/re_types/definitions/rerun/archetypes/line_strips3d.fbs index f8abe82bb49d..2b5c6f012bd0 100644 --- a/crates/re_types/definitions/rerun/archetypes/line_strips3d.fbs +++ b/crates/re_types/definitions/rerun/archetypes/line_strips3d.fbs @@ -34,6 +34,9 @@ table LineStrips3D ( // --- Optional --- /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3000); /// Optional `ClassId`s for the lines. diff --git a/crates/re_types/definitions/rerun/archetypes/points2d.fbs b/crates/re_types/definitions/rerun/archetypes/points2d.fbs index 48f729476dfe..86f79d9c0f21 100644 --- a/crates/re_types/definitions/rerun/archetypes/points2d.fbs +++ b/crates/re_types/definitions/rerun/archetypes/points2d.fbs @@ -38,6 +38,9 @@ table Points2D ( // --- Optional --- /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3000); /// An optional floating point value that specifies the 2D drawing order. diff --git a/crates/re_types/definitions/rerun/archetypes/points3d.fbs b/crates/re_types/definitions/rerun/archetypes/points3d.fbs index d192e685cc17..ba1047777390 100644 --- a/crates/re_types/definitions/rerun/archetypes/points3d.fbs +++ b/crates/re_types/definitions/rerun/archetypes/points3d.fbs @@ -36,6 +36,9 @@ table Points3D ( // --- Optional --- /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3000); /// Optional class Ids for the points. diff --git a/crates/re_types/src/archetypes/arrows2d.rs b/crates/re_types/src/archetypes/arrows2d.rs index b039def222d9..817ae17132c7 100644 --- a/crates/re_types/src/archetypes/arrows2d.rs +++ b/crates/re_types/src/archetypes/arrows2d.rs @@ -72,6 +72,9 @@ pub struct Arrows2D { pub colors: Option>, /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. pub labels: Option>, /// An optional floating point value that specifies the 2D drawing order. @@ -380,6 +383,9 @@ impl Arrows2D { } /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. #[inline] pub fn with_labels( mut self, diff --git a/crates/re_types/src/archetypes/arrows3d.rs b/crates/re_types/src/archetypes/arrows3d.rs index eaa3bdcf75cb..d9156774ae89 100644 --- a/crates/re_types/src/archetypes/arrows3d.rs +++ b/crates/re_types/src/archetypes/arrows3d.rs @@ -85,6 +85,9 @@ pub struct Arrows3D { pub colors: Option>, /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. pub labels: Option>, /// Optional class Ids for the points. @@ -370,6 +373,9 @@ impl Arrows3D { } /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. #[inline] pub fn with_labels( mut self, diff --git a/crates/re_types/src/archetypes/boxes2d.rs b/crates/re_types/src/archetypes/boxes2d.rs index 9ea14074d538..db597c8996d5 100644 --- a/crates/re_types/src/archetypes/boxes2d.rs +++ b/crates/re_types/src/archetypes/boxes2d.rs @@ -63,6 +63,9 @@ pub struct Boxes2D { pub radii: Option>, /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. pub labels: Option>, /// An optional floating point value that specifies the 2D drawing order. @@ -368,6 +371,9 @@ impl Boxes2D { } /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. #[inline] pub fn with_labels( mut self, diff --git a/crates/re_types/src/archetypes/boxes3d.rs b/crates/re_types/src/archetypes/boxes3d.rs index bb3a580f58a2..83717e27740f 100644 --- a/crates/re_types/src/archetypes/boxes3d.rs +++ b/crates/re_types/src/archetypes/boxes3d.rs @@ -81,6 +81,9 @@ pub struct Boxes3D { pub radii: Option>, /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. pub labels: Option>, /// Optional `ClassId`s for the boxes. @@ -392,6 +395,9 @@ impl Boxes3D { } /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. #[inline] pub fn with_labels( mut self, diff --git a/crates/re_types/src/archetypes/line_strips2d.rs b/crates/re_types/src/archetypes/line_strips2d.rs index 651f473adc6b..3be0d151450a 100644 --- a/crates/re_types/src/archetypes/line_strips2d.rs +++ b/crates/re_types/src/archetypes/line_strips2d.rs @@ -99,6 +99,9 @@ pub struct LineStrips2D { pub colors: Option>, /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. pub labels: Option>, /// An optional floating point value that specifies the 2D drawing order of each line strip. @@ -371,6 +374,9 @@ impl LineStrips2D { } /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. #[inline] pub fn with_labels( mut self, diff --git a/crates/re_types/src/archetypes/line_strips3d.rs b/crates/re_types/src/archetypes/line_strips3d.rs index 386c4a410b26..04e1066b395e 100644 --- a/crates/re_types/src/archetypes/line_strips3d.rs +++ b/crates/re_types/src/archetypes/line_strips3d.rs @@ -114,6 +114,9 @@ pub struct LineStrips3D { pub colors: Option>, /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. pub labels: Option>, /// Optional `ClassId`s for the lines. @@ -363,6 +366,9 @@ impl LineStrips3D { } /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. #[inline] pub fn with_labels( mut self, diff --git a/crates/re_types/src/archetypes/points2d.rs b/crates/re_types/src/archetypes/points2d.rs index 64eef93ba595..9636269ab576 100644 --- a/crates/re_types/src/archetypes/points2d.rs +++ b/crates/re_types/src/archetypes/points2d.rs @@ -112,6 +112,9 @@ pub struct Points2D { pub colors: Option>, /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. pub labels: Option>, /// An optional floating point value that specifies the 2D drawing order. @@ -415,6 +418,9 @@ impl Points2D { } /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. #[inline] pub fn with_labels( mut self, diff --git a/crates/re_types/src/archetypes/points3d.rs b/crates/re_types/src/archetypes/points3d.rs index 3e1657f894c0..2b0e3ef05806 100644 --- a/crates/re_types/src/archetypes/points3d.rs +++ b/crates/re_types/src/archetypes/points3d.rs @@ -110,6 +110,9 @@ pub struct Points3D { pub colors: Option>, /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. pub labels: Option>, /// Optional class Ids for the points. @@ -390,6 +393,9 @@ impl Points3D { } /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. #[inline] pub fn with_labels( mut self, diff --git a/rerun_cpp/src/rerun/archetypes/arrows2d.hpp b/rerun_cpp/src/rerun/archetypes/arrows2d.hpp index 7d2fe4606bc2..7307d0715ddd 100644 --- a/rerun_cpp/src/rerun/archetypes/arrows2d.hpp +++ b/rerun_cpp/src/rerun/archetypes/arrows2d.hpp @@ -65,6 +65,9 @@ namespace rerun::archetypes { std::optional> colors; /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. std::optional> labels; /// An optional floating point value that specifies the 2D drawing order. @@ -124,6 +127,9 @@ namespace rerun::archetypes { } /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. Arrows2D with_labels(Collection _labels) && { labels = std::move(_labels); // See: https://github.com/rerun-io/rerun/issues/4027 diff --git a/rerun_cpp/src/rerun/archetypes/arrows3d.hpp b/rerun_cpp/src/rerun/archetypes/arrows3d.hpp index b801d3996cca..96cf48cf1f3b 100644 --- a/rerun_cpp/src/rerun/archetypes/arrows3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/arrows3d.hpp @@ -80,6 +80,9 @@ namespace rerun::archetypes { std::optional> colors; /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. std::optional> labels; /// Optional class Ids for the points. @@ -135,6 +138,9 @@ namespace rerun::archetypes { } /// Optional text labels for the arrows. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. Arrows3D with_labels(Collection _labels) && { labels = std::move(_labels); // See: https://github.com/rerun-io/rerun/issues/4027 diff --git a/rerun_cpp/src/rerun/archetypes/boxes2d.hpp b/rerun_cpp/src/rerun/archetypes/boxes2d.hpp index 8742f92b6cfc..d4cb5a04e728 100644 --- a/rerun_cpp/src/rerun/archetypes/boxes2d.hpp +++ b/rerun_cpp/src/rerun/archetypes/boxes2d.hpp @@ -53,6 +53,9 @@ namespace rerun::archetypes { std::optional> radii; /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. std::optional> labels; /// An optional floating point value that specifies the 2D drawing order. @@ -148,6 +151,9 @@ namespace rerun::archetypes { } /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. Boxes2D with_labels(Collection _labels) && { labels = std::move(_labels); // See: https://github.com/rerun-io/rerun/issues/4027 diff --git a/rerun_cpp/src/rerun/archetypes/boxes3d.hpp b/rerun_cpp/src/rerun/archetypes/boxes3d.hpp index 195eb5421a94..b2f493ec15b2 100644 --- a/rerun_cpp/src/rerun/archetypes/boxes3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/boxes3d.hpp @@ -75,6 +75,9 @@ namespace rerun::archetypes { std::optional> radii; /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. std::optional> labels; /// Optional `ClassId`s for the boxes. @@ -173,6 +176,9 @@ namespace rerun::archetypes { } /// Optional text labels for the boxes. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. Boxes3D with_labels(Collection _labels) && { labels = std::move(_labels); // See: https://github.com/rerun-io/rerun/issues/4027 diff --git a/rerun_cpp/src/rerun/archetypes/line_strips2d.hpp b/rerun_cpp/src/rerun/archetypes/line_strips2d.hpp index e4188ff6b2cc..4067d2113bc2 100644 --- a/rerun_cpp/src/rerun/archetypes/line_strips2d.hpp +++ b/rerun_cpp/src/rerun/archetypes/line_strips2d.hpp @@ -98,6 +98,9 @@ namespace rerun::archetypes { std::optional> colors; /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. std::optional> labels; /// An optional floating point value that specifies the 2D drawing order of each line strip. @@ -139,6 +142,9 @@ namespace rerun::archetypes { } /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. LineStrips2D with_labels(Collection _labels) && { labels = std::move(_labels); // See: https://github.com/rerun-io/rerun/issues/4027 diff --git a/rerun_cpp/src/rerun/archetypes/line_strips3d.hpp b/rerun_cpp/src/rerun/archetypes/line_strips3d.hpp index 8cda61271eba..b930e4b76096 100644 --- a/rerun_cpp/src/rerun/archetypes/line_strips3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/line_strips3d.hpp @@ -110,6 +110,9 @@ namespace rerun::archetypes { std::optional> colors; /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. std::optional> labels; /// Optional `ClassId`s for the lines. @@ -146,6 +149,9 @@ namespace rerun::archetypes { } /// Optional text labels for the line strips. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. LineStrips3D with_labels(Collection _labels) && { labels = std::move(_labels); // See: https://github.com/rerun-io/rerun/issues/4027 diff --git a/rerun_cpp/src/rerun/archetypes/points2d.hpp b/rerun_cpp/src/rerun/archetypes/points2d.hpp index dd9ff2b8e054..c207744475f6 100644 --- a/rerun_cpp/src/rerun/archetypes/points2d.hpp +++ b/rerun_cpp/src/rerun/archetypes/points2d.hpp @@ -114,6 +114,9 @@ namespace rerun::archetypes { std::optional> colors; /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. std::optional> labels; /// An optional floating point value that specifies the 2D drawing order. @@ -164,6 +167,9 @@ namespace rerun::archetypes { } /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. Points2D with_labels(Collection _labels) && { labels = std::move(_labels); // See: https://github.com/rerun-io/rerun/issues/4027 diff --git a/rerun_cpp/src/rerun/archetypes/points3d.hpp b/rerun_cpp/src/rerun/archetypes/points3d.hpp index a3ec21c61c23..9a9b3f12874c 100644 --- a/rerun_cpp/src/rerun/archetypes/points3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/points3d.hpp @@ -109,6 +109,9 @@ namespace rerun::archetypes { std::optional> colors; /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. std::optional> labels; /// Optional class Ids for the points. @@ -154,6 +157,9 @@ namespace rerun::archetypes { } /// Optional text labels for the points. + /// + /// If there's a single label present, it will be placed at the center of the entity. + /// Otherwise, each instance will have its own label. Points3D with_labels(Collection _labels) && { labels = std::move(_labels); // See: https://github.com/rerun-io/rerun/issues/4027 diff --git a/rerun_py/rerun_sdk/rerun/archetypes/arrows2d.py b/rerun_py/rerun_sdk/rerun/archetypes/arrows2d.py index 045251637770..805d65b312e8 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/arrows2d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/arrows2d.py @@ -120,6 +120,9 @@ def _clear(cls) -> Arrows2D: ) # Optional text labels for the arrows. # + # If there's a single label present, it will be placed at the center of the entity. + # Otherwise, each instance will have its own label. + # # (Docstring intentionally commented out to hide this field from the docs) draw_order: components.DrawOrderBatch | None = field( diff --git a/rerun_py/rerun_sdk/rerun/archetypes/arrows3d.py b/rerun_py/rerun_sdk/rerun/archetypes/arrows3d.py index 136032200874..c8d9ae04ae7e 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/arrows3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/arrows3d.py @@ -119,6 +119,9 @@ def _clear(cls) -> Arrows3D: ) # Optional text labels for the arrows. # + # If there's a single label present, it will be placed at the center of the entity. + # Otherwise, each instance will have its own label. + # # (Docstring intentionally commented out to hide this field from the docs) class_ids: components.ClassIdBatch | None = field( diff --git a/rerun_py/rerun_sdk/rerun/archetypes/boxes2d.py b/rerun_py/rerun_sdk/rerun/archetypes/boxes2d.py index 4263784bb717..09af5d869226 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/boxes2d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/boxes2d.py @@ -106,6 +106,9 @@ def _clear(cls) -> Boxes2D: ) # Optional text labels for the boxes. # + # If there's a single label present, it will be placed at the center of the entity. + # Otherwise, each instance will have its own label. + # # (Docstring intentionally commented out to hide this field from the docs) draw_order: components.DrawOrderBatch | None = field( diff --git a/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py b/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py index c5cdf206da10..3823585f7259 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py @@ -130,6 +130,9 @@ def _clear(cls) -> Boxes3D: ) # Optional text labels for the boxes. # + # If there's a single label present, it will be placed at the center of the entity. + # Otherwise, each instance will have its own label. + # # (Docstring intentionally commented out to hide this field from the docs) class_ids: components.ClassIdBatch | None = field( diff --git a/rerun_py/rerun_sdk/rerun/archetypes/line_strips2d.py b/rerun_py/rerun_sdk/rerun/archetypes/line_strips2d.py index d700518ad521..e98c3e729218 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/line_strips2d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/line_strips2d.py @@ -116,6 +116,9 @@ def __init__( Optional colors for the line strips. labels: Optional text labels for the line strips. + + If there's a single label present, it will be placed at the center of the entity. + Otherwise, each instance will have its own label. draw_order: An optional floating point value that specifies the 2D drawing order of each line strip. @@ -186,6 +189,9 @@ def _clear(cls) -> LineStrips2D: ) # Optional text labels for the line strips. # + # If there's a single label present, it will be placed at the center of the entity. + # Otherwise, each instance will have its own label. + # # (Docstring intentionally commented out to hide this field from the docs) draw_order: components.DrawOrderBatch | None = field( diff --git a/rerun_py/rerun_sdk/rerun/archetypes/line_strips3d.py b/rerun_py/rerun_sdk/rerun/archetypes/line_strips3d.py index 7b266e522e80..5de092c0d477 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/line_strips3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/line_strips3d.py @@ -134,6 +134,9 @@ def __init__( Optional colors for the line strips. labels: Optional text labels for the line strips. + + If there's a single label present, it will be placed at the center of the entity. + Otherwise, each instance will have its own label. class_ids: Optional `ClassId`s for the lines. @@ -197,6 +200,9 @@ def _clear(cls) -> LineStrips3D: ) # Optional text labels for the line strips. # + # If there's a single label present, it will be placed at the center of the entity. + # Otherwise, each instance will have its own label. + # # (Docstring intentionally commented out to hide this field from the docs) class_ids: components.ClassIdBatch | None = field( diff --git a/rerun_py/rerun_sdk/rerun/archetypes/points2d.py b/rerun_py/rerun_sdk/rerun/archetypes/points2d.py index ebbf205bee35..fc5ea795de6b 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/points2d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/points2d.py @@ -154,6 +154,9 @@ def _clear(cls) -> Points2D: ) # Optional text labels for the points. # + # If there's a single label present, it will be placed at the center of the entity. + # Otherwise, each instance will have its own label. + # # (Docstring intentionally commented out to hide this field from the docs) draw_order: components.DrawOrderBatch | None = field( diff --git a/rerun_py/rerun_sdk/rerun/archetypes/points3d.py b/rerun_py/rerun_sdk/rerun/archetypes/points3d.py index 873a6f2a0145..b30955ac4748 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/points3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/points3d.py @@ -145,6 +145,9 @@ def _clear(cls) -> Points3D: ) # Optional text labels for the points. # + # If there's a single label present, it will be placed at the center of the entity. + # Otherwise, each instance will have its own label. + # # (Docstring intentionally commented out to hide this field from the docs) class_ids: components.ClassIdBatch | None = field( From 37c297de551a4cbe97081ca110d7f2dcb70f86a4 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Thu, 4 Jul 2024 10:27:10 +0200 Subject: [PATCH 14/16] missing inline attributes --- crates/re_viewer_context/src/annotations.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/re_viewer_context/src/annotations.rs b/crates/re_viewer_context/src/annotations.rs index d43ee938625f..0fd6ca04e081 100644 --- a/crates/re_viewer_context/src/annotations.rs +++ b/crates/re_viewer_context/src/annotations.rs @@ -198,6 +198,7 @@ impl ResolvedAnnotationInfos { } } + #[inline] pub fn len(&self) -> usize { match self { Self::Same(n, _) => *n, @@ -205,6 +206,7 @@ impl ResolvedAnnotationInfos { } } + #[inline] #[must_use] pub fn is_empty(&self) -> bool { match self { From 832dd0dfd0e4c3068810c10fffbd43698e22ee0c Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Thu, 4 Jul 2024 10:30:11 +0200 Subject: [PATCH 15/16] document process label methods and assert initial conditions --- .../src/visualizers/boxes2d.rs | 13 ++++++++-- .../src/visualizers/utilities/labels.rs | 24 +++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs index df681ca13996..1ff88bb03681 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs @@ -42,6 +42,11 @@ impl Default for Boxes2DVisualizer { // 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 Boxes2DVisualizer { + /// Produces 2d rect ui labels from component data. + /// + /// Does nothing if there's no positions or no labels passed. + /// Assumes that there's at least a single color in `colors`. + /// Otherwise, produces one label per center position passed. fn process_labels<'a>( entity_path: &'a EntityPath, half_sizes: &'a [HalfSizes2D], @@ -50,9 +55,13 @@ impl Boxes2DVisualizer { colors: &'a [egui::Color32], annotation_infos: &'a ResolvedAnnotationInfos, ) -> impl Iterator + 'a { + debug_assert!( + labels.is_empty() || !colors.is_empty(), + "Cannot add labels without colors" + ); + let labels = clamped(labels, annotation_infos.len()); - let colors = clamped(colors, annotation_infos.len()); // Assumes colors is already populated with at least one color. - let centers = centers.chain(std::iter::repeat(&Position2D::ZERO)); + let colors = clamped(colors, annotation_infos.len()); itertools::izip!(annotation_infos.iter(), half_sizes, centers, labels, colors) .enumerate() diff --git a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs index 4608aee3f4df..c1522cad0676 100644 --- a/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs +++ b/crates/re_space_view_spatial/src/visualizers/utilities/labels.rs @@ -33,6 +33,11 @@ pub struct UiLabel { /// TODO(#4451): Hiding of labels should be configurable. This can be the heuristic for it. pub const MAX_NUM_LABELS_PER_ENTITY: usize = 30; +/// Produces 3D ui labels from component data. +/// +/// Does nothing if there's no positions or no labels passed. +/// Assumes that there's at least a single color in `colors`. +/// Otherwise, produces one label per position passed. pub fn process_labels_3d<'a>( entity_path: &'a EntityPath, positions: impl Iterator + 'a, @@ -41,8 +46,13 @@ pub fn process_labels_3d<'a>( annotation_infos: &'a ResolvedAnnotationInfos, world_from_obj: glam::Affine3A, ) -> impl Iterator + 'a { + debug_assert!( + labels.is_empty() || !colors.is_empty(), + "Cannot add labels without colors" + ); + let labels = clamped(labels, annotation_infos.len()); - let colors = clamped(colors, annotation_infos.len()); // Assumes colors is already populated with at least one color. + let colors = clamped(colors, annotation_infos.len()); itertools::izip!(annotation_infos.iter(), positions, labels, colors) .enumerate() @@ -57,6 +67,11 @@ pub fn process_labels_3d<'a>( }) } +/// Produces 2D ui labels from component data. +/// +/// Does nothing if there's no positions or no labels passed. +/// Assumes that there's at least a single color in `colors`. +/// Otherwise, produces one label per position passed. pub fn process_labels_2d<'a>( entity_path: &'a EntityPath, positions: impl Iterator + 'a, @@ -65,8 +80,13 @@ pub fn process_labels_2d<'a>( annotation_infos: &'a ResolvedAnnotationInfos, world_from_obj: glam::Affine3A, ) -> impl Iterator + 'a { + debug_assert!( + labels.is_empty() || !colors.is_empty(), + "Cannot add labels without colors" + ); + let labels = clamped(labels, annotation_infos.len()); - let colors = clamped(colors, annotation_infos.len()); // Assumes colors is already populated with at least one color. + let colors = clamped(colors, annotation_infos.len()); itertools::izip!(annotation_infos.iter(), positions, labels, colors) .enumerate() From 00414945c14142b139ae11c10f598274d82d846f Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Thu, 4 Jul 2024 10:37:37 +0200 Subject: [PATCH 16/16] fix casing typo --- crates/re_space_view_spatial/src/visualizers/boxes2d.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs index 1ff88bb03681..837ff59b76c6 100644 --- a/crates/re_space_view_spatial/src/visualizers/boxes2d.rs +++ b/crates/re_space_view_spatial/src/visualizers/boxes2d.rs @@ -42,7 +42,7 @@ impl Default for Boxes2DVisualizer { // 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 Boxes2DVisualizer { - /// Produces 2d rect ui labels from component data. + /// Produces 2D rect ui labels from component data. /// /// Does nothing if there's no positions or no labels passed. /// Assumes that there's at least a single color in `colors`.