Skip to content

Commit afca28b

Browse files
jleibsemilk
andauthored
Introduce a new component for MarkerShape and use it in SeriesPoint (#5004)
### What - Add new MarkerShape component - Register a component-editor for it - Use it in PointVisualizerSystem / TimeSeries space-view - Bump up the radius for the point visualizer system ![image](https://github.com/rerun-io/rerun/assets/3312232/c3f065f4-5bd8-4241-85b8-db4986b5b989) TODO: - #5005 ### 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/5004/index.html) * Using examples from latest `main` build: [app.rerun.io](https://app.rerun.io/pr/5004/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/5004/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/5004) - [Docs preview](https://rerun.io/preview/d958ad44008ad21a5313a52985e397b0e04e4d74/docs) <!--DOCS-PREVIEW--> - [Examples preview](https://rerun.io/preview/d958ad44008ad21a5313a52985e397b0e04e4d74/examples) <!--EXAMPLES-PREVIEW--> - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) --------- Co-authored-by: Emil Ernerfeldt <[email protected]>
1 parent 1dad7c8 commit afca28b

28 files changed

Lines changed: 668 additions & 33 deletions

File tree

crates/re_data_ui/src/editors.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ use re_data_store::{DataStore, LatestAtQuery};
55
use re_log_types::EntityPath;
66
use re_query::ComponentWithInstances;
77
use re_types::{
8-
components::{Color, Radius, ScalarScattering, Text},
8+
components::{Color, MarkerShape, Radius, ScalarScattering, Text},
99
Component, Loggable,
1010
};
1111
use re_viewer_context::{UiVerbosity, ViewerContext};
1212

13+
// ----
14+
1315
#[allow(clippy::too_many_arguments)]
1416
fn edit_color_ui(
1517
ctx: &ViewerContext<'_>,
@@ -55,6 +57,8 @@ fn default_color(
5557
Color::from_rgb(255, 255, 255)
5658
}
5759

60+
// ----
61+
5862
#[allow(clippy::too_many_arguments)]
5963
fn edit_text_ui(
6064
ctx: &ViewerContext<'_>,
@@ -94,6 +98,8 @@ fn default_text(
9498
Text::from(entity_path.to_string())
9599
}
96100

101+
// ----
102+
97103
#[allow(clippy::too_many_arguments)]
98104
fn edit_scatter_ui(
99105
ctx: &ViewerContext<'_>,
@@ -141,6 +147,8 @@ fn default_scatter(
141147
ScalarScattering::from(false)
142148
}
143149

150+
// ----
151+
144152
#[allow(clippy::too_many_arguments)]
145153
fn edit_radius_ui(
146154
ctx: &ViewerContext<'_>,
@@ -186,6 +194,55 @@ fn default_radius(
186194
Radius::from(1.0)
187195
}
188196

197+
// ----
198+
199+
#[allow(clippy::too_many_arguments)]
200+
fn edit_marker_shape_ui(
201+
ctx: &ViewerContext<'_>,
202+
ui: &mut egui::Ui,
203+
_verbosity: UiVerbosity,
204+
query: &LatestAtQuery,
205+
store: &DataStore,
206+
entity_path: &EntityPath,
207+
override_path: &EntityPath,
208+
component: &ComponentWithInstances,
209+
instance_key: &re_types::components::InstanceKey,
210+
) {
211+
let current_marker = component
212+
.lookup::<MarkerShape>(instance_key)
213+
.ok()
214+
.unwrap_or_else(|| default_marker_shape(ctx, query, store, entity_path));
215+
216+
let mut edit_marker = current_marker;
217+
218+
let marker_text = edit_marker.as_str();
219+
220+
egui::ComboBox::from_id_source("marker_shape")
221+
.selected_text(marker_text)
222+
.show_ui(ui, |ui| {
223+
ui.style_mut().wrap = Some(false);
224+
for marker in MarkerShape::all_markers() {
225+
ui.selectable_value(&mut edit_marker, marker, marker.as_str());
226+
}
227+
});
228+
229+
if edit_marker != current_marker {
230+
ctx.save_blueprint_component(override_path, edit_marker);
231+
}
232+
}
233+
234+
#[inline]
235+
fn default_marker_shape(
236+
_ctx: &ViewerContext<'_>,
237+
_query: &LatestAtQuery,
238+
_store: &DataStore,
239+
_entity_path: &EntityPath,
240+
) -> MarkerShape {
241+
MarkerShape::default()
242+
}
243+
244+
// ----
245+
189246
fn register_editor<'a, C: Component + Loggable + 'static>(
190247
registry: &mut re_viewer_context::ComponentUiRegistry,
191248
default: fn(&ViewerContext<'_>, &LatestAtQuery, &DataStore, &EntityPath) -> C,
@@ -218,4 +275,5 @@ pub fn register_editors(registry: &mut re_viewer_context::ComponentUiRegistry) {
218275
register_editor::<Text>(registry, default_text, edit_text_ui);
219276
register_editor::<ScalarScattering>(registry, default_scatter, edit_scatter_ui);
220277
register_editor::<Radius>(registry, default_radius, edit_radius_ui);
278+
register_editor::<MarkerShape>(registry, default_marker_shape, edit_marker_shape_ui);
221279
}

crates/re_space_view_time_series/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod util;
1111
mod visualizer_system;
1212

1313
use re_log_types::EntityPath;
14+
use re_types::components::MarkerShape;
1415
use re_viewer_context::external::re_entity_db::TimeSeriesAggregator;
1516
pub use space_view_class::TimeSeriesSpaceView;
1617

@@ -36,6 +37,11 @@ pub struct PlotPointAttrs {
3637
pub kind: PlotSeriesKind,
3738
}
3839

40+
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
41+
pub struct ScatterAttrs {
42+
pub marker: MarkerShape,
43+
}
44+
3945
impl PartialEq for PlotPointAttrs {
4046
fn eq(&self, rhs: &Self) -> bool {
4147
let Self {
@@ -63,7 +69,7 @@ struct PlotPoint {
6369
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6470
pub enum PlotSeriesKind {
6571
Continuous,
66-
Scatter,
72+
Scatter(ScatterAttrs),
6773
Clear,
6874
}
6975

crates/re_space_view_time_series/src/point_visualizer_system.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use re_query_cache::{MaybeCachedComponentData, QueryError};
22
use re_types::archetypes;
3+
use re_types::components::MarkerShape;
34
use re_types::{
45
archetypes::SeriesPoint,
56
components::{Color, Radius, Scalar, Text},
@@ -14,6 +15,7 @@ use crate::overrides::initial_override_color;
1415
use crate::util::{
1516
determine_plot_bounds_and_time_per_pixel, determine_time_range, points_to_series,
1617
};
18+
use crate::ScatterAttrs;
1719
use crate::{overrides::lookup_override, PlotPoint, PlotPointAttrs, PlotSeries, PlotSeriesKind};
1820

1921
/// The system for rendering [`SeriesPoint`] archetypes.
@@ -29,6 +31,10 @@ impl IdentifiedViewSystem for SeriesPointSystem {
2931
}
3032
}
3133

34+
// We use a larger default radius for scatter plots so the marker is
35+
// visible.
36+
const DEFAULT_RADIUS: f32 = 3.0;
37+
3238
impl VisualizerSystem for SeriesPointSystem {
3339
fn visualizer_query_info(&self) -> VisualizerQueryInfo {
3440
let mut query_info = VisualizerQueryInfo::from_archetype::<archetypes::Scalar>();
@@ -37,6 +43,8 @@ impl VisualizerSystem for SeriesPointSystem {
3743
.map(ToOwned::to_owned)
3844
.collect::<ComponentNameSet>();
3945
query_info.queried.append(&mut series_point_queried);
46+
// TODO(jleibs): Use StrokeWidth instead
47+
query_info.queried.insert(Radius::name());
4048
query_info
4149
}
4250

@@ -76,6 +84,8 @@ impl VisualizerSystem for SeriesPointSystem {
7684
) -> Option<re_log_types::DataCell> {
7785
if *component == Color::name() {
7886
Some([initial_override_color(entity_path)].into())
87+
} else if *component == Radius::name() {
88+
Some([Radius(DEFAULT_RADIUS)].into())
7989
} else {
8090
None
8191
}
@@ -125,16 +135,18 @@ impl SeriesPointSystem {
125135

126136
let override_radius = lookup_override::<Radius>(data_result, ctx).map(|r| r.0);
127137

138+
let override_marker = lookup_override::<MarkerShape>(data_result, ctx);
139+
128140
let query = re_data_store::RangeQuery::new(query.timeline, time_range);
129141

130142
// TODO(jleibs): need to do a "joined" archetype query
131143
query_caches
132-
.query_archetype_pov1_comp2::<archetypes::Scalar, Scalar, Color, Text, _>(
144+
.query_archetype_pov1_comp3::<archetypes::Scalar, Scalar, Color, MarkerShape, Text, _>(
133145
ctx.app_options.experimental_primary_caching_range,
134146
store,
135147
&query.clone().into(),
136148
&data_result.entity_path,
137-
|((time, _row_id), _, scalars, colors, labels)| {
149+
|((time, _row_id), _, scalars, colors, markers, labels)| {
138150
let Some(time) = time else {
139151
return;
140152
}; // scalars cannot be timeless
@@ -154,12 +166,16 @@ impl SeriesPointSystem {
154166
return;
155167
}
156168

157-
for (scalar, color, label) in itertools::izip!(
169+
for (scalar, color, marker, label) in itertools::izip!(
158170
scalars.iter(),
159171
MaybeCachedComponentData::iter_or_repeat_opt(
160172
&colors,
161173
scalars.len()
162174
),
175+
MaybeCachedComponentData::iter_or_repeat_opt(
176+
&markers,
177+
scalars.len()
178+
),
163179
//MaybeCachedComponentData::iter_or_repeat_opt(&radii, scalars.len()),
164180
MaybeCachedComponentData::iter_or_repeat_opt(
165181
&labels,
@@ -178,7 +194,9 @@ impl SeriesPointSystem {
178194
let radius = override_radius
179195
.unwrap_or_else(|| radius.map_or(DEFAULT_RADIUS, |r| r.0));
180196

181-
const DEFAULT_RADIUS: f32 = 0.75;
197+
let marker = override_marker.unwrap_or(marker.unwrap_or_default());
198+
199+
182200

183201
points.push(PlotPoint {
184202
time: time.as_i64(),
@@ -187,7 +205,7 @@ impl SeriesPointSystem {
187205
label,
188206
color,
189207
radius,
190-
kind: PlotSeriesKind::Scatter,
208+
kind: PlotSeriesKind::Scatter(ScatterAttrs{marker}),
191209
},
192210
});
193211
}

crates/re_space_view_time_series/src/space_view_class.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -406,30 +406,31 @@ It can greatly improve performance (and readability) in such situations as it pr
406406
time_ctrl_write.pause();
407407
}
408408

409-
for line in all_plot_series {
410-
let points = line
409+
for series in all_plot_series {
410+
let points = series
411411
.points
412412
.iter()
413413
.map(|p| [(p.0 - time_offset) as _, p.1])
414414
.collect::<Vec<_>>();
415415

416-
let color = line.color;
417-
let id = egui::Id::new(line.entity_path.hash());
418-
plot_item_id_to_entity_path.insert(id, line.entity_path.clone());
416+
let color = series.color;
417+
let id = egui::Id::new(series.entity_path.hash());
418+
plot_item_id_to_entity_path.insert(id, series.entity_path.clone());
419419

420-
match line.kind {
420+
match series.kind {
421421
PlotSeriesKind::Continuous => plot_ui.line(
422422
Line::new(points)
423-
.name(&line.label)
423+
.name(&series.label)
424424
.color(color)
425-
.width(line.width)
425+
.width(series.width)
426426
.id(id),
427427
),
428-
PlotSeriesKind::Scatter => plot_ui.points(
428+
PlotSeriesKind::Scatter(scatter_attrs) => plot_ui.points(
429429
Points::new(points)
430-
.name(&line.label)
430+
.name(&series.label)
431431
.color(color)
432-
.radius(line.width)
432+
.radius(series.width)
433+
.shape(scatter_attrs.marker.into())
433434
.id(id),
434435
),
435436
// Break up the chart. At some point we might want something fancier.

crates/re_space_view_time_series/src/util.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use re_viewer_context::{external::re_entity_db::TimeSeriesAggregator, ViewQuery,
33

44
use crate::{
55
aggregation::{AverageAggregator, MinMaxAggregator},
6-
PlotPoint, PlotSeries, PlotSeriesKind,
6+
PlotPoint, PlotSeries, PlotSeriesKind, ScatterAttrs,
77
};
88

99
/// Find the plot bounds and the per-ui-point delta from egui.
@@ -101,7 +101,7 @@ pub fn points_to_series(
101101
// Can't draw a single point as a continuous line, so fall back on scatter
102102
let mut kind = points[0].attrs.kind;
103103
if kind == PlotSeriesKind::Continuous {
104-
kind = PlotSeriesKind::Scatter;
104+
kind = PlotSeriesKind::Scatter(ScatterAttrs::default());
105105
}
106106

107107
all_series.push(PlotSeries {

crates/re_space_view_time_series/src/visualizer_system.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use re_viewer_context::{
1212
use crate::{
1313
overrides::{initial_override_color, lookup_override},
1414
util::{determine_plot_bounds_and_time_per_pixel, determine_time_range, points_to_series},
15-
PlotPoint, PlotPointAttrs, PlotSeries, PlotSeriesKind,
15+
PlotPoint, PlotPointAttrs, PlotSeries, PlotSeriesKind, ScatterAttrs,
1616
};
1717

1818
/// The legacy system for rendering [`TimeSeriesScalar`] archetypes.
@@ -174,8 +174,8 @@ impl LegacyTimeSeriesSystem {
174174
let radius = override_radius
175175
.unwrap_or_else(|| radius.map_or(DEFAULT_RADIUS, |r| r.0));
176176

177-
let kind = if scattered {
178-
PlotSeriesKind::Scatter
177+
let kind= if scattered {
178+
PlotSeriesKind::Scatter(ScatterAttrs::default())
179179
} else {
180180
PlotSeriesKind::Continuous
181181
};

crates/re_types/definitions/rerun/archetypes/series_point.fbs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@ table SeriesPoint (
1818
/// Color for the corresponding series.
1919
// TODO(jleibs): This should be batch if we make a batch Scalars loggable.
2020
color: rerun.components.Color ("attr.rerun.component_optional", nullable, order: 1000);
21+
22+
/// What shape to use to represent the point
23+
marker: rerun.components.MarkerShape ("attr.rerun.component_optional", nullable, order: 2000);
2124
}

crates/re_types/definitions/rerun/components.fbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ include "./components/instance_key.fbs";
1212
include "./components/keypoint_id.fbs";
1313
include "./components/line_strip2d.fbs";
1414
include "./components/line_strip3d.fbs";
15+
include "./components/marker_shape.fbs";
1516
include "./components/material.fbs";
1617
include "./components/media_type.fbs";
1718
include "./components/mesh_properties.fbs";
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
include "arrow/attributes.fbs";
2+
include "python/attributes.fbs";
3+
include "rust/attributes.fbs";
4+
5+
include "rerun/datatypes.fbs";
6+
include "rerun/attributes.fbs";
7+
8+
namespace rerun.components;
9+
10+
// TODO(#3384)
11+
/*
12+
enum MarkerShape: byte {
13+
Circle = 1,
14+
Diamond = 2,
15+
Square = 3,
16+
Cross = 4,
17+
Plus = 5,
18+
Up = 6,
19+
Down = 7,
20+
Left = 8,
21+
Right = 9,
22+
Asterisk = 10,
23+
}
24+
*/
25+
26+
/// Shape of a marker.
27+
struct MarkerShape (
28+
"attr.docs.unreleased",
29+
"attr.rust.derive": "PartialEq, Eq, PartialOrd, Copy",
30+
"attr.rust.repr": "transparent"
31+
) {
32+
shape: ubyte (order: 100);
33+
}

0 commit comments

Comments
 (0)