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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! The rectangle primitive. Also good for drawing squares.

use super::super::drawable::*;
use super::super::transform::*;
use coord::{Coord, ToUnsigned};

// TODO: Impl Default so people can leave the color bit out
/// Rectangle primitive
#[derive(Debug, Clone, Copy)]
pub struct Rect {
    /// Top left point of the rect
    pub top_left: Coord,

    /// Bottom right point of the rect
    pub bottom_right: Coord,

    /// Border color
    pub color: Color,
}

impl Rect {
    /// Create a new rectangle from the top left point to the bottom right point with a given border
    /// color
    pub fn new(top_left: Coord, bottom_right: Coord, color: u8) -> Self {
        Rect {
            top_left,
            bottom_right,
            color,
        }
    }
}

impl<'a> IntoIterator for &'a Rect {
    type Item = Pixel;
    type IntoIter = RectIterator;

    fn into_iter(self) -> Self::IntoIter {
        RectIterator {
            top_left: self.top_left,
            bottom_right: self.bottom_right,
            color: self.color,
            x: self.top_left[0],
            y: self.top_left[1],
            screen_size: (self.bottom_right - self.top_left).abs(),
        }
    }
}

/// Pixel iterator for each pixel in the rect border
#[derive(Debug, Clone, Copy)]
pub struct RectIterator {
    top_left: Coord,
    bottom_right: Coord,
    color: Color,
    x: i32,
    y: i32,
    screen_size: Coord,
}

impl Iterator for RectIterator {
    type Item = Pixel;

    fn next(&mut self) -> Option<Self::Item> {
        // Entire object is off the top left of the screen, so render nothing
        if (self.top_left[0] < 0 || self.top_left[1] < 0)
            && (self.bottom_right[0] < 0 || self.bottom_right[1] < 0)
        {
            return None;
        }

        let coord = loop {
            // If we're below the rect, it's completely rendered and we're done
            if self.y > self.bottom_right[1] {
                return None;
            }

            let coord = Coord::new(self.x, self.y);

            // Step across 1 if rendering top/bottom lines
            if self.y == self.top_left[1] || self.y == self.bottom_right[1] {
                self.x += 1;
            }
            // Skip across rect empty space if rendering left/right lines
            else {
                self.x += self.screen_size[0];
            }

            // Reached end of row? Jump down one line
            if self.x > self.bottom_right[0] {
                self.x = self.top_left[0];
                self.y += 1;
            }

            if coord[0] >= 0 && coord[1] >= 0 {
                break coord;
            }
        };

        Some((coord.to_unsigned(), self.color))
    }
}

impl Drawable for Rect {}

impl Transform for Rect {
    /// Translate the rect from its current position to a new position by (x, y) pixels, returning
    /// a new `Rect`. For a mutating transform, see `translate_mut`.
    ///
    /// ```
    /// # use embedded_graphics::primitives::Rect;
    /// # use embedded_graphics::transform::Transform;
    /// # use embedded_graphics::coord::Coord;
    ///
    /// let rect = Rect::new(Coord::new(5, 10), Coord::new(15, 20), 1);
    /// let moved = rect.translate(Coord::new(10, 10));
    ///
    /// assert_eq!(moved.top_left, Coord::new(15, 20));
    /// assert_eq!(moved.bottom_right, Coord::new(25, 30));
    /// ```
    fn translate(&self, by: Coord) -> Self {
        Self {
            top_left: self.top_left + by,
            bottom_right: self.bottom_right + by,
            ..*self
        }
    }

    /// Translate the rect from its current position to a new position by (x, y) pixels.
    ///
    /// ```
    /// # use embedded_graphics::primitives::Rect;
    /// # use embedded_graphics::transform::Transform;
    /// # use embedded_graphics::coord::Coord;
    ///
    /// let mut rect = Rect::new(Coord::new(5, 10), Coord::new(15, 20), 1);
    /// rect.translate_mut(Coord::new(10, 10));
    ///
    /// assert_eq!(rect.top_left, Coord::new(15, 20));
    /// assert_eq!(rect.bottom_right, Coord::new(25, 30));
    /// ```
    fn translate_mut(&mut self, by: Coord) -> &mut Self {
        self.top_left += by;
        self.bottom_right += by;

        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use unsignedcoord::UnsignedCoord;

    #[test]
    fn it_can_be_translated() {
        let rect = Rect::new(Coord::new(5, 10), Coord::new(15, 20), 1);
        let moved = rect.translate(Coord::new(10, 10));

        assert_eq!(moved.top_left, Coord::new(15, 20));
        assert_eq!(moved.bottom_right, Coord::new(25, 30));
    }

    #[test]
    fn it_draws_unfilled_rect() {
        let mut rect = Rect::new(Coord::new(2, 2), Coord::new(4, 4), 1).into_iter();

        assert_eq!(rect.next(), Some((UnsignedCoord::new(2, 2), 1)));
        assert_eq!(rect.next(), Some((UnsignedCoord::new(3, 2), 1)));
        assert_eq!(rect.next(), Some((UnsignedCoord::new(4, 2), 1)));

        assert_eq!(rect.next(), Some((UnsignedCoord::new(2, 3), 1)));
        assert_eq!(rect.next(), Some((UnsignedCoord::new(4, 3), 1)));

        assert_eq!(rect.next(), Some((UnsignedCoord::new(2, 4), 1)));
        assert_eq!(rect.next(), Some((UnsignedCoord::new(3, 4), 1)));
        assert_eq!(rect.next(), Some((UnsignedCoord::new(4, 4), 1)));
    }

    #[test]
    fn it_can_be_negative() {
        let mut rect = Rect::new(Coord::new(-2, -2), Coord::new(2, 2), 1).into_iter();

        // TODO: Macro
        // Only the bottom right corner of the rect should be visible
        assert_eq!(rect.next(), Some((UnsignedCoord::new(2, 0), 1)));
        assert_eq!(rect.next(), Some((UnsignedCoord::new(2, 1), 1)));
        assert_eq!(rect.next(), Some((UnsignedCoord::new(0, 2), 1)));
        assert_eq!(rect.next(), Some((UnsignedCoord::new(1, 2), 1)));
        assert_eq!(rect.next(), Some((UnsignedCoord::new(2, 2), 1)));
    }
}