collision/volume/
sphere.rs1use cgmath::{BaseFloat, Point3, Vector3};
4use cgmath::prelude::*;
5
6use crate::{Aabb3, Line3, Plane, Ray3};
7use crate::prelude::*;
8
9#[derive(Copy, Clone, PartialEq, Debug)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct Sphere<S: BaseFloat> {
13 pub center: Point3<S>,
15 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 #[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}