use super::line::*;
use super::super::geo::*;
const RAY_DIVISOR_SMALLEST_VALUE: f64 = 2e-12;
pub fn line_intersects_line<L: Line>(line1: &L, line2: &L) -> Option<L::Point>
where
L::Point: Coordinate2D,
{
let line1_points = line1.points();
let line2_points = line2.points();
let ((x1, y1), (x2, y2)) = (line1_points.0.coords(), line1_points.1.coords());
let ((x3, y3), (x4, y4)) = (line2_points.0.coords(), line2_points.1.coords());
let ua = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
let ub = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
if (0.0..=1.0).contains(&ua) && (0.0..=1.0).contains(&ub) {
Some(L::Point::from_components(&[
x1+(ua*(x2-x1)),
y1+(ua*(y2-y1))
]))
} else {
None
}
}
pub fn line_intersects_ray<L: Line>(line: &L, ray: &L) -> Option<L::Point>
where
L::Point: Coordinate2D,
{
let line_points = line.points();
let ray_points = ray.points();
let ((x1, y1), (x2, y2)) = (line_points.0.coords(), line_points.1.coords());
let ((x3, y3), (x4, y4)) = (ray_points.0.coords(), ray_points.1.coords());
let ua = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
if (0.0..=1.0).contains(&ua) {
Some(L::Point::from_components(&[
x1+(ua*(x2-x1)),
y1+(ua*(y2-y1))
]))
} else {
None
}
}
pub fn ray_intersects_ray<L: Line>(line: &L, ray: &L) -> Option<L::Point>
where
L::Point: Coordinate2D,
{
let line_points = line.points();
let ray_points = ray.points();
let ((x1, y1), (x2, y2)) = (line_points.0.coords(), line_points.1.coords());
let ((x3, y3), (x4, y4)) = (ray_points.0.coords(), ray_points.1.coords());
let divisor = (y4-y3)*(x2-x1) - (x4-x3)*(y2-y1);
if divisor.abs() > RAY_DIVISOR_SMALLEST_VALUE {
let ua = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / divisor;
Some(L::Point::from_components(&[
x1+(ua*(x2-x1)),
y1+(ua*(y2-y1))
]))
} else {
None
}
}
pub fn line_clip_to_bounds<L: Line>(line: &L, bounds: &(L::Point, L::Point)) -> Option<L>
where
L::Point: Coordinate2D,
{
let line_points = line.points();
let ((x1, y1), (x2, y2)) = (line_points.0.coords(), line_points.1.coords());
let (dx, dy) = (x2-x1, y2-y1);
let (xmin, ymin) = (bounds.0.x().min(bounds.1.x()), bounds.0.y().min(bounds.1.y()));
let (xmax, ymax) = (bounds.0.x().max(bounds.1.x()), bounds.0.y().max(bounds.1.y()));
let delta = [-dx, dx, -dy, dy];
let edge = [x1-xmin, xmax-x1, y1-ymin, ymax-y1];
let mut t1 = 0.0;
let mut t2 = 1.0;
for (delta, edge) in delta.iter().zip(edge.iter()) {
if delta == &0.0 {
if edge < &0.0 {
return None;
}
} else {
let t = edge/delta;
if delta < &0.0 && t1 < t {
t1 = t;
} else if delta > &0.0 && t2 > t {
t2 = t;
}
}
}
if t1 > t2 || t1 > 1.0 || t2 < 0.0 {
None
} else {
let p1 = L::Point::from_components(&[x1 + t1*dx, y1 + t1*dy]);
let p2 = L::Point::from_components(&[x1 + t2*dx, y1 + t2*dy]);
Some(L::from_points(p1, p2))
}
}