use super::Vector2;
use super::base_math::min_max;
use crate::core::math::parametric_from_point;
use crate::core::traits::Real;
#[derive(Debug, Copy, Clone)]
pub enum LineCircleIntr<T>
where
T: Real,
{
NoIntersect,
TangentIntersect {
t0: T,
},
TwoIntersects {
t0: T,
t1: T,
},
}
pub fn line_circle_intr<T>(
p0: Vector2<T>,
p1: Vector2<T>,
radius: T,
circle_center: Vector2<T>,
epsilon: T,
) -> LineCircleIntr<T>
where
T: Real,
{
use LineCircleIntr::*;
let dx = p1.x - p0.x;
let dy = p1.y - p0.y;
let h = circle_center.x;
let k = circle_center.y;
let eps = epsilon;
if p0.fuzzy_eq_eps(p1, eps) {
let xh = (p0.x + p1.x) / T::two() - h;
let yk = (p0.y + p1.y) / T::two() - k;
if (xh * xh + yk * yk).fuzzy_eq_eps(radius * radius, eps) {
return TangentIntersect { t0: T::zero() };
}
return NoIntersect;
}
let p0_shifted = p0 - circle_center;
let p1_shifted = p1 - circle_center;
let (a, b, c) = if dx.fuzzy_eq_zero() {
let x_pos = (p1_shifted.x + p0_shifted.x) / T::two();
(T::one(), T::zero(), -x_pos)
} else {
let m = dy / dx;
(m, -T::one(), p1_shifted.y - m * p1_shifted.x)
};
let a2 = a * a;
let b2 = b * b;
let c2 = c * c;
let r2 = radius * radius;
let a2_b2 = a2 + b2;
let shortest_dist = c.abs() / (a2_b2).sqrt();
if shortest_dist > radius + eps {
return NoIntersect;
}
let x0 = -a * c / a2_b2 + h;
let y0 = -b * c / a2_b2 + k;
if shortest_dist.fuzzy_eq_eps(radius, eps) {
let t = parametric_from_point(p0, p1, Vector2::new(x0, y0), eps);
return TangentIntersect { t0: t };
}
let d = r2 - c2 / a2_b2;
let mult = (d / a2_b2).abs().sqrt();
let x_sol1 = x0 + b * mult;
let x_sol2 = x0 - b * mult;
let y_sol1 = y0 - a * mult;
let y_sol2 = y0 + a * mult;
let sol1 = parametric_from_point(p0, p1, Vector2::new(x_sol1, y_sol1), eps);
let sol2 = parametric_from_point(p0, p1, Vector2::new(x_sol2, y_sol2), eps);
let (t0, t1) = min_max(sol1, sol2);
TwoIntersects { t0, t1 }
}