1use super::UnknownUnit;
11use crate::approxord::{max, min};
12use crate::num::*;
13use crate::point::{point2, Point2D};
14use crate::rect::Rect;
15use crate::scale::Scale;
16use crate::side_offsets::SideOffsets2D;
17use crate::size::Size2D;
18use crate::vector::{vec2, Vector2D};
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, DivAssign, Mul, MulAssign, Sub};
29
30#[repr(C)]
60#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
61#[cfg_attr(
62 feature = "serde",
63 serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
64)]
65pub struct Box2D<T, U> {
66 pub min: Point2D<T, U>,
67 pub max: Point2D<T, U>,
68}
69
70impl<T: Hash, U> Hash for Box2D<T, U> {
71 fn hash<H: Hasher>(&self, h: &mut H) {
72 self.min.hash(h);
73 self.max.hash(h);
74 }
75}
76
77impl<T: Copy, U> Copy for Box2D<T, U> {}
78
79impl<T: Clone, U> Clone for Box2D<T, U> {
80 fn clone(&self) -> Self {
81 Self::new(self.min.clone(), self.max.clone())
82 }
83}
84
85impl<T: PartialEq, U> PartialEq for Box2D<T, U> {
86 fn eq(&self, other: &Self) -> bool {
87 self.min.eq(&other.min) && self.max.eq(&other.max)
88 }
89}
90
91impl<T: Eq, U> Eq for Box2D<T, U> {}
92
93impl<T: fmt::Debug, U> fmt::Debug for Box2D<T, U> {
94 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95 f.debug_tuple("Box2D")
96 .field(&self.min)
97 .field(&self.max)
98 .finish()
99 }
100}
101
102impl<T, U> Box2D<T, U> {
103 #[inline]
105 pub const fn new(min: Point2D<T, U>, max: Point2D<T, U>) -> Self {
106 Box2D { min, max }
107 }
108}
109
110impl<T, U> Box2D<T, U>
111where
112 T: PartialOrd,
113{
114 #[inline]
119 pub fn is_negative(&self) -> bool {
120 self.max.x < self.min.x || self.max.y < self.min.y
121 }
122
123 #[inline]
125 pub fn is_empty(&self) -> bool {
126 !(self.max.x > self.min.x && self.max.y > self.min.y)
127 }
128
129 #[inline]
131 pub fn intersects(&self, other: &Self) -> bool {
132 self.min.x < other.max.x
133 && self.max.x > other.min.x
134 && self.min.y < other.max.y
135 && self.max.y > other.min.y
136 }
137
138 #[inline]
142 pub fn contains(&self, p: Point2D<T, U>) -> bool {
143 self.min.x <= p.x && p.x < self.max.x && self.min.y <= p.y && p.y < self.max.y
144 }
145
146 #[inline]
150 pub fn contains_box(&self, other: &Self) -> bool {
151 other.is_empty()
152 || (self.min.x <= other.min.x
153 && other.max.x <= self.max.x
154 && self.min.y <= other.min.y
155 && other.max.y <= self.max.y)
156 }
157}
158
159impl<T, U> Box2D<T, U>
160where
161 T: Copy + PartialOrd,
162{
163 #[inline]
164 pub fn to_non_empty(&self) -> Option<Self> {
165 if self.is_empty() {
166 return None;
167 }
168
169 Some(*self)
170 }
171
172 #[inline]
174 pub fn intersection(&self, other: &Self) -> Option<Self> {
175 let b = self.intersection_unchecked(other);
176
177 if b.is_empty() {
178 return None;
179 }
180
181 Some(b)
182 }
183
184 #[inline]
191 pub fn intersection_unchecked(&self, other: &Self) -> Self {
192 Box2D {
193 min: point2(max(self.min.x, other.min.x), max(self.min.y, other.min.y)),
194 max: point2(min(self.max.x, other.max.x), min(self.max.y, other.max.y)),
195 }
196 }
197
198 #[inline]
199 pub fn union(&self, other: &Self) -> Self {
200 Box2D {
201 min: point2(min(self.min.x, other.min.x), min(self.min.y, other.min.y)),
202 max: point2(max(self.max.x, other.max.x), max(self.max.y, other.max.y)),
203 }
204 }
205}
206
207impl<T, U> Box2D<T, U>
208where
209 T: Copy + Add<T, Output = T>,
210{
211 #[inline]
213 pub fn translate(&self, by: Vector2D<T, U>) -> Self {
214 Box2D {
215 min: self.min + by,
216 max: self.max + by,
217 }
218 }
219}
220
221impl<T, U> Box2D<T, U>
222where
223 T: Copy + Sub<T, Output = T>,
224{
225 #[inline]
226 pub fn size(&self) -> Size2D<T, U> {
227 (self.max - self.min).to_size()
228 }
229
230 #[inline]
231 pub fn width(&self) -> T {
232 self.max.x - self.min.x
233 }
234
235 #[inline]
236 pub fn height(&self) -> T {
237 self.max.y - self.min.y
238 }
239
240 #[inline]
241 pub fn to_rect(&self) -> Rect<T, U> {
242 Rect {
243 origin: self.min,
244 size: self.size(),
245 }
246 }
247}
248
249impl<T, U> Box2D<T, U>
250where
251 T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
252{
253 #[inline]
255 #[must_use]
256 pub fn inflate(&self, width: T, height: T) -> Self {
257 Box2D {
258 min: point2(self.min.x - width, self.min.y - height),
259 max: point2(self.max.x + width, self.max.y + height),
260 }
261 }
262
263 pub fn inner_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
268 Box2D {
269 min: self.min + vec2(offsets.left, offsets.top),
270 max: self.max - vec2(offsets.right, offsets.bottom),
271 }
272 }
273
274 pub fn outer_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
278 Box2D {
279 min: self.min - vec2(offsets.left, offsets.top),
280 max: self.max + vec2(offsets.right, offsets.bottom),
281 }
282 }
283}
284
285impl<T, U> Box2D<T, U>
286where
287 T: Copy + Zero + PartialOrd,
288{
289 #[inline]
291 pub fn from_size(size: Size2D<T, U>) -> Self {
292 let zero = Point2D::zero();
293 let point = size.to_vector().to_point();
294 Box2D::from_points(&[zero, point])
295 }
296
297 pub fn from_points<I>(points: I) -> Self
299 where
300 I: IntoIterator,
301 I::Item: Borrow<Point2D<T, U>>,
302 {
303 let mut points = points.into_iter();
304
305 let (mut min_x, mut min_y) = match points.next() {
306 Some(first) => first.borrow().to_tuple(),
307 None => return Box2D::zero(),
308 };
309
310 let (mut max_x, mut max_y) = (min_x, min_y);
311 for point in points {
312 let p = point.borrow();
313 if p.x < min_x {
314 min_x = p.x
315 }
316 if p.x > max_x {
317 max_x = p.x
318 }
319 if p.y < min_y {
320 min_y = p.y
321 }
322 if p.y > max_y {
323 max_y = p.y
324 }
325 }
326
327 Box2D {
328 min: point2(min_x, min_y),
329 max: point2(max_x, max_y),
330 }
331 }
332}
333
334impl<T, U> Box2D<T, U>
335where
336 T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
337{
338 #[inline]
340 pub fn lerp(&self, other: Self, t: T) -> Self {
341 Self::new(self.min.lerp(other.min, t), self.max.lerp(other.max, t))
342 }
343}
344
345impl<T, U> Box2D<T, U>
346where
347 T: Copy + One + Add<Output = T> + Div<Output = T>,
348{
349 pub fn center(&self) -> Point2D<T, U> {
350 let two = T::one() + T::one();
351 (self.min + self.max.to_vector()) / two
352 }
353}
354
355impl<T, U> Box2D<T, U>
356where
357 T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
358{
359 #[inline]
360 pub fn area(&self) -> T {
361 let size = self.size();
362 size.width * size.height
363 }
364}
365
366impl<T, U> Box2D<T, U>
367where
368 T: Zero,
369{
370 pub fn zero() -> Self {
372 Box2D::new(Point2D::zero(), Point2D::zero())
373 }
374}
375
376impl<T: Copy + Mul, U> Mul<T> for Box2D<T, U> {
377 type Output = Box2D<T::Output, U>;
378
379 #[inline]
380 fn mul(self, scale: T) -> Self::Output {
381 Box2D::new(self.min * scale, self.max * scale)
382 }
383}
384
385impl<T: Copy + MulAssign, U> MulAssign<T> for Box2D<T, U> {
386 #[inline]
387 fn mul_assign(&mut self, scale: T) {
388 *self *= Scale::new(scale);
389 }
390}
391
392impl<T: Copy + Div, U> Div<T> for Box2D<T, U> {
393 type Output = Box2D<T::Output, U>;
394
395 #[inline]
396 fn div(self, scale: T) -> Self::Output {
397 Box2D::new(self.min / scale, self.max / scale)
398 }
399}
400
401impl<T: Copy + DivAssign, U> DivAssign<T> for Box2D<T, U> {
402 #[inline]
403 fn div_assign(&mut self, scale: T) {
404 *self /= Scale::new(scale);
405 }
406}
407
408impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box2D<T, U1> {
409 type Output = Box2D<T::Output, U2>;
410
411 #[inline]
412 fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
413 Box2D::new(self.min * scale, self.max * scale)
414 }
415}
416
417impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Box2D<T, U> {
418 #[inline]
419 fn mul_assign(&mut self, scale: Scale<T, U, U>) {
420 self.min *= scale;
421 self.max *= scale;
422 }
423}
424
425impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Box2D<T, U2> {
426 type Output = Box2D<T::Output, U1>;
427
428 #[inline]
429 fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
430 Box2D::new(self.min / scale, self.max / scale)
431 }
432}
433
434impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Box2D<T, U> {
435 #[inline]
436 fn div_assign(&mut self, scale: Scale<T, U, U>) {
437 self.min /= scale;
438 self.max /= scale;
439 }
440}
441
442impl<T, U> Box2D<T, U>
443where
444 T: Copy,
445{
446 #[inline]
448 pub fn to_untyped(&self) -> Box2D<T, UnknownUnit> {
449 Box2D::new(self.min.to_untyped(), self.max.to_untyped())
450 }
451
452 #[inline]
454 pub fn from_untyped(c: &Box2D<T, UnknownUnit>) -> Box2D<T, U> {
455 Box2D::new(Point2D::from_untyped(c.min), Point2D::from_untyped(c.max))
456 }
457
458 #[inline]
460 pub fn cast_unit<V>(&self) -> Box2D<T, V> {
461 Box2D::new(self.min.cast_unit(), self.max.cast_unit())
462 }
463
464 #[inline]
465 pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
466 where
467 T: Mul<S, Output = T>,
468 {
469 Box2D {
470 min: point2(self.min.x * x, self.min.y * y),
471 max: point2(self.max.x * x, self.max.y * y),
472 }
473 }
474}
475
476impl<T: NumCast + Copy, U> Box2D<T, U> {
477 #[inline]
483 pub fn cast<NewT: NumCast>(&self) -> Box2D<NewT, U> {
484 Box2D::new(self.min.cast(), self.max.cast())
485 }
486
487 pub fn try_cast<NewT: NumCast>(&self) -> Option<Box2D<NewT, U>> {
493 match (self.min.try_cast(), self.max.try_cast()) {
494 (Some(a), Some(b)) => Some(Box2D::new(a, b)),
495 _ => None,
496 }
497 }
498
499 #[inline]
503 pub fn to_f32(&self) -> Box2D<f32, U> {
504 self.cast()
505 }
506
507 #[inline]
509 pub fn to_f64(&self) -> Box2D<f64, U> {
510 self.cast()
511 }
512
513 #[inline]
519 pub fn to_usize(&self) -> Box2D<usize, U> {
520 self.cast()
521 }
522
523 #[inline]
529 pub fn to_u32(&self) -> Box2D<u32, U> {
530 self.cast()
531 }
532
533 #[inline]
539 pub fn to_i32(&self) -> Box2D<i32, U> {
540 self.cast()
541 }
542
543 #[inline]
549 pub fn to_i64(&self) -> Box2D<i64, U> {
550 self.cast()
551 }
552}
553
554impl<T, U> Box2D<T, U>
555where
556 T: Round,
557{
558 #[must_use]
568 pub fn round(&self) -> Self {
569 Box2D::new(self.min.round(), self.max.round())
570 }
571}
572
573impl<T, U> Box2D<T, U>
574where
575 T: Floor + Ceil,
576{
577 #[must_use]
580 pub fn round_in(&self) -> Self {
581 let min = self.min.ceil();
582 let max = self.max.floor();
583 Box2D { min, max }
584 }
585
586 #[must_use]
589 pub fn round_out(&self) -> Self {
590 let min = self.min.floor();
591 let max = self.max.ceil();
592 Box2D { min, max }
593 }
594}
595
596impl<T, U> From<Size2D<T, U>> for Box2D<T, U>
597where
598 T: Copy + Zero + PartialOrd,
599{
600 fn from(b: Size2D<T, U>) -> Self {
601 Self::from_size(b)
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use crate::default::Box2D;
608 use crate::side_offsets::SideOffsets2D;
609 use crate::{point2, size2, vec2, Point2D};
610 #[test]
613 fn test_size() {
614 let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
615 assert_eq!(b.size().width, 20.0);
616 assert_eq!(b.size().height, 20.0);
617 }
618
619 #[test]
620 fn test_width_height() {
621 let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
622 assert!(b.width() == 20.0);
623 assert!(b.height() == 20.0);
624 }
625
626 #[test]
627 fn test_center() {
628 let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
629 assert_eq!(b.center(), Point2D::zero());
630 }
631
632 #[test]
633 fn test_area() {
634 let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
635 assert_eq!(b.area(), 400.0);
636 }
637
638 #[test]
639 fn test_from_points() {
640 let b = Box2D::from_points(&[point2(50.0, 160.0), point2(100.0, 25.0)]);
641 assert_eq!(b.min, point2(50.0, 25.0));
642 assert_eq!(b.max, point2(100.0, 160.0));
643 }
644
645 #[test]
646 fn test_round_in() {
647 let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_in();
648 assert_eq!(b.min.x, -25.0);
649 assert_eq!(b.min.y, -40.0);
650 assert_eq!(b.max.x, 60.0);
651 assert_eq!(b.max.y, 36.0);
652 }
653
654 #[test]
655 fn test_round_out() {
656 let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_out();
657 assert_eq!(b.min.x, -26.0);
658 assert_eq!(b.min.y, -41.0);
659 assert_eq!(b.max.x, 61.0);
660 assert_eq!(b.max.y, 37.0);
661 }
662
663 #[test]
664 fn test_round() {
665 let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round();
666 assert_eq!(b.min.x, -25.0);
667 assert_eq!(b.min.y, -40.0);
668 assert_eq!(b.max.x, 60.0);
669 assert_eq!(b.max.y, 37.0);
670 }
671
672 #[test]
673 fn test_from_size() {
674 let b = Box2D::from_size(size2(30.0, 40.0));
675 assert!(b.min == Point2D::zero());
676 assert!(b.size().width == 30.0);
677 assert!(b.size().height == 40.0);
678 }
679
680 #[test]
681 fn test_inner_box() {
682 let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
683 let b = b.inner_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
684 assert_eq!(b.max.x, 80.0);
685 assert_eq!(b.max.y, 155.0);
686 assert_eq!(b.min.x, 60.0);
687 assert_eq!(b.min.y, 35.0);
688 }
689
690 #[test]
691 fn test_outer_box() {
692 let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
693 let b = b.outer_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
694 assert_eq!(b.max.x, 120.0);
695 assert_eq!(b.max.y, 165.0);
696 assert_eq!(b.min.x, 40.0);
697 assert_eq!(b.min.y, 15.0);
698 }
699
700 #[test]
701 fn test_translate() {
702 let size = size2(15.0, 15.0);
703 let mut center = (size / 2.0).to_vector().to_point();
704 let b = Box2D::from_size(size);
705 assert_eq!(b.center(), center);
706 let translation = vec2(10.0, 2.5);
707 let b = b.translate(translation);
708 center += translation;
709 assert_eq!(b.center(), center);
710 assert_eq!(b.max.x, 25.0);
711 assert_eq!(b.max.y, 17.5);
712 assert_eq!(b.min.x, 10.0);
713 assert_eq!(b.min.y, 2.5);
714 }
715
716 #[test]
717 fn test_union() {
718 let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(0.0, 20.0)]);
719 let b2 = Box2D::from_points(&[point2(0.0, 20.0), point2(20.0, -20.0)]);
720 let b = b1.union(&b2);
721 assert_eq!(b.max.x, 20.0);
722 assert_eq!(b.max.y, 20.0);
723 assert_eq!(b.min.x, -20.0);
724 assert_eq!(b.min.y, -20.0);
725 }
726
727 #[test]
728 fn test_intersects() {
729 let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
730 let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
731 assert!(b1.intersects(&b2));
732 }
733
734 #[test]
735 fn test_intersection_unchecked() {
736 let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
737 let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
738 let b = b1.intersection_unchecked(&b2);
739 assert_eq!(b.max.x, 10.0);
740 assert_eq!(b.max.y, 20.0);
741 assert_eq!(b.min.x, -10.0);
742 assert_eq!(b.min.y, -20.0);
743 }
744
745 #[test]
746 fn test_intersection() {
747 let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
748 let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
749 assert!(b1.intersection(&b2).is_some());
750
751 let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(-10.0, 20.0)]);
752 let b2 = Box2D::from_points(&[point2(10.0, 20.0), point2(15.0, -20.0)]);
753 assert!(b1.intersection(&b2).is_none());
754 }
755
756 #[test]
757 fn test_scale() {
758 let b = Box2D::from_points(&[point2(-10.0, -10.0), point2(10.0, 10.0)]);
759 let b = b.scale(0.5, 0.5);
760 assert_eq!(b.max.x, 5.0);
761 assert_eq!(b.max.y, 5.0);
762 assert_eq!(b.min.x, -5.0);
763 assert_eq!(b.min.y, -5.0);
764 }
765
766 #[test]
767 fn test_lerp() {
768 let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(-10.0, -10.0)]);
769 let b2 = Box2D::from_points(&[point2(10.0, 10.0), point2(20.0, 20.0)]);
770 let b = b1.lerp(b2, 0.5);
771 assert_eq!(b.center(), Point2D::zero());
772 assert_eq!(b.size().width, 10.0);
773 assert_eq!(b.size().height, 10.0);
774 }
775
776 #[test]
777 fn test_contains() {
778 let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
779 assert!(b.contains(point2(-15.3, 10.5)));
780 }
781
782 #[test]
783 fn test_contains_box() {
784 let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
785 let b2 = Box2D::from_points(&[point2(-14.3, -16.5), point2(6.7, 17.6)]);
786 assert!(b1.contains_box(&b2));
787 }
788
789 #[test]
790 fn test_inflate() {
791 let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
792 let b = b.inflate(10.0, 5.0);
793 assert_eq!(b.size().width, 60.0);
794 assert_eq!(b.size().height, 50.0);
795 assert_eq!(b.center(), Point2D::zero());
796 }
797
798 #[test]
799 fn test_is_empty() {
800 for i in 0..2 {
801 let mut coords_neg = [-20.0, -20.0];
802 let mut coords_pos = [20.0, 20.0];
803 coords_neg[i] = 0.0;
804 coords_pos[i] = 0.0;
805 let b = Box2D::from_points(&[Point2D::from(coords_neg), Point2D::from(coords_pos)]);
806 assert!(b.is_empty());
807 }
808 }
809
810 #[test]
811 fn test_nan_empty() {
812 use std::f32::NAN;
813 assert!(Box2D { min: point2(NAN, 2.0), max: point2(1.0, 3.0) }.is_empty());
814 assert!(Box2D { min: point2(0.0, NAN), max: point2(1.0, 2.0) }.is_empty());
815 assert!(Box2D { min: point2(1.0, -2.0), max: point2(NAN, 2.0) }.is_empty());
816 assert!(Box2D { min: point2(1.0, -2.0), max: point2(0.0, NAN) }.is_empty());
817 }
818}