//! hexwork-render: camera → raycast/DDA → glyph+color buffer → ANSI diff. //! Render thread reads triple-buffered snapshots; sim never blocks on render. //! Full spec: docs/05-renderer-spec.md. pub mod ansi; pub mod camera; pub mod env; pub mod palette; mod production; pub mod projection; pub mod ray; pub mod scene; pub mod shade; pub mod snapshot; mod transfer; /// Distance density ramp, near → far (docs/05 §5.1). pub const DENSITY_RAMP: &[char] = &['█', '▓', '▒', '░', '=', '-', ':', '.', '·', ' ']; /// Map a 0.0–1.0 distance fraction to a density glyph. pub fn density_glyph(frac: f32) -> char { let f = frac.clamp(0.0, 1.0); let i = (f * (DENSITY_RAMP.len() - 1) as f32).round() as usize; DENSITY_RAMP[i] } /// One cell of the frame buffer: glyph + truecolor fg/bg. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Cell { pub glyph: char, pub fg: [u8; 3], pub bg: [u8; 3], } impl Default for Cell { fn default() -> Self { Self { glyph: ' ', fg: [0, 0, 0], bg: [0, 0, 0], } } } /// Frame buffer: W×H cells, pooled and reused (zero hot-path allocs). #[derive(Clone, Debug)] pub struct FrameBuffer { pub w: usize, pub h: usize, pub cells: Vec, } impl FrameBuffer { pub fn new(w: usize, h: usize) -> Self { Self { w, h, cells: vec![Cell::default(); w * h], } } pub fn clear(&mut self) { for c in self.cells.iter_mut() { *c = Cell::default(); } } /// Count cells differing from a previous frame (for ANSI diff budgets). pub fn diff_count(&self, prev: &FrameBuffer) -> usize { assert_eq!((self.w, self.h), (prev.w, prev.h)); self.cells .iter() .zip(prev.cells.iter()) .filter(|(a, b)| a != b) .count() } } /// Which camera views the same sim (docs/05 §5.1). #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Camera { FirstPerson, ChaseOrbit, TopTactical, } #[cfg(test)] mod tests { use super::*; #[test] fn ramp_endpoints() { assert_eq!(density_glyph(0.0), '█'); assert_eq!(density_glyph(1.0), ' '); } #[test] fn identical_frames_diff_zero() { let a = FrameBuffer::new(80, 24); let b = FrameBuffer::new(80, 24); assert_eq!(a.diff_count(&b), 0); } }