meditate-core 0.5.2

Pure, I/O-free breath engine and orb renderers shared by the meditate CLI and its WASM web build.
Documentation
use super::{Renderer, Surface};

/// Renders the surface without color, mapping brightness to a five-step block
/// ramp. Used on 16-color, NO_COLOR, and dumb terminals.
pub struct Mono;

const RAMP: [char; 5] = [' ', '', '', '', ''];

impl Renderer for Mono {
    fn encode(&self, surface: &Surface) -> String {
        let rows = surface.height() / 2;
        let mut out = String::new();
        for cy in 0..rows {
            for x in 0..surface.width() {
                if let Some(g) = surface.glyph(x, cy) {
                    // Mono carries depth by density: the glyph is presence; its
                    // color is dropped, and no color codes are emitted.
                    out.push(g.ch);
                } else {
                    let top = surface.get(x, cy * 2).luma();
                    let bottom = surface.get(x, cy * 2 + 1).luma();
                    out.push(ramp_char((top + bottom) / 2.0));
                }
            }
            if cy + 1 < rows {
                out.push_str("\r\n");
            }
        }
        out
    }
}

fn ramp_char(luma: f32) -> char {
    let index = (luma.clamp(0.0, 1.0) * (RAMP.len() - 1) as f32).round() as usize;
    RAMP[index]
}