parry2d/partitioning/bvh/
bvh_traverse.rs

1use super::BvhNode;
2use crate::math::Real;
3use crate::partitioning::Bvh;
4use smallvec::SmallVec;
5
6const TRAVERSAL_STACK_SIZE: usize = 32;
7
8pub struct Leaves<'a, Check: Fn(&BvhNode) -> bool> {
9    tree: &'a Bvh,
10    next: Option<&'a BvhNode>,
11    stack: SmallVec<[&'a BvhNode; TRAVERSAL_STACK_SIZE]>,
12    check: Check,
13}
14
15impl<'a, Check: Fn(&BvhNode) -> bool> Leaves<'a, Check> {
16    pub fn new(tree: &'a Bvh, check: Check) -> Leaves<'a, Check> {
17        let mut stack = SmallVec::default();
18        let mut next = None;
19
20        if let Some(root) = tree.nodes.first() {
21            if check(&root.left) {
22                next = Some(&root.left);
23            }
24
25            if root.right.leaf_count() > 0 && check(&root.right) {
26                stack.push(&root.right);
27            }
28        }
29
30        Leaves {
31            tree,
32            next,
33            stack,
34            check,
35        }
36    }
37}
38
39impl<'a, Check: Fn(&BvhNode) -> bool> Iterator for Leaves<'a, Check> {
40    type Item = u32;
41    fn next(&mut self) -> Option<Self::Item> {
42        loop {
43            if self.next.is_none() {
44                self.next = self.stack.pop();
45            }
46
47            let node = self.next.take()?;
48
49            if node.is_leaf() {
50                return Some(node.children);
51            }
52
53            let children = &self.tree.nodes[node.children as usize];
54            let left = &children.left;
55            let right = &children.right;
56
57            if (self.check)(left) {
58                self.next = Some(left);
59            }
60
61            if (self.check)(right) {
62                if self.next.is_none() {
63                    self.next = Some(right);
64                } else {
65                    self.stack.push(right);
66                }
67            }
68        }
69    }
70}
71
72/// Cost associated to a BVH leaf during best-first traversal.
73pub trait BvhLeafCost {
74    /// The cost value associated to the leaf.
75    ///
76    /// Best-first searches for the leaf with the lowest cost.
77    fn cost(&self) -> Real;
78}
79
80impl BvhLeafCost for Real {
81    #[inline(always)]
82    fn cost(&self) -> Real {
83        *self
84    }
85}
86
87impl<T> BvhLeafCost for (Real, T) {
88    #[inline(always)]
89    fn cost(&self) -> Real {
90        self.0
91    }
92}
93
94impl Bvh {
95    /// Iterates through the leaves, in depth-first order.
96    ///
97    /// The `check_node` closure is called on every traversed node. If it returns `false` then the
98    /// node and all its descendants won’t be iterated on. This is useful for pruning whole
99    /// sub-trees based on a geometric predicate on the node’s AABB.
100    ///
101    /// See also the [`Bvh::traverse`] function which is slightly less convenient since it doesn’t
102    /// rely on the iterator system, but takes a closure that implements [`FnMut`] instead of [`Fn`].
103    pub fn leaves<F: Fn(&BvhNode) -> bool>(&self, check_node: F) -> Leaves<'_, F> {
104        Leaves::new(self, check_node)
105    }
106}
107
108/// Controls the execution flow of [`Bvh::traverse`].
109pub enum TraversalAction {
110    /// The traversal will continue on the children of the tested node.
111    Continue,
112    /// The traversal will skip all descendants of the tested node.
113    Prune,
114    /// The traversal will exit immediately.
115    EarlyExit,
116}
117
118impl Bvh {
119    #[inline(always)]
120    pub(crate) fn traversal_stack() -> SmallVec<[u32; 32]> {
121        Default::default()
122    }
123
124    /// Traverse the tree in depth-first order.
125    ///
126    /// The `check_node` closure is called on every traversed node. The returned [`TraversalAction`]
127    /// controls whether a given node (and all its subtree) needs to be traversed, skipped, or if
128    /// the traversal needs to exit immediately (for example if you were looking for only one
129    /// particular node).
130    ///
131    /// See also the [`Bvh::leaves`] iterator which is a more convenient way of traversing the tree,
132    /// but is slightly limited in terms of node checking. In particular the closure
133    /// given to [`Bvh::traverse`] is mutable can hold any state so its check can depend on previous
134    /// execution of itself during the traversal.
135    pub fn traverse(&self, mut check_node: impl FnMut(&BvhNode) -> TraversalAction) {
136        let mut stack = Self::traversal_stack();
137        let mut curr_id = 0;
138
139        if self.nodes.is_empty() {
140            return;
141        } else if self.nodes[0].right.leaf_count() == 0 {
142            // Special case for partial root.
143            let _ = check_node(&self.nodes[0].left);
144            return;
145        }
146
147        loop {
148            let node = &self.nodes[curr_id as usize];
149            let left = &node.left;
150            let right = &node.right;
151            let go_left = match check_node(left) {
152                TraversalAction::Continue => !left.is_leaf(),
153                TraversalAction::Prune => false,
154                TraversalAction::EarlyExit => return,
155            };
156            let go_right = match check_node(right) {
157                TraversalAction::Continue => !right.is_leaf(),
158                TraversalAction::Prune => false,
159                TraversalAction::EarlyExit => return,
160            };
161
162            match (go_left, go_right) {
163                (true, true) => {
164                    curr_id = left.children;
165                    stack.push(right.children);
166                }
167                (true, false) => curr_id = left.children,
168                (false, true) => curr_id = right.children,
169                (false, false) => {
170                    let Some(next) = stack.pop() else {
171                        return;
172                    };
173                    curr_id = next;
174                }
175            }
176        }
177    }
178
179    /// Find the leaf that minimizes their associated cost.
180    pub fn find_best<L: BvhLeafCost>(
181        &self,
182        max_cost: Real,
183        aabb_cost: impl Fn(&BvhNode, Real) -> Real,
184        leaf_cost: impl Fn(u32, Real) -> Option<L>,
185    ) -> Option<(u32, L)> {
186        // A stack with 32 elements should be more than enough in most cases.
187        let mut stack = Self::traversal_stack();
188        let mut best_val = None;
189        let mut best_cost = max_cost;
190        let mut best_id = u32::MAX;
191        let mut curr_id = 0;
192
193        if self.nodes.is_empty() {
194            return None;
195        } else if self.nodes[0].right.leaf_count() == 0 {
196            // Special case for partial root.
197            let leaf = &self.nodes[0].left;
198            if aabb_cost(leaf, max_cost) < max_cost {
199                let cost = leaf_cost(leaf.children, best_cost)?;
200                return (cost.cost() < max_cost).then_some((leaf.children, cost));
201            } else {
202                return None;
203            }
204        }
205
206        loop {
207            let node = &self.nodes[curr_id as usize];
208            let mut left = &node.left;
209            let mut right = &node.right;
210
211            let mut left_score = aabb_cost(left, best_cost);
212            let mut right_score = aabb_cost(right, best_cost);
213
214            if left_score > right_score {
215                core::mem::swap(&mut left_score, &mut right_score);
216                core::mem::swap(&mut left, &mut right);
217            }
218
219            let mut found_next = false;
220            if left_score < best_cost && left_score != Real::MAX {
221                if left.is_leaf() {
222                    if let Some(primitive_val) = leaf_cost(left.children, best_cost) {
223                        let primitive_score = primitive_val.cost();
224                        if primitive_score < best_cost {
225                            best_val = Some(primitive_val);
226                            best_cost = primitive_score;
227                            best_id = left.children;
228                        }
229                    }
230                } else {
231                    curr_id = left.children;
232                    found_next = true;
233                }
234            }
235
236            if right_score < best_cost && right_score != Real::MAX {
237                if right.is_leaf() {
238                    if let Some(primitive_val) = leaf_cost(right.children, best_cost) {
239                        let primitive_score = primitive_val.cost();
240                        if primitive_score < best_cost {
241                            best_val = Some(primitive_val);
242                            best_cost = primitive_score;
243                            best_id = right.children;
244                        }
245                    }
246                } else if found_next {
247                    stack.push(right.children);
248                } else {
249                    curr_id = right.children;
250                    found_next = true;
251                }
252            }
253
254            if !found_next {
255                if let Some(next) = stack.pop() {
256                    curr_id = next;
257                } else {
258                    return best_val.map(|val| (best_id, val));
259                }
260            }
261        }
262    }
263}