Skip to content

Commit

Permalink
Break up plot charts when there's a Clear (#4957)
Browse files Browse the repository at this point in the history
  • Loading branch information
teh-cmc authored Jan 30, 2024
1 parent 2c60146 commit 5fd5ae9
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 32 deletions.
2 changes: 2 additions & 0 deletions crates/re_space_view_time_series/src/space_view_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ impl SpaceViewClass for TimeSeriesSpaceView {
.color(color)
.radius(line.width),
),
// Break up the chart. At some point we might want something fancier.
PlotSeriesKind::Clear => {}
}
}

Expand Down
58 changes: 36 additions & 22 deletions crates/re_space_view_time_series/src/visualizer_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct PlotPointAttrs {
pub label: Option<String>,
pub color: egui::Color32,
pub radius: f32,
pub scattered: bool,
pub kind: PlotSeriesKind,
}

impl PartialEq for PlotPointAttrs {
Expand All @@ -28,12 +28,12 @@ impl PartialEq for PlotPointAttrs {
label,
color,
radius,
scattered,
kind,
} = self;
label.eq(&rhs.label)
&& color.eq(&rhs.color)
&& radius.total_cmp(&rhs.radius).is_eq()
&& scattered.eq(&rhs.scattered)
&& kind.eq(&rhs.kind)
}
}

Expand All @@ -46,10 +46,11 @@ struct PlotPoint {
attrs: PlotPointAttrs,
}

#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PlotSeriesKind {
Continuous,
Scatter,
Clear,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -240,6 +241,21 @@ impl TimeSeriesSystem {
return;
}; // scalars cannot be timeless

// This is a clear: we want to split the chart.
if scalars.is_empty() {
points.push(PlotPoint {
time: time.as_i64(),
value: 0.0,
attrs: PlotPointAttrs {
label: None,
color: egui::Color32::BLACK,
radius: 0.0,
kind: PlotSeriesKind::Clear,
},
});
return;
}

for (scalar, scattered, color, radius, label) in itertools::izip!(
scalars.iter(),
MaybeCachedComponentData::iter_or_repeat_opt(&scatterings, scalars.len()),
Expand All @@ -258,6 +274,12 @@ impl TimeSeriesSystem {
let radius = override_radius
.unwrap_or_else(|| radius.map_or(DEFAULT_RADIUS, |r| r.0));

let kind = if scattered {
PlotSeriesKind::Scatter
} else {
PlotSeriesKind::Continuous
};

const DEFAULT_RADIUS: f32 = 0.75;

points.push(PlotPoint {
Expand All @@ -267,7 +289,7 @@ impl TimeSeriesSystem {
label,
color,
radius,
scattered,
kind,
},
});
}
Expand Down Expand Up @@ -385,12 +407,8 @@ impl TimeSeriesSystem {
label: line_label.to_owned(),
color: attrs.color,
width: 2.0 * attrs.radius,
kind: if attrs.scattered {
PlotSeriesKind::Scatter
} else {
PlotSeriesKind::Continuous
},
points: Vec::with_capacity(num_points),
kind: attrs.kind,
};

for (i, p) in points.into_iter().enumerate() {
Expand All @@ -402,31 +420,27 @@ impl TimeSeriesSystem {
// Attributes changed since last point, break up the current run into a
// line segment, and start the next one.

attrs = p.attrs.clone();
let kind = if attrs.scattered {
PlotSeriesKind::Scatter
} else {
PlotSeriesKind::Continuous
};

attrs = p.attrs;
let prev_line = std::mem::replace(
&mut line,
PlotSeries {
label: line_label.to_owned(),
color: attrs.color,
width: 2.0 * attrs.radius,
kind,
kind: attrs.kind,
points: Vec::with_capacity(num_points - i),
},
);

let cur_continuous = matches!(attrs.kind, PlotSeriesKind::Continuous);
let prev_continuous = matches!(prev_line.kind, PlotSeriesKind::Continuous);

let prev_point = *prev_line.points.last().unwrap();
self.lines.push(prev_line);

// If the previous point was continuous and the current point is continuous
// too, then we want the 2 segments to appear continuous even though they
// are actually split from a data standpoint.
let cur_continuous = matches!(kind, PlotSeriesKind::Continuous);
let prev_continuous = matches!(kind, PlotSeriesKind::Continuous);
if cur_continuous && prev_continuous {
line.points.push(prev_point);
}
Expand Down Expand Up @@ -656,13 +670,13 @@ fn are_aggregatable(point1: &PlotPoint, point2: &PlotPoint, window_size: usize)
label,
color,
radius: _,
scattered,
kind,
} = attrs;

// We cannot aggregate two points that don't live in the same aggregation window to start with.
// This is very common with e.g. sparse datasets.
time.abs_diff(point2.time) <= window_size as u64
&& *label == point2.attrs.label
&& *color == point2.attrs.color
&& *scattered == point2.attrs.scattered
&& *kind == point2.attrs.kind
}
6 changes: 4 additions & 2 deletions crates/re_types/definitions/rerun/archetypes/clear.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ namespace rerun.archetypes;
/// Any logged components after the clear are unaffected by the clear.
///
/// This implies that a range query that includes time points that are before the clear,
/// still returns all components at the given path(s), except those logged directly before the clear.
/// Meaning that in practice clears are ineffective for time series plots and other usages of visible time ranges.
/// still returns all components at the given path(s).
/// Meaning that in practice clears are ineffective when making use of visible time ranges.
/// Scalar plots are an exception: they track clears and use them to represent holes in the
/// data (i.e. discontinuous lines).
///
/// \example clear_simple title="Flat" image="https://static.rerun.io/clear_simple/2f5df95fcc53e9f0552f65670aef7f94830c5c1a/1200w.png"
/// \example clear_recursive !api "Recursive"
Expand Down
6 changes: 4 additions & 2 deletions crates/re_types_core/src/archetypes/clear.rs

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

6 changes: 4 additions & 2 deletions docs/content/reference/types/archetypes/clear.md

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

6 changes: 4 additions & 2 deletions rerun_cpp/src/rerun/archetypes/clear.hpp

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

6 changes: 4 additions & 2 deletions rerun_py/rerun_sdk/rerun/archetypes/clear.py

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

0 comments on commit 5fd5ae9

Please sign in to comment.