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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
use crate::{Vect, Rect, Circ};
use crate::math::{Intersection, IntersectionPoints};

#[derive(Debug, Copy, Clone)]
pub struct Ray {
    pub o: Vect,
    pub v: Vect,
}

impl Ray {
    #[inline]
    pub fn new(ox: f32, oy: f32, vx: f32, vy: f32) -> Self {
        Self { o: Vect { x: ox, y: oy }, v: Vect { x: vx, y: vy } }
    }

    /// ray can be also expressed as line from a to b

    #[inline]
    pub fn from_points(a: Vect, b: Vect) -> Self {
        Self { o: a, v: b - a, }
    }

    /// returns whether point belongs to the INFINITE LINE Ray expresses

    #[inline]
    pub fn is_on(&self, pos: Vect) -> bool {
        self.v.y * pos.x - self.v.x * pos.y - self.v.y * self.o.x + self.v.x * self.o.y == 0.0
    }

    /// returns whether point in in AABB this raycast expresses

    #[inline]
    pub fn within(&self, pos: Vect) -> bool {
        let (mix, max) = if self.v.x > 0.0 {
            (self.o.x, self.o.x + self.v.x)
        } else {
            (self.o.x + self.v.x, self.o.x)
        };

        let (miy, may) = if self.v.y > 0.0 {
            (self.o.y, self.o.y + self.v.y)
        } else {
            (self.o.y + self.v.y, self.o.y)
        };

        pos.x >= mix && pos.x <= max && pos.y >= miy && pos.y <= may
    }

    /// combination of within and is_on, if it returns true point belongs to a Ray

    #[inline]
    pub fn contains(&self, pos: Vect) -> bool {
        self.within(pos) && self.is_on(pos)
    }

    /// projects x coordinate to y coordinate. This is mainly an helper for intersection calculations

    #[inline]
    pub fn prj_x(&self, x: f32) -> f32 {
        (self.v.y * x - self.v.y * self.o.x + self.v.x * self.o.y) / self.v.x
    }

    /// opposite of prj_x

    #[inline]
    pub fn prj_y(&self, y: f32) -> f32 {
        (self.v.x * y + self.v.y * self.o.x - self.v.x * self.o.y) / self.v.y
    }

    #[inline]
    pub fn project_to_grid(&self, grid_resolution: Vect) -> Self {
        Self { o: self.o / grid_resolution, v: self.v / grid_resolution }
    }

    /// returns ray in opposite direction

    #[inline]
    pub fn inverted(&self) -> Self {
        Self { o: self.o + self.v, v: self.v.inverted() }
    }
}

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

impl Intersection<Rect> for Ray {
    #[inline]
    fn intersects(&self, o: &Rect) -> bool {
        let res = self.intersects_points(o);
        res[0].is_some() || res[1].is_some()
    }
}

impl Intersection<Circ> for Ray {
    #[inline]
    fn intersects(&self, o: &Circ) -> bool {
        let res = self.intersects_points(o);
        res[0].is_some() || res[1].is_some()
    }
}

impl IntersectionPoints<Ray> for Ray {
    #[inline]
    fn intersects_points(&self, o: &Ray) -> [Option<Vect>; 2] {
        let a = self.v.y * self.o.x * o.v.x - self.v.x * self.o.y * o.v.x;
        let b = o.v.y * o.o.x * self.v.x - o.v.x * o.o.y * self.v.x;
        let c = o.v.y * self.v.x - self.v.y * o.v.x;

        if c == 0.0 {
            return [None, None]
        }

        let x = (b - a) / c;
        let res = Vect::new(x, self.prj_x(x));
        if o.within(res) {
            return [Some(res), None]
        }

        [None, Some(res)]
    }
}

impl IntersectionPoints<Rect> for Ray {
    #[inline]
    fn intersects_points(&self, o: &Rect) -> [Option<Vect>; 2] {
        let mut points = [None; 2];
        let mut i = 0;
        for ray in o.to_rays().iter() {
            let res = self.intersects_points(ray)[0];
            if res.is_some() {
                points[i] = res;
                i += 1;
            }
        }
        points
    }
}

impl IntersectionPoints<Circ> for Ray {
    #[inline]
    fn intersects_points(&self, o: &Circ) -> [Option<Vect>; 2] {
        let xv2 = self.v.x * self.v.x;
        let yv2 = self.v.y * self.v.y;

        let a = yv2 + xv2;
        let b = 2.0 * (
            self.v.y * self.v.x * self.o.y +
            self.v.y * o.c.y * self.v.x -
            xv2 * o.c.x -
            yv2 * self.o.x
        );
        let c = xv2 * ( self.o.y.powi(2) + o.c.y.powi(2) - o.r.powi(2) + o.c.x.powi(2) ) +
            yv2 * self.o.x.powi(2) +
            2.0 * self.v.y * self.o.x * self.v.x * (self.o.y - o.c.y) +
            2.0 * self.o.y * o.c.y * xv2;

        let mut dis = b * b - 4.0 * c * a;
        if dis < 0.0 {
            return [None, None];
        } else if dis == 0.0 {
            let x = -b / (2.0 * a);
            let v = Vect::new(x, self.prj_x(x));
            return [ if self.within(v) {Some(v)} else {None}, None]
        }

        dis = dis.sqrt();
        let x1 = (-b - dis)/(2.0 * a);
        let x2 = (-b + dis)/(2.0 * a);

        let v1 = Vect::new(x1, self.prj_x(x1));
        let v2 = Vect::new(x2, self.prj_x(x2));

        [if self.within(v1) {Some(v1)} else {None}, if self.within(v2) {Some(v2)} else {None}]
    }
}

#[cfg(test)]
mod tests {
    use super::Ray;
    use crate::{ Circ ,Vect, IntersectionPoints};
    use std::fs::read;

    #[test]
    fn intersect_ray_test() {
        let base = ray!(0, 0, 100, 0);
        assert_eq!([Some(vect!(0, 0)), None], base.intersects_points(&ray!(0, 0, 0, 1)));
        assert_eq!([Some(vect!(4, 0)), None], base.intersects_points(&ray!(4, 0, 0, 1)));
        assert_eq!([None, Some(vect!(4, 0))], base.intersects_points(&ray!(4, 2, 0, 1)));
        assert_eq!([None, None], base.intersects_points(&ray!(4, 2, 1, 0)));
    }

    #[test]
    fn intersect_circle_test() {
        let base = ray!(-5, 0, 10, 0);
        assert_eq!([Some(vect!(-4.0, 0.0)), Some(vect!(4.0, 0.0))] ,base.intersects_points(&circ!(0, 0; 4)));
        assert_eq!([Some(vect!(0.0, 0.0)), None] ,base.intersects_points(&circ!(0, 4; 4)));
        assert_eq!([None, None] ,base.intersects_points(&circ!(0, 5; 4)));
        assert_eq!([None, None] ,base.intersects_points(&circ!(0, 0; 10)));
    }
}