1use crate::nearest_neighbor::{BruteForceSearch, KdTree};
4use threecrate_core::{NearestNeighborSearch, Point3f, PointCloud};
5
6pub trait PointCloudNeighbors {
8 fn k_nearest_neighbors(&self, k: usize) -> Vec<Vec<(usize, f32)>>;
34
35 fn find_k_nearest(&self, query: &Point3f, k: usize) -> Vec<(usize, f32)>;
44
45 fn find_radius_neighbors(&self, query: &Point3f, radius: f32) -> Vec<(usize, f32)>;
54
55 fn find_k_nearest_brute_force(&self, query: &Point3f, k: usize) -> Vec<(usize, f32)>;
67
68 fn find_radius_neighbors_brute_force(&self, query: &Point3f, radius: f32) -> Vec<(usize, f32)>;
77}
78
79impl PointCloudNeighbors for PointCloud<Point3f> {
80 fn k_nearest_neighbors(&self, k: usize) -> Vec<Vec<(usize, f32)>> {
81 if self.is_empty() || k == 0 {
82 return Vec::new();
83 }
84
85 let kdtree = KdTree::new(&self.points).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).expect("Failed to build KD-tree");
113
114 kdtree.find_k_nearest(query, k)
115 }
116
117 fn find_radius_neighbors(&self, query: &Point3f, radius: f32) -> Vec<(usize, f32)> {
118 if self.is_empty() || radius <= 0.0 {
119 return Vec::new();
120 }
121
122 let kdtree = KdTree::new(&self.points).expect("Failed to build KD-tree");
123
124 kdtree.find_radius_neighbors(query, radius)
125 }
126
127 fn find_k_nearest_brute_force(&self, query: &Point3f, k: usize) -> Vec<(usize, f32)> {
128 if self.is_empty() || k == 0 {
129 return Vec::new();
130 }
131
132 let brute_force = BruteForceSearch::new(&self.points);
133 brute_force.find_k_nearest(query, k)
134 }
135
136 fn find_radius_neighbors_brute_force(&self, query: &Point3f, radius: f32) -> Vec<(usize, f32)> {
137 if self.is_empty() || radius <= 0.0 {
138 return Vec::new();
139 }
140
141 let brute_force = BruteForceSearch::new(&self.points);
142 brute_force.find_radius_neighbors(query, radius)
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use threecrate_core::Point3f;
150
151 #[test]
152 #[ignore] fn test_point_cloud_k_nearest_neighbors() {
154 let mut cloud = PointCloud::new();
155 cloud.push(Point3f::new(0.0, 0.0, 0.0));
156 cloud.push(Point3f::new(1.0, 0.0, 0.0));
157 cloud.push(Point3f::new(0.0, 1.0, 0.0));
158 cloud.push(Point3f::new(1.0, 1.0, 0.0));
159
160 let neighbors = cloud.k_nearest_neighbors(2);
161 assert_eq!(neighbors.len(), 4);
162
163 for point_neighbors in &neighbors {
165 assert_eq!(point_neighbors.len(), 2);
166 }
167 }
168
169 #[test]
170 #[ignore] 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 #[ignore] fn test_brute_force_consistency() {
208 let mut cloud = PointCloud::new();
209 cloud.push(Point3f::new(0.0, 0.0, 0.0));
210 cloud.push(Point3f::new(1.0, 0.0, 0.0));
211 cloud.push(Point3f::new(0.0, 1.0, 0.0));
212 cloud.push(Point3f::new(1.0, 1.0, 0.0));
213
214 let query = Point3f::new(0.5, 0.5, 0.0);
215 let k = 2;
216
217 let mut kdtree_result = cloud.find_k_nearest(&query, k);
218 let mut brute_result = cloud.find_k_nearest_brute_force(&query, k);
219
220 kdtree_result.sort_by(|a, b| {
222 a.1.partial_cmp(&b.1)
223 .unwrap_or(std::cmp::Ordering::Equal)
224 .then(a.0.cmp(&b.0))
225 });
226 brute_result.sort_by(|a, b| {
227 a.1.partial_cmp(&b.1)
228 .unwrap_or(std::cmp::Ordering::Equal)
229 .then(a.0.cmp(&b.0))
230 });
231
232 assert_eq!(kdtree_result.len(), brute_result.len());
233
234 for (kdtree_neighbor, brute_neighbor) in kdtree_result.iter().zip(brute_result.iter()) {
236 assert!((kdtree_neighbor.1 - brute_neighbor.1).abs() < 1e-6);
237 }
238 }
239}