1#![warn(missing_docs)]
49
50use approx::{AbsDiffEq, RelativeEq};
51use nalgebra as na;
52use num_traits::Float;
53use std::fmt::Debug;
54
55#[derive(Clone, Debug, PartialEq)]
57pub struct BoundingBox<S: 'static + Debug + Copy + PartialEq> {
58 pub min: na::Point3<S>,
60 pub max: na::Point3<S>,
62}
63
64fn point_min<S: 'static + Float + Debug>(p: &[na::Point3<S>]) -> na::Point3<S> {
65 p.iter().fold(
66 na::Point3::<S>::new(S::infinity(), S::infinity(), S::infinity()),
67 |mut min, current| {
68 min.x = min.x.min(current.x);
69 min.y = min.y.min(current.y);
70 min.z = min.z.min(current.z);
71 min
72 },
73 )
74}
75fn point_max<S: 'static + Float + Debug>(p: &[na::Point3<S>]) -> na::Point3<S> {
76 p.iter().fold(
77 na::Point3::<S>::new(S::neg_infinity(), S::neg_infinity(), S::neg_infinity()),
78 |mut max, current| {
79 max.x = max.x.max(current.x);
80 max.y = max.y.max(current.y);
81 max.z = max.z.max(current.z);
82 max
83 },
84 )
85}
86
87impl<S: Float + Debug + na::RealField + simba::scalar::RealField> BoundingBox<S> {
88 pub fn infinity() -> BoundingBox<S> {
90 BoundingBox {
91 min: na::Point3::<S>::new(S::neg_infinity(), S::neg_infinity(), S::neg_infinity()),
92 max: na::Point3::<S>::new(S::infinity(), S::infinity(), S::infinity()),
93 }
94 }
95 pub fn neg_infinity() -> BoundingBox<S> {
97 BoundingBox {
98 min: na::Point3::<S>::new(S::infinity(), S::infinity(), S::infinity()),
99 max: na::Point3::<S>::new(S::neg_infinity(), S::neg_infinity(), S::neg_infinity()),
100 }
101 }
102 pub fn new(a: &na::Point3<S>, b: &na::Point3<S>) -> BoundingBox<S> {
104 BoundingBox {
105 min: na::Point3::<S>::new(
106 Float::min(a.x, b.x),
107 Float::min(a.y, b.y),
108 Float::min(a.z, b.z),
109 ),
110 max: na::Point3::<S>::new(
111 Float::max(a.x, b.x),
112 Float::max(a.y, b.y),
113 Float::max(a.z, b.z),
114 ),
115 }
116 }
117 pub fn union(&self, other: &BoundingBox<S>) -> BoundingBox<S> {
119 BoundingBox {
120 min: point_min(&[self.min, other.min]),
121 max: point_max(&[self.max, other.max]),
122 }
123 }
124 pub fn intersection(&self, other: &BoundingBox<S>) -> BoundingBox<S> {
126 BoundingBox {
127 min: point_max(&[self.min, other.min]),
128 max: point_min(&[self.max, other.max]),
129 }
130 }
131 pub fn transform(&self, mat: &na::Matrix4<S>) -> BoundingBox<S> {
133 let a = &self.min;
134 let b = &self.max;
135 let corners = [
136 mat.transform_point(&na::Point3::<S>::new(a.x, a.y, a.z)),
137 mat.transform_point(&na::Point3::<S>::new(a.x, a.y, b.z)),
138 mat.transform_point(&na::Point3::<S>::new(a.x, b.y, a.z)),
139 mat.transform_point(&na::Point3::<S>::new(a.x, b.y, b.z)),
140 mat.transform_point(&na::Point3::<S>::new(b.x, a.y, a.z)),
141 mat.transform_point(&na::Point3::<S>::new(b.x, a.y, b.z)),
142 mat.transform_point(&na::Point3::<S>::new(b.x, b.y, a.z)),
143 mat.transform_point(&na::Point3::<S>::new(b.x, b.y, b.z)),
144 ];
145 BoundingBox {
146 min: point_min(&corners),
147 max: point_max(&corners),
148 }
149 }
150 pub fn dilate(&mut self, d: S) -> &mut Self {
152 self.min.x -= d;
153 self.min.y -= d;
154 self.min.z -= d;
155 self.max.x += d;
156 self.max.y += d;
157 self.max.z += d;
158 self
159 }
160 pub fn insert(&mut self, o: &na::Point3<S>) -> &mut Self {
162 self.min.x = Float::min(self.min.x, o.x);
163 self.min.y = Float::min(self.min.y, o.y);
164 self.min.z = Float::min(self.min.z, o.z);
165 self.max.x = Float::max(self.max.x, o.x);
166 self.max.y = Float::max(self.max.y, o.y);
167 self.max.z = Float::max(self.max.z, o.z);
168 self
169 }
170 pub fn dim(&self) -> na::Vector3<S> {
172 self.max - self.min
173 }
174 pub fn distance(&self, p: &na::Point3<S>) -> S {
177 let xval = Float::max(p.x - self.max.x, self.min.x - p.x);
181 let yval = Float::max(p.y - self.max.y, self.min.y - p.y);
182 let zval = Float::max(p.z - self.max.z, self.min.z - p.z);
183 Float::max(xval, Float::max(yval, zval))
184 }
185 pub fn contains(&self, p: &na::Point3<S>) -> bool {
187 p.x >= self.min.x
188 && p.x <= self.max.x
189 && p.y >= self.min.y
190 && p.y <= self.max.y
191 && p.z >= self.min.z
192 && p.z <= self.max.z
193 }
194}
195
196impl<T: Float> AbsDiffEq for BoundingBox<T>
197where
198 <T as AbsDiffEq>::Epsilon: Copy,
199 T: AbsDiffEq + Debug,
200{
201 type Epsilon = <T as AbsDiffEq>::Epsilon;
202
203 fn default_epsilon() -> Self::Epsilon {
204 <T as AbsDiffEq>::default_epsilon()
205 }
206
207 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
208 na::Point3::abs_diff_eq(&self.min, &other.min, epsilon)
209 && na::Point3::abs_diff_eq(&self.max, &other.max, epsilon)
210 }
211}
212
213impl<T: Float> RelativeEq for BoundingBox<T>
214where
215 <T as AbsDiffEq>::Epsilon: Copy,
216 T: RelativeEq + Debug,
217{
218 fn default_max_relative() -> <T as AbsDiffEq>::Epsilon {
219 <T as RelativeEq>::default_max_relative()
220 }
221
222 fn relative_eq(
223 &self,
224 other: &Self,
225 epsilon: <T as AbsDiffEq>::Epsilon,
226 max_relative: <T as AbsDiffEq>::Epsilon,
227 ) -> bool {
228 na::Point3::relative_eq(&self.min, &other.min, epsilon, max_relative)
229 && na::Point3::relative_eq(&self.max, &other.max, epsilon, max_relative)
230 }
231}
232
233#[cfg(test)]
234mod test {
235 use super::*;
236 use approx::assert_relative_eq;
237
238 #[test]
239 fn box_contains_points_inside() {
240 let bbox =
241 BoundingBox::<f64>::new(&na::Point3::new(0., 0., 0.), &na::Point3::new(1., 2., 3.));
242 assert!(bbox.contains(&na::Point3::new(0., 0., 0.)));
243 assert!(bbox.contains(&na::Point3::new(0., 1., 0.)));
244 assert!(bbox.contains(&na::Point3::new(1., 0., 1.)));
245 assert!(bbox.contains(&na::Point3::new(1., 1., 1.)));
246 assert!(!bbox.contains(&na::Point3::new(2., 2., 2.)));
247 assert!(!bbox.contains(&na::Point3::new(-1., -1., -1.)));
248 }
249
250 #[test]
251 fn transform_with_translation() {
252 let bbox =
253 BoundingBox::<f64>::new(&na::Point3::new(0., 0., 0.), &na::Point3::new(1., 1., 1.));
254 assert_relative_eq!(
255 bbox.transform(&na::Translation3::new(1., 2., 3.).to_homogeneous()),
256 BoundingBox::<f64>::new(&na::Point3::new(1., 2., 3.), &na::Point3::new(2., 3., 4.),)
257 );
258 }
259
260 #[test]
261 fn transform_with_rotation() {
262 let bbox =
263 BoundingBox::<f64>::new(&na::Point3::new(0., 0., 0.), &na::Point3::new(1., 1., 1.));
264 assert_relative_eq!(
265 bbox.transform(
266 &na::Rotation::from_euler_angles(::std::f64::consts::PI / 2., 0., 0.)
267 .to_homogeneous()
268 ),
269 BoundingBox::<f64>::new(&na::Point3::new(0., -1., 0.), &na::Point3::new(1., 0., 1.),)
270 );
271 }
272
273 #[test]
274 fn union_of_two_boxes() {
275 let bbox1 =
276 BoundingBox::<f64>::new(&na::Point3::new(0., 0., 0.), &na::Point3::new(4., 8., 16.));
277 let bbox2 =
278 BoundingBox::<f64>::new(&na::Point3::new(2., 2., 2.), &na::Point3::new(16., 4., 8.));
279 assert_relative_eq!(
280 bbox1.union(&bbox2),
281 BoundingBox::<f64>::new(&na::Point3::new(0., 0., 0.), &na::Point3::new(16., 8., 16.),)
282 );
283 }
284
285 #[test]
286 fn intersection_of_two_boxes() {
287 let bbox1 =
288 BoundingBox::<f64>::new(&na::Point3::new(0., 0., 0.), &na::Point3::new(4., 8., 16.));
289 let bbox2 =
290 BoundingBox::<f64>::new(&na::Point3::new(2., 2., 2.), &na::Point3::new(16., 4., 8.));
291 assert_relative_eq!(
292 bbox1.intersection(&bbox2),
293 BoundingBox::<f64>::new(&na::Point3::new(2., 2., 2.), &na::Point3::new(4., 4., 8.),)
294 );
295 }
296
297 #[test]
298 fn dilate() {
299 let mut bbox =
300 BoundingBox::<f64>::new(&na::Point3::new(0., 0., 0.), &na::Point3::new(1., 1., 1.));
301 assert_relative_eq!(
302 bbox.dilate(0.1),
303 &mut BoundingBox::<f64>::new(
304 &na::Point3::new(-0.1, -0.1, -0.1),
305 &na::Point3::new(1.1, 1.1, 1.1),
306 )
307 );
308 assert_relative_eq!(
309 bbox.dilate(-0.5),
310 &mut BoundingBox::<f64>::new(
311 &na::Point3::new(0.4, 0.4, 0.4),
312 &na::Point3::new(0.6, 0.6, 0.6),
313 )
314 );
315 }
316
317 #[test]
318 fn box_contains_inserted_points() {
319 let mut bbox = BoundingBox::neg_infinity();
320 let p1 = na::Point3::new(1., 0., 0.);
321 let p2 = na::Point3::new(0., 2., 3.);
322 assert!(!bbox.contains(&p1));
323 bbox.insert(&p1);
324 assert!(bbox.contains(&p1));
325 assert!(!bbox.contains(&p2));
326 bbox.insert(&p2);
327 assert!(bbox.contains(&p2));
328 }
329}