plotters_backend/rasterizer/
circle.rs

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 outer_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 = outer_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 outer_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 = outer_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 f = f as i32;
172                let t = t as i32;
173                check_result!(b.draw_line(
174                    (center.0 + h, center.1 + f),
175                    (center.0 + h, center.1 + t),
176                    &style.color()
177                ));
178                check_result!(b.draw_line(
179                    (center.0 - h, center.1 + f),
180                    (center.0 - h, center.1 + t),
181                    &style.color()
182                ));
183
184                check_result!(b.draw_line(
185                    (center.0 + f + 1, center.1 + h),
186                    (center.0 + t - 1, center.1 + h),
187                    &style.color()
188                ));
189                check_result!(b.draw_line(
190                    (center.0 + f + 1, center.1 - h),
191                    (center.0 + t - 1, center.1 - h),
192                    &style.color()
193                ));
194
195                Ok(())
196            }
197        ));
198    }
199
200    check_result!(draw_part_c::<B, _>(
201        radius.1 as i32,
202        radius.0 as i32,
203        |p, r| draw_sweep_line(b, style, center, (0, 1), p, r)
204    ));
205    check_result!(draw_part_c::<B, _>(
206        radius.1 as i32,
207        radius.0 as i32,
208        |p, r| draw_sweep_line(b, style, center, (0, -1), p, r)
209    ));
210    check_result!(draw_part_c::<B, _>(
211        radius.1 as i32,
212        radius.0 as i32,
213        |p, r| draw_sweep_line(b, style, center, (1, 0), p, r)
214    ));
215    check_result!(draw_part_c::<B, _>(
216        radius.1 as i32,
217        radius.0 as i32,
218        |p, r| draw_sweep_line(b, style, center, (-1, 0), p, r)
219    ));
220
221    let d_inner = ((radius.1 as f64) / (2f64).sqrt()) as i32;
222    let d_outer = (((radius.0 as f64) / (2f64).sqrt()) as i32).min(radius.1 as i32 - 1);
223    let d_outer_actually = (radius.1 as i32).min(
224        (radius.0 as f64 * radius.0 as f64 - radius.1 as f64 * radius.1 as f64 / 2.0)
225            .sqrt()
226            .ceil() as i32,
227    );
228
229    check_result!(b.draw_line(
230        (center.0 - d_inner, center.1 - d_inner),
231        (center.0 - d_outer, center.1 - d_outer),
232        &style.color()
233    ));
234    check_result!(b.draw_line(
235        (center.0 + d_inner, center.1 - d_inner),
236        (center.0 + d_outer, center.1 - d_outer),
237        &style.color()
238    ));
239    check_result!(b.draw_line(
240        (center.0 - d_inner, center.1 + d_inner),
241        (center.0 - d_outer, center.1 + d_outer),
242        &style.color()
243    ));
244    check_result!(b.draw_line(
245        (center.0 + d_inner, center.1 + d_inner),
246        (center.0 + d_outer, center.1 + d_outer),
247        &style.color()
248    ));
249
250    check_result!(b.draw_line(
251        (center.0 - d_inner, center.1 + d_inner),
252        (center.0 - d_outer_actually, center.1 + d_inner),
253        &style.color()
254    ));
255    check_result!(b.draw_line(
256        (center.0 + d_inner, center.1 - d_inner),
257        (center.0 + d_inner, center.1 - d_outer_actually),
258        &style.color()
259    ));
260    check_result!(b.draw_line(
261        (center.0 + d_inner, center.1 + d_inner),
262        (center.0 + d_inner, center.1 + d_outer_actually),
263        &style.color()
264    ));
265    check_result!(b.draw_line(
266        (center.0 + d_inner, center.1 + d_inner),
267        (center.0 + d_outer_actually, center.1 + d_inner),
268        &style.color()
269    ));
270
271    Ok(())
272}
273
274pub fn draw_circle<B: DrawingBackend, S: BackendStyle>(
275    b: &mut B,
276    center: BackendCoord,
277    mut radius: u32,
278    style: &S,
279    mut fill: bool,
280) -> Result<(), DrawingErrorKind<B::ErrorType>> {
281    if style.color().alpha == 0.0 {
282        return Ok(());
283    }
284
285    if !fill && style.stroke_width() != 1 {
286        let inner_radius = radius - (style.stroke_width() / 2).min(radius);
287        radius += style.stroke_width() / 2;
288        if inner_radius > 0 {
289            return draw_annulus(b, center, (radius, inner_radius), style);
290        } else {
291            fill = true;
292        }
293    }
294
295    let min = (f64::from(radius) * (1.0 - (2f64).sqrt() / 2.0)).ceil() as i32;
296    let max = (f64::from(radius) * (1.0 + (2f64).sqrt() / 2.0)).floor() as i32;
297
298    let range = min..=max;
299
300    let (up, down) = (
301        range.start() + center.1 - radius as i32,
302        range.end() + center.1 - radius as i32,
303    );
304
305    for dy in range {
306        let dy = dy - radius as i32;
307        let y = center.1 + dy;
308
309        let lx = (f64::from(radius) * f64::from(radius)
310            - (f64::from(dy) * f64::from(dy)).max(1e-5))
311        .sqrt();
312
313        let left = center.0 - lx.floor() as i32;
314        let right = center.0 + lx.floor() as i32;
315
316        let v = lx - lx.floor();
317
318        let x = center.0 + dy;
319        let top = center.1 - lx.floor() as i32;
320        let bottom = center.1 + lx.floor() as i32;
321
322        if fill {
323            check_result!(b.draw_line((left, y), (right, y), &style.color()));
324            check_result!(b.draw_line((x, top), (x, up - 1), &style.color()));
325            check_result!(b.draw_line((x, down + 1), (x, bottom), &style.color()));
326        } else {
327            check_result!(b.draw_pixel((left, y), style.color().mix(1.0 - v)));
328            check_result!(b.draw_pixel((right, y), style.color().mix(1.0 - v)));
329
330            check_result!(b.draw_pixel((x, top), style.color().mix(1.0 - v)));
331            check_result!(b.draw_pixel((x, bottom), style.color().mix(1.0 - v)));
332        }
333
334        check_result!(b.draw_pixel((left - 1, y), style.color().mix(v)));
335        check_result!(b.draw_pixel((right + 1, y), style.color().mix(v)));
336        check_result!(b.draw_pixel((x, top - 1), style.color().mix(v)));
337        check_result!(b.draw_pixel((x, bottom + 1), style.color().mix(v)));
338    }
339
340    Ok(())
341}