pub fn intensity_to_braille_char(intensity: f32) -> char {
let i = intensity.clamp(0.0, 1.0);
const RAMP: [u8; 9] = [
0x00, 0x01, 0x03, 0x07, 0x27, 0x6F, 0xEF, 0xFF, 0xFF, ];
let idx = (i * (RAMP.len() as f32 - 1.0)).round() as usize;
let dots = RAMP[idx];
(0x2800 + dots as u32) as u8 as char
}
fn colorize_green(ch: char, intensity: f32) -> String {
let code = if intensity > 0.66 {
"\x1b[1;92m"
}
else if intensity > 0.33 {
"\x1b[92m"
}
else if intensity > 0.0 {
"\x1b[32m"
}
else {
"\x1b[0m"
}; format!("{}{}\x1b[0m", code, ch)
}
pub fn intensity_buffer_to_green_braille(buffer: &[Vec<f32>]) -> String {
if buffer.is_empty() || buffer[0].is_empty() {
return String::new();
}
let height = buffer.len();
let width = buffer[0].len();
let mut out = String::new();
let step_y = 4usize;
let step_x = 2usize;
for y in (0..height).step_by(step_y) {
for x in (0..width).step_by(step_x) {
let mut max_i = 0.0_f32;
for row in buffer.iter().take((y + step_y).min(height)).skip(y) {
for &v in row.iter().take((x + step_x).min(width)).skip(x) {
max_i = max_i.max(v);
}
}
let ch = intensity_to_braille_char(max_i);
out.push_str(&colorize_green(ch, max_i));
}
out.push('\n');
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_intensity_to_braille_monotonic() {
let a = intensity_to_braille_char(0.1) as u32;
let b = intensity_to_braille_char(0.9) as u32;
assert!(b >= a);
}
#[test]
fn test_buffer_to_braille_contains_ansi() {
let mut buf = vec![vec![0.0_f32; 8]; 8];
buf[4][4] = 1.0;
let s = intensity_buffer_to_green_braille(&buf);
assert!(s.contains("\x1b["));
assert!(s.lines().count() >= 2);
}
}