1use std::ops::Add;
2use std::ops::AddAssign;
3use std::ops::Sub;
4use std::ops::SubAssign;
5
6use crate::num::FromRounded;
7use crate::num::Num;
8use crate::num::ToRounded;
9
10use crate::geom::Line;
11use crate::geom::Point;
12use crate::geom::Size;
13
14mod circle_circumference_points_iterator;
15pub use self::circle_circumference_points_iterator::*;
16
17mod circle_circumference_lines_iterator;
18pub use self::circle_circumference_lines_iterator::*;
19
20#[derive(Copy, Clone, Debug)]
21pub struct Circle<N: Num = f32>(pub Point<N>, pub N);
22
23impl<N: Num> Circle<N> {
24 pub fn new_zero_value() -> Self {
25 Circle(Point::new_zero_value(), N::zero())
26 }
27
28 pub fn with_radius(centre_point: Point<N>, radius: N) -> Self {
29 Self(centre_point, radius)
30 }
31
32 pub fn with_diameter(centre_point: Point<N>, diameter: N) -> Self {
33 Self::with_radius(centre_point, diameter / (N::one() + N::one()))
34 }
35
36 pub fn move_xy(&mut self, xy: Point<N>) {
37 self.0 += xy;
38 }
39
40 pub fn move_x(&mut self, x: N) {
41 self.0.move_x(x);
42 }
43
44 pub fn move_y(&mut self, y: N) {
45 self.0.move_y(y);
46 }
47
48 pub fn width(&self) -> N {
49 self.radius()
50 }
51
52 pub fn height(&self) -> N {
53 self.radius()
54 }
55
56 pub fn radius(&self) -> N {
57 self.1
58 }
59
60 pub fn radius_sqrd(&self) -> N {
61 self.radius() * self.radius()
62 }
63
64 pub fn to_size(&self) -> Size<N> {
65 Size(self.width(), self.height())
66 }
67
68 pub fn centre(&self) -> Point<N> {
69 self.0
70 }
71
72 pub fn mult_radius(&mut self, n: N) {
73 self.1 *= n;
74 }
75
76 pub fn div_radius(&mut self, n: N) {
77 self.1 /= n;
78 }
79
80 pub fn add_radius(&mut self, n: N) {
81 self.1 += n;
82 }
83
84 pub fn sub_radius(&mut self, n: N) {
85 self.1 -= n;
86 }
87
88 pub fn abs(&self) -> Self {
89 Self(self.centre().abs(), self.radius().abs())
90 }
91
92 pub fn min(self, other: Self) -> Self {
93 Self(
94 self.centre().min(other.centre()),
95 self.radius().min(other.radius()),
96 )
97 }
98
99 pub fn max(self, other: Self) -> Self {
100 Self(
101 self.centre().max(other.centre()),
102 self.radius().max(other.radius()),
103 )
104 }
105
106 pub fn overlaps(&self, other: Self) -> bool {
107 let self_rounded = self.to_rounded();
108 let other_rounded = other.to_rounded();
109
110 let distance = self_rounded.centre().hypot_to(other_rounded.centre());
111 let radius_distance = self_rounded.radius().abs() + other_rounded.radius().abs();
112
113 distance < radius_distance
114 }
115
116 pub fn contains_point(&self, point: Point<N>) -> bool {
117 self.centre().hypot_to(point) <= self.radius()
118 }
119
120 pub fn iter_circumference_points(self, num_points: usize) -> CircleCircumferencePointsIterator {
121 CircleCircumferencePointsIterator::new(self, num_points)
122 }
123
124 pub fn iter_circumference_lines(self, num_lines: usize) -> CircleCircumferenceLinesIterator {
125 CircleCircumferenceLinesIterator::new(self, num_lines)
126 }
127
128 pub fn rotate_around_zero(self, rotation: f32) -> Self {
129 Circle(self.centre().rotate_around_zero(rotation), self.radius())
130 }
131
132 pub fn rotate_around_point(self, rotation: f32, point: Point<N>) -> Self {
133 Circle(
134 self.centre().rotate_around_point(rotation, point),
135 self.radius(),
136 )
137 }
138
139 pub fn overlaps_line(self, other: Line<N>) -> bool {
140 self.distance_to_line(other) <= self.to_f32().radius()
141 }
142
143 fn distance_to_line(self, line: Line<N>) -> f32 {
144 let self_f32 = self.to_f32();
145 let line_f32 = line.to_f32();
146
147 let v1 = line_f32.diff().to_point();
148 let mut v2 = line_f32.start().distance_to(self_f32.centre()).to_point();
149 let u = (v2.x() * v1.x() + v2.y() * v1.y()) / (v1.y() * v1.y() + v1.x() * v1.x());
150
151 if u >= 0.0 && u <= 1.0 {
152 let mut x = (v1.x() * u + line_f32.start().x()) - self_f32.centre().x();
153 let mut y = (v1.y() * u + line_f32.start().y()) - self_f32.centre().y();
154 x *= x;
155 y *= y;
156
157 return (y + x).sqrt(); }
159
160 let mut x = self_f32.centre().x() - line_f32.end().x();
162 let mut y = self_f32.centre().y() - line_f32.end().y();
163 x *= x;
164 y *= y;
165
166 v2 *= v2;
167
168 let first = (v2.y() + v2.x()).sqrt();
169 let second = (y + x).sqrt();
170 return first.min(second); }
172
173 #[allow(dead_code)]
174 pub(crate) fn to_f32(self) -> Circle<f32> {
175 self.to_rounded()
176 }
177}
178
179impl Circle<f32> {
180 #[allow(dead_code)]
181 pub(crate) fn from_f32<N: Num>(self) -> Circle<N> {
182 Circle(
183 self.centre().from_f32(),
184 FromRounded::from_rounded(self.radius()),
185 )
186 }
187}
188
189impl<O: Num, N: Num + ToRounded<O>> ToRounded<Circle<O>> for Circle<N> {
190 fn to_rounded(self) -> Circle<O> {
191 Circle(self.centre().to_rounded(), self.radius().to_rounded())
192 }
193}
194
195impl<N: Num> Add<Point<N>> for Circle<N> {
196 type Output = Self;
197
198 fn add(self, other: Point<N>) -> Self {
199 Circle(self.centre() + other, self.radius())
200 }
201}
202
203impl<N: Num> AddAssign<Point<N>> for Circle<N> {
204 fn add_assign(&mut self, other: Point<N>) {
205 self.0 += other;
206 }
207}
208
209impl<N: Num> Sub<Point<N>> for Circle<N> {
210 type Output = Self;
211
212 fn sub(self, other: Point<N>) -> Self {
213 Circle(self.centre() - other, self.radius())
214 }
215}
216
217impl<N: Num> SubAssign<Point<N>> for Circle<N> {
218 fn sub_assign(&mut self, other: Point<N>) {
219 self.0 -= other;
220 }
221}
222
223impl<N: Num> PartialEq for Circle<N> {
224 fn eq(&self, other: &Self) -> bool {
225 self.0 == other.0 && self.1 == other.1
226 }
227}
228
229#[cfg(test)]
230mod overlaps {
231 use super::*;
232
233 #[test]
234 fn it_should_overlap_with_another_circle_when_negative_radius_used() {
235 let a = Circle(Point(288.0, 179.0), 44.0);
236 let b = Circle(Point(341.0, 196.0), -148.0);
237
238 assert_eq!(a.overlaps(b), true);
239 assert_eq!(b.overlaps(a), true);
240 }
241
242 #[test]
243 fn it_should_overlap_with_another_circle_when_within_its_radius() {
244 let a = Circle(Point(10, 10), 5);
245 let b = Circle(Point(14, 14), 2);
246
247 assert_eq!(a.overlaps(b), true);
248 }
249
250 #[test]
251 fn it_should_overlap_with_another_circle_when_within_their_radius() {
252 let a = Circle(Point(10, 10), 2);
253 let b = Circle(Point(14, 14), 5);
254
255 assert_eq!(a.overlaps(b), true);
256 }
257
258 #[test]
259 fn it_should_not_overlap_with_another_circle_when_combined_radius_too_small() {
260 let a = Circle(Point(10, 10), 2);
261 let b = Circle(Point(15, 15), 2);
262
263 assert_eq!(a.overlaps(b), false);
264 }
265}
266
267#[cfg(test)]
268mod overlaps_line {
269 use super::*;
270
271 #[test]
272 fn it_should_not_overlap_line_that_doesnt_cut() {
273 let circle: Circle<i32> = Circle(Point(10, 10), 4);
274 let line = Line(Point(0, 10), Point(10, 15));
275 assert_eq!(circle.overlaps_line(line), false);
276 }
277
278 #[test]
279 fn it_should_overlap_line_cutting_through() {
280 let circle: Circle<i32> = Circle(Point(10, 10), 5);
281 let line = Line(Point(0, 10), Point(20, 10));
282 assert!(circle.overlaps_line(line));
283 }
284
285 #[test]
286 fn it_should_overlap_line_that_starts_in_circle() {
287 let circle: Circle<i32> = Circle(Point(10, 10), 5);
288 let line = Line(Point(9, 10), Point(20, 10));
289 assert!(circle.overlaps_line(line));
290 }
291
292 #[test]
293 fn it_should_overlap_line_that_end_in_circle() {
294 let circle: Circle<i32> = Circle(Point(10, 10), 5);
295 let line = Line(Point(0, 10), Point(11, 10));
296 assert!(circle.overlaps_line(line));
297 }
298
299 #[test]
300 fn it_should_overlap_line_that_end_and_starts_in_circle() {
301 let circle: Circle<i32> = Circle(Point(10, 10), 5);
302 let line = Line(Point(9, 10), Point(11, 10));
303 assert!(circle.overlaps_line(line));
304 }
305
306 #[test]
307 fn it_should_overlap_line_that_end_and_starts_in_circle_in_opposite_direction() {
308 let circle: Circle<i32> = Circle(Point(10, 10), 5);
309 let line = Line(Point(11, 10), Point(9, 10));
310 assert!(circle.overlaps_line(line));
311 }
312
313 #[test]
314 fn it_should_not_overlap_an_infinite_line() {
315 let circle: Circle<f32> = Circle(Point(10.0, 10.0), 5.0);
316 let line: Line<f32> = Line(Point(10.0, 20.0), Point(10.0, 30.0));
317 assert_eq!(false, circle.overlaps_line(line));
318 }
319}