diff --git a/crates/build/re_types_builder/src/codegen/python/mod.rs b/crates/build/re_types_builder/src/codegen/python/mod.rs
index e85f2d38efab..62ddf04f2553 100644
--- a/crates/build/re_types_builder/src/codegen/python/mod.rs
+++ b/crates/build/re_types_builder/src/codegen/python/mod.rs
@@ -2649,8 +2649,8 @@ fn quote_columnar_methods(reporter: &Reporter, obj: &Object, objects: &Objects)
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/docs/content/howto/logging/send-columns.md b/docs/content/howto/logging/send-columns.md
index 335a45a83a91..d28c40b1caba 100644
--- a/docs/content/howto/logging/send-columns.md
+++ b/docs/content/howto/logging/send-columns.md
@@ -63,9 +63,31 @@ snippet: archetypes/points3d_column_updates
+
Each row in the component column can be a batch of data, e.g. a batch of positions.
This lets you log the evolution of a point cloud over time efficiently.
+### Updating a fixed number of arrows over time, in a single operation
+
+Consider this snippet, using the row-oriented `log` API:
+
+snippet: archetypes/arrows3d_row_updates
+
+which can be translated to the column-oriented `send_columns` API as such:
+
+snippet: archetypes/arrows3d_column_updates
+
+
+
+
+
+
+
+
+
+Each row in the component column can be a batch of data, e.g. a batch of positions.
+This lets you log the evolution of a set of arrows over time efficiently.
+
### Updating a transform over time, in a single operation
diff --git a/docs/snippets/INDEX.md b/docs/snippets/INDEX.md
index b2ca2997c4bf..93fbf36ded31 100644
--- a/docs/snippets/INDEX.md
+++ b/docs/snippets/INDEX.md
@@ -20,6 +20,7 @@ Use it to quickly find copy-pastable snippets of code for any Rerun feature you'
| Feature | Example | Description | Python | Rust | C++ |
| ------- | ------- | ----------- | :----: | :--: | :-------: |
+| **Update rows** | `arrows3d_row_updates` | Update a set of vectors over time | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_row_updates.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_row_updates.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_row_updates.cpp) |
| **Update rows** | `scalar_row_updates` | Update a scalar over time | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/scalar_row_updates.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/scalar_row_updates.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/scalar_row_updates.cpp) |
| **Update rows** | `points3d_row_updates` | Update a point cloud over time | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_row_updates.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_row_updates.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_row_updates.cpp) |
| **Update rows** | `transform3d_row_updates` | Update a transform over time | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/transform3d_row_updates.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/transform3d_row_updates.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/transform3d_row_updates.cpp) |
@@ -55,6 +56,8 @@ _All snippets, organized by the [`Archetype`](https://rerun.io/docs/reference/ty
| **[`AnnotationContext`](https://rerun.io/docs/reference/types/archetypes/annotation_context)** | `archetypes/segmentation_image_simple` | Create and log a segmentation image | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/segmentation_image_simple.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/segmentation_image_simple.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/segmentation_image_simple.cpp) |
| **[`Arrows2D`](https://rerun.io/docs/reference/types/archetypes/arrows2d)** | `archetypes/arrows2d_simple` | Log a batch of 2D arrows | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows2d_simple.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows2d_simple.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows2d_simple.cpp) |
| **[`Arrows3D`](https://rerun.io/docs/reference/types/archetypes/arrows3d)** | `archetypes/arrows3d_simple` | Log a batch of 3D arrows | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_simple.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_simple.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_simple.cpp) |
+| **[`Arrows3D`](https://rerun.io/docs/reference/types/archetypes/arrows3d)** | `archetypes/arrows3d_row_updates` | Update a set of vectors over time | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_row_updates.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_row_updates.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_row_updates.cpp) |
+| **[`Arrows3D`](https://rerun.io/docs/reference/types/archetypes/arrows3d)** | `archetypes/arrows3d_column_updates` | Update a set of vectors over time, in a single operation | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_column_updates.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_column_updates.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_column_updates.cpp) |
| **[`Arrows3D`](https://rerun.io/docs/reference/types/archetypes/arrows3d)** | `archetypes/clear_recursive` | Log and then clear data recursively | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/clear_recursive.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/clear_recursive.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/clear_recursive.cpp) |
| **[`Arrows3D`](https://rerun.io/docs/reference/types/archetypes/arrows3d)** | `archetypes/clear_simple` | Log and then clear data | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/clear_simple.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/clear_simple.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/clear_simple.cpp) |
| **[`Arrows3D`](https://rerun.io/docs/reference/types/archetypes/arrows3d)** | `archetypes/transform3d_simple` | Log different transforms between three arrows | [🐍](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/transform3d_simple.py) | [🦀](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/transform3d_simple.rs) | [🌊](https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/transform3d_simple.cpp) |
diff --git a/docs/snippets/all/archetypes/arrows3d_column_updates.cpp b/docs/snippets/all/archetypes/arrows3d_column_updates.cpp
new file mode 100644
index 000000000000..d2c78ad32a83
--- /dev/null
+++ b/docs/snippets/all/archetypes/arrows3d_column_updates.cpp
@@ -0,0 +1,52 @@
+// Update a set of vectors over time, in a single operation.
+//
+// This is semantically equivalent to the `arrows3d_row_updates` example, albeit much faster.
+
+#include
+#include
+
+#include
+#include
+
+using namespace std::chrono_literals;
+
+int main() {
+ const auto rec = rerun::RecordingStream("rerun_example_arrows3d_column_updates");
+ rec.spawn().exit_on_failure();
+
+ // Prepare a fixed sequence of arrows over 5 timesteps.
+ // Origins stay constant, vectors change magnitude and direction, and each timestep has a unique color.
+ std::vector> origins;
+ std::vector> vectors;
+
+ for (size_t i = 0; i < 5; i++) {
+ float fi = static_cast(i);
+
+ auto xs = rerun::demo::linspace(-1.f, 1.f, 5);
+ auto zs = rerun::demo::linspace(fi / 10.f, fi, 5);
+ for (size_t j = 0; j < 5; j++) {
+ std::array origin = {xs[j], xs[j], 0.0};
+ std::array vector = {xs[j], xs[j], zs[j]};
+
+ origins.emplace_back(origin);
+ vectors.emplace_back(vector);
+ }
+ }
+
+ // At each timestep, all arrows share the same but changing color.
+ std::vector colors = {0xFF0000FF, 0x00FF00FF, 0x0000FFFF, 0xFFFF00FF, 0x00FFFFFF};
+
+ // Log at seconds 10-14
+ auto times = rerun::Collection{10s, 11s, 12s, 13s, 14s};
+ auto time_column = rerun::TimeColumn::from_durations("time", std::move(times));
+
+ auto arrows =
+ rerun::Arrows3D().with_origins(origins).with_vectors(vectors).columns({5, 5, 5, 5, 5});
+
+ rec.send_columns(
+ "arrows",
+ time_column,
+ arrows,
+ rerun::Arrows3D::update_fields().with_colors(colors).columns()
+ );
+}
diff --git a/docs/snippets/all/archetypes/arrows3d_column_updates.py b/docs/snippets/all/archetypes/arrows3d_column_updates.py
new file mode 100644
index 000000000000..0441e5be08ed
--- /dev/null
+++ b/docs/snippets/all/archetypes/arrows3d_column_updates.py
@@ -0,0 +1,28 @@
+"""
+Update a set of vectors over time, in a single operation.
+
+This is semantically equivalent to the `arrows3d_row_updates` example, albeit much faster.
+"""
+
+import numpy as np
+import rerun as rr
+
+rr.init("rerun_example_arrows3d_column_updates", spawn=True)
+
+# Prepare a fixed sequence of arrows over 5 timesteps.
+# Origins stay constant, vectors change magnitude and direction, and each timestep has a unique color.
+times = np.arange(10, 15, 1.0)
+
+# At each time step, all arrows maintain their origin.
+origins = [np.linspace((-1, -1, 0), (1, 1, 0), 5)] * 5
+vectors = [np.linspace((1, 1, i / 10), (1, 1, i), 5) for i in range(5)]
+
+
+# At each timestep, all arrows share the same but changing color.
+colors = [0xFF0000FF, 0x00FF00FF, 0x0000FFFF, 0xFFFF00FF, 0x00FFFFFF]
+
+rr.send_columns(
+ "arrows",
+ indexes=[rr.IndexColumn("time", timedelta=times)],
+ columns=[*rr.Arrows3D.columns(origins=origins, vectors=vectors, colors=colors)],
+)
diff --git a/docs/snippets/all/archetypes/arrows3d_column_updates.rs b/docs/snippets/all/archetypes/arrows3d_column_updates.rs
new file mode 100644
index 000000000000..f43e2f35227b
--- /dev/null
+++ b/docs/snippets/all/archetypes/arrows3d_column_updates.rs
@@ -0,0 +1,40 @@
+//! Update a set of vectors over time, in a single operation.
+//!
+//! This is semantically equivalent to the `arrows3d_row_updates` example, albeit much faster.
+
+use rerun::demo_util::linspace;
+
+fn main() -> Result<(), Box> {
+ let rec =
+ rerun::RecordingStreamBuilder::new("rerun_example_arrows3d_column_updates").spawn()?;
+ let times = rerun::TimeColumn::new_duration_seconds("time", 10..15);
+
+ // Prepare a fixed sequence of arrows over 5 timesteps.
+ // Origins stay constant, vectors change magnitude and direction, and each timestep has a unique color.
+ let (origins, vectors): (Vec<_>, Vec<_>) = (0..5)
+ .map(|i| {
+ let i = i as f32;
+ (
+ linspace(-1., 1., 5).map(move |x| (x, x, 0.)),
+ linspace(-1., 1., 5)
+ .zip(linspace(i / 10., i, 5))
+ .map(|(x, z)| (x, x, z)),
+ )
+ })
+ .collect();
+
+ // At each timestep, all arrows share the same but changing color.
+ let colors = [0xFF0000FF, 0x00FF00FF, 0x0000FFFF, 0xFFFF00FF, 0x00FFFFFF];
+
+ let arrows = rerun::Arrows3D::update_fields()
+ .with_origins(origins.into_iter().flatten())
+ .with_vectors(vectors.into_iter().flatten())
+ .columns([5, 5, 5, 5, 5])?;
+ let color = rerun::Arrows3D::update_fields()
+ .with_colors(colors)
+ .columns_of_unit_batches()?;
+
+ rec.send_columns("arrows", [times], arrows.chain(color))?;
+
+ Ok(())
+}
diff --git a/docs/snippets/all/archetypes/arrows3d_row_updates.cpp b/docs/snippets/all/archetypes/arrows3d_row_updates.cpp
new file mode 100644
index 000000000000..ffc6466eacf5
--- /dev/null
+++ b/docs/snippets/all/archetypes/arrows3d_row_updates.cpp
@@ -0,0 +1,49 @@
+// Update a set of vectors over time.
+//
+// See also the `arrows3d_column_updates` example, which achieves the same thing in a single operation.
+
+#include
+#include
+
+#include
+#include
+
+int main() {
+ const auto rec = rerun::RecordingStream("rerun_example_arrows3d_row_updates");
+ rec.spawn().exit_on_failure();
+
+ // Prepare a fixed sequence of arrows over 5 timesteps.
+ // Origins stay constant, vectors change magnitude and direction, and each timestep has a unique color.
+ std::vector, 5>> origins;
+ std::vector, 5>> vectors;
+
+ for (size_t i = 0; i < 5; i++) {
+ float fi = static_cast(i);
+
+ auto xs = rerun::demo::linspace(-1.f, 1.f, 5);
+ auto zs = rerun::demo::linspace(fi / 10.f, fi, 5);
+
+ std::array, 5> origin;
+ std::array, 5> vector;
+ for (size_t j = 0; j < 5; j++) {
+ origin[j] = {xs[j], xs[j], 0.0};
+ vector[j] = {xs[j], xs[j], zs[j]};
+ }
+
+ origins.emplace_back(origin);
+ vectors.emplace_back(vector);
+ }
+
+ // At each timestep, all arrows share the same but changing color.
+ std::vector colors = {0xFF0000FF, 0x00FF00FF, 0x0000FFFF, 0xFFFF00FF, 0x00FFFFFF};
+
+ for (size_t i = 0; i < 5; i++) {
+ rec.set_index_duration_secs("time", 10.0 + static_cast(i));
+ rec.log(
+ "arrows",
+ rerun::Arrows3D::from_vectors(vectors[i])
+ .with_origins(origins[i])
+ .with_colors(colors[i])
+ );
+ }
+}
diff --git a/docs/snippets/all/archetypes/arrows3d_row_updates.py b/docs/snippets/all/archetypes/arrows3d_row_updates.py
new file mode 100644
index 000000000000..eb60b93076ad
--- /dev/null
+++ b/docs/snippets/all/archetypes/arrows3d_row_updates.py
@@ -0,0 +1,26 @@
+"""
+Update a set of vectors over time.
+
+See also the `arrows3d_column_updates` example, which achieves the same thing in a single operation.
+"""
+
+import numpy as np
+import rerun as rr
+
+rr.init("rerun_example_arrows3d_row_updates", spawn=True)
+
+# Prepare a fixed sequence of arrows over 5 timesteps.
+# Origins stay constant, vectors change magnitude and direction, and each timestep has a unique color.
+times = np.arange(10, 15, 1.0)
+
+# At each time step, all arrows maintain their origin.
+origins = np.linspace((-1, -1, 0), (1, 1, 0), 5)
+vectors = [np.linspace((1, 1, i / 10), (1, 1, i), 5) for i in range(5)]
+
+
+# At each timestep, all arrows share the same but changing color.
+colors = [0xFF0000FF, 0x00FF00FF, 0x0000FFFF, 0xFFFF00FF, 0x00FFFFFF]
+
+for i in range(5):
+ rr.set_index("time", timedelta=10 + i)
+ rr.log("arrows", rr.Arrows3D(vectors=vectors[i], origins=origins, colors=colors[i]))
diff --git a/docs/snippets/all/archetypes/arrows3d_row_updates.rs b/docs/snippets/all/archetypes/arrows3d_row_updates.rs
new file mode 100644
index 000000000000..d99e21d89733
--- /dev/null
+++ b/docs/snippets/all/archetypes/arrows3d_row_updates.rs
@@ -0,0 +1,38 @@
+//! Update a set of vectors over time.
+//!
+//! See also the `arrows3d_column_updates` example, which achieves the same thing in a single operation.
+
+use rerun::demo_util::linspace;
+
+fn main() -> Result<(), Box> {
+ let rec = rerun::RecordingStreamBuilder::new("rerun_example_arrows3d_row_updates").spawn()?;
+
+ // Prepare a fixed sequence of arrows over 5 timesteps.
+ // Origins stay constant, vectors change magnitude and direction, and each timestep has a unique color.
+ let (origins, vectors): (Vec<_>, Vec<_>) = (0..5)
+ .map(|i| {
+ let i = i as f32;
+ (
+ linspace(-1., 1., 5).map(move |x| (x, x, 0.)),
+ linspace(-1., 1., 5)
+ .zip(linspace(i / 10., i, 5))
+ .map(|(x, z)| (x, x, z)),
+ )
+ })
+ .collect();
+
+ // At each timestep, all arrows share the same but changing color.
+ let colors = [0xFF0000FF, 0x00FF00FF, 0x0000FFFF, 0xFFFF00FF, 0x00FFFFFF];
+
+ for (time, origins, vectors, color) in itertools::izip!(10..15, origins, vectors, colors) {
+ rec.set_duration_seconds("time", time);
+
+ let arrows = rerun::Arrows3D::from_vectors(vectors)
+ .with_origins(origins)
+ .with_colors([color]);
+
+ rec.log("arrows", &arrows)?;
+ }
+
+ Ok(())
+}
diff --git a/docs/snippets/snippets.toml b/docs/snippets/snippets.toml
index 10315d74992b..4426fad1a937 100644
--- a/docs/snippets/snippets.toml
+++ b/docs/snippets/snippets.toml
@@ -20,6 +20,7 @@ features = [
[
"Update rows",
[
+ "archetypes/arrows3d_row_updates",
"archetypes/scalar_row_updates",
"archetypes/points3d_row_updates",
"archetypes/transform3d_row_updates",
@@ -28,6 +29,7 @@ features = [
[
"Update columns",
[
+ "archetypes/arrows3d_row_columns",
"archetypes/scalar_column_updates",
"archetypes/points3d_column_updates",
"archetypes/transform3d_column_updates",
diff --git a/lychee.toml b/lychee.toml
index e8605a4a51e2..e1dc39bbd204 100644
--- a/lychee.toml
+++ b/lychee.toml
@@ -157,22 +157,10 @@ exclude = [
# '^file:///', # Ignore local file links. They need to be tested, but it's useful for external links we have to ping.
# Snippets that haven't been released yet.
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_column_updates.rs',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_partial_updates.cpp',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_partial_updates.py',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_partial_updates.rs',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_partial_updates_legacy.cpp',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_partial_updates_legacy.py',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/points3d_partial_updates_legacy.rs',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/transform3d_partial_updates.cpp',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/transform3d_partial_updates.py',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/transform3d_partial_updates.rs',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/howto/any_batch_value_column_updates.cpp',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/howto/any_batch_value_column_updates.rs',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/howto/any_values_column_updates.cpp',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/howto/any_values_column_updates.rs',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/tutorials/any_values.cpp',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/tutorials/any_values.rs',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/tutorials/extra_values.cpp',
- 'https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/tutorials/extra_values.rs',
+ "https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_row_updates.rs",
+ "https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_column_updates.rs",
+ "https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_column_updates.cpp",
+ "https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_row_updates.cpp",
+ "https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_row_updates.py",
+ "https://github.com/rerun-io/rerun/blob/main/docs/snippets/all/archetypes/arrows3d_column_updates.py",
]
diff --git a/rerun_py/rerun_sdk/rerun/_validators.py b/rerun_py/rerun_sdk/rerun/_validators.py
index f6cb4c6da37b..15225f2f7990 100644
--- a/rerun_py/rerun_sdk/rerun/_validators.py
+++ b/rerun_py/rerun_sdk/rerun/_validators.py
@@ -81,10 +81,9 @@ def flat_np_array_from_array_like(array: npt.NDArray[Any], dimension: int) -> np
if len(array.shape) == 1:
valid = (array.shape[0] % dimension) == 0
elif len(array.shape) >= 2:
- valid = array.shape[1] == dimension
-
- # Don't care about trailing dimensions if they're all 1.
- valid = valid and all(d == 1 for d in array.shape[2:])
+ # Get the last dimension that is not 1. If all dimensions are 1, this returns 1.
+ last_non_singleton = next((d for d in reversed(array.shape[1:]) if d != 1), 1)
+ valid = last_non_singleton == dimension
if not valid:
raise ValueError(
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/annotation_context.py b/rerun_py/rerun_sdk/rerun/archetypes/annotation_context.py
index 800617498a3b..cb9290c677da 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/annotation_context.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/annotation_context.py
@@ -171,8 +171,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/arrows2d.py b/rerun_py/rerun_sdk/rerun/archetypes/arrows2d.py
index 7b90addbb5fc..5c48e9eb94ca 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/arrows2d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/arrows2d.py
@@ -243,8 +243,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/arrows3d.py b/rerun_py/rerun_sdk/rerun/archetypes/arrows3d.py
index 85847de00234..bd4afbefd3d3 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/arrows3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/arrows3d.py
@@ -229,8 +229,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py
index 95de65c23a05..0af59dacb9aa 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/asset3d.py
@@ -192,8 +192,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/asset_video.py b/rerun_py/rerun_sdk/rerun/archetypes/asset_video.py
index 053c22772e7b..8444455e1a26 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/asset_video.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/asset_video.py
@@ -226,8 +226,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/bar_chart.py b/rerun_py/rerun_sdk/rerun/archetypes/bar_chart.py
index 63400ac74eec..3405236b10ee 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/bar_chart.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/bar_chart.py
@@ -167,8 +167,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/boxes2d.py b/rerun_py/rerun_sdk/rerun/archetypes/boxes2d.py
index b0d6c017a213..b0f56bb42284 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/boxes2d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/boxes2d.py
@@ -228,8 +228,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py b/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py
index 980857897295..0193c1e1bf32 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/boxes3d.py
@@ -276,8 +276,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/capsules3d.py b/rerun_py/rerun_sdk/rerun/archetypes/capsules3d.py
index 6bf1da9f50b8..c86213d276df 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/capsules3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/capsules3d.py
@@ -274,8 +274,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/clear.py b/rerun_py/rerun_sdk/rerun/archetypes/clear.py
index b5297c9bfb3c..87c99d150a0d 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/clear.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/clear.py
@@ -142,8 +142,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/depth_image.py b/rerun_py/rerun_sdk/rerun/archetypes/depth_image.py
index 43b254ef8b53..8c11df4fd813 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/depth_image.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/depth_image.py
@@ -270,8 +270,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids3d.py b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids3d.py
index df3e94d97e3b..cc352363e47a 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/ellipsoids3d.py
@@ -277,8 +277,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/encoded_image.py b/rerun_py/rerun_sdk/rerun/archetypes/encoded_image.py
index 82fb1c33d349..0e24d244587a 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/encoded_image.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/encoded_image.py
@@ -187,8 +187,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py
index 90f5249629e4..882bf2f05c5b 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py
@@ -183,8 +183,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py
index 501e559373dd..b07af7cede2e 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py
@@ -186,8 +186,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/graph_edges.py b/rerun_py/rerun_sdk/rerun/archetypes/graph_edges.py
index ad01961f2479..d0919cd1ff2b 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/graph_edges.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/graph_edges.py
@@ -183,8 +183,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/graph_nodes.py b/rerun_py/rerun_sdk/rerun/archetypes/graph_nodes.py
index 49c8e4bb7293..57cccec697e2 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/graph_nodes.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/graph_nodes.py
@@ -240,8 +240,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/image.py b/rerun_py/rerun_sdk/rerun/archetypes/image.py
index b57ef3e7b754..59007f3689fd 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/image.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/image.py
@@ -221,8 +221,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/instance_poses3d.py b/rerun_py/rerun_sdk/rerun/archetypes/instance_poses3d.py
index b3f4ab534f42..d3285b28ae52 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/instance_poses3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/instance_poses3d.py
@@ -245,8 +245,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/line_strips2d.py b/rerun_py/rerun_sdk/rerun/archetypes/line_strips2d.py
index 534e3b8eeeca..ef53f7f93256 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/line_strips2d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/line_strips2d.py
@@ -319,8 +319,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/line_strips3d.py b/rerun_py/rerun_sdk/rerun/archetypes/line_strips3d.py
index 2c7c5f69efd0..386cb74f2e61 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/line_strips3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/line_strips3d.py
@@ -309,8 +309,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py
index 7d4bc22e4d6f..32ccf84f7ad7 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/mesh3d.py
@@ -292,8 +292,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/pinhole.py b/rerun_py/rerun_sdk/rerun/archetypes/pinhole.py
index 12d289f7c652..8653accc738a 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/pinhole.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/pinhole.py
@@ -267,8 +267,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/points2d.py b/rerun_py/rerun_sdk/rerun/archetypes/points2d.py
index e94a3bfb50f4..e54deb6e6530 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/points2d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/points2d.py
@@ -298,8 +298,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/points3d.py b/rerun_py/rerun_sdk/rerun/archetypes/points3d.py
index a9f7b43eb607..0bd1cb3a8b7b 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/points3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/points3d.py
@@ -344,8 +344,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/scalar.py b/rerun_py/rerun_sdk/rerun/archetypes/scalar.py
index 77b640b7b3b0..455196fe50b4 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/scalar.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/scalar.py
@@ -196,8 +196,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/segmentation_image.py b/rerun_py/rerun_sdk/rerun/archetypes/segmentation_image.py
index 0f1574abf4a3..5794465a98a1 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/segmentation_image.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/segmentation_image.py
@@ -189,8 +189,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/series_line.py b/rerun_py/rerun_sdk/rerun/archetypes/series_line.py
index 69035631dee9..4636d62019e7 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/series_line.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/series_line.py
@@ -265,8 +265,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/series_point.py b/rerun_py/rerun_sdk/rerun/archetypes/series_point.py
index 4faf97813a14..a67bac7909ac 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/series_point.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/series_point.py
@@ -267,8 +267,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/tensor.py b/rerun_py/rerun_sdk/rerun/archetypes/tensor.py
index d059ebbac33a..6475c7278352 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/tensor.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/tensor.py
@@ -175,8 +175,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/text_document.py b/rerun_py/rerun_sdk/rerun/archetypes/text_document.py
index 725fa8fe6039..250fe9b3a40b 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/text_document.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/text_document.py
@@ -227,8 +227,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/text_log.py b/rerun_py/rerun_sdk/rerun/archetypes/text_log.py
index cc8e02a17689..56812a32e892 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/text_log.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/text_log.py
@@ -196,8 +196,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/transform3d.py b/rerun_py/rerun_sdk/rerun/archetypes/transform3d.py
index 6e9ce2ec708e..08b5a902d64c 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/transform3d.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/transform3d.py
@@ -443,8 +443,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/video_frame_reference.py b/rerun_py/rerun_sdk/rerun/archetypes/video_frame_reference.py
index 5c996f2496a6..03a49c8447d7 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/video_frame_reference.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/video_frame_reference.py
@@ -242,8 +242,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/rerun_sdk/rerun/archetypes/view_coordinates.py b/rerun_py/rerun_sdk/rerun/archetypes/view_coordinates.py
index 2f102b0de940..0cbd91079c96 100644
--- a/rerun_py/rerun_sdk/rerun/archetypes/view_coordinates.py
+++ b/rerun_py/rerun_sdk/rerun/archetypes/view_coordinates.py
@@ -173,8 +173,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/tests/test_types/archetypes/affix_fuzzer1.py b/rerun_py/tests/test_types/archetypes/affix_fuzzer1.py
index c6775d645927..3cb6f4abaaf4 100644
--- a/rerun_py/tests/test_types/archetypes/affix_fuzzer1.py
+++ b/rerun_py/tests/test_types/archetypes/affix_fuzzer1.py
@@ -280,8 +280,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/tests/test_types/archetypes/affix_fuzzer2.py b/rerun_py/tests/test_types/archetypes/affix_fuzzer2.py
index 057716e88557..6ecbfeb816c0 100644
--- a/rerun_py/tests/test_types/archetypes/affix_fuzzer2.py
+++ b/rerun_py/tests/test_types/archetypes/affix_fuzzer2.py
@@ -256,8 +256,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/tests/test_types/archetypes/affix_fuzzer3.py b/rerun_py/tests/test_types/archetypes/affix_fuzzer3.py
index b84b0fcaaebb..b86458a575f3 100644
--- a/rerun_py/tests/test_types/archetypes/affix_fuzzer3.py
+++ b/rerun_py/tests/test_types/archetypes/affix_fuzzer3.py
@@ -249,8 +249,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/tests/test_types/archetypes/affix_fuzzer4.py b/rerun_py/tests/test_types/archetypes/affix_fuzzer4.py
index 5323aab974a6..e798eba3fe40 100644
--- a/rerun_py/tests/test_types/archetypes/affix_fuzzer4.py
+++ b/rerun_py/tests/test_types/archetypes/affix_fuzzer4.py
@@ -249,8 +249,8 @@ def columns(
for batch in batches:
arrow_array = batch.as_arrow_array()
- # For primitive arrays, we infer partition size from the input shape.
- if pa.types.is_primitive(arrow_array.type):
+ # For primitive arrays and fixed size list arrays, we infer partition size from the input shape.
+ if pa.types.is_primitive(arrow_array.type) or pa.types.is_fixed_size_list(arrow_array.type):
param = kwargs[batch.component_descriptor().archetype_field_name] # type: ignore[index]
shape = np.shape(param) # type: ignore[arg-type]
diff --git a/rerun_py/tests/unit/test_validators.py b/rerun_py/tests/unit/test_validators.py
new file mode 100644
index 000000000000..5c504a256766
--- /dev/null
+++ b/rerun_py/tests/unit/test_validators.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+from typing import Any
+
+import numpy as np
+import numpy.typing as npt
+import pytest
+from rerun._validators import flat_np_array_from_array_like
+
+VALID_CASES = [
+ # 1D array: length is a multiple of dimension
+ (np.array([1, 2, 3, 4, 5, 6]), 3),
+ (np.array([10, 20, 30, 40]), 2),
+ # 2D array: shape is (n, dimension)
+ (np.array([[1, 2, 3], [4, 5, 6]]), 3),
+ # 3D array with extra singleton dimensions: last non-singleton dimension is dimension
+ (np.array([[[1, 2, 3]], [[4, 5, 6]]]), 3),
+ # Edge case: multi-dimensional array where all non-first dimensions are singletons (defaults to 1)
+ (np.array([[[1]]]), 1),
+ # Array of shape (1, 1, 1, 3)
+ (np.array([[[[1, 2, 3]]]]), 3),
+ # Array of shape (1, 1, 2, 3)
+ (np.array([[[[1, 2, 3], [4, 5, 6]]]]), 3),
+ # Array of shape (2, 2, 3)
+ (
+ np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]),
+ 3,
+ ),
+ # Array of shape (5, 1, 1, 3)
+ (np.arange(15).reshape(5, 1, 1, 3), 3),
+ # Array of shape (2, 1, 2) where the last non-singleton dimension is 2
+ (np.array([[[1, 2]], [[3, 4]]]), 2),
+]
+
+
+@pytest.mark.parametrize(["input_array", "dimension"], VALID_CASES)
+def test_flat_np_array_from_array_like_valid(input_array: npt.NDArray[Any], dimension: int) -> None:
+ np.testing.assert_array_equal(flat_np_array_from_array_like(input_array, dimension), input_array.reshape(-1))
+
+
+INVALID_CASES = [
+ # 1D array: length not a multiple of dimension
+ (np.array([1, 2, 3, 4]), 3),
+ (np.array([1, 2, 3]), 2),
+ # 2D array: shape is (n, m) where m != dimension
+ (np.array([[1, 2], [3, 4]]), 3),
+ # 3D array: last non-singleton dimension is not equal to dimension
+ (np.array([[[1, 2]], [[3, 4]]]), 3),
+ # Edge case: multi-dimensional array where all non-first dimensions are singletons (defaults to 1) but dimension is not 1
+ (np.array([[[1]]]), 2),
+ # 3D array with shape (2, 2, 2): last non-singleton dimension is 2, not equal to expected 3
+ (np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]), 3),
+ # 2D array with shape (3, 4): last dimension is 4, expected 3
+ (np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]), 3),
+ # 3D array with shape (1, 1, 4): last non-singleton dimension is 4, expected 3
+ (np.array([[[1, 2, 3, 4]]]), 3),
+ # 4D array with shape (1, 2, 1, 3): last non-singleton dimension is 3, expected 2
+ (np.array([[[[1, 2, 3]], [[4, 5, 6]]]]), 2),
+]
+
+
+@pytest.mark.parametrize(["input_array", "dimension"], INVALID_CASES)
+def test_flat_np_array_from_array_like_invalid(input_array: npt.NDArray[Any], dimension: int) -> None:
+ with pytest.raises(ValueError):
+ flat_np_array_from_array_like(input_array, dimension)