geo_types/geometry/
rect.rs

1use crate::{coord, polygon, Coord, CoordFloat, CoordNum, Line, Polygon};
2
3/// An _axis-aligned_ bounded 2D rectangle whose area is
4/// defined by minimum and maximum `Coord`s.
5///
6/// The constructors and setters ensure the maximum
7/// `Coord` is greater than or equal to the minimum.
8/// Thus, a `Rect`s width, height, and area is guaranteed to
9/// be greater than or equal to zero.
10///
11/// **Note.** While `Rect` implements `MapCoords` and
12/// `RotatePoint` algorithmic traits, the usage is expected
13/// to maintain the axis alignment. In particular, only
14/// rotation by integer multiples of 90 degrees, will
15/// preserve the original shape. In other cases, the min,
16/// and max points are rotated or transformed, and a new
17/// rectangle is created (with coordinate swaps to ensure
18/// min < max).
19///
20/// # Examples
21///
22/// ```
23/// use geo_types::{coord, Rect};
24///
25/// let rect = Rect::new(
26///     coord! { x: 0., y: 4.},
27///     coord! { x: 3., y: 10.},
28/// );
29///
30/// assert_eq!(3., rect.width());
31/// assert_eq!(6., rect.height());
32/// assert_eq!(
33///     coord! { x: 1.5, y: 7. },
34///     rect.center()
35/// );
36/// ```
37#[derive(Eq, PartialEq, Clone, Copy, Hash)]
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39pub struct Rect<T: CoordNum = f64> {
40    min: Coord<T>,
41    max: Coord<T>,
42}
43
44impl<T: CoordNum> Rect<T> {
45    /// Creates a new rectangle from two corner coordinates.
46    ///
47    /// Coords are stored and returned (by iterators) in CCW order
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// use geo_types::{coord, Rect};
53    ///
54    /// let rect = Rect::new(
55    ///     coord! { x: 10., y: 20. },
56    ///     coord! { x: 30., y: 10. }
57    /// );
58    /// assert_eq!(rect.min(), coord! { x: 10., y: 10. });
59    /// assert_eq!(rect.max(), coord! { x: 30., y: 20. });
60    /// ```
61    pub fn new<C>(c1: C, c2: C) -> Self
62    where
63        C: Into<Coord<T>>,
64    {
65        let c1 = c1.into();
66        let c2 = c2.into();
67        let (min_x, max_x) = if c1.x < c2.x {
68            (c1.x, c2.x)
69        } else {
70            (c2.x, c1.x)
71        };
72        let (min_y, max_y) = if c1.y < c2.y {
73            (c1.y, c2.y)
74        } else {
75            (c2.y, c1.y)
76        };
77        Self {
78            min: coord! { x: min_x, y: min_y },
79            max: coord! { x: max_x, y: max_y },
80        }
81    }
82
83    #[deprecated(
84        since = "0.6.2",
85        note = "Use `Rect::new` instead, since `Rect::try_new` will never Error"
86    )]
87    #[allow(deprecated)]
88    pub fn try_new<C>(c1: C, c2: C) -> Result<Rect<T>, InvalidRectCoordinatesError>
89    where
90        C: Into<Coord<T>>,
91    {
92        Ok(Rect::new(c1, c2))
93    }
94
95    /// Returns the minimum `Coord` of the `Rect`.
96    ///
97    /// # Examples
98    ///
99    /// ```rust
100    /// use geo_types::{coord, Rect};
101    ///
102    /// let rect = Rect::new(
103    ///     coord! { x: 5., y: 5. },
104    ///     coord! { x: 15., y: 15. },
105    /// );
106    ///
107    /// assert_eq!(rect.min(), coord! { x: 5., y: 5. });
108    /// ```
109    pub fn min(self) -> Coord<T> {
110        self.min
111    }
112
113    /// Set the `Rect`’s minimum coordinate.
114    ///
115    /// # Panics
116    ///
117    /// Panics if `min`’s x/y is greater than the maximum coordinate’s x/y.
118    pub fn set_min<C>(&mut self, min: C)
119    where
120        C: Into<Coord<T>>,
121    {
122        self.min = min.into();
123        self.assert_valid_bounds();
124    }
125
126    /// Returns the maximum `Coord` of the `Rect`.
127    ///
128    /// # Examples
129    ///
130    /// ```rust
131    /// use geo_types::{coord, Rect};
132    ///
133    /// let rect = Rect::new(
134    ///     coord! { x: 5., y: 5. },
135    ///     coord! { x: 15., y: 15. },
136    /// );
137    ///
138    /// assert_eq!(rect.max(), coord! { x: 15., y: 15. });
139    /// ```
140    pub fn max(self) -> Coord<T> {
141        self.max
142    }
143
144    /// Set the `Rect`’s maximum coordinate.
145    ///
146    /// # Panics
147    ///
148    /// Panics if `max`’s x/y is less than the minimum coordinate’s x/y.
149    pub fn set_max<C>(&mut self, max: C)
150    where
151        C: Into<Coord<T>>,
152    {
153        self.max = max.into();
154        self.assert_valid_bounds();
155    }
156
157    /// Returns the width of the `Rect`.
158    ///
159    /// # Examples
160    ///
161    /// ```rust
162    /// use geo_types::{coord, Rect};
163    ///
164    /// let rect = Rect::new(
165    ///     coord! { x: 5., y: 5. },
166    ///     coord! { x: 15., y: 15. },
167    /// );
168    ///
169    /// assert_eq!(rect.width(), 10.);
170    /// ```
171    pub fn width(self) -> T {
172        self.max().x - self.min().x
173    }
174
175    /// Returns the height of the `Rect`.
176    ///
177    /// # Examples
178    ///
179    /// ```rust
180    /// use geo_types::{coord, Rect};
181    ///
182    /// let rect = Rect::new(
183    ///     coord! { x: 5., y: 5. },
184    ///     coord! { x: 15., y: 15. },
185    /// );
186    ///
187    /// assert_eq!(rect.height(), 10.);
188    /// ```
189    pub fn height(self) -> T {
190        self.max().y - self.min().y
191    }
192
193    /// Create a `Polygon` from the `Rect`.
194    ///
195    /// # Examples
196    ///
197    /// ```rust
198    /// use geo_types::{coord, Rect, polygon};
199    ///
200    /// let rect = Rect::new(
201    ///     coord! { x: 0., y: 0. },
202    ///     coord! { x: 1., y: 2. },
203    /// );
204    ///
205    /// // Output is CCW
206    /// assert_eq!(
207    ///     rect.to_polygon(),
208    ///     polygon![
209    ///         (x: 1., y: 0.),
210    ///         (x: 1., y: 2.),
211    ///         (x: 0., y: 2.),
212    ///         (x: 0., y: 0.),
213    ///         (x: 1., y: 0.),
214    ///     ],
215    /// );
216    /// ```
217    pub fn to_polygon(self) -> Polygon<T> {
218        polygon![
219            (x: self.max.x, y: self.min.y),
220            (x: self.max.x, y: self.max.y),
221            (x: self.min.x, y: self.max.y),
222            (x: self.min.x, y: self.min.y),
223            (x: self.max.x, y: self.min.y),
224        ]
225    }
226
227    pub fn to_lines(&self) -> [Line<T>; 4] {
228        [
229            Line::new(
230                coord! {
231                    x: self.max.x,
232                    y: self.min.y,
233                },
234                coord! {
235                    x: self.max.x,
236                    y: self.max.y,
237                },
238            ),
239            Line::new(
240                coord! {
241                    x: self.max.x,
242                    y: self.max.y,
243                },
244                coord! {
245                    x: self.min.x,
246                    y: self.max.y,
247                },
248            ),
249            Line::new(
250                coord! {
251                    x: self.min.x,
252                    y: self.max.y,
253                },
254                coord! {
255                    x: self.min.x,
256                    y: self.min.y,
257                },
258            ),
259            Line::new(
260                coord! {
261                    x: self.min.x,
262                    y: self.min.y,
263                },
264                coord! {
265                    x: self.max.x,
266                    y: self.min.y,
267                },
268            ),
269        ]
270    }
271
272    /// Split a rectangle into two rectangles along the X-axis with equal widths.
273    ///
274    /// # Examples
275    ///
276    /// ```
277    /// let rect = geo_types::Rect::new(
278    ///     geo_types::coord! { x: 0., y: 0. },
279    ///     geo_types::coord! { x: 4., y: 4. },
280    /// );
281    ///
282    /// let [rect1, rect2] = rect.split_x();
283    ///
284    /// assert_eq!(
285    ///     geo_types::Rect::new(
286    ///         geo_types::coord! { x: 0., y: 0. },
287    ///         geo_types::coord! { x: 2., y: 4. },
288    ///     ),
289    ///     rect1,
290    /// );
291    /// assert_eq!(
292    ///     geo_types::Rect::new(
293    ///         geo_types::coord! { x: 2., y: 0. },
294    ///         geo_types::coord! { x: 4., y: 4. },
295    ///     ),
296    ///     rect2,
297    /// );
298    /// ```
299    pub fn split_x(self) -> [Rect<T>; 2] {
300        let two = T::one() + T::one();
301        let mid_x = self.min().x + self.width() / two;
302        [
303            Rect::new(self.min(), coord! { x: mid_x, y: self.max().y }),
304            Rect::new(coord! { x: mid_x, y: self.min().y }, self.max()),
305        ]
306    }
307
308    /// Split a rectangle into two rectangles along the Y-axis with equal heights.
309    ///
310    /// # Examples
311    ///
312    /// ```
313    /// let rect = geo_types::Rect::new(
314    ///     geo_types::coord! { x: 0., y: 0. },
315    ///     geo_types::coord! { x: 4., y: 4. },
316    /// );
317    ///
318    /// let [rect1, rect2] = rect.split_y();
319    ///
320    /// assert_eq!(
321    ///     geo_types::Rect::new(
322    ///         geo_types::coord! { x: 0., y: 0. },
323    ///         geo_types::coord! { x: 4., y: 2. },
324    ///     ),
325    ///     rect1,
326    /// );
327    /// assert_eq!(
328    ///     geo_types::Rect::new(
329    ///         geo_types::coord! { x: 0., y: 2. },
330    ///         geo_types::coord! { x: 4., y: 4. },
331    ///     ),
332    ///     rect2,
333    /// );
334    /// ```
335    pub fn split_y(self) -> [Rect<T>; 2] {
336        let two = T::one() + T::one();
337        let mid_y = self.min().y + self.height() / two;
338        [
339            Rect::new(self.min(), coord! { x: self.max().x, y: mid_y }),
340            Rect::new(coord! { x: self.min().x, y: mid_y }, self.max()),
341        ]
342    }
343
344    fn assert_valid_bounds(&self) {
345        if !self.has_valid_bounds() {
346            panic!("{}", RECT_INVALID_BOUNDS_ERROR);
347        }
348    }
349
350    fn has_valid_bounds(&self) -> bool {
351        self.min.x <= self.max.x && self.min.y <= self.max.y
352    }
353}
354
355impl<T: CoordFloat> Rect<T> {
356    /// Returns the center `Coord` of the `Rect`.
357    ///
358    /// # Examples
359    ///
360    /// ```rust
361    /// use geo_types::{coord, Rect};
362    ///
363    /// let rect = Rect::new(
364    ///     coord! { x: 5., y: 5. },
365    ///     coord! { x: 15., y: 15. },
366    /// );
367    ///
368    /// assert_eq!(rect.center(), coord! { x: 10., y: 10. });
369    /// ```
370    pub fn center(self) -> Coord<T> {
371        let two = T::one() + T::one();
372        coord! {
373            x: (self.max.x + self.min.x) / two,
374            y: (self.max.y + self.min.y) / two,
375        }
376    }
377}
378
379static RECT_INVALID_BOUNDS_ERROR: &str = "Failed to create Rect: 'min' coordinate's x/y value must be smaller or equal to the 'max' x/y value";
380
381#[cfg(any(feature = "approx", test))]
382mod approx_integration {
383    use super::*;
384    use approx::{AbsDiffEq, RelativeEq, UlpsEq};
385
386    impl<T> RelativeEq for Rect<T>
387    where
388        T: CoordNum + RelativeEq<Epsilon = T>,
389    {
390        #[inline]
391        fn default_max_relative() -> Self::Epsilon {
392            T::default_max_relative()
393        }
394
395        /// Equality assertion within a relative limit.
396        ///
397        /// # Examples
398        ///
399        /// ```
400        /// use geo_types::Rect;
401        ///
402        /// let a = Rect::new((0.0, 0.0), (10.0, 10.0));
403        /// let b = Rect::new((0.0, 0.0), (10.01, 10.0));
404        ///
405        /// approx::assert_relative_eq!(a, b, max_relative=0.1);
406        /// approx::assert_relative_ne!(a, b, max_relative=0.0001);
407        /// ```
408        #[inline]
409        fn relative_eq(
410            &self,
411            other: &Self,
412            epsilon: Self::Epsilon,
413            max_relative: Self::Epsilon,
414        ) -> bool {
415            if !self.min.relative_eq(&other.min, epsilon, max_relative) {
416                return false;
417            }
418
419            if !self.max.relative_eq(&other.max, epsilon, max_relative) {
420                return false;
421            }
422
423            true
424        }
425    }
426
427    impl<T> AbsDiffEq for Rect<T>
428    where
429        T: CoordNum + AbsDiffEq<Epsilon = T>,
430    {
431        type Epsilon = T;
432
433        #[inline]
434        fn default_epsilon() -> Self::Epsilon {
435            T::default_epsilon()
436        }
437
438        /// Equality assertion with an absolute limit.
439        ///
440        /// # Examples
441        ///
442        /// ```
443        /// use geo_types::{point, Rect};
444        ///
445        /// let a = Rect::new((0.0, 0.0), (10.0, 10.0));
446        /// let b = Rect::new((0.0, 0.0), (10.01, 10.0));
447        ///
448        /// approx::abs_diff_eq!(a, b, epsilon=0.1);
449        /// approx::abs_diff_ne!(a, b, epsilon=0.001);
450        /// ```
451        #[inline]
452        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
453            if !self.min.abs_diff_eq(&other.min, epsilon) {
454                return false;
455            }
456
457            if !self.max.abs_diff_eq(&other.max, epsilon) {
458                return false;
459            }
460
461            true
462        }
463    }
464    impl<T> UlpsEq for Rect<T>
465    where
466        T: CoordNum + UlpsEq<Epsilon = T>,
467    {
468        fn default_max_ulps() -> u32 {
469            T::default_max_ulps()
470        }
471
472        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
473            if !self.min.ulps_eq(&other.min, epsilon, max_ulps) {
474                return false;
475            }
476            if !self.max.ulps_eq(&other.max, epsilon, max_ulps) {
477                return false;
478            }
479            true
480        }
481    }
482}
483
484#[deprecated(
485    since = "0.6.2",
486    note = "Use `Rect::new` instead, since `Rect::try_new` will never Error"
487)]
488#[derive(Debug, Copy, Clone, PartialEq, Eq)]
489pub struct InvalidRectCoordinatesError;
490
491#[cfg(feature = "std")]
492#[allow(deprecated)]
493impl std::error::Error for InvalidRectCoordinatesError {}
494
495#[allow(deprecated)]
496impl core::fmt::Display for InvalidRectCoordinatesError {
497    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
498        write!(f, "{RECT_INVALID_BOUNDS_ERROR}")
499    }
500}
501
502#[cfg(test)]
503mod test {
504    use super::*;
505    use crate::coord;
506
507    #[test]
508    fn rect() {
509        let rect = Rect::new((10, 10), (20, 20));
510        assert_eq!(rect.min, coord! { x: 10, y: 10 });
511        assert_eq!(rect.max, coord! { x: 20, y: 20 });
512
513        let rect = Rect::new((20, 20), (10, 10));
514        assert_eq!(rect.min, coord! { x: 10, y: 10 });
515        assert_eq!(rect.max, coord! { x: 20, y: 20 });
516
517        let rect = Rect::new((10, 20), (20, 10));
518        assert_eq!(rect.min, coord! { x: 10, y: 10 });
519        assert_eq!(rect.max, coord! { x: 20, y: 20 });
520    }
521
522    #[test]
523    fn rect_width() {
524        let rect = Rect::new((10, 10), (20, 20));
525        assert_eq!(rect.width(), 10);
526    }
527
528    #[test]
529    fn rect_height() {
530        let rect = Rect::new((10., 10.), (20., 20.));
531        assert_relative_eq!(rect.height(), 10.);
532    }
533
534    #[test]
535    fn rect_center() {
536        assert_relative_eq!(
537            Rect::new((0., 10.), (10., 90.)).center(),
538            Coord::from((5., 50.))
539        );
540        assert_relative_eq!(
541            Rect::new((-42., -42.), (42., 42.)).center(),
542            Coord::from((0., 0.))
543        );
544        assert_relative_eq!(
545            Rect::new((0., 0.), (0., 0.)).center(),
546            Coord::from((0., 0.))
547        );
548    }
549}