parry3d_f64/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; // for .sin_cos()
13
14use crate::query::{Ray, RayCast};
15#[cfg(feature = "rkyv")]
16use rkyv::{bytecheck, CheckBytes};
17
18/// An Axis Aligned Bounding Box.
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
21#[cfg_attr(
22    feature = "rkyv",
23    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
24    archive(as = "Self")
25)]
26#[derive(Debug, PartialEq, Copy, Clone)]
27#[repr(C)]
28pub struct Aabb {
29    pub mins: Point<Real>,
30    pub maxs: Point<Real>,
31}
32
33impl Aabb {
34    /// The vertex indices of each edge of this `Aabb`.
35    ///
36    /// This gives, for each edge of this `Aabb`, the indices of its
37    /// vertices when taken from the `self.vertices()` array.
38    /// Here is how the faces are numbered, assuming
39    /// a right-handed coordinate system:
40    ///
41    /// ```text
42    ///    y             3 - 2
43    ///    |           7 − 6 |
44    ///    ___ x       |   | 1  (the zero is below 3 and on the left of 1,
45    ///   /            4 - 5     hidden by the 4-5-6-7 face.)
46    ///  z
47    /// ```
48    #[cfg(feature = "dim3")]
49    pub const EDGES_VERTEX_IDS: [(usize, usize); 12] = [
50        (0, 1),
51        (1, 2),
52        (3, 2),
53        (0, 3),
54        (4, 5),
55        (5, 6),
56        (7, 6),
57        (4, 7),
58        (0, 4),
59        (1, 5),
60        (2, 6),
61        (3, 7),
62    ];
63
64    /// The vertex indices of each face of this `Aabb`.
65    ///
66    /// This gives, for each face of this `Aabb`, the indices of its
67    /// vertices when taken from the `self.vertices()` array.
68    /// Here is how the faces are numbered, assuming
69    /// a right-handed coordinate system:
70    ///
71    /// ```text
72    ///    y             3 - 2
73    ///    |           7 − 6 |
74    ///    ___ x       |   | 1  (the zero is below 3 and on the left of 1,
75    ///   /            4 - 5     hidden by the 4-5-6-7 face.)
76    ///  z
77    /// ```
78    #[cfg(feature = "dim3")]
79    pub const FACES_VERTEX_IDS: [(usize, usize, usize, usize); 6] = [
80        // Face with normal +X
81        (1, 2, 6, 5),
82        // Face with normal -X
83        (0, 3, 7, 4),
84        // Face with normal +Y
85        (2, 3, 7, 6),
86        // Face with normal -Y
87        (1, 0, 4, 5),
88        // Face with normal +Z
89        (4, 5, 6, 7),
90        // Face with normal -Z
91        (0, 1, 2, 3),
92    ];
93
94    /// The vertex indices of each face of this `Aabb`.
95    ///
96    /// This gives, for each face of this `Aabb`, the indices of its
97    /// vertices when taken from the `self.vertices()` array.
98    /// Here is how the faces are numbered, assuming
99    /// a right-handed coordinate system:
100    ///
101    /// ```text
102    ///    y             3 - 2
103    ///    |             |   |
104    ///    ___ x         0 - 1
105    /// ```
106    #[cfg(feature = "dim2")]
107    pub const FACES_VERTEX_IDS: [(usize, usize); 4] = [
108        // Face with normal +X
109        (1, 2),
110        // Face with normal -X
111        (3, 0),
112        // Face with normal +Y
113        (2, 3),
114        // Face with normal -Y
115        (0, 1),
116    ];
117
118    /// Creates a new Aabb.
119    ///
120    /// # Arguments:
121    ///   * `mins` - position of the point with the smallest coordinates.
122    ///   * `maxs` - position of the point with the highest coordinates. Each component of `mins`
123    ///     must be smaller than the related components of `maxs`.
124    #[inline]
125    pub fn new(mins: Point<Real>, maxs: Point<Real>) -> Aabb {
126        Aabb { mins, maxs }
127    }
128
129    /// Creates an invalid `Aabb` with `mins` components set to `Real::max_values` and `maxs`components set to `-Real::max_values`.
130    ///
131    /// This is often used as the initial values of some `Aabb` merging algorithms.
132    #[inline]
133    pub fn new_invalid() -> Self {
134        Self::new(
135            Vector::repeat(Real::max_value()).into(),
136            Vector::repeat(-Real::max_value()).into(),
137        )
138    }
139
140    /// Creates a new `Aabb` from its center and its half-extents.
141    #[inline]
142    pub fn from_half_extents(center: Point<Real>, half_extents: Vector<Real>) -> Self {
143        Self::new(center - half_extents, center + half_extents)
144    }
145
146    /// Creates a new `Aabb` from a set of points.
147    pub fn from_points<'a, I>(pts: I) -> Self
148    where
149        I: IntoIterator<Item = &'a Point<Real>>,
150    {
151        super::aabb_utils::local_point_cloud_aabb(pts)
152    }
153
154    /// The center of this `Aabb`.
155    #[inline]
156    pub fn center(&self) -> Point<Real> {
157        na::center(&self.mins, &self.maxs)
158    }
159
160    /// The half extents of this `Aabb`.
161    #[inline]
162    pub fn half_extents(&self) -> Vector<Real> {
163        let half: Real = na::convert::<f64, Real>(0.5);
164        (self.maxs - self.mins) * half
165    }
166
167    /// The volume of this `Aabb`.
168    #[inline]
169    pub fn volume(&self) -> Real {
170        let extents = self.extents();
171        #[cfg(feature = "dim2")]
172        return extents.x * extents.y;
173        #[cfg(feature = "dim3")]
174        return extents.x * extents.y * extents.z;
175    }
176
177    /// The extents of this `Aabb`.
178    #[inline]
179    pub fn extents(&self) -> Vector<Real> {
180        self.maxs - self.mins
181    }
182
183    /// Enlarges this `Aabb` so it also contains the point `pt`.
184    pub fn take_point(&mut self, pt: Point<Real>) {
185        self.mins = self.mins.coords.inf(&pt.coords).into();
186        self.maxs = self.maxs.coords.sup(&pt.coords).into();
187    }
188
189    /// Computes the `Aabb` bounding `self` transformed by `m`.
190    #[inline]
191    pub fn transform_by(&self, m: &Isometry<Real>) -> Self {
192        let ls_center = self.center();
193        let center = m * ls_center;
194        let ws_half_extents = m.absolute_transform_vector(&self.half_extents());
195
196        Aabb::new(center + (-ws_half_extents), center + ws_half_extents)
197    }
198
199    /// Computes the Aabb bounding `self` translated by `translation`.
200    #[inline]
201    pub fn translated(mut self, translation: &Vector<Real>) -> Self {
202        self.mins += translation;
203        self.maxs += translation;
204        self
205    }
206
207    #[inline]
208    pub fn scaled(self, scale: &Vector<Real>) -> Self {
209        let a = self.mins.coords.component_mul(scale);
210        let b = self.maxs.coords.component_mul(scale);
211        Self {
212            mins: a.inf(&b).into(),
213            maxs: a.sup(&b).into(),
214        }
215    }
216
217    /// Returns an AABB with the same center as `self` but with extents scaled by `scale`.
218    ///
219    /// # Parameters
220    /// - `scale`: the scaling factor. It can be non-uniform and/or negative. The AABB being
221    ///   symmetric wrt. its center, a negative scale value has the same effect as scaling
222    ///   by its absolute value.
223    #[inline]
224    #[must_use]
225    pub fn scaled_wrt_center(self, scale: &Vector<Real>) -> Self {
226        let center = self.center();
227        // Multiply the extents by the scale. Negative scaling might modify the half-extent
228        // sign, so we take the absolute value. The AABB being symmetric that absolute value
229        // is  valid.
230        let half_extents = self.half_extents().component_mul(scale).abs();
231        Self::from_half_extents(center, half_extents)
232    }
233
234    /// The smallest bounding sphere containing this `Aabb`.
235    #[inline]
236    pub fn bounding_sphere(&self) -> BoundingSphere {
237        let center = self.center();
238        let radius = na::distance(&self.mins, &self.maxs) * 0.5;
239        BoundingSphere::new(center, radius)
240    }
241
242    /// Does this AABB contains a point expressed in the same coordinate frame as `self`?
243    #[inline]
244    pub fn contains_local_point(&self, point: &Point<Real>) -> bool {
245        for i in 0..DIM {
246            if point[i] < self.mins[i] || point[i] > self.maxs[i] {
247                return false;
248            }
249        }
250
251        true
252    }
253
254    /// Does this AABB intersects an AABB `aabb2` moving at velocity `vel12` relative to `self`?
255    #[inline]
256    pub fn intersects_moving_aabb(&self, aabb2: &Self, vel12: Vector<Real>) -> bool {
257        // Minkowski sum.
258        let msum = Aabb {
259            mins: self.mins - aabb2.maxs.coords,
260            maxs: self.maxs - aabb2.mins.coords,
261        };
262        let ray = Ray::new(Point::origin(), vel12);
263
264        msum.intersects_local_ray(&ray, 1.0)
265    }
266
267    /// Computes the intersection of this `Aabb` and another one.
268    pub fn intersection(&self, other: &Aabb) -> Option<Aabb> {
269        let result = Aabb {
270            mins: Point::from(self.mins.coords.sup(&other.mins.coords)),
271            maxs: Point::from(self.maxs.coords.inf(&other.maxs.coords)),
272        };
273
274        for i in 0..DIM {
275            if result.mins[i] > result.maxs[i] {
276                return None;
277            }
278        }
279
280        Some(result)
281    }
282
283    /// Computes two AABBs for the intersection between two translated and rotated AABBs.
284    ///
285    /// This method returns two AABBs: the first is expressed in the local-space of `self`,
286    /// and the second is expressed in the local-space of `aabb2`.
287    pub fn aligned_intersections(
288        &self,
289        pos12: &Isometry<Real>,
290        aabb2: &Self,
291    ) -> Option<(Aabb, Aabb)> {
292        let pos21 = pos12.inverse();
293
294        let aabb2_1 = aabb2.transform_by(pos12);
295        let inter1_1 = self.intersection(&aabb2_1)?;
296        let inter1_2 = inter1_1.transform_by(&pos21);
297
298        let aabb1_2 = self.transform_by(&pos21);
299        let inter2_2 = aabb2.intersection(&aabb1_2)?;
300        let inter2_1 = inter2_2.transform_by(pos12);
301
302        Some((
303            inter1_1.intersection(&inter2_1)?,
304            inter1_2.intersection(&inter2_2)?,
305        ))
306    }
307
308    /// Returns the difference between this `Aabb` and `rhs`.
309    ///
310    /// Removing another `Aabb` from `self` will result in zero, one, or up to 4 (in 2D) or 8 (in 3D)
311    /// new smaller Aabbs.
312    pub fn difference(&self, rhs: &Aabb) -> ArrayVec<Self, TWO_DIM> {
313        self.difference_with_cut_sequence(rhs).0
314    }
315
316    /// Returns the difference between this `Aabb` and `rhs`.
317    ///
318    /// Removing another `Aabb` from `self` will result in zero, one, or up to 4 (in 2D) or 8 (in 3D)
319    /// new smaller Aabbs.
320    ///
321    /// # Return
322    /// This returns a pair where the first item are the new Aabbs and the second item is
323    /// the sequence of cuts applied to `self` to obtain the new Aabbs. Each cut is performed
324    /// along one axis identified by `-1, -2, -3` for `-X, -Y, -Z` and `1, 2, 3` for `+X, +Y, +Z`, and
325    /// the plane’s bias.
326    ///
327    /// The cuts are applied sequentially. For example, if `result.1[0]` contains `1`, then it means
328    /// that `result.0[0]` is equal to the piece of `self` lying in the negative half-space delimited
329    /// by the plane with outward normal `+X`. Then, the other piece of `self` generated by this cut
330    /// (i.e. the piece of `self` lying in the positive half-space delimited by the plane with outward
331    /// normal `+X`) is the one that will be affected by the next cut.
332    ///
333    /// The returned cut sequence will be empty if the aabbs are disjoint.
334    pub fn difference_with_cut_sequence(
335        &self,
336        rhs: &Aabb,
337    ) -> (ArrayVec<Self, TWO_DIM>, ArrayVec<(i8, Real), TWO_DIM>) {
338        let mut result = ArrayVec::new();
339        let mut cut_sequence = ArrayVec::new();
340
341        // NOTE: special case when the boxes are disjoint.
342        //       This isn’t exactly the same as `!self.intersects(rhs)`
343        //       because of the equality.
344        for i in 0..DIM {
345            if self.mins[i] >= rhs.maxs[i] || self.maxs[i] <= rhs.mins[i] {
346                result.push(*self);
347                return (result, cut_sequence);
348            }
349        }
350
351        let mut rest = *self;
352
353        for i in 0..DIM {
354            if rhs.mins[i] > rest.mins[i] {
355                let mut fragment = rest;
356                fragment.maxs[i] = rhs.mins[i];
357                rest.mins[i] = rhs.mins[i];
358                result.push(fragment);
359                cut_sequence.push((i as i8 + 1, rhs.mins[i]));
360            }
361
362            if rhs.maxs[i] < rest.maxs[i] {
363                let mut fragment = rest;
364                fragment.mins[i] = rhs.maxs[i];
365                rest.maxs[i] = rhs.maxs[i];
366                result.push(fragment);
367                cut_sequence.push((-(i as i8 + 1), -rhs.maxs[i]));
368            }
369        }
370
371        (result, cut_sequence)
372    }
373
374    /// Computes the vertices of this `Aabb`.
375    ///
376    /// The vertices are given in the following order in a right-handed coordinate system:
377    /// ```text
378    ///    y             3 - 2
379    ///    |             |   |
380    ///    ___ x         0 - 1
381    /// ```
382    #[inline]
383    #[cfg(feature = "dim2")]
384    pub fn vertices(&self) -> [Point<Real>; 4] {
385        [
386            Point::new(self.mins.x, self.mins.y),
387            Point::new(self.maxs.x, self.mins.y),
388            Point::new(self.maxs.x, self.maxs.y),
389            Point::new(self.mins.x, self.maxs.y),
390        ]
391    }
392
393    /// Computes the vertices of this `Aabb`.
394    ///
395    /// The vertices are given in the following order, in a right-handed coordinate system:
396    /// ```text
397    ///    y             3 - 2
398    ///    |           7 − 6 |
399    ///    ___ x       |   | 1  (the zero is below 3 and on the left of 1,
400    ///   /            4 - 5     hidden by the 4-5-6-7 face.)
401    ///  z
402    /// ```
403    #[inline]
404    #[cfg(feature = "dim3")]
405    pub fn vertices(&self) -> [Point<Real>; 8] {
406        [
407            Point::new(self.mins.x, self.mins.y, self.mins.z),
408            Point::new(self.maxs.x, self.mins.y, self.mins.z),
409            Point::new(self.maxs.x, self.maxs.y, self.mins.z),
410            Point::new(self.mins.x, self.maxs.y, self.mins.z),
411            Point::new(self.mins.x, self.mins.y, self.maxs.z),
412            Point::new(self.maxs.x, self.mins.y, self.maxs.z),
413            Point::new(self.maxs.x, self.maxs.y, self.maxs.z),
414            Point::new(self.mins.x, self.maxs.y, self.maxs.z),
415        ]
416    }
417
418    /// Splits this `Aabb` at its center, into four parts (as in a quad-tree).
419    #[inline]
420    #[cfg(feature = "dim2")]
421    pub fn split_at_center(&self) -> [Aabb; 4] {
422        let center = self.center();
423
424        [
425            Aabb::new(self.mins, center),
426            Aabb::new(
427                Point::new(center.x, self.mins.y),
428                Point::new(self.maxs.x, center.y),
429            ),
430            Aabb::new(center, self.maxs),
431            Aabb::new(
432                Point::new(self.mins.x, center.y),
433                Point::new(center.x, self.maxs.y),
434            ),
435        ]
436    }
437
438    /// Splits this `Aabb` at its center, into eight parts (as in an octree).
439    #[inline]
440    #[cfg(feature = "dim3")]
441    pub fn split_at_center(&self) -> [Aabb; 8] {
442        let center = self.center();
443
444        [
445            Aabb::new(
446                Point::new(self.mins.x, self.mins.y, self.mins.z),
447                Point::new(center.x, center.y, center.z),
448            ),
449            Aabb::new(
450                Point::new(center.x, self.mins.y, self.mins.z),
451                Point::new(self.maxs.x, center.y, center.z),
452            ),
453            Aabb::new(
454                Point::new(center.x, center.y, self.mins.z),
455                Point::new(self.maxs.x, self.maxs.y, center.z),
456            ),
457            Aabb::new(
458                Point::new(self.mins.x, center.y, self.mins.z),
459                Point::new(center.x, self.maxs.y, center.z),
460            ),
461            Aabb::new(
462                Point::new(self.mins.x, self.mins.y, center.z),
463                Point::new(center.x, center.y, self.maxs.z),
464            ),
465            Aabb::new(
466                Point::new(center.x, self.mins.y, center.z),
467                Point::new(self.maxs.x, center.y, self.maxs.z),
468            ),
469            Aabb::new(
470                Point::new(center.x, center.y, center.z),
471                Point::new(self.maxs.x, self.maxs.y, self.maxs.z),
472            ),
473            Aabb::new(
474                Point::new(self.mins.x, center.y, center.z),
475                Point::new(center.x, self.maxs.y, self.maxs.z),
476            ),
477        ]
478    }
479
480    /// Projects every point of `Aabb` on an arbitrary axis.
481    pub fn project_on_axis(&self, axis: &UnitVector<Real>) -> (Real, Real) {
482        let cuboid = Cuboid::new(self.half_extents());
483        let shift = cuboid
484            .local_support_point_toward(axis)
485            .coords
486            .dot(axis)
487            .abs();
488        let center = self.center().coords.dot(axis);
489        (center - shift, center + shift)
490    }
491
492    #[cfg(feature = "dim3")]
493    #[cfg(feature = "alloc")]
494    pub fn intersects_spiral(
495        &self,
496        point: &Point<Real>,
497        center: &Point<Real>,
498        axis: &UnitVector<Real>,
499        linvel: &Vector<Real>,
500        angvel: Real,
501    ) -> bool {
502        use crate::utils::WBasis;
503        use crate::utils::{Interval, IntervalFunction};
504        use alloc::vec;
505
506        struct SpiralPlaneDistance {
507            center: Point<Real>,
508            tangents: [Vector<Real>; 2],
509            linvel: Vector<Real>,
510            angvel: Real,
511            point: na::Vector2<Real>,
512            plane: Vector<Real>,
513            bias: Real,
514        }
515
516        impl SpiralPlaneDistance {
517            fn spiral_pt_at(&self, t: Real) -> Point<Real> {
518                let angle = t * self.angvel;
519
520                // NOTE: we construct the rotation matrix explicitly here instead
521                //       of using `Rotation2::new()` because we will use similar
522                //       formulas on the interval methods.
523                let (sin, cos) = angle.sin_cos();
524                let rotmat = na::Matrix2::new(cos, -sin, sin, cos);
525
526                let rotated_pt = rotmat * self.point;
527                let shift = self.tangents[0] * rotated_pt.x + self.tangents[1] * rotated_pt.y;
528                self.center + self.linvel * t + shift
529            }
530        }
531
532        impl IntervalFunction<Real> for SpiralPlaneDistance {
533            fn eval(&self, t: Real) -> Real {
534                let point_pos = self.spiral_pt_at(t);
535                point_pos.coords.dot(&self.plane) - self.bias
536            }
537
538            fn eval_interval(&self, t: Interval<Real>) -> Interval<Real> {
539                // This is the same as `self.eval` except that `t` is an interval.
540                let angle = t * self.angvel;
541                let (sin, cos) = angle.sin_cos();
542                let rotmat = na::Matrix2::new(cos, -sin, sin, cos);
543
544                let rotated_pt = rotmat * self.point.map(Interval::splat);
545                let shift = self.tangents[0].map(Interval::splat) * rotated_pt.x
546                    + self.tangents[1].map(Interval::splat) * rotated_pt.y;
547                let point_pos =
548                    self.center.map(Interval::splat) + self.linvel.map(Interval::splat) * t + shift;
549                point_pos.coords.dot(&self.plane.map(Interval::splat)) - Interval::splat(self.bias)
550            }
551
552            fn eval_interval_gradient(&self, t: Interval<Real>) -> Interval<Real> {
553                let angle = t * self.angvel;
554                let (sin, cos) = angle.sin_cos();
555                let rotmat = na::Matrix2::new(-sin, -cos, cos, -sin) * Interval::splat(self.angvel);
556
557                let rotated_pt = rotmat * self.point.map(Interval::splat);
558                let shift = self.tangents[0].map(Interval::splat) * rotated_pt.x
559                    + self.tangents[1].map(Interval::splat) * rotated_pt.y;
560                let point_vel = shift + self.linvel.map(Interval::splat);
561                point_vel.dot(&self.plane.map(Interval::splat))
562            }
563        }
564
565        let tangents = axis.orthonormal_basis();
566        let dpos = point - center;
567        let mut distance_fn = SpiralPlaneDistance {
568            center: *center,
569            tangents,
570            linvel: *linvel,
571            angvel,
572            point: na::Vector2::new(dpos.dot(&tangents[0]), dpos.dot(&tangents[1])),
573            plane: Vector::x(),
574            bias: 0.0,
575        };
576
577        // Check the 8 planar faces of the Aabb.
578        let mut roots = vec![];
579        let mut candidates = vec![];
580
581        let planes = [
582            (-self.mins[0], -Vector::x(), 0),
583            (self.maxs[0], Vector::x(), 0),
584            (-self.mins[1], -Vector::y(), 1),
585            (self.maxs[1], Vector::y(), 1),
586            (-self.mins[2], -Vector::z(), 2),
587            (self.maxs[2], Vector::z(), 2),
588        ];
589
590        let range = self.project_on_axis(axis);
591        let range_bias = center.coords.dot(axis);
592        let interval = Interval::sort(range.0, range.1) - range_bias;
593
594        for (bias, axis, i) in &planes {
595            distance_fn.plane = *axis;
596            distance_fn.bias = *bias;
597
598            crate::utils::find_root_intervals_to(
599                &distance_fn,
600                interval,
601                1.0e-5,
602                1.0e-5,
603                100,
604                &mut roots,
605                &mut candidates,
606            );
607
608            for root in roots.drain(..) {
609                let point = distance_fn.spiral_pt_at(root.midpoint());
610                let (j, k) = ((i + 1) % 3, (i + 2) % 3);
611                if point[j] >= self.mins[j]
612                    && point[j] <= self.maxs[j]
613                    && point[k] >= self.mins[k]
614                    && point[k] <= self.maxs[k]
615                {
616                    return true;
617                }
618            }
619        }
620
621        false
622    }
623}
624
625impl BoundingVolume for Aabb {
626    #[inline]
627    fn center(&self) -> Point<Real> {
628        self.center()
629    }
630
631    #[inline]
632    fn intersects(&self, other: &Aabb) -> bool {
633        na::partial_le(&self.mins, &other.maxs) && na::partial_ge(&self.maxs, &other.mins)
634    }
635
636    #[inline]
637    fn contains(&self, other: &Aabb) -> bool {
638        na::partial_le(&self.mins, &other.mins) && na::partial_ge(&self.maxs, &other.maxs)
639    }
640
641    #[inline]
642    fn merge(&mut self, other: &Aabb) {
643        self.mins = self.mins.inf(&other.mins);
644        self.maxs = self.maxs.sup(&other.maxs);
645    }
646
647    #[inline]
648    fn merged(&self, other: &Aabb) -> Aabb {
649        Aabb {
650            mins: self.mins.inf(&other.mins),
651            maxs: self.maxs.sup(&other.maxs),
652        }
653    }
654
655    #[inline]
656    fn loosen(&mut self, amount: Real) {
657        assert!(amount >= 0.0, "The loosening margin must be positive.");
658        self.mins += Vector::repeat(-amount);
659        self.maxs += Vector::repeat(amount);
660    }
661
662    #[inline]
663    fn loosened(&self, amount: Real) -> Aabb {
664        assert!(amount >= 0.0, "The loosening margin must be positive.");
665        Aabb {
666            mins: self.mins + Vector::repeat(-amount),
667            maxs: self.maxs + Vector::repeat(amount),
668        }
669    }
670
671    #[inline]
672    fn tighten(&mut self, amount: Real) {
673        assert!(amount >= 0.0, "The tightening margin must be positive.");
674        self.mins += Vector::repeat(amount);
675        self.maxs += Vector::repeat(-amount);
676        assert!(
677            na::partial_le(&self.mins, &self.maxs),
678            "The tightening margin is to large."
679        );
680    }
681
682    #[inline]
683    fn tightened(&self, amount: Real) -> Aabb {
684        assert!(amount >= 0.0, "The tightening margin must be positive.");
685
686        Aabb::new(
687            self.mins + Vector::repeat(amount),
688            self.maxs + Vector::repeat(-amount),
689        )
690    }
691}