parry3d/partitioning/bvh/
bvh_validation.rs

1use crate::partitioning::bvh::bvh_tree::BvhNodeIndex;
2use crate::partitioning::Bvh;
3use crate::utils::hashset::HashSet;
4
5impl Bvh {
6    /// Counts the number of leaves that can be reached from the node at index `id`.
7    ///
8    /// This is mostly a utility for debugging.
9    pub fn reachable_leaf_count(&self, id: u32) -> u32 {
10        if self.nodes.is_empty() {
11            0
12        } else if self.nodes[0].right.leaf_count() == 0 {
13            (id == 0) as u32
14        } else {
15            let node = &self.nodes[id as usize];
16            let left_count = if node.left.is_leaf() {
17                1
18            } else {
19                self.reachable_leaf_count(node.left.children)
20            };
21            let right_count = if node.right.is_leaf() {
22                1
23            } else {
24                self.reachable_leaf_count(node.right.children)
25            };
26            left_count + right_count
27        }
28    }
29
30    /// Counts the number of leaves of `self`, starting with the subtree indexed at
31    /// `id`, that are marked as changed.
32    ///
33    /// This mostly a utility for debugging.
34    pub fn changed_leaf_count(&self, id: u32) -> u32 {
35        if self.nodes.is_empty() {
36            0
37        } else if self.nodes[0].right.leaf_count() == 0 {
38            (id == 0 && self.nodes[0].left.data.is_changed()) as u32
39        } else {
40            let node = &self.nodes[id as usize];
41            let left_count = if node.left.is_leaf() {
42                node.left.is_changed() as u32
43            } else {
44                self.changed_leaf_count(node.left.children)
45            };
46            let right_count = if node.right.is_leaf() {
47                node.right.is_changed() as u32
48            } else {
49                self.changed_leaf_count(node.right.children)
50            };
51            left_count + right_count
52        }
53    }
54
55    /// Panics if the tree isn’t well-formed.
56    ///
57    /// The tree is well-formed if it is topologically correct (internal indices are all valid) and
58    /// geometrically correct (node metadata of a parent bound the ones of the children).
59    ///
60    /// Returns the calculated leaf count.
61    pub fn assert_well_formed(&self) {
62        if self.is_empty() {
63            return;
64        } else if self.nodes[0].right.leaf_count() == 0 {
65            assert_eq!(self.nodes[0].leaf_count(), 1);
66            assert!(self.nodes[0].left.is_leaf());
67            return;
68        }
69
70        let mut loop_detection = HashSet::default();
71        let _ = self.assert_well_formed_recurse(0, &mut loop_detection);
72    }
73
74    fn assert_well_formed_recurse(&self, node_id: u32, loop_detection: &mut HashSet<u32>) -> u32 {
75        let node = &self.nodes[node_id as usize];
76
77        if !loop_detection.insert(node_id) {
78            panic!("Detected loop. Node {} visited twice.", node_id);
79        }
80
81        let left_count = if node.left.is_leaf() {
82            let leaf_data = self.leaf_node_indices[node.left.children as usize];
83            assert_eq!(leaf_data, BvhNodeIndex::left(node_id));
84            1
85        } else {
86            let calculated_leaf_count =
87                self.assert_well_formed_recurse(node.left.children, loop_detection);
88            let child = &self.nodes[node.left.children as usize];
89            assert_eq!(
90                self.parents[node.left.children as usize],
91                BvhNodeIndex::left(node_id)
92            );
93            assert_eq!(
94                child.right.is_changed() || child.left.is_changed(),
95                node.left.is_changed()
96            );
97            assert!(node.left.contains(&child.left));
98            assert!(node.left.contains(&child.right));
99            assert_eq!(
100                node.left.leaf_count(),
101                child.left.leaf_count() + child.right.leaf_count()
102            );
103            assert_eq!(node.left.leaf_count(), calculated_leaf_count);
104            calculated_leaf_count
105        };
106
107        let right_count = if node.right.is_leaf() {
108            let leaf_data = self.leaf_node_indices[node.right.children as usize];
109            assert_eq!(leaf_data, BvhNodeIndex::right(node_id));
110            1
111        } else {
112            let calculated_leaf_count =
113                self.assert_well_formed_recurse(node.right.children, loop_detection);
114            let child = &self.nodes[node.right.children as usize];
115            assert_eq!(
116                self.parents[node.right.children as usize],
117                BvhNodeIndex::right(node_id)
118            );
119            assert_eq!(
120                child.right.is_changed() || child.left.is_changed(),
121                node.right.is_changed()
122            );
123            assert!(node.right.contains(&child.left));
124            assert!(node.right.contains(&child.right));
125            assert_eq!(
126                node.right.leaf_count(),
127                child.left.leaf_count() + child.right.leaf_count()
128            );
129            assert_eq!(calculated_leaf_count, node.right.leaf_count());
130            calculated_leaf_count
131        };
132
133        left_count + right_count
134    }
135
136    /// Similar to [`Self::assert_well_formed`] but doesn’t check the geometry (i.e. it won’t
137    /// check that parent AABBs enclose child AABBs).
138    ///
139    /// This can be useful for checking intermediate states of the tree after topological changes
140    /// but before refitting.
141    pub fn assert_well_formed_topology_only(&self) {
142        let _ = self.assert_well_formed_topology_only_recurse(0);
143    }
144
145    fn assert_well_formed_topology_only_recurse(&self, node_id: u32) -> u32 {
146        let node = &self.nodes[node_id as usize];
147
148        let left_count = if node.left.is_leaf() {
149            assert!(self
150                .leaf_node_indices
151                .contains_key(node.left.children as usize));
152            1
153        } else {
154            let calculated_leaf_count =
155                self.assert_well_formed_topology_only_recurse(node.left.children);
156            let child = &self.nodes[node.left.children as usize];
157            assert_eq!(
158                self.parents[node.left.children as usize],
159                BvhNodeIndex::left(node_id)
160            );
161            assert_eq!(
162                node.left.leaf_count(),
163                child.left.leaf_count() + child.right.leaf_count()
164            );
165            assert_eq!(node.left.leaf_count(), calculated_leaf_count);
166            calculated_leaf_count
167        };
168
169        let right_count = if node.right.is_leaf() {
170            assert!(self
171                .leaf_node_indices
172                .contains_key(node.right.children as usize));
173            1
174        } else {
175            let calculated_leaf_count =
176                self.assert_well_formed_topology_only_recurse(node.right.children);
177            let child = &self.nodes[node.right.children as usize];
178            assert_eq!(
179                self.parents[node.right.children as usize],
180                BvhNodeIndex::right(node_id)
181            );
182            assert_eq!(
183                node.right.leaf_count(),
184                child.left.leaf_count() + child.right.leaf_count()
185            );
186            assert_eq!(calculated_leaf_count, node.right.leaf_count());
187            calculated_leaf_count
188        };
189
190        left_count + right_count
191    }
192
193    /// Panics if the nodes of `self` are not stored in depth-first order on its internal storage.
194    ///
195    /// Depth-first ordering of `self`’s internals are guaranteed by [`Self::refit`].
196    pub fn assert_is_depth_first(&self) {
197        if self.is_empty() || self.nodes[0].right.leaf_count() == 0 {
198            // Trivially correct and avoids special-cases afterward.
199            return;
200        }
201
202        let mut stack = alloc::vec![0];
203        let mut loop_id = 0;
204        while let Some(id) = stack.pop() {
205            assert_eq!(loop_id, id);
206            loop_id += 1;
207            let node = &self.nodes[id as usize];
208
209            if !node.right.is_leaf() {
210                stack.push(node.right.children);
211            }
212
213            if !node.left.is_leaf() {
214                stack.push(node.left.children);
215            }
216        }
217    }
218}