use super::super::drawable::*;
use super::super::transform::*;
use coord::{Coord, ToUnsigned};
#[derive(Debug, Copy, Clone)]
pub struct Circle {
pub center: Coord,
pub radius: u32,
pub color: Color,
}
impl Circle {
pub fn new(center: Coord, radius: u32, color: u8) -> Self {
Circle {
center,
radius,
color,
}
}
}
impl<'a> IntoIterator for &'a Circle {
type Item = Pixel;
type IntoIter = CircleIterator;
fn into_iter(self) -> Self::IntoIter {
CircleIterator {
center: self.center,
radius: self.radius,
color: self.color,
octant: 0,
idx: 0,
x: 0,
y: self.radius,
d: 1 - self.radius as i32,
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct CircleIterator {
center: Coord,
radius: u32,
color: Color,
octant: u32,
idx: u32,
x: u32,
y: u32,
d: i32,
}
impl Iterator for CircleIterator {
type Item = Pixel;
fn next(&mut self) -> Option<Self::Item> {
let item = loop {
if self.x > self.y {
break None;
}
let mx = self.center[0];
let my = self.center[1];
if self.octant > 7 {
self.octant = 0;
self.x += 1;
if self.d < 0 {
self.d += 2 * self.x as i32 + 3;
} else {
self.d += 2 * (self.x as i32 - self.y as i32) + 5;
self.y -= 1;
}
}
let item = match self.octant {
0 => Some((mx + self.x as i32, my + self.y as i32)),
1 => Some((mx + self.x as i32, my - self.y as i32)),
2 => Some((mx - self.x as i32, my + self.y as i32)),
3 => Some((mx - self.x as i32, my - self.y as i32)),
4 => Some((mx + self.y as i32, my + self.x as i32)),
5 => Some((mx + self.y as i32, my - self.x as i32)),
6 => Some((mx - self.y as i32, my + self.x as i32)),
7 => Some((mx - self.y as i32, my - self.x as i32)),
_ => None,
};
self.octant += 1;
if let Some(i) = item {
if i.0 > 0 && i.1 > 0 {
break item;
}
}
};
item.map(|(x, y)| (Coord::new(x, y).to_unsigned(), self.color))
}
}
impl Drawable for Circle {}
impl Transform for Circle {
fn translate(&self, by: Coord) -> Self {
Self {
center: self.center + by,
..*self
}
}
fn translate_mut(&mut self, by: Coord) -> &mut Self {
self.center += by;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_handles_offscreen_coords() {
let mut circ = Circle::new(Coord::new(-10, -10), 5, 1).into_iter();
assert_eq!(circ.next(), None);
}
#[test]
fn it_handles_partially_on_screen_coords() {
let mut circ = Circle::new(Coord::new(-5, -5), 30, 1).into_iter();
assert!(circ.next().is_some());
}
}