diff --git a/Cargo.lock b/Cargo.lock index 3a3ac49d34d2..ce1624ca4794 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9754,7 +9754,6 @@ dependencies = [ "re_tracing", "re_types", "thiserror 2.0.17", - "vec1", ] [[package]] diff --git a/crates/store/re_tf/Cargo.toml b/crates/store/re_tf/Cargo.toml index f4eaad077826..3c61f2bf80dd 100644 --- a/crates/store/re_tf/Cargo.toml +++ b/crates/store/re_tf/Cargo.toml @@ -37,7 +37,6 @@ itertools.workspace = true nohash-hasher.workspace = true parking_lot.workspace = true thiserror.workspace = true -vec1 = { workspace = true, features = ["smallvec-v1"] } [dev-dependencies] criterion.workspace = true diff --git a/crates/store/re_tf/src/lib.rs b/crates/store/re_tf/src/lib.rs index cc7858cb48b2..e67b70175426 100644 --- a/crates/store/re_tf/src/lib.rs +++ b/crates/store/re_tf/src/lib.rs @@ -146,7 +146,7 @@ mod transform_resolution_cache; pub mod convert; -pub use transform_forest::{PinholeTreeRoot, TransformForest, TransformFromToError, TransformInfo}; +pub use transform_forest::{PinholeTreeRoot, TransformForest, TransformFromToError, TreeTransform}; pub use transform_queries::{query_view_coordinates, query_view_coordinates_at_closest_ancestor}; pub use transform_resolution_cache::{ CachedTransformsForTimeline, ResolvedPinholeProjection, TransformResolutionCache, diff --git a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__root.snap b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__root.snap index 7e7bf88bbb8a..0865b474fc91 100644 --- a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__root.snap +++ b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__root.snap @@ -6,7 +6,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -32,39 +32,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, ), ), ( tf#/top, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -90,63 +64,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 11.0, - 0.0, - 0.0, - ), - }, - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 21.0, - 0.0, - 0.0, - ), - }, - ], }, ), ), ( tf#/top/pinhole, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -172,39 +96,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 1.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.5, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.6666666666666666, - ), - }, - translation: DVec3( - -49.0, - -49.0, - 1.0, - ), - }, - ], }, ), ), ( tf#/top/child3d, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -230,32 +128,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 1.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 1.0, - 0.0, - 1.0, - ), - }, - ], }, ), ), @@ -270,7 +142,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/top/pinhole/child2d, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -296,39 +168,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 1.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.5, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.6666666666666666, - ), - }, - translation: DVec3( - -47.0, - -49.0, - 1.0, - ), - }, - ], }, ), ), ( tf#/top/pure_leaf_pinhole, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pure_leaf_pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -354,32 +200,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 1.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.5, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.6666666666666666, - ), - }, - translation: DVec3( - -49.0, - -50.0, - 1.0, - ), - }, - ], }, ), ), diff --git a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top.snap b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top.snap index bd921f7e6193..7e86880d69d2 100644 --- a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top.snap +++ b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top.snap @@ -6,7 +6,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -32,39 +32,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - -1.0, - 0.0, - 0.0, - ), - }, - ], }, ), ), ( tf#/top, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -90,63 +64,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 10.0, - 0.0, - 0.0, - ), - }, - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 20.0, - 0.0, - 0.0, - ), - }, - ], }, ), ), ( tf#/top/pinhole, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -172,39 +96,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 1.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.5, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.6666666666666666, - ), - }, - translation: DVec3( - -50.0, - -49.0, - 1.0, - ), - }, - ], }, ), ), ( tf#/top/child3d, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -230,32 +128,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 1.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - ], }, ), ), @@ -270,7 +142,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/top/pinhole/child2d, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -296,39 +168,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 1.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.5, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.6666666666666666, - ), - }, - translation: DVec3( - -48.0, - -49.0, - 1.0, - ), - }, - ], }, ), ), ( tf#/top/pure_leaf_pinhole, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pure_leaf_pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -354,32 +200,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 1.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.5, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.6666666666666666, - ), - }, - translation: DVec3( - -50.0, - -50.0, - 1.0, - ), - }, - ], }, ), ), diff --git a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pinhole.snap b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pinhole.snap index e0d1ca9a3eb0..2e39768445f7 100644 --- a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pinhole.snap +++ b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pinhole.snap @@ -6,7 +6,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -32,39 +32,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" -1.5, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 49.998, - 99.996, - -1.5, - ), - }, - ], }, ), ), ( tf#/top, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -90,63 +64,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" -1.5, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 50.019999999999996, - 99.996, - -1.5, - ), - }, - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 50.04, - 99.996, - -1.5, - ), - }, - ], }, ), ), ( tf#/top/pinhole, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -172,39 +96,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, ), ), ( tf#/top/child3d, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -230,32 +128,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" -1.497, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 50.0, - 99.996, - -1.497, - ), - }, - ], }, ), ), @@ -270,7 +142,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/top/pinhole/child2d, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -296,32 +168,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 2.0, - 0.0, - 0.0, - ), - }, - ], }, ), ), diff --git a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pinhole_child2d.snap b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pinhole_child2d.snap index 94dc1288436a..32c0b882b40b 100644 --- a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pinhole_child2d.snap +++ b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pinhole_child2d.snap @@ -6,7 +6,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -32,39 +32,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" -1.5, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 47.998, - 99.996, - -1.5, - ), - }, - ], }, ), ), ( tf#/top, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -90,63 +64,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" -1.5, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 48.019999999999996, - 99.996, - -1.5, - ), - }, - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 48.04, - 99.996, - -1.5, - ), - }, - ], }, ), ), ( tf#/top/pinhole, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -172,39 +96,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - -2.0, - 0.0, - 0.0, - ), - }, - ], }, ), ), ( tf#/top/child3d, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -230,32 +128,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" -1.497, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 48.0, - 99.996, - -1.497, - ), - }, - ], }, ), ), @@ -270,7 +142,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/top/pinhole/child2d, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -296,32 +168,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, ), ), diff --git a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pure_leaf_pinhole.snap b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pure_leaf_pinhole.snap index 8a93f9c1f2b3..39e0162ca1e1 100644 --- a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pure_leaf_pinhole.snap +++ b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_entity_hierarchy__transform_from_to__top_pure_leaf_pinhole.snap @@ -6,7 +6,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -32,39 +32,13 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" -1.5, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 49.998, - 100.0, - -1.5, - ), - }, - ], }, ), ), ( tf#/top, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -90,56 +64,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" -1.5, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 50.019999999999996, - 100.0, - -1.5, - ), - }, - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 50.04, - 100.0, - -1.5, - ), - }, - ], }, ), ), @@ -157,7 +81,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/top/child3d, Ok( - TransformInfo { + TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -183,32 +107,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" -1.497, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 0.002, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 0.004, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 0.003, - ), - }, - translation: DVec3( - 50.0, - 100.0, - -1.497, - ), - }, - ], }, ), ), @@ -234,7 +132,7 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" ( tf#/top/pure_leaf_pinhole, Ok( - TransformInfo { + TreeTransform { root: tf#/top/pure_leaf_pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -260,32 +158,6 @@ expression: "pretty_print_transform_frame_ids_in(&result, &transform_cache)" 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, ), ), diff --git a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_frame_hierarchy__all_on_single_entity.snap b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_frame_hierarchy__all_on_single_entity.snap index 41bb90fe2f48..4cedb3c30d60 100644 --- a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_frame_hierarchy__all_on_single_entity.snap +++ b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_frame_hierarchy__all_on_single_entity.snap @@ -3,7 +3,7 @@ source: crates/store/re_tf/src/transform_forest.rs expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_frame,\n&transform_cache)" --- { - top: TransformInfo { + top: TreeTransform { root: root, target_from_source: DAffine3 { matrix3: DMat3 { @@ -29,34 +29,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 1.0, - 0.0, - 0.0, - ), - }, - ], }, - tf#/: TransformInfo { + tf#/: TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -82,34 +56,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - tf#/tf: TransformInfo { + tf#/tf: TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -135,34 +83,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - child0: TransformInfo { + child0: TreeTransform { root: root, target_from_source: DAffine3 { matrix3: DMat3 { @@ -188,34 +110,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 3.0, - 0.0, - 0.0, - ), - }, - ], }, - root: TransformInfo { + root: TreeTransform { root: root, target_from_source: DAffine3 { matrix3: DMat3 { @@ -241,34 +137,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - child2d: TransformInfo { + child2d: TreeTransform { root: pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -294,34 +164,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 2.0, - 0.0, - ), - }, - ], }, - child1: TransformInfo { + child1: TreeTransform { root: root, target_from_source: DAffine3 { matrix3: DMat3 { @@ -347,34 +191,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 4.0, - 0.0, - 0.0, - ), - }, - ], }, - pinhole: TransformInfo { + pinhole: TreeTransform { root: pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -400,31 +218,5 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, } diff --git a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_frame_hierarchy__multiple_entities.snap b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_frame_hierarchy__multiple_entities.snap index a41eb94f8771..b17529aeeffa 100644 --- a/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_frame_hierarchy__multiple_entities.snap +++ b/crates/store/re_tf/src/snapshots/re_tf__transform_forest__tests__simple_frame_hierarchy__multiple_entities.snap @@ -3,7 +3,7 @@ source: crates/store/re_tf/src/transform_forest.rs expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_frame,\n&transform_cache)" --- { - tf#/transforms1: TransformInfo { + tf#/transforms1: TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -29,34 +29,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - top: TransformInfo { + top: TreeTransform { root: root, target_from_source: DAffine3 { matrix3: DMat3 { @@ -82,34 +56,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 1.0, - 0.0, - 0.0, - ), - }, - ], }, - tf#/: TransformInfo { + tf#/: TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -135,34 +83,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - tf#/transforms4: TransformInfo { + tf#/transforms4: TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -188,34 +110,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - root: TransformInfo { + root: TreeTransform { root: root, target_from_source: DAffine3 { matrix3: DMat3 { @@ -241,34 +137,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - child0: TransformInfo { + child0: TreeTransform { root: root, target_from_source: DAffine3 { matrix3: DMat3 { @@ -294,34 +164,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 3.0, - 0.0, - 0.0, - ), - }, - ], }, - child2d: TransformInfo { + child2d: TreeTransform { root: pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -347,34 +191,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 2.0, - 0.0, - ), - }, - ], }, - tf#/transforms0: TransformInfo { + tf#/transforms0: TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -400,34 +218,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - child1: TransformInfo { + child1: TreeTransform { root: root, target_from_source: DAffine3 { matrix3: DMat3 { @@ -453,34 +245,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 4.0, - 0.0, - 0.0, - ), - }, - ], }, - pinhole: TransformInfo { + pinhole: TreeTransform { root: pinhole, target_from_source: DAffine3 { matrix3: DMat3 { @@ -506,34 +272,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - tf#/transforms3: TransformInfo { + tf#/transforms3: TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -559,34 +299,8 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, - tf#/transforms2: TransformInfo { + tf#/transforms2: TreeTransform { root: tf#/, target_from_source: DAffine3 { matrix3: DMat3 { @@ -612,31 +326,5 @@ expression: "pretty_print_transform_frame_ids_in(&transform_forest.root_from_fra 0.0, ), }, - target_from_instances: [ - DAffine3 { - matrix3: DMat3 { - x_axis: DVec3( - 1.0, - 0.0, - 0.0, - ), - y_axis: DVec3( - 0.0, - 1.0, - 0.0, - ), - z_axis: DVec3( - 0.0, - 0.0, - 1.0, - ), - }, - translation: DVec3( - 0.0, - 0.0, - 0.0, - ), - }, - ], }, } diff --git a/crates/store/re_tf/src/transform_forest.rs b/crates/store/re_tf/src/transform_forest.rs index 8085896a1da9..59118e0a16e4 100644 --- a/crates/store/re_tf/src/transform_forest.rs +++ b/crates/store/re_tf/src/transform_forest.rs @@ -1,5 +1,4 @@ use nohash_hasher::{IntMap, IntSet}; -use vec1::smallvec_v1::SmallVec1; use crate::frame_id_registry::FrameIdRegistry; use crate::transform_resolution_cache::ParentFromChildTransform; @@ -9,13 +8,12 @@ use crate::{ }; use re_chunk_store::LatestAtQuery; -use re_entity_db::{EntityDb, EntityPath}; -use re_types::ArchetypeName; +use re_entity_db::EntityDb; use re_types::components::TransformFrameId; /// Details on how to transform from a source to a target frame. #[derive(Clone, Debug, PartialEq)] -pub struct TransformInfo { +pub struct TreeTransform { /// Root frame this transform belongs to. /// /// ⚠️ This is the root of the tree this transform belongs to, @@ -25,67 +23,23 @@ pub struct TransformInfo { /// We could add target and maybe even source to this, but we want to keep this struct small'ish. /// On that note, it may be good to split this in the future, as most of the time we're only interested in the /// source->target affine transform. - root: TransformFrameIdHash, + pub root: TransformFrameIdHash, /// The transform from this frame to the target's space. /// - /// ⚠️ Does not include per instance poses! ⚠️ /// Include 3D-from-2D / 2D-from-3D pinhole transform if present. - target_from_source: glam::DAffine3, - - /// List of transforms per instance including poses. - /// - /// If no poses are present, this is always the same as [`Self::target_from_source`]. - /// (also implying that in this case there is only a single element). - /// If there are poses, there may be more than one element. - target_from_instances: SmallVec1<[glam::DAffine3; 1]>, + pub target_from_source: glam::DAffine3, } -impl TransformInfo { +impl TreeTransform { fn new_root(root: TransformFrameIdHash) -> Self { Self { root, target_from_source: glam::DAffine3::IDENTITY, - target_from_instances: SmallVec1::new(glam::DAffine3::IDENTITY), } } - /// Returns the root frame of the tree this transform belongs to. - /// - /// This is **not** necessarily the transform's target frame. - #[inline] - pub fn tree_root(&self) -> TransformFrameIdHash { - self.root - } - - /// Warns that multiple transforms on an entity are not supported. - #[inline] - fn warn_on_per_instance_transform(&self, entity_name: &EntityPath, archetype: ArchetypeName) { - if self.target_from_instances.len() > 1 { - re_log::warn_once!( - "There are multiple poses for entity {entity_name:?}'s transform frame. {archetype:?} supports only one transform per entity. Using the first one." - ); - } - } - - /// Returns the first instance transform and warns if there are multiple. - #[inline] - pub fn single_transform_required_for_entity( - &self, - entity_name: &EntityPath, - archetype: ArchetypeName, - ) -> glam::DAffine3 { - self.warn_on_per_instance_transform(entity_name, archetype); - *self.target_from_instances.first() - } - - /// Returns the target from instance transforms. - #[inline] - pub fn target_from_instances(&self) -> &SmallVec1<[glam::DAffine3; 1]> { - &self.target_from_instances - } - - /// Multiplies all transforms from the left by `target_from_reference` + /// Multiplies the transform from the left by `target_from_reference` /// /// Or in other words: /// `reference_from_source = self` @@ -94,19 +48,13 @@ impl TransformInfo { let Self { root, target_from_source: reference_from_source, - target_from_instances: reference_from_source_instances, } = self; let target_from_source = target_from_reference * reference_from_source; - let target_from_source_instances = left_multiply_smallvec1_of_transforms( - target_from_reference, - reference_from_source_instances, - ); Self { root: *root, target_from_source, - target_from_instances: target_from_source_instances, } } } @@ -152,7 +100,7 @@ struct TargetInfo { struct SourceInfo<'a> { id: TransformFrameIdHash, root: TransformFrameIdHash, - root_from_source: &'a TransformInfo, + root_from_source: &'a TreeTransform, } /// Properties of a pinhole transform tree root. @@ -198,7 +146,7 @@ pub struct TransformForest { /// /// Roots are also contained, targeting themselves with identity. /// This simplifies lookups. - root_from_frame: IntMap, + root_from_frame: IntMap, // // TODO(RR-2667): Store errors that occur during the graph walk } @@ -299,7 +247,7 @@ impl TransformForest { // That parent apparently won't show up in any transform stack (we didn't walk there because there was no information about it!) // So if we don't add this root now to our `root_from_frame` map, we'd never fill out the required self-reference! self.root_from_frame - .insert(parent_frame, TransformInfo::new_root(parent_frame)); + .insert(parent_frame, TreeTransform::new_root(parent_frame)); (parent_frame, glam::DAffine3::IDENTITY) } @@ -355,15 +303,9 @@ impl TransformForest { root_from_current_frame = glam::DAffine3::IDENTITY; } - // Collect & compute poses. - let pose_transforms = &transforms.child_from_instance_poses; - let root_from_instances = - compute_root_from_poses(root_from_current_frame, pose_transforms); - - let transform_root_from_current = TransformInfo { + let transform_root_from_current = TreeTransform { root: root_frame, target_from_source: root_from_current_frame, - target_from_instances: root_from_instances, }; let previous_transform = self @@ -463,7 +405,7 @@ impl TransformForest { /// Returns the transform information of how to get from a given frame to its tree root. #[inline] - pub fn root_from_frame(&self, frame: TransformFrameIdHash) -> Option<&TransformInfo> { + pub fn root_from_frame(&self, frame: TransformFrameIdHash) -> Option<&TreeTransform> { self.root_from_frame.get(&frame) } @@ -487,7 +429,7 @@ impl TransformForest { ) -> impl Iterator< Item = ( TransformFrameIdHash, - Result, + Result, ), > { // We're looking for a common root between source and target. @@ -504,11 +446,9 @@ impl TransformForest { // Invert `root_from_target` to get `target.from_root`. let target = { - let TransformInfo { + let TreeTransform { root: target_root, target_from_source: root_from_entity, - // Don't care about instance transforms on the target frame, as they don't tree-propagate. - target_from_instances: _, } = &root_from_target; TargetInfo { @@ -588,7 +528,7 @@ fn from_2d_source_to_3d_target( source_pinhole_tree_root: &PinholeTreeRoot, lookup_image_plane_distance: &dyn Fn(TransformFrameIdHash) -> f64, target_from_image_plane_cache: &mut IntMap, -) -> Result { +) -> Result { let PinholeTreeRoot { parent_tree_root, pinhole_projection, @@ -624,7 +564,7 @@ fn from_3d_source_to_2d_target( source: &SourceInfo<'_>, target_pinhole_tree_root: &PinholeTreeRoot, target_from_source_root_cache: &mut IntMap, -) -> Result { +) -> Result { let PinholeTreeRoot { parent_tree_root, pinhole_projection, @@ -665,36 +605,6 @@ fn from_3d_source_to_2d_target( Ok(source.root_from_source.left_multiply(*target_from_root)) } -fn left_multiply_smallvec1_of_transforms( - target_from_reference: glam::DAffine3, - reference_from_source: &SmallVec1<[glam::DAffine3; 1]>, -) -> SmallVec1<[glam::DAffine3; 1]> { - // Easiest to deal with SmallVec1 in-place. - let mut target_from_source = reference_from_source.clone(); - for transform in &mut target_from_source { - *transform = target_from_reference * *transform; - } - target_from_source -} - -fn compute_root_from_poses( - root_from_entity: glam::DAffine3, - instance_from_poses: &[glam::DAffine3], -) -> SmallVec1<[glam::DAffine3; 1]> { - let Ok(mut reference_from_poses) = - SmallVec1::<[glam::DAffine3; 1]>::try_from_slice(instance_from_poses) - else { - return SmallVec1::new(root_from_entity); - }; - - // Until now `reference_from_poses` is actually `reference_from_entity`. - for reference_from_pose in &mut reference_from_poses { - let entity_from_pose = *reference_from_pose; - *reference_from_pose = root_from_entity * entity_from_pose; - } - reference_from_poses -} - fn pinhole3d_from_image_plane( resolved_pinhole_projection: &ResolvedPinholeProjection, pinhole_image_plane_distance: f64, @@ -743,7 +653,6 @@ struct ParentChildTransforms { parent_frame: Option, child_frame: TransformFrameIdHash, parent_from_child: Option, - child_from_instance_poses: Vec, pinhole_projection: Option, } @@ -755,16 +664,13 @@ fn transforms_at( transforms_for_timeline: &CachedTransformsForTimeline, ) -> ParentChildTransforms { let mut parent_from_child; - let child_from_instance_poses; let pinhole_projection; if let Some(source_transforms) = transforms_for_timeline.frame_transforms(child_frame) { parent_from_child = source_transforms.latest_at_transform(entity_db, query); - child_from_instance_poses = source_transforms.latest_at_instance_poses(entity_db, query); pinhole_projection = source_transforms.latest_at_pinhole(entity_db, query); } else { parent_from_child = None; - child_from_instance_poses = Vec::new(); pinhole_projection = None; } @@ -808,7 +714,6 @@ fn transforms_at( parent_frame, child_frame, parent_from_child, - child_from_instance_poses, pinhole_projection, } } @@ -823,7 +728,7 @@ mod tests { use re_chunk_store::Chunk; use re_entity_db::EntityDb; - use re_log_types::{StoreInfo, TimeCell, TimePoint, Timeline, TimelineName}; + use re_log_types::{EntityPath, StoreInfo, TimeCell, TimePoint, Timeline, TimelineName}; use re_types::components::TransformFrameId; use re_types::{RowId, archetypes, components}; @@ -1388,14 +1293,11 @@ mod tests { .collect::>(), vec![( TransformFrameIdHash::from_str("new_top"), - Ok(TransformInfo { + Ok(TreeTransform { root: TransformFrameIdHash::from_str("new_top"), target_from_source: glam::DAffine3::from_translation(glam::dvec3( -5.0, 0.0, 0.0 )), - target_from_instances: SmallVec1::new(glam::DAffine3::from_translation( - glam::dvec3(-5.0, 0.0, 0.0) - )), }) )] ); diff --git a/crates/store/re_tf/src/transform_resolution_cache.rs b/crates/store/re_tf/src/transform_resolution_cache.rs index 5dd6e9e45b0c..97fdf28b80ec 100644 --- a/crates/store/re_tf/src/transform_resolution_cache.rs +++ b/crates/store/re_tf/src/transform_resolution_cache.rs @@ -20,7 +20,7 @@ use crate::{ use re_arrow_util::ArrowArrayDowncastRef as _; use re_chunk_store::{Chunk, LatestAtQuery, external::arrow}; use re_entity_db::EntityDb; -use re_log_types::{EntityPath, TimeInt, TimelineName}; +use re_log_types::{EntityPath, EntityPathHash, TimeInt, TimelineName}; use re_types::{ComponentIdentifier, archetypes, components}; /// Resolves all transform relationship defining components to affine transforms for fast lookup. @@ -31,10 +31,11 @@ use re_types::{ComponentIdentifier, archetypes, components}; /// There are different kinds of transforms handled here: /// * [`archetypes::Transform3D`] /// Tree transforms that should propagate in the tree (via [`crate::TransformForest`]). -/// * [`archetypes::InstancePoses3D`] -/// Instance poses that should be applied to the tree transforms (via [`crate::TransformForest`]) but not propagate. /// * [`components::PinholeProjection`] and [`components::ViewCoordinates`] /// Pinhole projections & associated view coordinates used for visualizing cameras in 3D and embedding 2D in 3D +/// * [`archetypes::InstancePoses3D`] +/// Instance poses that should be applied to the tree transforms (via [`crate::TransformForest`]) but not propagate. +/// Also unlike tree transforms, these are not associated with transform frames but rather with entity paths. pub struct TransformResolutionCache { /// The frame id registry is co-located in the resolution cache for convenience: /// the resolution cache is often the lowest level of transform access and @@ -54,6 +55,7 @@ impl Default for TransformResolutionCache { // `CachedTransformsForTimeline` intentionally doesn't implement Default to not accidentally create it without considering static transforms. static_timeline: CachedTransformsForTimeline { per_child_frame_transforms: Default::default(), + per_entity_poses: Default::default(), non_recursive_clears: Default::default(), recursive_clears: Default::default(), // Unused for static timeline. }, @@ -100,7 +102,12 @@ impl SizeBytes for ParentFromChildTransform { pub struct CachedTransformsForTimeline { /// Transforms information for each child frame to a parent frame over time. // Note that these are potentially a lot of mutexes, but `parking_lot`-Mutex are incredibly lightweight on all platforms, so not a memory concern. - per_child_frame_transforms: IntMap, + per_child_frame_transforms: IntMap, + + /// Instance pose information for each entity over time. + /// + /// Unlike all other transforms, poses are associated with an entity path, not a frame. + per_entity_poses: IntMap, /// We need to keep track of all clears that ever happened and when. /// Otherwise, new incoming frames may not correctly change their transform at the time of clear. @@ -120,29 +127,30 @@ impl CachedTransformsForTimeline { .map(|(transform_frame, static_transforms)| { ( *transform_frame, - TransformsForChildFrame::new_for_new_empty_timeline( + TreeTransformsForChildFrame::new_for_new_empty_timeline( *timeline, static_transforms, ), ) }) .collect(), + per_entity_poses: static_transforms.per_entity_poses.clone(), non_recursive_clears: IntMap::default(), recursive_clears: IntMap::default(), } } - fn get_or_create_transforms_temporal( + fn get_or_create_tree_transforms_temporal( &mut self, entity_path: &EntityPath, child_frame: TransformFrameIdHash, timeline: TimelineName, static_timeline: &mut Self, - ) -> &mut TransformsForChildFrame { + ) -> &mut TreeTransformsForChildFrame { self.per_child_frame_transforms .entry(child_frame) .or_insert_with(|| { - TransformsForChildFrame::new( + TreeTransformsForChildFrame::new( entity_path.clone(), child_frame, timeline, @@ -153,14 +161,42 @@ impl CachedTransformsForTimeline { }) } - fn get_or_create_transforms_static( + fn get_or_create_tree_transforms_static( &mut self, entity_path: &EntityPath, child_frame: TransformFrameIdHash, - ) -> &mut TransformsForChildFrame { + ) -> &mut TreeTransformsForChildFrame { self.per_child_frame_transforms .entry(child_frame) - .or_insert_with(|| TransformsForChildFrame::new_empty(entity_path.clone(), child_frame)) + .or_insert_with(|| { + TreeTransformsForChildFrame::new_empty(entity_path.clone(), child_frame) + }) + } + + fn get_or_create_pose_transforms_temporal( + &mut self, + entity_path: &EntityPath, + static_timeline: &mut Self, + ) -> &mut PoseTransformForEntity { + self.per_entity_poses + .entry(entity_path.hash()) + .or_insert_with(|| { + PoseTransformForEntity::new( + entity_path.clone(), + static_timeline, + &self.non_recursive_clears, + &self.recursive_clears, + ) + }) + } + + fn get_or_create_pose_transforms_static( + &mut self, + entity_path: &EntityPath, + ) -> &mut PoseTransformForEntity { + self.per_entity_poses + .entry(entity_path.hash()) + .or_insert_with(|| PoseTransformForEntity::new_empty(entity_path.clone())) } fn add_clear(&mut self, cleared_path: &EntityPath, cleared_time: TimeInt) { @@ -172,6 +208,9 @@ impl CachedTransformsForTimeline { transforms.events.get_mut().insert_clear(cleared_time); } } + if let Some(poses) = self.per_entity_poses.get_mut(&cleared_path.hash()) { + poses.insert_clear(cleared_time); + } // Store for future reference, so we can apply this on incoming. self.non_recursive_clears @@ -197,6 +236,12 @@ impl CachedTransformsForTimeline { } } + for poses in self.per_entity_poses.values_mut() { + if poses.entity_path.starts_with(recursively_cleared_path) { + poses.insert_clear(cleared_time); + } + } + // Store for future reference. self.recursive_clears .entry(recursively_cleared_path.clone()) @@ -221,6 +266,9 @@ impl CachedTransformsForTimeline { transforms.events.get_mut().remove_at(cleared_time); } } + if let Some(poses) = self.per_entity_poses.get_mut(&cleared_path.hash()) { + poses.poses_per_time.get_mut().remove(&cleared_time); + } } fn remove_recursive_clear( @@ -248,6 +296,11 @@ impl CachedTransformsForTimeline { transforms.events.get_mut().remove_at(cleared_time); } } + for poses in self.per_entity_poses.values_mut() { + if poses.entity_path.starts_with(recursively_cleared_path) { + poses.poses_per_time.get_mut().remove(&cleared_time); + } + } } /// Returns all transforms for a given child frame. @@ -255,10 +308,16 @@ impl CachedTransformsForTimeline { pub fn frame_transforms( &self, source_frame: TransformFrameIdHash, - ) -> Option<&TransformsForChildFrame> { + ) -> Option<&TreeTransformsForChildFrame> { self.per_child_frame_transforms.get(&source_frame) } + /// Returns all instance poses for a given entity path. + #[inline] + pub fn pose_transforms(&self, entity_path: EntityPathHash) -> Option<&PoseTransformForEntity> { + self.per_entity_poses.get(&entity_path) + } + /// All child frames for which we have connections to a parent. pub fn all_child_frames(&self) -> impl Iterator { self.per_child_frame_transforms.keys().copied() @@ -271,11 +330,13 @@ impl SizeBytes for CachedTransformsForTimeline { per_child_frame_transforms, non_recursive_clears, recursive_clears, + per_entity_poses, } = self; per_child_frame_transforms.heap_size_bytes() + non_recursive_clears.heap_size_bytes() + recursive_clears.heap_size_bytes() + + per_entity_poses.heap_size_bytes() } } @@ -302,8 +363,6 @@ impl SizeBytes for CachedTransformValue { type FrameTransformTimeMap = BTreeMap>; -type PoseTransformTimeMap = BTreeMap>>; - type PinholeProjectionMap = BTreeMap>; #[derive(Clone, Debug, PartialEq)] @@ -312,16 +371,14 @@ struct TransformsForChildFrameEvents { /// Whenever it changes, the previous parent frame is no longer reachable. frame_transforms: FrameTransformTimeMap, - pose_transforms: Option>, - pinhole_projections: Option>, + pinhole_projections: PinholeProjectionMap, } impl TransformsForChildFrameEvents { fn new_empty() -> Self { Self { frame_transforms: BTreeMap::new(), - pose_transforms: None, - pinhole_projections: None, + pinhole_projections: BTreeMap::new(), } } @@ -329,14 +386,10 @@ impl TransformsForChildFrameEvents { fn insert_clear(&mut self, time: TimeInt) { let Self { frame_transforms, - pose_transforms, pinhole_projections, } = self; - let pose_transforms = pose_transforms.get_or_insert(Default::default()); - let pinhole_projections = pinhole_projections.get_or_insert(Default::default()); frame_transforms.insert(time, CachedTransformValue::Cleared); - pose_transforms.insert(time, CachedTransformValue::Cleared); pinhole_projections.insert(time, CachedTransformValue::Cleared); } @@ -344,14 +397,10 @@ impl TransformsForChildFrameEvents { fn insert_clears(&mut self, time: &BTreeSet) { let Self { frame_transforms, - pose_transforms, pinhole_projections, } = self; - let pose_transforms = pose_transforms.get_or_insert(Default::default()); - let pinhole_projections = pinhole_projections.get_or_insert(Default::default()); frame_transforms.extend(time.iter().map(|t| (*t, CachedTransformValue::Cleared))); - pose_transforms.extend(time.iter().map(|t| (*t, CachedTransformValue::Cleared))); pinhole_projections.extend(time.iter().map(|t| (*t, CachedTransformValue::Cleared))); } @@ -359,33 +408,20 @@ impl TransformsForChildFrameEvents { fn remove_at(&mut self, time: TimeInt) { let Self { frame_transforms, - pose_transforms, pinhole_projections, } = self; frame_transforms.remove(&time); - if let Some(pose_transforms) = pose_transforms.as_mut() { - pose_transforms.remove(&time); - } - if let Some(pinhole_projections) = &mut pinhole_projections.as_mut() { - pinhole_projections.remove(&time); - } + pinhole_projections.remove(&time); } fn is_empty(&self) -> bool { let Self { frame_transforms, - pose_transforms, pinhole_projections, } = self; - frame_transforms.is_empty() - && pose_transforms - .as_ref() - .is_none_or(|poses| poses.is_empty()) - && pinhole_projections - .as_ref() - .is_none_or(|pinholes| pinholes.is_empty()) + frame_transforms.is_empty() && pinhole_projections.is_empty() } } @@ -393,13 +429,10 @@ impl SizeBytes for TransformsForChildFrameEvents { fn heap_size_bytes(&self) -> u64 { let Self { frame_transforms, - pose_transforms, pinhole_projections, } = self; - frame_transforms.heap_size_bytes() - + pose_transforms.heap_size_bytes() - + pinhole_projections.heap_size_bytes() + frame_transforms.heap_size_bytes() + pinhole_projections.heap_size_bytes() } } @@ -413,7 +446,7 @@ impl SizeBytes for TransformsForChildFrameEvents { /// Doing so would mean that queries using `re_query` yield information about a _different_ child frame /// which we then can't add to the cache entries of the current frame. #[derive(Debug)] -pub struct TransformsForChildFrame { +pub struct TreeTransformsForChildFrame { // Is None if this is about static time. #[cfg(debug_assertions)] timeline: Option, @@ -432,7 +465,7 @@ pub struct TransformsForChildFrame { events: Mutex, } -impl Clone for TransformsForChildFrame { +impl Clone for TreeTransformsForChildFrame { fn clone(&self) -> Self { Self { #[cfg(debug_assertions)] @@ -444,13 +477,13 @@ impl Clone for TransformsForChildFrame { } } -impl PartialEq for TransformsForChildFrame { +impl PartialEq for TreeTransformsForChildFrame { fn eq(&self, other: &Self) -> bool { self.child_frame == other.child_frame && *self.events.lock() == *other.events.lock() } } -impl SizeBytes for TransformsForChildFrame { +impl SizeBytes for TreeTransformsForChildFrame { fn heap_size_bytes(&self) -> u64 { let Self { associated_entity_path, @@ -516,7 +549,7 @@ impl SizeBytes for ResolvedPinholeProjection { } } -impl TransformsForChildFrame { +impl TreeTransformsForChildFrame { fn new( associated_entity_path: EntityPath, child_frame: TransformFrameIdHash, @@ -583,18 +616,10 @@ impl TransformsForChildFrame { add_invalidated_entry_if_not_already_cleared(&mut events.frame_transforms, time); } - /// Inserts an invalidation point for pose transforms. - fn invalidate_pose_transforms_at(&mut self, time: TimeInt) { - let events = self.events.get_mut(); - let poses = events.pose_transforms.get_or_insert_default(); - add_invalidated_entry_if_not_already_cleared(poses, time); - } - /// Inserts an invalidation point for pinhole projections. fn invalidate_pinhole_projection_at(&mut self, time: TimeInt) { let events = self.events.get_mut(); - let pinhole_projections = events.pinhole_projections.get_or_insert_default(); - add_invalidated_entry_if_not_already_cleared(pinhole_projections, time); + add_invalidated_entry_if_not_already_cleared(&mut events.pinhole_projections, time); } #[inline] @@ -646,42 +671,6 @@ impl TransformsForChildFrame { } } - #[inline] - pub fn latest_at_instance_poses( - &self, - entity_db: &EntityDb, - query: &LatestAtQuery, - ) -> Vec { - #[cfg(debug_assertions)] // `self.timeline` is only present with `debug_assertions` enabled. - debug_assert!(Some(query.timeline()) == self.timeline || self.timeline.is_none()); - - let mut events = self.events.lock(); - - let Some(pose_transforms) = events.pose_transforms.as_mut() else { - return Vec::new(); - }; - let Some((_t, pose_transform)) = pose_transforms.range_mut(..query.at().inc()).next_back() - else { - return Vec::new(); - }; - - // Separate check to work around borrow checker issues. - if pose_transform == &CachedTransformValue::Invalidated { - *pose_transform = - CachedTransformValue::Resident(query_and_resolve_instance_poses_at_entity( - &self.associated_entity_path, - entity_db, - query, - )); - } - - match pose_transform { - CachedTransformValue::Resident(transform) => transform.clone(), - CachedTransformValue::Cleared => Vec::new(), - CachedTransformValue::Invalidated => unreachable!("Just made transform cache-resident"), - } - } - #[inline] pub fn latest_at_pinhole( &self, @@ -695,7 +684,6 @@ impl TransformsForChildFrame { let pinhole_projection = events .pinhole_projections - .as_mut()? .range_mut(..query.at().inc()) .next_back()? .1; @@ -732,6 +720,118 @@ impl TransformsForChildFrame { } } +/// All instance poses for a given entity over time. +/// +/// Similar to [`TreeTransformsForChildFrame`], but for poses associated with an entity path. +#[derive(Debug)] +pub struct PoseTransformForEntity { + entity_path: EntityPath, + poses_per_time: Mutex>>>, +} + +impl Clone for PoseTransformForEntity { + fn clone(&self) -> Self { + Self { + entity_path: self.entity_path.clone(), + poses_per_time: Mutex::new(self.poses_per_time.lock().clone()), + } + } +} + +impl SizeBytes for PoseTransformForEntity { + fn heap_size_bytes(&self) -> u64 { + let Self { + entity_path, + poses_per_time, + } = self; + + entity_path.heap_size_bytes() + poses_per_time.lock().heap_size_bytes() + } +} + +impl PoseTransformForEntity { + fn new( + entity_path: EntityPath, + static_timeline: &mut CachedTransformsForTimeline, + non_recursive_clears: &IntMap>, + recursive_clears: &IntMap>, + ) -> Self { + let mut poses = Self::new_empty(entity_path); + + // Take over static events. + if let Some(static_transforms) = static_timeline + .per_entity_poses + .get_mut(&poses.entity_path.hash()) + { + *poses.poses_per_time.get_mut() = static_transforms.poses_per_time.get_mut().clone(); + } + + // Take over clear events. + if let Some(cleared_times) = non_recursive_clears.get(&poses.entity_path) { + poses.insert_clears(cleared_times); + } + for (recursively_cleared_path, times) in recursive_clears { + if poses.entity_path.starts_with(recursively_cleared_path) { + poses.insert_clears(times); + } + } + + poses + } + + fn new_empty(entity_path: EntityPath) -> Self { + Self { + entity_path, + poses_per_time: Mutex::new(BTreeMap::new()), + } + } + + pub fn latest_at_instance_poses( + &self, + entity_db: &EntityDb, + query: &LatestAtQuery, + ) -> Vec { + let mut poses_per_time = self.poses_per_time.lock(); + + let Some((_t, pose_transform)) = poses_per_time.range_mut(..query.at().inc()).next_back() + else { + return Vec::new(); + }; + + // Separate check to work around borrow checker issues. + if pose_transform == &CachedTransformValue::Invalidated { + *pose_transform = CachedTransformValue::Resident( + query_and_resolve_instance_poses_at_entity(&self.entity_path, entity_db, query), + ); + } + + match pose_transform { + CachedTransformValue::Resident(transform) => transform.clone(), + CachedTransformValue::Cleared => Vec::new(), + CachedTransformValue::Invalidated => unreachable!("Just made transform cache-resident"), + } + } + + /// Inserts a cleared transform for the given times. + fn insert_clear(&mut self, time: TimeInt) { + self.poses_per_time + .get_mut() + .insert(time, CachedTransformValue::Cleared); + } + + /// Insert several cleared transforms for the given times. + fn insert_clears(&mut self, time: &BTreeSet) { + self.poses_per_time + .get_mut() + .extend(time.iter().map(|t| (*t, CachedTransformValue::Cleared))); + } + + /// Inserts an invalidation point for poses. + fn invalidate_at(&mut self, time: TimeInt) { + add_invalidated_entry_if_not_already_cleared(self.poses_per_time.get_mut(), time); + } +} + impl TransformResolutionCache { /// Returns the registry of all known frame ids. #[inline] @@ -845,7 +945,7 @@ impl TransformResolutionCache { iter_child_frames_in_chunk(chunk, *timeline, transform_child_frame_component) { per_timeline - .get_or_create_transforms_temporal( + .get_or_create_tree_transforms_temporal( entity_path, frame, *timeline, @@ -855,22 +955,10 @@ impl TransformResolutionCache { } } if aspects.contains(TransformAspect::Pose) { - for (time, frame) in - // TODO(RR-2627): piggy backs on transform3d right now, shouldn't do that. - iter_child_frames_in_chunk( - chunk, - *timeline, - transform_child_frame_component, - ) - { + for (time, _) in chunk.iter_indices(timeline) { per_timeline - .get_or_create_transforms_temporal( - entity_path, - frame, - *timeline, - static_timeline, - ) - .invalidate_pose_transforms_at(time); + .get_or_create_pose_transforms_temporal(entity_path, static_timeline) + .invalidate_at(time); } } if aspects.contains(TransformAspect::PinholeOrViewCoordinates) { @@ -878,7 +966,7 @@ impl TransformResolutionCache { iter_child_frames_in_chunk(chunk, *timeline, pinhole_child_frame_component) { per_timeline - .get_or_create_transforms_temporal( + .get_or_create_tree_transforms_temporal( entity_path, frame, *timeline, @@ -933,12 +1021,12 @@ impl TransformResolutionCache { debug_assert_eq!(time, TimeInt::STATIC); let frame_transforms = - static_timeline.get_or_create_transforms_static(entity_path, frame); + static_timeline.get_or_create_tree_transforms_static(entity_path, frame); frame_transforms.invalidate_transform_at(TimeInt::STATIC); for (timeline, per_timeline) in &mut self.per_timeline { per_timeline - .get_or_create_transforms_temporal( + .get_or_create_tree_transforms_temporal( entity_path, frame, *timeline, @@ -949,30 +1037,14 @@ impl TransformResolutionCache { } } if aspects.contains(TransformAspect::Pose) { - for (time, frame) in - // TODO(RR-2627): piggy backs on transform3d right now, shouldn't do that. - iter_child_frames_in_chunk( - chunk, - place_holder_timeline, - transform_child_frame_component, - ) - { - debug_assert_eq!(time, TimeInt::STATIC); - - let frame_transforms = - static_timeline.get_or_create_transforms_static(entity_path, frame); - frame_transforms.invalidate_pose_transforms_at(TimeInt::STATIC); - - for (timeline, per_timeline) in &mut self.per_timeline { - per_timeline - .get_or_create_transforms_temporal( - entity_path, - frame, - *timeline, - static_timeline, - ) - .invalidate_pose_transforms_at(TimeInt::STATIC); - } + let frame_transforms = + static_timeline.get_or_create_pose_transforms_static(entity_path); + frame_transforms.invalidate_at(TimeInt::STATIC); + + for per_timeline in self.per_timeline.values_mut() { + per_timeline + .get_or_create_pose_transforms_temporal(entity_path, static_timeline) + .invalidate_at(TimeInt::STATIC); } } if aspects.contains(TransformAspect::PinholeOrViewCoordinates) { @@ -984,12 +1056,12 @@ impl TransformResolutionCache { debug_assert_eq!(time, TimeInt::STATIC); let frame_transforms = - static_timeline.get_or_create_transforms_static(entity_path, frame); + static_timeline.get_or_create_tree_transforms_static(entity_path, frame); frame_transforms.invalidate_pinhole_projection_at(TimeInt::STATIC); for (timeline, per_timeline) in &mut self.per_timeline { per_timeline - .get_or_create_transforms_temporal( + .get_or_create_tree_transforms_temporal( entity_path, frame, *timeline, @@ -1052,21 +1124,10 @@ impl TransformResolutionCache { } } if aspects.contains(TransformAspect::Pose) { - for (time, frame) in - // TODO(RR-2627): piggy backs on transform3d right now, shouldn't do that. - iter_child_frames_in_chunk( - chunk, - *timeline, - transform_child_frame_component, - ) - { - if let Some(transforms) = - per_timeline.per_child_frame_transforms.get_mut(&frame) + for (time, _) in chunk.iter_indices(timeline) { + if let Some(poses) = per_timeline.per_entity_poses.get_mut(&entity_path.hash()) { - let events = transforms.events.get_mut(); - if let Some(pose_transforms) = events.pose_transforms.as_mut() { - pose_transforms.remove(&time); - } + poses.poses_per_time.get_mut().remove(&time); } } } @@ -1078,9 +1139,7 @@ impl TransformResolutionCache { per_timeline.per_child_frame_transforms.get_mut(&frame) { let events = transforms.events.get_mut(); - if let Some(pinhole_projections) = events.pinhole_projections.as_mut() { - pinhole_projections.remove(&time); - } + events.pinhole_projections.remove(&time); } } } @@ -1331,9 +1390,7 @@ mod tests { #[cfg(debug_assertions)] assert_eq!(transforms.timeline, Some(*timeline.name())); assert_eq!(transforms.events.lock().frame_transforms.len(), 1); - assert_eq!(transforms.events.lock().pose_transforms, None); - assert_eq!(transforms.events.lock().pinhole_projections, None); - + assert_eq!(transforms.events.lock().pinhole_projections.len(), 0); Ok(()) } @@ -1472,9 +1529,7 @@ mod tests { let transforms_per_timeline = cache.transforms_for_timeline(*timeline.name()); let transforms = transforms_per_timeline - .frame_transforms(TransformFrameIdHash::from_entity_path(&EntityPath::from( - "my_entity", - ))) + .pose_transforms(EntityPath::from("my_entity").hash()) .unwrap(); assert_eq!( @@ -1505,9 +1560,7 @@ mod tests { // Timelines that the cache has never seen should still have the static poses. let transforms_per_timeline = cache.transforms_for_timeline(TimelineName::new("other")); let transforms = transforms_per_timeline - .frame_transforms(TransformFrameIdHash::from_entity_path(&EntityPath::from( - "my_entity", - ))) + .pose_transforms(EntityPath::from("my_entity").hash()) .unwrap(); assert_eq!( transforms.latest_at_instance_poses( @@ -1828,9 +1881,7 @@ mod tests { let timeline = *timeline.name(); let transforms_per_timeline = cache.transforms_for_timeline(timeline); let transforms = transforms_per_timeline - .frame_transforms(TransformFrameIdHash::from_entity_path(&EntityPath::from( - "my_entity", - ))) + .pose_transforms(EntityPath::from("my_entity").hash()) .unwrap(); assert_eq!( diff --git a/crates/store/re_types/definitions/rerun/archetypes/instance_poses3d.fbs b/crates/store/re_types/definitions/rerun/archetypes/instance_poses3d.fbs index b3ed5cf6ab16..a204f933598d 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/instance_poses3d.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/instance_poses3d.fbs @@ -1,10 +1,11 @@ namespace rerun.archetypes; -/// One or more transforms between the current entity and its parent. Unlike [archetypes.Transform3D], it is *not* propagated in the transform hierarchy. +/// One or more transforms applied on the current entity's transform frame. /// -/// If both [archetypes.InstancePoses3D] and [archetypes.Transform3D] are present, -/// first the tree propagating [archetypes.Transform3D] is applied, then [archetypes.InstancePoses3D]. +/// Unlike [archetypes.Transform3D], it is *not* propagated in the transform hierarchy. +/// If [archetypes.CoordinateFrame] is specified, it acts relative to that coordinate frame, +/// otherwise it is relative to the entity's implicit coordinate frame. /// /// Whenever you log this archetype, the state of the resulting overall pose is fully reset to the new archetype. /// This means that if you first log a pose with only a translation, and then log one with only a rotation, @@ -53,6 +54,4 @@ table InstancePoses3D ( /// /// Any update to this field will reset all other transform properties that aren't changed in the same log call or `send_columns` row. mat3x3: [rerun.components.PoseTransformMat3x3] ("attr.rerun.component_optional", nullable, order: 1500); - - // TODO(RR-2627): Make frame id configurable. } diff --git a/crates/store/re_types/src/archetypes/instance_poses3d.rs b/crates/store/re_types/src/archetypes/instance_poses3d.rs index fa0113b25904..017da5b0fe81 100644 --- a/crates/store/re_types/src/archetypes/instance_poses3d.rs +++ b/crates/store/re_types/src/archetypes/instance_poses3d.rs @@ -21,10 +21,11 @@ use ::re_types_core::{ComponentBatch as _, SerializedComponentBatch}; use ::re_types_core::{ComponentDescriptor, ComponentType}; use ::re_types_core::{DeserializationError, DeserializationResult}; -/// **Archetype**: One or more transforms between the current entity and its parent. Unlike [`archetypes::Transform3D`][crate::archetypes::Transform3D], it is *not* propagated in the transform hierarchy. +/// **Archetype**: One or more transforms applied on the current entity's transform frame. /// -/// If both [`archetypes::InstancePoses3D`][crate::archetypes::InstancePoses3D] and [`archetypes::Transform3D`][crate::archetypes::Transform3D] are present, -/// first the tree propagating [`archetypes::Transform3D`][crate::archetypes::Transform3D] is applied, then [`archetypes::InstancePoses3D`][crate::archetypes::InstancePoses3D]. +/// Unlike [`archetypes::Transform3D`][crate::archetypes::Transform3D], it is *not* propagated in the transform hierarchy. +/// If [`archetypes::CoordinateFrame`][crate::archetypes::CoordinateFrame] is specified, it acts relative to that coordinate frame, +/// otherwise it is relative to the entity's implicit coordinate frame. /// /// Whenever you log this archetype, the state of the resulting overall pose is fully reset to the new archetype. /// This means that if you first log a pose with only a translation, and then log one with only a rotation, diff --git a/crates/viewer/re_data_ui/src/transform_frames.rs b/crates/viewer/re_data_ui/src/transform_frames.rs index 288e5acdbbb7..2c1bbcda1a39 100644 --- a/crates/viewer/re_data_ui/src/transform_frames.rs +++ b/crates/viewer/re_data_ui/src/transform_frames.rs @@ -74,8 +74,9 @@ impl TransformFramesUi { let mut frame_id_hash = TransformFrameIdHash::new(&frame_id); let caches = ctx.store_context.caches; - let transform_cache = caches - .entry(|c: &mut TransformDatabaseStoreCache| c.lock_transform_cache(ctx.recording())); + let transform_cache = caches.entry(|c: &mut TransformDatabaseStoreCache| { + c.read_lock_transform_cache(ctx.recording()) + }); let frame_ids = transform_cache.frame_id_registry(); let transforms = transform_cache.transforms_for_timeline(*ctx.time_ctrl.timeline().name()); diff --git a/crates/viewer/re_view_spatial/src/contexts/mod.rs b/crates/viewer/re_view_spatial/src/contexts/mod.rs index 2ffd3341da21..46c5e8ef9d94 100644 --- a/crates/viewer/re_view_spatial/src/contexts/mod.rs +++ b/crates/viewer/re_view_spatial/src/contexts/mod.rs @@ -2,15 +2,14 @@ mod depth_offsets; mod transform_tree_context; pub use depth_offsets::EntityDepthOffsets; -use re_tf::TransformInfo; -use re_types::ViewClassIdentifier; -use re_view::AnnotationSceneContext; -pub use transform_tree_context::TransformTreeContext; +pub use transform_tree_context::{TransformInfo, TransformTreeContext}; // ----------------------------------------------------------------------------- use re_renderer::DepthOffset; +use re_types::ViewClassIdentifier; +use re_view::AnnotationSceneContext; use re_viewer_context::{Annotations, ViewClassRegistryError}; /// Context objects for a single entity in a spatial scene. diff --git a/crates/viewer/re_view_spatial/src/contexts/transform_tree_context.rs b/crates/viewer/re_view_spatial/src/contexts/transform_tree_context.rs index cef0a6ae6417..08f015418cd9 100644 --- a/crates/viewer/re_view_spatial/src/contexts/transform_tree_context.rs +++ b/crates/viewer/re_view_spatial/src/contexts/transform_tree_context.rs @@ -2,9 +2,9 @@ use std::sync::Arc; use nohash_hasher::IntMap; use re_chunk_store::LatestAtQuery; -use re_log_types::EntityPathHash; -use re_tf::{TransformFrameId, TransformFrameIdHash}; -use re_types::{archetypes, components::ImagePlaneDistance}; +use re_log_types::{EntityPath, EntityPathHash}; +use re_tf::{TransformFrameId, TransformFrameIdHash, TreeTransform}; +use re_types::{ArchetypeName, archetypes, components::ImagePlaneDistance}; use re_view::{DataResultQuery as _, latest_at_with_blueprint_resolved_data}; use re_viewer_context::{ DataResult, IdentifiedViewSystem, TransformDatabaseStoreCache, ViewContext, ViewContextSystem, @@ -14,6 +14,85 @@ use vec1::smallvec_v1::SmallVec1; type FrameIdMapping = IntMap; +/// Details on how to transform an entity (!) to the origin of the view's space. +#[derive(Clone, Debug, PartialEq)] +pub struct TransformInfo { + /// Root frame this transform belongs to. + /// + /// ⚠️ This is the root of the tree this transform belongs to, + /// not necessarily what the transform transforms into. + /// + /// Implementation note: + /// We could add target and maybe even source to this, but we want to keep this struct small'ish. + /// On that note, it may be good to split this in the future, as most of the time we're only interested in the + /// source->target affine transform. + root: TransformFrameIdHash, + + /// The transform from this frame to the target's space. + /// + /// ⚠️ Does not include per instance poses! ⚠️ + /// Include 3D-from-2D / 2D-from-3D pinhole transform if present. + target_from_source: glam::DAffine3, + + /// List of transforms per instance including poses. + /// + /// If no poses are present, this is always the same as [`Self::target_from_source`]. + /// (also implying that in this case there is only a single element). + /// If there are poses, there may be more than one element. + target_from_instances: SmallVec1<[glam::DAffine3; 1]>, +} + +impl TransformInfo { + fn new(tree_transform: &TreeTransform, mut pose_transforms: Vec) -> Self { + for pose_transforms in &mut pose_transforms { + *pose_transforms = tree_transform.target_from_source * *pose_transforms; + } + let target_from_instances = SmallVec1::try_from_vec(pose_transforms) + .unwrap_or_else(|_| SmallVec1::new(tree_transform.target_from_source)); + + Self { + root: tree_transform.root, + target_from_source: tree_transform.target_from_source, + target_from_instances, + } + } + + /// Returns the root frame of the tree this transform belongs to. + /// + /// This is **not** necessarily the transform's target frame. + #[inline] + pub fn tree_root(&self) -> TransformFrameIdHash { + self.root + } + + /// Warns that multiple transforms on an entity are not supported. + #[inline] + fn warn_on_per_instance_transform(&self, entity_name: &EntityPath, archetype: ArchetypeName) { + if self.target_from_instances.len() > 1 { + re_log::warn_once!( + "There are multiple poses for entity {entity_name:?}'s transform frame. {archetype:?} supports only one transform per entity. Using the first one." + ); + } + } + + /// Returns the first instance transform and warns if there are multiple. + #[inline] + pub fn single_transform_required_for_entity( + &self, + entity_name: &EntityPath, + archetype: ArchetypeName, + ) -> glam::DAffine3 { + self.warn_on_per_instance_transform(entity_name, archetype); + *self.target_from_instances.first() + } + + /// Returns the target from instance transforms. + #[inline] + pub fn target_from_instances(&self) -> &SmallVec1<[glam::DAffine3; 1]> { + &self.target_from_instances + } +} + /// Provides a transform tree for the view & time it operates on. /// /// Will do the necessary bulk processing of transform information and make it available @@ -21,8 +100,7 @@ type FrameIdMapping = IntMap; #[derive(Clone)] pub struct TransformTreeContext { transform_forest: Arc, - transform_infos: - IntMap>, + transform_infos: IntMap>, target_frame: TransformFrameIdHash, target_frame_pinhole_root: Option, entity_transform_id_mapping: EntityTransformIdMapping, @@ -72,8 +150,9 @@ impl ViewContextSystem for TransformTreeContext { ctx: &re_viewer_context::ViewerContext<'_>, ) -> ViewContextSystemOncePerFrameResult { let caches = ctx.store_context.caches; - let transform_cache = caches - .entry(|c: &mut TransformDatabaseStoreCache| c.lock_transform_cache(ctx.recording())); + let transform_cache = caches.entry(|c: &mut TransformDatabaseStoreCache| { + c.read_lock_transform_cache(ctx.recording()) + }); let transform_forest = re_tf::TransformForest::new(ctx.recording(), &transform_cache, &ctx.current_query()); @@ -111,7 +190,7 @@ impl ViewContextSystem for TransformTreeContext { let latest_at_query = query.latest_at_query(); - let transform_infos_per_frame = self + let tree_transforms_per_frame = self .transform_forest .transform_from_to( self.target_frame, @@ -138,17 +217,34 @@ impl ViewContextSystem for TransformTreeContext { self.transform_infos = { re_tracing::profile_scope!("transform info lookup"); - transform_infos_per_frame + let caches = ctx.viewer_ctx.store_context.caches; + let transform_cache = caches.entry(|c: &mut TransformDatabaseStoreCache| { + c.read_lock_transform_cache(ctx.recording()) + }); + let transforms = transform_cache.transforms_for_timeline(query.timeline); + + let latest_at_query = &query.latest_at_query(); + + tree_transforms_per_frame .into_iter() - .filter_map(|(transform_frame_id_hash, transform_info)| { - self.entity_transform_id_mapping + .filter_map(|(transform_frame_id_hash, tree_transform)| { + let entity_paths_for_frame = self + .entity_transform_id_mapping .transform_frame_id_to_entity_path - .get(&transform_frame_id_hash) - .map(|entity_path_hashes| { - entity_path_hashes.iter().map(move |entity_path_hash| { - (*entity_path_hash, transform_info.clone()) - }) - }) + .get(&transform_frame_id_hash)?; + let transform_infos = + entity_paths_for_frame.iter().map(move |entity_path_hash| { + let transform_info = map_tree_transform_to_transform_info( + ctx, + &tree_transform, + transforms, + latest_at_query, + entity_path_hash, + ); + (*entity_path_hash, transform_info) + }); + + Some(transform_infos) }) .flatten() .collect() @@ -159,8 +255,8 @@ impl ViewContextSystem for TransformTreeContext { .root_from_frame(self.target_frame) .and_then(|info| { self.transform_forest - .pinhole_tree_root_info(info.tree_root()) - .map(|_pinhole_info| info.tree_root()) + .pinhole_tree_root_info(info.root) + .map(|_pinhole_info| info.root) }); } @@ -169,13 +265,31 @@ impl ViewContextSystem for TransformTreeContext { } } +fn map_tree_transform_to_transform_info( + ctx: &ViewContext<'_>, + tree_transform: &Result, + transforms: &re_tf::CachedTransformsForTimeline, + latest_at_query: &LatestAtQuery, + entity_path_hash: &EntityPathHash, +) -> Result { + let tree_transform = tree_transform.as_ref().map_err(|err| err.clone())?; + let poses = transforms + .pose_transforms(*entity_path_hash) + .map(|pose_transforms| { + pose_transforms.latest_at_instance_poses(ctx.recording(), latest_at_query) + }) + .unwrap_or_default(); + + Ok(TransformInfo::new(tree_transform, poses)) +} + impl TransformTreeContext { /// Returns a transform info describing how to get to the view's target frame for the given entity. #[inline] pub fn target_from_entity_path( &self, entity_path_hash: EntityPathHash, - ) -> Option<&Result> { + ) -> Option<&Result> { self.transform_infos.get(&entity_path_hash) } @@ -236,7 +350,7 @@ impl TransformTreeContext { fn lookup_image_plane_distance( ctx: &ViewContext<'_>, - entity_path_hashes: &SmallVec1<[EntityPathHash; 1]>, + entity_path: &SmallVec1<[EntityPathHash; 1]>, latest_at_query: &LatestAtQuery, ) -> f32 { // If there's several entity paths (with pinhole cameras) for the same transform id, @@ -247,7 +361,7 @@ fn lookup_image_plane_distance( // we don't know the full entity path names. // // We're letting it slide for now since it's kinda hard to get into that situation. - let entity_path_hash = *entity_path_hashes.first(); + let entity_path_hash = *entity_path.first(); ctx.query_result .tree diff --git a/crates/viewer/re_view_spatial/src/visualizers/utilities/transform_retrieval.rs b/crates/viewer/re_view_spatial/src/visualizers/utilities/transform_retrieval.rs index e9d94ada2955..500c0e4cbc20 100644 --- a/crates/viewer/re_view_spatial/src/visualizers/utilities/transform_retrieval.rs +++ b/crates/viewer/re_view_spatial/src/visualizers/utilities/transform_retrieval.rs @@ -1,9 +1,11 @@ use re_log_types::EntityPath; -use re_tf::TransformInfo; use re_types::ViewClassIdentifier; use re_viewer_context::{ViewClass as _, VisualizerExecutionOutput}; -use crate::{contexts::TransformTreeContext, view_kind::SpatialViewKind}; +use crate::{ + contexts::{TransformInfo, TransformTreeContext}, + view_kind::SpatialViewKind, +}; /// Derive the spatial view kind from the view class identifier. pub fn spatial_view_kind_from_view_class(class: ViewClassIdentifier) -> SpatialViewKind { @@ -93,7 +95,7 @@ pub fn transform_info_for_entity_or_report_error<'a>( fn is_valid_space_for_content( entity_path: &EntityPath, transform_context: &TransformTreeContext, - transform: &re_tf::TransformInfo, + transform: &TransformInfo, content_kind: Option, view_kind: SpatialViewKind, output: &mut VisualizerExecutionOutput, diff --git a/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_boxes.png b/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_boxes.png index c49701dbf78c..33154c4e30c1 100644 --- a/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_boxes.png +++ b/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_boxes.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9c66147ed59ed9f4dd828034ecd27aba53bfecd710358516fc80cc90006afa1 -size 52246 +oid sha256:c8e96a40bfd70b4951c59b38d44b112e3aeaee754261384f7d79a1c361d1d85d +size 53419 diff --git a/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_points.png b/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_points.png index 6d346559b084..f8f6a67b62c3 100644 --- a/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_points.png +++ b/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_points.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23b0cd5d6215f4a34e84ceb70730d2acd3d76c138df32777cde87daa88ce6381 -size 14826 +oid sha256:17a9547d4df67cb96931cac92be257be041e8d8709a19b8bfe1472fe32090101 +size 20246 diff --git a/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_spheres.png b/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_spheres.png index f9466cecf73a..0ad50022dcbd 100644 --- a/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_spheres.png +++ b/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_spheres.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:446a5c6c9c1bea2748de2e4ee5ee3612554770c49bb932895fcff33be04307f1 -size 52714 +oid sha256:9afaaf4070a990e9c0f31a8e0f2d21869a74be29c55df084e96c6c71ad5db2a0 +size 54240 diff --git a/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_transform_axes.png b/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_transform_axes.png index 8ec7db4b5cd1..1844e454d2d2 100644 --- a/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_transform_axes.png +++ b/crates/viewer/re_view_spatial/tests/snapshots/transform_clamping_with_base_transform_FrameHierarchy_transform_axes.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:801313ef594808139f7ccf3e47eeb25b17a8e501eb3a4ee793275a394ba36f72 -size 77442 +oid sha256:ca349243bda5a26bdb83e3400e29f0cf4fd0d4b55934097dcd504dc7b4ce381a +size 78293 diff --git a/crates/viewer/re_view_spatial/tests/transform_clamping.rs b/crates/viewer/re_view_spatial/tests/transform_clamping.rs index ab5d744b5a80..400de8f7158b 100644 --- a/crates/viewer/re_view_spatial/tests/transform_clamping.rs +++ b/crates/viewer/re_view_spatial/tests/transform_clamping.rs @@ -207,6 +207,8 @@ fn test_transform_clamping(base_transform: BaseTransform) { BaseTransform::FrameHierarchy => { // Put everything under a frame that has an identity relationship with a frame called "base". + // Note that this means we end up with several `InstancePoses3D` which are all relative to "base". + // Since poses are always relative to their entity's frame, this should work out fine and give us the same result as `BaseTransform::EntityHierarchy`! let entity_paths = test_context .store_hub .lock() @@ -217,20 +219,11 @@ fn test_transform_clamping(base_transform: BaseTransform) { .cloned() .collect::>(); for path in entity_paths { - let frame = format!("frame_{path}"); test_context.log_entity(path, |builder| { - builder - .with_archetype_auto_row( - TimePoint::STATIC, - &re_types::archetypes::CoordinateFrame::new(frame.clone()), - ) - .with_archetype_auto_row( - TimePoint::STATIC, - &re_types::archetypes::Transform3D::default() - .with_child_frame(frame) - .with_parent_frame("base"), - ) - // TODO(RR-2627): Where there's an instance transform we also have to change its frame! + builder.with_archetype_auto_row( + TimePoint::STATIC, + &re_types::archetypes::CoordinateFrame::new("base"), + ) }); } diff --git a/crates/viewer/re_viewer_context/src/cache/transform_database_store.rs b/crates/viewer/re_viewer_context/src/cache/transform_database_store.rs index ba0ff6f1a360..053362513a03 100644 --- a/crates/viewer/re_viewer_context/src/cache/transform_database_store.rs +++ b/crates/viewer/re_viewer_context/src/cache/transform_database_store.rs @@ -1,37 +1,39 @@ -use parking_lot::{ArcMutexGuard, Mutex, RawMutex}; -use re_byte_size::SizeBytes; use std::sync::Arc; -use super::{Cache, CacheMemoryReport}; +use parking_lot::{ArcRwLockReadGuard, RawRwLock, RwLock}; + +use re_byte_size::SizeBytes; use re_chunk_store::ChunkStoreEvent; use re_entity_db::EntityDb; use re_tf::TransformResolutionCache; +use super::{Cache, CacheMemoryReport}; + /// Stores a [`TransformResolutionCache`] for each recording. /// /// Ensures that the cache stays up to date. #[derive(Default)] pub struct TransformDatabaseStoreCache { initialized: bool, - transform_cache: Arc>, + transform_cache: Arc>, } impl TransformDatabaseStoreCache { /// Gets access to the transform cache. /// /// If the cache was newly added, will make sure that all existing chunks in the entity db are processed. - pub fn lock_transform_cache( + pub fn read_lock_transform_cache( &mut self, entity_db: &EntityDb, - ) -> ArcMutexGuard { + ) -> ArcRwLockReadGuard { if !self.initialized { - self.initialized = true; + self.initialized = true; // There can't be a race here since we have `&mut self``. self.transform_cache - .lock() + .write() .add_chunks(entity_db.storage_engine().store().iter_chunks()); } - self.transform_cache.lock_arc() + self.transform_cache.read_arc() } } @@ -41,9 +43,8 @@ impl SizeBytes for TransformDatabaseStoreCache { initialized, transform_cache, } = self; - let cache = transform_cache.lock(); - initialized.heap_size_bytes() + cache.total_size_bytes() + initialized.heap_size_bytes() + transform_cache.read().heap_size_bytes() } } @@ -69,12 +70,12 @@ impl Cache for TransformDatabaseStoreCache { re_tracing::profile_function!(); debug_assert!( - self.transform_cache.try_lock().is_some(), + self.transform_cache.try_write().is_some(), "Transform cache is still locked on processing store events. This should never happen." ); self.transform_cache - .lock() + .write() .process_store_events(events.iter().copied()); } diff --git a/docs/content/reference/types/archetypes.md b/docs/content/reference/types/archetypes.md index 78b7a309b8cd..30b56e2e9f35 100644 --- a/docs/content/reference/types/archetypes.md +++ b/docs/content/reference/types/archetypes.md @@ -57,7 +57,7 @@ This page lists all built-in archetypes. * [`Capsules3D`](archetypes/capsules3d.md): 3D capsules; cylinders with hemispherical caps. * [`Cylinders3D`](archetypes/cylinders3d.md): 3D cylinders with flat caps. * [`Ellipsoids3D`](archetypes/ellipsoids3d.md): 3D ellipsoids or spheres. -* [`InstancePoses3D`](archetypes/instance_poses3d.md): One or more transforms between the current entity and its parent. Unlike [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d), it is *not* propagated in the transform hierarchy. +* [`InstancePoses3D`](archetypes/instance_poses3d.md): One or more transforms applied on the current entity's transform frame. * [`LineStrips3D`](archetypes/line_strips3d.md): 3D line strips with positions and optional colors, radii, labels, etc. * [`Mesh3D`](archetypes/mesh3d.md): A 3D triangle mesh as specified by its per-mesh and per-vertex properties. * [`Points3D`](archetypes/points3d.md): A 3D point cloud with positions and optional colors, radii, labels, etc. diff --git a/docs/content/reference/types/archetypes/instance_poses3d.md b/docs/content/reference/types/archetypes/instance_poses3d.md index 4a4e4b7458c9..5439b3b270ce 100644 --- a/docs/content/reference/types/archetypes/instance_poses3d.md +++ b/docs/content/reference/types/archetypes/instance_poses3d.md @@ -3,10 +3,11 @@ title: "InstancePoses3D" --- -One or more transforms between the current entity and its parent. Unlike [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d), it is *not* propagated in the transform hierarchy. +One or more transforms applied on the current entity's transform frame. -If both [`archetypes.InstancePoses3D`](https://rerun.io/docs/reference/types/archetypes/instance_poses3d) and [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) are present, -first the tree propagating [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d) is applied, then [`archetypes.InstancePoses3D`](https://rerun.io/docs/reference/types/archetypes/instance_poses3d). +Unlike [`archetypes.Transform3D`](https://rerun.io/docs/reference/types/archetypes/transform3d), it is *not* propagated in the transform hierarchy. +If [`archetypes.CoordinateFrame`](https://rerun.io/docs/reference/types/archetypes/coordinate_frame) is specified, it acts relative to that coordinate frame, +otherwise it is relative to the entity's implicit coordinate frame. Whenever you log this archetype, the state of the resulting overall pose is fully reset to the new archetype. This means that if you first log a pose with only a translation, and then log one with only a rotation, diff --git a/rerun_cpp/src/rerun/archetypes/instance_poses3d.hpp b/rerun_cpp/src/rerun/archetypes/instance_poses3d.hpp index fe9910b90d1d..ed5cf382f0a7 100644 --- a/rerun_cpp/src/rerun/archetypes/instance_poses3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/instance_poses3d.hpp @@ -19,10 +19,11 @@ #include namespace rerun::archetypes { - /// **Archetype**: One or more transforms between the current entity and its parent. Unlike `archetypes::Transform3D`, it is *not* propagated in the transform hierarchy. + /// **Archetype**: One or more transforms applied on the current entity's transform frame. /// - /// If both `archetypes::InstancePoses3D` and `archetypes::Transform3D` are present, - /// first the tree propagating `archetypes::Transform3D` is applied, then `archetypes::InstancePoses3D`. + /// Unlike `archetypes::Transform3D`, it is *not* propagated in the transform hierarchy. + /// If `archetypes::CoordinateFrame` is specified, it acts relative to that coordinate frame, + /// otherwise it is relative to the entity's implicit coordinate frame. /// /// Whenever you log this archetype, the state of the resulting overall pose is fully reset to the new archetype. /// This means that if you first log a pose with only a translation, and then log one with only a rotation, diff --git a/rerun_py/rerun_sdk/rerun/archetypes/instance_poses3d.py b/rerun_py/rerun_sdk/rerun/archetypes/instance_poses3d.py index 6add31a04b7b..ec4e4b14e3f4 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/instance_poses3d.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/instance_poses3d.py @@ -24,10 +24,11 @@ @define(str=False, repr=False, init=False) class InstancePoses3D(Archetype): """ - **Archetype**: One or more transforms between the current entity and its parent. Unlike [`archetypes.Transform3D`][rerun.archetypes.Transform3D], it is *not* propagated in the transform hierarchy. + **Archetype**: One or more transforms applied on the current entity's transform frame. - If both [`archetypes.InstancePoses3D`][rerun.archetypes.InstancePoses3D] and [`archetypes.Transform3D`][rerun.archetypes.Transform3D] are present, - first the tree propagating [`archetypes.Transform3D`][rerun.archetypes.Transform3D] is applied, then [`archetypes.InstancePoses3D`][rerun.archetypes.InstancePoses3D]. + Unlike [`archetypes.Transform3D`][rerun.archetypes.Transform3D], it is *not* propagated in the transform hierarchy. + If [`archetypes.CoordinateFrame`][rerun.archetypes.CoordinateFrame] is specified, it acts relative to that coordinate frame, + otherwise it is relative to the entity's implicit coordinate frame. Whenever you log this archetype, the state of the resulting overall pose is fully reset to the new archetype. This means that if you first log a pose with only a translation, and then log one with only a rotation,