embedded_graphics/primitives/circle/
points.rs

1use core::ops::Range;
2
3use crate::{
4    geometry::{Dimensions, Point, PointExt},
5    primitives::{circle::Circle, common::Scanline},
6};
7
8/// Iterator over all points inside the circle.
9#[derive(Clone, Eq, PartialEq, Hash, Debug)]
10#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
11pub struct Points {
12    scanlines: Scanlines,
13    current_scanline: Scanline,
14}
15
16impl Points {
17    pub(in crate::primitives) fn new(circle: &Circle) -> Self {
18        Self {
19            scanlines: Scanlines::new(circle),
20            current_scanline: Scanline::new_empty(0),
21        }
22    }
23}
24
25impl Iterator for Points {
26    type Item = Point;
27
28    fn next(&mut self) -> Option<Self::Item> {
29        self.current_scanline.next().or_else(|| {
30            self.current_scanline = self.scanlines.next()?;
31            self.current_scanline.next()
32        })
33    }
34}
35
36#[derive(Clone, Eq, PartialEq, Hash, Debug)]
37#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
38pub struct Scanlines {
39    rows: Range<i32>,
40    columns: Range<i32>,
41    pub(super) center_2x: Point,
42    threshold: u32,
43}
44
45impl Scanlines {
46    pub fn new(circle: &Circle) -> Self {
47        let bounding_box = circle.bounding_box();
48
49        Self {
50            rows: bounding_box.rows(),
51            columns: bounding_box.columns(),
52            center_2x: circle.center_2x(),
53            threshold: circle.threshold(),
54        }
55    }
56}
57
58impl Iterator for Scanlines {
59    type Item = Scanline;
60
61    fn next(&mut self) -> Option<Self::Item> {
62        let y = self.rows.next()?;
63
64        self.columns
65            .clone()
66            // find first pixel that is inside the threshold
67            .find(|x| {
68                let delta = Point::new(*x, y) * 2 - self.center_2x;
69                (delta.length_squared() as u32) < self.threshold
70            })
71            // shorten the scanline by right side of the same amount as the left side
72            .map(|x| Scanline::new(y, x..self.columns.end - (x - self.columns.start)))
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::{
80        geometry::Point, mock_display::MockDisplay, pixelcolor::BinaryColor, primitives::PointsIter,
81    };
82
83    fn test_circle(diameter: u32, pattern: &[&str]) {
84        let display = MockDisplay::from_points(
85            Circle::new(Point::new(0, 0), diameter).points(),
86            BinaryColor::On,
87        );
88
89        display.assert_pattern(pattern);
90    }
91
92    #[test]
93    fn circle_1() {
94        #[rustfmt::skip]
95        test_circle(1, &[
96            "#",
97        ],);
98    }
99
100    #[test]
101    fn circle_2() {
102        #[rustfmt::skip]
103        test_circle(2, &[
104            "##",
105            "##",
106        ],);
107    }
108
109    #[test]
110    fn circle_3() {
111        #[rustfmt::skip]
112        test_circle(3, &[
113            " # ",
114            "###",
115            " # ",
116        ],);
117    }
118
119    #[test]
120    fn circle_4() {
121        #[rustfmt::skip]
122        test_circle(4, &[
123            " ## ",
124            "####",
125            "####",
126            " ## ",
127        ],);
128    }
129
130    #[test]
131    fn circle_5() {
132        #[rustfmt::skip]
133        test_circle(5, &[
134            " ### ",
135            "#####",
136            "#####",
137            "#####",
138            " ### ",
139        ],);
140    }
141
142    #[test]
143    fn circle_6() {
144        #[rustfmt::skip]
145        test_circle(6, &[
146            " #### ",
147            "######",
148            "######",
149            "######",
150            "######",
151            " #### ",
152        ],);
153    }
154
155    #[test]
156    fn circle_7() {
157        #[rustfmt::skip]
158        test_circle(7, &[
159            "  ###  ",
160            " ##### ",
161            "#######",
162            "#######",
163            "#######",
164            " ##### ",
165            "  ###  ",
166        ],);
167    }
168
169    #[test]
170    fn circle_8() {
171        #[rustfmt::skip]
172        test_circle(8, &[
173            "  ####  ",
174            " ###### ",
175            "########",
176            "########",
177            "########",
178            "########",
179            " ###### ",
180            "  ####  ",
181        ],);
182    }
183
184    #[test]
185    fn circle_9() {
186        #[rustfmt::skip]
187        test_circle(9, &[
188            "  #####  ",
189            " ####### ",
190            "#########",
191            "#########",
192            "#########",
193            "#########",
194            "#########",
195            " ####### ",
196            "  #####  ",
197        ],);
198    }
199}