-
Notifications
You must be signed in to change notification settings - Fork 373
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Arrow2D Archetype & Visualizer (#4713)
### 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
Showing
59 changed files
with
1,904 additions
and
3 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
218 changes: 218 additions & 0 deletions
218
crates/re_space_view_spatial/src/visualizers/arrows2d.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.