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