use egui::Color32;
use facett_core::Theme;
#[derive(Clone, Copy, Debug)]
pub struct Ramp {
stops: [Color32; 3],
}
impl Ramp {
pub fn sequential(th: &Theme) -> Self {
let low = lerp_color(th.bg, th.accent, 0.25);
Self { stops: [low, th.accent, th.glow] }
}
pub fn diverging(th: &Theme) -> Self {
Self { stops: [th.accent, th.text_dim, th.glow] }
}
pub fn at(&self, t: f32) -> Color32 {
let t = t.clamp(0.0, 1.0);
if t <= 0.5 {
lerp_color(self.stops[0], self.stops[1], t * 2.0)
} else {
lerp_color(self.stops[1], self.stops[2], (t - 0.5) * 2.0)
}
}
pub fn stops(&self) -> [Color32; 3] {
self.stops
}
}
pub use facett_core::look::color::lerp_color;
pub fn normalize(v: f64, min: f64, max: f64) -> f32 {
if !(max > min) {
return 0.5;
}
(((v - min) / (max - min)) as f32).clamp(0.0, 1.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lerp_endpoints_and_mid() {
let a = Color32::from_rgb(0, 0, 0);
let b = Color32::from_rgb(100, 200, 50);
assert_eq!(lerp_color(a, b, 0.0), a);
assert_eq!(lerp_color(a, b, 1.0), b);
let m = lerp_color(a, b, 0.5);
assert_eq!((m.r(), m.g(), m.b()), (50, 100, 25));
}
#[test]
fn ramp_hits_its_stops() {
let r = Ramp::sequential(&Theme::default());
let [lo, mid, hi] = r.stops();
assert_eq!(r.at(0.0), lo);
assert_eq!(r.at(0.5), mid);
assert_eq!(r.at(1.0), hi);
}
#[test]
fn ramp_clamps_out_of_range() {
let r = Ramp::diverging(&Theme::sci_fi());
assert_eq!(r.at(-3.0), r.at(0.0));
assert_eq!(r.at(9.0), r.at(1.0));
}
#[test]
fn normalize_handles_degenerate_range() {
assert_eq!(normalize(5.0, 5.0, 5.0), 0.5);
assert_eq!(normalize(0.0, 0.0, 10.0), 0.0);
assert_eq!(normalize(10.0, 0.0, 10.0), 1.0);
assert_eq!(normalize(2.5, 0.0, 10.0), 0.25);
assert_eq!(normalize(-1.0, 0.0, 10.0), 0.0);
assert_eq!(normalize(99.0, 0.0, 10.0), 1.0);
}
}