use ratatui::style::Color;
pub const GRADIENT_WARM: &[(Color, f32); 5] = &[
(Color::Rgb(0x1A, 0x05, 0x05), 0.0),
(Color::Rgb(0x3A, 0x1A, 0x15), 0.25),
(Color::Rgb(0x6B, 0x3A, 0x2A), 0.50),
(Color::Rgb(0xB5, 0x7A, 0x35), 0.75),
(Color::Rgb(0xE8, 0xA0, 0x35), 1.0),
];
pub const GRADIENT_COOL: &[(Color, f32); 5] = &[
(Color::Rgb(0x0A, 0x1A, 0x25), 0.0),
(Color::Rgb(0x1A, 0x3A, 0x45), 0.25),
(Color::Rgb(0x4D, 0x8A, 0x8A), 0.50),
(Color::Rgb(0x6D, 0xAE, 0xAE), 0.75),
(Color::Rgb(0xE8, 0xE4, 0xD9), 1.0),
];
pub fn lerp_color(a: Color, b: Color, t: f32) -> Color {
let t = t.clamp(0.0, 1.0);
match (a, b) {
(Color::Rgb(ar, ag, ab), Color::Rgb(br, bg, bb)) => Color::Rgb(
(ar as f32 + (br as f32 - ar as f32) * t) as u8,
(ag as f32 + (bg as f32 - ag as f32) * t) as u8,
(ab as f32 + (bb as f32 - ab as f32) * t) as u8,
),
_ => a,
}
}
pub fn multi_stop_color(stops: &[(Color, f32)], t: f32) -> Color {
let t = t.clamp(0.0, 1.0);
if stops.is_empty() {
return Color::Rgb(0, 0, 0);
}
if stops.len() == 1 {
return stops[0].0;
}
for i in 0..stops.len() - 1 {
if t >= stops[i].1 && t <= stops[i + 1].1 {
let seg_len = stops[i + 1].1 - stops[i].1;
let seg_t = if seg_len == 0.0 { 0.0 } else { (t - stops[i].1) / seg_len };
return lerp_color(stops[i].0, stops[i + 1].0, seg_t);
}
}
stops.last().unwrap().0
}
pub fn gradient_horizontal(width: u16, stops: &[(Color, f32)]) -> Vec<Color> {
if width == 0 {
return Vec::new();
}
(0..width)
.map(|i| {
let t = i as f32 / (width - 1) as f32;
multi_stop_color(stops, t)
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lerp_identity() {
let a = Color::Rgb(10, 20, 30);
let b = Color::Rgb(100, 200, 250);
assert_eq!(lerp_color(a, b, 0.0), a);
assert_eq!(lerp_color(a, b, 1.0), b);
}
#[test]
fn lerp_midpoint() {
let a = Color::Rgb(0, 0, 0);
let b = Color::Rgb(100, 200, 0);
assert_eq!(lerp_color(a, b, 0.5), Color::Rgb(50, 100, 0));
}
#[test]
fn lerp_clamps_t() {
let a = Color::Rgb(0, 0, 0);
let b = Color::Rgb(100, 0, 0);
assert_eq!(lerp_color(a, b, -0.5), a);
assert_eq!(lerp_color(a, b, 1.5), b);
}
#[test]
fn lerp_non_rgb_fallback() {
let a = Color::Red;
let b = Color::Rgb(100, 0, 0);
assert_eq!(lerp_color(a, b, 0.5), a);
}
#[test]
fn multi_stop_single() {
let stops = &[(Color::Rgb(50, 50, 50), 0.0)];
assert_eq!(multi_stop_color(stops, 0.0), Color::Rgb(50, 50, 50));
assert_eq!(multi_stop_color(stops, 0.5), Color::Rgb(50, 50, 50));
}
#[test]
fn multi_stop_warm_midpoint() {
let c = multi_stop_color(GRADIENT_WARM, 0.5);
assert_eq!(c, Color::Rgb(0x6B, 0x3A, 0x2A));
}
#[test]
fn gradient_horizontal_empty() {
assert!(gradient_horizontal(0, GRADIENT_WARM).is_empty());
}
#[test]
fn gradient_horizontal_single() {
let colors = gradient_horizontal(1, GRADIENT_WARM);
assert_eq!(colors.len(), 1);
assert_eq!(colors[0], GRADIENT_WARM.last().unwrap().0);
}
#[test]
fn gradient_horizontal_width_matches() {
let colors = gradient_horizontal(10, GRADIENT_WARM);
assert_eq!(colors.len(), 10);
assert_eq!(colors[0], GRADIENT_WARM[0].0);
assert_eq!(colors[9], GRADIENT_WARM.last().unwrap().0);
}
}