1use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind};
2
3fn draw_part_a<
4 B: DrawingBackend,
5 Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>,
6>(
7 height: f64,
8 radius: u32,
9 mut draw: Draw,
10) -> Result<(), DrawingErrorKind<B::ErrorType>> {
11 let half_width = (radius as f64 * radius as f64
12 - (radius as f64 - height) * (radius as f64 - height))
13 .sqrt();
14
15 let x0 = (-half_width).ceil() as i32;
16 let x1 = half_width.floor() as i32;
17
18 let y0 = (radius as f64 - height).ceil();
19
20 for x in x0..=x1 {
21 let y1 = (radius as f64 * radius as f64 - x as f64 * x as f64).sqrt();
22 check_result!(draw(x, (y0, y1)));
23 }
24
25 Ok(())
26}
27
28fn draw_part_b<
29 B: DrawingBackend,
30 Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>,
31>(
32 from: f64,
33 size: f64,
34 mut draw: Draw,
35) -> Result<(), DrawingErrorKind<B::ErrorType>> {
36 let from = from.floor();
37 for x in (from - size).floor() as i32..=from as i32 {
38 check_result!(draw(x, (-x as f64, x as f64)));
39 }
40 Ok(())
41}
42
43fn draw_part_c<
44 B: DrawingBackend,
45 Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>,
46>(
47 r: i32,
48 r_limit: i32,
49 mut draw: Draw,
50) -> Result<(), DrawingErrorKind<B::ErrorType>> {
51 let half_size = r as f64 / (2f64).sqrt();
52
53 let (x0, x1) = ((-half_size).ceil() as i32, half_size.floor() as i32);
54
55 for x in x0..x1 {
56 let outter_y0 = ((r_limit as f64) * (r_limit as f64) - x as f64 * x as f64).sqrt();
57 let inner_y0 = r as f64 - 1.0;
58 let mut y1 = outter_y0.min(inner_y0);
59 let y0 = ((r as f64) * (r as f64) - x as f64 * x as f64).sqrt();
60
61 if y0 > y1 {
62 y1 = y0.ceil();
63 if y1 >= r as f64 {
64 continue;
65 }
66 }
67
68 check_result!(draw(x, (y0, y1)));
69 }
70
71 for x in x1 + 1..r {
72 let outter_y0 = ((r_limit as f64) * (r_limit as f64) - x as f64 * x as f64).sqrt();
73 let inner_y0 = r as f64 - 1.0;
74 let y0 = outter_y0.min(inner_y0);
75 let y1 = x as f64;
76
77 if y1 < y0 {
78 check_result!(draw(x, (y0, y1 + 1.0)));
79 check_result!(draw(-x, (y0, y1 + 1.0)));
80 }
81 }
82
83 Ok(())
84}
85
86fn draw_sweep_line<B: DrawingBackend, S: BackendStyle>(
87 b: &mut B,
88 style: &S,
89 (x0, y0): BackendCoord,
90 (dx, dy): (i32, i32),
91 p0: i32,
92 (s, e): (f64, f64),
93) -> Result<(), DrawingErrorKind<B::ErrorType>> {
94 let mut s = if dx < 0 || dy < 0 { -s } else { s };
95 let mut e = if dx < 0 || dy < 0 { -e } else { e };
96 if s > e {
97 std::mem::swap(&mut s, &mut e);
98 }
99
100 let vs = s.ceil() - s;
101 let ve = e - e.floor();
102
103 if dx == 0 {
104 check_result!(b.draw_line(
105 (p0 + x0, s.ceil() as i32 + y0),
106 (p0 + x0, e.floor() as i32 + y0),
107 &style.color()
108 ));
109 check_result!(b.draw_pixel((p0 + x0, s.ceil() as i32 + y0 - 1), style.color().mix(vs)));
110 check_result!(b.draw_pixel((p0 + x0, e.floor() as i32 + y0 + 1), style.color().mix(ve)));
111 } else {
112 check_result!(b.draw_line(
113 (s.ceil() as i32 + x0, p0 + y0),
114 (e.floor() as i32 + x0, p0 + y0),
115 &style.color()
116 ));
117 check_result!(b.draw_pixel((s.ceil() as i32 + x0 - 1, p0 + y0), style.color().mix(vs)));
118 check_result!(b.draw_pixel((e.floor() as i32 + x0 + 1, p0 + y0), style.color().mix(ve)));
119 }
120
121 Ok(())
122}
123
124fn draw_annulus<B: DrawingBackend, S: BackendStyle>(
125 b: &mut B,
126 center: BackendCoord,
127 radius: (u32, u32),
128 style: &S,
129) -> Result<(), DrawingErrorKind<B::ErrorType>> {
130 let a0 = ((radius.0 - radius.1) as f64).min(radius.0 as f64 * (1.0 - 1.0 / (2f64).sqrt()));
131 let a1 = (radius.0 as f64 - a0 - radius.1 as f64).max(0.0);
132
133 check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line(
134 b,
135 style,
136 center,
137 (0, 1),
138 p,
139 r
140 )));
141 check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line(
142 b,
143 style,
144 center,
145 (0, -1),
146 p,
147 r
148 )));
149 check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line(
150 b,
151 style,
152 center,
153 (1, 0),
154 p,
155 r
156 )));
157 check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line(
158 b,
159 style,
160 center,
161 (-1, 0),
162 p,
163 r
164 )));
165
166 if a1 > 0.0 {
167 check_result!(draw_part_b::<B, _>(
168 radius.0 as f64 - a0,
169 a1.floor(),
170 |h, (f, t)| {
171 let h = h as i32;
172 let f = f as i32;
173 let t = t as i32;
174 check_result!(b.draw_line(
175 (center.0 + h, center.1 + f),
176 (center.0 + h, center.1 + t),
177 &style.color()
178 ));
179 check_result!(b.draw_line(
180 (center.0 - h, center.1 + f),
181 (center.0 - h, center.1 + t),
182 &style.color()
183 ));
184
185 check_result!(b.draw_line(
186 (center.0 + f + 1, center.1 + h),
187 (center.0 + t - 1, center.1 + h),
188 &style.color()
189 ));
190 check_result!(b.draw_line(
191 (center.0 + f + 1, center.1 - h),
192 (center.0 + t - 1, center.1 - h),
193 &style.color()
194 ));
195
196 Ok(())
197 }
198 ));
199 }
200
201 check_result!(draw_part_c::<B, _>(
202 radius.1 as i32,
203 radius.0 as i32,
204 |p, r| draw_sweep_line(b, style, center, (0, 1), p, r)
205 ));
206 check_result!(draw_part_c::<B, _>(
207 radius.1 as i32,
208 radius.0 as i32,
209 |p, r| draw_sweep_line(b, style, center, (0, -1), p, r)
210 ));
211 check_result!(draw_part_c::<B, _>(
212 radius.1 as i32,
213 radius.0 as i32,
214 |p, r| draw_sweep_line(b, style, center, (1, 0), p, r)
215 ));
216 check_result!(draw_part_c::<B, _>(
217 radius.1 as i32,
218 radius.0 as i32,
219 |p, r| draw_sweep_line(b, style, center, (-1, 0), p, r)
220 ));
221
222 let d_inner = ((radius.1 as f64) / (2f64).sqrt()) as i32;
223 let d_outter = (((radius.0 as f64) / (2f64).sqrt()) as i32).min(radius.1 as i32 - 1);
224 let d_outter_actually = (radius.1 as i32).min(
225 (radius.0 as f64 * radius.0 as f64 - radius.1 as f64 * radius.1 as f64 / 2.0)
226 .sqrt()
227 .ceil() as i32,
228 );
229
230 check_result!(b.draw_line(
231 (center.0 - d_inner, center.1 - d_inner),
232 (center.0 - d_outter, center.1 - d_outter),
233 &style.color()
234 ));
235 check_result!(b.draw_line(
236 (center.0 + d_inner, center.1 - d_inner),
237 (center.0 + d_outter, center.1 - d_outter),
238 &style.color()
239 ));
240 check_result!(b.draw_line(
241 (center.0 - d_inner, center.1 + d_inner),
242 (center.0 - d_outter, center.1 + d_outter),
243 &style.color()
244 ));
245 check_result!(b.draw_line(
246 (center.0 + d_inner, center.1 + d_inner),
247 (center.0 + d_outter, center.1 + d_outter),
248 &style.color()
249 ));
250
251 check_result!(b.draw_line(
252 (center.0 - d_inner, center.1 + d_inner),
253 (center.0 - d_outter_actually, center.1 + d_inner),
254 &style.color()
255 ));
256 check_result!(b.draw_line(
257 (center.0 + d_inner, center.1 - d_inner),
258 (center.0 + d_inner, center.1 - d_outter_actually),
259 &style.color()
260 ));
261 check_result!(b.draw_line(
262 (center.0 + d_inner, center.1 + d_inner),
263 (center.0 + d_inner, center.1 + d_outter_actually),
264 &style.color()
265 ));
266 check_result!(b.draw_line(
267 (center.0 + d_inner, center.1 + d_inner),
268 (center.0 + d_outter_actually, center.1 + d_inner),
269 &style.color()
270 ));
271
272 Ok(())
273}
274
275pub fn draw_circle<B: DrawingBackend, S: BackendStyle>(
276 b: &mut B,
277 center: BackendCoord,
278 mut radius: u32,
279 style: &S,
280 mut fill: bool,
281) -> Result<(), DrawingErrorKind<B::ErrorType>> {
282 if style.color().alpha == 0.0 {
283 return Ok(());
284 }
285
286 if !fill && style.stroke_width() != 1 {
287 let inner_radius = radius - (style.stroke_width() / 2).min(radius);
288 radius += style.stroke_width() / 2;
289 if inner_radius > 0 {
290 return draw_annulus(b, center, (radius, inner_radius), style);
291 } else {
292 fill = true;
293 }
294 }
295
296 let min = (f64::from(radius) * (1.0 - (2f64).sqrt() / 2.0)).ceil() as i32;
297 let max = (f64::from(radius) * (1.0 + (2f64).sqrt() / 2.0)).floor() as i32;
298
299 let range = min..=max;
300
301 let (up, down) = (
302 range.start() + center.1 - radius as i32,
303 range.end() + center.1 - radius as i32,
304 );
305
306 for dy in range {
307 let dy = dy - radius as i32;
308 let y = center.1 + dy;
309
310 let lx = (f64::from(radius) * f64::from(radius)
311 - (f64::from(dy) * f64::from(dy)).max(1e-5))
312 .sqrt();
313
314 let left = center.0 - lx.floor() as i32;
315 let right = center.0 + lx.floor() as i32;
316
317 let v = lx - lx.floor();
318
319 let x = center.0 + dy;
320 let top = center.1 - lx.floor() as i32;
321 let bottom = center.1 + lx.floor() as i32;
322
323 if fill {
324 check_result!(b.draw_line((left, y), (right, y), &style.color()));
325 check_result!(b.draw_line((x, top), (x, up - 1), &style.color()));
326 check_result!(b.draw_line((x, down + 1), (x, bottom), &style.color()));
327 } else {
328 check_result!(b.draw_pixel((left, y), style.color().mix(1.0 - v)));
329 check_result!(b.draw_pixel((right, y), style.color().mix(1.0 - v)));
330
331 check_result!(b.draw_pixel((x, top), style.color().mix(1.0 - v)));
332 check_result!(b.draw_pixel((x, bottom), style.color().mix(1.0 - v)));
333 }
334
335 check_result!(b.draw_pixel((left - 1, y), style.color().mix(v)));
336 check_result!(b.draw_pixel((right + 1, y), style.color().mix(v)));
337 check_result!(b.draw_pixel((x, top - 1), style.color().mix(v)));
338 check_result!(b.draw_pixel((x, bottom + 1), style.color().mix(v)));
339 }
340
341 Ok(())
342}
343