Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 20e3011

Browse files
teh-cmcgrtlr
authored andcommitted
Always bootstrap secondaries in plot view (#10713)
The plot view visualizes scalar data within a specific time range, without any kind of time-alignment / bootstrapping behavior: * For the scalar themselves, this is what you want: if you're trying to plot some data between t=100 and t=200, you don't want to display a point from t=20 (and _extended bounds_ will take care of lines crossing the limit). * For the secondary components (colors, radii, names, etc), this is a problem though: you don't want your plot to change color depending on what the currently visible time range is! Secondary components have to be bootstrapped. Before: https://github.com/user-attachments/assets/4ad5e448-e6a9-46ed-bb02-7c88cb8cd3d1 After: https://github.com/user-attachments/assets/03030ff5-d836-4e9f-9e70-2871c7afbdcc --------- Co-authored-by: Jochen Görtler <[email protected]>
1 parent 0f819c8 commit 20e3011

10 files changed

+227
-20
lines changed

.typos.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,6 @@ extend-ignore-re = [
167167
"_LEVL_", # USed in air_traffic_data.py
168168

169169
"thrEEone", # Used in a test
170+
171+
"muh_scalars", # Weird name introduced in `crates/viewer/re_view_time_series/tests/basic.rs` introduced in https://github.com/rerun-io/rerun/pull/10713
170172
]

crates/viewer/re_view_time_series/src/line_visualizer_system.rs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
use itertools::Itertools as _;
22

3-
use re_chunk_store::{RangeQuery, RowId};
3+
use re_chunk_store::{LatestAtQuery, RangeQuery, RowId};
44
use re_log_types::{EntityPath, TimeInt};
55
use re_types::{
66
Archetype as _,
77
archetypes::{self},
88
components::{AggregationPolicy, Color, Name, SeriesVisible, StrokeWidth},
99
};
10-
use re_view::{RangeResultsExt as _, range_with_blueprint_resolved_data};
10+
use re_view::{
11+
RangeResultsExt as _, latest_at_with_blueprint_resolved_data,
12+
range_with_blueprint_resolved_data,
13+
};
1114
use re_viewer_context::external::re_entity_db::InstancePath;
1215
use re_viewer_context::{
1316
IdentifiedViewSystem, QueryContext, TypedComponentFallbackProvider, ViewContext, ViewQuery,
@@ -228,16 +231,37 @@ impl SeriesLinesSystem {
228231
allocate_plot_points(&query, &default_point, &all_scalar_chunks, num_series);
229232

230233
collect_scalars(&all_scalar_chunks, &mut points_per_series);
234+
235+
// The plot view visualizes scalar data within a specific time range, without any kind
236+
// of time-alignment / bootstrapping behavior:
237+
// * For the scalar themselves, this is what you want: if you're trying to plot some
238+
// data between t=100 and t=200, you don't want to display a point from t=20 (and
239+
// _extended bounds_ will take care of lines crossing the limit).
240+
// * For the secondary components (colors, radii, names, etc), this is a problem
241+
// though: you don't want your plot to change color depending on what the currently
242+
// visible time range is! Secondary components have to be bootstrapped.
243+
let query_shadowed_components = false;
244+
let bootstrapped_results = latest_at_with_blueprint_resolved_data(
245+
ctx,
246+
None,
247+
&LatestAtQuery::new(query.timeline, query.range.min()),
248+
data_result,
249+
archetypes::SeriesLines::all_components().iter(),
250+
query_shadowed_components,
251+
);
252+
231253
collect_colors(
232254
entity_path,
233255
&query,
256+
&bootstrapped_results,
234257
&results,
235258
&all_scalar_chunks,
236259
&mut points_per_series,
237260
&archetypes::SeriesLines::descriptor_colors(),
238261
);
239262
collect_radius_ui(
240263
&query,
264+
&bootstrapped_results,
241265
&results,
242266
&all_scalar_chunks,
243267
&mut points_per_series,
@@ -247,9 +271,14 @@ impl SeriesLinesSystem {
247271

248272
// Now convert the `PlotPoints` into `Vec<PlotSeries>`
249273
let aggregation_policy_descr = archetypes::SeriesLines::descriptor_aggregation_policy();
250-
let aggregator = results
274+
let aggregator = bootstrapped_results
251275
.get_optional_chunks(aggregation_policy_descr.clone())
252276
.iter()
277+
.chain(
278+
results
279+
.get_optional_chunks(aggregation_policy_descr.clone())
280+
.iter(),
281+
)
253282
.find(|chunk| !chunk.is_empty())
254283
.and_then(|chunk| {
255284
chunk
@@ -311,13 +340,15 @@ impl SeriesLinesSystem {
311340

312341
let series_visibility = collect_series_visibility(
313342
&query,
343+
&bootstrapped_results,
314344
&results,
315345
num_series,
316346
archetypes::SeriesLines::descriptor_visible_series(),
317347
);
318348
let series_names = collect_series_name(
319349
self,
320350
&query_ctx,
351+
&bootstrapped_results,
321352
&results,
322353
num_series,
323354
&archetypes::SeriesLines::descriptor_names(),
@@ -368,6 +399,26 @@ fn collect_recursive_clears(
368399
let mut clear_entity_path = entity_path.clone();
369400
let clear_descriptor = archetypes::Clear::descriptor_is_recursive();
370401

402+
// Bootstrap in case there's a pending clear out of the visible time range.
403+
{
404+
let results = ctx.recording_engine().cache().latest_at(
405+
&LatestAtQuery::new(query.timeline, query.range.min()),
406+
&clear_entity_path,
407+
[&clear_descriptor],
408+
);
409+
410+
cleared_indices.extend(
411+
results
412+
.iter_as(*query.timeline(), clear_descriptor.clone())
413+
.slice::<bool>()
414+
.filter_map(|(index, is_recursive_buffer)| {
415+
let is_recursive =
416+
!is_recursive_buffer.is_empty() && is_recursive_buffer.value(0);
417+
(is_recursive || clear_entity_path == *entity_path).then_some(index)
418+
}),
419+
);
420+
}
421+
371422
loop {
372423
let results =
373424
ctx.recording_engine()

crates/viewer/re_view_time_series/src/point_visualizer_system.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use itertools::Itertools as _;
22

3+
use re_chunk_store::LatestAtQuery;
34
use re_types::{
45
Archetype as _, archetypes,
56
components::{Color, MarkerShape, MarkerSize, Name, SeriesVisible},
67
};
7-
use re_view::{clamped_or_nothing, range_with_blueprint_resolved_data};
8+
use re_view::{
9+
clamped_or_nothing, latest_at_with_blueprint_resolved_data, range_with_blueprint_resolved_data,
10+
};
811
use re_viewer_context::{
912
IdentifiedViewSystem, QueryContext, TypedComponentFallbackProvider, ViewContext, ViewQuery,
1013
ViewStateExt as _, ViewSystemExecutionError, VisualizerQueryInfo, VisualizerSystem,
@@ -233,16 +236,37 @@ impl SeriesPointsSystem {
233236
allocate_plot_points(&query, &default_point, &all_scalar_chunks, num_series);
234237

235238
collect_scalars(&all_scalar_chunks, &mut points_per_series);
239+
240+
// The plot view visualizes scalar data within a specific time range, without any kind
241+
// of time-alignment / bootstrapping behavior:
242+
// * For the scalar themselves, this is what you want: if you're trying to plot some
243+
// data between t=100 and t=200, you don't want to display a point from t=20 (and
244+
// _extended bounds_ will take care of lines crossing the limit).
245+
// * For the secondary components (colors, radii, names, etc), this is a problem
246+
// though: you don't want your plot to change color depending on what the currently
247+
// visible time range is! Secondary components have to be bootstrapped.
248+
let query_shadowed_components = false;
249+
let bootstrapped_results = latest_at_with_blueprint_resolved_data(
250+
ctx,
251+
None,
252+
&LatestAtQuery::new(query.timeline, query.range.min()),
253+
data_result,
254+
archetypes::SeriesPoints::all_components().iter(),
255+
query_shadowed_components,
256+
);
257+
236258
collect_colors(
237259
entity_path,
238260
&query,
261+
&bootstrapped_results,
239262
&results,
240263
&all_scalar_chunks,
241264
&mut points_per_series,
242265
&archetypes::SeriesPoints::descriptor_colors(),
243266
);
244267
collect_radius_ui(
245268
&query,
269+
&bootstrapped_results,
246270
&results,
247271
&all_scalar_chunks,
248272
&mut points_per_series,
@@ -256,8 +280,17 @@ impl SeriesPointsSystem {
256280
re_tracing::profile_scope!("fill marker shapes");
257281

258282
{
259-
let all_marker_shapes_chunks =
260-
results.get_optional_chunks(archetypes::SeriesPoints::descriptor_markers());
283+
let all_marker_shapes_chunks = bootstrapped_results
284+
.get_optional_chunks(archetypes::SeriesPoints::descriptor_markers())
285+
.iter()
286+
.cloned()
287+
.chain(
288+
results
289+
.get_optional_chunks(archetypes::SeriesPoints::descriptor_markers())
290+
.iter()
291+
.cloned(),
292+
)
293+
.collect_vec();
261294

262295
if all_marker_shapes_chunks.len() == 1
263296
&& all_marker_shapes_chunks[0].is_static()
@@ -344,13 +377,15 @@ impl SeriesPointsSystem {
344377

345378
let series_visibility = collect_series_visibility(
346379
&query,
380+
&bootstrapped_results,
347381
&results,
348382
num_series,
349383
archetypes::SeriesPoints::descriptor_visible_series(),
350384
);
351385
let series_names = collect_series_name(
352386
self,
353387
&query_ctx,
388+
&bootstrapped_results,
354389
&results,
355390
num_series,
356391
&archetypes::SeriesPoints::descriptor_names(),

crates/viewer/re_view_time_series/src/series_query.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,19 @@ pub fn determine_num_series(all_scalar_chunks: &ChunksWithDescriptor<'_>) -> usi
3131
/// Queries the visibility flags for all series in a query.
3232
pub fn collect_series_visibility(
3333
query: &RangeQuery,
34+
bootstrapped_results: &re_view::HybridLatestAtResults<'_>,
3435
results: &HybridRangeResults<'_>,
3536
num_series: usize,
3637
visibility_descriptor: ComponentDescriptor,
3738
) -> Vec<bool> {
38-
results
39-
.iter_as(*query.timeline(), visibility_descriptor)
39+
bootstrapped_results
40+
.iter_as(*query.timeline(), visibility_descriptor.clone())
4041
.slice::<bool>()
42+
.chain(
43+
results
44+
.iter_as(*query.timeline(), visibility_descriptor)
45+
.slice::<bool>(),
46+
)
4147
.next()
4248
.map_or_else(
4349
|| vec![true; num_series], // By default all series are visible.
@@ -118,6 +124,7 @@ pub fn collect_scalars(
118124
pub fn collect_colors(
119125
entity_path: &EntityPath,
120126
query: &RangeQuery,
127+
bootstrapped_results: &re_view::HybridLatestAtResults<'_>,
121128
results: &re_view::HybridRangeResults<'_>,
122129
all_scalar_chunks: &ChunksWithDescriptor<'_>,
123130
points_per_series: &mut smallvec::SmallVec<[Vec<PlotPoint>; 1]>,
@@ -134,7 +141,19 @@ pub fn collect_colors(
134141
#[expect(clippy::disallowed_methods)] // This is not a hard-coded color.
135142
re_renderer::Color32::from_rgba_unmultiplied(r, g, b, a)
136143
}
137-
let all_color_chunks = results.get_optional_chunks(color_descriptor.clone());
144+
145+
let all_color_chunks = bootstrapped_results
146+
.get_optional_chunks(color_descriptor.clone())
147+
.iter()
148+
.cloned()
149+
.chain(
150+
results
151+
.get_optional_chunks(color_descriptor.clone())
152+
.iter()
153+
.cloned(),
154+
)
155+
.collect_vec();
156+
138157
if all_color_chunks.len() == 1 && all_color_chunks[0].is_static() {
139158
re_tracing::profile_scope!("override/default fast path");
140159

@@ -212,15 +231,17 @@ pub fn collect_colors(
212231
pub fn collect_series_name(
213232
fallback_provider: &dyn TypedComponentFallbackProvider<components::Name>,
214233
query_ctx: &QueryContext<'_>,
234+
bootstrapped_results: &re_view::HybridLatestAtResults<'_>,
215235
results: &re_view::HybridRangeResults<'_>,
216236
num_series: usize,
217237
name_descriptor: &ComponentDescriptor,
218238
) -> Vec<String> {
219239
re_tracing::profile_function!();
220240

221-
let mut series_names: Vec<String> = results
241+
let mut series_names: Vec<String> = bootstrapped_results
222242
.get_optional_chunks(name_descriptor.clone())
223243
.iter()
244+
.chain(results.get_optional_chunks(name_descriptor.clone()).iter())
224245
.find(|chunk| !chunk.is_empty())
225246
.and_then(|chunk| chunk.iter_slices::<String>(name_descriptor.clone()).next())
226247
.map(|slice| slice.into_iter().map(|s| s.to_string()).collect())
@@ -243,6 +264,7 @@ pub fn collect_series_name(
243264
/// Collects `radius_ui` for the series into pre-allocated plot points.
244265
pub fn collect_radius_ui(
245266
query: &RangeQuery,
267+
bootstrapped_results: &re_view::HybridLatestAtResults<'_>,
246268
results: &re_view::HybridRangeResults<'_>,
247269
all_scalar_chunks: &ChunksWithDescriptor<'_>,
248270
points_per_series: &mut smallvec::SmallVec<[Vec<PlotPoint>; 1]>,
@@ -254,7 +276,17 @@ pub fn collect_radius_ui(
254276
let num_series = points_per_series.len();
255277

256278
{
257-
let all_radius_chunks = results.get_optional_chunks(radius_descriptor.clone());
279+
let all_radius_chunks = bootstrapped_results
280+
.get_optional_chunks(radius_descriptor.clone())
281+
.iter()
282+
.cloned()
283+
.chain(
284+
results
285+
.get_optional_chunks(radius_descriptor.clone())
286+
.iter()
287+
.cloned(),
288+
)
289+
.collect_vec();
258290

259291
if all_radius_chunks.len() == 1 && all_radius_chunks[0].is_static() {
260292
re_tracing::profile_scope!("override/default fast path");

0 commit comments

Comments
 (0)