orbtk_utils/
rectangle.rs

1use crate::{Point, Size};
2
3/// A `Rectangle` is normally expressed as a top-left corner and a size
4///
5/// # Examples
6/// ```rust
7/// # use orbtk_utils::Rectangle;
8/// let rectangle = Rectangle::new((0., 5.),(10., 7.));
9///
10/// assert_eq!(rectangle.x(), 0.);
11/// assert_eq!(rectangle.y(), 5.);
12/// assert_eq!(rectangle.width(), 10.);
13/// assert_eq!(rectangle.height(), 7.);
14/// ```
15#[derive(Copy, Clone, Default, Debug, PartialEq)]
16pub struct Rectangle {
17    /// Position of the rectangle.
18    position: Point,
19
20    size: Size,
21}
22
23impl Rectangle {
24    /// Create a new rectangle with the given parameters.
25    pub fn new(position: impl Into<Point>, size: impl Into<Size>) -> Self {
26        Rectangle {
27            position: position.into(),
28            size: size.into(),
29        }
30    }
31
32    /// Gets x coordiante.
33    pub fn x(&self) -> f64 {
34        self.position.x()
35    }
36
37    /// Sets x coordinate.
38    pub fn set_x(&mut self, x: impl Into<f64>) {
39        self.position.set_x(x);
40    }
41
42    /// Gets y coordinate.
43    pub fn y(&self) -> f64 {
44        self.position.y()
45    }
46
47    /// Sets y coordinate.
48    pub fn set_y(&mut self, y: impl Into<f64>) {
49        self.position.set_y(y);
50    }
51
52    /// Gets position as `Point`.
53    pub fn position(&self) -> Point {
54        self.position
55    }
56
57    /// Sets position.
58    pub fn set_position(&mut self, position: impl Into<Point>) {
59        self.position = position.into();
60    }
61
62    /// Gets the width.
63    pub fn width(&self) -> f64 {
64        self.size.width()
65    }
66
67    /// Sets the width.
68    pub fn set_width(&mut self, width: impl Into<f64>) {
69        self.size.set_width(width.into());
70    }
71
72    /// Gets the height.
73    pub fn height(&self) -> f64 {
74        self.size.height()
75    }
76
77    /// Sets the height.
78    pub fn set_height(&mut self, height: impl Into<f64>) {
79        self.size.set_height(height.into());
80    }
81
82    /// Gets the size with width and height.
83    pub fn size(&self) -> Size {
84        self.size
85    }
86
87    /// Sets the size with width and height.
88    pub fn set_size(&mut self, width: impl Into<f64>, height: impl Into<f64>) {
89        self.size.set_width(width.into());
90        self.size.set_height(height.into());
91    }
92
93    /// Checks if this rect contains the given `point`.
94    pub fn contains(&self, point: impl Into<Point>) -> bool {
95        let point: Point = point.into();
96        point.x() >= self.x()
97            && point.x() <= self.x() + self.width()
98            && point.y() >= self.y()
99            && point.y() <= self.y() + self.height()
100    }
101
102    /// Checks if this rect contains the given `rect`.
103    pub fn contains_rect(&self, rect: &Rectangle) -> bool {
104        let p1 = rect.position();
105        let p2 = (p1.x() + rect.width(), p1.y() + rect.height());
106        self.contains(p1) && self.contains(p2)
107    }
108
109    /// Checks if this rect intersects with the given `rect`.
110    pub fn intersects(&self, rect: &Rectangle) -> bool {
111        !(rect.x() > (self.x() + self.width())
112            || self.x() > (rect.x() + rect.width())
113            || rect.y() > (self.y() + self.height())
114            || self.y() > (rect.y() + rect.height()))
115    }
116
117    /// Joins this rectangle with another one, the result is
118    /// a rectangle in which the two parents fit.
119    pub fn join_with_rectangle(&mut self, other: &Rectangle) {
120        if other.x() < self.x() {
121            self.set_width(self.width() + self.x() - other.x());
122            self.set_x(other.x());
123        }
124        if other.y() < self.y() {
125            self.set_height(self.height() + self.y() - other.y());
126            self.set_y(other.y());
127        }
128        if other.x() + other.width() > self.x() + self.width() {
129            self.set_width(other.x() + other.width() - self.x());
130        }
131        if other.y() + other.height() > self.y() + self.height() {
132            self.set_height(other.y() + other.height() - self.y());
133        }
134    }
135
136    /// Extends this rectangle to cover the given point.
137    pub fn join_with_point(&mut self, point: &Point) {
138        if point.x() < self.x() {
139            self.set_width(self.width() + self.x() - point.x());
140            self.set_x(point.x());
141        }
142        if point.y() < self.y() {
143            self.set_height(self.height() + self.y() - point.y());
144            self.set_y(point.y());
145        }
146        if point.x() > self.x() + self.width() {
147            self.set_width(point.x() - self.x());
148        }
149        if point.y() > self.y() + self.height() {
150            self.set_height(point.y() - self.y());
151        }
152    }
153
154    /// Box itself inside another rectangle
155    pub fn box_into(&mut self, container: Rectangle) {
156        if self.x() < container.x() {
157            self.set_width(self.width() - (container.x() - self.x()));
158            self.set_x(container.x());
159        }
160        if self.y() < container.y() {
161            self.set_height(self.height() - (container.y() - self.y()));
162            self.set_y(container.y());
163        }
164        if self.x() + self.width() > container.x() + container.width() {
165            self.set_width(container.width() - container.x() + self.x());
166        }
167        if self.y() + self.height() > container.y() + container.height() {
168            self.set_height(container.height() - container.y() + self.y());
169        }
170    }
171}
172
173// --- Conversions ---
174
175impl From<(Point, Size)> for Rectangle {
176    fn from(t: (Point, Size)) -> Self {
177        Rectangle::new(t.0, t.1)
178    }
179}
180
181impl From<(i32, i32, i32, i32)> for Rectangle {
182    fn from(t: (i32, i32, i32, i32)) -> Self {
183        Rectangle::new((t.0 as f64, t.1 as f64), (t.2 as f64, t.3 as f64))
184    }
185}
186
187impl From<(f64, f64, f64, f64)> for Rectangle {
188    fn from(t: (f64, f64, f64, f64)) -> Self {
189        Rectangle::new((t.0, t.1), (t.2, t.3))
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use crate::prelude::*;
196
197    #[test]
198    fn test_new() {
199        let rect = Rectangle::new((5.0, 10.0), (20.0, 30.0));
200
201        assert!(crate::f64_cmp(rect.x(), 5.0));
202        assert!(crate::f64_cmp(rect.y(), 10.0));
203        assert!(crate::f64_cmp(rect.width(), 20.0));
204        assert!(crate::f64_cmp(rect.height(), 30.0));
205    }
206
207    #[test]
208    fn test_contains() {
209        let rect = Rectangle::new((5.0, 10.0), (20.0, 30.0));
210
211        // Contains point in its origin
212        let p = Point::new(5.0, 10.0);
213        assert!(rect.contains(p), "{:?}", p);
214
215        // Contains point in its bottom right corner
216        let p = Point::new(25.0, 40.0);
217        assert!(rect.contains(p), "{:?}", p);
218
219        // Contains normal point
220        let p = Point::new(15.0, 15.0);
221        assert!(rect.contains(p), "{:?}", p);
222
223        // Doesn't contain point with x out of rect
224        let p = Point::new(30.0, 15.0);
225        assert!(!rect.contains(p), "{:?}", p);
226
227        // Doesn't contain point with y out of rect
228        let p = Point::new(15.0, 50.0);
229        assert!(!rect.contains(p), "{:?}", p);
230
231        // Doesn't contain point with both x and y out of rect
232        let p = Point::new(30.0, 40.0);
233        assert!(!rect.contains(p), "{:?}", p);
234    }
235
236    #[test]
237    fn test_contains_rect() {
238        let rect = Rectangle::new((5.0, 10.0), (20.0, 30.0));
239
240        // Contains itself
241        let r = Rectangle::new((5.0, 10.0), (20.0, 30.0));
242        assert!(rect.contains_rect(&r), "{:?}", r);
243
244        // Contains rect on one of its edges
245        let r = Rectangle::new((5.0, 20.0), (10.0, 20.0));
246        assert!(rect.contains_rect(&r), "{:?}", r);
247
248        // Contains rect on two of its edges
249        let r = Rectangle::new((5.0, 10.0), (10.0, 20.0));
250        assert!(rect.contains_rect(&r), "{:?}", r);
251
252        // Contains rect completly inside
253        let r = Rectangle::new((10.0, 20.0), (5.0, 10.0));
254        assert!(rect.contains_rect(&r), "{:?}", r);
255
256        // Does not contain rect partially inside
257        let r = Rectangle::new((20.0, 25.0), (20.0, 30.0));
258        assert!(!rect.contains_rect(&r), "{:?}", r);
259
260        // Does not contain rect completely outside
261        let r = Rectangle::new((50.0, 100.0), (20.0, 30.0));
262        assert!(!rect.contains_rect(&r), "{:?}", r);
263    }
264
265    #[test]
266    fn test_intersects() {
267        let rect = Rectangle::new((5.0, 10.0), (20.0, 30.0));
268
269        // Intersects with itself
270        let r = Rectangle::new((5.0, 10.0), (20.0, 30.0));
271        assert!(rect.intersects(&r), "{:?}", r);
272
273        // Intersects with rect with origin on right edge
274        let r = Rectangle::new((25.0, 10.0), (20.0, 30.0));
275        assert!(rect.intersects(&r), "{:?}", r);
276
277        // Intersects with rect with end on left edge
278        let r = Rectangle::new((-15.0, 10.0), (20.0, 30.0));
279        assert!(rect.intersects(&r), "{:?}", r);
280
281        // Intersects with rect with origin on bottom edge
282        let r = Rectangle::new((5.0, 40.0), (20.0, 30.0));
283        assert!(rect.intersects(&r), "{:?}", r);
284
285        // Intersects with rect with end on upper edge
286        let r = Rectangle::new((5.0, -20.0), (20.0, 30.0));
287        assert!(rect.intersects(&r), "{:?}", r);
288
289        // Does not intersect with rect where origin is further
290        // right than origin + width of this rect
291        let r = Rectangle::new((30.0, 10.0), (20.0, 30.0));
292        assert!(!rect.intersects(&r), "{:?}", r);
293
294        // Does not intersect with rect where end + width is further
295        // left than origin of this rect
296        let r = Rectangle::new((-20.0, 10.0), (20.0, 30.0));
297        assert!(!rect.intersects(&r), "{:?}", r);
298
299        // Does not intersect with rect where origin is further
300        // down than origin + width of this rect
301        let r = Rectangle::new((5.0, 50.0), (20.0, 30.0));
302        assert!(!rect.intersects(&r), "{:?}", r);
303
304        // Does not intersect with rect where origin + height is further
305        // up than origin of this rect
306        let r = Rectangle::new((5.0, -30.0), (20.0, 30.0));
307        assert!(!rect.intersects(&r), "{:?}", r);
308    }
309}