1use std::collections::HashSet;
2
3use crate::math::Point;
4use super::Color;
5use super::Plot;
6
7pub struct Circle {
16 pub color: Color,
18
19 pub radius: i64, r2_check: i64,
25}
26
27pub struct Orientation {
33 pub color: Color,
35
36 pub radius: i64,
39}
40
41impl Circle {
42
43 pub fn new(color: Color, radius: i64) -> Circle {
45 Circle {
46 color,
47 radius,
48 r2_check: ((radius as f32 - 0.5) * (radius as f32 - 0.5)) as i64
49 }
50 }
51
52 pub fn draw(&self, plot: &mut Plot, pt: Point) {
54 let c = plot.frame.pt_to_px(pt);
55 for dx in 0..self.radius {
56 for dy in 0..self.radius {
57 if (dx * dx + dy * dy) <= self.r2_check {
58 plot.put_pixel_safe(c.0 - dx, c.1 - dy, self.color);
59 plot.put_pixel_safe(c.0 - dx, c.1 + dy, self.color);
60 plot.put_pixel_safe(c.0 + dx, c.1 - dy, self.color);
61 plot.put_pixel_safe(c.0 + dx, c.1 + dy, self.color);
62 }
63 }
64 }
65 }
66
67 #[allow(dead_code)]
68 fn draw_test_impl(&self, center_pixel: Point) -> HashSet<(i64, i64)> {
69 let mut pixels = HashSet::new();
70 let c = (center_pixel.x.round() as i64, center_pixel.y.round() as i64);
71 for dx in 0..self.radius {
72 for dy in 0..self.radius {
73 if (dx * dx + dy * dy) <= self.r2_check {
74 pixels.insert((c.0 + dx, c.1 + dy));
75 pixels.insert((c.0 - dx, c.1 + dy));
76 pixels.insert((c.0 + dx, c.1 - dy));
77 pixels.insert((c.0 - dx, c.1 - dy));
78 }
79 }
80 }
81 pixels
82 }
83}
84
85impl Orientation {
86
87 pub fn new(color: Color, radius: i64) -> Orientation {
89 Orientation {
90 color,
91 radius,
92 }
93 }
94
95 pub fn draw(&self, plot: &mut Plot, pt: Point, angle: f64) {
97 let c = plot.frame.pt_to_px(pt);
98 let r = self.radius as f64;
99 let a = angle;
100 let x1 = (c.0 as f64) - (r + 0.5) * a.cos();
101 let x2 = (c.0 as f64) + (r + 0.5) * a.cos();
102 let y1 = (c.1 as f64) - (r + 0.5) * a.sin();
103 let y2 = (c.1 as f64) + (r + 0.5) * a.sin();
104 plot.draw_pixel_line((x1, y1), (x2, y2), self.color);
105 }
106
107}
108
109#[test]
110fn it_draws_a_point() {
111 use std::collections::HashSet;
112
113 let tests = vec![
114 (Point::new(0.0, 0.0), 1, vec![(0, 0)]),
115 (Point::new(0.4, 0.4), 1, vec![(0, 0)]),
116 (Point::new(0.5, 0.1), 1, vec![(1, 0)]),
117 (Point::new(-0.51, -0.4), 1, vec![(-1, 0)]),
118 ];
119
120 for (center, radius, expected) in tests.into_iter() {
121 let c = Circle::new(super::RED, radius);
122 let expected_set: HashSet<(i64, i64)> = expected.into_iter().collect();
123 let actual_set: HashSet<(i64, i64)> = c.draw_test_impl(center).into_iter().collect();
124 assert_eq!(actual_set, expected_set);
125 }
126}
127
128#[test]
129fn it_draws_a_3_circle_at_origin() {
130 use std::collections::HashSet;
131
132 let center = Point::new(0.0, 0.0);
133 let radius = 2;
134 let expected = vec![
135 (-1, 1), (0, 1), (1, 1),
136 (-1, 0), (0, 0), (1, 0),
137 (-1, -1), (0, -1), (1, -1),
138 ].into_iter().collect();
139
140 let actual: HashSet<(i64, i64)> = Circle::new(super::RED, radius)
141 .draw_test_impl(center)
142 .into_iter().collect();
143 assert_eq!(actual, expected);
144}
145
146#[test]
147fn it_draws_a_3_circle_offset_from_origin() {
148 use std::collections::HashSet;
149
150 let center = Point::new(0.49, 0.4);
151 let radius = 2;
152 let expected = vec![
153 (-1, 1), (0, 1), (1, 1),
154 (-1, 0), (0, 0), (1, 0),
155 (-1, -1), (0, -1), (1, -1),
156 ].into_iter().collect();
157
158 let actual: HashSet<(i64, i64)> = Circle::new(super::RED, radius)
159 .draw_test_impl(center)
160 .into_iter().collect();
161 assert_eq!(actual, expected);
162
163 let center = Point::new(0.5, 0.4);
164 let radius = 2;
165 let expected = vec![
166 (0, 1), (1, 1), (2, 1),
167 (0, 0), (1, 0), (2, 0),
168 (0, -1), (1, -1), (2, -1),
169 ].into_iter().collect();
170
171 let actual: HashSet<(i64, i64)> = Circle::new(super::RED, radius)
172 .draw_test_impl(center)
173 .into_iter().collect();
174 assert_eq!(actual, expected);
175}
176
177#[test]
178fn it_draws_a_5_circle_offset_at_origin() {
179 use std::collections::HashSet;
180
181 let center = Point::new(0.0, 0.0);
182 let radius = 3;
183 let expected = vec![
184 (-1, 2), (0, 2), (1, 2),
185 (-2, 1), (-1, 1), (0, 1), (1, 1), (2, 1),
186 (-2, 0), (-1, 0), (0, 0), (1, 0), (2, 0),
187 (-2, -1), (-1, -1), (0, -1), (1, -1), (2,-1),
188 (-1, -2), (0, -2), (1, -2)
189 ].into_iter().collect();
190
191 let actual: HashSet<(i64, i64)> = Circle::new(super::RED, radius)
192 .draw_test_impl(center)
193 .into_iter().collect();
194 assert_eq!(actual, expected);
195}