diff --git a/crates/re_data_ui/src/item.rs b/crates/re_data_ui/src/item.rs index ee57ea0685a2..c1409714fb3d 100644 --- a/crates/re_data_ui/src/item.rs +++ b/crates/re_data_ui/src/item.rs @@ -11,7 +11,7 @@ impl DataUi for Item { query: &re_arrow_store::LatestAtQuery, ) { match self { - Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => { + Item::SpaceView(_) | Item::DataBlueprintGroup(_, _, _) => { // Shouldn't be reachable since SelectionPanel::contents doesn't show data ui for these. // If you add something in here make sure to adjust SelectionPanel::contents accordingly. } diff --git a/crates/re_data_ui/src/item_ui.rs b/crates/re_data_ui/src/item_ui.rs index 347e00c71d08..ab3767286cb5 100644 --- a/crates/re_data_ui/src/item_ui.rs +++ b/crates/re_data_ui/src/item_ui.rs @@ -6,7 +6,7 @@ use egui::Ui; use re_data_store::InstancePath; use re_log_types::{ComponentPath, EntityPath, TimeInt, Timeline}; use re_viewer_context::{ - DataBlueprintGroupHandle, HoverHighlight, Item, SpaceViewId, UiVerbosity, ViewerContext, + DataQueryId, HoverHighlight, Item, SpaceViewId, UiVerbosity, ViewerContext, }; use super::DataUi; @@ -197,9 +197,10 @@ pub fn data_blueprint_group_button_to( ui: &mut egui::Ui, text: impl Into, space_view_id: SpaceViewId, - group_handle: DataBlueprintGroupHandle, + query_id: DataQueryId, + entity_path: EntityPath, ) -> egui::Response { - let item = Item::DataBlueprintGroup(space_view_id, group_handle); + let item = Item::DataBlueprintGroup(space_view_id, query_id, entity_path); let response = ctx .re_ui .selectable_label_with_icon( diff --git a/crates/re_log_types/src/path/entity_path.rs b/crates/re_log_types/src/path/entity_path.rs index 111be28417d5..fff87bb47e75 100644 --- a/crates/re_log_types/src/path/entity_path.rs +++ b/crates/re_log_types/src/path/entity_path.rs @@ -261,6 +261,20 @@ impl From for String { } } +impl From for EntityPath { + #[inline] + fn from(value: re_types_core::datatypes::EntityPath) -> Self { + EntityPath::parse_forgiving(&value.0) + } +} + +impl From<&EntityPath> for re_types_core::datatypes::EntityPath { + #[inline] + fn from(value: &EntityPath) -> Self { + Self(value.to_string().into()) + } +} + // ---------------------------------------------------------------------------- use re_types_core::Loggable; diff --git a/crates/re_log_types/src/path/entity_path_expr.rs b/crates/re_log_types/src/path/entity_path_expr.rs index ea14f74962ec..3cc99e86dde8 100644 --- a/crates/re_log_types/src/path/entity_path_expr.rs +++ b/crates/re_log_types/src/path/entity_path_expr.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use crate::EntityPath; /// An expression that corresponds to multiple [`EntityPath`]s within a tree. @@ -44,11 +46,30 @@ impl EntityPathExpr { } } +impl Display for EntityPathExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Exact(path) => path.fmt(f), + Self::Recursive(path) => { + if path.is_root() { + write!(f, "/") + } else { + write!(f, "{path}/") + } + } + } + } +} + impl From<&str> for EntityPathExpr { #[inline] fn from(path: &str) -> Self { if let Some(path) = path.strip_suffix('/') { - Self::Recursive(EntityPath::from(path)) + if path.is_empty() { + Self::Recursive(EntityPath::root()) + } else { + Self::Recursive(EntityPath::from(path)) + } } else { Self::Exact(EntityPath::from(path)) } diff --git a/crates/re_space_view/src/blueprint/query_expressions.rs b/crates/re_space_view/src/blueprint/query_expressions.rs index 2724c536c217..19e37d15a140 100644 --- a/crates/re_space_view/src/blueprint/query_expressions.rs +++ b/crates/re_space_view/src/blueprint/query_expressions.rs @@ -24,7 +24,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// **Blueprint**: A set of expressions used for a `DataQueryBlueprint`. /// /// Unstable. Used for the ongoing blueprint experimentations. -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct QueryExpressions { /// A set of strings that can be parsed as `EntityPathExpression`s. pub expressions: Vec<::re_types_core::ArrowString>, diff --git a/crates/re_space_view/src/data_query.rs b/crates/re_space_view/src/data_query.rs index a49deeb3e71c..c49fa8f0bb02 100644 --- a/crates/re_space_view/src/data_query.rs +++ b/crates/re_space_view/src/data_query.rs @@ -15,6 +15,20 @@ pub trait PropertyResolver { fn resolve_entity_overrides(&self, ctx: &StoreContext<'_>) -> EntityOverrides; } +pub struct NoopResolver {} + +impl PropertyResolver for NoopResolver { + fn resolve_entity_overrides(&self, _ctx: &StoreContext<'_>) -> EntityOverrides { + EntityOverrides { + root: EntityProperties::default(), + individual: EntityPropertyMap::default(), + group: EntityPropertyMap::default(), + } + } +} + +pub static NOOP_RESOLVER: NoopResolver = NoopResolver {}; + /// The common trait implemented for data queries /// /// Both interfaces return [`re_viewer_context::DataResult`]s, which are self-contained description of the data diff --git a/crates/re_space_view/src/data_query_blueprint.rs b/crates/re_space_view/src/data_query_blueprint.rs index 6143c771e05c..6c8694f73ff9 100644 --- a/crates/re_space_view/src/data_query_blueprint.rs +++ b/crates/re_space_view/src/data_query_blueprint.rs @@ -1,10 +1,12 @@ use nohash_hasher::IntSet; use once_cell::sync::Lazy; -use re_data_store::{EntityProperties, EntityTree}; +use re_data_store::{ + EntityProperties, EntityPropertiesComponent, EntityPropertyMap, EntityTree, StoreDb, +}; use re_log_types::{EntityPath, EntityPathExpr}; use re_viewer_context::{ DataQueryId, DataQueryResult, DataResult, DataResultHandle, DataResultNode, DataResultTree, - EntitiesPerSystem, EntitiesPerSystemPerClass, SpaceViewClassName, + EntitiesPerSystem, EntitiesPerSystemPerClass, SpaceViewClassName, SpaceViewId, StoreContext, }; use slotmap::SlotMap; use smallvec::SmallVec; @@ -23,6 +25,7 @@ use crate::{blueprint::QueryExpressions, DataQuery, EntityOverrides, PropertyRes /// The results of recursive expressions are only included if they are found within the [`EntityTree`] /// and for which there is a valid `ViewPart` system. This keeps recursive expressions from incorrectly /// picking up irrelevant data within the tree. +#[derive(Clone, PartialEq, Eq)] pub struct DataQueryBlueprint { pub id: DataQueryId, pub space_view_class_name: SpaceViewClassName, @@ -30,7 +33,67 @@ pub struct DataQueryBlueprint { } impl DataQueryBlueprint { - pub const OVERRIDES_PREFIX: &str = "overrides"; + pub fn is_equivalent(&self, other: &DataQueryBlueprint) -> bool { + self.space_view_class_name.eq(&other.space_view_class_name) + && self.expressions.eq(&other.expressions) + } +} + +impl DataQueryBlueprint { + pub const INDIVIDUAL_OVERRIDES_PREFIX: &str = "individual_overrides"; + pub const RECURSIVE_OVERRIDES_PREFIX: &str = "recursive_overrides"; + + pub fn new<'a>( + space_view_class_name: SpaceViewClassName, + queries_entities: impl Iterator, + ) -> Self { + Self { + id: DataQueryId::random(), + space_view_class_name, + expressions: queries_entities + .map(|exp| exp.to_string().into()) + .collect::>() + .into(), + } + } + + pub fn try_from_db( + path: &EntityPath, + blueprint_db: &StoreDb, + space_view_class_name: SpaceViewClassName, + ) -> Option { + let expressions = blueprint_db + .store() + .query_timeless_component::(path) + .map(|c| c.value)?; + + let id = DataQueryId::from_entity_path(path); + + Some(Self { + id, + space_view_class_name, + expressions, + }) + } + + pub fn build_resolver<'a>( + &self, + container: SpaceViewId, + auto_properties: &'a EntityPropertyMap, + ) -> DataQueryPropertyResolver<'a> { + DataQueryPropertyResolver { + auto_properties, + default_stack: vec![container.as_entity_path(), self.id.as_entity_path()], + individual_override_root: self + .id + .as_entity_path() + .join(&Self::INDIVIDUAL_OVERRIDES_PREFIX.into()), + recursive_override_root: self + .id + .as_entity_path() + .join(&Self::RECURSIVE_OVERRIDES_PREFIX.into()), + } + } } impl DataQuery for DataQueryBlueprint { @@ -93,6 +156,7 @@ impl<'a> QueryExpressionEvaluator<'a> { .expressions .expressions .iter() + .filter(|exp| !exp.as_str().is_empty()) .map(|exp| EntityPathExpr::from(exp.as_str())) .collect(); @@ -166,18 +230,29 @@ impl<'a> QueryExpressionEvaluator<'a> { } let base_entity_path = self.blueprint.id.as_entity_path().clone(); - let prefix = EntityPath::from(DataQueryBlueprint::OVERRIDES_PREFIX); - let override_path = base_entity_path.join(&prefix).join(&entity_path); + + let individual_override_path = base_entity_path + .join(&DataQueryBlueprint::INDIVIDUAL_OVERRIDES_PREFIX.into()) + .join(&entity_path); + let recursive_override_path = base_entity_path + .join(&DataQueryBlueprint::RECURSIVE_OVERRIDES_PREFIX.into()) + .join(&entity_path); let self_leaf = if !view_parts.is_empty() || exact_match { + let individual_props = overrides.individual.get_opt(&entity_path); + let mut leaf_resolved_properties = resolved_properties.clone(); + + if let Some(props) = individual_props { + leaf_resolved_properties = leaf_resolved_properties.with_child(props); + } Some(data_results.insert(DataResultNode { data_result: DataResult { entity_path: entity_path.clone(), view_parts, is_group: false, individual_properties: overrides.individual.get_opt(&entity_path).cloned(), - resolved_properties: resolved_properties.clone(), - override_path: override_path.clone(), + resolved_properties: leaf_resolved_properties, + override_path: individual_override_path, }, children: Default::default(), })) @@ -196,7 +271,7 @@ impl<'a> QueryExpressionEvaluator<'a> { self.add_entity_tree_to_data_results_recursive( subtree, overrides, - inherited, + &resolved_properties, data_results, recursive_match, // Once we have hit a recursive match, it's always propagated ) @@ -207,7 +282,8 @@ impl<'a> QueryExpressionEvaluator<'a> { if children.is_empty() || children.len() == 1 && self_leaf.is_some() { self_leaf } else { - let individual_properties = overrides.individual.get_opt(&entity_path).cloned(); + // The 'individual' properties of a group are the group overrides + let individual_properties = overrides.group.get_opt(&entity_path).cloned(); Some(data_results.insert(DataResultNode { data_result: DataResult { entity_path, @@ -215,7 +291,7 @@ impl<'a> QueryExpressionEvaluator<'a> { is_group: true, individual_properties, resolved_properties, - override_path, + override_path: recursive_override_path, }, children, })) @@ -223,10 +299,92 @@ impl<'a> QueryExpressionEvaluator<'a> { } } +pub struct DataQueryPropertyResolver<'a> { + auto_properties: &'a EntityPropertyMap, + default_stack: Vec, + individual_override_root: EntityPath, + recursive_override_root: EntityPath, +} + +impl DataQueryPropertyResolver<'_> { + fn resolve_entity_overrides_for_path( + &self, + ctx: &StoreContext<'_>, + props_path: &EntityPath, + ) -> EntityPropertyMap { + re_tracing::profile_function!(); + let blueprint = ctx.blueprint; + + let mut prop_map = self.auto_properties.clone(); + + if let Some(tree) = blueprint.entity_db().tree.subtree(props_path) { + tree.visit_children_recursively(&mut |path: &EntityPath| { + if let Some(props) = blueprint + .store() + .query_timeless_component_quiet::(path) + { + let overridden_path = + EntityPath::from(&path.as_slice()[props_path.len()..path.len()]); + prop_map.update(overridden_path, props.value.props); + } + }); + } + prop_map + } +} + +impl<'a> PropertyResolver for DataQueryPropertyResolver<'a> { + /// Helper function to lookup the properties for a given entity path. + /// + /// We start with the auto properties for the `SpaceView` as the base layer and + /// then incrementally override from there. + fn resolve_entity_overrides(&self, ctx: &StoreContext<'_>) -> EntityOverrides { + re_tracing::profile_function!(); + let blueprint = ctx.blueprint; + + let mut root: EntityProperties = Default::default(); + for prefix in &self.default_stack { + if let Some(overrides) = ctx + .blueprint + .store() + .query_timeless_component::(prefix) + { + root = root.with_child(&overrides.value.props); + } + } + + let mut individual = self.auto_properties.clone(); + + if let Some(tree) = blueprint + .entity_db() + .tree + .subtree(&self.individual_override_root) + { + tree.visit_children_recursively(&mut |path: &EntityPath| { + if let Some(props) = blueprint + .store() + .query_timeless_component::(path) + { + let overridden_path = EntityPath::from( + &path.as_slice()[self.individual_override_root.len()..path.len()], + ); + individual.update(overridden_path, props.value.props); + } + }); + } + + EntityOverrides { + root, + individual: self.resolve_entity_overrides_for_path(ctx, &self.individual_override_root), + group: self.resolve_entity_overrides_for_path(ctx, &self.recursive_override_root), + } + } +} + #[cfg(feature = "testing")] #[cfg(test)] mod tests { - use re_data_store::{EntityPropertyMap, StoreDb}; + use re_data_store::StoreDb; use re_log_types::{example_components::MyPoint, DataRow, RowId, StoreId, TimePoint, Timeline}; use re_viewer_context::StoreContext; @@ -334,6 +492,13 @@ mod tests { "parent/skipped/child2", ], ), + ( + vec!["not/found"], + // TODO(jleibs): Making this work requires merging the EntityTree walk with a minimal-coverage ExactMatchTree walk + // not crucial for now until we expose a free-form UI for entering paths. + // vec!["/", "not/", "not/found"]), + vec![], + ), ]; for (input, outputs) in scenarios { diff --git a/crates/re_space_view/src/lib.rs b/crates/re_space_view/src/lib.rs index c7b05496a090..a93ddb6cb0f2 100644 --- a/crates/re_space_view/src/lib.rs +++ b/crates/re_space_view/src/lib.rs @@ -11,7 +11,7 @@ mod space_view_contents; mod unreachable_transform_reason; pub use blueprint::QueryExpressions; -pub use data_query::{DataQuery, EntityOverrides, PropertyResolver}; +pub use data_query::{DataQuery, EntityOverrides, PropertyResolver, NOOP_RESOLVER}; pub use data_query_blueprint::DataQueryBlueprint; pub use screenshot::ScreenshotMode; pub use space_view_contents::{DataBlueprintGroup, SpaceViewContents}; diff --git a/crates/re_time_panel/src/data_density_graph.rs b/crates/re_time_panel/src/data_density_graph.rs index dcf33636c35c..e6b0b540b2c6 100644 --- a/crates/re_time_panel/src/data_density_graph.rs +++ b/crates/re_time_panel/src/data_density_graph.rs @@ -544,7 +544,7 @@ fn show_row_ids_tooltip( Item::InstancePath(_, path) => { item_ui::instance_path_button(ctx, ui, None, path); } - Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => { + Item::SpaceView(_) | Item::DataBlueprintGroup(_, _, _) => { // No extra info. This should never happen, but not worth printing a warning over. // Even if it does go here, the ui after will still look ok. } diff --git a/crates/re_types/definitions/rerun/blueprint/query_expressions.fbs b/crates/re_types/definitions/rerun/blueprint/query_expressions.fbs index 378be4bf54c3..1b6e9d99f5d2 100644 --- a/crates/re_types/definitions/rerun/blueprint/query_expressions.fbs +++ b/crates/re_types/definitions/rerun/blueprint/query_expressions.fbs @@ -13,7 +13,7 @@ namespace rerun.blueprint; /// /// Unstable. Used for the ongoing blueprint experimentations. table QueryExpressions ( - "attr.rust.derive_only": "Clone", + "attr.rust.derive": "PartialEq, Eq", "attr.rust.override_crate": "re_space_view" ) { /// A set of strings that can be parsed as `EntityPathExpression`s. diff --git a/crates/re_types/definitions/rerun/blueprint/space_view_component.fbs b/crates/re_types/definitions/rerun/blueprint/space_view_component.fbs index 05d3d5c8e6da..27672cf77faf 100644 --- a/crates/re_types/definitions/rerun/blueprint/space_view_component.fbs +++ b/crates/re_types/definitions/rerun/blueprint/space_view_component.fbs @@ -1,9 +1,10 @@ include "arrow/attributes.fbs"; +include "docs/attributes.fbs"; include "python/attributes.fbs"; include "rust/attributes.fbs"; -include "rerun/datatypes.fbs"; include "rerun/attributes.fbs"; +include "rerun/datatypes.fbs"; namespace rerun.blueprint; @@ -13,8 +14,27 @@ namespace rerun.blueprint; /// /// Unstable. Used for the ongoing blueprint experimentations. table SpaceViewComponent ( - "attr.rust.derive_only": "Clone", - "attr.rust.override_crate": "re_viewport" + "attr.docs.unreleased", + "attr.rust.derive_only": "Clone" ) { - space_view: [ubyte] (order: 100, "attr.rust.serde_type": "crate::SpaceViewBlueprint"); + /// The name of the view. + display_name: string (order: 100); + + /// The class of the view. + class_name: string (order: 200); + + /// The "anchor point" of this space view. + /// + /// The transform at this path forms the reference point for all scene->world transforms in this space view. + /// I.e. the position of this entity path in space forms the origin of the coordinate system in this space view. + /// Furthermore, this is the primary indicator for heuristics on what entities we show in this space view. + space_origin: rerun.datatypes.EntityPath (order: 300); + + /// True if the user is expected to add entities themselves. False otherwise. + entities_determined_by_user: bool (order: 400); + + /// `BlueprintId`s of the `DataQuery`s that make up this `SpaceView`. + /// + /// It determines which entities are part of the spaceview. + contents: [rerun.datatypes.Uuid] (order: 500); } diff --git a/crates/re_types/definitions/rerun/datatypes.fbs b/crates/re_types/definitions/rerun/datatypes.fbs index 36a5fd4a1e46..a58086f2b59b 100644 --- a/crates/re_types/definitions/rerun/datatypes.fbs +++ b/crates/re_types/definitions/rerun/datatypes.fbs @@ -3,6 +3,7 @@ include "./datatypes/annotation_info.fbs"; include "./datatypes/class_description.fbs"; include "./datatypes/class_description_map_elem.fbs"; include "./datatypes/class_id.fbs"; +include "./datatypes/entity_path.fbs"; include "./datatypes/float32.fbs"; include "./datatypes/keypoint_id.fbs"; include "./datatypes/keypoint_pair.fbs"; @@ -23,6 +24,7 @@ include "./datatypes/translation_and_mat3x3.fbs"; include "./datatypes/translation_rotation_scale3d.fbs"; include "./datatypes/uint32.fbs"; include "./datatypes/utf8.fbs"; +include "./datatypes/uuid.fbs"; include "./datatypes/uvec2d.fbs"; include "./datatypes/uvec3d.fbs"; include "./datatypes/uvec4d.fbs"; diff --git a/crates/re_types/definitions/rerun/datatypes/entity_path.fbs b/crates/re_types/definitions/rerun/datatypes/entity_path.fbs new file mode 100644 index 000000000000..3efedd999f8f --- /dev/null +++ b/crates/re_types/definitions/rerun/datatypes/entity_path.fbs @@ -0,0 +1,24 @@ +include "arrow/attributes.fbs"; +include "docs/attributes.fbs"; +include "python/attributes.fbs"; +include "rust/attributes.fbs"; + +include "rerun/datatypes.fbs"; +include "rerun/attributes.fbs"; + +namespace rerun.datatypes; + +// --- + +/// A path to an entity in the `DataStore`. +table EntityPath ( + "attr.arrow.transparent", + "attr.docs.unreleased", + "attr.rust.derive": "Default", + "attr.rust.repr": "transparent", + "attr.rust.tuple_struct", + "attr.rust.override_crate": "re_types_core" +) { + // TODO(jleibs): This should be a special primitive + path: string (order: 100); +} diff --git a/crates/re_types/definitions/rerun/datatypes/uuid.fbs b/crates/re_types/definitions/rerun/datatypes/uuid.fbs new file mode 100644 index 000000000000..36858fb23e05 --- /dev/null +++ b/crates/re_types/definitions/rerun/datatypes/uuid.fbs @@ -0,0 +1,23 @@ +include "arrow/attributes.fbs"; +include "docs/attributes.fbs"; +include "python/attributes.fbs"; +include "rust/attributes.fbs"; + +include "rerun/datatypes.fbs"; +include "rerun/attributes.fbs"; + +namespace rerun.datatypes; + +// --- + +/// A 16-byte uuid. +struct Uuid ( + // TODO(jleibs): Figure out why we can't make this transparent. + // The deserializer barfs on list of fixed-sized-list. + //"attr.arrow.transparent", + "attr.docs.unreleased", + "attr.rust.derive": "Default", + "attr.rust.repr": "transparent" +) { + bytes: [ubyte: 16] (order: 100); +} diff --git a/crates/re_types/src/blueprint/.gitattributes b/crates/re_types/src/blueprint/.gitattributes new file mode 100644 index 000000000000..07e796c792f7 --- /dev/null +++ b/crates/re_types/src/blueprint/.gitattributes @@ -0,0 +1,5 @@ +# DO NOT EDIT! This file is generated by crates/re_types_builder/src/lib.rs + +.gitattributes linguist-generated=true +mod.rs linguist-generated=true +space_view_component.rs linguist-generated=true diff --git a/crates/re_types/src/blueprint/mod.rs b/crates/re_types/src/blueprint/mod.rs new file mode 100644 index 000000000000..9b6387ede326 --- /dev/null +++ b/crates/re_types/src/blueprint/mod.rs @@ -0,0 +1,6 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/rust/api.rs + +mod space_view_component; +mod space_view_component_ext; + +pub use self::space_view_component::SpaceViewComponent; diff --git a/crates/re_types/src/blueprint/space_view_component.rs b/crates/re_types/src/blueprint/space_view_component.rs new file mode 100644 index 000000000000..ead3d794cf44 --- /dev/null +++ b/crates/re_types/src/blueprint/space_view_component.rs @@ -0,0 +1,716 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/rust/api.rs +// Based on "crates/re_types/definitions/rerun/blueprint/space_view_component.fbs". + +#![allow(trivial_numeric_casts)] +#![allow(unused_imports)] +#![allow(unused_parens)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::iter_on_single_items)] +#![allow(clippy::map_flatten)] +#![allow(clippy::match_wildcard_for_single_variants)] +#![allow(clippy::needless_question_mark)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::unnecessary_cast)] + +use ::re_types_core::external::arrow2; +use ::re_types_core::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Blueprint**: A view of a space. +/// +/// Unstable. Used for the ongoing blueprint experimentations. +#[derive(Clone)] +pub struct SpaceViewComponent { + /// The name of the view. + pub display_name: ::re_types_core::ArrowString, + + /// The class of the view. + pub class_name: ::re_types_core::ArrowString, + + /// The "anchor point" of this space view. + /// + /// The transform at this path forms the reference point for all scene->world transforms in this space view. + /// I.e. the position of this entity path in space forms the origin of the coordinate system in this space view. + /// Furthermore, this is the primary indicator for heuristics on what entities we show in this space view. + pub space_origin: crate::datatypes::EntityPath, + + /// True if the user is expected to add entities themselves. False otherwise. + pub entities_determined_by_user: bool, + + /// `BlueprintId`s of the `DataQuery`s that make up this `SpaceView`. + /// + /// It determines which entities are part of the spaceview. + pub contents: Vec, +} + +::re_types_core::macros::impl_into_cow!(SpaceViewComponent); + +impl ::re_types_core::Loggable for SpaceViewComponent { + type Name = ::re_types_core::ComponentName; + + #[inline] + fn name() -> Self::Name { + "rerun.blueprint.SpaceViewComponent".into() + } + + #[allow(clippy::wildcard_imports)] + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + use arrow2::datatypes::*; + DataType::Struct(vec![ + Field { + name: "display_name".to_owned(), + data_type: DataType::Utf8, + is_nullable: false, + metadata: [].into(), + }, + Field { + name: "class_name".to_owned(), + data_type: DataType::Utf8, + is_nullable: false, + metadata: [].into(), + }, + Field { + name: "space_origin".to_owned(), + data_type: ::arrow_datatype(), + is_nullable: false, + metadata: [].into(), + }, + Field { + name: "entities_determined_by_user".to_owned(), + data_type: DataType::Boolean, + is_nullable: false, + metadata: [].into(), + }, + Field { + name: "contents".to_owned(), + data_type: DataType::List(Box::new(Field { + name: "item".to_owned(), + data_type: ::arrow_datatype(), + is_nullable: false, + metadata: [].into(), + })), + is_nullable: false, + metadata: [].into(), + }, + ]) + } + + #[allow(clippy::wildcard_imports)] + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + re_tracing::profile_function!(); + use ::re_types_core::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, datatypes::*}; + Ok({ + let (somes, data): (Vec<_>, Vec<_>) = data + .into_iter() + .map(|datum| { + let datum: Option<::std::borrow::Cow<'a, Self>> = datum.map(Into::into); + (datum.is_some(), datum) + }) + .unzip(); + let bitmap: Option = { + let any_nones = somes.iter().any(|some| !*some); + any_nones.then(|| somes.into()) + }; + StructArray::new( + ::arrow_datatype(), + vec![ + { + let (somes, display_name): (Vec<_>, Vec<_>) = data + .iter() + .map(|datum| { + let datum = datum.as_ref().map(|datum| { + let Self { display_name, .. } = &**datum; + display_name.clone() + }); + (datum.is_some(), datum) + }) + .unzip(); + let display_name_bitmap: Option = { + let any_nones = somes.iter().any(|some| !*some); + any_nones.then(|| somes.into()) + }; + { + let inner_data: arrow2::buffer::Buffer = display_name + .iter() + .flatten() + .flat_map(|s| s.0.clone()) + .collect(); + let offsets = arrow2::offset::Offsets::::try_from_lengths( + display_name.iter().map(|opt| { + opt.as_ref().map(|datum| datum.0.len()).unwrap_or_default() + }), + ) + .unwrap() + .into(); + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + unsafe { + Utf8Array::::new_unchecked( + DataType::Utf8, + offsets, + inner_data, + display_name_bitmap, + ) + } + .boxed() + } + }, + { + let (somes, class_name): (Vec<_>, Vec<_>) = data + .iter() + .map(|datum| { + let datum = datum.as_ref().map(|datum| { + let Self { class_name, .. } = &**datum; + class_name.clone() + }); + (datum.is_some(), datum) + }) + .unzip(); + let class_name_bitmap: Option = { + let any_nones = somes.iter().any(|some| !*some); + any_nones.then(|| somes.into()) + }; + { + let inner_data: arrow2::buffer::Buffer = class_name + .iter() + .flatten() + .flat_map(|s| s.0.clone()) + .collect(); + let offsets = arrow2::offset::Offsets::::try_from_lengths( + class_name.iter().map(|opt| { + opt.as_ref().map(|datum| datum.0.len()).unwrap_or_default() + }), + ) + .unwrap() + .into(); + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + unsafe { + Utf8Array::::new_unchecked( + DataType::Utf8, + offsets, + inner_data, + class_name_bitmap, + ) + } + .boxed() + } + }, + { + let (somes, space_origin): (Vec<_>, Vec<_>) = data + .iter() + .map(|datum| { + let datum = datum.as_ref().map(|datum| { + let Self { space_origin, .. } = &**datum; + space_origin.clone() + }); + (datum.is_some(), datum) + }) + .unzip(); + let space_origin_bitmap: Option = { + let any_nones = somes.iter().any(|some| !*some); + any_nones.then(|| somes.into()) + }; + { + let inner_data: arrow2::buffer::Buffer = space_origin + .iter() + .flatten() + .flat_map(|datum| { + let crate::datatypes::EntityPath(data0) = datum; + data0.0.clone() + }) + .collect(); + let offsets = arrow2::offset::Offsets::::try_from_lengths( + space_origin.iter().map(|opt| { + opt.as_ref() + .map(|datum| { + let crate::datatypes::EntityPath(data0) = datum; + data0.0.len() + }) + .unwrap_or_default() + }), + ) + .unwrap() + .into(); + + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + unsafe { + Utf8Array::::new_unchecked( + DataType::Utf8, + offsets, + inner_data, + space_origin_bitmap, + ) + } + .boxed() + } + }, + { + let (somes, entities_determined_by_user): (Vec<_>, Vec<_>) = data + .iter() + .map(|datum| { + let datum = datum.as_ref().map(|datum| { + let Self { + entities_determined_by_user, + .. + } = &**datum; + entities_determined_by_user.clone() + }); + (datum.is_some(), datum) + }) + .unzip(); + let entities_determined_by_user_bitmap: Option = { + let any_nones = somes.iter().any(|some| !*some); + any_nones.then(|| somes.into()) + }; + BooleanArray::new( + DataType::Boolean, + entities_determined_by_user + .into_iter() + .map(|v| v.unwrap_or_default()) + .collect(), + entities_determined_by_user_bitmap, + ) + .boxed() + }, + { + let (somes, contents): (Vec<_>, Vec<_>) = data + .iter() + .map(|datum| { + let datum = datum.as_ref().map(|datum| { + let Self { contents, .. } = &**datum; + contents.clone() + }); + (datum.is_some(), datum) + }) + .unzip(); + let contents_bitmap: Option = { + let any_nones = somes.iter().any(|some| !*some); + any_nones.then(|| somes.into()) + }; + { + use arrow2::{buffer::Buffer, offset::OffsetsBuffer}; + let contents_inner_data: Vec<_> = contents + .iter() + .flatten() + .flatten() + .cloned() + .map(Some) + .collect(); + let contents_inner_bitmap: Option = None; + let offsets = arrow2::offset::Offsets::::try_from_lengths( + contents.iter().map(|opt| { + opt.as_ref().map(|datum| datum.len()).unwrap_or_default() + }), + ) + .unwrap() + .into(); + ListArray::new( + DataType::List(Box::new(Field { + name: "item".to_owned(), + data_type: ::arrow_datatype(), + is_nullable: false, + metadata: [].into(), + })), + offsets, + { + _ = contents_inner_bitmap; + crate::datatypes::Uuid::to_arrow_opt(contents_inner_data)? + }, + contents_bitmap, + ) + .boxed() + } + }, + ], + bitmap, + ) + .boxed() + }) + } + + #[allow(clippy::wildcard_imports)] + fn from_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + re_tracing::profile_function!(); + use ::re_types_core::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, buffer::*, datatypes::*}; + Ok({ + let arrow_data = arrow_data + .as_any() + .downcast_ref::() + .ok_or_else(|| { + DeserializationError::datatype_mismatch( + DataType::Struct(vec![ + Field { + name: "display_name".to_owned(), + data_type: DataType::Utf8, + is_nullable: false, + metadata: [].into(), + }, + Field { + name: "class_name".to_owned(), + data_type: DataType::Utf8, + is_nullable: false, + metadata: [].into(), + }, + Field { + name: "space_origin".to_owned(), + data_type: ::arrow_datatype(), + is_nullable: false, + metadata: [].into(), + }, + Field { + name: "entities_determined_by_user".to_owned(), + data_type: DataType::Boolean, + is_nullable: false, + metadata: [].into(), + }, + Field { + name: "contents".to_owned(), + data_type: DataType::List(Box::new(Field { + name: "item".to_owned(), + data_type: ::arrow_datatype(), + is_nullable: false, + metadata: [].into(), + })), + is_nullable: false, + metadata: [].into(), + }, + ]), + arrow_data.data_type().clone(), + ) + }) + .with_context("rerun.blueprint.SpaceViewComponent")?; + if arrow_data.is_empty() { + Vec::new() + } else { + let (arrow_data_fields, arrow_data_arrays) = + (arrow_data.fields(), arrow_data.values()); + let arrays_by_name: ::std::collections::HashMap<_, _> = arrow_data_fields + .iter() + .map(|field| field.name.as_str()) + .zip(arrow_data_arrays) + .collect(); + let display_name = { + if !arrays_by_name.contains_key("display_name") { + return Err(DeserializationError::missing_struct_field( + Self::arrow_datatype(), + "display_name", + )) + .with_context("rerun.blueprint.SpaceViewComponent"); + } + let arrow_data = &**arrays_by_name["display_name"]; + { + let arrow_data = arrow_data + .as_any() + .downcast_ref::>() + .ok_or_else(|| { + DeserializationError::datatype_mismatch( + DataType::Utf8, + arrow_data.data_type().clone(), + ) + }) + .with_context("rerun.blueprint.SpaceViewComponent#display_name")?; + let arrow_data_buf = arrow_data.values(); + let offsets = arrow_data.offsets(); + arrow2::bitmap::utils::ZipValidity::new_with_validity( + offsets.iter().zip(offsets.lengths()), + arrow_data.validity(), + ) + .map(|elem| { + elem.map(|(start, len)| { + let start = *start as usize; + let end = start + len; + if end as usize > arrow_data_buf.len() { + return Err(DeserializationError::offset_slice_oob( + (start, end), + arrow_data_buf.len(), + )); + } + + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + let data = + unsafe { arrow_data_buf.clone().sliced_unchecked(start, len) }; + Ok(data) + }) + .transpose() + }) + .map(|res_or_opt| { + res_or_opt.map(|res_or_opt| { + res_or_opt.map(|v| ::re_types_core::ArrowString(v)) + }) + }) + .collect::>>>() + .with_context("rerun.blueprint.SpaceViewComponent#display_name")? + .into_iter() + } + }; + let class_name = { + if !arrays_by_name.contains_key("class_name") { + return Err(DeserializationError::missing_struct_field( + Self::arrow_datatype(), + "class_name", + )) + .with_context("rerun.blueprint.SpaceViewComponent"); + } + let arrow_data = &**arrays_by_name["class_name"]; + { + let arrow_data = arrow_data + .as_any() + .downcast_ref::>() + .ok_or_else(|| { + DeserializationError::datatype_mismatch( + DataType::Utf8, + arrow_data.data_type().clone(), + ) + }) + .with_context("rerun.blueprint.SpaceViewComponent#class_name")?; + let arrow_data_buf = arrow_data.values(); + let offsets = arrow_data.offsets(); + arrow2::bitmap::utils::ZipValidity::new_with_validity( + offsets.iter().zip(offsets.lengths()), + arrow_data.validity(), + ) + .map(|elem| { + elem.map(|(start, len)| { + let start = *start as usize; + let end = start + len; + if end as usize > arrow_data_buf.len() { + return Err(DeserializationError::offset_slice_oob( + (start, end), + arrow_data_buf.len(), + )); + } + + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + let data = + unsafe { arrow_data_buf.clone().sliced_unchecked(start, len) }; + Ok(data) + }) + .transpose() + }) + .map(|res_or_opt| { + res_or_opt.map(|res_or_opt| { + res_or_opt.map(|v| ::re_types_core::ArrowString(v)) + }) + }) + .collect::>>>() + .with_context("rerun.blueprint.SpaceViewComponent#class_name")? + .into_iter() + } + }; + let space_origin = { + if !arrays_by_name.contains_key("space_origin") { + return Err(DeserializationError::missing_struct_field( + Self::arrow_datatype(), + "space_origin", + )) + .with_context("rerun.blueprint.SpaceViewComponent"); + } + let arrow_data = &**arrays_by_name["space_origin"]; + { + let arrow_data = arrow_data + .as_any() + .downcast_ref::>() + .ok_or_else(|| { + DeserializationError::datatype_mismatch( + DataType::Utf8, + arrow_data.data_type().clone(), + ) + }) + .with_context("rerun.blueprint.SpaceViewComponent#space_origin")?; + let arrow_data_buf = arrow_data.values(); + let offsets = arrow_data.offsets(); + arrow2::bitmap::utils::ZipValidity::new_with_validity( + offsets.iter().zip(offsets.lengths()), + arrow_data.validity(), + ) + .map(|elem| { + elem.map(|(start, len)| { + let start = *start as usize; + let end = start + len; + if end as usize > arrow_data_buf.len() { + return Err(DeserializationError::offset_slice_oob( + (start, end), + arrow_data_buf.len(), + )); + } + + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + let data = + unsafe { arrow_data_buf.clone().sliced_unchecked(start, len) }; + Ok(data) + }) + .transpose() + }) + .map(|res_or_opt| { + res_or_opt.map(|res_or_opt| { + res_or_opt.map(|v| { + crate::datatypes::EntityPath(::re_types_core::ArrowString(v)) + }) + }) + }) + .collect::>>>() + .with_context("rerun.blueprint.SpaceViewComponent#space_origin")? + .into_iter() + } + }; + let entities_determined_by_user = { + if !arrays_by_name.contains_key("entities_determined_by_user") { + return Err(DeserializationError::missing_struct_field( + Self::arrow_datatype(), + "entities_determined_by_user", + )) + .with_context("rerun.blueprint.SpaceViewComponent"); + } + let arrow_data = &**arrays_by_name["entities_determined_by_user"]; + arrow_data + .as_any() + .downcast_ref::() + .ok_or_else(|| { + DeserializationError::datatype_mismatch( + DataType::Boolean, + arrow_data.data_type().clone(), + ) + }) + .with_context( + "rerun.blueprint.SpaceViewComponent#entities_determined_by_user", + )? + .into_iter() + }; + let contents = { + if !arrays_by_name.contains_key("contents") { + return Err(DeserializationError::missing_struct_field( + Self::arrow_datatype(), + "contents", + )) + .with_context("rerun.blueprint.SpaceViewComponent"); + } + let arrow_data = &**arrays_by_name["contents"]; + { + let arrow_data = arrow_data + .as_any() + .downcast_ref::>() + .ok_or_else(|| { + DeserializationError::datatype_mismatch( + DataType::List(Box::new(Field { + name: "item".to_owned(), + data_type: ::arrow_datatype(), + is_nullable: false, + metadata: [].into(), + })), + arrow_data.data_type().clone(), + ) + }) + .with_context("rerun.blueprint.SpaceViewComponent#contents")?; + if arrow_data.is_empty() { + Vec::new() + } else { + let arrow_data_inner = { + let arrow_data_inner = &**arrow_data.values(); + crate::datatypes::Uuid::from_arrow_opt(arrow_data_inner) + .with_context("rerun.blueprint.SpaceViewComponent#contents")? + .into_iter() + .collect::>() + }; + let offsets = arrow_data.offsets(); + arrow2::bitmap::utils::ZipValidity::new_with_validity( + offsets.iter().zip(offsets.lengths()), + arrow_data.validity(), + ) + .map(|elem| { + elem.map(|(start, len)| { + let start = *start as usize; + let end = start + len; + if end as usize > arrow_data_inner.len() { + return Err(DeserializationError::offset_slice_oob( + (start, end), + arrow_data_inner.len(), + )); + } + + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + let data = unsafe { + arrow_data_inner.get_unchecked(start as usize..end as usize) + }; + let data = data + .iter() + .cloned() + .map(Option::unwrap_or_default) + .collect(); + Ok(data) + }) + .transpose() + }) + .collect::>>>()? + } + .into_iter() + } + }; + arrow2::bitmap::utils::ZipValidity::new_with_validity( + ::itertools::izip!( + display_name, class_name, space_origin, + entities_determined_by_user, contents + ), + arrow_data.validity(), + ) + .map(|opt| { + opt + .map(| + ( + display_name, + class_name, + space_origin, + entities_determined_by_user, + contents, + )| + Ok(Self { + display_name: display_name + .ok_or_else(DeserializationError::missing_data) + .with_context( + "rerun.blueprint.SpaceViewComponent#display_name", + )?, + class_name: class_name + .ok_or_else(DeserializationError::missing_data) + .with_context( + "rerun.blueprint.SpaceViewComponent#class_name", + )?, + space_origin: space_origin + .ok_or_else(DeserializationError::missing_data) + .with_context( + "rerun.blueprint.SpaceViewComponent#space_origin", + )?, + entities_determined_by_user: entities_determined_by_user + .ok_or_else(DeserializationError::missing_data) + .with_context( + "rerun.blueprint.SpaceViewComponent#entities_determined_by_user", + )?, + contents: contents + .ok_or_else(DeserializationError::missing_data) + .with_context( + "rerun.blueprint.SpaceViewComponent#contents", + )?, + })) + .transpose() + }) + .collect::>>() + .with_context("rerun.blueprint.SpaceViewComponent")? + } + }) + } +} diff --git a/crates/re_types/src/blueprint/space_view_component_ext.rs b/crates/re_types/src/blueprint/space_view_component_ext.rs new file mode 100644 index 000000000000..6b5f36073085 --- /dev/null +++ b/crates/re_types/src/blueprint/space_view_component_ext.rs @@ -0,0 +1,7 @@ +use super::SpaceViewComponent; + +impl std::fmt::Debug for SpaceViewComponent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "SpaceViewComponent") + } +} diff --git a/crates/re_types/src/datatypes/.gitattributes b/crates/re_types/src/datatypes/.gitattributes index 77abd8451886..ecf405502f49 100644 --- a/crates/re_types/src/datatypes/.gitattributes +++ b/crates/re_types/src/datatypes/.gitattributes @@ -24,6 +24,7 @@ tensor_dimension.rs linguist-generated=true transform3d.rs linguist-generated=true translation_and_mat3x3.rs linguist-generated=true translation_rotation_scale3d.rs linguist-generated=true +uuid.rs linguist-generated=true uvec2d.rs linguist-generated=true uvec3d.rs linguist-generated=true uvec4d.rs linguist-generated=true diff --git a/crates/re_types/src/datatypes/mod.rs b/crates/re_types/src/datatypes/mod.rs index 85e43430e5b9..46afd51bc751 100644 --- a/crates/re_types/src/datatypes/mod.rs +++ b/crates/re_types/src/datatypes/mod.rs @@ -43,6 +43,8 @@ mod translation_and_mat3x3; mod translation_and_mat3x3_ext; mod translation_rotation_scale3d; mod translation_rotation_scale3d_ext; +mod uuid; +mod uuid_ext; mod uvec2d; mod uvec2d_ext; mod uvec3d; @@ -78,6 +80,7 @@ pub use self::tensor_dimension::TensorDimension; pub use self::transform3d::Transform3D; pub use self::translation_and_mat3x3::TranslationAndMat3x3; pub use self::translation_rotation_scale3d::TranslationRotationScale3D; +pub use self::uuid::Uuid; pub use self::uvec2d::UVec2D; pub use self::uvec3d::UVec3D; pub use self::uvec4d::UVec4D; diff --git a/crates/re_viewport/src/blueprint/space_view_component.rs b/crates/re_types/src/datatypes/uuid.rs similarity index 53% rename from crates/re_viewport/src/blueprint/space_view_component.rs rename to crates/re_types/src/datatypes/uuid.rs index 2dfaf058511e..dae46f2984f1 100644 --- a/crates/re_viewport/src/blueprint/space_view_component.rs +++ b/crates/re_types/src/datatypes/uuid.rs @@ -1,5 +1,5 @@ // DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/rust/api.rs -// Based on "crates/re_types/definitions/rerun/blueprint/space_view_component.fbs". +// Based on "crates/re_types/definitions/rerun/datatypes/uuid.fbs". #![allow(trivial_numeric_casts)] #![allow(unused_imports)] @@ -21,36 +21,35 @@ use ::re_types_core::SerializationResult; use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; use ::re_types_core::{DeserializationError, DeserializationResult}; -/// **Blueprint**: A view of a space. -/// -/// Unstable. Used for the ongoing blueprint experimentations. -#[derive(Clone)] -pub struct SpaceViewComponent { - pub space_view: crate::SpaceViewBlueprint, +/// **Datatype**: A 16-byte uuid. +#[derive(Clone, Debug, Default)] +#[repr(transparent)] +pub struct Uuid { + pub bytes: [u8; 16usize], } -impl From for SpaceViewComponent { +impl From<[u8; 16usize]> for Uuid { #[inline] - fn from(space_view: crate::SpaceViewBlueprint) -> Self { - Self { space_view } + fn from(bytes: [u8; 16usize]) -> Self { + Self { bytes } } } -impl From for crate::SpaceViewBlueprint { +impl From for [u8; 16usize] { #[inline] - fn from(value: SpaceViewComponent) -> Self { - value.space_view + fn from(value: Uuid) -> Self { + value.bytes } } -::re_types_core::macros::impl_into_cow!(SpaceViewComponent); +::re_types_core::macros::impl_into_cow!(Uuid); -impl ::re_types_core::Loggable for SpaceViewComponent { - type Name = ::re_types_core::ComponentName; +impl ::re_types_core::Loggable for Uuid { + type Name = ::re_types_core::DatatypeName; #[inline] fn name() -> Self::Name { - "rerun.blueprint.SpaceViewComponent".into() + "rerun.datatypes.Uuid".into() } #[allow(clippy::wildcard_imports)] @@ -58,13 +57,16 @@ impl ::re_types_core::Loggable for SpaceViewComponent { fn arrow_datatype() -> arrow2::datatypes::DataType { use arrow2::datatypes::*; DataType::Struct(vec![Field { - name: "space_view".to_owned(), - data_type: DataType::List(Box::new(Field { - name: "item".to_owned(), - data_type: DataType::UInt8, - is_nullable: false, - metadata: [].into(), - })), + name: "bytes".to_owned(), + data_type: DataType::FixedSizeList( + Box::new(Field { + name: "item".to_owned(), + data_type: DataType::UInt8, + is_nullable: false, + metadata: [].into(), + }), + 16usize, + ), is_nullable: false, metadata: [].into(), }]) @@ -93,70 +95,60 @@ impl ::re_types_core::Loggable for SpaceViewComponent { any_nones.then(|| somes.into()) }; StructArray::new( - ::arrow_datatype(), + ::arrow_datatype(), vec![{ - let (somes, space_view): (Vec<_>, Vec<_>) = data + let (somes, bytes): (Vec<_>, Vec<_>) = data .iter() .map(|datum| { let datum = datum.as_ref().map(|datum| { - let Self { space_view, .. } = &**datum; - space_view.clone() + let Self { bytes, .. } = &**datum; + bytes.clone() }); (datum.is_some(), datum) }) .unzip(); - let space_view_bitmap: Option = { + let bytes_bitmap: Option = { let any_nones = somes.iter().any(|some| !*some); any_nones.then(|| somes.into()) }; { use arrow2::{buffer::Buffer, offset::OffsetsBuffer}; - let buffers: Vec>> = space_view + let bytes_inner_data: Vec<_> = bytes .iter() - .map(|opt| { - use ::re_types_core::SerializationError; - opt.as_ref() - .map(|b| { - let mut buf = Vec::new(); - rmp_serde::encode::write_named(&mut buf, b).map_err( - |err| { - SerializationError::serde_failure(err.to_string()) - }, - )?; - Ok(buf) - }) - .transpose() - }) - .collect::>>()?; - let offsets = arrow2::offset::Offsets::::try_from_lengths( - buffers - .iter() - .map(|opt| opt.as_ref().map(|buf| buf.len()).unwrap_or_default()), - ) - .unwrap() - .into(); - let space_view_inner_bitmap: Option = None; - let space_view_inner_data: Buffer = buffers - .into_iter() .flatten() - .collect::>() - .concat() - .into(); - ListArray::new( - DataType::List(Box::new(Field { - name: "item".to_owned(), - data_type: DataType::UInt8, - is_nullable: false, - metadata: [].into(), - })), - offsets, + .flatten() + .cloned() + .map(Some) + .collect(); + let bytes_inner_bitmap: Option = + bytes_bitmap.as_ref().map(|bitmap| { + bitmap + .iter() + .map(|i| std::iter::repeat(i).take(16usize)) + .flatten() + .collect::>() + .into() + }); + FixedSizeListArray::new( + DataType::FixedSizeList( + Box::new(Field { + name: "item".to_owned(), + data_type: DataType::UInt8, + is_nullable: false, + metadata: [].into(), + }), + 16usize, + ), PrimitiveArray::new( DataType::UInt8, - space_view_inner_data, - space_view_inner_bitmap, + bytes_inner_data + .into_iter() + .map(|v| v.unwrap_or_default()) + .collect(), + bytes_inner_bitmap, ) .boxed(), - space_view_bitmap, + bytes_bitmap, ) .boxed() } @@ -184,20 +176,23 @@ impl ::re_types_core::Loggable for SpaceViewComponent { .ok_or_else(|| { DeserializationError::datatype_mismatch( DataType::Struct(vec![Field { - name: "space_view".to_owned(), - data_type: DataType::List(Box::new(Field { - name: "item".to_owned(), - data_type: DataType::UInt8, - is_nullable: false, - metadata: [].into(), - })), + name: "bytes".to_owned(), + data_type: DataType::FixedSizeList( + Box::new(Field { + name: "item".to_owned(), + data_type: DataType::UInt8, + is_nullable: false, + metadata: [].into(), + }), + 16usize, + ), is_nullable: false, metadata: [].into(), }]), arrow_data.data_type().clone(), ) }) - .with_context("rerun.blueprint.SpaceViewComponent")?; + .with_context("rerun.datatypes.Uuid")?; if arrow_data.is_empty() { Vec::new() } else { @@ -208,34 +203,40 @@ impl ::re_types_core::Loggable for SpaceViewComponent { .map(|field| field.name.as_str()) .zip(arrow_data_arrays) .collect(); - let space_view = { - if !arrays_by_name.contains_key("space_view") { + let bytes = { + if !arrays_by_name.contains_key("bytes") { return Err(DeserializationError::missing_struct_field( Self::arrow_datatype(), - "space_view", + "bytes", )) - .with_context("rerun.blueprint.SpaceViewComponent"); + .with_context("rerun.datatypes.Uuid"); } - let arrow_data = &**arrays_by_name["space_view"]; + let arrow_data = &**arrays_by_name["bytes"]; { let arrow_data = arrow_data .as_any() - .downcast_ref::>() + .downcast_ref::() .ok_or_else(|| { DeserializationError::datatype_mismatch( - DataType::List(Box::new(Field { - name: "item".to_owned(), - data_type: DataType::UInt8, - is_nullable: false, - metadata: [].into(), - })), + DataType::FixedSizeList( + Box::new(Field { + name: "item".to_owned(), + data_type: DataType::UInt8, + is_nullable: false, + metadata: [].into(), + }), + 16usize, + ), arrow_data.data_type().clone(), ) }) - .with_context("rerun.blueprint.SpaceViewComponent#space_view")?; + .with_context("rerun.datatypes.Uuid#bytes")?; if arrow_data.is_empty() { Vec::new() } else { + let offsets = (0..) + .step_by(16usize) + .zip((16usize..).step_by(16usize).take(arrow_data.len())); let arrow_data_inner = { let arrow_data_inner = &**arrow_data.values(); arrow_data_inner @@ -247,18 +248,18 @@ impl ::re_types_core::Loggable for SpaceViewComponent { arrow_data_inner.data_type().clone(), ) }) - .with_context("rerun.blueprint.SpaceViewComponent#space_view")? - .values() + .with_context("rerun.datatypes.Uuid#bytes")? + .into_iter() + .map(|opt| opt.copied()) + .collect::>() }; - let offsets = arrow_data.offsets(); arrow2::bitmap::utils::ZipValidity::new_with_validity( - offsets.iter().zip(offsets.lengths()), + offsets, arrow_data.validity(), ) .map(|elem| { - elem.map(|(start, len)| { - let start = *start as usize; - let end = start + len; + elem.map(|(start, end)| { + debug_assert!(end - start == 16usize); if end as usize > arrow_data_inner.len() { return Err(DeserializationError::offset_slice_oob( (start, end), @@ -268,17 +269,11 @@ impl ::re_types_core::Loggable for SpaceViewComponent { #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] let data = unsafe { - arrow_data_inner - .clone() - .sliced_unchecked(start as usize, end - start as usize) + arrow_data_inner.get_unchecked(start as usize..end as usize) }; - let data = rmp_serde::from_slice::( - data.as_slice(), - ) - .map_err(|err| { - DeserializationError::serde_failure(err.to_string()) - })?; - Ok(data) + let data = data.iter().cloned().map(Option::unwrap_or_default); + let arr = array_init::from_iter(data).unwrap(); + Ok(arr) }) .transpose() }) @@ -288,23 +283,21 @@ impl ::re_types_core::Loggable for SpaceViewComponent { } }; arrow2::bitmap::utils::ZipValidity::new_with_validity( - ::itertools::izip!(space_view), + ::itertools::izip!(bytes), arrow_data.validity(), ) .map(|opt| { - opt.map(|(space_view)| { + opt.map(|(bytes)| { Ok(Self { - space_view: space_view + bytes: bytes .ok_or_else(DeserializationError::missing_data) - .with_context( - "rerun.blueprint.SpaceViewComponent#space_view", - )?, + .with_context("rerun.datatypes.Uuid#bytes")?, }) }) .transpose() }) .collect::>>() - .with_context("rerun.blueprint.SpaceViewComponent")? + .with_context("rerun.datatypes.Uuid")? } }) } diff --git a/crates/re_types/src/datatypes/uuid_ext.rs b/crates/re_types/src/datatypes/uuid_ext.rs new file mode 100644 index 000000000000..59c579b4df76 --- /dev/null +++ b/crates/re_types/src/datatypes/uuid_ext.rs @@ -0,0 +1,30 @@ +use super::Uuid; + +impl From for uuid::Uuid { + #[inline] + fn from(uuid: Uuid) -> Self { + uuid::Uuid::from_bytes(uuid.bytes) + } +} + +impl From for Uuid { + #[inline] + fn from(uuid: uuid::Uuid) -> Self { + Self { + bytes: *uuid.as_bytes(), + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_uuid() { + let uuid = uuid::Uuid::new_v4(); + + let uuid_datatype: super::Uuid = uuid.into(); + + let uuid_roundtrip: uuid::Uuid = uuid_datatype.into(); + assert_eq!(uuid, uuid_roundtrip); + } +} diff --git a/crates/re_types/src/lib.rs b/crates/re_types/src/lib.rs index ebc97e6671d7..d59bc5c6e5cb 100644 --- a/crates/re_types/src/lib.rs +++ b/crates/re_types/src/lib.rs @@ -245,6 +245,9 @@ pub mod datatypes { pub use re_types_core::datatypes::*; } +/// The blueprint-specific components. +pub mod blueprint; + #[cfg(feature = "datagen")] pub mod datagen; diff --git a/crates/re_types_builder/src/codegen/docs/mod.rs b/crates/re_types_builder/src/codegen/docs/mod.rs index c2e68ecf9683..94ce7de45de0 100644 --- a/crates/re_types_builder/src/codegen/docs/mod.rs +++ b/crates/re_types_builder/src/codegen/docs/mod.rs @@ -258,11 +258,18 @@ fn write_used_by(o: &mut String, reporter: &Reporter, object: &Object, object_ma for ty in object_map.values() { for field in &ty.fields { if field.typ.fqname() == Some(object.fqname.as_str()) { + let is_unreleased = ty.is_attr_set(crate::ATTR_DOCS_UNRELEASED); + let speculative_marker = if is_unreleased { + "?speculative-link" + } else { + "" + }; used_by.push(format!( - "* [`{}`](../{}/{}.md)", + "* [`{}`](../{}/{}.md{})", ty.name, ty.kind.plural_snake_case(), - ty.snake_case_name() + ty.snake_case_name(), + speculative_marker )); } } diff --git a/crates/re_types_core/src/datatypes/.gitattributes b/crates/re_types_core/src/datatypes/.gitattributes index 5c6cfc18298d..570fad02cada 100644 --- a/crates/re_types_core/src/datatypes/.gitattributes +++ b/crates/re_types_core/src/datatypes/.gitattributes @@ -1,6 +1,7 @@ # DO NOT EDIT! This file is generated by crates/re_types_builder/src/lib.rs .gitattributes linguist-generated=true +entity_path.rs linguist-generated=true float32.rs linguist-generated=true mod.rs linguist-generated=true uint32.rs linguist-generated=true diff --git a/crates/re_types_core/src/datatypes/entity_path.rs b/crates/re_types_core/src/datatypes/entity_path.rs new file mode 100644 index 000000000000..893a2d8a74e7 --- /dev/null +++ b/crates/re_types_core/src/datatypes/entity_path.rs @@ -0,0 +1,168 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/rust/api.rs +// Based on "crates/re_types/definitions/rerun/datatypes/entity_path.fbs". + +#![allow(trivial_numeric_casts)] +#![allow(unused_imports)] +#![allow(unused_parens)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::iter_on_single_items)] +#![allow(clippy::map_flatten)] +#![allow(clippy::match_wildcard_for_single_variants)] +#![allow(clippy::needless_question_mark)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::unnecessary_cast)] + +use crate::external::arrow2; +use crate::ComponentName; +use crate::SerializationResult; +use crate::{ComponentBatch, MaybeOwnedComponentBatch}; +use crate::{DeserializationError, DeserializationResult}; + +/// **Datatype**: A path to an entity in the `DataStore`. +#[derive(Clone, Debug, Default)] +#[repr(transparent)] +pub struct EntityPath(pub crate::ArrowString); + +impl From for EntityPath { + #[inline] + fn from(path: crate::ArrowString) -> Self { + Self(path) + } +} + +impl From for crate::ArrowString { + #[inline] + fn from(value: EntityPath) -> Self { + value.0 + } +} + +crate::macros::impl_into_cow!(EntityPath); + +impl crate::Loggable for EntityPath { + type Name = crate::DatatypeName; + + #[inline] + fn name() -> Self::Name { + "rerun.datatypes.EntityPath".into() + } + + #[allow(clippy::wildcard_imports)] + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + use arrow2::datatypes::*; + DataType::Utf8 + } + + #[allow(clippy::wildcard_imports)] + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + re_tracing::profile_function!(); + use crate::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, datatypes::*}; + Ok({ + let (somes, data0): (Vec<_>, Vec<_>) = data + .into_iter() + .map(|datum| { + let datum: Option<::std::borrow::Cow<'a, Self>> = datum.map(Into::into); + let datum = datum.map(|datum| { + let Self(data0) = datum.into_owned(); + data0 + }); + (datum.is_some(), datum) + }) + .unzip(); + let data0_bitmap: Option = { + let any_nones = somes.iter().any(|some| !*some); + any_nones.then(|| somes.into()) + }; + { + let inner_data: arrow2::buffer::Buffer = + data0.iter().flatten().flat_map(|s| s.0.clone()).collect(); + let offsets = arrow2::offset::Offsets::::try_from_lengths( + data0 + .iter() + .map(|opt| opt.as_ref().map(|datum| datum.0.len()).unwrap_or_default()), + ) + .unwrap() + .into(); + + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + unsafe { + Utf8Array::::new_unchecked( + Self::arrow_datatype(), + offsets, + inner_data, + data0_bitmap, + ) + } + .boxed() + } + }) + } + + #[allow(clippy::wildcard_imports)] + fn from_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + re_tracing::profile_function!(); + use crate::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, buffer::*, datatypes::*}; + Ok({ + let arrow_data = arrow_data + .as_any() + .downcast_ref::>() + .ok_or_else(|| { + DeserializationError::datatype_mismatch( + DataType::Utf8, + arrow_data.data_type().clone(), + ) + }) + .with_context("rerun.datatypes.EntityPath#path")?; + let arrow_data_buf = arrow_data.values(); + let offsets = arrow_data.offsets(); + arrow2::bitmap::utils::ZipValidity::new_with_validity( + offsets.iter().zip(offsets.lengths()), + arrow_data.validity(), + ) + .map(|elem| { + elem.map(|(start, len)| { + let start = *start as usize; + let end = start + len; + if end as usize > arrow_data_buf.len() { + return Err(DeserializationError::offset_slice_oob( + (start, end), + arrow_data_buf.len(), + )); + } + + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + let data = unsafe { arrow_data_buf.clone().sliced_unchecked(start, len) }; + Ok(data) + }) + .transpose() + }) + .map(|res_or_opt| { + res_or_opt.map(|res_or_opt| res_or_opt.map(|v| crate::ArrowString(v))) + }) + .collect::>>>() + .with_context("rerun.datatypes.EntityPath#path")? + .into_iter() + } + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .map(|res| res.map(|v| Some(Self(v)))) + .collect::>>>() + .with_context("rerun.datatypes.EntityPath#path") + .with_context("rerun.datatypes.EntityPath")?) + } +} diff --git a/crates/re_types_core/src/datatypes/mod.rs b/crates/re_types_core/src/datatypes/mod.rs index 793e2a405cfc..5d1bbe547bb5 100644 --- a/crates/re_types_core/src/datatypes/mod.rs +++ b/crates/re_types_core/src/datatypes/mod.rs @@ -1,10 +1,12 @@ // DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/rust/api.rs +mod entity_path; mod float32; mod uint32; mod utf8; mod utf8_ext; +pub use self::entity_path::EntityPath; pub use self::float32::Float32; pub use self::uint32::UInt32; pub use self::utf8::Utf8; diff --git a/crates/re_viewer/src/app_state.rs b/crates/re_viewer/src/app_state.rs index 3d309deb87b8..2ceec4bf5eec 100644 --- a/crates/re_viewer/src/app_state.rs +++ b/crates/re_viewer/src/app_state.rs @@ -122,15 +122,19 @@ impl AppState { .blueprint .space_views .values_mut() - .map(|space_view| { - ( - space_view.query_id(), - space_view.contents.execute_query( - space_view, - store_context, - &entities_per_system_per_class, - ), - ) + .flat_map(|space_view| { + space_view.queries.iter().map(|query| { + let resolver = + query.build_resolver(space_view.id, &space_view.auto_properties); + ( + query.id, + query.execute_query( + &resolver, + store_context, + &entities_per_system_per_class, + ), + ) + }) }) .collect::<_>() }; @@ -172,15 +176,19 @@ impl AppState { .blueprint .space_views .values_mut() - .map(|space_view| { - ( - space_view.query_id(), - space_view.contents.execute_query( - space_view, - store_context, - &entities_per_system_per_class, - ), - ) + .flat_map(|space_view| { + space_view.queries.iter().map(|query| { + let resolver = + query.build_resolver(space_view.id, &space_view.auto_properties); + ( + query.id, + query.execute_query( + &resolver, + store_context, + &entities_per_system_per_class, + ), + ) + }) }) .collect::<_>() }; diff --git a/crates/re_viewer/src/blueprint_validation.rs b/crates/re_viewer/src/blueprint_validation.rs index 4d031038a0fd..57dccdcde33b 100644 --- a/crates/re_viewer/src/blueprint_validation.rs +++ b/crates/re_viewer/src/blueprint_validation.rs @@ -1,9 +1,10 @@ use re_arrow_store::LatestAtQuery; use re_data_store::{EntityPropertiesComponent, StoreDb}; use re_log_types::Timeline; +use re_types::blueprint::SpaceViewComponent; use re_types_core::Component; use re_viewport::{ - blueprint::{AutoSpaceViews, SpaceViewComponent, SpaceViewMaximized, ViewportLayout}, + blueprint::{AutoSpaceViews, SpaceViewMaximized, ViewportLayout}, external::re_space_view::QueryExpressions, }; diff --git a/crates/re_viewer/src/ui/selection_history_ui.rs b/crates/re_viewer/src/ui/selection_history_ui.rs index 9216f69074c1..062ea5bf7f1a 100644 --- a/crates/re_viewer/src/ui/selection_history_ui.rs +++ b/crates/re_viewer/src/ui/selection_history_ui.rs @@ -177,17 +177,7 @@ fn item_to_string(blueprint: &ViewportBlueprint<'_>, item: &Item) -> String { } } Item::InstancePath(_, entity_path) => entity_path.to_string(), - Item::DataBlueprintGroup(sid, handle) => { - if let Some(space_view) = blueprint.space_view(sid) { - if let Some(group) = space_view.contents.group(*handle) { - group.display_name.clone() - } else { - format!("", space_view.display_name) - } - } else { - "".to_owned() - } - } + Item::DataBlueprintGroup(_sid, _qid, entity_path) => entity_path.to_string(), Item::ComponentPath(path) => { format!("{} {}", path.entity_path, path.component_name.short_name(),) } diff --git a/crates/re_viewer/src/ui/selection_panel.rs b/crates/re_viewer/src/ui/selection_panel.rs index 359a223f257f..236f7b79550f 100644 --- a/crates/re_viewer/src/ui/selection_panel.rs +++ b/crates/re_viewer/src/ui/selection_panel.rs @@ -4,6 +4,7 @@ use re_data_store::{ ColorMapper, Colormap, EditableAutoValue, EntityPath, EntityProperties, VisibleHistory, }; use re_data_ui::{image_meaning_for_entity, item_ui, DataUi}; +use re_log_types::{DataRow, RowId, TimePoint}; use re_space_view_time_series::TimeSeriesSpaceView; use re_types::{ components::{PinholeProjection, Transform3D}, @@ -12,10 +13,10 @@ use re_types::{ use re_ui::list_item::ListItem; use re_ui::ReUi; use re_viewer_context::{ - gpu_bridge::colormap_dropdown_button_ui, Item, SpaceViewClassName, SpaceViewId, UiVerbosity, - ViewerContext, + gpu_bridge::colormap_dropdown_button_ui, Item, SpaceViewClassName, SpaceViewId, SystemCommand, + SystemCommandSender as _, UiVerbosity, ViewerContext, }; -use re_viewport::{Viewport, ViewportBlueprint}; +use re_viewport::{external::re_space_view::QueryExpressions, Viewport, ViewportBlueprint}; use crate::ui::visible_history::visible_history_ui; @@ -149,7 +150,7 @@ fn has_data_section(item: &Item) -> bool { match item { Item::ComponentPath(_) | Item::InstancePath(_, _) => true, // Skip data ui since we don't know yet what to show for these. - Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => false, + Item::SpaceView(_) | Item::DataBlueprintGroup(_, _, _) => false, } } @@ -259,22 +260,23 @@ fn what_is_selected_ui( list_existing_data_blueprints(ui, ctx, &instance_path.entity_path, viewport); } } - Item::DataBlueprintGroup(space_view_id, data_blueprint_group_handle) => { + Item::DataBlueprintGroup(space_view_id, _query_id, entity_path) => { if let Some(space_view) = viewport.space_view(space_view_id) { - if let Some(group) = space_view.contents.group(*data_blueprint_group_handle) { - item_title_ui( - ctx.re_ui, - ui, - group.display_name.as_str(), - Some(&re_ui::icons::CONTAINER), - &format!("Group {:?}", group.display_name), - ); + item_title_ui( + ctx.re_ui, + ui, + &entity_path.to_string(), + Some(&re_ui::icons::CONTAINER), + &format!( + "Group {:?} as shown in Space View {:?}", + entity_path, space_view.display_name + ), + ); - ui.horizontal(|ui| { - ui.label("in"); - space_view_button(ctx, ui, space_view); - }); - } + ui.horizontal(|ui| { + ui.label("in"); + space_view_button(ctx, ui, space_view); + }); } } } @@ -306,7 +308,7 @@ fn list_existing_data_blueprints( entity_path: &EntityPath, blueprint: &ViewportBlueprint<'_>, ) { - let space_views_with_path = blueprint.space_views_containing_entity_path(entity_path); + let space_views_with_path = blueprint.space_views_containing_entity_path(ctx, entity_path); if space_views_with_path.is_empty() { ui.weak("(Not shown in any Space View)"); @@ -413,6 +415,38 @@ fn blueprint_ui( } }); + if let Some(space_view) = viewport.blueprint.space_view(space_view_id) { + if let Some(query) = space_view.queries.first() { + let expressions = query.expressions.expressions.join("\n"); + let mut edited_expressions = expressions.clone(); + + ui.text_edit_multiline(&mut edited_expressions); + + if edited_expressions != expressions { + let timepoint = TimePoint::timeless(); + + let expressions_component = QueryExpressions { + expressions: edited_expressions.split('\n').map(|s| s.into()).collect(), + }; + + let row = DataRow::from_cells1_sized( + RowId::random(), + query.id.as_entity_path(), + timepoint.clone(), + 1, + [expressions_component], + ) + .unwrap(); + + ctx.command_sender + .send_system(SystemCommand::UpdateBlueprint( + ctx.store_context.blueprint.store_id().clone(), + vec![row], + )); + } + } + } + ui.add_space(ui.spacing().item_spacing.y); if let Some(space_view) = viewport.blueprint.space_view_mut(space_view_id) { @@ -516,37 +550,34 @@ fn blueprint_ui( } } - Item::DataBlueprintGroup(space_view_id, data_blueprint_group_handle) => { + Item::DataBlueprintGroup(space_view_id, query_id, group_path) => { if let Some(space_view) = viewport.blueprint.space_view_mut(space_view_id) { - if let Some(group) = space_view.contents.group_mut(*data_blueprint_group_handle) { - let group_path = group.group_path.clone(); - let as_group = true; - - let query_result = ctx.lookup_query_result(space_view.query_id()); - if let Some(data_result) = query_result - .tree - .lookup_result_by_path_and_group(&group_path, as_group) - .cloned() - { - let space_view_class = *space_view.class_name(); - let mut props = data_result - .individual_properties - .clone() - .unwrap_or_default(); + let as_group = true; - entity_props_ui( - ctx, - ui, - &space_view_class, - None, - &mut props, - &data_result.resolved_properties, - ); - data_result.save_override(Some(props), ctx); - } - } else { - ctx.selection_state_mut().clear_current(); + let query_result = ctx.lookup_query_result(*query_id); + if let Some(data_result) = query_result + .tree + .lookup_result_by_path_and_group(group_path, as_group) + .cloned() + { + let space_view_class = *space_view.class_name(); + let mut props = data_result + .individual_properties + .clone() + .unwrap_or_default(); + + entity_props_ui( + ctx, + ui, + &space_view_class, + None, + &mut props, + &data_result.resolved_properties, + ); + data_result.save_override(Some(props), ctx); } + } else { + ctx.selection_state_mut().clear_current(); } } diff --git a/crates/re_viewer_context/src/blueprint_id.rs b/crates/re_viewer_context/src/blueprint_id.rs index 260e73865d50..718f2a215188 100644 --- a/crates/re_viewer_context/src/blueprint_id.rs +++ b/crates/re_viewer_context/src/blueprint_id.rs @@ -104,6 +104,23 @@ impl From for BlueprintId { } } +impl From for BlueprintId { + #[inline] + fn from(id: re_types::datatypes::Uuid) -> Self { + Self { + id: id.into(), + _registry: std::marker::PhantomData, + } + } +} + +impl From> for re_types::datatypes::Uuid { + #[inline] + fn from(id: BlueprintId) -> Self { + id.id.into() + } +} + impl std::fmt::Display for BlueprintId { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/crates/re_viewer_context/src/item.rs b/crates/re_viewer_context/src/item.rs index 090675291815..8e02f2c3c289 100644 --- a/crates/re_viewer_context/src/item.rs +++ b/crates/re_viewer_context/src/item.rs @@ -3,7 +3,9 @@ use itertools::Itertools as _; use re_data_store::InstancePath; use re_log_types::{ComponentPath, DataPath, EntityPath}; -use super::{DataBlueprintGroupHandle, SpaceViewId}; +use crate::DataQueryId; + +use super::SpaceViewId; /// One "thing" in the UI. /// @@ -15,7 +17,7 @@ pub enum Item { ComponentPath(ComponentPath), SpaceView(SpaceViewId), InstancePath(Option, InstancePath), - DataBlueprintGroup(SpaceViewId, DataBlueprintGroupHandle), + DataBlueprintGroup(SpaceViewId, DataQueryId, EntityPath), } impl From for Item { @@ -85,7 +87,9 @@ impl std::fmt::Debug for Item { Item::ComponentPath(s) => s.fmt(f), Item::SpaceView(s) => write!(f, "{s:?}"), Item::InstancePath(sid, path) => write!(f, "({sid:?}, {path})"), - Item::DataBlueprintGroup(sid, handle) => write!(f, "({sid:?}, {handle:?})"), + Item::DataBlueprintGroup(sid, qid, entity_path) => { + write!(f, "({sid:?}, {qid:?}, {entity_path:?})") + } } } } @@ -106,7 +110,7 @@ impl Item { } Item::ComponentPath(_) => "Entity Component", Item::SpaceView(_) => "Space View", - Item::DataBlueprintGroup(_, _) => "Group", + Item::DataBlueprintGroup(_, _, _) => "Group", } } } @@ -195,7 +199,7 @@ pub fn resolve_mono_instance_path_item( *space_view, resolve_mono_instance_path(query, store, instance), ), - Item::ComponentPath(_) | Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => { + Item::ComponentPath(_) | Item::SpaceView(_) | Item::DataBlueprintGroup(_, _, _) => { item.clone() } } diff --git a/crates/re_viewer_context/src/query_context.rs b/crates/re_viewer_context/src/query_context.rs index e37cfcf1aba6..42ae3dfe136a 100644 --- a/crates/re_viewer_context/src/query_context.rs +++ b/crates/re_viewer_context/src/query_context.rs @@ -20,6 +20,13 @@ pub struct DataQueryResult { pub tree: DataResultTree, } +impl DataQueryResult { + #[inline] + pub fn is_empty(&self) -> bool { + self.tree.is_empty() + } +} + impl Clone for DataQueryResult { fn clone(&self) -> Self { re_tracing::profile_function!(); @@ -121,6 +128,11 @@ impl DataResultTree { .and_then(|handle| self.lookup_result(*handle)) } + #[inline] + pub fn is_empty(&self) -> bool { + self.data_results_by_path.is_empty() + } + fn visit_recursive( &self, handle: DataResultHandle, diff --git a/crates/re_viewer_context/src/selection_state.rs b/crates/re_viewer_context/src/selection_state.rs index 7c7a1cc7e109..2832bceba134 100644 --- a/crates/re_viewer_context/src/selection_state.rs +++ b/crates/re_viewer_context/src/selection_state.rs @@ -206,7 +206,7 @@ impl SelectionState { .hovered_previous_frame .iter() .any(|current| match current { - Item::ComponentPath(_) | Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => { + Item::ComponentPath(_) | Item::SpaceView(_) | Item::DataBlueprintGroup(_, _, _) => { current == test } diff --git a/crates/re_viewport/src/blueprint/.gitattributes b/crates/re_viewport/src/blueprint/.gitattributes index 457fbed10db5..87f1cd5ff1a0 100644 --- a/crates/re_viewport/src/blueprint/.gitattributes +++ b/crates/re_viewport/src/blueprint/.gitattributes @@ -3,6 +3,5 @@ .gitattributes linguist-generated=true auto_space_views.rs linguist-generated=true mod.rs linguist-generated=true -space_view_component.rs linguist-generated=true space_view_maximized.rs linguist-generated=true viewport_layout.rs linguist-generated=true diff --git a/crates/re_viewport/src/blueprint/mod.rs b/crates/re_viewport/src/blueprint/mod.rs index 8b5a40300d30..3c2166644527 100644 --- a/crates/re_viewport/src/blueprint/mod.rs +++ b/crates/re_viewport/src/blueprint/mod.rs @@ -1,14 +1,11 @@ // DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/rust/api.rs mod auto_space_views; -mod space_view_component; -mod space_view_component_ext; mod space_view_maximized; mod space_view_maximized_ext; mod viewport_layout; mod viewport_layout_ext; pub use self::auto_space_views::AutoSpaceViews; -pub use self::space_view_component::SpaceViewComponent; pub use self::space_view_maximized::SpaceViewMaximized; pub use self::viewport_layout::ViewportLayout; diff --git a/crates/re_viewport/src/blueprint/space_view_component_ext.rs b/crates/re_viewport/src/blueprint/space_view_component_ext.rs deleted file mode 100644 index 52a923b1832c..000000000000 --- a/crates/re_viewport/src/blueprint/space_view_component_ext.rs +++ /dev/null @@ -1,28 +0,0 @@ -use super::SpaceViewComponent; - -impl std::fmt::Debug for SpaceViewComponent { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "SpaceViewComponent") - } -} - -#[test] -fn test_space_view_component() { - use re_types::Loggable as _; - - let space_view = crate::SpaceViewBlueprint::new( - "Spatial".into(), - &"foo".into(), - std::iter::once(&"foo/bar".into()), - ); - - let data = [SpaceViewComponent { space_view }]; - let array: Box = - SpaceViewComponent::to_arrow(data.as_slice()).unwrap(); - let ret: Vec = SpaceViewComponent::from_arrow(array.as_ref()).unwrap(); - assert_eq!(data.len(), ret.len()); - assert!(data - .iter() - .zip(ret) - .all(|(l, r)| !l.space_view.has_edits(&r.space_view))); -} diff --git a/crates/re_viewport/src/space_view.rs b/crates/re_viewport/src/space_view.rs index 8d20ca5908fd..85e599240a48 100644 --- a/crates/re_viewport/src/space_view.rs +++ b/crates/re_viewport/src/space_view.rs @@ -1,27 +1,23 @@ -use nohash_hasher::IntMap; -use re_data_store::{EntityPath, EntityProperties, EntityTree, TimeInt, VisibleHistory}; +use ahash::HashSet; +use re_data_store::{EntityPath, EntityProperties, StoreDb, TimeInt, VisibleHistory}; use re_data_store::{EntityPropertiesComponent, EntityPropertyMap}; + use re_renderer::ScreenshotProcessor; -use re_space_view::{EntityOverrides, PropertyResolver, ScreenshotMode, SpaceViewContents}; +use re_space_view::{ + DataQueryBlueprint, EntityOverrides, PropertyResolver, ScreenshotMode, SpaceViewContents, +}; use re_space_view_time_series::TimeSeriesSpaceView; +use re_types::blueprint::SpaceViewComponent; use re_viewer_context::{ - DataQueryId, DataResult, DynSpaceViewClass, EntitiesPerSystem, PerSystemDataResults, + DataQueryId, DataResult, DynSpaceViewClass, PerSystemDataResults, PerSystemEntities, SpaceViewClassName, SpaceViewHighlights, SpaceViewId, SpaceViewState, SpaceViewSystemRegistry, StoreContext, ViewerContext, }; -use crate::{ - space_info::SpaceInfoCollection, - space_view_heuristics::{ - compute_heuristic_context_for_entities, is_entity_processed_by_class, - reachable_entities_from_root, - }, -}; - // ---------------------------------------------------------------------------- /// A view of a space. -#[derive(Clone, serde::Deserialize, serde::Serialize)] +#[derive(Clone)] pub struct SpaceViewBlueprint { pub id: SpaceViewId, pub display_name: String, @@ -33,35 +29,17 @@ pub struct SpaceViewBlueprint { /// Furthermore, this is the primary indicator for heuristics on what entities we show in this space view. pub space_origin: EntityPath, - /// The data blueprint tree, has blueprint settings for all blueprint groups and entities in this spaceview. - /// It determines which entities are part of the spaceview. - pub contents: SpaceViewContents, + /// The data queries that are part of this space view. + pub queries: Vec, /// True if the user is expected to add entities themselves. False otherwise. pub entities_determined_by_user: bool, /// Auto Properties // TODO(jleibs): This needs to be per-query - #[serde(skip)] pub auto_properties: EntityPropertyMap, } -// Default needed for deserialization when adding/changing fields. -impl Default for SpaceViewBlueprint { - fn default() -> Self { - let id = SpaceViewId::invalid(); - Self { - id, - display_name: "invalid".to_owned(), - class_name: SpaceViewClassName::invalid(), - space_origin: EntityPath::root(), - contents: SpaceViewContents::new(id), - entities_determined_by_user: Default::default(), - auto_properties: Default::default(), - } - } -} - /// Determine whether this `SpaceViewBlueprint` has user-edits relative to another `SpaceViewBlueprint` impl SpaceViewBlueprint { pub fn has_edits(&self, other: &Self) -> bool { @@ -70,7 +48,7 @@ impl SpaceViewBlueprint { display_name, class_name, space_origin, - contents, + queries, entities_determined_by_user, auto_properties: _, } = self; @@ -79,16 +57,17 @@ impl SpaceViewBlueprint { || display_name != &other.display_name || class_name != &other.class_name || space_origin != &other.space_origin - || contents.has_edits(&other.contents) + || queries.iter().map(|q| q.id).collect::>() + != other.queries.iter().map(|q| q.id).collect::>() || entities_determined_by_user != &other.entities_determined_by_user } } impl SpaceViewBlueprint { - pub fn new<'a>( + pub fn new( space_view_class: SpaceViewClassName, space_path: &EntityPath, - queries_entities: impl Iterator, + query: DataQueryBlueprint, ) -> Self { // We previously named the [`SpaceView`] after the [`EntityPath`] if there was only a single entity. However, // this led to somewhat confusing and inconsistent behavior. See https://github.com/rerun-io/rerun/issues/1220 @@ -103,20 +82,52 @@ impl SpaceViewBlueprint { let id = SpaceViewId::random(); - let mut contents = SpaceViewContents::new(id); - contents.insert_entities_according_to_hierarchy(queries_entities, space_path); - Self { display_name, class_name: space_view_class, id, space_origin: space_path.clone(), - contents, + queries: vec![query], entities_determined_by_user: false, auto_properties: Default::default(), } } + pub fn try_from_db(path: &EntityPath, blueprint_db: &StoreDb) -> Option { + let SpaceViewComponent { + display_name, + class_name, + space_origin, + entities_determined_by_user, + contents, + } = blueprint_db + .store() + .query_timeless_component::(path) + .map(|c| c.value)?; + + let id = SpaceViewId::from_entity_path(path); + + let class_name = class_name.as_str().into(); + + let queries = contents + .into_iter() + .map(DataQueryId::from) + .filter_map(|id| { + DataQueryBlueprint::try_from_db(&id.as_entity_path(), blueprint_db, class_name) + }) + .collect(); + + Some(Self { + id, + display_name: display_name.to_string(), + class_name, + space_origin: space_origin.into(), + queries, + entities_determined_by_user, + auto_properties: Default::default(), + }) + } + pub fn class_name(&self) -> &SpaceViewClassName { &self.class_name } @@ -138,30 +149,8 @@ impl SpaceViewBlueprint { pub fn on_frame_start( &mut self, ctx: &mut ViewerContext<'_>, - spaces_info: &SpaceInfoCollection, view_state: &mut dyn SpaceViewState, ) { - let empty_map = IntMap::default(); - - let entities_per_system_for_class = ctx - .entities_per_system_per_class - .get(self.class_name()) - .unwrap_or(&empty_map); - - if !self.entities_determined_by_user { - // Add entities that have been logged since we were created. - let reachable_entities = reachable_entities_from_root(&self.space_origin, spaces_info); - let queries_entities = reachable_entities.iter().filter(|ent_path| { - entities_per_system_for_class - .iter() - .any(|(_, ents)| ents.contains(ent_path)) - }); - self.contents - .insert_entities_according_to_hierarchy(queries_entities, &self.space_origin); - } - - self.reset_systems_per_entity_path(entities_per_system_for_class); - while ScreenshotProcessor::next_readback_result( ctx.render_ctx, self.id.gpu_readback_id(), @@ -170,10 +159,29 @@ impl SpaceViewBlueprint { .is_some() {} + let query_result = ctx.lookup_query_result(self.query_id()).clone(); + + // TODO(#4377): Use PerSystemDataResults + let mut per_system_entities = PerSystemEntities::default(); + { + re_tracing::profile_scope!("per_system_data_results"); + + query_result.tree.visit(&mut |handle| { + if let Some(result) = query_result.tree.lookup_result(handle) { + for system in &result.view_parts { + per_system_entities + .entry(*system) + .or_default() + .insert(result.entity_path.clone()); + } + } + }); + } + self.class(ctx.space_view_class_registry).on_frame_start( ctx, view_state, - self.contents.per_system_entities(), + &per_system_entities, &mut self.auto_properties, ); } @@ -277,83 +285,6 @@ impl SpaceViewBlueprint { }); } - /// Removes a subtree of entities from the blueprint tree. - /// - /// Ignores all entities that aren't part of the blueprint. - pub fn remove_entity_subtree(&mut self, tree: &EntityTree) { - re_tracing::profile_function!(); - - tree.visit_children_recursively(&mut |path: &EntityPath| { - self.contents.remove_entity(path); - self.entities_determined_by_user = true; - }); - } - - /// Adds a subtree of entities to the blueprint tree and creates groups as needed. - /// - /// Ignores all entities that can't be added or are already added. - pub fn add_entity_subtree( - &mut self, - ctx: &ViewerContext<'_>, - tree: &EntityTree, - spaces_info: &SpaceInfoCollection, - ) { - re_tracing::profile_function!(); - - let heuristic_context = compute_heuristic_context_for_entities(ctx.store_db); - - let mut entities = Vec::new(); - tree.visit_children_recursively(&mut |entity_path: &EntityPath| { - if is_entity_processed_by_class( - ctx, - &self.class_name, - entity_path, - heuristic_context - .get(entity_path) - .copied() - .unwrap_or_default(), - &ctx.current_query(), - ) && !self.contents.contains_entity(entity_path) - && spaces_info - .is_reachable_by_transform(entity_path, &self.space_origin) - .is_ok() - { - entities.push(entity_path.clone()); - } - }); - - if !entities.is_empty() { - self.contents - .insert_entities_according_to_hierarchy(entities.iter(), &self.space_origin); - self.entities_determined_by_user = true; - } - } - - /// Resets the [`SpaceViewContents::per_system_entities`] for all paths that are part of this space view. - pub fn reset_systems_per_entity_path( - &mut self, - entities_per_system_for_class: &EntitiesPerSystem, - ) { - re_tracing::profile_function!(); - - // TODO(andreas): We believe this is *correct* but not necessarily optimal. Pay attention - // to the algorithmic complexity here as we consider changing the indexing and - // access patterns of these structures in the future. - let mut per_system_entities = re_viewer_context::PerSystemEntities::new(); - for (system, entities) in entities_per_system_for_class { - per_system_entities.insert( - *system, - self.contents - .entity_paths() - .filter(|ent_path| entities.contains(ent_path)) - .cloned() - .collect(), - ); - } - - *self.contents.per_system_entities_mut() = per_system_entities; - } - #[inline] pub fn entity_path(&self) -> EntityPath { self.id.as_entity_path() @@ -361,7 +292,10 @@ impl SpaceViewBlueprint { #[inline] pub fn query_id(&self) -> DataQueryId { - self.id.uuid().into() + // TODO(jleibs): Return all queries + self.queries + .first() + .map_or(DataQueryId::invalid(), |q| q.id) } pub fn root_data_result(&self, ctx: &StoreContext<'_>) -> DataResult { @@ -448,6 +382,7 @@ mod tests { use re_data_store::StoreDb; use re_log_types::{DataCell, DataRow, RowId, StoreId, TimePoint}; use re_space_view::DataQuery as _; + use re_types::archetypes::Points3D; use re_viewer_context::{EntitiesPerSystemPerClass, StoreContext}; use super::*; @@ -468,18 +403,34 @@ mod tests { #[test] fn test_overrides() { - let recording = StoreDb::new(StoreId::random(re_log_types::StoreKind::Recording)); + let mut recording = StoreDb::new(StoreId::random(re_log_types::StoreKind::Recording)); let mut blueprint = StoreDb::new(StoreId::random(re_log_types::StoreKind::Blueprint)); + let points = Points3D::new(vec![[1.0, 2.0, 3.0]]); + + for path in [ + "parent".into(), + "parent/skip/child1".into(), + "parent/skip/child2".into(), + ] { + let row = + DataRow::from_archetype(RowId::random(), TimePoint::timeless(), path, &points) + .unwrap(); + recording.add_data_row(row).ok(); + } + let space_view = SpaceViewBlueprint::new( "3D".into(), &EntityPath::root(), - [ - &"parent".into(), - &"parent/skip/child1".into(), - &"parent/skip/child2".into(), - ] - .into_iter(), + DataQueryBlueprint::new( + "3D".into(), + [ + &"parent".into(), + &"parent/skip/child1".into(), + &"parent/skip/child2".into(), + ] + .into_iter(), + ), ); let mut entities_per_system_per_class = EntitiesPerSystemPerClass::default(); @@ -496,6 +447,10 @@ mod tests { .collect() }); + let query = space_view.queries.first().unwrap(); + + let resolver = query.build_resolver(space_view.id, &space_view.auto_properties); + // No overrides set. Everybody has default values. { let ctx = StoreContext { @@ -504,11 +459,7 @@ mod tests { all_recordings: vec![], }; - let query_result = space_view.contents.execute_query( - &space_view, - &ctx, - &entities_per_system_per_class, - ); + let query_result = query.execute_query(&resolver, &ctx, &entities_per_system_per_class); let parent = query_result .tree @@ -542,11 +493,7 @@ mod tests { all_recordings: vec![], }; - let query_result = space_view.contents.execute_query( - &space_view, - &ctx, - &entities_per_system_per_class, - ); + let query_result = query.execute_query(&resolver, &ctx, &entities_per_system_per_class); let parent_group = query_result .tree @@ -589,11 +536,7 @@ mod tests { all_recordings: vec![], }; - let query_result = space_view.contents.execute_query( - &space_view, - &ctx, - &entities_per_system_per_class, - ); + let query_result = query.execute_query(&resolver, &ctx, &entities_per_system_per_class); let parent = query_result .tree @@ -635,11 +578,7 @@ mod tests { all_recordings: vec![], }; - let query_result = space_view.contents.execute_query( - &space_view, - &ctx, - &entities_per_system_per_class, - ); + let query_result = query.execute_query(&resolver, &ctx, &entities_per_system_per_class); let parent = query_result .tree @@ -676,11 +615,7 @@ mod tests { all_recordings: vec![], }; - let query_result = space_view.contents.execute_query( - &space_view, - &ctx, - &entities_per_system_per_class, - ); + let query_result = query.execute_query(&resolver, &ctx, &entities_per_system_per_class); let parent = query_result .tree diff --git a/crates/re_viewport/src/space_view_entity_picker.rs b/crates/re_viewport/src/space_view_entity_picker.rs index c86b4cec3f0d..28564dc96562 100644 --- a/crates/re_viewport/src/space_view_entity_picker.rs +++ b/crates/re_viewport/src/space_view_entity_picker.rs @@ -1,7 +1,6 @@ use itertools::Itertools; use nohash_hasher::IntMap; -use re_data_store::{EntityPath, EntityTree, InstancePath}; -use re_data_ui::item_ui; +use re_data_store::{EntityPath, EntityTree}; use re_viewer_context::{SpaceViewId, ViewerContext}; use crate::{ @@ -170,14 +169,17 @@ fn add_entities_tree_ui( } fn add_entities_line_ui( - ctx: &mut ViewerContext<'_>, + _ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui, - spaces_info: &SpaceInfoCollection, - name: &str, - entity_tree: &EntityTree, - space_view: &mut SpaceViewBlueprint, - entities_add_info: &IntMap, + _spaces_info: &SpaceInfoCollection, + _name: &str, + _entity_tree: &EntityTree, + _space_view: &mut SpaceViewBlueprint, + _entities_add_info: &IntMap, ) { + // TODO(#4377): Reformulate this in terms of modifying query expressions + ui.label("Not implemented"); + /* ui.horizontal(|ui| { let entity_path = &entity_tree.path; @@ -248,6 +250,7 @@ fn add_entities_line_ui( } }); }); + */ } /// Describes if an entity path can be added to a space view. @@ -275,6 +278,7 @@ impl CanAddToSpaceView { } /// Can be added and spaceview doesn't have it already. + #[allow(dead_code)] pub fn is_compatible_and_missing(&self) -> bool { self == &CanAddToSpaceView::Compatible { already_added: false, @@ -300,6 +304,7 @@ impl CanAddToSpaceView { } #[derive(Default)] +#[allow(dead_code)] struct EntityAddInfo { can_add: CanAddToSpaceView, can_add_self_or_descendant: CanAddToSpaceView, @@ -310,7 +315,7 @@ fn create_entity_add_info( tree: &EntityTree, heuristic_context_per_entity: &HeuristicFilterContextPerEntity, space_view: &SpaceViewBlueprint, - spaces_info: &SpaceInfoCollection, + _spaces_info: &SpaceInfoCollection, ) -> IntMap { let mut meta_data: IntMap = IntMap::default(); @@ -318,6 +323,11 @@ fn create_entity_add_info( let heuristic_context_per_entity = heuristic_context_per_entity.get(entity_path).copied().unwrap_or_default(); let can_add: CanAddToSpaceView = if is_entity_processed_by_class(ctx, space_view.class_name(), entity_path, heuristic_context_per_entity, &ctx.current_query()) { + // TODO(#4377): Reformulate this in terms of modifying query expressions + CanAddToSpaceView::No { + reason: "Not implemented".to_owned(), + } + /* match spaces_info.is_reachable_by_transform(entity_path, &space_view.space_origin) { Ok(()) => CanAddToSpaceView::Compatible { already_added: space_view.contents.contains_entity(entity_path), @@ -326,6 +336,7 @@ fn create_entity_add_info( reason: reason.to_string(), }, } + */ } else { CanAddToSpaceView::No { reason: format!( diff --git a/crates/re_viewport/src/space_view_heuristics.rs b/crates/re_viewport/src/space_view_heuristics.rs index 3aec0ca7a6f6..cbb5143e3520 100644 --- a/crates/re_viewport/src/space_view_heuristics.rs +++ b/crates/re_viewport/src/space_view_heuristics.rs @@ -4,15 +4,14 @@ use nohash_hasher::{IntMap, IntSet}; use re_arrow_store::{LatestAtQuery, Timeline}; use re_data_store::{EntityPath, EntityTree}; -use re_log_types::TimeInt; -use re_types::{ - archetypes::{Image, SegmentationImage}, - components::{DisconnectedSpace, TensorData}, - Archetype, ComponentNameSet, -}; +use re_log_types::{EntityPathExpr, TimeInt}; +use re_space_view::{DataQuery as _, DataQueryBlueprint, NOOP_RESOLVER}; +use re_types::components::{DisconnectedSpace, TensorData}; +use re_types::ComponentNameSet; use re_viewer_context::{ - AutoSpawnHeuristic, EntitiesPerSystem, EntitiesPerSystemPerClass, HeuristicFilterContext, - SpaceViewClassName, ViewContextCollection, ViewPartCollection, ViewSystemName, ViewerContext, + AutoSpawnHeuristic, DataQueryResult, EntitiesPerSystem, EntitiesPerSystemPerClass, + HeuristicFilterContext, PerSystemEntities, SpaceViewClassName, ViewContextCollection, + ViewPartCollection, ViewSystemName, ViewerContext, }; use tinyvec::TinyVec; @@ -58,7 +57,7 @@ pub fn all_possible_space_views( ctx: &ViewerContext<'_>, spaces_info: &SpaceInfoCollection, entities_per_system_per_class: &EntitiesPerSystemPerClass, -) -> Vec { +) -> Vec<(SpaceViewBlueprint, DataQueryResult)> { re_tracing::profile_function!(); for (class_name, entities_per_system) in entities_per_system_per_class { @@ -108,16 +107,29 @@ pub fn all_possible_space_views( entities_used_by_any_part_system_of_class .iter() - .filter_map(|(class_name, entities_used_by_any_part_system)| { - let candidate = SpaceViewBlueprint::new( + .filter_map(|(class_name, _entities_used_by_any_part_system)| { + // TODO(#4377): The need to run a query-per-candidate for all possible candidates + // is way too expensive. This needs to be optimized significantly. + let candidate_query = DataQueryBlueprint::new( *class_name, - &candidate_space_path.clone(), - reachable_entities - .iter() - .filter(|ent_path| entities_used_by_any_part_system.contains(ent_path)), + std::iter::once(&EntityPathExpr::Recursive(candidate_space_path.clone())), + ); + + let results = candidate_query.execute_query( + &NOOP_RESOLVER, + ctx.store_context, + entities_per_system_per_class, ); - if candidate.contents.entity_paths().next().is_some() { - Some(candidate) + + if !results.is_empty() { + Some(( + SpaceViewBlueprint::new( + *class_name, + candidate_space_path, + candidate_query, + ), + results, + )) } else { None } @@ -127,34 +139,38 @@ pub fn all_possible_space_views( .collect_vec() } -fn contains_any_image(ent_path: &EntityPath, store: &re_arrow_store::DataStore) -> bool { - store - .all_components(&Timeline::log_time(), ent_path) - .unwrap_or_default() - .iter() - .any(|comp| { - *comp == SegmentationImage::indicator().name() || *comp == Image::indicator().name() - }) -} - fn is_interesting_space_view_at_root( - data_store: &re_arrow_store::DataStore, - candidate: &SpaceViewBlueprint, + _data_store: &re_arrow_store::DataStore, + query_results: &DataQueryResult, ) -> bool { - // Not interesting if it has only data blueprint groups and no direct entities. - // -> If there In that case we want spaceviews at those groups. - if candidate.contents.root_group().entities.is_empty() { - return false; - } + if let Some(root) = query_results.tree.root_node() { + // Not interesting if it has only data blueprint groups and no direct entities. + // -> If there In that case we want spaceviews at those groups. + if root.children.iter().all(|child| { + query_results + .tree + .lookup_node(*child) + .map_or(true, |child| child.data_result.view_parts.is_empty()) + }) { + return false; + } - // TODO(andreas): We have to figure out how to do this kind of heuristic in a more generic way without deep knowledge of re_types. - // - // If there are any images directly under the root, don't create root space either. - // -> For images we want more fine grained control and resort to child-of-root spaces only. - for entity_path in &candidate.contents.root_group().entities { - if contains_any_image(entity_path, data_store) { + // TODO(andreas): We have to figure out how to do this kind of heuristic in a more generic way without deep knowledge of re_types. + // + // If there are any images directly under the root, don't create root space either. + // -> For images we want more fine grained control and resort to child-of-root spaces only. + if root.children.iter().any(|child| { + query_results + .tree + .lookup_node(*child) + .map_or(false, |child| { + child.data_result.view_parts.contains(&"Images".into()) // TODO(jleibs): Refer to `ImagesPart` + }) + }) { return false; } + } else { + return false; } true @@ -209,9 +225,9 @@ pub fn default_created_space_views( // First pass to look for interesting roots, as their existence influences the heuristic for non-roots! let classes_with_interesting_roots = candidates .iter() - .filter_map(|space_view_candidate| { + .filter_map(|(space_view_candidate, query_results)| { (space_view_candidate.space_origin.is_root() - && is_interesting_space_view_at_root(store, space_view_candidate)) + && is_interesting_space_view_at_root(store, query_results)) .then_some(*space_view_candidate.class_name()) }) .collect::>(); @@ -220,22 +236,34 @@ pub fn default_created_space_views( // Main pass through all candidates. // We first check if a candidate is "interesting" and then split it up/modify it further if required. - for mut candidate in candidates { - // In order to have per_system_entities correctly computed, we need to reset it first - freshly created ones do not. - let Some(entities_per_system_for_class) = + for (candidate, query_result) in candidates { + let Some(_entities_per_system_for_class) = entities_per_system_per_class.get(candidate.class_name()) else { // Should never reach this, but if we would there would be no entities in this candidate so skipping makes sense. continue; }; - candidate.reset_systems_per_entity_path(entities_per_system_for_class); + + // TODO(#4377): Can spawn heuristics consume the query_result directly? + let mut per_system_entities = PerSystemEntities::default(); + { + re_tracing::profile_scope!("per_system_data_results"); + + query_result.tree.visit(&mut |handle| { + if let Some(result) = query_result.tree.lookup_result(handle) { + for system in &result.view_parts { + per_system_entities + .entry(*system) + .or_default() + .insert(result.entity_path.clone()); + } + } + }); + } + let spawn_heuristic = candidate .class(ctx.space_view_class_registry) - .auto_spawn_heuristic( - ctx, - &candidate.space_origin, - candidate.contents.per_system_entities(), - ); + .auto_spawn_heuristic(ctx, &candidate.space_origin, &per_system_entities); if spawn_heuristic == AutoSpawnHeuristic::NeverSpawn { continue; @@ -256,15 +284,23 @@ pub fn default_created_space_views( } if spawn_one_space_view_per_entity(candidate.class_name()) { - for entity_path in candidate.contents.entity_paths() { - let mut space_view = SpaceViewBlueprint::new( - *candidate.class_name(), - entity_path, - std::iter::once(entity_path), - ); - space_view.entities_determined_by_user = true; // Suppress auto adding of entities. - space_views.push((space_view, AutoSpawnHeuristic::AlwaysSpawn)); - } + query_result.tree.visit(&mut |handle| { + if let Some(result) = query_result.tree.lookup_result(handle) { + if !result.view_parts.is_empty() { + let query = DataQueryBlueprint::new( + *candidate.class_name(), + std::iter::once(&EntityPathExpr::Exact(result.entity_path.clone())), + ); + let mut space_view = SpaceViewBlueprint::new( + *candidate.class_name(), + &result.entity_path, + query, + ); + space_view.entities_determined_by_user = true; // Suppress auto adding of entities. + space_views.push((space_view, AutoSpawnHeuristic::AlwaysSpawn)); + } + } + }); continue; } @@ -301,7 +337,7 @@ pub fn default_created_space_views( } if should_spawn_new { - // 2D views with images get extra treatment as well. + // Spatial views with images get extra treatment as well. if is_spatial_2d_class(candidate.class_name()) { #[derive(Hash, PartialEq, Eq)] enum ImageBucketing { @@ -312,30 +348,39 @@ pub fn default_created_space_views( let mut images_by_bucket: HashMap> = HashMap::default(); - // For this we're only interested in the direct children. - for entity_path in &candidate.contents.root_group().entities { - if let Some(tensor) = - store.query_latest_component::(entity_path, &query) - { - if let Some([height, width, _]) = tensor.image_height_width_channels() { - if store - .query_latest_component::( - entity_path, - &query, - ) - .is_some() - { - // Put everything in the same bucket if it has a draw order. - images_by_bucket - .entry(ImageBucketing::ExplicitDrawOrder) - .or_default() - .push(entity_path.clone()); - } else { - // Otherwise, distinguish buckets by image size. - images_by_bucket - .entry(ImageBucketing::BySize((height, width))) - .or_default() - .push(entity_path.clone()); + if let Some(root) = query_result.tree.root_node() { + // For this we're only interested in the direct children. + for child in &root.children { + if let Some(node) = query_result.tree.lookup_node(*child) { + if !node.data_result.view_parts.is_empty() { + let entity_path = &node.data_result.entity_path; + if let Some(tensor) = store + .query_latest_component::(entity_path, &query) + { + if let Some([height, width, _]) = + tensor.image_height_width_channels() + { + if store + .query_latest_component::( + entity_path, + &query, + ) + .is_some() + { + // Put everything in the same bucket if it has a draw order. + images_by_bucket + .entry(ImageBucketing::ExplicitDrawOrder) + .or_default() + .push(entity_path.clone()); + } else { + // Otherwise, distinguish buckets by image size. + images_by_bucket + .entry(ImageBucketing::BySize((height, width))) + .or_default() + .push(entity_path.clone()); + } + } + } } } } @@ -343,27 +388,21 @@ pub fn default_created_space_views( if images_by_bucket.len() > 1 { // If all images end up in the same bucket, proceed as normal. Otherwise stack images as instructed. - for bucket in images_by_bucket.keys() { - // Ignore every image from another bucket. Keep all other entities. - let images_of_different_size = images_by_bucket + for bucket in images_by_bucket.values() { + let expressions: Vec<_> = bucket .iter() - .filter_map(|(other_bucket, images)| { - (bucket != other_bucket).then_some(images) - }) - .flatten() - .cloned() - .collect::>(); - let entities = candidate - .contents - .entity_paths() - .filter(|path| !images_of_different_size.contains(path)) - .cloned() - .collect_vec(); + .map(|path| EntityPathExpr::Exact(path.clone())) + .collect(); + + let query = DataQueryBlueprint::new( + *candidate.class_name(), + expressions.iter(), + ); let mut space_view = SpaceViewBlueprint::new( *candidate.class_name(), &candidate.space_origin, - entities.iter(), + query, ); space_view.entities_determined_by_user = true; // Suppress auto adding of entities. space_views.push((space_view, AutoSpawnHeuristic::AlwaysSpawn)); @@ -531,7 +570,7 @@ pub fn identify_entities_per_system_per_class( ComponentNameSet, IntMap>, > = HashMap::default(); - for (class_name, (context_collection, part_collection)) in &system_collections_per_class { + for (class_name, (_context_collection, part_collection)) in &system_collections_per_class { for (system_name, part) in part_collection.iter_with_names() { systems_per_required_components .entry(part.required_components().into_iter().collect()) @@ -540,6 +579,8 @@ pub fn identify_entities_per_system_per_class( .or_default() .push(system_name); } + // TODO(#4377): Handle context systems but keep them parallel + /* for (system_name, part) in context_collection.iter_with_names() { for components in part.compatible_component_sets() { systems_per_required_components @@ -550,6 +591,7 @@ pub fn identify_entities_per_system_per_class( .push(system_name); } } + */ } systems_per_required_components }; diff --git a/crates/re_viewport/src/space_view_highlights.rs b/crates/re_viewport/src/space_view_highlights.rs index bf0430dd8548..a0bc834ac946 100644 --- a/crates/re_viewport/src/space_view_highlights.rs +++ b/crates/re_viewport/src/space_view_highlights.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use egui::NumExt; use nohash_hasher::IntMap; -use re_log_types::{EntityPath, EntityPathHash}; +use re_log_types::EntityPathHash; use re_renderer::OutlineMaskPreference; use re_viewer_context::{ HoverHighlight, Item, SelectionHighlight, SelectionState, SpaceViewEntityHighlight, @@ -15,7 +15,7 @@ use crate::SpaceViewBlueprint; pub fn highlights_for_space_view( selection_state: &SelectionState, space_view_id: SpaceViewId, - space_views: &BTreeMap, + _space_views: &BTreeMap, ) -> SpaceViewHighlights { re_tracing::profile_function!(); @@ -40,7 +40,9 @@ pub fn highlights_for_space_view( match current_selection { Item::ComponentPath(_) | Item::SpaceView(_) => {} - Item::DataBlueprintGroup(group_space_view_id, group_handle) => { + Item::DataBlueprintGroup(_space_view_id, _query_id, _entity_path) => { + // TODO(#4377): Fix DataBlueprintGroup + /* if *group_space_view_id == space_view_id { if let Some(space_view) = space_views.get(group_space_view_id) { // Everything in the same group should receive the same selection outline. @@ -63,6 +65,7 @@ pub fn highlights_for_space_view( ); } } + */ } Item::InstancePath(selected_space_view_context, selected_instance) => { @@ -114,7 +117,9 @@ pub fn highlights_for_space_view( match current_hover { Item::ComponentPath(_) | Item::SpaceView(_) => {} - Item::DataBlueprintGroup(group_space_view_id, group_handle) => { + Item::DataBlueprintGroup(_space_view_id, _query_id, _entity_path) => { + // TODO(#4377): Fix DataBlueprintGroup + /* // Unlike for selected objects/data we are more picky for data blueprints with our hover highlights // since they are truly local to a space view. if *group_space_view_id == space_view_id { @@ -136,6 +141,7 @@ pub fn highlights_for_space_view( ); } } + */ } Item::InstancePath(_, selected_instance) => { diff --git a/crates/re_viewport/src/viewport.rs b/crates/re_viewport/src/viewport.rs index 58fe1214b8e4..f47b7c6f56b0 100644 --- a/crates/re_viewport/src/viewport.rs +++ b/crates/re_viewport/src/viewport.rs @@ -183,7 +183,7 @@ impl<'a, 'b> Viewport<'a, 'b> { space_view.class_name(), ); - space_view.on_frame_start(ctx, spaces_info, space_view_state); + space_view.on_frame_start(ctx, space_view_state); } if self.blueprint.auto_space_views { @@ -208,8 +208,10 @@ impl<'a, 'b> Viewport<'a, 'b> { return false; } if existing_view - .contents - .contains_all_entities_from(&space_view_candidate.contents) + .queries + .iter() + .zip(space_view_candidate.queries.iter()) + .all(|(q1, q2)| q1.is_equivalent(q2)) { // This space view wouldn't add anything we haven't already return false; diff --git a/crates/re_viewport/src/viewport_blueprint.rs b/crates/re_viewport/src/viewport_blueprint.rs index c13f34983ecc..85154a69becb 100644 --- a/crates/re_viewport/src/viewport_blueprint.rs +++ b/crates/re_viewport/src/viewport_blueprint.rs @@ -4,6 +4,7 @@ use ahash::HashMap; use re_data_store::{EntityPath, StoreDb}; use re_log_types::{DataRow, RowId, TimePoint}; +use re_types::blueprint::SpaceViewComponent; use re_types_core::{archetypes::Clear, AsComponents as _}; use re_viewer_context::{ CommandSender, Item, SpaceViewClassName, SpaceViewId, SystemCommand, SystemCommandSender, @@ -11,7 +12,7 @@ use re_viewer_context::{ }; use crate::{ - blueprint::{AutoSpaceViews, SpaceViewComponent, SpaceViewMaximized, ViewportLayout}, + blueprint::{AutoSpaceViews, SpaceViewMaximized, ViewportLayout}, space_info::SpaceInfoCollection, space_view::SpaceViewBlueprint, space_view_heuristics::default_created_space_views, @@ -160,16 +161,10 @@ impl<'a> ViewportBlueprint<'a> { .map(|space_view_id| self.space_view(&space_view_id).is_some()) .unwrap_or(true), Item::SpaceView(space_view_id) => self.space_view(space_view_id).is_some(), - Item::DataBlueprintGroup(space_view_id, data_blueprint_group_handle) => { - if let Some(space_view) = self.space_view(space_view_id) { - space_view - .contents - .group(*data_blueprint_group_handle) - .is_some() - } else { - false - } - } + Item::DataBlueprintGroup(space_view_id, query_id, _entity_path) => self + .space_views + .get(space_view_id) + .map_or(false, |sv| sv.queries.iter().any(|q| q.id == *query_id)), } } @@ -231,11 +226,21 @@ impl<'a> ViewportBlueprint<'a> { space_view_id } - pub fn space_views_containing_entity_path(&self, path: &EntityPath) -> Vec { + #[allow(clippy::unused_self)] + pub fn space_views_containing_entity_path( + &self, + ctx: &ViewerContext<'_>, + path: &EntityPath, + ) -> Vec { self.space_views .iter() .filter_map(|(space_view_id, space_view)| { - if space_view.contents.contains_entity(path) { + let query_result = ctx.lookup_query_result(space_view.query_id()); + if query_result + .tree + .lookup_result_by_path_and_group(path, false) + .is_some() + { Some(*space_view_id) } else { None @@ -326,33 +331,6 @@ fn add_delta_from_single_component<'a, C>( // ---------------------------------------------------------------------------- -pub fn load_space_view_blueprint( - path: &EntityPath, - blueprint_db: &re_data_store::StoreDb, -) -> Option { - re_tracing::profile_function!(); - - let mut space_view = blueprint_db - .store() - .query_timeless_component_quiet::(path) - .map(|c| c.value.space_view); - - // Blueprint data migrations can leave us unable to parse the expected id from the source-data - // We always want the id to match the one derived from the EntityPath since this id is how - // we would end up removing it from the blueprint. - let expected_id = SpaceViewId::from_entity_path(path); - if let Some(space_view) = &mut space_view { - if space_view.id != SpaceViewId::invalid() && space_view.id != expected_id { - re_log::warn_once!( - "SpaceViewBlueprint id is inconsistent with path: {:?}", - space_view.id - ); - } - space_view.id = expected_id; - } - space_view -} - pub fn load_viewport_blueprint(blueprint_db: &re_data_store::StoreDb) -> ViewportBlueprint<'_> { re_tracing::profile_function!(); @@ -365,7 +343,7 @@ pub fn load_viewport_blueprint(blueprint_db: &re_data_store::StoreDb) -> Viewpor space_views .children .values() - .filter_map(|view_tree| load_space_view_blueprint(&view_tree.path, blueprint_db)) + .filter_map(|view_tree| SpaceViewBlueprint::try_from_db(&view_tree.path, blueprint_db)) .map(|sv| (sv.id, sv)) .collect() } else { @@ -442,10 +420,23 @@ pub fn sync_space_view( let timepoint = TimePoint::timeless(); let component = SpaceViewComponent { - space_view: space_view.clone(), + display_name: space_view.display_name.clone().into(), + class_name: space_view.class_name().as_str().into(), + space_origin: (&space_view.space_origin).into(), + entities_determined_by_user: space_view.entities_determined_by_user, + contents: space_view.queries.iter().map(|q| q.id.into()).collect(), }; add_delta_from_single_component(deltas, &space_view.entity_path(), &timepoint, component); + + for query in &space_view.queries { + add_delta_from_single_component( + deltas, + &query.id.as_entity_path(), + &timepoint, + query.expressions.clone(), + ); + } } } diff --git a/crates/re_viewport/src/viewport_blueprint_ui.rs b/crates/re_viewport/src/viewport_blueprint_ui.rs index cacd3bd8b698..fadf14e04dea 100644 --- a/crates/re_viewport/src/viewport_blueprint_ui.rs +++ b/crates/re_viewport/src/viewport_blueprint_ui.rs @@ -6,7 +6,7 @@ use re_data_ui::item_ui; use re_ui::list_item::ListItem; use re_ui::ReUi; use re_viewer_context::{ - DataResultHandle, DataResultNode, DataResultTree, HoverHighlight, Item, SpaceViewId, + DataQueryResult, DataResultHandle, DataResultNode, HoverHighlight, Item, SpaceViewId, ViewerContext, }; @@ -195,7 +195,7 @@ impl ViewportBlueprint<'_> { Self::space_view_blueprint_ui( ctx, ui, - result_tree, + &query_result, result_handle, space_view, visible_child, @@ -226,12 +226,12 @@ impl ViewportBlueprint<'_> { fn space_view_blueprint_ui( ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui, - result_tree: &DataResultTree, + query_result: &DataQueryResult, result_handle: DataResultHandle, space_view: &mut SpaceViewBlueprint, space_view_visible: bool, ) { - let Some(top_node) = result_tree.lookup_node(result_handle) else { + let Some(top_node) = query_result.tree.lookup_node(result_handle) else { debug_assert!(false, "Invalid data result handle in data result tree"); return; }; @@ -244,18 +244,19 @@ impl ViewportBlueprint<'_> { .children .iter() .filter(|c| { - result_tree + query_result + .tree .lookup_result(**c) .map_or(false, |c| !c.is_group) }) - .chain( - top_node - .children - .iter() - .filter(|c| result_tree.lookup_result(**c).map_or(false, |c| c.is_group)), - ) + .chain(top_node.children.iter().filter(|c| { + query_result + .tree + .lookup_result(**c) + .map_or(false, |c| c.is_group) + })) { - let Some(child_node) = result_tree.lookup_node(*child) else { + let Some(child_node) = query_result.tree.lookup_node(*child) else { debug_assert!(false, "DataResultNode {top_node:?} has an invalid child"); continue; }; @@ -264,11 +265,8 @@ impl ViewportBlueprint<'_> { let entity_path = &child_node.data_result.entity_path; let item = if data_result.is_group { - let group_handle = space_view - .contents - .group_handle_for_entity_path(entity_path); // If we can't find a group_handle for some reason, use the default, null handle. - Item::DataBlueprintGroup(space_view.id, group_handle.unwrap_or_default()) + Item::DataBlueprintGroup(space_view.id, query_result.id, entity_path.clone()) } else { Item::InstancePath( Some(space_view.id), @@ -313,7 +311,8 @@ impl ViewportBlueprint<'_> { let response = remove_button_ui(re_ui, ui, "Remove Entity from the Space View"); if response.clicked() { - space_view.contents.remove_entity(entity_path); + // TODO(#4377): Fix entity removal + //space_view.contents.remove_entity(entity_path); space_view.entities_determined_by_user = true; } @@ -359,7 +358,7 @@ impl ViewportBlueprint<'_> { Self::space_view_blueprint_ui( ctx, ui, - result_tree, + query_result, *child, space_view, space_view_visible, @@ -375,6 +374,8 @@ impl ViewportBlueprint<'_> { }); if remove_group { + // TODO(#4377): Fix group removal + /* if let Some(group_handle) = space_view .contents .group_handle_for_entity_path(entity_path) @@ -382,6 +383,7 @@ impl ViewportBlueprint<'_> { space_view.contents.remove_group(group_handle); space_view.entities_determined_by_user = true; } + */ } response @@ -407,10 +409,10 @@ impl ViewportBlueprint<'_> { |ui| { ui.style_mut().wrap = Some(false); - for space_view in + for (space_view, _) in all_possible_space_views(ctx, spaces_info, ctx.entities_per_system_per_class) .into_iter() - .sorted_by_key(|space_view| space_view.space_origin.to_string()) + .sorted_by_key(|(space_view, _)| space_view.space_origin.to_string()) { if ctx .re_ui diff --git a/docs/content/reference/types/datatypes.md b/docs/content/reference/types/datatypes.md index bd9c1a7050ce..0b639323a04f 100644 --- a/docs/content/reference/types/datatypes.md +++ b/docs/content/reference/types/datatypes.md @@ -12,6 +12,7 @@ Data types are the lowest layer of the data model hierarchy * [`ClassDescription`](datatypes/class_description.md) * [`ClassDescriptionMapElem`](datatypes/class_description_map_elem.md) * [`ClassId`](datatypes/class_id.md) +* [`EntityPath`](datatypes/entity_path.md) * [`Float32`](datatypes/float32.md) * [`KeypointId`](datatypes/keypoint_id.md) * [`KeypointPair`](datatypes/keypoint_pair.md) @@ -35,6 +36,7 @@ Data types are the lowest layer of the data model hierarchy * [`UVec3D`](datatypes/uvec3d.md) * [`UVec4D`](datatypes/uvec4d.md) * [`Utf8`](datatypes/utf8.md) +* [`Uuid`](datatypes/uuid.md) * [`Vec2D`](datatypes/vec2d.md) * [`Vec3D`](datatypes/vec3d.md) * [`Vec4D`](datatypes/vec4d.md) diff --git a/docs/content/reference/types/datatypes/.gitattributes b/docs/content/reference/types/datatypes/.gitattributes index 8908077d0e0b..707aa642d9e7 100644 --- a/docs/content/reference/types/datatypes/.gitattributes +++ b/docs/content/reference/types/datatypes/.gitattributes @@ -6,6 +6,7 @@ annotation_info.md linguist-generated=true class_description.md linguist-generated=true class_description_map_elem.md linguist-generated=true class_id.md linguist-generated=true +entity_path.md linguist-generated=true float32.md linguist-generated=true keypoint_id.md linguist-generated=true keypoint_pair.md linguist-generated=true @@ -26,6 +27,7 @@ translation_and_mat3x3.md linguist-generated=true translation_rotation_scale3d.md linguist-generated=true uint32.md linguist-generated=true utf8.md linguist-generated=true +uuid.md linguist-generated=true uvec2d.md linguist-generated=true uvec3d.md linguist-generated=true uvec4d.md linguist-generated=true diff --git a/docs/content/reference/types/datatypes/entity_path.md b/docs/content/reference/types/datatypes/entity_path.md new file mode 100644 index 000000000000..f53ee1b71e41 --- /dev/null +++ b/docs/content/reference/types/datatypes/entity_path.md @@ -0,0 +1,16 @@ +--- +title: "EntityPath" +--- + +A path to an entity in the `DataStore`. + + +## Links + * 🌊 [C++ API docs for `EntityPath`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1datatypes_1_1EntityPath.html?speculative-link) + * 🐍 [Python API docs for `EntityPath`](https://ref.rerun.io/docs/python/stable/common/datatypes?speculative-link#rerun.datatypes.EntityPath) + * 🦀 [Rust API docs for `EntityPath`](https://docs.rs/rerun/latest/rerun/datatypes/struct.EntityPath.html?speculative-link) + + +## Used by + +* [`SpaceViewComponent`](../blueprint/space_view_component.md?speculative-link) diff --git a/docs/content/reference/types/datatypes/uuid.md b/docs/content/reference/types/datatypes/uuid.md new file mode 100644 index 000000000000..5e6bfa39e4c2 --- /dev/null +++ b/docs/content/reference/types/datatypes/uuid.md @@ -0,0 +1,16 @@ +--- +title: "Uuid" +--- + +A 16-byte uuid. + + +## Links + * 🌊 [C++ API docs for `Uuid`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1datatypes_1_1Uuid.html?speculative-link) + * 🐍 [Python API docs for `Uuid`](https://ref.rerun.io/docs/python/stable/common/datatypes?speculative-link#rerun.datatypes.Uuid) + * 🦀 [Rust API docs for `Uuid`](https://docs.rs/rerun/latest/rerun/datatypes/struct.Uuid.html?speculative-link) + + +## Used by + +* [`SpaceViewComponent`](../blueprint/space_view_component.md?speculative-link) diff --git a/rerun_cpp/src/rerun/blueprint/space_view_component.cpp b/rerun_cpp/src/rerun/blueprint/space_view_component.cpp index 4fc4752141fe..50937952cbcf 100644 --- a/rerun_cpp/src/rerun/blueprint/space_view_component.cpp +++ b/rerun_cpp/src/rerun/blueprint/space_view_component.cpp @@ -3,6 +3,9 @@ #include "space_view_component.hpp" +#include "../datatypes/entity_path.hpp" +#include "../datatypes/uuid.hpp" + #include #include @@ -12,9 +15,19 @@ namespace rerun { const std::shared_ptr& Loggable::arrow_datatype( ) { static const auto datatype = arrow::struct_({ + arrow::field("display_name", arrow::utf8(), false), + arrow::field("class_name", arrow::utf8(), false), + arrow::field( + "space_origin", + Loggable::arrow_datatype(), + false + ), + arrow::field("entities_determined_by_user", arrow::boolean(), false), arrow::field( - "space_view", - arrow::list(arrow::field("item", arrow::uint8(), false)), + "contents", + arrow::list( + arrow::field("item", Loggable::arrow_datatype(), false) + ), false ), }); @@ -36,19 +49,55 @@ namespace rerun { } { - auto field_builder = static_cast(builder->field_builder(0)); - auto value_builder = static_cast(field_builder->value_builder()); + auto field_builder = static_cast(builder->field_builder(0)); + ARROW_RETURN_NOT_OK(field_builder->Reserve(static_cast(num_elements))); + for (size_t elem_idx = 0; elem_idx < num_elements; elem_idx += 1) { + ARROW_RETURN_NOT_OK(field_builder->Append(elements[elem_idx].display_name)); + } + } + { + auto field_builder = static_cast(builder->field_builder(1)); + ARROW_RETURN_NOT_OK(field_builder->Reserve(static_cast(num_elements))); + for (size_t elem_idx = 0; elem_idx < num_elements; elem_idx += 1) { + ARROW_RETURN_NOT_OK(field_builder->Append(elements[elem_idx].class_name)); + } + } + { + auto field_builder = static_cast(builder->field_builder(2)); + ARROW_RETURN_NOT_OK(field_builder->Reserve(static_cast(num_elements))); + for (size_t elem_idx = 0; elem_idx < num_elements; elem_idx += 1) { + RR_RETURN_NOT_OK(Loggable::fill_arrow_array_builder( + field_builder, + &elements[elem_idx].space_origin, + 1 + )); + } + } + { + auto field_builder = static_cast(builder->field_builder(3)); + ARROW_RETURN_NOT_OK(field_builder->Reserve(static_cast(num_elements))); + for (size_t elem_idx = 0; elem_idx < num_elements; elem_idx += 1) { + ARROW_RETURN_NOT_OK( + field_builder->Append(elements[elem_idx].entities_determined_by_user) + ); + } + } + { + auto field_builder = static_cast(builder->field_builder(4)); + auto value_builder = static_cast(field_builder->value_builder()); ARROW_RETURN_NOT_OK(field_builder->Reserve(static_cast(num_elements))); ARROW_RETURN_NOT_OK(value_builder->Reserve(static_cast(num_elements * 2))); for (size_t elem_idx = 0; elem_idx < num_elements; elem_idx += 1) { const auto& element = elements[elem_idx]; ARROW_RETURN_NOT_OK(field_builder->Append()); - ARROW_RETURN_NOT_OK(value_builder->AppendValues( - element.space_view.data(), - static_cast(element.space_view.size()), - nullptr - )); + if (element.contents.data()) { + RR_RETURN_NOT_OK(Loggable::fill_arrow_array_builder( + value_builder, + element.contents.data(), + element.contents.size() + )); + } } } ARROW_RETURN_NOT_OK(builder->AppendValues(static_cast(num_elements), nullptr)); diff --git a/rerun_cpp/src/rerun/blueprint/space_view_component.hpp b/rerun_cpp/src/rerun/blueprint/space_view_component.hpp index 00279e3abdf7..588a451b6aad 100644 --- a/rerun_cpp/src/rerun/blueprint/space_view_component.hpp +++ b/rerun_cpp/src/rerun/blueprint/space_view_component.hpp @@ -4,11 +4,13 @@ #pragma once #include "../collection.hpp" +#include "../datatypes/entity_path.hpp" +#include "../datatypes/uuid.hpp" #include "../result.hpp" #include #include -#include +#include namespace arrow { class Array; @@ -21,18 +23,29 @@ namespace rerun::blueprint { /// /// Unstable. Used for the ongoing blueprint experimentations. struct SpaceViewComponent { - rerun::Collection space_view; + /// The name of the view. + std::string display_name; - public: - SpaceViewComponent() = default; + /// The class of the view. + std::string class_name; + + /// The "anchor point" of this space view. + /// + /// The transform at this path forms the reference point for all scene->world transforms in this space view. + /// I.e. the position of this entity path in space forms the origin of the coordinate system in this space view. + /// Furthermore, this is the primary indicator for heuristics on what entities we show in this space view. + rerun::datatypes::EntityPath space_origin; - SpaceViewComponent(rerun::Collection space_view_) - : space_view(std::move(space_view_)) {} + /// True if the user is expected to add entities themselves. False otherwise. + bool entities_determined_by_user; - SpaceViewComponent& operator=(rerun::Collection space_view_) { - space_view = std::move(space_view_); - return *this; - } + /// `BlueprintId`s of the `DataQuery`s that make up this `SpaceView`. + /// + /// It determines which entities are part of the spaceview. + rerun::Collection contents; + + public: + SpaceViewComponent() = default; }; } // namespace rerun::blueprint diff --git a/rerun_cpp/src/rerun/datatypes.hpp b/rerun_cpp/src/rerun/datatypes.hpp index db6ee81223d6..e53520c1aad4 100644 --- a/rerun_cpp/src/rerun/datatypes.hpp +++ b/rerun_cpp/src/rerun/datatypes.hpp @@ -7,6 +7,7 @@ #include "datatypes/class_description.hpp" #include "datatypes/class_description_map_elem.hpp" #include "datatypes/class_id.hpp" +#include "datatypes/entity_path.hpp" #include "datatypes/float32.hpp" #include "datatypes/keypoint_id.hpp" #include "datatypes/keypoint_pair.hpp" @@ -27,6 +28,7 @@ #include "datatypes/translation_rotation_scale3d.hpp" #include "datatypes/uint32.hpp" #include "datatypes/utf8.hpp" +#include "datatypes/uuid.hpp" #include "datatypes/uvec2d.hpp" #include "datatypes/uvec3d.hpp" #include "datatypes/uvec4d.hpp" diff --git a/rerun_cpp/src/rerun/datatypes/.gitattributes b/rerun_cpp/src/rerun/datatypes/.gitattributes index 2a5bb68060ed..e30cdfaeb468 100644 --- a/rerun_cpp/src/rerun/datatypes/.gitattributes +++ b/rerun_cpp/src/rerun/datatypes/.gitattributes @@ -11,6 +11,8 @@ class_description_map_elem.cpp linguist-generated=true class_description_map_elem.hpp linguist-generated=true class_id.cpp linguist-generated=true class_id.hpp linguist-generated=true +entity_path.cpp linguist-generated=true +entity_path.hpp linguist-generated=true float32.cpp linguist-generated=true float32.hpp linguist-generated=true keypoint_id.cpp linguist-generated=true @@ -51,6 +53,8 @@ uint32.cpp linguist-generated=true uint32.hpp linguist-generated=true utf8.cpp linguist-generated=true utf8.hpp linguist-generated=true +uuid.cpp linguist-generated=true +uuid.hpp linguist-generated=true uvec2d.cpp linguist-generated=true uvec2d.hpp linguist-generated=true uvec3d.cpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/datatypes/entity_path.cpp b/rerun_cpp/src/rerun/datatypes/entity_path.cpp new file mode 100644 index 000000000000..3e909dc9d7b4 --- /dev/null +++ b/rerun_cpp/src/rerun/datatypes/entity_path.cpp @@ -0,0 +1,57 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/re_types/definitions/rerun/datatypes/entity_path.fbs". + +#include "entity_path.hpp" + +#include +#include + +namespace rerun::datatypes {} + +namespace rerun { + const std::shared_ptr& Loggable::arrow_datatype() { + static const auto datatype = arrow::utf8(); + return datatype; + } + + rerun::Error Loggable::fill_arrow_array_builder( + arrow::StringBuilder* builder, const datatypes::EntityPath* elements, size_t num_elements + ) { + if (builder == nullptr) { + return rerun::Error(ErrorCode::UnexpectedNullArgument, "Passed array builder is null."); + } + if (elements == nullptr) { + return rerun::Error( + ErrorCode::UnexpectedNullArgument, + "Cannot serialize null pointer to arrow array." + ); + } + + ARROW_RETURN_NOT_OK(builder->Reserve(static_cast(num_elements))); + for (size_t elem_idx = 0; elem_idx < num_elements; elem_idx += 1) { + ARROW_RETURN_NOT_OK(builder->Append(elements[elem_idx].path)); + } + + return Error::ok(); + } + + Result> Loggable::to_arrow( + const datatypes::EntityPath* instances, size_t num_instances + ) { + // TODO(andreas): Allow configuring the memory pool. + arrow::MemoryPool* pool = arrow::default_memory_pool(); + auto datatype = arrow_datatype(); + + ARROW_ASSIGN_OR_RAISE(auto builder, arrow::MakeBuilder(datatype, pool)) + if (instances && num_instances > 0) { + RR_RETURN_NOT_OK(Loggable::fill_arrow_array_builder( + static_cast(builder.get()), + instances, + num_instances + )); + } + std::shared_ptr array; + ARROW_RETURN_NOT_OK(builder->Finish(&array)); + return array; + } +} // namespace rerun diff --git a/rerun_cpp/src/rerun/datatypes/entity_path.hpp b/rerun_cpp/src/rerun/datatypes/entity_path.hpp new file mode 100644 index 000000000000..c459109f2fb1 --- /dev/null +++ b/rerun_cpp/src/rerun/datatypes/entity_path.hpp @@ -0,0 +1,59 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/re_types/definitions/rerun/datatypes/entity_path.fbs". + +#pragma once + +#include "../result.hpp" + +#include +#include +#include +#include + +namespace arrow { + class Array; + class DataType; + class StringBuilder; +} // namespace arrow + +namespace rerun::datatypes { + /// **Datatype**: A path to an entity in the `DataStore`. + struct EntityPath { + std::string path; + + public: + EntityPath() = default; + + EntityPath(std::string path_) : path(std::move(path_)) {} + + EntityPath& operator=(std::string path_) { + path = std::move(path_); + return *this; + } + }; +} // namespace rerun::datatypes + +namespace rerun { + template + struct Loggable; + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.datatypes.EntityPath"; + + /// Returns the arrow data type this type corresponds to. + static const std::shared_ptr& arrow_datatype(); + + /// Fills an arrow array builder with an array of this type. + static rerun::Error fill_arrow_array_builder( + arrow::StringBuilder* builder, const datatypes::EntityPath* elements, + size_t num_elements + ); + + /// Serializes an array of `rerun::datatypes::EntityPath` into an arrow array. + static Result> to_arrow( + const datatypes::EntityPath* instances, size_t num_instances + ); + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/datatypes/uuid.cpp b/rerun_cpp/src/rerun/datatypes/uuid.cpp new file mode 100644 index 000000000000..44634d3764c3 --- /dev/null +++ b/rerun_cpp/src/rerun/datatypes/uuid.cpp @@ -0,0 +1,73 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/re_types/definitions/rerun/datatypes/uuid.fbs". + +#include "uuid.hpp" + +#include +#include + +namespace rerun::datatypes {} + +namespace rerun { + const std::shared_ptr& Loggable::arrow_datatype() { + static const auto datatype = arrow::struct_({ + arrow::field( + "bytes", + arrow::fixed_size_list(arrow::field("item", arrow::uint8(), false), 16), + false + ), + }); + return datatype; + } + + rerun::Error Loggable::fill_arrow_array_builder( + arrow::StructBuilder* builder, const datatypes::Uuid* elements, size_t num_elements + ) { + if (builder == nullptr) { + return rerun::Error(ErrorCode::UnexpectedNullArgument, "Passed array builder is null."); + } + if (elements == nullptr) { + return rerun::Error( + ErrorCode::UnexpectedNullArgument, + "Cannot serialize null pointer to arrow array." + ); + } + + { + auto field_builder = + static_cast(builder->field_builder(0)); + auto value_builder = static_cast(field_builder->value_builder()); + + ARROW_RETURN_NOT_OK(field_builder->AppendValues(static_cast(num_elements))); + static_assert(sizeof(elements[0].bytes) == sizeof(elements[0])); + ARROW_RETURN_NOT_OK(value_builder->AppendValues( + elements[0].bytes.data(), + static_cast(num_elements * 16), + nullptr + )); + } + ARROW_RETURN_NOT_OK(builder->AppendValues(static_cast(num_elements), nullptr)); + + return Error::ok(); + } + + Result> Loggable::to_arrow( + const datatypes::Uuid* instances, size_t num_instances + ) { + // TODO(andreas): Allow configuring the memory pool. + arrow::MemoryPool* pool = arrow::default_memory_pool(); + auto datatype = arrow_datatype(); + + ARROW_ASSIGN_OR_RAISE(auto builder, arrow::MakeBuilder(datatype, pool)) + if (instances && num_instances > 0) { + RR_RETURN_NOT_OK(Loggable::fill_arrow_array_builder( + static_cast(builder.get()), + instances, + num_instances + )); + } + std::shared_ptr array; + ARROW_RETURN_NOT_OK(builder->Finish(&array)); + return array; + } +} // namespace rerun diff --git a/rerun_cpp/src/rerun/datatypes/uuid.hpp b/rerun_cpp/src/rerun/datatypes/uuid.hpp new file mode 100644 index 000000000000..ebf26236bfa7 --- /dev/null +++ b/rerun_cpp/src/rerun/datatypes/uuid.hpp @@ -0,0 +1,57 @@ +// DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/re_types/definitions/rerun/datatypes/uuid.fbs". + +#pragma once + +#include "../result.hpp" + +#include +#include +#include + +namespace arrow { + class Array; + class DataType; + class StructBuilder; +} // namespace arrow + +namespace rerun::datatypes { + /// **Datatype**: A 16-byte uuid. + struct Uuid { + std::array bytes; + + public: + Uuid() = default; + + Uuid(std::array bytes_) : bytes(bytes_) {} + + Uuid& operator=(std::array bytes_) { + bytes = bytes_; + return *this; + } + }; +} // namespace rerun::datatypes + +namespace rerun { + template + struct Loggable; + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.datatypes.Uuid"; + + /// Returns the arrow data type this type corresponds to. + static const std::shared_ptr& arrow_datatype(); + + /// Fills an arrow array builder with an array of this type. + static rerun::Error fill_arrow_array_builder( + arrow::StructBuilder* builder, const datatypes::Uuid* elements, size_t num_elements + ); + + /// Serializes an array of `rerun::datatypes::Uuid` into an arrow array. + static Result> to_arrow( + const datatypes::Uuid* instances, size_t num_instances + ); + }; +} // namespace rerun diff --git a/rerun_py/rerun_sdk/rerun/blueprint/space_view_component.py b/rerun_py/rerun_sdk/rerun/blueprint/space_view_component.py index 3290cc13c532..83c4ebb90747 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/space_view_component.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/space_view_component.py @@ -7,14 +7,10 @@ from typing import Any, Sequence, Union -import numpy as np -import numpy.typing as npt from attrs import define, field +from .. import datatypes from .._baseclasses import BaseBatch, BaseExtensionType -from .._converters import ( - to_np_uint8, -) __all__ = [ "SpaceViewComponent", @@ -25,6 +21,15 @@ ] +def _space_view_component__space_origin__special_field_converter_override( + x: datatypes.EntityPathLike +) -> datatypes.EntityPath: + if isinstance(x, datatypes.EntityPath): + return x + else: + return datatypes.EntityPath(x) + + @define(init=False) class SpaceViewComponent: """ @@ -33,17 +38,78 @@ class SpaceViewComponent: Unstable. Used for the ongoing blueprint experimentations. """ - def __init__(self: Any, space_view: SpaceViewComponentLike): - """Create a new instance of the SpaceViewComponent blueprint.""" + def __init__( + self: Any, + display_name: str, + class_name: str, + space_origin: datatypes.EntityPathLike, + entities_determined_by_user: bool, + contents: datatypes.UuidArrayLike, + ): + """ + Create a new instance of the SpaceViewComponent blueprint. + + Parameters + ---------- + display_name: + The name of the view. + class_name: + The class of the view. + space_origin: + The "anchor point" of this space view. + + The transform at this path forms the reference point for all scene->world transforms in this space view. + I.e. the position of this entity path in space forms the origin of the coordinate system in this space view. + Furthermore, this is the primary indicator for heuristics on what entities we show in this space view. + entities_determined_by_user: + True if the user is expected to add entities themselves. False otherwise. + contents: + `BlueprintId`s of the `DataQuery`s that make up this `SpaceView`. + + It determines which entities are part of the spaceview. + """ # You can define your own __init__ function as a member of SpaceViewComponentExt in space_view_component_ext.py - self.__attrs_init__(space_view=space_view) - - space_view: npt.NDArray[np.uint8] = field(converter=to_np_uint8) - - def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: - # You can define your own __array__ function as a member of SpaceViewComponentExt in space_view_component_ext.py - return np.asarray(self.space_view, dtype=dtype) + self.__attrs_init__( + display_name=display_name, + class_name=class_name, + space_origin=space_origin, + entities_determined_by_user=entities_determined_by_user, + contents=contents, + ) + + display_name: str = field(converter=str) + # The name of the view. + # + # (Docstring intentionally commented out to hide this field from the docs) + + class_name: str = field(converter=str) + # The class of the view. + # + # (Docstring intentionally commented out to hide this field from the docs) + + space_origin: datatypes.EntityPath = field( + converter=_space_view_component__space_origin__special_field_converter_override + ) + # The "anchor point" of this space view. + # + # The transform at this path forms the reference point for all scene->world transforms in this space view. + # I.e. the position of this entity path in space forms the origin of the coordinate system in this space view. + # Furthermore, this is the primary indicator for heuristics on what entities we show in this space view. + # + # (Docstring intentionally commented out to hide this field from the docs) + + entities_determined_by_user: bool = field(converter=bool) + # True if the user is expected to add entities themselves. False otherwise. + # + # (Docstring intentionally commented out to hide this field from the docs) + + contents: list[datatypes.Uuid] = field() + # `BlueprintId`s of the `DataQuery`s that make up this `SpaceView`. + # + # It determines which entities are part of the spaceview. + # + # (Docstring intentionally commented out to hide this field from the docs) SpaceViewComponentLike = SpaceViewComponent diff --git a/rerun_py/rerun_sdk/rerun/datatypes/.gitattributes b/rerun_py/rerun_sdk/rerun/datatypes/.gitattributes index 5d1fe077b76f..c461d2e7028a 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/datatypes/.gitattributes @@ -7,6 +7,7 @@ annotation_info.py linguist-generated=true class_description.py linguist-generated=true class_description_map_elem.py linguist-generated=true class_id.py linguist-generated=true +entity_path.py linguist-generated=true float32.py linguist-generated=true keypoint_id.py linguist-generated=true keypoint_pair.py linguist-generated=true @@ -27,6 +28,7 @@ translation_and_mat3x3.py linguist-generated=true translation_rotation_scale3d.py linguist-generated=true uint32.py linguist-generated=true utf8.py linguist-generated=true +uuid.py linguist-generated=true uvec2d.py linguist-generated=true uvec3d.py linguist-generated=true uvec4d.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/datatypes/__init__.py b/rerun_py/rerun_sdk/rerun/datatypes/__init__.py index aa7a383709e2..89743577400d 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/__init__.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/__init__.py @@ -25,6 +25,7 @@ ClassDescriptionMapElemType, ) from .class_id import ClassId, ClassIdArrayLike, ClassIdBatch, ClassIdLike, ClassIdType +from .entity_path import EntityPath, EntityPathArrayLike, EntityPathBatch, EntityPathLike, EntityPathType from .float32 import Float32, Float32ArrayLike, Float32Batch, Float32Like, Float32Type from .keypoint_id import KeypointId, KeypointIdArrayLike, KeypointIdBatch, KeypointIdLike, KeypointIdType from .keypoint_pair import KeypointPair, KeypointPairArrayLike, KeypointPairBatch, KeypointPairLike, KeypointPairType @@ -75,6 +76,7 @@ ) from .uint32 import UInt32, UInt32ArrayLike, UInt32Batch, UInt32Like, UInt32Type from .utf8 import Utf8, Utf8ArrayLike, Utf8Batch, Utf8Like, Utf8Type +from .uuid import Uuid, UuidArrayLike, UuidBatch, UuidLike, UuidType from .uvec2d import UVec2D, UVec2DArrayLike, UVec2DBatch, UVec2DLike, UVec2DType from .uvec3d import UVec3D, UVec3DArrayLike, UVec3DBatch, UVec3DLike, UVec3DType from .uvec4d import UVec4D, UVec4DArrayLike, UVec4DBatch, UVec4DLike, UVec4DType @@ -108,6 +110,11 @@ "ClassIdBatch", "ClassIdLike", "ClassIdType", + "EntityPath", + "EntityPathArrayLike", + "EntityPathBatch", + "EntityPathLike", + "EntityPathType", "Float32", "Float32ArrayLike", "Float32Batch", @@ -223,6 +230,11 @@ "Utf8Batch", "Utf8Like", "Utf8Type", + "Uuid", + "UuidArrayLike", + "UuidBatch", + "UuidLike", + "UuidType", "Vec2D", "Vec2DArrayLike", "Vec2DBatch", diff --git a/rerun_py/rerun_sdk/rerun/datatypes/entity_path.py b/rerun_py/rerun_sdk/rerun/datatypes/entity_path.py new file mode 100644 index 000000000000..518b9cc96e36 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/datatypes/entity_path.py @@ -0,0 +1,53 @@ +# DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/python.rs +# Based on "crates/re_types/definitions/rerun/datatypes/entity_path.fbs". + +# You can extend this class by creating a "EntityPathExt" class in "entity_path_ext.py". + +from __future__ import annotations + +from typing import Any, Sequence, Union + +import pyarrow as pa +from attrs import define, field + +from .._baseclasses import BaseBatch, BaseExtensionType + +__all__ = ["EntityPath", "EntityPathArrayLike", "EntityPathBatch", "EntityPathLike", "EntityPathType"] + + +@define(init=False) +class EntityPath: + """**Datatype**: A path to an entity in the `DataStore`.""" + + def __init__(self: Any, path: EntityPathLike): + """Create a new instance of the EntityPath datatype.""" + + # You can define your own __init__ function as a member of EntityPathExt in entity_path_ext.py + self.__attrs_init__(path=path) + + path: str = field(converter=str) + + def __str__(self) -> str: + return str(self.path) + + +EntityPathLike = EntityPath +EntityPathArrayLike = Union[ + EntityPath, + Sequence[EntityPathLike], +] + + +class EntityPathType(BaseExtensionType): + _TYPE_NAME: str = "rerun.datatypes.EntityPath" + + def __init__(self) -> None: + pa.ExtensionType.__init__(self, pa.utf8(), self._TYPE_NAME) + + +class EntityPathBatch(BaseBatch[EntityPathArrayLike]): + _ARROW_TYPE = EntityPathType() + + @staticmethod + def _native_to_pa_array(data: EntityPathArrayLike, data_type: pa.DataType) -> pa.Array: + raise NotImplementedError # You need to implement native_to_pa_array_override in entity_path_ext.py diff --git a/rerun_py/rerun_sdk/rerun/datatypes/uuid.py b/rerun_py/rerun_sdk/rerun/datatypes/uuid.py new file mode 100644 index 000000000000..337ab71206ff --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/datatypes/uuid.py @@ -0,0 +1,72 @@ +# DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/python.rs +# Based on "crates/re_types/definitions/rerun/datatypes/uuid.fbs". + +# You can extend this class by creating a "UuidExt" class in "uuid_ext.py". + +from __future__ import annotations + +from typing import Any, Sequence, Union + +import numpy as np +import numpy.typing as npt +import pyarrow as pa +from attrs import define, field + +from .._baseclasses import BaseBatch, BaseExtensionType +from .._converters import ( + to_np_uint8, +) + +__all__ = ["Uuid", "UuidArrayLike", "UuidBatch", "UuidLike", "UuidType"] + + +@define(init=False) +class Uuid: + """**Datatype**: A 16-byte uuid.""" + + def __init__(self: Any, bytes: UuidLike): + """Create a new instance of the Uuid datatype.""" + + # You can define your own __init__ function as a member of UuidExt in uuid_ext.py + self.__attrs_init__(bytes=bytes) + + bytes: npt.NDArray[np.uint8] = field(converter=to_np_uint8) + + def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: + # You can define your own __array__ function as a member of UuidExt in uuid_ext.py + return np.asarray(self.bytes, dtype=dtype) + + +UuidLike = Uuid +UuidArrayLike = Union[ + Uuid, + Sequence[UuidLike], +] + + +class UuidType(BaseExtensionType): + _TYPE_NAME: str = "rerun.datatypes.Uuid" + + def __init__(self) -> None: + pa.ExtensionType.__init__( + self, + pa.struct( + [ + pa.field( + "bytes", + pa.list_(pa.field("item", pa.uint8(), nullable=False, metadata={}), 16), + nullable=False, + metadata={}, + ) + ] + ), + self._TYPE_NAME, + ) + + +class UuidBatch(BaseBatch[UuidArrayLike]): + _ARROW_TYPE = UuidType() + + @staticmethod + def _native_to_pa_array(data: UuidArrayLike, data_type: pa.DataType) -> pa.Array: + raise NotImplementedError # You need to implement native_to_pa_array_override in uuid_ext.py diff --git a/rerun_py/src/python_bridge.rs b/rerun_py/src/python_bridge.rs index bf6c26190601..59e1f7047bde 100644 --- a/rerun_py/src/python_bridge.rs +++ b/rerun_py/src/python_bridge.rs @@ -11,8 +11,9 @@ use pyo3::{ types::{PyBytes, PyDict}, }; -use re_viewer_context::SpaceViewId; -use re_viewport::{blueprint::SpaceViewComponent, SpaceViewBlueprint, VIEWPORT_PATH}; +//use re_viewer_context::SpaceViewId; +//use re_viewport::{SpaceViewBlueprint, VIEWPORT_PATH}; +use re_viewport::VIEWPORT_PATH; use re_log_types::{DataRow, StoreKind}; use rerun::{ @@ -761,12 +762,17 @@ fn set_panel(entity_path: &str, is_expanded: bool, blueprint: Option<&PyRecordin #[pyfunction] fn add_space_view( - name: &str, - space_view_class: &str, - origin: &str, - entity_paths: Vec<&str>, - blueprint: Option<&PyRecordingStream>, -) { + _name: &str, + _space_view_class: &str, + _origin: &str, + _entity_paths: Vec<&str>, + _blueprint: Option<&PyRecordingStream>, +) -> PyResult<()> { + Err(PyRuntimeError::new_err( + "add_space_view is broken until blueprint refactoring is complete: https://github.com/rerun-io/rerun/issues/4167", + )) + + /* let Some(blueprint) = get_blueprint_recording(blueprint) else { return; }; @@ -798,6 +804,7 @@ fn add_space_view( // TODO(jleibs) timeless? Something else? let timeless = true; blueprint.record_row(row, !timeless); + */ } #[pyfunction]