1use 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 fn translate(&self, by: Point) -> Self {
56 Self {
57 top_left: self.top_left + by,
58 ..*self
59 }
60 }
61
62 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]
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}