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};
20#[cfg(feature = "malloc_size_of")]
21use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
22use num_traits::{Float, NumCast};
23#[cfg(feature = "serde")]
24use serde::{Deserialize, Serialize};
25
26use core::borrow::Borrow;
27use core::cmp::PartialOrd;
28use core::fmt;
29use core::hash::{Hash, Hasher};
30use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Range, Sub};
31
32#[repr(C)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35#[cfg_attr(
36 feature = "serde",
37 serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
38)]
39pub struct Box3D<T, U> {
40 pub min: Point3D<T, U>,
41 pub max: Point3D<T, U>,
42}
43
44impl<T: Hash, U> Hash for Box3D<T, U> {
45 fn hash<H: Hasher>(&self, h: &mut H) {
46 self.min.hash(h);
47 self.max.hash(h);
48 }
49}
50
51impl<T: Copy, U> Copy for Box3D<T, U> {}
52
53impl<T: Clone, U> Clone for Box3D<T, U> {
54 fn clone(&self) -> Self {
55 Self::new(self.min.clone(), self.max.clone())
56 }
57}
58
59impl<T: PartialEq, U> PartialEq for Box3D<T, U> {
60 fn eq(&self, other: &Self) -> bool {
61 self.min.eq(&other.min) && self.max.eq(&other.max)
62 }
63}
64
65impl<T: Eq, U> Eq for Box3D<T, U> {}
66
67impl<T: fmt::Debug, U> fmt::Debug for Box3D<T, U> {
68 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 f.debug_tuple("Box3D")
70 .field(&self.min)
71 .field(&self.max)
72 .finish()
73 }
74}
75
76#[cfg(feature = "arbitrary")]
77impl<'a, T, U> arbitrary::Arbitrary<'a> for Box3D<T, U>
78where
79 T: arbitrary::Arbitrary<'a>,
80{
81 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
82 Ok(Box3D::new(
83 arbitrary::Arbitrary::arbitrary(u)?,
84 arbitrary::Arbitrary::arbitrary(u)?,
85 ))
86 }
87}
88
89#[cfg(feature = "bytemuck")]
90unsafe impl<T: Zeroable, U> Zeroable for Box3D<T, U> {}
91
92#[cfg(feature = "bytemuck")]
93unsafe impl<T: Pod, U: 'static> Pod for Box3D<T, U> {}
94
95#[cfg(feature = "malloc_size_of")]
96impl<T: MallocSizeOf, U> MallocSizeOf for Box3D<T, U> {
97 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
98 self.min.size_of(ops) + self.max.size_of(ops)
99 }
100}
101
102impl<T, U> Box3D<T, U> {
103 #[inline]
105 pub const fn new(min: Point3D<T, U>, max: Point3D<T, U>) -> Self {
106 Box3D { min, max }
107 }
108
109 #[inline]
111 pub fn from_origin_and_size(origin: Point3D<T, U>, size: Size3D<T, U>) -> Self
112 where
113 T: Copy + Add<T, Output = T>,
114 {
115 Box3D {
116 min: origin,
117 max: point3(
118 origin.x + size.width,
119 origin.y + size.height,
120 origin.z + size.depth,
121 ),
122 }
123 }
124
125 #[inline]
127 pub fn from_size(size: Size3D<T, U>) -> Self
128 where
129 T: Zero,
130 {
131 Box3D {
132 min: Point3D::zero(),
133 max: point3(size.width, size.height, size.depth),
134 }
135 }
136}
137
138impl<T, U> Box3D<T, U>
139where
140 T: PartialOrd,
141{
142 #[inline]
147 pub fn is_negative(&self) -> bool {
148 self.max.x < self.min.x || self.max.y < self.min.y || self.max.z < self.min.z
149 }
150
151 #[inline]
153 pub fn is_empty(&self) -> bool {
154 !(self.max.x > self.min.x && self.max.y > self.min.y && self.max.z > self.min.z)
155 }
156
157 #[inline]
158 pub fn intersects(&self, other: &Self) -> bool {
159 self.min.x < other.max.x
160 && self.max.x > other.min.x
161 && self.min.y < other.max.y
162 && self.max.y > other.min.y
163 && self.min.z < other.max.z
164 && self.max.z > other.min.z
165 }
166
167 #[inline]
194 pub fn contains(&self, other: Point3D<T, U>) -> bool {
195 (self.min.x <= other.x)
196 & (other.x < self.max.x)
197 & (self.min.y <= other.y)
198 & (other.y < self.max.y)
199 & (self.min.z <= other.z)
200 & (other.z < self.max.z)
201 }
202
203 #[inline]
228 pub fn contains_inclusive(&self, other: Point3D<T, U>) -> bool {
229 (self.min.x <= other.x)
230 & (other.x <= self.max.x)
231 & (self.min.y <= other.y)
232 & (other.y <= self.max.y)
233 & (self.min.z <= other.z)
234 & (other.z <= self.max.z)
235 }
236
237 #[inline]
241 pub fn contains_box(&self, other: &Self) -> bool {
242 other.is_empty()
243 || ((self.min.x <= other.min.x)
244 & (other.max.x <= self.max.x)
245 & (self.min.y <= other.min.y)
246 & (other.max.y <= self.max.y)
247 & (self.min.z <= other.min.z)
248 & (other.max.z <= self.max.z))
249 }
250}
251
252impl<T, U> Box3D<T, U>
253where
254 T: Copy + PartialOrd,
255{
256 #[inline]
257 pub fn to_non_empty(&self) -> Option<Self> {
258 if self.is_empty() {
259 return None;
260 }
261
262 Some(*self)
263 }
264
265 #[inline]
266 pub fn intersection(&self, other: &Self) -> Option<Self> {
267 let b = self.intersection_unchecked(other);
268
269 if b.is_empty() {
270 return None;
271 }
272
273 Some(b)
274 }
275
276 pub fn intersection_unchecked(&self, other: &Self) -> Self {
277 let intersection_min = Point3D::new(
278 max(self.min.x, other.min.x),
279 max(self.min.y, other.min.y),
280 max(self.min.z, other.min.z),
281 );
282
283 let intersection_max = Point3D::new(
284 min(self.max.x, other.max.x),
285 min(self.max.y, other.max.y),
286 min(self.max.z, other.max.z),
287 );
288
289 Box3D::new(intersection_min, intersection_max)
290 }
291
292 #[inline]
296 pub fn union(&self, other: &Self) -> Self {
297 if other.is_empty() {
298 return *self;
299 }
300 if self.is_empty() {
301 return *other;
302 }
303
304 Box3D::new(
305 Point3D::new(
306 min(self.min.x, other.min.x),
307 min(self.min.y, other.min.y),
308 min(self.min.z, other.min.z),
309 ),
310 Point3D::new(
311 max(self.max.x, other.max.x),
312 max(self.max.y, other.max.y),
313 max(self.max.z, other.max.z),
314 ),
315 )
316 }
317}
318
319impl<T, U> Box3D<T, U>
320where
321 T: Copy + Add<T, Output = T>,
322{
323 #[inline]
325 #[must_use]
326 pub fn translate(&self, by: Vector3D<T, U>) -> Self {
327 Box3D {
328 min: self.min + by,
329 max: self.max + by,
330 }
331 }
332}
333
334impl<T, U> Box3D<T, U>
335where
336 T: Copy + Sub<T, Output = T>,
337{
338 #[inline]
339 pub fn size(&self) -> Size3D<T, U> {
340 Size3D::new(
341 self.max.x - self.min.x,
342 self.max.y - self.min.y,
343 self.max.z - self.min.z,
344 )
345 }
346
347 #[inline]
348 pub fn width(&self) -> T {
349 self.max.x - self.min.x
350 }
351
352 #[inline]
353 pub fn height(&self) -> T {
354 self.max.y - self.min.y
355 }
356
357 #[inline]
358 pub fn depth(&self) -> T {
359 self.max.z - self.min.z
360 }
361}
362
363impl<T, U> Box3D<T, U>
364where
365 T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
366{
367 #[inline]
369 #[must_use]
370 pub fn inflate(&self, width: T, height: T, depth: T) -> Self {
371 Box3D::new(
372 Point3D::new(self.min.x - width, self.min.y - height, self.min.z - depth),
373 Point3D::new(self.max.x + width, self.max.y + height, self.max.z + depth),
374 )
375 }
376}
377
378impl<T, U> Box3D<T, U>
379where
380 T: Copy + Zero + PartialOrd,
381{
382 pub fn from_points<I>(points: I) -> Self
431 where
432 I: IntoIterator,
433 I::Item: Borrow<Point3D<T, U>>,
434 {
435 let mut points = points.into_iter();
436
437 let (mut min_x, mut min_y, mut min_z) = match points.next() {
438 Some(first) => first.borrow().to_tuple(),
439 None => return Box3D::zero(),
440 };
441 let (mut max_x, mut max_y, mut max_z) = (min_x, min_y, min_z);
442
443 for point in points {
444 let p = point.borrow();
445 if p.x < min_x {
446 min_x = p.x;
447 }
448 if p.x > max_x {
449 max_x = p.x;
450 }
451 if p.y < min_y {
452 min_y = p.y;
453 }
454 if p.y > max_y {
455 max_y = p.y;
456 }
457 if p.z < min_z {
458 min_z = p.z;
459 }
460 if p.z > max_z {
461 max_z = p.z;
462 }
463 }
464
465 Box3D {
466 min: point3(min_x, min_y, min_z),
467 max: point3(max_x, max_y, max_z),
468 }
469 }
470}
471
472impl<T, U> Box3D<T, U>
473where
474 T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
475{
476 #[inline]
478 pub fn lerp(&self, other: Self, t: T) -> Self {
479 Self::new(self.min.lerp(other.min, t), self.max.lerp(other.max, t))
480 }
481}
482
483impl<T, U> Box3D<T, U>
484where
485 T: Copy + One + Add<Output = T> + Div<Output = T>,
486{
487 pub fn center(&self) -> Point3D<T, U> {
488 let two = T::one() + T::one();
489 (self.min + self.max.to_vector()) / two
490 }
491}
492
493impl<T, U> Box3D<T, U>
494where
495 T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
496{
497 #[inline]
498 pub fn volume(&self) -> T {
499 let size = self.size();
500 size.width * size.height * size.depth
501 }
502
503 #[inline]
504 pub fn xy_area(&self) -> T {
505 let size = self.size();
506 size.width * size.height
507 }
508
509 #[inline]
510 pub fn yz_area(&self) -> T {
511 let size = self.size();
512 size.depth * size.height
513 }
514
515 #[inline]
516 pub fn xz_area(&self) -> T {
517 let size = self.size();
518 size.depth * size.width
519 }
520}
521
522impl<T, U> Box3D<T, U>
523where
524 T: Zero,
525{
526 pub fn zero() -> Self {
528 Box3D::new(Point3D::zero(), Point3D::zero())
529 }
530}
531
532impl<T: Copy + Mul, U> Mul<T> for Box3D<T, U> {
533 type Output = Box3D<T::Output, U>;
534
535 #[inline]
536 fn mul(self, scale: T) -> Self::Output {
537 Box3D::new(self.min * scale, self.max * scale)
538 }
539}
540
541impl<T: Copy + MulAssign, U> MulAssign<T> for Box3D<T, U> {
542 #[inline]
543 fn mul_assign(&mut self, scale: T) {
544 self.min *= scale;
545 self.max *= scale;
546 }
547}
548
549impl<T: Copy + Div, U> Div<T> for Box3D<T, U> {
550 type Output = Box3D<T::Output, U>;
551
552 #[inline]
553 fn div(self, scale: T) -> Self::Output {
554 Box3D::new(self.min / scale.clone(), self.max / scale)
555 }
556}
557
558impl<T: Copy + DivAssign, U> DivAssign<T> for Box3D<T, U> {
559 #[inline]
560 fn div_assign(&mut self, scale: T) {
561 self.min /= scale;
562 self.max /= scale;
563 }
564}
565
566impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box3D<T, U1> {
567 type Output = Box3D<T::Output, U2>;
568
569 #[inline]
570 fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
571 Box3D::new(self.min * scale.clone(), self.max * scale)
572 }
573}
574
575impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Box3D<T, U> {
576 #[inline]
577 fn mul_assign(&mut self, scale: Scale<T, U, U>) {
578 self.min *= scale.clone();
579 self.max *= scale;
580 }
581}
582
583impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Box3D<T, U2> {
584 type Output = Box3D<T::Output, U1>;
585
586 #[inline]
587 fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
588 Box3D::new(self.min / scale.clone(), self.max / scale)
589 }
590}
591
592impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Box3D<T, U> {
593 #[inline]
594 fn div_assign(&mut self, scale: Scale<T, U, U>) {
595 self.min /= scale.clone();
596 self.max /= scale;
597 }
598}
599
600impl<T, U> Box3D<T, U>
601where
602 T: Copy,
603{
604 #[inline]
605 pub fn x_range(&self) -> Range<T> {
606 self.min.x..self.max.x
607 }
608
609 #[inline]
610 pub fn y_range(&self) -> Range<T> {
611 self.min.y..self.max.y
612 }
613
614 #[inline]
615 pub fn z_range(&self) -> Range<T> {
616 self.min.z..self.max.z
617 }
618
619 #[inline]
621 pub fn to_untyped(&self) -> Box3D<T, UnknownUnit> {
622 Box3D {
623 min: self.min.to_untyped(),
624 max: self.max.to_untyped(),
625 }
626 }
627
628 #[inline]
630 pub fn from_untyped(c: &Box3D<T, UnknownUnit>) -> Box3D<T, U> {
631 Box3D {
632 min: Point3D::from_untyped(c.min),
633 max: Point3D::from_untyped(c.max),
634 }
635 }
636
637 #[inline]
639 pub fn cast_unit<V>(&self) -> Box3D<T, V> {
640 Box3D::new(self.min.cast_unit(), self.max.cast_unit())
641 }
642
643 #[inline]
644 pub fn scale<S: Copy>(&self, x: S, y: S, z: S) -> Self
645 where
646 T: Mul<S, Output = T>,
647 {
648 Box3D::new(
649 Point3D::new(self.min.x * x, self.min.y * y, self.min.z * z),
650 Point3D::new(self.max.x * x, self.max.y * y, self.max.z * z),
651 )
652 }
653}
654
655impl<T: NumCast + Copy, U> Box3D<T, U> {
656 #[inline]
666 pub fn cast<NewT: NumCast>(&self) -> Box3D<NewT, U> {
667 Box3D::new(self.min.cast(), self.max.cast())
668 }
669
670 pub fn try_cast<NewT: NumCast>(&self) -> Option<Box3D<NewT, U>> {
680 match (self.min.try_cast(), self.max.try_cast()) {
681 (Some(a), Some(b)) => Some(Box3D::new(a, b)),
682 _ => None,
683 }
684 }
685
686 #[inline]
690 pub fn to_f32(&self) -> Box3D<f32, U> {
691 self.cast()
692 }
693
694 #[inline]
696 pub fn to_f64(&self) -> Box3D<f64, U> {
697 self.cast()
698 }
699
700 #[inline]
706 pub fn to_usize(&self) -> Box3D<usize, U> {
707 self.cast()
708 }
709
710 #[inline]
716 pub fn to_u32(&self) -> Box3D<u32, U> {
717 self.cast()
718 }
719
720 #[inline]
726 pub fn to_i32(&self) -> Box3D<i32, U> {
727 self.cast()
728 }
729
730 #[inline]
736 pub fn to_i64(&self) -> Box3D<i64, U> {
737 self.cast()
738 }
739}
740
741impl<T: Float, U> Box3D<T, U> {
742 #[inline]
744 pub fn is_finite(self) -> bool {
745 self.min.is_finite() && self.max.is_finite()
746 }
747}
748
749impl<T, U> Box3D<T, U>
750where
751 T: Round,
752{
753 #[must_use]
763 pub fn round(&self) -> Self {
764 Box3D::new(self.min.round(), self.max.round())
765 }
766}
767
768impl<T, U> Box3D<T, U>
769where
770 T: Floor + Ceil,
771{
772 #[must_use]
775 pub fn round_in(&self) -> Self {
776 Box3D {
777 min: self.min.ceil(),
778 max: self.max.floor(),
779 }
780 }
781
782 #[must_use]
785 pub fn round_out(&self) -> Self {
786 Box3D {
787 min: self.min.floor(),
788 max: self.max.ceil(),
789 }
790 }
791}
792
793impl<T, U> From<Size3D<T, U>> for Box3D<T, U>
794where
795 T: Copy + Zero + PartialOrd,
796{
797 fn from(b: Size3D<T, U>) -> Self {
798 Self::from_size(b)
799 }
800}
801
802impl<T: Default, U> Default for Box3D<T, U> {
803 fn default() -> Self {
804 Box3D {
805 min: Default::default(),
806 max: Default::default(),
807 }
808 }
809}
810
811pub fn box3d<T: Copy, U>(
813 min_x: T,
814 min_y: T,
815 min_z: T,
816 max_x: T,
817 max_y: T,
818 max_z: T,
819) -> Box3D<T, U> {
820 Box3D::new(
821 Point3D::new(min_x, min_y, min_z),
822 Point3D::new(max_x, max_y, max_z),
823 )
824}
825
826#[cfg(test)]
827mod tests {
828 use crate::default::{Box3D, Point3D};
829 use crate::{point3, size3, vec3};
830
831 #[test]
832 fn test_new() {
833 let b = Box3D::new(point3(-1.0, -1.0, -1.0), point3(1.0, 1.0, 1.0));
834 assert!(b.min.x == -1.0);
835 assert!(b.min.y == -1.0);
836 assert!(b.min.z == -1.0);
837 assert!(b.max.x == 1.0);
838 assert!(b.max.y == 1.0);
839 assert!(b.max.z == 1.0);
840 }
841
842 #[test]
843 fn test_size() {
844 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
845 assert!(b.size().width == 20.0);
846 assert!(b.size().height == 20.0);
847 assert!(b.size().depth == 20.0);
848 }
849
850 #[test]
851 fn test_width_height_depth() {
852 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
853 assert!(b.width() == 20.0);
854 assert!(b.height() == 20.0);
855 assert!(b.depth() == 20.0);
856 }
857
858 #[test]
859 fn test_center() {
860 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
861 assert!(b.center() == Point3D::zero());
862 }
863
864 #[test]
865 fn test_volume() {
866 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
867 assert!(b.volume() == 8000.0);
868 }
869
870 #[test]
871 fn test_area() {
872 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
873 assert!(b.xy_area() == 400.0);
874 assert!(b.yz_area() == 400.0);
875 assert!(b.xz_area() == 400.0);
876 }
877
878 #[test]
879 fn test_from_points() {
880 let b = Box3D::from_points(&[point3(50.0, 160.0, 12.5), point3(100.0, 25.0, 200.0)]);
881 assert!(b.min == point3(50.0, 25.0, 12.5));
882 assert!(b.max == point3(100.0, 160.0, 200.0));
883 }
884
885 #[test]
886 fn test_min_max() {
887 let b = Box3D::from_points(&[point3(50.0, 25.0, 12.5), point3(100.0, 160.0, 200.0)]);
888 assert!(b.min.x == 50.0);
889 assert!(b.min.y == 25.0);
890 assert!(b.min.z == 12.5);
891 assert!(b.max.x == 100.0);
892 assert!(b.max.y == 160.0);
893 assert!(b.max.z == 200.0);
894 }
895
896 #[test]
897 fn test_round_in() {
898 let b =
899 Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round_in();
900 assert!(b.min.x == -25.0);
901 assert!(b.min.y == -40.0);
902 assert!(b.min.z == -70.0);
903 assert!(b.max.x == 60.0);
904 assert!(b.max.y == 36.0);
905 assert!(b.max.z == 89.0);
906 }
907
908 #[test]
909 fn test_round_out() {
910 let b = Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)])
911 .round_out();
912 assert!(b.min.x == -26.0);
913 assert!(b.min.y == -41.0);
914 assert!(b.min.z == -71.0);
915 assert!(b.max.x == 61.0);
916 assert!(b.max.y == 37.0);
917 assert!(b.max.z == 90.0);
918 }
919
920 #[test]
921 fn test_round() {
922 let b =
923 Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round();
924 assert!(b.min.x == -25.0);
925 assert!(b.min.y == -40.0);
926 assert!(b.min.z == -71.0);
927 assert!(b.max.x == 60.0);
928 assert!(b.max.y == 37.0);
929 assert!(b.max.z == 90.0);
930 }
931
932 #[test]
933 fn test_from_size() {
934 let b = Box3D::from_size(size3(30.0, 40.0, 50.0));
935 assert!(b.min == Point3D::zero());
936 assert!(b.size().width == 30.0);
937 assert!(b.size().height == 40.0);
938 assert!(b.size().depth == 50.0);
939 }
940
941 #[test]
942 fn test_translate() {
943 let size = size3(15.0, 15.0, 200.0);
944 let mut center = (size / 2.0).to_vector().to_point();
945 let b = Box3D::from_size(size);
946 assert!(b.center() == center);
947 let translation = vec3(10.0, 2.5, 9.5);
948 let b = b.translate(translation);
949 center += translation;
950 assert!(b.center() == center);
951 assert!(b.max.x == 25.0);
952 assert!(b.max.y == 17.5);
953 assert!(b.max.z == 209.5);
954 assert!(b.min.x == 10.0);
955 assert!(b.min.y == 2.5);
956 assert!(b.min.z == 9.5);
957 }
958
959 #[test]
960 fn test_union() {
961 let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(0.0, 20.0, 20.0)]);
962 let b2 = Box3D::from_points(&[point3(0.0, 20.0, 20.0), point3(20.0, -20.0, -20.0)]);
963 let b = b1.union(&b2);
964 assert!(b.max.x == 20.0);
965 assert!(b.max.y == 20.0);
966 assert!(b.max.z == 20.0);
967 assert!(b.min.x == -20.0);
968 assert!(b.min.y == -20.0);
969 assert!(b.min.z == -20.0);
970 assert!(b.volume() == (40.0 * 40.0 * 40.0));
971 }
972
973 #[test]
974 fn test_intersects() {
975 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
976 let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
977 assert!(b1.intersects(&b2));
978 }
979
980 #[test]
981 fn test_intersection_unchecked() {
982 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
983 let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
984 let b = b1.intersection_unchecked(&b2);
985 assert!(b.max.x == 10.0);
986 assert!(b.max.y == 20.0);
987 assert!(b.max.z == 20.0);
988 assert!(b.min.x == -10.0);
989 assert!(b.min.y == -20.0);
990 assert!(b.min.z == -20.0);
991 assert!(b.volume() == (20.0 * 40.0 * 40.0));
992 }
993
994 #[test]
995 fn test_intersection() {
996 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
997 let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
998 assert!(b1.intersection(&b2).is_some());
999
1000 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(-10.0, 20.0, 20.0)]);
1001 let b2 = Box3D::from_points(&[point3(10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
1002 assert!(b1.intersection(&b2).is_none());
1003 }
1004
1005 #[test]
1006 fn test_scale() {
1007 let b = Box3D::from_points(&[point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0)]);
1008 let b = b.scale(0.5, 0.5, 0.5);
1009 assert!(b.max.x == 5.0);
1010 assert!(b.max.y == 5.0);
1011 assert!(b.max.z == 5.0);
1012 assert!(b.min.x == -5.0);
1013 assert!(b.min.y == -5.0);
1014 assert!(b.min.z == -5.0);
1015 }
1016
1017 #[test]
1018 fn test_zero() {
1019 let b = Box3D::<f64>::zero();
1020 assert!(b.max.x == 0.0);
1021 assert!(b.max.y == 0.0);
1022 assert!(b.max.z == 0.0);
1023 assert!(b.min.x == 0.0);
1024 assert!(b.min.y == 0.0);
1025 assert!(b.min.z == 0.0);
1026 }
1027
1028 #[test]
1029 fn test_lerp() {
1030 let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(-10.0, -10.0, -10.0)]);
1031 let b2 = Box3D::from_points(&[point3(10.0, 10.0, 10.0), point3(20.0, 20.0, 20.0)]);
1032 let b = b1.lerp(b2, 0.5);
1033 assert!(b.center() == Point3D::zero());
1034 assert!(b.size().width == 10.0);
1035 assert!(b.size().height == 10.0);
1036 assert!(b.size().depth == 10.0);
1037 }
1038
1039 #[test]
1040 fn test_contains() {
1041 let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
1042 assert!(b.contains(point3(-15.3, 10.5, 18.4)));
1043 }
1044
1045 #[test]
1046 fn test_contains_box() {
1047 let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
1048 let b2 = Box3D::from_points(&[point3(-14.3, -16.5, -19.3), point3(6.7, 17.6, 2.5)]);
1049 assert!(b1.contains_box(&b2));
1050 }
1051
1052 #[test]
1053 fn test_inflate() {
1054 let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
1055 let b = b.inflate(10.0, 5.0, 2.0);
1056 assert!(b.size().width == 60.0);
1057 assert!(b.size().height == 50.0);
1058 assert!(b.size().depth == 44.0);
1059 assert!(b.center() == Point3D::zero());
1060 }
1061
1062 #[test]
1063 fn test_is_empty() {
1064 for i in 0..3 {
1065 let mut coords_neg = [-20.0, -20.0, -20.0];
1066 let mut coords_pos = [20.0, 20.0, 20.0];
1067 coords_neg[i] = 0.0;
1068 coords_pos[i] = 0.0;
1069 let b = Box3D::from_points(&[Point3D::from(coords_neg), Point3D::from(coords_pos)]);
1070 assert!(b.is_empty());
1071 }
1072 }
1073
1074 #[test]
1075 #[rustfmt::skip]
1076 fn test_nan_empty_or_negative() {
1077 use std::f32::NAN;
1078 assert!(Box3D { min: point3(NAN, 2.0, 1.0), max: point3(1.0, 3.0, 5.0) }.is_empty());
1079 assert!(Box3D { min: point3(0.0, NAN, 1.0), max: point3(1.0, 2.0, 5.0) }.is_empty());
1080 assert!(Box3D { min: point3(1.0, -2.0, NAN), max: point3(3.0, 2.0, 5.0) }.is_empty());
1081 assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(NAN, 2.0, 5.0) }.is_empty());
1082 assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, NAN, 5.0) }.is_empty());
1083 assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, 1.0, NAN) }.is_empty());
1084 }
1085}