1 | use core::ops::Range; |
2 | |
3 | use 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))] |
11 | pub struct Points { |
12 | scanlines: Scanlines, |
13 | current_scanline: Scanline, |
14 | } |
15 | |
16 | impl 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 | |
25 | impl 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))] |
38 | pub struct Scanlines { |
39 | rows: Range<i32>, |
40 | columns: Range<i32>, |
41 | pub(super) center_2x: Point, |
42 | threshold: u32, |
43 | } |
44 | |
45 | impl Scanlines { |
46 | pub fn new(circle: &Circle) -> Self { |
47 | let bounding_box: Rectangle = 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 | |
58 | impl Iterator for Scanlines { |
59 | type Item = Scanline; |
60 | |
61 | fn next(&mut self) -> Option<Self::Item> { |
62 | let y: i32 = self.rows.next()?; |
63 | |
64 | self.columns |
65 | .clone() |
66 | // find first pixel that is inside the threshold |
67 | .find(|x: &i32| { |
68 | let delta: Point = 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: i32| Scanline::new(y, x:x..self.columns.end - (x - self.columns.start))) |
73 | } |
74 | } |
75 | |
76 | #[cfg (test)] |
77 | mod 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 | } |
200 | |