conspire 0.7.1

The Rust interface to conspire.
Documentation
#[cfg(test)]
mod test;

use crate::geometry::{grid::Voxels, mesh::Mesh};
use std::array::from_fn;

const TETS_4: [[usize; 4]; 1] = [[0, 1, 2, 3]];
const TETS_5: [[usize; 4]; 2] = [[0, 1, 2, 4], [0, 2, 3, 4]];
const TETS_6: [[usize; 4]; 3] = [[0, 1, 2, 3], [1, 4, 5, 3], [1, 2, 5, 3]];
const TETS_8: [[usize; 4]; 6] = [
    [0, 1, 2, 6],
    [0, 2, 3, 6],
    [0, 3, 7, 6],
    [0, 7, 4, 6],
    [0, 4, 5, 6],
    [0, 5, 1, 6],
];

impl Voxels<usize> {
    pub fn from_finite_elements(mesh: &Mesh<3>, size: f64) -> Self {
        let coordinates = mesh.coordinates();
        let mut min = [f64::INFINITY; 3];
        let mut max = [f64::NEG_INFINITY; 3];
        for point in coordinates {
            (0..3).for_each(|ax| {
                min[ax] = min[ax].min(point[ax]);
                max[ax] = max[ax].max(point[ax]);
            });
        }
        let nel: [usize; 3] = from_fn(|ax| (((max[ax] - min[ax]) / size).ceil() as usize).max(1));
        let [nx, ny, nz] = nel;
        let mut data = vec![0usize; nx * ny * nz];
        let numbers = mesh.blocks();
        for (block, connectivity) in mesh.iter().enumerate() {
            let material = numbers.map_or(block + 1, |numbers| numbers[block]);
            for nodes in connectivity {
                let tets: &[[usize; 4]] = match nodes.len() {
                    4 => &TETS_4,
                    5 => &TETS_5,
                    6 => &TETS_6,
                    8 => &TETS_8,
                    _ => continue,
                };
                let points: Vec<[f64; 3]> = nodes
                    .iter()
                    .map(|&node| from_fn(|ax| coordinates[node][ax]))
                    .collect();
                let mut lo = [usize::MAX; 3];
                let mut hi = [0usize; 3];
                for point in &points {
                    (0..3).for_each(|ax| {
                        let l = ((point[ax] - min[ax]) / size).floor().max(0.0) as usize;
                        let h = (((point[ax] - min[ax]) / size).floor() as usize + 1).min(nel[ax]);
                        lo[ax] = lo[ax].min(l);
                        hi[ax] = hi[ax].max(h);
                    });
                }
                for k in lo[2]..hi[2] {
                    for j in lo[1]..hi[1] {
                        for i in lo[0]..hi[0] {
                            let center =
                                from_fn(|ax| min[ax] + ([i, j, k][ax] as f64 + 0.5) * size);
                            if tets
                                .iter()
                                .any(|tet| inside(center, from_fn(|vertex| points[tet[vertex]])))
                            {
                                data[i + nx * j + nx * ny * k] = material;
                            }
                        }
                    }
                }
            }
        }
        Voxels::new(data, nel)
    }
}

fn inside(query: [f64; 3], tet: [[f64; 3]; 4]) -> bool {
    let volume = orient(tet[0], tet[1], tet[2], tet[3]);
    if volume == 0.0 {
        return false;
    }
    let sign = volume.signum();
    let tolerance = -1e-9 * volume.abs();
    sign * orient(query, tet[1], tet[2], tet[3]) >= tolerance
        && sign * orient(tet[0], query, tet[2], tet[3]) >= tolerance
        && sign * orient(tet[0], tet[1], query, tet[3]) >= tolerance
        && sign * orient(tet[0], tet[1], tet[2], query) >= tolerance
}

fn orient(a: [f64; 3], b: [f64; 3], c: [f64; 3], d: [f64; 3]) -> f64 {
    let u = from_fn::<_, 3, _>(|ax| b[ax] - a[ax]);
    let v = from_fn::<_, 3, _>(|ax| c[ax] - a[ax]);
    let w = from_fn::<_, 3, _>(|ax| d[ax] - a[ax]);
    u[0] * (v[1] * w[2] - v[2] * w[1]) - u[1] * (v[0] * w[2] - v[2] * w[0])
        + u[2] * (v[0] * w[1] - v[1] * w[0])
}