use crate::geometry::orient2d;
use alloc::fmt::Debug;
use libm::atan2;
use s2json::{GetXY, NewXY, Point};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct IntersectionOfSegments<Q: NewXY> {
pub point: Q,
pub u: f64,
pub t: f64,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct IntersectionOfSegmentsRobust<Q: NewXY> {
pub point: Q,
pub u: f64,
pub t: f64,
pub u_vec: Q,
pub t_vec: Q,
pub u_angle: f64,
pub t_angle: f64,
}
impl<Q: NewXY> IntersectionOfSegmentsRobust<Q> {
#[allow(clippy::too_many_arguments)]
pub fn new(
x: f64,
y: f64,
u: f64,
t: f64,
u_vec: Q,
t_vec: Q,
u_angle: f64,
t_angle: f64,
) -> Self {
Self { point: Q::new_xy(x, y), u, t, u_vec, t_vec, u_angle, t_angle }
}
}
pub fn intersection_of_segments<P: GetXY, Q: NewXY>(
a: (&P, &P),
b: (&P, &P),
) -> Option<IntersectionOfSegments<Q>> {
let (p, p2) = a;
let (q, q2) = b;
let r = Point::new_xy(p2.x() - p.x(), p2.y() - p.y());
let s = Point::new_xy(q2.x() - q.x(), q2.y() - q.y());
let cross = r.x() * s.y() - r.y() * s.x();
if cross == 0. {
return None;
}
let u = ((q.x() - p.x()) * s.y() - (q.y() - p.y()) * s.x()) / cross;
let t = ((q.x() - p.x()) * r.y() - (q.y() - p.y()) * r.x()) / cross;
if (0. ..=1.).contains(&t) && (0. ..=1.).contains(&u) {
Some(IntersectionOfSegments {
point: Q::new_xy(p.x() + u * r.x(), p.y() + u * r.y()),
u,
t,
})
} else {
None
}
}
pub fn intersection_of_segments_robust<P: GetXY + PartialEq, Q: NewXY + Clone>(
a: (&P, &P),
b: (&P, &P),
same_ring: bool,
) -> Option<IntersectionOfSegmentsRobust<Q>> {
let (x1, y1) = a.0.xy();
let (x2, y2) = a.1.xy();
let (x3, y3) = b.0.xy();
let (x4, y4) = b.1.xy();
let (dx_a, dy_a) = (x2 - x1, y2 - y1);
let (dx_b, dy_b) = (x4 - x3, y4 - y3);
let (dx_c, dy_c) = (x1 - x3, y1 - y3);
if (dx_a == 0. && dy_a == 0.) || (dx_b == 0. && dy_b == 0.) {
return None;
}
let u_angle = if dx_a == 0. && dy_a == 0. { 0. } else { atan2(dy_a, dx_a) };
let t_angle = if dx_b == 0. && dy_b == 0. { 0. } else { atan2(dy_b, dx_b) };
if same_ring {
if a.1 == b.0 || a.1 == b.1 || a.0 == b.0 || a.0 == b.1 {
return None;
}
} else {
let zero = Q::new_xy(0., 0.);
if a.1 == b.0 {
let u_vec = Q::new_xy(dx_a, dy_a);
return Some(IntersectionOfSegmentsRobust::new(
x2, y2, 1., 0., u_vec, zero, u_angle, t_angle,
));
}
if a.1 == b.1 {
let u_vec = Q::new_xy(dx_a, dy_a);
let t_vec = Q::new_xy(dx_b, dy_b);
return Some(IntersectionOfSegmentsRobust::new(
x2, y2, 1., 1., u_vec, t_vec, u_angle, t_angle,
));
}
if a.0 == b.0 {
let zero_2 = zero.clone();
return Some(IntersectionOfSegmentsRobust::new(
x1, y1, 0., 0., zero, zero_2, u_angle, t_angle,
));
}
if a.0 == b.1 {
let t_vec = Q::new_xy(dx_b, dy_b);
return Some(IntersectionOfSegmentsRobust::new(
x1, y1, 0., 1., zero, t_vec, u_angle, t_angle,
));
}
}
let denom = dy_b * dx_a - dx_b * dy_a;
if denom == 0. {
return None;
}
let orient1 = orient2d(x1, y1, x2, y2, x3, y3);
let orient2 = orient2d(x1, y1, x2, y2, x4, y4);
if (orient1 > 0. && orient2 > 0.) || (orient1 < 0. && orient2 < 0.) {
return None;
}
let nume_a = dx_b * dy_c - dy_b * dx_c;
let nume_b = dx_a * dy_c - dy_a * dx_c;
let u_a = nume_a / denom;
let u_b = nume_b / denom;
if (0. ..=1.).contains(&u_a) && (0. ..=1.).contains(&u_b) {
let mut x = x1 + u_a * dx_a;
let mut y = y1 + u_a * dy_a;
let u_vec = Q::new_xy(u_a * dx_a, u_a * dy_a);
let t_vec = Q::new_xy(u_b * dx_b, u_b * dy_b);
if u_a != 0. && u_a != 1. && u_b != 0. && u_b != 1. {
if u_a <= 0.5 && x == x1 && y == y1 {
if dx_a != 0. {
x = if dx_a > 0. { x.next_up() } else { x.next_down() };
}
if dy_a != 0. {
y = if dy_a > 0. { y.next_up() } else { y.next_down() };
}
} else if u_a > 0.5 && x == x2 && y == y2 {
if dx_a != 0. {
x = if dx_a < 0. { x.next_up() } else { x.next_down() };
}
if dy_a != 0. {
y = if dy_a < 0. { y.next_up() } else { y.next_down() };
}
} else if u_b <= 0.5 && x == x3 && y == y3 {
if dx_b != 0. {
x = if dx_b > 0. { x.next_up() } else { x.next_down() };
}
if dy_b != 0. {
y = if dy_b > 0. { y.next_up() } else { y.next_down() };
}
} else if u_b > 0.5 && x == x4 && y == y4 {
if dx_b != 0. {
x = if dx_b < 0. { x.next_up() } else { x.next_down() };
}
if dy_b != 0. {
y = if dy_b < 0. { y.next_up() } else { y.next_down() };
}
}
}
Some(IntersectionOfSegmentsRobust::new(x, y, u_a, u_b, u_vec, t_vec, u_angle, t_angle))
} else {
None
}
}