graphics_shapes/
circle.rs1use crate::prelude::*;
2use crate::shape_box::ShapeBox;
3use crate::{coord, new_hash_set};
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8#[derive(Debug, Clone, Eq, PartialEq, Hash)]
9pub struct Circle {
10 center: Coord,
11 radius: usize,
12}
13
14impl IntersectsContains for Circle {}
15
16impl Circle {
17 #[must_use]
18 pub fn new<P: Into<Coord>>(center: P, radius: usize) -> Self {
19 Self {
20 center: center.into(),
21 radius,
22 }
23 }
24}
25
26impl Circle {
27 #[inline]
31 #[must_use]
32 pub fn radius(&self) -> usize {
33 self.radius
34 }
35}
36
37impl Shape for Circle {
38 fn from_points(points: &[Coord]) -> Self
40 where
41 Self: Sized,
42 {
43 debug_assert!(points.len() >= 2);
44 let radius = points[0].distance(points[1]);
45 Circle::new(points[0], radius)
46 }
47
48 fn rebuild(&self, points: &[Coord]) -> Self
50 where
51 Self: Sized,
52 {
53 Circle::from_points(points)
54 }
55
56 fn translate_by(&self, delta: Coord) -> Self {
57 Circle::new(self.center + delta, self.radius)
58 }
59
60 fn move_to(&self, point: Coord) -> Self {
61 Circle::new(point, self.radius)
62 }
63
64 fn move_center_to(&self, point: Coord) -> Self
65 where
66 Self: Sized,
67 {
68 Circle::new(point, self.radius)
69 }
70
71 fn contains(&self, point: Coord) -> bool {
72 let dist = self.center.distance(point);
73 dist <= self.radius
74 }
75
76 fn points(&self) -> Vec<Coord> {
78 vec![self.center, Coord::from_angle(self.center, self.radius, 0)]
79 }
80
81 #[inline]
82 fn center(&self) -> Coord {
83 self.center
84 }
85
86 #[inline]
87 fn left(&self) -> isize {
88 self.center.x - (self.radius as isize)
89 }
90
91 #[inline]
92 fn right(&self) -> isize {
93 self.center.x + (self.radius as isize)
94 }
95
96 #[inline]
97 fn top(&self) -> isize {
98 self.center.y - (self.radius as isize)
99 }
100
101 #[inline]
102 fn bottom(&self) -> isize {
103 self.center.y + (self.radius as isize)
104 }
105
106 fn outline_pixels(&self) -> Vec<Coord> {
107 let cx = self.center.x;
108 let cy = self.center.y;
109 let mut d = (5_isize - (self.radius as isize) * 4) / 4;
110 let mut x = 0;
111 let mut y = self.radius as isize;
112 let mut output = new_hash_set();
113
114 while x <= y {
115 output.insert(coord!(cx + x, cy + y));
116 output.insert(coord!(cx + x, cy - y));
117 output.insert(coord!(cx - x, cy + y));
118 output.insert(coord!(cx - x, cy - y));
119 output.insert(coord!(cx + y, cy + x));
120 output.insert(coord!(cx + y, cy - x));
121 output.insert(coord!(cx - y, cy + x));
122 output.insert(coord!(cx - y, cy - x));
123 if d < 0 {
124 d += 2 * x + 1
125 } else {
126 d += 2 * (x - y) + 1;
127 y -= 1;
128 }
129 x += 1;
130 }
131
132 output.into_iter().collect()
133 }
134
135 fn filled_pixels(&self) -> Vec<Coord> {
136 let mut output = new_hash_set();
137 let cx = self.center.x;
138 let cy = self.center.y;
139 let squared_radius = (self.radius * self.radius) as isize;
140 for y in 0..(self.radius as isize) {
141 let up = cy - y;
142 let down = cy + y;
143 let half_width = (((squared_radius - y * y) as f64).sqrt().round() as isize).max(0);
144 for x in 0..=half_width {
145 let left = cx - x;
146 let right = cx + x;
147 output.insert(coord!(left, up));
148 output.insert(coord!(right, up));
149 output.insert(coord!(left, down));
150 output.insert(coord!(right, down));
151 }
152 }
153 output.into_iter().collect()
154 }
155
156 fn to_shape_box(&self) -> ShapeBox {
157 ShapeBox::Circle(self.clone())
158 }
159}
160
161impl Circle {
162 #[must_use]
163 #[deprecated(since = "0.2.0", note = "use as_outer_rect instead")]
164 pub fn as_rect(&self) -> Rect {
165 Rect::new((self.left(), self.top()), (self.right(), self.bottom()))
166 }
167
168 #[must_use]
170 pub fn as_outer_rect(&self) -> Rect {
171 Rect::new((self.left(), self.top()), (self.right(), self.bottom()))
172 }
173
174 #[must_use]
176 pub fn as_inner_rect(&self) -> Rect {
177 let top_left = Coord::from_angle(self.center, self.radius, 315);
178 let bottom_right = Coord::from_angle(self.center, self.radius, 135);
179 Rect::new(top_left, bottom_right)
180 }
181
182 #[must_use]
184 pub fn as_radius_line(&self) -> Line {
185 Line::new((self.center.x, self.center.y), (self.center.x, self.top()))
186 }
187
188 #[must_use]
190 pub fn as_horizontal_line(&self) -> Line {
191 Line::new((self.left(), self.center.y), (self.right(), self.center.y))
192 }
193
194 #[must_use]
196 pub fn as_vertical_line(&self) -> Line {
197 Line::new((self.center.x, self.top()), (self.center.x, self.bottom()))
198 }
199
200 #[must_use]
201 pub fn as_ellipse(&self) -> Ellipse {
202 Ellipse::new(self.center, self.radius * 2, self.radius * 2)
203 }
204}
205
206#[cfg(test)]
207mod test {
208 use crate::coord;
209 use crate::prelude::*;
210
211 #[test]
212 fn move_center() {
213 let circle = Circle::new((100, 100), 20);
214 let moved = circle.move_center_to(coord!(50, 50));
215
216 assert_eq!(moved.center, coord!(50, 50));
217 }
218}