use mcu_utils::lerp;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ContrastCurve {
pub low: f64,
pub normal: f64,
pub medium: f64,
pub high: f64,
}
impl ContrastCurve {
pub fn new(low: f64, normal: f64, medium: f64, high: f64) -> Self {
ContrastCurve {
low,
normal,
medium,
high,
}
}
pub fn get(&self, contrast_level: f64) -> f64 {
if contrast_level <= -1.0 {
self.low
} else if contrast_level < 0.0 {
let t = (contrast_level - (-1.0)) / 1.0;
lerp(self.low, self.normal, t)
} else if contrast_level < 0.5 {
let t = (contrast_level - 0.0) / 0.5;
lerp(self.normal, self.medium, t)
} else if contrast_level < 1.0 {
let t = (contrast_level - 0.5) / 0.5;
lerp(self.medium, self.high, t)
} else {
self.high
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
assert_eq!(curve.low, 1.0);
assert_eq!(curve.normal, 4.5);
assert_eq!(curve.medium, 7.0);
assert_eq!(curve.high, 11.0);
}
#[test]
fn test_debug() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let debug_str = format!("{:?}", curve);
assert!(debug_str.contains("ContrastCurve"));
}
#[test]
fn test_clone() {
let curve1 = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let curve2 = curve1; assert_eq!(curve1, curve2);
}
#[test]
fn test_copy() {
let curve1 = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let curve2 = curve1;
assert_eq!(curve1, curve2);
}
#[test]
fn test_partial_eq() {
let curve1 = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let curve2 = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let curve3 = ContrastCurve::new(1.0, 4.5, 7.0, 12.0);
assert_eq!(curve1, curve2);
assert_ne!(curve1, curve3);
}
#[test]
fn test_get_at_low_boundary() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(-1.0);
assert!((result - 1.0).abs() < 1e-10);
}
#[test]
fn test_get_at_normal_boundary() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(0.0);
assert!((result - 4.5).abs() < 1e-10);
}
#[test]
fn test_get_at_medium_boundary() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(0.5);
assert!((result - 7.0).abs() < 1e-10);
}
#[test]
fn test_get_at_high_boundary() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(1.0);
assert!((result - 11.0).abs() < 1e-10);
}
#[test]
fn test_get_below_low_boundary() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(-2.0);
assert!((result - 1.0).abs() < 1e-10);
}
#[test]
fn test_get_far_below_low_boundary() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(-100.0);
assert!((result - 1.0).abs() < 1e-10);
}
#[test]
fn test_get_quarter_way_low_to_normal() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(-0.75);
let expected = 1.0 + (4.5 - 1.0) * 0.25;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_get_midway_low_to_normal() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(-0.5);
let expected = 1.0 + (4.5 - 1.0) * 0.5;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_get_three_quarter_way_low_to_normal() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(-0.25);
let expected = 1.0 + (4.5 - 1.0) * 0.75;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_get_quarter_way_normal_to_medium() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(0.125);
let expected = 4.5 + (7.0 - 4.5) * 0.25;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_get_midway_normal_to_medium() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(0.25);
let expected = 4.5 + (7.0 - 4.5) * 0.5;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_get_three_quarter_way_normal_to_medium() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(0.375);
let expected = 4.5 + (7.0 - 4.5) * 0.75;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_get_quarter_way_medium_to_high() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(0.625);
let expected = 7.0 + (11.0 - 7.0) * 0.25;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_get_midway_medium_to_high() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(0.75);
let expected = 7.0 + (11.0 - 7.0) * 0.5;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_get_three_quarter_way_medium_to_high() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(0.875);
let expected = 7.0 + (11.0 - 7.0) * 0.75;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_get_above_high_boundary() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(2.0);
assert!((result - 11.0).abs() < 1e-10);
}
#[test]
fn test_get_far_above_high_boundary() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let result = curve.get(100.0);
assert!((result - 11.0).abs() < 1e-10);
}
#[test]
fn test_get_all_same_values() {
let curve = ContrastCurve::new(5.0, 5.0, 5.0, 5.0);
assert!((curve.get(-1.0) - 5.0).abs() < 1e-10);
assert!((curve.get(-0.5) - 5.0).abs() < 1e-10);
assert!((curve.get(0.0) - 5.0).abs() < 1e-10);
assert!((curve.get(0.25) - 5.0).abs() < 1e-10);
assert!((curve.get(0.5) - 5.0).abs() < 1e-10);
assert!((curve.get(0.75) - 5.0).abs() < 1e-10);
assert!((curve.get(1.0) - 5.0).abs() < 1e-10);
}
#[test]
fn test_get_with_realistic_wcag_values() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 21.0);
assert!((curve.get(-1.0) - 1.0).abs() < 1e-10);
assert!((curve.get(0.0) - 4.5).abs() < 1e-10);
assert!((curve.get(0.5) - 7.0).abs() < 1e-10);
assert!((curve.get(1.0) - 21.0).abs() < 1e-10);
}
#[test]
fn test_get_zero_values() {
let curve = ContrastCurve::new(0.0, 0.0, 0.0, 0.0);
assert!((curve.get(-1.0) - 0.0).abs() < 1e-10);
assert!((curve.get(0.5) - 0.0).abs() < 1e-10);
assert!((curve.get(1.0) - 0.0).abs() < 1e-10);
}
#[test]
fn test_get_negative_values() {
let curve = ContrastCurve::new(-5.0, -2.0, 1.0, 4.0);
assert!((curve.get(-1.0) - (-5.0)).abs() < 1e-10);
assert!((curve.get(0.0) - (-2.0)).abs() < 1e-10);
assert!((curve.get(0.5) - 1.0).abs() < 1e-10);
assert!((curve.get(1.0) - 4.0).abs() < 1e-10);
}
#[test]
fn test_get_very_small_increments() {
let curve = ContrastCurve::new(1.0, 4.5, 7.0, 11.0);
let mut prev = curve.get(-1.0);
for i in 0..100 {
let level = -1.0 + (i as f64) * 0.02;
let val = curve.get(level);
assert!(
val >= prev - 1e-10,
"Value should be monotonic at level {}",
level
);
prev = val;
}
}
}