use pretty_assertions::assert_eq; use ros_pointcloud2::prelude::*; #[cfg(feature = "derive")] use std::fmt::Debug; macro_rules! convert_from_into { ($point:ty, $cloud:expr) => { convert_from_into_in_out_cloud!($cloud, $point, $cloud, $point); }; } macro_rules! convert_from_into_vec { ($point:ty, $cloud:expr) => { convert_from_into_in_out_cloud_vec!($cloud, $point, $cloud, $point); }; } macro_rules! convert_from_into_in_out_cloud { ($in_cloud:expr, $in_point:ty, $out_cloud:expr, $out_point:ty) => { let msg = PointCloud2Msg::try_from_iter($in_cloud.clone().into_iter()); assert!(msg.is_ok(), "{:?}", msg); let msg = msg.unwrap(); let to_p_type = msg.try_into_iter(); assert!(to_p_type.is_ok()); let to_p_type = to_p_type.unwrap(); let back_to_type = to_p_type.collect::>(); let orig_cloud: Vec<$out_point> = $out_cloud.iter().cloned().collect(); assert_eq!(orig_cloud, back_to_type); }; } 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()); assert!(msg.is_ok(), "{:?}", msg); let msg = msg.unwrap(); let to_p_type = msg.try_into_iter(); assert!(to_p_type.is_ok()); let to_p_type = to_p_type.unwrap(); let back_to_type = to_p_type.collect::>(); let orig_cloud: Vec<$out_point> = $out_cloud.iter().cloned().collect(); assert_eq!(orig_cloud, back_to_type); }; } #[test] fn write_cloud() { 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), ]; 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() { 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), ]; let msg = PointCloud2Msg::try_from_vec(cloud); assert!(msg.is_ok()); } #[test] fn write_empty_cloud_vec() { let cloud: Vec = vec![]; let msg = PointCloud2Msg::try_from_vec(cloud); assert!(msg.is_ok()); assert!(msg.unwrap().data.is_empty()); } #[test] fn write_empty_cloud_iter() { let cloud: Vec = vec![]; let msg = PointCloud2Msg::try_from_iter(cloud); assert!(msg.is_ok()); assert!(msg.unwrap().data.is_empty()); } #[test] #[cfg(feature = "rayon")] fn conv_cloud_par_iter() { 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), ]; let copy = cloud.clone(); let msg: Result = PointCloud2Msg::try_from_vec(cloud); assert!(msg.is_ok()); let msg = msg.unwrap(); let to_p_type = msg.try_into_par_iter(); assert!(to_p_type.is_ok()); let to_p_type = to_p_type.unwrap(); let back_to_type = to_p_type.collect::>(); assert_eq!(copy, back_to_type); } #[test] #[cfg(feature = "rayon")] fn conv_cloud_par_par_iter() { 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), ]; let copy = cloud.clone(); let msg = PointCloud2Msg::try_from_par_iter(cloud.into_par_iter()); assert!(msg.is_ok()); let msg = msg.unwrap(); let to_p_type = msg.try_into_par_iter(); assert!(to_p_type.is_ok()); let to_p_type = to_p_type.unwrap(); let back_to_type = to_p_type.collect::>(); assert_eq!(copy, back_to_type); } #[test] #[cfg(feature = "derive")] fn custom_xyz_f32() { #[derive(Debug, PartialEq, Clone, Default)] #[repr(C, align(4))] struct CustomPoint { x: f32, y: 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() } } unsafe 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![ CustomPoint { x: 1.0, y: 2.0, z: 3.0, }, CustomPoint { x: 4.0, y: 5.0, z: 6.0, }, CustomPoint { x: 7.0, y: 8.0, z: 9.0, }, ] ); } #[test] fn custom_xyzi_f32() { let cloud: Vec = vec![ CustomPointXYZI { x: 0.0, y: 1.0, z: 5.0, i: 0, }, CustomPointXYZI { x: 1.0, y: 1.5, z: 5.0, i: 1, }, CustomPointXYZI { x: 1.3, y: 1.6, z: 5.7, i: 2, }, CustomPointXYZI { x: f32::MAX, y: f32::MIN, z: f32::MAX, i: u8::MAX, }, ]; #[derive(Debug, PartialEq, Clone, Default)] #[repr(C, align(4))] struct CustomPointXYZI { x: f32, y: f32, z: 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() } } unsafe 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)] #[repr(C, align(4))] struct CustomPoint { x: f32, y: f32, z: f32, r: u8, g: u8, b: u8, 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() } } unsafe 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, y: 1.0, z: 5.0, r: 0, g: 0, b: 0, a: 0, }, CustomPoint { x: 1.0, y: 1.5, z: 5.0, r: 1, g: 1, b: 1, a: 1, }, CustomPoint { x: 1.3, y: 1.6, z: 5.7, r: 2, g: 2, b: 2, a: 2, }, CustomPoint { x: f32::MAX, y: f32::MIN, z: f32::MAX, r: u8::MAX, g: u8::MAX, b: u8::MAX, a: u8::MAX, }, ]; convert_from_into!(CustomPoint, cloud); } #[test] fn converterxyz() { 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), ]; convert_from_into!(PointXYZ, cloud); } #[test] fn converterxyzrgba() { convert_from_into!( PointXYZRGBA, vec![ PointXYZRGBA::new(0.0, 1.0, 5.0, 0, 0, 0, 0), PointXYZRGBA::new(1.0, 1.5, 5.0, 1, 1, 1, 1), PointXYZRGBA::new(1.3, 1.6, 5.7, 2, 2, 2, 2), PointXYZRGBA::new( f32::MAX, f32::MIN, f32::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX ), ] ); } #[test] fn converterxyzinormal() { convert_from_into!( PointXYZINormal, vec![ PointXYZINormal::new(0.0, 1.0, 5.0, 0.0, 0.0, 0.0, 0.0), PointXYZINormal::new(1.0, 1.5, 5.0, 1.0, 1.0, 1.0, 1.0), PointXYZINormal::new(1.3, 1.6, 5.7, 2.0, 2.0, 2.0, 2.0), ] ); } #[test] fn converterxyzrgbnormal() { convert_from_into!( PointXYZRGBNormal, vec![ PointXYZRGBNormal::new(0.0, 1.0, 5.0, RGB::new(0, 0, 0), 0.0, 0.0, 0.0), PointXYZRGBNormal::new(1.0, 1.5, 5.0, RGB::new(1, 1, 1), 1.0, 1.0, 1.0), PointXYZRGBNormal::new(1.3, 1.6, 5.7, RGB::new(2, 2, 2), 2.0, 2.0, 2.0), PointXYZRGBNormal::new( f32::MAX, f32::MIN, f32::MAX, RGB::new(u8::MAX, u8::MAX, u8::MAX), f32::MAX, f32::MAX, f32::MAX, ), ] ); } #[test] fn converterxyznormal() { convert_from_into!( PointXYZNormal, vec![ PointXYZNormal::new(0.0, 1.0, 5.0, 0.0, 0.0, 0.0), PointXYZNormal::new(1.0, 1.5, 5.0, 1.0, 1.0, 1.0), PointXYZNormal::new(1.3, 1.6, 5.7, 2.0, 2.0, 2.0), PointXYZNormal::new(f32::MAX, f32::MIN, f32::MAX, f32::MAX, f32::MAX, f32::MAX), ] ); } #[test] fn converterxyzrgbl() { convert_from_into!( PointXYZRGBL, vec![ PointXYZRGBL::new(0.0, 1.0, 5.0, 0, 0, 0, 0), PointXYZRGBL::new(1.0, 1.5, 5.0, 1, 1, 1, 1), PointXYZRGBL::new(1.3, 1.6, 5.7, 2, 2, 2, 2), PointXYZRGBL::new( f32::MAX, f32::MIN, f32::MAX, u8::MAX, u8::MAX, u8::MAX, u32::MAX ), ] ); } #[test] fn converterxyzrgb() { convert_from_into!( PointXYZRGB, vec![ PointXYZRGB::new(0.0, 1.0, 5.0, 0, 0, 0), PointXYZRGB::new(1.0, 1.5, 5.0, 1, 1, 1), PointXYZRGB::new(1.3, 1.6, 5.7, 2, 2, 2), PointXYZRGB::new(f32::MAX, f32::MIN, f32::MAX, u8::MAX, u8::MAX, u8::MAX), ] ); } #[test] fn converterxyzrgb_from_vec() { convert_from_into_vec!( PointXYZRGB, vec![ PointXYZRGB::new(0.0, 1.0, 5.0, 0, 0, 0), PointXYZRGB::new(1.3, 1.6, 5.7, 2, 2, 2), PointXYZRGB::new(f32::MAX, f32::MIN, f32::MAX, u8::MAX, u8::MAX, u8::MAX), ] ); } #[test] fn converterxyzl() { convert_from_into!( PointXYZL, vec![ PointXYZL::new(0.0, 1.0, 5.0, 0), PointXYZL::new(1.0, 1.5, 5.0, 1), PointXYZL::new(1.3, 1.6, 5.7, 2), PointXYZL::new(f32::MAX, f32::MIN, f32::MAX, u32::MAX), ] ); } #[test] fn converterxyzi() { convert_from_into!( PointXYZI, vec![ PointXYZI::new(0.0, 1.0, 5.0, 0.0), PointXYZI::new(1.0, 1.5, 5.0, 1.0), PointXYZI::new(1.3, 1.6, 5.7, 2.0), PointXYZI::new(f32::MAX, f32::MIN, f32::MAX, f32::MAX), ] ); } #[test] fn write_xyzi_read_xyz() { let write_cloud = vec![ PointXYZI::new(0.0, 1.0, 5.0, 0.0), PointXYZI::new(1.0, 1.5, 5.0, 1.0), PointXYZI::new(1.3, 1.6, 5.7, 2.0), PointXYZI::new(f32::MAX, f32::MIN, f32::MAX, f32::MAX), ]; let read_cloud = [ 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), ]; convert_from_into_in_out_cloud!(write_cloud, PointXYZI, read_cloud, PointXYZ); } #[test] fn write_xyzi_read_xyz_vec() { let write_cloud = vec![ PointXYZI::new(0.0, 1.0, 5.0, 0.0), PointXYZI::new(1.0, 1.5, 5.0, 1.0), PointXYZI::new(1.3, 1.6, 5.7, 2.0), PointXYZI::new(f32::MAX, f32::MIN, f32::MAX, f32::MAX), ]; let read_cloud = [ 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), ]; convert_from_into_in_out_cloud_vec!(write_cloud, PointXYZI, read_cloud, PointXYZ); } #[test] fn write_less_than_available() { #[derive(Debug, PartialEq, Clone, Default)] #[repr(C, align(4))] struct CustomPoint { x: f32, y: f32, z: f32, dummy: f32, } impl From> for CustomPoint { fn from(point: RPCL2Point<3>) -> Self { Self { x: point[0].get(), y: point[1].get(), z: point[2].get(), dummy: 0.0, } } } impl From for RPCL2Point<3> { fn from(point: CustomPoint) -> Self { [point.x.into(), point.y.into(), point.z.into()].into() } } unsafe 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), ]) } } let write_cloud = vec![ CustomPoint { x: 1.0, y: 2.0, z: 3.0, dummy: -10.0, }, CustomPoint { x: 4.0, y: 5.0, z: 6.0, dummy: -10.0, }, CustomPoint { x: 7.0, y: 8.0, z: 9.0, dummy: -10.0, }, ]; let read_cloud = [ CustomPoint { x: 1.0, y: 2.0, z: 3.0, dummy: 0.0, }, CustomPoint { x: 4.0, y: 5.0, z: 6.0, dummy: 0.0, }, CustomPoint { x: 7.0, y: 8.0, z: 9.0, dummy: 0.0, }, ]; convert_from_into_in_out_cloud!(write_cloud, CustomPoint, read_cloud, CustomPoint); }