mapping_algorithms/point_clouds/nearest_neighbour.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
24use nalgebra::{Point, Scalar};
25use num_traits::{Bounded, NumOps};
26
27use crate::utils::distance_squared;
28
29/// Finds the closest matching target point to the passed source point.
30///
31/// # Arguments
32/// * `point`: A [`Point`], for which to find the closest point.
33/// * `all_points`: A slice of [`Point`], representing the target point cloud.
34///
35/// # Generics
36/// * `T`: Either an [`f32`] or [`f64`].
37/// * `N`: A const usize, representing the number of dimensions in the points.
38///
39/// # Returns
40/// An [`Option`] of [`Point`], representing said closest point, empty if no points were provided.
41#[inline]
42#[cfg_attr(
43 feature = "tracing",
44 tracing::instrument("Find Closest Points", skip_all)
45)]
46pub fn find_nearest_neighbour_naive<T, const N: usize>(
47 point: &Point<T, N>,
48 all_points: &[Point<T, N>],
49) -> Option<Point<T, N>>
50where
51 T: Bounded + Copy + Default + NumOps + PartialOrd + Scalar,
52{
53 if all_points.is_empty() {
54 return None;
55 }
56
57 let mut current_distance = T::max_value();
58 let mut current_point = all_points[0]; // Guaranteed to exist
59
60 for target_point in all_points.iter() {
61 let distance = distance_squared(point, target_point);
62 if distance < current_distance {
63 current_distance = distance;
64 current_point = *target_point;
65 }
66 }
67
68 Some(current_point)
69}
70
71#[cfg(test)]
72mod tests {
73 use nalgebra::{Point, Point2};
74
75 use crate::Vec;
76
77 use super::*;
78
79 #[test]
80 fn test_find_closest_point() {
81 // Given:
82 // A set of target points
83 let target_points = Vec::from([
84 Point2::new(1.0, 1.0),
85 Point2::new(2.0, 2.0),
86 Point2::new(5.0, 5.0),
87 Point2::new(8.0, 8.0),
88 ]);
89
90 // A transformed point
91 let transformed_point = Point2::new(4.0, 4.0);
92
93 // When:
94 // Finding the closest point
95 let closest_point = find_nearest_neighbour_naive(&transformed_point, &target_points);
96
97 // Expect the closest point to be (5.0, 5.0)
98 assert_eq!(closest_point, Some(Point2::new(5.0, 5.0)));
99 }
100
101 #[test]
102 fn test_find_closest_point_with_empty_target() {
103 // Given:
104 // An empty set of target points
105 let target_points: Vec<Point<f64, 2>> = Vec::new();
106
107 // A transformed point
108 let transformed_point = Point2::new(4.0, 4.0);
109
110 // This should panic as the target_points array is empty
111 assert_eq!(
112 find_nearest_neighbour_naive(&transformed_point, &target_points),
113 None
114 );
115 }
116}