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