diff --git a/crates/re_space_view_bar_chart/src/space_view_class.rs b/crates/re_space_view_bar_chart/src/space_view_class.rs index 8230cef4b660d..06a0acdb60ebd 100644 --- a/crates/re_space_view_bar_chart/src/space_view_class.rs +++ b/crates/re_space_view_bar_chart/src/space_view_class.rs @@ -164,8 +164,10 @@ impl SpaceViewClass for BarChartSpaceView { fn create_bar_chart>( ent_path: &EntityPath, values: impl Iterator, + color: &Option, ) -> BarChart { - let color = auto_color(hash(ent_path) as _); + let color = + color.map_or_else(|| auto_color(hash(ent_path) as _), |color| color.into()); let fill = color.gamma_multiply(0.75).additive(); // make sure overlapping bars are obvious BarChart::new( values @@ -183,25 +185,45 @@ impl SpaceViewClass for BarChartSpaceView { .color(color) } - for (ent_path, tensor) in charts { + for (ent_path, (tensor, color)) in charts { let chart = match &tensor.buffer { - TensorBuffer::U8(data) => create_bar_chart(ent_path, data.iter().copied()), - TensorBuffer::U16(data) => create_bar_chart(ent_path, data.iter().copied()), - TensorBuffer::U32(data) => create_bar_chart(ent_path, data.iter().copied()), - TensorBuffer::U64(data) => { - create_bar_chart(ent_path, data.iter().copied().map(|v| v as f64)) + TensorBuffer::U8(data) => { + create_bar_chart(ent_path, data.iter().copied(), color) } - TensorBuffer::I8(data) => create_bar_chart(ent_path, data.iter().copied()), - TensorBuffer::I16(data) => create_bar_chart(ent_path, data.iter().copied()), - TensorBuffer::I32(data) => create_bar_chart(ent_path, data.iter().copied()), - TensorBuffer::I64(data) => { - create_bar_chart(ent_path, data.iter().copied().map(|v| v as f64)) + TensorBuffer::U16(data) => { + create_bar_chart(ent_path, data.iter().copied(), color) } + TensorBuffer::U32(data) => { + create_bar_chart(ent_path, data.iter().copied(), color) + } + TensorBuffer::U64(data) => create_bar_chart( + ent_path, + data.iter().copied().map(|v| v as f64), + color, + ), + TensorBuffer::I8(data) => { + create_bar_chart(ent_path, data.iter().copied(), color) + } + TensorBuffer::I16(data) => { + create_bar_chart(ent_path, data.iter().copied(), color) + } + TensorBuffer::I32(data) => { + create_bar_chart(ent_path, data.iter().copied(), color) + } + TensorBuffer::I64(data) => create_bar_chart( + ent_path, + data.iter().copied().map(|v| v as f64), + color, + ), TensorBuffer::F16(data) => { - create_bar_chart(ent_path, data.iter().map(|f| f.to_f32())) + create_bar_chart(ent_path, data.iter().map(|f| f.to_f32()), color) + } + TensorBuffer::F32(data) => { + create_bar_chart(ent_path, data.iter().copied(), color) + } + TensorBuffer::F64(data) => { + create_bar_chart(ent_path, data.iter().copied(), color) } - TensorBuffer::F32(data) => create_bar_chart(ent_path, data.iter().copied()), - TensorBuffer::F64(data) => create_bar_chart(ent_path, data.iter().copied()), TensorBuffer::Jpeg(_) => { re_log::warn_once!( "trying to display JPEG data as a bar chart ({:?})", diff --git a/crates/re_space_view_bar_chart/src/view_part_system.rs b/crates/re_space_view_bar_chart/src/view_part_system.rs index ccd26abb5662d..a46a061f4c1ad 100644 --- a/crates/re_space_view_bar_chart/src/view_part_system.rs +++ b/crates/re_space_view_bar_chart/src/view_part_system.rs @@ -4,6 +4,7 @@ use re_arrow_store::LatestAtQuery; use re_data_store::EntityPath; use re_types::{ archetypes::{BarChart, Tensor}, + components::Color, datatypes::TensorData, Archetype, ComponentNameSet, }; @@ -15,7 +16,7 @@ use re_viewer_context::{ /// A bar chart system, with everything needed to render it. #[derive(Default)] pub struct BarChartViewPartSystem { - pub charts: BTreeMap, + pub charts: BTreeMap)>, } impl NamedViewSystem for BarChartViewPartSystem { @@ -82,10 +83,17 @@ impl ViewPartSystem for BarChartViewPartSystem { &query, ); + let color = store.query_latest_component::( + &data_result.entity_path, + &query, + ); + if let Some(tensor) = tensor { if tensor.is_vector() { - self.charts - .insert(data_result.entity_path.clone(), tensor.value.0.clone()); + self.charts.insert( + data_result.entity_path.clone(), + (tensor.value.0.clone(), color.map(|c| c.value)), + ); // shallow clones } } diff --git a/crates/re_types/definitions/rerun/archetypes/bar_chart.fbs b/crates/re_types/definitions/rerun/archetypes/bar_chart.fbs index 40a71fd773f16..bcb3c6d093d75 100644 --- a/crates/re_types/definitions/rerun/archetypes/bar_chart.fbs +++ b/crates/re_types/definitions/rerun/archetypes/bar_chart.fbs @@ -18,4 +18,9 @@ table BarChart ( /// The values. Should always be a rank-1 tensor. values: rerun.components.TensorData ("attr.rerun.component_required", required, order: 1000); + + // --- Optional --- + + /// The color of the bar chart + color: rerun.components.Color ("attr.rerun.component_optional", nullable, order: 2000); } diff --git a/crates/re_types/src/archetypes/bar_chart.rs b/crates/re_types/src/archetypes/bar_chart.rs index 59a197f81edd3..5064d3a90ba50 100644 --- a/crates/re_types/src/archetypes/bar_chart.rs +++ b/crates/re_types/src/archetypes/bar_chart.rs @@ -53,6 +53,9 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; pub struct BarChart { /// The values. Should always be a rank-1 tensor. pub values: crate::components::TensorData, + + /// The color of the bar chart + pub color: Option, } static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = @@ -61,20 +64,26 @@ static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = once_cell::sync::Lazy::new(|| ["rerun.components.BarChartIndicator".into()]); -static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = - once_cell::sync::Lazy::new(|| ["rerun.components.InstanceKey".into()]); +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 2usize]> = + once_cell::sync::Lazy::new(|| { + [ + "rerun.components.Color".into(), + "rerun.components.InstanceKey".into(), + ] + }); -static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 3usize]> = +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = once_cell::sync::Lazy::new(|| { [ "rerun.components.TensorData".into(), "rerun.components.BarChartIndicator".into(), + "rerun.components.Color".into(), "rerun.components.InstanceKey".into(), ] }); impl BarChart { - pub const NUM_COMPONENTS: usize = 3usize; + pub const NUM_COMPONENTS: usize = 4usize; } /// Indicator component for the [`BarChart`] [`::re_types_core::Archetype`] @@ -137,7 +146,20 @@ impl ::re_types_core::Archetype for BarChart { .ok_or_else(DeserializationError::missing_data) .with_context("rerun.archetypes.BarChart#values")? }; - Ok(Self { values }) + let color = if let Some(array) = arrays_by_name.get("rerun.components.Color") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.BarChart#color")? + .into_iter() + .next() + .flatten() + .ok_or_else(DeserializationError::missing_data) + .with_context("rerun.archetypes.BarChart#color")? + }) + } else { + None + }; + Ok(Self { values, color }) } } @@ -148,6 +170,9 @@ impl ::re_types_core::AsComponents for BarChart { [ Some(Self::indicator()), Some((&self.values as &dyn ComponentBatch).into()), + self.color + .as_ref() + .map(|comp| (comp as &dyn ComponentBatch).into()), ] .into_iter() .flatten() @@ -164,6 +189,12 @@ impl BarChart { pub fn new(values: impl Into) -> Self { Self { values: values.into(), + color: None, } } + + pub fn with_color(mut self, color: impl Into) -> Self { + self.color = Some(color.into()); + self + } } diff --git a/docs/content/reference/types/archetypes/bar_chart.md b/docs/content/reference/types/archetypes/bar_chart.md index b2968c6eb0bd2..fa8c13e471491 100644 --- a/docs/content/reference/types/archetypes/bar_chart.md +++ b/docs/content/reference/types/archetypes/bar_chart.md @@ -10,6 +10,8 @@ The x values will be the indices of the array, and the bar heights will be the p **Required**: [`TensorData`](../components/tensor_data.md) +**Optional**: [`Color`](../components/color.md) + ## Links * 🌊 [C++ API docs for `BarChart`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1archetypes_1_1BarChart.html) * 🐍 [Python API docs for `BarChart`](https://ref.rerun.io/docs/python/stable/common/archetypes#rerun.archetypes.BarChart) diff --git a/docs/content/reference/types/components/color.md b/docs/content/reference/types/components/color.md index 1c87b314c3eb7..38ed184b81c9f 100644 --- a/docs/content/reference/types/components/color.md +++ b/docs/content/reference/types/components/color.md @@ -20,6 +20,7 @@ byte is `R` and the least significant byte is `A`. ## Used by * [`Arrows3D`](../archetypes/arrows3d.md) +* [`BarChart`](../archetypes/bar_chart.md) * [`Boxes2D`](../archetypes/boxes2d.md) * [`Boxes3D`](../archetypes/boxes3d.md) * [`LineStrips2D`](../archetypes/line_strips2d.md) diff --git a/rerun_cpp/src/rerun/archetypes/bar_chart.cpp b/rerun_cpp/src/rerun/archetypes/bar_chart.cpp index d33e8998af19b..ab523309b91da 100644 --- a/rerun_cpp/src/rerun/archetypes/bar_chart.cpp +++ b/rerun_cpp/src/rerun/archetypes/bar_chart.cpp @@ -14,13 +14,18 @@ namespace rerun { ) { using namespace archetypes; std::vector cells; - cells.reserve(2); + cells.reserve(3); { auto result = DataCell::from_loggable(archetype.values); RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } + if (archetype.color.has_value()) { + auto result = DataCell::from_loggable(archetype.color.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } { auto indicator = BarChart::IndicatorComponent(); auto result = DataCell::from_loggable(indicator); diff --git a/rerun_cpp/src/rerun/archetypes/bar_chart.hpp b/rerun_cpp/src/rerun/archetypes/bar_chart.hpp index b1f23431f5a04..7be245772266a 100644 --- a/rerun_cpp/src/rerun/archetypes/bar_chart.hpp +++ b/rerun_cpp/src/rerun/archetypes/bar_chart.hpp @@ -4,12 +4,15 @@ #pragma once #include "../collection.hpp" +#include "../compiler_utils.hpp" +#include "../components/color.hpp" #include "../components/tensor_data.hpp" #include "../data_cell.hpp" #include "../indicator_component.hpp" #include "../result.hpp" #include +#include #include #include @@ -37,6 +40,9 @@ namespace rerun::archetypes { /// The values. Should always be a rank-1 tensor. rerun::components::TensorData values; + /// The color of the bar chart + std::optional color; + public: static constexpr const char IndicatorComponentName[] = "rerun.components.BarChartIndicator"; @@ -162,6 +168,13 @@ namespace rerun::archetypes { explicit BarChart(rerun::components::TensorData _values) : values(std::move(_values)) {} + /// The color of the bar chart + BarChart with_color(rerun::components::Color _color) && { + color = std::move(_color); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + /// Returns the number of primary instances of this archetype. size_t num_instances() const { return 1; diff --git a/rerun_py/rerun_sdk/rerun/archetypes/bar_chart.py b/rerun_py/rerun_sdk/rerun/archetypes/bar_chart.py index dd42b715c5d61..9b235935891dd 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/bar_chart.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/bar_chart.py @@ -44,7 +44,7 @@ class BarChart(BarChartExt, Archetype): """ - def __init__(self: Any, values: datatypes.TensorDataLike): + def __init__(self: Any, values: datatypes.TensorDataLike, *, color: datatypes.Rgba32Like | None = None): """ Create a new instance of the BarChart archetype. @@ -52,11 +52,13 @@ def __init__(self: Any, values: datatypes.TensorDataLike): ---------- values: The values. Should always be a rank-1 tensor. + color: + The color of the bar chart """ # You can define your own __init__ function as a member of BarChartExt in bar_chart_ext.py with catch_and_log_exceptions(context=self.__class__.__name__): - self.__attrs_init__(values=values) + self.__attrs_init__(values=values, color=color) return self.__attrs_clear__() @@ -64,6 +66,7 @@ def __attrs_clear__(self) -> None: """Convenience method for calling `__attrs_init__` with all `None`s.""" self.__attrs_init__( values=None, # type: ignore[arg-type] + color=None, # type: ignore[arg-type] ) @classmethod @@ -81,5 +84,14 @@ def _clear(cls) -> BarChart: # # (Docstring intentionally commented out to hide this field from the docs) + color: components.ColorBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.ColorBatch._optional, # type: ignore[misc] + ) + # The color of the bar chart + # + # (Docstring intentionally commented out to hide this field from the docs) + __str__ = Archetype.__str__ __repr__ = Archetype.__repr__