Skip to main content

box3d_rust/dynamic_tree/
rebuild.rs

1// Tree rebuild with the median-split heuristic from dynamic_tree.c.
2// The SAH heuristic (B3_TREE_HEURISTIC == 1) is compiled out upstream and is
3// not ported.
4// SPDX-FileCopyrightText: 2025 Erin Catto
5// SPDX-License-Identifier: MIT
6
7use super::{DynamicTree, ENLARGED_NODE, TREE_STACK_SIZE};
8use crate::core::NULL_INDEX;
9use crate::math_functions::{aabb_center, aabb_union, add, max, min, mul_sv, sub, Vec3};
10
11fn max_u16(a: u16, b: u16) -> u16 {
12    if a > b {
13        a
14    } else {
15        b
16    }
17}
18
19/// Median split heuristic. (static b3PartitionMid)
20fn partition_mid(indices: &mut [i32], centers: &mut [Vec3]) -> i32 {
21    let count = indices.len();
22
23    // Handle trivial case
24    if count <= 2 {
25        return (count / 2) as i32;
26    }
27
28    let mut lower_bound = centers[0];
29    let mut upper_bound = centers[0];
30
31    for center in centers.iter().skip(1) {
32        lower_bound = min(lower_bound, *center);
33        upper_bound = max(upper_bound, *center);
34    }
35
36    let d = sub(upper_bound, lower_bound);
37    let c = mul_sv(0.5, add(lower_bound, upper_bound));
38
39    // Partition longest axis using the Hoare partition scheme
40    // https://en.wikipedia.org/wiki/Quicksort
41    // https://nicholasvadivelu.com/2021/01/11/array-partition/
42    let (mut i1, mut i2) = (0usize, count);
43    if d.x >= d.y && d.x >= d.z {
44        let pivot = c.x;
45
46        while i1 < i2 {
47            while i1 < i2 && centers[i1].x < pivot {
48                i1 += 1;
49            }
50
51            while i1 < i2 && centers[i2 - 1].x >= pivot {
52                i2 -= 1;
53            }
54
55            if i1 < i2 {
56                indices.swap(i1, i2 - 1);
57                centers.swap(i1, i2 - 1);
58
59                i1 += 1;
60                i2 -= 1;
61            }
62        }
63    } else if d.y >= d.z {
64        let pivot = c.y;
65
66        while i1 < i2 {
67            while i1 < i2 && centers[i1].y < pivot {
68                i1 += 1;
69            }
70
71            while i1 < i2 && centers[i2 - 1].y >= pivot {
72                i2 -= 1;
73            }
74
75            if i1 < i2 {
76                indices.swap(i1, i2 - 1);
77                centers.swap(i1, i2 - 1);
78
79                i1 += 1;
80                i2 -= 1;
81            }
82        }
83    } else {
84        let pivot = c.z;
85
86        while i1 < i2 {
87            while i1 < i2 && centers[i1].z < pivot {
88                i1 += 1;
89            }
90
91            while i1 < i2 && centers[i2 - 1].z >= pivot {
92                i2 -= 1;
93            }
94
95            if i1 < i2 {
96                indices.swap(i1, i2 - 1);
97                centers.swap(i1, i2 - 1);
98
99                i1 += 1;
100                i2 -= 1;
101            }
102        }
103    }
104    debug_assert!(i1 == i2);
105
106    if i1 > 0 && i1 < count {
107        i1 as i32
108    } else {
109        (count / 2) as i32
110    }
111}
112
113/// Temporary data used to track the rebuild of a tree node.
114/// (struct b3RebuildItem)
115#[derive(Clone, Copy, Default)]
116struct RebuildItem {
117    node_index: i32,
118    child_count: i32,
119    // Leaf indices
120    start_index: i32,
121    split_index: i32,
122    end_index: i32,
123}
124
125impl DynamicTree {
126    /// Returns the root node index. (static b3BuildTree)
127    fn build_tree(&mut self, leaf_count: i32) -> i32 {
128        if leaf_count == 1 {
129            let leaf = self.leaf_indices[0];
130            self.nodes[leaf as usize].parent = NULL_INDEX;
131            return leaf;
132        }
133
134        let mut stack = vec![RebuildItem::default(); TREE_STACK_SIZE];
135        let mut top = 0usize;
136
137        stack[0].node_index = self.allocate_node();
138        stack[0].child_count = -1;
139        stack[0].start_index = 0;
140        stack[0].end_index = leaf_count;
141        stack[0].split_index = {
142            // Split borrows: partition operates on the rebuild scratch arrays.
143            let (indices, centers) = (&mut self.leaf_indices, &mut self.leaf_centers);
144            partition_mid(
145                &mut indices[..leaf_count as usize],
146                &mut centers[..leaf_count as usize],
147            )
148        };
149
150        loop {
151            stack[top].child_count += 1;
152            let item = stack[top];
153
154            if item.child_count == 2 {
155                // This internal node has both children established
156
157                if top == 0 {
158                    // all done
159                    break;
160                }
161
162                let parent_item = stack[top - 1];
163
164                if parent_item.child_count == 0 {
165                    debug_assert!(self.nodes[parent_item.node_index as usize].child1 == NULL_INDEX);
166                    self.nodes[parent_item.node_index as usize].child1 = item.node_index;
167                } else {
168                    debug_assert!(parent_item.child_count == 1);
169                    debug_assert!(self.nodes[parent_item.node_index as usize].child2 == NULL_INDEX);
170                    self.nodes[parent_item.node_index as usize].child2 = item.node_index;
171                }
172
173                let node_index = item.node_index as usize;
174                debug_assert!(self.nodes[node_index].parent == NULL_INDEX);
175                self.nodes[node_index].parent = parent_item.node_index;
176
177                debug_assert!(self.nodes[node_index].child1 != NULL_INDEX);
178                debug_assert!(self.nodes[node_index].child2 != NULL_INDEX);
179                let c1 = self.nodes[node_index].child1 as usize;
180                let c2 = self.nodes[node_index].child2 as usize;
181
182                self.nodes[node_index].aabb = aabb_union(self.nodes[c1].aabb, self.nodes[c2].aabb);
183                self.nodes[node_index].height =
184                    1 + max_u16(self.nodes[c1].height, self.nodes[c2].height);
185                self.nodes[node_index].category_bits =
186                    self.nodes[c1].category_bits | self.nodes[c2].category_bits;
187
188                // Pop stack
189                top -= 1;
190            } else {
191                let (start_index, end_index) = if item.child_count == 0 {
192                    (item.start_index, item.split_index)
193                } else {
194                    debug_assert!(item.child_count == 1);
195                    (item.split_index, item.end_index)
196                };
197
198                let count = end_index - start_index;
199
200                if count == 1 {
201                    let child_index = self.leaf_indices[start_index as usize];
202                    let node_index = item.node_index as usize;
203
204                    if item.child_count == 0 {
205                        debug_assert!(self.nodes[node_index].child1 == NULL_INDEX);
206                        self.nodes[node_index].child1 = child_index;
207                    } else {
208                        debug_assert!(item.child_count == 1);
209                        debug_assert!(self.nodes[node_index].child2 == NULL_INDEX);
210                        self.nodes[node_index].child2 = child_index;
211                    }
212
213                    debug_assert!(self.nodes[child_index as usize].parent == NULL_INDEX);
214                    self.nodes[child_index as usize].parent = item.node_index;
215                } else {
216                    debug_assert!(count > 0);
217                    debug_assert!(top < TREE_STACK_SIZE);
218
219                    top += 1;
220                    let node_index = self.allocate_node();
221                    let split_index = {
222                        let (s, e) = (start_index as usize, end_index as usize);
223                        let (indices, centers) = (&mut self.leaf_indices, &mut self.leaf_centers);
224                        partition_mid(&mut indices[s..e], &mut centers[s..e])
225                    };
226                    let new_item = &mut stack[top];
227                    new_item.node_index = node_index;
228                    new_item.child_count = -1;
229                    new_item.start_index = start_index;
230                    new_item.end_index = end_index;
231                    new_item.split_index = split_index + start_index;
232                }
233            }
234        }
235
236        let root_index = stack[0].node_index as usize;
237        debug_assert!(self.nodes[root_index].parent == NULL_INDEX);
238        debug_assert!(self.nodes[root_index].child1 != NULL_INDEX);
239        debug_assert!(self.nodes[root_index].child2 != NULL_INDEX);
240
241        let c1 = self.nodes[root_index].child1 as usize;
242        let c2 = self.nodes[root_index].child2 as usize;
243
244        self.nodes[root_index].aabb = aabb_union(self.nodes[c1].aabb, self.nodes[c2].aabb);
245        self.nodes[root_index].height = 1 + max_u16(self.nodes[c1].height, self.nodes[c2].height);
246        self.nodes[root_index].category_bits =
247            self.nodes[c1].category_bits | self.nodes[c2].category_bits;
248
249        stack[0].node_index
250    }
251
252    /// Rebuild the tree while retaining subtrees that haven't changed.
253    /// Returns the number of boxes sorted. (b3DynamicTree_Rebuild)
254    pub fn rebuild(&mut self, full_build: bool) -> i32 {
255        let proxy_count = self.proxy_count;
256        if proxy_count == 0 {
257            return 0;
258        }
259
260        // Ensure capacity for rebuild space
261        if proxy_count > self.rebuild_capacity {
262            let new_capacity = proxy_count + proxy_count / 2;
263
264            self.leaf_indices = vec![0; new_capacity as usize];
265            self.leaf_centers = vec![Vec3::default(); new_capacity as usize];
266            self.rebuild_capacity = new_capacity;
267        }
268
269        let mut leaf_count = 0usize;
270        let mut stack = [0i32; TREE_STACK_SIZE];
271        let mut stack_count = 0usize;
272
273        let mut node_index = self.root;
274
275        // Gather all proxy nodes that have grown and all internal nodes that
276        // haven't grown. Both are considered leaves in the tree rebuild.
277        // Free all internal nodes that have grown.
278        loop {
279            let node = self.nodes[node_index as usize];
280            if node.is_leaf() || (node.flags & ENLARGED_NODE == 0 && !full_build) {
281                self.leaf_indices[leaf_count] = node_index;
282                self.leaf_centers[leaf_count] = aabb_center(node.aabb);
283                leaf_count += 1;
284
285                // Detach
286                self.nodes[node_index as usize].parent = NULL_INDEX;
287            } else {
288                let doomed_node_index = node_index;
289
290                // Handle children
291                node_index = node.child1;
292
293                debug_assert!(stack_count < TREE_STACK_SIZE);
294                if stack_count < TREE_STACK_SIZE {
295                    stack[stack_count] = node.child2;
296                    stack_count += 1;
297                }
298
299                // Remove doomed node
300                self.free_node(doomed_node_index);
301
302                continue;
303            }
304
305            if stack_count == 0 {
306                break;
307            }
308
309            stack_count -= 1;
310            node_index = stack[stack_count];
311        }
312
313        debug_assert!(leaf_count <= proxy_count as usize);
314
315        self.root = self.build_tree(leaf_count as i32);
316
317        self.validate();
318
319        leaf_count as i32
320    }
321}