mutils/geom/
circle.rs

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(); // return distance from line
158        }
159
160        // get distance from end points
161        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); // return smaller of two distances as the result
171    }
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}