pub trait Scale {
fn map(&self, value: f64) -> f64;
fn inverse(&self, normalized: f64) -> f64;
}
pub struct LinearScale {
pub domain_min: f64,
pub domain_max: f64,
}
impl Scale for LinearScale {
fn map(&self, value: f64) -> f64 {
if (self.domain_max - self.domain_min).abs() < f64::EPSILON {
return 0.5;
}
(value - self.domain_min) / (self.domain_max - self.domain_min)
}
fn inverse(&self, normalized: f64) -> f64 {
normalized * (self.domain_max - self.domain_min) + self.domain_min
}
}
#[cfg(test)]
mod tests {
use super::Scale;
#[test]
fn linear_scale_map() {
let scale = super::LinearScale {
domain_min: 0.0,
domain_max: 10.0,
};
assert_eq!(scale.map(5.0), 0.5);
assert_eq!(scale.map(0.0), 0.0);
assert_eq!(scale.map(10.0), 1.0);
}
#[test]
fn linear_scale_inverse() {
let scale = super::LinearScale {
domain_min: 0.0,
domain_max: 10.0,
};
assert_eq!(scale.inverse(0.5), 5.0);
assert_eq!(scale.inverse(0.0), 0.0);
assert_eq!(scale.inverse(1.0), 10.0);
}
#[test]
fn linear_scale_inverse_clamped() {
let scale = super::LinearScale {
domain_min: 0.0,
domain_max: 10.0,
};
assert_eq!(scale.inverse(-0.5), -5.0);
assert_eq!(scale.inverse(1.5), 15.0);
}
#[test]
fn linear_scale_zero_domain_maps_to_midpoint() {
let scale = super::LinearScale {
domain_min: 5.0,
domain_max: 5.0,
};
assert_eq!(scale.map(5.0), 0.5);
assert_eq!(scale.map(0.0), 0.5);
}
}