parry2d/bounding_volume/aabb.rs
1//! Axis Aligned Bounding Box.
2
3use crate::bounding_volume::{BoundingSphere, BoundingVolume};
4use crate::math::{Isometry, Point, Real, UnitVector, Vector, DIM, TWO_DIM};
5use crate::shape::{Cuboid, SupportMap};
6use crate::utils::IsometryOps;
7use arrayvec::ArrayVec;
8use na;
9use num::Bounded;
10
11#[cfg(all(feature = "dim3", not(feature = "std")))]
12use na::ComplexField;
13// for .sin_cos()
14
15use crate::query::{Ray, RayCast};
16#[cfg(feature = "rkyv")]
17use rkyv::{bytecheck, CheckBytes};
18
19/// An Axis-Aligned Bounding Box (AABB).
20///
21/// An AABB is the simplest bounding volume, defined by its minimum and maximum corners.
22/// It's called "axis-aligned" because its edges are always parallel to the coordinate axes
23/// (X, Y, and Z in 3D), making it very fast to test and compute.
24///
25/// # Structure
26///
27/// - **mins**: The point with the smallest coordinates on each axis (bottom-left-back corner)
28/// - **maxs**: The point with the largest coordinates on each axis (top-right-front corner)
29/// - **Invariant**: `mins.x ≤ maxs.x`, `mins.y ≤ maxs.y` (and `mins.z ≤ maxs.z` in 3D)
30///
31/// # Properties
32///
33/// - **Axis-aligned**: Edges always parallel to coordinate axes
34/// - **Conservative**: May be larger than the actual shape for rotated objects
35/// - **Fast**: Intersection tests are very cheap (just coordinate comparisons)
36/// - **Hierarchical**: Perfect for spatial data structures (BVH, quadtree, octree)
37///
38/// # Use Cases
39///
40/// AABBs are fundamental to collision detection and are used for:
41///
42/// - **Broad-phase collision detection**: Quickly eliminate distant object pairs
43/// - **Spatial partitioning**: Building BVHs, quadtrees, and octrees
44/// - **View frustum culling**: Determining what's visible
45/// - **Ray tracing acceleration**: Quickly rejecting non-intersecting rays
46/// - **Bounding volume for any shape**: Every shape can compute its AABB
47///
48/// # Performance
49///
50/// AABBs are the fastest bounding volume for:
51/// - Intersection tests: O(1) with just 6 comparisons (3D)
52/// - Merging: O(1) with component-wise min/max
53/// - Contains test: O(1) with coordinate comparisons
54///
55/// # Limitations
56///
57/// - **Rotation invariance**: Must be recomputed when objects rotate
58/// - **Tightness**: May waste space for rotated or complex shapes
59/// - **No orientation**: Cannot represent oriented bounding boxes (OBB)
60///
61/// # Example
62///
63/// ```rust
64/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
65/// use parry3d::bounding_volume::Aabb;
66/// use nalgebra::Point3;
67///
68/// // Create an AABB for a unit cube centered at origin
69/// let mins = Point3::new(-0.5, -0.5, -0.5);
70/// let maxs = Point3::new(0.5, 0.5, 0.5);
71/// let aabb = Aabb::new(mins, maxs);
72///
73/// // Check if a point is inside
74/// let point = Point3::origin();
75/// assert!(aabb.contains_local_point(&point));
76///
77/// // Get center and extents
78/// assert_eq!(aabb.center(), Point3::origin());
79/// assert_eq!(aabb.extents().x, 1.0); // Full width
80/// assert_eq!(aabb.half_extents().x, 0.5); // Half width
81/// # }
82/// ```
83///
84/// ```rust
85/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
86/// use parry3d::bounding_volume::Aabb;
87/// use nalgebra::Point3;
88///
89/// // Create from a set of points
90/// let points = vec![
91/// Point3::new(1.0, 2.0, 3.0),
92/// Point3::new(-1.0, 4.0, 2.0),
93/// Point3::new(0.0, 0.0, 5.0),
94/// ];
95/// let aabb = Aabb::from_points(points);
96///
97/// // The AABB encloses all points
98/// assert_eq!(aabb.mins, Point3::new(-1.0, 0.0, 2.0));
99/// assert_eq!(aabb.maxs, Point3::new(1.0, 4.0, 5.0));
100/// # }
101/// ```
102#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
103#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
104#[cfg_attr(
105 feature = "rkyv",
106 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
107 archive(as = "Self")
108)]
109#[derive(Debug, PartialEq, Copy, Clone)]
110#[repr(C)]
111pub struct Aabb {
112 /// The point with minimum coordinates (bottom-left-back corner).
113 ///
114 /// Each component (`x`, `y`, `z`) should be less than or equal to the
115 /// corresponding component in `maxs`.
116 pub mins: Point<Real>,
117
118 /// The point with maximum coordinates (top-right-front corner).
119 ///
120 /// Each component (`x`, `y`, `z`) should be greater than or equal to the
121 /// corresponding component in `mins`.
122 pub maxs: Point<Real>,
123}
124
125impl Aabb {
126 /// The vertex indices of each edge of this `Aabb`.
127 ///
128 /// This gives, for each edge of this `Aabb`, the indices of its
129 /// vertices when taken from the `self.vertices()` array.
130 /// Here is how the faces are numbered, assuming
131 /// a right-handed coordinate system:
132 ///
133 /// ```text
134 /// y 3 - 2
135 /// | 7 − 6 |
136 /// ___ x | | 1 (the zero is below 3 and on the left of 1,
137 /// / 4 - 5 hidden by the 4-5-6-7 face.)
138 /// z
139 /// ```
140 #[cfg(feature = "dim3")]
141 pub const EDGES_VERTEX_IDS: [(usize, usize); 12] = [
142 (0, 1),
143 (1, 2),
144 (3, 2),
145 (0, 3),
146 (4, 5),
147 (5, 6),
148 (7, 6),
149 (4, 7),
150 (0, 4),
151 (1, 5),
152 (2, 6),
153 (3, 7),
154 ];
155
156 /// The vertex indices of each face of this `Aabb`.
157 ///
158 /// This gives, for each face of this `Aabb`, the indices of its
159 /// vertices when taken from the `self.vertices()` array.
160 /// Here is how the faces are numbered, assuming
161 /// a right-handed coordinate system:
162 ///
163 /// ```text
164 /// y 3 - 2
165 /// | 7 − 6 |
166 /// ___ x | | 1 (the zero is below 3 and on the left of 1,
167 /// / 4 - 5 hidden by the 4-5-6-7 face.)
168 /// z
169 /// ```
170 #[cfg(feature = "dim3")]
171 pub const FACES_VERTEX_IDS: [(usize, usize, usize, usize); 6] = [
172 // Face with normal +X
173 (1, 2, 6, 5),
174 // Face with normal -X
175 (0, 3, 7, 4),
176 // Face with normal +Y
177 (2, 3, 7, 6),
178 // Face with normal -Y
179 (1, 0, 4, 5),
180 // Face with normal +Z
181 (4, 5, 6, 7),
182 // Face with normal -Z
183 (0, 1, 2, 3),
184 ];
185
186 /// The vertex indices of each face of this `Aabb`.
187 ///
188 /// This gives, for each face of this `Aabb`, the indices of its
189 /// vertices when taken from the `self.vertices()` array.
190 /// Here is how the faces are numbered, assuming
191 /// a right-handed coordinate system:
192 ///
193 /// ```text
194 /// y 3 - 2
195 /// | | |
196 /// ___ x 0 - 1
197 /// ```
198 #[cfg(feature = "dim2")]
199 pub const FACES_VERTEX_IDS: [(usize, usize); 4] = [
200 // Face with normal +X
201 (1, 2),
202 // Face with normal -X
203 (3, 0),
204 // Face with normal +Y
205 (2, 3),
206 // Face with normal -Y
207 (0, 1),
208 ];
209
210 /// Creates a new AABB from its minimum and maximum corners.
211 ///
212 /// # Arguments
213 ///
214 /// * `mins` - The point with the smallest coordinates on each axis
215 /// * `maxs` - The point with the largest coordinates on each axis
216 ///
217 /// # Invariant
218 ///
219 /// Each component of `mins` should be ≤ the corresponding component of `maxs`.
220 ///
221 /// # Example
222 ///
223 /// ```
224 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
225 /// use parry3d::bounding_volume::Aabb;
226 /// use nalgebra::Point3;
227 ///
228 /// // Create a 2x2x2 cube centered at origin
229 /// let aabb = Aabb::new(
230 /// Point3::new(-1.0, -1.0, -1.0),
231 /// Point3::new(1.0, 1.0, 1.0)
232 /// );
233 ///
234 /// assert_eq!(aabb.center(), Point3::origin());
235 /// assert_eq!(aabb.extents(), nalgebra::Vector3::new(2.0, 2.0, 2.0));
236 /// # }
237 /// ```
238 #[inline]
239 pub fn new(mins: Point<Real>, maxs: Point<Real>) -> Aabb {
240 Aabb { mins, maxs }
241 }
242
243 /// Creates an invalid AABB with inverted bounds.
244 ///
245 /// The resulting AABB has `mins` set to maximum values and `maxs` set to
246 /// minimum values. This is useful as an initial value for AABB merging
247 /// algorithms (similar to starting a min operation with infinity).
248 ///
249 /// # Example
250 ///
251 /// ```
252 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
253 /// use parry3d::bounding_volume::{Aabb, BoundingVolume};
254 /// use nalgebra::Point3;
255 ///
256 /// let mut aabb = Aabb::new_invalid();
257 ///
258 /// // Merge with actual points to build proper AABB
259 /// aabb.merge(&Aabb::new(Point3::new(1.0, 2.0, 3.0), Point3::new(1.0, 2.0, 3.0)));
260 /// aabb.merge(&Aabb::new(Point3::new(-1.0, 0.0, 2.0), Point3::new(-1.0, 0.0, 2.0)));
261 ///
262 /// // Now contains the merged bounds
263 /// assert_eq!(aabb.mins, Point3::new(-1.0, 0.0, 2.0));
264 /// assert_eq!(aabb.maxs, Point3::new(1.0, 2.0, 3.0));
265 /// # }
266 /// ```
267 #[inline]
268 pub fn new_invalid() -> Self {
269 Self::new(
270 Vector::repeat(Real::max_value()).into(),
271 Vector::repeat(-Real::max_value()).into(),
272 )
273 }
274
275 /// Creates a new AABB from its center and half-extents.
276 ///
277 /// This is often more intuitive than specifying min and max corners.
278 ///
279 /// # Arguments
280 ///
281 /// * `center` - The center point of the AABB
282 /// * `half_extents` - Half the dimensions along each axis
283 ///
284 /// # Example
285 ///
286 /// ```
287 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
288 /// use parry3d::bounding_volume::Aabb;
289 /// use nalgebra::{Point3, Vector3};
290 ///
291 /// // Create a 10x6x8 box centered at (5, 0, 0)
292 /// let aabb = Aabb::from_half_extents(
293 /// Point3::new(5.0, 0.0, 0.0),
294 /// Vector3::new(5.0, 3.0, 4.0)
295 /// );
296 ///
297 /// assert_eq!(aabb.mins, Point3::new(0.0, -3.0, -4.0));
298 /// assert_eq!(aabb.maxs, Point3::new(10.0, 3.0, 4.0));
299 /// assert_eq!(aabb.center(), Point3::new(5.0, 0.0, 0.0));
300 /// # }
301 /// ```
302 #[inline]
303 pub fn from_half_extents(center: Point<Real>, half_extents: Vector<Real>) -> Self {
304 Self::new(center - half_extents, center + half_extents)
305 }
306
307 /// Creates a new AABB that tightly encloses a set of points (references).
308 ///
309 /// Computes the minimum and maximum coordinates across all points.
310 ///
311 /// # Arguments
312 ///
313 /// * `pts` - An iterator over point references
314 ///
315 /// # Example
316 ///
317 /// ```
318 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
319 /// use parry3d::bounding_volume::Aabb;
320 /// use nalgebra::Point3;
321 ///
322 /// let points = vec![
323 /// Point3::new(1.0, 2.0, 3.0),
324 /// Point3::new(-1.0, 4.0, 2.0),
325 /// Point3::new(0.0, 0.0, 5.0),
326 /// ];
327 ///
328 /// let aabb = Aabb::from_points_ref(&points);
329 /// assert_eq!(aabb.mins, Point3::new(-1.0, 0.0, 2.0));
330 /// assert_eq!(aabb.maxs, Point3::new(1.0, 4.0, 5.0));
331 /// # }
332 /// ```
333 pub fn from_points_ref<'a, I>(pts: I) -> Self
334 where
335 I: IntoIterator<Item = &'a Point<Real>>,
336 {
337 super::aabb_utils::local_point_cloud_aabb(pts.into_iter().copied())
338 }
339
340 /// Creates a new AABB that tightly encloses a set of points (values).
341 ///
342 /// Computes the minimum and maximum coordinates across all points.
343 ///
344 /// # Arguments
345 ///
346 /// * `pts` - An iterator over point values
347 ///
348 /// # Example
349 ///
350 /// ```
351 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
352 /// use parry3d::bounding_volume::Aabb;
353 /// use nalgebra::Point3;
354 ///
355 /// let aabb = Aabb::from_points(vec![
356 /// Point3::new(1.0, 2.0, 3.0),
357 /// Point3::new(-1.0, 4.0, 2.0),
358 /// Point3::new(0.0, 0.0, 5.0),
359 /// ]);
360 ///
361 /// assert_eq!(aabb.mins, Point3::new(-1.0, 0.0, 2.0));
362 /// assert_eq!(aabb.maxs, Point3::new(1.0, 4.0, 5.0));
363 /// # }
364 /// ```
365 pub fn from_points<I>(pts: I) -> Self
366 where
367 I: IntoIterator<Item = Point<Real>>,
368 {
369 super::aabb_utils::local_point_cloud_aabb(pts)
370 }
371
372 /// Returns the center point of this AABB.
373 ///
374 /// The center is the midpoint between `mins` and `maxs`.
375 ///
376 /// # Example
377 ///
378 /// ```
379 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
380 /// use parry3d::bounding_volume::Aabb;
381 /// use nalgebra::Point3;
382 ///
383 /// let aabb = Aabb::new(
384 /// Point3::new(-2.0, -3.0, -4.0),
385 /// Point3::new(2.0, 3.0, 4.0)
386 /// );
387 ///
388 /// assert_eq!(aabb.center(), Point3::origin());
389 /// # }
390 /// ```
391 #[inline]
392 pub fn center(&self) -> Point<Real> {
393 na::center(&self.mins, &self.maxs)
394 }
395
396 /// Returns the half-extents of this AABB.
397 ///
398 /// Half-extents are half the dimensions along each axis.
399 ///
400 /// # Example
401 ///
402 /// ```
403 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
404 /// use parry3d::bounding_volume::Aabb;
405 /// use nalgebra::{Point3, Vector3};
406 ///
407 /// let aabb = Aabb::new(
408 /// Point3::new(-5.0, -3.0, -2.0),
409 /// Point3::new(5.0, 3.0, 2.0)
410 /// );
411 ///
412 /// let half = aabb.half_extents();
413 /// assert_eq!(half, Vector3::new(5.0, 3.0, 2.0));
414 ///
415 /// // Full dimensions are 2 * half_extents
416 /// let full = aabb.extents();
417 /// assert_eq!(full, Vector3::new(10.0, 6.0, 4.0));
418 /// # }
419 /// ```
420 #[inline]
421 pub fn half_extents(&self) -> Vector<Real> {
422 let half: Real = na::convert::<f64, Real>(0.5);
423 (self.maxs - self.mins) * half
424 }
425
426 /// Returns the volume of this AABB.
427 ///
428 /// - **2D**: Returns the area (width × height)
429 /// - **3D**: Returns the volume (width × height × depth)
430 ///
431 /// # Example
432 ///
433 /// ```
434 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
435 /// use parry3d::bounding_volume::Aabb;
436 /// use nalgebra::Point3;
437 ///
438 /// // A 2x3x4 box
439 /// let aabb = Aabb::new(
440 /// Point3::origin(),
441 /// Point3::new(2.0, 3.0, 4.0)
442 /// );
443 ///
444 /// assert_eq!(aabb.volume(), 24.0); // 2 * 3 * 4
445 /// # }
446 /// ```
447 #[inline]
448 pub fn volume(&self) -> Real {
449 let extents = self.extents();
450 #[cfg(feature = "dim2")]
451 return extents.x * extents.y;
452 #[cfg(feature = "dim3")]
453 return extents.x * extents.y * extents.z;
454 }
455
456 /// In 3D, returns the half-area. In 2D returns the half-perimeter of the AABB.
457 pub fn half_area_or_perimeter(&self) -> Real {
458 #[cfg(feature = "dim2")]
459 return self.half_perimeter();
460 #[cfg(feature = "dim3")]
461 return self.half_area();
462 }
463
464 /// The half perimeter of this `Aabb`.
465 #[cfg(feature = "dim2")]
466 pub fn half_perimeter(&self) -> Real {
467 let extents = self.extents();
468 extents.x + extents.y
469 }
470
471 /// The half area of this `Aabb`.
472 #[cfg(feature = "dim3")]
473 pub fn half_area(&self) -> Real {
474 let extents = self.extents();
475 extents.x * (extents.y + extents.z) + extents.y * extents.z
476 }
477
478 /// The extents of this `Aabb`.
479 #[inline]
480 pub fn extents(&self) -> Vector<Real> {
481 self.maxs - self.mins
482 }
483
484 /// Enlarges this `Aabb` so it also contains the point `pt`.
485 pub fn take_point(&mut self, pt: Point<Real>) {
486 self.mins = self.mins.coords.inf(&pt.coords).into();
487 self.maxs = self.maxs.coords.sup(&pt.coords).into();
488 }
489
490 /// Computes the `Aabb` bounding `self` transformed by `m`.
491 #[inline]
492 pub fn transform_by(&self, m: &Isometry<Real>) -> Self {
493 let ls_center = self.center();
494 let center = m * ls_center;
495 let ws_half_extents = m.absolute_transform_vector(&self.half_extents());
496
497 Aabb::new(center + (-ws_half_extents), center + ws_half_extents)
498 }
499
500 /// Computes the Aabb bounding `self` translated by `translation`.
501 #[inline]
502 pub fn translated(mut self, translation: &Vector<Real>) -> Self {
503 self.mins += translation;
504 self.maxs += translation;
505 self
506 }
507
508 #[inline]
509 pub fn scaled(self, scale: &Vector<Real>) -> Self {
510 let a = self.mins.coords.component_mul(scale);
511 let b = self.maxs.coords.component_mul(scale);
512 Self {
513 mins: a.inf(&b).into(),
514 maxs: a.sup(&b).into(),
515 }
516 }
517
518 /// Returns an AABB with the same center as `self` but with extents scaled by `scale`.
519 ///
520 /// # Parameters
521 /// - `scale`: the scaling factor. It can be non-uniform and/or negative. The AABB being
522 /// symmetric wrt. its center, a negative scale value has the same effect as scaling
523 /// by its absolute value.
524 #[inline]
525 #[must_use]
526 pub fn scaled_wrt_center(self, scale: &Vector<Real>) -> Self {
527 let center = self.center();
528 // Multiply the extents by the scale. Negative scaling might modify the half-extent
529 // sign, so we take the absolute value. The AABB being symmetric that absolute value
530 // is valid.
531 let half_extents = self.half_extents().component_mul(scale).abs();
532 Self::from_half_extents(center, half_extents)
533 }
534
535 /// The smallest bounding sphere containing this `Aabb`.
536 #[inline]
537 pub fn bounding_sphere(&self) -> BoundingSphere {
538 let center = self.center();
539 let radius = na::distance(&self.mins, &self.maxs) * 0.5;
540 BoundingSphere::new(center, radius)
541 }
542
543 /// Does this AABB contains a point expressed in the same coordinate frame as `self`?
544 #[inline]
545 pub fn contains_local_point(&self, point: &Point<Real>) -> bool {
546 for i in 0..DIM {
547 if point[i] < self.mins[i] || point[i] > self.maxs[i] {
548 return false;
549 }
550 }
551
552 true
553 }
554
555 /// Computes the distance between the origin and this AABB.
556 pub fn distance_to_origin(&self) -> Real {
557 self.mins
558 .coords
559 .sup(&-self.maxs.coords)
560 .sup(&Vector::zeros())
561 .norm()
562 }
563
564 /// Does this AABB intersects an AABB `aabb2` moving at velocity `vel12` relative to `self`?
565 #[inline]
566 pub fn intersects_moving_aabb(&self, aabb2: &Self, vel12: Vector<Real>) -> bool {
567 // Minkowski sum.
568 let msum = Aabb {
569 mins: self.mins - aabb2.maxs.coords,
570 maxs: self.maxs - aabb2.mins.coords,
571 };
572 let ray = Ray::new(Point::origin(), vel12);
573
574 msum.intersects_local_ray(&ray, 1.0)
575 }
576
577 /// Computes the intersection of this `Aabb` and another one.
578 pub fn intersection(&self, other: &Aabb) -> Option<Aabb> {
579 let result = Aabb {
580 mins: Point::from(self.mins.coords.sup(&other.mins.coords)),
581 maxs: Point::from(self.maxs.coords.inf(&other.maxs.coords)),
582 };
583
584 for i in 0..DIM {
585 if result.mins[i] > result.maxs[i] {
586 return None;
587 }
588 }
589
590 Some(result)
591 }
592
593 /// Computes two AABBs for the intersection between two translated and rotated AABBs.
594 ///
595 /// This method returns two AABBs: the first is expressed in the local-space of `self`,
596 /// and the second is expressed in the local-space of `aabb2`.
597 pub fn aligned_intersections(
598 &self,
599 pos12: &Isometry<Real>,
600 aabb2: &Self,
601 ) -> Option<(Aabb, Aabb)> {
602 let pos21 = pos12.inverse();
603
604 let aabb2_1 = aabb2.transform_by(pos12);
605 let inter1_1 = self.intersection(&aabb2_1)?;
606 let inter1_2 = inter1_1.transform_by(&pos21);
607
608 let aabb1_2 = self.transform_by(&pos21);
609 let inter2_2 = aabb2.intersection(&aabb1_2)?;
610 let inter2_1 = inter2_2.transform_by(pos12);
611
612 Some((
613 inter1_1.intersection(&inter2_1)?,
614 inter1_2.intersection(&inter2_2)?,
615 ))
616 }
617
618 /// Returns the difference between this `Aabb` and `rhs`.
619 ///
620 /// Removing another `Aabb` from `self` will result in zero, one, or up to 4 (in 2D) or 8 (in 3D)
621 /// new smaller Aabbs.
622 pub fn difference(&self, rhs: &Aabb) -> ArrayVec<Self, TWO_DIM> {
623 self.difference_with_cut_sequence(rhs).0
624 }
625
626 /// Returns the difference between this `Aabb` and `rhs`.
627 ///
628 /// Removing another `Aabb` from `self` will result in zero, one, or up to 4 (in 2D) or 8 (in 3D)
629 /// new smaller Aabbs.
630 ///
631 /// # Return
632 /// This returns a pair where the first item are the new Aabbs and the second item is
633 /// the sequence of cuts applied to `self` to obtain the new Aabbs. Each cut is performed
634 /// along one axis identified by `-1, -2, -3` for `-X, -Y, -Z` and `1, 2, 3` for `+X, +Y, +Z`, and
635 /// the plane’s bias.
636 ///
637 /// The cuts are applied sequentially. For example, if `result.1[0]` contains `1`, then it means
638 /// that `result.0[0]` is equal to the piece of `self` lying in the negative half-space delimited
639 /// by the plane with outward normal `+X`. Then, the other piece of `self` generated by this cut
640 /// (i.e. the piece of `self` lying in the positive half-space delimited by the plane with outward
641 /// normal `+X`) is the one that will be affected by the next cut.
642 ///
643 /// The returned cut sequence will be empty if the aabbs are disjoint.
644 pub fn difference_with_cut_sequence(
645 &self,
646 rhs: &Aabb,
647 ) -> (ArrayVec<Self, TWO_DIM>, ArrayVec<(i8, Real), TWO_DIM>) {
648 let mut result = ArrayVec::new();
649 let mut cut_sequence = ArrayVec::new();
650
651 // NOTE: special case when the boxes are disjoint.
652 // This isn’t exactly the same as `!self.intersects(rhs)`
653 // because of the equality.
654 for i in 0..DIM {
655 if self.mins[i] >= rhs.maxs[i] || self.maxs[i] <= rhs.mins[i] {
656 result.push(*self);
657 return (result, cut_sequence);
658 }
659 }
660
661 let mut rest = *self;
662
663 for i in 0..DIM {
664 if rhs.mins[i] > rest.mins[i] {
665 let mut fragment = rest;
666 fragment.maxs[i] = rhs.mins[i];
667 rest.mins[i] = rhs.mins[i];
668 result.push(fragment);
669 cut_sequence.push((i as i8 + 1, rhs.mins[i]));
670 }
671
672 if rhs.maxs[i] < rest.maxs[i] {
673 let mut fragment = rest;
674 fragment.mins[i] = rhs.maxs[i];
675 rest.maxs[i] = rhs.maxs[i];
676 result.push(fragment);
677 cut_sequence.push((-(i as i8 + 1), -rhs.maxs[i]));
678 }
679 }
680
681 (result, cut_sequence)
682 }
683
684 /// Computes the vertices of this `Aabb`.
685 ///
686 /// The vertices are given in the following order in a right-handed coordinate system:
687 /// ```text
688 /// y 3 - 2
689 /// | | |
690 /// ___ x 0 - 1
691 /// ```
692 #[inline]
693 #[cfg(feature = "dim2")]
694 pub fn vertices(&self) -> [Point<Real>; 4] {
695 [
696 Point::new(self.mins.x, self.mins.y),
697 Point::new(self.maxs.x, self.mins.y),
698 Point::new(self.maxs.x, self.maxs.y),
699 Point::new(self.mins.x, self.maxs.y),
700 ]
701 }
702
703 /// Computes the vertices of this `Aabb`.
704 ///
705 /// The vertices are given in the following order, in a right-handed coordinate system:
706 /// ```text
707 /// y 3 - 2
708 /// | 7 − 6 |
709 /// ___ x | | 1 (the zero is below 3 and on the left of 1,
710 /// / 4 - 5 hidden by the 4-5-6-7 face.)
711 /// z
712 /// ```
713 #[inline]
714 #[cfg(feature = "dim3")]
715 pub fn vertices(&self) -> [Point<Real>; 8] {
716 [
717 Point::new(self.mins.x, self.mins.y, self.mins.z),
718 Point::new(self.maxs.x, self.mins.y, self.mins.z),
719 Point::new(self.maxs.x, self.maxs.y, self.mins.z),
720 Point::new(self.mins.x, self.maxs.y, self.mins.z),
721 Point::new(self.mins.x, self.mins.y, self.maxs.z),
722 Point::new(self.maxs.x, self.mins.y, self.maxs.z),
723 Point::new(self.maxs.x, self.maxs.y, self.maxs.z),
724 Point::new(self.mins.x, self.maxs.y, self.maxs.z),
725 ]
726 }
727
728 /// Splits this `Aabb` at its center, into four parts (as in a quad-tree).
729 #[inline]
730 #[cfg(feature = "dim2")]
731 pub fn split_at_center(&self) -> [Aabb; 4] {
732 let center = self.center();
733
734 [
735 Aabb::new(self.mins, center),
736 Aabb::new(
737 Point::new(center.x, self.mins.y),
738 Point::new(self.maxs.x, center.y),
739 ),
740 Aabb::new(center, self.maxs),
741 Aabb::new(
742 Point::new(self.mins.x, center.y),
743 Point::new(center.x, self.maxs.y),
744 ),
745 ]
746 }
747
748 /// Splits this `Aabb` at its center, into eight parts (as in an octree).
749 #[inline]
750 #[cfg(feature = "dim3")]
751 pub fn split_at_center(&self) -> [Aabb; 8] {
752 let center = self.center();
753
754 [
755 Aabb::new(
756 Point::new(self.mins.x, self.mins.y, self.mins.z),
757 Point::new(center.x, center.y, center.z),
758 ),
759 Aabb::new(
760 Point::new(center.x, self.mins.y, self.mins.z),
761 Point::new(self.maxs.x, center.y, center.z),
762 ),
763 Aabb::new(
764 Point::new(center.x, center.y, self.mins.z),
765 Point::new(self.maxs.x, self.maxs.y, center.z),
766 ),
767 Aabb::new(
768 Point::new(self.mins.x, center.y, self.mins.z),
769 Point::new(center.x, self.maxs.y, center.z),
770 ),
771 Aabb::new(
772 Point::new(self.mins.x, self.mins.y, center.z),
773 Point::new(center.x, center.y, self.maxs.z),
774 ),
775 Aabb::new(
776 Point::new(center.x, self.mins.y, center.z),
777 Point::new(self.maxs.x, center.y, self.maxs.z),
778 ),
779 Aabb::new(
780 Point::new(center.x, center.y, center.z),
781 Point::new(self.maxs.x, self.maxs.y, self.maxs.z),
782 ),
783 Aabb::new(
784 Point::new(self.mins.x, center.y, center.z),
785 Point::new(center.x, self.maxs.y, self.maxs.z),
786 ),
787 ]
788 }
789
790 /// Enlarges this AABB on each side by the given `half_extents`.
791 #[must_use]
792 pub fn add_half_extents(&self, half_extents: &Vector<Real>) -> Self {
793 Self {
794 mins: self.mins - half_extents,
795 maxs: self.maxs + half_extents,
796 }
797 }
798
799 /// Projects every point of `Aabb` on an arbitrary axis.
800 pub fn project_on_axis(&self, axis: &UnitVector<Real>) -> (Real, Real) {
801 let cuboid = Cuboid::new(self.half_extents());
802 let shift = cuboid
803 .local_support_point_toward(axis)
804 .coords
805 .dot(axis)
806 .abs();
807 let center = self.center().coords.dot(axis);
808 (center - shift, center + shift)
809 }
810
811 #[cfg(feature = "dim3")]
812 #[cfg(feature = "alloc")]
813 pub fn intersects_spiral(
814 &self,
815 point: &Point<Real>,
816 center: &Point<Real>,
817 axis: &UnitVector<Real>,
818 linvel: &Vector<Real>,
819 angvel: Real,
820 ) -> bool {
821 use crate::utils::WBasis;
822 use crate::utils::{Interval, IntervalFunction};
823 use alloc::vec;
824
825 struct SpiralPlaneDistance {
826 center: Point<Real>,
827 tangents: [Vector<Real>; 2],
828 linvel: Vector<Real>,
829 angvel: Real,
830 point: na::Vector2<Real>,
831 plane: Vector<Real>,
832 bias: Real,
833 }
834
835 impl SpiralPlaneDistance {
836 fn spiral_pt_at(&self, t: Real) -> Point<Real> {
837 let angle = t * self.angvel;
838
839 // NOTE: we construct the rotation matrix explicitly here instead
840 // of using `Rotation2::new()` because we will use similar
841 // formulas on the interval methods.
842 let (sin, cos) = angle.sin_cos();
843 let rotmat = na::Matrix2::new(cos, -sin, sin, cos);
844
845 let rotated_pt = rotmat * self.point;
846 let shift = self.tangents[0] * rotated_pt.x + self.tangents[1] * rotated_pt.y;
847 self.center + self.linvel * t + shift
848 }
849 }
850
851 impl IntervalFunction<Real> for SpiralPlaneDistance {
852 fn eval(&self, t: Real) -> Real {
853 let point_pos = self.spiral_pt_at(t);
854 point_pos.coords.dot(&self.plane) - self.bias
855 }
856
857 fn eval_interval(&self, t: Interval<Real>) -> Interval<Real> {
858 // This is the same as `self.eval` except that `t` is an interval.
859 let angle = t * self.angvel;
860 let (sin, cos) = angle.sin_cos();
861 let rotmat = na::Matrix2::new(cos, -sin, sin, cos);
862
863 let rotated_pt = rotmat * self.point.map(Interval::splat);
864 let shift = self.tangents[0].map(Interval::splat) * rotated_pt.x
865 + self.tangents[1].map(Interval::splat) * rotated_pt.y;
866 let point_pos =
867 self.center.map(Interval::splat) + self.linvel.map(Interval::splat) * t + shift;
868 point_pos.coords.dot(&self.plane.map(Interval::splat)) - Interval::splat(self.bias)
869 }
870
871 fn eval_interval_gradient(&self, t: Interval<Real>) -> Interval<Real> {
872 let angle = t * self.angvel;
873 let (sin, cos) = angle.sin_cos();
874 let rotmat = na::Matrix2::new(-sin, -cos, cos, -sin) * Interval::splat(self.angvel);
875
876 let rotated_pt = rotmat * self.point.map(Interval::splat);
877 let shift = self.tangents[0].map(Interval::splat) * rotated_pt.x
878 + self.tangents[1].map(Interval::splat) * rotated_pt.y;
879 let point_vel = shift + self.linvel.map(Interval::splat);
880 point_vel.dot(&self.plane.map(Interval::splat))
881 }
882 }
883
884 let tangents = axis.orthonormal_basis();
885 let dpos = point - center;
886 let mut distance_fn = SpiralPlaneDistance {
887 center: *center,
888 tangents,
889 linvel: *linvel,
890 angvel,
891 point: na::Vector2::new(dpos.dot(&tangents[0]), dpos.dot(&tangents[1])),
892 plane: Vector::x(),
893 bias: 0.0,
894 };
895
896 // Check the 8 planar faces of the Aabb.
897 let mut roots = vec![];
898 let mut candidates = vec![];
899
900 let planes = [
901 (-self.mins[0], -Vector::x(), 0),
902 (self.maxs[0], Vector::x(), 0),
903 (-self.mins[1], -Vector::y(), 1),
904 (self.maxs[1], Vector::y(), 1),
905 (-self.mins[2], -Vector::z(), 2),
906 (self.maxs[2], Vector::z(), 2),
907 ];
908
909 let range = self.project_on_axis(axis);
910 let range_bias = center.coords.dot(axis);
911 let interval = Interval::sort(range.0, range.1) - range_bias;
912
913 for (bias, axis, i) in &planes {
914 distance_fn.plane = *axis;
915 distance_fn.bias = *bias;
916
917 crate::utils::find_root_intervals_to(
918 &distance_fn,
919 interval,
920 1.0e-5,
921 1.0e-5,
922 100,
923 &mut roots,
924 &mut candidates,
925 );
926
927 for root in roots.drain(..) {
928 let point = distance_fn.spiral_pt_at(root.midpoint());
929 let (j, k) = ((i + 1) % 3, (i + 2) % 3);
930 if point[j] >= self.mins[j]
931 && point[j] <= self.maxs[j]
932 && point[k] >= self.mins[k]
933 && point[k] <= self.maxs[k]
934 {
935 return true;
936 }
937 }
938 }
939
940 false
941 }
942}
943
944impl BoundingVolume for Aabb {
945 #[inline]
946 fn center(&self) -> Point<Real> {
947 self.center()
948 }
949
950 #[inline]
951 fn intersects(&self, other: &Aabb) -> bool {
952 na::partial_le(&self.mins, &other.maxs) && na::partial_ge(&self.maxs, &other.mins)
953 }
954
955 #[inline]
956 fn contains(&self, other: &Aabb) -> bool {
957 na::partial_le(&self.mins, &other.mins) && na::partial_ge(&self.maxs, &other.maxs)
958 }
959
960 #[inline]
961 fn merge(&mut self, other: &Aabb) {
962 self.mins = self.mins.inf(&other.mins);
963 self.maxs = self.maxs.sup(&other.maxs);
964 }
965
966 #[inline]
967 fn merged(&self, other: &Aabb) -> Aabb {
968 Aabb {
969 mins: self.mins.inf(&other.mins),
970 maxs: self.maxs.sup(&other.maxs),
971 }
972 }
973
974 #[inline]
975 fn loosen(&mut self, amount: Real) {
976 assert!(amount >= 0.0, "The loosening margin must be positive.");
977 self.mins += Vector::repeat(-amount);
978 self.maxs += Vector::repeat(amount);
979 }
980
981 #[inline]
982 fn loosened(&self, amount: Real) -> Aabb {
983 assert!(amount >= 0.0, "The loosening margin must be positive.");
984 Aabb {
985 mins: self.mins + Vector::repeat(-amount),
986 maxs: self.maxs + Vector::repeat(amount),
987 }
988 }
989
990 #[inline]
991 fn tighten(&mut self, amount: Real) {
992 assert!(amount >= 0.0, "The tightening margin must be positive.");
993 self.mins += Vector::repeat(amount);
994 self.maxs += Vector::repeat(-amount);
995 assert!(
996 na::partial_le(&self.mins, &self.maxs),
997 "The tightening margin is to large."
998 );
999 }
1000
1001 #[inline]
1002 fn tightened(&self, amount: Real) -> Aabb {
1003 assert!(amount >= 0.0, "The tightening margin must be positive.");
1004
1005 Aabb::new(
1006 self.mins + Vector::repeat(amount),
1007 self.maxs + Vector::repeat(-amount),
1008 )
1009 }
1010}