embedded_graphics_core/primitives/rectangle/mod.rs
1//! The rectangle primitive.
2
3mod points;
4
5use crate::{
6 geometry::{AnchorPoint, AnchorX, AnchorY, Dimensions, Point, Size},
7 primitives::PointsIter,
8};
9use az::SaturatingAs;
10use core::{
11 cmp::min,
12 ops::{Range, RangeInclusive},
13};
14pub 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))]
50pub 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
58impl Dimensions for Rectangle {
59 fn bounding_box(&self) -> Rectangle {
60 *self
61 }
62}
63
64impl 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.
76const fn center_offset(size: Size) -> Size {
77 size.saturating_sub(Size::new_equal(1)).div_u32(2)
78}
79
80impl 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.
548fn overlaps(first: RangeInclusive<i32>, second: RangeInclusive<i32>) -> bool {
549 second.contains(first.start())
550 || second.contains(first.end())
551 || first.start() < second.start() && first.end() > second.end()
552}
553
554#[cfg(test)]
555mod 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}