glamour/
rect.rs

1//! 2D axis-aligned rectangles
2
3use approx::AbsDiffEq;
4use num_traits::ConstZero;
5
6use crate::{
7    Box2, IntUnit, Point2, Size2, Unit, Vector2,
8    traits::{Contains, Intersection, Union},
9    unit::FloatUnit,
10};
11
12/// 2D axis-aligned rectangle represented as "origin" and "size".
13#[cfg_attr(
14    all(not(target_arch = "wasm32"), feature = "wasmtime"),
15    derive(
16        wasmtime::component::ComponentType,
17        wasmtime::component::Lower,
18        wasmtime::component::Lift
19    )
20)]
21#[cfg_attr(
22    all(not(target_arch = "wasm32"), feature = "wasmtime"),
23    component(record)
24)]
25#[cfg_attr(feature = "facet", derive(facet_derive::Facet))]
26#[repr(C)]
27pub struct Rect<U: Unit = f32> {
28    /// Lower bound of the rect.
29    pub origin: Point2<U>,
30    /// Size of the rect.
31    pub size: Size2<U>,
32}
33
34/// SAFETY: All members are `Pod`, and we are `#[repr(C)]`
35unsafe impl<T: Unit> bytemuck::Pod for Rect<T> {}
36/// SAFETY: All members are `Pod`, and we are `#[repr(C)]`
37unsafe impl<T: Unit> bytemuck::Zeroable for Rect<T> {}
38
39impl<T: Unit> Clone for Rect<T> {
40    fn clone(&self) -> Self {
41        *self
42    }
43}
44impl<T: Unit> Copy for Rect<T> {}
45
46impl<T: Unit> Default for Rect<T> {
47    fn default() -> Self {
48        Self::ZERO
49    }
50}
51
52impl<T: Unit> Rect<T> {
53    /// Zero rect (origin = 0.0, size = 0.0).
54    pub const ZERO: Self = Rect {
55        origin: Point2::ZERO,
56        size: Size2::ZERO,
57    };
58
59    /// New Rect from origin/size.
60    pub fn new(origin: Point2<T>, size: Size2<T>) -> Rect<T> {
61        Rect { origin, size }
62    }
63
64    /// Create a Rect from origin and size.
65    ///
66    /// This is similar to `new()`, except that the arguments are converted via `Into` for convenience.
67    pub fn from_origin_and_size(
68        origin: impl Into<Point2<T>>,
69        size: impl Into<Size2<T>>,
70    ) -> Rect<T> {
71        Self::new(origin.into(), size.into())
72    }
73
74    /// Rect at (0.0, 0.0) with `size`.
75    #[inline]
76    #[must_use]
77    pub const fn from_size(size: Size2<T>) -> Self {
78        Rect {
79            origin: Point2::ZERO,
80            size,
81        }
82    }
83
84    /// Create from [`Box2`].
85    ///
86    /// Note: This may lose precision due to floating point arithmetic.
87    #[inline]
88    #[must_use]
89    pub fn from_box(b: Box2<T>) -> Self {
90        Self::from_min_max(b.min, b.max)
91    }
92
93    /// Create from min/max points.
94    #[inline]
95    #[must_use]
96    pub fn from_min_max(min: Point2<T>, max: Point2<T>) -> Self {
97        let size = Size2::from_vector(max - min);
98        let origin = min;
99        Rect { origin, size }
100    }
101
102    /// Calculate the bounding rect that covers all `points`.
103    #[must_use]
104    pub fn from_points<I>(points: I) -> Self
105    where
106        I: IntoIterator<Item = Point2<T>>,
107    {
108        Box2::from_points(points).into()
109    }
110
111    /// Get rect lower bound (`origin`).
112    #[inline]
113    #[must_use]
114    pub const fn min(&self) -> Point2<T> {
115        self.origin
116    }
117
118    /// Get rect upper bound (`origin + size`).
119    #[inline]
120    #[must_use]
121    pub fn max(&self) -> Point2<T> {
122        self.origin + self.size.to_vector()
123    }
124
125    /// Width of the rectangle.
126    #[inline]
127    #[must_use]
128    pub const fn width(&self) -> T::Scalar {
129        self.size.width
130    }
131
132    /// Height of the rectangle.
133    #[inline]
134    #[must_use]
135    pub const fn height(&self) -> T::Scalar {
136        self.size.height
137    }
138
139    /// Range of X coordinates covered by this rectangle.
140    #[inline]
141    #[must_use]
142    pub fn x_range(&self) -> core::ops::Range<T::Scalar> {
143        let (min, max) = (self.min(), self.max());
144        min.x..max.x
145    }
146
147    /// Range of Y coordinates covered by this rectangle.
148    #[inline]
149    #[must_use]
150    pub fn y_range(&self) -> core::ops::Range<T::Scalar> {
151        let (min, max) = (self.min(), self.max());
152        min.y..max.y
153    }
154
155    /// Corners of the rectangle, clockwise from top left.
156    #[must_use]
157    pub fn corners(&self) -> [Point2<T>; 4] {
158        let (min, max) = (self.origin, self.max());
159        let top_left = min;
160        let top_right = (max.x, min.y).into();
161        let bottom_right = max;
162        let bottom_left = (min.x, max.y).into();
163        [top_left, top_right, bottom_right, bottom_left]
164    }
165
166    /// Get the point at the center of the rect.
167    #[inline]
168    #[must_use]
169    pub fn center(&self) -> Point2<T> {
170        self.origin + self.size.to_vector() / (Vector2::ONE + Vector2::ONE)
171    }
172
173    /// Translate a copy of the rect by vector.
174    #[must_use]
175    pub fn translate(self, by: Vector2<T>) -> Self {
176        Rect {
177            origin: self.origin.translate(by),
178            size: self.size,
179        }
180    }
181
182    /// Increase the size of the rect by moving the origin back by the size, and
183    /// increasing the size of the rectangle by `2 * by`.
184    ///
185    /// #### Example
186    ///
187    /// ```rust
188    /// # use glamour::prelude::*;
189    /// let r = Rect::<f32>::from_origin_and_size((10.0, 10.0), (10.0, 10.0));
190    /// let r = r.inflate((10.0, 10.0).into());
191    /// assert_eq!(r.origin, point!(0.0, 0.0));
192    /// assert_eq!(r.size, size!(30.0, 30.0));
193    /// ```
194    #[must_use]
195    pub fn inflate(&self, by: Size2<T>) -> Self {
196        Rect {
197            origin: self.origin - by.to_vector(),
198            size: self.size + by + by,
199        }
200    }
201
202    /// Convert to [`Box2`].
203    #[inline]
204    #[must_use]
205    pub fn to_box2(&self) -> Box2<T> {
206        (*self).into()
207    }
208
209    /// Get the area of the rect (equivalent to `self.size.area()`).
210    #[inline]
211    #[must_use]
212    pub fn area(&self) -> T::Scalar {
213        self.size.area()
214    }
215
216    /// True if size is zero or negative or NaN.
217    #[inline]
218    #[must_use]
219    pub fn is_empty(&self) -> bool {
220        self.size.is_empty()
221    }
222
223    /// True if size is negative or NaN.
224    #[inline]
225    #[must_use]
226    pub fn is_negative(&self) -> bool {
227        !(self.size.width >= T::Scalar::ZERO && self.size.height >= T::Scalar::ZERO)
228    }
229
230    /// Convert to (origin, size) tuple.
231    #[inline]
232    #[must_use]
233    pub fn to_tuple(self) -> (Point2<T>, Size2<T>) {
234        (self.origin, self.size)
235    }
236
237    /// New rect from (origin, size) tuple.
238    #[inline]
239    #[must_use]
240    pub fn from_tuple((origin, size): (Point2<T>, Size2<T>)) -> Self {
241        Self::new(origin, size)
242    }
243
244    /// Bitcast an untyped instance to self.
245    #[inline]
246    #[must_use]
247    pub fn from_untyped(untyped: Rect<T::Scalar>) -> Self {
248        Rect {
249            origin: Point2::from_untyped(untyped.origin),
250            size: Size2::from_untyped(untyped.size),
251        }
252    }
253
254    /// Bitcast to an untyped scalar unit.
255    #[inline]
256    #[must_use]
257    pub fn to_untyped(self) -> Rect<T::Scalar> {
258        Rect {
259            origin: self.origin.to_untyped(),
260            size: self.size.to_untyped(),
261        }
262    }
263
264    /// Cast to a different coordinate space with scalar type conversion. Returns `None` if any component could not be
265    /// converted to the target scalar type.
266    #[inline]
267    #[must_use]
268    pub fn try_cast<T2>(self) -> Option<Rect<T2>>
269    where
270        T2: Unit,
271    {
272        Some(Rect {
273            origin: self.origin.try_cast()?,
274            size: self.size.try_cast()?,
275        })
276    }
277
278    /// Cast to a different coordinate space with scalar type conversion through the `as` operator (potentially
279    /// narrowing or losing precision).
280    #[must_use]
281    pub fn as_<T2>(self) -> Rect<T2>
282    where
283        T: Unit<Scalar: num_traits::AsPrimitive<T2::Scalar>>,
284        T2: Unit,
285    {
286        Rect {
287            origin: self.origin.as_(),
288            size: self.size.as_(),
289        }
290    }
291}
292
293impl<T: FloatUnit> Rect<T> {
294    /// True if the rect only contains finite and non-NaN coordinates.
295    #[inline]
296    #[must_use]
297    pub fn is_finite(&self) -> bool {
298        self.origin.is_finite() && self.size.is_finite()
299    }
300
301    /// Round all coordinates.
302    ///
303    /// Note: This may create an empty rect from a non-empty rect.
304    ///
305    /// #### Example
306    ///
307    /// ```rust
308    /// # use glamour::prelude::*;
309    /// let r = Rect::<f32>::from_origin_and_size((0.51, 0.49), (0.51, 0.49));
310    /// let r = r.round();
311    /// assert_eq!(r, Rect::<f32>::new(point!(1.0, 0.0), size!(1.0, 0.0)));
312    /// ```
313    #[inline]
314    #[must_use]
315    pub fn round(self) -> Self {
316        Rect {
317            origin: self.origin.round(),
318            size: self.size.round(),
319        }
320    }
321
322    /// Round all coordinates towards the center of the rect.
323    ///
324    /// This function needs to convert the rect to a [`Box2`] before rounding,
325    /// which loses both performance and precision. Use [`Box2`] if you need to
326    /// perform this operation frequently.
327    ///
328    /// Note: This may create an empty rect from a non-empty rect.
329    ///
330    /// #### Example
331    ///
332    /// ```rust
333    /// # use glamour::prelude::*;
334    /// let r = Rect::<f32>::from_origin_and_size((0.51, 0.49), (1.51, 1.49));
335    /// let r = r.round_in();
336    /// assert_eq!(r, Rect::<f32>::new(point!(1.0, 1.0), size!(1.0, 0.0)));
337    /// ```
338    #[inline]
339    #[must_use]
340    pub fn round_in(self) -> Self {
341        self.to_box2().round_in().to_rect()
342    }
343
344    /// Round all coordinates away from the center of the rect.
345    ///
346    /// This function needs to convert the rect to a [`Box2`] before rounding,
347    /// which loses both performance and precision. Use [`Box2`] if you need to
348    /// perform this operation frequently.
349    ///
350    /// Note: As opposed to [`Rect::round()`] and [`Rect::round_in()`], this
351    /// will not create an empty rect from a non-empty rect.
352    ///
353    /// #### Example
354    /// ```rust
355    /// # use glamour::prelude::*;
356    /// let r = Rect::<f32>::from_origin_and_size((0.51, 0.49), (1.51, 1.49));
357    /// let r = r.round_out();
358    /// assert_eq!(r, Rect::new(point!(0.0, 0.0), size!(3.0, 2.0)));
359    /// ```
360    #[inline]
361    #[must_use]
362    pub fn round_out(self) -> Self {
363        self.to_box2().round_out().to_rect()
364    }
365
366    /// Linear interpolation between two rects.
367    #[inline]
368    #[must_use]
369    pub fn lerp(self, other: Self, t: T::Scalar) -> Self {
370        Rect {
371            origin: self.origin.lerp(other.origin, t),
372            size: self.size.lerp(other.size, t),
373        }
374    }
375}
376
377impl<T: Unit> PartialEq for Rect<T> {
378    #[inline]
379    fn eq(&self, other: &Self) -> bool {
380        self.origin == other.origin && self.size == other.size
381    }
382}
383impl<T: IntUnit> Eq for Rect<T> {}
384
385impl<T: FloatUnit> AbsDiffEq for Rect<T> {
386    type Epsilon = <T::Scalar as approx::AbsDiffEq>::Epsilon;
387
388    #[must_use]
389    fn default_epsilon() -> Self::Epsilon {
390        T::Scalar::default_epsilon()
391    }
392
393    #[must_use]
394    fn abs_diff_eq(&self, other: &Rect<T>, epsilon: Self::Epsilon) -> bool {
395        self.origin.abs_diff_eq(&other.origin, epsilon)
396            && self.size.abs_diff_eq(&other.size, epsilon)
397    }
398
399    #[must_use]
400    fn abs_diff_ne(&self, other: &Rect<T>, epsilon: Self::Epsilon) -> bool {
401        self.origin.abs_diff_ne(&other.origin, epsilon)
402            || self.size.abs_diff_ne(&other.size, epsilon)
403    }
404}
405
406impl<T: FloatUnit> approx::RelativeEq for Rect<T> {
407    #[must_use]
408    fn default_max_relative() -> Self::Epsilon {
409        T::Scalar::default_max_relative()
410    }
411
412    #[must_use]
413    fn relative_eq(
414        &self,
415        other: &Rect<T>,
416        epsilon: Self::Epsilon,
417        max_relative: Self::Epsilon,
418    ) -> bool {
419        self.origin
420            .relative_eq(&other.origin, epsilon, max_relative)
421            && self.size.relative_eq(&other.size, epsilon, max_relative)
422    }
423
424    #[must_use]
425    fn relative_ne(
426        &self,
427        other: &Rect<T>,
428        epsilon: Self::Epsilon,
429        max_relative: Self::Epsilon,
430    ) -> bool {
431        self.origin
432            .relative_ne(&other.origin, epsilon, max_relative)
433            || self.size.relative_ne(&other.size, epsilon, max_relative)
434    }
435}
436
437impl<T: FloatUnit> approx::UlpsEq for Rect<T> {
438    #[must_use]
439    fn default_max_ulps() -> u32 {
440        T::Scalar::default_max_ulps()
441    }
442
443    #[must_use]
444    fn ulps_eq(&self, other: &Rect<T>, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
445        self.origin.ulps_eq(&other.origin, epsilon, max_ulps)
446            && self.size.ulps_eq(&other.size, epsilon, max_ulps)
447    }
448
449    #[must_use]
450    fn ulps_ne(&self, other: &Rect<T>, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
451        self.origin.ulps_ne(&other.origin, epsilon, max_ulps)
452            || self.size.ulps_ne(&other.size, epsilon, max_ulps)
453    }
454}
455
456impl<T: Unit> Contains<Point2<T>> for Rect<T> {
457    #[inline]
458    #[must_use]
459    fn contains(&self, point: &Point2<T>) -> bool {
460        // Try to avoid losing too much precision.
461        let p2 = *point - self.origin;
462        point.x >= self.origin.x
463            && point.y >= self.origin.y
464            && p2.x <= self.size.width
465            && p2.y <= self.size.height
466    }
467}
468
469impl<T: Unit> Intersection<Point2<T>> for Rect<T> {
470    type Intersection = Point2<T>;
471
472    fn intersects(&self, thing: &Point2<T>) -> bool {
473        let diff = *thing - self.origin;
474        diff.x >= T::Scalar::ZERO
475            && diff.y >= T::Scalar::ZERO
476            && diff.x < self.size.width
477            && diff.y < self.size.height
478    }
479
480    fn intersection(&self, thing: &Point2<T>) -> Option<Self::Intersection> {
481        if self.intersects(thing) {
482            Some(*thing)
483        } else {
484            None
485        }
486    }
487}
488
489impl<T: Unit> Intersection<Rect<T>> for Rect<T> {
490    type Intersection = Rect<T>;
491
492    #[inline]
493    #[must_use]
494    fn intersects(&self, thing: &Rect<T>) -> bool {
495        self.intersects(&thing.to_box2())
496    }
497
498    #[inline]
499    #[must_use]
500    fn intersection(&self, thing: &Rect<T>) -> Option<Self::Intersection> {
501        self.intersection(&thing.to_box2())
502    }
503}
504
505impl<T: Unit> Intersection<Box2<T>> for Rect<T> {
506    type Intersection = Rect<T>;
507
508    #[inline]
509    #[must_use]
510    fn intersects(&self, thing: &Box2<T>) -> bool {
511        self.to_box2().intersects(thing)
512    }
513
514    #[inline]
515    #[must_use]
516    fn intersection(&self, thing: &Box2<T>) -> Option<Self::Intersection> {
517        self.to_box2().intersection(thing).map(Into::into)
518    }
519}
520
521impl<T> Union<Rect<T>> for Rect<T>
522where
523    T: Unit,
524{
525    type Union = Rect<T>;
526
527    #[inline]
528    #[must_use]
529    fn union(self, other: Self) -> Self {
530        if self.is_empty() {
531            return other;
532        }
533        if other.is_empty() {
534            return self;
535        }
536
537        let origin = self.origin.min(other.origin);
538        let max = self.max().max(other.max());
539        let size = (max - origin).to_size();
540
541        Rect { origin, size }
542    }
543}
544
545impl<T: Unit> core::fmt::Debug for Rect<T> {
546    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
547        f.debug_struct("Rect")
548            .field("origin", &self.origin)
549            .field("size", &self.size)
550            .finish()
551    }
552}
553
554#[cfg(test)]
555mod tests {
556    use approx::{
557        assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne,
558        assert_ulps_eq, assert_ulps_ne,
559    };
560
561    type Rect = super::Rect<i32>;
562    type RectF = super::Rect<f32>;
563    type Size = super::Size2<i32>;
564    type Box2 = super::Box2<i32>;
565    type Point = super::Point2<i32>;
566    type PointF = super::Point2<f32>;
567
568    #[test]
569    fn basics() {
570        let zero = Rect::ZERO;
571        assert_eq!(zero.origin, (0, 0));
572        assert_eq!(zero.size, (0, 0));
573
574        let from_size = Rect::from_size(Size {
575            width: 100,
576            height: 200,
577        });
578        assert_eq!(from_size.origin, (0, 0));
579        assert_eq!(from_size.size, (100, 200));
580        assert_eq!(from_size.min(), from_size.origin);
581        assert_eq!(from_size.max(), (100, 200));
582        assert_eq!(from_size.width(), 100);
583        assert_eq!(from_size.height(), 200);
584
585        let (_origin, _size) = from_size.to_tuple();
586    }
587
588    #[test]
589    fn equality() {
590        use crate::{Point2, Size2};
591        let a = RectF::new(Point2::new(0.0, 0.0), Size2::new(1.0, 1.0));
592        let b = a.translate((0.0, 1.0).into());
593
594        assert_abs_diff_eq!(a, a);
595        assert_relative_eq!(a, a);
596        assert_ulps_eq!(a, a);
597        assert_abs_diff_ne!(a, b);
598        assert_relative_ne!(a, b);
599        assert_ulps_ne!(a, b);
600    }
601
602    #[test]
603    fn from_box() {
604        let b = Box2::new((100, 200).into(), (300, 400).into());
605        let r = Rect::from_box(b);
606        assert_eq!(r.origin, (100, 200));
607        assert_eq!(r.size, (200, 200));
608
609        let r2 = Rect::from_min_max((100, 200).into(), (300, 400).into());
610        assert_eq!(r2, r);
611    }
612
613    #[test]
614    fn from_points() {
615        let points: [Point; 1] = [(100, 100)].map(Point::from);
616
617        let r = Rect::from_points(points);
618        assert_eq!(r, Rect::from_origin_and_size((100, 100), (0, 0)));
619
620        let points: [Point; 10] = [
621            (-10, 10),
622            (10, -10),
623            (200, -2),
624            (1, 1),
625            (2, 2),
626            (3, 4),
627            (5, 6),
628            (250, -11),
629            (1, 1),
630            (1, 1),
631        ]
632        .map(Point::from);
633
634        let r = Rect::from_points(points);
635        assert_eq!(r.origin, (-10, -11));
636        assert_eq!(r.size, (260, 21));
637    }
638
639    #[test]
640    fn xy_range() {
641        let rect = Rect::from_origin_and_size((10, 10), (10, 10));
642
643        let x_range = rect.x_range();
644        assert!(x_range.eq([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]));
645        let y_range = rect.y_range();
646        assert!(y_range.eq([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]));
647    }
648
649    #[test]
650    fn corners() {
651        let r = Rect::from_origin_and_size((10, 11), (12, 13));
652        let [nw, ne, se, sw] = r.corners();
653        assert_eq!(nw, (10, 11));
654        assert_eq!(ne, (22, 11));
655        assert_eq!(se, (22, 24));
656        assert_eq!(sw, (10, 24));
657    }
658
659    #[test]
660    fn center() {
661        let r = Rect::from_origin_and_size((10, 19), (100, 200));
662        assert_eq!(r.center(), (60, 119));
663
664        let r = Rect::from_origin_and_size((-10, -10), (20, 20));
665        assert_eq!(r.center(), (0, 0));
666
667        let r = Rect::from_origin_and_size((0, 0), (-1, -1));
668        assert_eq!(r.center(), (0, 0));
669
670        let r = Rect::from_origin_and_size((0, 0), (-2, -2));
671        assert_eq!(r.center(), (-1, -1));
672    }
673
674    #[test]
675    fn translate() {
676        let r = Rect::from_origin_and_size((10, 20), (11, 12));
677        let r = r.translate(crate::Vector2::new(2, 3));
678        assert_eq!(r.origin, (12, 23));
679        assert_eq!(r.size, (11, 12));
680    }
681
682    #[test]
683    fn negative_empty() {
684        let r = Rect::from_size((-1, -1).into());
685        assert!(r.is_empty());
686        assert!(r.is_negative());
687
688        let r = Rect::from_size((0, 0).into());
689        assert!(r.is_empty());
690        assert!(!r.is_negative());
691
692        let r = Rect::from_size((1, 1).into());
693        assert!(!r.is_empty());
694        assert!(!r.is_negative());
695
696        // Negative zero.
697
698        let r = RectF::from_size((-0.0, 0.0).into());
699        assert!(r.is_empty());
700        assert!(!r.is_negative());
701
702        let r = RectF::from_origin_and_size((1.0, 1.0), (-0.0, -0.0));
703        assert!(r.is_empty());
704        assert!(!r.is_negative());
705
706        // NaN
707
708        let r = RectF::from_size((f32::NAN, f32::NAN).into());
709        assert!(r.is_empty());
710        assert!(r.is_negative());
711
712        let r = RectF::from_origin_and_size((f32::NAN, 1.0), (1.0, 1.0));
713        assert!(!r.is_empty());
714        assert!(!r.is_negative());
715    }
716
717    #[test]
718    fn contains() {
719        use super::Contains;
720
721        let r: RectF = RectF::from_origin_and_size((10.0, 10.0), (10.0, 10.0));
722        assert!(r.contains(&PointF::new(10.0, 10.0)));
723        assert!(r.contains(&PointF::new(20.0, 20.0)));
724        assert!(!r.contains(&PointF::new(10.0, 9.999_999)));
725        assert!(!r.contains(&PointF::new(9.999_999, 10.0)));
726    }
727
728    #[test]
729    fn intersection() {
730        use super::Intersection;
731
732        let r = RectF::from_origin_and_size((10.0, 10.0), (10.0, 10.0));
733        assert!(r.intersects(&PointF::new(10.0, 10.0)));
734        assert!(!r.intersects(&PointF::new(20.0, 20.0)));
735        assert!(!r.intersects(&PointF::new(10.0, 9.999_999)));
736        assert!(!r.intersects(&PointF::new(9.999_999, 10.0)));
737
738        assert_eq!(
739            r.intersection(&PointF::new(10.0, 10.0)),
740            Some(PointF::new(10.0, 10.0))
741        );
742        assert_eq!(r.intersection(&PointF::new(20.0, 20.0)), None);
743        assert_eq!(r.intersection(&PointF::new(10.0, 9.999_999)), None);
744        assert_eq!(r.intersection(&PointF::new(9.999_999, 10.0)), None);
745
746        // r2 covers all of r.
747        let r2 = RectF::from_origin_and_size((5.0, 5.0), (15.0, 15.0));
748        assert!(r2.intersects(&r));
749        assert_eq!(
750            r2.intersection(&r),
751            Some(RectF {
752                origin: (10.0, 10.0).into(),
753                size: (10.0, 10.0).into(),
754            })
755        );
756
757        // r2 covers the lower bound of r.
758        let r2 = RectF::from_origin_and_size((5.0, 5.0), (10.0, 10.0));
759        assert!(r2.intersects(&r));
760        assert_eq!(
761            r2.intersection(&r),
762            Some(RectF {
763                origin: (10.0, 10.0).into(),
764                size: (5.0, 5.0).into(),
765            })
766        );
767    }
768
769    #[test]
770    fn union() {
771        use super::Union;
772
773        let r = RectF::from_origin_and_size((10.0, 10.0), (10.0, 10.0));
774
775        assert_eq!(r.union(RectF::ZERO), r);
776        assert_eq!(RectF::ZERO.union(r), r);
777
778        assert_eq!(r.union(r), r);
779        assert_eq!(
780            r.union(RectF {
781                origin: (0.0, 0.0).into(),
782                size: (30.0, 30.0).into(),
783            }),
784            RectF {
785                origin: (0.0, 0.0).into(),
786                size: (30.0, 30.0).into(),
787            }
788        );
789        assert_eq!(
790            r.union(RectF {
791                origin: (-1.0, -1.0).into(),
792                size: (2.0, 2.0).into(),
793            }),
794            RectF {
795                origin: (-1.0, -1.0).into(),
796                size: (21.0, 21.0).into(),
797            }
798        );
799    }
800
801    #[test]
802    fn lerp() {
803        let src = RectF::from_origin_and_size((0.0, 0.0), (1.0, 1.0));
804        let dst = RectF::from_origin_and_size((1.0, 1.0), (2.0, 2.0));
805        assert_eq!(
806            src.lerp(dst, 0.5),
807            RectF {
808                origin: (0.5, 0.5).into(),
809                size: (1.5, 1.5).into(),
810            }
811        );
812    }
813
814    #[test]
815    fn area() {
816        let r = RectF::from_origin_and_size((-1.0, -1.0), (10.0, 10.0));
817        assert_eq!(r.area(), 100.0);
818        assert_eq!(r.size.area(), r.area());
819    }
820
821    #[test]
822    fn is_finite() {
823        assert!(RectF::ZERO.is_finite());
824        assert!(RectF::new(super::Point2::ZERO, super::Size2::ONE).is_finite());
825
826        let r = RectF::from_origin_and_size((-1.0, -1.0), (10.0, f32::NAN));
827        assert!(!r.is_finite());
828
829        let r = RectF::from_origin_and_size((-1.0, f32::NAN), (10.0, 10.0));
830        assert!(!r.is_finite());
831    }
832
833    #[test]
834    fn to_tuple() {
835        let r = Rect::from_tuple((Point::new(10, 10), Size::new(20, 20)));
836        assert_eq!(r.origin, (10, 10));
837        assert_eq!(r.size, (20, 20));
838
839        let r = Rect::new(
840            crate::Point2 { x: 10, y: 10 },
841            crate::Size2 {
842                width: 20,
843                height: 20,
844            },
845        );
846        let (origin, size) = r.to_tuple();
847        assert_eq!(origin, (10, 10));
848        assert_eq!(size, (20, 20));
849    }
850
851    #[test]
852    fn round_out() {
853        let rect = RectF::from_origin_and_size((0.8, 0.8), (0.1, 0.1)).round_out();
854        assert_eq!(rect.origin, (0.0, 0.0));
855        assert_eq!(rect.size, (1.0, 1.0));
856
857        let rect = RectF::from_origin_and_size((0.8, 0.8), (0.3, 0.3)).round_out();
858        assert_eq!(rect.origin, (0.0, 0.0));
859        assert_eq!(rect.size, (2.0, 2.0));
860
861        let rect = RectF::from_origin_and_size((0.1, 0.1), (0.3, 0.3)).round_out();
862        assert_eq!(rect.origin, (0.0, 0.0));
863        assert_eq!(rect.size, (1.0, 1.0));
864    }
865
866    #[test]
867    fn casting() {
868        let r = RectF::from_origin_and_size((0.1, 0.2), (0.3, 0.4));
869        let r2 = r.try_cast::<i32>().unwrap();
870        assert_eq!(r2.origin, (0, 0));
871        assert_eq!(r2.size, (0, 0));
872
873        let r2 = r.as_::<i32>();
874        assert_eq!(r2.origin, (0, 0));
875        assert_eq!(r2.size, (0, 0));
876
877        assert_eq!(r.to_untyped(), r);
878        assert_eq!(RectF::from_untyped(r), r);
879    }
880
881    #[test]
882    fn gaslight_coverage() {
883        extern crate alloc;
884
885        fn clone_me<T: Clone>(v: &T) -> T {
886            v.clone()
887        }
888        _ = clone_me(&Rect::ZERO);
889
890        _ = alloc::format!("{:?}", Rect::default());
891    }
892}