1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! `Rectangle` utility methods
//!
//! This module implements a few usfeul extensions to `Rectangle`.
use embedded_graphics::{prelude::*, primitives::Rectangle};

/// The trait that describes the extension methods.
pub trait RectExt {
    /// Create a new `Rectangle` from a top left point and a `Size`
    fn with_size(top_left: Point, size: Size) -> Rectangle;

    /// Return the horizontal center coordinate
    ///
    /// *Note:* when an object's width is an even number, the returned center point will not
    ///         be perfectly in the middle.
    fn center_x(&self) -> i32;

    /// Return the vertical center coordinate
    ///
    /// *Note:* when an object's height is an even number, the returned center point will not
    ///         be perfectly in the middle.
    fn center_y(&self) -> i32;

    /// Return the center point
    /// *Note:* when an object's width or height is an even number, the returned center point will
    ///         not be perfectly in the middle.
    fn center(&self) -> Point;

    /// Return the bounding `Rectangle` that encompasses both `Rectangles`
    fn enveloping(&self, other: &Rectangle) -> Rectangle;
}

impl RectExt for Rectangle {
    #[inline]
    fn with_size(top_left: Point, size: Size) -> Rectangle {
        Self::new(
            top_left,
            top_left + Point::new(size.width as i32 - 1, size.height as i32 - 1),
        )
    }

    #[inline]
    fn center_x(&self) -> i32 {
        (self.top_left.x + self.bottom_right.x) / 2
    }

    #[inline]
    fn center_y(&self) -> i32 {
        (self.top_left.y + self.bottom_right.y) / 2
    }

    #[inline]
    fn center(&self) -> Point {
        Point::new(self.center_x(), self.center_y())
    }

    #[inline]
    fn enveloping(&self, other: &Rectangle) -> Rectangle {
        Self::new(
            Point::new(
                self.top_left.x.min(other.top_left.x),
                self.top_left.y.min(other.top_left.y),
            ),
            Point::new(
                self.bottom_right.x.max(other.bottom_right.x),
                self.bottom_right.y.max(other.bottom_right.y),
            ),
        )
    }
}

/// This trait contains an override for the buggy embedded-graphics size implementation
///
/// This trait is for internal use only, use `View::size` instead.
pub trait RectSize {
    /// Return the `Size` of the `Rectangle`
    ///
    /// The `size` method provided by `embedded-graphics 0.6.2` returns an incorrect value.
    /// *Note:* Implementation assumes `top_left` and `bottom_right` coordinates are specified
    ///         properly, i.e. `top_left.x < bottom_right.x`, etc.
    fn size(self) -> Size;
}

impl RectSize for Rectangle {
    #[inline]
    fn size(self) -> Size {
        // TODO: remove if fixed in embedded-graphics
        let width = (self.bottom_right.x - self.top_left.x) as u32 + 1;
        let height = (self.bottom_right.y - self.top_left.y) as u32 + 1;

        Size::new(width, height)
    }
}

#[cfg(test)]
mod test {
    use crate::prelude::*;
    use embedded_graphics::{prelude::*, primitives::Rectangle};

    #[test]
    fn test_sized() {
        let rect0 = Rectangle::with_size(Point::new(-1, -1), Size::new(3, 3));

        assert_eq!(Point::new(1, 1), rect0.bottom_right);
    }

    #[test]
    fn test_enveloping() {
        let rect0 = Rectangle::new(Point::new(-1, -1), Point::new(0, 0));
        let rect1 = Rectangle::new(Point::zero(), Point::new(1, 1));
        let rect2 = Rectangle::new(Point::zero(), Point::new(2, 2));

        assert_eq!(rect2, rect2.enveloping(&rect1));
        assert_eq!(rect2, rect1.enveloping(&rect2));
        assert_eq!(
            Rectangle::new(Point::new(-1, -1), Point::new(2, 2),),
            rect0.enveloping(&rect2)
        );
    }
}