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 } }
}
#[inline]
pub fn from_points(a: Vect, b: Vect) -> Self {
Self { o: a, v: b - a, }
}
#[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
}
#[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
}
#[inline]
pub fn contains(&self, pos: Vect) -> bool {
self.within(pos) && self.is_on(pos)
}
#[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
}
#[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 }
}
#[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)));
}
}