use crate::render::palette::RgbColor;
pub fn gauge(
ratio: f32,
def_ratio: f32,
width: usize,
fill: RgbColor,
hi: RgbColor,
dim: RgbColor,
) -> Vec<(char, RgbColor)> {
let filled = (ratio * width as f32).round() as usize;
let defc = (def_ratio * (width as f32 - 1.0)).round() as usize;
(0..width)
.map(|i| {
if i == defc {
('│', hi)
} else if i < filled {
('█', fill)
} else {
('░', dim)
}
})
.collect()
}
pub fn heatmap_slider(
ratio: f32,
def_ratio: f32,
width: usize,
truecolor: bool,
accent: RgbColor,
dim: RgbColor,
) -> Vec<(char, RgbColor)> {
let filled = (ratio * width as f32).round() as usize;
let knob_pos = filled.min(width.saturating_sub(1));
let defc = (def_ratio * (width as f32 - 1.0)).round() as usize;
(0..width)
.map(|i| {
if i == knob_pos {
('●', accent)
} else if i == defc {
('│', dim)
} else if i < filled {
let col = if truecolor {
let t = i as f32 / width as f32;
RgbColor {
r: (90.0 + 140.0 * t) as u8,
g: 200,
b: (120.0 - 60.0 * t) as u8,
}
} else {
accent
};
('━', col)
} else {
('━', dim)
}
})
.collect()
}
pub fn sparkline(hist: &[f32]) -> String {
const BARS: [char; 8] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
hist.iter()
.map(|v| BARS[((v.clamp(0.0, 1.0)) * 7.0).round() as usize])
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::render::palette::RgbColor;
const A: RgbColor = RgbColor { r: 0, g: 200, b: 0 };
const D: RgbColor = RgbColor {
r: 80,
g: 80,
b: 80,
};
#[test]
fn gauge_width_and_default_tick() {
let g = gauge(0.5, 0.25, 8, A, A, D);
assert_eq!(g.len(), 8);
assert!(g.iter().any(|(c, _)| *c == '│')); assert!(g.iter().any(|(c, _)| *c == '█')); }
#[test]
fn heatmap_falls_back_to_solid_in_256() {
let s = heatmap_slider(0.8, 0.5, 10, false, A, D);
assert!(s
.iter()
.filter(|(c, _)| *c == '━')
.all(|(_, col)| *col == A || *col == D));
}
#[test]
fn sparkline_maps_range() {
assert_eq!(sparkline(&[0.0, 1.0]).chars().next().unwrap(), '▁');
}
}