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}