#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ResponseCurve {
Linear,
#[cfg(feature = "std-math")]
Logarithmic,
}
impl ResponseCurve {
#[inline]
pub fn apply(&self, normalized: f32) -> f32 {
match self {
ResponseCurve::Linear => normalized,
#[cfg(feature = "std-math")]
ResponseCurve::Logarithmic => apply_logarithmic(normalized),
}
}
}
#[cfg(feature = "std-math")]
#[inline]
fn apply_logarithmic(normalized: f32) -> f32 {
const E3_MINUS_1: f32 = 19.085_537;
let x = normalized.clamp(0.0, 1.0);
let exp_3x = libm::expf(3.0 * x);
(exp_3x - 1.0) / E3_MINUS_1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_linear_curve() {
let curve = ResponseCurve::Linear;
assert_eq!(curve.apply(0.0), 0.0);
assert_eq!(curve.apply(0.25), 0.25);
assert_eq!(curve.apply(0.5), 0.5);
assert_eq!(curve.apply(0.75), 0.75);
assert_eq!(curve.apply(1.0), 1.0);
}
#[cfg(feature = "std-math")]
#[test]
fn test_logarithmic_curve() {
let curve = ResponseCurve::Logarithmic;
let result_0 = curve.apply(0.0);
let result_1 = curve.apply(1.0);
assert!(
(result_0 - 0.0).abs() < 0.001,
"Expected ~0.0, got {}",
result_0
);
assert!(
(result_1 - 1.0).abs() < 0.001,
"Expected ~1.0, got {}",
result_1
);
let result_quarter = curve.apply(0.25);
assert!(
result_quarter < 0.15,
"Expected <0.15, got {}",
result_quarter
);
let result_half = curve.apply(0.5);
assert!(result_half < 0.5, "Expected <0.5, got {}", result_half);
assert!(result_0 < result_quarter);
assert!(result_quarter < result_half);
assert!(result_half < result_1);
}
#[cfg(feature = "std-math")]
#[test]
fn test_logarithmic_curve_clamping() {
let curve = ResponseCurve::Logarithmic;
assert!((curve.apply(-0.1) - 0.0).abs() < 0.001);
assert!((curve.apply(1.1) - 1.0).abs() < 0.001);
}
#[test]
fn test_response_curve_copy() {
let curve1 = ResponseCurve::Linear;
let curve2 = curve1;
assert_eq!(curve1, curve2);
}
}