pub fn rescale(cos: f32, floor: f32) -> f32 {
if matches!(floor.partial_cmp(&1.0), Some(std::cmp::Ordering::Less)) {
((cos - floor) / (1.0 - floor)).clamp(0.0, 1.0)
} else {
cos.clamp(0.0, 1.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn maps_the_floor_to_zero_and_one_to_one() {
assert!((rescale(0.70, 0.70) - 0.0).abs() < 1e-6);
assert!((rescale(1.00, 0.70) - 1.0).abs() < 1e-6);
}
#[test]
fn puts_two_backends_on_the_same_scale() {
let bge = rescale(0.725, 0.45); let granite = rescale(0.850, 0.70); assert!((bge - granite).abs() < 1e-5, "{bge} vs {granite}");
assert!((bge - 0.5).abs() < 1e-5);
}
#[test]
fn clamps_below_the_floor() {
assert_eq!(rescale(0.10, 0.70), 0.0);
assert_eq!(rescale(-0.50, 0.70), 0.0);
}
#[test]
fn a_zero_floor_is_the_identity() {
for c in [0.0f32, 0.25, 0.5, 0.75, 1.0] {
assert!((rescale(c, 0.0) - c).abs() < 1e-6, "{c}");
}
}
#[test]
fn a_degenerate_floor_does_not_divide_by_zero() {
assert!(rescale(0.8, 1.0).is_finite());
assert!(rescale(0.8, 1.5).is_finite());
assert!(rescale(0.8, f32::NAN).is_finite());
}
#[test]
fn rescaling_preserves_ordering() {
let raw = [0.60f32, 0.72, 0.85, 0.99];
let scaled: Vec<f32> = raw.iter().map(|c| rescale(*c, 0.55)).collect();
for w in scaled.windows(2) {
assert!(w[0] < w[1], "ordering changed: {scaled:?}");
}
}
}