use std::collections::HashSet;
use crate::math::Point;
use super::Color;
use super::Plot;
pub struct Circle {
pub color: Color,
pub radius: i64, r2_check: i64,
}
pub struct Orientation {
pub color: Color,
pub radius: i64,
}
impl Circle {
pub fn new(color: Color, radius: i64) -> Circle {
Circle {
color,
radius,
r2_check: ((radius as f32 - 0.5) * (radius as f32 - 0.5)) as i64
}
}
pub fn draw(&self, plot: &mut Plot, pt: Point) {
let c = plot.frame.pt_to_px(pt);
for dx in 0..self.radius {
for dy in 0..self.radius {
if (dx * dx + dy * dy) <= self.r2_check {
plot.put_pixel_safe(c.0 - dx, c.1 - dy, self.color);
plot.put_pixel_safe(c.0 - dx, c.1 + dy, self.color);
plot.put_pixel_safe(c.0 + dx, c.1 - dy, self.color);
plot.put_pixel_safe(c.0 + dx, c.1 + dy, self.color);
}
}
}
}
#[allow(dead_code)]
fn draw_test_impl(&self, center_pixel: Point) -> HashSet<(i64, i64)> {
let mut pixels = HashSet::new();
let c = (center_pixel.x.round() as i64, center_pixel.y.round() as i64);
for dx in 0..self.radius {
for dy in 0..self.radius {
if (dx * dx + dy * dy) <= self.r2_check {
pixels.insert((c.0 + dx, c.1 + dy));
pixels.insert((c.0 - dx, c.1 + dy));
pixels.insert((c.0 + dx, c.1 - dy));
pixels.insert((c.0 - dx, c.1 - dy));
}
}
}
pixels
}
}
impl Orientation {
pub fn new(color: Color, radius: i64) -> Orientation {
Orientation {
color,
radius,
}
}
pub fn draw(&self, plot: &mut Plot, pt: Point, angle: f64) {
let c = plot.frame.pt_to_px(pt);
let r = self.radius as f64;
let a = angle;
let x1 = (c.0 as f64) - (r + 0.5) * a.cos();
let x2 = (c.0 as f64) + (r + 0.5) * a.cos();
let y1 = (c.1 as f64) - (r + 0.5) * a.sin();
let y2 = (c.1 as f64) + (r + 0.5) * a.sin();
plot.draw_pixel_line((x1, y1), (x2, y2), self.color);
}
}
#[test]
fn it_draws_a_point() {
use std::collections::HashSet;
let tests = vec![
(Point::new(0.0, 0.0), 1, vec![(0, 0)]),
(Point::new(0.4, 0.4), 1, vec![(0, 0)]),
(Point::new(0.5, 0.1), 1, vec![(1, 0)]),
(Point::new(-0.51, -0.4), 1, vec![(-1, 0)]),
];
for (center, radius, expected) in tests.into_iter() {
let c = Circle::new(super::RED, radius);
let expected_set: HashSet<(i64, i64)> = expected.into_iter().collect();
let actual_set: HashSet<(i64, i64)> = c.draw_test_impl(center).into_iter().collect();
assert_eq!(actual_set, expected_set);
}
}
#[test]
fn it_draws_a_3_circle_at_origin() {
use std::collections::HashSet;
let center = Point::new(0.0, 0.0);
let radius = 2;
let expected = vec![
(-1, 1), (0, 1), (1, 1),
(-1, 0), (0, 0), (1, 0),
(-1, -1), (0, -1), (1, -1),
].into_iter().collect();
let actual: HashSet<(i64, i64)> = Circle::new(super::RED, radius)
.draw_test_impl(center)
.into_iter().collect();
assert_eq!(actual, expected);
}
#[test]
fn it_draws_a_3_circle_offset_from_origin() {
use std::collections::HashSet;
let center = Point::new(0.49, 0.4);
let radius = 2;
let expected = vec![
(-1, 1), (0, 1), (1, 1),
(-1, 0), (0, 0), (1, 0),
(-1, -1), (0, -1), (1, -1),
].into_iter().collect();
let actual: HashSet<(i64, i64)> = Circle::new(super::RED, radius)
.draw_test_impl(center)
.into_iter().collect();
assert_eq!(actual, expected);
let center = Point::new(0.5, 0.4);
let radius = 2;
let expected = vec![
(0, 1), (1, 1), (2, 1),
(0, 0), (1, 0), (2, 0),
(0, -1), (1, -1), (2, -1),
].into_iter().collect();
let actual: HashSet<(i64, i64)> = Circle::new(super::RED, radius)
.draw_test_impl(center)
.into_iter().collect();
assert_eq!(actual, expected);
}
#[test]
fn it_draws_a_5_circle_offset_at_origin() {
use std::collections::HashSet;
let center = Point::new(0.0, 0.0);
let radius = 3;
let expected = vec![
(-1, 2), (0, 2), (1, 2),
(-2, 1), (-1, 1), (0, 1), (1, 1), (2, 1),
(-2, 0), (-1, 0), (0, 0), (1, 0), (2, 0),
(-2, -1), (-1, -1), (0, -1), (1, -1), (2,-1),
(-1, -2), (0, -2), (1, -2)
].into_iter().collect();
let actual: HashSet<(i64, i64)> = Circle::new(super::RED, radius)
.draw_test_impl(center)
.into_iter().collect();
assert_eq!(actual, expected);
}