parry2d/partitioning/bvh/bvh_tree.rs
1use super::bvh_optimize::BvhIncrementalOptimizationState;
2use super::BvhOptimizationHeapEntry;
3use crate::bounding_volume::{Aabb, BoundingVolume};
4use crate::math::{Real, Vector};
5use crate::query::{Ray, RayCast};
6use crate::utils::VecMap;
7use alloc::collections::{BinaryHeap, VecDeque};
8use alloc::vec::Vec;
9use core::ops::{Deref, DerefMut, Index, IndexMut};
10
11/// The strategy for one-time build of the BVH tree.
12///
13/// This enum controls which algorithm is used when constructing a BVH from scratch. Different
14/// strategies offer different trade-offs between construction speed and final tree quality
15/// (measured by ray-casting performance and other query efficiency).
16///
17/// # Strategy Comparison
18///
19/// - **Binned**: Fast construction with good overall quality. Best for general-purpose use.
20/// - **PLOC**: Slower construction but produces higher quality trees. Best when ray-casting
21/// performance is critical and construction time is less important.
22///
23/// # Performance Notes
24///
25/// - Neither strategy is currently parallelized, though PLOC is designed to support parallelization.
26/// - Tree quality affects query performance: better trees mean fewer node visits during traversals.
27/// - For dynamic scenes with frequent updates, choose based on initial construction performance.
28///
29/// # Example
30///
31/// ```rust
32/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
33/// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
34/// use parry3d::bounding_volume::Aabb;
35/// use parry3d::math::Vector;
36///
37/// // Create some AABBs for objects in the scene
38/// let aabbs = vec![
39/// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
40/// Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)),
41/// Aabb::new(Vector::new(0.0, 2.0, 0.0), Vector::new(1.0, 3.0, 1.0)),
42/// ];
43///
44/// // Use binned strategy for general purpose (default)
45/// let bvh_binned = Bvh::from_leaves(BvhBuildStrategy::Binned, &aabbs);
46/// assert_eq!(bvh_binned.leaf_count(), 3);
47///
48/// // Use PLOC strategy for ray-casting heavy applications
49/// let bvh_ploc = Bvh::from_leaves(BvhBuildStrategy::Ploc, &aabbs);
50/// assert_eq!(bvh_ploc.leaf_count(), 3);
51/// # }
52/// ```
53///
54/// # See Also
55///
56/// - [`Bvh::from_leaves`] - Construct a BVH using a specific strategy
57/// - [`Bvh::from_iter`] - Construct a BVH from an iterator
58#[derive(Default, Clone, Debug, Copy, PartialEq, Eq)]
59pub enum BvhBuildStrategy {
60 /// The tree is built using the binned strategy.
61 ///
62 /// This implements the strategy from "On fast Construction of SAH-based Bounding Volume Hierarchies"
63 /// by Ingo Wald. It uses binning to quickly approximate the Surface Area Heuristic (SAH) cost
64 /// function, resulting in fast construction times with good tree quality.
65 ///
66 /// **Recommended for**: General-purpose usage, dynamic scenes, initial prototyping.
67 #[default]
68 Binned,
69 /// The tree is built using the Locally-Ordered Clustering technique.
70 ///
71 /// This implements the strategy from "Parallel Locally-Ordered Clustering for Bounding Volume
72 /// Hierarchy Construction" by Meister and Bittner. It produces higher quality trees at the cost
73 /// of slower construction. The algorithm is designed for parallelization but the current
74 /// implementation is sequential.
75 ///
76 /// **Recommended for**: Ray-casting heavy workloads, static scenes, when query performance
77 /// is more important than construction time.
78 Ploc,
79}
80
81/// Workspace data for various operations on the BVH tree.
82///
83/// This structure holds temporary buffers and working memory used during BVH operations
84/// such as refitting, rebuilding, and optimization. The data inside can be freed at any time
85/// without affecting the correctness of BVH results.
86///
87/// # Purpose
88///
89/// Many BVH operations require temporary allocations for intermediate results. By reusing
90/// the same `BvhWorkspace` across multiple operations, you can significantly reduce allocation
91/// overhead and improve performance, especially in performance-critical loops.
92///
93/// # Usage Pattern
94///
95/// 1. Create a workspace once (or use [`Default::default()`])
96/// 2. Pass it to BVH operations that accept a workspace parameter
97/// 3. Reuse the same workspace for subsequent operations
98/// 4. The workspace grows to accommodate the largest operation size
99///
100/// # Memory Management
101///
102/// - The workspace grows as needed but never automatically shrinks
103/// - You can drop and recreate the workspace to free memory
104/// - All data is private and managed internally by the BVH
105///
106/// # Example
107///
108/// ```rust
109/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
110/// use parry3d::partitioning::{Bvh, BvhBuildStrategy, BvhWorkspace};
111/// use parry3d::bounding_volume::Aabb;
112/// use parry3d::math::Vector;
113///
114/// let aabbs = vec![
115/// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
116/// Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)),
117/// ];
118///
119/// let mut bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
120/// let mut workspace = BvhWorkspace::default();
121///
122/// // Refit the tree after leaf movements
123/// bvh.refit(&mut workspace);
124///
125/// // Reuse the same workspace for optimization
126/// bvh.optimize_incremental(&mut workspace);
127///
128/// // The workspace can be reused across multiple BVH operations
129/// # }
130/// ```
131///
132/// # See Also
133///
134/// - [`Bvh::refit`] - Update AABBs after leaf movement
135/// - [`Bvh::optimize_incremental`](Bvh::optimize_incremental) - Incremental tree optimization
136#[derive(Clone, Default)]
137pub struct BvhWorkspace {
138 pub(super) refit_tmp: BvhNodeVec,
139 pub(super) rebuild_leaves: Vec<BvhNode>,
140 pub(super) optimization_roots: Vec<u32>,
141 pub(super) queue: BinaryHeap<BvhOptimizationHeapEntry>,
142 pub(super) dequeue: VecDeque<u32>,
143 pub(super) traversal_stack: Vec<u32>,
144}
145
146/// A piece of data packing state flags as well as leaf counts for a BVH tree node.
147#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
148#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
149#[cfg_attr(
150 feature = "rkyv",
151 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
152)]
153#[repr(transparent)]
154pub struct BvhNodeData(u32);
155const CHANGED: u32 = 0b01;
156const CHANGE_PENDING: u32 = 0b11;
157
158impl BvhNodeData {
159 #[inline(always)]
160 pub(super) fn with_leaf_count_and_pending_change(leaf_count: u32) -> Self {
161 Self(leaf_count | (CHANGE_PENDING << 30))
162 }
163
164 #[inline(always)]
165 pub(super) fn leaf_count(self) -> u32 {
166 self.0 & 0x3fff_ffff
167 }
168
169 #[inline(always)]
170 pub(super) fn is_changed(self) -> bool {
171 self.0 >> 30 == CHANGED
172 }
173
174 #[inline(always)]
175 pub(super) fn is_change_pending(self) -> bool {
176 self.0 >> 30 == CHANGE_PENDING
177 }
178
179 #[inline(always)]
180 pub(super) fn add_leaf_count(&mut self, added: u32) {
181 self.0 += added;
182 }
183
184 #[inline(always)]
185 pub(super) fn set_change_pending(&mut self) {
186 self.0 |= CHANGE_PENDING << 30;
187 }
188
189 /// Collapses any change flag (pending or resolved) into the resolved `CHANGED` state.
190 ///
191 /// Used by partial refitting on internal nodes: a pending flag on an internal node
192 /// would otherwise read as "unchanged" (`is_changed() == false`) during traversals.
193 #[inline(always)]
194 pub(super) fn normalize_change_flag(&mut self) {
195 if self.0 >> 30 != 0 {
196 *self = Self((self.0 & 0x3fff_ffff) | (CHANGED << 30));
197 }
198 }
199
200 #[inline(always)]
201 pub(super) fn resolve_pending_change(&mut self) {
202 if self.is_change_pending() {
203 *self = Self((self.0 & 0x3fff_ffff) | (CHANGED << 30));
204 } else {
205 *self = Self(self.0 & 0x3fff_ffff);
206 }
207 }
208
209 pub(super) fn merged(self, other: Self) -> Self {
210 let leaf_count = self.leaf_count() + other.leaf_count();
211 let changed = (self.0 >> 30) | (other.0 >> 30);
212 Self(leaf_count | changed << 30)
213 }
214}
215
216/// A pair of tree nodes forming a 2-wide BVH node.
217///
218/// The BVH uses a memory layout where nodes are stored in pairs (left and right children)
219/// to improve cache coherency and enable SIMD optimizations. This structure represents
220/// a single entry in the BVH's node array.
221///
222/// # Node Validity
223///
224/// Both `left` and `right` are guaranteed to be valid except for one special case:
225/// - **Single leaf tree**: Only `left` is valid, `right` is zeroed
226/// - **All other cases**: Both `left` and `right` are valid (tree has at least 2 leaves)
227///
228/// # Memory Layout
229///
230/// In 3D with f32 precision and SIMD enabled, this structure is:
231/// - **Size**: 64 bytes (cache line aligned)
232/// - **Alignment**: 64 bytes (matches typical CPU cache lines)
233/// - This alignment improves performance by reducing cache misses
234///
235/// # Example
236///
237/// ```rust
238/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
239/// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
240/// use parry3d::bounding_volume::Aabb;
241/// use parry3d::math::Vector;
242///
243/// let aabbs = vec![
244/// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
245/// Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)),
246/// Aabb::new(Vector::new(4.0, 0.0, 0.0), Vector::new(5.0, 1.0, 1.0)),
247/// ];
248///
249/// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
250///
251/// // Access the root node's children
252/// // The BVH stores nodes as BvhNodeWide pairs internally
253/// assert_eq!(bvh.leaf_count(), 3);
254/// # }
255/// ```
256///
257/// # See Also
258///
259/// - [`BvhNode`] - Individual node in the pair
260/// - [`Bvh`] - The main BVH structure
261#[derive(Copy, Clone, Debug)]
262#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
263#[cfg_attr(
264 feature = "rkyv",
265 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
266)]
267#[repr(C)]
268// PERF: the size of this struct is 64 bytes but has a default alignment of 16 (in f32 + 3d + simd mode).
269// Forcing an alignment of 64 won’t add padding, and makes aligns it with most cache lines.
270#[cfg_attr(all(feature = "dim3", feature = "f32"), repr(align(64)))]
271pub struct BvhNodeWide {
272 pub(super) left: BvhNode,
273 pub(super) right: BvhNode,
274}
275
276// NOTE: if this assertion fails with a weird "0 - 1 would overflow" error, it means the equality doesn’t hold.
277#[cfg(all(feature = "dim3", feature = "f32"))]
278static_assertions::const_assert_eq!(align_of::<BvhNodeWide>(), 64);
279#[cfg(all(feature = "dim3", feature = "f32"))]
280static_assertions::assert_eq_size!(BvhNodeWide, [u8; 64]);
281
282impl BvhNodeWide {
283 /// Creates a new `BvhNodeWide` with both children zeroed out.
284 ///
285 /// This is primarily used internally during BVH construction and should rarely
286 /// be needed in user code.
287 ///
288 /// # Example
289 ///
290 /// ```
291 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
292 /// use parry3d::partitioning::BvhNodeWide;
293 ///
294 /// let node_wide = BvhNodeWide::zeros();
295 /// assert_eq!(node_wide.leaf_count(), 0);
296 /// # }
297 /// ```
298 #[inline(always)]
299 pub fn zeros() -> Self {
300 Self {
301 left: BvhNode::zeros(),
302 right: BvhNode::zeros(),
303 }
304 }
305
306 /// Returns the two nodes as an array of references.
307 ///
308 /// This is useful for accessing the left or right node by index (0 or 1 respectively)
309 /// instead of by name. Index 0 is the left node, index 1 is the right node.
310 ///
311 /// # Example
312 ///
313 /// ```
314 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
315 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
316 /// use parry3d::bounding_volume::{Aabb, BoundingVolume};
317 /// use parry3d::math::Vector;
318 ///
319 /// let aabbs = vec![
320 /// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
321 /// Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)),
322 /// ];
323 ///
324 /// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
325 /// // The root AABB should contain both leaves
326 /// assert!(bvh.root_aabb().contains(&aabbs[0]));
327 /// assert!(bvh.root_aabb().contains(&aabbs[1]));
328 /// # }
329 /// ```
330 ///
331 /// # See Also
332 ///
333 /// - [`as_array_mut`](Self::as_array_mut) - Mutable version
334 #[inline(always)]
335 pub fn as_array(&self) -> [&BvhNode; 2] {
336 [&self.left, &self.right]
337 }
338
339 /// Returns the two nodes as an array of mutable references.
340 ///
341 /// This is useful for modifying the left or right node by index (0 or 1 respectively)
342 /// instead of by name. Index 0 is the left node, index 1 is the right node.
343 ///
344 /// # Example
345 ///
346 /// ```
347 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
348 /// use parry3d::partitioning::BvhNodeWide;
349 /// use parry3d::math::Vector;
350 ///
351 /// let mut node_wide = BvhNodeWide::zeros();
352 /// let nodes = node_wide.as_array_mut();
353 ///
354 /// // Scale both nodes by 2.0
355 /// let scale = Vector::splat(2.0);
356 /// nodes[0].scale(scale);
357 /// nodes[1].scale(scale);
358 /// # }
359 /// ```
360 ///
361 /// # See Also
362 ///
363 /// - [`as_array`](Self::as_array) - Immutable version
364 #[inline(always)]
365 pub fn as_array_mut(&mut self) -> [&mut BvhNode; 2] {
366 [&mut self.left, &mut self.right]
367 }
368
369 /// Merges both child nodes to create their parent node.
370 ///
371 /// The parent's AABB will be the union of both children's AABBs, and the parent's
372 /// leaf count will be the sum of both children's leaf counts. The `my_id` parameter
373 /// becomes the parent's `children` field, pointing back to this `BvhNodeWide`.
374 ///
375 /// # Arguments
376 ///
377 /// * `my_id` - The index of this `BvhNodeWide` in the BVH's node array
378 ///
379 /// # Returns
380 ///
381 /// A new `BvhNode` representing the parent of both children.
382 pub fn merged(&self, my_id: u32) -> BvhNode {
383 self.left.merged(&self.right, my_id)
384 }
385
386 /// Returns the total number of leaves contained in both child nodes.
387 ///
388 /// This is the sum of the leaf counts of the left and right children. For leaf
389 /// nodes, the count is 1. For internal nodes, it's the sum of their descendants.
390 ///
391 /// # Returns
392 ///
393 /// The total number of leaves in the subtrees rooted at both children.
394 pub fn leaf_count(&self) -> u32 {
395 self.left.leaf_count() + self.right.leaf_count()
396 }
397}
398
399#[repr(C)] // SAFETY: needed to ensure SIMD aabb checks rely on the layout.
400#[cfg(all(feature = "dim3", feature = "f32"))]
401pub(super) struct BvhNodeSimd {
402 mins: glamx::Vec3A,
403 maxs: glamx::Vec3A,
404}
405
406// SAFETY: compile-time assertions to ensure we can transmute between `BvhNode` and `BvhNodeSimd`.
407#[cfg(all(feature = "dim3", feature = "f32"))]
408static_assertions::assert_eq_align!(BvhNode, BvhNodeSimd);
409#[cfg(all(feature = "dim3", feature = "f32"))]
410static_assertions::assert_eq_size!(BvhNode, BvhNodeSimd);
411
412/// A single node (internal or leaf) of a BVH.
413///
414/// Each node stores an axis-aligned bounding box (AABB) that encompasses all geometry
415/// contained within its subtree. A node is either:
416/// - **Leaf**: Contains a single piece of geometry (leaf_count == 1)
417/// - **Internal**: Contains two child nodes (leaf_count > 1)
418///
419/// # Structure
420///
421/// - **AABB**: Stored as separate `mins` and `maxs` points for efficiency
422/// - **Children**: For internal nodes, index to a `BvhNodeWide` containing two child nodes.
423/// For leaf nodes, this is the user-provided leaf data (typically an index).
424/// - **Leaf Count**: Number of leaves in the subtree (1 for leaves, sum of children for internal)
425///
426/// # Memory Layout
427///
428/// The structure is carefully laid out for optimal performance:
429/// - In 3D with f32: 32 bytes, 16-byte aligned (for SIMD operations)
430/// - Fields ordered to enable efficient SIMD AABB tests
431/// - The `#[repr(C)]` ensures predictable layout for unsafe optimizations
432///
433/// # Example
434///
435/// ```rust
436/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
437/// use parry3d::partitioning::BvhNode;
438/// use parry3d::bounding_volume::Aabb;
439/// use parry3d::math::Vector;
440///
441/// // Create a leaf node
442/// let aabb = Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0));
443/// let leaf = BvhNode::leaf(aabb, 42);
444///
445/// assert!(leaf.is_leaf());
446/// assert_eq!(leaf.leaf_data(), Some(42));
447/// assert_eq!(leaf.aabb(), aabb);
448/// # }
449/// ```
450///
451/// # See Also
452///
453/// - `BvhNodeWide` - Pair of nodes stored together
454/// - [`Bvh`] - The main BVH structure
455#[derive(Copy, Clone, Debug, PartialEq)]
456#[repr(C)] // SAFETY: needed to ensure SIMD aabb checks rely on the layout.
457#[cfg_attr(all(feature = "f32", feature = "dim3"), repr(align(16)))]
458#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
459#[cfg_attr(
460 feature = "rkyv",
461 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
462)]
463pub struct BvhNode {
464 /// Mins coordinates of the node’s bounding volume.
465 pub(super) mins: Vector,
466 /// Children of this node. A node has either 0 (i.e. it’s a leaf) or 2 children.
467 ///
468 /// If [`Self::leaf_count`] is 1, then the node has 0 children and is a leaf.
469 pub(super) children: u32,
470 /// Maxs coordinates of this node’s bounding volume.
471 pub(super) maxs: Vector,
472 /// Packed data associated to this node (leaf count and flags).
473 pub(super) data: BvhNodeData,
474}
475
476impl BvhNode {
477 #[inline(always)]
478 pub(super) fn zeros() -> Self {
479 Self {
480 mins: Vector::ZERO,
481 children: 0,
482 maxs: Vector::ZERO,
483 data: BvhNodeData(0),
484 }
485 }
486
487 /// Creates a new leaf node with the given AABB and user data.
488 ///
489 /// Leaf nodes represent actual geometry in the scene. Each leaf stores:
490 /// - The AABB of the geometry it represents
491 /// - A user-provided `leaf_data` value (typically an index into your geometry array)
492 ///
493 /// # Arguments
494 ///
495 /// * `aabb` - The axis-aligned bounding box for this leaf's geometry
496 /// * `leaf_data` - User data associated with this leaf (typically an index or ID)
497 ///
498 /// # Returns
499 ///
500 /// A new `BvhNode` representing a leaf with the given properties.
501 ///
502 /// # Example
503 ///
504 /// ```
505 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
506 /// use parry3d::partitioning::BvhNode;
507 /// use parry3d::bounding_volume::Aabb;
508 /// use parry3d::math::Vector;
509 ///
510 /// // Create an AABB for a unit cube
511 /// let aabb = Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0));
512 ///
513 /// // Create a leaf node with index 0
514 /// let leaf = BvhNode::leaf(aabb, 0);
515 ///
516 /// assert!(leaf.is_leaf());
517 /// assert_eq!(leaf.leaf_data(), Some(0));
518 /// assert_eq!(leaf.aabb(), aabb);
519 /// # }
520 /// ```
521 ///
522 /// # See Also
523 ///
524 /// - [`is_leaf`](Self::is_leaf) - Check if a node is a leaf
525 /// - [`leaf_data`](Self::leaf_data) - Get the leaf data back
526 #[inline(always)]
527 pub fn leaf(aabb: Aabb, leaf_data: u32) -> BvhNode {
528 Self {
529 mins: aabb.mins,
530 maxs: aabb.maxs,
531 children: leaf_data,
532 data: BvhNodeData::with_leaf_count_and_pending_change(1),
533 }
534 }
535
536 /// Returns the user data associated with this leaf node, if it is a leaf.
537 ///
538 /// For leaf nodes, this returns the `leaf_data` value that was provided when the
539 /// leaf was created (typically an index into your geometry array). For internal
540 /// nodes, this returns `None`.
541 ///
542 /// # Returns
543 ///
544 /// - `Some(leaf_data)` if this is a leaf node
545 /// - `None` if this is an internal node
546 ///
547 /// # Example
548 ///
549 /// ```
550 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
551 /// use parry3d::partitioning::BvhNode;
552 /// use parry3d::bounding_volume::Aabb;
553 /// use parry3d::math::Vector;
554 ///
555 /// let aabb = Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0));
556 /// let leaf = BvhNode::leaf(aabb, 42);
557 ///
558 /// assert_eq!(leaf.leaf_data(), Some(42));
559 /// # }
560 /// ```
561 ///
562 /// # See Also
563 ///
564 /// - [`leaf`](Self::leaf) - Create a leaf node
565 /// - [`is_leaf`](Self::is_leaf) - Check if a node is a leaf
566 #[inline(always)]
567 pub fn leaf_data(&self) -> Option<u32> {
568 self.is_leaf().then_some(self.children)
569 }
570
571 /// Returns `true` if this node is a leaf.
572 ///
573 /// A node is a leaf if its leaf count is exactly 1, meaning it represents a single
574 /// piece of geometry rather than a subtree of nodes.
575 ///
576 /// # Returns
577 ///
578 /// `true` if this is a leaf node, `false` if it's an internal node.
579 ///
580 /// # Example
581 ///
582 /// ```
583 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
584 /// use parry3d::partitioning::BvhNode;
585 /// use parry3d::bounding_volume::Aabb;
586 /// use parry3d::math::Vector;
587 ///
588 /// let aabb = Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0));
589 /// let leaf = BvhNode::leaf(aabb, 0);
590 ///
591 /// assert!(leaf.is_leaf());
592 /// # }
593 /// ```
594 ///
595 /// # See Also
596 ///
597 /// - [`leaf_data`](Self::leaf_data) - Get the leaf's user data
598 #[inline(always)]
599 pub fn is_leaf(&self) -> bool {
600 self.leaf_count() == 1
601 }
602
603 #[inline(always)]
604 pub(super) fn leaf_count(&self) -> u32 {
605 self.data.leaf_count()
606 }
607
608 #[inline(always)]
609 #[cfg(all(feature = "dim3", feature = "f32"))]
610 pub(super) fn as_simd(&self) -> &BvhNodeSimd {
611 // SAFETY: BvhNode is declared with the alignment
612 // and size of two SimdReal.
613 unsafe { core::mem::transmute(self) }
614 }
615
616 #[inline(always)]
617 pub(super) fn merged(&self, other: &Self, children: u32) -> Self {
618 Self {
619 mins: self.mins.min(other.mins),
620 children,
621 maxs: self.maxs.max(other.maxs),
622 data: self.data.merged(other.data),
623 }
624 }
625
626 /// Returns the minimum corner of this node's AABB.
627 ///
628 /// The AABB (axis-aligned bounding box) is defined by two corners: the minimum
629 /// corner (with the smallest coordinates on all axes) and the maximum corner.
630 ///
631 /// # Returns
632 ///
633 /// A point representing the minimum corner of the AABB.
634 ///
635 /// # Example
636 ///
637 /// ```
638 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
639 /// use parry3d::partitioning::BvhNode;
640 /// use parry3d::bounding_volume::Aabb;
641 /// use parry3d::math::Vector;
642 ///
643 /// let aabb = Aabb::new(Vector::new(1.0, 2.0, 3.0), Vector::new(4.0, 5.0, 6.0));
644 /// let node = BvhNode::leaf(aabb, 0);
645 ///
646 /// assert_eq!(node.mins(), Vector::new(1.0, 2.0, 3.0));
647 /// # }
648 /// ```
649 ///
650 /// # See Also
651 ///
652 /// - [`maxs`](Self::maxs) - Get the maximum corner
653 /// - [`aabb`](Self::aabb) - Get the full AABB
654 #[inline]
655 pub fn mins(&self) -> Vector {
656 self.mins
657 }
658
659 /// Returns the maximum corner of this node's AABB.
660 ///
661 /// The AABB (axis-aligned bounding box) is defined by two corners: the minimum
662 /// corner and the maximum corner (with the largest coordinates on all axes).
663 ///
664 /// # Returns
665 ///
666 /// A point representing the maximum corner of the AABB.
667 ///
668 /// # Example
669 ///
670 /// ```
671 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
672 /// use parry3d::partitioning::BvhNode;
673 /// use parry3d::bounding_volume::Aabb;
674 /// use parry3d::math::Vector;
675 ///
676 /// let aabb = Aabb::new(Vector::new(1.0, 2.0, 3.0), Vector::new(4.0, 5.0, 6.0));
677 /// let node = BvhNode::leaf(aabb, 0);
678 ///
679 /// assert_eq!(node.maxs(), Vector::new(4.0, 5.0, 6.0));
680 /// # }
681 /// ```
682 ///
683 /// # See Also
684 ///
685 /// - [`mins`](Self::mins) - Get the minimum corner
686 /// - [`aabb`](Self::aabb) - Get the full AABB
687 #[inline]
688 pub fn maxs(&self) -> Vector {
689 self.maxs
690 }
691
692 /// Returns this node's AABB as an `Aabb` struct.
693 ///
694 /// Nodes store their AABBs as separate `mins` and `maxs` points for efficiency.
695 /// This method reconstructs the full `Aabb` structure.
696 ///
697 /// # Returns
698 ///
699 /// An `Aabb` representing this node's bounding box.
700 ///
701 /// # Example
702 ///
703 /// ```
704 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
705 /// use parry3d::partitioning::BvhNode;
706 /// use parry3d::bounding_volume::Aabb;
707 /// use parry3d::math::Vector;
708 ///
709 /// let original_aabb = Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0));
710 /// let node = BvhNode::leaf(original_aabb, 0);
711 ///
712 /// assert_eq!(node.aabb(), original_aabb);
713 /// # }
714 /// ```
715 ///
716 /// # See Also
717 ///
718 /// - [`mins`](Self::mins) - Get just the minimum corner
719 /// - [`maxs`](Self::maxs) - Get just the maximum corner
720 #[inline]
721 pub fn aabb(&self) -> Aabb {
722 Aabb {
723 mins: self.mins,
724 maxs: self.maxs,
725 }
726 }
727
728 /// Returns the center point of this node's AABB.
729 ///
730 /// The center is calculated as the midpoint between the minimum and maximum corners
731 /// on all axes: `(mins + maxs) / 2`.
732 ///
733 /// # Returns
734 ///
735 /// A point representing the center of the AABB.
736 ///
737 /// # Example
738 ///
739 /// ```
740 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
741 /// use parry3d::partitioning::BvhNode;
742 /// use parry3d::bounding_volume::Aabb;
743 /// use parry3d::math::Vector;
744 ///
745 /// let aabb = Aabb::new(Vector::ZERO, Vector::new(2.0, 4.0, 6.0));
746 /// let node = BvhNode::leaf(aabb, 0);
747 ///
748 /// assert_eq!(node.center(), Vector::new(1.0, 2.0, 3.0));
749 /// # }
750 /// ```
751 #[inline]
752 pub fn center(&self) -> Vector {
753 self.mins.midpoint(self.maxs)
754 }
755
756 /// Returns `true` if this node has been marked as changed.
757 ///
758 /// The BVH uses change tracking during incremental updates to identify which parts
759 /// of the tree need refitting or optimization. This flag is set when a node or its
760 /// descendants have been modified.
761 ///
762 /// # Returns
763 ///
764 /// `true` if the node is marked as changed, `false` otherwise.
765 ///
766 /// # Example
767 ///
768 /// ```
769 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
770 /// use parry3d::partitioning::BvhNode;
771 /// use parry3d::bounding_volume::Aabb;
772 /// use parry3d::math::Vector;
773 ///
774 /// let aabb = Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0));
775 /// let node = BvhNode::leaf(aabb, 0);
776 ///
777 /// // New leaf nodes are marked as changed (pending change)
778 /// // This is used internally for tracking modifications
779 /// # }
780 /// ```
781 ///
782 /// # See Also
783 ///
784 /// - [`Bvh::refit`] - Uses change tracking to update the tree
785 #[inline(always)]
786 pub fn is_changed(&self) -> bool {
787 self.data.is_changed()
788 }
789
790 /// Scales this node's AABB by the given factor.
791 ///
792 /// Each coordinate of both the minimum and maximum corners is multiplied by the
793 /// corresponding component of the scale vector. This is useful when scaling an
794 /// entire scene or object.
795 ///
796 /// # Arguments
797 ///
798 /// * `scale` - The scale factor to apply (per-axis)
799 ///
800 /// # Example
801 ///
802 /// ```
803 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
804 /// use parry3d::partitioning::BvhNode;
805 /// use parry3d::bounding_volume::Aabb;
806 /// use parry3d::math::Vector;
807 ///
808 /// let aabb = Aabb::new(Vector::new(1.0, 1.0, 1.0), Vector::new(2.0, 2.0, 2.0));
809 /// let mut node = BvhNode::leaf(aabb, 0);
810 ///
811 /// node.scale(Vector::new(2.0, 2.0, 2.0));
812 ///
813 /// assert_eq!(node.mins(), Vector::new(2.0, 2.0, 2.0));
814 /// assert_eq!(node.maxs(), Vector::new(4.0, 4.0, 4.0));
815 /// # }
816 /// ```
817 ///
818 /// # See Also
819 ///
820 /// - [`Bvh::scale`] - Scale an entire BVH tree
821 #[inline]
822 pub fn scale(&mut self, scale: Vector) {
823 let new_mins = self.mins * scale;
824 let new_maxs = self.maxs * scale;
825 // When scale has negative components, mins/maxs swap on those axes.
826 // Use component-wise min/max to maintain the AABB invariant (mins <= maxs).
827 self.mins = new_mins.min(new_maxs);
828 self.maxs = new_mins.max(new_maxs);
829 }
830
831 /// Calculates the volume of this node's AABB.
832 ///
833 /// The volume is the product of the extents on all axes:
834 /// - In 2D: width × height (returns area)
835 /// - In 3D: width × height × depth (returns volume)
836 ///
837 /// # Returns
838 ///
839 /// The volume (or area in 2D) of the AABB.
840 ///
841 /// # Example
842 ///
843 /// ```
844 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
845 /// use parry3d::partitioning::BvhNode;
846 /// use parry3d::bounding_volume::Aabb;
847 /// use parry3d::math::Vector;
848 ///
849 /// // Create a 2×3×4 box
850 /// let aabb = Aabb::new(Vector::ZERO, Vector::new(2.0, 3.0, 4.0));
851 /// let node = BvhNode::leaf(aabb, 0);
852 ///
853 /// assert_eq!(node.volume(), 24.0); // 2 * 3 * 4 = 24
854 /// # }
855 /// ```
856 ///
857 /// # See Also
858 ///
859 /// - [`merged_volume`](Self::merged_volume) - Volume of merged AABBs
860 #[inline]
861 pub fn volume(&self) -> Real {
862 // TODO PERF: simd optimizations?
863 let extents = self.maxs - self.mins;
864 #[cfg(feature = "dim2")]
865 return extents.x * extents.y;
866 #[cfg(feature = "dim3")]
867 return extents.x * extents.y * extents.z;
868 }
869
870 /// Calculates the volume of the AABB that would result from merging this node with another.
871 ///
872 /// This computes the volume of the smallest AABB that would contain both this node's
873 /// AABB and the other node's AABB, without actually creating the merged AABB. This is
874 /// useful during BVH construction for evaluating different tree configurations.
875 ///
876 /// # Arguments
877 ///
878 /// * `other` - The other node to merge with
879 ///
880 /// # Returns
881 ///
882 /// The volume (or area in 2D) of the merged AABB.
883 ///
884 /// # Performance
885 ///
886 /// This is more efficient than creating the merged AABB and then computing its volume.
887 ///
888 /// # Example
889 ///
890 /// ```
891 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
892 /// use parry3d::partitioning::BvhNode;
893 /// use parry3d::bounding_volume::Aabb;
894 /// use parry3d::math::Vector;
895 ///
896 /// let aabb1 = Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0));
897 /// let aabb2 = Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0));
898 ///
899 /// let node1 = BvhNode::leaf(aabb1, 0);
900 /// let node2 = BvhNode::leaf(aabb2, 1);
901 ///
902 /// // Merged AABB spans from (0,0,0) to (3,1,1) = 3×1×1 = 3
903 /// assert_eq!(node1.merged_volume(&node2), 3.0);
904 /// # }
905 /// ```
906 ///
907 /// # See Also
908 ///
909 /// - [`volume`](Self::volume) - Volume of a single node
910 pub fn merged_volume(&self, other: &Self) -> Real {
911 // TODO PERF: simd optimizations?
912 let mins = self.mins.min(other.mins);
913 let maxs = self.maxs.max(other.maxs);
914 let extents = maxs - mins;
915
916 #[cfg(feature = "dim2")]
917 return extents.x * extents.y;
918 #[cfg(feature = "dim3")]
919 return extents.x * extents.y * extents.z;
920 }
921
922 /// Tests if this node's AABB intersects another node's AABB.
923 ///
924 /// Two AABBs intersect if they overlap on all axes. This includes cases where
925 /// they only touch at their boundaries.
926 ///
927 /// # Arguments
928 ///
929 /// * `other` - The other node to test intersection with
930 ///
931 /// # Returns
932 ///
933 /// `true` if the AABBs intersect, `false` otherwise.
934 ///
935 /// # Performance
936 ///
937 /// In 3D with f32, this uses vectorized comparisons for improved
938 /// performance.
939 ///
940 /// # Example
941 ///
942 /// ```
943 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
944 /// use parry3d::partitioning::BvhNode;
945 /// use parry3d::bounding_volume::Aabb;
946 /// use parry3d::math::Vector;
947 ///
948 /// let aabb1 = Aabb::new(Vector::ZERO, Vector::new(2.0, 2.0, 2.0));
949 /// let aabb2 = Aabb::new(Vector::new(1.0, 1.0, 1.0), Vector::new(3.0, 3.0, 3.0));
950 /// let aabb3 = Aabb::new(Vector::new(5.0, 5.0, 5.0), Vector::new(6.0, 6.0, 6.0));
951 ///
952 /// let node1 = BvhNode::leaf(aabb1, 0);
953 /// let node2 = BvhNode::leaf(aabb2, 1);
954 /// let node3 = BvhNode::leaf(aabb3, 2);
955 ///
956 /// assert!(node1.intersects(&node2)); // Overlapping
957 /// assert!(!node1.intersects(&node3)); // Separated
958 /// # }
959 /// ```
960 ///
961 /// # See Also
962 ///
963 /// - [`contains`](Self::contains) - Check full containment
964 #[cfg(not(all(feature = "dim3", feature = "f32")))]
965 pub fn intersects(&self, other: &Self) -> bool {
966 self.mins.cmple(other.maxs).all() && self.maxs.cmpge(other.mins).all()
967 }
968
969 /// Tests if this node's AABB intersects another node's AABB.
970 ///
971 /// Two AABBs intersect if they overlap on all axes. This includes cases where
972 /// they only touch at their boundaries.
973 ///
974 /// # Arguments
975 ///
976 /// * `other` - The other node to test intersection with
977 ///
978 /// # Returns
979 ///
980 /// `true` if the AABBs intersect, `false` otherwise.
981 ///
982 /// # Performance
983 ///
984 /// This version uses SIMD optimizations for improved performance on supported platforms.
985 ///
986 /// # Example
987 ///
988 /// ```
989 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
990 /// use parry3d::partitioning::BvhNode;
991 /// use parry3d::bounding_volume::Aabb;
992 /// use parry3d::math::Vector;
993 ///
994 /// let aabb1 = Aabb::new(Vector::ZERO, Vector::new(2.0, 2.0, 2.0));
995 /// let aabb2 = Aabb::new(Vector::new(1.0, 1.0, 1.0), Vector::new(3.0, 3.0, 3.0));
996 /// let aabb3 = Aabb::new(Vector::new(5.0, 5.0, 5.0), Vector::new(6.0, 6.0, 6.0));
997 ///
998 /// let node1 = BvhNode::leaf(aabb1, 0);
999 /// let node2 = BvhNode::leaf(aabb2, 1);
1000 /// let node3 = BvhNode::leaf(aabb3, 2);
1001 ///
1002 /// assert!(node1.intersects(&node2)); // Overlapping
1003 /// assert!(!node1.intersects(&node3)); // Separated
1004 /// # }
1005 /// ```
1006 ///
1007 /// # See Also
1008 ///
1009 /// - [`contains`](Self::contains) - Check full containment
1010 #[cfg(all(feature = "dim3", feature = "f32"))]
1011 pub fn intersects(&self, other: &Self) -> bool {
1012 let simd_self = self.as_simd();
1013 let simd_other = other.as_simd();
1014 (simd_self.mins.cmple(simd_other.maxs) & simd_self.maxs.cmpge(simd_other.mins)).all()
1015 }
1016
1017 /// Tests if this node's AABB fully contains another node's AABB.
1018 ///
1019 /// One AABB contains another if the other AABB is completely inside or on the
1020 /// boundary of this AABB on all axes.
1021 ///
1022 /// # Arguments
1023 ///
1024 /// * `other` - The other node to test containment of
1025 ///
1026 /// # Returns
1027 ///
1028 /// `true` if this AABB fully contains the other AABB, `false` otherwise.
1029 ///
1030 /// # Performance
1031 ///
1032 /// In 3D with f32, this uses vectorized comparisons for improved
1033 /// performance.
1034 ///
1035 /// # Example
1036 ///
1037 /// ```
1038 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1039 /// use parry3d::partitioning::BvhNode;
1040 /// use parry3d::bounding_volume::Aabb;
1041 /// use parry3d::math::Vector;
1042 ///
1043 /// let large = Aabb::new(Vector::ZERO, Vector::new(10.0, 10.0, 10.0));
1044 /// let small = Aabb::new(Vector::new(2.0, 2.0, 2.0), Vector::new(5.0, 5.0, 5.0));
1045 ///
1046 /// let node_large = BvhNode::leaf(large, 0);
1047 /// let node_small = BvhNode::leaf(small, 1);
1048 ///
1049 /// assert!(node_large.contains(&node_small)); // Large contains small
1050 /// assert!(!node_small.contains(&node_large)); // Small doesn't contain large
1051 /// # }
1052 /// ```
1053 ///
1054 /// # See Also
1055 ///
1056 /// - [`intersects`](Self::intersects) - Check any overlap
1057 /// - [`contains_aabb`](Self::contains_aabb) - Contains an `Aabb` directly
1058 #[cfg(not(all(feature = "dim3", feature = "f32")))]
1059 pub fn contains(&self, other: &Self) -> bool {
1060 self.mins.cmple(other.mins).all() && self.maxs.cmpge(other.maxs).all()
1061 }
1062
1063 /// Tests if this node's AABB fully contains another node's AABB.
1064 ///
1065 /// One AABB contains another if the other AABB is completely inside or on the
1066 /// boundary of this AABB on all axes.
1067 ///
1068 /// # Arguments
1069 ///
1070 /// * `other` - The other node to test containment of
1071 ///
1072 /// # Returns
1073 ///
1074 /// `true` if this AABB fully contains the other AABB, `false` otherwise.
1075 ///
1076 /// # Performance
1077 ///
1078 /// This version uses SIMD optimizations for improved performance on supported platforms.
1079 ///
1080 /// # Example
1081 ///
1082 /// ```
1083 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1084 /// use parry3d::partitioning::BvhNode;
1085 /// use parry3d::bounding_volume::Aabb;
1086 /// use parry3d::math::Vector;
1087 ///
1088 /// let large = Aabb::new(Vector::ZERO, Vector::new(10.0, 10.0, 10.0));
1089 /// let small = Aabb::new(Vector::new(2.0, 2.0, 2.0), Vector::new(5.0, 5.0, 5.0));
1090 ///
1091 /// let node_large = BvhNode::leaf(large, 0);
1092 /// let node_small = BvhNode::leaf(small, 1);
1093 ///
1094 /// assert!(node_large.contains(&node_small)); // Large contains small
1095 /// assert!(!node_small.contains(&node_large)); // Small doesn't contain large
1096 /// # }
1097 /// ```
1098 ///
1099 /// # See Also
1100 ///
1101 /// - [`intersects`](Self::intersects) - Check any overlap
1102 /// - [`contains_aabb`](Self::contains_aabb) - Contains an `Aabb` directly
1103 #[cfg(all(feature = "dim3", feature = "f32"))]
1104 pub fn contains(&self, other: &Self) -> bool {
1105 let simd_self = self.as_simd();
1106 let simd_other = other.as_simd();
1107 (simd_self.mins.cmple(simd_other.mins) & simd_self.maxs.cmpge(simd_other.maxs)).all()
1108 }
1109
1110 /// Tests if this node's AABB fully contains the given AABB.
1111 ///
1112 /// This is similar to [`contains`](Self::contains) but takes an `Aabb` directly
1113 /// instead of another `BvhNode`.
1114 ///
1115 /// # Arguments
1116 ///
1117 /// * `other` - The AABB to test containment of
1118 ///
1119 /// # Returns
1120 ///
1121 /// `true` if this node's AABB fully contains the other AABB, `false` otherwise.
1122 ///
1123 /// # Example
1124 ///
1125 /// ```
1126 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1127 /// use parry3d::partitioning::BvhNode;
1128 /// use parry3d::bounding_volume::Aabb;
1129 /// use parry3d::math::Vector;
1130 ///
1131 /// let large = Aabb::new(Vector::ZERO, Vector::new(10.0, 10.0, 10.0));
1132 /// let small = Aabb::new(Vector::new(2.0, 2.0, 2.0), Vector::new(5.0, 5.0, 5.0));
1133 ///
1134 /// let node = BvhNode::leaf(large, 0);
1135 ///
1136 /// assert!(node.contains_aabb(&small));
1137 /// # }
1138 /// ```
1139 ///
1140 /// # See Also
1141 ///
1142 /// - [`contains`](Self::contains) - Contains another `BvhNode`
1143 pub fn contains_aabb(&self, other: &Aabb) -> bool {
1144 // TODO PERF: simd optimizations?
1145 self.mins.cmple(other.mins).all() && self.maxs.cmpge(other.maxs).all()
1146 }
1147
1148 /// Casts a ray against this node's AABB.
1149 ///
1150 /// Computes the time of impact (parameter `t`) where the ray first intersects
1151 /// the AABB. The actual hit point is `ray.origin + ray.dir * t`.
1152 ///
1153 /// # Arguments
1154 ///
1155 /// * `ray` - The ray to cast
1156 /// * `max_toi` - Maximum time of impact to consider (typically use `f32::MAX` or `f64::MAX`)
1157 ///
1158 /// # Returns
1159 ///
1160 /// - The time of impact if the ray hits the AABB within `max_toi`
1161 /// - `Real::MAX` if there is no hit or the hit is beyond `max_toi`
1162 ///
1163 /// # Example
1164 ///
1165 /// ```
1166 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1167 /// use parry3d::partitioning::BvhNode;
1168 /// use parry3d::bounding_volume::Aabb;
1169 /// use parry3d::query::Ray;
1170 /// use parry3d::math::Vector;
1171 ///
1172 /// let aabb = Aabb::new(Vector::new(5.0, -1.0, -1.0), Vector::new(6.0, 1.0, 1.0));
1173 /// let node = BvhNode::leaf(aabb, 0);
1174 ///
1175 /// // Ray from origin along X axis
1176 /// let ray = Ray::new(Vector::ZERO, Vector::new(1.0, 0.0, 0.0));
1177 ///
1178 /// let toi = node.cast_ray(&ray, f32::MAX);
1179 /// assert_eq!(toi, 5.0); // Ray hits at x=5.0
1180 /// # }
1181 /// ```
1182 ///
1183 /// # See Also
1184 ///
1185 /// - [`Ray`] - Ray structure
1186 /// - [`Bvh::traverse`] - For traversing the full BVH with ray casts
1187 pub fn cast_ray(&self, ray: &Ray, max_toi: Real) -> Real {
1188 self.aabb()
1189 .cast_local_ray(ray, max_toi, true)
1190 .unwrap_or(Real::MAX)
1191 }
1192
1193 /// Casts a ray on this AABB, with SIMD optimizations.
1194 ///
1195 /// Returns `Real::MAX` if there is no hit.
1196 #[cfg(all(feature = "dim3", feature = "f32"))]
1197 pub(super) fn cast_inv_ray_simd(&self, ray: &super::bvh_queries::SimdInvRay) -> f32 {
1198 let simd_self = self.as_simd();
1199 let t1 = (simd_self.mins - ray.origin) * ray.inv_dir;
1200 let t2 = (simd_self.maxs - ray.origin) * ray.inv_dir;
1201
1202 let tmin = t1.min(t2);
1203 let tmax = t1.max(t2);
1204 // let tmin = tmin.as_array_ref();
1205 // let tmax = tmax.as_array_ref();
1206 let tmin_n = tmin.max_element(); // tmin[0].max(tmin[1].max(tmin[2]));
1207 let tmax_n = tmax.min_element(); // tmax[0].min(tmax[1].min(tmax[2]));
1208
1209 if tmax_n >= tmin_n && tmax_n >= 0.0 {
1210 tmin_n
1211 } else {
1212 f32::MAX
1213 }
1214 }
1215}
1216
1217/// An index identifying a single BVH tree node.
1218///
1219/// The BVH stores nodes in pairs (`BvhNodeWide`), where each pair contains a left and
1220/// right child. This index encodes both which pair and which side (left or right) in a
1221/// single `usize` value for efficient storage and manipulation.
1222///
1223/// # Encoding
1224///
1225/// The index is encoded as: `(wide_node_index << 1) | is_right`
1226/// - The upper bits identify the `BvhNodeWide` (pair of nodes)
1227/// - The lowest bit indicates left (0) or right (1)
1228///
1229/// # Example
1230///
1231/// ```rust
1232/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1233/// use parry3d::partitioning::BvhNodeIndex;
1234///
1235/// // Create indices for the left and right children of node pair 5
1236/// let left = BvhNodeIndex::left(5);
1237/// let right = BvhNodeIndex::right(5);
1238///
1239/// assert_eq!(left.sibling(), right);
1240/// assert_eq!(right.sibling(), left);
1241///
1242/// // Decompose to get the pair index and side
1243/// let (pair_idx, is_right) = left.decompose();
1244/// assert_eq!(pair_idx, 5);
1245/// assert_eq!(is_right, false);
1246/// # }
1247/// ```
1248///
1249/// # See Also
1250///
1251/// - `BvhNodeWide` - The pair of nodes this index points into
1252/// - [`Bvh`] - The main BVH structure
1253#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
1254#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1255#[cfg_attr(
1256 feature = "rkyv",
1257 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
1258)]
1259pub struct BvhNodeIndex(pub usize);
1260
1261impl BvhNodeIndex {
1262 pub(super) const LEFT: bool = false;
1263 pub(super) const RIGHT: bool = true;
1264
1265 /// Decomposes this index into its components.
1266 ///
1267 /// Returns a tuple of `(wide_node_index, is_right)` where:
1268 /// - `wide_node_index` is the index into the BVH's array of `BvhNodeWide` pairs
1269 /// - `is_right` is `false` for left child, `true` for right child
1270 ///
1271 /// # Returns
1272 ///
1273 /// A tuple `(usize, bool)` containing the pair index and side flag.
1274 ///
1275 /// # Example
1276 ///
1277 /// ```
1278 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1279 /// use parry3d::partitioning::BvhNodeIndex;
1280 ///
1281 /// let left = BvhNodeIndex::left(10);
1282 /// let (pair_idx, is_right) = left.decompose();
1283 ///
1284 /// assert_eq!(pair_idx, 10);
1285 /// assert_eq!(is_right, false);
1286 /// # }
1287 /// ```
1288 ///
1289 /// # See Also
1290 ///
1291 /// - [`new`](Self::new) - Construct from components
1292 #[inline]
1293 pub fn decompose(self) -> (usize, bool) {
1294 (self.0 >> 1, (self.0 & 0b01) != 0)
1295 }
1296
1297 /// Returns the sibling of this node.
1298 ///
1299 /// If this index points to the left child of a pair, returns the right child.
1300 /// If this index points to the right child, returns the left child.
1301 ///
1302 /// # Returns
1303 ///
1304 /// The `BvhNodeIndex` of the sibling node.
1305 ///
1306 /// # Example
1307 ///
1308 /// ```
1309 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1310 /// use parry3d::partitioning::BvhNodeIndex;
1311 ///
1312 /// let left = BvhNodeIndex::left(5);
1313 /// let right = BvhNodeIndex::right(5);
1314 ///
1315 /// assert_eq!(left.sibling(), right);
1316 /// assert_eq!(right.sibling(), left);
1317 /// # }
1318 /// ```
1319 #[inline]
1320 pub fn sibling(self) -> Self {
1321 // Just flip the first bit to switch between left and right child.
1322 Self(self.0 ^ 0b01)
1323 }
1324
1325 /// Creates an index for the left child of a node pair.
1326 ///
1327 /// # Arguments
1328 ///
1329 /// * `id` - The index of the `BvhNodeWide` pair in the BVH's node array
1330 ///
1331 /// # Returns
1332 ///
1333 /// A `BvhNodeIndex` pointing to the left child of the specified pair.
1334 ///
1335 /// # Example
1336 ///
1337 /// ```
1338 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1339 /// use parry3d::partitioning::BvhNodeIndex;
1340 ///
1341 /// let left_child = BvhNodeIndex::left(0);
1342 /// let (pair_idx, is_right) = left_child.decompose();
1343 ///
1344 /// assert_eq!(pair_idx, 0);
1345 /// assert_eq!(is_right, false);
1346 /// # }
1347 /// ```
1348 ///
1349 /// # See Also
1350 ///
1351 /// - [`right`](Self::right) - Create index for right child
1352 /// - [`new`](Self::new) - Create index with explicit side
1353 #[inline]
1354 pub fn left(id: u32) -> Self {
1355 Self::new(id, Self::LEFT)
1356 }
1357
1358 /// Creates an index for the right child of a node pair.
1359 ///
1360 /// # Arguments
1361 ///
1362 /// * `id` - The index of the `BvhNodeWide` pair in the BVH's node array
1363 ///
1364 /// # Returns
1365 ///
1366 /// A `BvhNodeIndex` pointing to the right child of the specified pair.
1367 ///
1368 /// # Example
1369 ///
1370 /// ```
1371 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1372 /// use parry3d::partitioning::BvhNodeIndex;
1373 ///
1374 /// let right_child = BvhNodeIndex::right(0);
1375 /// let (pair_idx, is_right) = right_child.decompose();
1376 ///
1377 /// assert_eq!(pair_idx, 0);
1378 /// assert_eq!(is_right, true);
1379 /// # }
1380 /// ```
1381 ///
1382 /// # See Also
1383 ///
1384 /// - [`left`](Self::left) - Create index for left child
1385 /// - [`new`](Self::new) - Create index with explicit side
1386 #[inline]
1387 pub fn right(id: u32) -> Self {
1388 Self::new(id, Self::RIGHT)
1389 }
1390
1391 /// Creates a new node index from a pair ID and side flag.
1392 ///
1393 /// # Arguments
1394 ///
1395 /// * `id` - The index of the `BvhNodeWide` pair in the BVH's node array
1396 /// * `is_right` - `false` for left child, `true` for right child
1397 ///
1398 /// # Returns
1399 ///
1400 /// A `BvhNodeIndex` encoding both the pair and the side.
1401 ///
1402 /// # Example
1403 ///
1404 /// ```
1405 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1406 /// use parry3d::partitioning::BvhNodeIndex;
1407 ///
1408 /// let left = BvhNodeIndex::new(3, false);
1409 /// let right = BvhNodeIndex::new(3, true);
1410 ///
1411 /// assert_eq!(left, BvhNodeIndex::left(3));
1412 /// assert_eq!(right, BvhNodeIndex::right(3));
1413 /// # }
1414 /// ```
1415 ///
1416 /// # See Also
1417 ///
1418 /// - [`left`](Self::left) - Convenience method for left child
1419 /// - [`right`](Self::right) - Convenience method for right child
1420 #[inline]
1421 pub fn new(id: u32, is_right: bool) -> Self {
1422 Self(((id as usize) << 1) | (is_right as usize))
1423 }
1424}
1425
1426#[derive(Clone, Debug, Default)]
1427#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1428#[cfg_attr(
1429 feature = "rkyv",
1430 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
1431)]
1432pub(crate) struct BvhNodeVec(pub(crate) Vec<BvhNodeWide>);
1433
1434impl Deref for BvhNodeVec {
1435 type Target = Vec<BvhNodeWide>;
1436 fn deref(&self) -> &Self::Target {
1437 &self.0
1438 }
1439}
1440
1441impl DerefMut for BvhNodeVec {
1442 fn deref_mut(&mut self) -> &mut Self::Target {
1443 &mut self.0
1444 }
1445}
1446
1447impl Index<usize> for BvhNodeVec {
1448 type Output = BvhNodeWide;
1449
1450 #[inline(always)]
1451 fn index(&self, index: usize) -> &Self::Output {
1452 &self.0[index]
1453 }
1454}
1455
1456impl IndexMut<usize> for BvhNodeVec {
1457 #[inline(always)]
1458 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
1459 &mut self.0[index]
1460 }
1461}
1462
1463impl Index<BvhNodeIndex> for BvhNodeVec {
1464 type Output = BvhNode;
1465
1466 #[inline(always)]
1467 fn index(&self, index: BvhNodeIndex) -> &Self::Output {
1468 self.0[index.0 >> 1].as_array()[index.0 & 1]
1469 }
1470}
1471
1472impl IndexMut<BvhNodeIndex> for BvhNodeVec {
1473 #[inline(always)]
1474 fn index_mut(&mut self, index: BvhNodeIndex) -> &mut Self::Output {
1475 self.0[index.0 >> 1].as_array_mut()[index.0 & 1]
1476 }
1477}
1478
1479/// A Bounding Volume Hierarchy (BVH) for spatial queries and collision detection.
1480///
1481/// A BVH is a tree structure where each node contains an Axis-Aligned Bounding Box (AABB)
1482/// that encloses all geometry in its subtree. Leaf nodes represent individual objects,
1483/// while internal nodes partition space hierarchically. This enables efficient spatial
1484/// queries by allowing entire subtrees to be culled during traversal.
1485///
1486/// # What is a BVH and Why Use It?
1487///
1488/// A Bounding Volume Hierarchy organizes geometric objects (represented by their AABBs)
1489/// into a binary tree. Each internal node's AABB bounds the union of its two children's
1490/// AABBs. This hierarchical structure enables:
1491///
1492/// - **Fast spatial queries**: Ray casting, point queries, and AABB intersection tests
1493/// - **Broad-phase collision detection**: Quickly find potentially colliding pairs
1494/// - **Efficient culling**: Skip entire branches that don't intersect query regions
1495///
1496/// ## Performance Benefits
1497///
1498/// Without a BVH, testing N objects against M queries requires O(N × M) tests.
1499/// With a BVH, this reduces to approximately O(M × log N) for most queries,
1500/// providing dramatic speedups for scenes with many objects:
1501///
1502/// - **1,000 objects**: ~10x faster for ray casting
1503/// - **10,000 objects**: ~100x faster for ray casting
1504/// - **Critical for**: Real-time applications (games, physics engines, robotics)
1505///
1506/// ## Structure
1507///
1508/// The BVH is a binary tree where:
1509/// - **Leaf nodes**: Contain references to actual geometry (via user-provided indices)
1510/// - **Internal nodes**: Contain two children and an AABB encompassing both
1511/// - **Root**: The top-level node encompassing the entire scene
1512///
1513/// # Basic Usage - Static Scenes
1514///
1515/// For scenes where objects don't move, build the BVH once and query repeatedly:
1516///
1517/// ```rust
1518/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1519/// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
1520/// use parry3d::bounding_volume::Aabb;
1521/// use parry3d::math::Vector;
1522///
1523/// // Create AABBs for your objects
1524/// let objects = vec![
1525/// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
1526/// Aabb::new(Vector::new(5.0, 0.0, 0.0), Vector::new(6.0, 1.0, 1.0)),
1527/// Aabb::new(Vector::new(10.0, 0.0, 0.0), Vector::new(11.0, 1.0, 1.0)),
1528/// ];
1529///
1530/// // Build the BVH - the index of each AABB becomes its leaf ID
1531/// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &objects);
1532///
1533/// // Query which objects intersect a region
1534/// let query_region = Aabb::new(
1535/// Vector::new(-1.0, -1.0, -1.0),
1536/// Vector::new(2.0, 2.0, 2.0)
1537/// );
1538///
1539/// for leaf_id in bvh.intersect_aabb(&query_region) {
1540/// println!("Object {} intersects the query region", leaf_id);
1541/// // leaf_id corresponds to the index in the original 'objects' vec
1542/// }
1543/// # }
1544/// ```
1545///
1546/// # Dynamic Scenes - Adding and Updating Objects
1547///
1548/// The BVH supports dynamic scenes where objects move or are added/removed:
1549///
1550/// ```rust
1551/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1552/// use parry3d::partitioning::{Bvh, BvhWorkspace};
1553/// use parry3d::bounding_volume::Aabb;
1554/// use parry3d::math::Vector;
1555///
1556/// let mut bvh = Bvh::new();
1557/// let mut workspace = BvhWorkspace::default();
1558///
1559/// // Add objects dynamically with custom IDs
1560/// bvh.insert(Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)), 100);
1561/// bvh.insert(Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)), 200);
1562///
1563/// // Update an object's position (by re-inserting with same ID)
1564/// bvh.insert(Aabb::new(Vector::new(0.5, 0.5, 0.0), Vector::new(1.5, 1.5, 1.0)), 100);
1565///
1566/// // Refit the tree after updates for optimal query performance
1567/// bvh.refit(&mut workspace);
1568///
1569/// // Remove an object
1570/// bvh.remove(200);
1571/// # }
1572/// ```
1573///
1574/// # Ray Casting Example
1575///
1576/// Find the closest object hit by a ray:
1577///
1578/// ```rust
1579/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1580/// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
1581/// use parry3d::bounding_volume::Aabb;
1582/// use parry3d::query::{Ray, RayCast};
1583/// use parry3d::math::Vector;
1584///
1585/// let objects = vec![
1586/// Aabb::new(Vector::new(0.0, 0.0, 5.0), Vector::new(1.0, 1.0, 6.0)),
1587/// Aabb::new(Vector::new(0.0, 0.0, 10.0), Vector::new(1.0, 1.0, 11.0)),
1588/// ];
1589///
1590/// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &objects);
1591///
1592/// // Cast a ray forward along the Z axis
1593/// let ray = Ray::new(Vector::new(0.5, 0.5, 0.0), Vector::new(0.0, 0.0, 1.0));
1594/// let max_distance = 100.0;
1595///
1596/// // The BVH finds potentially intersecting leaves, then you test actual geometry
1597/// if let Some((leaf_id, hit_time)) = bvh.cast_ray(&ray, max_distance, |leaf_id, best_hit| {
1598/// // Test ray against the actual geometry for this leaf
1599/// // For this example, we test against the AABB itself
1600/// let aabb = &objects[leaf_id as usize];
1601/// aabb.cast_local_ray(&ray, best_hit, true)
1602/// }) {
1603/// println!("Ray hit object {} at distance {}", leaf_id, hit_time);
1604/// let hit_point = ray.point_at(hit_time);
1605/// println!("Hit point: {:?}", hit_point);
1606/// }
1607/// # }
1608/// ```
1609///
1610/// # Construction Strategies
1611///
1612/// Different build strategies offer trade-offs between build time and query performance:
1613///
1614/// ```rust
1615/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1616/// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
1617/// use parry3d::bounding_volume::Aabb;
1618/// use parry3d::math::Vector;
1619///
1620/// let aabbs = vec![
1621/// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
1622/// Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)),
1623/// ];
1624///
1625/// // Binned strategy: Fast construction, good quality (recommended default)
1626/// let bvh_binned = Bvh::from_leaves(BvhBuildStrategy::Binned, &aabbs);
1627///
1628/// // PLOC strategy: Slower construction, best quality for ray-casting
1629/// // Use this for static scenes with heavy query workloads
1630/// let bvh_ploc = Bvh::from_leaves(BvhBuildStrategy::Ploc, &aabbs);
1631/// # }
1632/// ```
1633///
1634/// # Maintenance for Dynamic Scenes
1635///
1636/// The BVH provides operations to maintain good performance as scenes change:
1637///
1638/// ## Refitting
1639///
1640/// After objects move, update the tree's AABBs efficiently:
1641///
1642/// ```rust
1643/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1644/// use parry3d::partitioning::{Bvh, BvhWorkspace};
1645/// use parry3d::bounding_volume::Aabb;
1646/// use parry3d::math::Vector;
1647///
1648/// let mut bvh = Bvh::new();
1649/// let mut workspace = BvhWorkspace::default();
1650///
1651/// // Insert initial objects
1652/// bvh.insert(Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)), 0);
1653/// bvh.insert(Aabb::new(Vector::new(5.0, 0.0, 0.0), Vector::new(6.0, 1.0, 1.0)), 1);
1654///
1655/// // Simulate object movement every frame
1656/// for frame in 0..100 {
1657/// let offset = frame as f32 * 0.1;
1658/// bvh.insert(Aabb::new(
1659/// Vector::new(offset, 0.0, 0.0),
1660/// Vector::new(1.0 + offset, 1.0, 1.0)
1661/// ), 0);
1662///
1663/// // Refit updates internal AABBs - very fast operation
1664/// bvh.refit(&mut workspace);
1665///
1666/// // Now you can query the BVH with updated positions
1667/// }
1668/// # }
1669/// ```
1670///
1671/// ## Incremental Optimization
1672///
1673/// For scenes with continuous movement, incrementally improve tree quality:
1674///
1675/// ```rust
1676/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1677/// use parry3d::partitioning::{Bvh, BvhWorkspace};
1678/// use parry3d::bounding_volume::Aabb;
1679/// use parry3d::math::Vector;
1680///
1681/// let mut bvh = Bvh::new();
1682/// let mut workspace = BvhWorkspace::default();
1683///
1684/// // Build initial tree
1685/// for i in 0..1000 {
1686/// let aabb = Aabb::new(
1687/// Vector::new(i as f32, 0.0, 0.0),
1688/// Vector::new(i as f32 + 1.0, 1.0, 1.0)
1689/// );
1690/// bvh.insert(aabb, i);
1691/// }
1692///
1693/// // In your update loop:
1694/// for frame in 0..100 {
1695/// // Update object positions...
1696///
1697/// bvh.refit(&mut workspace);
1698///
1699/// // Incrementally optimize tree quality (rebuilds small parts of tree)
1700/// // Call this every few frames, not every frame
1701/// if frame % 5 == 0 {
1702/// bvh.optimize_incremental(&mut workspace);
1703/// }
1704/// }
1705/// # }
1706/// ```
1707///
1708/// # Typical Workflows
1709///
1710/// ## Static Scene (Build Once, Query Many Times)
1711/// 1. Create AABBs for all objects
1712/// 2. Build BVH with `from_leaves`
1713/// 3. Query repeatedly (ray casting, intersection tests, etc.)
1714///
1715/// ## Dynamic Scene (Objects Move)
1716/// 1. Build initial BVH or start empty
1717/// 2. Each frame:
1718/// - Update positions with `insert`
1719/// - Call `refit` to update tree AABBs
1720/// - Perform queries
1721/// 3. Occasionally call `optimize_incremental` (every 5-10 frames)
1722///
1723/// ## Fully Dynamic (Objects Added/Removed)
1724/// 1. Start with empty BVH
1725/// 2. Add objects with `insert` as they're created
1726/// 3. Remove objects with `remove` as they're destroyed
1727/// 4. Call `refit` after batch updates
1728/// 5. Call `optimize_incremental` periodically
1729///
1730/// # Performance Tips
1731///
1732/// - **Reuse `BvhWorkspace`**: Pass the same workspace to multiple operations to avoid
1733/// allocations
1734/// - **Batch updates**: Update many leaves, then call `refit` once instead of refitting
1735/// after each update
1736/// - **Optimize periodically**: Call `optimize_incremental` every few frames for highly
1737/// dynamic scenes, not every frame
1738/// - **Choose right strategy**: Use Binned for most cases, PLOC for static scenes with
1739/// heavy ray-casting
1740/// - **Use `insert_or_update_partially`**: For bulk updates followed by a single `refit`
1741///
1742/// # Complexity
1743///
1744/// - **Construction**: O(n log n) where n is the number of leaves
1745/// - **Query (average)**: O(log n) for well-balanced trees
1746/// - **Insert**: O(log n) average
1747/// - **Remove**: O(log n) average
1748/// - **Refit**: O(n) but very fast (just updates AABBs)
1749/// - **Memory**: ~64 bytes per pair of children (3D f32 SIMD), O(n) total
1750///
1751/// # See Also
1752///
1753/// - [`BvhBuildStrategy`] - Choose construction algorithm (Binned vs PLOC)
1754/// - [`BvhWorkspace`] - Reusable workspace to avoid allocations
1755/// - [`BvhNode`] - Individual tree nodes with AABBs
1756#[derive(Clone, Debug, Default)]
1757#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1758#[cfg_attr(
1759 feature = "rkyv",
1760 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
1761)]
1762pub struct Bvh {
1763 pub(super) nodes: BvhNodeVec,
1764 // Parent indices for elements in `nodes`.
1765 // We don’t store this in `Self::nodes` since it’s only useful for node removal.
1766 pub(super) parents: Vec<BvhNodeIndex>,
1767 pub(super) leaf_node_indices: VecMap<BvhNodeIndex>,
1768 // NOTE: this cannot be in the workspace as we need this to survive serialization/deserialization
1769 // to maintain determinism.
1770 pub(super) optimization: BvhIncrementalOptimizationState,
1771 // Wide-node slots orphaned by leaf removals, reused by subsequent insertions
1772 // so remove/insert cycles don't grow the node array unboundedly between
1773 // compacting refits. The slots are zeroed when freed (a stale leaf copy left
1774 // in an orphaned slot would be picked up by [`Bvh::rebuild`]'s raw node scan).
1775 // Compacting refits and rebuilds (which recreate the node array and drop the
1776 // orphaned slots) clear this list.
1777 // NOTE: must survive serialization to maintain determinism (the slot reuse
1778 // order affects the tree topology produced by later insertions).
1779 #[cfg_attr(feature = "serde-serialize", serde(default))]
1780 pub(super) free_wide_nodes: Vec<u32>,
1781}
1782
1783impl Bvh {
1784 /// Creates an empty BVH with no leaves.
1785 ///
1786 /// This is equivalent to `Bvh::default()` but more explicit. Use this when you plan
1787 /// to incrementally build the tree using [`insert`](Self::insert), or when you need
1788 /// an empty placeholder BVH.
1789 ///
1790 /// # Example
1791 ///
1792 /// ```
1793 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1794 /// use parry3d::partitioning::Bvh;
1795 ///
1796 /// let bvh = Bvh::new();
1797 /// assert!(bvh.is_empty());
1798 /// assert_eq!(bvh.leaf_count(), 0);
1799 /// # }
1800 /// ```
1801 ///
1802 /// # See Also
1803 ///
1804 /// - [`from_leaves`](Self::from_leaves) - Build from AABBs
1805 /// - [`from_iter`](Self::from_iter) - Build from an iterator
1806 pub fn new() -> Self {
1807 Self::default()
1808 }
1809
1810 /// Creates a new BVH from a slice of AABBs.
1811 ///
1812 /// Each AABB in the slice becomes a leaf in the BVH. The leaf at index `i` in the slice
1813 /// will have leaf data `i`, which can be used to identify which object a query result
1814 /// refers to.
1815 ///
1816 /// # Arguments
1817 ///
1818 /// * `strategy` - The construction algorithm to use (see [`BvhBuildStrategy`])
1819 /// * `leaves` - Slice of AABBs, one for each object in the scene
1820 ///
1821 /// # Returns
1822 ///
1823 /// A new `Bvh` containing all the leaves organized in a tree structure.
1824 ///
1825 /// # Performance
1826 ///
1827 /// - **Time**: O(n log n) where n is the number of leaves
1828 /// - **Space**: O(n) additional memory during construction
1829 ///
1830 /// # Example
1831 ///
1832 /// ```
1833 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1834 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
1835 /// use parry3d::bounding_volume::Aabb;
1836 /// use parry3d::math::Vector;
1837 ///
1838 /// let aabbs = vec![
1839 /// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
1840 /// Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)),
1841 /// Aabb::new(Vector::new(4.0, 0.0, 0.0), Vector::new(5.0, 1.0, 1.0)),
1842 /// ];
1843 ///
1844 /// let bvh = Bvh::from_leaves(BvhBuildStrategy::Binned, &aabbs);
1845 ///
1846 /// assert_eq!(bvh.leaf_count(), 3);
1847 /// // Leaf 0 corresponds to aabbs[0], leaf 1 to aabbs[1], etc.
1848 /// # }
1849 /// ```
1850 ///
1851 /// # See Also
1852 ///
1853 /// - [`from_iter`](Self::from_iter) - Build from an iterator with custom indices
1854 /// - [`BvhBuildStrategy`] - Choose construction algorithm
1855 pub fn from_leaves(strategy: BvhBuildStrategy, leaves: &[Aabb]) -> Self {
1856 Self::from_iter(strategy, leaves.iter().copied().enumerate())
1857 }
1858
1859 /// Creates a new BVH from an iterator of (index, AABB) pairs.
1860 ///
1861 /// This is more flexible than [`from_leaves`](Self::from_leaves) as it allows you to
1862 /// provide custom leaf indices. This is useful when your objects don't have contiguous
1863 /// indices, or when you want to use sparse IDs.
1864 ///
1865 /// # Arguments
1866 ///
1867 /// * `strategy` - The construction algorithm to use (see [`BvhBuildStrategy`])
1868 /// * `leaves` - Iterator yielding `(index, aabb)` pairs
1869 ///
1870 /// # Returns
1871 ///
1872 /// A new `Bvh` containing all the leaves organized in a tree structure.
1873 ///
1874 /// # Notes
1875 ///
1876 /// - Indices are stored internally as `u32`, but the iterator accepts `usize` for convenience
1877 /// - You can use `.enumerate()` directly on an AABB iterator
1878 /// - Indices larger than `u32::MAX` will overflow
1879 ///
1880 /// # Performance
1881 ///
1882 /// - **Time**: O(n log n) where n is the number of leaves
1883 /// - **Space**: O(n) additional memory during construction
1884 ///
1885 /// # Example
1886 ///
1887 /// ```
1888 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1889 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
1890 /// use parry3d::bounding_volume::Aabb;
1891 /// use parry3d::math::Vector;
1892 ///
1893 /// // Create a BVH with custom indices
1894 /// let leaves = vec![
1895 /// (10, Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0))),
1896 /// (20, Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0))),
1897 /// (30, Aabb::new(Vector::new(4.0, 0.0, 0.0), Vector::new(5.0, 1.0, 1.0))),
1898 /// ];
1899 ///
1900 /// let bvh = Bvh::from_iter(BvhBuildStrategy::Binned, leaves.into_iter());
1901 ///
1902 /// assert_eq!(bvh.leaf_count(), 3);
1903 /// // Leaf data will be 10, 20, 30 instead of 0, 1, 2
1904 /// # }
1905 /// ```
1906 ///
1907 /// # See Also
1908 ///
1909 /// - [`from_leaves`](Self::from_leaves) - Simpler version with automatic indices
1910 /// - [`BvhBuildStrategy`] - Choose construction algorithm
1911 pub fn from_iter<It>(strategy: BvhBuildStrategy, leaves: It) -> Self
1912 where
1913 It: IntoIterator<Item = (usize, Aabb)>,
1914 {
1915 let leaves = leaves.into_iter();
1916 let (capacity_lo, capacity_up) = leaves.size_hint();
1917 let capacity = capacity_up.unwrap_or(capacity_lo);
1918
1919 let mut result = Self::new();
1920 let mut workspace = BvhWorkspace::default();
1921 workspace.rebuild_leaves.reserve(capacity);
1922 result.leaf_node_indices.reserve_len(capacity);
1923
1924 for (leaf_id, leaf_aabb) in leaves {
1925 workspace
1926 .rebuild_leaves
1927 .push(BvhNode::leaf(leaf_aabb, leaf_id as u32));
1928 let _ = result
1929 .leaf_node_indices
1930 .insert(leaf_id, BvhNodeIndex::default());
1931 }
1932
1933 // Handle special cases that don’t play well with the rebuilds.
1934 match workspace.rebuild_leaves.len() {
1935 0 => {}
1936 1 => {
1937 result.nodes.push(BvhNodeWide {
1938 left: workspace.rebuild_leaves[0],
1939 right: BvhNode::zeros(),
1940 });
1941 result.parents.push(BvhNodeIndex::default());
1942 result.leaf_node_indices[0] = BvhNodeIndex::left(0);
1943 }
1944 2 => {
1945 result.nodes.push(BvhNodeWide {
1946 left: workspace.rebuild_leaves[0],
1947 right: workspace.rebuild_leaves[1],
1948 });
1949 result.parents.push(BvhNodeIndex::default());
1950 result.leaf_node_indices[0] = BvhNodeIndex::left(0);
1951 result.leaf_node_indices[1] = BvhNodeIndex::right(0);
1952 }
1953 _ => {
1954 result.nodes.reserve(capacity);
1955 result.parents.reserve(capacity);
1956 result.parents.clear();
1957 result.nodes.push(BvhNodeWide::zeros());
1958 result.parents.push(BvhNodeIndex::default());
1959
1960 match strategy {
1961 BvhBuildStrategy::Ploc => {
1962 result.rebuild_range_ploc(0, &mut workspace.rebuild_leaves)
1963 }
1964 BvhBuildStrategy::Binned => {
1965 result.rebuild_range_binned(0, &mut workspace.rebuild_leaves)
1966 }
1967 }
1968
1969 // Layout in depth-first order.
1970 result.refit(&mut workspace);
1971 }
1972 }
1973
1974 result
1975 }
1976
1977 /// Returns the AABB that bounds all geometry in this BVH.
1978 ///
1979 /// This is the AABB of the root node, which encompasses all leaves in the tree.
1980 /// For an empty BVH, returns an invalid AABB (with mins > maxs).
1981 ///
1982 /// # Returns
1983 ///
1984 /// An `Aabb` that contains all objects in the BVH.
1985 ///
1986 /// # Example
1987 ///
1988 /// ```
1989 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1990 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
1991 /// use parry3d::bounding_volume::{Aabb, BoundingVolume};
1992 /// use parry3d::math::Vector;
1993 ///
1994 /// let aabbs = vec![
1995 /// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
1996 /// Aabb::new(Vector::new(5.0, 0.0, 0.0), Vector::new(6.0, 1.0, 1.0)),
1997 /// ];
1998 ///
1999 /// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
2000 /// let root_aabb = bvh.root_aabb();
2001 ///
2002 /// // Root AABB contains both leaves
2003 /// assert!(root_aabb.contains(&aabbs[0]));
2004 /// assert!(root_aabb.contains(&aabbs[1]));
2005 /// # }
2006 /// ```
2007 ///
2008 /// # See Also
2009 ///
2010 /// - [`is_empty`](Self::is_empty) - Check if BVH has no leaves
2011 pub fn root_aabb(&self) -> Aabb {
2012 match self.leaf_count() {
2013 0 => Aabb::new_invalid(),
2014 1 => self.nodes[0].left.aabb(),
2015 _ => self.nodes[0]
2016 .left
2017 .aabb()
2018 .merged(&self.nodes[0].right.aabb()),
2019 }
2020 }
2021
2022 /// Scales all AABBs in the tree by the given factors.
2023 ///
2024 /// Each AABB's coordinates are multiplied by the corresponding scale components, with
2025 /// mins and maxs swapped on any axis where the scale is negative to preserve the
2026 /// AABB invariant. This is useful when scaling an entire scene or changing coordinate
2027 /// systems, including reflections.
2028 ///
2029 /// # Arguments
2030 ///
2031 /// * `scale` - Per-axis scale factors. Each component must be non-zero.
2032 ///
2033 /// # Panics
2034 ///
2035 /// Undefined behavior if any scale component is zero (degenerate AABB).
2036 ///
2037 /// # Example
2038 ///
2039 /// ```
2040 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2041 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
2042 /// use parry3d::bounding_volume::Aabb;
2043 /// use parry3d::math::Vector;
2044 ///
2045 /// let aabbs = vec![
2046 /// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
2047 /// ];
2048 ///
2049 /// let mut bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
2050 ///
2051 /// // Scale by 2x on all axes
2052 /// bvh.scale(Vector::new(2.0, 2.0, 2.0));
2053 ///
2054 /// let root = bvh.root_aabb();
2055 /// assert_eq!(root.maxs, Vector::new(2.0, 2.0, 2.0));
2056 /// # }
2057 /// ```
2058 ///
2059 /// # See Also
2060 ///
2061 /// - [`BvhNode::scale`] - Scale a single node
2062 pub fn scale(&mut self, scale: Vector) {
2063 for node in self.nodes.0.iter_mut() {
2064 node.left.scale(scale);
2065 node.right.scale(scale);
2066 }
2067 }
2068
2069 /// Returns `true` if this BVH contains no leaves.
2070 ///
2071 /// An empty BVH has no geometry and cannot be queried meaningfully.
2072 ///
2073 /// # Returns
2074 ///
2075 /// `true` if the BVH is empty, `false` otherwise.
2076 ///
2077 /// # Example
2078 ///
2079 /// ```
2080 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2081 /// use parry3d::partitioning::Bvh;
2082 ///
2083 /// let empty_bvh = Bvh::new();
2084 /// assert!(empty_bvh.is_empty());
2085 /// # }
2086 /// ```
2087 ///
2088 /// # See Also
2089 ///
2090 /// - [`leaf_count`](Self::leaf_count) - Get the number of leaves
2091 pub fn is_empty(&self) -> bool {
2092 self.nodes.is_empty()
2093 }
2094
2095 /// Returns a reference to the leaf node with the given index.
2096 ///
2097 /// The `leaf_key` is the index that was provided when constructing the BVH
2098 /// (either the position in the slice for [`from_leaves`](Self::from_leaves),
2099 /// or the custom index for [`from_iter`](Self::from_iter)).
2100 ///
2101 /// # Arguments
2102 ///
2103 /// * `leaf_key` - The leaf index to look up
2104 ///
2105 /// # Returns
2106 ///
2107 /// - `Some(&BvhNode)` if a leaf with that index exists
2108 /// - `None` if no leaf with that index exists
2109 ///
2110 /// # Example
2111 ///
2112 /// ```
2113 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2114 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
2115 /// use parry3d::bounding_volume::Aabb;
2116 /// use parry3d::math::Vector;
2117 ///
2118 /// let aabbs = vec![
2119 /// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
2120 /// ];
2121 ///
2122 /// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
2123 ///
2124 /// // Leaf 0 exists (from aabbs[0])
2125 /// assert!(bvh.leaf_node(0).is_some());
2126 ///
2127 /// // Leaf 1 doesn't exist
2128 /// assert!(bvh.leaf_node(1).is_none());
2129 /// # }
2130 /// ```
2131 ///
2132 /// # See Also
2133 ///
2134 /// - [`remove`](Self::remove) - Remove a leaf by index
2135 pub fn leaf_node(&self, leaf_key: u32) -> Option<&BvhNode> {
2136 let idx = self.leaf_node_indices.get(leaf_key as usize)?;
2137 Some(&self.nodes[*idx])
2138 }
2139
2140 /// Estimates the total memory usage of this BVH in bytes.
2141 ///
2142 /// This includes both the stack size of the `Bvh` struct itself and all
2143 /// heap-allocated memory (node arrays, parent indices, leaf index maps).
2144 ///
2145 /// # Returns
2146 ///
2147 /// Approximate memory usage in bytes.
2148 ///
2149 /// # Example
2150 ///
2151 /// ```
2152 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2153 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
2154 /// use parry3d::bounding_volume::Aabb;
2155 /// use parry3d::math::Vector;
2156 ///
2157 /// let aabbs: Vec<_> = (0..100)
2158 /// .map(|i| {
2159 /// let f = i as f32;
2160 /// Aabb::new(Vector::new(f, 0.0, 0.0), Vector::new(f + 1.0, 1.0, 1.0))
2161 /// })
2162 /// .collect();
2163 ///
2164 /// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
2165 ///
2166 /// println!("BVH memory usage: {} bytes", bvh.total_memory_size());
2167 /// # }
2168 /// ```
2169 ///
2170 /// # See Also
2171 ///
2172 /// - [`heap_memory_size`](Self::heap_memory_size) - Only heap-allocated memory
2173 pub fn total_memory_size(&self) -> usize {
2174 size_of::<Self>() + self.heap_memory_size()
2175 }
2176
2177 /// Estimates the heap-allocated memory usage of this BVH in bytes.
2178 ///
2179 /// This only counts dynamically allocated memory (nodes, indices, etc.) and
2180 /// excludes the stack size of the `Bvh` struct itself.
2181 ///
2182 /// # Returns
2183 ///
2184 /// Approximate heap memory usage in bytes.
2185 ///
2186 /// # Example
2187 ///
2188 /// ```
2189 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2190 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
2191 /// use parry3d::bounding_volume::Aabb;
2192 /// use parry3d::math::Vector;
2193 ///
2194 /// let aabbs: Vec<_> = (0..100)
2195 /// .map(|i| {
2196 /// let f = i as f32;
2197 /// Aabb::new(Vector::new(f, 0.0, 0.0), Vector::new(f + 1.0, 1.0, 1.0))
2198 /// })
2199 /// .collect();
2200 ///
2201 /// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
2202 ///
2203 /// println!("BVH heap memory: {} bytes", bvh.heap_memory_size());
2204 /// # }
2205 /// ```
2206 ///
2207 /// # See Also
2208 ///
2209 /// - [`total_memory_size`](Self::total_memory_size) - Total memory including stack
2210 pub fn heap_memory_size(&self) -> usize {
2211 let Self {
2212 nodes,
2213 parents,
2214 leaf_node_indices,
2215 optimization: _,
2216 free_wide_nodes,
2217 } = self;
2218 nodes.capacity() * size_of::<BvhNodeWide>()
2219 + parents.capacity() * size_of::<BvhNodeIndex>()
2220 + leaf_node_indices.capacity() * size_of::<BvhNodeIndex>()
2221 + free_wide_nodes.capacity() * size_of::<u32>()
2222 }
2223
2224 /// Computes the depth of the subtree rooted at the specified node.
2225 ///
2226 /// The depth is the number of levels from the root to the deepest leaf. A single
2227 /// node has depth 1, a node with two leaf children has depth 2, etc.
2228 ///
2229 /// # Arguments
2230 ///
2231 /// * `node_id` - The index of the root node of the subtree (use 0 for the entire tree)
2232 ///
2233 /// # Returns
2234 ///
2235 /// The depth of the subtree, or 0 for an empty tree.
2236 ///
2237 /// # Example
2238 ///
2239 /// ```
2240 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2241 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
2242 /// use parry3d::bounding_volume::Aabb;
2243 /// use parry3d::math::Vector;
2244 ///
2245 /// let aabbs: Vec<_> = (0..4)
2246 /// .map(|i| {
2247 /// let f = i as f32;
2248 /// Aabb::new(Vector::new(f, 0.0, 0.0), Vector::new(f + 1.0, 1.0, 1.0))
2249 /// })
2250 /// .collect();
2251 ///
2252 /// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
2253 ///
2254 /// // Get depth of entire tree
2255 /// let depth = bvh.subtree_depth(0);
2256 /// assert!(depth >= 2); // At least 2 levels with 4 leaves
2257 /// # }
2258 /// ```
2259 ///
2260 /// # See Also
2261 ///
2262 /// - [`leaf_count`](Self::leaf_count) - Number of leaves in the tree
2263 pub fn subtree_depth(&self, node_id: u32) -> u32 {
2264 if node_id == 0 && self.nodes.is_empty() {
2265 return 0;
2266 } else if node_id == 0 && self.nodes.len() == 1 {
2267 return 1 + (self.nodes[0].right.leaf_count() != 0) as u32;
2268 }
2269
2270 let node = &self.nodes[node_id as usize];
2271
2272 let left_depth = if node.left.is_leaf() {
2273 1
2274 } else {
2275 self.subtree_depth(node.left.children)
2276 };
2277
2278 let right_depth = if node.right.is_leaf() {
2279 1
2280 } else {
2281 self.subtree_depth(node.right.children)
2282 };
2283
2284 left_depth.max(right_depth) + 1
2285 }
2286
2287 /// Returns the number of leaves in this BVH.
2288 ///
2289 /// Each leaf represents one geometric object that was provided during construction
2290 /// or added via [`insert`](Self::insert).
2291 ///
2292 /// # Returns
2293 ///
2294 /// The total number of leaves in the tree.
2295 ///
2296 /// # Example
2297 ///
2298 /// ```
2299 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2300 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
2301 /// use parry3d::bounding_volume::Aabb;
2302 /// use parry3d::math::Vector;
2303 ///
2304 /// let aabbs = vec![
2305 /// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
2306 /// Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)),
2307 /// Aabb::new(Vector::new(4.0, 0.0, 0.0), Vector::new(5.0, 1.0, 1.0)),
2308 /// ];
2309 ///
2310 /// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
2311 /// assert_eq!(bvh.leaf_count(), 3);
2312 /// # }
2313 /// ```
2314 ///
2315 /// # See Also
2316 ///
2317 /// - [`is_empty`](Self::is_empty) - Check if the tree has no leaves
2318 pub fn leaf_count(&self) -> u32 {
2319 if self.nodes.is_empty() {
2320 0
2321 } else {
2322 self.nodes[0].leaf_count()
2323 }
2324 }
2325
2326 /// Removes a leaf from the BVH.
2327 ///
2328 /// This removes the leaf with the specified index and updates the tree structure
2329 /// accordingly. The sibling of the removed leaf moves up to take its parent's place,
2330 /// and all ancestor AABBs and leaf counts are updated.
2331 ///
2332 /// # Arguments
2333 ///
2334 /// * `leaf_index` - The index of the leaf to remove (the same index used when constructing)
2335 ///
2336 /// # Performance
2337 ///
2338 /// - **Time**: O(h) where h is the tree height (typically O(log n))
2339 /// - Updates AABBs and leaf counts for all ancestors of the removed leaf
2340 /// - For heavily unbalanced trees, consider rebuilding or rebalancing after many removals
2341 ///
2342 /// # Notes
2343 ///
2344 /// - If the leaf doesn't exist, this is a no-op
2345 /// - Removing the last leaf results in an empty BVH
2346 /// - The tree structure remains valid after removal
2347 ///
2348 /// # Example
2349 ///
2350 /// ```
2351 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2352 /// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
2353 /// use parry3d::bounding_volume::Aabb;
2354 /// use parry3d::math::Vector;
2355 ///
2356 /// let aabbs = vec![
2357 /// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
2358 /// Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)),
2359 /// Aabb::new(Vector::new(4.0, 0.0, 0.0), Vector::new(5.0, 1.0, 1.0)),
2360 /// ];
2361 ///
2362 /// let mut bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &aabbs);
2363 /// assert_eq!(bvh.leaf_count(), 3);
2364 ///
2365 /// // Remove the middle leaf
2366 /// bvh.remove(1);
2367 /// assert_eq!(bvh.leaf_count(), 2);
2368 ///
2369 /// // Leaf 1 no longer exists
2370 /// assert!(bvh.leaf_node(1).is_none());
2371 /// # }
2372 /// ```
2373 ///
2374 /// # See Also
2375 ///
2376 /// - [`insert`](Self::insert) - Add a new leaf to the BVH
2377 /// - [`refit`](Self::refit) - Update AABBs after leaf movements
2378 /// - [`optimize_incremental`](Self::optimize_incremental) - Improve tree quality
2379 // TODO: should we make a version that doesn't traverse the parents?
2380 // If we do, we must be very careful that the leaf counts that become
2381 // invalid don't break other algorithm… (and, in particular, the root
2382 // special case that checks if its right element has 0 leaf count).
2383 pub fn remove(&mut self, leaf_index: u32) {
2384 if let Some(node_index) = self.leaf_node_indices.remove(leaf_index as usize) {
2385 if self.leaf_node_indices.is_empty() {
2386 // We deleted the last leaf! Remove the root.
2387 self.nodes.clear();
2388 self.parents.clear();
2389 self.free_wide_nodes.clear();
2390 return;
2391 }
2392
2393 let sibling = node_index.sibling();
2394 let (wide_node_index, is_right) = node_index.decompose();
2395
2396 if wide_node_index == 0 {
2397 if self.nodes[sibling].is_leaf() {
2398 // If the sibling is a leaf, we end up with a partial root.
2399 // There is no parent pointer to update.
2400 if !is_right {
2401 // We remove the left leaf. Move the right leaf in its place.
2402 let moved_index = self.nodes[0].right.children;
2403 self.nodes[0].left = self.nodes[0].right;
2404 self.leaf_node_indices[moved_index as usize] = BvhNodeIndex::left(0);
2405 }
2406
2407 // Now we can just clear the right leaf.
2408 self.nodes[0].right = BvhNode::zeros();
2409
2410 // Clean up orphaned nodes. With a partial root, only node[0] is
2411 // reachable. Previous removes may have left orphaned wide nodes
2412 // that were waiting for refit to compact them. If we don't truncate
2413 // here, the tree appears as a single-leaf tree with unreachable
2414 // nodes, which corrupts optimize_incremental.
2415 self.nodes.truncate(1);
2416 self.parents.truncate(1);
2417 self.free_wide_nodes.clear();
2418 } else {
2419 // The sibling isn’t a leaf. It becomes the new root at index 0.
2420 let old_sibling_slot = self.nodes[sibling].children;
2421 self.nodes[0] = self.nodes[old_sibling_slot as usize];
2422 self.nodes[old_sibling_slot as usize] = BvhNodeWide::zeros();
2423 self.free_wide_nodes.push(old_sibling_slot);
2424 // Both parent pointers need to be updated since both nodes moved to the root.
2425 let new_root = &mut self.nodes[0];
2426 if new_root.left.is_leaf() {
2427 self.leaf_node_indices[new_root.left.children as usize] =
2428 BvhNodeIndex::left(0);
2429 } else {
2430 self.parents[new_root.left.children as usize] = BvhNodeIndex::left(0);
2431 }
2432 if new_root.right.is_leaf() {
2433 self.leaf_node_indices[new_root.right.children as usize] =
2434 BvhNodeIndex::right(0);
2435 } else {
2436 self.parents[new_root.right.children as usize] = BvhNodeIndex::right(0);
2437 }
2438 }
2439 } else {
2440 // The sibling moves to the parent. The affected wide node is no longer accessible,
2441 // but we can just leave it there, it will get cleaned up at the next refit.
2442 let parent = self.parents[wide_node_index];
2443 let sibling = &self.nodes[sibling];
2444
2445 if sibling.is_leaf() {
2446 self.leaf_node_indices[sibling.children as usize] = parent;
2447 } else {
2448 self.parents[sibling.children as usize] = parent;
2449 }
2450
2451 self.nodes[parent] = *sibling;
2452
2453 // The removed leaf's wide node is now unreachable: zero it (so raw
2454 // node scans like `Bvh::rebuild` can't pick up its stale leaf
2455 // copies) and recycle its slot for later insertions.
2456 self.nodes[wide_node_index] = BvhNodeWide::zeros();
2457 self.free_wide_nodes.push(wide_node_index as u32);
2458
2459 // TODO: we could use that propagation as an opportunity to
2460 // apply some rotations?
2461 let mut curr = parent.decompose().0;
2462 while curr != 0 {
2463 let parent = self.parents[curr];
2464 self.nodes[parent] = self.nodes[curr].merged(curr as u32);
2465 curr = parent.decompose().0;
2466 }
2467 }
2468 }
2469 }
2470
2471 // pub fn quality_metric(&self) -> Real {
2472 // let mut metric = 0.0;
2473 // for i in 0..self.nodes.len() {
2474 // if !self.nodes[i].is_leaf() {
2475 // metric += self.sah_cost(i);
2476 // }
2477 // }
2478 // metric
2479 // }
2480}