From f69dfe93f298122870ca970a4e6187c0239e58b8 Mon Sep 17 00:00:00 2001 From: stelzo Date: Mon, 20 May 2024 01:14:27 +0200 Subject: [PATCH 01/11] merge main toml --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 148dc83..3d23d79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ros_pointcloud2" -version = "0.5.0-rc.1" +version = "0.5.0-rc.2" edition = "2021" authors = ["Christopher Sieh "] description = "Customizable conversions for working with sensor_msgs/PointCloud2." @@ -35,8 +35,8 @@ rosrust = { version = "0.9.11", optional = true } r2r = { version = "0.8.4", optional = true } rayon = { version = "1", optional = true } nalgebra = { version = "0.32.5", optional = true, default-features = false } -rpcl2-derive = { version = "0.1", optional = true } -type-layout = { version = "0.2", package = "type-layout-syn2", optional = true } +rpcl2-derive = { version = "0.2.0", optional = true, path = "../rpcl2-derive" } +memoffset = { version = "0.9", optional = true } sensor_msgs = { version = "*", optional = true } std_msgs = { version = "*", optional = true } @@ -51,11 +51,11 @@ rclrs_msg = ["dep:sensor_msgs", "dep:std_msgs", "dep:builtin_interfaces"] rosrust_msg = ["dep:rosrust_msg", "dep:rosrust"] r2r_msg = ["dep:r2r"] rayon = ["dep:rayon"] -derive = ["dep:rpcl2-derive", "dep:type-layout"] +derive = ["dep:rpcl2-derive", "dep:memoffset"] nalgebra = ["dep:nalgebra"] std = ["nalgebra/std"] -default = ["std", "derive", "rclrs_msg"] +default = ["std", "rclrs_msg"] [package.metadata.docs.rs] features = ["derive", "nalgebra", "rayon"] From d7d51e0cc20d6e0396ddebda656a67c4d5fd3665 Mon Sep 17 00:00:00 2001 From: stelzo Date: Mon, 20 May 2024 01:20:39 +0200 Subject: [PATCH 02/11] merge main --- Cargo.toml | 2 +- src/iterator.rs | 25 +-- src/lib.rs | 384 ++++++++++++++++++++-------------------------- src/points.rs | 207 ++++++++++++++----------- src/prelude.rs | 9 +- tests/e2e_test.rs | 175 ++++++++++++++------- 6 files changed, 423 insertions(+), 379 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3d23d79..93b323f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ rosrust = { version = "0.9.11", optional = true } r2r = { version = "0.8.4", optional = true } rayon = { version = "1", optional = true } nalgebra = { version = "0.32.5", optional = true, default-features = false } -rpcl2-derive = { version = "0.2.0", optional = true, path = "../rpcl2-derive" } +rpcl2-derive = { version = "0.2.0", optional = true } memoffset = { version = "0.9", optional = true } sensor_msgs = { version = "*", optional = true } diff --git a/src/iterator.rs b/src/iterator.rs index 7e6b7c7..cf7cc60 100644 --- a/src/iterator.rs +++ b/src/iterator.rs @@ -1,6 +1,6 @@ //! Iterator implementations for [`PointCloud2Msg`] including a parallel iterator for rayon. use crate::{ - Endian, FieldDatatype, Fields, MsgConversionError, PointCloud2Msg, PointConvertible, PointData, + Endian, FieldDatatype, MsgConversionError, PointCloud2Msg, PointConvertible, PointData, RPCL2Point, }; @@ -27,12 +27,12 @@ use alloc::vec::Vec; /// pub struct PointCloudIterator where - C: Fields, + C: PointConvertible, { iteration: usize, iteration_back: usize, data: ByteBufferView, - phantom_c: core::marker::PhantomData, // internally used for pdata names array + _phantom: core::marker::PhantomData, } #[cfg(feature = "rayon")] @@ -250,7 +250,7 @@ impl ByteBufferView { impl TryFrom for PointCloudIterator where - C: Fields, + C: PointConvertible, { type Error = MsgConversionError; @@ -262,8 +262,10 @@ where fn try_from(cloud: PointCloud2Msg) -> Result { let mut pdata_with_offsets = vec![(String::default(), FieldDatatype::default(), 0); N]; - let not_found_fieldnames = C::field_names_ordered() - .into_iter() + let fields_only = crate::ordered_field_names::(); + + let not_found_fieldnames = fields_only + .iter() .map(|name| { let found = cloud.fields.iter().any(|field| field.name == *name); (name, found) @@ -279,9 +281,8 @@ where return Err(MsgConversionError::FieldsNotFound(names_not_found)); } - let ordered_fieldnames = C::field_names_ordered(); for (field, with_offset) in cloud.fields.iter().zip(pdata_with_offsets.iter_mut()) { - if ordered_fieldnames.contains(&field.name.as_str()) { + if fields_only.contains(&field.name) { *with_offset = ( field.name.clone(), field.datatype.try_into()?, @@ -342,14 +343,14 @@ where iteration: 0, iteration_back: cloud_length - 1, data, - phantom_c: core::marker::PhantomData, + _phantom: core::marker::PhantomData, }) } } impl PointCloudIterator where - C: Fields, + C: PointConvertible, { #[inline] #[must_use] @@ -358,7 +359,7 @@ where iteration: 0, iteration_back: data.len() - 1, data, - phantom_c: core::marker::PhantomData, + _phantom: core::marker::PhantomData, } } @@ -495,4 +496,4 @@ mod test { let first_right = right.next(); assert!(first_right.is_none()); } -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index d4dfa2e..bfee774 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,8 @@ //! The library provides the [`PointCloud2Msg`] type, which implements conversions to and from `Vec` and (parallel) iterators. //! //! Vector conversions are optimized for direct copy. They are useful when you just move similar data around. They are usually a good default. -//! - [`try_from_vec`](PointCloud2Msg::try_from_vec) requires `derive` feature (enabled by default) -//! - [`try_into_vec`](PointCloud2Msg::try_into_vec) requires `derive` feature (enabled by default) +//! - [`try_from_vec`](PointCloud2Msg::try_from_vec) +//! - [`try_into_vec`](PointCloud2Msg::try_into_vec) //! //! You can use the iterator functions for more control over the conversion process. //! - [`try_from_iter`](PointCloud2Msg::try_from_iter) @@ -64,16 +64,27 @@ //! # Custom Points //! Implement [`PointConvertible`] for your point with the `derive` feature or manually. //! +//! ## Derive (recommended) +//! ```ignore +//! #[derive(Clone, Debug, PartialEq, Copy, Default, PointConvertible)] +//! pub struct MyPointXYZI { +//! pub x: f32, +//! pub y: f32, +//! pub z: f32, +//! #[rpcl2(rename("i"))] +//! pub intensity: f32, +//! } +//! ``` +//! +//! ## Manual //! ``` //! use ros_pointcloud2::prelude::*; //! -//! #[cfg_attr(feature = "derive", derive(PointConvertible, TypeLayout))] //! #[derive(Clone, Debug, PartialEq, Copy, Default)] //! pub struct MyPointXYZI { //! pub x: f32, //! pub y: f32, //! pub z: f32, -//! #[cfg_attr(feature = "derive", rpcl2(rename("i")))] //! pub intensity: f32, //! } //! @@ -83,30 +94,28 @@ //! } //! } //! -//! // Manual implementation of PointConvertible without derive. -//! #[cfg(not(feature = "derive"))] -//! impl Fields<4> for MyPointXYZI { -//! fn field_names_ordered() -> [&'static str; 4] { -//! ["x", "y", "z", "i"] // Note the different field name for intensity. -//! } -//! } -//! -//! #[cfg(not(feature = "derive"))] //! impl From> for MyPointXYZI { //! fn from(point: RPCL2Point<4>) -> Self { //! Self::new(point[0].get(), point[1].get(), point[2].get(), point[3].get()) //! } //! } //! -//! #[cfg(not(feature = "derive"))] //! impl From for RPCL2Point<4> { //! fn from(point: MyPointXYZI) -> Self { //! [point.x.into(), point.y.into(), point.z.into(), point.intensity.into()].into() //! } //! } //! -//! #[cfg(not(feature = "derive"))] -//! impl PointConvertible<4> for MyPointXYZI {} +//! impl PointConvertible<4> for MyPointXYZI { +//! fn layout() -> LayoutDescription { +//! LayoutDescription::new(&[ +//! LayoutField::new("x", "f32", 4), +//! LayoutField::new("y", "f32", 4), +//! LayoutField::new("z", "f32", 4), +//! LayoutField::new("intensity", "f32", 4), +//! ]) +//! } +//! } //! //! let first_p = MyPointXYZI::new(1.0, 2.0, 3.0, 0.5); //! let cloud_points = vec![first_p, MyPointXYZI::new(4.0, 5.0, 6.0, 0.5)]; @@ -114,21 +123,9 @@ //! let cloud_points_out: Vec = msg_out.try_into_iter().unwrap().collect(); //! assert_eq!(first_p, *cloud_points_out.first().unwrap()); //! ``` -//! -//! An example without `#[cfg_attr]` looks like this: -//! ```ignore -//! #[derive(Clone, Debug, PartialEq, Copy, Default, PointConvertible, TypeLayout)] -//! pub struct MyPointXYZI { -//! pub x: f32, -//! pub y: f32, -//! pub z: f32, -//! #[rpcl2(rename("i"))] -//! pub intensity: f32, -//! } -//! ``` #![crate_type = "lib"] #![cfg_attr(docsrs, feature(doc_cfg))] -#![doc(html_root_url = "https://docs.rs/ros_pointcloud2/0.5.0-rc.1")] +#![doc(html_root_url = "https://docs.rs/ros_pointcloud2/0.5.0-rc.2")] #![warn(clippy::print_stderr)] #![warn(clippy::print_stdout)] #![warn(clippy::unwrap_used)] @@ -139,14 +136,6 @@ #![cfg_attr(not(feature = "std"), no_std)] // Setup an allocator with #[global_allocator] // see: https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html -#![warn(clippy::unwrap_used)] -#![warn(clippy::cargo)] -#![warn(clippy::std_instead_of_core)] -#![warn(clippy::alloc_instead_of_core)] -#![warn(clippy::std_instead_of_alloc)] -#![cfg_attr(not(feature = "std"), no_std)] -// Setup an allocator with #[global_allocator] -// see: https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html pub mod points; pub mod prelude; @@ -157,6 +146,9 @@ pub mod iterator; use crate::ros::{HeaderMsg, PointFieldMsg}; #[cfg(feature = "derive")] +#[doc(hidden)] +pub use memoffset; + use core::str::FromStr; #[macro_use] @@ -230,7 +222,6 @@ impl std::error::Error for MsgConversionError { } } -#[cfg(feature = "derive")] fn system_endian() -> Endian { if cfg!(target_endian = "big") { Endian::Big @@ -241,6 +232,43 @@ fn system_endian() -> Endian { } } +/// Description of the memory layout of a type with named fields. +#[derive(Clone, Debug)] +pub struct LayoutDescription(Vec); + +impl LayoutDescription { + pub fn new(fields: &[LayoutField]) -> Self { + Self(fields.into()) + } +} + +/// Enum to describe the field type and size in a padded or unpadded layout. +#[derive(Clone, Debug)] +pub enum LayoutField { + Field { + name: alloc::borrow::Cow<'static, str>, + ty: alloc::borrow::Cow<'static, str>, + size: usize, + }, + Padding { + size: usize, + }, +} + +impl LayoutField { + pub fn new(name: &'static str, ty: &'static str, size: usize) -> Self { + LayoutField::Field { + name: name.into(), + ty: ty.into(), + size, + } + } + + pub fn padding(size: usize) -> Self { + LayoutField::Padding { size } + } +} + /// The intermediate point cloud type for ROS integrations. /// /// To assert consistency, the type should be build with the [`PointCloud2MsgBuilder`]. @@ -273,7 +301,6 @@ pub enum Denseness { Sparse, } -#[cfg(feature = "derive")] enum ByteSimilarity { Equal, Overlapping, @@ -428,8 +455,32 @@ pub struct CloudDimensions { pub height: u32, } +fn ordered_field_names>() -> Vec { + C::layout() + .0 + .iter() + .filter(|field| { + matches!( + field, + LayoutField::Field { + name: _, + ty: _, + size: _, + } + ) + }) + .map(|field| match field { + LayoutField::Field { + name, + ty: _, + size: _, + } => name.to_string(), + _ => unreachable!("Fields must be filtered before."), + }) + .collect() +} + impl PointCloud2Msg { - #[cfg(feature = "derive")] #[inline] fn byte_similarity(&self) -> Result where @@ -438,26 +489,26 @@ impl PointCloud2Msg { let point: RPCL2Point = C::default().into(); debug_assert!(point.fields.len() == N); - let field_names = C::field_names_ordered(); + let field_names = ordered_field_names::(); debug_assert!(field_names.len() == N); - let layout = TypeLayoutInfo::try_from(C::type_layout())?; - debug_assert!(field_names.len() == layout.fields.len()); + let layout = KnownLayoutInfo::try_from(C::layout())?; + debug_assert!(field_names.len() <= layout.fields.len()); let mut offset: u32 = 0; let mut field_counter = 0; - for (f, msg_f) in layout.fields.iter().zip(self.fields.iter()) { + for f in layout.fields.iter() { match f { PointField::Field { datatype, size, count, } => { - let f_translated = String::from_str(field_names[field_counter]) - .expect("Field name is not a valid string."); + let msg_f = &self.fields[field_counter]; + let f_translated = &field_names[field_counter]; field_counter += 1; - if msg_f.name != f_translated + if msg_f.name != *f_translated || msg_f.offset != offset || msg_f.datatype != *datatype || msg_f.count != 1 @@ -503,7 +554,7 @@ impl PointCloud2Msg { let point: RPCL2Point = C::default().into(); debug_assert!(point.fields.len() == N); - let field_names = C::field_names_ordered(); + let field_names = crate::ordered_field_names::(); debug_assert!(field_names.len() == N); let mut pdata_offsets_acc: u32 = 0; @@ -519,7 +570,7 @@ impl PointCloud2Msg { let _ = FieldDatatype::try_from(datatype_code)?; *field_val = PointFieldMsg { - name: field_name.into(), + name: field_name, offset: pdata_offsets_acc, datatype: datatype_code, count: 1, @@ -557,8 +608,8 @@ impl PointCloud2Msg { } /// Create a PointCloud2Msg from a parallel iterator. Requires the `rayon` and `derive` feature to be enabled. - #[cfg(all(feature = "rayon", feature = "derive"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "rayon", feature = "derive"))))] + #[cfg(feature = "rayon")] + #[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] pub fn try_from_par_iter( iterable: impl rayon::iter::ParallelIterator, ) -> Result @@ -585,8 +636,6 @@ impl PointCloud2Msg { /// /// # Errors /// Returns an error if the byte buffer does not match the expected layout or the message contains other discrepancies. - #[cfg(feature = "derive")] - #[cfg_attr(docsrs, doc(cfg(feature = "derive")))] pub fn try_from_vec(vec: Vec) -> Result where C: PointConvertible, @@ -597,17 +646,15 @@ impl PointCloud2Msg { let point: RPCL2Point = C::default().into(); debug_assert!(point.fields.len() == N); - let field_names = C::field_names_ordered(); + let field_names = crate::ordered_field_names::(); debug_assert!(field_names.len() == N); - let layout = TypeLayoutInfo::try_from(C::type_layout())?; - debug_assert!(field_names.len() == layout.fields.len()); + let layout = KnownLayoutInfo::try_from(C::layout())?; + debug_assert!(field_names.len() <= layout.fields.len()); let mut offset = 0; - let mut fields: Vec = Vec::with_capacity(layout.fields.len()); + let mut fields: Vec = Vec::with_capacity(field_names.len()); for f in layout.fields.into_iter() { - let f_translated = String::from_str(field_names[fields.len()]) - .expect("Field name is not a valid string."); match f { PointField::Field { datatype, @@ -615,7 +662,7 @@ impl PointCloud2Msg { count, } => { fields.push(PointFieldMsg { - name: f_translated, + name: field_names[fields.len()].clone(), offset, datatype, ..Default::default() @@ -674,8 +721,6 @@ impl PointCloud2Msg { /// /// # Errors /// Returns an error if the byte buffer does not match the expected layout or the message contains other discrepancies. - #[cfg(feature = "derive")] - #[cfg_attr(docsrs, doc(cfg(feature = "derive")))] pub fn try_into_vec(self) -> Result, MsgConversionError> where C: PointConvertible, @@ -778,14 +823,6 @@ pub struct RPCL2Point { fields: [PointData; N], } -impl Default for RPCL2Point { - fn default() -> Self { - Self { - fields: [PointData::default(); N], - } - } -} - impl core::ops::Index for RPCL2Point { type Output = PointData; @@ -802,157 +839,97 @@ impl From<[PointData; N]> for RPCL2Point { /// Trait to enable point conversions on the fly. /// -/// # Example +/// Implement this trait for your custom point you want to read or write in the message. +/// It is strongly recommended to enable the `derive` feature and use the `#[derive(PointConvertible)]` macro. +/// This prevents common errors when implementing this trait by hand. +/// +/// Be aware that Rust does not guarantee the memory layout of structs. Learn more [here](https://doc.rust-lang.org/reference/type-layout.html). +/// To make layouting more predictable and thus faster for C++ node interactions, use the `#[repr(C)]` attribute on your struct. +/// An example for diverging point layouts with padding can be seen in the source code of [this](points::PointXYZRGBA::layout) implementation. +/// +/// The generic parameter `N` is the number of fields in the point type. There can be more (hidden) fields that pad the layout but they do not count for the N. +/// For +/// +/// # Derive +/// ```ignore +/// use ros_pointcloud2::prelude::*; +/// +/// #[derive(Clone, Debug, PartialEq, Copy, Default, PointConvertible)] +/// #[repr(C, align(4))] +/// pub struct MyPointXYZL { +/// pub x: f32, +/// pub y: f32, +/// pub z: f32, +/// #[rpcl2(rename("l"))] +/// pub label: u8, +/// } +/// ``` +/// +/// # Manual /// ``` /// use ros_pointcloud2::prelude::*; /// /// #[derive(Clone, Debug, PartialEq, Copy, Default)] -/// pub struct MyPointXYZI { +/// #[repr(C, align(4))] +/// pub struct MyPointXYZL { /// pub x: f32, /// pub y: f32, /// pub z: f32, -/// pub intensity: f32, +/// pub label: u8, /// } /// -/// impl From for RPCL2Point<4> { -/// fn from(point: MyPointXYZI) -> Self { -/// [point.x.into(), point.y.into(), point.z.into(), point.intensity.into()].into() +/// impl From for RPCL2Point<4> { +/// fn from(point: MyPointXYZL) -> Self { +/// [point.x.into(), point.y.into(), point.z.into(), point.label.into()].into() /// } /// } /// -/// impl From> for MyPointXYZI { +/// impl From> for MyPointXYZL { /// fn from(point: RPCL2Point<4>) -> Self { /// Self { /// x: point[0].get(), /// y: point[1].get(), /// z: point[2].get(), -/// intensity: point[3].get(), +/// label: point[3].get(), /// } /// } /// } /// -/// impl Fields<4> for MyPointXYZI { -/// fn field_names_ordered() -> [&'static str; 4] { -/// ["x", "y", "z", "intensity"] -/// } -/// } -/// -/// impl PointConvertible<4> for MyPointXYZI {} -/// ``` -#[cfg_attr(docsrs, doc(cfg(not(feature = "derive"))))] -#[cfg(not(feature = "derive"))] -pub trait PointConvertible: - From> + Into> + Fields + Clone + Default -{ -} - -/// Trait to enable point conversions on the fly. -/// -/// Implement this trait for your custom point you want to read or write in the message. -/// For a more convenient way to implement this trait, enable the `derive` feature and use the `#[derive(PointConvertible, TypeLayout)]` macro. -/// -/// # Derive Example -/// ``` -/// use ros_pointcloud2::prelude::*; -/// -/// #[derive(Clone, Debug, PartialEq, Copy, Default, PointConvertible, TypeLayout)] -/// pub struct MyPointXYZI { -/// pub x: f32, -/// pub y: f32, -/// pub z: f32, -/// pub intensity: f32, -/// } -/// ``` -/// -/// # Manual Example -/// ``` -/// use ros_pointcloud2::prelude::*; -/// -/// #[derive(Clone, Debug, PartialEq, Copy, Default, TypeLayout)] -/// pub struct MyPointXYZI { -/// pub x: f32, -/// pub y: f32, -/// pub z: f32, -/// pub intensity: f32, -/// } -/// -/// impl From for RPCL2Point<4> { -/// fn from(point: MyPointXYZI) -> Self { -/// [point.x.into(), point.y.into(), point.z.into(), point.intensity.into()].into() +/// impl PointConvertible<4> for MyPointXYZL { +/// fn layout() -> LayoutDescription { +/// LayoutDescription::new(&[ +/// LayoutField::new("x", "f32", 4), +/// LayoutField::new("y", "f32", 4), +/// LayoutField::new("z", "f32", 4), +/// LayoutField::new("l", "u8", 1), +/// LayoutField::padding(3), +/// ]) /// } /// } -/// -/// impl From> for MyPointXYZI { -/// fn from(point: RPCL2Point<4>) -> Self { -/// Self { -/// x: point[0].get(), -/// y: point[1].get(), -/// z: point[2].get(), -/// intensity: point[3].get(), -/// } -/// } -/// } -/// -/// impl Fields<4> for MyPointXYZI { -/// fn field_names_ordered() -> [&'static str; 4] { -/// ["x", "y", "z", "intensity"] -/// } -/// } -/// -/// impl PointConvertible<4> for MyPointXYZI {} /// ``` -#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] -#[cfg(feature = "derive")] pub trait PointConvertible: - type_layout::TypeLayout + From> + Into> + Fields + Default + From> + Into> + Default + Sized { + fn layout() -> LayoutDescription; } -/// Matching field names from each data point. -/// Always make sure to use the same order as in your conversion implementation to have a correct mapping. -/// -/// This trait is needed to implement the `PointConvertible` trait. -/// -/// # Example -/// ``` -/// use ros_pointcloud2::prelude::*; -/// -/// #[derive(Clone, Debug, PartialEq, Copy)] -/// pub struct MyPointXYZI { -/// pub x: f32, -/// pub y: f32, -/// pub z: f32, -/// pub intensity: f32, -/// } -/// -/// impl Fields<4> for MyPointXYZI { -/// fn field_names_ordered() -> [&'static str; 4] { -/// ["x", "y", "z", "intensity"] -/// } -/// } -/// ``` -pub trait Fields { - fn field_names_ordered() -> [&'static str; N]; -} - -#[cfg(feature = "derive")] +#[derive(Debug)] enum PointField { Padding(u32), Field { size: u32, datatype: u8, count: u32 }, } -#[cfg(feature = "derive")] -struct TypeLayoutInfo { +#[derive(Debug)] +struct KnownLayoutInfo { fields: Vec, } -#[cfg(feature = "derive")] -impl TryFrom for PointField { +impl TryFrom for PointField { type Error = MsgConversionError; - fn try_from(f: type_layout::Field) -> Result { + fn try_from(f: LayoutField) -> Result { match f { - type_layout::Field::Field { name: _, ty, size } => { + LayoutField::Field { name: _, ty, size } => { let typename: String = ty.into_owned().to_lowercase(); let datatype = FieldDatatype::from_str(typename.as_str())?; Ok(Self::Field { @@ -961,21 +938,19 @@ impl TryFrom for PointField { count: 1, }) } - type_layout::Field::Padding { size } => Ok(Self::Padding(size.try_into()?)), + LayoutField::Padding { size } => Ok(Self::Padding(size.try_into()?)), } } } -#[cfg(feature = "derive")] -impl TryFrom for TypeLayoutInfo { +impl TryFrom for KnownLayoutInfo { type Error = MsgConversionError; - fn try_from(t: type_layout::TypeLayoutInfo) -> Result { - let fields: Vec = t - .fields - .into_iter() - .map(PointField::try_from) - .collect::, _>>()?; + fn try_from(t: LayoutDescription) -> Result { + let fields: Vec = + t.0.into_iter() + .map(PointField::try_from) + .collect::, _>>()?; Ok(Self { fields }) } } @@ -1470,29 +1445,4 @@ impl FromBytes for u8 { fn from_le_bytes(bytes: PointDataBuffer) -> Self { Self::from_le_bytes([bytes[0]]) } -} - -#[cfg(test)] -#[cfg(feature = "derive")] -mod tests { - use super::Fields; - use rpcl2_derive::Fields; - - use alloc::string::String; - - #[allow(dead_code)] - #[derive(Fields)] - struct TestStruct { - field1: String, - #[rpcl2(rename("renamed_field"))] - field2: i32, - field3: f64, - field4: bool, - } - - #[test] - fn test_struct_names() { - let names = TestStruct::field_names_ordered(); - assert_eq!(names, ["field1", "renamed_field", "field3", "field4"]); - } -} +} \ No newline at end of file diff --git a/src/points.rs b/src/points.rs index 9c8d0af..87def59 100644 --- a/src/points.rs +++ b/src/points.rs @@ -1,18 +1,12 @@ //! Predefined point types commonly used in ROS. -use crate::{Fields, PointConvertible, RPCL2Point}; - -#[cfg(feature = "derive")] -use type_layout::TypeLayout; - -#[cfg(feature = "derive")] -use alloc::vec::Vec; +use crate::{LayoutDescription, LayoutField, PointConvertible, RPCL2Point}; /// A packed RGB color encoding as used in ROS tools. #[derive(Clone, Copy)] -#[repr(C)] +#[repr(C, align(4))] pub union RGB { packed: f32, - unpacked: [u8; 4], // 1 byte padding + unpacked: [u8; 4], // Padding } unsafe impl Send for RGB {} @@ -112,8 +106,7 @@ impl From for RGB { /// Predefined point type commonly used in ROS with PCL. /// This is a 3D point with x, y, z coordinates. #[derive(Clone, Debug, PartialEq, Copy, Default)] -#[cfg_attr(feature = "derive", derive(TypeLayout))] -#[repr(C)] +#[repr(C, align(16))] pub struct PointXYZ { pub x: f32, pub y: f32, @@ -155,12 +148,6 @@ impl PointXYZ { unsafe impl Send for PointXYZ {} unsafe impl Sync for PointXYZ {} -impl Fields<3> for PointXYZ { - fn field_names_ordered() -> [&'static str; 3] { - ["x", "y", "z"] - } -} - impl From> for PointXYZ { fn from(point: RPCL2Point<3>) -> Self { Self::new(point[0].get(), point[1].get(), point[2].get()) @@ -173,13 +160,21 @@ impl From for RPCL2Point<3> { } } -impl PointConvertible<3> for PointXYZ {} +impl PointConvertible<3> for PointXYZ { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::padding(4), + ]) + } +} /// Predefined point type commonly used in ROS with PCL. /// This is a 3D point with x, y, z coordinates and an intensity value. #[derive(Clone, Debug, PartialEq, Copy, Default)] -#[cfg_attr(feature = "derive", derive(TypeLayout))] -#[repr(C)] +#[repr(C, align(16))] pub struct PointXYZI { pub x: f32, pub y: f32, @@ -203,12 +198,6 @@ impl PointXYZI { unsafe impl Send for PointXYZI {} unsafe impl Sync for PointXYZI {} -impl Fields<4> for PointXYZI { - fn field_names_ordered() -> [&'static str; 4] { - ["x", "y", "z", "intensity"] - } -} - impl From> for PointXYZI { fn from(point: RPCL2Point<4>) -> Self { Self::new( @@ -232,13 +221,21 @@ impl From for RPCL2Point<4> { } } -impl PointConvertible<4> for PointXYZI {} +impl PointConvertible<4> for PointXYZI { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("intensity", "f32", 4), + ]) + } +} /// Predefined point type commonly used in ROS with PCL. /// This is a 3D point with x, y, z coordinates and a label. #[derive(Clone, Debug, PartialEq, Copy, Default)] -#[cfg_attr(feature = "derive", derive(TypeLayout))] -#[repr(C)] +#[repr(C, align(16))] pub struct PointXYZL { pub x: f32, pub y: f32, @@ -262,12 +259,6 @@ impl PointXYZL { unsafe impl Send for PointXYZL {} unsafe impl Sync for PointXYZL {} -impl Fields<4> for PointXYZL { - fn field_names_ordered() -> [&'static str; 4] { - ["x", "y", "z", "label"] - } -} - impl From> for PointXYZL { fn from(point: RPCL2Point<4>) -> Self { Self::new( @@ -291,13 +282,21 @@ impl From for RPCL2Point<4> { } } -impl PointConvertible<4> for PointXYZL {} +impl PointConvertible<4> for PointXYZL { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("label", "u32", 4), + ]) + } +} /// Predefined point type commonly used in ROS with PCL. /// This is a 3D point with x, y, z coordinates and an RGB color value. #[derive(Clone, Debug, PartialEq, Copy, Default)] -#[cfg_attr(feature = "derive", derive(TypeLayout))] -#[repr(C)] +#[repr(C, align(16))] pub struct PointXYZRGB { pub x: f32, pub y: f32, @@ -338,12 +337,6 @@ impl PointXYZRGB { unsafe impl Send for PointXYZRGB {} unsafe impl Sync for PointXYZRGB {} -impl Fields<4> for PointXYZRGB { - fn field_names_ordered() -> [&'static str; 4] { - ["x", "y", "z", "rgb"] - } -} - impl From> for PointXYZRGB { fn from(point: RPCL2Point<4>) -> Self { Self { @@ -367,14 +360,22 @@ impl From for RPCL2Point<4> { } } -impl PointConvertible<4> for PointXYZRGB {} +impl PointConvertible<4> for PointXYZRGB { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("rgb", "RGB", 4), + ]) + } +} /// Predefined point type commonly used in ROS with PCL. /// This is a 3D point with x, y, z coordinates and an RGBA color value. /// The alpha channel is commonly used as padding but this crate uses every channel and no padding. #[derive(Clone, Debug, PartialEq, Copy, Default)] -#[cfg_attr(feature = "derive", derive(TypeLayout))] -#[repr(C)] +#[repr(C, align(16))] pub struct PointXYZRGBA { pub x: f32, pub y: f32, @@ -416,12 +417,6 @@ impl PointXYZRGBA { unsafe impl Send for PointXYZRGBA {} unsafe impl Sync for PointXYZRGBA {} -impl Fields<5> for PointXYZRGBA { - fn field_names_ordered() -> [&'static str; 5] { - ["x", "y", "z", "rgb", "a"] - } -} - impl From> for PointXYZRGBA { fn from(point: RPCL2Point<5>) -> Self { Self { @@ -447,13 +442,23 @@ impl From for RPCL2Point<5> { } } -impl PointConvertible<5> for PointXYZRGBA {} +impl PointConvertible<5> for PointXYZRGBA { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("rgb", "RGB", 4), + LayoutField::new("a", "u8", 1), + LayoutField::padding(15), + ]) + } +} /// Predefined point type commonly used in ROS with PCL. /// This is a 3D point with x, y, z coordinates, an RGB color value and a normal vector. #[derive(Clone, Debug, PartialEq, Copy, Default)] -#[cfg_attr(feature = "derive", derive(TypeLayout))] -#[repr(C)] +#[repr(C, align(16))] pub struct PointXYZRGBNormal { pub x: f32, pub y: f32, @@ -512,12 +517,6 @@ impl PointXYZRGBNormal { unsafe impl Send for PointXYZRGBNormal {} unsafe impl Sync for PointXYZRGBNormal {} -impl Fields<7> for PointXYZRGBNormal { - fn field_names_ordered() -> [&'static str; 7] { - ["x", "y", "z", "rgb", "normal_x", "normal_y", "normal_z"] - } -} - impl From> for PointXYZRGBNormal { fn from(point: RPCL2Point<7>) -> Self { Self { @@ -547,13 +546,25 @@ impl From for RPCL2Point<7> { } } -impl PointConvertible<7> for PointXYZRGBNormal {} +impl PointConvertible<7> for PointXYZRGBNormal { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("rgb", "RGB", 4), + LayoutField::new("normal_x", "f32", 4), + LayoutField::new("normal_y", "f32", 4), + LayoutField::new("normal_z", "f32", 4), + LayoutField::padding(4), + ]) + } +} /// Predefined point type commonly used in ROS with PCL. /// This is a 3D point with x, y, z coordinates, an intensity value and a normal vector. #[derive(Clone, Debug, PartialEq, Copy, Default)] -#[cfg_attr(feature = "derive", derive(TypeLayout))] -#[repr(C)] +#[repr(C, align(16))] pub struct PointXYZINormal { pub x: f32, pub y: f32, @@ -597,12 +608,6 @@ impl PointXYZINormal { unsafe impl Send for PointXYZINormal {} unsafe impl Sync for PointXYZINormal {} -impl Fields<7> for PointXYZINormal { - fn field_names_ordered() -> [&'static str; 7] { - ["x", "y", "z", "i", "normal_x", "normal_y", "normal_z"] - } -} - impl From> for PointXYZINormal { fn from(point: RPCL2Point<7>) -> Self { Self::new( @@ -632,13 +637,25 @@ impl From for RPCL2Point<7> { } } -impl PointConvertible<7> for PointXYZINormal {} +impl PointConvertible<7> for PointXYZINormal { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("intensity", "f32", 4), + LayoutField::new("normal_x", "f32", 4), + LayoutField::new("normal_y", "f32", 4), + LayoutField::new("normal_z", "f32", 4), + LayoutField::padding(4), + ]) + } +} /// Predefined point type commonly used in ROS with PCL. /// This is a 3D point with x, y, z coordinates and a label. #[derive(Clone, Debug, PartialEq, Copy, Default)] -#[cfg_attr(feature = "derive", derive(TypeLayout))] -#[repr(C)] +#[repr(C, align(16))] pub struct PointXYZRGBL { pub x: f32, pub y: f32, @@ -686,12 +703,6 @@ impl PointXYZRGBL { } } -impl Fields<5> for PointXYZRGBL { - fn field_names_ordered() -> [&'static str; 5] { - ["x", "y", "z", "rgb", "label"] - } -} - impl From> for PointXYZRGBL { fn from(point: RPCL2Point<5>) -> Self { Self { @@ -717,13 +728,23 @@ impl From for RPCL2Point<5> { } } -impl PointConvertible<5> for PointXYZRGBL {} +impl PointConvertible<5> for PointXYZRGBL { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("rgb", "RGB", 4), + LayoutField::new("label", "u32", 4), + LayoutField::padding(12), + ]) + } +} /// Predefined point type commonly used in ROS with PCL. /// This is a 3D point with x, y, z coordinates and a normal vector. #[derive(Clone, Debug, PartialEq, Copy, Default)] -#[cfg_attr(feature = "derive", derive(TypeLayout))] -#[repr(C)] +#[repr(C, align(16))] pub struct PointXYZNormal { pub x: f32, pub y: f32, @@ -757,12 +778,6 @@ impl PointXYZNormal { unsafe impl Send for PointXYZNormal {} unsafe impl Sync for PointXYZNormal {} -impl Fields<6> for PointXYZNormal { - fn field_names_ordered() -> [&'static str; 6] { - ["x", "y", "z", "normal_x", "normal_y", "normal_z"] - } -} - impl From> for PointXYZNormal { fn from(point: RPCL2Point<6>) -> Self { Self::new( @@ -790,4 +805,16 @@ impl From for RPCL2Point<6> { } } -impl PointConvertible<6> for PointXYZNormal {} +impl PointConvertible<6> for PointXYZNormal { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("normal_x", "f32", 4), + LayoutField::new("normal_y", "f32", 4), + LayoutField::new("normal_z", "f32", 4), + LayoutField::padding(8), + ]) + } +} \ No newline at end of file diff --git a/src/prelude.rs b/src/prelude.rs index fd746fa..11cf41f 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,7 +1,7 @@ //! Commonly used types and traits for predefined and custom point conversions. pub use crate::{ - FieldDatatype, Fields, FromBytes, GetFieldDatatype, MsgConversionError, PointCloud2Msg, - PointConvertible, PointDataBuffer, RPCL2Point, + FieldDatatype, FromBytes, GetFieldDatatype, LayoutDescription, LayoutField, MsgConversionError, + PointCloud2Msg, PointConvertible, PointDataBuffer, RPCL2Point, }; pub use crate::points::*; @@ -11,7 +11,4 @@ pub use crate::ros::*; pub use rayon::prelude::*; #[cfg(feature = "derive")] -pub use type_layout::TypeLayout; - -#[cfg(feature = "derive")] -pub use rpcl2_derive::*; +pub use rpcl2_derive::*; \ No newline at end of file diff --git a/tests/e2e_test.rs b/tests/e2e_test.rs index 69fb781..81d2c9b 100644 --- a/tests/e2e_test.rs +++ b/tests/e2e_test.rs @@ -9,7 +9,6 @@ macro_rules! convert_from_into { }; } -#[cfg(feature = "derive")] macro_rules! convert_from_into_vec { ($point:ty, $cloud:expr) => { convert_from_into_in_out_cloud_vec!($cloud, $point, $cloud, $point); @@ -30,7 +29,6 @@ macro_rules! convert_from_into_in_out_cloud { }; } -#[cfg(feature = "derive")] macro_rules! convert_from_into_in_out_cloud_vec { ($in_cloud:expr, $in_point:ty, $out_cloud:expr, $out_point:ty) => { let msg = PointCloud2Msg::try_from_vec($in_cloud.clone()); @@ -59,7 +57,6 @@ fn write_cloud() { } #[test] -#[cfg(feature = "derive")] fn write_cloud_from_vec() { let cloud = vec![ PointXYZ::new(0.0, 1.0, 5.0), @@ -73,7 +70,6 @@ fn write_cloud_from_vec() { } #[test] -#[cfg(feature = "derive")] fn write_empty_cloud_vec() { let cloud: Vec = vec![]; let msg = PointCloud2Msg::try_from_vec(cloud); @@ -90,7 +86,7 @@ fn write_empty_cloud_iter() { } #[test] -#[cfg(all(feature = "derive", feature = "rayon"))] +#[cfg(feature = "rayon")] fn conv_cloud_par_iter() { let cloud = vec![ PointXYZ::new(0.0, 1.0, 5.0), @@ -110,7 +106,7 @@ fn conv_cloud_par_iter() { } #[test] -#[cfg(all(feature = "derive", feature = "rayon"))] +#[cfg(feature = "rayon")] fn conv_cloud_par_par_iter() { let cloud = vec![ PointXYZ::new(0.0, 1.0, 5.0), @@ -133,7 +129,7 @@ fn conv_cloud_par_par_iter() { #[test] #[cfg(feature = "derive")] fn custom_xyz_f32() { - #[derive(Debug, PartialEq, Clone, Default, PointConvertible, TypeLayout)] + #[derive(Debug, PartialEq, Clone, Default)] #[repr(C)] struct CustomPoint { x: f32, @@ -141,6 +137,32 @@ fn custom_xyz_f32() { z: f32, } + impl From> for CustomPoint { + fn from(point: RPCL2Point<3>) -> Self { + Self { + x: point[0].get(), + y: point[1].get(), + z: point[2].get(), + } + } + } + + impl From for RPCL2Point<3> { + fn from(point: CustomPoint) -> Self { + [point.x.into(), point.y.into(), point.z.into()].into() + } + } + + impl PointConvertible<3> for CustomPoint { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + ]) + } + } + convert_from_into!( CustomPoint, vec![ @@ -164,7 +186,6 @@ fn custom_xyz_f32() { } #[test] -#[cfg(feature = "derive")] fn custom_xyzi_f32() { let cloud: Vec = vec![ CustomPointXYZI { @@ -193,7 +214,7 @@ fn custom_xyzi_f32() { }, ]; - #[derive(Debug, PartialEq, Clone, Default, PointConvertible, TypeLayout)] + #[derive(Debug, PartialEq, Clone, Default)] #[repr(C)] struct CustomPointXYZI { x: f32, @@ -202,13 +223,48 @@ fn custom_xyzi_f32() { i: u8, } + impl From> for CustomPointXYZI { + fn from(point: RPCL2Point<4>) -> Self { + Self { + x: point[0].get(), + y: point[1].get(), + z: point[2].get(), + i: point[3].get(), + } + } + } + + impl From for RPCL2Point<4> { + fn from(point: CustomPointXYZI) -> Self { + [ + point.x.into(), + point.y.into(), + point.z.into(), + point.i.into(), + ] + .into() + } + } + + impl PointConvertible<4> for CustomPointXYZI { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("i", "u8", 1), + LayoutField::padding(3), + ]) + } + } + convert_from_into!(CustomPointXYZI, cloud); } #[test] #[cfg(feature = "derive")] fn custom_rgba_f32() { - #[derive(Debug, PartialEq, Clone, Default, PointConvertible, TypeLayout)] + #[derive(Debug, PartialEq, Clone, Default)] #[repr(C)] struct CustomPoint { x: f32, @@ -220,6 +276,53 @@ fn custom_rgba_f32() { a: u8, } + impl From> for CustomPoint { + fn from(point: RPCL2Point<7>) -> Self { + Self { + x: point[0].get(), + y: point[1].get(), + z: point[2].get(), + r: point[3].get(), + g: point[4].get(), + b: point[5].get(), + a: point[6].get(), + } + } + } + + impl From for RPCL2Point<7> { + fn from(point: CustomPoint) -> Self { + [ + point.x.into(), + point.y.into(), + point.z.into(), + point.r.into(), + point.g.into(), + point.b.into(), + point.a.into(), + ] + .into() + } + } + + impl PointConvertible<7> for CustomPoint { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + LayoutField::new("r", "u8", 1), + LayoutField::padding(3), + LayoutField::new("g", "u8", 1), + LayoutField::padding(3), + LayoutField::new("b", "u8", 1), + LayoutField::padding(3), + LayoutField::new("a", "u8", 1), + LayoutField::padding(3), + ]) + } + } + let cloud = vec![ CustomPoint { x: 0.0, @@ -375,7 +478,6 @@ fn converterxyzrgb() { } #[test] -#[cfg(feature = "derive")] fn converterxyzrgb_from_vec() { convert_from_into_vec!( PointXYZRGB, @@ -433,7 +535,6 @@ fn write_xyzi_read_xyz() { } #[test] -#[cfg(feature = "derive")] fn write_xyzi_read_xyz_vec() { let write_cloud = vec![ PointXYZI::new(0.0, 1.0, 5.0, 0.0), @@ -453,9 +554,8 @@ fn write_xyzi_read_xyz_vec() { } #[test] -#[cfg(feature = "derive")] fn write_less_than_available() { - #[derive(Debug, PartialEq, Clone, Default, TypeLayout)] + #[derive(Debug, PartialEq, Clone, Default)] #[repr(C)] struct CustomPoint { x: f32, @@ -481,14 +581,16 @@ fn write_less_than_available() { } } - impl Fields<3> for CustomPoint { - fn field_names_ordered() -> [&'static str; 3] { - ["x", "y", "z"] + impl PointConvertible<3> for CustomPoint { + fn layout() -> LayoutDescription { + LayoutDescription::new(&[ + LayoutField::new("x", "f32", 4), + LayoutField::new("y", "f32", 4), + LayoutField::new("z", "f32", 4), + ]) } } - impl PointConvertible<3> for CustomPoint {} - let write_cloud = vec![ CustomPoint { x: 1.0, @@ -532,37 +634,4 @@ fn write_less_than_available() { ]; convert_from_into_in_out_cloud!(write_cloud, CustomPoint, read_cloud, CustomPoint); -} - -#[test] -#[cfg(feature = "derive")] -#[allow(unused_variables)] -fn readme() { - use ros_pointcloud2::prelude::*; - - // PointXYZ (and many others) are provided by the crate. - let cloud_points = vec![ - PointXYZI::new(91.486, -4.1, 42.0001, 0.1), - PointXYZI::new(f32::MAX, f32::MIN, f32::MAX, f32::MIN), - ]; - - let out_msg = PointCloud2Msg::try_from_vec(cloud_points).unwrap(); - - // Convert the ROS crate message type, we will use r2r here. - // let msg: r2r::sensor_msgs::msg::PointCloud2 = out_msg.into(); - // Publish ... - // ... now incoming from a topic. - // let in_msg: PointCloud2Msg = msg.into(); - let in_msg = out_msg; - - let processed_cloud = in_msg - .try_into_iter() - .unwrap() - .map(|point: PointXYZ| { - // Define the info you want to have from the Msg. - // Some logic here ... - - point - }) - .collect::>(); -} +} \ No newline at end of file From b5fc791776062808d5a34fa7fdce396942647afb Mon Sep 17 00:00:00 2001 From: stelzo Date: Tue, 21 May 2024 13:34:25 +0200 Subject: [PATCH 03/11] rc.3 --- Cargo.toml | 8 ++++---- README.md | 43 ++++++++++++++++--------------------------- src/iterator.rs | 13 ++++++++++--- src/lib.rs | 28 +++++++++++++++------------- src/points.rs | 2 +- src/prelude.rs | 2 +- 6 files changed, 47 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 93b323f..c056abc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ros_pointcloud2" -version = "0.5.0-rc.2" +version = "0.5.0-rc.3" edition = "2021" authors = ["Christopher Sieh "] description = "Customizable conversions for working with sensor_msgs/PointCloud2." @@ -32,10 +32,10 @@ rust-version = "1.63" [dependencies] rosrust_msg = { version = "0.1", optional = true } rosrust = { version = "0.9.11", optional = true } -r2r = { version = "0.8.4", optional = true } +r2r = { version = "0.9", optional = true } rayon = { version = "1", optional = true } -nalgebra = { version = "0.32.5", optional = true, default-features = false } -rpcl2-derive = { version = "0.2.0", optional = true } +nalgebra = { version = "0.32", optional = true, default-features = false } +rpcl2-derive = { version = "0.2", optional = true } memoffset = { version = "0.9", optional = true } sensor_msgs = { version = "*", optional = true } diff --git a/README.md b/README.md index ae64298..b94934c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ros_pointcloud2 uses its own type for the message `PointCloud2Msg` to keep the library framework agnostic. ROS1 and ROS2 are supported with feature flags. -Get started with the example below, check out the other use cases in the `examples` folder or see the [Documentation](https://docs.rs/ros_pointcloud2/0.5.0-rc.1/) for a complete guide. +Get started with the example below, check out the other use cases in the `examples` folder or see the [Documentation](https://docs.rs/ros_pointcloud2/0.5.0-rc.3/) for a complete guide. ## Quickstart @@ -57,9 +57,9 @@ You can use `rosrust` and `r2r` by enabling the respective feature: ```toml [dependencies] -ros_pointcloud2 = { version = "*", features = ["r2r_msg", "derive"]} +ros_pointcloud2 = { version = "*", features = ["r2r_msg"]} # or -ros_pointcloud2 = { version = "*", features = ["rosrust_msg", "derive"]} +ros_pointcloud2 = { version = "*", features = ["rosrust_msg"]} ``` ### rclrs (ros2_rust) @@ -68,7 +68,7 @@ Features do not work properly with `rcrls` because the messages are linked exter ```toml [dependencies] -ros_pointcloud2 = { git = "https://github.com/stelzo/ros_pointcloud2", tag = "v0.5.0-rc.1_rclrs" } +ros_pointcloud2 = { git = "https://github.com/stelzo/ros_pointcloud2", tag = "v0.5.0-rc.3_rclrs" } ``` Also, indicate the following dependencies to your linker inside the `package.xml` of your package. @@ -84,32 +84,21 @@ Please open an issue or PR if you need other integrations. ## Performance This library offers a speed up when compared to PointCloudLibrary (PCL) conversions but the specific factor depends heavily on the use case and system. -The `_vec` conversions are on average ~6x faster than PCL while the single core iteration `_iter` functions are around ~2x faster. -Parallelization with `_par_iter` gives a ~9x speed up compared to an OpenMP accelerated PCL pipeline. - -The results are measured on an Intel i7-14700 with benchmarks from [this repository](https://github.com/stelzo/ros_pcl_conv_bench). +See [this repository](https://github.com/stelzo/ros_pcl_conv_bench) for a detailed benchmark. For minimizing the conversion overhead in general, always use the functions that best fit your use case. -## `no_std` Environments +### License -The `_iter` conversions are compatible with `#[no_std]` environments if an allocator is provided. This is due to the fact that names for point fields do not have a maximum length, and PointCloud2 data vectors can have arbitrary sizes. Use `default-features = false` to disable std for this crate. Only `nalgebra` can be added as an additional feature in this case. + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + -## License +
-Licensed under either of - -- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### type-layout - -For compatibility reasons, a patched version of `type-layout` is included in this repository. The original crate can be found [here](https://crates.io/crates/type-layout). After the patch is applied on the original `type-layout` crate ([PR](https://github.com/LPGhatguy/type-layout/pull/9)), the local dependency will be changed to the original crate. - -`type-layout` is licensed under MIT or Apache-2.0 and Copyright by Lucien Greathouse. The changes are highlighted in the diff of the PR. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + diff --git a/src/iterator.rs b/src/iterator.rs index cf7cc60..a17f4a9 100644 --- a/src/iterator.rs +++ b/src/iterator.rs @@ -319,9 +319,16 @@ where return Err(MsgConversionError::DataLengthMismatch); } - let last_offset = offsets.last().expect("Dimensionality is 0."); + let last_offset = match offsets.last() { + Some(offset) => *offset, + None => return Err(MsgConversionError::DataLengthMismatch), + }; + + let last_pdata = match pdata.last() { + Some(pdata) => pdata, + None => return Err(MsgConversionError::DataLengthMismatch), + }; - let last_pdata = pdata.last().expect("Dimensionality is 0."); let size_with_last_pdata = last_offset + last_pdata.1.size(); if size_with_last_pdata > point_step_size { return Err(MsgConversionError::DataLengthMismatch); @@ -496,4 +503,4 @@ mod test { let first_right = right.next(); assert!(first_right.is_none()); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index bfee774..1b7b494 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,11 +11,11 @@ //! - [`try_into_iter`](PointCloud2Msg::try_into_iter) //! //! These feature predictable performance but they do not scale well with large clouds. Learn more about that in the [performance section](https://github.com/stelzo/ros_pointcloud2?tab=readme-ov-file#performance) of the repository. -//! The iterators are useful when your conversions are more complex than a simple copy or you the cloud is small enough. +//! The iterators are useful when your conversions are more complex than a simple copy or the cloud is small enough. //! //! When the cloud is getting larger or you are doing a lot of processing per point, switch to the parallel iterators. //! - [`try_into_par_iter`](PointCloud2Msg::try_into_par_iter) requires `rayon` feature -//! - [`try_from_par_iter`](PointCloud2Msg::try_from_par_iter) requires `rayon` + `derive` feature +//! - [`try_from_par_iter`](PointCloud2Msg::try_from_par_iter) requires `rayon` feature //! //! For ROS interoperability, there are integrations avialable with feature flags. If you miss a message type, please open an issue or a PR. //! See the [`ros`] module for more information on how to integrate more libraries. @@ -56,10 +56,10 @@ //! - r2r_msg — Integration for the ROS2 library [r2r](https://github.com/sequenceplanner/r2r). //! - rosrust_msg — Integration with the [rosrust](https://github.com/adnanademovic/rosrust) library for ROS1 message types. //! - (rclrs_msg) — Integration for ROS2 [rclrs](https://github.com/ros2-rust/ros2_rust) but it currently needs [this workaround](https://github.com/stelzo/ros_pointcloud2?tab=readme-ov-file#rclrs-ros2_rust). -//! - derive *(enabled by default)* — Enables the `_vec` functions and offers helpful custom point derive macros for the [`PointConvertible`] trait. -//! - rayon — Parallel iterator support for `_par_iter` functions. [`PointCloud2Msg::try_from_par_iter`] additionally needs the 'derive' feature. +//! - derive — Offers implementations for the [`PointConvertible`] trait needed for custom points. +//! - rayon — Parallel iterator support for `_par_iter` functions. //! - nalgebra — Predefined points offer a nalgebra typed getter for coordinates (e.g. [`xyz`](points::PointXYZ::xyz)). -//! - std *(enabled by default)* — Use the standard library. `no_std` only works standalone or with the 'nalgebra' feature. +//! - std *(enabled by default)* — Omit this feature to use this library in no_std environments. ROS integrations and 'rayon' will not work with no_std. //! //! # Custom Points //! Implement [`PointConvertible`] for your point with the `derive` feature or manually. @@ -125,10 +125,11 @@ //! ``` #![crate_type = "lib"] #![cfg_attr(docsrs, feature(doc_cfg))] -#![doc(html_root_url = "https://docs.rs/ros_pointcloud2/0.5.0-rc.2")] +#![doc(html_root_url = "https://docs.rs/ros_pointcloud2/0.5.0-rc.3")] #![warn(clippy::print_stderr)] #![warn(clippy::print_stdout)] #![warn(clippy::unwrap_used)] +#![warn(clippy::expect_used)] #![warn(clippy::cargo)] #![warn(clippy::std_instead_of_core)] #![warn(clippy::alloc_instead_of_core)] @@ -474,7 +475,7 @@ fn ordered_field_names>() -> Vec name, ty: _, size: _, - } => name.to_string(), + } => name.as_ref().into(), _ => unreachable!("Fields must be filtered before."), }) .collect() @@ -492,12 +493,13 @@ impl PointCloud2Msg { let field_names = ordered_field_names::(); debug_assert!(field_names.len() == N); - let layout = KnownLayoutInfo::try_from(C::layout())?; - debug_assert!(field_names.len() <= layout.fields.len()); + let target_layout = KnownLayoutInfo::try_from(C::layout())?; + debug_assert!(field_names.len() <= target_layout.fields.len()); + debug_assert!(self.fields.len() <= target_layout.fields.len()); let mut offset: u32 = 0; let mut field_counter = 0; - for f in layout.fields.iter() { + for f in target_layout.fields.iter() { match f { PointField::Field { datatype, @@ -913,13 +915,13 @@ pub trait PointConvertible: fn layout() -> LayoutDescription; } -#[derive(Debug)] +#[derive(Debug, Clone)] enum PointField { Padding(u32), Field { size: u32, datatype: u8, count: u32 }, } -#[derive(Debug)] +#[derive(Debug, Clone)] struct KnownLayoutInfo { fields: Vec, } @@ -1445,4 +1447,4 @@ impl FromBytes for u8 { fn from_le_bytes(bytes: PointDataBuffer) -> Self { Self::from_le_bytes([bytes[0]]) } -} \ No newline at end of file +} diff --git a/src/points.rs b/src/points.rs index 87def59..11f93c8 100644 --- a/src/points.rs +++ b/src/points.rs @@ -817,4 +817,4 @@ impl PointConvertible<6> for PointXYZNormal { LayoutField::padding(8), ]) } -} \ No newline at end of file +} diff --git a/src/prelude.rs b/src/prelude.rs index 11cf41f..b591716 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,4 +11,4 @@ pub use crate::ros::*; pub use rayon::prelude::*; #[cfg(feature = "derive")] -pub use rpcl2_derive::*; \ No newline at end of file +pub use rpcl2_derive::*; From ff5452f6b5fe4ddefbdf622945b1fdfc23e5d4f4 Mon Sep 17 00:00:00 2001 From: Christopher Sieh Date: Sat, 22 Jun 2024 11:14:06 +0200 Subject: [PATCH 04/11] merge main --- src/lib.rs | 44 ++++++++++++++++++++++++++++---------------- src/points.rs | 20 ++++++++++---------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1b7b494..d58e259 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,7 @@ //! ## Derive (recommended) //! ```ignore //! #[derive(Clone, Debug, PartialEq, Copy, Default, PointConvertible)] +//! #[repr(C, align(4))] //! pub struct MyPointXYZI { //! pub x: f32, //! pub y: f32, @@ -81,6 +82,7 @@ //! use ros_pointcloud2::prelude::*; //! //! #[derive(Clone, Debug, PartialEq, Copy, Default)] +//! #[repr(C, align(4))] //! pub struct MyPointXYZI { //! pub x: f32, //! pub y: f32, @@ -106,7 +108,7 @@ //! } //! } //! -//! impl PointConvertible<4> for MyPointXYZI { +//! unsafe impl PointConvertible<4> for MyPointXYZI { //! fn layout() -> LayoutDescription { //! LayoutDescription::new(&[ //! LayoutField::new("x", "f32", 4), @@ -169,6 +171,7 @@ pub enum MsgConversionError { FieldsNotFound(Vec), UnsupportedFieldCount, NumberConversion, + ExhaustedSource, } impl From for MsgConversionError { @@ -212,10 +215,17 @@ impl core::fmt::Display for MsgConversionError { MsgConversionError::NumberConversion => { write!(f, "The number is too large to be converted into a PointCloud2 supported datatype.") } + MsgConversionError::ExhaustedSource => { + write!( + f, + "The conversion requests more data from the source type than is available." + ) + } } } } +#[allow(clippy::std_instead_of_core)] // will be stable soon (https://github.com/rust-lang/rust/issues/103765) #[cfg(feature = "std")] impl std::error::Error for MsgConversionError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { @@ -487,13 +497,9 @@ impl PointCloud2Msg { where C: PointConvertible, { - let point: RPCL2Point = C::default().into(); - debug_assert!(point.fields.len() == N); - let field_names = ordered_field_names::(); - debug_assert!(field_names.len() == N); - let target_layout = KnownLayoutInfo::try_from(C::layout())?; + debug_assert!(field_names.len() <= target_layout.fields.len()); debug_assert!(self.fields.len() <= target_layout.fields.len()); @@ -506,8 +512,12 @@ impl PointCloud2Msg { size, count, } => { - let msg_f = &self.fields[field_counter]; - let f_translated = &field_names[field_counter]; + if field_counter >= self.fields.len() || field_counter >= field_names.len() { + return Err(MsgConversionError::ExhaustedSource); + } + + let msg_f = unsafe { self.fields.get_unchecked(field_counter) }; + let f_translated = unsafe { field_names.get_unchecked(field_counter) }; field_counter += 1; if msg_f.name != *f_translated @@ -533,7 +543,7 @@ impl PointCloud2Msg { }) } - /// Create a [`PointCloud2Msg`] from any iterable type. + /// Create a [`PointCloud2Msg`] from any iterable type that implements [`PointConvertible`]. /// /// # Example /// ``` @@ -687,7 +697,7 @@ impl PointCloud2Msg { let bytes_total = vec.len() * point_step as usize; cloud.data.resize(bytes_total, u8::default()); - let raw_data: *mut C = cloud.data.as_ptr() as *mut C; + let raw_data: *mut C = cloud.data.as_mut_ptr() as *mut C; unsafe { core::ptr::copy_nonoverlapping( vec.as_ptr().cast::(), @@ -897,7 +907,7 @@ impl From<[PointData; N]> for RPCL2Point { /// } /// } /// -/// impl PointConvertible<4> for MyPointXYZL { +/// unsafe impl PointConvertible<4> for MyPointXYZL { /// fn layout() -> LayoutDescription { /// LayoutDescription::new(&[ /// LayoutField::new("x", "f32", 4), @@ -909,7 +919,10 @@ impl From<[PointData; N]> for RPCL2Point { /// } /// } /// ``` -pub trait PointConvertible: +/// # Safety +/// The layout is used for raw memory interpretation, where safety can not be guaranteed by the compiler. +/// Take care when implementing the layout, especially in combination with `#[repr]` or use the `derive` feature when possible to prevent common errors. +pub unsafe trait PointConvertible: From> + Into> + Default + Sized { fn layout() -> LayoutDescription; @@ -1006,11 +1019,10 @@ impl PointData { #[inline] fn from_buffer(data: &[u8], offset: usize, datatype: FieldDatatype, endian: Endian) -> Self { debug_assert!(data.len() >= offset + datatype.size()); - let bytes = [u8::default(); core::mem::size_of::()]; + let mut bytes = [u8::default(); core::mem::size_of::()]; unsafe { let data_ptr = data.as_ptr().add(offset); - let bytes_ptr = bytes.as_ptr() as *mut u8; - core::ptr::copy_nonoverlapping(data_ptr, bytes_ptr, datatype.size()); + core::ptr::copy_nonoverlapping(data_ptr, bytes.as_mut_ptr(), datatype.size()); } Self { @@ -1447,4 +1459,4 @@ impl FromBytes for u8 { fn from_le_bytes(bytes: PointDataBuffer) -> Self { Self::from_le_bytes([bytes[0]]) } -} +} \ No newline at end of file diff --git a/src/points.rs b/src/points.rs index 11f93c8..d094c44 100644 --- a/src/points.rs +++ b/src/points.rs @@ -160,7 +160,7 @@ impl From for RPCL2Point<3> { } } -impl PointConvertible<3> for PointXYZ { +unsafe impl PointConvertible<3> for PointXYZ { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -221,7 +221,7 @@ impl From for RPCL2Point<4> { } } -impl PointConvertible<4> for PointXYZI { +unsafe impl PointConvertible<4> for PointXYZI { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -282,7 +282,7 @@ impl From for RPCL2Point<4> { } } -impl PointConvertible<4> for PointXYZL { +unsafe impl PointConvertible<4> for PointXYZL { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -360,7 +360,7 @@ impl From for RPCL2Point<4> { } } -impl PointConvertible<4> for PointXYZRGB { +unsafe impl PointConvertible<4> for PointXYZRGB { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -442,7 +442,7 @@ impl From for RPCL2Point<5> { } } -impl PointConvertible<5> for PointXYZRGBA { +unsafe impl PointConvertible<5> for PointXYZRGBA { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -546,7 +546,7 @@ impl From for RPCL2Point<7> { } } -impl PointConvertible<7> for PointXYZRGBNormal { +unsafe impl PointConvertible<7> for PointXYZRGBNormal { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -637,7 +637,7 @@ impl From for RPCL2Point<7> { } } -impl PointConvertible<7> for PointXYZINormal { +unsafe impl PointConvertible<7> for PointXYZINormal { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -728,7 +728,7 @@ impl From for RPCL2Point<5> { } } -impl PointConvertible<5> for PointXYZRGBL { +unsafe impl PointConvertible<5> for PointXYZRGBL { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -805,7 +805,7 @@ impl From for RPCL2Point<6> { } } -impl PointConvertible<6> for PointXYZNormal { +unsafe impl PointConvertible<6> for PointXYZNormal { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -817,4 +817,4 @@ impl PointConvertible<6> for PointXYZNormal { LayoutField::padding(8), ]) } -} +} \ No newline at end of file From 7d0d7d53a032beaa8fc0ac09e05f516f81ecf283 Mon Sep 17 00:00:00 2001 From: stelzo Date: Sun, 23 Jun 2024 19:05:41 +0200 Subject: [PATCH 05/11] rclrs jazzy tests --- .github/workflows/rclrs_jazzy.yml | 21 +++++++++++++++++++ tests/Dockerfile_rclrs_jazzy | 34 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 .github/workflows/rclrs_jazzy.yml create mode 100644 tests/Dockerfile_rclrs_jazzy diff --git a/.github/workflows/rclrs_jazzy.yml b/.github/workflows/rclrs_jazzy.yml new file mode 100644 index 0000000..ff2082e --- /dev/null +++ b/.github/workflows/rclrs_jazzy.yml @@ -0,0 +1,21 @@ +name: rclrs_jazzy + +on: + push: + branches: + - rclrs + pull_request: + branches: + - rclrs + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + tests_jazzy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: docker build . --file ./rpcl2/tests/Dockerfile_rclrs_jazzy --tag rclrs_jazzy + - run: docker run rclrs_jazzy cargo test --features derive,nalgebra,rayon \ No newline at end of file diff --git a/tests/Dockerfile_rclrs_jazzy b/tests/Dockerfile_rclrs_jazzy new file mode 100644 index 0000000..e8ed49b --- /dev/null +++ b/tests/Dockerfile_rclrs_jazzy @@ -0,0 +1,34 @@ +# syntax=docker/dockerfile:1 +FROM ros:jazzy + +# Update default packages +RUN apt-get update + +# Get Ubuntu packages +RUN apt-get install -y \ + build-essential \ + curl \ + libclang-dev \ + git \ + python3-pip \ + python3-vcstool + +# Get Rust +RUN curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | bash -s -- -y +RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc +RUN . $HOME/.cargo/env && cargo install --debug cargo-ament-build +RUN pip install git+https://github.com/colcon/colcon-cargo.git +RUN pip install git+https://github.com/colcon/colcon-ros-cargo.git + +WORKDIR /ros2_rust_build +RUN git clone https://github.com/ros2-rust/ros2_rust.git src/ros2_rust +RUN vcs import src < src/ros2_rust/ros2_rust_jazzy.repos + +WORKDIR /ros2_rust_build/src/ros_pointcloud2_tests +COPY . . + +WORKDIR /ros2_rust_build +RUN . $HOME/.cargo/env && . /opt/ros/jazzy/setup.sh && colcon build + +RUN chmod +x /ros2_rust_build/src/ros_pointcloud2_tests/rpcl2/tests/rclrs_test.bash +ENTRYPOINT [ "/ros2_rust_build/src/ros_pointcloud2_tests/rpcl2/tests/rclrs_test.bash" ] \ No newline at end of file From 4b9a4ddae3649000b847911773d4719a4acf7da3 Mon Sep 17 00:00:00 2001 From: stelzo Date: Sun, 23 Jun 2024 19:10:15 +0200 Subject: [PATCH 06/11] fix subfolder --- .github/workflows/rclrs_jazzy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rclrs_jazzy.yml b/.github/workflows/rclrs_jazzy.yml index ff2082e..fc1f5ae 100644 --- a/.github/workflows/rclrs_jazzy.yml +++ b/.github/workflows/rclrs_jazzy.yml @@ -17,5 +17,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - run: docker build . --file ./rpcl2/tests/Dockerfile_rclrs_jazzy --tag rclrs_jazzy + - run: docker build . --file ./tests/Dockerfile_rclrs_jazzy --tag rclrs_jazzy - run: docker run rclrs_jazzy cargo test --features derive,nalgebra,rayon \ No newline at end of file From d72a4491d4017d0dd5ed7df64eff43e4caff0d4a Mon Sep 17 00:00:00 2001 From: stelzo Date: Sun, 23 Jun 2024 19:20:29 +0200 Subject: [PATCH 07/11] unsafe impl in tests --- tests/Dockerfile_rclrs_jazzy | 5 +++-- tests/e2e_test.rs | 30 ++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/tests/Dockerfile_rclrs_jazzy b/tests/Dockerfile_rclrs_jazzy index e8ed49b..a8263c1 100644 --- a/tests/Dockerfile_rclrs_jazzy +++ b/tests/Dockerfile_rclrs_jazzy @@ -10,6 +10,7 @@ RUN apt-get install -y \ curl \ libclang-dev \ git \ + python3 \ python3-pip \ python3-vcstool @@ -17,8 +18,8 @@ RUN apt-get install -y \ RUN curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | bash -s -- -y RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc RUN . $HOME/.cargo/env && cargo install --debug cargo-ament-build -RUN pip install git+https://github.com/colcon/colcon-cargo.git -RUN pip install git+https://github.com/colcon/colcon-ros-cargo.git +RUN python3 -m pip install git+https://github.com/colcon/colcon-cargo.git +RUN python3 -m pip install git+https://github.com/colcon/colcon-ros-cargo.git WORKDIR /ros2_rust_build RUN git clone https://github.com/ros2-rust/ros2_rust.git src/ros2_rust diff --git a/tests/e2e_test.rs b/tests/e2e_test.rs index 81d2c9b..3c31017 100644 --- a/tests/e2e_test.rs +++ b/tests/e2e_test.rs @@ -1,3 +1,4 @@ +use pretty_assertions::assert_eq; use ros_pointcloud2::prelude::*; #[cfg(feature = "derive")] @@ -55,6 +56,19 @@ fn write_cloud() { let msg = PointCloud2Msg::try_from_iter(cloud); assert!(msg.is_ok()); } +/* +#[test] +fn collect_vec() { + let cloud = vec![ + PointXYZ::new(0.0, 1.0, 5.0), + PointXYZ::new(1.0, 1.5, 5.0), + PointXYZ::new(1.3, 1.6, 5.7), + PointXYZ::new(f32::MAX, f32::MIN, f32::MAX), + ] + .into_iter(); + + let msg: Result = cloud.collect(); +}*/ #[test] fn write_cloud_from_vec() { @@ -130,7 +144,7 @@ fn conv_cloud_par_par_iter() { #[cfg(feature = "derive")] fn custom_xyz_f32() { #[derive(Debug, PartialEq, Clone, Default)] - #[repr(C)] + #[repr(C, align(4))] struct CustomPoint { x: f32, y: f32, @@ -153,7 +167,7 @@ fn custom_xyz_f32() { } } - impl PointConvertible<3> for CustomPoint { + unsafe impl PointConvertible<3> for CustomPoint { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -215,7 +229,7 @@ fn custom_xyzi_f32() { ]; #[derive(Debug, PartialEq, Clone, Default)] - #[repr(C)] + #[repr(C, align(4))] struct CustomPointXYZI { x: f32, y: f32, @@ -246,7 +260,7 @@ fn custom_xyzi_f32() { } } - impl PointConvertible<4> for CustomPointXYZI { + unsafe impl PointConvertible<4> for CustomPointXYZI { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -265,7 +279,7 @@ fn custom_xyzi_f32() { #[cfg(feature = "derive")] fn custom_rgba_f32() { #[derive(Debug, PartialEq, Clone, Default)] - #[repr(C)] + #[repr(C, align(4))] struct CustomPoint { x: f32, y: f32, @@ -305,7 +319,7 @@ fn custom_rgba_f32() { } } - impl PointConvertible<7> for CustomPoint { + unsafe impl PointConvertible<7> for CustomPoint { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), @@ -556,7 +570,7 @@ fn write_xyzi_read_xyz_vec() { #[test] fn write_less_than_available() { #[derive(Debug, PartialEq, Clone, Default)] - #[repr(C)] + #[repr(C, align(4))] struct CustomPoint { x: f32, y: f32, @@ -581,7 +595,7 @@ fn write_less_than_available() { } } - impl PointConvertible<3> for CustomPoint { + unsafe impl PointConvertible<3> for CustomPoint { fn layout() -> LayoutDescription { LayoutDescription::new(&[ LayoutField::new("x", "f32", 4), From e9591cb70eea6af245f32d36ce70718a1e9f9eec Mon Sep 17 00:00:00 2001 From: stelzo Date: Sun, 23 Jun 2024 19:25:00 +0200 Subject: [PATCH 08/11] override pip sys deps for colcon --- tests/Dockerfile_rclrs_jazzy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Dockerfile_rclrs_jazzy b/tests/Dockerfile_rclrs_jazzy index a8263c1..649de22 100644 --- a/tests/Dockerfile_rclrs_jazzy +++ b/tests/Dockerfile_rclrs_jazzy @@ -18,8 +18,8 @@ RUN apt-get install -y \ RUN curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | bash -s -- -y RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc RUN . $HOME/.cargo/env && cargo install --debug cargo-ament-build -RUN python3 -m pip install git+https://github.com/colcon/colcon-cargo.git -RUN python3 -m pip install git+https://github.com/colcon/colcon-ros-cargo.git +RUN pip install --break-system-packages git+https://github.com/colcon/colcon-cargo.git +RUN pip install --break-system-packages git+https://github.com/colcon/colcon-ros-cargo.git WORKDIR /ros2_rust_build RUN git clone https://github.com/ros2-rust/ros2_rust.git src/ros2_rust From a2076137c60ce4e0696a182a938de2dcf1c43e3f Mon Sep 17 00:00:00 2001 From: stelzo Date: Sun, 23 Jun 2024 19:34:32 +0200 Subject: [PATCH 09/11] missing dep --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index c056abc..bf253d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ builtin_interfaces = { version = "*", optional = true } [dev-dependencies] rand = "0.8" criterion = { version = "0.5", features = ["html_reports"] } +pretty_assertions = "1.0" [features] rclrs_msg = ["dep:sensor_msgs", "dep:std_msgs", "dep:builtin_interfaces"] From 10eb3230742390c8863f2e30791293190f7550f2 Mon Sep 17 00:00:00 2001 From: stelzo Date: Sun, 23 Jun 2024 19:45:58 +0200 Subject: [PATCH 10/11] bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bf253d3..28365fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ros_pointcloud2" -version = "0.5.0-rc.3" +version = "0.5.0" edition = "2021" authors = ["Christopher Sieh "] description = "Customizable conversions for working with sensor_msgs/PointCloud2." From 43f3d4e42be639ef23e2a5d4c7474ac9daa606e3 Mon Sep 17 00:00:00 2001 From: stelzo Date: Sun, 23 Jun 2024 19:51:09 +0200 Subject: [PATCH 11/11] bump version --- README.md | 4 ++-- src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b94934c..98e8ccd 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ros_pointcloud2 uses its own type for the message `PointCloud2Msg` to keep the library framework agnostic. ROS1 and ROS2 are supported with feature flags. -Get started with the example below, check out the other use cases in the `examples` folder or see the [Documentation](https://docs.rs/ros_pointcloud2/0.5.0-rc.3/) for a complete guide. +Get started with the example below, check out the other use cases in the `examples` folder or see the [Documentation](https://docs.rs/ros_pointcloud2/0.5.0/) for a complete guide. ## Quickstart @@ -68,7 +68,7 @@ Features do not work properly with `rcrls` because the messages are linked exter ```toml [dependencies] -ros_pointcloud2 = { git = "https://github.com/stelzo/ros_pointcloud2", tag = "v0.5.0-rc.3_rclrs" } +ros_pointcloud2 = { git = "https://github.com/stelzo/ros_pointcloud2", tag = "v0.5.0_rclrs" } ``` Also, indicate the following dependencies to your linker inside the `package.xml` of your package. diff --git a/src/lib.rs b/src/lib.rs index d58e259..6ac25cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,7 +127,7 @@ //! ``` #![crate_type = "lib"] #![cfg_attr(docsrs, feature(doc_cfg))] -#![doc(html_root_url = "https://docs.rs/ros_pointcloud2/0.5.0-rc.3")] +#![doc(html_root_url = "https://docs.rs/ros_pointcloud2/0.5.0")] #![warn(clippy::print_stderr)] #![warn(clippy::print_stdout)] #![warn(clippy::unwrap_used)]