graphics_rs/shapes/
circle.rs

1use crate::traits::{canvas::Canvas, is_color::IsColor, shape::Shape};
2
3pub struct Circle {
4    cx: i64,
5    cy: i64,
6    radius: usize,
7}
8
9impl Circle {
10    pub fn new(cx: i64, cy: i64, radius: usize) -> Self {
11        Self { cx, cy, radius }
12    }
13
14    fn get_circle_rect_area(&self, canvas: &mut impl Canvas) -> (i64, i64, i64, i64) {
15        let x1 = canvas.clamp_col(self.cx - self.radius as i64);
16        let x2 = canvas.clamp_col(self.cx + self.radius as i64);
17        let y1 = canvas.clamp_row(self.cy - self.radius as i64);
18        let y2 = canvas.clamp_row(self.cy + self.radius as i64);
19
20        (x1, x2, y1, y2)
21    }
22
23    fn draw_antialiased(&mut self, canvas: &mut impl Canvas, x1: i64, x2: i64, y1: i64, y2: i64) {
24        for row in y1..=y2 {
25            for col in x1..=x2 {
26                let resolution = canvas.resolution();
27
28                let mut count: usize = 0;
29                for i in 0..resolution as i64 {
30                    for j in 0..resolution as i64 {
31                        let res1 = (resolution + 1) as i64;
32                        let dx = col * res1 * 2 + 2 + i * 2 - res1 * self.cx as i64 * 2 - res1;
33                        let dy = row * res1 * 2 + 2 + j * 2 - res1 * self.cy as i64 * 2 - res1;
34
35                        if dx.pow(2) + dy.pow(2) <= res1.pow(2) * (self.radius as i64).pow(2) * 4 {
36                            count += 1;
37                        }
38                    }
39                }
40
41                let alpha = canvas.color().alpha() as f64 * count as f64 / resolution.pow(2) as f64;
42                let color = canvas
43                    .color()
44                    .with_alpha(alpha.clamp(0f64, u8::MAX as f64) as u8);
45                canvas.set_pixel_color(row as usize, col as usize, color);
46            }
47        }
48    }
49
50    fn draw_simple(&mut self, canvas: &mut impl Canvas, x1: i64, x2: i64, y1: i64, y2: i64) {
51        for row in y1..=y2 {
52            for col in x1..=x2 {
53                let valid_pixel = row.abs_diff(self.cy).pow(2) + col.abs_diff(self.cx).pow(2)
54                    <= self.radius.pow(2) as u64;
55
56                if valid_pixel {
57                    if canvas.fits_inside(row, col) {
58                        canvas.set_pixel(row as usize, col as usize);
59                    }
60                }
61            }
62        }
63    }
64}
65
66impl Shape for Circle {
67    fn draw_to(&mut self, canvas: &mut impl Canvas) {
68        let (x1, x2, y1, y2) = self.get_circle_rect_area(canvas);
69
70        if canvas.antialiasing() {
71            self.draw_antialiased(canvas, x1, x2, y1, y2);
72        } else {
73            self.draw_simple(canvas, x1, x2, y1, y2);
74        }
75    }
76}