iron_shapes/
rect.rs

1// Copyright (c) 2018-2020 Thomas Kramer.
2// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! Data structures and functions for dealing with rectangles which consist of
7//! vertical and horizontal edges.
8
9use crate::cmp::{max, min};
10use crate::edge::IntoEdges;
11use crate::polygon::{Polygon, ToPolygon};
12use crate::prelude::{Point, REdge, Vector};
13use crate::traits::*;
14use crate::CoordinateType;
15use num_traits::{NumCast, One, Zero};
16use std::ops::{Add, Div, Mul, Sub};
17
18/// A rectangle which is oriented along the x an y axis and
19/// represented by its lower left and upper right corner.
20#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub struct Rect<T> {
23    /// Lower left corner of the rectangle.
24    pub lower_left: Point<T>,
25    /// Upper right corner of the rectangle.
26    pub upper_right: Point<T>,
27}
28
29impl<T: PartialOrd + Copy> Rect<T> {
30    /// Construct the bounding box of the two points. Order does not matter.
31    ///
32    /// # Examples
33    ///
34    /// ```
35    /// use iron_shapes::prelude::*;
36    ///
37    /// // Create a rectangle based on two corner points.
38    /// let rect1 = Rect::new(Point::new(0, 0), Point::new(1, 2));
39    /// // Any type that implements `Into<Point<T>>` can be used for the corner points.
40    /// let rect2 = Rect::new((1, 2), (0, 0));
41    /// // Ordering of the corner points does not matter.
42    /// assert_eq!(rect1, rect2);
43    /// // Even though `(0, 0)` was passed as second argument it is recognized as lower left corner.
44    /// assert_eq!(rect2.lower_left(), Point::new(0, 0));
45    /// ```
46    pub fn new<C>(c1: C, c2: C) -> Self
47    where
48        C: Into<Point<T>>,
49    {
50        let p1 = c1.into();
51        let p2 = c2.into();
52
53        let (x1, x2) = if p1.x < p2.x {
54            (p1.x, p2.x)
55        } else {
56            (p2.x, p1.x)
57        };
58
59        let (y1, y2) = if p1.y < p2.y {
60            (p1.y, p2.y)
61        } else {
62            (p2.y, p1.y)
63        };
64
65        Rect {
66            lower_left: Point::new(x1, y1),
67            upper_right: Point::new(x2, y2),
68        }
69    }
70}
71
72impl<T: Copy> Rect<T> {
73    /// Get the lower left corner.
74    #[inline]
75    pub fn lower_left(&self) -> Point<T> {
76        self.lower_left
77    }
78
79    /// Get the upper left corner.
80    #[inline]
81    pub fn upper_left(&self) -> Point<T> {
82        Point::new(self.lower_left.x, self.upper_right.y)
83    }
84
85    /// Get the upper right corner.
86    #[inline]
87    pub fn upper_right(&self) -> Point<T> {
88        self.upper_right
89    }
90
91    /// Get the lower right corner.
92    #[inline]
93    pub fn lower_right(&self) -> Point<T> {
94        Point::new(self.upper_right.x, self.lower_left.y)
95    }
96}
97
98impl<T: PartialOrd + Copy> Rect<T> {
99    /// Check if rectangle contains the point.
100    /// Inclusive boundaries.
101    ///
102    /// # Example
103    /// ```
104    /// use iron_shapes::prelude::*;
105    /// let rect = Rect::new((0, 0), (10, 20));
106    /// // Contains point somewhere in the center.
107    /// assert!(rect.contains_point(Point::new(5, 5)));
108    /// // Also contains point on the boundaries.
109    /// assert!(rect.contains_point(Point::new(0, 0)));
110    /// // Does not contain point outside of the rectangle.
111    /// assert!(!rect.contains_point(Point::new(10, 21)));
112    /// ```
113    pub fn contains_point(&self, p: Point<T>) -> bool {
114        self.lower_left.x <= p.x
115            && p.x <= self.upper_right.x
116            && self.lower_left.y <= p.y
117            && p.y <= self.upper_right.y
118    }
119
120    /// Check if rectangle contains the point.
121    /// Exclusive boundaries.
122    ///
123    /// # Example
124    /// ```
125    /// use iron_shapes::prelude::*;
126    /// let rect = Rect::new((0, 0), (10, 20));
127    /// // Contains point somewhere in the center.
128    /// assert!(rect.contains_point_exclusive(Point::new(5, 5)));
129    /// // Does not contain points on boundaries.
130    /// assert!(!rect.contains_point_exclusive(Point::new(0, 0)));
131    /// // Does not contain point outside of the rectangle.
132    /// assert!(!rect.contains_point_exclusive(Point::new(10, 21)));
133    /// ```
134    pub fn contains_point_exclusive(&self, p: Point<T>) -> bool {
135        self.lower_left.x < p.x
136            && p.x < self.upper_right.x
137            && self.lower_left.y < p.y
138            && p.y < self.upper_right.y
139    }
140
141    /// Check if rectangle contains other rectangle.
142    /// Inclusive boundaries.
143    ///
144    /// # Example
145    ///
146    /// ```
147    /// use iron_shapes::prelude::*;
148    ///
149    /// let outer = Rect::new((0, 0), (2, 2));
150    /// let inner = Rect::new((0, 0), (1, 1));
151    /// assert!(outer.contains_rectangle(&inner));
152    /// assert!(!inner.contains_rectangle(&outer));
153    /// ```
154    pub fn contains_rectangle(&self, other: &Self) -> bool {
155        self.contains_point(other.lower_left) && self.contains_point(other.upper_right)
156    }
157
158    /// Check if rectangle contains other rectangle.
159    /// Exclusive boundaries.
160    ///
161    /// # Example
162    ///
163    /// ```
164    /// use iron_shapes::prelude::*;
165    ///
166    /// let outer = Rect::new((0, 0), (3, 3));
167    /// let inner = Rect::new((1, 1), (2, 2));
168    /// assert!(outer.contains_rectangle_exclusive(&inner));
169    /// assert!(!inner.contains_rectangle_exclusive(&outer));
170    ///
171    /// let not_inner = Rect::new((0, 0), (1, 1)); // This shares the boundary with `outer`.
172    /// assert!(!outer.contains_rectangle_exclusive(&not_inner));
173    /// ```
174    pub fn contains_rectangle_exclusive(&self, other: &Self) -> bool {
175        self.contains_point_exclusive(other.lower_left)
176            && self.contains_point_exclusive(other.upper_right)
177    }
178
179    /// Test if the both rectangles touch each other, i.e. if they either share a boundary or are overlapping.
180    pub fn touches(&self, other: &Self) -> bool {
181        !(self.lower_left.x > other.upper_right.x
182            || self.lower_left.y > other.upper_right.y
183            || self.upper_right.x < other.lower_left.x
184            || self.upper_right.y < other.lower_left.y)
185    }
186
187    /// Compute the boolean intersection of two rectangles.
188    /// This function excludes the boundaries, hence a zero-area intersection is considered `None`.
189    /// See `intersection_inclusive_bounds()` zero-area intersections should be returned as `Some(rectangle)`.
190    ///
191    /// # Example
192    ///
193    /// ```
194    /// use iron_shapes::prelude::*;
195    ///
196    /// // Create two overlapping rectangles.
197    /// let a = Rect::new((0, 0), (2, 2));
198    /// let b = Rect::new((1, 1), (3, 3));
199    ///
200    /// // Compute the intersection.
201    /// assert_eq!(a.intersection(&b), Some(Rect::new((1, 1), (2, 2))));
202    ///
203    /// // Create a non-overlapping rectangle.
204    /// let c = Rect::new((100, 100), (200, 200));
205    /// // The intersection with a non-overlapping rectangle is `None`.
206    /// assert_eq!(a.intersection(&c), None);
207    /// ```
208    pub fn intersection(&self, other: &Self) -> Option<Self> {
209        let llx = max(self.lower_left.x, other.lower_left.x);
210        let lly = max(self.lower_left.y, other.lower_left.y);
211
212        let urx = min(self.upper_right.x, other.upper_right.x);
213        let ury = min(self.upper_right.y, other.upper_right.y);
214
215        if llx < urx && lly < ury {
216            Some(Rect::new((llx, lly), (urx, ury)))
217        } else {
218            None
219        }
220    }
221
222    /// Compute the boolean intersection of two rectangles and include the boundaries.
223    /// This allows to get zero-area intersection results for example if the two
224    /// rectangles touch on a boundary or one of the rectangle is already zero-area.
225    ///
226    /// # Example
227    ///
228    /// ```
229    /// use iron_shapes::prelude::*;
230    ///
231    /// // Create two rectangles which intersect in a single point.
232    /// let a = Rect::new((0, 0), (2, 2));
233    /// let b = Rect::new((2, 2), (3, 3));
234    ///
235    /// // Compute the intersection.
236    /// assert_eq!(a.intersection_inclusive_bounds(&b), Some(Rect::new((2, 2), (2, 2))));
237    ///
238    /// ```
239    pub fn intersection_inclusive_bounds(&self, other: &Self) -> Option<Self> {
240        let llx = max(self.lower_left.x, other.lower_left.x);
241        let lly = max(self.lower_left.y, other.lower_left.y);
242
243        let urx = min(self.upper_right.x, other.upper_right.x);
244        let ury = min(self.upper_right.y, other.upper_right.y);
245
246        if llx <= urx && lly <= ury {
247            Some(Rect::new((llx, lly), (urx, ury)))
248        } else {
249            None
250        }
251    }
252
253    /// Create the smallest `Rect` that contains the original `Rect` and the `point`.
254    ///
255    /// # Example
256    ///
257    /// ```
258    /// use iron_shapes::prelude::*;
259    ///
260    /// let r1 = Rect::new((0,0), (1,2));
261    ///
262    /// let r2 = r1.add_point(Point::new(10, 11));
263    ///
264    /// assert_eq!(r2, Rect::new((0,0), (10,11)));
265    ///
266    /// ```
267    pub fn add_point(&self, point: Point<T>) -> Self {
268        Rect::new(
269            Point::new(
270                min(self.lower_left.x, point.x),
271                min(self.lower_left.y, point.y),
272            ),
273            Point::new(
274                max(self.upper_right.x, point.x),
275                max(self.upper_right.y, point.y),
276            ),
277        )
278    }
279
280    /// Get the smallest `Rect` that contains both rectangles `self` and `rect`.
281    ///
282    /// # Example
283    ///
284    /// ```
285    /// use iron_shapes::prelude::*;
286    ///
287    /// let r1 = Rect::new((0,0), (1,2));
288    /// let r2 = Rect::new((4,5), (6,7));
289    ///
290    /// let r3 = r1.add_rect(&r2);
291    ///
292    /// assert_eq!(r3, Rect::new((0,0), (6,7)));
293    ///
294    /// ```
295    pub fn add_rect(&self, rect: &Self) -> Self {
296        self.add_point(rect.lower_left).add_point(rect.upper_right)
297    }
298}
299
300impl<T: Sub<Output = T> + Copy + Ord + Zero> Rect<T> {
301    /// Compute the shortest from the rectangle to the point `p`.
302    /// The distance is zero if the point is inside the rectangle.
303    ///
304    /// # Example
305    ///
306    /// ```
307    /// use iron_shapes::prelude::*;
308    ///
309    /// let r = Rect::new((0,0), (10, 10));
310    ///
311    /// assert_eq!(r.distance_to_point((5, 15).into()), Vector::new(0, 5));
312    ///
313    /// // Distance to point inside the rectangle is zero.
314    /// assert_eq!(r.distance_to_point((5, 5).into()), Vector::new(0, 0));
315    ///
316    /// ```
317    pub fn distance_to_point(&self, p: Point<T>) -> Vector<T> {
318        let ll = self.lower_left();
319        let ul = self.upper_right();
320
321        // Compute x component of distance.
322        let dx_neg = (p.x - ll.x).min(Zero::zero());
323        let dx_pos = (p.x - ul.x).max(Zero::zero());
324        let dx = dx_neg + dx_pos;
325
326        // Compute y component of distance.
327        let dy_neg = (p.y - ll.y).min(Zero::zero());
328        let dy_pos = (p.y - ul.y).max(Zero::zero());
329        let dy = dy_neg + dy_pos;
330
331        Vector::new(dx, dy)
332    }
333}
334
335#[test]
336fn test_distance_to_point() {
337    let rect = Rect::new((10, 10), (20, 20));
338
339    assert_eq!(rect.distance_to_point((15, 15).into()).norm1(), 0);
340    assert_eq!(rect.distance_to_point((10, 10).into()).norm1(), 0);
341    assert_eq!(rect.distance_to_point((10, 20).into()).norm1(), 0);
342
343    assert_eq!(rect.distance_to_point((0, 0).into()).norm1(), 20);
344    assert_eq!(rect.distance_to_point((0, 5).into()).norm1(), 15);
345    assert_eq!(rect.distance_to_point((0, 10).into()).norm1(), 10);
346    assert_eq!(rect.distance_to_point((0, 15).into()).norm1(), 10);
347    assert_eq!(rect.distance_to_point((0, 20).into()).norm1(), 10);
348    assert_eq!(rect.distance_to_point((0, 25).into()).norm1(), 15);
349}
350
351impl<T: Copy + Sub<Output = T>> Rect<T> {
352    /// Compute the width of the rectangle.
353    #[inline]
354    pub fn width(&self) -> T {
355        self.upper_right.x - self.lower_left.x
356    }
357
358    /// Compute the height of the rectangle.
359    #[inline]
360    pub fn height(&self) -> T {
361        self.upper_right.y - self.lower_left.y
362    }
363}
364
365impl<T: Copy + Add<Output = T> + Div<Output = T> + One> Rect<T> {
366    /// Get the center point of the rectangle.
367    /// When using integer coordinates the resulting
368    /// coordinates will be truncated to the next integers.
369    pub fn center(&self) -> Point<T> {
370        let two = T::one() + T::one();
371        (self.lower_left() + self.upper_right()) / two
372    }
373}
374
375impl<T: Copy + Add<Output = T> + Sub<Output = T> + PartialOrd> Rect<T> {
376    /// Create an enlarged copy of this rectangle.
377    /// The vertical boundaries will be shifted towards the outside by `add_x`.
378    /// The horizontal boundaries will be shifted towards the outside by `add_y`.
379    pub fn sized(&self, add_x: T, add_y: T) -> Self {
380        Rect::new(
381            (self.lower_left.x - add_x, self.lower_left.y - add_y),
382            (self.upper_right.x + add_x, self.upper_right.y + add_y),
383        )
384    }
385
386    /// Create an enlarged copy of this rectangle.
387    pub fn sized_isotropic(&self, add: T) -> Self {
388        self.sized(add, add)
389    }
390}
391
392impl<T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T>> DoubledOrientedArea<T>
393    for Rect<T>
394{
395    /// Calculate doubled oriented area of rectangle.
396    fn area_doubled_oriented(&self) -> T {
397        let diff = self.upper_right - self.lower_left;
398        let area = diff.x * diff.y;
399        area + area
400    }
401}
402
403impl<T: Copy> BoundingBox<T> for Rect<T> {
404    /// Get bounding box of rectangle (which is equal to the rectangle itself).
405    fn bounding_box(&self) -> Rect<T> {
406        *self
407    }
408}
409
410impl<T: Copy> TryBoundingBox<T> for Rect<T> {
411    /// Get bounding box of rectangle (always exists).
412    fn try_bounding_box(&self) -> Option<Rect<T>> {
413        Some(*self)
414    }
415}
416
417/// Point wise transformation of the two corner points.
418impl<T: Copy + PartialOrd> MapPointwise<T> for Rect<T> {
419    /// Point wise transformation.
420    fn transform<F>(&self, transformation: F) -> Self
421    where
422        F: Fn(Point<T>) -> Point<T>,
423    {
424        Self::new(
425            transformation(self.lower_left),
426            transformation(self.upper_right),
427        )
428    }
429}
430
431/// Iterate over all points of the rectangle.
432/// Starts with the lower left corner and iterates counter clock-wise.
433impl<'a, T> IntoIterator for &'a Rect<T>
434where
435    T: Copy,
436{
437    type Item = Point<T>;
438    type IntoIter = std::array::IntoIter<Self::Item, 4>;
439
440    fn into_iter(self) -> Self::IntoIter {
441        [
442            self.lower_left(),
443            self.lower_right(),
444            self.upper_right(),
445            self.upper_left(),
446        ]
447        .into_iter()
448    }
449}
450
451/// Iterate over all points of the rectangle.
452/// Starts with the lower left corner and iterates counter clock-wise.
453impl<T> IntoIterator for Rect<T>
454where
455    T: Copy,
456{
457    type Item = Point<T>;
458    type IntoIter = std::array::IntoIter<Self::Item, 4>;
459
460    fn into_iter(self) -> Self::IntoIter {
461        (&self).into_iter()
462    }
463}
464
465impl<T: Copy> ToPolygon<T> for Rect<T> {
466    fn to_polygon(&self) -> Polygon<T> {
467        Polygon::from(self)
468    }
469}
470
471impl<T: CoordinateType + NumCast, Dst: CoordinateType + NumCast> TryCastCoord<T, Dst> for Rect<T> {
472    type Output = Rect<Dst>;
473
474    fn try_cast(&self) -> Option<Self::Output> {
475        match (self.lower_left.try_cast(), self.upper_right.try_cast()) {
476            (Some(ll), Some(ur)) => Some(Rect::new(ll, ur)),
477            _ => None,
478        }
479    }
480}
481
482#[cfg(test)]
483mod tests {
484    use super::*;
485
486    #[test]
487    fn test_rect_intersection() {
488        let a = Rect::new((0, 0), (2, 4));
489        let b = Rect::new((1, 2), (3, 5));
490        assert_eq!(a.intersection(&b), Some(Rect::new((1, 2), (2, 4))));
491
492        let a = Rect::new((0, 0), (2, 2));
493        let b = Rect::new((1, 1), (3, 3));
494        assert_eq!(a.intersection(&b), Some(Rect::new((1, 1), (2, 2))));
495
496        let a = Rect::new((0, 0), (1, 1));
497        let b = Rect::new((2, 2), (3, 3));
498        assert_eq!(a.intersection(&b), None);
499
500        let a = Rect::new((0, 0), (2, 2));
501        let b = Rect::new((1, 2), (5, 5));
502        assert_eq!(a.intersection(&b), None);
503    }
504}
505
506/// Iterator over edges of a rectangle.
507#[derive(Clone)]
508pub struct RectEdgeIterator<T> {
509    rect: Rect<T>,
510    pos: u8,
511}
512
513impl<T> RectEdgeIterator<T> {
514    fn new(rect: Rect<T>) -> Self {
515        Self { rect, pos: 0 }
516    }
517}
518
519impl<T: CoordinateType> Iterator for RectEdgeIterator<T> {
520    type Item = REdge<T>;
521
522    fn next(&mut self) -> Option<Self::Item> {
523        if self.pos >= 4 {
524            None
525        } else {
526            let point = |idx: u8| -> Point<T> {
527                match idx {
528                    0 => self.rect.lower_right(),
529                    1 => self.rect.upper_right(),
530                    2 => self.rect.upper_left(),
531                    3 => self.rect.lower_left(),
532                    _ => unreachable!(),
533                }
534            };
535            let edge = REdge::new(point(self.pos), point((self.pos + 1) % 4));
536            self.pos += 1;
537            Some(edge)
538        }
539    }
540}
541
542impl<T: CoordinateType> IntoEdges<T> for &Rect<T> {
543    type Edge = REdge<T>;
544    type EdgeIter = RectEdgeIterator<T>;
545
546    fn into_edges(self) -> Self::EdgeIter {
547        RectEdgeIterator::new(*self)
548    }
549}
550
551#[test]
552fn test_edges_iterator() {
553    let rect = Rect::new((1, 2), (3, 4));
554    let edges: Vec<_> = rect.into_edges().collect();
555    assert_eq!(
556        edges,
557        vec![
558            REdge::new((3, 2), (3, 4)),
559            REdge::new((3, 4), (1, 4)),
560            REdge::new((1, 4), (1, 2)),
561            REdge::new((1, 2), (3, 2)),
562        ]
563    );
564}