diff --git a/Cargo.lock b/Cargo.lock index 2a8a347f7113..c07a79d8734d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5096,6 +5096,18 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "japanese_alley" +version = "0.26.0-alpha.1+dev" +dependencies = [ + "anyhow", + "arrow", + "clap", + "itertools 0.14.0", + "re_arrow_util", + "rerun", +] + [[package]] name = "jiff" version = "0.2.15" diff --git a/crates/utils/re_arrow_util/src/transform.rs b/crates/utils/re_arrow_util/src/transform.rs index 254c0b4efda7..08f58555a7e4 100644 --- a/crates/utils/re_arrow_util/src/transform.rs +++ b/crates/utils/re_arrow_util/src/transform.rs @@ -668,7 +668,7 @@ impl Transform for Flatten { /// /// The underlying bytes buffer is reused, making this transformation almost zero-copy. #[derive(Clone, Debug, Default)] -pub struct BinaryToListUInt8 { +pub struct BinaryToListUInt8 { _from_offset: PhantomData, _to_offset: PhantomData, } diff --git a/examples/rust/japanese_alley/Cargo.toml b/examples/rust/japanese_alley/Cargo.toml new file mode 100644 index 000000000000..d0e716ca8884 --- /dev/null +++ b/examples/rust/japanese_alley/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "japanese_alley" +version = "0.26.0-alpha.1+dev" +edition = "2024" +rust-version = "1.88" +license = "MIT OR Apache-2.0" +publish = false + +[dependencies] +rerun = { path = "../../../crates/top/rerun", features = [ + "web_viewer", + "clap", +] } + +anyhow.workspace = true +arrow.workspace = true +clap = { workspace = true, features = ["derive"] } +itertools.workspace = true +re_arrow_util = { workspace = true } diff --git a/examples/rust/japanese_alley/src/main.rs b/examples/rust/japanese_alley/src/main.rs new file mode 100644 index 000000000000..0afeb19e6022 --- /dev/null +++ b/examples/rust/japanese_alley/src/main.rs @@ -0,0 +1,109 @@ +use arrow::array::{Float32Array, Float64Array, ListArray}; +use re_arrow_util::transform::{ + BinaryToListUInt8, Cast, MapFixedSizeList, MapList, StructToFixedList, Transform, +}; +use rerun::{ + EncodedImage, InstancePoses3D, Points3D, + dataframe::EntityPathFilter, + external::re_log, + lenses::{Error, LensBuilder, LensesSink, Op}, + sink::GrpcSink, +}; + +#[derive(Debug, clap::Parser)] +#[clap(author, version, about)] +struct Args { + #[command(flatten)] + rerun: rerun::clap::RerunArgs, + + /// The path to the MCAP file. + filepath: std::path::PathBuf, +} + +fn list_binary_to_list_uint8(input: &ListArray) -> Result { + Ok(MapList::new(BinaryToListUInt8::::new()).transform(input)?) +} + +fn convert_list_struct_to_list_fixed(list_array: &ListArray) -> Result { + // Arrow transformations can work on any Arrow-level. + let pipeline = MapList::new(StructToFixedList::new(["x", "y", "z"]).then( + MapFixedSizeList::new(Cast::::new()), + )); + Ok(pipeline.transform(list_array)?) +} + +// TODO: This example is still missing `tf`-style transforms. + +fn main() -> anyhow::Result<()> { + re_log::setup_logging(); + + use clap::Parser as _; + let args = Args::parse(); + + // The following could be improved with columnar archetype APIs. + let dummy_point = Points3D::new([[0.0f32, 0.0, 0.0]]) + .columns_of_unit_batches() + .unwrap() + .next() + .unwrap(); + + // plural + let instance_poses_lens = + LensBuilder::for_input_column(EntityPathFilter::all(), "foxglove.PosesInFrame:message") + .add_component_column( + InstancePoses3D::descriptor_translations(), + [ + // Lens operations always work on component-column level. + Op::access_field("poses"), + Op::flatten(), + Op::access_field("position"), + Op::func(convert_list_struct_to_list_fixed), + ], + ) + .add_static_component_column( + dummy_point.descriptor.clone(), + [Op::constant(dummy_point.list_array.clone())], + ) + .build(); + + // singular + let instance_pose_lens = + LensBuilder::for_input_column(EntityPathFilter::all(), "foxglove.PoseInFrame:message") + .add_component_column( + InstancePoses3D::descriptor_translations(), + [ + // Lens operations always work on component-column level. + Op::access_field("pose"), + Op::access_field("position"), + Op::func(convert_list_struct_to_list_fixed), + ], + ) + .add_static_component_column( + dummy_point.descriptor, + [Op::constant(dummy_point.list_array)], + ) + .build(); + + let image_lens = + LensBuilder::for_input_column(EntityPathFilter::all(), "foxglove.CompressedImage:message") + // TODO: We leave out the `format` column because the `png` contents are not a valid MIME type. + .add_component_column( + EncodedImage::descriptor_blob(), + [ + Op::access_field("data"), + Op::func(list_binary_to_list_uint8), + ], + ) + .build(); + + let lenses_sink = LensesSink::new(GrpcSink::default()) + .with_lens(image_lens) + .with_lens(instance_pose_lens) + .with_lens(instance_poses_lens); + + let (rec, _serve_guard) = args.rerun.init("rerun_example_japanese_alley")?; + rec.set_sink(Box::new(lenses_sink)); + rec.log_file_from_path(args.filepath, None, false)?; + + Ok(()) +}