embedded_graphics/primitives/rectangle/
mod.rs

1//! The rectangle primitive. Also good for drawing squares.
2
3use crate::{
4    geometry::{Point, Size},
5    primitives::{ContainsPoint, OffsetOutline, Primitive},
6    transform::Transform,
7};
8
9pub use embedded_graphics_core::primitives::{rectangle::Points, Rectangle};
10
11mod styled;
12
13pub use styled::StyledPixelsIterator;
14
15impl Primitive for Rectangle {}
16
17impl ContainsPoint for Rectangle {
18    fn contains(&self, point: Point) -> bool {
19        if point.x >= self.top_left.x && point.y >= self.top_left.y {
20            self.bottom_right().map_or(false, |bottom_right| {
21                point.x <= bottom_right.x && point.y <= bottom_right.y
22            })
23        } else {
24            false
25        }
26    }
27}
28
29impl OffsetOutline for Rectangle {
30    fn offset(&self, offset: i32) -> Self {
31        let size = if offset >= 0 {
32            self.size.saturating_add(Size::new_equal(offset as u32 * 2))
33        } else {
34            self.size
35                .saturating_sub(Size::new_equal((-offset) as u32 * 2))
36        };
37
38        Self::with_center(self.center(), size)
39    }
40}
41
42impl Transform for Rectangle {
43    /// Translate the rect from its current position to a new position by (x, y) pixels, returning
44    /// a new `Rectangle`. For a mutating transform, see `translate_mut`.
45    ///
46    /// ```
47    /// # use embedded_graphics::primitives::Rectangle;
48    /// # use embedded_graphics::prelude::*;
49    /// let rect = Rectangle::new(Point::new(5, 10), Size::new(10, 10));
50    /// let moved = rect.translate(Point::new(10, 10));
51    ///
52    /// assert_eq!(moved.top_left, Point::new(15, 20));
53    /// assert_eq!(moved.size, Size::new(10, 10));
54    /// ```
55    fn translate(&self, by: Point) -> Self {
56        Self {
57            top_left: self.top_left + by,
58            ..*self
59        }
60    }
61
62    /// Translate the rect from its current position to a new position by (x, y) pixels.
63    ///
64    /// ```
65    /// # use embedded_graphics::primitives::Rectangle;
66    /// # use embedded_graphics::prelude::*;
67    /// let mut rect = Rectangle::new(Point::new(5, 10), Size::new(10, 10));
68    /// rect.translate_mut(Point::new(10, 10));
69    ///
70    /// assert_eq!(rect.top_left, Point::new(15, 20));
71    /// assert_eq!(rect.size, Size::new(10, 10));
72    /// ```
73    fn translate_mut(&mut self, by: Point) -> &mut Self {
74        self.top_left += by;
75
76        self
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use crate::{
84        geometry::{AnchorPoint, Dimensions, Point, Size},
85        primitives::PointsIter,
86    };
87
88    #[test]
89    fn dimensions() {
90        let rect = Rectangle::new(Point::new(5, 10), Size::new(10, 20));
91        let moved = rect.translate(Point::new(-10, -20));
92
93        assert_eq!(
94            rect.bounding_box(),
95            Rectangle::new(Point::new(5, 10), Size::new(10, 20))
96        );
97
98        assert_eq!(
99            moved.bounding_box(),
100            Rectangle::new(Point::new(-5, -10), Size::new(10, 20))
101        );
102    }
103
104    #[test]
105    fn it_can_be_translated() {
106        let rect = Rectangle::new(Point::new(5, 10), Size::new(10, 20));
107        let moved = rect.translate(Point::new(10, 15));
108
109        let bounding_box = moved.bounding_box();
110        assert_eq!(bounding_box.top_left, Point::new(15, 25));
111        assert_eq!(bounding_box.size, Size::new(10, 20));
112    }
113
114    #[test]
115    fn it_can_be_negative() {
116        let negative = Rectangle::new(Point::new(-2, -2), Size::new(4, 4)).points();
117
118        let positive = Rectangle::new(Point::new(2, 2), Size::new(4, 4)).points();
119
120        assert!(negative.eq(positive.map(|p| p - Point::new(4, 4))));
121    }
122
123    #[test]
124    fn contains() {
125        let outer = Rectangle::new(Point::zero(), Size::new(10, 10));
126        let inner = Rectangle::new(Point::new(2, 4), Size::new(3, 5));
127
128        for p in outer.points() {
129            let expected = p.x >= 2 && p.x < 2 + 3 && p.y >= 4 && p.y < 4 + 5;
130
131            assert_eq!(inner.contains(p), expected, "{:?}", p);
132        }
133    }
134
135    #[test]
136    fn center() {
137        let odd = Rectangle::new(Point::new(10, 20), Size::new(5, 7));
138        assert_eq!(odd.center(), Point::new(12, 23));
139
140        let even = Rectangle::new(Point::new(20, 30), Size::new(4, 8));
141        assert_eq!(even.center(), Point::new(21, 33));
142    }
143
144    #[test]
145    fn bottom_right() {
146        let zero = Rectangle::new(Point::new(10, 20), Size::zero());
147        assert_eq!(zero.bottom_right(), None);
148
149        let odd = Rectangle::new(Point::new(10, 20), Size::new(5, 7));
150        assert_eq!(odd.bottom_right(), Some(Point::new(14, 26)));
151
152        let even = Rectangle::new(Point::new(20, 30), Size::new(4, 8));
153        assert_eq!(even.bottom_right(), Some(Point::new(23, 37)));
154    }
155
156    #[test]
157    fn rectangle_intersection() {
158        let rect1 = Rectangle::new(Point::new_equal(10), Size::new(20, 30));
159        let rect2 = Rectangle::new(Point::new_equal(25), Size::new(30, 40));
160
161        assert_eq!(
162            rect1.intersection(&rect2),
163            Rectangle::new(Point::new_equal(25), Size::new(5, 15))
164        );
165    }
166
167    #[test]
168    fn rectangle_no_intersection() {
169        let rect1 = Rectangle::new(Point::new_equal(10), Size::new(20, 30));
170        let rect2 = Rectangle::new(Point::new_equal(35), Size::new(30, 40));
171
172        assert!(rect1.intersection(&rect2).is_zero_sized());
173    }
174
175    #[test]
176    fn rectangle_complete_intersection() {
177        let rect1 = Rectangle::new(Point::new_equal(10), Size::new(20, 30));
178        let rect2 = rect1;
179
180        assert_eq!(rect1.intersection(&rect2), rect1);
181    }
182
183    #[test]
184    fn rectangle_contained_intersection() {
185        let rect1 = Rectangle::with_corners(Point::new_equal(10), Point::new(20, 30));
186        let rect2 = Rectangle::with_corners(Point::new_equal(5), Point::new(30, 40));
187
188        assert_eq!(rect1.intersection(&rect2), rect1);
189    }
190
191    #[test]
192    fn zero_sized_intersection() {
193        let rect1 = Rectangle::new(Point::new(1, 2), Size::new(0, 0));
194        let rect2 = Rectangle::new(Point::new(-10, -10), Size::new(20, 20));
195
196        assert_eq!(rect1.intersection(&rect2), rect1);
197
198        let rect1 = Rectangle::new(Point::new(-10, -10), Size::new(20, 20));
199        let rect2 = Rectangle::new(Point::new(2, 3), Size::new(0, 0));
200
201        assert_eq!(rect1.intersection(&rect2), rect2);
202    }
203
204    /// Test for issue #452
205    ///
206    /// Rectangles can intersect even if no corner of any rectangle is contained inside the other
207    /// rectangle.
208    ///
209    /// Example:
210    ///
211    ///     ****
212    ///     *  *
213    /// ############
214    /// #   *  *   #
215    /// #   *  *   #
216    /// ############
217    ///     *  *
218    ///     ****
219    #[test]
220    fn issue_452_broken_intersection_check() {
221        let rect1 = Rectangle::new(Point::new(50, 0), Size::new(75, 200));
222        let rect2 = Rectangle::new(Point::new(0, 75), Size::new(200, 50));
223
224        let expected = Rectangle::new(Point::new(50, 75), Size::new(75, 50));
225
226        assert_eq!(rect1.intersection(&rect2), expected);
227        assert_eq!(rect2.intersection(&rect1), expected);
228    }
229
230    #[test]
231    fn offset() {
232        let center = Point::new(10, 20);
233        let rect = Rectangle::with_center(center, Size::new(3, 4));
234
235        assert_eq!(rect.offset(0), rect);
236
237        assert_eq!(
238            rect.offset(1),
239            Rectangle::with_center(center, Size::new(5, 6))
240        );
241        assert_eq!(
242            rect.offset(2),
243            Rectangle::with_center(center, Size::new(7, 8))
244        );
245
246        assert_eq!(
247            rect.offset(-1),
248            Rectangle::with_center(center, Size::new(1, 2))
249        );
250        assert_eq!(
251            rect.offset(-2),
252            Rectangle::with_center(center, Size::new(0, 0))
253        );
254        assert_eq!(
255            rect.offset(-3),
256            Rectangle::with_center(center, Size::new(0, 0))
257        );
258    }
259
260    #[test]
261    fn resized_smaller() {
262        let rect = Rectangle::new(Point::new(10, 20), Size::new(30, 40));
263
264        for &(anchor_point, expected_top_left) in &[
265            (AnchorPoint::TopLeft, Point::new(10, 20)),
266            (AnchorPoint::TopCenter, Point::new(20, 20)),
267            (AnchorPoint::TopRight, Point::new(30, 20)),
268            (AnchorPoint::CenterLeft, Point::new(10, 30)),
269            (AnchorPoint::Center, Point::new(20, 30)),
270            (AnchorPoint::CenterRight, Point::new(30, 30)),
271            (AnchorPoint::BottomLeft, Point::new(10, 40)),
272            (AnchorPoint::BottomCenter, Point::new(20, 40)),
273            (AnchorPoint::BottomRight, Point::new(30, 40)),
274        ] {
275            let resized = rect.resized(Size::new(10, 20), anchor_point);
276
277            assert_eq!(
278                resized,
279                Rectangle::new(expected_top_left, Size::new(10, 20)),
280                "{:?}",
281                anchor_point,
282            );
283        }
284    }
285
286    #[test]
287    fn resized_larger() {
288        let rect = Rectangle::new(Point::new(10, 20), Size::new(30, 40));
289
290        for &(anchor_point, expected_top_left) in &[
291            (AnchorPoint::TopLeft, Point::new(10, 20)),
292            (AnchorPoint::TopCenter, Point::new(5, 20)),
293            (AnchorPoint::TopRight, Point::new(0, 20)),
294            (AnchorPoint::CenterLeft, Point::new(10, 15)),
295            (AnchorPoint::Center, Point::new(5, 15)),
296            (AnchorPoint::CenterRight, Point::new(0, 15)),
297            (AnchorPoint::BottomLeft, Point::new(10, 10)),
298            (AnchorPoint::BottomCenter, Point::new(5, 10)),
299            (AnchorPoint::BottomRight, Point::new(0, 10)),
300        ] {
301            let resized = rect.resized(Size::new(40, 50), anchor_point);
302
303            assert_eq!(
304                resized,
305                Rectangle::new(expected_top_left, Size::new(40, 50)),
306                "{:?}",
307                anchor_point,
308            );
309        }
310    }
311
312    #[test]
313    fn resized_zero_sized() {
314        let rect = Rectangle::new(Point::new(10, 20), Size::zero());
315
316        for &(anchor_point, expected_top_left) in &[
317            (AnchorPoint::TopLeft, Point::new(10, 20)),
318            (AnchorPoint::TopCenter, Point::new(8, 20)),
319            (AnchorPoint::TopRight, Point::new(6, 20)),
320            (AnchorPoint::CenterLeft, Point::new(10, 17)),
321            (AnchorPoint::Center, Point::new(8, 17)),
322            (AnchorPoint::CenterRight, Point::new(6, 17)),
323            (AnchorPoint::BottomLeft, Point::new(10, 14)),
324            (AnchorPoint::BottomCenter, Point::new(8, 14)),
325            (AnchorPoint::BottomRight, Point::new(6, 14)),
326        ] {
327            let resized = rect.resized(Size::new(5, 7), anchor_point);
328
329            assert_eq!(
330                resized,
331                Rectangle::new(expected_top_left, Size::new(5, 7)),
332                "{:?}",
333                anchor_point,
334            );
335        }
336    }
337
338    #[test]
339    fn resized_to_zero_sized() {
340        let rect = Rectangle::new(Point::new(10, 20), Size::new(21, 31));
341
342        for &(anchor_point, expected_top_left) in &[
343            (AnchorPoint::TopLeft, Point::new(10, 20)),
344            (AnchorPoint::TopCenter, Point::new(20, 20)),
345            (AnchorPoint::TopRight, Point::new(30, 20)),
346            (AnchorPoint::CenterLeft, Point::new(10, 35)),
347            (AnchorPoint::Center, Point::new(20, 35)),
348            (AnchorPoint::CenterRight, Point::new(30, 35)),
349            (AnchorPoint::BottomLeft, Point::new(10, 50)),
350            (AnchorPoint::BottomCenter, Point::new(20, 50)),
351            (AnchorPoint::BottomRight, Point::new(30, 50)),
352        ] {
353            let resized = rect.resized(Size::zero(), anchor_point);
354
355            assert_eq!(
356                resized,
357                Rectangle::new(expected_top_left, Size::zero()),
358                "{:?}",
359                anchor_point,
360            );
361        }
362    }
363
364    #[test]
365    fn anchor_point() {
366        let rect = Rectangle::new(Point::new(10, 20), Size::new(21, 31));
367
368        for &(anchor_point, expected) in &[
369            (AnchorPoint::TopLeft, Point::new(10, 20)),
370            (AnchorPoint::TopCenter, Point::new(20, 20)),
371            (AnchorPoint::TopRight, Point::new(30, 20)),
372            (AnchorPoint::CenterLeft, Point::new(10, 35)),
373            (AnchorPoint::Center, Point::new(20, 35)),
374            (AnchorPoint::CenterRight, Point::new(30, 35)),
375            (AnchorPoint::BottomLeft, Point::new(10, 50)),
376            (AnchorPoint::BottomCenter, Point::new(20, 50)),
377            (AnchorPoint::BottomRight, Point::new(30, 50)),
378        ] {
379            assert_eq!(
380                rect.anchor_point(anchor_point),
381                expected,
382                "{:?}",
383                anchor_point,
384            );
385        }
386    }
387
388    #[test]
389    fn rows_and_columns_zero_sized() {
390        let rect = Rectangle::zero();
391
392        assert_eq!(
393            rect.rows().next(),
394            None,
395            "the rows iterator for a zero sized rectangle shouldn't return any items"
396        );
397
398        assert_eq!(
399            rect.columns().next(),
400            None,
401            "the columns iterator for a zero sized rectangle shouldn't return any items"
402        );
403    }
404}