1use super::UnknownUnit;
11use scale::TypedScale;
12use num::*;
13use rect::TypedRect;
14use point::{point2, TypedPoint2D};
15use vector::{vec2, TypedVector2D};
16use side_offsets::TypedSideOffsets2D;
17use size::TypedSize2D;
18use approxord::{min, max};
19
20use num_traits::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, Mul, Sub};
29
30
31#[repr(C)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[cfg_attr(feature = "serde", serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>")))]
35pub struct TypedBox2D<T, U> {
36 pub min: TypedPoint2D<T, U>,
37 pub max: TypedPoint2D<T, U>,
38}
39
40pub type Box2D<T> = TypedBox2D<T, UnknownUnit>;
42
43impl<T: Hash, U> Hash for TypedBox2D<T, U> {
44 fn hash<H: Hasher>(&self, h: &mut H) {
45 self.min.hash(h);
46 self.max.hash(h);
47 }
48}
49
50impl<T: Copy, U> Copy for TypedBox2D<T, U> {}
51
52impl<T: Copy, U> Clone for TypedBox2D<T, U> {
53 fn clone(&self) -> Self {
54 *self
55 }
56}
57
58impl<T: PartialEq, U> PartialEq<TypedBox2D<T, U>> for TypedBox2D<T, U> {
59 fn eq(&self, other: &Self) -> bool {
60 self.min.eq(&other.min) && self.max.eq(&other.max)
61 }
62}
63
64impl<T: Eq, U> Eq for TypedBox2D<T, U> {}
65
66impl<T: fmt::Debug, U> fmt::Debug for TypedBox2D<T, U> {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 write!(f, "TypedBox2D({:?}, {:?})", self.min, self.max)
69 }
70}
71
72impl<T: fmt::Display, U> fmt::Display for TypedBox2D<T, U> {
73 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
74 write!(formatter, "Box2D({}, {})", self.min, self.max)
75 }
76}
77
78impl<T, U> TypedBox2D<T, U> {
79 pub fn new(min: TypedPoint2D<T, U>, max: TypedPoint2D<T, U>) -> Self {
81 TypedBox2D {
82 min,
83 max,
84 }
85 }
86}
87
88impl<T, U> TypedBox2D<T, U>
89where
90 T: Copy + Zero + PartialOrd,
91{
92 #[inline]
94 pub fn from_size(size: TypedSize2D<T, U>) -> Self {
95 let zero = TypedPoint2D::zero();
96 let point = size.to_vector().to_point();
97 TypedBox2D::from_points(&[zero, point])
98 }
99}
100
101impl<T, U> TypedBox2D<T, U>
102where
103 T: Copy + PartialOrd,
104{
105 #[inline]
110 pub fn is_negative(&self) -> bool {
111 self.max.x < self.min.x || self.max.y < self.min.y
112 }
113
114 #[inline]
116 pub fn is_empty_or_negative(&self) -> bool {
117 self.max.x <= self.min.x || self.max.y <= self.min.y
118 }
119
120 #[inline]
122 pub fn intersects(&self, other: &Self) -> bool {
123 self.min.x < other.max.x
124 && self.max.x > other.min.x
125 && self.min.y < other.max.y
126 && self.max.y > other.min.y
127 }
128
129 #[inline]
133 pub fn intersection(&self, other: &Self) -> Self {
134 TypedBox2D {
135 min: point2(
136 max(self.min.x, other.min.x),
137 max(self.min.y, other.min.y),
138 ),
139 max: point2(
140 min(self.max.x, other.max.x),
141 min(self.max.y, other.max.y),
142 )
143 }
144 }
145
146 #[inline]
148 pub fn try_intersection(&self, other: &Self) -> Option<Self> {
149 let intersection = self.intersection(other);
150
151 if intersection.is_negative() {
152 return None;
153 }
154
155 Some(intersection)
156 }
157}
158
159impl<T, U> TypedBox2D<T, U>
160where
161 T: Copy + Add<T, Output = T>,
162{
163 #[inline]
165 pub fn translate(&self, by: &TypedVector2D<T, U>) -> Self {
166 Self::new(self.min + *by, self.max + *by)
167 }
168}
169
170impl<T, U> TypedBox2D<T, U>
171where
172 T: Copy + PartialOrd + Zero,
173{
174 #[inline]
178 pub fn contains(&self, p: &TypedPoint2D<T, U>) -> bool {
179 self.min.x <= p.x && p.x < self.max.x
180 && self.min.y <= p.y && p.y < self.max.y
181 }
182}
183
184impl<T, U> TypedBox2D<T, U>
185where
186 T: Copy + PartialOrd + Zero + Sub<T, Output = T>,
187{
188 #[inline]
192 pub fn contains_box(&self, other: &Self) -> bool {
193 other.is_empty_or_negative()
194 || (self.min.x <= other.min.x && other.max.x <= self.max.x
195 && self.min.y <= other.min.y && other.max.y <= self.max.y)
196 }
197}
198
199impl<T, U> TypedBox2D<T, U>
200where
201 T: Copy + Sub<T, Output = T>,
202{
203 #[inline]
204 pub fn size(&self)-> TypedSize2D<T, U> {
205 (self.max - self.min).to_size()
206 }
207
208 #[inline]
209 pub fn to_rect(&self) -> TypedRect<T, U> {
210 TypedRect {
211 origin: self.min,
212 size: self.size(),
213 }
214 }
215}
216
217impl<T, U> TypedBox2D<T, U>
218where
219 T: Copy + PartialEq + Add<T, Output = T> + Sub<T, Output = T>,
220{
221 #[inline]
223 #[cfg_attr(feature = "unstable", must_use)]
224 pub fn inflate(&self, width: T, height: T) -> Self {
225 TypedBox2D {
226 min: point2(self.min.x - width, self.min.y - height),
227 max: point2(self.max.x + width, self.max.y + height),
228 }
229 }
230}
231
232impl<T, U> TypedBox2D<T, U>
233where
234 T: Copy + Zero + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
235{
236 pub fn inner_box(&self, offsets: TypedSideOffsets2D<T, U>) -> Self {
241 TypedBox2D {
242 min: self.min + vec2(offsets.left, offsets.top),
243 max: self.max - vec2(offsets.right, offsets.bottom),
244 }
245 }
246
247 pub fn outer_box(&self, offsets: TypedSideOffsets2D<T, U>) -> Self {
251 TypedBox2D {
252 min: self.min - vec2(offsets.left, offsets.top),
253 max: self.max + vec2(offsets.right, offsets.bottom),
254 }
255 }
256}
257
258
259impl<T, U> TypedBox2D<T, U>
260where
261 T: Copy + Zero + PartialOrd,
262{
263 pub fn from_points<I>(points: I) -> Self
265 where
266 I: IntoIterator,
267 I::Item: Borrow<TypedPoint2D<T, U>>,
268 {
269 let mut points = points.into_iter();
270
271 let (mut min_x, mut min_y) = match points.next() {
273 Some(first) => (first.borrow().x, first.borrow().y),
274 None => return TypedBox2D::zero(),
275 };
276 let (mut max_x, mut max_y) = (min_x, min_y);
277
278 {
279 let mut assign_min_max = |point: I::Item| {
280 let p = point.borrow();
281 if p.x < min_x {
282 min_x = p.x
283 }
284 if p.x > max_x {
285 max_x = p.x
286 }
287 if p.y < min_y {
288 min_y = p.y
289 }
290 if p.y > max_y {
291 max_y = p.y
292 }
293 };
294
295 match points.next() {
296 Some(second) => assign_min_max(second),
297 None => return TypedBox2D::zero(),
298 }
299
300 for point in points {
301 assign_min_max(point);
302 }
303 }
304
305 TypedBox2D {
306 min: point2(min_x, min_y),
307 max: point2(max_x, max_y),
308 }
309 }
310}
311
312impl<T, U> TypedBox2D<T, U>
313where
314 T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
315{
316 #[inline]
320 pub fn lerp(&self, other: Self, t: T) -> Self {
321 Self::new(
322 self.min.lerp(other.min, t),
323 self.max.lerp(other.max, t),
324 )
325 }
326}
327
328impl<T, U> TypedBox2D<T, U>
329where
330 T: Copy + One + Add<Output = T> + Div<Output = T>,
331{
332 pub fn center(&self) -> TypedPoint2D<T, U> {
333 let two = T::one() + T::one();
334 (self.min + self.max.to_vector()) / two
335 }
336}
337
338impl<T, U> TypedBox2D<T, U>
339where
340 T: Copy + PartialOrd,
341{
342 #[inline]
343 pub fn union(&self, other: &Self) -> Self {
344 TypedBox2D {
345 min: point2(
346 min(self.min.x, other.min.x),
347 min(self.min.y, other.min.y),
348 ),
349 max: point2(
350 max(self.max.x, other.max.x),
351 max(self.max.y, other.max.y),
352 ),
353 }
354 }
355}
356
357impl<T, U> TypedBox2D<T, U>
358where
359 T: Copy,
360{
361 #[inline]
362 pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
363 where
364 T: Mul<S, Output = T>
365 {
366 TypedBox2D {
367 min: point2(self.min.x * x, self.min.y * y),
368 max: point2(self.max.x * x, self.max.y * y),
369 }
370 }
371}
372
373impl<T, U> TypedBox2D<T, U>
374where
375 T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
376{
377 #[inline]
378 pub fn area(&self) -> T {
379 let size = self.size();
380 size.width * size.height
381 }
382}
383
384impl<T, U> TypedBox2D<T, U>
385where
386 T: Copy + Zero,
387{
388 pub fn zero() -> Self {
390 TypedBox2D::new(TypedPoint2D::zero(), TypedPoint2D::zero())
391 }
392}
393
394impl<T, U> TypedBox2D<T, U>
395where
396 T: PartialEq,
397{
398 #[inline]
400 pub fn is_empty(&self) -> bool {
401 self.min.x == self.max.x || self.min.y == self.max.y
402 }
403}
404
405impl<T, U> Mul<T> for TypedBox2D<T, U>
406where
407 T: Copy + Mul<T, Output = T>,
408{
409 type Output = Self;
410 #[inline]
411 fn mul(self, scale: T) -> Self {
412 TypedBox2D::new(self.min * scale, self.max * scale)
413 }
414}
415
416impl<T, U> Div<T> for TypedBox2D<T, U>
417where
418 T: Copy + Div<T, Output = T>,
419{
420 type Output = Self;
421 #[inline]
422 fn div(self, scale: T) -> Self {
423 TypedBox2D::new(self.min / scale, self.max / scale)
424 }
425}
426
427impl<T, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedBox2D<T, U1>
428where
429 T: Copy + Mul<T, Output = T>,
430{
431 type Output = TypedBox2D<T, U2>;
432 #[inline]
433 fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedBox2D<T, U2> {
434 TypedBox2D::new(self.min * scale, self.max * scale)
435 }
436}
437
438impl<T, U1, U2> Div<TypedScale<T, U1, U2>> for TypedBox2D<T, U2>
439where
440 T: Copy + Div<T, Output = T>,
441{
442 type Output = TypedBox2D<T, U1>;
443 #[inline]
444 fn div(self, scale: TypedScale<T, U1, U2>) -> TypedBox2D<T, U1> {
445 TypedBox2D::new(self.min / scale, self.max / scale)
446 }
447}
448
449impl<T, Unit> TypedBox2D<T, Unit>
450where
451 T: Copy,
452{
453 pub fn to_untyped(&self) -> Box2D<T> {
455 TypedBox2D::new(self.min.to_untyped(), self.max.to_untyped())
456 }
457
458 pub fn from_untyped(c: &Box2D<T>) -> TypedBox2D<T, Unit> {
460 TypedBox2D::new(
461 TypedPoint2D::from_untyped(&c.min),
462 TypedPoint2D::from_untyped(&c.max),
463 )
464 }
465}
466
467impl<T0, Unit> TypedBox2D<T0, Unit>
468where
469 T0: NumCast + Copy,
470{
471 pub fn cast<T1: NumCast + Copy>(&self) -> TypedBox2D<T1, Unit> {
477 TypedBox2D::new(
478 self.min.cast(),
479 self.max.cast(),
480 )
481 }
482
483 pub fn try_cast<T1: NumCast + Copy>(&self) -> Option<TypedBox2D<T1, Unit>> {
489 match (self.min.try_cast(), self.max.try_cast()) {
490 (Some(a), Some(b)) => Some(TypedBox2D::new(a, b)),
491 _ => None,
492 }
493 }
494}
495
496impl<T, U> TypedBox2D<T, U>
497where
498 T: Round,
499{
500 #[cfg_attr(feature = "unstable", must_use)]
510 pub fn round(&self) -> Self {
511 TypedBox2D::new(self.min.round(), self.max.round())
512 }
513}
514
515impl<T, U> TypedBox2D<T, U>
516where
517 T: Floor + Ceil,
518{
519 #[cfg_attr(feature = "unstable", must_use)]
522 pub fn round_in(&self) -> Self {
523 let min = self.min.ceil();
524 let max = self.max.floor();
525 TypedBox2D { min, max }
526 }
527
528 #[cfg_attr(feature = "unstable", must_use)]
531 pub fn round_out(&self) -> Self {
532 let min = self.min.floor();
533 let max = self.max.ceil();
534 TypedBox2D { min, max }
535 }
536}
537
538impl<T: NumCast + Copy, Unit> TypedBox2D<T, Unit> {
540 pub fn to_f32(&self) -> TypedBox2D<f32, Unit> {
542 self.cast()
543 }
544
545 pub fn to_f64(&self) -> TypedBox2D<f64, Unit> {
547 self.cast()
548 }
549
550 pub fn to_usize(&self) -> TypedBox2D<usize, Unit> {
556 self.cast()
557 }
558
559 pub fn to_u32(&self) -> TypedBox2D<u32, Unit> {
565 self.cast()
566 }
567
568 pub fn to_i32(&self) -> TypedBox2D<i32, Unit> {
574 self.cast()
575 }
576
577 pub fn to_i64(&self) -> TypedBox2D<i64, Unit> {
583 self.cast()
584 }
585}
586
587impl<T, U> From<TypedSize2D<T, U>> for TypedBox2D<T, U>
588where
589 T: Copy + Zero + PartialOrd,
590{
591 fn from(b: TypedSize2D<T, U>) -> Self {
592 Self::from_size(b)
593 }
594}
595
596#[cfg(test)]
597mod tests {
598 use side_offsets::SideOffsets2D;
599 use size::size2;
600 use point::Point2D;
601 use super::*;
602
603 #[test]
604 fn test_size() {
605 let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
606 assert_eq!(b.size().width, 20.0);
607 assert_eq!(b.size().height, 20.0);
608 }
609
610 #[test]
611 fn test_center() {
612 let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
613 assert_eq!(b.center(), Point2D::zero());
614 }
615
616 #[test]
617 fn test_area() {
618 let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
619 assert_eq!(b.area(), 400.0);
620 }
621
622 #[test]
623 fn test_from_points() {
624 let b = Box2D::from_points(&[point2(50.0, 160.0), point2(100.0, 25.0)]);
625 assert_eq!(b.min, point2(50.0, 25.0));
626 assert_eq!(b.max, point2(100.0, 160.0));
627 }
628
629 #[test]
630 fn test_round_in() {
631 let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_in();
632 assert_eq!(b.min.x, -25.0);
633 assert_eq!(b.min.y, -40.0);
634 assert_eq!(b.max.x, 60.0);
635 assert_eq!(b.max.y, 36.0);
636 }
637
638 #[test]
639 fn test_round_out() {
640 let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_out();
641 assert_eq!(b.min.x,-26.0);
642 assert_eq!(b.min.y, -41.0);
643 assert_eq!(b.max.x, 61.0);
644 assert_eq!(b.max.y, 37.0);
645 }
646
647 #[test]
648 fn test_round() {
649 let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round();
650 assert_eq!(b.min.x,-26.0);
651 assert_eq!(b.min.y, -40.0);
652 assert_eq!(b.max.x, 60.0);
653 assert_eq!(b.max.y, 37.0);
654 }
655
656 #[test]
657 fn test_from_size() {
658 let b = Box2D::from_size(size2(30.0, 40.0));
659 assert!(b.min == Point2D::zero());
660 assert!(b.size().width == 30.0);
661 assert!(b.size().height == 40.0);
662 }
663
664 #[test]
665 fn test_inner_box() {
666 let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
667 let b = b.inner_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
668 assert_eq!(b.max.x, 80.0);
669 assert_eq!(b.max.y, 155.0);
670 assert_eq!(b.min.x, 60.0);
671 assert_eq!(b.min.y, 35.0);
672 }
673
674 #[test]
675 fn test_outer_box() {
676 let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
677 let b = b.outer_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
678 assert_eq!(b.max.x, 120.0);
679 assert_eq!(b.max.y, 165.0);
680 assert_eq!(b.min.x, 40.0);
681 assert_eq!(b.min.y, 15.0);
682 }
683
684 #[test]
685 fn test_translate() {
686 let size = size2(15.0, 15.0);
687 let mut center = (size / 2.0).to_vector().to_point();
688 let b = Box2D::from_size(size);
689 assert_eq!(b.center(), center);
690 let translation = vec2(10.0, 2.5);
691 let b = b.translate(&translation);
692 center += translation;
693 assert_eq!(b.center(), center);
694 assert_eq!(b.max.x, 25.0);
695 assert_eq!(b.max.y, 17.5);
696 assert_eq!(b.min.x, 10.0);
697 assert_eq!(b.min.y, 2.5);
698 }
699
700 #[test]
701 fn test_union() {
702 let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(0.0, 20.0)]);
703 let b2 = Box2D::from_points(&[point2(0.0, 20.0), point2(20.0, -20.0)]);
704 let b = b1.union(&b2);
705 assert_eq!(b.max.x, 20.0);
706 assert_eq!(b.max.y, 20.0);
707 assert_eq!(b.min.x, -20.0);
708 assert_eq!(b.min.y, -20.0);
709 }
710
711 #[test]
712 fn test_intersects() {
713 let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
714 let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
715 assert!(b1.intersects(&b2));
716 }
717
718 #[test]
719 fn test_intersection() {
720 let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
721 let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
722 let b = b1.intersection(&b2);
723 assert_eq!(b.max.x, 10.0);
724 assert_eq!(b.max.y, 20.0);
725 assert_eq!(b.min.x, -10.0);
726 assert_eq!(b.min.y, -20.0);
727 }
728
729 #[test]
730 fn test_try_intersection() {
731 let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
732 let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
733 assert!(b1.try_intersection(&b2).is_some());
734
735 let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(-10.0, 20.0)]);
736 let b2 = Box2D::from_points(&[point2(10.0, 20.0), point2(15.0, -20.0)]);
737 assert!(b1.try_intersection(&b2).is_none());
738 }
739
740 #[test]
741 fn test_scale() {
742 let b = Box2D::from_points(&[point2(-10.0, -10.0), point2(10.0, 10.0)]);
743 let b = b.scale(0.5, 0.5);
744 assert_eq!(b.max.x, 5.0);
745 assert_eq!(b.max.y, 5.0);
746 assert_eq!(b.min.x, -5.0);
747 assert_eq!(b.min.y, -5.0);
748 }
749
750 #[test]
751 fn test_lerp() {
752 let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(-10.0, -10.0)]);
753 let b2 = Box2D::from_points(&[point2(10.0, 10.0), point2(20.0, 20.0)]);
754 let b = b1.lerp(b2, 0.5);
755 assert_eq!(b.center(), Point2D::zero());
756 assert_eq!(b.size().width, 10.0);
757 assert_eq!(b.size().height, 10.0);
758 }
759
760 #[test]
761 fn test_contains() {
762 let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
763 assert!(b.contains(&point2(-15.3, 10.5)));
764 }
765
766 #[test]
767 fn test_contains_box() {
768 let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
769 let b2 = Box2D::from_points(&[point2(-14.3, -16.5), point2(6.7, 17.6)]);
770 assert!(b1.contains_box(&b2));
771 }
772
773 #[test]
774 fn test_inflate() {
775 let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
776 let b = b.inflate(10.0, 5.0);
777 assert_eq!(b.size().width, 60.0);
778 assert_eq!(b.size().height, 50.0);
779 assert_eq!(b.center(), Point2D::zero());
780 }
781
782 #[test]
783 fn test_is_empty() {
784 for i in 0..2 {
785 let mut coords_neg = [-20.0, -20.0];
786 let mut coords_pos = [20.0, 20.0];
787 coords_neg[i] = 0.0;
788 coords_pos[i] = 0.0;
789 let b = Box2D::from_points(&[Point2D::from(coords_neg), Point2D::from(coords_pos)]);
790 assert!(b.is_empty());
791 }
792 }
793}