1 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9 
10 use super::UnknownUnit;
11 use scale::Scale;
12 use num::*;
13 use rect::Rect;
14 use point::{point2, Point2D};
15 use vector::{vec2, Vector2D};
16 use side_offsets::SideOffsets2D;
17 use size::Size2D;
18 use nonempty::NonEmpty;
19 use approxord::{min, max};
20 
21 use num_traits::NumCast;
22 #[cfg(feature = "serde")]
23 use serde::{Deserialize, Serialize};
24 
25 use core::borrow::Borrow;
26 use core::cmp::PartialOrd;
27 use core::fmt;
28 use core::hash::{Hash, Hasher};
29 use core::ops::{Add, Div, Mul, Sub};
30 
31 
32 /// An axis aligned rectangle represented by its minimum and maximum coordinates.
33 #[repr(C)]
34 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35 #[cfg_attr(feature = "serde", serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>")))]
36 pub struct Box2D<T, U> {
37     pub min: Point2D<T, U>,
38     pub max: Point2D<T, U>,
39 }
40 
41 impl<T: Hash, U> Hash for Box2D<T, U> {
hash<H: Hasher>(&self, h: &mut H)42     fn hash<H: Hasher>(&self, h: &mut H) {
43         self.min.hash(h);
44         self.max.hash(h);
45     }
46 }
47 
48 impl<T: Copy, U> Copy for Box2D<T, U> {}
49 
50 impl<T: Clone, U> Clone for Box2D<T, U> {
clone(&self) -> Self51     fn clone(&self) -> Self {
52         Self::new(self.min.clone(), self.max.clone())
53     }
54 }
55 
56 impl<T: PartialEq, U> PartialEq<Box2D<T, U>> for Box2D<T, U> {
eq(&self, other: &Self) -> bool57     fn eq(&self, other: &Self) -> bool {
58         self.min.eq(&other.min) && self.max.eq(&other.max)
59     }
60 }
61 
62 impl<T: Eq, U> Eq for Box2D<T, U> {}
63 
64 impl<T: fmt::Debug, U> fmt::Debug for Box2D<T, U> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result65     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66         f.debug_tuple("Box2D")
67             .field(&self.min)
68             .field(&self.max)
69             .finish()
70     }
71 }
72 
73 impl<T: fmt::Display, U> fmt::Display for Box2D<T, U> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result74     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75         write!(f, "Box2D(")?;
76         fmt::Display::fmt(&self.min, f)?;
77         write!(f, ", ")?;
78         fmt::Display::fmt(&self.max, f)?;
79         write!(f, ")")
80     }
81 }
82 
83 impl<T, U> Box2D<T, U> {
84     /// Constructor.
85     #[inline]
new(min: Point2D<T, U>, max: Point2D<T, U>) -> Self86     pub const fn new(min: Point2D<T, U>, max: Point2D<T, U>) -> Self {
87         Box2D {
88             min,
89             max,
90         }
91     }
92 }
93 
94 impl<T, U> Box2D<T, U>
95 where
96     T: Copy + Zero + PartialOrd,
97 {
98     /// Creates a Box2D of the given size, at offset zero.
99     #[inline]
from_size(size: Size2D<T, U>) -> Self100     pub fn from_size(size: Size2D<T, U>) -> Self {
101         let zero = Point2D::zero();
102         let point = size.to_vector().to_point();
103         Box2D::from_points(&[zero, point])
104     }
105 }
106 
107 impl<T, U> Box2D<T, U>
108 where
109     T: PartialOrd,
110 {
111     /// Returns true if the box has a negative area.
112     ///
113     /// The common interpretation for a negative box is to consider it empty. It can be obtained
114     /// by calculating the intersection of two boxes that do not intersect.
115     #[inline]
is_negative(&self) -> bool116     pub fn is_negative(&self) -> bool {
117         self.max.x < self.min.x || self.max.y < self.min.y
118     }
119 
120     /// Returns true if the size is zero or negative.
121     #[inline]
is_empty_or_negative(&self) -> bool122     pub fn is_empty_or_negative(&self) -> bool {
123         self.max.x <= self.min.x || self.max.y <= self.min.y
124     }
125 
126     /// Returns `true` if the two boxes intersect.
127     #[inline]
intersects(&self, other: &Self) -> bool128     pub fn intersects(&self, other: &Self) -> bool {
129         self.min.x < other.max.x
130             && self.max.x > other.min.x
131             && self.min.y < other.max.y
132             && self.max.y > other.min.y
133     }
134 
135     /// Returns `true` if this box contains the point. Points are considered
136     /// in the box if they are on the front, left or top faces, but outside if they
137     /// are on the back, right or bottom faces.
138     #[inline]
contains(&self, p: Point2D<T, U>) -> bool139     pub fn contains(&self, p: Point2D<T, U>) -> bool {
140         self.min.x <= p.x && p.x < self.max.x
141             && self.min.y <= p.y && p.y < self.max.y
142     }
143 
144     /// Returns `true` if this box contains the interior of the other box. Always
145     /// returns `true` if other is empty, and always returns `false` if other is
146     /// nonempty but this box is empty.
147     #[inline]
contains_box(&self, other: &Self) -> bool148     pub fn contains_box(&self, other: &Self) -> bool {
149         other.is_empty_or_negative()
150             || (self.min.x <= other.min.x && other.max.x <= self.max.x
151                 && self.min.y <= other.min.y && other.max.y <= self.max.y)
152     }
153 }
154 
155 impl<T, U> Box2D<T, U>
156 where
157     T: Copy + PartialOrd,
158 {
159     #[inline]
to_non_empty(&self) -> Option<NonEmpty<Self>>160     pub fn to_non_empty(&self) -> Option<NonEmpty<Self>> {
161         if self.is_empty_or_negative() {
162             return None;
163         }
164 
165         Some(NonEmpty(*self))
166     }
167     /// Computes the intersection of two boxes.
168     ///
169     /// The result is a negative box if the boxes do not intersect.
170     #[inline]
intersection(&self, other: &Self) -> Self171     pub fn intersection(&self, other: &Self) -> Self {
172         Box2D {
173             min: point2(
174                 max(self.min.x, other.min.x),
175                 max(self.min.y, other.min.y),
176             ),
177             max: point2(
178                 min(self.max.x, other.max.x),
179                 min(self.max.y, other.max.y),
180             )
181         }
182     }
183 
184     /// Computes the intersection of two boxes, returning `None` if the boxes do not intersect.
185     #[inline]
try_intersection(&self, other: &Self) -> Option<NonEmpty<Self>>186     pub fn try_intersection(&self, other: &Self) -> Option<NonEmpty<Self>> {
187         let intersection = self.intersection(other);
188 
189         if intersection.is_negative() {
190             return None;
191         }
192 
193         Some(NonEmpty(intersection))
194     }
195 }
196 
197 impl<T, U> Box2D<T, U>
198 where
199     T: Copy + Add<T, Output = T>,
200 {
201     /// Returns the same box, translated by a vector.
202     #[inline]
translate(&self, by: Vector2D<T, U>) -> Self203     pub fn translate(&self, by: Vector2D<T, U>) -> Self {
204         Box2D {
205             min: self.min + by,
206             max: self.max + by,
207         }
208     }
209 }
210 
211 impl<T, U> Box2D<T, U>
212 where
213     T: Copy + Sub<T, Output = T>,
214 {
215     #[inline]
size(&self)-> Size2D<T, U>216     pub fn size(&self)-> Size2D<T, U> {
217         (self.max - self.min).to_size()
218     }
219 
220     #[inline]
width(&self) -> T221     pub fn width(&self) -> T {
222         self.max.x - self.min.x
223     }
224 
225     #[inline]
height(&self) -> T226     pub fn height(&self) -> T {
227         self.max.y - self.min.y
228     }
229 
230     #[inline]
to_rect(&self) -> Rect<T, U>231     pub fn to_rect(&self) -> Rect<T, U> {
232         Rect {
233             origin: self.min,
234             size: self.size(),
235         }
236     }
237 }
238 
239 impl<T, U> Box2D<T, U>
240 where
241     T: Copy + PartialEq + Add<T, Output = T> + Sub<T, Output = T>,
242 {
243     /// Inflates the box by the specified sizes on each side respectively.
244     #[inline]
245     #[must_use]
inflate(&self, width: T, height: T) -> Self246     pub fn inflate(&self, width: T, height: T) -> Self {
247         Box2D {
248             min: point2(self.min.x - width, self.min.y - height),
249             max: point2(self.max.x + width, self.max.y + height),
250         }
251     }
252 }
253 
254 impl<T, U> Box2D<T, U>
255 where
256     T: Copy + Zero + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
257 {
258     /// Calculate the size and position of an inner box.
259     ///
260     /// Subtracts the side offsets from all sides. The horizontal, vertical
261     /// and applicate offsets must not be larger than the original side length.
inner_box(&self, offsets: SideOffsets2D<T, U>) -> Self262     pub fn inner_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
263         Box2D {
264             min: self.min + vec2(offsets.left, offsets.top),
265             max: self.max - vec2(offsets.right, offsets.bottom),
266         }
267     }
268 
269     /// Calculate the b and position of an outer box.
270     ///
271     /// Add the offsets to all sides. The expanded box is returned.
outer_box(&self, offsets: SideOffsets2D<T, U>) -> Self272     pub fn outer_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
273         Box2D {
274             min: self.min - vec2(offsets.left, offsets.top),
275             max: self.max + vec2(offsets.right, offsets.bottom),
276         }
277     }
278 }
279 
280 
281 impl<T, U> Box2D<T, U>
282 where
283     T: Copy + Zero + PartialOrd,
284 {
285     /// Returns the smallest box containing all of the provided points.
from_points<I>(points: I) -> Self where I: IntoIterator, I::Item: Borrow<Point2D<T, U>>,286     pub fn from_points<I>(points: I) -> Self
287     where
288         I: IntoIterator,
289         I::Item: Borrow<Point2D<T, U>>,
290     {
291         let mut points = points.into_iter();
292 
293         let (mut min_x, mut min_y) = match points.next() {
294             Some(first) => (first.borrow().x, first.borrow().y),
295             None => return Box2D::zero(),
296         };
297 
298         let (mut max_x, mut max_y) = (min_x, min_y);
299         for point in points {
300             let p = point.borrow();
301             if p.x < min_x {
302                 min_x = p.x
303             }
304             if p.x > max_x {
305                 max_x = p.x
306             }
307             if p.y < min_y {
308                 min_y = p.y
309             }
310             if p.y > max_y {
311                 max_y = p.y
312             }
313         }
314 
315         Box2D {
316             min: point2(min_x, min_y),
317             max: point2(max_x, max_y),
318         }
319     }
320 }
321 
322 impl<T, U> Box2D<T, U>
323 where
324     T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
325 {
326     /// Linearly interpolate between this box and another box.
327     #[inline]
lerp(&self, other: Self, t: T) -> Self328     pub fn lerp(&self, other: Self, t: T) -> Self {
329         Self::new(
330             self.min.lerp(other.min, t),
331             self.max.lerp(other.max, t),
332         )
333     }
334 }
335 
336 impl<T, U> Box2D<T, U>
337 where
338     T: Copy + One + Add<Output = T> + Div<Output = T>,
339 {
center(&self) -> Point2D<T, U>340     pub fn center(&self) -> Point2D<T, U> {
341         let two = T::one() + T::one();
342         (self.min + self.max.to_vector()) / two
343     }
344 }
345 
346 impl<T, U> Box2D<T, U>
347 where
348     T: Copy + PartialOrd,
349 {
350     #[inline]
union(&self, other: &Self) -> Self351     pub fn union(&self, other: &Self) -> Self {
352         Box2D {
353             min: point2(
354                 min(self.min.x, other.min.x),
355                 min(self.min.y, other.min.y),
356             ),
357             max: point2(
358                 max(self.max.x, other.max.x),
359                 max(self.max.y, other.max.y),
360             ),
361         }
362     }
363 }
364 
365 impl<T, U> Box2D<T, U>
366 where
367     T: Copy,
368 {
369     #[inline]
scale<S: Copy>(&self, x: S, y: S) -> Self where T: Mul<S, Output = T>370     pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
371     where
372         T: Mul<S, Output = T>
373     {
374         Box2D {
375             min: point2(self.min.x * x, self.min.y * y),
376             max: point2(self.max.x * x, self.max.y * y),
377         }
378     }
379 }
380 
381 impl<T, U> Box2D<T, U>
382 where
383     T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
384 {
385     #[inline]
area(&self) -> T386     pub fn area(&self) -> T {
387         let size = self.size();
388         size.width * size.height
389     }
390 }
391 
392 impl<T, U> Box2D<T, U>
393 where
394     T: Zero,
395 {
396     /// Constructor, setting all sides to zero.
zero() -> Self397     pub fn zero() -> Self {
398         Box2D::new(Point2D::zero(), Point2D::zero())
399     }
400 }
401 
402 impl<T, U> Box2D<T, U>
403 where
404     T: PartialEq,
405 {
406     /// Returns true if the size is zero.
407     #[inline]
is_empty(&self) -> bool408     pub fn is_empty(&self) -> bool {
409         self.min.x == self.max.x || self.min.y == self.max.y
410     }
411 }
412 
413 impl<T, U> Mul<T> for Box2D<T, U>
414 where
415     T: Copy + Mul<T, Output = T>,
416 {
417     type Output = Self;
418     #[inline]
mul(self, scale: T) -> Self419     fn mul(self, scale: T) -> Self {
420         Box2D::new(self.min * scale, self.max * scale)
421     }
422 }
423 
424 impl<T, U> Div<T> for Box2D<T, U>
425 where
426     T: Copy + Div<T, Output = T>,
427 {
428     type Output = Self;
429     #[inline]
div(self, scale: T) -> Self430     fn div(self, scale: T) -> Self {
431         Box2D::new(self.min / scale, self.max / scale)
432     }
433 }
434 
435 impl<T, U1, U2> Mul<Scale<T, U1, U2>> for Box2D<T, U1>
436 where
437     T: Copy + Mul<T, Output = T>,
438 {
439     type Output = Box2D<T, U2>;
440     #[inline]
mul(self, scale: Scale<T, U1, U2>) -> Box2D<T, U2>441     fn mul(self, scale: Scale<T, U1, U2>) -> Box2D<T, U2> {
442         Box2D::new(self.min * scale, self.max * scale)
443     }
444 }
445 
446 impl<T, U1, U2> Div<Scale<T, U1, U2>> for Box2D<T, U2>
447 where
448     T: Copy + Div<T, Output = T>,
449 {
450     type Output = Box2D<T, U1>;
451     #[inline]
div(self, scale: Scale<T, U1, U2>) -> Box2D<T, U1>452     fn div(self, scale: Scale<T, U1, U2>) -> Box2D<T, U1> {
453         Box2D::new(self.min / scale, self.max / scale)
454     }
455 }
456 
457 impl<T, U> Box2D<T, U>
458 where
459     T: Copy,
460 {
461     /// Drop the units, preserving only the numeric value.
462     #[inline]
to_untyped(&self) -> Box2D<T, UnknownUnit>463     pub fn to_untyped(&self) -> Box2D<T, UnknownUnit> {
464         Box2D::new(self.min.to_untyped(), self.max.to_untyped())
465     }
466 
467     /// Tag a unitless value with units.
468     #[inline]
from_untyped(c: &Box2D<T, UnknownUnit>) -> Box2D<T, U>469     pub fn from_untyped(c: &Box2D<T, UnknownUnit>) -> Box2D<T, U> {
470         Box2D::new(
471             Point2D::from_untyped(c.min),
472             Point2D::from_untyped(c.max),
473         )
474     }
475 
476     /// Cast the unit
477     #[inline]
cast_unit<V>(&self) -> Box2D<T, V>478     pub fn cast_unit<V>(&self) -> Box2D<T, V> {
479         Box2D::new(self.min.cast_unit(), self.max.cast_unit())
480     }
481 }
482 
483 impl<T, U> Box2D<T, U>
484 where
485     T: NumCast + Copy,
486 {
487     /// Cast from one numeric representation to another, preserving the units.
488     ///
489     /// When casting from floating point to integer coordinates, the decimals are truncated
490     /// as one would expect from a simple cast, but this behavior does not always make sense
491     /// geometrically. Consider using round(), round_in or round_out() before casting.
492     #[inline]
cast<NewT: NumCast>(&self) -> Box2D<NewT, U>493     pub fn cast<NewT: NumCast>(&self) -> Box2D<NewT, U> {
494         Box2D::new(
495             self.min.cast(),
496             self.max.cast(),
497         )
498     }
499 
500     /// Fallible cast from one numeric representation to another, preserving the units.
501     ///
502     /// When casting from floating point to integer coordinates, the decimals are truncated
503     /// as one would expect from a simple cast, but this behavior does not always make sense
504     /// geometrically. Consider using round(), round_in or round_out() before casting.
try_cast<NewT: NumCast>(&self) -> Option<Box2D<NewT, U>>505     pub fn try_cast<NewT: NumCast>(&self) -> Option<Box2D<NewT, U>> {
506         match (self.min.try_cast(), self.max.try_cast()) {
507             (Some(a), Some(b)) => Some(Box2D::new(a, b)),
508             _ => None,
509         }
510     }
511 }
512 
513 impl<T, U> Box2D<T, U>
514 where
515     T: Round,
516 {
517     /// Return a box with edges rounded to integer coordinates, such that
518     /// the returned box has the same set of pixel centers as the original
519     /// one.
520     /// Values equal to 0.5 round up.
521     /// Suitable for most places where integral device coordinates
522     /// are needed, but note that any translation should be applied first to
523     /// avoid pixel rounding errors.
524     /// Note that this is *not* rounding to nearest integer if the values are negative.
525     /// They are always rounding as floor(n + 0.5).
526     #[must_use]
round(&self) -> Self527     pub fn round(&self) -> Self {
528         Box2D::new(self.min.round(), self.max.round())
529     }
530 }
531 
532 impl<T, U> Box2D<T, U>
533 where
534     T: Floor + Ceil,
535 {
536     /// Return a box with faces/edges rounded to integer coordinates, such that
537     /// the original box contains the resulting box.
538     #[must_use]
round_in(&self) -> Self539     pub fn round_in(&self) -> Self {
540         let min = self.min.ceil();
541         let max = self.max.floor();
542         Box2D { min, max }
543     }
544 
545     /// Return a box with faces/edges rounded to integer coordinates, such that
546     /// the original box is contained in the resulting box.
547     #[must_use]
round_out(&self) -> Self548     pub fn round_out(&self) -> Self {
549         let min = self.min.floor();
550         let max = self.max.ceil();
551         Box2D { min, max }
552     }
553 }
554 
555 // Convenience functions for common casts
556 impl<T: NumCast + Copy, U> Box2D<T, U> {
557     /// Cast into an `f32` box.
558     #[inline]
to_f32(&self) -> Box2D<f32, U>559     pub fn to_f32(&self) -> Box2D<f32, U> {
560         self.cast()
561     }
562 
563     /// Cast into an `f64` box.
564     #[inline]
to_f64(&self) -> Box2D<f64, U>565     pub fn to_f64(&self) -> Box2D<f64, U> {
566         self.cast()
567     }
568 
569     /// Cast into an `usize` box, truncating decimals if any.
570     ///
571     /// When casting from floating point boxes, it is worth considering whether
572     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
573     /// obtain the desired conversion behavior.
574     #[inline]
to_usize(&self) -> Box2D<usize, U>575     pub fn to_usize(&self) -> Box2D<usize, U> {
576         self.cast()
577     }
578 
579     /// Cast into an `u32` box, truncating decimals if any.
580     ///
581     /// When casting from floating point boxes, it is worth considering whether
582     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
583     /// obtain the desired conversion behavior.
584     #[inline]
to_u32(&self) -> Box2D<u32, U>585     pub fn to_u32(&self) -> Box2D<u32, U> {
586         self.cast()
587     }
588 
589     /// Cast into an `i32` box, truncating decimals if any.
590     ///
591     /// When casting from floating point boxes, it is worth considering whether
592     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
593     /// obtain the desired conversion behavior.
594     #[inline]
to_i32(&self) -> Box2D<i32, U>595     pub fn to_i32(&self) -> Box2D<i32, U> {
596         self.cast()
597     }
598 
599     /// Cast into an `i64` box, truncating decimals if any.
600     ///
601     /// When casting from floating point boxes, it is worth considering whether
602     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
603     /// obtain the desired conversion behavior.
604     #[inline]
to_i64(&self) -> Box2D<i64, U>605     pub fn to_i64(&self) -> Box2D<i64, U> {
606         self.cast()
607     }
608 }
609 
610 impl<T, U> From<Size2D<T, U>> for Box2D<T, U>
611 where
612     T: Copy + Zero + PartialOrd,
613 {
from(b: Size2D<T, U>) -> Self614     fn from(b: Size2D<T, U>) -> Self {
615         Self::from_size(b)
616     }
617 }
618 
619 #[cfg(test)]
620 mod tests {
621     use side_offsets::SideOffsets2D;
622     use {Point2D, point2, vec2, size2};
623     use default::Box2D;
624     //use super::*;
625 
626     #[test]
test_size()627     fn test_size() {
628         let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
629         assert_eq!(b.size().width, 20.0);
630         assert_eq!(b.size().height, 20.0);
631     }
632 
633     #[test]
test_width_height()634     fn test_width_height() {
635         let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
636         assert!(b.width() == 20.0);
637         assert!(b.height() == 20.0);
638     }
639 
640     #[test]
test_center()641     fn test_center() {
642         let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
643         assert_eq!(b.center(), Point2D::zero());
644     }
645 
646     #[test]
test_area()647     fn test_area() {
648         let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
649         assert_eq!(b.area(), 400.0);
650     }
651 
652     #[test]
test_from_points()653     fn test_from_points() {
654         let b = Box2D::from_points(&[point2(50.0, 160.0), point2(100.0, 25.0)]);
655         assert_eq!(b.min, point2(50.0, 25.0));
656         assert_eq!(b.max, point2(100.0, 160.0));
657     }
658 
659     #[test]
test_round_in()660     fn test_round_in() {
661         let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_in();
662         assert_eq!(b.min.x, -25.0);
663         assert_eq!(b.min.y, -40.0);
664         assert_eq!(b.max.x, 60.0);
665         assert_eq!(b.max.y, 36.0);
666     }
667 
668     #[test]
test_round_out()669     fn test_round_out() {
670         let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_out();
671         assert_eq!(b.min.x,-26.0);
672         assert_eq!(b.min.y, -41.0);
673         assert_eq!(b.max.x, 61.0);
674         assert_eq!(b.max.y, 37.0);
675     }
676 
677     #[test]
test_round()678     fn test_round() {
679         let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round();
680         assert_eq!(b.min.x,-26.0);
681         assert_eq!(b.min.y, -40.0);
682         assert_eq!(b.max.x, 60.0);
683         assert_eq!(b.max.y, 37.0);
684     }
685 
686     #[test]
test_from_size()687     fn test_from_size() {
688         let b = Box2D::from_size(size2(30.0, 40.0));
689         assert!(b.min == Point2D::zero());
690         assert!(b.size().width == 30.0);
691         assert!(b.size().height == 40.0);
692     }
693 
694     #[test]
test_inner_box()695     fn test_inner_box() {
696         let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
697         let b = b.inner_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
698         assert_eq!(b.max.x, 80.0);
699         assert_eq!(b.max.y, 155.0);
700         assert_eq!(b.min.x, 60.0);
701         assert_eq!(b.min.y, 35.0);
702     }
703 
704     #[test]
test_outer_box()705     fn test_outer_box() {
706         let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
707         let b = b.outer_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
708         assert_eq!(b.max.x, 120.0);
709         assert_eq!(b.max.y, 165.0);
710         assert_eq!(b.min.x, 40.0);
711         assert_eq!(b.min.y, 15.0);
712     }
713 
714     #[test]
test_translate()715     fn test_translate() {
716         let size = size2(15.0, 15.0);
717         let mut center = (size / 2.0).to_vector().to_point();
718         let b = Box2D::from_size(size);
719         assert_eq!(b.center(), center);
720         let translation = vec2(10.0, 2.5);
721         let b = b.translate(translation);
722         center += translation;
723         assert_eq!(b.center(), center);
724         assert_eq!(b.max.x, 25.0);
725         assert_eq!(b.max.y, 17.5);
726         assert_eq!(b.min.x, 10.0);
727         assert_eq!(b.min.y, 2.5);
728     }
729 
730     #[test]
test_union()731     fn test_union() {
732         let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(0.0, 20.0)]);
733         let b2 = Box2D::from_points(&[point2(0.0, 20.0), point2(20.0, -20.0)]);
734         let b = b1.union(&b2);
735         assert_eq!(b.max.x, 20.0);
736         assert_eq!(b.max.y, 20.0);
737         assert_eq!(b.min.x, -20.0);
738         assert_eq!(b.min.y, -20.0);
739     }
740 
741     #[test]
test_intersects()742     fn test_intersects() {
743         let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
744         let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
745         assert!(b1.intersects(&b2));
746     }
747 
748     #[test]
test_intersection()749     fn test_intersection() {
750         let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
751         let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
752         let b = b1.intersection(&b2);
753         assert_eq!(b.max.x, 10.0);
754         assert_eq!(b.max.y, 20.0);
755         assert_eq!(b.min.x, -10.0);
756         assert_eq!(b.min.y, -20.0);
757     }
758 
759     #[test]
test_try_intersection()760     fn test_try_intersection() {
761         let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
762         let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
763         assert!(b1.try_intersection(&b2).is_some());
764 
765         let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(-10.0, 20.0)]);
766         let b2 = Box2D::from_points(&[point2(10.0, 20.0), point2(15.0, -20.0)]);
767         assert!(b1.try_intersection(&b2).is_none());
768     }
769 
770     #[test]
test_scale()771     fn test_scale() {
772         let b = Box2D::from_points(&[point2(-10.0, -10.0), point2(10.0, 10.0)]);
773         let b = b.scale(0.5, 0.5);
774         assert_eq!(b.max.x, 5.0);
775         assert_eq!(b.max.y, 5.0);
776         assert_eq!(b.min.x, -5.0);
777         assert_eq!(b.min.y, -5.0);
778     }
779 
780     #[test]
test_lerp()781     fn test_lerp() {
782         let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(-10.0, -10.0)]);
783         let b2 = Box2D::from_points(&[point2(10.0, 10.0), point2(20.0, 20.0)]);
784         let b = b1.lerp(b2, 0.5);
785         assert_eq!(b.center(), Point2D::zero());
786         assert_eq!(b.size().width, 10.0);
787         assert_eq!(b.size().height, 10.0);
788     }
789 
790     #[test]
test_contains()791     fn test_contains() {
792         let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
793         assert!(b.contains(point2(-15.3, 10.5)));
794     }
795 
796     #[test]
test_contains_box()797     fn test_contains_box() {
798         let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
799         let b2 = Box2D::from_points(&[point2(-14.3, -16.5), point2(6.7, 17.6)]);
800         assert!(b1.contains_box(&b2));
801     }
802 
803     #[test]
test_inflate()804     fn test_inflate() {
805         let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
806         let b = b.inflate(10.0, 5.0);
807         assert_eq!(b.size().width, 60.0);
808         assert_eq!(b.size().height, 50.0);
809         assert_eq!(b.center(), Point2D::zero());
810     }
811 
812     #[test]
test_is_empty()813     fn test_is_empty() {
814         for i in 0..2 {
815             let mut coords_neg = [-20.0, -20.0];
816             let mut coords_pos = [20.0, 20.0];
817             coords_neg[i] = 0.0;
818             coords_pos[i] = 0.0;
819             let b = Box2D::from_points(&[Point2D::from(coords_neg), Point2D::from(coords_pos)]);
820             assert!(b.is_empty());
821         }
822     }
823 }
824