Skip to content

Commit

Permalink
Arrow2D Archetype & Visualizer (#4713)
Browse files Browse the repository at this point in the history
### What

Adds support for 2D arrows.
Lots of copy paste of 3D arrow things, but not so much to be terrible.
Was actually pleasantly surprised that I didn't need much
codegen-extensions. Deduplication (in particular in the Visualizer)
takes more effort than it's worth I figured :)


The full vertical: Archetype, extensions, visualizer roundtrip tests,
roundtripable examples!
Only left out dedicated archetype tests since there's nothing new &
interesting here really.

That's what the example looks like: (kept it intentionally simple)

<img width="999" alt="image"
src="https://github.com/rerun-io/rerun/assets/1220815/2550370e-c2e0-4a88-869f-783443d14358">


### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested the web demo (if applicable):
* Using newly built examples:
[app.rerun.io](https://app.rerun.io/pr/4713/index.html)
* Using examples from latest `main` build:
[app.rerun.io](https://app.rerun.io/pr/4713/index.html?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[app.rerun.io](https://app.rerun.io/pr/4713/index.html?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG

- [PR Build Summary](https://build.rerun.io/pr/4713)
- [Docs
preview](https://rerun.io/preview/dc2e334d98665c0f6da6c674b0b0e0cc4e93fbdd/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/dc2e334d98665c0f6da6c674b0b0e0cc4e93fbdd/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
  • Loading branch information
Wumpf authored Jan 8, 2024
1 parent 1d2de95 commit de1f467
Showing 59 changed files with 1,904 additions and 3 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

218 changes: 218 additions & 0 deletions crates/re_space_view_spatial/src/visualizers/arrows2d.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
use re_entity_db::{EntityPath, InstancePathHash};
use re_query::{ArchetypeView, QueryError};
use re_renderer::renderer::LineStripFlags;
use re_types::{
archetypes::Arrows2D,
components::{Position2D, Text, Vector2D},
Archetype as _, ComponentNameSet,
};
use re_viewer_context::{
ApplicableEntities, IdentifiedViewSystem, ResolvedAnnotationInfos,
SpaceViewSystemExecutionError, ViewContextCollection, ViewQuery, ViewerContext,
VisualizableEntities, VisualizableFilterContext, VisualizerSystem,
};

use super::{picking_id_from_instance_key, process_annotations, SpatialViewVisualizerData};
use crate::{
contexts::{EntityDepthOffsets, SpatialSceneEntityContext},
view_kind::SpatialSpaceViewKind,
visualizers::{
entity_iterator::process_archetype_views, filter_visualizable_2d_entities, process_colors,
process_radii, UiLabel, UiLabelTarget,
},
};

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)),
}
}
}

impl Arrows2DVisualizer {
fn process_labels<'a>(
arch_view: &'a ArchetypeView<Arrows2D>,
instance_path_hashes: &'a [InstancePathHash],
colors: &'a [egui::Color32],
annotation_infos: &'a ResolvedAnnotationInfos,
world_from_obj: glam::Affine3A,
) -> Result<impl Iterator<Item = UiLabel> + 'a, QueryError> {
let labels = itertools::izip!(
annotation_infos.iter(),
arch_view.iter_required_component::<Vector2D>()?,
arch_view.iter_optional_component::<Position2D>()?,
arch_view.iter_optional_component::<Text>()?,
colors,
instance_path_hashes,
)
.filter_map(
move |(annotation_info, vector, origin, label, color, labeled_instance)| {
let origin = origin.unwrap_or(Position2D::ZERO);
let label = annotation_info.label(label.as_ref().map(|l| l.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: *labeled_instance,
})
}
_ => None,
}
},
);
Ok(labels)
}

