use std::ops::{Add, Div, Mul, Neg, Sub};
use crate::{
geometry::{
point::{Point, PointOps},
spatial_element::SpatialElement,
vector::{Vector, VectorOps},
},
numeric::scalar::Scalar,
};
pub const EPS: f64 = 1e-10;
#[inline(always)]
pub fn f64_next_up(x: f64) -> f64 {
if x.is_nan() || x == f64::INFINITY {
return x;
}
if x == -0.0 {
return f64::MIN_POSITIVE;
} let mut bits = x.to_bits();
if x >= 0.0 {
bits += 1;
} else {
bits -= 1;
}
f64::from_bits(bits)
}
#[inline(always)]
pub fn f64_next_down(x: f64) -> f64 {
if x.is_nan() || x == f64::NEG_INFINITY {
return x;
}
if x == 0.0 {
return -f64::MIN_POSITIVE;
} let mut bits = x.to_bits();
if x > 0.0 {
bits -= 1;
} else {
bits += 1;
}
f64::from_bits(bits)
}
pub fn triangle_area_2d<T: Scalar>(p0: &Point<T, 2>, p1: &Point<T, 2>, p2: &Point<T, 2>) -> T
where
for<'a> &'a T: Sub<&'a T, Output = T> + Mul<&'a T, Output = T>,
{
let v1 = [&p1[0] - &p0[0], &p1[1] - &p0[1]];
let v2 = [&p2[0] - &p0[0], &p2[1] - &p0[1]];
let cross = &v1[0] * &v2[1] - &v1[1] * &v2[0];
cross.abs() / T::from(2.0)
}
pub fn segment_intersect_2d<T: Scalar>(
a0: &Point<T, 2>,
a1: &Point<T, 2>,
b0: &Point<T, 2>,
b1: &Point<T, 2>,
) -> Option<(Point<T, 2>, Point<T, 2>)>
where
Point<T, 2>: SpatialElement<T, 2>,
for<'a> &'a T: Add<&'a T, Output = T>
+ Sub<&'a T, Output = T>
+ Div<&'a T, Output = T>
+ Mul<&'a T, Output = T>,
{
let da = [&a1[0] - &a0[0], &a1[1] - &a0[1]];
let db = [&b1[0] - &b0[0], &b1[1] - &b0[1]];
let denom = &da[0] * &db[1] - &da[1] * &db[0];
if denom.is_zero() {
let diff = [&b0[0] - &a0[0], &b0[1] - &a0[1]];
let cross = &diff[0] * &da[1] - &diff[1] * &da[0];
if cross.is_zero() {
let len2 = &da[0] * &da[0] + &da[1] * &da[1];
if !len2.is_positive() {
return None;
}
let t0 = &(&diff[0] * &da[0] + &diff[1] * &da[1]) / &len2;
let t1 = &(&(&b1[0] - &a0[0]) * &da[0] + &(&b1[1] - &a0[1]) * &da[1]) / &len2;
let (tmin, tmax) = if t0 < t1 { (t0, t1) } else { (t1, t0) };
let start = tmin.max(0.0.into());
let end = tmax.min(1.0.into());
if start <= end {
let p_start = Point::<T, 2>::from_vals([
&a0[0] + &(&da[0] * &start),
&a0[1] + &(&da[1] * &start),
]);
let p_end = Point::<T, 2>::from_vals([
&a0[0] + &(&da[0] * &end),
&a0[1] + &(&da[1] * &end),
]);
return Some((p_start, p_end));
}
}
return None;
}
let diff = [&b0[0] - &a0[0], &b0[1] - &a0[1]];
let s = &(&diff[0] * &db[1] - &diff[1] * &db[0]) / &denom;
let u = &(&diff[0] * &da[1] - &diff[1] * &da[0]) / &denom;
if s >= (-EPS).into()
&& s <= (1.0 + EPS).into()
&& u >= (-EPS).into()
&& u <= (1.0 + EPS).into()
{
let ix = &a0[0] + &(&s * &da[0]);
let iy = &a0[1] + &(&s * &da[1]);
return Some((
Point::<T, 2>::from_vals([ix.clone(), iy.clone()]),
Point::<T, 2>::from_vals([ix, iy]),
));
}
None
}
pub fn distance_point_triangle_squared<T: Scalar>(
p: &Point<T, 3>,
a: &Point<T, 3>,
b: &Point<T, 3>,
c: &Point<T, 3>,
) -> T
where
Point<T, 3>: PointOps<T, 3, Vector = Vector<T, 3>>,
Vector<T, 3>: VectorOps<T, 3, Cross = Vector<T, 3>>,
for<'a> &'a T: Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Add<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
let ab = Vector::<T, 3>::from_vals([&b[0] - &a[0], &b[1] - &a[1], &b[2] - &a[2]]);
let ac = Vector::<T, 3>::from_vals([&c[0] - &a[0], &c[1] - &a[1], &c[2] - &a[2]]);
let ap = Vector::<T, 3>::from_vals([&p[0] - &a[0], &p[1] - &a[1], &p[2] - &a[2]]);
let n = ab.cross(&ac);
let nn2 = n.dot(&n);
if nn2.is_zero() {
return distance_point_segment_squared(p, a, b)
.min(distance_point_segment_squared(p, b, c))
.min(distance_point_segment_squared(p, c, a));
}
let d1 = &ab[0] * &ap[0] + &ab[1] * &ap[1] + &ab[2] * &ap[2];
let d2 = &ac[0] * &ap[0] + &ac[1] * &ap[1] + &ac[2] * &ap[2];
if d1.is_negative_or_zero() && d2.is_negative_or_zero() {
return &ap[0] * &ap[0] + &ap[1] * &ap[1] + &ap[2] * &ap[2];
}
let bp = Point::<T, 3>::from_vals([&p[0] - &b[0], &p[1] - &b[1], &p[2] - &b[2]]);
let d3 = &ab[0] * &bp[0] + &ab[1] * &bp[1] + &ab[2] * &bp[2];
let d4 = &ac[0] * &bp[0] + &ac[1] * &bp[1] + &ac[2] * &bp[2];
if d3.is_positive_or_zero() && d4 <= d3 {
return &bp[0] * &bp[0] + &bp[1] * &bp[1] + &bp[2] * &bp[2];
}
let vc = &d1 * &d4 - &d3 * &d2;
if vc.is_negative_or_zero() && d1.is_positive_or_zero() && d3.is_negative_or_zero() {
let v = &d1 / &(&d1 - &d3);
let proj = Point::<T, 3>::from_vals([
&a[0] + &(&v * &ab[0]),
&a[1] + &(&v * &ab[1]),
&a[2] + &(&v * &ab[2]),
]);
let diff = Point::<T, 3>::from_vals([&p[0] - &proj[0], &p[1] - &proj[1], &p[2] - &proj[2]]);
return &diff[0] * &diff[0] + &diff[1] * &diff[1] + &diff[2] * &diff[2];
}
let cp = Point::<T, 3>::from_vals([&p[0] - &c[0], &p[1] - &c[1], &p[2] - &c[2]]);
let d5 = &ab[0] * &cp[0] + &ab[1] * &cp[1] + &ab[2] * &cp[2];
let d6 = &ac[0] * &cp[0] + &ac[1] * &cp[1] + &ac[2] * &cp[2];
if d6.is_positive_or_zero() && d5 <= d6 {
let w = &d6 / &(&d6 - &d2);
let proj = Point::<T, 3>::from_vals([
&a[0] + &(&w * &ac[0]),
&a[1] + &(&w * &ac[1]),
&a[2] + &(&w * &ac[2]),
]);
let diff = Point::<T, 3>::from_vals([&p[0] - &proj[0], &p[1] - &proj[1], &p[2] - &proj[2]]);
return &diff[0] * &diff[0] + &diff[1] * &diff[1] + &diff[2] * &diff[2];
}
let vb = &d5 * &d2 - &d1 * &d6;
if vb.is_negative_or_zero()
&& (&d4 - &d3).is_positive_or_zero()
&& (&d5 - &d6).is_positive_or_zero()
{
let t = (&d4 - &d3) / ((&d4 - &d3) + (&d5 - &d6));
let proj = Point::<T, 3>::from_vals([
&b[0] + &(&(&c[0] - &b[0]) * &t),
&b[1] + &(&(&c[1] - &b[1]) * &t),
&b[2] + &(&(&c[2] - &b[2]) * &t),
]);
let diff = Point::<T, 3>::from_vals([&p[0] - &proj[0], &p[1] - &proj[1], &p[2] - &proj[2]]);
return &diff[0] * &diff[0] + &diff[1] * &diff[1] + &diff[2] * &diff[2];
}
let t_plane = &ap.dot(&n) / &nn2;
let proj = Point::from(&p.clone().as_vector() - &n.scale(&t_plane));
let v0 = ac;
let v1 = ab;
let v2 = &proj - &a;
let dot00 = v0.dot(&v0);
let dot01 = v0.dot(&v1);
let dot11 = v1.dot(&v1);
let dot02 = v0.dot(&v2.as_vector());
let dot12 = v1.dot(&v2.as_vector());
let inv_denom = T::one() / (&dot00 * &dot11 - &dot01 * &dot01);
let u = &(&dot11 * &dot02 - &dot01 * &dot12) * &inv_denom;
let v = &(&dot00 * &dot12 - &dot01 * &dot02) * &inv_denom;
if u >= T::zero() && v >= T::zero() && u + v <= T::one() {
let d_plane = ap.dot(&n);
return &d_plane * &d_plane / nn2;
}
distance_point_segment_squared(p, a, b)
.min(distance_point_segment_squared(p, b, c))
.min(distance_point_segment_squared(p, c, a))
}
pub fn distance_point_segment_squared<T: Scalar>(
p: &Point<T, 3>,
a: &Point<T, 3>,
b: &Point<T, 3>,
) -> T
where
Point<T, 3>: PointOps<T, 3, Vector = Vector<T, 3>>,
Vector<T, 3>: VectorOps<T, 3>,
for<'a> &'a T: Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Add<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
let ab = b - a;
let mut t = (p - a).as_vector().dot(&ab.as_vector()) / ab.as_vector().dot(&ab.as_vector());
if t.is_negative() {
t = T::zero();
} else if t > T::one() {
t = T::one();
}
let ab_by_t = ab.as_vector().scale(&t);
let proj = Point::from(&a.as_vector() + &ab_by_t);
(p - &proj).as_vector().dot(&(p - &proj).as_vector())
}
pub fn barycentric_coords<T: Scalar, const N: usize>(
p: &Point<T, N>,
a: &Point<T, N>,
b: &Point<T, N>,
c: &Point<T, N>,
) -> Option<(T, T, T)>
where
Point<T, N>: PointOps<T, N, Vector = Vector<T, N>>,
Vector<T, N>: VectorOps<T, N>,
for<'a> &'a T: Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Add<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
let v0 = (b - a).as_vector();
let v1 = (c - a).as_vector();
let v2 = (p - a).as_vector();
let d00 = v0.dot(&v0);
let d01 = v0.dot(&v1);
let d11 = v1.dot(&v1);
let d20 = v2.dot(&v0);
let d21 = v2.dot(&v1);
let denom = &d00 * &d11 - &d01 * &d01;
if denom.is_zero() {
return None; }
let v = (&d11 * &d20 - &d01 * &d21) / denom.clone(); let w = (&d00 * &d21 - &d01 * &d20) / denom; let u = &(&T::one() - &v) - &w;
Some((u, v, w))
}
#[inline]
pub fn proj_along<T: Scalar, const N: usize>(
v: &Vector<T, N>,
d: &Vector<T, N>,
eps: &T,
) -> Vector<T, N>
where
Point<T, N>: PointOps<T, N, Vector = Vector<T, N>>,
Vector<T, N>: VectorOps<T, N, Cross = Vector<T, N>>,
for<'a> &'a T: Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Add<&'a T, Output = T>
+ Div<&'a T, Output = T>
+ Neg<Output = T>,
{
let dd = d.dot(&d);
if &dd <= &eps {
return Vector::default();
}
d.scale(&(v.dot(&d) / dd))
}
#[inline]
pub fn point_from_segment_and_u<T: Scalar, const N: usize>(
a: &Point<T, N>,
b: &Point<T, N>,
u: &T,
) -> Point<T, N>
where
Vector<T, N>: VectorOps<T, N>,
Point<T, N>: PointOps<T, N, Vector = Vector<T, N>>,
for<'a> &'a T: Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Add<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
let ab = (b - a).as_vector();
a + &ab.scale(&u).0
}