1use super::UnknownUnit;
11use length::Length;
12use scale::TypedScale;
13use num::*;
14use point::TypedPoint3D;
15use vector::TypedVector3D;
16use size::TypedSize3D;
17use approxord::{min, max};
18
19use num_traits::NumCast;
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22
23use core::borrow::Borrow;
24use core::cmp::PartialOrd;
25use core::fmt;
26use core::hash::{Hash, Hasher};
27use core::ops::{Add, Div, Mul, Sub};
28
29
30#[repr(C)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33#[cfg_attr(feature = "serde", serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>")))]
34pub struct TypedBox3D<T, U> {
35 pub min: TypedPoint3D<T, U>,
36 pub max: TypedPoint3D<T, U>,
37}
38
39pub type Box3D<T> = TypedBox3D<T, UnknownUnit>;
41
42impl<T: Hash, U> Hash for TypedBox3D<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 TypedBox3D<T, U> {}
50
51impl<T: Copy, U> Clone for TypedBox3D<T, U> {
52 fn clone(&self) -> Self {
53 *self
54 }
55}
56
57impl<T: PartialEq, U> PartialEq<TypedBox3D<T, U>> for TypedBox3D<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 TypedBox3D<T, U> {}
64
65impl<T: fmt::Debug, U> fmt::Debug for TypedBox3D<T, U> {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 write!(f, "TypedBox3D({:?}, {:?})", self.min, self.max)
68 }
69}
70
71impl<T: fmt::Display, U> fmt::Display for TypedBox3D<T, U> {
72 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
73 write!(formatter, "Box3D({}, {})", self.min, self.max)
74 }
75}
76
77impl<T, U> TypedBox3D<T, U> {
78 pub fn new(min: TypedPoint3D<T, U>, max: TypedPoint3D<T, U>) -> Self {
80 TypedBox3D {
81 min,
82 max,
83 }
84 }
85}
86
87impl<T, U> TypedBox3D<T, U>
88where
89 T: Copy + Zero + PartialOrd,
90{
91 #[inline]
93 pub fn from_size(size: TypedSize3D<T, U>) -> Self {
94 let zero = TypedPoint3D::zero();
95 let point = size.to_vector().to_point();
96 TypedBox3D::from_points(&[zero, point])
97 }
98}
99
100impl<T, U> TypedBox3D<T, U>
101where
102 T: Copy + PartialOrd,
103{
104 #[inline]
109 pub fn is_negative(&self) -> bool {
110 self.max.x < self.min.x || self.max.y < self.min.y || self.max.z < self.min.z
111 }
112
113 #[inline]
115 pub fn is_empty_or_negative(&self) -> bool {
116 self.max.x <= self.min.x || self.max.y <= self.min.y || self.max.z <= self.min.z
117 }
118
119
120 #[inline]
121 pub fn intersects(&self, other: &Self) -> bool {
122 self.min.x < other.max.x
123 && self.max.x > other.min.x
124 && self.min.y < other.max.y
125 && self.max.y > other.min.y
126 && self.min.z < other.max.z
127 && self.max.z > other.min.z
128 }
129
130 #[inline]
131 pub fn try_intersection(&self, other: &Self) -> Option<Self> {
132 if !self.intersects(other) {
133 return None;
134 }
135
136 Some(self.intersection(other))
137 }
138
139 pub fn intersection(&self, other: &Self) -> Self {
140 let intersection_min = TypedPoint3D::new(
141 max(self.min.x, other.min.x),
142 max(self.min.y, other.min.y),
143 max(self.min.z, other.min.z),
144 );
145
146 let intersection_max = TypedPoint3D::new(
147 min(self.max.x, other.max.x),
148 min(self.max.y, other.max.y),
149 min(self.max.z, other.max.z),
150 );
151
152 TypedBox3D::new(
153 intersection_min,
154 intersection_max,
155 )
156 }
157}
158
159impl<T, U> TypedBox3D<T, U>
160where
161 T: Copy + Add<T, Output = T>,
162{
163 #[inline]
165 #[cfg_attr(feature = "unstable", must_use)]
166 pub fn translate(&self, by: &TypedVector3D<T, U>) -> Self {
167 Self::new(self.min + *by, self.max + *by)
168 }
169}
170
171impl<T, U> TypedBox3D<T, U>
172where
173 T: Copy + PartialOrd + Zero,
174{
175 #[inline]
179 pub fn contains(&self, other: &TypedPoint3D<T, U>) -> bool {
180 self.min.x <= other.x && other.x < self.max.x
181 && self.min.y <= other.y && other.y < self.max.y
182 && self.min.z <= other.z && other.z < self.max.z
183 }
184}
185
186impl<T, U> TypedBox3D<T, U>
187where
188 T: Copy + PartialOrd + Zero + Sub<T, Output = T>,
189{
190 #[inline]
194 pub fn contains_box(&self, other: &Self) -> bool {
195 other.is_empty_or_negative()
196 || (self.min.x <= other.min.x && other.max.x <= self.max.x
197 && self.min.y <= other.min.y && other.max.y <= self.max.y
198 && self.min.z <= other.min.z && other.max.z <= self.max.z)
199 }
200}
201
202impl<T, U> TypedBox3D<T, U>
203where
204 T: Copy + Sub<T, Output = T>,
205{
206 #[inline]
207 pub fn size(&self)-> TypedSize3D<T, U> {
208 TypedSize3D::new(
209 self.max.x - self.min.x,
210 self.max.y - self.min.y,
211 self.max.z - self.min.z,
212 )
213 }
214}
215
216impl<T, U> TypedBox3D<T, U>
217where
218 T: Copy + PartialEq + Add<T, Output = T> + Sub<T, Output = T>,
219{
220 #[inline]
222 #[cfg_attr(feature = "unstable", must_use)]
223 pub fn inflate(&self, width: T, height: T, depth: T) -> Self {
224 TypedBox3D::new(
225 TypedPoint3D::new(self.min.x - width, self.min.y - height, self.min.z - depth),
226 TypedPoint3D::new(self.max.x + width, self.max.y + height, self.max.z + depth),
227 )
228 }
229
230 #[inline]
231 #[cfg_attr(feature = "unstable", must_use)]
232 pub fn inflate_typed(&self, width: Length<T, U>, height: Length<T, U>, depth: Length<T, U>) -> Self {
233 self.inflate(width.get(), height.get(), depth.get())
234 }
235}
236
237impl<T, U> TypedBox3D<T, U>
238where
239 T: Copy + Zero + PartialOrd,
240{
241 pub fn from_points<I>(points: I) -> Self
243 where
244 I: IntoIterator,
245 I::Item: Borrow<TypedPoint3D<T, U>>,
246 {
247 let mut points = points.into_iter();
248
249 let (mut min_x, mut min_y, mut min_z) = match points.next() {
251 Some(first) => (first.borrow().x, first.borrow().y, first.borrow().z),
252 None => return TypedBox3D::zero(),
253 };
254 let (mut max_x, mut max_y, mut max_z) = (min_x, min_y, min_z);
255
256 {
257 let mut assign_min_max = |point: I::Item| {
258 let p = point.borrow();
259 if p.x < min_x {
260 min_x = p.x
261 }
262 if p.x > max_x {
263 max_x = p.x
264 }
265 if p.y < min_y {
266 min_y = p.y
267 }
268 if p.y > max_y {
269 max_y = p.y
270 }
271 if p.z < min_z {
272 min_z = p.z
273 }
274 if p.z > max_z {
275 max_z = p.z
276 }
277 };
278
279 match points.next() {
280 Some(second) => assign_min_max(second),
281 None => return TypedBox3D::zero(),
282 }
283
284 for point in points {
285 assign_min_max(point);
286 }
287 }
288
289 Self::new(TypedPoint3D::new(min_x, min_y, min_z), TypedPoint3D::new(max_x, max_y, max_z))
290 }
291}
292
293impl<T, U> TypedBox3D<T, U>
294where
295 T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
296{
297 #[inline]
301 pub fn lerp(&self, other: Self, t: T) -> Self {
302 Self::new(
303 self.min.lerp(other.min, t),
304 self.max.lerp(other.max, t),
305 )
306 }
307}
308
309impl<T, U> TypedBox3D<T, U>
310where
311 T: Copy + One + Add<Output = T> + Div<Output = T>,
312{
313 pub fn center(&self) -> TypedPoint3D<T, U> {
314 let two = T::one() + T::one();
315 (self.min + self.max.to_vector()) / two
316 }
317}
318
319impl<T, U> TypedBox3D<T, U>
320where
321 T: Copy + Clone + PartialOrd + Add<T, Output = T> + Sub<T, Output = T> + Zero,
322{
323 #[inline]
324 pub fn union(&self, other: &Self) -> Self {
325 TypedBox3D::new(
326 TypedPoint3D::new(
327 min(self.min.x, other.min.x),
328 min(self.min.y, other.min.y),
329 min(self.min.z, other.min.z),
330 ),
331 TypedPoint3D::new(
332 max(self.max.x, other.max.x),
333 max(self.max.y, other.max.y),
334 max(self.max.z, other.max.z),
335 ),
336 )
337 }
338}
339
340impl<T, U> TypedBox3D<T, U>
341where
342 T: Copy,
343{
344 #[inline]
345 pub fn scale<S: Copy>(&self, x: S, y: S, z: S) -> Self
346 where
347 T: Mul<S, Output = T>
348 {
349 TypedBox3D::new(
350 TypedPoint3D::new(self.min.x * x, self.min.y * y, self.min.z * z),
351 TypedPoint3D::new(self.max.x * x, self.max.y * y, self.max.z * z),
352 )
353 }
354}
355
356impl<T, U> TypedBox3D<T, U>
357where
358 T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
359{
360 #[inline]
361 pub fn volume(&self) -> T {
362 let size = self.size();
363 size.width * size.height * size.depth
364 }
365
366 #[inline]
367 pub fn xy_area(&self) -> T {
368 let size = self.size();
369 size.width * size.height
370 }
371
372 #[inline]
373 pub fn yz_area(&self) -> T {
374 let size = self.size();
375 size.depth * size.height
376 }
377
378 #[inline]
379 pub fn xz_area(&self) -> T {
380 let size = self.size();
381 size.depth * size.width
382 }
383}
384
385impl<T, U> TypedBox3D<T, U>
386where
387 T: Copy + Zero,
388{
389 pub fn zero() -> Self {
391 TypedBox3D::new(TypedPoint3D::zero(), TypedPoint3D::zero())
392 }
393}
394
395impl<T, U> TypedBox3D<T, U>
396where
397 T: PartialEq,
398{
399 #[inline]
401 pub fn is_empty(&self) -> bool {
402 self.min.x == self.max.x || self.min.y == self.max.y || self.min.z == self.max.z
403 }
404}
405
406impl<T, U> Mul<T> for TypedBox3D<T, U>
407where
408 T: Copy + Mul<T, Output = T>,
409{
410 type Output = Self;
411 #[inline]
412 fn mul(self, scale: T) -> Self {
413 TypedBox3D::new(self.min * scale, self.max * scale)
414 }
415}
416
417impl<T, U> Div<T> for TypedBox3D<T, U>
418where
419 T: Copy + Div<T, Output = T>,
420{
421 type Output = Self;
422 #[inline]
423 fn div(self, scale: T) -> Self {
424 TypedBox3D::new(self.min / scale, self.max / scale)
425 }
426}
427
428impl<T, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedBox3D<T, U1>
429where
430 T: Copy + Mul<T, Output = T>,
431{
432 type Output = TypedBox3D<T, U2>;
433 #[inline]
434 fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedBox3D<T, U2> {
435 TypedBox3D::new(self.min * scale, self.max * scale)
436 }
437}
438
439impl<T, U1, U2> Div<TypedScale<T, U1, U2>> for TypedBox3D<T, U2>
440where
441 T: Copy + Div<T, Output = T>,
442{
443 type Output = TypedBox3D<T, U1>;
444 #[inline]
445 fn div(self, scale: TypedScale<T, U1, U2>) -> TypedBox3D<T, U1> {
446 TypedBox3D::new(self.min / scale, self.max / scale)
447 }
448}
449
450impl<T, Unit> TypedBox3D<T, Unit>
451where
452 T: Copy,
453{
454 pub fn to_untyped(&self) -> Box3D<T> {
456 TypedBox3D::new(self.min.to_untyped(), self.max.to_untyped())
457 }
458
459 pub fn from_untyped(c: &Box3D<T>) -> TypedBox3D<T, Unit> {
461 TypedBox3D::new(
462 TypedPoint3D::from_untyped(&c.min),
463 TypedPoint3D::from_untyped(&c.max),
464 )
465 }
466}
467
468impl<T0, Unit> TypedBox3D<T0, Unit>
469where
470 T0: NumCast + Copy,
471{
472 pub fn cast<T1: NumCast + Copy>(&self) -> TypedBox3D<T1, Unit> {
478 TypedBox3D::new(
479 self.min.cast(),
480 self.max.cast(),
481 )
482 }
483
484 pub fn try_cast<T1: NumCast + Copy>(&self) -> Option<TypedBox3D<T1, Unit>> {
490 match (self.min.try_cast(), self.max.try_cast()) {
491 (Some(a), Some(b)) => Some(TypedBox3D::new(a, b)),
492 _ => None,
493 }
494 }
495}
496
497impl<T, U> TypedBox3D<T, U>
498where
499 T: Round,
500{
501 #[cfg_attr(feature = "unstable", must_use)]
511 pub fn round(&self) -> Self {
512 TypedBox3D::new(self.min.round(), self.max.round())
513 }
514}
515
516impl<T, U> TypedBox3D<T, U>
517where
518 T: Floor + Ceil,
519{
520 #[cfg_attr(feature = "unstable", must_use)]
523 pub fn round_in(&self) -> Self {
524 TypedBox3D {
525 min: self.min.ceil(),
526 max: self.max.floor(),
527 }
528 }
529
530 #[cfg_attr(feature = "unstable", must_use)]
533 pub fn round_out(&self) -> Self {
534 TypedBox3D {
535 min: self.min.floor(),
536 max: self.max.ceil(),
537 }
538 }
539}
540
541impl<T: NumCast + Copy, Unit> TypedBox3D<T, Unit> {
543 pub fn to_f32(&self) -> TypedBox3D<f32, Unit> {
545 self.cast()
546 }
547
548 pub fn to_f64(&self) -> TypedBox3D<f64, Unit> {
550 self.cast()
551 }
552
553 pub fn to_usize(&self) -> TypedBox3D<usize, Unit> {
559 self.cast()
560 }
561
562 pub fn to_u32(&self) -> TypedBox3D<u32, Unit> {
568 self.cast()
569 }
570
571 pub fn to_i32(&self) -> TypedBox3D<i32, Unit> {
577 self.cast()
578 }
579
580 pub fn to_i64(&self) -> TypedBox3D<i64, Unit> {
586 self.cast()
587 }
588}
589
590impl<T, U> From<TypedSize3D<T, U>> for TypedBox3D<T, U>
591where
592 T: Copy + Zero + PartialOrd,
593{
594 fn from(b: TypedSize3D<T, U>) -> Self {
595 Self::from_size(b)
596 }
597}
598
599pub fn box3d<T: Copy, U>(min_x: T, min_y: T, min_z: T, max_x: T, max_y: T, max_z: T) -> TypedBox3D<T, U> {
601 TypedBox3D::new(TypedPoint3D::new(min_x, min_y, min_z), TypedPoint3D::new(max_x, max_y, max_z))
602}
603
604#[cfg(test)]
605mod tests {
606 use vector::vec3;
607 use size::size3;
608 use point::{point3, Point3D};
609 use super::*;
610
611 #[test]
612 fn test_new() {
613 let b = Box3D::new(point3(-1.0, -1.0, -1.0), point3(1.0, 1.0, 1.0));
614 assert!(b.min.x == -1.0);
615 assert!(b.min.y == -1.0);
616 assert!(b.min.z == -1.0);
617 assert!(b.max.x == 1.0);
618 assert!(b.max.y == 1.0);
619 assert!(b.max.z == 1.0);
620 }
621
622 #[test]
623 fn test_size() {
624 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
625 assert!(b.size().width == 20.0);
626 assert!(b.size().height == 20.0);
627 assert!(b.size().depth == 20.0);
628 }
629
630 #[test]
631 fn test_center() {
632 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
633 assert!(b.center() == Point3D::zero());
634 }
635
636 #[test]
637 fn test_volume() {
638 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
639 assert!(b.volume() == 8000.0);
640 }
641
642 #[test]
643 fn test_area() {
644 let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
645 assert!(b.xy_area() == 400.0);
646 assert!(b.yz_area() == 400.0);
647 assert!(b.xz_area() == 400.0);
648 }
649
650 #[test]
651 fn test_from_points() {
652 let b = Box3D::from_points(&[point3(50.0, 160.0, 12.5), point3(100.0, 25.0, 200.0)]);
653 assert!(b.min == point3(50.0, 25.0, 12.5));
654 assert!(b.max == point3(100.0, 160.0, 200.0));
655 }
656
657 #[test]
658 fn test_min_max() {
659 let b = Box3D::from_points(&[point3(50.0, 25.0, 12.5), point3(100.0, 160.0, 200.0)]);
660 assert!(b.min.x == 50.0);
661 assert!(b.min.y == 25.0);
662 assert!(b.min.z == 12.5);
663 assert!(b.max.x == 100.0);
664 assert!(b.max.y == 160.0);
665 assert!(b.max.z == 200.0);
666 }
667
668 #[test]
669 fn test_round_in() {
670 let b = Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round_in();
671 assert!(b.min.x == -25.0);
672 assert!(b.min.y == -40.0);
673 assert!(b.min.z == -70.0);
674 assert!(b.max.x == 60.0);
675 assert!(b.max.y == 36.0);
676 assert!(b.max.z == 89.0);
677 }
678
679 #[test]
680 fn test_round_out() {
681 let b = Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round_out();
682 assert!(b.min.x == -26.0);
683 assert!(b.min.y == -41.0);
684 assert!(b.min.z == -71.0);
685 assert!(b.max.x == 61.0);
686 assert!(b.max.y == 37.0);
687 assert!(b.max.z == 90.0);
688 }
689
690 #[test]
691 fn test_round() {
692 let b = Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round();
693 assert!(b.min.x == -26.0);
694 assert!(b.min.y == -40.0);
695 assert!(b.min.z == -71.0);
696 assert!(b.max.x == 60.0);
697 assert!(b.max.y == 37.0);
698 assert!(b.max.z == 90.0);
699 }
700
701 #[test]
702 fn test_from_size() {
703 let b = Box3D::from_size(size3(30.0, 40.0, 50.0));
704 assert!(b.min == Point3D::zero());
705 assert!(b.size().width == 30.0);
706 assert!(b.size().height == 40.0);
707 assert!(b.size().depth == 50.0);
708 }
709
710 #[test]
711 fn test_translate() {
712 let size = size3(15.0, 15.0, 200.0);
713 let mut center = (size / 2.0).to_vector().to_point();
714 let b = Box3D::from_size(size);
715 assert!(b.center() == center);
716 let translation = vec3(10.0, 2.5, 9.5);
717 let b = b.translate(&translation);
718 center += translation;
719 assert!(b.center() == center);
720 assert!(b.max.x == 25.0);
721 assert!(b.max.y == 17.5);
722 assert!(b.max.z == 209.5);
723 assert!(b.min.x == 10.0);
724 assert!(b.min.y == 2.5);
725 assert!(b.min.z == 9.5);
726 }
727
728 #[test]
729 fn test_union() {
730 let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(0.0, 20.0, 20.0)]);
731 let b2 = Box3D::from_points(&[point3(0.0, 20.0, 20.0), point3(20.0, -20.0, -20.0)]);
732 let b = b1.union(&b2);
733 assert!(b.max.x == 20.0);
734 assert!(b.max.y == 20.0);
735 assert!(b.max.z == 20.0);
736 assert!(b.min.x == -20.0);
737 assert!(b.min.y == -20.0);
738 assert!(b.min.z == -20.0);
739 assert!(b.volume() == (40.0 * 40.0 * 40.0));
740 }
741
742 #[test]
743 fn test_intersects() {
744 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
745 let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
746 assert!(b1.intersects(&b2));
747 }
748
749 #[test]
750 fn test_intersection() {
751 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
752 let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
753 let b = b1.intersection(&b2);
754 assert!(b.max.x == 10.0);
755 assert!(b.max.y == 20.0);
756 assert!(b.max.z == 20.0);
757 assert!(b.min.x == -10.0);
758 assert!(b.min.y == -20.0);
759 assert!(b.min.z == -20.0);
760 assert!(b.volume() == (20.0 * 40.0 * 40.0));
761 }
762
763 #[test]
764 fn test_try_intersection() {
765 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
766 let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
767 assert!(b1.try_intersection(&b2).is_some());
768
769 let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(-10.0, 20.0, 20.0)]);
770 let b2 = Box3D::from_points(&[point3(10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
771 assert!(b1.try_intersection(&b2).is_none());
772 }
773
774 #[test]
775 fn test_scale() {
776 let b = Box3D::from_points(&[point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0)]);
777 let b = b.scale(0.5, 0.5, 0.5);
778 assert!(b.max.x == 5.0);
779 assert!(b.max.y == 5.0);
780 assert!(b.max.z == 5.0);
781 assert!(b.min.x == -5.0);
782 assert!(b.min.y == -5.0);
783 assert!(b.min.z == -5.0);
784 }
785
786 #[test]
787 fn test_zero() {
788 let b = Box3D::<f64>::zero();
789 assert!(b.max.x == 0.0);
790 assert!(b.max.y == 0.0);
791 assert!(b.max.z == 0.0);
792 assert!(b.min.x == 0.0);
793 assert!(b.min.y == 0.0);
794 assert!(b.min.z == 0.0);
795 }
796
797 #[test]
798 fn test_lerp() {
799 let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(-10.0, -10.0, -10.0)]);
800 let b2 = Box3D::from_points(&[point3(10.0, 10.0, 10.0), point3(20.0, 20.0, 20.0)]);
801 let b = b1.lerp(b2, 0.5);
802 assert!(b.center() == Point3D::zero());
803 assert!(b.size().width == 10.0);
804 assert!(b.size().height == 10.0);
805 assert!(b.size().depth == 10.0);
806 }
807
808 #[test]
809 fn test_contains() {
810 let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
811 assert!(b.contains(&point3(-15.3, 10.5, 18.4)));
812 }
813
814 #[test]
815 fn test_contains_box() {
816 let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
817 let b2 = Box3D::from_points(&[point3(-14.3, -16.5, -19.3), point3(6.7, 17.6, 2.5)]);
818 assert!(b1.contains_box(&b2));
819 }
820
821 #[test]
822 fn test_inflate() {
823 let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
824 let b = b.inflate(10.0, 5.0, 2.0);
825 assert!(b.size().width == 60.0);
826 assert!(b.size().height == 50.0);
827 assert!(b.size().depth == 44.0);
828 assert!(b.center() == Point3D::zero());
829 }
830
831 #[test]
832 fn test_is_empty() {
833 for i in 0..3 {
834 let mut coords_neg = [-20.0, -20.0, -20.0];
835 let mut coords_pos = [20.0, 20.0, 20.0];
836 coords_neg[i] = 0.0;
837 coords_pos[i] = 0.0;
838 let b = Box3D::from_points(&[Point3D::from(coords_neg), Point3D::from(coords_pos)]);
839 assert!(b.is_empty());
840 }
841 }
842}