const ELLIPK_A: [f64; 5] = [
1.38629436112,
0.09666344259,
0.03590092393,
0.03742563713,
0.01451196212,
];
const ELLIPK_B: [f64; 5] = [
0.5,
0.12498593597,
0.06880248576,
0.03328355346,
0.00441787012,
];
const ELLIPE_A: [f64; 5] = [
1.0,
0.44325141463,
0.06260601220,
0.04757383546,
0.01736506451,
];
const ELLIPE_B: [f64; 5] = [
0.0,
0.24998368310,
0.09200180037,
0.04069697526,
0.00526449639,
];
#[inline]
pub fn ellipk(m: f64) -> f64 {
let mut ellip: f64 = 0.0;
let c: f64 = 1.0 - m;
let logterm = c.powi(-1).ln();
for i in 0..5 {
ellip = logterm
.mul_add(ELLIPK_B[i], ELLIPK_A[i])
.mul_add(c.powi(i as i32), ellip);
}
ellip
}
#[inline]
pub fn ellipe(m: f64) -> f64 {
let mut ellip: f64 = 0.0;
let c: f64 = 1.0 - m;
let logterm = c.powi(-1).ln();
for i in 0..5 {
ellip = logterm
.mul_add(ELLIPE_B[i], ELLIPE_A[i])
.mul_add(c.powi(i as i32), ellip);
}
ellip
}
#[inline]
pub fn rss3(x: f64, y: f64, z: f64) -> f64 {
x.mul_add(x, y.mul_add(y, z.powi(2))).sqrt()
}
#[inline]
pub fn cross3(x0: f64, y0: f64, z0: f64, x1: f64, y1: f64, z1: f64) -> (f64, f64, f64) {
let xy = -x1 * y0;
let yz = -y1 * z0;
let zx = -z1 * x0;
let cx = y0.mul_add(z1, yz);
let cy = z0.mul_add(x1, zx);
let cz = x0.mul_add(y1, xy);
(cx, cy, cz)
}
#[inline]
pub fn cross3f(x0: f32, y0: f32, z0: f32, x1: f32, y1: f32, z1: f32) -> (f32, f32, f32) {
let xy = -x1 * y0;
let yz = -y1 * z0;
let zx = -z1 * x0;
let cx = y0.mul_add(z1, yz);
let cy = z0.mul_add(x1, zx);
let cz = x0.mul_add(y1, xy);
(cx, cy, cz)
}
#[inline]
pub fn dot3(x0: f64, y0: f64, z0: f64, x1: f64, y1: f64, z1: f64) -> f64 {
x0.mul_add(x1, y0.mul_add(y1, z0 * z1))
}
#[inline]
pub fn dot3f(x0: f32, y0: f32, z0: f32, x1: f32, y1: f32, z1: f32) -> f32 {
x0.mul_add(x1, y0.mul_add(y1, z0 * z1))
}
#[inline]
pub fn cartesian_to_cylindrical(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
let r = rss3(x, y, 0.0);
let phi = libm::atan2(y, x);
(r, phi, z)
}
#[inline]
pub fn cylindrical_to_cartesian(r: f64, phi: f64, z: f64) -> (f64, f64, f64) {
let x = r * libm::cos(phi);
let y = r * libm::sin(phi);
(x, y, z)
}
#[inline]
pub(crate) fn decompose_filament(
start: (f64, f64, f64),
end: (f64, f64, f64),
) -> ((f64, f64, f64), (f64, f64, f64)) {
let dl = (end.0 - start.0, end.1 - start.1, end.2 - start.2); let midpoint = (
dl.0.mul_add(0.5, start.0),
dl.1.mul_add(0.5, start.1),
dl.2.mul_add(0.5, start.2),
);
(midpoint, dl)
}
pub(crate) struct PointLineDistance {
pub(crate) perp: f64,
pub(crate) perp_hat: (f64, f64, f64),
pub(crate) dist_a: f64,
pub(crate) dist_b: f64,
pub(crate) frac: f64,
pub(crate) para_a: f64,
pub(crate) para_b: f64,
pub(crate) ab_norm: (f64, f64, f64),
}
#[inline]
pub(crate) fn point_line_distance_with_endpoints(
a: (f64, f64, f64),
b: (f64, f64, f64),
p: (f64, f64, f64),
r_min: f64,
) -> PointLineDistance {
let ab = (b.0 - a.0, b.1 - a.1, b.2 - a.2);
let ap = (p.0 - a.0, p.1 - a.1, p.2 - a.2);
let bp = (p.0 - b.0, p.1 - b.1, p.2 - b.2);
let ab2 = dot3(ab.0, ab.1, ab.2, ab.0, ab.1, ab.2); if ab2 == 0.0 {
let r_min = r_min.max(0.0);
let r_min_frac = r_min.max(f64::MIN_POSITIVE);
let dist_a = rss3(ap.0, ap.1, ap.2);
let dist_b = rss3(bp.0, bp.1, bp.2);
let frac = (dist_a / r_min_frac).min(1.0);
let dist_a = dist_a.max(r_min);
let dist_b = dist_b.max(r_min);
let perp = dist_a;
return PointLineDistance {
perp,
perp_hat: (0.0, 0.0, 0.0),
dist_a,
dist_b,
frac,
para_a: 0.0,
para_b: 0.0,
ab_norm: (0.0, 0.0, 0.0),
};
}
let ab_len = ab2.sqrt();
let ab_len_inv = ab_len.recip();
let ab_norm = (ab.0 * ab_len_inv, ab.1 * ab_len_inv, ab.2 * ab_len_inv);
let t = dot3(ap.0, ap.1, ap.2, ab.0, ab.1, ab.2) / ab2; let closest = (
t.mul_add(ab.0, a.0),
t.mul_add(ab.1, a.1),
t.mul_add(ab.2, a.2),
); let dp = (p.0 - closest.0, p.1 - closest.1, p.2 - closest.2); let perp_raw = rss3(dp.0, dp.1, dp.2); let perp_hat = if perp_raw > 0.0 {
let inv = perp_raw.recip();
(dp.0 * inv, dp.1 * inv, dp.2 * inv)
} else {
(0.0, 0.0, 0.0)
};
let r_min = r_min.max(f64::MIN_POSITIVE);
let para_a = dot3(ap.0, ap.1, ap.2, ab_norm.0, ab_norm.1, ab_norm.2);
let para_b = para_a - ab_len;
let frac = (perp_raw / r_min).min(1.0);
let perp = perp_raw.max(r_min);
let dist_a = perp.mul_add(perp, para_a * para_a).sqrt();
let dist_b = perp.mul_add(perp, para_b * para_b).sqrt();
PointLineDistance {
perp,
perp_hat,
dist_a,
dist_b,
frac,
para_a,
para_b,
ab_norm,
}
}
#[inline]
pub fn clip_nan(x: f64, v: f64) -> f64 {
if x.is_nan() { v } else { x }
}
#[inline(always)] pub fn switch_float(left: f64, right: f64, cond: bool) -> f64 {
let left_factor: f64 = match cond {
true => 1.0, false => 0.0,
};
let right_factor: f64 = 1.0 - left_factor;
let left_part = left * left_factor;
let right_part = right * right_factor;
clip_nan(left_part, 0.0) + clip_nan(right_part, 0.0)
}