diff --git a/crates/re_data_store/src/entity_properties.rs b/crates/re_data_store/src/entity_properties.rs index ad9c8f50f93f..606f97d1dd4c 100644 --- a/crates/re_data_store/src/entity_properties.rs +++ b/crates/re_data_store/src/entity_properties.rs @@ -1,7 +1,7 @@ use re_arrow_store::LatestAtQuery; use re_log_types::{ - external::arrow2_convert::deserialize::arrow_array_deserialize_iterator, msg_bundle::Component, - EntityPath, Transform, + external::arrow2_convert::deserialize::arrow_array_deserialize_iterator, + msg_bundle::DeserializableComponent, EntityPath, }; use crate::log_db::EntityDb; @@ -38,6 +38,10 @@ pub struct EntityProperties { pub visible: bool, pub visible_history: ExtraQueryHistory, pub interactive: bool, + + /// Distance of the projection plane. + /// + /// Only applies to pinhole cameras when in a spatial view, using 3D navigation. pinhole_image_plane_distance: Option>, } @@ -114,37 +118,39 @@ impl ExtraQueryHistory { // ---------------------------------------------------------------------------- -/// Get the latest value of the transform +/// Get the latest value for a given [`re_log_types::msg_bundle::Component`]. /// -/// We first look for the transform in the classic storage system since that's -/// what most users are still using. If we don't find the transform there, then -/// we check to see if it exists in the arrow storage. -pub fn query_transform( +/// This assumes that the row we get from the store only contains a single instance for this +/// component; it will log a warning otherwise. +pub fn query_latest_single( entity_db: &EntityDb, entity_path: &EntityPath, query: &LatestAtQuery, -) -> Option { +) -> Option +where + for<'b> &'b C::ArrayType: IntoIterator, +{ crate::profile_function!(); // Although it would be nice to use the `re_query` helpers for this, we would need to move // this out of re_data_store to avoid a circular dep. Since we don't need to do a join for - // transforms this is easy enough. + // single components this is easy enough. let data_store = &entity_db.data_store; - let components = [Transform::name()]; + let components = [C::name()]; - let row_indices = data_store.latest_at(query, entity_path, Transform::name(), &components)?; + let row_indices = data_store.latest_at(query, entity_path, C::name(), &components)?; let results = data_store.get(&components, &row_indices); let arr = results.get(0)?.as_ref()?.as_ref(); - let mut iter = arrow_array_deserialize_iterator::(arr).ok()?; + let mut iter = arrow_array_deserialize_iterator::(arr).ok()?; - let transform = iter.next(); + let component = iter.next(); if iter.next().is_some() { - re_log::warn_once!("Unexpected batch for Transform at: {}", entity_path); + re_log::warn_once!("Unexpected batch for {} at: {}", C::name(), entity_path); } - transform + component } diff --git a/crates/re_log_types/src/msg_bundle.rs b/crates/re_log_types/src/msg_bundle.rs index fc4f72acbf7b..05ea11410ed0 100644 --- a/crates/re_log_types/src/msg_bundle.rs +++ b/crates/re_log_types/src/msg_bundle.rs @@ -27,6 +27,7 @@ use arrow2::{ offset::Offsets, }; use arrow2_convert::{ + deserialize::{ArrowArray, ArrowDeserialize}, field::ArrowField, serialize::{ArrowSerialize, TryIntoArrow}, }; @@ -88,9 +89,8 @@ pub trait Component: ArrowField { } } -/// A trait to identify any [`Component`] that is ready to be collected and subsequently serialized -/// into an Arrow payload. -pub trait SerializableComponent +/// A [`Component`] that fulfils all the conditions required to be serialized as an Arrow payload. +pub trait SerializableComponent where Self: Component + ArrowSerialize + ArrowField + 'static, { @@ -101,6 +101,34 @@ impl SerializableComponent for C where { } +/// A [`Component`] that fulfils all the conditions required to be deserialized from an Arrow +/// payload. +/// +/// Note that due to the use of HRTBs in `arrow2_convert` traits, you will still need an extra HRTB +/// clause when marking a type as `DeserializableComponent`: +/// ```ignore +/// where +/// T: SerializableComponent, +/// for<'a> &'a T::ArrayType: IntoIterator, +/// ``` +pub trait DeserializableComponent +where + Self: Component, + Self: ArrowDeserialize + ArrowField + 'static, + Self::ArrayType: ArrowArray, + for<'b> &'b Self::ArrayType: IntoIterator, +{ +} + +impl DeserializableComponent for C +where + C: Component, + C: ArrowDeserialize + ArrowField + 'static, + C::ArrayType: ArrowArray, + for<'b> &'b C::ArrayType: IntoIterator, +{ +} + /// A [`ComponentBundle`] holds an Arrow component column, and its field name. /// /// A [`ComponentBundle`] can be created from a collection of any element that implements the diff --git a/crates/re_query/src/dataframe_util.rs b/crates/re_query/src/dataframe_util.rs index 01b88ae8e120..3cd97967111a 100644 --- a/crates/re_query/src/dataframe_util.rs +++ b/crates/re_query/src/dataframe_util.rs @@ -7,12 +7,8 @@ use polars_core::prelude::*; use re_arrow_store::ArrayExt; use re_log_types::{ component_types::InstanceKey, - external::arrow2_convert::{ - deserialize::{arrow_array_deserialize_iterator, ArrowArray, ArrowDeserialize}, - field::ArrowField, - serialize::ArrowSerialize, - }, - msg_bundle::Component, + external::arrow2_convert::deserialize::arrow_array_deserialize_iterator, + msg_bundle::{Component, DeserializableComponent, SerializableComponent}, }; use crate::{ @@ -55,10 +51,10 @@ fn fix_polars_nulls(array: &dyn Array) -> Box { } /// Iterator for a single column in a dataframe as the rust-native Component type -pub fn iter_column<'a, C: Component>(df: &'a DataFrame) -> impl Iterator> + 'a +pub fn iter_column<'a, C: DeserializableComponent>( + df: &'a DataFrame, +) -> impl Iterator> + 'a where - C: ArrowDeserialize + ArrowField + 'static, - C::ArrayType: ArrowArray, for<'b> &'b C::ArrayType: IntoIterator, { let res = match df.column(C::name().as_str()) { @@ -74,11 +70,7 @@ where res.into_iter() } -pub fn df_builder1(c0: &Vec>) -> crate::Result -where - C0: Component + 'static, - Option: ArrowSerialize + ArrowField>, -{ +pub fn df_builder1(c0: &Vec>) -> crate::Result { use arrow2::array::MutableArray; use re_log_types::external::arrow2_convert::serialize::arrow_serialize_to_mutable_array; @@ -92,10 +84,8 @@ where pub fn df_builder2(c0: &Vec>, c1: &Vec>) -> crate::Result where - C0: Component + 'static, - Option: ArrowSerialize + ArrowField>, - C1: Component + 'static, - Option: ArrowSerialize + ArrowField>, + C0: SerializableComponent, + C1: SerializableComponent, { use arrow2::array::MutableArray; use re_log_types::external::arrow2_convert::serialize::arrow_serialize_to_mutable_array; @@ -117,12 +107,9 @@ pub fn df_builder3( c2: &Vec>, ) -> crate::Result where - C0: Component + 'static, - Option: ArrowSerialize + ArrowField>, - C1: Component + 'static, - Option: ArrowSerialize + ArrowField>, - C2: Component + 'static, - Option: ArrowSerialize + ArrowField>, + C0: SerializableComponent, + C1: SerializableComponent, + C2: SerializableComponent, { use arrow2::array::MutableArray; use re_log_types::external::arrow2_convert::serialize::arrow_serialize_to_mutable_array; @@ -142,12 +129,10 @@ where } impl ComponentWithInstances { - pub fn as_df(&self) -> crate::Result + pub fn as_df( + &self, + ) -> crate::Result where - C0: Component, - Option: ArrowSerialize + ArrowField>, - C0: ArrowDeserialize + ArrowField + 'static, - C0::ArrayType: ArrowArray, for<'a> &'a C0::ArrayType: IntoIterator, { if C0::name() != self.name { @@ -169,8 +154,7 @@ impl ComponentWithInstances { impl EntityView where - Primary: Component + ArrowSerialize + ArrowDeserialize + ArrowField + 'static, - Primary::ArrayType: ArrowArray, + Primary: SerializableComponent + DeserializableComponent, for<'a> &'a Primary::ArrayType: IntoIterator, { pub fn as_df1(&self) -> crate::Result { @@ -184,10 +168,7 @@ where pub fn as_df2(&self) -> crate::Result where - C1: Clone + Component, - Option: ArrowSerialize + ArrowField>, - C1: ArrowDeserialize + ArrowField + 'static, - C1::ArrayType: ArrowArray, + C1: SerializableComponent + DeserializableComponent + Clone, for<'a> &'a C1::ArrayType: IntoIterator, { let instance_keys = self.primary.iter_instance_keys()?.map(Some).collect_vec(); diff --git a/crates/re_query/src/entity_view.rs b/crates/re_query/src/entity_view.rs index 0dc903392eb8..4dd67ec96235 100644 --- a/crates/re_query/src/entity_view.rs +++ b/crates/re_query/src/entity_view.rs @@ -6,11 +6,9 @@ use re_format::arrow; use re_log_types::{ component_types::InstanceKey, external::arrow2_convert::{ - deserialize::{arrow_array_deserialize_iterator, ArrowArray, ArrowDeserialize}, - field::ArrowField, - serialize::ArrowSerialize, + deserialize::arrow_array_deserialize_iterator, field::ArrowField, serialize::ArrowSerialize, }, - msg_bundle::Component, + msg_bundle::{Component, DeserializableComponent, SerializableComponent}, ComponentName, }; @@ -58,10 +56,10 @@ impl ComponentWithInstances { } /// Iterate over the values and convert them to a native `Component` - pub fn iter_values(&self) -> crate::Result> + '_> + pub fn iter_values( + &self, + ) -> crate::Result> + '_> where - C: ArrowDeserialize + ArrowField + 'static, - C::ArrayType: ArrowArray, for<'a> &'a C::ArrayType: IntoIterator, { if C::name() != self.name { @@ -77,10 +75,8 @@ impl ComponentWithInstances { } /// Look up the value that corresponds to a given `InstanceKey` and convert to `Component` - pub fn lookup(&self, instance_key: &InstanceKey) -> crate::Result + pub fn lookup(&self, instance_key: &InstanceKey) -> crate::Result where - C: ArrowDeserialize + ArrowField + 'static, - C::ArrayType: ArrowArray, for<'a> &'a C::ArrayType: IntoIterator, { if C::name() != self.name { @@ -132,14 +128,10 @@ impl ComponentWithInstances { } /// Produce a `ComponentWithInstances` from native component types - pub fn from_native( + pub fn from_native( instance_keys: Option<&Vec>, values: &Vec, - ) -> crate::Result - where - C: Component + 'static, - C: ArrowSerialize + ArrowField, - { + ) -> crate::Result { use re_log_types::external::arrow2_convert::serialize::arrow_serialize_to_mutable_array; let instance_keys = if let Some(keys) = instance_keys { @@ -296,10 +288,8 @@ where } } -impl EntityView +impl EntityView where - Primary: Component + ArrowSerialize + ArrowDeserialize + ArrowField + 'static, - Primary::ArrayType: ArrowArray, for<'a> &'a Primary::ArrayType: IntoIterator, { /// Iterate over the instance keys @@ -332,12 +322,10 @@ where /// Iterate over the values of a `Component`. /// /// Always produces an iterator of length `self.primary.len()` - pub fn iter_component( + pub fn iter_component( &self, ) -> crate::Result> + '_> where - C: Clone + ArrowDeserialize + ArrowField + 'static, - C::ArrayType: ArrowArray, for<'b> &'b C::ArrayType: IntoIterator, { let component = self.components.get(&C::name()); diff --git a/crates/re_query/src/visit.rs b/crates/re_query/src/visit.rs index cf13cdede0fb..733debf3955b 100644 --- a/crates/re_query/src/visit.rs +++ b/crates/re_query/src/visit.rs @@ -44,12 +44,7 @@ use re_log_types::{ component_types::InstanceKey, - external::arrow2_convert::{ - deserialize::{ArrowArray, ArrowDeserialize}, - field::ArrowField, - serialize::ArrowSerialize, - }, - msg_bundle::Component, + msg_bundle::{Component, DeserializableComponent, SerializableComponent}, }; use crate::EntityView; @@ -72,8 +67,7 @@ macro_rules! create_visitor { ) -> crate::Result<()> where $( - $CC: Clone + ArrowDeserialize + ArrowField + 'static, - $CC::ArrayType: ArrowArray, + $CC: Clone + DeserializableComponent, for<'a> &'a $CC::ArrayType: IntoIterator, )* { @@ -98,10 +92,8 @@ macro_rules! create_visitor { ); } -impl EntityView +impl EntityView where - Primary: Component + ArrowSerialize + ArrowDeserialize + ArrowField + 'static, - Primary::ArrayType: ArrowArray, for<'a> &'a Primary::ArrayType: IntoIterator, { create_visitor! {visit1; ;} diff --git a/crates/re_viewer/src/misc/space_info.rs b/crates/re_viewer/src/misc/space_info.rs index 979e8aa71f89..118a26890ee3 100644 --- a/crates/re_viewer/src/misc/space_info.rs +++ b/crates/re_viewer/src/misc/space_info.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use nohash_hasher::IntSet; use re_arrow_store::{LatestAtQuery, TimeInt, Timeline}; -use re_data_store::{log_db::EntityDb, query_transform, EntityPath, EntityTree}; +use re_data_store::{log_db::EntityDb, query_latest_single, EntityPath, EntityTree}; use re_log_types::{Transform, ViewCoordinates}; use re_query::query_entity_with_primary; @@ -112,7 +112,8 @@ impl SpaceInfoCollection { tree: &EntityTree, query: &LatestAtQuery, ) { - if let Some(transform) = query_transform(entity_db, &tree.path, query) { + if let Some(transform) = query_latest_single::(entity_db, &tree.path, query) + { // A set transform (likely non-identity) - create a new space. parent_space .child_spaces @@ -156,7 +157,7 @@ impl SpaceInfoCollection { let mut spaces_info = Self::default(); // Start at the root. The root is always part of the collection! - if query_transform(entity_db, &EntityPath::root(), &query).is_some() { + if query_latest_single::(entity_db, &EntityPath::root(), &query).is_some() { re_log::warn_once!("The root entity has a 'transform' component! This will have no effect. Did you mean to apply the transform elsewhere?"); } let mut root_space_info = SpaceInfo::new(EntityPath::root()); diff --git a/crates/re_viewer/src/misc/transform_cache.rs b/crates/re_viewer/src/misc/transform_cache.rs index f30f0bcae28d..87d4925b9e31 100644 --- a/crates/re_viewer/src/misc/transform_cache.rs +++ b/crates/re_viewer/src/misc/transform_cache.rs @@ -1,6 +1,8 @@ use nohash_hasher::IntMap; use re_arrow_store::LatestAtQuery; -use re_data_store::{log_db::EntityDb, query_transform, EntityPath, EntityPropertyMap, EntityTree}; +use re_data_store::{ + log_db::EntityDb, query_latest_single, EntityPath, EntityPropertyMap, EntityTree, +}; use crate::misc::TimeControl; @@ -222,7 +224,7 @@ fn transform_at( query: &LatestAtQuery, encountered_pinhole: &mut bool, ) -> Result, UnreachableTransform> { - if let Some(transform) = query_transform(entity_db, entity_path, query) { + if let Some(transform) = query_latest_single(entity_db, entity_path, query) { match transform { re_log_types::Transform::Rigid3(rigid) => Ok(Some(rigid.parent_from_child().to_mat4())), // If we're connected via 'unknown' it's not reachable @@ -269,7 +271,7 @@ fn inverse_transform_at( query: &LatestAtQuery, encountered_pinhole: &mut bool, ) -> Result, UnreachableTransform> { - if let Some(parent_transform) = query_transform(entity_db, entity_path, query) { + if let Some(parent_transform) = query_latest_single(entity_db, entity_path, query) { match parent_transform { re_log_types::Transform::Rigid3(rigid) => Ok(Some(rigid.child_from_parent().to_mat4())), // If we're connected via 'unknown', everything except whats under `parent_tree` is unreachable diff --git a/crates/re_viewer/src/ui/data_ui/component_ui_registry.rs b/crates/re_viewer/src/ui/data_ui/component_ui_registry.rs index 53fabf3e30a8..fe1156c1fbbd 100644 --- a/crates/re_viewer/src/ui/data_ui/component_ui_registry.rs +++ b/crates/re_viewer/src/ui/data_ui/component_ui_registry.rs @@ -4,11 +4,7 @@ use re_arrow_store::LatestAtQuery; use re_log_types::{ component_types::InstanceKey, external::arrow2, - external::arrow2_convert::{ - deserialize::{ArrowArray, ArrowDeserialize}, - field::ArrowField, - }, - msg_bundle::Component, + msg_bundle::{Component, DeserializableComponent}, ComponentName, }; use re_query::ComponentWithInstances; @@ -74,10 +70,8 @@ impl Default for ComponentUiRegistry { } impl ComponentUiRegistry { - fn add(&mut self) + fn add(&mut self) where - C: Component + DataUi + ArrowDeserialize + ArrowField + 'static, - C::ArrayType: ArrowArray, for<'a> &'a C::ArrayType: IntoIterator, { self.components.insert( diff --git a/crates/re_viewer/src/ui/selection_panel.rs b/crates/re_viewer/src/ui/selection_panel.rs index 5b6449b83e5c..aef84d37d014 100644 --- a/crates/re_viewer/src/ui/selection_panel.rs +++ b/crates/re_viewer/src/ui/selection_panel.rs @@ -1,5 +1,5 @@ -use re_data_store::{query_transform, EntityPath, EntityProperties}; -use re_log_types::TimeType; +use re_data_store::{query_latest_single, EntityPath, EntityProperties}; +use re_log_types::{TimeType, Transform}; use crate::{ ui::{view_spatial::SpatialNavigationMode, Blueprint}, @@ -407,11 +407,12 @@ fn entity_props_ui( } ui.end_row(); + // pinhole_image_plane_distance if view_state.state_spatial.nav_mode == SpatialNavigationMode::ThreeD { if let Some(entity_path) = entity_path { let query = ctx.current_query(); if let Some(re_log_types::Transform::Pinhole(pinhole)) = - query_transform(&ctx.log_db.entity_db, entity_path, &query) + query_latest_single::(&ctx.log_db.entity_db, entity_path, &query) { ui.label("Image plane distance"); let mut distance = entity_props.pinhole_image_plane_distance(&pinhole); diff --git a/crates/re_viewer/src/ui/space_view_heuristics.rs b/crates/re_viewer/src/ui/space_view_heuristics.rs index ee2bff612291..1c1bbe02a739 100644 --- a/crates/re_viewer/src/ui/space_view_heuristics.rs +++ b/crates/re_viewer/src/ui/space_view_heuristics.rs @@ -4,7 +4,7 @@ use ahash::HashMap; use itertools::Itertools; use nohash_hasher::IntSet; use re_arrow_store::{DataStore, LatestAtQuery, Timeline}; -use re_data_store::{log_db::EntityDb, query_transform, ComponentName, EntityPath}; +use re_data_store::{log_db::EntityDb, query_latest_single, ComponentName, EntityPath}; use re_log_types::{ component_types::{Tensor, TensorTrait}, msg_bundle::Component, @@ -100,7 +100,7 @@ fn is_interesting_space_view_not_at_root( // .. an unknown transform, the children can't be shown otherwise // .. an pinhole transform, we'd like to see the world from this camera's pov as well! if candidate.category == ViewCategory::Spatial { - if let Some(transform) = query_transform(entity_db, &candidate.space_path, query) { + if let Some(transform) = query_latest_single(entity_db, &candidate.space_path, query) { match transform { re_log_types::Transform::Rigid3(_) => {} re_log_types::Transform::Pinhole(_) | re_log_types::Transform::Unknown => { diff --git a/crates/re_viewer/src/ui/view_category.rs b/crates/re_viewer/src/ui/view_category.rs index 9148281d00d6..0d049ce19569 100644 --- a/crates/re_viewer/src/ui/view_category.rs +++ b/crates/re_viewer/src/ui/view_category.rs @@ -1,5 +1,5 @@ use re_arrow_store::{LatestAtQuery, TimeInt}; -use re_data_store::{query_transform, EntityPath, LogDb, Timeline}; +use re_data_store::{query_latest_single, EntityPath, LogDb, Timeline}; use re_log_types::{ component_types::{ Box3D, LineStrip2D, LineStrip3D, Point2D, Point3D, Rect2D, Scalar, Tensor, TensorTrait, @@ -73,7 +73,7 @@ pub fn categorize_entity_path( // If it has a transform we might want to visualize it in space // (as of writing we do that only for projections, i.e. cameras, but visualizations for rigid transforms may be added) - if query_transform( + if query_latest_single::( &log_db.entity_db, entity_path, &LatestAtQuery::new(timeline, TimeInt::MAX),