imageproc/
rect.rs

1//! Basic manipulation of rectangles.
2
3use std::cmp;
4
5/// A rectangular region of non-zero width and height.
6/// # Examples
7/// ```
8/// use imageproc::rect::Rect;
9/// use imageproc::rect::Region;
10///
11/// // Construct a rectangle with top-left corner at (4, 5), width 6 and height 7.
12/// let rect = Rect::at(4, 5).of_size(6, 7);
13///
14/// // Contains top-left point:
15/// assert_eq!(rect.left(), 4);
16/// assert_eq!(rect.top(), 5);
17/// assert!(rect.contains(rect.left(), rect.top()));
18///
19/// // Contains bottom-right point, at (left + width - 1, top + height - 1):
20/// assert_eq!(rect.right(), 9);
21/// assert_eq!(rect.bottom(), 11);
22/// assert!(rect.contains(rect.right(), rect.bottom()));
23/// ```
24#[derive(Copy, Clone, Debug, PartialEq, Eq)]
25pub struct Rect {
26    left: i32,
27    top: i32,
28    width: u32,
29    height: u32,
30}
31
32/// A geometrical representation of a set of 2D points with coordinate type T.
33pub trait Region<T> {
34    /// Whether this region contains the given point.
35    fn contains(&self, x: T, y: T) -> bool;
36}
37
38impl Rect {
39    /// Reduces possibility of confusing coordinates and dimensions
40    /// when specifying rects.
41    ///
42    /// See the [struct-level documentation](struct.Rect.html) for examples.
43    pub fn at(x: i32, y: i32) -> RectPosition {
44        RectPosition { left: x, top: y }
45    }
46
47    /// Smallest y-coordinate reached by rect.
48    ///
49    /// See the [struct-level documentation](struct.Rect.html) for examples.
50    pub fn top(&self) -> i32 {
51        self.top
52    }
53
54    /// Smallest x-coordinate reached by rect.
55    ///
56    /// See the [struct-level documentation](struct.Rect.html) for examples.
57    pub fn left(&self) -> i32 {
58        self.left
59    }
60
61    /// Greatest y-coordinate reached by rect.
62    ///
63    /// See the [struct-level documentation](struct.Rect.html) for examples.
64    pub fn bottom(&self) -> i32 {
65        self.top + (self.height as i32) - 1
66    }
67
68    /// Greatest x-coordinate reached by rect.
69    ///
70    /// See the [struct-level documentation](struct.Rect.html) for examples.
71    pub fn right(&self) -> i32 {
72        self.left + (self.width as i32) - 1
73    }
74
75    /// Width of rect.
76    pub fn width(&self) -> u32 {
77        self.width
78    }
79
80    /// Height of rect.
81    pub fn height(&self) -> u32 {
82        self.height
83    }
84
85    /// Returns the intersection of self and other, or none if they are are disjoint.
86    ///
87    /// # Examples
88    /// ```
89    /// use imageproc::rect::Rect;
90    /// use imageproc::rect::Region;
91    ///
92    /// // Intersecting a rectangle with itself
93    /// let r = Rect::at(4, 5).of_size(6, 7);
94    /// assert_eq!(r.intersect(r), Some(r));
95    ///
96    /// // Intersecting overlapping but non-equal rectangles
97    /// let r = Rect::at(0, 0).of_size(5, 5);
98    /// let s = Rect::at(1, 4).of_size(10, 12);
99    /// let i = Rect::at(1, 4).of_size(4, 1);
100    /// assert_eq!(r.intersect(s), Some(i));
101    ///
102    /// // Intersecting disjoint rectangles
103    /// let r = Rect::at(0, 0).of_size(5, 5);
104    /// let s = Rect::at(10, 10).of_size(100, 12);
105    /// assert_eq!(r.intersect(s), None);
106    /// ```
107    pub fn intersect(&self, other: Rect) -> Option<Rect> {
108        let left = cmp::max(self.left, other.left);
109        let top = cmp::max(self.top, other.top);
110        let right = cmp::min(self.right(), other.right());
111        let bottom = cmp::min(self.bottom(), other.bottom());
112
113        if right < left || bottom < top {
114            return None;
115        }
116
117        Some(Rect {
118            left,
119            top,
120            width: (right - left) as u32 + 1,
121            height: (bottom - top) as u32 + 1,
122        })
123    }
124}
125
126impl Region<i32> for Rect {
127    fn contains(&self, x: i32, y: i32) -> bool {
128        self.left <= x && x <= self.right() && self.top <= y && y <= self.bottom()
129    }
130}
131
132impl Region<f32> for Rect {
133    fn contains(&self, x: f32, y: f32) -> bool {
134        self.left as f32 <= x
135            && x <= self.right() as f32
136            && self.top as f32 <= y
137            && y <= self.bottom() as f32
138    }
139}
140
141/// Position of the top left of a rectangle.
142/// Only used when building a [`Rect`](struct.Rect.html).
143#[derive(Copy, Clone, Debug, PartialEq, Eq)]
144pub struct RectPosition {
145    left: i32,
146    top: i32,
147}
148
149impl RectPosition {
150    /// Construct a rectangle from a position and size. Width and height
151    /// are required to be strictly positive.
152    ///
153    /// See the [`Rect`](struct.Rect.html) documentation for examples.
154    pub fn of_size(self, width: u32, height: u32) -> Rect {
155        assert!(width > 0, "width must be strictly positive");
156        assert!(height > 0, "height must be strictly positive");
157        Rect {
158            left: self.left,
159            top: self.top,
160            width,
161            height,
162        }
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::{Rect, Region};
169
170    #[test]
171    #[should_panic]
172    fn test_rejects_empty_rectangle() {
173        Rect::at(1, 2).of_size(0, 1);
174    }
175
176    #[test]
177    fn test_contains_i32() {
178        let r = Rect::at(5, 5).of_size(6, 6);
179        assert!(r.contains(5, 5));
180        assert!(r.contains(10, 10));
181        assert!(!r.contains(10, 11));
182        assert!(!r.contains(11, 10));
183    }
184
185    #[test]
186    fn test_contains_f32() {
187        let r = Rect::at(5, 5).of_size(6, 6);
188        assert!(r.contains(5f32, 5f32));
189        assert!(!r.contains(10.1f32, 10f32));
190    }
191}