ncollide3d/bounding_volume/
bounding_sphere.rs

1//! Bounding sphere.
2
3use crate::bounding_volume::{BoundingVolume, HasBoundingVolume};
4use crate::math::{Isometry, Point};
5use na::{self, RealField};
6
7// Seems useful to help type inference. See issue #84.
8/// Computes the bounding sphere of a shape `g` transformed by `m`.
9///
10/// Same as `g.bounding_sphere(m)`.
11pub fn bounding_sphere<N, G: ?Sized>(g: &G, m: &Isometry<N>) -> BoundingSphere<N>
12where
13    N: RealField + Copy,
14    G: HasBoundingVolume<N, BoundingSphere<N>>,
15{
16    g.bounding_volume(m)
17}
18
19// Seems useful to help type inference. See issue #84.
20/// Computes the bounding sphere of a shape `g`.
21///
22/// Same as `g.local_bounding_sphere(m)`.
23pub fn local_bounding_sphere<N, G: ?Sized>(g: &G) -> BoundingSphere<N>
24where
25    N: RealField + Copy,
26    G: HasBoundingVolume<N, BoundingSphere<N>>,
27{
28    g.local_bounding_volume()
29}
30
31/// A Bounding Sphere.
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33#[derive(Debug, PartialEq, Copy, Clone)]
34pub struct BoundingSphere<N: RealField + Copy> {
35    center: Point<N>,
36    radius: N,
37}
38
39impl<N: RealField + Copy> BoundingSphere<N> {
40    /// Creates a new bounding sphere.
41    pub fn new(center: Point<N>, radius: N) -> BoundingSphere<N> {
42        BoundingSphere { center, radius }
43    }
44
45    /// The bounding sphere center.
46    #[inline]
47    pub fn center(&self) -> &Point<N> {
48        &self.center
49    }
50
51    /// The bounding sphere radius.
52    #[inline]
53    pub fn radius(&self) -> N {
54        self.radius
55    }
56
57    /// Transforms this bounding sphere by `m`.
58    #[inline]
59    pub fn transform_by(&self, m: &Isometry<N>) -> BoundingSphere<N> {
60        BoundingSphere::new(m * self.center, self.radius)
61    }
62}
63
64impl<N: RealField + Copy> BoundingVolume<N> for BoundingSphere<N> {
65    #[inline]
66    fn center(&self) -> Point<N> {
67        *self.center()
68    }
69
70    #[inline]
71    fn intersects(&self, other: &BoundingSphere<N>) -> bool {
72        // FIXME: refactor that with the code from narrow_phase::ball_ball::collide(...) ?
73        let delta_pos = other.center - self.center;
74        let distance_squared = delta_pos.norm_squared();
75        let sum_radius = self.radius + other.radius;
76
77        distance_squared <= sum_radius * sum_radius
78    }
79
80    #[inline]
81    fn contains(&self, other: &BoundingSphere<N>) -> bool {
82        let delta_pos = other.center - self.center;
83        let distance = delta_pos.norm();
84
85        distance + other.radius <= self.radius
86    }
87
88    #[inline]
89    fn merge(&mut self, other: &BoundingSphere<N>) {
90        let mut dir = *other.center() - *self.center();
91        let norm = dir.normalize_mut();
92
93        if norm.is_zero() {
94            if other.radius > self.radius {
95                self.radius = other.radius
96            }
97        } else {
98            let s_center_dir = self.center.coords.dot(&dir);
99            let o_center_dir = other.center.coords.dot(&dir);
100
101            let right;
102            let left;
103
104            if s_center_dir + self.radius > o_center_dir + other.radius {
105                right = self.center + dir * self.radius;
106            } else {
107                right = other.center + dir * other.radius;
108            }
109
110            if -s_center_dir + self.radius > -o_center_dir + other.radius {
111                left = self.center - dir * self.radius;
112            } else {
113                left = other.center - dir * other.radius;
114            }
115
116            self.center = na::center(&left, &right);
117            self.radius = na::distance(&right, &self.center);
118        }
119    }
120
121    #[inline]
122    fn merged(&self, other: &BoundingSphere<N>) -> BoundingSphere<N> {
123        let mut res = self.clone();
124
125        res.merge(other);
126
127        res
128    }
129
130    #[inline]
131    fn loosen(&mut self, amount: N) {
132        assert!(
133            amount >= na::zero(),
134            "The loosening margin must be positive."
135        );
136        self.radius = self.radius + amount
137    }
138
139    #[inline]
140    fn loosened(&self, amount: N) -> BoundingSphere<N> {
141        assert!(
142            amount >= na::zero(),
143            "The loosening margin must be positive."
144        );
145        BoundingSphere::new(self.center, self.radius + amount)
146    }
147
148    #[inline]
149    fn tighten(&mut self, amount: N) {
150        assert!(
151            amount >= na::zero(),
152            "The tightening margin must be positive."
153        );
154        assert!(amount <= self.radius, "The tightening margin is to large.");
155        self.radius = self.radius - amount
156    }
157
158    #[inline]
159    fn tightened(&self, amount: N) -> BoundingSphere<N> {
160        assert!(
161            amount >= na::zero(),
162            "The tightening margin must be positive."
163        );
164        assert!(amount <= self.radius, "The tightening margin is to large.");
165        BoundingSphere::new(self.center, self.radius - amount)
166    }
167}