use core::ops::{Add, Div, Mul, Sub};
use num_traits::{Float, FromPrimitive};
pub trait Scalar:
Float
+ FromPrimitive
+ Copy
+ Clone
+ Default
+ std::fmt::Debug
+ PartialOrd
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
+ Send
+ Sync
+ 'static
{
const ZERO: Self;
const ONE: Self;
fn min_positive() -> Self;
fn to_f64(self) -> f64;
}
#[inline]
pub(crate) fn cast<T: Scalar>(value: f64) -> T {
T::from_f64(value).expect("finite f64 literal should cast to target scalar")
}
impl Scalar for f32 {
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
#[inline]
fn min_positive() -> Self {
f32::MIN_POSITIVE
}
#[inline]
fn to_f64(self) -> f64 {
self as f64
}
}
impl Scalar for f64 {
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
#[inline]
fn min_positive() -> Self {
f64::MIN_POSITIVE
}
#[inline]
fn to_f64(self) -> f64 {
self
}
}
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 norm3<T: Scalar>(v: [T; 3]) -> T {
dot3(v, v).sqrt()
}
#[inline]
pub fn normalize3<T: Scalar>(v: [T; 3]) -> [T; 3] {
scale3(v, T::ONE / norm3(v))
}
#[inline]
pub fn cross3<T: Scalar>(a: [T; 3], b: [T; 3]) -> [T; 3] {
[
a[1].mul_add(b[2], (T::ZERO - b[1]) * a[2]),
a[2].mul_add(b[0], (T::ZERO - b[2]) * a[0]),
a[0].mul_add(b[1], (T::ZERO - b[0]) * a[1]),
]
}
#[inline]
pub fn dot3<T: Scalar>(a: [T; 3], b: [T; 3]) -> T {
a[0].mul_add(b[0], a[1].mul_add(b[1], a[2] * b[2]))
}
#[inline]
pub fn sub3<T: Scalar>(a: [T; 3], b: [T; 3]) -> [T; 3] {
[a[0] - b[0], a[1] - b[1], a[2] - b[2]]
}
#[inline]
pub fn add3<T: Scalar>(a: [T; 3], b: [T; 3]) -> [T; 3] {
[a[0] + b[0], a[1] + b[1], a[2] + b[2]]
}
#[inline]
pub fn add3_in_place<T: Scalar>(out: &mut [T; 3], value: [T; 3]) {
for axis in 0..3 {
out[axis] = out[axis] + value[axis];
}
}
#[inline]
pub fn scale3<T: Scalar>(value: [T; 3], scale: T) -> [T; 3] {
[value[0] * scale, value[1] * scale, value[2] * scale]
}
#[inline]
pub fn add_scaled3<T: Scalar>(a: [T; 3], b: [T; 3], scale: T) -> [T; 3] {
[
scale.mul_add(b[0], a[0]),
scale.mul_add(b[1], a[1]),
scale.mul_add(b[2], a[2]),
]
}
#[inline]
pub fn cartesian_to_cylindrical(point: [f64; 3]) -> [f64; 3] {
let [x, y, z] = point;
let r = norm3([x, y, 0.0]);
let phi = libm::atan2(y, x);
[r, phi, z]
}
#[inline]
pub fn cylindrical_to_cartesian(point: [f64; 3]) -> [f64; 3] {
let [r, phi, z] = point;
let x = r * libm::cos(phi);
let y = r * libm::sin(phi);
[x, y, z]
}
pub(crate) struct PointLineDistance<T: Scalar> {
pub(crate) perp: T,
pub(crate) perp_hat: [T; 3],
pub(crate) dist_a: T,
pub(crate) dist_b: T,
pub(crate) frac: T,
pub(crate) para_a: T,
pub(crate) para_b: T,
pub(crate) ab_norm: [T; 3],
}
#[inline]
pub(crate) fn point_line_distance_with_endpoints<T: Scalar>(
a: [T; 3],
b: [T; 3],
p: [T; 3],
r_min: T,
) -> PointLineDistance<T> {
let ab = sub3(b, a);
let ap = sub3(p, a);
let bp = sub3(p, b);
let ab2 = dot3(ab, ab); if ab2 == T::ZERO {
let r_min = max_scalar(r_min, T::ZERO);
let r_min_frac = max_scalar(r_min, T::min_positive());
let dist_a = norm3(ap);
let dist_b = norm3(bp);
let frac = min_scalar(dist_a / r_min_frac, T::ONE);
let dist_a = max_scalar(dist_a, r_min);
let dist_b = max_scalar(dist_b, r_min);
let perp = dist_a;
return PointLineDistance {
perp,
perp_hat: [T::ZERO; 3],
dist_a,
dist_b,
frac,
para_a: T::ZERO,
para_b: T::ZERO,
ab_norm: [T::ZERO; 3],
};
}
let ab_len = ab2.sqrt();
let ab_len_inv = T::ONE / ab_len;
let ab_norm = scale3(ab, ab_len_inv);
let t = dot3(ap, ab) / ab2; let closest = add_scaled3(a, ab, t); let dp = sub3(p, closest); let perp_raw = norm3(dp); let perp_hat = if perp_raw > T::ZERO {
let inv = T::ONE / perp_raw;
scale3(dp, inv)
} else {
[T::ZERO; 3]
};
let r_min = max_scalar(r_min, T::min_positive());
let para_a = dot3(ap, ab_norm);
let para_b = para_a - ab_len;
let frac = min_scalar(perp_raw / r_min, T::ONE);
let perp = max_scalar(perp_raw, 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]
fn min_scalar<T: Scalar>(a: T, b: T) -> T {
if a < b { a } else { b }
}
#[inline]
pub(crate) fn max_scalar<T: Scalar>(a: T, b: T) -> T {
if a > b { a } else { b }
}
#[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)
}