vox_geometry_rust/
kdtree2.rs

1/*
2 * // Copyright (c) 2021 Feng Yang
3 * //
4 * // I am making my contributions/submissions to this project solely in my
5 * // personal capacity and am not conveying any rights to any intellectual
6 * // property of any third parties.
7 */
8
9use crate::vector2::Vector2D;
10use crate::bounding_box2::BoundingBox2D;
11use std::cmp::Ordering;
12
13/// Simple K-d tree node.
14#[derive(Clone)]
15pub struct Node {
16    /// Split axis if flags < K, leaf indicator if flags == K.
17    flags: usize,
18
19    /// \brief Right child index.
20    /// Note that left child index is this node index + 1.
21    child: usize,
22
23    /// Item index.
24    item: usize,
25
26    /// Point stored in the node.
27    point: Vector2D,
28}
29
30impl Node {
31    /// Default constructor.
32    pub fn new() -> Node {
33        return Node {
34            flags: 0,
35            child: usize::MAX,
36            item: usize::MAX,
37            point: Vector2D::new_default(),
38        };
39    }
40
41    /// Initializes leaf node.
42    pub fn init_leaf(&mut self, it: usize, pt: &Vector2D) {
43        self.flags = 2;
44        self.item = it;
45        self.child = usize::MAX;
46        self.point = pt.clone();
47    }
48
49    /// Initializes internal node.
50    pub fn init_internal(&mut self, axis: usize, it: usize, c: usize, pt: &Vector2D) {
51        self.flags = axis;
52        self.item = it;
53        self.child = c;
54        self.point = pt.clone();
55    }
56
57    /// Returns true if leaf.
58    pub fn is_leaf(&self) -> bool {
59        return self.flags == 2;
60    }
61}
62
63/// Generic k-d tree structure.
64#[derive(Clone)]
65pub struct KdTree2 {
66    _points: Vec<Vector2D>,
67    _nodes: Vec<Node>,
68}
69
70impl KdTree2 {
71    /// Constructs an empty kD-tree instance.
72    pub fn new() -> KdTree2 {
73        return KdTree2 {
74            _points: vec![],
75            _nodes: vec![],
76        };
77    }
78
79    /// Builds internal acceleration structure for given points list.
80    pub fn build(&mut self, points: &Vec<Vector2D>) {
81        self._points = points.clone();
82
83        if self._points.is_empty() {
84            return;
85        }
86
87        self._nodes.clear();
88        let mut item_indices: Vec<usize> = (0..self._points.len()).collect();
89
90        self.build_internal(0, &mut item_indices, self._points.len(), 0);
91    }
92
93    ///
94    /// Invokes the callback function for each nearby point around the origin
95    /// within given radius.
96    ///
97    /// - parameter:  origin   The origin position.
98    /// - parameter:  radius   The search radius.
99    /// - parameter:  callback The callback function.
100    ///
101    pub fn for_each_nearby_point<Callback>(&self, origin: &Vector2D, radius: f64,
102                                           callback: &mut Callback) where Callback: FnMut(usize, &Vector2D) {
103        let r2 = radius * radius;
104
105        // prepare to traverse the tree for sphere
106        const K_MAX_TREE_DEPTH: usize = 8 * 32;
107        let mut todo: [Option<usize>; K_MAX_TREE_DEPTH] = [None; K_MAX_TREE_DEPTH];
108        let mut todo_pos = 0;
109
110        // traverse the tree nodes for sphere
111        let mut node: usize = 0;
112        while node < self._nodes.len() {
113            if self._nodes[node].item != usize::MAX &&
114                (self._nodes[node].point - *origin).length_squared() <= r2 {
115                callback(self._nodes[node].item, &self._nodes[node].point);
116            }
117
118            if self._nodes[node].is_leaf() {
119                // grab next node to process from todo stack
120                if todo_pos > 0 {
121                    // Dequeue
122                    todo_pos -= 1;
123                    node = todo[todo_pos].unwrap();
124                } else {
125                    break;
126                }
127            } else {
128                // get node children pointers for sphere
129                let first_child = node + 1;
130                let second_child = self._nodes[node].child;
131
132                // advance to next child node, possibly enqueue other child
133                let axis = self._nodes[node].flags;
134                let plane = self._nodes[node].point[axis];
135                if plane - origin[axis] > radius {
136                    node = first_child;
137                } else if origin[axis] - plane > radius {
138                    node = second_child;
139                } else {
140                    // enqueue second_child in todo stack
141                    todo[todo_pos] = Some(second_child);
142                    todo_pos += 1;
143                    node = first_child;
144                }
145            }
146        }
147    }
148
149    ///
150    /// Returns true if there are any nearby points for given origin within
151    /// radius.
152    ///
153    /// - parameter:  origin The origin.
154    /// - parameter:  radius The radius.
155    ///
156    /// \return     True if has nearby point, false otherwise.
157    ///
158    pub fn has_nearby_point(&self, origin: &Vector2D, radius: f64) -> bool {
159        let r2 = radius * radius;
160
161        // prepare to traverse the tree for sphere
162        const K_MAX_TREE_DEPTH: usize = 8 * 32;
163        let mut todo: [Option<usize>; K_MAX_TREE_DEPTH] = [None; K_MAX_TREE_DEPTH];
164        let mut todo_pos = 0;
165
166        // traverse the tree nodes for sphere
167        let mut node: usize = 0;
168        while node < self._nodes.len() {
169            if self._nodes[node].item != usize::MAX &&
170                (self._nodes[node].point - *origin).length_squared() <= r2 {
171                return true;
172            }
173
174            if self._nodes[node].is_leaf() {
175                // grab next node to process from todo stack
176                if todo_pos > 0 {
177                    // Dequeue
178                    todo_pos -= 1;
179                    node = todo[todo_pos].unwrap();
180                } else {
181                    break;
182                }
183            } else {
184                // get node children pointers for sphere
185                let first_child = node + 1;
186                let second_child = self._nodes[node].child;
187
188                // advance to next child node, possibly enqueue other child
189                let axis = self._nodes[node].flags;
190                let plane = self._nodes[node].point[axis];
191                if origin[axis] < plane && plane - origin[axis] > radius {
192                    node = first_child;
193                } else if origin[axis] > plane && origin[axis] - plane > radius {
194                    node = second_child;
195                } else {
196                    // enqueue second_child in todo stack
197                    todo[todo_pos] = Some(second_child);
198                    todo_pos += 1;
199                    node = first_child;
200                }
201            }
202        }
203
204        return false;
205    }
206
207    /// Returns index of the nearest point.
208    pub fn nearest_point(&self, origin: &Vector2D) -> usize {
209        // prepare to traverse the tree for sphere
210        const K_MAX_TREE_DEPTH: usize = 8 * 32;
211        let mut todo: [Option<usize>; K_MAX_TREE_DEPTH] = [None; K_MAX_TREE_DEPTH];
212        let mut todo_pos = 0;
213
214        // traverse the tree nodes for sphere
215        let mut node: usize = 0;
216        let mut nearest = 0;
217        let mut min_dist2 = (self._nodes[node].point - *origin).length_squared();
218
219        while node < self._nodes.len() {
220            let new_dist2 = (self._nodes[node].point - *origin).length_squared();
221            if new_dist2 <= min_dist2 {
222                nearest = self._nodes[node].item;
223                min_dist2 = new_dist2;
224            }
225
226            if self._nodes[node].is_leaf() {
227                // grab next node to process from todo stack
228                if todo_pos > 0 {
229                    // Dequeue
230                    todo_pos -= 1;
231                    node = todo[todo_pos].unwrap();
232                } else {
233                    break;
234                }
235            } else {
236                // get node children pointers for sphere
237                let first_child = node + 1;
238                let second_child = self._nodes[node].child;
239
240                // advance to next child node, possibly enqueue other child
241                let axis = self._nodes[node].flags;
242                let plane = self._nodes[node].point[axis];
243                let min_dist = f64::sqrt(min_dist2);
244                if plane - origin[axis] > min_dist {
245                    node = first_child;
246                } else if origin[axis] - plane > min_dist {
247                    node = second_child;
248                } else {
249                    // enqueue second_child in todo stack
250                    todo[todo_pos] = Some(second_child);
251                    todo_pos += 1;
252                    node = first_child;
253                }
254            }
255        }
256
257        return nearest;
258    }
259
260    /// Reserves memory space for this tree.
261    pub fn reserve(&mut self, num_points: usize, num_nodes: usize) {
262        self._points.resize(num_points, Vector2D::new_default());
263        self._nodes.resize(num_nodes, Node::new());
264    }
265
266    pub fn build_internal(&mut self, node_index: usize, item_indices: &mut [usize], n_items: usize,
267                          current_depth: usize) -> usize {
268        // add a node
269        self._nodes.push(Node::new());
270
271        // initialize leaf node if termination criteria met
272        if n_items == 0 {
273            self._nodes[node_index].init_leaf(usize::MAX, &Vector2D::new_default());
274            return current_depth + 1;
275        }
276        if n_items == 1 {
277            self._nodes[node_index].init_leaf(item_indices[0], &self._points[item_indices[0]]);
278            return current_depth + 1;
279        }
280
281        // choose which axis to split along
282        let mut node_bound = BoundingBox2D::new_default();
283        for i in 0..n_items {
284            node_bound.merge_vec(&self._points[item_indices[i]]);
285        }
286        let d = node_bound.upper_corner - node_bound.lower_corner;
287        let axis = d.dominant_axis();
288
289        // pick mid point
290        let (left, mid, right) = item_indices.select_nth_unstable_by(n_items, |a: &usize, b: &usize| {
291            return match self._points[*a][axis] < self._points[*b][axis] {
292                true => Ordering::Less,
293                false => Ordering::Greater
294            };
295        });
296
297        let mid_point = n_items / 2;
298        let node_len = self._nodes.len();
299        // recursively initialize children nodes
300        let d0 = self.build_internal(node_index + 1, left, mid_point, current_depth + 1);
301        self._nodes[node_index].init_internal(axis, *mid, node_len,
302                                              &self._points[*mid]);
303        let d1 = self.build_internal(self._nodes[node_index].child, right,
304                                     n_items - mid_point - 1, current_depth + 1);
305
306        return usize::max(d0, d1);
307    }
308}