rust_synth/math/
sigmoid.rs1#[inline]
8pub fn sigmoid(t: f64, k: f64, x0: f64) -> f64 {
9 1.0 / (1.0 + (-k * (t - x0)).exp())
10}
11
12#[inline]
14pub fn smoothstep(t: f64, a: f64, b: f64) -> f64 {
15 let x = ((t - a) / (b - a)).clamp(0.0, 1.0);
16 x * x * (3.0 - 2.0 * x)
17}
18
19#[inline]
21pub fn ease_in_out(t: f64) -> f64 {
22 let x = t.clamp(0.0, 1.0);
23 if x < 0.5 {
24 4.0 * x * x * x
25 } else {
26 let f = -2.0 * x + 2.0;
27 1.0 - f * f * f / 2.0
28 }
29}
30
31#[inline]
33pub fn softexp(t: f64, rate: f64) -> f64 {
34 let x = t.clamp(0.0, 1.0);
35 if rate.abs() < 1e-6 {
36 x
37 } else {
38 (rate * x).exp_m1() / rate.exp_m1()
39 }
40}
41
42#[inline]
43pub fn lerp(a: f64, b: f64, t: f64) -> f64 {
44 a + (b - a) * t
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use approx::assert_relative_eq;
51
52 #[test]
53 fn sigmoid_midpoint_is_half() {
54 assert_relative_eq!(sigmoid(5.0, 1.0, 5.0), 0.5, epsilon = 1e-12);
55 }
56
57 #[test]
58 fn smoothstep_endpoints() {
59 assert_eq!(smoothstep(-1.0, 0.0, 1.0), 0.0);
60 assert_eq!(smoothstep(2.0, 0.0, 1.0), 1.0);
61 }
62
63 #[test]
64 fn ease_is_monotone() {
65 let samples: Vec<f64> = (0..=100).map(|i| ease_in_out(i as f64 / 100.0)).collect();
66 for w in samples.windows(2) {
67 assert!(w[1] >= w[0] - 1e-12);
68 }
69 }
70}