renderer.rs
3.0 kB · rust · 89 lines
1use super::faces::quads;2use super::Cell3d;3use crate::math::cell::push_glyph;4use std::collections::HashMap;56/// Renders the cube as rows of glyphs, plane after plane, or of digits where no glyph is mapped.7///8/// ```9/// let cell = mrlyrs::math::three::carpet(3, 1).unwrap();10/// let rows = mrlyrs::math::three::text(&cell, None);11/// assert_eq!(rows.len(), 9);12/// assert_eq!(&rows[..3], ["111", "101", "111"]);13/// ```14pub fn text(cell: &Cell3d, glyphs: Option<&HashMap<u8, String>>) -> Vec<String> {15 let types = cell.types();16 let (depth, height, width) = (types.shape[0], types.shape[1], types.shape[2]);17 let mut rows = Vec::with_capacity(depth * height);18 for z in 0..depth {19 for y in 0..height {20 let mut row = String::with_capacity(width);21 for x in 0..width {22 let at = (z * height + y) * width + x;23 push_glyph(&mut row, types.at(at) as u8, glyphs);24 }25 rows.push(row);26 }27 }28 rows29}3031/// Writes the cube's exposed quads as a Wavefront OBJ, one shared vertex per corner.32///33/// ```34/// let obj = mrlyrs::math::three::to_obj(&mrlyrs::math::three::carpet(3, 1).unwrap());35/// assert_eq!(obj.lines().filter(|line| line.starts_with("v ")).count(), 64);36/// assert_eq!(obj.lines().filter(|line| line.starts_with("f ")).count(), 72);37/// ```38pub fn to_obj(cell: &Cell3d) -> String {39 let shape = &cell.types().shape;40 let side = shape[0].max(shape[1]).max(shape[2]) as f32;41 let half = side / 2.0;42 let whole = |v: f32| (v * half + half).round() as i64;43 let mut corners: Vec<[i64; 3]> = Vec::new();44 let mut seen: HashMap<[i64; 3], usize> = HashMap::new();45 let mut faces: Vec<[usize; 4]> = Vec::new();46 for quad in quads(cell) {47 let mut face = [0usize; 4];48 for (slot, vert) in quad.verts.iter().enumerate() {49 let key = [whole(vert.x), whole(vert.y), whole(vert.z)];50 let next = seen.len() + 1;51 face[slot] = *seen.entry(key).or_insert_with(|| {52 corners.push(key);53 next54 });55 }56 faces.push(face);57 }58 let mut out = String::new();59 for corner in &corners {60 out.push_str(&format!("v {} {} {}\n", corner[0], corner[1], corner[2]));61 }62 for face in &faces {63 out.push_str(&format!(64 "f {} {} {} {}\n",65 face[0], face[1], face[2], face[3]66 ));67 }68 out69}7071#[cfg(test)]72mod tests {73 use super::*;74 use crate::math::three::designs;75 #[test]76 fn text_walks_every_plane_of_the_sponge() {77 let rows = text(&designs::carpet(3, 1).unwrap(), None);78 assert_eq!(rows.len(), 9);79 assert_eq!(&rows[..3], ["111", "101", "111"]);80 }8182 #[test]83 fn the_sponge_writes_its_obj() {84 let obj = to_obj(&designs::carpet(3, 1).unwrap());85 assert_eq!(obj.lines().filter(|l| l.starts_with("v ")).count(), 64);86 assert_eq!(obj.lines().filter(|l| l.starts_with("f ")).count(), 72);87 assert_eq!(obj.len(), 1481);88 }89}