-
Notifications
You must be signed in to change notification settings - Fork 590
Allow specifying widths of BarChart bars
#12090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 11 commits
bda1cdf
3eb375e
c891ad5
22e52d2
feac47d
b31bc6f
9790999
08e9d90
7185306
9016abd
29a152e
85dba4c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -225,6 +225,7 @@ impl ViewClass for BarChartView { | |
| fn create_bar_chart<N: Into<f64>>( | ||
| ent_path: &EntityPath, | ||
| indexes: impl Iterator<Item = f64>, | ||
| widths: impl Iterator<Item = f32>, | ||
| values: impl Iterator<Item = N>, | ||
| color: &re_types::components::Color, | ||
| background_color: egui::Color32, | ||
|
|
@@ -240,10 +241,11 @@ impl ViewClass for BarChartView { | |
| "bar_chart", | ||
| values | ||
| .zip(indexes) | ||
| .zip(widths) | ||
| .enumerate() | ||
| .map(|(i, (value, index))| { | ||
| Bar::new(index + 0.5, value.into()) | ||
| .width(1.0) // No gaps | ||
| .map(|(i, ((value, index), width))| { | ||
| Bar::new(index + (0.5 * width as f64), value.into()) | ||
| .width(width as f64) | ||
| .name(format!("{ent_path} #{i}")) | ||
| .fill(fill) | ||
| .stroke((1.0, stroke_color)) | ||
|
|
@@ -260,6 +262,7 @@ impl ViewClass for BarChartView { | |
| abscissa, | ||
| values: tensor, | ||
| color, | ||
| widths, | ||
| }, | ||
| ) in charts | ||
| { | ||
|
|
@@ -277,85 +280,27 @@ impl ViewClass for BarChartView { | |
| TensorBuffer::F64(data) => data.iter().copied().collect(), | ||
| }; | ||
|
|
||
| let chart = match &tensor.buffer { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was thinking this should be a pretty uncontroversial refactor. Can split into another PR if necessary however.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to, appreciated! :) |
||
| TensorBuffer::U8(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied(), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::U16(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied(), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::U32(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied(), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::U64(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied().map(|v| v as f64), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::I8(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied(), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::I16(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied(), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::I32(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied(), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::I64(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied().map(|v| v as f64), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::F16(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().map(|f| f.to_f32()), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::F32(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied(), | ||
| color, | ||
| background_color, | ||
| ), | ||
| TensorBuffer::F64(data) => create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| data.iter().copied(), | ||
| color, | ||
| background_color, | ||
| ), | ||
| let data: ::arrow::buffer::ScalarBuffer<f64> = match &tensor.buffer { | ||
| TensorBuffer::U8(data) => data.iter().map(|v| *v as f64).collect(), | ||
| TensorBuffer::U16(data) => data.iter().map(|v| *v as f64).collect(), | ||
| TensorBuffer::U32(data) => data.iter().map(|v| *v as f64).collect(), | ||
| TensorBuffer::U64(data) => data.iter().map(|v| *v as f64).collect(), | ||
| TensorBuffer::I8(data) => data.iter().map(|v| *v as f64).collect(), | ||
| TensorBuffer::I16(data) => data.iter().map(|v| *v as f64).collect(), | ||
| TensorBuffer::I32(data) => data.iter().map(|v| *v as f64).collect(), | ||
| TensorBuffer::I64(data) => data.iter().map(|v| *v as f64).collect(), | ||
| TensorBuffer::F16(data) => data.iter().map(|v| f64::from(*v)).collect(), | ||
| TensorBuffer::F32(data) => data.iter().map(|v| *v as f64).collect(), | ||
| TensorBuffer::F64(data) => data.iter().copied().collect(), | ||
| }; | ||
| let chart = create_bar_chart( | ||
| ent_path, | ||
| arg.iter().copied(), | ||
| widths.iter().copied(), | ||
| data.iter().copied(), | ||
| color, | ||
| background_color, | ||
| ); | ||
|
|
||
| let id = egui::Id::new(ent_path.hash()); | ||
| plot_item_id_to_entity_path.insert(id, ent_path.clone()); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,18 +4,19 @@ use re_chunk_store::LatestAtQuery; | |
| use re_entity_db::EntityPath; | ||
| use re_types::{ | ||
| archetypes::BarChart, | ||
| components::{self}, | ||
| components::{self, Length}, | ||
| datatypes, | ||
| }; | ||
| use re_view::DataResultQuery as _; | ||
| use re_view::{DataResultQuery as _, RangeResultsExt as _}; | ||
| use re_viewer_context::{ | ||
| IdentifiedViewSystem, ViewContext, ViewContextCollection, ViewQuery, ViewSystemExecutionError, | ||
| VisualizerQueryInfo, VisualizerSystem, | ||
| VisualizerQueryInfo, VisualizerSystem, typed_fallback_for, | ||
| }; | ||
|
|
||
| #[derive(Default)] | ||
| pub struct BarChartData { | ||
| pub abscissa: datatypes::TensorData, | ||
| pub widths: Vec<f32>, | ||
| pub values: datatypes::TensorData, | ||
| pub color: components::Color, | ||
| } | ||
|
|
@@ -56,15 +57,36 @@ impl VisualizerSystem for BarChartVisualizerSystem { | |
| }; | ||
|
|
||
| if tensor.is_vector() { | ||
| let length: u64 = tensor.shape().iter().product(); | ||
|
|
||
| let abscissa: components::TensorData = | ||
| results.get_mono_with_fallback(BarChart::descriptor_abscissa().component); | ||
| let color = results.get_mono_with_fallback(BarChart::descriptor_color().component); | ||
| let widths = if let Some(chunked_widths) = | ||
| results.get_required_chunks(BarChart::descriptor_widths().component) | ||
| { | ||
| let mut widths = Vec::with_capacity(length as usize); | ||
| for slice in chunked_widths | ||
| .iter() | ||
| .flat_map(|chunk| chunk.iter_slices::<f32>()) | ||
| { | ||
| widths.extend_from_slice(slice); | ||
| } | ||
| widths | ||
| } else { | ||
| let fallback_width: Length = typed_fallback_for( | ||
| &ctx.query_context(data_result, &view_query.latest_at_query()), | ||
| BarChart::descriptor_widths().component, | ||
| ); | ||
| vec![fallback_width.0.into(); length as usize] | ||
| }; | ||
|
Comment on lines
+65
to
+82
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This goes into the right direction, but something we usually do is to repeat the last element often enough to ensure that we have enough of them. This has two advantages:
Unfortunately that's not handled by any query later and is done in-place most of everywhere. My suggestion would be to use the fact that we always have a let widths: &[f32] = results
.iter_as(view_query.timeline, BarChart::descriptor_widths().component)
.slice::<f32>()
.next() // It's a latest-at result, therefore this is a single row, therefore we there can't be more than 1 chunk involved.
.map_or(&[], |((_time, _row), slice)| slice);
let fallback_width = typed_fallback_for::<Length>(
&ctx.query_context(data_result, &view_query.latest_at_query()),
BarChart::descriptor_widths().component,
)
.0;
let widths = clamped_vec_or(fallback_width, length as usize, fallback_width);(there's some other subtle differences in how this plays out, but it has mostly the same effect.
I realize that some of this stuff goes pretty deep into Rerun data model arcana and our query utilities are really poorly documented and a bit all over the place. Plus since the tensor view has a lot of "special" queries it has no good examples yet (unlike most views it only deals with latest-at and it has to deal with the relatively strange
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will take a look after work. Yea, coming from bevy the data model is not quite as intuitive to an outsider 😄 But thank you for the quick reviews, I think I have some better understanding now. |
||
| self.charts.insert( | ||
| data_result.entity_path.clone(), | ||
| BarChartData { | ||
| abscissa: abscissa.0.clone(), | ||
| values: tensor.0.clone(), | ||
| color, | ||
| widths, | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.