ncollide3d/bounding_volume/
bounding_sphere.rs
1use crate::bounding_volume::{BoundingVolume, HasBoundingVolume};
4use crate::math::{Isometry, Point};
5use na::{self, RealField};
6
7pub 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
19pub 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#[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 pub fn new(center: Point<N>, radius: N) -> BoundingSphere<N> {
42 BoundingSphere { center, radius }
43 }
44
45 #[inline]
47 pub fn center(&self) -> &Point<N> {
48 &self.center
49 }
50
51 #[inline]
53 pub fn radius(&self) -> N {
54 self.radius
55 }
56
57 #[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 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}