rust_3d/
kd_tree.rs

1/*
2Copyright 2016 Martin Buck
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation the
7rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the Software
9is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall
12be included all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
20OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21*/
22
23//! KdTree https://en.wikipedia.org/wiki/K-d_tree
24
25use std::cmp::Ordering;
26
27use crate::*;
28
29//------------------------------------------------------------------------------
30
31#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
32/// KdTree https://en.wikipedia.org/wiki/K-d_tree
33pub struct KdTree<P>
34where
35    P: Is3D,
36{
37    root: Option<KdNode<P>>,
38}
39
40//------------------------------------------------------------------------------
41
42#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43struct KdNode<P>
44where
45    P: Is3D,
46{
47    pub left: Option<Box<KdNode<P>>>,
48    pub right: Option<Box<KdNode<P>>>,
49    pub val: P,
50    pub dimension: i8,
51}
52
53//------------------------------------------------------------------------------
54
55impl<P> IsTree3D<P> for KdTree<P>
56where
57    P: Is3D + Clone,
58{
59    fn size(&self) -> usize {
60        match self.root {
61            None => 0,
62            Some(ref node) => node.size(),
63        }
64    }
65
66    fn to_pointcloud(&self) -> PointCloud3D<P> {
67        let mut result = PointCloud3D::new();
68        if let Some(ref node) = self.root {
69            node.to_pointcloud_3d(&mut result);
70        }
71        result
72    }
73
74    fn build(&mut self, pc: PointCloud3D<P>) -> Result<()> {
75        match pc.len() {
76            0 => Err(ErrorKind::TooFewPoints),
77            _ => {
78                self.root = Some(KdNode::new(0, pc.data));
79                Ok(())
80            }
81        }
82    }
83}
84
85//------------------------------------------------------------------------------
86
87impl<PSearch, PFind> IsKNearestSearchable<PSearch, PFind> for KdTree<PFind>
88where
89    PSearch: Is3D,
90    PFind: Is3D + Clone,
91{
92    fn knearest(&self, search: &PSearch, n: usize, result: &mut Vec<PFind>) {
93        if n < 1 {
94            return;
95        }
96        if let Some(ref node) = self.root {
97            node.knearest(search, n, result);
98        }
99    }
100
101    fn nearest(&self, search: &PSearch) -> Result<PFind> {
102        //@todo implemented on its own, since the code can be faster without vecs
103        let mut result = Vec::new();
104        self.knearest(search, 1, &mut result);
105        match result.len() {
106            0 => Err(ErrorKind::TooFewPoints),
107            _ => {
108                let p = result[0].clone();
109                Ok(p)
110            }
111        }
112    }
113}
114
115impl<P> IsSphereSearchable<P> for KdTree<P>
116where
117    P: Is3D + Clone,
118{
119    fn in_sphere(&self, sphere: &Sphere, result: &mut Vec<P>) {
120        if let Some(ref node) = self.root {
121            node.in_sphere(sphere, result);
122        }
123    }
124}
125
126impl<P> IsBox3DSearchable<P> for KdTree<P>
127where
128    P: Is3D + Clone,
129{
130    fn in_box(&self, box_3d: &Box3D, result: &mut Vec<P>) {
131        if let Some(ref node) = self.root {
132            node.in_box(box_3d, result);
133        }
134    }
135}
136
137impl<P> KdNode<P>
138where
139    P: Is3D,
140{
141    pub fn size(&self) -> usize {
142        let mut result: usize = 0;
143        if let Some(ref n) = self.left {
144            result += n.size();
145        }
146        result += 1;
147        if let Some(ref n) = self.right {
148            result += n.size();
149        }
150        result
151    }
152
153    fn is_leaf(&self) -> bool {
154        self.left.is_none() && self.right.is_none()
155    }
156}
157
158impl<P> KdNode<P>
159where
160    P: Is3D + Clone,
161{
162    pub fn to_pointcloud_3d(&self, pc: &mut PointCloud3D<P>) {
163        if let Some(ref n) = self.left {
164            n.to_pointcloud_3d(pc);
165        }
166        pc.push(self.val.clone());
167        if let Some(ref n) = self.right {
168            n.to_pointcloud_3d(pc);
169        }
170    }
171
172    pub fn new(dim: i8, mut pc: Vec<P>) -> KdNode<P> {
173        let dimension = dim % 2;
174        if pc.len() == 1 {
175            return KdNode {
176                left: None,
177                right: None,
178                val: pc[0].clone(),
179                dimension,
180            };
181        }
182
183        pc.sort_by(|a, b| match dimension {
184            0 => a.x().partial_cmp(&b.x()).unwrap_or(Ordering::Equal),
185            1 => a.y().partial_cmp(&b.y()).unwrap_or(Ordering::Equal),
186            2 => a.z().partial_cmp(&b.z()).unwrap_or(Ordering::Equal),
187            _ => Ordering::Equal,
188        });
189        let median = pc.len() / 2;
190        let mut pc_left = Vec::new();
191        let mut pc_right = Vec::new();
192
193        let val = pc[median].clone();
194
195        for (i, p) in pc.into_iter().enumerate() {
196            if i < median {
197                pc_left.push(p);
198            } else if i > median {
199                pc_right.push(p);
200            }
201        }
202
203        let left = match pc_left.len() {
204            0 => None,
205            _ => Some(Box::new(KdNode::new(dimension + 1, pc_left))),
206        };
207
208        let right = match pc_right.len() {
209            0 => None,
210            _ => Some(Box::new(KdNode::new(dimension + 1, pc_right))),
211        };
212
213        KdNode {
214            left,
215            right,
216            val,
217            dimension,
218        }
219    }
220}
221
222impl<P> KdNode<P>
223where
224    P: Is3D + Clone,
225{
226    pub fn knearest<PSearch>(&self, search: &PSearch, n: usize, pc: &mut Vec<P>)
227    where
228        PSearch: Is3D,
229    {
230        if pc.len() < n || sqr_dist_3d(search, &self.val) < sqr_dist_3d(search, &pc[&pc.len() - 1])
231        {
232            pc.push(self.val.clone());
233        }
234
235        let comp = dimension_compare(search, &self.val, self.dimension);
236
237        match comp {
238            Ok(res) => match res {
239                Ordering::Less => {
240                    if let Some(ref node) = self.left {
241                        node.knearest(search, n, pc);
242                    }
243                }
244                _ => {
245                    if let Some(ref node) = self.right {
246                        node.knearest(search, n, pc);
247                    }
248                }
249            },
250            Err(_) => {}
251        }
252
253        Self::sort_and_limit(pc, search, n);
254
255        let (current_search, current_val) = match self.dimension {
256            0 => (search.x(), self.val.x()),
257            1 => (search.y(), self.val.y()),
258            _ => (search.z(), self.val.z()),
259        };
260
261        let distance_best = dist_3d(search, &pc[&pc.len() - 1]);
262        let border_left = current_search - distance_best;
263        let border_right = current_search + distance_best;
264
265        match comp {
266            Ok(res) => match res {
267                Ordering::Less => {
268                    if let Some(ref node) = self.right {
269                        if pc.len() < n || border_right >= current_val {
270                            node.knearest(search, n, pc);
271                        }
272                    }
273                }
274                _ => {
275                    if let Some(ref node) = self.left {
276                        if pc.len() < n || border_left <= current_val {
277                            node.knearest(search, n, pc);
278                        }
279                    }
280                }
281            },
282            Err(_) => {}
283        }
284
285        Self::sort_and_limit(pc, search, n);
286    }
287
288    pub fn in_sphere(&self, sphere: &Sphere, pc: &mut Vec<P>) {
289        if dist_3d(&sphere.center, &self.val) <= sphere.radius.get() {
290            pc.push(self.val.clone());
291        }
292
293        if self.is_leaf() {
294            return;
295        }
296
297        let comp = dimension_compare(&sphere.center, &self.val, self.dimension);
298
299        match comp {
300            Ok(res) => match res {
301                Ordering::Less => {
302                    if let Some(ref node) = self.left {
303                        node.in_sphere(sphere, pc);
304                    }
305                }
306                _ => {
307                    if let Some(ref node) = self.right {
308                        node.in_sphere(sphere, pc);
309                    }
310                }
311            },
312            Err(_) => {}
313        }
314
315        let (current_search, current_val) = match self.dimension {
316            0 => (sphere.x(), self.val.x()),
317            1 => (sphere.y(), self.val.y()),
318            _ => (sphere.z(), self.val.z()),
319        };
320
321        let border_left = current_search - sphere.radius.get();
322        let border_right = current_search + sphere.radius.get();
323
324        match comp {
325            Ok(res) => match res {
326                Ordering::Less => {
327                    if let Some(ref node) = self.right {
328                        if border_right >= current_val {
329                            node.in_sphere(sphere, pc);
330                        }
331                    }
332                }
333                _ => {
334                    if let Some(ref node) = self.left {
335                        if border_left <= current_val {
336                            node.in_sphere(sphere, pc);
337                        }
338                    }
339                }
340            },
341            Err(_) => {}
342        }
343    }
344
345    pub fn in_box(&self, box_3d: &Box3D, pc: &mut Vec<P>) {
346        if let (Ok(dist_x), Ok(dist_y), Ok(dist_z)) = (
347            dimension_dist(&box_3d.center, &self.val, 0),
348            dimension_dist(&box_3d.center, &self.val, 1),
349            dimension_dist(&box_3d.center, &self.val, 2),
350        ) {
351            if dist_x <= 0.5 * box_3d.size_x.get()
352                && dist_y <= 0.5 * box_3d.size_y.get()
353                && dist_z <= 0.5 * box_3d.size_z.get()
354            {
355                pc.push(self.val.clone());
356            }
357
358            if self.is_leaf() {
359                return;
360            }
361
362            let comp = dimension_compare(&box_3d.center, &self.val, self.dimension);
363
364            match comp {
365                Ok(res) => match res {
366                    Ordering::Less => {
367                        if let Some(ref node) = self.left {
368                            node.in_box(box_3d, pc);
369                        }
370                    }
371                    _ => {
372                        if let Some(ref node) = self.right {
373                            node.in_box(box_3d, pc);
374                        }
375                    }
376                },
377                Err(_) => {}
378            }
379
380            let (current_search, current_val, ref current_size) = match self.dimension {
381                0 => (box_3d.x(), self.val.x(), &box_3d.size_x),
382                1 => (box_3d.y(), self.val.y(), &box_3d.size_y),
383                _ => (box_3d.z(), self.val.z(), &box_3d.size_z),
384            };
385
386            let border_left = current_search - 0.5 * current_size.get();
387            let border_right = current_search + 0.5 * current_size.get();
388
389            match comp {
390                Ok(res) => match res {
391                    Ordering::Less => {
392                        if let Some(ref node) = self.right {
393                            if border_right >= current_val {
394                                node.in_box(box_3d, pc);
395                            }
396                        }
397                    }
398                    _ => {
399                        if let Some(ref node) = self.left {
400                            if border_left <= current_val {
401                                node.in_box(box_3d, pc);
402                            }
403                        }
404                    }
405                },
406                Err(_) => {}
407            }
408        }
409    }
410
411    fn sort_and_limit<'a, PSearch, PFind>(pc: &'a mut Vec<PFind>, search: &PSearch, max_size: usize)
412    where
413        PSearch: Is3D,
414        PFind: Is3D + Clone,
415    {
416        if pc.len() > max_size {
417            pc.sort_by(|a, b| {
418                sqr_dist_3d(search, a)
419                    .partial_cmp(&sqr_dist_3d(search, b))
420                    .unwrap_or(Ordering::Equal)
421            });
422            let mut result: Vec<PFind>;
423            result = Vec::new();
424            for i in pc.iter().take(max_size) {
425                result.push(i.clone());
426            }
427            *pc = result;
428        }
429    }
430}