use std::f32::consts;
#[cfg(all(target_arch = "x86_64", any(feature = "simd", feature = "simd-x86")))]
use std::arch::x86_64::*;
#[cfg(all(target_arch = "aarch64", any(feature = "simd", feature = "simd-arm")))]
use std::arch::aarch64::*;
pub const PI: f32 = consts::PI;
pub const TAU: f32 = consts::TAU;
pub const FRAC_PI_2: f32 = consts::FRAC_PI_2;
pub const FRAC_PI_3: f32 = consts::FRAC_PI_3;
pub const FRAC_PI_4: f32 = consts::FRAC_PI_4;
pub const FRAC_PI_6: f32 = consts::FRAC_PI_6;
pub const FRAC_2_PI: f32 = consts::FRAC_2_PI;
pub const SQRT_2: f32 = consts::SQRT_2;
pub const E: f32 = consts::E;
#[inline]
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t
}
#[inline]
pub fn clamp(value: f32, min: f32, max: f32) -> f32 {
if value < min {
min
} else if value > max {
max
} else {
value
}
}
#[inline]
pub fn clamp01(value: f32) -> f32 {
clamp(value, 0.0, 1.0)
}
#[inline]
pub fn smoothstep(edge0: f32, edge1: f32, x: f32) -> f32 {
let t = clamp01((x - edge0) / (edge1 - edge0));
t * t * (3.0 - 2.0 * t)
}
pub fn degrees_to_radians(degrees: f32) -> f32 {
degrees * PI / 180.0
}
pub fn radians_to_degrees(radians: f32) -> f32 {
radians * 180.0 / PI
}
#[inline]
pub fn min(a: f32, b: f32) -> f32 {
if a < b { a } else { b }
}
#[inline]
pub fn max(a: f32, b: f32) -> f32 {
if a > b { a } else { b }
}
#[inline]
pub fn f32_rsqrt(x: f32) -> f32 {
#[cfg(all(target_arch = "x86_64", any(feature = "simd", feature = "simd-x86")))]
{
unsafe {
let x_simd = _mm_set_ss(x);
let rsqrt_approx = _mm_rsqrt_ss(x_simd);
let half = _mm_set_ss(0.5);
let three = _mm_set_ss(3.0);
let refined = _mm_mul_ss(
rsqrt_approx,
_mm_sub_ss(
three,
_mm_mul_ps(
_mm_mul_ss(half, x_simd),
_mm_mul_ss(rsqrt_approx, rsqrt_approx),
),
),
);
_mm_cvtss_f32(refined)
}
}
#[cfg(all(target_arch = "aarch64", any(feature = "simd", feature = "simd-arm")))]
{
unsafe {
let x_simd = vdupq_n_f32(x);
let rsqrt_approx = vrsqrteq_f32(x_simd);
let muls = vmulq_f32(rsqrt_approx, rsqrt_approx);
let rsqrt = vmulq_f32(rsqrt_approx, vrsqrtsq_f32(x_simd, muls));
let muls2 = vmulq_f32(rsqrt, rsqrt);
let rsqrt = vmulq_f32(rsqrt, vrsqrtsq_f32(x_simd, muls2));
vgetq_lane_f32(rsqrt, 0)
}
}
#[cfg(not(any(
all(target_arch = "x86_64", any(feature = "simd", feature = "simd-x86")),
all(target_arch = "aarch64", any(feature = "simd", feature = "simd-arm"))
)))]
{
x.sqrt().recip()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lerp() {
assert!((lerp(0.0, 10.0, 0.5) - 5.0).abs() < 0.0001);
}
#[test]
fn test_clamp() {
assert_eq!(clamp(5.0, 0.0, 10.0), 5.0);
assert_eq!(clamp(-5.0, 0.0, 10.0), 0.0);
assert_eq!(clamp(15.0, 0.0, 10.0), 10.0);
}
#[test]
fn test_clamp01() {
assert_eq!(clamp01(0.5), 0.5);
assert_eq!(clamp01(-1.0), 0.0);
assert_eq!(clamp01(2.0), 1.0);
}
#[test]
fn test_min_max() {
assert_eq!(min(5.0, 10.0), 5.0);
assert_eq!(max(5.0, 10.0), 10.0);
}
}