1 | //! The rectangle primitive. |
2 | |
3 | mod points; |
4 | |
5 | use crate::{ |
6 | geometry::{AnchorPoint, AnchorX, AnchorY, Dimensions, Point, Size}, |
7 | primitives::PointsIter, |
8 | }; |
9 | use az::SaturatingAs; |
10 | use core::{ |
11 | cmp::min, |
12 | ops::{Range, RangeInclusive}, |
13 | }; |
14 | pub use points::Points; |
15 | |
16 | /// Rectangle primitive |
17 | /// |
18 | /// # Examples |
19 | /// |
20 | /// ## Create some rectangles with different styles |
21 | /// |
22 | /// ```rust |
23 | /// use embedded_graphics::{ |
24 | /// pixelcolor::Rgb565, prelude::*, primitives::{Rectangle, PrimitiveStyleBuilder}, |
25 | /// }; |
26 | /// # use embedded_graphics::mock_display::MockDisplay; |
27 | /// # let mut display = MockDisplay::default(); |
28 | /// |
29 | /// // Rectangle with red 3 pixel wide stroke and green fill with the top left corner at (30, 20) and |
30 | /// // a size of (10, 15) |
31 | /// let style = PrimitiveStyleBuilder::new() |
32 | /// .stroke_color(Rgb565::RED) |
33 | /// .stroke_width(3) |
34 | /// .fill_color(Rgb565::GREEN) |
35 | /// .build(); |
36 | /// |
37 | /// Rectangle::new(Point::new(30, 20), Size::new(10, 15)) |
38 | /// .into_styled(style) |
39 | /// .draw(&mut display)?; |
40 | /// |
41 | /// // Rectangle with translation applied |
42 | /// Rectangle::new(Point::new(30, 20), Size::new(10, 15)) |
43 | /// .translate(Point::new(-20, -10)) |
44 | /// .into_styled(style) |
45 | /// .draw(&mut display)?; |
46 | /// # Ok::<(), core::convert::Infallible>(()) |
47 | /// ``` |
48 | #[derive (Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)] |
49 | #[cfg_attr (feature = "defmt" , derive(::defmt::Format))] |
50 | pub struct Rectangle { |
51 | /// Top left point of the rectangle. |
52 | pub top_left: Point, |
53 | |
54 | /// Size of the rectangle. |
55 | pub size: Size, |
56 | } |
57 | |
58 | impl Dimensions for Rectangle { |
59 | fn bounding_box(&self) -> Rectangle { |
60 | *self |
61 | } |
62 | } |
63 | |
64 | impl PointsIter for Rectangle { |
65 | type Iter = Points; |
66 | |
67 | fn points(&self) -> Self::Iter { |
68 | self::Points::new(self) |
69 | } |
70 | } |
71 | |
72 | /// Returns the center offset. |
73 | /// |
74 | /// The center offset is defined as the offset between the top left corner and |
75 | /// the center point of a rectangle with the given size. |
76 | const fn center_offset(size: Size) -> Size { |
77 | size.saturating_sub(Size::new_equal(1)).div_u32(2) |
78 | } |
79 | |
80 | impl Rectangle { |
81 | /// Creates a new rectangle from the top left point and the size. |
82 | pub const fn new(top_left: Point, size: Size) -> Self { |
83 | Rectangle { top_left, size } |
84 | } |
85 | |
86 | /// Creates a new rectangle from two corners. |
87 | pub fn with_corners(corner_1: Point, corner_2: Point) -> Self { |
88 | let left = min(corner_1.x, corner_2.x); |
89 | let top = min(corner_1.y, corner_2.y); |
90 | |
91 | Rectangle { |
92 | top_left: Point::new(left, top), |
93 | size: Size::from_bounding_box(corner_1, corner_2), |
94 | } |
95 | } |
96 | |
97 | /// Creates a new rectangle from the center point and the size. |
98 | /// |
99 | /// For rectangles with even width and/or height the top left corner doesn't |
100 | /// align with the pixel grid. Because of this the coordinates of the top left |
101 | /// corner will be rounded up to the nearest integer coordinate. |
102 | pub const fn with_center(center: Point, size: Size) -> Self { |
103 | Rectangle { |
104 | top_left: center.sub_size(center_offset(size)), |
105 | size, |
106 | } |
107 | } |
108 | |
109 | /// Returns a zero sized rectangle. |
110 | pub const fn zero() -> Rectangle { |
111 | Rectangle::new(Point::zero(), Size::zero()) |
112 | } |
113 | |
114 | /// Returns the center of this rectangle. |
115 | /// |
116 | /// For rectangles with even width and/or height the returned value is rounded down |
117 | /// to the nearest integer coordinate. |
118 | pub fn center(&self) -> Point { |
119 | self.top_left + center_offset(self.size) |
120 | } |
121 | |
122 | /// Returns the bottom right corner of this rectangle. |
123 | /// |
124 | /// Because the smallest rectangle that can be represented by its corners |
125 | /// has a size of 1 x 1 pixels, this function returns `None` if the width or |
126 | /// height of the rectangle is zero. |
127 | pub fn bottom_right(&self) -> Option<Point> { |
128 | if self.size.width > 0 && self.size.height > 0 { |
129 | Some(self.top_left + self.size - Point::new(1, 1)) |
130 | } else { |
131 | None |
132 | } |
133 | } |
134 | |
135 | /// Return whether the rectangle contains a given point. |
136 | pub fn contains(&self, point: Point) -> bool { |
137 | if point.x >= self.top_left.x && point.y >= self.top_left.y { |
138 | self.bottom_right().map_or(false, |bottom_right| { |
139 | point.x <= bottom_right.x && point.y <= bottom_right.y |
140 | }) |
141 | } else { |
142 | false |
143 | } |
144 | } |
145 | |
146 | /// Returns a new `Rectangle` containing the intersection of `self` and `other`. |
147 | /// |
148 | /// If no intersection is present, this method will return a zero sized rectangle. |
149 | /// |
150 | /// # Examples |
151 | /// |
152 | /// ## Intersection |
153 | /// |
154 | /// This example draws two rectangles to a mock display using the `.` character, along with |
155 | /// their intersection shown with `#` characters. |
156 | /// |
157 | /// ```rust |
158 | /// use embedded_graphics::{ |
159 | /// mock_display::MockDisplay, pixelcolor::BinaryColor, prelude::*, |
160 | /// primitives::{Rectangle, PrimitiveStyle}, |
161 | /// }; |
162 | /// |
163 | /// let mut display = MockDisplay::new(); |
164 | /// # display.set_allow_overdraw(true); |
165 | /// |
166 | /// let rect1 = Rectangle::new(Point::zero(), Size::new(7, 8)); |
167 | /// let rect2 = Rectangle::new(Point::new(2, 3), Size::new(10, 7)); |
168 | /// |
169 | /// let intersection = rect1.intersection(&rect2); |
170 | /// |
171 | /// rect1 |
172 | /// .into_styled(PrimitiveStyle::with_stroke(BinaryColor::Off, 1)) |
173 | /// .draw(&mut display)?; |
174 | /// |
175 | /// rect2 |
176 | /// .into_styled(PrimitiveStyle::with_stroke(BinaryColor::Off, 1)) |
177 | /// .draw(&mut display)?; |
178 | /// |
179 | /// intersection |
180 | /// .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) |
181 | /// .draw(&mut display)?; |
182 | /// |
183 | /// display.assert_pattern(&[ |
184 | /// "....... " , |
185 | /// ". . " , |
186 | /// ". . " , |
187 | /// ". #####....." , |
188 | /// ". # # ." , |
189 | /// ". # # ." , |
190 | /// ". # # ." , |
191 | /// "..##### ." , |
192 | /// " . ." , |
193 | /// " .........." , |
194 | /// ]); |
195 | /// # Ok::<(), core::convert::Infallible>(()) |
196 | /// ``` |
197 | /// |
198 | /// ## No intersection |
199 | /// |
200 | /// This example creates two rectangles with no intersection between them. In this case, |
201 | /// `intersection` returns a zero-sized rectangle. |
202 | /// |
203 | /// ```rust |
204 | /// use embedded_graphics::{prelude::*, primitives::{Rectangle, PrimitiveStyle}}; |
205 | /// |
206 | /// let rect1 = Rectangle::new(Point::zero(), Size::new(7, 8)); |
207 | /// let rect2 = Rectangle::new(Point::new(10, 15), Size::new(10, 7)); |
208 | /// |
209 | /// let intersection = rect1.intersection(&rect2); |
210 | /// |
211 | /// assert!(intersection.is_zero_sized()); |
212 | /// # Ok::<(), core::convert::Infallible>(()) |
213 | /// ``` |
214 | pub fn intersection(&self, other: &Rectangle) -> Rectangle { |
215 | match (other.bottom_right(), self.bottom_right()) { |
216 | (Some(other_bottom_right), Some(self_bottom_right)) => { |
217 | if overlaps( |
218 | self.top_left.x..=self_bottom_right.x, |
219 | other.top_left.x..=other_bottom_right.x, |
220 | ) && overlaps( |
221 | self.top_left.y..=self_bottom_right.y, |
222 | other.top_left.y..=other_bottom_right.y, |
223 | ) { |
224 | return Rectangle::with_corners( |
225 | self.top_left.component_max(other.top_left), |
226 | self_bottom_right.component_min(other_bottom_right), |
227 | ); |
228 | } |
229 | } |
230 | (Some(_other_bottom_right), None) => { |
231 | // Check if zero sized self is inside other |
232 | if other.contains(self.top_left) { |
233 | return *self; |
234 | } |
235 | } |
236 | (None, Some(_self_bottom_right)) => { |
237 | // Check if zero sized other is inside self |
238 | if self.contains(other.top_left) { |
239 | return *other; |
240 | } |
241 | } |
242 | (None, None) => (), |
243 | }; |
244 | |
245 | // No overlap present |
246 | Rectangle::zero() |
247 | } |
248 | |
249 | /// Returns a resized copy of this rectangle. |
250 | /// |
251 | /// The rectangle is resized relative to the given anchor point. |
252 | /// |
253 | /// # Examples |
254 | /// |
255 | /// ``` |
256 | /// use embedded_graphics::{ |
257 | /// prelude::*, |
258 | /// primitives::rectangle::Rectangle, |
259 | /// geometry::AnchorPoint, |
260 | /// }; |
261 | /// |
262 | /// let rect = Rectangle::new(Point::new(20, 20), Size::new(10, 20)); |
263 | /// let resized = rect.resized(Size::new(20, 10), AnchorPoint::Center); |
264 | /// |
265 | /// assert_eq!( |
266 | /// resized, |
267 | /// Rectangle::new(Point::new(15, 25), Size::new(20, 10)) |
268 | /// ); |
269 | /// ``` |
270 | pub fn resized(&self, size: Size, anchor_point: AnchorPoint) -> Self { |
271 | let mut resized = self.clone(); |
272 | resized.resize_width_mut(size.width, anchor_point.x()); |
273 | resized.resize_height_mut(size.height, anchor_point.y()); |
274 | |
275 | resized |
276 | } |
277 | |
278 | /// Returns a new rectangle with the given width, resized relative to the given anchor edge. |
279 | /// |
280 | /// # Examples |
281 | /// |
282 | /// ``` |
283 | /// use embedded_graphics::{ |
284 | /// prelude::*, |
285 | /// primitives::rectangle::Rectangle, |
286 | /// geometry::AnchorX, |
287 | /// }; |
288 | /// |
289 | /// let rect = Rectangle::new(Point::new(20, 20), Size::new(10, 20)); |
290 | /// let resized = rect.resized_width(20, AnchorX::Center); |
291 | /// |
292 | /// assert_eq!( |
293 | /// resized, |
294 | /// Rectangle::new(Point::new(15, 20), Size::new(20, 20)) |
295 | /// ); |
296 | /// ``` |
297 | pub fn resized_width(&self, width: u32, anchor_x: AnchorX) -> Self { |
298 | let mut resized = self.clone(); |
299 | resized.resize_width_mut(width, anchor_x); |
300 | |
301 | resized |
302 | } |
303 | |
304 | /// Returns a new rectangle with the given height, resized relative to the given anchor edge. |
305 | /// |
306 | /// # Examples |
307 | /// |
308 | /// ``` |
309 | /// use embedded_graphics::{ |
310 | /// prelude::*, |
311 | /// primitives::rectangle::Rectangle, |
312 | /// geometry::AnchorY, |
313 | /// }; |
314 | /// |
315 | /// let rect = Rectangle::new(Point::new(20, 20), Size::new(10, 20)); |
316 | /// let resized = rect.resized_height(10, AnchorY::Center); |
317 | /// |
318 | /// assert_eq!( |
319 | /// resized, |
320 | /// Rectangle::new(Point::new(20, 25), Size::new(10, 10)) |
321 | /// ); |
322 | /// ``` |
323 | pub fn resized_height(&self, height: u32, anchor_y: AnchorY) -> Self { |
324 | let mut resized = self.clone(); |
325 | resized.resize_height_mut(height, anchor_y); |
326 | |
327 | resized |
328 | } |
329 | |
330 | fn resize_width_mut(&mut self, width: u32, anchor_x: AnchorX) { |
331 | // Assume size = 1 for zero sized dimensions. |
332 | let delta = |
333 | self.size.width.saturating_as::<i32>().max(1) - width.saturating_as::<i32>().max(1); |
334 | |
335 | self.top_left.x += match anchor_x { |
336 | AnchorX::Left => 0, |
337 | AnchorX::Center => delta / 2, |
338 | AnchorX::Right => delta, |
339 | }; |
340 | self.size.width = width; |
341 | } |
342 | |
343 | fn resize_height_mut(&mut self, height: u32, anchor_y: AnchorY) { |
344 | // Assume size = 1 for zero sized dimensions. |
345 | let delta = |
346 | self.size.height.saturating_as::<i32>().max(1) - height.saturating_as::<i32>().max(1); |
347 | |
348 | self.top_left.y += match anchor_y { |
349 | AnchorY::Top => 0, |
350 | AnchorY::Center => delta / 2, |
351 | AnchorY::Bottom => delta, |
352 | }; |
353 | self.size.height = height; |
354 | } |
355 | |
356 | /// Offset the rectangle by a given value. |
357 | /// |
358 | /// Negative values will shrink the rectangle. |
359 | pub fn offset(&self, offset: i32) -> Self { |
360 | let size = if offset >= 0 { |
361 | self.size.saturating_add(Size::new_equal(offset as u32 * 2)) |
362 | } else { |
363 | self.size |
364 | .saturating_sub(Size::new_equal((-offset) as u32 * 2)) |
365 | }; |
366 | |
367 | Self::with_center(self.center(), size) |
368 | } |
369 | |
370 | /// Returns an anchor point. |
371 | /// |
372 | /// # Examples |
373 | /// ``` |
374 | /// use embedded_graphics::{ |
375 | /// prelude::*, |
376 | /// primitives::rectangle::Rectangle, |
377 | /// geometry::AnchorPoint, |
378 | /// }; |
379 | /// |
380 | /// let mut rect = Rectangle::new(Point::new(20, 20), Size::new(11, 21)); |
381 | /// |
382 | /// assert_eq!(rect.anchor_point(AnchorPoint::TopLeft), Point::new(20, 20)); |
383 | /// assert_eq!( |
384 | /// rect.anchor_point(AnchorPoint::BottomCenter), |
385 | /// Point::new(25, 40) |
386 | /// ); |
387 | /// ``` |
388 | pub fn anchor_point(&self, anchor_point: AnchorPoint) -> Point { |
389 | Point::new( |
390 | self.anchor_x(anchor_point.x()), |
391 | self.anchor_y(anchor_point.y()), |
392 | ) |
393 | } |
394 | |
395 | /// Returns the X coordinate of a given anchor edge of the rectangle. |
396 | /// |
397 | /// # Examples |
398 | /// |
399 | /// ``` |
400 | /// use embedded_graphics::{ |
401 | /// prelude::*, |
402 | /// primitives::rectangle::Rectangle, |
403 | /// geometry::AnchorX, |
404 | /// }; |
405 | /// |
406 | /// let mut rect = Rectangle::new(Point::new(20, 20), Size::new(11, 21)); |
407 | /// |
408 | /// assert_eq!(rect.anchor_x(AnchorX::Left), 20); |
409 | /// assert_eq!(rect.anchor_x(AnchorX::Center), 25); |
410 | /// ``` |
411 | pub fn anchor_x(&self, anchor_x: AnchorX) -> i32 { |
412 | // Assume size = 1 for zero sized dimensions. |
413 | let delta = self.size.width.saturating_as::<i32>().max(1) - 1; |
414 | |
415 | self.top_left.x |
416 | + match anchor_x { |
417 | AnchorX::Left => 0, |
418 | AnchorX::Center => delta / 2, |
419 | AnchorX::Right => delta, |
420 | } |
421 | } |
422 | |
423 | /// Returns the Y coordinate of a given anchor edge of the rectangle. |
424 | /// |
425 | /// # Examples |
426 | /// |
427 | /// ``` |
428 | /// use embedded_graphics::{ |
429 | /// prelude::*, |
430 | /// primitives::rectangle::Rectangle, |
431 | /// geometry::AnchorY, |
432 | /// }; |
433 | /// |
434 | /// let mut rect = Rectangle::new(Point::new(20, 20), Size::new(11, 21)); |
435 | /// |
436 | /// assert_eq!(rect.anchor_y(AnchorY::Top), 20); |
437 | /// assert_eq!(rect.anchor_y(AnchorY::Bottom), 40); |
438 | /// ``` |
439 | pub fn anchor_y(&self, anchor_y: AnchorY) -> i32 { |
440 | // Assume size = 1 for zero sized dimensions. |
441 | let delta = self.size.height.saturating_as::<i32>().max(1) - 1; |
442 | |
443 | self.top_left.y |
444 | + match anchor_y { |
445 | AnchorY::Top => 0, |
446 | AnchorY::Center => delta / 2, |
447 | AnchorY::Bottom => delta, |
448 | } |
449 | } |
450 | |
451 | /// Returns the range of Y coordinates in this rectangle. |
452 | /// |
453 | /// # Examples |
454 | /// |
455 | /// ``` |
456 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
457 | /// |
458 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(3, 4)); |
459 | /// assert_eq!(rect.rows(), 20..24); |
460 | /// ``` |
461 | /// |
462 | /// By combining this method with [`columns`] it is possible to iterate over all pixels inside |
463 | /// the rectangle. This can be more flexible than using the [`points`] iterator, for example, |
464 | /// if a different iteration order is required or some operations should be called once per row. |
465 | /// |
466 | /// ``` |
467 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
468 | /// |
469 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(3, 4)); |
470 | /// |
471 | /// // Iterate over the y coordinates of the rows in reverse order. |
472 | /// for y in rect.rows().rev() { |
473 | /// for x in rect.columns() { |
474 | /// // use x, y coordinates |
475 | /// } |
476 | /// } |
477 | /// ``` |
478 | /// |
479 | /// [`columns`]: Rectangle::columns() |
480 | /// [`points`]: super::PointsIter::points |
481 | pub fn rows(&self) -> Range<i32> { |
482 | self.top_left.y |
483 | ..self |
484 | .top_left |
485 | .y |
486 | .saturating_add(self.size.height.saturating_as()) |
487 | } |
488 | |
489 | /// Returns the range of X coordinates in this rectangle. |
490 | /// |
491 | /// # Examples |
492 | /// |
493 | /// ``` |
494 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
495 | /// |
496 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(3, 4)); |
497 | /// |
498 | /// assert_eq!(rect.columns(), 10..13); |
499 | /// ``` |
500 | /// |
501 | /// By combining this method with [`rows`] it is possible to iterator over all pixels inside |
502 | /// the rectangle. This can be more flexible than using the [`points`] iterator, for example, |
503 | /// if a different iteration order is required or some operations should be called once per row. |
504 | /// |
505 | /// ``` |
506 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
507 | /// |
508 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(3, 4)); |
509 | /// |
510 | /// // Iterate over all points starting from the top right corner and advancing downwards. |
511 | /// for x in rect.columns().rev() { |
512 | /// for y in rect.rows() { |
513 | /// // use x, y coordinates |
514 | /// } |
515 | /// } |
516 | /// ``` |
517 | /// |
518 | /// [`rows`]: Rectangle::rows() |
519 | /// [`points`]: super::PointsIter::points |
520 | pub fn columns(&self) -> Range<i32> { |
521 | self.top_left.x |
522 | ..self |
523 | .top_left |
524 | .x |
525 | .saturating_add(self.size.width.saturating_as()) |
526 | } |
527 | |
528 | /// Returns `true` is the rectangle is zero sized. |
529 | /// |
530 | /// A rectangle is zero sized if the width or height are zero. |
531 | /// |
532 | /// # Examples |
533 | /// ``` |
534 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
535 | /// |
536 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(10, 20)); |
537 | /// assert_eq!(rect.is_zero_sized(), false); |
538 | /// |
539 | /// let rect = Rectangle::new(Point::new(10, 20), Size::zero()); |
540 | /// assert_eq!(rect.is_zero_sized(), true); |
541 | /// ``` |
542 | pub const fn is_zero_sized(&self) -> bool { |
543 | self.size.height == 0 || self.size.width == 0 |
544 | } |
545 | } |
546 | |
547 | /// Checks if the two ranges overlap. |
548 | fn overlaps(first: RangeInclusive<i32>, second: RangeInclusive<i32>) -> bool { |
549 | second.contains(item:first.start()) |
550 | || second.contains(item:first.end()) |
551 | || first.start() < second.start() && first.end() > second.end() |
552 | } |
553 | |
554 | #[cfg (test)] |
555 | mod tests { |
556 | use super::*; |
557 | use crate::geometry::{Dimensions, Point, Size}; |
558 | |
559 | #[test ] |
560 | fn dimensions() { |
561 | let rect = Rectangle::new(Point::new(5, 10), Size::new(10, 20)); |
562 | |
563 | assert_eq!( |
564 | rect.bounding_box(), |
565 | Rectangle::new(Point::new(5, 10), Size::new(10, 20)) |
566 | ); |
567 | } |
568 | |
569 | #[test ] |
570 | fn center() { |
571 | let odd = Rectangle::new(Point::new(10, 20), Size::new(5, 7)); |
572 | assert_eq!(odd.center(), Point::new(12, 23)); |
573 | |
574 | let even = Rectangle::new(Point::new(20, 30), Size::new(4, 8)); |
575 | assert_eq!(even.center(), Point::new(21, 33)); |
576 | } |
577 | |
578 | #[test ] |
579 | fn bottom_right() { |
580 | let zero = Rectangle::new(Point::new(10, 20), Size::zero()); |
581 | assert_eq!(zero.bottom_right(), None); |
582 | |
583 | let odd = Rectangle::new(Point::new(10, 20), Size::new(5, 7)); |
584 | assert_eq!(odd.bottom_right(), Some(Point::new(14, 26))); |
585 | |
586 | let even = Rectangle::new(Point::new(20, 30), Size::new(4, 8)); |
587 | assert_eq!(even.bottom_right(), Some(Point::new(23, 37))); |
588 | } |
589 | |
590 | #[test ] |
591 | fn rectangle_intersection() { |
592 | let rect1 = Rectangle::new(Point::new_equal(10), Size::new(20, 30)); |
593 | let rect2 = Rectangle::new(Point::new_equal(25), Size::new(30, 40)); |
594 | |
595 | assert_eq!( |
596 | rect1.intersection(&rect2), |
597 | Rectangle::new(Point::new_equal(25), Size::new(5, 15)) |
598 | ); |
599 | } |
600 | |
601 | #[test ] |
602 | fn rectangle_no_intersection() { |
603 | let rect1 = Rectangle::new(Point::new_equal(10), Size::new(20, 30)); |
604 | let rect2 = Rectangle::new(Point::new_equal(35), Size::new(30, 40)); |
605 | |
606 | assert_eq!( |
607 | rect1.intersection(&rect2), |
608 | Rectangle::new(Point::zero(), Size::zero()) |
609 | ); |
610 | } |
611 | |
612 | #[test ] |
613 | fn rectangle_complete_intersection() { |
614 | let rect1 = Rectangle::new(Point::new_equal(10), Size::new(20, 30)); |
615 | let rect2 = rect1; |
616 | |
617 | assert_eq!(rect1.intersection(&rect2), rect1); |
618 | } |
619 | |
620 | #[test ] |
621 | fn rectangle_contained_intersection() { |
622 | let rect1 = Rectangle::with_corners(Point::new_equal(10), Point::new(20, 30)); |
623 | let rect2 = Rectangle::with_corners(Point::new_equal(5), Point::new(30, 40)); |
624 | |
625 | assert_eq!(rect1.intersection(&rect2), rect1); |
626 | } |
627 | |
628 | #[test ] |
629 | fn zero_sized_intersection() { |
630 | let rect1 = Rectangle::new(Point::new(1, 2), Size::new(0, 0)); |
631 | let rect2 = Rectangle::new(Point::new(-10, -10), Size::new(20, 20)); |
632 | |
633 | assert_eq!(rect1.intersection(&rect2), rect1); |
634 | |
635 | let rect1 = Rectangle::new(Point::new(-10, -10), Size::new(20, 20)); |
636 | let rect2 = Rectangle::new(Point::new(2, 3), Size::new(0, 0)); |
637 | |
638 | assert_eq!(rect1.intersection(&rect2), rect2); |
639 | } |
640 | |
641 | /// Test for issue #452 |
642 | /// |
643 | /// Rectangles can intersect even if no corner of any rectangle is contained inside the other |
644 | /// rectangle. |
645 | /// |
646 | /// Example: |
647 | /// |
648 | /// **** |
649 | /// * * |
650 | /// ############ |
651 | /// # * * # |
652 | /// # * * # |
653 | /// ############ |
654 | /// * * |
655 | /// **** |
656 | #[test ] |
657 | fn issue_452_broken_intersection_check() { |
658 | let rect1 = Rectangle::new(Point::new(50, 0), Size::new(75, 200)); |
659 | let rect2 = Rectangle::new(Point::new(0, 75), Size::new(200, 50)); |
660 | |
661 | let expected = Rectangle::new(Point::new(50, 75), Size::new(75, 50)); |
662 | |
663 | assert_eq!(rect1.intersection(&rect2), expected); |
664 | assert_eq!(rect2.intersection(&rect1), expected); |
665 | } |
666 | |
667 | #[test ] |
668 | fn offset() { |
669 | let center = Point::new(10, 20); |
670 | let rect = Rectangle::with_center(center, Size::new(3, 4)); |
671 | |
672 | assert_eq!(rect.offset(0), rect); |
673 | |
674 | assert_eq!( |
675 | rect.offset(1), |
676 | Rectangle::with_center(center, Size::new(5, 6)) |
677 | ); |
678 | assert_eq!( |
679 | rect.offset(2), |
680 | Rectangle::with_center(center, Size::new(7, 8)) |
681 | ); |
682 | |
683 | assert_eq!( |
684 | rect.offset(-1), |
685 | Rectangle::with_center(center, Size::new(1, 2)) |
686 | ); |
687 | assert_eq!( |
688 | rect.offset(-2), |
689 | Rectangle::with_center(center, Size::new(0, 0)) |
690 | ); |
691 | assert_eq!( |
692 | rect.offset(-3), |
693 | Rectangle::with_center(center, Size::new(0, 0)) |
694 | ); |
695 | } |
696 | |
697 | fn test_resized(rect: Rectangle, target_size: Size, tests: &[(AnchorPoint, Point)]) { |
698 | for &(anchor_point, expected_top_left) in tests { |
699 | let resized = rect.resized(target_size, anchor_point); |
700 | let expected = Rectangle::new(expected_top_left, target_size); |
701 | |
702 | assert_eq!(resized, expected, "{:?}" , anchor_point); |
703 | |
704 | let resized_x = rect.resized_width(target_size.width, anchor_point.x()); |
705 | assert_eq!( |
706 | resized_x.top_left, |
707 | Point::new(resized.top_left.x, rect.top_left.y) |
708 | ); |
709 | assert_eq!( |
710 | resized_x.size, |
711 | Size::new(resized.size.width, rect.size.height) |
712 | ); |
713 | |
714 | let resized_y = rect.resized_height(target_size.height, anchor_point.y()); |
715 | assert_eq!( |
716 | resized_y.top_left, |
717 | Point::new(rect.top_left.x, resized.top_left.y) |
718 | ); |
719 | assert_eq!( |
720 | resized_y.size, |
721 | Size::new(rect.size.width, resized.size.height) |
722 | ); |
723 | } |
724 | } |
725 | |
726 | #[test ] |
727 | fn resized_smaller() { |
728 | test_resized( |
729 | Rectangle::new(Point::new(10, 20), Size::new(30, 40)), |
730 | Size::new(10, 20), |
731 | &[ |
732 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
733 | (AnchorPoint::TopCenter, Point::new(20, 20)), |
734 | (AnchorPoint::TopRight, Point::new(30, 20)), |
735 | (AnchorPoint::CenterLeft, Point::new(10, 30)), |
736 | (AnchorPoint::Center, Point::new(20, 30)), |
737 | (AnchorPoint::CenterRight, Point::new(30, 30)), |
738 | (AnchorPoint::BottomLeft, Point::new(10, 40)), |
739 | (AnchorPoint::BottomCenter, Point::new(20, 40)), |
740 | (AnchorPoint::BottomRight, Point::new(30, 40)), |
741 | ], |
742 | ); |
743 | } |
744 | |
745 | #[test ] |
746 | fn resized_larger() { |
747 | test_resized( |
748 | Rectangle::new(Point::new(10, 20), Size::new(30, 40)), |
749 | Size::new(40, 50), |
750 | &[ |
751 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
752 | (AnchorPoint::TopCenter, Point::new(5, 20)), |
753 | (AnchorPoint::TopRight, Point::new(0, 20)), |
754 | (AnchorPoint::CenterLeft, Point::new(10, 15)), |
755 | (AnchorPoint::Center, Point::new(5, 15)), |
756 | (AnchorPoint::CenterRight, Point::new(0, 15)), |
757 | (AnchorPoint::BottomLeft, Point::new(10, 10)), |
758 | (AnchorPoint::BottomCenter, Point::new(5, 10)), |
759 | (AnchorPoint::BottomRight, Point::new(0, 10)), |
760 | ], |
761 | ); |
762 | } |
763 | |
764 | #[test ] |
765 | fn resized_zero_sized() { |
766 | test_resized( |
767 | Rectangle::new(Point::new(10, 20), Size::zero()), |
768 | Size::new(5, 7), |
769 | &[ |
770 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
771 | (AnchorPoint::TopCenter, Point::new(8, 20)), |
772 | (AnchorPoint::TopRight, Point::new(6, 20)), |
773 | (AnchorPoint::CenterLeft, Point::new(10, 17)), |
774 | (AnchorPoint::Center, Point::new(8, 17)), |
775 | (AnchorPoint::CenterRight, Point::new(6, 17)), |
776 | (AnchorPoint::BottomLeft, Point::new(10, 14)), |
777 | (AnchorPoint::BottomCenter, Point::new(8, 14)), |
778 | (AnchorPoint::BottomRight, Point::new(6, 14)), |
779 | ], |
780 | ); |
781 | } |
782 | |
783 | #[test ] |
784 | fn resized_to_zero_sized() { |
785 | test_resized( |
786 | Rectangle::new(Point::new(10, 20), Size::new(21, 31)), |
787 | Size::zero(), |
788 | &[ |
789 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
790 | (AnchorPoint::TopCenter, Point::new(20, 20)), |
791 | (AnchorPoint::TopRight, Point::new(30, 20)), |
792 | (AnchorPoint::CenterLeft, Point::new(10, 35)), |
793 | (AnchorPoint::Center, Point::new(20, 35)), |
794 | (AnchorPoint::CenterRight, Point::new(30, 35)), |
795 | (AnchorPoint::BottomLeft, Point::new(10, 50)), |
796 | (AnchorPoint::BottomCenter, Point::new(20, 50)), |
797 | (AnchorPoint::BottomRight, Point::new(30, 50)), |
798 | ], |
799 | ); |
800 | } |
801 | |
802 | #[test ] |
803 | fn anchor_point() { |
804 | let rect = Rectangle::new(Point::new(10, 20), Size::new(21, 31)); |
805 | |
806 | for &(anchor_point, expected) in &[ |
807 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
808 | (AnchorPoint::TopCenter, Point::new(20, 20)), |
809 | (AnchorPoint::TopRight, Point::new(30, 20)), |
810 | (AnchorPoint::CenterLeft, Point::new(10, 35)), |
811 | (AnchorPoint::Center, Point::new(20, 35)), |
812 | (AnchorPoint::CenterRight, Point::new(30, 35)), |
813 | (AnchorPoint::BottomLeft, Point::new(10, 50)), |
814 | (AnchorPoint::BottomCenter, Point::new(20, 50)), |
815 | (AnchorPoint::BottomRight, Point::new(30, 50)), |
816 | ] { |
817 | assert_eq!( |
818 | rect.anchor_point(anchor_point), |
819 | expected, |
820 | "{:?}" , |
821 | anchor_point, |
822 | ); |
823 | |
824 | assert_eq!( |
825 | rect.anchor_x(anchor_point.x()), |
826 | expected.x, |
827 | "{:?}.x()" , |
828 | anchor_point |
829 | ); |
830 | |
831 | assert_eq!( |
832 | rect.anchor_y(anchor_point.y()), |
833 | expected.y, |
834 | "{:?}.y()" , |
835 | anchor_point |
836 | ); |
837 | } |
838 | } |
839 | |
840 | #[test ] |
841 | fn rows_and_columns_zero_sized() { |
842 | let rect = Rectangle::zero(); |
843 | |
844 | assert_eq!( |
845 | rect.rows().next(), |
846 | None, |
847 | "the rows iterator for a zero sized rectangle shouldn't return any items" |
848 | ); |
849 | |
850 | assert_eq!( |
851 | rect.columns().next(), |
852 | None, |
853 | "the columns iterator for a zero sized rectangle shouldn't return any items" |
854 | ); |
855 | } |
856 | } |
857 | |