use super::types::ColorScheme;
pub fn get_color_palette(scheme: ColorScheme, ncolors: usize) -> Vec<String> {
match scheme {
ColorScheme::Default => get_default_colors(ncolors),
ColorScheme::HighContrast => get_high_contrast_colors(ncolors),
ColorScheme::Viridis => get_viridis_colors(ncolors),
ColorScheme::Plasma => get_plasma_colors(ncolors),
ColorScheme::Grayscale => get_grayscale_colors(ncolors),
}
}
fn get_default_colors(ncolors: usize) -> Vec<String> {
let base_colors = vec![
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf", ];
base_colors
.into_iter()
.cycle()
.take(ncolors)
.map(|s| s.to_string())
.collect()
}
fn get_high_contrast_colors(ncolors: usize) -> Vec<String> {
let base_colors = vec![
"#000000", "#FFFFFF", "#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF", "#800000", "#008000", "#000080", "#808000", "#800080", "#008080", "#C0C0C0", "#808080", ];
base_colors
.into_iter()
.cycle()
.take(ncolors)
.map(|s| s.to_string())
.collect()
}
fn get_viridis_colors(ncolors: usize) -> Vec<String> {
let base_colors = vec![
"#440154", "#482777", "#3f4a8a", "#31678e", "#26838f", "#1f9d8a", "#6cce5a", "#b6de2b", "#fee825", "#fff200", ];
if ncolors <= base_colors.len() {
let step = base_colors.len() / ncolors.max(1);
base_colors
.into_iter()
.step_by(step.max(1))
.take(ncolors)
.map(|s| s.to_string())
.collect()
} else {
base_colors
.into_iter()
.cycle()
.take(ncolors)
.map(|s| s.to_string())
.collect()
}
}
fn get_plasma_colors(ncolors: usize) -> Vec<String> {
let base_colors = vec![
"#0c0887", "#5c01a6", "#900da4", "#bf3984", "#e16462", "#f89441", "#fdc328", "#f0f921", "#fcffa4", "#ffffff", ];
if ncolors <= base_colors.len() {
let step = base_colors.len() / ncolors.max(1);
base_colors
.into_iter()
.step_by(step.max(1))
.take(ncolors)
.map(|s| s.to_string())
.collect()
} else {
base_colors
.into_iter()
.cycle()
.take(ncolors)
.map(|s| s.to_string())
.collect()
}
}
fn get_grayscale_colors(ncolors: usize) -> Vec<String> {
if ncolors == 0 {
return Vec::new();
}
(0..ncolors)
.map(|i| {
let intensity = if ncolors == 1 {
128 } else {
255 * i / (ncolors - 1) };
format!("#{:02x}{:02x}{:02x}", intensity, intensity, intensity)
})
.collect()
}
pub fn interpolate_colors(
start_color: &str,
end_color: &str,
ncolors: usize,
) -> Result<Vec<String>, String> {
if ncolors == 0 {
return Ok(Vec::new());
}
let start_rgb = parse_hex_color(start_color)?;
let end_rgb = parse_hex_color(end_color)?;
let mut colors = Vec::with_capacity(ncolors);
for i in 0..ncolors {
let t = if ncolors == 1 {
0.5 } else {
i as f64 / (ncolors - 1) as f64
};
let r = (start_rgb.0 as f64 + t * (end_rgb.0 as f64 - start_rgb.0 as f64)) as u8;
let g = (start_rgb.1 as f64 + t * (end_rgb.1 as f64 - start_rgb.1 as f64)) as u8;
let b = (start_rgb.2 as f64 + t * (end_rgb.2 as f64 - start_rgb.2 as f64)) as u8;
colors.push(format!("#{:02x}{:02x}{:02x}", r, g, b));
}
Ok(colors)
}
fn parse_hex_color(hex_color: &str) -> Result<(u8, u8, u8), String> {
let hex = hex_color.trim_start_matches('#');
if hex.len() != 6 {
return Err(format!("Invalid hex color format: {}", hex_color));
}
let r = u8::from_str_radix(&hex[0..2], 16)
.map_err(|_| format!("Invalid red component: {}", &hex[0..2]))?;
let g = u8::from_str_radix(&hex[2..4], 16)
.map_err(|_| format!("Invalid green component: {}", &hex[2..4]))?;
let b = u8::from_str_radix(&hex[4..6], 16)
.map_err(|_| format!("Invalid blue component: {}", &hex[4..6]))?;
Ok((r, g, b))
}
pub fn rgb_to_hex(r: u8, g: u8, b: u8) -> String {
format!("#{:02x}{:02x}{:02x}", r, g, b)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_color_palette_default() {
let colors = get_color_palette(ColorScheme::Default, 5);
assert_eq!(colors.len(), 5);
assert!(colors.iter().all(|c| c.starts_with("#")));
assert_eq!(colors[0], "#1f77b4");
}
#[test]
fn test_get_color_palette_high_contrast() {
let colors = get_color_palette(ColorScheme::HighContrast, 3);
assert_eq!(colors.len(), 3);
assert_eq!(colors[0], "#000000");
assert_eq!(colors[1], "#FFFFFF");
assert_eq!(colors[2], "#FF0000");
}
#[test]
fn test_get_color_palette_viridis() {
let colors = get_color_palette(ColorScheme::Viridis, 4);
assert_eq!(colors.len(), 4);
assert!(colors.iter().all(|c| c.starts_with("#")));
}
#[test]
fn test_get_color_palette_plasma() {
let colors = get_color_palette(ColorScheme::Plasma, 3);
assert_eq!(colors.len(), 3);
assert!(colors.iter().all(|c| c.starts_with("#")));
}
#[test]
fn test_get_grayscale_colors() {
let colors = get_grayscale_colors(5);
assert_eq!(colors.len(), 5);
assert_eq!(colors[0], "#000000"); assert_eq!(colors[4], "#ffffff"); }
#[test]
fn test_get_grayscale_colors_single() {
let colors = get_grayscale_colors(1);
assert_eq!(colors.len(), 1);
assert_eq!(colors[0], "#808080"); }
#[test]
fn test_interpolate_colors() {
let colors = interpolate_colors("#ff0000", "#0000ff", 3).expect("Operation failed");
assert_eq!(colors.len(), 3);
assert_eq!(colors[0], "#ff0000"); assert_eq!(colors[2], "#0000ff"); }
#[test]
fn test_parse_hex_color() {
let (r, g, b) = parse_hex_color("#ff0000").expect("Operation failed");
assert_eq!((r, g, b), (255, 0, 0));
let (r, g, b) = parse_hex_color("00ff00").expect("Operation failed");
assert_eq!((r, g, b), (0, 255, 0));
}
#[test]
fn test_rgb_to_hex() {
assert_eq!(rgb_to_hex(255, 0, 0), "#ff0000");
assert_eq!(rgb_to_hex(0, 255, 0), "#00ff00");
assert_eq!(rgb_to_hex(0, 0, 255), "#0000ff");
assert_eq!(rgb_to_hex(128, 128, 128), "#808080");
}
#[test]
fn test_get_color_palette_empty() {
let colors = get_color_palette(ColorScheme::Default, 0);
assert_eq!(colors.len(), 0);
}
#[test]
fn test_interpolate_colors_empty() {
let colors = interpolate_colors("#ff0000", "#0000ff", 0).expect("Operation failed");
assert_eq!(colors.len(), 0);
}
}