Skip to main content

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