Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple instances of points/arrows/boxes with single label display label now at the center #6741

Merged
merged 17 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 41 additions & 68 deletions crates/re_space_view_spatial/src/visualizers/arrows2d.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -8,34 +7,31 @@ 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,
};

// ---

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)),
}
}
Expand All @@ -44,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<Item = &'a Position2D> + 'a,
labels: &'a [Text],
colors: &'a [egui::Color32],
annotation_infos: &'a ResolvedAnnotationInfos,
world_from_obj: glam::Affine3A,
) -> impl Iterator<Item = UiLabel> + '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<'_>,
Expand Down Expand Up @@ -115,31 +72,18 @@ impl Arrows2DVisualizer {
let colors =
process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors);

if num_instances <= self.max_labels {
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();
Expand All @@ -164,15 +108,44 @@ 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 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,
label_positions,
data.labels,
&colors,
&annotation_infos,
ent_context.world_from_entity,
));
}
}
}
}
Expand Down
110 changes: 40 additions & 70 deletions crates/re_space_view_spatial/src/visualizers/arrows3d.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -8,34 +7,31 @@ 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,
};

// ---

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)),
}
}
Expand All @@ -44,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<Item = &'a Position3D> + 'a,
labels: &'a [Text],
colors: &'a [egui::Color32],
annotation_infos: &'a ResolvedAnnotationInfos,
world_from_obj: glam::Affine3A,
) -> impl Iterator<Item = UiLabel> + '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<'_>,
Expand Down Expand Up @@ -117,31 +72,18 @@ impl Arrows3DVisualizer {
let colors =
process_color_slice(ctx, self, num_instances, &annotation_infos, data.colors);

if num_instances <= self.max_labels {
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();
Expand All @@ -167,15 +109,43 @@ 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 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()))
} else {
// Take middle point of every arrow.
let origins = clamped(data.origins, num_instances)
.chain(std::iter::repeat(&Position3D::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::Vec3::from(origin.0) + glam::Vec3::from(vector.0)) * 0.45
},
))
};

self.data.ui_labels.extend(process_labels_3d(
entity_path,
label_positions,
data.labels,
&colors,
&annotation_infos,
ent_context.world_from_entity,
));
}
}
}
}
Expand Down
Loading
Loading