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 box2d::Box2D;
14 use point::Point2D;
15 use vector::Vector2D;
16 use side_offsets::SideOffsets2D;
17 use size::Size2D;
18 use approxord::{min, max};
19 use nonempty::NonEmpty;
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, Range};
30 
31 
32 /// A 2d Rectangle optionally tagged with a unit.
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 Rect<T, U> {
37     pub origin: Point2D<T, U>,
38     pub size: Size2D<T, U>,
39 }
40 
41 impl<T: Hash, U> Hash for Rect<T, U> {
hash<H: Hasher>(&self, h: &mut H)42     fn hash<H: Hasher>(&self, h: &mut H) {
43         self.origin.hash(h);
44         self.size.hash(h);
45     }
46 }
47 
48 impl<T: Copy, U> Copy for Rect<T, U> {}
49 
50 impl<T: Copy, U> Clone for Rect<T, U> {
clone(&self) -> Self51     fn clone(&self) -> Self {
52         *self
53     }
54 }
55 
56 impl<T: PartialEq, U> PartialEq<Rect<T, U>> for Rect<T, U> {
eq(&self, other: &Self) -> bool57     fn eq(&self, other: &Self) -> bool {
58         self.origin.eq(&other.origin) && self.size.eq(&other.size)
59     }
60 }
61 
62 impl<T: Eq, U> Eq for Rect<T, U> {}
63 
64 impl<T: fmt::Debug, U> fmt::Debug for Rect<T, U> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result65     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66         write!(f, "Rect({:?} at {:?})", self.size, self.origin)
67     }
68 }
69 
70 impl<T: fmt::Display, U> fmt::Display for Rect<T, U> {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result71     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
72         write!(formatter, "Rect({} at {})", self.size, self.origin)
73     }
74 }
75 
76 impl<T: Default, U> Default for Rect<T, U> {
default() -> Self77     fn default() -> Self {
78         Rect::new(Default::default(), Default::default())
79     }
80 }
81 
82 impl<T, U> Rect<T, U> {
83     /// Constructor.
new(origin: Point2D<T, U>, size: Size2D<T, U>) -> Self84     pub fn new(origin: Point2D<T, U>, size: Size2D<T, U>) -> Self {
85         Rect {
86             origin,
87             size,
88         }
89     }
90 }
91 
92 impl<T, U> Rect<T, U>
93 where
94     T: Copy + Zero
95 {
96     /// Creates a rect of the given size, at offset zero.
from_size(size: Size2D<T, U>) -> Self97     pub fn from_size(size: Size2D<T, U>) -> Self {
98         Rect {
99             origin: Point2D::zero(),
100             size,
101         }
102     }
103 }
104 
105 impl<T, U> Rect<T, U>
106 where
107     T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output = T> + Sub<T, Output = T>,
108 {
109     #[inline]
intersects(&self, other: &Self) -> bool110     pub fn intersects(&self, other: &Self) -> bool {
111         self.origin.x < other.origin.x + other.size.width
112             && other.origin.x < self.origin.x + self.size.width
113             && self.origin.y < other.origin.y + other.size.height
114             && other.origin.y < self.origin.y + self.size.height
115     }
116 
117     #[inline]
min(&self) -> Point2D<T, U>118     pub fn min(&self) -> Point2D<T, U> {
119         self.origin
120     }
121 
122     #[inline]
max(&self) -> Point2D<T, U>123     pub fn max(&self) -> Point2D<T, U> {
124         self.origin + self.size
125     }
126 
127     #[inline]
max_x(&self) -> T128     pub fn max_x(&self) -> T {
129         self.origin.x + self.size.width
130     }
131 
132     #[inline]
min_x(&self) -> T133     pub fn min_x(&self) -> T {
134         self.origin.x
135     }
136 
137     #[inline]
max_y(&self) -> T138     pub fn max_y(&self) -> T {
139         self.origin.y + self.size.height
140     }
141 
142     #[inline]
min_y(&self) -> T143     pub fn min_y(&self) -> T {
144         self.origin.y
145     }
146 
147     #[inline]
x_range(&self) -> Range<T>148     pub fn x_range(&self) -> Range<T> {
149         self.min_x()..self.max_x()
150     }
151 
152     #[inline]
y_range(&self) -> Range<T>153     pub fn y_range(&self) -> Range<T> {
154         self.min_y()..self.max_y()
155     }
156 
157     #[inline]
intersection(&self, other: &Self) -> Option<Self>158     pub fn intersection(&self, other: &Self) -> Option<Self> {
159         if !self.intersects(other) {
160             return None;
161         }
162 
163         let upper_left = Point2D::new(
164             max(self.min_x(), other.min_x()),
165             max(self.min_y(), other.min_y()),
166         );
167         let lower_right_x = min(self.max_x(), other.max_x());
168         let lower_right_y = min(self.max_y(), other.max_y());
169 
170         Some(Rect::new(
171             upper_left,
172             Size2D::new(lower_right_x - upper_left.x, lower_right_y - upper_left.y),
173         ))
174     }
175 
176     /// Returns the same rectangle, translated by a vector.
177     #[inline]
178     #[must_use]
translate(&self, by: Vector2D<T, U>) -> Self179     pub fn translate(&self, by: Vector2D<T, U>) -> Self {
180         Self::new(self.origin + by, self.size)
181     }
182 
183     /// Returns true if this rectangle contains the point. Points are considered
184     /// in the rectangle if they are on the left or top edge, but outside if they
185     /// are on the right or bottom edge.
186     #[inline]
contains(&self, other: Point2D<T, U>) -> bool187     pub fn contains(&self, other: Point2D<T, U>) -> bool {
188         self.origin.x <= other.x && other.x < self.origin.x + self.size.width
189             && self.origin.y <= other.y && other.y < self.origin.y + self.size.height
190     }
191 
192     /// Returns true if this rectangle contains the interior of rect. Always
193     /// returns true if rect is empty, and always returns false if rect is
194     /// nonempty but this rectangle is empty.
195     #[inline]
contains_rect(&self, rect: &Self) -> bool196     pub fn contains_rect(&self, rect: &Self) -> bool {
197         rect.is_empty_or_negative()
198             || (self.min_x() <= rect.min_x() && rect.max_x() <= self.max_x()
199                 && self.min_y() <= rect.min_y() && rect.max_y() <= self.max_y())
200     }
201 
202     #[inline]
203     #[must_use]
inflate(&self, width: T, height: T) -> Self204     pub fn inflate(&self, width: T, height: T) -> Self {
205         Rect::new(
206             Point2D::new(self.origin.x - width, self.origin.y - height),
207             Size2D::new(
208                 self.size.width + width + width,
209                 self.size.height + height + height,
210             ),
211         )
212     }
213 
214     #[inline]
to_box2d(&self) -> Box2D<T, U>215     pub fn to_box2d(&self) -> Box2D<T, U> {
216         Box2D {
217             min: self.min(),
218             max: self.max(),
219         }
220     }
221 
222     /// Calculate the size and position of an inner rectangle.
223     ///
224     /// Subtracts the side offsets from all sides. The horizontal and vertical
225     /// offsets must not be larger than the original side length.
226     /// This method assumes y oriented downward.
inner_rect(&self, offsets: SideOffsets2D<T, U>) -> Self227     pub fn inner_rect(&self, offsets: SideOffsets2D<T, U>) -> Self {
228         let rect = Rect::new(
229             Point2D::new(
230                 self.origin.x + offsets.left,
231                 self.origin.y + offsets.top
232             ),
233             Size2D::new(
234                 self.size.width - offsets.horizontal(),
235                 self.size.height - offsets.vertical()
236             )
237         );
238         debug_assert!(rect.size.width >= Zero::zero());
239         debug_assert!(rect.size.height >= Zero::zero());
240         rect
241     }
242 
243     /// Calculate the size and position of an outer rectangle.
244     ///
245     /// Add the offsets to all sides. The expanded rectangle is returned.
246     /// This method assumes y oriented downward.
outer_rect(&self, offsets: SideOffsets2D<T, U>) -> Self247     pub fn outer_rect(&self, offsets: SideOffsets2D<T, U>) -> Self {
248         Rect::new(
249             Point2D::new(
250                 self.origin.x - offsets.left,
251                 self.origin.y - offsets.top
252             ),
253             Size2D::new(
254                 self.size.width + offsets.horizontal(),
255                 self.size.height + offsets.vertical()
256             )
257         )
258     }
259 
260     /// Returns the smallest rectangle defined by the top/bottom/left/right-most
261     /// points provided as parameter.
262     ///
263     /// Note: This function has a behavior that can be surprising because
264     /// the right-most and bottom-most points are exactly on the edge
265     /// of the rectangle while the `contains` function is has exclusive
266     /// semantic on these edges. This means that the right-most and bottom-most
267     /// points provided to `from_points` will count as not contained by the rect.
268     /// This behavior may change in the future.
from_points<I>(points: I) -> Self where I: IntoIterator, I::Item: Borrow<Point2D<T, U>>,269     pub fn from_points<I>(points: I) -> Self
270     where
271         I: IntoIterator,
272         I::Item: Borrow<Point2D<T, U>>,
273     {
274         let mut points = points.into_iter();
275 
276         let (mut min_x, mut min_y) = match points.next() {
277             Some(first) => (first.borrow().x, first.borrow().y),
278             None => return Rect::zero(),
279         };
280 
281         let (mut max_x, mut max_y) = (min_x, min_y);
282         for point in points {
283             let p = point.borrow();
284             if p.x < min_x {
285                 min_x = p.x
286             }
287             if p.x > max_x {
288                 max_x = p.x
289             }
290             if p.y < min_y {
291                 min_y = p.y
292             }
293             if p.y > max_y {
294                 max_y = p.y
295             }
296         }
297         Rect::new(
298             Point2D::new(min_x, min_y),
299             Size2D::new(max_x - min_x, max_y - min_y),
300         )
301     }
302 }
303 
304 impl<T, U> Rect<T, U>
305 where
306     T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
307 {
308     /// Linearly interpolate between this rectangle and another rectangle.
309     ///
310     /// `t` is expected to be between zero and one.
311     #[inline]
lerp(&self, other: Self, t: T) -> Self312     pub fn lerp(&self, other: Self, t: T) -> Self {
313         Self::new(
314             self.origin.lerp(other.origin, t),
315             self.size.lerp(other.size, t),
316         )
317     }
318 }
319 
320 impl<T, U> Rect<T, U>
321 where
322     T: Copy + One + Add<Output = T> + Div<Output = T>,
323 {
center(&self) -> Point2D<T, U>324     pub fn center(&self) -> Point2D<T, U> {
325         let two = T::one() + T::one();
326         self.origin + self.size.to_vector() / two
327     }
328 }
329 
330 impl<T, U> Rect<T, U>
331 where
332     T: Copy + Clone + PartialOrd + Add<T, Output = T> + Sub<T, Output = T> + Zero,
333 {
334     #[inline]
union(&self, other: &Self) -> Self335     pub fn union(&self, other: &Self) -> Self {
336         if self.size == Zero::zero() {
337             return *other;
338         }
339         if other.size == Zero::zero() {
340             return *self;
341         }
342 
343         let upper_left = Point2D::new(
344             min(self.min_x(), other.min_x()),
345             min(self.min_y(), other.min_y()),
346         );
347 
348         let lower_right_x = max(self.max_x(), other.max_x());
349         let lower_right_y = max(self.max_y(), other.max_y());
350 
351         Rect::new(
352             upper_left,
353             Size2D::new(lower_right_x - upper_left.x, lower_right_y - upper_left.y),
354         )
355     }
356 }
357 
358 impl<T, U> Rect<T, U> {
359     #[inline]
scale<S: Copy>(&self, x: S, y: S) -> Self where T: Copy + Clone + Mul<S, Output = T>,360     pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
361     where
362         T: Copy + Clone + Mul<S, Output = T>,
363     {
364         Rect::new(
365             Point2D::new(self.origin.x * x, self.origin.y * y),
366             Size2D::new(self.size.width * x, self.size.height * y),
367         )
368     }
369 }
370 
371 impl<T: Copy + Clone + Mul<T, Output = T>, U> Rect<T, U> {
372     #[inline]
area(&self) -> T373     pub fn area(&self) -> T {
374         self.size.area()
375     }
376 }
377 
378 impl<T: Copy + PartialEq + Zero, U> Rect<T, U> {
379     /// Constructor, setting all sides to zero.
zero() -> Self380     pub fn zero() -> Self {
381         Rect::new(Point2D::origin(), Size2D::zero())
382     }
383 
384     /// Returns true if the size is zero, regardless of the origin's value.
is_empty(&self) -> bool385     pub fn is_empty(&self) -> bool {
386         self.size.width == Zero::zero() || self.size.height == Zero::zero()
387     }
388 }
389 
390 impl<T: Copy + Zero + PartialOrd, U> Rect<T, U> {
391 
392     #[inline]
is_empty_or_negative(&self) -> bool393     pub fn is_empty_or_negative(&self) -> bool {
394         self.size.is_empty_or_negative()
395     }
396 
397     #[inline]
to_non_empty(&self) -> Option<NonEmpty<Self>>398     pub fn to_non_empty(&self) -> Option<NonEmpty<Self>> {
399         if self.is_empty_or_negative() {
400             return None;
401         }
402 
403         Some(NonEmpty(*self))
404     }
405 }
406 
407 impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for Rect<T, U> {
408     type Output = Self;
409     #[inline]
mul(self, scale: T) -> Self410     fn mul(self, scale: T) -> Self {
411         Rect::new(self.origin * scale, self.size * scale)
412     }
413 }
414 
415 impl<T: Copy + Div<T, Output = T>, U> Div<T> for Rect<T, U> {
416     type Output = Self;
417     #[inline]
div(self, scale: T) -> Self418     fn div(self, scale: T) -> Self {
419         Rect::new(self.origin / scale, self.size / scale)
420     }
421 }
422 
423 impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<Scale<T, U1, U2>> for Rect<T, U1> {
424     type Output = Rect<T, U2>;
425     #[inline]
mul(self, scale: Scale<T, U1, U2>) -> Rect<T, U2>426     fn mul(self, scale: Scale<T, U1, U2>) -> Rect<T, U2> {
427         Rect::new(self.origin * scale, self.size * scale)
428     }
429 }
430 
431 impl<T: Copy + Div<T, Output = T>, U1, U2> Div<Scale<T, U1, U2>> for Rect<T, U2> {
432     type Output = Rect<T, U1>;
433     #[inline]
div(self, scale: Scale<T, U1, U2>) -> Rect<T, U1>434     fn div(self, scale: Scale<T, U1, U2>) -> Rect<T, U1> {
435         Rect::new(self.origin / scale, self.size / scale)
436     }
437 }
438 
439 impl<T: Copy, Unit> Rect<T, Unit> {
440     /// Drop the units, preserving only the numeric value.
441     #[inline]
to_untyped(&self) -> Rect<T, UnknownUnit>442     pub fn to_untyped(&self) -> Rect<T, UnknownUnit> {
443         Rect::new(self.origin.to_untyped(), self.size.to_untyped())
444     }
445 
446     /// Tag a unitless value with units.
447     #[inline]
from_untyped(r: &Rect<T, UnknownUnit>) -> Rect<T, Unit>448     pub fn from_untyped(r: &Rect<T, UnknownUnit>) -> Rect<T, Unit> {
449         Rect::new(
450             Point2D::from_untyped(r.origin),
451             Size2D::from_untyped(r.size),
452         )
453     }
454 }
455 
456 impl<T0: NumCast + Copy, Unit> Rect<T0, Unit> {
457     /// Cast from one numeric representation to another, preserving the units.
458     ///
459     /// When casting from floating point to integer coordinates, the decimals are truncated
460     /// as one would expect from a simple cast, but this behavior does not always make sense
461     /// geometrically. Consider using round(), round_in or round_out() before casting.
cast<T1: NumCast + Copy>(&self) -> Rect<T1, Unit>462     pub fn cast<T1: NumCast + Copy>(&self) -> Rect<T1, Unit> {
463         Rect::new(
464             self.origin.cast(),
465             self.size.cast(),
466         )
467     }
468 
469     /// Fallible cast from one numeric representation to another, preserving the units.
470     ///
471     /// When casting from floating point to integer coordinates, the decimals are truncated
472     /// as one would expect from a simple cast, but this behavior does not always make sense
473     /// geometrically. Consider using round(), round_in or round_out() before casting.
try_cast<T1: NumCast + Copy>(&self) -> Option<Rect<T1, Unit>>474     pub fn try_cast<T1: NumCast + Copy>(&self) -> Option<Rect<T1, Unit>> {
475         match (self.origin.try_cast(), self.size.try_cast()) {
476             (Some(origin), Some(size)) => Some(Rect::new(origin, size)),
477             _ => None,
478         }
479     }
480 }
481 
482 impl<T: Floor + Ceil + Round + Add<T, Output = T> + Sub<T, Output = T>, U> Rect<T, U> {
483     /// Return a rectangle with edges rounded to integer coordinates, such that
484     /// the returned rectangle has the same set of pixel centers as the original
485     /// one.
486     /// Edges at offset 0.5 round up.
487     /// Suitable for most places where integral device coordinates
488     /// are needed, but note that any translation should be applied first to
489     /// avoid pixel rounding errors.
490     /// Note that this is *not* rounding to nearest integer if the values are negative.
491     /// They are always rounding as floor(n + 0.5).
492     #[must_use]
round(&self) -> Self493     pub fn round(&self) -> Self {
494         let origin = self.origin.round();
495         let size = self.origin.add_size(&self.size).round() - origin;
496         Rect::new(origin, Size2D::new(size.x, size.y))
497     }
498 
499     /// Return a rectangle with edges rounded to integer coordinates, such that
500     /// the original rectangle contains the resulting rectangle.
501     #[must_use]
round_in(&self) -> Self502     pub fn round_in(&self) -> Self {
503         let origin = self.origin.ceil();
504         let size = self.origin.add_size(&self.size).floor() - origin;
505         Rect::new(origin, Size2D::new(size.x, size.y))
506     }
507 
508     /// Return a rectangle with edges rounded to integer coordinates, such that
509     /// the original rectangle is contained in the resulting rectangle.
510     #[must_use]
round_out(&self) -> Self511     pub fn round_out(&self) -> Self {
512         let origin = self.origin.floor();
513         let size = self.origin.add_size(&self.size).ceil() - origin;
514         Rect::new(origin, Size2D::new(size.x, size.y))
515     }
516 }
517 
518 // Convenience functions for common casts
519 impl<T: NumCast + Copy, Unit> Rect<T, Unit> {
520     /// Cast into an `f32` rectangle.
to_f32(&self) -> Rect<f32, Unit>521     pub fn to_f32(&self) -> Rect<f32, Unit> {
522         self.cast()
523     }
524 
525     /// Cast into an `f64` rectangle.
to_f64(&self) -> Rect<f64, Unit>526     pub fn to_f64(&self) -> Rect<f64, Unit> {
527         self.cast()
528     }
529 
530     /// Cast into an `usize` rectangle, truncating decimals if any.
531     ///
532     /// When casting from floating point rectangles, it is worth considering whether
533     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
534     /// obtain the desired conversion behavior.
to_usize(&self) -> Rect<usize, Unit>535     pub fn to_usize(&self) -> Rect<usize, Unit> {
536         self.cast()
537     }
538 
539     /// Cast into an `u32` rectangle, truncating decimals if any.
540     ///
541     /// When casting from floating point rectangles, it is worth considering whether
542     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
543     /// obtain the desired conversion behavior.
to_u32(&self) -> Rect<u32, Unit>544     pub fn to_u32(&self) -> Rect<u32, Unit> {
545         self.cast()
546     }
547 
548     /// Cast into an `i32` rectangle, truncating decimals if any.
549     ///
550     /// When casting from floating point rectangles, it is worth considering whether
551     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
552     /// obtain the desired conversion behavior.
to_i32(&self) -> Rect<i32, Unit>553     pub fn to_i32(&self) -> Rect<i32, Unit> {
554         self.cast()
555     }
556 
557     /// Cast into an `i64` rectangle, truncating decimals if any.
558     ///
559     /// When casting from floating point rectangles, it is worth considering whether
560     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
561     /// obtain the desired conversion behavior.
to_i64(&self) -> Rect<i64, Unit>562     pub fn to_i64(&self) -> Rect<i64, Unit> {
563         self.cast()
564     }
565 }
566 
567 impl<T, U> From<Size2D<T, U>> for Rect<T, U>
568 where T: Copy + Zero
569 {
from(size: Size2D<T, U>) -> Self570     fn from(size: Size2D<T, U>) -> Self {
571         Self::from_size(size)
572     }
573 }
574 
575 /// Shorthand for `Rect::new(Point2D::new(x, y), Size2D::new(w, h))`.
rect<T: Copy, U>(x: T, y: T, w: T, h: T) -> Rect<T, U>576 pub fn rect<T: Copy, U>(x: T, y: T, w: T, h: T) -> Rect<T, U> {
577     Rect::new(Point2D::new(x, y), Size2D::new(w, h))
578 }
579 
580 #[cfg(test)]
581 mod tests {
582     use default::{Point2D, Rect, Size2D};
583     use {point2, vec2, rect, size2};
584     use side_offsets::SideOffsets2D;
585 
586     #[test]
test_translate()587     fn test_translate() {
588         let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
589         let pp = p.translate(vec2(10, 15));
590 
591         assert!(pp.size.width == 50);
592         assert!(pp.size.height == 40);
593         assert!(pp.origin.x == 10);
594         assert!(pp.origin.y == 15);
595 
596         let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
597         let rr = r.translate(vec2(0, -10));
598 
599         assert!(rr.size.width == 50);
600         assert!(rr.size.height == 40);
601         assert!(rr.origin.x == -10);
602         assert!(rr.origin.y == -15);
603     }
604 
605     #[test]
test_union()606     fn test_union() {
607         let p = Rect::new(Point2D::new(0, 0), Size2D::new(50, 40));
608         let q = Rect::new(Point2D::new(20, 20), Size2D::new(5, 5));
609         let r = Rect::new(Point2D::new(-15, -30), Size2D::new(200, 15));
610         let s = Rect::new(Point2D::new(20, -15), Size2D::new(250, 200));
611 
612         let pq = p.union(&q);
613         assert!(pq.origin == Point2D::new(0, 0));
614         assert!(pq.size == Size2D::new(50, 40));
615 
616         let pr = p.union(&r);
617         assert!(pr.origin == Point2D::new(-15, -30));
618         assert!(pr.size == Size2D::new(200, 70));
619 
620         let ps = p.union(&s);
621         assert!(ps.origin == Point2D::new(0, -15));
622         assert!(ps.size == Size2D::new(270, 200));
623     }
624 
625     #[test]
test_intersection()626     fn test_intersection() {
627         let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
628         let q = Rect::new(Point2D::new(5, 15), Size2D::new(10, 10));
629         let r = Rect::new(Point2D::new(-5, -5), Size2D::new(8, 8));
630 
631         let pq = p.intersection(&q);
632         assert!(pq.is_some());
633         let pq = pq.unwrap();
634         assert!(pq.origin == Point2D::new(5, 15));
635         assert!(pq.size == Size2D::new(5, 5));
636 
637         let pr = p.intersection(&r);
638         assert!(pr.is_some());
639         let pr = pr.unwrap();
640         assert!(pr.origin == Point2D::new(0, 0));
641         assert!(pr.size == Size2D::new(3, 3));
642 
643         let qr = q.intersection(&r);
644         assert!(qr.is_none());
645     }
646 
647     #[test]
test_contains()648     fn test_contains() {
649         let r = Rect::new(Point2D::new(-20, 15), Size2D::new(100, 200));
650 
651         assert!(r.contains(Point2D::new(0, 50)));
652         assert!(r.contains(Point2D::new(-10, 200)));
653 
654         // The `contains` method is inclusive of the top/left edges, but not the
655         // bottom/right edges.
656         assert!(r.contains(Point2D::new(-20, 15)));
657         assert!(!r.contains(Point2D::new(80, 15)));
658         assert!(!r.contains(Point2D::new(80, 215)));
659         assert!(!r.contains(Point2D::new(-20, 215)));
660 
661         // Points beyond the top-left corner.
662         assert!(!r.contains(Point2D::new(-25, 15)));
663         assert!(!r.contains(Point2D::new(-15, 10)));
664 
665         // Points beyond the top-right corner.
666         assert!(!r.contains(Point2D::new(85, 20)));
667         assert!(!r.contains(Point2D::new(75, 10)));
668 
669         // Points beyond the bottom-right corner.
670         assert!(!r.contains(Point2D::new(85, 210)));
671         assert!(!r.contains(Point2D::new(75, 220)));
672 
673         // Points beyond the bottom-left corner.
674         assert!(!r.contains(Point2D::new(-25, 210)));
675         assert!(!r.contains(Point2D::new(-15, 220)));
676 
677         let r = Rect::new(Point2D::new(-20.0, 15.0), Size2D::new(100.0, 200.0));
678         assert!(r.contains_rect(&r));
679         assert!(!r.contains_rect(&r.translate(vec2(0.1, 0.0))));
680         assert!(!r.contains_rect(&r.translate(vec2(-0.1, 0.0))));
681         assert!(!r.contains_rect(&r.translate(vec2(0.0, 0.1))));
682         assert!(!r.contains_rect(&r.translate(vec2(0.0, -0.1))));
683         // Empty rectangles are always considered as contained in other rectangles,
684         // even if their origin is not.
685         let p = Point2D::new(1.0, 1.0);
686         assert!(!r.contains(p));
687         assert!(r.contains_rect(&Rect::new(p, Size2D::zero())));
688     }
689 
690     #[test]
test_scale()691     fn test_scale() {
692         let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
693         let pp = p.scale(10, 15);
694 
695         assert!(pp.size.width == 500);
696         assert!(pp.size.height == 600);
697         assert!(pp.origin.x == 0);
698         assert!(pp.origin.y == 0);
699 
700         let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
701         let rr = r.scale(1, 20);
702 
703         assert!(rr.size.width == 50);
704         assert!(rr.size.height == 800);
705         assert!(rr.origin.x == -10);
706         assert!(rr.origin.y == -100);
707     }
708 
709     #[test]
test_inflate()710     fn test_inflate() {
711         let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 10));
712         let pp = p.inflate(10, 20);
713 
714         assert!(pp.size.width == 30);
715         assert!(pp.size.height == 50);
716         assert!(pp.origin.x == -10);
717         assert!(pp.origin.y == -20);
718 
719         let r = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
720         let rr = r.inflate(-2, -5);
721 
722         assert!(rr.size.width == 6);
723         assert!(rr.size.height == 10);
724         assert!(rr.origin.x == 2);
725         assert!(rr.origin.y == 5);
726     }
727 
728     #[test]
test_inner_outer_rect()729     fn test_inner_outer_rect() {
730         let inner_rect = Rect::new(point2(20, 40), size2(80, 100));
731         let offsets = SideOffsets2D::new(20, 10, 10, 10);
732         let outer_rect = inner_rect.outer_rect(offsets);
733         assert_eq!(outer_rect.origin.x, 10);
734         assert_eq!(outer_rect.origin.y, 20);
735         assert_eq!(outer_rect.size.width, 100);
736         assert_eq!(outer_rect.size.height, 130);
737         assert_eq!(outer_rect.inner_rect(offsets), inner_rect);
738     }
739 
740     #[test]
test_min_max_x_y()741     fn test_min_max_x_y() {
742         let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
743         assert!(p.max_y() == 40);
744         assert!(p.min_y() == 0);
745         assert!(p.max_x() == 50);
746         assert!(p.min_x() == 0);
747 
748         let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
749         assert!(r.max_y() == 35);
750         assert!(r.min_y() == -5);
751         assert!(r.max_x() == 40);
752         assert!(r.min_x() == -10);
753     }
754 
755     #[test]
test_is_empty()756     fn test_is_empty() {
757         assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 0u32)).is_empty());
758         assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(10u32, 0u32)).is_empty());
759         assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 10u32)).is_empty());
760         assert!(!Rect::new(Point2D::new(0u32, 0u32), Size2D::new(1u32, 1u32)).is_empty());
761         assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 0u32)).is_empty());
762         assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(10u32, 0u32)).is_empty());
763         assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 10u32)).is_empty());
764         assert!(!Rect::new(Point2D::new(10u32, 10u32), Size2D::new(1u32, 1u32)).is_empty());
765     }
766 
767     #[test]
test_round()768     fn test_round() {
769         let mut x = -2.0;
770         let mut y = -2.0;
771         let mut w = -2.0;
772         let mut h = -2.0;
773         while x < 2.0 {
774             while y < 2.0 {
775                 while w < 2.0 {
776                     while h < 2.0 {
777                         let rect = Rect::new(Point2D::new(x, y), Size2D::new(w, h));
778 
779                         assert!(rect.contains_rect(&rect.round_in()));
780                         assert!(rect.round_in().inflate(1.0, 1.0).contains_rect(&rect));
781 
782                         assert!(rect.round_out().contains_rect(&rect));
783                         assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round_out()));
784 
785                         assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round()));
786                         assert!(rect.round().inflate(1.0, 1.0).contains_rect(&rect));
787 
788                         h += 0.1;
789                     }
790                     w += 0.1;
791                 }
792                 y += 0.1;
793             }
794             x += 0.1
795         }
796     }
797 
798     #[test]
test_center()799     fn test_center() {
800         let r: Rect<i32> = rect(-2, 5, 4, 10);
801         assert_eq!(r.center(), point2(0, 10));
802 
803         let r: Rect<f32> = rect(1.0, 2.0, 3.0, 4.0);
804         assert_eq!(r.center(), point2(2.5, 4.0));
805     }
806 }
807