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 crate::box2d::Box2D;
12 use crate::num::*;
13 use crate::point::Point2D;
14 use crate::scale::Scale;
15 use crate::side_offsets::SideOffsets2D;
16 use crate::size::Size2D;
17 use crate::vector::Vector2D;
18 
19 use num_traits::{NumCast, Float};
20 #[cfg(feature = "serde")]
21 use serde::{Deserialize, Serialize};
22 
23 use core::borrow::Borrow;
24 use core::cmp::PartialOrd;
25 use core::fmt;
26 use core::hash::{Hash, Hasher};
27 use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Range, Sub};
28 
29 /// A 2d Rectangle optionally tagged with a unit.
30 ///
31 /// # Representation
32 ///
33 /// `Rect` is represented by an origin point and a size.
34 ///
35 /// See [`Box2D`] for a rectangle represented by two endpoints.
36 ///
37 /// # Empty rectangle
38 ///
39 /// A rectangle is considered empty (see [`is_empty`]) if any of the following is true:
40 /// - it's area is empty,
41 /// - it's area is negative (`size.x < 0` or `size.y < 0`),
42 /// - it contains NaNs.
43 ///
44 /// [`is_empty`]: #method.is_empty
45 /// [`Box2D`]: struct.Box2D.html
46 #[repr(C)]
47 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48 #[cfg_attr(
49     feature = "serde",
50     serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
51 )]
52 pub struct Rect<T, U> {
53     pub origin: Point2D<T, U>,
54     pub size: Size2D<T, U>,
55 }
56 
57 #[cfg(feature = "arbitrary")]
58 impl<'a, T, U> arbitrary::Arbitrary<'a> for Rect<T, U>
59 where
60     T: arbitrary::Arbitrary<'a>,
61 {
arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self>62     fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self>
63     {
64         let (origin, size) = arbitrary::Arbitrary::arbitrary(u)?;
65         Ok(Rect {
66             origin,
67             size,
68         })
69     }
70 }
71 
72 impl<T: Hash, U> Hash for Rect<T, U> {
hash<H: Hasher>(&self, h: &mut H)73     fn hash<H: Hasher>(&self, h: &mut H) {
74         self.origin.hash(h);
75         self.size.hash(h);
76     }
77 }
78 
79 impl<T: Copy, U> Copy for Rect<T, U> {}
80 
81 impl<T: Clone, U> Clone for Rect<T, U> {
clone(&self) -> Self82     fn clone(&self) -> Self {
83         Self::new(self.origin.clone(), self.size.clone())
84     }
85 }
86 
87 impl<T: PartialEq, U> PartialEq for Rect<T, U> {
eq(&self, other: &Self) -> bool88     fn eq(&self, other: &Self) -> bool {
89         self.origin.eq(&other.origin) && self.size.eq(&other.size)
90     }
91 }
92 
93 impl<T: Eq, U> Eq for Rect<T, U> {}
94 
95 impl<T: fmt::Debug, U> fmt::Debug for Rect<T, U> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result96     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97         write!(f, "Rect(")?;
98         fmt::Debug::fmt(&self.size, f)?;
99         write!(f, " at ")?;
100         fmt::Debug::fmt(&self.origin, f)?;
101         write!(f, ")")
102     }
103 }
104 
105 impl<T: Default, U> Default for Rect<T, U> {
default() -> Self106     fn default() -> Self {
107         Rect::new(Default::default(), Default::default())
108     }
109 }
110 
111 impl<T, U> Rect<T, U> {
112     /// Constructor.
113     #[inline]
new(origin: Point2D<T, U>, size: Size2D<T, U>) -> Self114     pub const fn new(origin: Point2D<T, U>, size: Size2D<T, U>) -> Self {
115         Rect { origin, size }
116     }
117 }
118 
119 impl<T, U> Rect<T, U>
120 where
121     T: Zero,
122 {
123     /// Constructor, setting all sides to zero.
124     #[inline]
zero() -> Self125     pub fn zero() -> Self {
126         Rect::new(Point2D::origin(), Size2D::zero())
127     }
128 
129     /// Creates a rect of the given size, at offset zero.
130     #[inline]
from_size(size: Size2D<T, U>) -> Self131     pub fn from_size(size: Size2D<T, U>) -> Self {
132         Rect {
133             origin: Point2D::zero(),
134             size,
135         }
136     }
137 }
138 
139 impl<T, U> Rect<T, U>
140 where
141     T: Copy + Add<T, Output = T>,
142 {
143     #[inline]
min(&self) -> Point2D<T, U>144     pub fn min(&self) -> Point2D<T, U> {
145         self.origin
146     }
147 
148     #[inline]
max(&self) -> Point2D<T, U>149     pub fn max(&self) -> Point2D<T, U> {
150         self.origin + self.size
151     }
152 
153     #[inline]
max_x(&self) -> T154     pub fn max_x(&self) -> T {
155         self.origin.x + self.size.width
156     }
157 
158     #[inline]
min_x(&self) -> T159     pub fn min_x(&self) -> T {
160         self.origin.x
161     }
162 
163     #[inline]
max_y(&self) -> T164     pub fn max_y(&self) -> T {
165         self.origin.y + self.size.height
166     }
167 
168     #[inline]
min_y(&self) -> T169     pub fn min_y(&self) -> T {
170         self.origin.y
171     }
172 
173     #[inline]
width(&self) -> T174     pub fn width(&self) -> T {
175         self.size.width
176     }
177 
178     #[inline]
height(&self) -> T179     pub fn height(&self) -> T {
180         self.size.height
181     }
182 
183     #[inline]
x_range(&self) -> Range<T>184     pub fn x_range(&self) -> Range<T> {
185         self.min_x()..self.max_x()
186     }
187 
188     #[inline]
y_range(&self) -> Range<T>189     pub fn y_range(&self) -> Range<T> {
190         self.min_y()..self.max_y()
191     }
192 
193     /// Returns the same rectangle, translated by a vector.
194     #[inline]
195     #[must_use]
translate(&self, by: Vector2D<T, U>) -> Self196     pub fn translate(&self, by: Vector2D<T, U>) -> Self {
197         Self::new(self.origin + by, self.size)
198     }
199 
200     #[inline]
to_box2d(&self) -> Box2D<T, U>201     pub fn to_box2d(&self) -> Box2D<T, U> {
202         Box2D {
203             min: self.min(),
204             max: self.max(),
205         }
206     }
207 }
208 
209 impl<T, U> Rect<T, U>
210 where
211     T: Copy + PartialOrd + Add<T, Output = T>,
212 {
213     /// Returns true if this rectangle contains the point. Points are considered
214     /// in the rectangle if they are on the left or top edge, but outside if they
215     /// are on the right or bottom edge.
216     #[inline]
contains(&self, p: Point2D<T, U>) -> bool217     pub fn contains(&self, p: Point2D<T, U>) -> bool {
218         self.to_box2d().contains(p)
219     }
220 
221     #[inline]
intersects(&self, other: &Self) -> bool222     pub fn intersects(&self, other: &Self) -> bool {
223         self.to_box2d().intersects(&other.to_box2d())
224     }
225 }
226 
227 impl<T, U> Rect<T, U>
228 where
229     T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
230 {
231     #[inline]
intersection(&self, other: &Self) -> Option<Self>232     pub fn intersection(&self, other: &Self) -> Option<Self> {
233         let box2d = self.to_box2d().intersection_unchecked(&other.to_box2d());
234 
235         if box2d.is_empty() {
236             return None;
237         }
238 
239         Some(box2d.to_rect())
240     }
241 }
242 
243 impl<T, U> Rect<T, U>
244 where
245     T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
246 {
247     #[inline]
248     #[must_use]
inflate(&self, width: T, height: T) -> Self249     pub fn inflate(&self, width: T, height: T) -> Self {
250         Rect::new(
251             Point2D::new(self.origin.x - width, self.origin.y - height),
252             Size2D::new(
253                 self.size.width + width + width,
254                 self.size.height + height + height,
255             ),
256         )
257     }
258 }
259 
260 impl<T, U> Rect<T, U>
261 where
262     T: Copy + Zero + PartialOrd + Add<T, Output = T>,
263 {
264     /// Returns true if this rectangle contains the interior of rect. Always
265     /// returns true if rect is empty, and always returns false if rect is
266     /// nonempty but this rectangle is empty.
267     #[inline]
contains_rect(&self, rect: &Self) -> bool268     pub fn contains_rect(&self, rect: &Self) -> bool {
269         rect.is_empty()
270             || (self.min_x() <= rect.min_x()
271                 && rect.max_x() <= self.max_x()
272                 && self.min_y() <= rect.min_y()
273                 && rect.max_y() <= self.max_y())
274     }
275 }
276 
277 impl<T, U> Rect<T, U>
278 where
279     T: Copy + Zero + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
280 {
281     /// Calculate the size and position of an inner rectangle.
282     ///
283     /// Subtracts the side offsets from all sides. The horizontal and vertical
284     /// offsets must not be larger than the original side length.
285     /// This method assumes y oriented downward.
inner_rect(&self, offsets: SideOffsets2D<T, U>) -> Self286     pub fn inner_rect(&self, offsets: SideOffsets2D<T, U>) -> Self {
287         let rect = Rect::new(
288             Point2D::new(self.origin.x + offsets.left, self.origin.y + offsets.top),
289             Size2D::new(
290                 self.size.width - offsets.horizontal(),
291                 self.size.height - offsets.vertical(),
292             ),
293         );
294         debug_assert!(rect.size.width >= Zero::zero());
295         debug_assert!(rect.size.height >= Zero::zero());
296         rect
297     }
298 }
299 
300 impl<T, U> Rect<T, U>
301 where
302     T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
303 {
304     /// Calculate the size and position of an outer rectangle.
305     ///
306     /// Add the offsets to all sides. The expanded rectangle is returned.
307     /// This method assumes y oriented downward.
outer_rect(&self, offsets: SideOffsets2D<T, U>) -> Self308     pub fn outer_rect(&self, offsets: SideOffsets2D<T, U>) -> Self {
309         Rect::new(
310             Point2D::new(self.origin.x - offsets.left, self.origin.y - offsets.top),
311             Size2D::new(
312                 self.size.width + offsets.horizontal(),
313                 self.size.height + offsets.vertical(),
314             ),
315         )
316     }
317 }
318 
319 impl<T, U> Rect<T, U>
320 where
321     T: Copy + Zero + PartialOrd + Sub<T, Output = T>,
322 {
323     /// Returns the smallest rectangle defined by the top/bottom/left/right-most
324     /// points provided as parameter.
325     ///
326     /// Note: This function has a behavior that can be surprising because
327     /// the right-most and bottom-most points are exactly on the edge
328     /// of the rectangle while the `contains` function is has exclusive
329     /// semantic on these edges. This means that the right-most and bottom-most
330     /// points provided to `from_points` will count as not contained by the rect.
331     /// This behavior may change in the future.
from_points<I>(points: I) -> Self where I: IntoIterator, I::Item: Borrow<Point2D<T, U>>,332     pub fn from_points<I>(points: I) -> Self
333     where
334         I: IntoIterator,
335         I::Item: Borrow<Point2D<T, U>>,
336     {
337         Box2D::from_points(points).to_rect()
338     }
339 }
340 
341 impl<T, U> Rect<T, U>
342 where
343     T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
344 {
345     /// Linearly interpolate between this rectangle and another rectangle.
346     #[inline]
lerp(&self, other: Self, t: T) -> Self347     pub fn lerp(&self, other: Self, t: T) -> Self {
348         Self::new(
349             self.origin.lerp(other.origin, t),
350             self.size.lerp(other.size, t),
351         )
352     }
353 }
354 
355 impl<T, U> Rect<T, U>
356 where
357     T: Copy + One + Add<Output = T> + Div<Output = T>,
358 {
center(&self) -> Point2D<T, U>359     pub fn center(&self) -> Point2D<T, U> {
360         let two = T::one() + T::one();
361         self.origin + self.size.to_vector() / two
362     }
363 }
364 
365 impl<T, U> Rect<T, U>
366 where
367     T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T> + Zero,
368 {
369     #[inline]
union(&self, other: &Self) -> Self370     pub fn union(&self, other: &Self) -> Self {
371         self.to_box2d().union(&other.to_box2d()).to_rect()
372     }
373 }
374 
375 impl<T, U> Rect<T, U> {
376     #[inline]
scale<S: Copy>(&self, x: S, y: S) -> Self where T: Copy + Mul<S, Output = T>,377     pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
378     where
379         T: Copy + Mul<S, Output = T>,
380     {
381         Rect::new(
382             Point2D::new(self.origin.x * x, self.origin.y * y),
383             Size2D::new(self.size.width * x, self.size.height * y),
384         )
385     }
386 }
387 
388 impl<T: Copy + Mul<T, Output = T>, U> Rect<T, U> {
389     #[inline]
area(&self) -> T390     pub fn area(&self) -> T {
391         self.size.area()
392     }
393 }
394 
395 impl<T: Copy + Zero + PartialOrd, U> Rect<T, U> {
396     #[inline]
is_empty(&self) -> bool397     pub fn is_empty(&self) -> bool {
398         self.size.is_empty()
399     }
400 }
401 
402 impl<T: Copy + Zero + PartialOrd, U> Rect<T, U> {
403     #[inline]
to_non_empty(&self) -> Option<Self>404     pub fn to_non_empty(&self) -> Option<Self> {
405         if self.is_empty() {
406             return None;
407         }
408 
409         Some(*self)
410     }
411 }
412 
413 impl<T: Copy + Mul, U> Mul<T> for Rect<T, U> {
414     type Output = Rect<T::Output, U>;
415 
416     #[inline]
mul(self, scale: T) -> Self::Output417     fn mul(self, scale: T) -> Self::Output {
418         Rect::new(self.origin * scale, self.size * scale)
419     }
420 }
421 
422 impl<T: Copy + MulAssign, U> MulAssign<T> for Rect<T, U> {
423     #[inline]
mul_assign(&mut self, scale: T)424     fn mul_assign(&mut self, scale: T) {
425         *self *= Scale::new(scale);
426     }
427 }
428 
429 impl<T: Copy + Div, U> Div<T> for Rect<T, U> {
430     type Output = Rect<T::Output, U>;
431 
432     #[inline]
div(self, scale: T) -> Self::Output433     fn div(self, scale: T) -> Self::Output {
434         Rect::new(self.origin / scale.clone(), self.size / scale)
435     }
436 }
437 
438 impl<T: Copy + DivAssign, U> DivAssign<T> for Rect<T, U> {
439     #[inline]
div_assign(&mut self, scale: T)440     fn div_assign(&mut self, scale: T) {
441         *self /= Scale::new(scale);
442     }
443 }
444 
445 impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Rect<T, U1> {
446     type Output = Rect<T::Output, U2>;
447 
448     #[inline]
mul(self, scale: Scale<T, U1, U2>) -> Self::Output449     fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
450         Rect::new(self.origin * scale.clone(), self.size * scale)
451     }
452 }
453 
454 impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Rect<T, U> {
455     #[inline]
mul_assign(&mut self, scale: Scale<T, U, U>)456     fn mul_assign(&mut self, scale: Scale<T, U, U>) {
457         self.origin *= scale.clone();
458         self.size *= scale;
459     }
460 }
461 
462 impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Rect<T, U2> {
463     type Output = Rect<T::Output, U1>;
464 
465     #[inline]
div(self, scale: Scale<T, U1, U2>) -> Self::Output466     fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
467         Rect::new(self.origin / scale.clone(), self.size / scale)
468     }
469 }
470 
471 impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Rect<T, U> {
472     #[inline]
div_assign(&mut self, scale: Scale<T, U, U>)473     fn div_assign(&mut self, scale: Scale<T, U, U>) {
474         self.origin /= scale.clone();
475         self.size /= scale;
476     }
477 }
478 
479 impl<T: Copy, U> Rect<T, U> {
480     /// Drop the units, preserving only the numeric value.
481     #[inline]
to_untyped(&self) -> Rect<T, UnknownUnit>482     pub fn to_untyped(&self) -> Rect<T, UnknownUnit> {
483         Rect::new(self.origin.to_untyped(), self.size.to_untyped())
484     }
485 
486     /// Tag a unitless value with units.
487     #[inline]
from_untyped(r: &Rect<T, UnknownUnit>) -> Rect<T, U>488     pub fn from_untyped(r: &Rect<T, UnknownUnit>) -> Rect<T, U> {
489         Rect::new(
490             Point2D::from_untyped(r.origin),
491             Size2D::from_untyped(r.size),
492         )
493     }
494 
495     /// Cast the unit
496     #[inline]
cast_unit<V>(&self) -> Rect<T, V>497     pub fn cast_unit<V>(&self) -> Rect<T, V> {
498         Rect::new(self.origin.cast_unit(), self.size.cast_unit())
499     }
500 }
501 
502 impl<T: NumCast + Copy, U> Rect<T, U> {
503     /// Cast from one numeric representation to another, preserving the units.
504     ///
505     /// When casting from floating point to integer coordinates, the decimals are truncated
506     /// as one would expect from a simple cast, but this behavior does not always make sense
507     /// geometrically. Consider using round(), round_in or round_out() before casting.
508     #[inline]
cast<NewT: NumCast>(&self) -> Rect<NewT, U>509     pub fn cast<NewT: NumCast>(&self) -> Rect<NewT, U> {
510         Rect::new(self.origin.cast(), self.size.cast())
511     }
512 
513     /// Fallible cast from one numeric representation to another, preserving the units.
514     ///
515     /// When casting from floating point to integer coordinates, the decimals are truncated
516     /// as one would expect from a simple cast, but this behavior does not always make sense
517     /// geometrically. Consider using round(), round_in or round_out() before casting.
try_cast<NewT: NumCast>(&self) -> Option<Rect<NewT, U>>518     pub fn try_cast<NewT: NumCast>(&self) -> Option<Rect<NewT, U>> {
519         match (self.origin.try_cast(), self.size.try_cast()) {
520             (Some(origin), Some(size)) => Some(Rect::new(origin, size)),
521             _ => None,
522         }
523     }
524 
525     // Convenience functions for common casts
526 
527     /// Cast into an `f32` rectangle.
528     #[inline]
to_f32(&self) -> Rect<f32, U>529     pub fn to_f32(&self) -> Rect<f32, U> {
530         self.cast()
531     }
532 
533     /// Cast into an `f64` rectangle.
534     #[inline]
to_f64(&self) -> Rect<f64, U>535     pub fn to_f64(&self) -> Rect<f64, U> {
536         self.cast()
537     }
538 
539     /// Cast into an `usize` 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.
544     #[inline]
to_usize(&self) -> Rect<usize, U>545     pub fn to_usize(&self) -> Rect<usize, U> {
546         self.cast()
547     }
548 
549     /// Cast into an `u32` rectangle, truncating decimals if any.
550     ///
551     /// When casting from floating point rectangles, it is worth considering whether
552     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
553     /// obtain the desired conversion behavior.
554     #[inline]
to_u32(&self) -> Rect<u32, U>555     pub fn to_u32(&self) -> Rect<u32, U> {
556         self.cast()
557     }
558 
559     /// Cast into an `u64` rectangle, truncating decimals if any.
560     ///
561     /// When casting from floating point rectangles, it is worth considering whether
562     /// to `round()`, `round_in()` or `round_out()` before the cast in order to
563     /// obtain the desired conversion behavior.
564     #[inline]
to_u64(&self) -> Rect<u64, U>565     pub fn to_u64(&self) -> Rect<u64, U> {
566         self.cast()
567     }
568 
569     /// Cast into an `i32` rectangle, truncating decimals if any.
570     ///
571     /// When casting from floating point rectangles, 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_i32(&self) -> Rect<i32, U>575     pub fn to_i32(&self) -> Rect<i32, U> {
576         self.cast()
577     }
578 
579     /// Cast into an `i64` rectangle, truncating decimals if any.
580     ///
581     /// When casting from floating point rectangles, 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_i64(&self) -> Rect<i64, U>585     pub fn to_i64(&self) -> Rect<i64, U> {
586         self.cast()
587     }
588 }
589 
590 impl<T: Float, U> Rect<T, U> {
591     /// Returns true if all members are finite.
592     #[inline]
is_finite(self) -> bool593     pub fn is_finite(self) -> bool {
594         self.origin.is_finite() && self.size.is_finite()
595     }
596 }
597 
598 impl<T: Floor + Ceil + Round + Add<T, Output = T> + Sub<T, Output = T>, U> Rect<T, U> {
599     /// Return a rectangle with edges rounded to integer coordinates, such that
600     /// the returned rectangle has the same set of pixel centers as the original
601     /// one.
602     /// Edges at offset 0.5 round up.
603     /// Suitable for most places where integral device coordinates
604     /// are needed, but note that any translation should be applied first to
605     /// avoid pixel rounding errors.
606     /// Note that this is *not* rounding to nearest integer if the values are negative.
607     /// They are always rounding as floor(n + 0.5).
608     ///
609     /// # Usage notes
610     /// Note, that when using with floating-point `T` types that method can significantly
611     /// loose precision for large values, so if you need to call this method very often it
612     /// is better to use [`Box2D`].
613     ///
614     /// [`Box2D`]: struct.Box2D.html
615     #[must_use]
round(&self) -> Self616     pub fn round(&self) -> Self {
617         self.to_box2d().round().to_rect()
618     }
619 
620     /// Return a rectangle with edges rounded to integer coordinates, such that
621     /// the original rectangle contains the resulting rectangle.
622     ///
623     /// # Usage notes
624     /// Note, that when using with floating-point `T` types that method can significantly
625     /// loose precision for large values, so if you need to call this method very often it
626     /// is better to use [`Box2D`].
627     ///
628     /// [`Box2D`]: struct.Box2D.html
629     #[must_use]
round_in(&self) -> Self630     pub fn round_in(&self) -> Self {
631         self.to_box2d().round_in().to_rect()
632     }
633 
634     /// Return a rectangle with edges rounded to integer coordinates, such that
635     /// the original rectangle is contained in the resulting rectangle.
636     ///
637     /// # Usage notes
638     /// Note, that when using with floating-point `T` types that method can significantly
639     /// loose precision for large values, so if you need to call this method very often it
640     /// is better to use [`Box2D`].
641     ///
642     /// [`Box2D`]: struct.Box2D.html
643     #[must_use]
round_out(&self) -> Self644     pub fn round_out(&self) -> Self {
645         self.to_box2d().round_out().to_rect()
646     }
647 }
648 
649 impl<T, U> From<Size2D<T, U>> for Rect<T, U>
650 where
651     T: Zero,
652 {
from(size: Size2D<T, U>) -> Self653     fn from(size: Size2D<T, U>) -> Self {
654         Self::from_size(size)
655     }
656 }
657 
658 /// Shorthand for `Rect::new(Point2D::new(x, y), Size2D::new(w, h))`.
rect<T, U>(x: T, y: T, w: T, h: T) -> Rect<T, U>659 pub const fn rect<T, U>(x: T, y: T, w: T, h: T) -> Rect<T, U> {
660     Rect::new(Point2D::new(x, y), Size2D::new(w, h))
661 }
662 
663 #[cfg(test)]
664 mod tests {
665     use crate::default::{Point2D, Rect, Size2D};
666     use crate::side_offsets::SideOffsets2D;
667     use crate::{point2, rect, size2, vec2};
668 
669     #[test]
test_translate()670     fn test_translate() {
671         let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
672         let pp = p.translate(vec2(10, 15));
673 
674         assert!(pp.size.width == 50);
675         assert!(pp.size.height == 40);
676         assert!(pp.origin.x == 10);
677         assert!(pp.origin.y == 15);
678 
679         let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
680         let rr = r.translate(vec2(0, -10));
681 
682         assert!(rr.size.width == 50);
683         assert!(rr.size.height == 40);
684         assert!(rr.origin.x == -10);
685         assert!(rr.origin.y == -15);
686     }
687 
688     #[test]
test_union()689     fn test_union() {
690         let p = Rect::new(Point2D::new(0, 0), Size2D::new(50, 40));
691         let q = Rect::new(Point2D::new(20, 20), Size2D::new(5, 5));
692         let r = Rect::new(Point2D::new(-15, -30), Size2D::new(200, 15));
693         let s = Rect::new(Point2D::new(20, -15), Size2D::new(250, 200));
694 
695         let pq = p.union(&q);
696         assert!(pq.origin == Point2D::new(0, 0));
697         assert!(pq.size == Size2D::new(50, 40));
698 
699         let pr = p.union(&r);
700         assert!(pr.origin == Point2D::new(-15, -30));
701         assert!(pr.size == Size2D::new(200, 70));
702 
703         let ps = p.union(&s);
704         assert!(ps.origin == Point2D::new(0, -15));
705         assert!(ps.size == Size2D::new(270, 200));
706     }
707 
708     #[test]
test_intersection()709     fn test_intersection() {
710         let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
711         let q = Rect::new(Point2D::new(5, 15), Size2D::new(10, 10));
712         let r = Rect::new(Point2D::new(-5, -5), Size2D::new(8, 8));
713 
714         let pq = p.intersection(&q);
715         assert!(pq.is_some());
716         let pq = pq.unwrap();
717         assert!(pq.origin == Point2D::new(5, 15));
718         assert!(pq.size == Size2D::new(5, 5));
719 
720         let pr = p.intersection(&r);
721         assert!(pr.is_some());
722         let pr = pr.unwrap();
723         assert!(pr.origin == Point2D::new(0, 0));
724         assert!(pr.size == Size2D::new(3, 3));
725 
726         let qr = q.intersection(&r);
727         assert!(qr.is_none());
728     }
729 
730     #[test]
test_intersection_overflow()731     fn test_intersection_overflow() {
732         // test some scenarios where the intersection can overflow but
733         // the min_x() and max_x() don't. Gecko currently fails these cases
734         let p = Rect::new(Point2D::new(-2147483648, -2147483648), Size2D::new(0, 0));
735         let q = Rect::new(
736             Point2D::new(2136893440, 2136893440),
737             Size2D::new(279552, 279552),
738         );
739         let r = Rect::new(Point2D::new(-2147483648, -2147483648), Size2D::new(1, 1));
740 
741         assert!(p.is_empty());
742         let pq = p.intersection(&q);
743         assert!(pq.is_none());
744 
745         let qr = q.intersection(&r);
746         assert!(qr.is_none());
747     }
748 
749     #[test]
test_contains()750     fn test_contains() {
751         let r = Rect::new(Point2D::new(-20, 15), Size2D::new(100, 200));
752 
753         assert!(r.contains(Point2D::new(0, 50)));
754         assert!(r.contains(Point2D::new(-10, 200)));
755 
756         // The `contains` method is inclusive of the top/left edges, but not the
757         // bottom/right edges.
758         assert!(r.contains(Point2D::new(-20, 15)));
759         assert!(!r.contains(Point2D::new(80, 15)));
760         assert!(!r.contains(Point2D::new(80, 215)));
761         assert!(!r.contains(Point2D::new(-20, 215)));
762 
763         // Points beyond the top-left corner.
764         assert!(!r.contains(Point2D::new(-25, 15)));
765         assert!(!r.contains(Point2D::new(-15, 10)));
766 
767         // Points beyond the top-right corner.
768         assert!(!r.contains(Point2D::new(85, 20)));
769         assert!(!r.contains(Point2D::new(75, 10)));
770 
771         // Points beyond the bottom-right corner.
772         assert!(!r.contains(Point2D::new(85, 210)));
773         assert!(!r.contains(Point2D::new(75, 220)));
774 
775         // Points beyond the bottom-left corner.
776         assert!(!r.contains(Point2D::new(-25, 210)));
777         assert!(!r.contains(Point2D::new(-15, 220)));
778 
779         let r = Rect::new(Point2D::new(-20.0, 15.0), Size2D::new(100.0, 200.0));
780         assert!(r.contains_rect(&r));
781         assert!(!r.contains_rect(&r.translate(vec2(0.1, 0.0))));
782         assert!(!r.contains_rect(&r.translate(vec2(-0.1, 0.0))));
783         assert!(!r.contains_rect(&r.translate(vec2(0.0, 0.1))));
784         assert!(!r.contains_rect(&r.translate(vec2(0.0, -0.1))));
785         // Empty rectangles are always considered as contained in other rectangles,
786         // even if their origin is not.
787         let p = Point2D::new(1.0, 1.0);
788         assert!(!r.contains(p));
789         assert!(r.contains_rect(&Rect::new(p, Size2D::zero())));
790     }
791 
792     #[test]
test_scale()793     fn test_scale() {
794         let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
795         let pp = p.scale(10, 15);
796 
797         assert!(pp.size.width == 500);
798         assert!(pp.size.height == 600);
799         assert!(pp.origin.x == 0);
800         assert!(pp.origin.y == 0);
801 
802         let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
803         let rr = r.scale(1, 20);
804 
805         assert!(rr.size.width == 50);
806         assert!(rr.size.height == 800);
807         assert!(rr.origin.x == -10);
808         assert!(rr.origin.y == -100);
809     }
810 
811     #[test]
test_inflate()812     fn test_inflate() {
813         let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 10));
814         let pp = p.inflate(10, 20);
815 
816         assert!(pp.size.width == 30);
817         assert!(pp.size.height == 50);
818         assert!(pp.origin.x == -10);
819         assert!(pp.origin.y == -20);
820 
821         let r = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
822         let rr = r.inflate(-2, -5);
823 
824         assert!(rr.size.width == 6);
825         assert!(rr.size.height == 10);
826         assert!(rr.origin.x == 2);
827         assert!(rr.origin.y == 5);
828     }
829 
830     #[test]
test_inner_outer_rect()831     fn test_inner_outer_rect() {
832         let inner_rect = Rect::new(point2(20, 40), size2(80, 100));
833         let offsets = SideOffsets2D::new(20, 10, 10, 10);
834         let outer_rect = inner_rect.outer_rect(offsets);
835         assert_eq!(outer_rect.origin.x, 10);
836         assert_eq!(outer_rect.origin.y, 20);
837         assert_eq!(outer_rect.size.width, 100);
838         assert_eq!(outer_rect.size.height, 130);
839         assert_eq!(outer_rect.inner_rect(offsets), inner_rect);
840     }
841 
842     #[test]
test_min_max_x_y()843     fn test_min_max_x_y() {
844         let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
845         assert!(p.max_y() == 40);
846         assert!(p.min_y() == 0);
847         assert!(p.max_x() == 50);
848         assert!(p.min_x() == 0);
849 
850         let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
851         assert!(r.max_y() == 35);
852         assert!(r.min_y() == -5);
853         assert!(r.max_x() == 40);
854         assert!(r.min_x() == -10);
855     }
856 
857     #[test]
test_width_height()858     fn test_width_height() {
859         let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
860         assert!(r.width() == 50);
861         assert!(r.height() == 40);
862     }
863 
864     #[test]
test_is_empty()865     fn test_is_empty() {
866         assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 0u32)).is_empty());
867         assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(10u32, 0u32)).is_empty());
868         assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 10u32)).is_empty());
869         assert!(!Rect::new(Point2D::new(0u32, 0u32), Size2D::new(1u32, 1u32)).is_empty());
870         assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 0u32)).is_empty());
871         assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(10u32, 0u32)).is_empty());
872         assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 10u32)).is_empty());
873         assert!(!Rect::new(Point2D::new(10u32, 10u32), Size2D::new(1u32, 1u32)).is_empty());
874     }
875 
876     #[test]
test_round()877     fn test_round() {
878         let mut x = -2.0;
879         let mut y = -2.0;
880         let mut w = -2.0;
881         let mut h = -2.0;
882         while x < 2.0 {
883             while y < 2.0 {
884                 while w < 2.0 {
885                     while h < 2.0 {
886                         let rect = Rect::new(Point2D::new(x, y), Size2D::new(w, h));
887 
888                         assert!(rect.contains_rect(&rect.round_in()));
889                         assert!(rect.round_in().inflate(1.0, 1.0).contains_rect(&rect));
890 
891                         assert!(rect.round_out().contains_rect(&rect));
892                         assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round_out()));
893 
894                         assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round()));
895                         assert!(rect.round().inflate(1.0, 1.0).contains_rect(&rect));
896 
897                         h += 0.1;
898                     }
899                     w += 0.1;
900                 }
901                 y += 0.1;
902             }
903             x += 0.1
904         }
905     }
906 
907     #[test]
test_center()908     fn test_center() {
909         let r: Rect<i32> = rect(-2, 5, 4, 10);
910         assert_eq!(r.center(), point2(0, 10));
911 
912         let r: Rect<f32> = rect(1.0, 2.0, 3.0, 4.0);
913         assert_eq!(r.center(), point2(2.5, 4.0));
914     }
915 
916     #[test]
test_nan()917     fn test_nan() {
918         let r1: Rect<f32> = rect(-2.0, 5.0, 4.0, std::f32::NAN);
919         let r2: Rect<f32> = rect(std::f32::NAN, -1.0, 3.0, 10.0);
920 
921         assert_eq!(r1.intersection(&r2), None);
922     }
923 }
924