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}