use nalgebra::{ComplexField, Point, Scalar};
use num_traits::{AsPrimitive, NumAssign};
use crate::{array, HashMap, Vec};
#[cfg_attr(
feature = "tracing",
tracing::instrument("Downsample Point Cloud Using Voxels", skip_all)
)]
pub fn downsample_point_cloud_voxel<T, O, const N: usize>(
points: &[Point<T, N>],
voxel_size: O,
) -> Vec<Point<T, N>>
where
O: AsPrimitive<isize> + ComplexField + Copy,
T: AsPrimitive<O> + Scalar + NumAssign,
usize: AsPrimitive<T>,
{
let mut voxel_map: HashMap<[isize; N], Vec<Point<T, N>>> = HashMap::new();
for point in points {
let voxel_coords: [isize; N] = array::from_fn(|idx| {
(AsPrimitive::<O>::as_(point[idx]) / voxel_size)
.floor()
.as_()
});
voxel_map.entry(voxel_coords).or_default().push(*point);
}
voxel_map
.into_values()
.map(|points_in_voxel| {
let num_points = points_in_voxel.len().as_();
let sum = points_in_voxel
.into_iter()
.fold(Point::default(), |acc, p| acc + p.coords);
sum / num_points
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use nalgebra::Point3;
#[test]
fn test_downsample_point_cloud() {
let point_cloud = [
Point3::new(-5.9, -5.0, -3.9), Point3::new(-6.0, -5.0, -4.0), Point3::new(-1.0, -2.0, -3.0),
Point3::new(0.0, 0.0, 0.0), Point3::new(0.05, 0.08, 0.01), Point3::new(1.0, 2.0, 3.0),
Point3::new(6.0, 5.0, 4.0),
];
let res = downsample_point_cloud_voxel(point_cloud.as_slice(), 0.5);
assert_eq!(res.len(), 5);
assert!(res
.iter()
.any(|element| *element == Point3::new(-5.95, -5.0, -3.95)));
}
}