use core::f32::consts::PI;
pub const KINDLE_SECONDS: f32 = 0.6;
pub fn flicker_factor(t: f32, phase: f32) -> f32 {
let wobble = (7.3 * t + phase).sin() * 0.5 + (13.1 * t + 1.7 * phase).sin() * 0.5;
0.85 + 0.15 * wobble
}
pub fn kindle_gain(t: f32) -> f32 {
let s = (t / KINDLE_SECONDS).clamp(0.0, 1.0);
let s = s * s * (3.0 - 2.0 * s);
s + 0.35 * (PI * s).sin()
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn the_flicker_stays_inside_its_budget_window() {
for phase in 0..7 {
let phase = phase as f32;
let mut t = 0.0;
while t <= 100.0 {
let f = flicker_factor(t, phase);
assert!((0.70..=1.00).contains(&f), "flicker {f} at t {t} phase {phase} left 0.70..1.00");
t += 0.01;
}
}
}
#[test]
fn the_kindle_flares_then_settles_to_one() {
assert_eq!(kindle_gain(0.0), 0.0);
assert!((kindle_gain(0.3) - 0.85).abs() < 1e-4, "half time {:?}", kindle_gain(0.3));
assert!((kindle_gain(0.6) - 1.0).abs() < 1e-5, "settled {:?}", kindle_gain(0.6));
assert_eq!(
kindle_gain(10.0),
kindle_gain(KINDLE_SECONDS),
"the gain is exactly 1 after KINDLE_SECONDS"
);
}
}