use crate::util::*;
use std::fmt;
#[derive(Clone, Default, Debug, Copy)]
pub struct Ray {
pub(crate) origin: Coordinate,
pub(crate) angle: Coordinate,
}
impl fmt::Display for Ray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Origin : (x, y) = ({}, {}) / Angle : (dx, dy) = ({}, {})",
self.origin.0, self.origin.1, self.angle.0, self.angle.1
)
}
}
impl Ray {
pub fn new(src: Coordinate, dst: Coordinate) -> Self {
Self {
origin: src,
angle: dst - src,
}
}
pub fn point(&self) -> Coordinate {
self.point_by_ratio(0.)
}
pub fn point_by_ratio(&self, ratio: f64) -> Coordinate {
self.origin + self.angle * ratio
}
pub(crate) fn bisector(&self, rhs: &Ray, origin: Coordinate, orient: bool) -> Self {
let mut ray = self.angle * rhs.angle.norm() + rhs.angle * self.angle.norm();
if feq(ray.0, 0.) && feq(ray.1, 0.) {
ray = (-self.angle.1, self.angle.0).into();
if orient {
ray = ray * -1.;
}
} else {
if orient && self.angle.outer_product(&ray) > 0.0 {
ray = ray * -1.0;
}
if !orient && self.angle.outer_product(&ray) < 0.0 {
ray = ray * -1.0;
}
}
Self { origin, angle: ray }
}
pub fn is_contain(&self, rhs: &Coordinate) -> bool {
if self.is_degenerated() {
return feq(self.origin.0, rhs.0) && feq(self.origin.1, rhs.1);
}
feq((*rhs - self.origin).outer_product(&self.angle), 0.)
}
pub fn is_intersect(&self, rhs: &Ray) -> bool {
let op = self.angle.outer_product(&rhs.angle);
if feq(op, 0.0) {
if self.is_contain(&rhs.origin) {
return true;
}
if rhs.is_contain(&self.origin) {
return true;
}
return false;
}
let i = (rhs.origin - self.origin).outer_product(&rhs.angle)
/ self.angle.outer_product(&rhs.angle);
let j = (rhs.origin - self.origin).outer_product(&self.angle)
/ self.angle.outer_product(&rhs.angle);
if fgeq(i, 0.) && fgeq(j, 0.) {
return true;
}
false
}
pub fn intersect(&self, rhs: &Ray) -> Coordinate {
let op = self.angle.outer_product(&rhs.angle);
if feq(op, 0.) {
if self.is_contain(&rhs.origin) {
if fgt((rhs.origin - self.origin) / self.angle, 0.) {
return rhs.origin;
} else {
return self.origin;
}
}
return (self.origin + rhs.origin) / 2.0;
}
let i = (rhs.origin - self.origin).outer_product(&rhs.angle)
/ self.angle.outer_product(&rhs.angle);
self.origin + self.angle * i
}
pub fn is_parallel(&self, rhs: &Ray) -> bool {
let op = self.angle.outer_product(&rhs.angle);
if feq(op, 0.0) && !self.is_contain(&rhs.origin) {
return true;
}
false
}
pub(crate) fn is_degenerated(&self) -> bool {
feq(self.angle.0, 0.) && feq(self.angle.1, 0.)
}
pub fn normalize(&mut self) {
if self.is_degenerated() {
return;
}
self.angle = self.angle / self.angle.norm();
}
pub(crate) fn orientation(&self, rhs: &Coordinate) -> i32 {
let res = self.angle.outer_product(&(*rhs - self.origin));
if feq(res, 0.) {
return 0;
}
if fgt(res, 0.) {
return 1;
}
-1
}
pub fn reverse(&self) -> Self {
Self {
origin: self.origin,
angle: self.angle * -1.,
}
}
pub fn rotate_by(&self, angle: f64) -> Self {
let nx = self.angle.0 * f64::cos(angle) - self.angle.1 * f64::sin(angle);
let ny = self.angle.0 * f64::sin(angle) + self.angle.1 * f64::cos(angle);
Self {
origin: self.origin,
angle: (nx, ny).into(),
}
}
}