use crate::color::schemes::ColorScheme;
use crate::error::DotmaxError;
use crate::grid::{BrailleGrid, Color};
#[must_use]
pub fn apply_color_scheme(intensities: &[Vec<f32>], scheme: &ColorScheme) -> Vec<Vec<Color>> {
if intensities.is_empty() {
return Vec::new();
}
let mut result = Vec::with_capacity(intensities.len());
for row in intensities {
let mut color_row = Vec::with_capacity(row.len());
for &intensity in row {
let normalized = normalize_intensity(intensity);
color_row.push(scheme.sample(normalized));
}
result.push(color_row);
}
result
}
pub fn apply_colors_to_grid(
grid: &mut BrailleGrid,
color_grid: &[Vec<Color>],
) -> Result<(), DotmaxError> {
let (width, height) = grid.dimensions();
if color_grid.len() != height {
return Err(DotmaxError::BufferSizeMismatch {
expected: height,
actual: color_grid.len(),
});
}
for (y, row) in color_grid.iter().enumerate() {
if row.len() != width {
return Err(DotmaxError::BufferSizeMismatch {
expected: width,
actual: row.len(),
});
}
for (x, &color) in row.iter().enumerate() {
grid.set_cell_color(x, y, color)?;
}
}
Ok(())
}
#[inline]
fn normalize_intensity(intensity: f32) -> f32 {
if intensity.is_nan() {
0.0
} else if intensity.is_infinite() {
if intensity.is_sign_positive() {
1.0
} else {
0.0
}
} else {
intensity.clamp(0.0, 1.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::color::schemes::{
blue_purple, cyan_magenta, grayscale, green_yellow, heat_map, monochrome, rainbow,
};
#[test]
fn test_apply_color_scheme_empty_input() {
let intensities: Vec<Vec<f32>> = vec![];
let scheme = grayscale();
let colors = apply_color_scheme(&intensities, &scheme);
assert!(colors.is_empty());
}
#[test]
fn test_apply_color_scheme_1x1() {
let intensities = vec![vec![0.5]];
let scheme = grayscale();
let colors = apply_color_scheme(&intensities, &scheme);
assert_eq!(colors.len(), 1);
assert_eq!(colors[0].len(), 1);
assert!(colors[0][0].r >= 127 && colors[0][0].r <= 128);
}
#[test]
fn test_apply_color_scheme_10x10() {
let intensities: Vec<Vec<f32>> = (0..10)
.map(|_| (0..10).map(|x| x as f32 / 10.0).collect())
.collect();
let scheme = heat_map();
let colors = apply_color_scheme(&intensities, &scheme);
assert_eq!(colors.len(), 10);
assert_eq!(colors[0].len(), 10);
}
#[test]
fn test_apply_color_scheme_80x24() {
let intensities: Vec<Vec<f32>> = (0..24)
.map(|y| {
(0..80)
.map(|x| (x as f32 / 80.0 + y as f32 / 24.0) / 2.0)
.collect()
})
.collect();
let scheme = rainbow();
let colors = apply_color_scheme(&intensities, &scheme);
assert_eq!(colors.len(), 24);
assert_eq!(colors[0].len(), 80);
}
#[test]
fn test_apply_color_scheme_boundary_values() {
let intensities = vec![vec![0.0, 0.5, 1.0]];
let scheme = grayscale();
let colors = apply_color_scheme(&intensities, &scheme);
assert_eq!(colors[0][0], Color::black());
assert_eq!(colors[0][2], Color::white());
}
#[test]
fn test_apply_colors_to_grid_success() {
let mut grid = BrailleGrid::new(3, 2).unwrap();
let colors = vec![
vec![
Color::rgb(255, 0, 0),
Color::rgb(0, 255, 0),
Color::rgb(0, 0, 255),
],
vec![
Color::rgb(255, 255, 0),
Color::rgb(0, 255, 255),
Color::rgb(255, 0, 255),
],
];
apply_colors_to_grid(&mut grid, &colors).unwrap();
assert_eq!(grid.get_color(0, 0), Some(Color::rgb(255, 0, 0)));
assert_eq!(grid.get_color(1, 0), Some(Color::rgb(0, 255, 0)));
assert_eq!(grid.get_color(2, 0), Some(Color::rgb(0, 0, 255)));
assert_eq!(grid.get_color(0, 1), Some(Color::rgb(255, 255, 0)));
assert_eq!(grid.get_color(1, 1), Some(Color::rgb(0, 255, 255)));
assert_eq!(grid.get_color(2, 1), Some(Color::rgb(255, 0, 255)));
}
#[test]
fn test_apply_colors_to_grid_height_mismatch() {
let mut grid = BrailleGrid::new(3, 3).unwrap();
let colors = vec![
vec![Color::black(), Color::black(), Color::black()],
];
let result = apply_colors_to_grid(&mut grid, &colors);
assert!(matches!(result, Err(DotmaxError::BufferSizeMismatch { .. })));
}
#[test]
fn test_apply_colors_to_grid_width_mismatch() {
let mut grid = BrailleGrid::new(3, 2).unwrap();
let colors = vec![
vec![Color::black(), Color::black()], vec![Color::black(), Color::black(), Color::black()],
];
let result = apply_colors_to_grid(&mut grid, &colors);
assert!(matches!(result, Err(DotmaxError::BufferSizeMismatch { .. })));
}
#[test]
fn test_apply_colors_to_grid_empty() {
let mut grid = BrailleGrid::new(3, 3).unwrap();
let colors: Vec<Vec<Color>> = vec![];
let result = apply_colors_to_grid(&mut grid, &colors);
assert!(matches!(result, Err(DotmaxError::BufferSizeMismatch { .. })));
}
#[test]
fn test_intensity_clamping_negative() {
let intensities = vec![vec![-0.5, -1.0, -100.0]];
let scheme = grayscale();
let colors = apply_color_scheme(&intensities, &scheme);
for color in &colors[0] {
assert_eq!(*color, Color::black());
}
}
#[test]
fn test_intensity_clamping_above_one() {
let intensities = vec![vec![1.5, 2.0, 100.0]];
let scheme = grayscale();
let colors = apply_color_scheme(&intensities, &scheme);
for color in &colors[0] {
assert_eq!(*color, Color::white());
}
}
#[test]
fn test_intensity_nan_handling() {
let intensities = vec![vec![f32::NAN]];
let scheme = grayscale();
let colors = apply_color_scheme(&intensities, &scheme);
assert_eq!(colors[0][0], Color::black());
}
#[test]
fn test_intensity_infinity_handling() {
let intensities = vec![vec![f32::INFINITY, f32::NEG_INFINITY]];
let scheme = grayscale();
let colors = apply_color_scheme(&intensities, &scheme);
assert_eq!(colors[0][0], Color::white());
assert_eq!(colors[0][1], Color::black());
}
#[test]
fn test_all_schemes_produce_valid_colors() {
let intensities = vec![vec![0.0, 0.25, 0.5, 0.75, 1.0]];
let schemes = vec![
rainbow(),
heat_map(),
blue_purple(),
green_yellow(),
cyan_magenta(),
grayscale(),
monochrome(),
];
for scheme in schemes {
let colors = apply_color_scheme(&intensities, &scheme);
assert_eq!(colors.len(), 1);
assert_eq!(colors[0].len(), 5);
}
}
#[test]
fn test_rainbow_scheme_produces_spectrum() {
let intensities = vec![vec![0.0, 0.5, 1.0]];
let scheme = rainbow();
let colors = apply_color_scheme(&intensities, &scheme);
let red = &colors[0][0];
assert_eq!(red.r, 255);
assert_eq!(red.g, 0);
assert_eq!(red.b, 0);
let purple = &colors[0][2];
assert!(purple.r > 200);
assert!(purple.b > 200);
}
#[test]
fn test_heat_map_scheme_thermal_gradient() {
let intensities = vec![vec![0.0, 1.0]];
let scheme = heat_map();
let colors = apply_color_scheme(&intensities, &scheme);
assert_eq!(colors[0][0], Color::black());
assert_eq!(colors[0][1], Color::white());
}
#[test]
fn test_normalize_intensity_normal_range() {
assert_eq!(normalize_intensity(0.0), 0.0);
assert_eq!(normalize_intensity(0.5), 0.5);
assert_eq!(normalize_intensity(1.0), 1.0);
}
#[test]
fn test_normalize_intensity_clamping() {
assert_eq!(normalize_intensity(-0.5), 0.0);
assert_eq!(normalize_intensity(1.5), 1.0);
}
#[test]
fn test_normalize_intensity_special_values() {
assert_eq!(normalize_intensity(f32::NAN), 0.0);
assert_eq!(normalize_intensity(f32::INFINITY), 1.0);
assert_eq!(normalize_intensity(f32::NEG_INFINITY), 0.0);
}
#[test]
fn test_full_pipeline_intensity_to_grid() {
let intensities: Vec<Vec<f32>> = (0..5)
.map(|y| (0..5).map(|x| (x + y) as f32 / 8.0).collect())
.collect();
let scheme = rainbow();
let colors = apply_color_scheme(&intensities, &scheme);
let mut grid = BrailleGrid::new(5, 5).unwrap();
apply_colors_to_grid(&mut grid, &colors).unwrap();
for y in 0..5 {
for x in 0..5 {
assert!(grid.get_color(x, y).is_some());
}
}
}
}