1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::{Vect, Rect, Ray, IntersectionPoints};
use crate::math::Intersection;

/// Circ is what we in geometry call circle. Circle is mainly for collision detection and supports

/// collisions with Ray, Rect and it self. There are also projection methods available.

#[derive(Copy, Clone, Debug)]
pub struct Circ {
    pub c: Vect,
    pub r: f32,
}

impl Circ {
    #[inline]
    pub fn new(x: f32, y: f32, r:f32) -> Self {
        Self { r, c: Vect { x, y } }
    }

    /// returns whether point ic in circle

    #[inline]
    pub fn contains(&self, pos: Vect) -> bool {
        self.c.dist(pos) <= self.r
    }

    /// projects x coordinate to y coordinate that belongs to circle or nan if there is no such y.

    /// mind that even if there are 2 possible solution for one x. If you need both solutions use

    /// precise_prf_x witch is slower but takes second solution to account.

    #[inline]
    pub fn prj_x(&self, x: f32) -> f32 {
        (self.r * self.r - (x - self.c.x).powi(2)).sqrt() - self.c.y
    }

    /// opposite of prj_x

    #[inline]
    pub fn prj_y(&self, y: f32) -> f32 {
        (self.r * self.r - (y - self.c.y).powi(2)).sqrt() - self.c.y
    }

    /// used for other calculations

    #[inline]
    pub fn formula_x(&self, x: f32) -> (f32, f32) {
        (2.0 * self.c.y, self.c.y * self.c.y - self.r * self.r + (x - self.c.x).powi(2))
    }

    /// used for other calculations

    #[inline]
    pub fn formula_y(&self, y: f32) -> (f32, f32) {
        (2.0 * self.c.x, self.c.x * self.c.x - self.r * self.r + (y - self.c.y).powi(2))
    }

    /// used for other calculations

    #[inline]
    pub fn precise_prj(&self, arg: f32, formula: fn(&Self, f32) -> (f32, f32)) -> [Option<f32>; 2] {
        let (b, c) = formula(self, arg);

        let mut dis = b * b - 4.0 * c;
        if dis < 0.0 {
            return [None, None];
        } else if dis == 0.0 {
            return [Some(-b/2.0), None]
        }

        dis = dis.sqrt();
        [Some((-b - dis)/2.0), Some((-b + dis)/2.0)]
    }

    /// projects x coordinate to zero one or two solutions. This method is considerably slower then

    /// prj_x

    #[inline]
    pub fn precise_prj_x(&self, x: f32) -> [Option<f32>; 2] {
        self.precise_prj(x, Self::formula_x)
    }

    /// opposite of precise_prj_x

    #[inline]
    pub fn precise_prj_y(&self, y: f32) -> [Option<f32>; 2] {
        self.precise_prj(y, Self::formula_y)
    }
}

impl Intersection<Circ> for Circ {
    #[inline]
    fn intersects(&self, o: &Circ) -> bool {
        self.c.dist(o.c) < self.r + o.r
    }
}

impl Intersection<Rect> for Circ {
    #[inline]
    fn intersects(&self, o: &Rect) -> bool {
        o.intersects(self)
    }
}

impl Intersection<Ray> for Circ {
    #[inline]
    fn intersects(&self, o: &Ray) -> bool {
        o.intersects(self)
    }
}

impl IntersectionPoints<Ray> for Circ {
    #[inline]
    fn intersects_points(&self, o: &Ray) -> [Option<Vect>; 2] {
        o.intersects_points(self)
    }
}

#[cfg(test)]
mod tests {
    use super::Circ;
    use crate::Vect;
    #[test]
    fn prj_test() {
        let base = circ!(0, 0; 10);
        assert_eq!(0.0 ,base.prj_y(10.0));
        assert_eq!(0.0 ,base.prj_x(10.0));
        assert_eq!(10.0 ,base.prj_y(0.0));
        assert_eq!(10.0 ,base.prj_x(0.0));
    }

    #[test]
    fn precise_pre_test() {
        let base = circ!(0, 0; 10);
        assert_eq!([Some(0.0), None] ,base.precise_prj_y(10.0));
        assert_eq!([Some(0.0), None] ,base.precise_prj_x(10.0));
        assert_eq!([Some(-8.0), Some(8.0)] ,base.precise_prj_y(6.0));
        assert_eq!([Some(-8.0), Some(8.0)] ,base.precise_prj_x(6.0));
    }
}