use crate::{Float, Point3};
use approx::{ulps_eq, AbsDiffEq};
pub const TOLERANCE: Float = 1.0e-7;
pub const TOLERANCE2: Float = TOLERANCE * TOLERANCE;
pub trait Tolerance: AbsDiffEq<Epsilon = Float> + Sized {
fn near(&self, other: Self) -> bool {
self.abs_diff_eq(&other, TOLERANCE)
}
fn near2(&self, other: Self) -> bool {
self.abs_diff_eq(&other, TOLERANCE2)
}
}
impl Tolerance for Float {}
pub fn inv_or_zero(delta: Float) -> Float {
if ulps_eq!(delta, 0.0) {
0.0
} else {
1.0 / delta
}
}
pub fn uniform_divide(range: (Float, Float), division: usize) -> Vec<Float> {
let (begin, end) = range;
let step = (end - begin) / division as Float;
let mut parameters = Vec::with_capacity(division + 1);
parameters.push(begin);
let mut u = begin;
for _ in 0..division - 1 {
u += step;
parameters.push(u);
}
parameters.push(end);
parameters
}
pub fn find_nearest_point(points: &[Point3], point: Point3) -> usize {
let mut min = Float::MAX;
let mut min_index = 0;
for (index, &vertex) in points.iter().enumerate() {
let distance = (point - vertex).length_squared();
if distance < min {
min = distance;
min_index = index;
}
}
min_index
}
pub fn distance_to_line_segment(a: Point3, b: Point3, p: Point3) -> Float {
let ap = p - a;
let ab = b - a;
let product = ap.dot(ab);
if product <= 0.0 {
return ap.length();
}
if product >= ab.length_squared() {
let bp = p - b;
return bp.length();
}
return ap.cross(ab).length() / ab.length();
}
use crate::curve::Curve;
pub fn find_nearest_parameter(
curve: &impl Curve,
der1: &impl Curve,
der2: &impl Curve,
point: Point3,
parameters: &[Float],
trials: usize,
) -> Float {
let mut u = parameters[find_nearest_point(
¶meters
.iter()
.map(|&u| curve.get_point(u))
.collect::<Vec<_>>(),
point,
)];
for _ in 0..trials {
let delta = curve.get_point(u) - point;
if delta.length_squared().near(0.0) {
return u;
}
let tangent = der1.get_point(u);
let f = tangent.dot(delta);
if f.near(0.0) {
return u;
}
let fprime = der2.get_point(u).dot(delta) + tangent.length_squared();
u -= f / fprime;
}
u
}