use crate::meshanalyzer::SearchResult;
use crate::prelude::MaximumTracker;
use crate::probe::EdgeAndCenterType;
use crate::probe::ball_end::BallEndProperties;
use vector_traits::num_traits::real::Real;
use vector_traits::prelude::{GenericScalar, GenericVector2, GenericVector3};
pub(crate) fn ball_end_to_edge_collision<T: GenericVector3>(
edge: EdgeAndCenterType<T>,
ball_end: BallEndProperties<T>,
site_index: u32,
mt: &mut MaximumTracker<SearchResult<T>>,
) {
let p0 = edge.p0;
let p1 = edge.p1;
let dp = p1 - p0;
if dp.x().abs() < T::Scalar::EPSILON && dp.y().abs() < T::Scalar::EPSILON {
z_sphere_distance_to_single_point::<T>(
site_index,
p1,
ball_end.r_sq,
(edge.center - p1.to_2d()).magnitude_sq(),
ball_end.r,
mt,
);
return;
}
let dp_mag_sq = dp.magnitude_sq();
let dp_xy = dp.to_2d();
let dep_xy_mag_sq = dp_xy.magnitude_sq();
let t = (edge.center - p0.to_2d()).dot(dp_xy) / dep_xy_mag_sq;
let closest_point_xy = p0.to_2d() + dp_xy * t;
let dist_sq_closest_point_xy = (edge.center - closest_point_xy).magnitude_sq();
let radius_factor_sq = T::Scalar::ONE - dist_sq_closest_point_xy / ball_end.r_sq;
if radius_factor_sq.is_sign_negative() {
return;
}
let (m_factor_sq, t_offset) = if dp.z() > T::Scalar::EPSILON {
let m_sq: T::Scalar = dp.z().powi(2) / (dp.x().powi(2) + dp.y().powi(2));
let m_factor_sq = T::Scalar::ONE + m_sq;
let d_rq_sq = ball_end.r_sq * (m_sq.powi(2) + m_sq) / (m_sq + T::Scalar::ONE);
(
m_factor_sq,
-(radius_factor_sq * d_rq_sq / dp_mag_sq).sqrt(),
)
} else {
(T::Scalar::ONE, T::Scalar::ZERO)
};
if t > T::Scalar::ONE + t_offset {
z_sphere_distance_to_single_point::<T>(
site_index,
p1,
ball_end.r_sq,
(edge.center - p1.to_2d()).magnitude_sq(),
ball_end.r,
mt,
);
return;
}
if t < t_offset {
z_sphere_distance_to_single_point::<T>(
site_index,
p0,
ball_end.r_sq,
(edge.center - p0.to_2d()).magnitude_sq(),
ball_end.r,
mt,
);
return;
}
mt.insert(SearchResult::new(
site_index,
p0.z() + t * dp.z() + ball_end.r * (m_factor_sq * radius_factor_sq).sqrt() - ball_end.r,
))
}
#[inline(always)]
fn z_sphere_distance_to_single_point<T: GenericVector3>(
site_index: u32,
p: T,
r_sq: T::Scalar,
dist_sq: T::Scalar,
probe_radius: T::Scalar,
mt: &mut MaximumTracker<SearchResult<T>>,
) {
let dist_sq_diff = r_sq - dist_sq;
if dist_sq_diff.is_sign_positive() {
mt.insert(SearchResult::new(
site_index,
p.z() + dist_sq_diff.sqrt() - probe_radius,
));
}
}