parry3d/partitioning/bvh/
bvh_tree.rs

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