parry3d/bounding_volume/
bounding_sphere.rs

1//! Bounding sphere.
2
3use crate::bounding_volume::BoundingVolume;
4use crate::math::{Isometry, Point, Real, Vector};
5use na;
6use num::Zero;
7
8#[cfg(feature = "rkyv")]
9use rkyv::{bytecheck, CheckBytes};
10
11/// A Bounding Sphere.
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
14#[cfg_attr(
15    feature = "rkyv",
16    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
17    archive(as = "Self")
18)]
19#[derive(Debug, PartialEq, Copy, Clone)]
20#[repr(C)]
21pub struct BoundingSphere {
22    pub center: Point<Real>,
23    pub radius: Real,
24}
25
26impl BoundingSphere {
27    /// Creates a new bounding sphere.
28    pub fn new(center: Point<Real>, radius: Real) -> BoundingSphere {
29        BoundingSphere { center, radius }
30    }
31
32    /// The bounding sphere center.
33    #[inline]
34    pub fn center(&self) -> &Point<Real> {
35        &self.center
36    }
37
38    /// The bounding sphere radius.
39    #[inline]
40    pub fn radius(&self) -> Real {
41        self.radius
42    }
43
44    /// Transforms this bounding sphere by `m`.
45    #[inline]
46    pub fn transform_by(&self, m: &Isometry<Real>) -> BoundingSphere {
47        BoundingSphere::new(m * self.center, self.radius)
48    }
49
50    /// Translates this bounding sphere by the given translation.
51    #[inline]
52    pub fn translated(&self, translation: &Vector<Real>) -> BoundingSphere {
53        BoundingSphere::new(self.center + translation, self.radius)
54    }
55}
56
57impl BoundingVolume for BoundingSphere {
58    #[inline]
59    fn center(&self) -> Point<Real> {
60        *self.center()
61    }
62
63    #[inline]
64    fn intersects(&self, other: &BoundingSphere) -> bool {
65        // TODO: refactor that with the code from narrow_phase::ball_ball::collide(...) ?
66        let delta_pos = other.center - self.center;
67        let distance_squared = delta_pos.norm_squared();
68        let sum_radius = self.radius + other.radius;
69
70        distance_squared <= sum_radius * sum_radius
71    }
72
73    #[inline]
74    fn contains(&self, other: &BoundingSphere) -> bool {
75        let delta_pos = other.center - self.center;
76        let distance = delta_pos.norm();
77
78        distance + other.radius <= self.radius
79    }
80
81    #[inline]
82    fn merge(&mut self, other: &BoundingSphere) {
83        let mut dir = *other.center() - *self.center();
84        let norm = dir.normalize_mut();
85
86        if norm.is_zero() {
87            if other.radius > self.radius {
88                self.radius = other.radius
89            }
90        } else {
91            let s_center_dir = self.center.coords.dot(&dir);
92            let o_center_dir = other.center.coords.dot(&dir);
93
94            let right = if s_center_dir + self.radius > o_center_dir + other.radius {
95                self.center + dir * self.radius
96            } else {
97                other.center + dir * other.radius
98            };
99
100            let left = if -s_center_dir + self.radius > -o_center_dir + other.radius {
101                self.center - dir * self.radius
102            } else {
103                other.center - dir * other.radius
104            };
105
106            self.center = na::center(&left, &right);
107            self.radius = na::distance(&right, &self.center);
108        }
109    }
110
111    #[inline]
112    fn merged(&self, other: &BoundingSphere) -> BoundingSphere {
113        let mut res = *self;
114
115        res.merge(other);
116
117        res
118    }
119
120    #[inline]
121    fn loosen(&mut self, amount: Real) {
122        assert!(amount >= 0.0, "The loosening margin must be positive.");
123        self.radius += amount
124    }
125
126    #[inline]
127    fn loosened(&self, amount: Real) -> BoundingSphere {
128        assert!(amount >= 0.0, "The loosening margin must be positive.");
129        BoundingSphere::new(self.center, self.radius + amount)
130    }
131
132    #[inline]
133    fn tighten(&mut self, amount: Real) {
134        assert!(amount >= 0.0, "The tightening margin must be positive.");
135        assert!(amount <= self.radius, "The tightening margin is to large.");
136        self.radius -= amount
137    }
138
139    #[inline]
140    fn tightened(&self, amount: Real) -> BoundingSphere {
141        assert!(amount >= 0.0, "The tightening margin must be positive.");
142        assert!(amount <= self.radius, "The tightening margin is to large.");
143        BoundingSphere::new(self.center, self.radius - amount)
144    }
145}