1 use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind};
2 
draw_part_a< B: DrawingBackend, Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>, >( height: f64, radius: u32, mut draw: Draw, ) -> Result<(), DrawingErrorKind<B::ErrorType>>3 fn 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 
draw_part_b< B: DrawingBackend, Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>, >( from: f64, size: f64, mut draw: Draw, ) -> Result<(), DrawingErrorKind<B::ErrorType>>28 fn 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 
draw_part_c< B: DrawingBackend, Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>, >( r: i32, r_limit: i32, mut draw: Draw, ) -> Result<(), DrawingErrorKind<B::ErrorType>>43 fn 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 
draw_sweep_line<B: DrawingBackend, S: BackendStyle>( b: &mut B, style: &S, (x0, y0): BackendCoord, (dx, dy): (i32, i32), p0: i32, (s, e): (f64, f64), ) -> Result<(), DrawingErrorKind<B::ErrorType>>86 fn 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 
draw_annulus<B: DrawingBackend, S: BackendStyle>( b: &mut B, center: BackendCoord, radius: (u32, u32), style: &S, ) -> Result<(), DrawingErrorKind<B::ErrorType>>124 fn 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 
draw_circle<B: DrawingBackend, S: BackendStyle>( b: &mut B, center: BackendCoord, mut radius: u32, style: &S, mut fill: bool, ) -> Result<(), DrawingErrorKind<B::ErrorType>>275 pub 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