use geo::LineIntersection;
use nalgebra::{Point2, Vector2};
use num_traits::NumCast;
use crate::{curve::NurbsCurve2D, misc::FloatingPoint};
pub fn to_line_helper<T: FloatingPoint>(p0: &Point2<T>, p1: &Point2<T>) -> geo::Line {
let x0 = <f64 as NumCast>::from(p0.x).unwrap();
let y0 = <f64 as NumCast>::from(p0.y).unwrap();
let x1 = <f64 as NumCast>::from(p1.x).unwrap();
let y1 = <f64 as NumCast>::from(p1.y).unwrap();
geo::Line::new(geo::coord! { x: x0, y: y0 }, geo::coord! { x: x1, y: y1 })
}
pub fn sharp_corner_intersection<T: FloatingPoint>(
vertices: [&Point2<T>; 4],
delta: T,
) -> anyhow::Result<Point2<T>> {
let v0 = vertices[0];
let v1 = vertices[1];
let v2 = vertices[2];
let v3 = vertices[3];
let d0 = (v1 - v0).normalize() * delta;
let l0 = to_line_helper(v0, &(v1 + d0));
let d1 = (v3 - v2).normalize() * delta;
let l1 = to_line_helper(&(v2 - d1), v3);
let it = geo::algorithm::line_intersection::line_intersection(l0, l1);
let it = it
.and_then(|it| match it {
LineIntersection::SinglePoint {
intersection: p,
is_proper: _,
} => Some(p),
_ => None,
})
.ok_or(anyhow::anyhow!("no intersection"))?;
Ok(Point2::new(
T::from_f64(it.x).unwrap(),
T::from_f64(it.y).unwrap(),
))
}
pub fn round_corner<T: FloatingPoint>(
v0: &Point2<T>,
v1: &Point2<T>,
v2: &Point2<T>,
distance: T,
) -> anyhow::Result<NurbsCurve2D<T>> {
let t = (v1 - v0).normalize();
let sign = distance.signum();
let n = Vector2::new(t.y, -t.x);
let d = n * distance;
let center = v1 - d;
let d0 = v1 - center;
let d1 = v2 - center;
let angle = d0.angle(&d1);
let angle = angle.abs();
NurbsCurve2D::try_arc(¢er, &(n * sign), &t, distance.abs(), T::zero(), angle)
}
pub fn smooth_corner<T: FloatingPoint>(
v0: &Point2<T>,
v1: &Point2<T>,
v2: &Point2<T>,
v3: &Point2<T>,
distance: T,
) -> anyhow::Result<NurbsCurve2D<T>> {
let frac_2_3 = T::from_f64(2.0 / 3.0).unwrap();
let d = distance.abs() * frac_2_3;
let d10 = if v1 == v0 {
Vector2::zeros()
} else {
(v1 - v0).normalize() * d
};
let d32 = if v3 == v2 {
Vector2::zeros()
} else {
(v3 - v2).normalize() * d
};
let bezier = NurbsCurve2D::bezier(&[*v1, v1 + d10, v2 - d32, *v2]);
Ok(bezier)
}