fn process_arch_view(
&mut self,
query: &ViewQuery<'_>,
arch_view: &ArchetypeView<Arrows2D>,
ent_path: &EntityPath,
ent_context: &SpatialSceneEntityContext<'_>,
) -> Result<(), QueryError> {
let annotation_infos =
process_annotations::<Vector2D, Arrows2D>(query, arch_view, &ent_context.annotations)?;

let colors = process_colors(arch_view, ent_path, &annotation_infos)?;
let radii = process_radii(arch_view, ent_path)?;

if arch_view.num_instances() <= self.max_labels {
// Max labels is small enough that we can afford iterating on the colors again.
let colors =
process_colors(arch_view, ent_path, &annotation_infos)?.collect::<Vec<_>>();

let instance_path_hashes_for_picking = {
re_tracing::profile_scope!("instance_hashes");
arch_view
.iter_instance_keys()
.map(|instance_key| InstancePathHash::instance(ent_path, instance_key))
.collect::<Vec<_>>()
};

self.data.ui_labels.extend(Self::process_labels(
arch_view,
&instance_path_hashes_for_picking,
&colors,
&annotation_infos,
ent_context.world_from_entity,
)?);
}

let mut line_builder = ent_context.shared_render_builders.lines();
let mut line_batch = line_builder
.batch("arrows2d")
.world_from_obj(ent_context.world_from_entity)
.outline_mask_ids(ent_context.highlight.overall)
.picking_object_id(re_renderer::PickingLayerObjectId(ent_path.hash64()));

let instance_keys = arch_view.iter_instance_keys();
let pick_ids = arch_view
.iter_instance_keys()
.map(picking_id_from_instance_key);
let vectors = arch_view.iter_required_component::<Vector2D>()?;
let origins = arch_view.iter_optional_component::<Position2D>()?;

let mut bounding_box = macaw::BoundingBox::nothing();

for (instance_key, vector, origin, radius, color, pick_id) in
itertools::izip!(instance_keys, vectors, origins, radii, colors, pick_ids)
{
let vector: glam::Vec2 = vector.0.into();
let origin: glam::Vec2 = origin.unwrap_or(Position2D::ZERO).0.into();
let end = origin + vector;

let segment = line_batch
.add_segment_2d(origin, end)
.radius(radius)
.color(color)
.flags(
LineStripFlags::FLAG_CAP_END_TRIANGLE
| LineStripFlags::FLAG_CAP_START_ROUND
| LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS,
)
.picking_instance_id(pick_id);

if let Some(outline_mask_ids) = ent_context.highlight.instances.get(&instance_key) {
segment.outline_mask_ids(*outline_mask_ids);
}

bounding_box.extend(origin.extend(0.0));
bounding_box.extend(end.extend(0.0));
}

self.data
.extend_bounding_box(bounding_box, ent_context.world_from_entity);

Ok(())
}
}

impl IdentifiedViewSystem for Arrows2DVisualizer {
fn identifier() -> re_viewer_context::ViewSystemIdentifier {
"Arrows2D".into()
}
}

