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