1use super::UnknownUnit;
11use crate::approxord::{max, min};
12use crate::num::*;
13use crate::point::{point3, Point3D};
14use crate::scale::Scale;
15use crate::size::Size3D;
16use crate::vector::Vector3D;
17
18#[cfg(feature = "bytemuck")]
19use bytemuck::{Pod, Zeroable};
20use num_traits::{Float, NumCast};
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23
24use core::borrow::Borrow;
25use core::cmp::PartialOrd;
26use core::fmt;
27use core::hash::{Hash, Hasher};
28use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Range, Sub};
29
30#[repr(C)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33#[cfg_attr(
34 feature = "serde",
35 serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
36)]
37pub struct Box3D<T, U> {
38 pub min: Point3D<T, U>,
39 pub max: Point3D<T, U>,
40}
41
42impl<T: Hash, U> Hash for Box3D<T, U> {
43 fn hash<H: Hasher>(&self, h: &mut H) {
44 self.min.hash(h);
45 self.max.hash(h);
46 }
47}
48
49impl<T: Copy, U> Copy for Box3D<T, U> {}
50
51impl<T: Clone, U> Clone for Box3D<T, U> {
52 fn clone(&self) -> Self {
53 Self::new(self.min.clone(), self.max.clone())
54 }
55}
56
57impl<T: PartialEq, U> PartialEq for Box3D<T, U> {
58 fn eq(&self, other: &Self) -> bool {
59 self.min.eq(&other.min) && self.max.eq(&other.max)
60 }
61}
62
63impl<T: Eq, U> Eq for Box3D<T, U> {}
64
65impl<T: fmt::Debug, U> fmt::Debug for Box3D<T, U> {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 f.debug_tuple("Box3D")
68 .field(&self.min)
69 .field(&self.max)
70 .finish()
71 }
72}
73
74#[cfg(feature = "arbitrary")]
75impl<'a, T, U> arbitrary::Arbitrary<'a> for Box3D<T, U>
76where
77 T: arbitrary::Arbitrary<'a>,
78{
79 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
80 Ok(Box3D::new(
81 arbitrary::Arbitrary::arbitrary(u)?,
82 arbitrary::Arbitrary::arbitrary(u)?,
83 ))
84 }
85}
86
87#[cfg(feature = "bytemuck")]
88unsafe impl<T: Zeroable, U> Zeroable for Box3D<T, U> {}
89
90#[cfg(feature = "bytemuck")]
91unsafe impl<T: Pod, U: 'static> Pod for Box3D<T, U> {}
92
93impl<T, U> Box3D<T, U> {
94 #[inline]
96 pub const fn new(min: Point3D<T, U>, max: Point3D<T, U>) -> Self {
97 Box3D { min, max }
98 }
99
100 #[inline]
102 pub fn from_origin_and_size(origin: Point3D<T, U>, size: Size3D<T, U>) -> Self
103 where
104 T: Copy + Add<T, Output = T>,
105 {
106 Box3D {
107 min: origin,
108 max: point3(
109 origin.x + size.width,
110 origin.y + size.height,
111 origin.z + size.depth,
112 ),
113 }
114 }
115
116 #[inline]
118 pub fn from_size(size: Size3D<T, U>) -> Self
119 where
120 T: Zero,
121 {
122 Box3D {
123 min: Point3D::zero(),
124 max: point3(size.width, size.height, size.depth),
125 }
126 }
127}
128
129impl<T, U> Box3D<T, U>
130where
131 T: PartialOrd,
132{
133 #[inline]
138 pub fn is_negative(&self) -> bool {
139 self.max.x < self.min.x || self.max.y < self.min.y || self.max.z < self.min.z
140 }
141
142 #[inline]
144 pub fn is_empty(&self) -> bool {
145 !(self.max.x > self.min.x && self.max.y > self.min.y && self.max.z > self.min.z)
146 }
147
148 #[inline]
149 pub fn intersects(&self, other: &Self) -> bool {
150 self.min.x < other.max.x
151 && self.max.x > other.min.x
152 && self.min.y < other.max.y
153 && self.max.y > other.min.y
154 && self.min.z < other.max.z
155 && self.max.z > other.min.z
156 }
157
158 #[inline]
162 pub fn contains(&self, other: Point3D<T, U>) -> bool {
163 (self.min.x <= other.x)
164 & (other.x < self.max.x)
165 & (self.min.y <= other.y)
166 & (other.y < self.max.y)
167 & (self.min.z <= other.z)
168 & (other.z < self.max.z)
169 }
170
171 #[inline]
174 pub fn contains_inclusive(&self, other: Point3D<T, U>) -> bool {
175 (self.min.x <= other.x)
176 & (other.x <= self.max.x)
177 & (self.min.y <= other.y)
178 & (other.y <= self.max.y)
179 & (self.min.z <= other.z)
180 & (other.z <= self.max.z)
181 }
182
183 #[inline]
187 pub fn contains_box(&self, other: &Self) -> bool {
188 other.is_empty()
189 || ((self.min.x <= other.min.x)
190 & (other.max.x <= self.max.x)
191 & (self.min.y <= other.min.y)
192 & (other.max.y <= self.max.y)
193 & (self.min.z <= other.min.z)
194 & (other.max.z <= self.max.z))
195 }
196}
197
198impl<T, U> Box3D<T, U>
199where
200 T: Copy + PartialOrd,
201{
202 #[inline]
203 pub fn to_non_empty(&self) -> Option<Self> {
204 if self.is_empty() {
205 return None;
206 }
207
208 Some(*self)
209 }
210
211 #[inline]
212 pub fn intersection(&self, other: &Self) -> Option<Self> {
213 let b = self.intersection_unchecked(other);
214
215 if b.is_empty() {
216 return None;
217 }
218
219 Some(b)
220 }
221
222 pub fn intersection_unchecked(&self, other: &Self) -> Self {
223 let intersection_min = Point3D::new(
224 max(self.min.x, other.min.x),
225 max(self.min.y, other.min.y),
226 max(self.min.z, other.min.z),
227 );
228
229 let intersection_max = Point3D::new(
230 min(self.max.x, other.max.x),
231 min(self.max.y, other.max.y),
232 min(self.max.z, other.max.z),
233 );
234
235 Box3D::new(intersection_min, intersection_max)
236 }
237
238 #[inline]
242 pub fn union(&self, other: &Self) -> Self {
243 if other.is_empty() {
244 return *self;
245 }
246 if self.is_empty() {
247 return *other;
248 }
249
250 Box3D::new(
251 Point3D::new(
252 min(self.min.x, other.min.x),
253 min(self.min.y, other.min.y),
254 min(self.min.z, other.min.z),
255 ),
256 Point3D::new(
257 max(self.max.x, other.max.x),
258 max(self.max.y, other.max.y),
259 max(self.max.z, other.max.z),
260 ),
261 )
262 }
263}
264
265impl<T, U> Box3D<T, U>
266where
267 T: Copy + Add<T, Output = T>,
268{
269 #[inline]
271 #[must_use]
272 pub fn translate(&self, by: Vector3D<T, U>) -> Self {
273 Box3D {
274 min: self.min + by,
275 max: self.max + by,
276 }
277 }
278}
279
280impl<T, U> Box3D<T, U>
281where
282 T: Copy + Sub<T, Output = T>,
283{
284 #[inline]
285 pub fn size(&self) -> Size3D<T, U> {
286 Size3D::new(
287 self.max.x - self.min.x,
288 self.max.y - self.min.y,
289 self.max.z - self.min.z,
290 )
291 }
292
293 #[inline]
294 pub fn width(&self) -> T {
295 self.max.x - self.min.x
296 }
297
298 #[inline]
299 pub fn height(&self) -> T {
300 self.max.y - self.min.y
301 }
302
303 #[inline]
304 pub fn depth(&self) -> T {
305 self.max.z - self.min.z
306 }
307}
308
309impl<T, U> Box3D<T, U>
310where
311 T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
312{
313 #[inline]
315 #[must_use]
316 pub fn inflate(&self, width: T, height: T, depth: T) -> Self {
317 Box3D::new(
318 Point3D::new(self.min.x - width, self.min.y - height, self.min.z - depth),
319 Point3D::new(self.max.x + width, self.max.y + height, self.max.z + depth),
320 )
321 }
322}
323
324impl<T, U> Box3D<T, U>
325where
326 T: Copy + Zero + PartialOrd,
327{
328 pub fn from_points<I>(points: I) -> Self
330 where
331 I: IntoIterator,
332 I::Item: Borrow<Point3D<T, U>>,
333 {
334 let mut points = points.into_iter();
335
336 let (mut min_x, mut min_y, mut min_z) = match points.next() {
337 Some(first) => first.borrow().to_tuple(),
338 None => return Box3D::zero(),
339 };
340 let (mut max_x, mut max_y, mut max_z) = (min_x, min_y, min_z);
341
342 for point in points {
343 let p = point.borrow();
344 if p.x < min_x {
345 min_x = p.x
346 }
347 if p.x > max_x {
348 max_x = p.x
349 }
350 if p.y < min_y {
351 min_y = p.y
352 }
353 if p.y > max_y {
354 max_y = p.y
355 }
356 if p.z < min_z {
357 min_z = p.z
358 }
359 if p.z > max_z {
360 max_z = p.z
361 }
362 }
363
364 Box3D {
365 min: point3(min_x, min_y, min_z),
366 max: point3(max_x, max_y, max_z),
367 }
368 }
369}
370
371impl<T, U> Box3D<T, U>
372where
373 T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
374{
375 #[inline]
377 pub fn lerp(&self, other: Self, t: T) -> Self {
378 Self::new(self.min.lerp(other.min, t), self.max.lerp(other.max, t))
379 }
380}
381
382impl<T, U> Box3D<T, U>
383where
384 T: Copy + One + Add<Output = T> + Div<Output = T>,
385{
386 pub fn center(&self) -> Point3D<T, U> {
387 let two = T::one() + T::one();
388 (self.min + self.max.to_vector()) / two
389 }
390}
391
392impl<T, U> Box3D<T, U>
393where
394 T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
395{
396 #[inline]
397 pub fn volume(&self) -> T {
398 let size = self.size();
399 size.width * size.height * size.depth
400 }
401
402 #[inline]
403 pub fn xy_area(&self) -> T {
404 let size = self.size();
405 size.width * size.height
406 }
407
408 #[inline]
409 pub fn yz_area(&self) -> T {
410 let size = self.size();
411 size.depth * size.height
412 }
413
414 #[inline]
415 pub fn xz_area(&self) -> T {
416 let size = self.size();
417 size.depth * size.width
418 }
419}
420
421impl<T, U> Box3D<T, U>
422where
423 T: Zero,
424{
425 pub fn zero() -> Self {
427 Box3D::new(Point3D::zero(), Point3D::zero())
428 }
429}
430
431impl<T: Copy + Mul, U> Mul<T> for Box3D<T, U> {
432 type Output = Box3D<T::Output, U>;
433
434 #[inline]
435 fn mul(self, scale: T) -> Self::Output {
436 Box3D::new(self.min * scale, self.max * scale)
437 }
438}
439
440impl<T: Copy + MulAssign, U> MulAssign<T> for Box3D<T, U> {
441 #[inline]
442 fn mul_assign(&mut self, scale: T) {
443 self.min *= scale;
444 self.max *= scale;
445 }
446}
447
448impl<T: Copy + Div, U> Div<T> for Box3D<T, U> {
449 type Output = Box3D<T::Output, U>;
450
451 #[inline]
452 fn div(self, scale: T) -> Self::Output {
453 Box3D::new(self.min / scale.clone(), self.max / scale)
454 }
455}
456
457impl<T: Copy + DivAssign, U> DivAssign<T> for Box3D<T, U> {
458 #[inline]
459 fn div_assign(&mut self, scale: T) {
460 self.min /= scale;
461 self.max /= scale;
462 }
463}
464
465impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box3D<T, U1> {
466 type Output = Box3D<T::Output, U2>;
467
468 #[inline]
469 fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
470 Box3D::new(self.min * scale.clone(), self.max * scale)
471 }
472}
473
474impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Box3D<T, U> {
475 #[inline]
476 fn mul_assign(&mut self, scale: Scale<T, U, U>) {
477 self.min *= scale.clone();
478 self.max *= scale;
479 }
480}
481
482impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Box3D<T, U2> {
483 type Output = Box3D<T::Output, U1>;
484
485 #[inline]
486 fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
487 Box3D::new(self.min / scale.clone(), self.max / scale)
488 }
489}
490
491impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Box3D<T, U> {
492 #[inline]
493 fn div_assign(&mut self, scale: Scale<T, U, U>) {
494 self.min /= scale.clone();
495 self.max /= scale;
496 }
497}
498
499impl<T, U> Box3D<T, U>
500where
501 T: Copy,
502{
503 #[inline]
504 pub fn x_range(&self) -> Range<T> {
505 self.min.x..self.max.x
506 }
507
508 #[inline]
509 pub fn y_range(&self) -> Range<T> {
510 self.min.y..self.max.y
511 }
512
513 #[inline]
514 pub fn z_range(&self) -> Range<T> {
515 self.min.z..self.max.z
516 }
517
518 #[inline]
520 pub fn to_untyped(&self) -> Box3D<T, UnknownUnit> {
521 Box3D {
522 min: self.min.to_untyped(),
523 max: self.max.to_untyped(),
524 }
525 }
526
527 #[inline]
529 pub fn from_untyped(c: &Box3D<T, UnknownUnit>) -> Box3D<T, U> {
530 Box3D {
531 min: Point3D::from_untyped(c.min),
532 max: Point3D::from_untyped(c.max),
533 }
534 }
535
536 #[inline]
538 pub fn cast_unit<V>(&self) -> Box3D<T, V> {
539 Box3D::new(self.min.cast_unit(), self.max.cast_unit())
540 }
541
542 #[inline]
543 pub fn scale<S: Copy>(&self, x: S, y: S, z: S) -> Self
544 where
545 T: Mul<S, Output = T>,
546 {
547 Box3D::new(
548 Point3D::new(self.min.x * x, self.min.y * y, self.min.z * z),
549 Point3D::new(self.max.x * x, self.max.y * y, self.max.z * z),
550 )
551 }
552}
553
554impl<T: NumCast + Copy, U> Box3D<T, U> {
555 #[inline]
565 pub fn cast<NewT: NumCast>(&self) -> Box3D<NewT, U> {
566 Box3D::new(self.min.cast(), self.max.cast())
567 }
568
569 pub fn try_cast<NewT: NumCast>(&self) -> Option<Box3D<NewT, U>> {
579 match (self.min.try_cast(), self.max.try_cast()) {
580 (Some(a), Some(b)) => Some(Box3D::new(a, b)),
581 _ => None,
582 }
583 }
584
585 #[inline]
589 pub fn to_f32(&self) -> Box3D<f32, U> {
590 self.cast()
591 }
592
593 #[inline]
595 pub fn to_f64(&self) -> Box3D<f64, U> {
596 self.cast()
597 }
598
599 #[inline]
605 pub fn to_usize(&self) -> Box3D<usize, U> {
606 self.cast()
607 }
608
609 #[inline]
615 pub fn to_u32(&self) -> Box3D<u32, U> {
616 self.cast()
617 }
618
619 #[inline]
625 pub fn to_i32(&self) -> Box3D<i32, U> {
626 self.cast()
627 }
628
629 #[inline]
635 pub fn to_i64(&self) -> Box3D<i64, U> {
636 self.cast()
637 }
638}
639
640impl<T: Float, U> Box3D<T, U> {
641 #[inline]
643 pub fn is_finite(self) -> bool {
644 self.min.is_finite() && self.max.is_finite()
645 }
646}
647
648impl<T, U> Box3D<T, U>
649where
650 T: Round,
651{
652 #[must_use]
662 pub fn round(&self) -> Self {
663 Box3D::new(self.min.round(), self.max.round())
664 }
665}
666
667impl<T, U> Box3D<T, U>
668where
669 T: Floor + Ceil,
670{
671 #[must_use]
674 pub fn round_in(&self) -> Self {
675 Box3D {
676 min: self.min.ceil(),
677 max: self.max.floor(),
678 }
679 }
680
681 #[must_use]
684 pub fn round_out(&self) -> Self {
685 Box3D {
686 min: self.min.floor(),
687 max: self.max.ceil(),
688 }
689 }
690}
691
692impl<T, U> From<Size3D<T, U>> for Box3D<T, U>
693where
694 T: Copy + Zero + PartialOrd,
695{
696 fn from(b: Size3D<T, U>) -> Self {
697 Self::from_size(b)
698 }
699}
700
701impl<T: Default, U> Default for Box3D<T, U> {
702 fn default() -> Self {
703 Box3D {
704 min: Default::default(),
705 max: Default::default(),
706 }
707 }
708}
709
710pub fn box3d<T: Copy, U>(
712 min_x: T,
713 min_y: T,
714 min_z: T,
715 max_x: T,
716 max_y: T,
717 max_z: T,
718) -> Box3D<T, U> {
719 Box3D::new(
720 Point3D::new(min_x, min_y, min_z),
721 Point3D::new(max_x, max_y, max_z),
722 )
723}
724
725#[cfg(test)]
726mod tests {
727 use crate::default::{Box3D, Point3D};
728 use crate::{point3, size3, vec3};
729
730 #[test]
731 fn test_new() {
732 let b = Box3D::new(point3(-1.0, -1.0, -1.0), point3(1.0, 1.0, 1.0));
733 assert!(b.min.x == -1.0);
734 assert!(b.min.y == -1.0);
735 assert!(b.min.z == -1.0);
736 assert!(b.max.x == 1.0);
737 assert!(b.max.y == 1.0);
738 assert!(b.max.z == 1.0);
739 }
740
741 #[test]
742 fn test_size() {
743 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
744 assert!(b.size().width == 20.0);
745 assert!(b.size().height == 20.0);
746 assert!(b.size().depth == 20.0);
747 }
748
749 #[test]
750 fn test_width_height_depth() {
751 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
752 assert!(b.width() == 20.0);
753 assert!(b.height() == 20.0);
754 assert!(b.depth() == 20.0);
755 }
756
757 #[test]
758 fn test_center() {
759 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
760 assert!(b.center() == Point3D::zero());
761 }
762
763 #[test]
764 fn test_volume() {
765 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
766 assert!(b.volume() == 8000.0);
767 }
768
769 #[test]
770 fn test_area() {
771 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
772 assert!(b.xy_area() == 400.0);
773 assert!(b.yz_area() == 400.0);
774 assert!(b.xz_area() == 400.0);
775 }
776
777 #[test]
778 fn test_from_points() {
779 let b = Box3D::from_points(&[point3(50.0, 160.0, 12.5), point3(100.0, 25.0, 200.0)]);
780 assert!(b.min == point3(50.0, 25.0, 12.5));
781 assert!(b.max == point3(100.0, 160.0, 200.0));
782 }
783
784 #[test]
785 fn test_min_max() {
786 let b = Box3D::from_points(&[point3(50.0, 25.0, 12.5), point3(100.0, 160.0, 200.0)]);
787 assert!(b.min.x == 50.0);
788 assert!(b.min.y == 25.0);
789 assert!(b.min.z == 12.5);
790 assert!(b.max.x == 100.0);
791 assert!(b.max.y == 160.0);
792 assert!(b.max.z == 200.0);
793 }
794
795 #[test]
796 fn test_round_in() {
797 let b =
798 Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round_in();
799 assert!(b.min.x == -25.0);
800 assert!(b.min.y == -40.0);
801 assert!(b.min.z == -70.0);
802 assert!(b.max.x == 60.0);
803 assert!(b.max.y == 36.0);
804 assert!(b.max.z == 89.0);
805 }
806
807 #[test]
808 fn test_round_out() {
809 let b = Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)])
810 .round_out();
811 assert!(b.min.x == -26.0);
812 assert!(b.min.y == -41.0);
813 assert!(b.min.z == -71.0);
814 assert!(b.max.x == 61.0);
815 assert!(b.max.y == 37.0);
816 assert!(b.max.z == 90.0);
817 }
818
819 #[test]
820 fn test_round() {
821 let b =
822 Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round();
823 assert!(b.min.x == -25.0);
824 assert!(b.min.y == -40.0);
825 assert!(b.min.z == -71.0);
826 assert!(b.max.x == 60.0);
827 assert!(b.max.y == 37.0);
828 assert!(b.max.z == 90.0);
829 }
830
831 #[test]
832 fn test_from_size() {
833 let b = Box3D::from_size(size3(30.0, 40.0, 50.0));
834 assert!(b.min == Point3D::zero());
835 assert!(b.size().width == 30.0);
836 assert!(b.size().height == 40.0);
837 assert!(b.size().depth == 50.0);
838 }
839
840 #[test]
841 fn test_translate() {
842 let size = size3(15.0, 15.0, 200.0);
843 let mut center = (size / 2.0).to_vector().to_point();
844 let b = Box3D::from_size(size);
845 assert!(b.center() == center);
846 let translation = vec3(10.0, 2.5, 9.5);
847 let b = b.translate(translation);
848 center += translation;
849 assert!(b.center() == center);
850 assert!(b.max.x == 25.0);
851 assert!(b.max.y == 17.5);
852 assert!(b.max.z == 209.5);
853 assert!(b.min.x == 10.0);
854 assert!(b.min.y == 2.5);
855 assert!(b.min.z == 9.5);
856 }
857
858 #[test]
859 fn test_union() {
860 let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(0.0, 20.0, 20.0)]);
861 let b2 = Box3D::from_points(&[point3(0.0, 20.0, 20.0), point3(20.0, -20.0, -20.0)]);
862 let b = b1.union(&b2);
863 assert!(b.max.x == 20.0);
864 assert!(b.max.y == 20.0);
865 assert!(b.max.z == 20.0);
866 assert!(b.min.x == -20.0);
867 assert!(b.min.y == -20.0);
868 assert!(b.min.z == -20.0);
869 assert!(b.volume() == (40.0 * 40.0 * 40.0));
870 }
871
872 #[test]
873 fn test_intersects() {
874 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
875 let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
876 assert!(b1.intersects(&b2));
877 }
878
879 #[test]
880 fn test_intersection_unchecked() {
881 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
882 let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
883 let b = b1.intersection_unchecked(&b2);
884 assert!(b.max.x == 10.0);
885 assert!(b.max.y == 20.0);
886 assert!(b.max.z == 20.0);
887 assert!(b.min.x == -10.0);
888 assert!(b.min.y == -20.0);
889 assert!(b.min.z == -20.0);
890 assert!(b.volume() == (20.0 * 40.0 * 40.0));
891 }
892
893 #[test]
894 fn test_intersection() {
895 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
896 let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
897 assert!(b1.intersection(&b2).is_some());
898
899 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(-10.0, 20.0, 20.0)]);
900 let b2 = Box3D::from_points(&[point3(10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
901 assert!(b1.intersection(&b2).is_none());
902 }
903
904 #[test]
905 fn test_scale() {
906 let b = Box3D::from_points(&[point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0)]);
907 let b = b.scale(0.5, 0.5, 0.5);
908 assert!(b.max.x == 5.0);
909 assert!(b.max.y == 5.0);
910 assert!(b.max.z == 5.0);
911 assert!(b.min.x == -5.0);
912 assert!(b.min.y == -5.0);
913 assert!(b.min.z == -5.0);
914 }
915
916 #[test]
917 fn test_zero() {
918 let b = Box3D::<f64>::zero();
919 assert!(b.max.x == 0.0);
920 assert!(b.max.y == 0.0);
921 assert!(b.max.z == 0.0);
922 assert!(b.min.x == 0.0);
923 assert!(b.min.y == 0.0);
924 assert!(b.min.z == 0.0);
925 }
926
927 #[test]
928 fn test_lerp() {
929 let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(-10.0, -10.0, -10.0)]);
930 let b2 = Box3D::from_points(&[point3(10.0, 10.0, 10.0), point3(20.0, 20.0, 20.0)]);
931 let b = b1.lerp(b2, 0.5);
932 assert!(b.center() == Point3D::zero());
933 assert!(b.size().width == 10.0);
934 assert!(b.size().height == 10.0);
935 assert!(b.size().depth == 10.0);
936 }
937
938 #[test]
939 fn test_contains() {
940 let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
941 assert!(b.contains(point3(-15.3, 10.5, 18.4)));
942 }
943
944 #[test]
945 fn test_contains_box() {
946 let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
947 let b2 = Box3D::from_points(&[point3(-14.3, -16.5, -19.3), point3(6.7, 17.6, 2.5)]);
948 assert!(b1.contains_box(&b2));
949 }
950
951 #[test]
952 fn test_inflate() {
953 let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
954 let b = b.inflate(10.0, 5.0, 2.0);
955 assert!(b.size().width == 60.0);
956 assert!(b.size().height == 50.0);
957 assert!(b.size().depth == 44.0);
958 assert!(b.center() == Point3D::zero());
959 }
960
961 #[test]
962 fn test_is_empty() {
963 for i in 0..3 {
964 let mut coords_neg = [-20.0, -20.0, -20.0];
965 let mut coords_pos = [20.0, 20.0, 20.0];
966 coords_neg[i] = 0.0;
967 coords_pos[i] = 0.0;
968 let b = Box3D::from_points(&[Point3D::from(coords_neg), Point3D::from(coords_pos)]);
969 assert!(b.is_empty());
970 }
971 }
972
973 #[test]
974 #[rustfmt::skip]
975 fn test_nan_empty_or_negative() {
976 use std::f32::NAN;
977 assert!(Box3D { min: point3(NAN, 2.0, 1.0), max: point3(1.0, 3.0, 5.0) }.is_empty());
978 assert!(Box3D { min: point3(0.0, NAN, 1.0), max: point3(1.0, 2.0, 5.0) }.is_empty());
979 assert!(Box3D { min: point3(1.0, -2.0, NAN), max: point3(3.0, 2.0, 5.0) }.is_empty());
980 assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(NAN, 2.0, 5.0) }.is_empty());
981 assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, NAN, 5.0) }.is_empty());
982 assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, 1.0, NAN) }.is_empty());
983 }
984}