1 | //! The rectangle primitive. Also good for drawing squares. |
2 | |
3 | use crate::{ |
4 | geometry::{Point, Size}, |
5 | primitives::{ContainsPoint, OffsetOutline, Primitive}, |
6 | transform::Transform, |
7 | }; |
8 | |
9 | pub use embedded_graphics_core::primitives::{rectangle::Points, Rectangle}; |
10 | |
11 | mod styled; |
12 | |
13 | pub use styled::StyledPixelsIterator; |
14 | |
15 | impl Primitive for Rectangle {} |
16 | |
17 | impl 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(default:false, |bottom_right: Point| { |
21 | point.x <= bottom_right.x && point.y <= bottom_right.y |
22 | }) |
23 | } else { |
24 | false |
25 | } |
26 | } |
27 | } |
28 | |
29 | impl OffsetOutline for Rectangle { |
30 | fn offset(&self, offset: i32) -> Self { |
31 | let size: 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 | |
42 | impl 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)] |
81 | mod 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 | } |
405 | |