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