rustbatch/math/
circle.rs

1use crate::{Vect, Rect, Ray, IntersectionPoints};
2use crate::math::Intersection;
3
4/// Circ is what we in geometry call circle. Circle is mainly for collision detection and supports
5/// collisions with Ray, Rect and it self. There are also projection methods available.
6#[derive(Copy, Clone, Debug)]
7pub struct Circ {
8    pub c: Vect,
9    pub r: f32,
10}
11
12impl Circ {
13    #[inline]
14    pub fn new(x: f32, y: f32, r:f32) -> Self {
15        Self { r, c: Vect { x, y } }
16    }
17
18    /// returns whether point ic in circle
19    #[inline]
20    pub fn contains(&self, pos: Vect) -> bool {
21        self.c.dist(pos) <= self.r
22    }
23
24    /// projects x coordinate to y coordinate that belongs to circle or nan if there is no such y.
25    /// mind that even if there are 2 possible solution for one x. If you need both solutions use
26    /// precise_prf_x witch is slower but takes second solution to account.
27    #[inline]
28    pub fn prj_x(&self, x: f32) -> f32 {
29        (self.r * self.r - (x - self.c.x).powi(2)).sqrt() - self.c.y
30    }
31
32    /// opposite of prj_x
33    #[inline]
34    pub fn prj_y(&self, y: f32) -> f32 {
35        (self.r * self.r - (y - self.c.y).powi(2)).sqrt() - self.c.y
36    }
37
38    /// used for other calculations
39    #[inline]
40    pub fn formula_x(&self, x: f32) -> (f32, f32) {
41        (2.0 * self.c.y, self.c.y * self.c.y - self.r * self.r + (x - self.c.x).powi(2))
42    }
43
44    /// used for other calculations
45    #[inline]
46    pub fn formula_y(&self, y: f32) -> (f32, f32) {
47        (2.0 * self.c.x, self.c.x * self.c.x - self.r * self.r + (y - self.c.y).powi(2))
48    }
49
50    /// used for other calculations
51    #[inline]
52    pub fn precise_prj(&self, arg: f32, formula: fn(&Self, f32) -> (f32, f32)) -> [Option<f32>; 2] {
53        let (b, c) = formula(self, arg);
54
55        let mut dis = b * b - 4.0 * c;
56        if dis < 0.0 {
57            return [None, None];
58        } else if dis == 0.0 {
59            return [Some(-b/2.0), None]
60        }
61
62        dis = dis.sqrt();
63        [Some((-b - dis)/2.0), Some((-b + dis)/2.0)]
64    }
65
66    /// projects x coordinate to zero one or two solutions. This method is considerably slower then
67    /// prj_x
68    #[inline]
69    pub fn precise_prj_x(&self, x: f32) -> [Option<f32>; 2] {
70        self.precise_prj(x, Self::formula_x)
71    }
72
73    /// opposite of precise_prj_x
74    #[inline]
75    pub fn precise_prj_y(&self, y: f32) -> [Option<f32>; 2] {
76        self.precise_prj(y, Self::formula_y)
77    }
78}
79
80impl Intersection<Circ> for Circ {
81    #[inline]
82    fn intersects(&self, o: &Circ) -> bool {
83        self.c.dist(o.c) < self.r + o.r
84    }
85}
86
87impl Intersection<Rect> for Circ {
88    #[inline]
89    fn intersects(&self, o: &Rect) -> bool {
90        o.intersects(self)
91    }
92}
93
94impl Intersection<Ray> for Circ {
95    #[inline]
96    fn intersects(&self, o: &Ray) -> bool {
97        o.intersects(self)
98    }
99}
100
101impl IntersectionPoints<Ray> for Circ {
102    #[inline]
103    fn intersects_points(&self, o: &Ray) -> [Option<Vect>; 2] {
104        o.intersects_points(self)
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::Circ;
111    use crate::Vect;
112    #[test]
113    fn prj_test() {
114        let base = circ!(0, 0; 10);
115        assert_eq!(0.0 ,base.prj_y(10.0));
116        assert_eq!(0.0 ,base.prj_x(10.0));
117        assert_eq!(10.0 ,base.prj_y(0.0));
118        assert_eq!(10.0 ,base.prj_x(0.0));
119    }
120
121    #[test]
122    fn precise_pre_test() {
123        let base = circ!(0, 0; 10);
124        assert_eq!([Some(0.0), None] ,base.precise_prj_y(10.0));
125        assert_eq!([Some(0.0), None] ,base.precise_prj_x(10.0));
126        assert_eq!([Some(-8.0), Some(8.0)] ,base.precise_prj_y(6.0));
127        assert_eq!([Some(-8.0), Some(8.0)] ,base.precise_prj_x(6.0));
128    }
129}