embedded_graphics/primitives/line/
points.rs

1use crate::{
2    geometry::Point,
3    primitives::line::{
4        bresenham::{self, Bresenham, BresenhamParameters},
5        Line,
6    },
7};
8
9/// Iterator over all points on the line.
10///
11/// See the [`points`] method documentation for more details.
12///
13/// [`points`]: struct.Line.html#method.points
14#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
15#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
16pub struct Points {
17    parameters: BresenhamParameters,
18    bresenham: Bresenham,
19    points_remaining: u32,
20}
21
22impl Points {
23    /// Creates an iterator over all points on the given line.
24    pub(in crate::primitives) fn new(line: &Line) -> Self {
25        let length = bresenham::major_length(line);
26        let parameters = BresenhamParameters::new(line);
27        let bresenham = Bresenham::new(line.start);
28
29        Self {
30            parameters,
31            bresenham,
32            points_remaining: length,
33        }
34    }
35
36    /// Creates an empty iterator.
37    pub(in crate::primitives) fn empty() -> Self {
38        let dummy = Line::new(Point::zero(), Point::zero());
39
40        let mut self_ = Self::new(&dummy);
41        self_.points_remaining = 0;
42
43        self_
44    }
45}
46
47impl Iterator for Points {
48    type Item = Point;
49
50    fn next(&mut self) -> Option<Self::Item> {
51        if self.points_remaining > 0 {
52            self.points_remaining -= 1;
53
54            Some(self.bresenham.next(&self.parameters))
55        } else {
56            None
57        }
58    }
59}
60
61#[cfg(test)]
62mod tests {
63
64    use super::*;
65    use crate::{
66        iterator::PixelIteratorExt, mock_display::MockDisplay, pixelcolor::BinaryColor,
67        primitives::PointsIter, Pixel,
68    };
69
70    fn test_points(start: Point, end: Point, expected: &[(i32, i32)]) {
71        let expected = expected.iter().copied().map(Point::from);
72
73        let points = Line::new(start, end).points();
74        assert!(points.eq(expected));
75    }
76
77    fn draw_lines(delta: Point) -> MockDisplay<BinaryColor> {
78        let mut display = MockDisplay::new();
79
80        for &quadrant in &[
81            Point::new(-1, -1),
82            Point::new(1, -1),
83            Point::new(-1, 1),
84            Point::new(1, 1),
85        ] {
86            let center = delta + Point::new_equal(1);
87            let start = center + quadrant;
88            let end = start + Point::new(delta.x * quadrant.x, delta.y * quadrant.y);
89
90            let line = Line::new(start, end);
91
92            line.points()
93                .map(|point| Pixel(point, BinaryColor::On))
94                .draw(&mut display)
95                .unwrap();
96        }
97
98        display
99    }
100
101    #[test]
102    fn lines_1() {
103        let delta = Point::new(6, 3);
104
105        let expected = MockDisplay::from_pattern(&[
106            "#             #",
107            " ##         ## ",
108            "   ##     ##   ",
109            "     ## ##     ",
110            "               ",
111            "     ## ##     ",
112            "   ##     ##   ",
113            " ##         ## ",
114            "#             #",
115        ]);
116
117        draw_lines(delta).assert_eq(&expected);
118
119        let expected = expected.swap_xy();
120        let delta = Point::new(delta.y, delta.x);
121        draw_lines(delta).assert_eq(&expected);
122    }
123
124    #[test]
125    fn lines_2() {
126        let delta = Point::new(9, 3);
127
128        let expected = MockDisplay::from_pattern(&[
129            "##                 ##",
130            "  ###           ###  ",
131            "     ###     ###     ",
132            "        ## ##        ",
133            "                     ",
134            "        ## ##        ",
135            "     ###     ###     ",
136            "  ###           ###  ",
137            "##                 ##",
138        ]);
139        draw_lines(delta).assert_eq(&expected);
140
141        let expected = expected.swap_xy();
142        let delta = Point::new(delta.y, delta.x);
143        draw_lines(delta).assert_eq(&expected);
144    }
145
146    #[test]
147    fn single_pixel() {
148        let start = Point::new(10, 10);
149        let end = Point::new(10, 10);
150
151        let expected = [(10, 10)];
152        test_points(start, end, &expected);
153    }
154
155    #[test]
156    fn short_correctly() {
157        let start = Point::new(2, 3);
158        let end = Point::new(3, 2);
159
160        let expected = [(2, 3), (3, 2)];
161        test_points(start, end, &expected);
162    }
163
164    #[test]
165    fn octant_1_correctly() {
166        let start = Point::new(10, 10);
167        let end = Point::new(15, 13);
168
169        let expected = [(10, 10), (11, 11), (12, 11), (13, 12), (14, 12), (15, 13)];
170        test_points(start, end, &expected);
171    }
172
173    #[test]
174    fn octant_2_correctly() {
175        let start = Point::new(10, 10);
176        let end = Point::new(13, 15);
177
178        let expected = [(10, 10), (11, 11), (11, 12), (12, 13), (12, 14), (13, 15)];
179        test_points(start, end, &expected);
180    }
181
182    #[test]
183    fn octant_3_correctly() {
184        let start = Point::new(10, 10);
185        let end = Point::new(7, 15);
186
187        let expected = [(10, 10), (9, 11), (9, 12), (8, 13), (8, 14), (7, 15)];
188        test_points(start, end, &expected);
189    }
190
191    #[test]
192    fn octant_4_correctly() {
193        let start = Point::new(10, 10);
194        let end = Point::new(5, 13);
195
196        let expected = [(10, 10), (9, 11), (8, 11), (7, 12), (6, 12), (5, 13)];
197        test_points(start, end, &expected);
198    }
199
200    #[test]
201    fn octant_5_correctly() {
202        let start = Point::new(10, 10);
203        let end = Point::new(5, 7);
204
205        let expected = [(10, 10), (9, 9), (8, 9), (7, 8), (6, 8), (5, 7)];
206        test_points(start, end, &expected);
207    }
208
209    #[test]
210    fn octant_6_correctly() {
211        let start = Point::new(10, 10);
212        let end = Point::new(7, 5);
213
214        let expected = [(10, 10), (9, 9), (9, 8), (8, 7), (8, 6), (7, 5)];
215        test_points(start, end, &expected);
216    }
217
218    #[test]
219    fn octant_7_correctly() {
220        let start = Point::new(10, 10);
221        let end = Point::new(13, 5);
222
223        let expected = [(10, 10), (11, 9), (11, 8), (12, 7), (12, 6), (13, 5)];
224        test_points(start, end, &expected);
225    }
226
227    #[test]
228    fn octant_8_correctly() {
229        let start = Point::new(10, 10);
230        let end = Point::new(15, 7);
231
232        let expected = [(10, 10), (11, 9), (12, 9), (13, 8), (14, 8), (15, 7)];
233        test_points(start, end, &expected);
234    }
235
236    #[test]
237    fn one_pixel_line() {
238        let p = Point::new(5, 6);
239        assert!(Line::new(p, p).points().eq(core::iter::once(p)));
240    }
241
242    #[test]
243    fn empty() {
244        assert!(Points::empty().eq(core::iter::empty()));
245    }
246}