BREP_cam 0.6.0

CAM toolpath engine contracts and geometric core for the BREP CAD kernel: setups, tools, operations, typed motion streams, cutter profiles, mesh snapshots and triangle spatial indexing
Documentation
//! Shared operations on indexed triangle positions, without owning mesh metadata.
use crate::model::{Bounds3, Point3};

pub(crate) struct MeshCore<'a> {
    pub positions: &'a [Point3],
    pub indices: &'a [u32],
}

impl MeshCore<'_> {
    pub fn triangle_count(&self) -> usize {
        self.indices.len() / 3
    }

    pub fn triangle(&self, i: usize) -> [Point3; 3] {
        let t = &self.indices[3 * i..3 * i + 3];
        [self.positions[t[0] as usize], self.positions[t[1] as usize], self.positions[t[2] as usize]]
    }

    pub fn signed_volume(&self) -> f64 {
        (0..self.triangle_count())
            .map(|i| {
                let [a, b, c] = self.triangle(i);
                a.dot(b.cross(c)) / 6.0
            })
            .sum()
    }

    pub fn bounds(&self) -> Bounds3 {
        Bounds3::from_points(self.positions.iter().copied())
    }

    pub fn is_closed(&self) -> bool {
        let mut keyed: Vec<([u64; 3], u32)> = self
            .positions
            .iter()
            .enumerate()
            .map(|(i, p)| ([crate::geom::canonical_bits(p.x), crate::geom::canonical_bits(p.y), crate::geom::canonical_bits(p.z)], i as u32))
            .collect();
        keyed.sort_unstable();
        let mut canon = vec![0u32; self.positions.len()];
        let mut i = 0;
        while i < keyed.len() {
            let mut j = i;
            while j < keyed.len() && keyed[j].0 == keyed[i].0 {
                canon[keyed[j].1 as usize] = keyed[i].1;
                j += 1;
            }
            i = j;
        }
        let mut edges: Vec<(u32, u32)> = Vec::with_capacity(self.indices.len());
        for t in self.indices.chunks_exact(3) {
            let (a, b, c) = (canon[t[0] as usize], canon[t[1] as usize], canon[t[2] as usize]);
            edges.push((a, b));
            edges.push((b, c));
            edges.push((c, a));
        }
        edges.sort_unstable();
        if edges.windows(2).any(|w| w[0] == w[1]) {
            return false;
        }
        edges.iter().all(|&(a, b)| edges.binary_search(&(b, a)).is_ok())
    }
}