kurbo/
rect.rs

1// Copyright 2019 the Kurbo Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! A rectangle.
5
6use core::fmt;
7use core::ops::{Add, Sub};
8
9use crate::{Ellipse, Insets, PathEl, Point, RoundedRect, RoundedRectRadii, Shape, Size, Vec2};
10
11#[cfg(not(feature = "std"))]
12use crate::common::FloatFuncs;
13
14/// A rectangle.
15#[derive(Clone, Copy, Default, PartialEq)]
16#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct Rect {
19    /// The minimum x coordinate (left edge).
20    pub x0: f64,
21    /// The minimum y coordinate (top edge in y-down spaces).
22    pub y0: f64,
23    /// The maximum x coordinate (right edge).
24    pub x1: f64,
25    /// The maximum y coordinate (bottom edge in y-down spaces).
26    pub y1: f64,
27}
28
29impl Rect {
30    /// The empty rectangle at the origin.
31    pub const ZERO: Rect = Rect::new(0., 0., 0., 0.);
32
33    /// A new rectangle from minimum and maximum coordinates.
34    #[inline(always)]
35    pub const fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> Rect {
36        Rect { x0, y0, x1, y1 }
37    }
38
39    /// A new rectangle from two points.
40    ///
41    /// The result will have non-negative width and height.
42    #[inline]
43    pub fn from_points(p0: impl Into<Point>, p1: impl Into<Point>) -> Rect {
44        let p0 = p0.into();
45        let p1 = p1.into();
46        Rect::new(p0.x, p0.y, p1.x, p1.y).abs()
47    }
48
49    /// A new rectangle from origin and size.
50    ///
51    /// The result will have non-negative width and height.
52    #[inline]
53    pub fn from_origin_size(origin: impl Into<Point>, size: impl Into<Size>) -> Rect {
54        let origin = origin.into();
55        Rect::from_points(origin, origin + size.into().to_vec2())
56    }
57
58    /// A new rectangle from center and size.
59    #[inline]
60    pub fn from_center_size(center: impl Into<Point>, size: impl Into<Size>) -> Rect {
61        let center = center.into();
62        let size = 0.5 * size.into();
63        Rect::new(
64            center.x - size.width,
65            center.y - size.height,
66            center.x + size.width,
67            center.y + size.height,
68        )
69    }
70
71    /// Create a new `Rect` with the same size as `self` and a new origin.
72    #[inline]
73    pub fn with_origin(self, origin: impl Into<Point>) -> Rect {
74        Rect::from_origin_size(origin, self.size())
75    }
76
77    /// Create a new `Rect` with the same origin as `self` and a new size.
78    #[inline]
79    pub fn with_size(self, size: impl Into<Size>) -> Rect {
80        Rect::from_origin_size(self.origin(), size)
81    }
82
83    /// Create a new `Rect` by applying the [`Insets`].
84    ///
85    /// This will not preserve negative width and height.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use kurbo::Rect;
91    /// let inset_rect = Rect::new(0., 0., 10., 10.,).inset(2.);
92    /// assert_eq!(inset_rect.width(), 14.0);
93    /// assert_eq!(inset_rect.x0, -2.0);
94    /// assert_eq!(inset_rect.x1, 12.0);
95    /// ```
96    #[inline]
97    pub fn inset(self, insets: impl Into<Insets>) -> Rect {
98        self + insets.into()
99    }
100
101    /// The width of the rectangle.
102    ///
103    /// Note: nothing forbids negative width.
104    #[inline]
105    pub fn width(&self) -> f64 {
106        self.x1 - self.x0
107    }
108
109    /// The height of the rectangle.
110    ///
111    /// Note: nothing forbids negative height.
112    #[inline]
113    pub fn height(&self) -> f64 {
114        self.y1 - self.y0
115    }
116
117    /// Returns the minimum value for the x-coordinate of the rectangle.
118    #[inline]
119    pub fn min_x(&self) -> f64 {
120        self.x0.min(self.x1)
121    }
122
123    /// Returns the maximum value for the x-coordinate of the rectangle.
124    #[inline]
125    pub fn max_x(&self) -> f64 {
126        self.x0.max(self.x1)
127    }
128
129    /// Returns the minimum value for the y-coordinate of the rectangle.
130    #[inline]
131    pub fn min_y(&self) -> f64 {
132        self.y0.min(self.y1)
133    }
134
135    /// Returns the maximum value for the y-coordinate of the rectangle.
136    #[inline]
137    pub fn max_y(&self) -> f64 {
138        self.y0.max(self.y1)
139    }
140
141    /// The origin of the rectangle.
142    ///
143    /// This is the top left corner in a y-down space and with
144    /// non-negative width and height.
145    #[inline(always)]
146    pub fn origin(&self) -> Point {
147        Point::new(self.x0, self.y0)
148    }
149
150    /// The size of the rectangle.
151    #[inline]
152    pub fn size(&self) -> Size {
153        Size::new(self.width(), self.height())
154    }
155
156    /// The area of the rectangle.
157    #[inline]
158    pub fn area(&self) -> f64 {
159        self.width() * self.height()
160    }
161
162    /// Whether this rectangle has zero area.
163    #[doc(alias = "is_empty")]
164    #[inline]
165    pub fn is_zero_area(&self) -> bool {
166        self.area() == 0.0
167    }
168
169    /// Whether this rectangle has zero area.
170    ///
171    /// Note: a rectangle with negative area is not considered empty.
172    #[inline]
173    #[deprecated(since = "0.11.1", note = "use is_zero_area instead")]
174    pub fn is_empty(&self) -> bool {
175        self.is_zero_area()
176    }
177
178    /// The center point of the rectangle.
179    #[inline]
180    pub fn center(&self) -> Point {
181        Point::new(0.5 * (self.x0 + self.x1), 0.5 * (self.y0 + self.y1))
182    }
183
184    /// Returns `true` if `point` lies within `self`.
185    #[inline]
186    pub fn contains(&self, point: Point) -> bool {
187        point.x >= self.x0 && point.x < self.x1 && point.y >= self.y0 && point.y < self.y1
188    }
189
190    /// Take absolute value of width and height.
191    ///
192    /// The resulting rect has the same extents as the original, but is
193    /// guaranteed to have non-negative width and height.
194    #[inline]
195    pub fn abs(&self) -> Rect {
196        let Rect { x0, y0, x1, y1 } = *self;
197        Rect::new(x0.min(x1), y0.min(y1), x0.max(x1), y0.max(y1))
198    }
199
200    /// The smallest rectangle enclosing two rectangles.
201    ///
202    /// Results are valid only if width and height are non-negative.
203    #[inline]
204    pub fn union(&self, other: Rect) -> Rect {
205        Rect::new(
206            self.x0.min(other.x0),
207            self.y0.min(other.y0),
208            self.x1.max(other.x1),
209            self.y1.max(other.y1),
210        )
211    }
212
213    /// Compute the union with one point.
214    ///
215    /// This method includes the perimeter of zero-area rectangles.
216    /// Thus, a succession of `union_pt` operations on a series of
217    /// points yields their enclosing rectangle.
218    ///
219    /// Results are valid only if width and height are non-negative.
220    pub fn union_pt(&self, pt: Point) -> Rect {
221        Rect::new(
222            self.x0.min(pt.x),
223            self.y0.min(pt.y),
224            self.x1.max(pt.x),
225            self.y1.max(pt.y),
226        )
227    }
228
229    /// The intersection of two rectangles.
230    ///
231    /// The result is zero-area if either input has negative width or
232    /// height. The result always has non-negative width and height.
233    ///
234    /// If you want to determine whether two rectangles intersect, use the
235    /// [`overlaps`] method instead.
236    ///
237    /// [`overlaps`]: Rect::overlaps
238    #[inline]
239    pub fn intersect(&self, other: Rect) -> Rect {
240        let x0 = self.x0.max(other.x0);
241        let y0 = self.y0.max(other.y0);
242        let x1 = self.x1.min(other.x1);
243        let y1 = self.y1.min(other.y1);
244        Rect::new(x0, y0, x1.max(x0), y1.max(y0))
245    }
246
247    /// Determines whether this rectangle overlaps with another in any way.
248    ///
249    /// Note that the edge of the rectangle is considered to be part of itself, meaning
250    /// that two rectangles that share an edge are considered to overlap.
251    ///
252    /// Returns `true` if the rectangles overlap, `false` otherwise.
253    ///
254    /// If you want to compute the *intersection* of two rectangles, use the
255    /// [`intersect`] method instead.
256    ///
257    /// [`intersect`]: Rect::intersect
258    ///
259    /// # Examples
260    ///
261    /// ```
262    /// use kurbo::Rect;
263    ///
264    /// let rect1 = Rect::new(0.0, 0.0, 10.0, 10.0);
265    /// let rect2 = Rect::new(5.0, 5.0, 15.0, 15.0);
266    /// assert!(rect1.overlaps(rect2));
267    ///
268    /// let rect1 = Rect::new(0.0, 0.0, 10.0, 10.0);
269    /// let rect2 = Rect::new(10.0, 0.0, 20.0, 10.0);
270    /// assert!(rect1.overlaps(rect2));
271    /// ```
272    #[inline]
273    pub fn overlaps(&self, other: Rect) -> bool {
274        self.x0 <= other.x1 && self.x1 >= other.x0 && self.y0 <= other.y1 && self.y1 >= other.y0
275    }
276
277    /// Returns whether this rectangle contains another rectangle.
278    ///
279    /// A rectangle is considered to contain another rectangle if the other
280    /// rectangle is fully enclosed within the bounds of this rectangle.
281    ///
282    /// # Examples
283    ///
284    /// ```
285    /// use kurbo::Rect;
286    ///
287    /// let rect1 = Rect::new(0.0, 0.0, 10.0, 10.0);
288    /// let rect2 = Rect::new(2.0, 2.0, 4.0, 4.0);
289    /// assert!(rect1.contains_rect(rect2));
290    /// ```
291    ///
292    /// Two equal rectangles are considered to contain each other.
293    ///
294    /// ```
295    /// use kurbo::Rect;
296    ///
297    /// let rect = Rect::new(0.0, 0.0, 10.0, 10.0);
298    /// assert!(rect.contains_rect(rect));
299    /// ```
300    #[inline]
301    pub fn contains_rect(&self, other: Rect) -> bool {
302        self.x0 <= other.x0 && self.y0 <= other.y0 && self.x1 >= other.x1 && self.y1 >= other.y1
303    }
304
305    /// Expand a rectangle by a constant amount in both directions.
306    ///
307    /// The logic simply applies the amount in each direction. If rectangle
308    /// area or added dimensions are negative, this could give odd results.
309    pub fn inflate(&self, width: f64, height: f64) -> Rect {
310        Rect::new(
311            self.x0 - width,
312            self.y0 - height,
313            self.x1 + width,
314            self.y1 + height,
315        )
316    }
317
318    /// Returns a new `Rect`,
319    /// with each coordinate value [rounded] to the nearest integer.
320    ///
321    /// # Examples
322    ///
323    /// ```
324    /// use kurbo::Rect;
325    /// let rect = Rect::new(3.3, 3.6, 3.0, -3.1).round();
326    /// assert_eq!(rect.x0, 3.0);
327    /// assert_eq!(rect.y0, 4.0);
328    /// assert_eq!(rect.x1, 3.0);
329    /// assert_eq!(rect.y1, -3.0);
330    /// ```
331    ///
332    /// [rounded]: f64::round
333    #[inline]
334    pub fn round(self) -> Rect {
335        Rect::new(
336            self.x0.round(),
337            self.y0.round(),
338            self.x1.round(),
339            self.y1.round(),
340        )
341    }
342
343    /// Returns a new `Rect`,
344    /// with each coordinate value [rounded up] to the nearest integer,
345    /// unless they are already an integer.
346    ///
347    /// # Examples
348    ///
349    /// ```
350    /// use kurbo::Rect;
351    /// let rect = Rect::new(3.3, 3.6, 3.0, -3.1).ceil();
352    /// assert_eq!(rect.x0, 4.0);
353    /// assert_eq!(rect.y0, 4.0);
354    /// assert_eq!(rect.x1, 3.0);
355    /// assert_eq!(rect.y1, -3.0);
356    /// ```
357    ///
358    /// [rounded up]: f64::ceil
359    #[inline]
360    pub fn ceil(self) -> Rect {
361        Rect::new(
362            self.x0.ceil(),
363            self.y0.ceil(),
364            self.x1.ceil(),
365            self.y1.ceil(),
366        )
367    }
368
369    /// Returns a new `Rect`,
370    /// with each coordinate value [rounded down] to the nearest integer,
371    /// unless they are already an integer.
372    ///
373    /// # Examples
374    ///
375    /// ```
376    /// use kurbo::Rect;
377    /// let rect = Rect::new(3.3, 3.6, 3.0, -3.1).floor();
378    /// assert_eq!(rect.x0, 3.0);
379    /// assert_eq!(rect.y0, 3.0);
380    /// assert_eq!(rect.x1, 3.0);
381    /// assert_eq!(rect.y1, -4.0);
382    /// ```
383    ///
384    /// [rounded down]: f64::floor
385    #[inline]
386    pub fn floor(self) -> Rect {
387        Rect::new(
388            self.x0.floor(),
389            self.y0.floor(),
390            self.x1.floor(),
391            self.y1.floor(),
392        )
393    }
394
395    /// Returns a new `Rect`,
396    /// with each coordinate value rounded away from the center of the `Rect`
397    /// to the nearest integer, unless they are already an integer.
398    /// That is to say this function will return the smallest possible `Rect`
399    /// with integer coordinates that is a superset of `self`.
400    ///
401    /// # Examples
402    ///
403    /// ```
404    /// use kurbo::Rect;
405    ///
406    /// // In positive space
407    /// let rect = Rect::new(3.3, 3.6, 5.6, 4.1).expand();
408    /// assert_eq!(rect.x0, 3.0);
409    /// assert_eq!(rect.y0, 3.0);
410    /// assert_eq!(rect.x1, 6.0);
411    /// assert_eq!(rect.y1, 5.0);
412    ///
413    /// // In both positive and negative space
414    /// let rect = Rect::new(-3.3, -3.6, 5.6, 4.1).expand();
415    /// assert_eq!(rect.x0, -4.0);
416    /// assert_eq!(rect.y0, -4.0);
417    /// assert_eq!(rect.x1, 6.0);
418    /// assert_eq!(rect.y1, 5.0);
419    ///
420    /// // In negative space
421    /// let rect = Rect::new(-5.6, -4.1, -3.3, -3.6).expand();
422    /// assert_eq!(rect.x0, -6.0);
423    /// assert_eq!(rect.y0, -5.0);
424    /// assert_eq!(rect.x1, -3.0);
425    /// assert_eq!(rect.y1, -3.0);
426    ///
427    /// // Inverse orientation
428    /// let rect = Rect::new(5.6, -3.6, 3.3, -4.1).expand();
429    /// assert_eq!(rect.x0, 6.0);
430    /// assert_eq!(rect.y0, -3.0);
431    /// assert_eq!(rect.x1, 3.0);
432    /// assert_eq!(rect.y1, -5.0);
433    /// ```
434    #[inline]
435    pub fn expand(self) -> Rect {
436        // The compiler optimizer will remove the if branching.
437        let (x0, x1) = if self.x0 < self.x1 {
438            (self.x0.floor(), self.x1.ceil())
439        } else {
440            (self.x0.ceil(), self.x1.floor())
441        };
442        let (y0, y1) = if self.y0 < self.y1 {
443            (self.y0.floor(), self.y1.ceil())
444        } else {
445            (self.y0.ceil(), self.y1.floor())
446        };
447        Rect::new(x0, y0, x1, y1)
448    }
449
450    /// Returns a new `Rect`,
451    /// with each coordinate value rounded towards the center of the `Rect`
452    /// to the nearest integer, unless they are already an integer.
453    /// That is to say this function will return the biggest possible `Rect`
454    /// with integer coordinates that is a subset of `self`.
455    ///
456    /// # Examples
457    ///
458    /// ```
459    /// use kurbo::Rect;
460    ///
461    /// // In positive space
462    /// let rect = Rect::new(3.3, 3.6, 5.6, 4.1).trunc();
463    /// assert_eq!(rect.x0, 4.0);
464    /// assert_eq!(rect.y0, 4.0);
465    /// assert_eq!(rect.x1, 5.0);
466    /// assert_eq!(rect.y1, 4.0);
467    ///
468    /// // In both positive and negative space
469    /// let rect = Rect::new(-3.3, -3.6, 5.6, 4.1).trunc();
470    /// assert_eq!(rect.x0, -3.0);
471    /// assert_eq!(rect.y0, -3.0);
472    /// assert_eq!(rect.x1, 5.0);
473    /// assert_eq!(rect.y1, 4.0);
474    ///
475    /// // In negative space
476    /// let rect = Rect::new(-5.6, -4.1, -3.3, -3.6).trunc();
477    /// assert_eq!(rect.x0, -5.0);
478    /// assert_eq!(rect.y0, -4.0);
479    /// assert_eq!(rect.x1, -4.0);
480    /// assert_eq!(rect.y1, -4.0);
481    ///
482    /// // Inverse orientation
483    /// let rect = Rect::new(5.6, -3.6, 3.3, -4.1).trunc();
484    /// assert_eq!(rect.x0, 5.0);
485    /// assert_eq!(rect.y0, -4.0);
486    /// assert_eq!(rect.x1, 4.0);
487    /// assert_eq!(rect.y1, -4.0);
488    /// ```
489    #[inline]
490    pub fn trunc(self) -> Rect {
491        // The compiler optimizer will remove the if branching.
492        let (x0, x1) = if self.x0 < self.x1 {
493            (self.x0.ceil(), self.x1.floor())
494        } else {
495            (self.x0.floor(), self.x1.ceil())
496        };
497        let (y0, y1) = if self.y0 < self.y1 {
498            (self.y0.ceil(), self.y1.floor())
499        } else {
500            (self.y0.floor(), self.y1.ceil())
501        };
502        Rect::new(x0, y0, x1, y1)
503    }
504
505    /// Scales the `Rect` by `factor` with respect to the origin (the point `(0, 0)`).
506    ///
507    /// # Examples
508    ///
509    /// ```
510    /// use kurbo::Rect;
511    ///
512    /// let rect = Rect::new(2., 2., 4., 6.).scale_from_origin(2.);
513    /// assert_eq!(rect.x0, 4.);
514    /// assert_eq!(rect.x1, 8.);
515    /// ```
516    #[inline]
517    pub fn scale_from_origin(self, factor: f64) -> Rect {
518        Rect {
519            x0: self.x0 * factor,
520            y0: self.y0 * factor,
521            x1: self.x1 * factor,
522            y1: self.y1 * factor,
523        }
524    }
525
526    /// Creates a new [`RoundedRect`] from this `Rect` and the provided
527    /// corner [radius](RoundedRectRadii).
528    #[inline]
529    pub fn to_rounded_rect(self, radii: impl Into<RoundedRectRadii>) -> RoundedRect {
530        RoundedRect::from_rect(self, radii)
531    }
532
533    /// Returns the [`Ellipse`] that is bounded by this `Rect`.
534    #[inline]
535    pub fn to_ellipse(self) -> Ellipse {
536        Ellipse::from_rect(self)
537    }
538
539    /// The aspect ratio of the `Rect`.
540    ///
541    /// This is defined as the height divided by the width. It measures the
542    /// "squareness" of the rectangle (a value of `1` is square).
543    ///
544    /// If the width is `0` the output will be `sign(y1 - y0) * infinity`.
545    ///
546    /// If The width and height are `0`, the result will be `NaN`.
547    #[inline]
548    pub fn aspect_ratio(&self) -> f64 {
549        self.size().aspect_ratio()
550    }
551
552    /// Returns the largest possible `Rect` that is fully contained in `self`
553    /// with the given `aspect_ratio`.
554    ///
555    /// The aspect ratio is specified fractionally, as `height / width`.
556    ///
557    /// The resulting rectangle will be centered if it is smaller than the
558    /// input rectangle.
559    ///
560    /// For the special case where the aspect ratio is `1.0`, the resulting
561    /// `Rect` will be square.
562    ///
563    /// # Examples
564    ///
565    /// ```
566    /// # use kurbo::Rect;
567    /// let outer = Rect::new(0.0, 0.0, 10.0, 20.0);
568    /// let inner = outer.contained_rect_with_aspect_ratio(1.0);
569    /// // The new `Rect` is a square centered at the center of `outer`.
570    /// assert_eq!(inner, Rect::new(0.0, 5.0, 10.0, 15.0));
571    /// ```
572    ///
573    pub fn contained_rect_with_aspect_ratio(&self, aspect_ratio: f64) -> Rect {
574        let (width, height) = (self.width(), self.height());
575        let self_aspect = height / width;
576
577        // TODO the parameter `1e-9` was chosen quickly and may not be optimal.
578        if (self_aspect - aspect_ratio).abs() < 1e-9 {
579            // short circuit
580            *self
581        } else if self_aspect.abs() < aspect_ratio.abs() {
582            // shrink x to fit
583            let new_width = height * aspect_ratio.recip();
584            let gap = (width - new_width) * 0.5;
585            let x0 = self.x0 + gap;
586            let x1 = self.x1 - gap;
587            Rect::new(x0, self.y0, x1, self.y1)
588        } else {
589            // shrink y to fit
590            let new_height = width * aspect_ratio;
591            let gap = (height - new_height) * 0.5;
592            let y0 = self.y0 + gap;
593            let y1 = self.y1 - gap;
594            Rect::new(self.x0, y0, self.x1, y1)
595        }
596    }
597
598    /// Is this rectangle [finite]?
599    ///
600    /// [finite]: f64::is_finite
601    #[inline]
602    pub fn is_finite(&self) -> bool {
603        self.x0.is_finite() && self.x1.is_finite() && self.y0.is_finite() && self.y1.is_finite()
604    }
605
606    /// Is this rectangle [NaN]?
607    ///
608    /// [NaN]: f64::is_nan
609    #[inline]
610    pub fn is_nan(&self) -> bool {
611        self.x0.is_nan() || self.y0.is_nan() || self.x1.is_nan() || self.y1.is_nan()
612    }
613}
614
615impl From<(Point, Point)> for Rect {
616    #[inline(always)]
617    fn from(points: (Point, Point)) -> Rect {
618        Rect::from_points(points.0, points.1)
619    }
620}
621
622impl From<(Point, Size)> for Rect {
623    fn from(params: (Point, Size)) -> Rect {
624        Rect::from_origin_size(params.0, params.1)
625    }
626}
627
628impl Add<Vec2> for Rect {
629    type Output = Rect;
630
631    #[inline]
632    fn add(self, v: Vec2) -> Rect {
633        Rect::new(self.x0 + v.x, self.y0 + v.y, self.x1 + v.x, self.y1 + v.y)
634    }
635}
636
637impl Sub<Vec2> for Rect {
638    type Output = Rect;
639
640    #[inline]
641    fn sub(self, v: Vec2) -> Rect {
642        Rect::new(self.x0 - v.x, self.y0 - v.y, self.x1 - v.x, self.y1 - v.y)
643    }
644}
645
646impl Sub for Rect {
647    type Output = Insets;
648
649    #[inline]
650    fn sub(self, other: Rect) -> Insets {
651        let x0 = other.x0 - self.x0;
652        let y0 = other.y0 - self.y0;
653        let x1 = self.x1 - other.x1;
654        let y1 = self.y1 - other.y1;
655        Insets { x0, y0, x1, y1 }
656    }
657}
658
659#[doc(hidden)]
660pub struct RectPathIter {
661    rect: Rect,
662    ix: usize,
663}
664
665impl Shape for Rect {
666    type PathElementsIter<'iter> = RectPathIter;
667
668    fn path_elements(&self, _tolerance: f64) -> RectPathIter {
669        RectPathIter { rect: *self, ix: 0 }
670    }
671
672    // It's a bit of duplication having both this and the impl method, but
673    // removing that would require using the trait. We'll leave it for now.
674    #[inline]
675    fn area(&self) -> f64 {
676        Rect::area(self)
677    }
678
679    #[inline]
680    fn perimeter(&self, _accuracy: f64) -> f64 {
681        2.0 * (self.width().abs() + self.height().abs())
682    }
683
684    /// Note: this function is carefully designed so that if the plane is
685    /// tiled with rectangles, the winding number will be nonzero for exactly
686    /// one of them.
687    #[inline]
688    fn winding(&self, pt: Point) -> i32 {
689        let xmin = self.x0.min(self.x1);
690        let xmax = self.x0.max(self.x1);
691        let ymin = self.y0.min(self.y1);
692        let ymax = self.y0.max(self.y1);
693        if pt.x >= xmin && pt.x < xmax && pt.y >= ymin && pt.y < ymax {
694            if (self.x1 > self.x0) ^ (self.y1 > self.y0) {
695                -1
696            } else {
697                1
698            }
699        } else {
700            0
701        }
702    }
703
704    #[inline]
705    fn bounding_box(&self) -> Rect {
706        self.abs()
707    }
708
709    #[inline(always)]
710    fn as_rect(&self) -> Option<Rect> {
711        Some(*self)
712    }
713
714    #[inline]
715    fn contains(&self, pt: Point) -> bool {
716        self.contains(pt)
717    }
718}
719
720// This is clockwise in a y-down coordinate system for positive area.
721impl Iterator for RectPathIter {
722    type Item = PathEl;
723
724    fn next(&mut self) -> Option<PathEl> {
725        self.ix += 1;
726        match self.ix {
727            1 => Some(PathEl::MoveTo(Point::new(self.rect.x0, self.rect.y0))),
728            2 => Some(PathEl::LineTo(Point::new(self.rect.x1, self.rect.y0))),
729            3 => Some(PathEl::LineTo(Point::new(self.rect.x1, self.rect.y1))),
730            4 => Some(PathEl::LineTo(Point::new(self.rect.x0, self.rect.y1))),
731            5 => Some(PathEl::ClosePath),
732            _ => None,
733        }
734    }
735}
736
737impl fmt::Debug for Rect {
738    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
739        if f.alternate() {
740            write!(
741                f,
742                "Rect {{ origin: {:?}, size: {:?} }}",
743                self.origin(),
744                self.size()
745            )
746        } else {
747            write!(
748                f,
749                "Rect {{ x0: {:?}, y0: {:?}, x1: {:?}, y1: {:?} }}",
750                self.x0, self.y0, self.x1, self.y1
751            )
752        }
753    }
754}
755
756impl fmt::Display for Rect {
757    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
758        write!(f, "Rect {{ ")?;
759        fmt::Display::fmt(&self.origin(), f)?;
760        write!(f, " ")?;
761        fmt::Display::fmt(&self.size(), f)?;
762        write!(f, " }}")
763    }
764}
765
766#[cfg(test)]
767mod tests {
768    use crate::{Point, Rect, Shape};
769
770    fn assert_approx_eq(x: f64, y: f64) {
771        assert!((x - y).abs() < 1e-7);
772    }
773
774    #[test]
775    fn area_sign() {
776        let r = Rect::new(0.0, 0.0, 10.0, 10.0);
777        let center = r.center();
778        assert_approx_eq(r.area(), 100.0);
779
780        assert_eq!(r.winding(center), 1);
781
782        let p = r.to_path(1e-9);
783        assert_approx_eq(r.area(), p.area());
784        assert_eq!(r.winding(center), p.winding(center));
785
786        let r_flip = Rect::new(0.0, 10.0, 10.0, 0.0);
787        assert_approx_eq(r_flip.area(), -100.0);
788
789        assert_eq!(r_flip.winding(Point::new(5.0, 5.0)), -1);
790        let p_flip = r_flip.to_path(1e-9);
791        assert_approx_eq(r_flip.area(), p_flip.area());
792        assert_eq!(r_flip.winding(center), p_flip.winding(center));
793    }
794
795    #[test]
796    fn display() {
797        let r = Rect::from_origin_size((10., 12.23214), (22.222222222, 23.1));
798        assert_eq!(
799            format!("{r}"),
800            "Rect { (10, 12.23214) (22.222222222×23.1) }"
801        );
802        assert_eq!(format!("{r:.2}"), "Rect { (10.00, 12.23) (22.22×23.10) }");
803    }
804
805    /* TODO uncomment when a (possibly approximate) equality test has been decided on
806    #[test]
807    fn rect_from_center_size() {
808        assert_eq!(
809            Rect::from_center_size(Point::new(3.0, 2.0), Size::new(2.0, 4.0)),
810            Rect::new(2.0, 0.0, 4.0, 4.0)
811        );
812    }
813    */
814
815    #[test]
816    fn contained_rect_with_aspect_ratio() {
817        fn case(outer: [f64; 4], aspect_ratio: f64, expected: [f64; 4]) {
818            let outer = Rect::new(outer[0], outer[1], outer[2], outer[3]);
819            let expected = Rect::new(expected[0], expected[1], expected[2], expected[3]);
820            assert_eq!(
821                outer.contained_rect_with_aspect_ratio(aspect_ratio),
822                expected
823            );
824        }
825        // squares (different point orderings)
826        case([0.0, 0.0, 10.0, 20.0], 1.0, [0.0, 5.0, 10.0, 15.0]);
827        case([0.0, 20.0, 10.0, 0.0], 1.0, [0.0, 5.0, 10.0, 15.0]);
828        case([10.0, 0.0, 0.0, 20.0], 1.0, [10.0, 15.0, 0.0, 5.0]);
829        case([10.0, 20.0, 0.0, 0.0], 1.0, [10.0, 15.0, 0.0, 5.0]);
830        // non-square
831        case([0.0, 0.0, 10.0, 20.0], 0.5, [0.0, 7.5, 10.0, 12.5]);
832        // same aspect ratio
833        case([0.0, 0.0, 10.0, 20.0], 2.0, [0.0, 0.0, 10.0, 20.0]);
834        // negative aspect ratio
835        case([0.0, 0.0, 10.0, 20.0], -1.0, [0.0, 15.0, 10.0, 5.0]);
836        // infinite aspect ratio
837        case([0.0, 0.0, 10.0, 20.0], f64::INFINITY, [5.0, 0.0, 5.0, 20.0]);
838        // zero aspect ratio
839        case([0.0, 0.0, 10.0, 20.0], 0.0, [0.0, 10.0, 10.0, 10.0]);
840        // zero width rect
841        case([0.0, 0.0, 0.0, 20.0], 1.0, [0.0, 10.0, 0.0, 10.0]);
842        // many zeros
843        case([0.0, 0.0, 0.0, 20.0], 0.0, [0.0, 10.0, 0.0, 10.0]);
844        // everything zero
845        case([0.0, 0.0, 0.0, 0.0], 0.0, [0.0, 0.0, 0.0, 0.0]);
846    }
847
848    #[test]
849    fn aspect_ratio() {
850        let test = Rect::new(0.0, 0.0, 1.0, 1.0);
851        assert!((test.aspect_ratio() - 1.0).abs() < 1e-6);
852    }
853
854    #[test]
855    fn contained_rect_overlaps() {
856        let outer = Rect::new(0.0, 0.0, 10.0, 10.0);
857        let inner = Rect::new(2.0, 2.0, 4.0, 4.0);
858        assert!(outer.overlaps(inner));
859    }
860
861    #[test]
862    fn overlapping_rect_overlaps() {
863        let a = Rect::new(0.0, 0.0, 10.0, 10.0);
864        let b = Rect::new(5.0, 5.0, 15.0, 15.0);
865        assert!(a.overlaps(b));
866    }
867
868    #[test]
869    fn disjoint_rect_overlaps() {
870        let a = Rect::new(0.0, 0.0, 10.0, 10.0);
871        let b = Rect::new(11.0, 11.0, 15.0, 15.0);
872        assert!(!a.overlaps(b));
873    }
874
875    #[test]
876    fn sharing_edge_overlaps() {
877        let a = Rect::new(0.0, 0.0, 10.0, 10.0);
878        let b = Rect::new(10.0, 0.0, 20.0, 10.0);
879        assert!(a.overlaps(b));
880    }
881
882    // Test the two other directions in case there is a bug that only appears in one direction.
883    #[test]
884    fn disjoint_rect_overlaps_negative() {
885        let a = Rect::new(0.0, 0.0, 10.0, 10.0);
886        let b = Rect::new(-10.0, -10.0, -5.0, -5.0);
887        assert!(!a.overlaps(b));
888    }
889
890    #[test]
891    fn contained_rectangle_contains() {
892        let outer = Rect::new(0.0, 0.0, 10.0, 10.0);
893        let inner = Rect::new(2.0, 2.0, 4.0, 4.0);
894        assert!(outer.contains_rect(inner));
895    }
896
897    #[test]
898    fn overlapping_rectangle_contains() {
899        let outer = Rect::new(0.0, 0.0, 10.0, 10.0);
900        let inner = Rect::new(5.0, 5.0, 15.0, 15.0);
901        assert!(!outer.contains_rect(inner));
902    }
903
904    #[test]
905    fn disjoint_rectangle_contains() {
906        let outer = Rect::new(0.0, 0.0, 10.0, 10.0);
907        let inner = Rect::new(11.0, 11.0, 15.0, 15.0);
908        assert!(!outer.contains_rect(inner));
909    }
910}