collision/volume/
sphere.rs

1//! Bounding sphere
2
3use cgmath::{BaseFloat, Point3, Vector3};
4use cgmath::prelude::*;
5
6use crate::{Aabb3, Line3, Plane, Ray3};
7use crate::prelude::*;
8
9/// Bounding sphere.
10#[derive(Copy, Clone, PartialEq, Debug)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct Sphere<S: BaseFloat> {
13    /// Center point of the sphere in world space
14    pub center: Point3<S>,
15    /// Sphere radius
16    pub radius: S,
17}
18
19impl<S> Bound for Sphere<S>
20where
21    S: BaseFloat,
22{
23    type Point = Point3<S>;
24
25    fn min_extent(&self) -> Point3<S> {
26        self.center + Vector3::from_value(-self.radius)
27    }
28
29    fn max_extent(&self) -> Point3<S> {
30        self.center + Vector3::from_value(self.radius)
31    }
32
33    fn with_margin(&self, add: Vector3<S>) -> Self {
34        let max = add.x.max(add.y).max(add.z);
35        Sphere {
36            center: self.center,
37            radius: self.radius + max,
38        }
39    }
40
41    fn transform_volume<T>(&self, transform: &T) -> Self
42    where
43        T: Transform<Self::Point>,
44    {
45        Sphere {
46            center: transform.transform_point(self.center),
47            radius: self.radius,
48        }
49    }
50
51    fn empty() -> Self {
52        Self {
53            center: Point3::origin(),
54            radius: S::zero(),
55        }
56    }
57}
58
59impl<S: BaseFloat> Continuous<Ray3<S>> for Sphere<S> {
60    type Result = Point3<S>;
61    fn intersection(&self, r: &Ray3<S>) -> Option<Point3<S>> {
62        let s = self;
63
64        let l = s.center - r.origin;
65        let tca = l.dot(r.direction);
66        if tca < S::zero() {
67            return None;
68        }
69        let d2 = l.dot(l) - tca * tca;
70        if d2 > s.radius * s.radius {
71            return None;
72        }
73        let thc = (s.radius * s.radius - d2).sqrt();
74        Some(r.origin + r.direction * (tca - thc))
75    }
76}
77
78impl<S: BaseFloat> Discrete<Ray3<S>> for Sphere<S> {
79    fn intersects(&self, r: &Ray3<S>) -> bool {
80        let s = self;
81        let l = s.center - r.origin;
82        let tca = l.dot(r.direction);
83        if tca < S::zero() {
84            return false;
85        }
86        let d2 = l.dot(l) - tca * tca;
87        d2 <= s.radius * s.radius
88    }
89}
90
91impl<S: BaseFloat> Discrete<Sphere<S>> for Sphere<S> {
92    fn intersects(&self, s2: &Sphere<S>) -> bool {
93        let s1 = self;
94
95        let distance = s1.center.distance2(s2.center);
96        let radiuses = s1.radius + s2.radius;
97
98        distance <= radiuses * radiuses
99    }
100}
101
102impl<S: BaseFloat> PlaneBound<S> for Sphere<S> {
103    fn relate_plane(&self, plane: Plane<S>) -> Relation {
104        let dist = self.center.dot(plane.n) - plane.d;
105        if dist > self.radius {
106            Relation::In
107        } else if dist < -self.radius {
108            Relation::Out
109        } else {
110            Relation::Cross
111        }
112    }
113}
114
115impl<S: BaseFloat> Contains<Aabb3<S>> for Sphere<S> {
116    // will return true for border hits
117    #[inline]
118    fn contains(&self, aabb: &Aabb3<S>) -> bool {
119        let radius_sq = self.radius * self.radius;
120        for c in &aabb.to_corners() {
121            if c.distance2(self.center) > radius_sq {
122                return false;
123            }
124        }
125        true
126    }
127}
128
129impl<S: BaseFloat> Contains<Point3<S>> for Sphere<S> {
130    #[inline]
131    fn contains(&self, p: &Point3<S>) -> bool {
132        self.center.distance2(*p) <= self.radius * self.radius
133    }
134}
135
136impl<S: BaseFloat> Contains<Line3<S>> for Sphere<S> {
137    #[inline]
138    fn contains(&self, line: &Line3<S>) -> bool {
139        self.contains(&line.origin) && self.contains(&line.dest)
140    }
141}
142
143impl<S: BaseFloat> Contains<Sphere<S>> for Sphere<S> {
144    #[inline]
145    fn contains(&self, other: &Sphere<S>) -> bool {
146        let center_dist = self.center.distance(other.center);
147        (center_dist + other.radius) <= self.radius
148    }
149}
150
151impl<S: BaseFloat> Union for Sphere<S> {
152    type Output = Sphere<S>;
153
154    fn union(&self, other: &Sphere<S>) -> Sphere<S> {
155        if self.contains(other) {
156            return *self;
157        }
158        if other.contains(self) {
159            return *other;
160        }
161        let two = S::one() + S::one();
162        let center_diff = other.center - self.center;
163        let center_diff_s = center_diff.magnitude();
164        let radius = (self.radius + other.radius + center_diff_s) / two;
165        Sphere {
166            radius,
167            center: self.center + center_diff * (radius - self.radius) / center_diff_s,
168        }
169    }
170}
171
172impl<S: BaseFloat> Union<Aabb3<S>> for Sphere<S> {
173    type Output = Sphere<S>;
174
175    fn union(&self, aabb: &Aabb3<S>) -> Sphere<S> {
176        if self.contains(aabb) {
177            return *self;
178        }
179        let aabb_radius = aabb.max().distance(aabb.center());
180        if aabb.contains(self) {
181            return Sphere {
182                center: aabb.center(),
183                radius: aabb_radius,
184            };
185        }
186        let two = S::one() + S::one();
187        let center_diff = aabb.center() - self.center;
188        let center_diff_s = aabb.center().distance(self.center);
189        let radius = (self.radius + aabb_radius + center_diff_s) / two;
190        Sphere {
191            center: self.center + center_diff * (radius - self.radius) / center_diff_s,
192            radius,
193        }
194    }
195}
196
197impl<S: BaseFloat> SurfaceArea for Sphere<S> {
198    type Scalar = S;
199
200    fn surface_area(&self) -> S {
201        use std::f64::consts::PI;
202
203        let two = S::one() + S::one();
204        let four = two + two;
205        let pi = S::from(PI).unwrap();
206        four * pi * self.radius * self.radius
207    }
208}