mapping_algorithms/point_clouds/mod.rs
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright (c) [2023 - Present] Emily Matheys <emilymatt96@gmail.com>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24pub use downsample::downsample_point_cloud_voxel;
25pub use icp::{
26 icp, icp_iteration, ICPConfiguration, ICPConfigurationBuilder, ICPError, ICPResult, ICPSuccess,
27};
28pub use lex_sort::{lex_sort, lex_sort_in_place, lex_sort_ref};
29pub use nearest_neighbour::find_nearest_neighbour_naive;
30
31use nalgebra::{
32 AbstractRotation, ClosedAddAssign, ClosedDivAssign, Isometry, Point, RealField, Scalar,
33};
34use num_traits::{AsPrimitive, Zero};
35
36use crate::{array, Vec};
37
38mod downsample;
39mod icp;
40mod lex_sort;
41mod nearest_neighbour;
42
43#[cfg(feature = "pregenerated")]
44#[doc = "Contains pregenerated functions for single precision point cloud mapping-algorithms."]
45pub mod single_precision {
46 pub use super::icp::single_precision::*;
47}
48
49#[cfg(feature = "pregenerated")]
50#[doc = "Contains pregenerated functions for double precision point cloud mapping-algorithms."]
51pub mod double_precision {
52 pub use super::icp::double_precision::*;
53}
54
55/// Calculates the mean(centroid) of the point cloud.
56///
57/// # Arguments
58/// * points: a slice of [`Point`], representing the point cloud.
59///
60/// # Generics
61/// * `T`: Either an [`f32`] or [`f64`].
62/// * `N`: A const usize, representing the number of dimensions in the points.
63///
64/// # Returns
65/// A [`Point`], representing the point cloud centroid.
66/// Returns Point::default() if point cloud is empty.
67#[inline]
68#[cfg_attr(
69 feature = "tracing",
70 tracing::instrument("Calculate Mean Point", skip_all)
71)]
72pub fn calculate_point_cloud_center<T, const N: usize>(points: &[Point<T, N>]) -> Point<T, N>
73where
74 T: ClosedAddAssign + ClosedDivAssign + Copy + Scalar + Zero,
75 usize: AsPrimitive<T>,
76{
77 if points.is_empty() {
78 return Point::default();
79 }
80
81 points
82 .iter()
83 .fold(Point::<T, N>::from([T::zero(); N]), |acc, it| {
84 Point::from(acc.coords + it.coords)
85 })
86 / points.len().as_()
87}
88
89/// Generates a randomized points cloud within a specified spherical range.
90///
91/// # Arguments
92/// * `num_points`: a [`usize`], specifying the amount of points to generate
93/// * `range`: a [`crate::ops::RangeInclusive`] specifying the normal distribution of points.
94///
95/// # Generics
96/// * `T`: Either an [`f32`] or [`f64`].
97/// * `N`: A const usize, representing the number of dimensions to use.
98///
99/// # Returns
100/// A [`Vec`] of [`Point`] representing the point cloud.
101#[cfg_attr(
102 feature = "tracing",
103 tracing::instrument("Generate Randomized Point Cloud", skip_all, level = "debug")
104)]
105pub fn generate_point_cloud<T, const N: usize>(
106 num_points: usize,
107 ranges: [crate::ops::RangeInclusive<T>; N],
108) -> Vec<Point<T, N>>
109where
110 T: PartialOrd + rand::distributions::uniform::SampleUniform + Scalar,
111{
112 use rand::{Rng, SeedableRng};
113 let mut rng = rand::rngs::SmallRng::seed_from_u64(3765665954583626552);
114
115 (0..num_points)
116 .map(|_| Point::from(array::from_fn(|idx| rng.gen_range(ranges[idx].clone()))))
117 .collect()
118} // Just calls a different function a number of times, no specific test needed
119
120/// Transform a point cloud, returning a transformed copy.
121/// This function does not mutate the original point cloud.
122///
123/// # Arguments
124/// * `source_points`: a slice of [`Point`], representing the point cloud
125/// * `isometry_matrix`: a transform that implements [`AbstractRotation`], to use for the transformation.
126///
127/// # Generics
128/// * `T`: Either an [`f32`] or [`f64`].
129/// * `N`: A const usize, either `2` or `3`.
130/// * `R`: An [`AbstractRotation`] for `T` and `N`.
131///
132/// # Returns
133/// A [`Vec`] of [`Point`] containing the transformed point cloud.
134#[inline]
135#[cfg_attr(
136 feature = "tracing",
137 tracing::instrument("Transform Point Cloud", skip_all)
138)]
139pub fn transform_point_cloud<T, const N: usize, R>(
140 source_points: &[Point<T, N>],
141 isometry_matrix: Isometry<T, R, N>,
142) -> Vec<Point<T, N>>
143where
144 T: RealField,
145 R: AbstractRotation<T, N>,
146{
147 source_points
148 .iter()
149 .map(|point| isometry_matrix.transform_point(point))
150 .collect()
151} // Just calls a different function a number of times, no specific test needed
152
153#[cfg(test)]
154mod tests {
155 use nalgebra::{Point2, Point3};
156
157 use super::*;
158
159 #[test]
160 fn test_empty_point_cloud_center() {
161 assert_eq!(calculate_point_cloud_center(&[]), Point2::new(0.0, 0.0));
162 }
163
164 #[test]
165 fn test_calculate_point_cloud_center() {
166 let point_cloud = [
167 Point3::new(1.0, 2.0, 3.0),
168 Point3::new(2.0, 3.0, 4.0),
169 Point3::new(3.0, 4.0, 5.0),
170 Point3::new(-2.0, -1.0, 0.0),
171 Point3::new(-5.0, -2.0, -3.0),
172 Point3::new(1.0, 0.0, 0.0),
173 ];
174
175 assert_eq!(
176 calculate_point_cloud_center(point_cloud.as_slice()),
177 Point3::new(0.0, 1.0, 1.5)
178 );
179 }
180}