impl VisualizerSystem for Arrows2DVisualizer {
fn required_components(&self) -> ComponentNameSet {
Arrows2D::required_components()
.iter()
.map(ToOwned::to_owned)
.collect()
}

fn indicator_components(&self) -> ComponentNameSet {
std::iter::once(Arrows2D::indicator().name()).collect()
}

fn filter_visualizable_entities(
&self,
entities: ApplicableEntities,
context: &dyn VisualizableFilterContext,
) -> VisualizableEntities {
re_tracing::profile_function!();
filter_visualizable_2d_entities(entities, context)
}

fn execute(
&mut self,
ctx: &ViewerContext<'_>,
query: &ViewQuery<'_>,
view_ctx: &ViewContextCollection,
) -> Result<Vec<re_renderer::QueueableDrawData>, SpaceViewSystemExecutionError> {
process_archetype_views::<Arrows2DVisualizer, Arrows2D, { Arrows2D::NUM_COMPONENTS }, _>(
ctx,
query,
view_ctx,
view_ctx.get::<EntityDepthOffsets>()?.lines2d,
|_ctx, ent_path, _ent_props, arch_view, ent_context| {
self.process_arch_view(query, &arch_view, ent_path, ent_context)
},
)?;

Ok(Vec::new()) // TODO(andreas): Optionally return point & line draw data once SharedRenderBuilders is gone.
}

fn data(&self) -> Option<&dyn std::any::Any> {
Some(self.data.as_any())
}

fn as_any(&self) -> &dyn std::any::Any {
self
}
}
2 changes: 1 addition & 1 deletion crates/re_space_view_spatial/src/visualizers/arrows3d.rs
Original file line number Diff line number Diff line change
@@ -115,7 +115,7 @@ impl Arrows3DVisualizer {

let mut line_builder = ent_context.shared_render_builders.lines();
let mut line_batch = line_builder
.batch("arrows")
.batch("arrows3d")
.world_from_obj(ent_context.world_from_entity)
.outline_mask_ids(ent_context.highlight.overall)
.picking_object_id(re_renderer::PickingLayerObjectId(ent_path.hash64()));
3 changes: 3 additions & 0 deletions crates/re_space_view_spatial/src/visualizers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Responsible for populating `SceneSpatialPrimitives` and `SceneSpatialUiData`
mod arrows2d;
mod arrows3d;
mod assets3d;
mod boxes2d;
@@ -53,6 +54,7 @@ pub fn register_2d_spatial_visualizers(
// Note: 2D spatial systems don't include cameras as this
// visualizer only shows a 2D projection WITHIN a 3D view.
system_registry.register_visualizer::<arrows3d::Arrows3DVisualizer>()?;
system_registry.register_visualizer::<arrows2d::Arrows2DVisualizer>()?;
system_registry.register_visualizer::<assets3d::Asset3DVisualizer>()?;
system_registry.register_visualizer::<boxes2d::Boxes2DVisualizer>()?;
system_registry.register_visualizer::<boxes3d::Boxes3DVisualizer>()?;
@@ -70,6 +72,7 @@ pub fn register_3d_spatial_visualizers(
system_registry: &mut SpaceViewSystemRegistrator<'_>,
) -> Result<(), SpaceViewClassRegistryError> {
system_registry.register_visualizer::<arrows3d::Arrows3DVisualizer>()?;
system_registry.register_visualizer::<arrows2d::Arrows2DVisualizer>()?;
system_registry.register_visualizer::<assets3d::Asset3DVisualizer>()?;
system_registry.register_visualizer::<boxes2d::Boxes2DVisualizer>()?;
system_registry.register_visualizer::<boxes3d::Boxes3DVisualizer>()?;
1 change: 1 addition & 0 deletions crates/re_types/definitions/rerun/archetypes.fbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
include "./archetypes/annotation_context.fbs";
include "./archetypes/arrows2d.fbs";
include "./archetypes/arrows3d.fbs";
include "./archetypes/asset3d.fbs";
include "./archetypes/bar_chart.fbs";
54 changes: 54 additions & 0 deletions crates/re_types/definitions/rerun/archetypes/arrows2d.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
include "fbs/attributes.fbs";
include "rust/attributes.fbs";
include "cpp/attributes.fbs";

include "rerun/datatypes.fbs";
include "rerun/components.fbs";

namespace rerun.archetypes;

// ---

/// 2D arrows with optional colors, radii, labels, etc.
///
/// \example arrow2d_simple title="Simple batch of 2D Arrows" image="https://static.rerun.io/arrow2d_simple/59f044ccc03f7bc66ee802288f75706618b29a6e/1200w.png"
table Arrows2D (
"attr.rust.derive": "PartialEq",
"attr.rust.new_pub_crate",
"attr.cpp.no_field_ctors",
"attr.docs.unreleased"
) {
// --- Required ---

/// All the vectors for each arrow in the batch.
vectors: [rerun.components.Vector2D] ("attr.rerun.component_required", order: 1000);

// --- Recommended ---

/// All the origin (base) positions for each arrow in the batch.
///
/// If no origins are set, (0, 0) is used as the origin for each arrow.
origins: [rerun.components.Position2D] ("attr.rerun.component_recommended", nullable, order: 2000);

// --- Optional ---

/// Optional radii for the arrows.
///
/// The shaft is rendered as a line with `radius = 0.5 * radius`.
/// The tip is rendered with `height = 2.0 * radius` and `radius = 1.0 * radius`.
radii: [rerun.components.Radius] ("attr.rerun.component_optional", nullable, order: 3000);

/// Optional colors for the points.
colors: [rerun.components.Color] ("attr.rerun.component_optional", nullable, order: 3100);

/// Optional text labels for the arrows.
labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3200);

/// Optional class Ids for the points.
///
/// The class ID provides colors and labels if not specified explicitly.
class_ids: [rerun.components.ClassId] ("attr.rerun.component_optional", nullable, order: 3300);

/// Unique identifiers for each individual point in the batch.
instance_keys: [rerun.components.InstanceKey] ("attr.rerun.component_optional", nullable, order: 3400);
}
1 change: 1 addition & 0 deletions crates/re_types/definitions/rerun/components.fbs
Original file line number Diff line number Diff line change
@@ -28,5 +28,6 @@ include "./components/tensor_data.fbs";
include "./components/text_log_level.fbs";
include "./components/text.fbs";
include "./components/transform3d.fbs";
include "./components/vector2d.fbs";
include "./components/vector3d.fbs";
include "./components/view_coordinates.fbs";
19 changes: 19 additions & 0 deletions crates/re_types/definitions/rerun/components/vector2d.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
include "arrow/attributes.fbs";
include "python/attributes.fbs";
include "fbs/attributes.fbs";
include "rust/attributes.fbs";

include "../datatypes/vec2d.fbs";

namespace rerun.components;

// ---

/// A vector in 2D space.
struct Vector2D (
"attr.rust.derive": "Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable",
"attr.rust.repr": "transparent",
"attr.docs.unreleased"
) {
vector: rerun.datatypes.Vec2D (order: 100);
}
1 change: 1 addition & 0 deletions crates/re_types/src/archetypes/.gitattributes

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit de1f467

Please sign in to comment.