faces.rs
6.2 kB · rust · 196 lines
1use super::graph::edge_graph;2use super::Cell3d;3use crate::space::Vec3;45/// An outward face of a filled site: its normal and four corners.6pub struct Quad {7 /// The outward unit normal.8 pub normal: Vec3,9 /// The four corners in winding order.10 pub verts: [Vec3; 4],11}1213/// Returns one outward quad per exposed face, scaled into the unit box.14pub fn quads(cell: &Cell3d) -> Vec<Quad> {15 let grid = cell.types();16 let (dx, dy, dz) = (grid.shape[0], grid.shape[1], grid.shape[2]);17 let side = dx.max(dy).max(dz) as f32;18 let half = side / 2.0;19 let point = |i: usize, j: usize, k: usize| -> Vec3 {20 Vec3::new(21 (i as f32 - half) / half,22 (j as f32 - half) / half,23 (k as f32 - half) / half,24 )25 };26 let empty = |i: isize, j: isize, k: isize| -> bool {27 if i < 0 || j < 0 || k < 0 {28 return true;29 }30 let (i, j, k) = (i as usize, j as usize, k as usize);31 if i >= dx || j >= dy || k >= dz {32 return true;33 }34 grid.get(&[i, j, k]) == 035 };36 let mut out = Vec::new();37 for i in 0..dx {38 for j in 0..dy {39 for k in 0..dz {40 if grid.get(&[i, j, k]) == 0 {41 continue;42 }43 let (ii, jj, kk) = (i as isize, j as isize, k as isize);44 if empty(ii - 1, jj, kk) {45 out.push(Quad {46 normal: Vec3::new(-1.0, 0.0, 0.0),47 verts: [48 point(i, j, k),49 point(i, j, k + 1),50 point(i, j + 1, k + 1),51 point(i, j + 1, k),52 ],53 });54 }55 if empty(ii + 1, jj, kk) {56 out.push(Quad {57 normal: Vec3::new(1.0, 0.0, 0.0),58 verts: [59 point(i + 1, j, k),60 point(i + 1, j + 1, k),61 point(i + 1, j + 1, k + 1),62 point(i + 1, j, k + 1),63 ],64 });65 }66 if empty(ii, jj - 1, kk) {67 out.push(Quad {68 normal: Vec3::new(0.0, -1.0, 0.0),69 verts: [70 point(i, j, k),71 point(i + 1, j, k),72 point(i + 1, j, k + 1),73 point(i, j, k + 1),74 ],75 });76 }77 if empty(ii, jj + 1, kk) {78 out.push(Quad {79 normal: Vec3::new(0.0, 1.0, 0.0),80 verts: [81 point(i, j + 1, k),82 point(i, j + 1, k + 1),83 point(i + 1, j + 1, k + 1),84 point(i + 1, j + 1, k),85 ],86 });87 }88 if empty(ii, jj, kk - 1) {89 out.push(Quad {90 normal: Vec3::new(0.0, 0.0, -1.0),91 verts: [92 point(i, j, k),93 point(i, j + 1, k),94 point(i + 1, j + 1, k),95 point(i + 1, j, k),96 ],97 });98 }99 if empty(ii, jj, kk + 1) {100 out.push(Quad {101 normal: Vec3::new(0.0, 0.0, 1.0),102 verts: [103 point(i, j, k + 1),104 point(i + 1, j, k + 1),105 point(i + 1, j + 1, k + 1),106 point(i, j + 1, k + 1),107 ],108 });109 }110 }111 }112 }113 out114}115116/// Returns the cell's edge-graph segments, scaled into the unit box.117pub fn wires(cell: &Cell3d) -> Vec<[Vec3; 2]> {118 let grid = cell.types();119 let side = grid.shape[0].max(grid.shape[1]).max(grid.shape[2]) as f32;120 let half = side / 2.0;121 let point = |p: &[f64]| {122 Vec3::new(123 (p[2] as f32 - half) / half,124 (p[1] as f32 - half) / half,125 (p[0] as f32 - half) / half,126 )127 };128 let Ok(net) = edge_graph(cell) else {129 return Vec::new();130 };131 net.branches132 .iter()133 .map(|branch| {134 [135 point(&net.nodes[branch.parent].position),136 point(&net.nodes[branch.child].position),137 ]138 })139 .collect()140}141142#[cfg(test)]143mod tests {144 use super::*;145 use crate::three::{ones, xtree};146147 #[test]148 fn a_solid_cell_has_six_outward_faces() {149 let cell = ones(1, 1).unwrap();150 let faces = quads(&cell);151 assert_eq!(faces.len(), 6);152 let normals: Vec<Vec3> = faces.iter().map(|q| q.normal).collect();153 for axis in [154 Vec3::new(1.0, 0.0, 0.0),155 Vec3::new(-1.0, 0.0, 0.0),156 Vec3::new(0.0, 1.0, 0.0),157 Vec3::new(0.0, -1.0, 0.0),158 Vec3::new(0.0, 0.0, 1.0),159 Vec3::new(0.0, 0.0, -1.0),160 ] {161 assert!(normals.contains(&axis));162 }163 }164165 #[test]166 fn adjacent_solid_cells_hide_their_shared_face() {167 let cell = ones(2, 1).unwrap();168 let faces = quads(&cell);169 assert_eq!(faces.len(), 6 * 8 - 12 * 2);170 }171172 #[test]173 fn a_solid_cell_wires_its_twelve_edges() {174 let lines = wires(&ones(1, 1).unwrap());175 assert_eq!(lines.len(), 12);176 for [a, b] in lines {177 assert!(a.x.abs() <= 1.0 && a.y.abs() <= 1.0 && a.z.abs() <= 1.0);178 assert!(a != b);179 }180 }181182 #[test]183 fn the_wires_of_an_asymmetric_cell_frame_its_quads() {184 let cell = xtree(3, 2).unwrap();185 let mut ends: Vec<Vec3> = Vec::new();186 for [a, b] in wires(&cell) {187 ends.push(a);188 ends.push(b);189 }190 for quad in quads(&cell) {191 for vert in quad.verts {192 assert!(ends.contains(&vert));193 }194 }195 }196}