Skip to main content

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}