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