parry3d/partitioning/bvh/
bvh_tree.rs

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