From e66438b9fbfe0c1e654c09973abcc5c63ed65645 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 8 Mar 2024 10:00:49 +0100 Subject: [PATCH] Improved tracking of which space views were generated by a heuristic (#5419) ### What * Fixes #5404 We now track haches of all "recommended" space views that we spawned so far. Seems to work much more reliably so far than anything else we had so far. Natrually, I kept the "is this is a redundant space view" logic since it's still useful to have around both to figure duplicates within a set of recommendation and to figure out if user created space views cover what the heuristic suggests. ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using newly built examples: [app.rerun.io](https://app.rerun.io/pr/5419/index.html) * Using examples from latest `main` build: [app.rerun.io](https://app.rerun.io/pr/5419/index.html?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [app.rerun.io](https://app.rerun.io/pr/5419/index.html?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! - [PR Build Summary](https://build.rerun.io/pr/5419) - [Docs preview](https://rerun.io/preview/bd21f004abd144d1e57e7f12a7dd9f0bbfc57bf5/docs) - [Examples preview](https://rerun.io/preview/bd21f004abd144d1e57e7f12a7dd9f0bbfc57bf5/examples) - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) --- .../src/path/entity_path_filter.rs | 12 ++ .../re_space_view/src/space_view_contents.rs | 25 +-- .../re_types/definitions/rerun/blueprint.fbs | 6 +- .../archetypes/space_view_contents.fbs | 7 - .../archetypes/viewport_blueprint.fbs | 18 +- .../entities_determined_by_user.fbs | 22 -- .../components/viewer_recommendation_hash.fbs | 23 +++ .../re_types/definitions/rerun/datatypes.fbs | 1 + .../definitions/rerun/datatypes/uint32.fbs | 2 + .../definitions/rerun/datatypes/uint64.fbs | 20 ++ .../archetypes/space_view_contents.rs | 53 +---- .../src/blueprint/components/.gitattributes | 2 +- .../re_types/src/blueprint/components/mod.rs | 4 +- .../components/viewer_recommendation_hash.rs | 189 ++++++++++++++++++ .../src/datatypes/.gitattributes | 1 + crates/re_types_core/src/datatypes/mod.rs | 2 + .../src/datatypes/uint64.rs} | 56 +++--- .../src/blueprint/validation_gen/mod.rs | 4 +- .../src/space_view/spawn_heuristics.rs | 1 + .../archetypes/viewport_blueprint.rs | 64 +++++- crates/re_viewport/src/viewport.rs | 63 +----- crates/re_viewport/src/viewport_blueprint.rs | 182 ++++++++++++----- docs/content/reference/types/datatypes.md | 1 + .../reference/types/datatypes/.gitattributes | 1 + .../reference/types/datatypes/uint64.md | 16 ++ .../archetypes/space_view_contents.cpp | 7 +- .../archetypes/space_view_contents.hpp | 20 -- .../archetypes/viewport_blueprint.cpp | 7 +- .../archetypes/viewport_blueprint.hpp | 37 +++- rerun_cpp/src/rerun/blueprint/components.hpp | 2 +- .../rerun/blueprint/components/.gitattributes | 4 +- .../entities_determined_by_user.cpp | 64 ------ .../entities_determined_by_user.hpp | 57 ------ .../components/viewer_recommendation_hash.cpp | 60 ++++++ .../components/viewer_recommendation_hash.hpp | 77 +++++++ rerun_cpp/src/rerun/datatypes.hpp | 1 + rerun_cpp/src/rerun/datatypes/.gitattributes | 2 + rerun_cpp/src/rerun/datatypes/uint64.cpp | 57 ++++++ rerun_cpp/src/rerun/datatypes/uint64.hpp | 61 ++++++ .../archetypes/space_view_contents.py | 25 +-- .../archetypes/viewport_blueprint.py | 36 +++- .../rerun/blueprint/components/.gitattributes | 2 +- .../rerun/blueprint/components/__init__.py | 20 +- .../components/entities_determined_by_user.py | 64 ------ .../entities_determined_by_user_ext.py | 18 -- .../components/viewer_recommendation_hash.py | 32 +++ .../rerun_sdk/rerun/datatypes/.gitattributes | 1 + .../rerun_sdk/rerun/datatypes/__init__.py | 6 + rerun_py/rerun_sdk/rerun/datatypes/uint32.py | 18 +- .../rerun_sdk/rerun/datatypes/uint32_ext.py | 18 ++ rerun_py/rerun_sdk/rerun/datatypes/uint64.py | 61 ++++++ .../rerun_sdk/rerun/datatypes/uint64_ext.py | 18 ++ .../tests/unit/test_space_view_contents.py | 21 +- .../tests/unit/test_viewport_blueprint.py | 39 +++- 54 files changed, 1059 insertions(+), 551 deletions(-) delete mode 100644 crates/re_types/definitions/rerun/blueprint/components/entities_determined_by_user.fbs create mode 100644 crates/re_types/definitions/rerun/blueprint/components/viewer_recommendation_hash.fbs create mode 100644 crates/re_types/definitions/rerun/datatypes/uint64.fbs create mode 100644 crates/re_types/src/blueprint/components/viewer_recommendation_hash.rs rename crates/{re_types/src/blueprint/components/entities_determined_by_user.rs => re_types_core/src/datatypes/uint64.rs} (65%) create mode 100644 docs/content/reference/types/datatypes/uint64.md delete mode 100644 rerun_cpp/src/rerun/blueprint/components/entities_determined_by_user.cpp delete mode 100644 rerun_cpp/src/rerun/blueprint/components/entities_determined_by_user.hpp create mode 100644 rerun_cpp/src/rerun/blueprint/components/viewer_recommendation_hash.cpp create mode 100644 rerun_cpp/src/rerun/blueprint/components/viewer_recommendation_hash.hpp create mode 100644 rerun_cpp/src/rerun/datatypes/uint64.cpp create mode 100644 rerun_cpp/src/rerun/datatypes/uint64.hpp delete mode 100644 rerun_py/rerun_sdk/rerun/blueprint/components/entities_determined_by_user.py delete mode 100644 rerun_py/rerun_sdk/rerun/blueprint/components/entities_determined_by_user_ext.py create mode 100644 rerun_py/rerun_sdk/rerun/blueprint/components/viewer_recommendation_hash.py create mode 100644 rerun_py/rerun_sdk/rerun/datatypes/uint32_ext.py create mode 100644 rerun_py/rerun_sdk/rerun/datatypes/uint64.py create mode 100644 rerun_py/rerun_sdk/rerun/datatypes/uint64_ext.py diff --git a/crates/re_log_types/src/path/entity_path_filter.rs b/crates/re_log_types/src/path/entity_path_filter.rs index 57872810f6dc..557999d0184a 100644 --- a/crates/re_log_types/src/path/entity_path_filter.rs +++ b/crates/re_log_types/src/path/entity_path_filter.rs @@ -40,6 +40,18 @@ pub struct EntityPathFilter { rules: BTreeMap, } +impl std::hash::Hash for EntityPathFilter { + fn hash(&self, state: &mut H) { + self.formatted().hash(state); + } +} + +impl std::fmt::Debug for EntityPathFilter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "EntityPathFilter({:?})", self.formatted()) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct EntityPathRule { pub path: EntityPath, diff --git a/crates/re_space_view/src/space_view_contents.rs b/crates/re_space_view/src/space_view_contents.rs index 8894313ca14a..7254883e89ff 100644 --- a/crates/re_space_view/src/space_view_contents.rs +++ b/crates/re_space_view/src/space_view_contents.rs @@ -8,10 +8,7 @@ use re_entity_db::{ }; use re_log_types::{path::RuleEffect, EntityPath, EntityPathFilter, EntityPathRule, StoreKind}; use re_types::{ - blueprint::{ - archetypes as blueprint_archetypes, - components::{EntitiesDeterminedByUser, QueryExpression}, - }, + blueprint::{archetypes as blueprint_archetypes, components::QueryExpression}, Archetype as _, }; use re_types_core::{components::VisualizerOverrides, ComponentName}; @@ -42,9 +39,6 @@ pub struct SpaceViewContents { pub space_view_class_identifier: SpaceViewClassIdentifier, pub entity_path_filter: EntityPathFilter, - - /// True if the user is expected to add entities themselves. False otherwise. - pub entities_determined_by_user: bool, } impl SpaceViewContents { @@ -94,7 +88,6 @@ impl SpaceViewContents { blueprint_entity_path, space_view_class_identifier, entity_path_filter, - entities_determined_by_user: false, } } @@ -109,10 +102,7 @@ impl SpaceViewContents { blueprint_archetypes::SpaceViewContents, >(id, blueprint_db, query); - let blueprint_archetypes::SpaceViewContents { - query, - entities_determined_by_user, - } = contents.unwrap_or_else(|err| { + let blueprint_archetypes::SpaceViewContents { query } = contents.unwrap_or_else(|err| { re_log::warn_once!( "Failed to load SpaceViewContents for {:?} from blueprint store at {:?}: {}", id, @@ -128,7 +118,6 @@ impl SpaceViewContents { blueprint_entity_path, space_view_class_identifier, entity_path_filter, - entities_determined_by_user: entities_determined_by_user.map_or(false, |b| b.0), } } @@ -141,8 +130,7 @@ impl SpaceViewContents { pub fn save_to_blueprint_store(&self, ctx: &ViewerContext<'_>) { ctx.save_blueprint_archetype( self.blueprint_entity_path.clone(), - &blueprint_archetypes::SpaceViewContents::new(self.entity_path_filter.formatted()) - .with_entities_determined_by_user(self.entities_determined_by_user), + &blueprint_archetypes::SpaceViewContents::new(self.entity_path_filter.formatted()), ); } @@ -159,13 +147,6 @@ impl SpaceViewContents { &self.blueprint_entity_path, &QueryExpression(new_entity_path_filter.formatted().into()), ); - - if !self.entities_determined_by_user { - ctx.save_blueprint_component( - &self.blueprint_entity_path, - &EntitiesDeterminedByUser(true), - ); - } } pub fn build_resolver<'a>( diff --git a/crates/re_types/definitions/rerun/blueprint.fbs b/crates/re_types/definitions/rerun/blueprint.fbs index 0cbf7422ad4f..d4613676c074 100644 --- a/crates/re_types/definitions/rerun/blueprint.fbs +++ b/crates/re_types/definitions/rerun/blueprint.fbs @@ -3,11 +3,12 @@ include "./blueprint/components/auto_layout.fbs"; include "./blueprint/components/auto_space_views.fbs"; include "./blueprint/components/column_share.fbs"; include "./blueprint/components/container_kind.fbs"; -include "./blueprint/components/entities_determined_by_user.fbs"; +include "./blueprint/components/corner_2d.fbs"; include "./blueprint/components/entity_properties_component.fbs"; include "./blueprint/components/grid_columns.fbs"; include "./blueprint/components/included_content.fbs"; include "./blueprint/components/included_space_view.fbs"; +include "./blueprint/components/lock_range_during_zoom.fbs"; include "./blueprint/components/panel_view.fbs"; include "./blueprint/components/query_expression.fbs"; include "./blueprint/components/root_container.fbs"; @@ -15,9 +16,8 @@ include "./blueprint/components/row_share.fbs"; include "./blueprint/components/space_view_class.fbs"; include "./blueprint/components/space_view_maximized.fbs"; include "./blueprint/components/space_view_origin.fbs"; +include "./blueprint/components/viewer_recommendation_hash.fbs"; include "./blueprint/components/visible.fbs"; -include "./blueprint/components/corner_2d.fbs"; -include "./blueprint/components/lock_range_during_zoom.fbs"; include "./blueprint/archetypes/container_blueprint.fbs"; include "./blueprint/archetypes/space_view_blueprint.fbs"; diff --git a/crates/re_types/definitions/rerun/blueprint/archetypes/space_view_contents.fbs b/crates/re_types/definitions/rerun/blueprint/archetypes/space_view_contents.fbs index 045d33ca0c82..e8da863a7cfb 100644 --- a/crates/re_types/definitions/rerun/blueprint/archetypes/space_view_contents.fbs +++ b/crates/re_types/definitions/rerun/blueprint/archetypes/space_view_contents.fbs @@ -18,11 +18,4 @@ table SpaceViewContents ( /// /// They determine which entities are part of the spaceview. query: rerun.blueprint.components.QueryExpression ("attr.rerun.component_required", order: 1000); - - // --- Optional --- - - /// True if the user is has added entities themselves. False otherwise. - /// - /// This is used by the viewer to determine whether it should regard this space view as created by the heuristic or not. - entities_determined_by_user: rerun.blueprint.components.EntitiesDeterminedByUser ("attr.rerun.component_optional", nullable, order: 2000); } diff --git a/crates/re_types/definitions/rerun/blueprint/archetypes/viewport_blueprint.fbs b/crates/re_types/definitions/rerun/blueprint/archetypes/viewport_blueprint.fbs index e36bd4a9629c..8a00279674e7 100644 --- a/crates/re_types/definitions/rerun/blueprint/archetypes/viewport_blueprint.fbs +++ b/crates/re_types/definitions/rerun/blueprint/archetypes/viewport_blueprint.fbs @@ -26,7 +26,7 @@ table ViewportBlueprint ( /// Show one tab as maximized? maximized: rerun.blueprint.components.SpaceViewMaximized ("attr.rerun.component_optional", nullable, order: 3000); - // TODO(andreas): This is to be removed in the future, all new space views without an explicit container + // TODO(andreas): This is to be removed in the future, all new Space Views without an explicit container // should always insert themselves using a heuristic. /// Whether the viewport layout is determined automatically. /// @@ -34,7 +34,19 @@ table ViewportBlueprint ( /// This defaults to `false` and is automatically set to `false` when there is user determined layout. auto_layout: rerun.blueprint.components.AutoLayout ("attr.rerun.component_optional", nullable, order: 4000); - // TODO(jleibs): This should come with an optional container id that specifies where to insert new space views. - /// Whether or not space views should be created automatically. + // TODO(jleibs): This should come with an optional container id that specifies where to insert new Space Views. + /// Whether or not Space Views should be created automatically. + /// + /// If `true`, the viewer will only add Space Views that it hasn't considered previously (as identified by `past_viewer_recommendations`) + /// and which aren't deemed redundant to existing Space Views. + /// This defaults to `false` and is automatically set to `false` when the user adds Space Views manually in the viewer. auto_space_views: rerun.blueprint.components.AutoSpaceViews ("attr.rerun.component_optional", nullable, order: 5000); + + /// Hashes of all recommended Space Views the viewer has already added and that should not be added again. + /// + /// This is an internal field and should not be set usually. + /// If you want the viewer from stopping to add Space Views, you should set `auto_space_views` to `false`. + /// + /// The viewer uses this to determine whether it should keep adding Space Views. + past_viewer_recommendations: [rerun.blueprint.components.ViewerRecommendationHash] ("attr.rerun.component_optional", nullable, order: 6000); } diff --git a/crates/re_types/definitions/rerun/blueprint/components/entities_determined_by_user.fbs b/crates/re_types/definitions/rerun/blueprint/components/entities_determined_by_user.fbs deleted file mode 100644 index 0b54cdb618fa..000000000000 --- a/crates/re_types/definitions/rerun/blueprint/components/entities_determined_by_user.fbs +++ /dev/null @@ -1,22 +0,0 @@ -include "arrow/attributes.fbs"; -include "python/attributes.fbs"; -include "rust/attributes.fbs"; - -include "rerun/datatypes.fbs"; -include "rerun/attributes.fbs"; - -namespace rerun.blueprint.components; - -// --- - -/// Whether the space view entities were manually edited. -table EntitiesDeterminedByUser ( - "attr.arrow.transparent", - "attr.rerun.scope": "blueprint", - "attr.python.aliases": "bool", - "attr.rust.derive": "Copy, Default, PartialEq, Eq, PartialOrd, Ord", - "attr.rust.repr": "transparent", - "attr.rust.tuple_struct" -) { - value: bool (order: 100); -} diff --git a/crates/re_types/definitions/rerun/blueprint/components/viewer_recommendation_hash.fbs b/crates/re_types/definitions/rerun/blueprint/components/viewer_recommendation_hash.fbs new file mode 100644 index 000000000000..6bcfa9c2753e --- /dev/null +++ b/crates/re_types/definitions/rerun/blueprint/components/viewer_recommendation_hash.fbs @@ -0,0 +1,23 @@ +include "arrow/attributes.fbs"; +include "python/attributes.fbs"; +include "rust/attributes.fbs"; + +include "rerun/datatypes.fbs"; +include "rerun/attributes.fbs"; + +namespace rerun.blueprint.components; + +// --- + +/// Hash of a viewer recommendation. +/// +/// The formation of this hash is considered an internal implementation detail of the viewer. +table ViewerRecommendationHash ( + "attr.arrow.transparent", + "attr.rerun.scope": "blueprint", + "attr.python.aliases": "str", + "attr.rust.derive": "PartialEq, Eq, PartialOrd, Ord", + "attr.rust.repr": "transparent" +) { + value: rerun.datatypes.UInt64 (order: 100); +} diff --git a/crates/re_types/definitions/rerun/datatypes.fbs b/crates/re_types/definitions/rerun/datatypes.fbs index cae6edc0caa6..9c70e334df00 100644 --- a/crates/re_types/definitions/rerun/datatypes.fbs +++ b/crates/re_types/definitions/rerun/datatypes.fbs @@ -23,6 +23,7 @@ include "./datatypes/transform3d.fbs"; include "./datatypes/translation_and_mat3x3.fbs"; include "./datatypes/translation_rotation_scale3d.fbs"; include "./datatypes/uint32.fbs"; +include "./datatypes/uint64.fbs"; include "./datatypes/utf8.fbs"; include "./datatypes/uuid.fbs"; include "./datatypes/uvec2d.fbs"; diff --git a/crates/re_types/definitions/rerun/datatypes/uint32.fbs b/crates/re_types/definitions/rerun/datatypes/uint32.fbs index a129265ff45c..fa3cd2d479b3 100644 --- a/crates/re_types/definitions/rerun/datatypes/uint32.fbs +++ b/crates/re_types/definitions/rerun/datatypes/uint32.fbs @@ -9,6 +9,8 @@ namespace rerun.datatypes; /// A 32bit unsigned integer. struct UInt32 ( "attr.arrow.transparent", + "attr.python.aliases": "int", + "attr.python.array_aliases": "int, npt.NDArray[np.uint32]", "attr.rust.derive": "Copy, PartialEq, Eq, PartialOrd, Ord", "attr.rust.override_crate": "re_types_core", "attr.rust.tuple_struct" diff --git a/crates/re_types/definitions/rerun/datatypes/uint64.fbs b/crates/re_types/definitions/rerun/datatypes/uint64.fbs new file mode 100644 index 000000000000..051c5107913b --- /dev/null +++ b/crates/re_types/definitions/rerun/datatypes/uint64.fbs @@ -0,0 +1,20 @@ +include "arrow/attributes.fbs"; +include "python/attributes.fbs"; +include "fbs/attributes.fbs"; +include "rust/attributes.fbs"; +include "docs/attributes.fbs"; + +namespace rerun.datatypes; + +/// A 64bit unsigned integer. +struct UInt64 ( + "attr.arrow.transparent", + "attr.python.aliases": "int", + "attr.python.array_aliases": "int, npt.NDArray[np.uint64]", + "attr.rust.derive": "Copy, PartialEq, Eq, PartialOrd, Ord", + "attr.rust.override_crate": "re_types_core", + "attr.rust.tuple_struct", + "attr.docs.unreleased" +) { + value: uint64 (order: 100); +} diff --git a/crates/re_types/src/blueprint/archetypes/space_view_contents.rs b/crates/re_types/src/blueprint/archetypes/space_view_contents.rs index f23be1489e2e..45a696c086cb 100644 --- a/crates/re_types/src/blueprint/archetypes/space_view_contents.rs +++ b/crates/re_types/src/blueprint/archetypes/space_view_contents.rs @@ -28,23 +28,17 @@ pub struct SpaceViewContents { /// /// They determine which entities are part of the spaceview. pub query: crate::blueprint::components::QueryExpression, - - /// True if the user is has added entities themselves. False otherwise. - /// - /// This is used by the viewer to determine whether it should regard this space view as created by the heuristic or not. - pub entities_determined_by_user: Option, } impl ::re_types_core::SizeBytes for SpaceViewContents { #[inline] fn heap_size_bytes(&self) -> u64 { - self.query.heap_size_bytes() + self.entities_determined_by_user.heap_size_bytes() + self.query.heap_size_bytes() } #[inline] fn is_pod() -> bool { ::is_pod() - && >::is_pod() } } @@ -54,26 +48,20 @@ static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = once_cell::sync::Lazy::new(|| ["rerun.blueprint.components.SpaceViewContentsIndicator".into()]); -static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 2usize]> = - once_cell::sync::Lazy::new(|| { - [ - "rerun.blueprint.components.EntitiesDeterminedByUser".into(), - "rerun.components.InstanceKey".into(), - ] - }); +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = + once_cell::sync::Lazy::new(|| ["rerun.components.InstanceKey".into()]); -static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 3usize]> = once_cell::sync::Lazy::new(|| { [ "rerun.blueprint.components.QueryExpression".into(), "rerun.blueprint.components.SpaceViewContentsIndicator".into(), - "rerun.blueprint.components.EntitiesDeterminedByUser".into(), "rerun.components.InstanceKey".into(), ] }); impl SpaceViewContents { - pub const NUM_COMPONENTS: usize = 4usize; + pub const NUM_COMPONENTS: usize = 3usize; } /// Indicator component for the [`SpaceViewContents`] [`::re_types_core::Archetype`] @@ -136,23 +124,7 @@ impl ::re_types_core::Archetype for SpaceViewContents { .ok_or_else(DeserializationError::missing_data) .with_context("rerun.blueprint.archetypes.SpaceViewContents#query")? }; - let entities_determined_by_user = if let Some(array) = - arrays_by_name.get("rerun.blueprint.components.EntitiesDeterminedByUser") - { - ::from_arrow_opt(&**array) - .with_context( - "rerun.blueprint.archetypes.SpaceViewContents#entities_determined_by_user", - )? - .into_iter() - .next() - .flatten() - } else { - None - }; - Ok(Self { - query, - entities_determined_by_user, - }) + Ok(Self { query }) } } @@ -163,9 +135,6 @@ impl ::re_types_core::AsComponents for SpaceViewContents { [ Some(Self::indicator()), Some((&self.query as &dyn ComponentBatch).into()), - self.entities_determined_by_user - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch).into()), ] .into_iter() .flatten() @@ -182,16 +151,6 @@ impl SpaceViewContents { pub fn new(query: impl Into) -> Self { Self { query: query.into(), - entities_determined_by_user: None, } } - - #[inline] - pub fn with_entities_determined_by_user( - mut self, - entities_determined_by_user: impl Into, - ) -> Self { - self.entities_determined_by_user = Some(entities_determined_by_user.into()); - self - } } diff --git a/crates/re_types/src/blueprint/components/.gitattributes b/crates/re_types/src/blueprint/components/.gitattributes index 78fa8bbf6a18..1c53f8775af7 100644 --- a/crates/re_types/src/blueprint/components/.gitattributes +++ b/crates/re_types/src/blueprint/components/.gitattributes @@ -4,7 +4,6 @@ active_tab.rs linguist-generated=true column_share.rs linguist-generated=true corner2d.rs linguist-generated=true -entities_determined_by_user.rs linguist-generated=true included_content.rs linguist-generated=true lock_range_during_zoom.rs linguist-generated=true mod.rs linguist-generated=true @@ -12,4 +11,5 @@ query_expression.rs linguist-generated=true row_share.rs linguist-generated=true space_view_class.rs linguist-generated=true space_view_origin.rs linguist-generated=true +viewer_recommendation_hash.rs linguist-generated=true visible.rs linguist-generated=true diff --git a/crates/re_types/src/blueprint/components/mod.rs b/crates/re_types/src/blueprint/components/mod.rs index 474b2d5d4305..f29b4819a347 100644 --- a/crates/re_types/src/blueprint/components/mod.rs +++ b/crates/re_types/src/blueprint/components/mod.rs @@ -4,23 +4,23 @@ mod active_tab; mod column_share; mod corner2d; mod corner2d_ext; -mod entities_determined_by_user; mod included_content; mod lock_range_during_zoom; mod query_expression; mod row_share; mod space_view_class; mod space_view_origin; +mod viewer_recommendation_hash; mod visible; pub use self::active_tab::ActiveTab; pub use self::column_share::ColumnShare; pub use self::corner2d::Corner2D; -pub use self::entities_determined_by_user::EntitiesDeterminedByUser; pub use self::included_content::IncludedContent; pub use self::lock_range_during_zoom::LockRangeDuringZoom; pub use self::query_expression::QueryExpression; pub use self::row_share::RowShare; pub use self::space_view_class::SpaceViewClass; pub use self::space_view_origin::SpaceViewOrigin; +pub use self::viewer_recommendation_hash::ViewerRecommendationHash; pub use self::visible::Visible; diff --git a/crates/re_types/src/blueprint/components/viewer_recommendation_hash.rs b/crates/re_types/src/blueprint/components/viewer_recommendation_hash.rs new file mode 100644 index 000000000000..db6d43f424a4 --- /dev/null +++ b/crates/re_types/src/blueprint/components/viewer_recommendation_hash.rs @@ -0,0 +1,189 @@ +// 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/components/viewer_recommendation_hash.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}; + +/// **Component**: Hash of a viewer recommendation. +/// +/// The formation of this hash is considered an internal implementation detail of the viewer. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct ViewerRecommendationHash(pub crate::datatypes::UInt64); + +impl ::re_types_core::SizeBytes for ViewerRecommendationHash { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.0.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + ::is_pod() + } +} + +impl> From for ViewerRecommendationHash { + fn from(v: T) -> Self { + Self(v.into()) + } +} + +impl std::borrow::Borrow for ViewerRecommendationHash { + #[inline] + fn borrow(&self) -> &crate::datatypes::UInt64 { + &self.0 + } +} + +impl std::ops::Deref for ViewerRecommendationHash { + type Target = crate::datatypes::UInt64; + + #[inline] + fn deref(&self) -> &crate::datatypes::UInt64 { + &self.0 + } +} + +::re_types_core::macros::impl_into_cow!(ViewerRecommendationHash); + +impl ::re_types_core::Loggable for ViewerRecommendationHash { + type Name = ::re_types_core::ComponentName; + + #[inline] + fn name() -> Self::Name { + "rerun.blueprint.components.ViewerRecommendationHash".into() + } + + #[allow(clippy::wildcard_imports)] + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + use arrow2::datatypes::*; + DataType::UInt64 + } + + #[allow(clippy::wildcard_imports)] + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + use ::re_types_core::{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()) + }; + PrimitiveArray::new( + Self::arrow_datatype(), + data0 + .into_iter() + .map(|datum| { + datum + .map(|datum| { + let crate::datatypes::UInt64(data0) = datum; + data0 + }) + .unwrap_or_default() + }) + .collect(), + data0_bitmap, + ) + .boxed() + }) + } + + #[allow(clippy::wildcard_imports)] + fn from_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + use ::re_types_core::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, buffer::*, datatypes::*}; + Ok(arrow_data + .as_any() + .downcast_ref::() + .ok_or_else(|| { + let expected = Self::arrow_datatype(); + let actual = arrow_data.data_type().clone(); + DeserializationError::datatype_mismatch(expected, actual) + }) + .with_context("rerun.blueprint.components.ViewerRecommendationHash#value")? + .into_iter() + .map(|opt| opt.copied()) + .map(|res_or_opt| res_or_opt.map(|v| crate::datatypes::UInt64(v))) + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .map(|res| res.map(|v| Some(Self(v)))) + .collect::>>>() + .with_context("rerun.blueprint.components.ViewerRecommendationHash#value") + .with_context("rerun.blueprint.components.ViewerRecommendationHash")?) + } + + #[allow(clippy::wildcard_imports)] + #[inline] + fn from_arrow(arrow_data: &dyn arrow2::array::Array) -> DeserializationResult> + where + Self: Sized, + { + use ::re_types_core::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, buffer::*, datatypes::*}; + if let Some(validity) = arrow_data.validity() { + if validity.unset_bits() != 0 { + return Err(DeserializationError::missing_data()); + } + } + Ok({ + let slice = arrow_data + .as_any() + .downcast_ref::() + .ok_or_else(|| { + let expected = DataType::UInt64; + let actual = arrow_data.data_type().clone(); + DeserializationError::datatype_mismatch(expected, actual) + }) + .with_context("rerun.blueprint.components.ViewerRecommendationHash#value")? + .values() + .as_slice(); + { + slice + .iter() + .copied() + .map(|v| crate::datatypes::UInt64(v)) + .map(|v| Self(v)) + .collect::>() + } + }) + } +} diff --git a/crates/re_types_core/src/datatypes/.gitattributes b/crates/re_types_core/src/datatypes/.gitattributes index 570fad02cada..5d1c22973176 100644 --- a/crates/re_types_core/src/datatypes/.gitattributes +++ b/crates/re_types_core/src/datatypes/.gitattributes @@ -5,4 +5,5 @@ entity_path.rs linguist-generated=true float32.rs linguist-generated=true mod.rs linguist-generated=true uint32.rs linguist-generated=true +uint64.rs linguist-generated=true utf8.rs linguist-generated=true diff --git a/crates/re_types_core/src/datatypes/mod.rs b/crates/re_types_core/src/datatypes/mod.rs index 5d1bbe547bb5..dd64cf5f950c 100644 --- a/crates/re_types_core/src/datatypes/mod.rs +++ b/crates/re_types_core/src/datatypes/mod.rs @@ -3,10 +3,12 @@ mod entity_path; mod float32; mod uint32; +mod uint64; mod utf8; mod utf8_ext; pub use self::entity_path::EntityPath; pub use self::float32::Float32; pub use self::uint32::UInt32; +pub use self::uint64::UInt64; pub use self::utf8::Utf8; diff --git a/crates/re_types/src/blueprint/components/entities_determined_by_user.rs b/crates/re_types_core/src/datatypes/uint64.rs similarity index 65% rename from crates/re_types/src/blueprint/components/entities_determined_by_user.rs rename to crates/re_types_core/src/datatypes/uint64.rs index 8c8419bb0dd8..73f270e9515e 100644 --- a/crates/re_types/src/blueprint/components/entities_determined_by_user.rs +++ b/crates/re_types_core/src/datatypes/uint64.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/components/entities_determined_by_user.fbs". +// Based on "crates/re_types/definitions/rerun/datatypes/uint64.fbs". #![allow(trivial_numeric_casts)] #![allow(unused_imports)] @@ -15,18 +15,17 @@ #![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}; +use crate::external::arrow2; +use crate::ComponentName; +use crate::SerializationResult; +use crate::{ComponentBatch, MaybeOwnedComponentBatch}; +use crate::{DeserializationError, DeserializationResult}; -/// **Component**: Whether the space view entities were manually edited. -#[derive(Clone, Debug, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] -#[repr(transparent)] -pub struct EntitiesDeterminedByUser(pub bool); +/// **Datatype**: A 64bit unsigned integer. +#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct UInt64(pub u64); -impl ::re_types_core::SizeBytes for EntitiesDeterminedByUser { +impl crate::SizeBytes for UInt64 { #[inline] fn heap_size_bytes(&self) -> u64 { self.0.heap_size_bytes() @@ -34,39 +33,39 @@ impl ::re_types_core::SizeBytes for EntitiesDeterminedByUser { #[inline] fn is_pod() -> bool { - ::is_pod() + ::is_pod() } } -impl From for EntitiesDeterminedByUser { +impl From for UInt64 { #[inline] - fn from(value: bool) -> Self { + fn from(value: u64) -> Self { Self(value) } } -impl From for bool { +impl From for u64 { #[inline] - fn from(value: EntitiesDeterminedByUser) -> Self { + fn from(value: UInt64) -> Self { value.0 } } -::re_types_core::macros::impl_into_cow!(EntitiesDeterminedByUser); +crate::macros::impl_into_cow!(UInt64); -impl ::re_types_core::Loggable for EntitiesDeterminedByUser { - type Name = ::re_types_core::ComponentName; +impl crate::Loggable for UInt64 { + type Name = crate::DatatypeName; #[inline] fn name() -> Self::Name { - "rerun.blueprint.components.EntitiesDeterminedByUser".into() + "rerun.datatypes.UInt64".into() } #[allow(clippy::wildcard_imports)] #[inline] fn arrow_datatype() -> arrow2::datatypes::DataType { use arrow2::datatypes::*; - DataType::Boolean + DataType::UInt64 } #[allow(clippy::wildcard_imports)] @@ -76,7 +75,7 @@ impl ::re_types_core::Loggable for EntitiesDeterminedByUser { where Self: Clone + 'a, { - use ::re_types_core::{Loggable as _, ResultExt as _}; + use crate::{Loggable as _, ResultExt as _}; use arrow2::{array::*, datatypes::*}; Ok({ let (somes, data0): (Vec<_>, Vec<_>) = data @@ -94,7 +93,7 @@ impl ::re_types_core::Loggable for EntitiesDeterminedByUser { let any_nones = somes.iter().any(|some| !*some); any_nones.then(|| somes.into()) }; - BooleanArray::new( + PrimitiveArray::new( Self::arrow_datatype(), data0.into_iter().map(|v| v.unwrap_or_default()).collect(), data0_bitmap, @@ -110,22 +109,23 @@ impl ::re_types_core::Loggable for EntitiesDeterminedByUser { where Self: Sized, { - use ::re_types_core::{Loggable as _, ResultExt as _}; + use crate::{Loggable as _, ResultExt as _}; use arrow2::{array::*, buffer::*, datatypes::*}; Ok(arrow_data .as_any() - .downcast_ref::() + .downcast_ref::() .ok_or_else(|| { let expected = Self::arrow_datatype(); let actual = arrow_data.data_type().clone(); DeserializationError::datatype_mismatch(expected, actual) }) - .with_context("rerun.blueprint.components.EntitiesDeterminedByUser#value")? + .with_context("rerun.datatypes.UInt64#value")? .into_iter() + .map(|opt| opt.copied()) .map(|v| v.ok_or_else(DeserializationError::missing_data)) .map(|res| res.map(|v| Some(Self(v)))) .collect::>>>() - .with_context("rerun.blueprint.components.EntitiesDeterminedByUser#value") - .with_context("rerun.blueprint.components.EntitiesDeterminedByUser")?) + .with_context("rerun.datatypes.UInt64#value") + .with_context("rerun.datatypes.UInt64")?) } } diff --git a/crates/re_viewer/src/blueprint/validation_gen/mod.rs b/crates/re_viewer/src/blueprint/validation_gen/mod.rs index 3cf19c55cb6d..cc7e7cb98c24 100644 --- a/crates/re_viewer/src/blueprint/validation_gen/mod.rs +++ b/crates/re_viewer/src/blueprint/validation_gen/mod.rs @@ -6,13 +6,13 @@ use re_entity_db::EntityDb; pub use re_types::blueprint::components::ActiveTab; pub use re_types::blueprint::components::ColumnShare; pub use re_types::blueprint::components::Corner2D; -pub use re_types::blueprint::components::EntitiesDeterminedByUser; pub use re_types::blueprint::components::IncludedContent; pub use re_types::blueprint::components::LockRangeDuringZoom; pub use re_types::blueprint::components::QueryExpression; pub use re_types::blueprint::components::RowShare; pub use re_types::blueprint::components::SpaceViewClass; pub use re_types::blueprint::components::SpaceViewOrigin; +pub use re_types::blueprint::components::ViewerRecommendationHash; pub use re_types::blueprint::components::Visible; pub use re_viewport::blueprint::components::AutoLayout; pub use re_viewport::blueprint::components::AutoSpaceViews; @@ -32,7 +32,6 @@ pub fn is_valid_blueprint(blueprint: &EntityDb) -> bool { && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) - && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) @@ -45,5 +44,6 @@ pub fn is_valid_blueprint(blueprint: &EntityDb) -> bool { && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) + && validate_component::(blueprint) && validate_component::(blueprint) } diff --git a/crates/re_viewer_context/src/space_view/spawn_heuristics.rs b/crates/re_viewer_context/src/space_view/spawn_heuristics.rs index 08a0e5ceda8a..628987c1603f 100644 --- a/crates/re_viewer_context/src/space_view/spawn_heuristics.rs +++ b/crates/re_viewer_context/src/space_view/spawn_heuristics.rs @@ -1,6 +1,7 @@ use re_log_types::{EntityPath, EntityPathFilter}; /// Properties of a space view that as recommended to be spawned by default via space view spawn heuristics. +#[derive(Hash, Debug)] pub struct RecommendedSpaceView { pub root: EntityPath, pub query_filter: EntityPathFilter, diff --git a/crates/re_viewport/src/blueprint/archetypes/viewport_blueprint.rs b/crates/re_viewport/src/blueprint/archetypes/viewport_blueprint.rs index 20288f5f519d..b902fbe52da0 100644 --- a/crates/re_viewport/src/blueprint/archetypes/viewport_blueprint.rs +++ b/crates/re_viewport/src/blueprint/archetypes/viewport_blueprint.rs @@ -39,8 +39,21 @@ pub struct ViewportBlueprint { /// This defaults to `false` and is automatically set to `false` when there is user determined layout. pub auto_layout: Option, - /// Whether or not space views should be created automatically. + /// Whether or not Space Views should be created automatically. + /// + /// If `true`, the viewer will only add Space Views that it hasn't considered previously (as identified by `past_viewer_recommendations`) + /// and which aren't deemed redundant to existing Space Views. + /// This defaults to `false` and is automatically set to `false` when the user adds Space Views manually in the viewer. pub auto_space_views: Option, + + /// Hashes of all recommended Space Views the viewer has already added and that should not be added again. + /// + /// This is an internal field and should not be set usually. + /// If you want the viewer from stopping to add Space Views, you should set `auto_space_views` to `false`. + /// + /// The viewer uses this to determine whether it should keep adding Space Views. + pub past_viewer_recommendations: + Option>, } impl ::re_types_core::SizeBytes for ViewportBlueprint { @@ -51,6 +64,7 @@ impl ::re_types_core::SizeBytes for ViewportBlueprint { + self.maximized.heap_size_bytes() + self.auto_layout.heap_size_bytes() + self.auto_space_views.heap_size_bytes() + + self.past_viewer_recommendations.heap_size_bytes() } #[inline] @@ -60,6 +74,7 @@ impl ::re_types_core::SizeBytes for ViewportBlueprint { && >::is_pod() && >::is_pod() && >::is_pod() + && >>::is_pod() } } @@ -69,18 +84,19 @@ static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = once_cell::sync::Lazy::new(|| ["rerun.blueprint.components.ViewportBlueprintIndicator".into()]); -static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 5usize]> = +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 6usize]> = once_cell::sync::Lazy::new(|| { [ "rerun.blueprint.components.AutoLayout".into(), "rerun.blueprint.components.AutoSpaceViews".into(), "rerun.blueprint.components.RootContainer".into(), "rerun.blueprint.components.SpaceViewMaximized".into(), + "rerun.blueprint.components.ViewerRecommendationHash".into(), "rerun.components.InstanceKey".into(), ] }); -static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 7usize]> = +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 8usize]> = once_cell::sync::Lazy::new(|| { [ "rerun.blueprint.components.IncludedSpaceView".into(), @@ -89,12 +105,13 @@ static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 7usize]> = "rerun.blueprint.components.AutoSpaceViews".into(), "rerun.blueprint.components.RootContainer".into(), "rerun.blueprint.components.SpaceViewMaximized".into(), + "rerun.blueprint.components.ViewerRecommendationHash".into(), "rerun.components.InstanceKey".into(), ] }); impl ViewportBlueprint { - pub const NUM_COMPONENTS: usize = 7usize; + pub const NUM_COMPONENTS: usize = 8usize; } /// Indicator component for the [`ViewportBlueprint`] [`::re_types_core::Archetype`] @@ -197,12 +214,31 @@ impl ::re_types_core::Archetype for ViewportBlueprint { } else { None }; + let past_viewer_recommendations = if let Some(array) = + arrays_by_name.get("rerun.blueprint.components.ViewerRecommendationHash") + { + Some({ + ::from_arrow_opt(&**array) + .with_context( + "rerun.blueprint.archetypes.ViewportBlueprint#past_viewer_recommendations", + )? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context( + "rerun.blueprint.archetypes.ViewportBlueprint#past_viewer_recommendations", + )? + }) + } else { + None + }; Ok(Self { space_views, root_container, maximized, auto_layout, auto_space_views, + past_viewer_recommendations, }) } } @@ -226,6 +262,9 @@ impl ::re_types_core::AsComponents for ViewportBlueprint { self.auto_space_views .as_ref() .map(|comp| (comp as &dyn ComponentBatch).into()), + self.past_viewer_recommendations + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), ] .into_iter() .flatten() @@ -250,6 +289,7 @@ impl ViewportBlueprint { maximized: None, auto_layout: None, auto_space_views: None, + past_viewer_recommendations: None, } } @@ -288,4 +328,20 @@ impl ViewportBlueprint { self.auto_space_views = Some(auto_space_views.into()); self } + + #[inline] + pub fn with_past_viewer_recommendations( + mut self, + past_viewer_recommendations: impl IntoIterator< + Item = impl Into, + >, + ) -> Self { + self.past_viewer_recommendations = Some( + past_viewer_recommendations + .into_iter() + .map(Into::into) + .collect(), + ); + self + } } diff --git a/crates/re_viewport/src/viewport.rs b/crates/re_viewport/src/viewport.rs index 5c065192f8df..473395323298 100644 --- a/crates/re_viewport/src/viewport.rs +++ b/crates/re_viewport/src/viewport.rs @@ -8,7 +8,6 @@ use once_cell::sync::Lazy; use re_entity_db::EntityPropertyMap; use re_renderer::ScreenshotProcessor; -use re_space_view::SpaceViewBlueprint; use re_ui::{Icon, ReUi}; use re_viewer_context::{ ContainerId, Item, SpaceViewClassIdentifier, SpaceViewClassRegistry, SpaceViewId, @@ -331,67 +330,7 @@ impl<'a, 'b> Viewport<'a, 'b> { space_view.on_frame_start(ctx, space_view_state.as_mut(), auto_properties); } - if self.blueprint.auto_space_views() { - self.spawn_heuristic_space_views(ctx); - } - } - - fn spawn_heuristic_space_views(&mut self, ctx: &ViewerContext<'_>) { - re_tracing::profile_function!(); - - for entry in ctx.space_view_class_registry.iter_registry() { - let class_id = entry.class.identifier(); - let spawn_heuristics = entry.class.spawn_heuristics(ctx); - - re_tracing::profile_scope!("filter_recommendations_for", class_id); - - // Remove all space views that we already have on screen. - let existing_path_filters = self - .blueprint - .space_views - .values() - .filter(|space_view| space_view.class_identifier() == &class_id) - .map(|space_view| &space_view.contents.entity_path_filter) - .collect::>(); - let recommended_space_views = spawn_heuristics - .recommended_space_views - .into_iter() - .filter(|recommended_view| { - existing_path_filters.iter().all(|existing_filter| { - !existing_filter.is_superset_of(&recommended_view.query_filter) - }) - }) - .collect::>(); - - // Remove all space views that are redundant within the remaining recommendation. - // This n^2 loop should only run ever for frames that add new space views. - let final_recommendations = - recommended_space_views - .iter() - .enumerate() - .filter(|(j, candidate)| { - recommended_space_views - .iter() - .enumerate() - .all(|(i, other)| { - i == *j - || !other.query_filter.is_superset_of(&candidate.query_filter) - }) - }); - - self.blueprint.add_space_views( - final_recommendations.map(|(_, recommendation)| { - SpaceViewBlueprint::new( - class_id, - &recommendation.root, - recommendation.query_filter.clone(), - ) - }), - ctx, - None, - None, - ); - } + self.blueprint.on_frame_start(ctx); } /// Process any deferred `TreeActions` and then sync to blueprint diff --git a/crates/re_viewport/src/viewport_blueprint.rs b/crates/re_viewport/src/viewport_blueprint.rs index a952c00da885..428a7979a90b 100644 --- a/crates/re_viewport/src/viewport_blueprint.rs +++ b/crates/re_viewport/src/viewport_blueprint.rs @@ -4,11 +4,17 @@ use std::sync::atomic::{AtomicBool, Ordering}; use ahash::HashMap; use egui_tiles::{SimplificationOptions, TileId}; +use nohash_hasher::IntSet; use re_data_store::LatestAtQuery; use re_entity_db::EntityPath; +use re_log_types::hash::Hash64; use re_query::query_archetype; use re_space_view::SpaceViewBlueprint; -use re_viewer_context::{ContainerId, Item, SpaceViewClassIdentifier, SpaceViewId, ViewerContext}; +use re_types::blueprint::components::ViewerRecommendationHash; +use re_viewer_context::{ + ContainerId, Item, SpaceViewClassIdentifier, SpaceViewId, SpaceViewSpawnHeuristics, + ViewerContext, +}; use crate::{ blueprint::components::{ @@ -51,6 +57,9 @@ pub struct ViewportBlueprint { /// Note: we use a mutex here because writes needs to be effective immediately during the frame. auto_space_views: AtomicBool, + /// Hashes of all recommended space views the viewer has already added and that should not be added again. + past_viewer_recommendation_hashes: IntSet, + /// Channel to pass Blueprint mutation messages back to the [`crate::Viewport`] tree_action_sender: std::sync::mpsc::Sender, } @@ -64,12 +73,15 @@ impl ViewportBlueprint { ) -> Self { re_tracing::profile_function!(); - let arch = match query_archetype::( - blueprint_db.store(), - query, - &VIEWPORT_PATH.into(), - ) - .and_then(|arch| arch.to_archetype()) + let crate::blueprint::archetypes::ViewportBlueprint { + space_views, + root_container, + maximized, + auto_layout, + auto_space_views, + past_viewer_recommendations: past_viewer_recommendation_hashes, + } = match query_archetype(blueprint_db.store(), query, &VIEWPORT_PATH.into()) + .and_then(|arch| arch.to_archetype()) { Ok(arch) => arch, Err(re_query::QueryError::PrimaryNotFound(_)) => { @@ -87,7 +99,7 @@ impl ViewportBlueprint { }; let space_view_ids: Vec = - arch.space_views.iter().map(|id| id.0.into()).collect(); + space_views.into_iter().map(|id| id.0.into()).collect(); let space_views: BTreeMap = space_view_ids .into_iter() @@ -115,21 +127,17 @@ impl ViewportBlueprint { .map(|c| (c.id, c)) .collect(); - let auto_layout = arch.auto_layout.unwrap_or_default().0; - - let root_container = arch.root_container.map(|id| id.0.into()); - - let auto_space_views = arch.auto_space_views.map_or_else( - || { - // Only enable auto-space-views if this is the app-default blueprint - blueprint_db - .store_info() - .map_or(false, |ri| ri.is_app_default_blueprint()) - }, - |auto| auto.0, - ); + let root_container = root_container.map(|id| id.0.into()); - let maximized = arch.maximized.map(|id| id.0.into()); + // Auto layouting and auto space view are only enabled if no blueprint has been provided by the user. + // Only enable auto-space-views if this is the app-default blueprint + let is_app_default_blueprint = blueprint_db + .store_info() + .map_or(false, |ri| ri.is_app_default_blueprint()); + let auto_layout = + AtomicBool::new(auto_layout.map_or(is_app_default_blueprint, |auto| auto.0)); + let auto_space_views = + AtomicBool::new(auto_space_views.map_or(is_app_default_blueprint, |auto| auto.0)); let tree = build_tree_from_space_views_and_containers( space_views.values(), @@ -137,37 +145,23 @@ impl ViewportBlueprint { root_container, ); + let past_viewer_recommendation_hashes = past_viewer_recommendation_hashes + .unwrap_or_default() + .into_iter() + .map(|h| Hash64::from_u64(h.0 .0)) + .collect(); + ViewportBlueprint { space_views, containers, root_container, tree, - maximized, - auto_layout: auto_layout.into(), - auto_space_views: auto_space_views.into(), + maximized: maximized.map(|id| id.0.into()), + auto_layout, + auto_space_views, + past_viewer_recommendation_hashes, tree_action_sender, } - - // TODO(jleibs): Need to figure out if we have to re-enable support for - // auto-discovery of SpaceViews logged via the experimental blueprint APIs. - /* - let unknown_space_views: HashMap<_, _> = space_views - .iter() - .filter(|(k, _)| !viewport_layout.space_view_keys.contains(k)) - .map(|(k, v)| (*k, v.clone())) - .collect(); - */ - - // TODO(jleibs): It seems we shouldn't call this until later, after we've created - // the snapshot. Doing this here means we are mutating the state before it goes - // into the snapshot. For example, even if there's no visibility in the - // store, this will end up with default-visibility, which then *won't* be saved back. - // TODO(jleibs): what to do about auto-discovery? - /* - for (_, view) in unknown_space_views { - viewport.add_space_view(view); - } - */ } /// Determine whether all views in a blueprint are invalid. @@ -283,6 +277,102 @@ impl ViewportBlueprint { self.set_auto_space_views(false, ctx); } + pub fn on_frame_start(&self, ctx: &ViewerContext<'_>) { + if self.auto_space_views() { + self.spawn_heuristic_space_views(ctx); + } + } + + fn spawn_heuristic_space_views(&self, ctx: &ViewerContext<'_>) { + re_tracing::profile_function!(); + + for entry in ctx.space_view_class_registry.iter_registry() { + let class_id = entry.class.identifier(); + let SpaceViewSpawnHeuristics { + mut recommended_space_views, + } = entry.class.spawn_heuristics(ctx); + + re_tracing::profile_scope!("filter_recommendations_for", class_id); + + // Remove all space views that we already spawned via heuristic before. + recommended_space_views.retain(|recommended_view| { + !self + .past_viewer_recommendation_hashes + .contains(&Hash64::hash(recommended_view)) + }); + + // Each of the remaining recommendations would individually be a candidate for spawning if there were + // no other space views in the viewport. + // In the following steps we further filter this list depending on what's on screen already, + // as well as redundancy within the recommendation itself BUT this is an important checkpoint: + // All the other views may change due to user interaction, but this does *not* mean + // that we should suddenly spawn the views we're filtering out here. + // Therefore everything so far needs to be added to `past_viewer_recommendations`, + // which marks this as "already processed recommendation". + // + // Example: + // Recommendation contains `/**` and `/camera/**`. + // We filter out `/camera/**` because that would be redundant to `/**`. + // If now the user edits the space view at `/**` to be `/points/**`, that does *not* + // mean we should suddenly add `/camera/**` to the viewport. + if !recommended_space_views.is_empty() { + let new_viewer_recommendation_hashes = self + .past_viewer_recommendation_hashes + .iter() + .cloned() + .chain(recommended_space_views.iter().map(Hash64::hash)) + .map(|hash| ViewerRecommendationHash(hash.hash64().into())) + .collect::>(); + + ctx.save_blueprint_component( + &VIEWPORT_PATH.into(), + &new_viewer_recommendation_hashes, + ); + } + + // Remove all space views that have all the entities we already have on screen. + let existing_path_filters = self + .space_views + .values() + .filter(|space_view| space_view.class_identifier() == &class_id) + .map(|space_view| &space_view.contents.entity_path_filter) + .collect::>(); + recommended_space_views.retain(|recommended_view| { + existing_path_filters.iter().all(|existing_filter| { + !existing_filter.is_superset_of(&recommended_view.query_filter) + }) + }); + + // Remove all space views that are redundant within the remaining recommendation. + // This n^2 loop should only run ever for frames that add new space views. + let final_recommendations = recommended_space_views + .iter() + .enumerate() + .filter(|(j, candidate)| { + recommended_space_views + .iter() + .enumerate() + .all(|(i, other)| { + i == *j || !other.query_filter.is_superset_of(&candidate.query_filter) + }) + }) + .map(|(_, recommendation)| recommendation); + + self.add_space_views( + final_recommendations.map(|recommendation| { + SpaceViewBlueprint::new( + class_id, + &recommendation.root, + recommendation.query_filter.clone(), + ) + }), + ctx, + None, + None, + ); + } + } + /// Add a set of space views to the viewport. /// /// The space view is added to the root container, or, if provided, to a given parent container. diff --git a/docs/content/reference/types/datatypes.md b/docs/content/reference/types/datatypes.md index 0b639323a04f..9bc0c16c5b21 100644 --- a/docs/content/reference/types/datatypes.md +++ b/docs/content/reference/types/datatypes.md @@ -32,6 +32,7 @@ Data types are the lowest layer of the data model hierarchy * [`TranslationAndMat3x3`](datatypes/translation_and_mat3x3.md) * [`TranslationRotationScale3D`](datatypes/translation_rotation_scale3d.md) * [`UInt32`](datatypes/uint32.md) +* [`UInt64`](datatypes/uint64.md) * [`UVec2D`](datatypes/uvec2d.md) * [`UVec3D`](datatypes/uvec3d.md) * [`UVec4D`](datatypes/uvec4d.md) diff --git a/docs/content/reference/types/datatypes/.gitattributes b/docs/content/reference/types/datatypes/.gitattributes index 707aa642d9e7..c9c2f3a81adc 100644 --- a/docs/content/reference/types/datatypes/.gitattributes +++ b/docs/content/reference/types/datatypes/.gitattributes @@ -26,6 +26,7 @@ transform3d.md linguist-generated=true translation_and_mat3x3.md linguist-generated=true translation_rotation_scale3d.md linguist-generated=true uint32.md linguist-generated=true +uint64.md linguist-generated=true utf8.md linguist-generated=true uuid.md linguist-generated=true uvec2d.md linguist-generated=true diff --git a/docs/content/reference/types/datatypes/uint64.md b/docs/content/reference/types/datatypes/uint64.md new file mode 100644 index 000000000000..4984dca79ebc --- /dev/null +++ b/docs/content/reference/types/datatypes/uint64.md @@ -0,0 +1,16 @@ +--- +title: "UInt64" +--- + +A 64bit unsigned integer. + +## Fields + +* value: `u64` + +## Links + * 🌊 [C++ API docs for `UInt64`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1datatypes_1_1UInt64.html?speculative-link) + * 🐍 [Python API docs for `UInt64`](https://ref.rerun.io/docs/python/stable/common/datatypes?speculative-link#rerun.datatypes.UInt64) + * 🦀 [Rust API docs for `UInt64`](https://docs.rs/rerun/latest/rerun/datatypes/struct.UInt64.html?speculative-link) + + diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/space_view_contents.cpp b/rerun_cpp/src/rerun/blueprint/archetypes/space_view_contents.cpp index 525540368ba2..876f1d970444 100644 --- a/rerun_cpp/src/rerun/blueprint/archetypes/space_view_contents.cpp +++ b/rerun_cpp/src/rerun/blueprint/archetypes/space_view_contents.cpp @@ -14,18 +14,13 @@ namespace rerun { ) { using namespace blueprint::archetypes; std::vector cells; - cells.reserve(3); + cells.reserve(2); { auto result = DataCell::from_loggable(archetype.query); RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } - if (archetype.entities_determined_by_user.has_value()) { - auto result = DataCell::from_loggable(archetype.entities_determined_by_user.value()); - RR_RETURN_NOT_OK(result.error); - cells.push_back(std::move(result.value)); - } { auto indicator = SpaceViewContents::IndicatorComponent(); auto result = DataCell::from_loggable(indicator); diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/space_view_contents.hpp b/rerun_cpp/src/rerun/blueprint/archetypes/space_view_contents.hpp index a65b1c476214..3a7eeb186f0c 100644 --- a/rerun_cpp/src/rerun/blueprint/archetypes/space_view_contents.hpp +++ b/rerun_cpp/src/rerun/blueprint/archetypes/space_view_contents.hpp @@ -3,16 +3,13 @@ #pragma once -#include "../../blueprint/components/entities_determined_by_user.hpp" #include "../../blueprint/components/query_expression.hpp" #include "../../collection.hpp" -#include "../../compiler_utils.hpp" #include "../../data_cell.hpp" #include "../../indicator_component.hpp" #include "../../result.hpp" #include -#include #include #include @@ -24,12 +21,6 @@ namespace rerun::blueprint::archetypes { /// They determine which entities are part of the spaceview. rerun::blueprint::components::QueryExpression query; - /// True if the user is has added entities themselves. False otherwise. - /// - /// This is used by the viewer to determine whether it should regard this space view as created by the heuristic or not. - std::optional - entities_determined_by_user; - public: static constexpr const char IndicatorComponentName[] = "rerun.blueprint.components.SpaceViewContentsIndicator"; @@ -44,17 +35,6 @@ namespace rerun::blueprint::archetypes { explicit SpaceViewContents(rerun::blueprint::components::QueryExpression _query) : query(std::move(_query)) {} - /// True if the user is has added entities themselves. False otherwise. - /// - /// This is used by the viewer to determine whether it should regard this space view as created by the heuristic or not. - SpaceViewContents with_entities_determined_by_user( - rerun::blueprint::components::EntitiesDeterminedByUser _entities_determined_by_user - ) && { - entities_determined_by_user = std::move(_entities_determined_by_user); - // See: https://github.com/rerun-io/rerun/issues/4027 - RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) - } - /// Returns the number of primary instances of this archetype. size_t num_instances() const { return 1; diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/viewport_blueprint.cpp b/rerun_cpp/src/rerun/blueprint/archetypes/viewport_blueprint.cpp index 85f2d414882e..e4f142f58638 100644 --- a/rerun_cpp/src/rerun/blueprint/archetypes/viewport_blueprint.cpp +++ b/rerun_cpp/src/rerun/blueprint/archetypes/viewport_blueprint.cpp @@ -14,7 +14,7 @@ namespace rerun { ) { using namespace blueprint::archetypes; std::vector cells; - cells.reserve(6); + cells.reserve(7); { auto result = DataCell::from_loggable(archetype.space_views); @@ -41,6 +41,11 @@ namespace rerun { RR_RETURN_NOT_OK(result.error); cells.push_back(std::move(result.value)); } + if (archetype.past_viewer_recommendations.has_value()) { + auto result = DataCell::from_loggable(archetype.past_viewer_recommendations.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } { auto indicator = ViewportBlueprint::IndicatorComponent(); auto result = DataCell::from_loggable(indicator); diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/viewport_blueprint.hpp b/rerun_cpp/src/rerun/blueprint/archetypes/viewport_blueprint.hpp index 49ccd536be15..31458d45b3f4 100644 --- a/rerun_cpp/src/rerun/blueprint/archetypes/viewport_blueprint.hpp +++ b/rerun_cpp/src/rerun/blueprint/archetypes/viewport_blueprint.hpp @@ -8,6 +8,7 @@ #include "../../blueprint/components/included_space_view.hpp" #include "../../blueprint/components/root_container.hpp" #include "../../blueprint/components/space_view_maximized.hpp" +#include "../../blueprint/components/viewer_recommendation_hash.hpp" #include "../../collection.hpp" #include "../../compiler_utils.hpp" #include "../../data_cell.hpp" @@ -37,9 +38,22 @@ namespace rerun::blueprint::archetypes { /// This defaults to `false` and is automatically set to `false` when there is user determined layout. std::optional auto_layout; - /// Whether or not space views should be created automatically. + /// Whether or not Space Views should be created automatically. + /// + /// If `true`, the viewer will only add Space Views that it hasn't considered previously (as identified by `past_viewer_recommendations`) + /// and which aren't deemed redundant to existing Space Views. + /// This defaults to `false` and is automatically set to `false` when the user adds Space Views manually in the viewer. std::optional auto_space_views; + /// Hashes of all recommended Space Views the viewer has already added and that should not be added again. + /// + /// This is an internal field and should not be set usually. + /// If you want the viewer from stopping to add Space Views, you should set `auto_space_views` to `false`. + /// + /// The viewer uses this to determine whether it should keep adding Space Views. + std::optional> + past_viewer_recommendations; + public: static constexpr const char IndicatorComponentName[] = "rerun.blueprint.components.ViewportBlueprintIndicator"; @@ -84,7 +98,11 @@ namespace rerun::blueprint::archetypes { RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } - /// Whether or not space views should be created automatically. + /// Whether or not Space Views should be created automatically. + /// + /// If `true`, the viewer will only add Space Views that it hasn't considered previously (as identified by `past_viewer_recommendations`) + /// and which aren't deemed redundant to existing Space Views. + /// This defaults to `false` and is automatically set to `false` when the user adds Space Views manually in the viewer. ViewportBlueprint with_auto_space_views( rerun::blueprint::components::AutoSpaceViews _auto_space_views ) && { @@ -93,6 +111,21 @@ namespace rerun::blueprint::archetypes { RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } + /// Hashes of all recommended Space Views the viewer has already added and that should not be added again. + /// + /// This is an internal field and should not be set usually. + /// If you want the viewer from stopping to add Space Views, you should set `auto_space_views` to `false`. + /// + /// The viewer uses this to determine whether it should keep adding Space Views. + ViewportBlueprint with_past_viewer_recommendations( + Collection + _past_viewer_recommendations + ) && { + past_viewer_recommendations = std::move(_past_viewer_recommendations); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + /// Returns the number of primary instances of this archetype. size_t num_instances() const { return space_views.size(); diff --git a/rerun_cpp/src/rerun/blueprint/components.hpp b/rerun_cpp/src/rerun/blueprint/components.hpp index 8e75ce46a846..8fe7b4745f1a 100644 --- a/rerun_cpp/src/rerun/blueprint/components.hpp +++ b/rerun_cpp/src/rerun/blueprint/components.hpp @@ -8,7 +8,6 @@ #include "blueprint/components/column_share.hpp" #include "blueprint/components/container_kind.hpp" #include "blueprint/components/corner2d.hpp" -#include "blueprint/components/entities_determined_by_user.hpp" #include "blueprint/components/entity_properties_component.hpp" #include "blueprint/components/grid_columns.hpp" #include "blueprint/components/included_content.hpp" @@ -21,4 +20,5 @@ #include "blueprint/components/space_view_class.hpp" #include "blueprint/components/space_view_maximized.hpp" #include "blueprint/components/space_view_origin.hpp" +#include "blueprint/components/viewer_recommendation_hash.hpp" #include "blueprint/components/visible.hpp" diff --git a/rerun_cpp/src/rerun/blueprint/components/.gitattributes b/rerun_cpp/src/rerun/blueprint/components/.gitattributes index fc29749b3760..aebedc0c9630 100644 --- a/rerun_cpp/src/rerun/blueprint/components/.gitattributes +++ b/rerun_cpp/src/rerun/blueprint/components/.gitattributes @@ -13,8 +13,6 @@ container_kind.cpp linguist-generated=true container_kind.hpp linguist-generated=true corner2d.cpp linguist-generated=true corner2d.hpp linguist-generated=true -entities_determined_by_user.cpp linguist-generated=true -entities_determined_by_user.hpp linguist-generated=true entity_properties_component.cpp linguist-generated=true entity_properties_component.hpp linguist-generated=true grid_columns.cpp linguist-generated=true @@ -39,5 +37,7 @@ space_view_maximized.cpp linguist-generated=true space_view_maximized.hpp linguist-generated=true space_view_origin.cpp linguist-generated=true space_view_origin.hpp linguist-generated=true +viewer_recommendation_hash.cpp linguist-generated=true +viewer_recommendation_hash.hpp linguist-generated=true visible.cpp linguist-generated=true visible.hpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/blueprint/components/entities_determined_by_user.cpp b/rerun_cpp/src/rerun/blueprint/components/entities_determined_by_user.cpp deleted file mode 100644 index 4a5ed89609bb..000000000000 --- a/rerun_cpp/src/rerun/blueprint/components/entities_determined_by_user.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// 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/blueprint/components/entities_determined_by_user.fbs". - -#include "entities_determined_by_user.hpp" - -#include -#include - -namespace rerun::blueprint::components {} - -namespace rerun { - const std::shared_ptr& - Loggable::arrow_datatype() { - static const auto datatype = arrow::boolean(); - return datatype; - } - - rerun::Error - Loggable::fill_arrow_array_builder( - arrow::BooleanBuilder* builder, - const blueprint::components::EntitiesDeterminedByUser* 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." - ); - } - - static_assert(sizeof(*elements) == sizeof(elements->value)); - ARROW_RETURN_NOT_OK(builder->AppendValues( - reinterpret_cast(&elements->value), - static_cast(num_elements) - )); - - return Error::ok(); - } - - Result> - Loggable::to_arrow( - const blueprint::components::EntitiesDeterminedByUser* 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/blueprint/components/entities_determined_by_user.hpp b/rerun_cpp/src/rerun/blueprint/components/entities_determined_by_user.hpp deleted file mode 100644 index 4b089ab578e4..000000000000 --- a/rerun_cpp/src/rerun/blueprint/components/entities_determined_by_user.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// 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/blueprint/components/entities_determined_by_user.fbs". - -#pragma once - -#include "../../result.hpp" - -#include -#include - -namespace arrow { - class Array; - class BooleanBuilder; - class DataType; -} // namespace arrow - -namespace rerun::blueprint::components { - /// **Component**: Whether the space view entities were manually edited. - struct EntitiesDeterminedByUser { - bool value; - - public: - EntitiesDeterminedByUser() = default; - - EntitiesDeterminedByUser(bool value_) : value(value_) {} - - EntitiesDeterminedByUser& operator=(bool value_) { - value = value_; - return *this; - } - }; -} // namespace rerun::blueprint::components - -namespace rerun { - template - struct Loggable; - - /// \private - template <> - struct Loggable { - static constexpr const char Name[] = "rerun.blueprint.components.EntitiesDeterminedByUser"; - - /// 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::BooleanBuilder* builder, - const blueprint::components::EntitiesDeterminedByUser* elements, size_t num_elements - ); - - /// Serializes an array of `rerun::blueprint:: components::EntitiesDeterminedByUser` into an arrow array. - static Result> to_arrow( - const blueprint::components::EntitiesDeterminedByUser* instances, size_t num_instances - ); - }; -} // namespace rerun diff --git a/rerun_cpp/src/rerun/blueprint/components/viewer_recommendation_hash.cpp b/rerun_cpp/src/rerun/blueprint/components/viewer_recommendation_hash.cpp new file mode 100644 index 000000000000..b92576dc9b6a --- /dev/null +++ b/rerun_cpp/src/rerun/blueprint/components/viewer_recommendation_hash.cpp @@ -0,0 +1,60 @@ +// 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/blueprint/components/viewer_recommendation_hash.fbs". + +#include "viewer_recommendation_hash.hpp" + +#include "../../datatypes/uint64.hpp" + +#include +#include + +namespace rerun::blueprint::components {} + +namespace rerun { + const std::shared_ptr& + Loggable::arrow_datatype() { + static const auto datatype = Loggable::arrow_datatype(); + return datatype; + } + + rerun::Error + Loggable::fill_arrow_array_builder( + arrow::UInt64Builder* builder, + const blueprint::components::ViewerRecommendationHash* elements, size_t num_elements + ) { + static_assert( + sizeof(rerun::datatypes::UInt64) == + sizeof(blueprint::components::ViewerRecommendationHash) + ); + RR_RETURN_NOT_OK(Loggable::fill_arrow_array_builder( + builder, + reinterpret_cast(elements), + num_elements + )); + + return Error::ok(); + } + + Result> + Loggable::to_arrow( + const blueprint::components::ViewerRecommendationHash* 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/blueprint/components/viewer_recommendation_hash.hpp b/rerun_cpp/src/rerun/blueprint/components/viewer_recommendation_hash.hpp new file mode 100644 index 000000000000..c342c6285abe --- /dev/null +++ b/rerun_cpp/src/rerun/blueprint/components/viewer_recommendation_hash.hpp @@ -0,0 +1,77 @@ +// 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/blueprint/components/viewer_recommendation_hash.fbs". + +#pragma once + +#include "../../datatypes/uint64.hpp" +#include "../../result.hpp" + +#include +#include + +namespace arrow { + /// \private + template + class NumericBuilder; + + class Array; + class DataType; + class UInt64Type; + using UInt64Builder = NumericBuilder; +} // namespace arrow + +namespace rerun::blueprint::components { + /// **Component**: Hash of a viewer recommendation. + /// + /// The formation of this hash is considered an internal implementation detail of the viewer. + struct ViewerRecommendationHash { + rerun::datatypes::UInt64 value; + + public: + ViewerRecommendationHash() = default; + + ViewerRecommendationHash(rerun::datatypes::UInt64 value_) : value(value_) {} + + ViewerRecommendationHash& operator=(rerun::datatypes::UInt64 value_) { + value = value_; + return *this; + } + + ViewerRecommendationHash(uint64_t value_) : value(value_) {} + + ViewerRecommendationHash& operator=(uint64_t value_) { + value = value_; + return *this; + } + + /// Cast to the underlying UInt64 datatype + operator rerun::datatypes::UInt64() const { + return value; + } + }; +} // namespace rerun::blueprint::components + +namespace rerun { + template + struct Loggable; + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.blueprint.components.ViewerRecommendationHash"; + + /// 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::UInt64Builder* builder, + const blueprint::components::ViewerRecommendationHash* elements, size_t num_elements + ); + + /// Serializes an array of `rerun::blueprint:: components::ViewerRecommendationHash` into an arrow array. + static Result> to_arrow( + const blueprint::components::ViewerRecommendationHash* instances, size_t num_instances + ); + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/datatypes.hpp b/rerun_cpp/src/rerun/datatypes.hpp index e53520c1aad4..60d458ef8f9a 100644 --- a/rerun_cpp/src/rerun/datatypes.hpp +++ b/rerun_cpp/src/rerun/datatypes.hpp @@ -27,6 +27,7 @@ #include "datatypes/translation_and_mat3x3.hpp" #include "datatypes/translation_rotation_scale3d.hpp" #include "datatypes/uint32.hpp" +#include "datatypes/uint64.hpp" #include "datatypes/utf8.hpp" #include "datatypes/uuid.hpp" #include "datatypes/uvec2d.hpp" diff --git a/rerun_cpp/src/rerun/datatypes/.gitattributes b/rerun_cpp/src/rerun/datatypes/.gitattributes index e30cdfaeb468..3fe8d752c348 100644 --- a/rerun_cpp/src/rerun/datatypes/.gitattributes +++ b/rerun_cpp/src/rerun/datatypes/.gitattributes @@ -51,6 +51,8 @@ translation_rotation_scale3d.cpp linguist-generated=true translation_rotation_scale3d.hpp linguist-generated=true uint32.cpp linguist-generated=true uint32.hpp linguist-generated=true +uint64.cpp linguist-generated=true +uint64.hpp linguist-generated=true utf8.cpp linguist-generated=true utf8.hpp linguist-generated=true uuid.cpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/datatypes/uint64.cpp b/rerun_cpp/src/rerun/datatypes/uint64.cpp new file mode 100644 index 000000000000..4aa873041cae --- /dev/null +++ b/rerun_cpp/src/rerun/datatypes/uint64.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/uint64.fbs". + +#include "uint64.hpp" + +#include +#include + +namespace rerun::datatypes {} + +namespace rerun { + const std::shared_ptr& Loggable::arrow_datatype() { + static const auto datatype = arrow::uint64(); + return datatype; + } + + rerun::Error Loggable::fill_arrow_array_builder( + arrow::UInt64Builder* builder, const datatypes::UInt64* 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." + ); + } + + static_assert(sizeof(*elements) == sizeof(elements->value)); + ARROW_RETURN_NOT_OK( + builder->AppendValues(&elements->value, static_cast(num_elements)) + ); + + return Error::ok(); + } + + Result> Loggable::to_arrow( + const datatypes::UInt64* 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/uint64.hpp b/rerun_cpp/src/rerun/datatypes/uint64.hpp new file mode 100644 index 000000000000..9886a239d1a6 --- /dev/null +++ b/rerun_cpp/src/rerun/datatypes/uint64.hpp @@ -0,0 +1,61 @@ +// 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/uint64.fbs". + +#pragma once + +#include "../result.hpp" + +#include +#include + +namespace arrow { + /// \private + template + class NumericBuilder; + + class Array; + class DataType; + class UInt64Type; + using UInt64Builder = NumericBuilder; +} // namespace arrow + +namespace rerun::datatypes { + /// **Datatype**: A 64bit unsigned integer. + struct UInt64 { + uint64_t value; + + public: + UInt64() = default; + + UInt64(uint64_t value_) : value(value_) {} + + UInt64& operator=(uint64_t value_) { + value = value_; + return *this; + } + }; +} // namespace rerun::datatypes + +namespace rerun { + template + struct Loggable; + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.datatypes.UInt64"; + + /// 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::UInt64Builder* builder, const datatypes::UInt64* elements, size_t num_elements + ); + + /// Serializes an array of `rerun::datatypes::UInt64` into an arrow array. + static Result> to_arrow( + const datatypes::UInt64* instances, size_t num_instances + ); + }; +} // namespace rerun diff --git a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/space_view_contents.py b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/space_view_contents.py index 63083eaa6a18..3f38a598acbe 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/space_view_contents.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/space_view_contents.py @@ -21,12 +21,7 @@ class SpaceViewContents(Archetype): """**Archetype**: The contents of a `SpaceView`.""" - def __init__( - self: Any, - query: datatypes.Utf8Like, - *, - entities_determined_by_user: blueprint_components.EntitiesDeterminedByUserLike | None = None, - ): + def __init__(self: Any, query: datatypes.Utf8Like): """ Create a new instance of the SpaceViewContents archetype. @@ -36,16 +31,12 @@ def __init__( The `QueryExpression` that populates the contents for the `SpaceView`. They determine which entities are part of the spaceview. - entities_determined_by_user: - True if the user is has added entities themselves. False otherwise. - - This is used by the viewer to determine whether it should regard this space view as created by the heuristic or not. """ # You can define your own __init__ function as a member of SpaceViewContentsExt in space_view_contents_ext.py with catch_and_log_exceptions(context=self.__class__.__name__): - self.__attrs_init__(query=query, entities_determined_by_user=entities_determined_by_user) + self.__attrs_init__(query=query) return self.__attrs_clear__() @@ -53,7 +44,6 @@ def __attrs_clear__(self) -> None: """Convenience method for calling `__attrs_init__` with all `None`s.""" self.__attrs_init__( query=None, # type: ignore[arg-type] - entities_determined_by_user=None, # type: ignore[arg-type] ) @classmethod @@ -73,16 +63,5 @@ def _clear(cls) -> SpaceViewContents: # # (Docstring intentionally commented out to hide this field from the docs) - entities_determined_by_user: blueprint_components.EntitiesDeterminedByUserBatch | None = field( - metadata={"component": "optional"}, - default=None, - converter=blueprint_components.EntitiesDeterminedByUserBatch._optional, # type: ignore[misc] - ) - # True if the user is has added entities themselves. False otherwise. - # - # This is used by the viewer to determine whether it should regard this space view as created by the heuristic or not. - # - # (Docstring intentionally commented out to hide this field from the docs) - __str__ = Archetype.__str__ __repr__ = Archetype.__repr__ # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/viewport_blueprint.py b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/viewport_blueprint.py index dcac87f9827b..fcace42edb95 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/viewport_blueprint.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/viewport_blueprint.py @@ -29,6 +29,7 @@ def __init__( maximized: datatypes.UuidLike | None = None, auto_layout: blueprint_components.AutoLayoutLike | None = None, auto_space_views: blueprint_components.AutoSpaceViewsLike | None = None, + past_viewer_recommendations: datatypes.UInt64ArrayLike | None = None, ): """ Create a new instance of the ViewportBlueprint archetype. @@ -47,7 +48,18 @@ def __init__( If `true`, the container layout will be reset whenever a new space view is added or removed. This defaults to `false` and is automatically set to `false` when there is user determined layout. auto_space_views: - Whether or not space views should be created automatically. + Whether or not Space Views should be created automatically. + + If `true`, the viewer will only add Space Views that it hasn't considered previously (as identified by `past_viewer_recommendations`) + and which aren't deemed redundant to existing Space Views. + This defaults to `false` and is automatically set to `false` when the user adds Space Views manually in the viewer. + past_viewer_recommendations: + Hashes of all recommended Space Views the viewer has already added and that should not be added again. + + This is an internal field and should not be set usually. + If you want the viewer from stopping to add Space Views, you should set `auto_space_views` to `false`. + + The viewer uses this to determine whether it should keep adding Space Views. """ @@ -59,6 +71,7 @@ def __init__( maximized=maximized, auto_layout=auto_layout, auto_space_views=auto_space_views, + past_viewer_recommendations=past_viewer_recommendations, ) return self.__attrs_clear__() @@ -71,6 +84,7 @@ def __attrs_clear__(self) -> None: maximized=None, # type: ignore[arg-type] auto_layout=None, # type: ignore[arg-type] auto_space_views=None, # type: ignore[arg-type] + past_viewer_recommendations=None, # type: ignore[arg-type] ) @classmethod @@ -123,7 +137,25 @@ def _clear(cls) -> ViewportBlueprint: default=None, converter=blueprint_components.AutoSpaceViewsBatch._optional, # type: ignore[misc] ) - # Whether or not space views should be created automatically. + # Whether or not Space Views should be created automatically. + # + # If `true`, the viewer will only add Space Views that it hasn't considered previously (as identified by `past_viewer_recommendations`) + # and which aren't deemed redundant to existing Space Views. + # This defaults to `false` and is automatically set to `false` when the user adds Space Views manually in the viewer. + # + # (Docstring intentionally commented out to hide this field from the docs) + + past_viewer_recommendations: blueprint_components.ViewerRecommendationHashBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=blueprint_components.ViewerRecommendationHashBatch._optional, # type: ignore[misc] + ) + # Hashes of all recommended Space Views the viewer has already added and that should not be added again. + # + # This is an internal field and should not be set usually. + # If you want the viewer from stopping to add Space Views, you should set `auto_space_views` to `false`. + # + # The viewer uses this to determine whether it should keep adding Space Views. # # (Docstring intentionally commented out to hide this field from the docs) diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes b/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes index 51674ecac5ed..7b5b77705d5a 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes @@ -8,7 +8,6 @@ auto_space_views.py linguist-generated=true column_share.py linguist-generated=true container_kind.py linguist-generated=true corner2d.py linguist-generated=true -entities_determined_by_user.py linguist-generated=true entity_properties_component.py linguist-generated=true grid_columns.py linguist-generated=true included_content.py linguist-generated=true @@ -21,4 +20,5 @@ row_share.py linguist-generated=true space_view_class.py linguist-generated=true space_view_maximized.py linguist-generated=true space_view_origin.py linguist-generated=true +viewer_recommendation_hash.py linguist-generated=true visible.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py b/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py index 63533ad61c36..f0eda9f5563b 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py @@ -20,13 +20,6 @@ ContainerKindType, ) from .corner2d import Corner2D, Corner2DArrayLike, Corner2DBatch, Corner2DLike, Corner2DType -from .entities_determined_by_user import ( - EntitiesDeterminedByUser, - EntitiesDeterminedByUserArrayLike, - EntitiesDeterminedByUserBatch, - EntitiesDeterminedByUserLike, - EntitiesDeterminedByUserType, -) from .entity_properties_component import ( EntityPropertiesComponent, EntityPropertiesComponentArrayLike, @@ -51,6 +44,11 @@ from .space_view_class import SpaceViewClass, SpaceViewClassBatch, SpaceViewClassType from .space_view_maximized import SpaceViewMaximized, SpaceViewMaximizedBatch, SpaceViewMaximizedType from .space_view_origin import SpaceViewOrigin, SpaceViewOriginBatch, SpaceViewOriginType +from .viewer_recommendation_hash import ( + ViewerRecommendationHash, + ViewerRecommendationHashBatch, + ViewerRecommendationHashType, +) from .visible import Visible, VisibleArrayLike, VisibleBatch, VisibleLike, VisibleType __all__ = [ @@ -82,11 +80,6 @@ "Corner2DBatch", "Corner2DLike", "Corner2DType", - "EntitiesDeterminedByUser", - "EntitiesDeterminedByUserArrayLike", - "EntitiesDeterminedByUserBatch", - "EntitiesDeterminedByUserLike", - "EntitiesDeterminedByUserType", "EntityPropertiesComponent", "EntityPropertiesComponentArrayLike", "EntityPropertiesComponentBatch", @@ -133,6 +126,9 @@ "SpaceViewOrigin", "SpaceViewOriginBatch", "SpaceViewOriginType", + "ViewerRecommendationHash", + "ViewerRecommendationHashBatch", + "ViewerRecommendationHashType", "Visible", "VisibleArrayLike", "VisibleBatch", diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/entities_determined_by_user.py b/rerun_py/rerun_sdk/rerun/blueprint/components/entities_determined_by_user.py deleted file mode 100644 index 7e4775420eaf..000000000000 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/entities_determined_by_user.py +++ /dev/null @@ -1,64 +0,0 @@ -# DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/python/mod.rs -# Based on "crates/re_types/definitions/rerun/blueprint/components/entities_determined_by_user.fbs". - -# You can extend this class by creating a "EntitiesDeterminedByUserExt" class in "entities_determined_by_user_ext.py". - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Sequence, Union - -import pyarrow as pa -from attrs import define, field - -from ..._baseclasses import BaseBatch, BaseExtensionType, ComponentBatchMixin -from .entities_determined_by_user_ext import EntitiesDeterminedByUserExt - -__all__ = [ - "EntitiesDeterminedByUser", - "EntitiesDeterminedByUserArrayLike", - "EntitiesDeterminedByUserBatch", - "EntitiesDeterminedByUserLike", - "EntitiesDeterminedByUserType", -] - - -@define(init=False) -class EntitiesDeterminedByUser(EntitiesDeterminedByUserExt): - """**Component**: Whether the space view entities were manually edited.""" - - def __init__(self: Any, value: EntitiesDeterminedByUserLike): - """Create a new instance of the EntitiesDeterminedByUser component.""" - - # You can define your own __init__ function as a member of EntitiesDeterminedByUserExt in entities_determined_by_user_ext.py - self.__attrs_init__(value=value) - - def __bool__(self) -> bool: - return self.value - - value: bool = field(converter=bool) - - -if TYPE_CHECKING: - EntitiesDeterminedByUserLike = Union[EntitiesDeterminedByUser, bool] -else: - EntitiesDeterminedByUserLike = Any - -EntitiesDeterminedByUserArrayLike = Union[ - EntitiesDeterminedByUser, - Sequence[EntitiesDeterminedByUserLike], -] - - -class EntitiesDeterminedByUserType(BaseExtensionType): - _TYPE_NAME: str = "rerun.blueprint.components.EntitiesDeterminedByUser" - - def __init__(self) -> None: - pa.ExtensionType.__init__(self, pa.bool_(), self._TYPE_NAME) - - -class EntitiesDeterminedByUserBatch(BaseBatch[EntitiesDeterminedByUserArrayLike], ComponentBatchMixin): - _ARROW_TYPE = EntitiesDeterminedByUserType() - - @staticmethod - def _native_to_pa_array(data: EntitiesDeterminedByUserArrayLike, data_type: pa.DataType) -> pa.Array: - return EntitiesDeterminedByUserExt.native_to_pa_array_override(data, data_type) diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/entities_determined_by_user_ext.py b/rerun_py/rerun_sdk/rerun/blueprint/components/entities_determined_by_user_ext.py deleted file mode 100644 index 6299c31116f5..000000000000 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/entities_determined_by_user_ext.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -import numpy as np -import pyarrow as pa - -if TYPE_CHECKING: - from . import EntitiesDeterminedByUserArrayLike - - -class EntitiesDeterminedByUserExt: - """Extension for [EntitiesDeterminedByUser][rerun.blueprint.components.EntitiesDeterminedByUser].""" - - @staticmethod - def native_to_pa_array_override(data: EntitiesDeterminedByUserArrayLike, data_type: pa.DataType) -> pa.Array: - array = np.asarray(data, dtype=np.bool_).flatten() - return pa.array(array, type=data_type) diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/viewer_recommendation_hash.py b/rerun_py/rerun_sdk/rerun/blueprint/components/viewer_recommendation_hash.py new file mode 100644 index 000000000000..b8b3c2b1ce4e --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/viewer_recommendation_hash.py @@ -0,0 +1,32 @@ +# DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/python/mod.rs +# Based on "crates/re_types/definitions/rerun/blueprint/components/viewer_recommendation_hash.fbs". + +# You can extend this class by creating a "ViewerRecommendationHashExt" class in "viewer_recommendation_hash_ext.py". + +from __future__ import annotations + +from ... import datatypes +from ..._baseclasses import ComponentBatchMixin + +__all__ = ["ViewerRecommendationHash", "ViewerRecommendationHashBatch", "ViewerRecommendationHashType"] + + +class ViewerRecommendationHash(datatypes.UInt64): + """ + **Component**: Hash of a viewer recommendation. + + The formation of this hash is considered an internal implementation detail of the viewer. + """ + + # You can define your own __init__ function as a member of ViewerRecommendationHashExt in viewer_recommendation_hash_ext.py + + # Note: there are no fields here because ViewerRecommendationHash delegates to datatypes.UInt64 + pass + + +class ViewerRecommendationHashType(datatypes.UInt64Type): + _TYPE_NAME: str = "rerun.blueprint.components.ViewerRecommendationHash" + + +class ViewerRecommendationHashBatch(datatypes.UInt64Batch, ComponentBatchMixin): + _ARROW_TYPE = ViewerRecommendationHashType() diff --git a/rerun_py/rerun_sdk/rerun/datatypes/.gitattributes b/rerun_py/rerun_sdk/rerun/datatypes/.gitattributes index c461d2e7028a..4cbc75d706ad 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/datatypes/.gitattributes @@ -27,6 +27,7 @@ transform3d.py linguist-generated=true translation_and_mat3x3.py linguist-generated=true translation_rotation_scale3d.py linguist-generated=true uint32.py linguist-generated=true +uint64.py linguist-generated=true utf8.py linguist-generated=true uuid.py linguist-generated=true uvec2d.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 214e7ea14d64..3b16797e8d15 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/__init__.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/__init__.py @@ -75,6 +75,7 @@ TranslationRotationScale3DType, ) from .uint32 import UInt32, UInt32ArrayLike, UInt32Batch, UInt32Like, UInt32Type +from .uint64 import UInt64, UInt64ArrayLike, UInt64Batch, UInt64Like, UInt64Type from .utf8 import Utf8, Utf8ArrayLike, Utf8Batch, Utf8Like, Utf8Type from .uuid import Uuid, UuidArrayLike, UuidBatch, UuidLike, UuidType from .uvec2d import UVec2D, UVec2DArrayLike, UVec2DBatch, UVec2DLike, UVec2DType @@ -210,6 +211,11 @@ "UInt32Batch", "UInt32Like", "UInt32Type", + "UInt64", + "UInt64ArrayLike", + "UInt64Batch", + "UInt64Like", + "UInt64Type", "UVec2D", "UVec2DArrayLike", "UVec2DBatch", diff --git a/rerun_py/rerun_sdk/rerun/datatypes/uint32.py b/rerun_py/rerun_sdk/rerun/datatypes/uint32.py index 4181718edfd5..3856c9707bbe 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/uint32.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/uint32.py @@ -5,7 +5,7 @@ from __future__ import annotations -from typing import Any, Sequence, Union +from typing import TYPE_CHECKING, Any, Sequence, Union import numpy as np import numpy.typing as npt @@ -13,12 +13,13 @@ from attrs import define, field from .._baseclasses import BaseBatch, BaseExtensionType +from .uint32_ext import UInt32Ext __all__ = ["UInt32", "UInt32ArrayLike", "UInt32Batch", "UInt32Like", "UInt32Type"] @define(init=False) -class UInt32: +class UInt32(UInt32Ext): """**Datatype**: A 32bit unsigned integer.""" def __init__(self: Any, value: UInt32Like): @@ -37,11 +38,12 @@ def __int__(self) -> int: return int(self.value) -UInt32Like = UInt32 -UInt32ArrayLike = Union[ - UInt32, - Sequence[UInt32Like], -] +if TYPE_CHECKING: + UInt32Like = Union[UInt32, int] +else: + UInt32Like = Any + +UInt32ArrayLike = Union[UInt32, Sequence[UInt32Like], int, npt.NDArray[np.uint32]] class UInt32Type(BaseExtensionType): @@ -56,4 +58,4 @@ class UInt32Batch(BaseBatch[UInt32ArrayLike]): @staticmethod def _native_to_pa_array(data: UInt32ArrayLike, data_type: pa.DataType) -> pa.Array: - raise NotImplementedError # You need to implement native_to_pa_array_override in uint32_ext.py + return UInt32Ext.native_to_pa_array_override(data, data_type) diff --git a/rerun_py/rerun_sdk/rerun/datatypes/uint32_ext.py b/rerun_py/rerun_sdk/rerun/datatypes/uint32_ext.py new file mode 100644 index 000000000000..85a4067464fc --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/datatypes/uint32_ext.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import numpy as np +import pyarrow as pa + +if TYPE_CHECKING: + from . import UInt32ArrayLike + + +class UInt32Ext: + """Extension for [UInt32][rerun.datatypes.UInt32].""" + + @staticmethod + def native_to_pa_array_override(data: UInt32ArrayLike, data_type: pa.DataType) -> pa.Array: + array = np.asarray(data, dtype=np.uint32).flatten() + return pa.array(array, type=data_type) diff --git a/rerun_py/rerun_sdk/rerun/datatypes/uint64.py b/rerun_py/rerun_sdk/rerun/datatypes/uint64.py new file mode 100644 index 000000000000..5a56ad7761b7 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/datatypes/uint64.py @@ -0,0 +1,61 @@ +# DO NOT EDIT! This file was auto-generated by crates/re_types_builder/src/codegen/python/mod.rs +# Based on "crates/re_types/definitions/rerun/datatypes/uint64.fbs". + +# You can extend this class by creating a "UInt64Ext" class in "uint64_ext.py". + +from __future__ import annotations + +from typing import TYPE_CHECKING, 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 .uint64_ext import UInt64Ext + +__all__ = ["UInt64", "UInt64ArrayLike", "UInt64Batch", "UInt64Like", "UInt64Type"] + + +@define(init=False) +class UInt64(UInt64Ext): + """**Datatype**: A 64bit unsigned integer.""" + + def __init__(self: Any, value: UInt64Like): + """Create a new instance of the UInt64 datatype.""" + + # You can define your own __init__ function as a member of UInt64Ext in uint64_ext.py + self.__attrs_init__(value=value) + + value: int = field(converter=int) + + def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: + # You can define your own __array__ function as a member of UInt64Ext in uint64_ext.py + return np.asarray(self.value, dtype=dtype) + + def __int__(self) -> int: + return int(self.value) + + +if TYPE_CHECKING: + UInt64Like = Union[UInt64, int] +else: + UInt64Like = Any + +UInt64ArrayLike = Union[UInt64, Sequence[UInt64Like], int, npt.NDArray[np.uint64]] + + +class UInt64Type(BaseExtensionType): + _TYPE_NAME: str = "rerun.datatypes.UInt64" + + def __init__(self) -> None: + pa.ExtensionType.__init__(self, pa.uint64(), self._TYPE_NAME) + + +class UInt64Batch(BaseBatch[UInt64ArrayLike]): + _ARROW_TYPE = UInt64Type() + + @staticmethod + def _native_to_pa_array(data: UInt64ArrayLike, data_type: pa.DataType) -> pa.Array: + return UInt64Ext.native_to_pa_array_override(data, data_type) diff --git a/rerun_py/rerun_sdk/rerun/datatypes/uint64_ext.py b/rerun_py/rerun_sdk/rerun/datatypes/uint64_ext.py new file mode 100644 index 000000000000..87dc951c3129 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/datatypes/uint64_ext.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import numpy as np +import pyarrow as pa + +if TYPE_CHECKING: + from . import UInt64ArrayLike + + +class UInt64Ext: + """Extension for [UInt64][rerun.datatypes.UInt64].""" + + @staticmethod + def native_to_pa_array_override(data: UInt64ArrayLike, data_type: pa.DataType) -> pa.Array: + array = np.asarray(data, dtype=np.uint64).flatten() + return pa.array(array, type=data_type) diff --git a/rerun_py/tests/unit/test_space_view_contents.py b/rerun_py/tests/unit/test_space_view_contents.py index 67938335358a..36ead02d1e83 100644 --- a/rerun_py/tests/unit/test_space_view_contents.py +++ b/rerun_py/tests/unit/test_space_view_contents.py @@ -1,50 +1,35 @@ from __future__ import annotations import itertools -from typing import Optional, cast +from typing import cast from rerun.blueprint.archetypes.space_view_contents import SpaceViewContents -from rerun.blueprint.components.entities_determined_by_user import ( - EntitiesDeterminedByUser, - EntitiesDeterminedByUserBatch, - EntitiesDeterminedByUserLike, -) from rerun.blueprint.components.query_expression import QueryExpression, QueryExpressionBatch from rerun.datatypes.utf8 import Utf8Like -from .common_arrays import none_empty_or_value - def test_space_view_contents() -> None: query_array = ["+ /**\n- /robot", QueryExpression("+ /**\n- /robot")] - entities_determined_by_user_arrays = [False, EntitiesDeterminedByUser(False), None] all_arrays = itertools.zip_longest( query_array, - entities_determined_by_user_arrays, ) - for query, entities_determined_by_user in all_arrays: - query = query if query is not None else query_array[-1] + for (query,) in all_arrays: + # query = query if query is not None else query_array[-1] # mypy can't track types properly through itertools zip so re-cast query = cast(Utf8Like, query) - entities_determined_by_user = cast(Optional[EntitiesDeterminedByUserLike], entities_determined_by_user) print( "rr.SpaceViewContents(\n", f" {query!r}\n", - f" entities_determined_by_user={entities_determined_by_user!r}\n", ")", ) arch = SpaceViewContents( query, - entities_determined_by_user=entities_determined_by_user, ) print(f"{arch}\n") # Equality checks on some of these are a bit silly, but at least they test out that the serialization code runs without problems. assert arch.query == QueryExpressionBatch("+ /**\n- /robot") - assert arch.entities_determined_by_user == EntitiesDeterminedByUserBatch._optional( - none_empty_or_value(arch.entities_determined_by_user, False) - ) diff --git a/rerun_py/tests/unit/test_viewport_blueprint.py b/rerun_py/tests/unit/test_viewport_blueprint.py index 6bda35b8f607..153ef1ea467a 100644 --- a/rerun_py/tests/unit/test_viewport_blueprint.py +++ b/rerun_py/tests/unit/test_viewport_blueprint.py @@ -1,13 +1,20 @@ from __future__ import annotations import itertools +from typing import Optional, cast from rerun.blueprint.archetypes.viewport_blueprint import ViewportBlueprint -from rerun.blueprint.components.auto_layout import AutoLayoutBatch -from rerun.blueprint.components.auto_space_views import AutoSpaceViewsBatch +from rerun.blueprint.components.auto_layout import AutoLayoutBatch, AutoLayoutLike +from rerun.blueprint.components.auto_space_views import AutoSpaceViewsBatch, AutoSpaceViewsLike from rerun.blueprint.components.included_space_view import IncludedSpaceViewBatch from rerun.blueprint.components.root_container import RootContainerBatch from rerun.blueprint.components.space_view_maximized import SpaceViewMaximizedBatch +from rerun.blueprint.components.viewer_recommendation_hash import ( + ViewerRecommendationHash, + ViewerRecommendationHashBatch, +) +from rerun.datatypes.uint64 import UInt64ArrayLike +from rerun.datatypes.uuid import UuidArrayLike, UuidLike from .common_arrays import none_empty_or_value, uuid_bytes0, uuid_bytes1, uuids_arrays @@ -24,6 +31,11 @@ def test_viewport_blueprint() -> None: ] auto_layout_arrays = [None, True] auto_space_views_arrays = [None, False] + viewer_recommendation_hash_arrays = [ + None, + [123, 321], + [ViewerRecommendationHash(123), ViewerRecommendationHash(321)], + ] all_arrays = itertools.zip_longest( space_views_arrays, @@ -31,11 +43,27 @@ def test_viewport_blueprint() -> None: maximized_arrays, auto_layout_arrays, auto_space_views_arrays, + viewer_recommendation_hash_arrays, ) - for space_views, root_container, maximized, auto_layout, auto_space_views in all_arrays: + for ( + space_views, + root_container, + maximized, + auto_layout, + auto_space_views, + past_viewer_recommendations, + ) in all_arrays: space_views = space_views if space_views is not None else space_views_arrays[-1] + # mypy can't track types properly through itertools zip so re-cast + space_views = cast(UuidArrayLike, space_views) + root_container = cast(Optional[UuidLike], root_container) + maximized = cast(Optional[UuidLike], maximized) + auto_layout = cast(Optional[AutoLayoutLike], auto_layout) + auto_space_views = cast(Optional[AutoSpaceViewsLike], auto_space_views) + past_viewer_recommendations = cast(Optional[UInt64ArrayLike], past_viewer_recommendations) + print( "rr.ViewportBlueprint(\n", f" space_views={space_views!r}\n", @@ -43,6 +71,7 @@ def test_viewport_blueprint() -> None: f" maximized={maximized!r}\n", f" auto_layout={auto_layout!r}\n", f" auto_space_views={auto_space_views!r}\n", + f" past_viewer_recommendations={past_viewer_recommendations!r}\n", ")", ) arch = ViewportBlueprint( @@ -51,6 +80,7 @@ def test_viewport_blueprint() -> None: maximized=maximized, auto_layout=auto_layout, auto_space_views=auto_space_views, + past_viewer_recommendations=past_viewer_recommendations, ) print(f"{arch}\n") @@ -59,3 +89,6 @@ def test_viewport_blueprint() -> None: assert arch.maximized == SpaceViewMaximizedBatch._optional(none_empty_or_value(maximized, uuid_bytes1)) assert arch.auto_layout == AutoLayoutBatch._optional(none_empty_or_value(auto_layout, True)) assert arch.auto_space_views == AutoSpaceViewsBatch._optional(none_empty_or_value(auto_space_views, False)) + assert arch.past_viewer_recommendations == ViewerRecommendationHashBatch._optional( + none_empty_or_value(past_viewer_recommendations, [123, 321]) + )