1use crate::*;
26
27#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub struct BoundingBox3D {
32 min: Point3D,
33 max: Point3D,
34}
35
36impl BoundingBox3D {
37 pub fn new<P1, P2>(min: &P1, max: &P2) -> Result<BoundingBox3D>
39 where
40 P1: Is3D,
41 P2: Is3D,
42 {
43 if min.x() == max.x() || min.y() == max.y() || min.z() == max.z() {
44 Err(ErrorKind::MinMaxEqual)
45 } else if min.x() > max.x() || min.y() > max.y() || min.z() > max.z() {
46 Err(ErrorKind::MinMaxSwapped)
47 } else {
48 Ok(BoundingBox3D {
49 min: Point3D {
50 x: min.x(),
51 y: min.y(),
52 z: min.z(),
53 },
54 max: Point3D {
55 x: max.x(),
56 y: max.y(),
57 z: max.z(),
58 },
59 })
60 }
61 }
62 pub fn from_iterator<'a, It3D, P>(source: It3D) -> Result<BoundingBox3D>
64 where
65 It3D: IntoIterator<Item = &'a P>,
66 P: 'a + Is3D + Sized,
67 {
68 let mut count = 0;
69
70 let mut minx: f64 = 0.0;
71 let mut miny: f64 = 0.0;
72 let mut minz: f64 = 0.0;
73 let mut maxx: f64 = 0.0;
74 let mut maxy: f64 = 0.0;
75 let mut maxz: f64 = 0.0;
76
77 for p in source {
78 if count == 0 {
79 minx = p.x();
80 miny = p.y();
81 minz = p.z();
82 maxx = p.x();
83 maxy = p.y();
84 maxz = p.z();
85 count += 1;
86 continue;
87 }
88 if p.x() < minx {
89 minx = p.x();
90 }
91 if p.y() < miny {
92 miny = p.y();
93 }
94 if p.z() < minz {
95 minz = p.z();
96 }
97 if p.x() > maxx {
98 maxx = p.x();
99 }
100 if p.y() > maxy {
101 maxy = p.y();
102 }
103 if p.z() > maxz {
104 maxz = p.z();
105 }
106 count += 1;
107 }
108 if count >= 2 {
109 Self::new(
110 &Point3D {
111 x: minx,
112 y: miny,
113 z: minz,
114 },
115 &Point3D {
116 x: maxx,
117 y: maxy,
118 z: maxz,
119 },
120 )
121 } else {
122 Err(ErrorKind::TooFewPoints)
123 }
124 }
125 pub fn from_into_iterator<It3D, P>(source: It3D) -> Result<BoundingBox3D>
127 where
128 It3D: IntoIterator<Item = P>,
129 P: Is3D + Sized,
130 {
131 let mut count = 0;
132
133 let mut minx: f64 = 0.0;
134 let mut miny: f64 = 0.0;
135 let mut minz: f64 = 0.0;
136 let mut maxx: f64 = 0.0;
137 let mut maxy: f64 = 0.0;
138 let mut maxz: f64 = 0.0;
139
140 for p in source {
141 if count == 0 {
142 minx = p.x();
143 miny = p.y();
144 minz = p.z();
145 maxx = p.x();
146 maxy = p.y();
147 maxz = p.z();
148 count += 1;
149 continue;
150 }
151 if p.x() < minx {
152 minx = p.x();
153 }
154 if p.y() < miny {
155 miny = p.y();
156 }
157 if p.z() < minz {
158 minz = p.z();
159 }
160 if p.x() > maxx {
161 maxx = p.x();
162 }
163 if p.y() > maxy {
164 maxy = p.y();
165 }
166 if p.z() > maxz {
167 maxz = p.z();
168 }
169 count += 1;
170 }
171 if count >= 2 {
172 Self::new(
173 &Point3D {
174 x: minx,
175 y: miny,
176 z: minz,
177 },
178 &Point3D {
179 x: maxx,
180 y: maxy,
181 z: maxz,
182 },
183 )
184 } else {
185 Err(ErrorKind::TooFewPoints)
186 }
187 }
188 pub fn min_p(&self) -> Point3D {
190 self.min.clone()
191 }
192 pub fn max_p(&self) -> Point3D {
194 self.max.clone()
195 }
196 pub fn size_x(&self) -> Positive {
198 Positive::new((self.max.x() - self.min.x()).abs()).unwrap() }
200 pub fn size_y(&self) -> Positive {
202 Positive::new((self.max.y() - self.min.y()).abs()).unwrap() }
204 pub fn size_z(&self) -> Positive {
206 Positive::new((self.max.z() - self.min.z()).abs()).unwrap() }
208 pub fn sizes(&self) -> [Positive; 3] {
210 [self.size_x(), self.size_y(), self.size_z()]
211 }
212 pub fn center_bb(&self) -> Point3D {
214 Point3D {
215 x: self.min.x() + (self.max.x() - self.min.x()) / 2.0,
216 y: self.min.y() + (self.max.y() - self.min.y()) / 2.0,
217 z: self.min.z() + (self.max.z() - self.min.z()) / 2.0,
218 }
219 }
220 pub fn is_inside(&self, other: &BoundingBox3D) -> bool {
222 self.min.x() > other.min.x()
223 && self.min.y() > other.min.y()
224 && self.min.z() > other.min.z()
225 && self.max.x() < other.max.x()
226 && self.max.y() < other.max.y()
227 && self.max.z() < other.max.z()
228 }
229 pub fn contains<P>(&self, other: &P) -> bool
231 where
232 Self: Sized,
233 P: Is3D,
234 {
235 other.x() > self.min.x()
236 && other.x() < self.max.x()
237 && other.y() > self.min.y()
238 && other.y() < self.max.y()
239 && other.z() > self.min.z()
240 && other.z() < self.max.z()
241 }
242 pub fn has_inside(&self, other: &BoundingBox3D) -> bool {
244 self.min.x() < other.min.x()
245 && self.min.y() < other.min.y()
246 && self.min.z() < other.min.z()
247 && self.max.x() > other.max.x()
248 && self.max.y() > other.max.y()
249 && self.max.z() > other.max.z()
250 }
251 pub fn collides_with(&self, other: &BoundingBox3D) -> bool {
253 2.0 * (self.center_bb().x - other.center_bb().x).abs()
254 < ((self.size_x() + other.size_x()).get())
255 && 2.0 * (self.center_bb().y - other.center_bb().y).abs()
256 < ((self.size_y() + other.size_y()).get())
257 && 2.0 * (self.center_bb().z - other.center_bb().z).abs()
258 < ((self.size_z() + other.size_z()).get())
259 }
260 pub fn crossing_x_value(&self, x: f64) -> bool {
262 self.min.x() < x && self.max.x() > x
263 }
264 pub fn crossing_y_value(&self, y: f64) -> bool {
266 self.min.y() < y && self.max.y() > y
267 }
268 pub fn crossing_z_value(&self, z: f64) -> bool {
270 self.min.z() < z && self.max.z() > z
271 }
272 pub fn corners(&self) -> [Point3D; 8] {
274 [
275 Point3D::new(self.min.x(), self.min.y(), self.min.z()),
276 Point3D::new(self.min.x(), self.min.y(), self.max.z()),
277 Point3D::new(self.min.x(), self.max.y(), self.min.z()),
278 Point3D::new(self.min.x(), self.max.y(), self.max.z()),
279 Point3D::new(self.max.x(), self.min.y(), self.min.z()),
280 Point3D::new(self.max.x(), self.min.y(), self.max.z()),
281 Point3D::new(self.max.x(), self.max.y(), self.min.z()),
282 Point3D::new(self.max.x(), self.max.y(), self.max.z()),
283 ]
284 }
285 pub fn distance<P>(&self, other: &P) -> NonNegative
287 where
288 P: Is3D,
289 {
290 let sqr_dist = self.sqr_distance(other).get();
291 NonNegative::new(sqr_dist.sqrt()).unwrap()
292 }
293 pub fn sqr_distance<P>(&self, other: &P) -> NonNegative
295 where
296 P: Is3D,
297 {
298 let dx = max_f64_3(
299 self.min_p().x() - other.x(),
300 0.0,
301 other.x() - self.max_p().x(),
302 );
303 let dy = max_f64_3(
304 self.min_p().y() - other.y(),
305 0.0,
306 other.y() - self.max_p().y(),
307 );
308 let dz = max_f64_3(
309 self.min_p().z() - other.z(),
310 0.0,
311 other.z() - self.max_p().z(),
312 );
313 NonNegative::new(dx * dx + dy * dy + dz * dz).unwrap()
314 }
315}
316
317impl Default for BoundingBox3D {
320 fn default() -> Self {
321 BoundingBox3D {
322 min: Point3D {
323 x: -0.5,
324 y: -0.5,
325 z: -0.5,
326 },
327 max: Point3D {
328 x: 0.5,
329 y: 0.5,
330 z: 0.5,
331 },
332 }
333 }
334}
335
336impl IsND for BoundingBox3D {
337 fn n_dimensions() -> usize {
338 3
339 }
340
341 fn position_nd(&self, dimension: usize) -> Result<f64> {
342 self.center_bb().position_nd(dimension)
343 }
344}
345
346impl Is3D for BoundingBox3D {
347 #[inline(always)]
348 fn x(&self) -> f64 {
349 self.center_bb().x()
350 }
351
352 #[inline(always)]
353 fn y(&self) -> f64 {
354 self.center_bb().y()
355 }
356
357 #[inline(always)]
358 fn z(&self) -> f64 {
359 self.center_bb().z()
360 }
361}
362
363impl IsMovable3D for BoundingBox3D {
364 fn move_by(&mut self, x: f64, y: f64, z: f64) {
365 self.min.move_by(x, y, z);
366 self.max.move_by(x, y, z);
367 }
368}
369
370impl HasBoundingBox3D for BoundingBox3D {
371 fn bounding_box(&self) -> BoundingBox3D {
372 BoundingBox3D::new(&self.min, &self.max).unwrap() }
374}
375
376impl HasBoundingBox3DMaybe for BoundingBox3D {
377 fn bounding_box_maybe(&self) -> Result<BoundingBox3D> {
378 Ok(self.bounding_box())
379 }
380}
381
382impl HasDistanceTo<BoundingBox3D> for BoundingBox3D {
383 fn sqr_distance(&self, other: &BoundingBox3D) -> NonNegative {
384 let mut dx = 0.0;
385 let mut dy = 0.0;
386 let mut dz = 0.0;
387
388 if other.max_p().x() < self.min_p().x() {
389 dx = other.max_p().x() - self.min_p().x();
390 } else if other.min_p().x() > self.max_p().x() {
391 dx = other.min_p().x() - self.max_p().x();
392 }
393
394 if other.max_p().y() < self.min_p().y() {
395 dy = other.max_p().y() - self.min_p().y();
396 } else if other.min_p().y() > self.max_p().y() {
397 dy = other.min_p().y() - self.max_p().y();
398 }
399
400 if other.max_p().z() < self.min_p().z() {
401 dz = other.max_p().z() - self.min_p().z();
402 } else if other.min_p().z() > self.max_p().z() {
403 dz = other.min_p().z() - self.max_p().z();
404 }
405
406 NonNegative::new(dx * dx + dy * dy + dz * dz).unwrap()
407 }
408}
409
410impl IsScalable for BoundingBox3D {
411 fn scale(&mut self, factor: Positive) {
412 let c = self.center_bb();
413 let min_x = c.x - (0.5 * factor.get() * self.size_x().get());
414 let max_x = c.x + (0.5 * factor.get() * self.size_x().get());
415 let min_y = c.y - (0.5 * factor.get() * self.size_y().get());
416 let max_y = c.y + (0.5 * factor.get() * self.size_y().get());
417 let min_z = c.z - (0.5 * factor.get() * self.size_z().get());
418 let max_z = c.z + (0.5 * factor.get() * self.size_z().get());
419
420 self.min.set_xyz(min_x, min_y, min_z);
421 self.max.set_xyz(max_x, max_y, max_z);
422 }
423}
424
425impl IsMergeable for BoundingBox3D {
426 fn consume(&mut self, other: Self) {
427 let (mut min_x, mut min_y, mut min_z) = (self.min.x(), self.min.y(), self.min.z());
428 let (mut max_x, mut max_y, mut max_z) = (self.max.x(), self.max.y(), self.max.z());
429
430 if other.min.x() < min_x {
431 min_x = other.min.x()
432 }
433 if other.min.y() < min_y {
434 min_y = other.min.y()
435 }
436 if other.min.z() < min_z {
437 min_z = other.min.z()
438 }
439
440 if other.max.x() > max_x {
441 max_x = other.max.x()
442 }
443 if other.max.y() > max_y {
444 max_y = other.max.y()
445 }
446 if other.max.z() > max_z {
447 max_z = other.max.z()
448 }
449
450 self.min.set_xyz(min_x, min_y, min_z);
451 self.max.set_xyz(max_x, max_y, max_z);
452 }
453
454 fn combine(&self, other: &Self) -> Self {
455 let (mut min_x, mut min_y, mut min_z) = (self.min.x(), self.min.y(), self.min.z());
456 let (mut max_x, mut max_y, mut max_z) = (self.max.x(), self.max.y(), self.max.z());
457
458 if other.min.x() < min_x {
459 min_x = other.min.x()
460 }
461 if other.min.y() < min_y {
462 min_y = other.min.y()
463 }
464 if other.min.z() < min_z {
465 min_z = other.min.z()
466 }
467
468 if other.max.x() > max_x {
469 max_x = other.max.x()
470 }
471 if other.max.y() > max_y {
472 max_y = other.max.y()
473 }
474 if other.max.z() > max_z {
475 max_z = other.max.z()
476 }
477
478 let min = Point3D::new(min_x, min_y, min_z);
479 let max = Point3D::new(max_x, max_y, max_z);
480
481 BoundingBox3D { min, max }
482 }
483}
484
485impl IsSATObject for BoundingBox3D {
486 fn for_each_point<F>(&self, f: &mut F)
487 where
488 F: FnMut(&Point3D),
489 {
490 let (minx, miny, minz) = (self.min_p().x(), self.min_p().y(), self.min_p().z());
491 let (maxx, maxy, maxz) = (self.max_p().x(), self.max_p().y(), self.max_p().z());
492
493 f(&Point3D::new(minx, miny, minz));
494 f(&Point3D::new(minx, miny, maxz));
495 f(&Point3D::new(minx, maxy, minz));
496 f(&Point3D::new(minx, maxy, maxz));
497 f(&Point3D::new(maxx, miny, minz));
498 f(&Point3D::new(maxx, miny, maxz));
499 f(&Point3D::new(maxx, maxy, minz));
500 f(&Point3D::new(maxx, maxy, maxz));
501 }
502
503 fn for_each_axis<F>(&self, f: &mut F)
504 where
505 F: FnMut(&Norm3D),
506 {
507 f(&Norm3D::norm_x());
508 f(&Norm3D::norm_y());
509 f(&Norm3D::norm_z());
510 }
511}