1use threecrate_core::{PointCloud, Point3f, NearestNeighborSearch};
4use crate::nearest_neighbor::{KdTree, BruteForceSearch};
5
6pub trait PointCloudNeighbors {
8 fn k_nearest_neighbors(&self, k: usize) -> Vec<Vec<(usize, f32)>>;
33
34 fn find_k_nearest(&self, query: &Point3f, k: usize) -> Vec<(usize, f32)>;
43
44 fn find_radius_neighbors(&self, query: &Point3f, radius: f32) -> Vec<(usize, f32)>;
53
54 fn find_k_nearest_brute_force(&self, query: &Point3f, k: usize) -> Vec<(usize, f32)>;
66
67 fn find_radius_neighbors_brute_force(&self, query: &Point3f, radius: f32) -> Vec<(usize, f32)>;
76}
77
78impl PointCloudNeighbors for PointCloud<Point3f> {
79 fn k_nearest_neighbors(&self, k: usize) -> Vec<Vec<(usize, f32)>> {
80 if self.is_empty() || k == 0 {
81 return Vec::new();
82 }
83
84 let kdtree = KdTree::new(&self.points)
86 .expect("Failed to build KD-tree");
87
88 let mut results = Vec::with_capacity(self.len());
89
90 for (i, query_point) in self.points.iter().enumerate() {
91 let mut neighbors = kdtree.find_k_nearest(query_point, k + 1); neighbors.retain(|&(idx, _)| idx != i);
95
96 if neighbors.len() > k {
98 neighbors.truncate(k);
99 }
100
101 results.push(neighbors);
102 }
103
104 results
105 }
106
107 fn find_k_nearest(&self, query: &Point3f, k: usize) -> Vec<(usize, f32)> {
108 if self.is_empty() || k == 0 {
109 return Vec::new();
110 }
111
112 let kdtree = KdTree::new(&self.points)
113 .expect("Failed to build KD-tree");
114
115 kdtree.find_k_nearest(query, k)
116 }
117
118 fn find_radius_neighbors(&self, query: &Point3f, radius: f32) -> Vec<(usize, f32)> {
119 if self.is_empty() || radius <= 0.0 {
120 return Vec::new();
121 }
122
123 let kdtree = KdTree::new(&self.points)
124 .expect("Failed to build KD-tree");
125
126 kdtree.find_radius_neighbors(query, radius)
127 }
128
129 fn find_k_nearest_brute_force(&self, query: &Point3f, k: usize) -> Vec<(usize, f32)> {
130 if self.is_empty() || k == 0 {
131 return Vec::new();
132 }
133
134 let brute_force = BruteForceSearch::new(&self.points);
135 brute_force.find_k_nearest(query, k)
136 }
137
138 fn find_radius_neighbors_brute_force(&self, query: &Point3f, radius: f32) -> Vec<(usize, f32)> {
139 if self.is_empty() || radius <= 0.0 {
140 return Vec::new();
141 }
142
143 let brute_force = BruteForceSearch::new(&self.points);
144 brute_force.find_radius_neighbors(query, radius)
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use threecrate_core::Point3f;
152
153 #[test]
154 fn test_point_cloud_k_nearest_neighbors() {
155 let mut cloud = PointCloud::new();
156 cloud.push(Point3f::new(0.0, 0.0, 0.0));
157 cloud.push(Point3f::new(1.0, 0.0, 0.0));
158 cloud.push(Point3f::new(0.0, 1.0, 0.0));
159 cloud.push(Point3f::new(1.0, 1.0, 0.0));
160
161 let neighbors = cloud.k_nearest_neighbors(2);
162 assert_eq!(neighbors.len(), 4);
163
164 for point_neighbors in &neighbors {
166 assert_eq!(point_neighbors.len(), 2);
167 }
168 }
169
170 #[test]
171 fn test_point_cloud_find_k_nearest() {
172 let mut cloud = PointCloud::new();
173 cloud.push(Point3f::new(0.0, 0.0, 0.0));
174 cloud.push(Point3f::new(1.0, 0.0, 0.0));
175 cloud.push(Point3f::new(0.0, 1.0, 0.0));
176 cloud.push(Point3f::new(1.0, 1.0, 0.0));
177
178 let query = Point3f::new(0.5, 0.5, 0.0);
179 let nearest = cloud.find_k_nearest(&query, 2);
180
181 assert_eq!(nearest.len(), 2);
182 assert!(nearest[0].1 <= nearest[1].1); }
184
185 #[test]
186 fn test_point_cloud_radius_neighbors() {
187 let mut cloud = PointCloud::new();
188 cloud.push(Point3f::new(0.0, 0.0, 0.0));
189 cloud.push(Point3f::new(1.0, 0.0, 0.0));
190 cloud.push(Point3f::new(0.0, 1.0, 0.0));
191 cloud.push(Point3f::new(1.0, 1.0, 0.0));
192
193 let query = Point3f::new(0.5, 0.5, 0.0);
194 let radius_neighbors = cloud.find_radius_neighbors(&query, 1.0);
195
196 assert_eq!(radius_neighbors.len(), 4);
198
199 for (_, distance) in &radius_neighbors {
201 assert!(*distance <= 1.0);
202 }
203 }
204
205 #[test]
206 fn test_brute_force_consistency() {
207 let mut cloud = PointCloud::new();
208 cloud.push(Point3f::new(0.0, 0.0, 0.0));
209 cloud.push(Point3f::new(1.0, 0.0, 0.0));
210 cloud.push(Point3f::new(0.0, 1.0, 0.0));
211 cloud.push(Point3f::new(1.0, 1.0, 0.0));
212
213 let query = Point3f::new(0.5, 0.5, 0.0);
214 let k = 2;
215
216 let mut kdtree_result = cloud.find_k_nearest(&query, k);
217 let mut brute_result = cloud.find_k_nearest_brute_force(&query, k);
218
219 kdtree_result.sort_by(|a, b| {
221 a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)
222 .then(a.0.cmp(&b.0))
223 });
224 brute_result.sort_by(|a, b| {
225 a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)
226 .then(a.0.cmp(&b.0))
227 });
228
229 assert_eq!(kdtree_result.len(), brute_result.len());
230
231 for (kdtree_neighbor, brute_neighbor) in kdtree_result.iter().zip(brute_result.iter()) {
233 assert!((kdtree_neighbor.1 - brute_neighbor.1).abs() < 1e-6);
234 }
235 }
236}