Skip to main content

threecrate_algorithms/
point_cloud_ops.rs

1//! Point cloud operations including k-nearest neighbors search
2
3use threecrate_core::{PointCloud, Point3f, NearestNeighborSearch};
4use crate::nearest_neighbor::{KdTree, BruteForceSearch};
5
6/// Extension trait for PointCloud to add k-nearest neighbors functionality
7pub trait PointCloudNeighbors {
8    /// Find k nearest neighbors for each point in the cloud using KD-tree
9    /// 
10    /// This method returns a vector where each element contains the indices and distances
11    /// of the k nearest neighbors for the corresponding point in the cloud.
12    /// 
13    /// # Arguments
14    /// * `k` - Number of nearest neighbors to find for each point
15    /// 
16    /// # Returns
17    /// * `Vec<Vec<(usize, f32)>>` - Vector of neighbor results for each point
18    /// 
19    /// # Example
20    /// ```rust,no_run
21    /// // Temporarily disabled due to stack overflow - needs investigation
22    /// use threecrate_core::{PointCloud, Point3f};
23    /// use threecrate_algorithms::point_cloud_ops::PointCloudNeighbors;
24    ///
25    /// let mut cloud = PointCloud::new();
26    /// cloud.push(Point3f::new(0.0, 0.0, 0.0));
27    /// cloud.push(Point3f::new(1.0, 0.0, 0.0));
28    /// cloud.push(Point3f::new(0.0, 1.0, 0.0));
29    ///
30    /// let neighbors = cloud.k_nearest_neighbors(2);
31    /// // neighbors[0] contains the 2 nearest neighbors for point 0
32    /// ```
33    fn k_nearest_neighbors(&self, k: usize) -> Vec<Vec<(usize, f32)>>;
34
35    /// Find k nearest neighbors for a specific query point using KD-tree
36    /// 
37    /// # Arguments
38    /// * `query` - The query point to find neighbors for
39    /// * `k` - Number of nearest neighbors to find
40    /// 
41    /// # Returns
42    /// * `Vec<(usize, f32)>` - Vector of (index, distance) pairs for the k nearest neighbors
43    fn find_k_nearest(&self, query: &Point3f, k: usize) -> Vec<(usize, f32)>;
44
45    /// Find all neighbors within a given radius using KD-tree
46    /// 
47    /// # Arguments
48    /// * `query` - The query point to find neighbors for
49    /// * `radius` - Search radius
50    /// 
51    /// # Returns
52    /// * `Vec<(usize, f32)>` - Vector of (index, distance) pairs for neighbors within radius
53    fn find_radius_neighbors(&self, query: &Point3f, radius: f32) -> Vec<(usize, f32)>;
54
55    /// Find k nearest neighbors using brute force search (for small datasets or testing)
56    /// 
57    /// This method is useful for small point clouds or when you want to verify
58    /// the results of the KD-tree implementation.
59    /// 
60    /// # Arguments
61    /// * `query` - The query point to find neighbors for
62    /// * `k` - Number of nearest neighbors to find
63    /// 
64    /// # Returns
65    /// * `Vec<(usize, f32)>` - Vector of (index, distance) pairs for the k nearest neighbors
66    fn find_k_nearest_brute_force(&self, query: &Point3f, k: usize) -> Vec<(usize, f32)>;
67
68    /// Find all neighbors within a given radius using brute force search
69    /// 
70    /// # Arguments
71    /// * `query` - The query point to find neighbors for
72    /// * `radius` - Search radius
73    /// 
74    /// # Returns
75    /// * `Vec<(usize, f32)>` - Vector of (index, distance) pairs for neighbors within radius
76    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        // Use KD-tree for efficient search
86        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); // +1 to exclude self
93            
94            // Remove the point itself from its own neighbors
95            neighbors.retain(|&(idx, _)| idx != i);
96            
97            // Ensure we have exactly k neighbors (or fewer if not enough points)
98            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] // Temporarily disabled due to stack overflow - needs investigation
156    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        // Each point should have 2 neighbors (excluding itself)
167        for point_neighbors in &neighbors {
168            assert_eq!(point_neighbors.len(), 2);
169        }
170    }
171
172    #[test]
173    #[ignore] // Temporarily disabled due to stack overflow - needs investigation
174    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); // Should be sorted by distance
186    }
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        // Should find all 4 points within radius 1.0
200        assert_eq!(radius_neighbors.len(), 4);
201        
202        // All distances should be within radius
203        for (_, distance) in &radius_neighbors {
204            assert!(*distance <= 1.0);
205        }
206    }
207
208    #[test]
209    #[ignore] // Temporarily disabled due to stack overflow - needs investigation
210    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        // Sort by distance first, then by index for consistent comparison
224        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        // Check that the distances match (within tolerance)
236        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}