use crate::{
geometry::{
Point2, Point3, Segment2, Segment3, Vector3, point::Point, spatial_element::SpatialElement,
vector::VectorOps,
},
kernel::{are_collinear, orient},
numeric::scalar::Scalar,
};
use std::ops::{Add, Div, Mul, Sub};
#[derive(Debug, Clone)]
pub enum SegmentIntersection2<T>
where
T: Scalar,
for<'a> &'a T: Add<&'a T, Output = T>
+ Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
None,
Point(Point2<T>),
Overlapping(Segment2<T>),
}
impl<T> PartialEq for SegmentIntersection2<T>
where
T: Scalar,
for<'a> &'a T: Add<&'a T, Output = T>
+ Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(SegmentIntersection2::None, SegmentIntersection2::None) => true,
(SegmentIntersection2::Point(p1), SegmentIntersection2::Point(p2)) => p1 == p2,
(SegmentIntersection2::Overlapping(s1), SegmentIntersection2::Overlapping(s2)) => {
s1 == s2
}
_ => false,
}
}
}
pub fn segment_segment_intersection_2<T>(
seg1: &Segment2<T>,
seg2: &Segment2<T>,
) -> SegmentIntersection2<T>
where
T: Scalar,
for<'a> &'a T: Add<&'a T, Output = T>
+ Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
let a = &seg1.a;
let b = &seg1.b;
let c = &seg2.a;
let d = &seg2.b;
let o1 = orient(&[a.clone(), b.clone(), c.clone()]);
let o2 = orient(&[a.clone(), b.clone(), d.clone()]);
let o3 = orient(&[c.clone(), d.clone(), a.clone()]);
let o4 = orient(&[c.clone(), d.clone(), b.clone()]);
let zero = T::zero();
let intersecting = (&o1 * &o2) <= zero && (&o3 * &o4) <= zero;
if intersecting {
if o1.abs().is_positive()
|| o2.abs().is_positive()
|| o3.abs().is_positive()
|| o4.abs().is_positive()
{
let (x1, y1) = (&a[0], &a[1]);
let (x2, y2) = (&b[0], &b[1]);
let (x3, y3) = (&c[0], &c[1]);
let (x4, y4) = (&d[0], &d[1]);
let denom = &(&(x1 - x2) * &(y3 - y4)) - &(&(y1 - y2) * &(x3 - x4));
if denom.abs().is_zero() {
return SegmentIntersection2::None; }
let px_num = &(&(&(x1 * y2) - &(y1 * x2)) * &(x3 - x4))
- &(&(x1 - x2) * &(&(x3 * y4) - &(y3 * x4)));
let py_num = &(&(&(x1 * y2) - &(y1 * x2)) * &(y3 - y4))
- &(&(y1 - y2) * &(&(x3 * y4) - &(y3 * x4)));
let px = &px_num / &denom;
let py = &py_num / &denom;
return SegmentIntersection2::Point(Point::<T, 2>::from_vals([px, py]));
}
if are_collinear(&a, &b, &c) {
let mut pts = [a, b, c, d];
pts.sort_by(|p1, p2| {
p1[0]
.partial_cmp(&p2[0])
.unwrap_or(std::cmp::Ordering::Equal)
.then(
p1[1]
.partial_cmp(&p2[1])
.unwrap_or(std::cmp::Ordering::Equal),
)
});
let s = Segment2::new(&pts[1], &pts[2]);
return SegmentIntersection2::Overlapping(s);
}
}
SegmentIntersection2::None
}
#[derive(Debug, Clone)]
pub enum SegmentIntersection3<T>
where
T: Scalar,
{
None,
Point(Point3<T>),
Overlapping(Segment3<T>),
}
impl<T> PartialEq for SegmentIntersection3<T>
where
T: Scalar,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(SegmentIntersection3::None, SegmentIntersection3::None) => true,
(SegmentIntersection3::Point(p1), SegmentIntersection3::Point(p2)) => p1 == p2,
(SegmentIntersection3::Overlapping(s1), SegmentIntersection3::Overlapping(s2)) => {
s1 == s2
}
_ => false,
}
}
}
pub fn segment_segment_intersection_3<T>(
seg1: &Segment3<T>,
seg2: &Segment3<T>,
) -> SegmentIntersection3<T>
where
T: Scalar,
for<'a> &'a T: Add<&'a T, Output = T>
+ Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
let p1 = &seg1.a;
let p2 = &seg1.b;
let q1 = &seg2.a;
let q2 = &seg2.b;
let d1 = Vector3::<T>::from(Point3::<T>::from_vals([
&p2[0] - &p1[0],
&p2[1] - &p1[1],
&p2[2] - &p1[2],
]));
let d2 = Vector3::<T>::from(Point3::<T>::from_vals([
&q2[0] - &q1[0],
&q2[1] - &q1[1],
&q2[2] - &q1[2],
]));
let r = Vector3::<T>::from(Point3::<T>::from_vals([
&p1[0] - &q1[0],
&p1[1] - &q1[1],
&p1[2] - &q1[2],
]));
let a = d1.dot(&d1); let b = d1.dot(&d2);
let c = d2.dot(&d2); let d = d1.dot(&r);
let e = d2.dot(&r);
let denom = &(&a * &c) - &(&b * &b);
let zero = T::zero();
let (s, t) = if denom.abs().is_positive() {
let s = &(&(&b * &e) - &(&c * &d)) / &denom;
let t = &(&(&a * &e) - &(&b * &d)) / &denom;
(s, t)
} else {
if are_collinear(p1, p2, q1) {
let mut pts = [p1, p2, q1, q2];
pts.sort_by(|p1, p2| {
p1[0]
.partial_cmp(&p2[0])
.unwrap_or(std::cmp::Ordering::Equal)
.then(
p1[1]
.partial_cmp(&p2[1])
.unwrap_or(std::cmp::Ordering::Equal),
)
.then(
p1[2]
.partial_cmp(&p2[2])
.unwrap_or(std::cmp::Ordering::Equal),
)
});
return SegmentIntersection3::Overlapping(Segment3 {
a: pts[1].clone(),
b: pts[2].clone(),
});
}
return SegmentIntersection3::None;
};
let s_clamped: T = if s < zero {
zero.clone()
} else if s > T::from(1) {
T::from(1)
} else {
s
};
let t_clamped = if t < zero {
zero.clone()
} else if t > T::from(1) {
T::from(1)
} else {
t
};
let closest_p = Point3::<T>::from_vals([
&p1[0] + &(&d1[0] * &s_clamped),
&p1[1] + &(&d1[1] * &s_clamped),
&p1[2] + &(&d1[2] * &s_clamped),
]);
let closest_q = Point3::<T>::from_vals([
&q1[0] + &(&d2[0] * &t_clamped),
&q1[1] + &(&d2[1] * &t_clamped),
&q1[2] + &(&d2[2] * &t_clamped),
]);
let dx = &closest_p[0] - &closest_q[0];
let dy = &closest_p[1] - &closest_q[1];
let dz = &closest_p[2] - &closest_q[2];
let dist_squared = &(&(&dx * &dx) + &(&dy * &dy)) + &(&dz * &dz);
if dist_squared <= zero {
SegmentIntersection3::Point(closest_p)
} else {
SegmentIntersection3::None
}
}