use core::f32::consts::LOG2_E;
use core::ops::Add;
use core::ops::Mul;
use core::ops::RangeInclusive;
pub fn lerp<T>(range: RangeInclusive<T>, t: f32) -> T
where
f32: Mul<T, Output = T>,
T: Add<T, Output = T> + Copy,
{
(1.0 - t) * *range.start() + t * *range.end()
}
pub fn remap(x: f32, from: RangeInclusive<f32>, to: RangeInclusive<f32>) -> f32 {
let t = (x - from.start()) / (from.end() - from.start());
lerp(to, t)
}
#[inline]
pub fn remap_clamp(x: f32, from: RangeInclusive<f32>, to: RangeInclusive<f32>) -> f32 {
if x <= *from.start() {
*to.start()
} else if *from.end() <= x {
*to.end()
} else {
let t = (x - from.start()) / (from.end() - from.start());
if 1.0 <= t { *to.end() } else { lerp(to, t) }
}
}
#[inline(always)]
pub fn exp_decay(curr: f32, target: f32, decay_rate: f32, dt: f32) -> f32 {
target + (curr - target) * exp_fast(-decay_rate * dt)
}
#[inline(always)]
pub fn log2_fast(x: f32) -> f32 {
let vx = f32::to_bits(x);
let mx = f32::from_bits((vx & 0x007FFFFF_u32) | 0x3f000000);
let mut y = vx as f32;
y *= 1.192_092_9e-7_f32;
y - 124.225_52_f32 - 1.498_030_3_f32 * mx - 1.725_88_f32 / (0.352_088_72_f32 + mx)
}
#[inline(always)]
pub fn ln_fast(x: f32) -> f32 {
core::f32::consts::LN_2 * log2_fast(x)
}
#[inline(always)]
pub fn exp_fast(p: f32) -> f32 {
exp2_fast(p * LOG2_E)
}
#[inline(always)]
pub fn exp2_fast(p: f32) -> f32 {
let offset = if p < 0.0 { 1.0_f32 } else { 0.0_f32 };
let clipp = if p < -126.0 { -126.0_f32 } else { p };
let w = clipp as i32;
let z = clipp - (w as f32) + offset;
let v = ((1 << 23) as f32
* (clipp + 121.274055_f32 + 27.728024_f32 / (4.8425255_f32 - z) - 1.4901291_f32 * z))
as u32;
f32::from_bits(v)
}
#[inline(always)]
pub fn powf_fast(x: f32, p: f32) -> f32 {
exp2_fast(p * log2_fast(x))
}
#[allow(clippy::float_cmp)]
#[cfg(test)]
mod test {
use super::*;
#[test]
fn remapping() {
assert_eq!(remap(0.2, -1.0..=1.0, -2.0..=2.0), 0.4000001);
assert_eq!(remap_clamp(0.2, -1.0..=1.0, -2.0..=2.0), 0.4000001);
assert_eq!(remap(3.0, -1.0..=1.0, -2.0..=2.0), 6.0);
assert_eq!(remap_clamp(3.0, -1.0..=1.0, -2.0..=2.0), 2.0);
assert!(remap(0.0, 0.0..=0.0, -2.0..=2.0).is_nan());
assert_eq!(remap_clamp(0.0, 0.0..=0.0, -2.0..=2.0), -2.0);
}
}