//! draw 2d circles
use crate::Image;
use std::ops::DerefMut;
impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
/// Draws a circle, using the [Bresenham's circle](https://en.wikipedia.org/wiki/Midpoint_circle_algorithm) algorithm.
/// ```
/// # use fimg::Image;
/// let mut i = Image::alloc(50, 50);
/// i.border_circle((25, 25), 20, [255]);
/// # assert_eq!(i.buffer(), include_bytes!("../../tdata/circle.imgbuf"));
/// ```
pub fn border_circle(&mut self, (xc, yc): (i32, i32), radius: i32, c: [u8; CHANNELS]) {
let mut x = 0;
let mut y = radius;
let mut p = 1 - radius;
/// bounds the pixels
macro_rules! bound {
($($x:expr,$y:expr);+;) => {
$(if $x >= 0 && $x < self.width() as i32 && $y >= 0 && $y < self.height() as i32 {
// SAFETY: ^
unsafe { self.set_pixel($x as u32, $y as u32, c) };
})+
};
}
while x <= y {
bound! {
xc + x, yc + y;
xc + y, yc + x;
xc - y, yc + x;
xc - x, yc + y;
xc - x, yc - y;
xc - y, yc - x;
xc + y, yc - x;
xc + x, yc - y;
};
x += 1;
if p < 0 {
p += 2 * x + 1;
} else {
y -= 1;
p += 2 * (x - y) + 1;
}
}
}
/// Draw a filled circle.
/// ```
/// # use fimg::Image;
/// let mut i = Image::alloc(50, 50);
/// i.circle((25, 25), 20, [255]);
/// # assert_eq!(i.buffer(), include_bytes!("../../tdata/circle2.imgbuf"));
/// ```
pub fn circle(&mut self, (xc, yc): (i32, i32), radius: i32, c: [u8; CHANNELS]) {
for x in -radius..radius {
let h = ((radius * radius - x * x) as f32).sqrt().round() as i32;
for y in -h..h {
let x = x + xc;
let y = y + yc;
if x >= 0 && x < self.width() as i32 && y >= 0 && y < self.height() as i32 {
// SAFETY: ^
unsafe { self.set_pixel(x as u32, y as u32, c) };
}
}
}
}
}