parry3d/bounding_volume/
bounding_sphere.rs1use 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#[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 pub fn new(center: Point<Real>, radius: Real) -> BoundingSphere {
29 BoundingSphere { center, radius }
30 }
31
32 #[inline]
34 pub fn center(&self) -> &Point<Real> {
35 &self.center
36 }
37
38 #[inline]
40 pub fn radius(&self) -> Real {
41 self.radius
42 }
43
44 #[inline]
46 pub fn transform_by(&self, m: &Isometry<Real>) -> BoundingSphere {
47 BoundingSphere::new(m * self.center, self.radius)
48 }
49
50 #[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 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}