renderer.rs
5.7 kB · rust · 139 lines
1use super::models::Cell3d;2use crate::dim::push_glyph;3use std::collections::HashMap;45/// Renders the cell as one grid of glyph rows per z slice.6pub fn text(cell: &Cell3d, glyphs: Option<&HashMap<u8, char>>) -> Vec<Vec<String>> {7 let shape = &cell.types().shape;8 let mut slices = Vec::with_capacity(shape[2]);9 for z in 0..shape[2] {10 let mut rows = Vec::with_capacity(shape[0]);11 for y in 0..shape[0] {12 let mut row = String::with_capacity(shape[1]);13 for x in 0..shape[1] {14 let v = cell.types().get(&[y, x, z]);15 push_glyph(&mut row, v, glyphs);16 }17 rows.push(row);18 }19 slices.push(rows);20 }21 slices22}2324/// Renders the cell's exposed faces as a Wavefront OBJ string.25pub fn obj(cell: &Cell3d) -> String {26 let grid = cell.types();27 let (dx, dy, dz) = (grid.shape[0], grid.shape[1], grid.shape[2]);28 let mut vertices: Vec<(usize, usize, usize)> = Vec::new();29 let mut vertex_map: HashMap<(usize, usize, usize), usize> = HashMap::new();30 let mut faces: Vec<(usize, usize, usize, usize)> = Vec::new();31 let add = |map: &mut HashMap<(usize, usize, usize), usize>,32 verts: &mut Vec<(usize, usize, usize)>,33 v: (usize, usize, usize)|34 -> usize {35 *map.entry(v).or_insert_with(|| {36 verts.push(v);37 verts.len()38 })39 };40 for i in 0..dx {41 for j in 0..dy {42 for k in 0..dz {43 if grid.get(&[i, j, k]) == 0 {44 continue;45 }46 let empty = |i: isize, j: isize, k: isize| -> bool {47 if i < 0 || j < 0 || k < 0 {48 return true;49 }50 let (i, j, k) = (i as usize, j as usize, k as usize);51 if i >= dx || j >= dy || k >= dz {52 return true;53 }54 grid.get(&[i, j, k]) == 055 };56 let (ii, jj, kk) = (i as isize, j as isize, k as isize);57 if empty(ii - 1, jj, kk) {58 let v1 = add(&mut vertex_map, &mut vertices, (i, j, k));59 let v2 = add(&mut vertex_map, &mut vertices, (i, j, k + 1));60 let v3 = add(&mut vertex_map, &mut vertices, (i, j + 1, k + 1));61 let v4 = add(&mut vertex_map, &mut vertices, (i, j + 1, k));62 faces.push((v1, v2, v3, v4));63 }64 if empty(ii + 1, jj, kk) {65 let v1 = add(&mut vertex_map, &mut vertices, (i + 1, j, k));66 let v2 = add(&mut vertex_map, &mut vertices, (i + 1, j + 1, k));67 let v3 = add(&mut vertex_map, &mut vertices, (i + 1, j + 1, k + 1));68 let v4 = add(&mut vertex_map, &mut vertices, (i + 1, j, k + 1));69 faces.push((v1, v2, v3, v4));70 }71 if empty(ii, jj - 1, kk) {72 let v1 = add(&mut vertex_map, &mut vertices, (i, j, k));73 let v2 = add(&mut vertex_map, &mut vertices, (i + 1, j, k));74 let v3 = add(&mut vertex_map, &mut vertices, (i + 1, j, k + 1));75 let v4 = add(&mut vertex_map, &mut vertices, (i, j, k + 1));76 faces.push((v1, v2, v3, v4));77 }78 if empty(ii, jj + 1, kk) {79 let v1 = add(&mut vertex_map, &mut vertices, (i, j + 1, k));80 let v2 = add(&mut vertex_map, &mut vertices, (i, j + 1, k + 1));81 let v3 = add(&mut vertex_map, &mut vertices, (i + 1, j + 1, k + 1));82 let v4 = add(&mut vertex_map, &mut vertices, (i + 1, j + 1, k));83 faces.push((v1, v2, v3, v4));84 }85 if empty(ii, jj, kk - 1) {86 let v1 = add(&mut vertex_map, &mut vertices, (i, j, k));87 let v2 = add(&mut vertex_map, &mut vertices, (i, j + 1, k));88 let v3 = add(&mut vertex_map, &mut vertices, (i + 1, j + 1, k));89 let v4 = add(&mut vertex_map, &mut vertices, (i + 1, j, k));90 faces.push((v1, v2, v3, v4));91 }92 if empty(ii, jj, kk + 1) {93 let v1 = add(&mut vertex_map, &mut vertices, (i, j, k + 1));94 let v2 = add(&mut vertex_map, &mut vertices, (i + 1, j, k + 1));95 let v3 = add(&mut vertex_map, &mut vertices, (i + 1, j + 1, k + 1));96 let v4 = add(&mut vertex_map, &mut vertices, (i, j + 1, k + 1));97 faces.push((v1, v2, v3, v4));98 }99 }100 }101 }102 let mut out = String::new();103 for (x, y, z) in &vertices {104 out.push_str(&format!("v {x} {z} {}\n", dy - y));105 }106 for (a, b, c, d) in &faces {107 out.push_str(&format!("f {a} {b} {c} {d}\n"));108 }109 out110}111112#[cfg(test)]113mod tests {114 use super::*;115 use crate::three::designs;116 #[test]117 fn single_cube_obj() {118 let c = designs::ones(1, 1).unwrap();119 let o = obj(&c);120 assert_eq!(o.matches("v ").count(), 8);121 assert_eq!(o.matches("f ").count(), 6);122 }123 #[test]124 fn menger_obj_has_interior_faces_culled() {125 let c = designs::carpet(3, 1).unwrap();126 let o = obj(&c);127 let faces = o.matches("f ").count();128 assert!(faces < 20 * 6);129 assert!(faces > 6);130 }131 #[test]132 fn text_slices() {133 let c = designs::ztree(3, 1).unwrap();134 let slices = text(&c, None);135 assert_eq!(slices.len(), 3);136 assert_eq!(slices[0].len(), 3);137 assert_eq!(slices[0][0].len(), 3);138 }139}