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}