use std::num::NonZeroU32;
#[cfg(test)]
mod test;
mod assemble;
mod build;
mod classify;
mod cleanup;
mod face;
mod geometry;
mod lattice;
mod snap;
mod split;
mod tables;
mod topology;
use crate::{
geometry::{
Coordinate, Direction,
mesh::{
Mesh,
tessellation::{D, Tessellation},
},
ntree::{Balance, Balancing, CurvatureSizing, Dualization, Octree, Pairing, Sizing},
},
math::{Quantity, Scalar},
units::Length,
};
use geometry::contained;
use std::collections::HashMap;
const COLLAPSE_FRACTION: Scalar = 0.2;
const CROSSING_TOLERANCE: Quantity<Length> = Length::meters(1.0e-8);
const GRAZING_TOLERANCE: Scalar = 1.0e-4;
const PADDING: u16 = 2;
const SLIVER_FRACTION: Scalar = 0.1;
const SNAP_FEATURE: Scalar = 0.5;
const SNAP_HARD: Scalar = 0.05;
const SNAP_QUALITY: Scalar = 0.3;
const SNAP_SOFT: Scalar = 0.2;
const FACES: [[usize; 4]; 6] = [
[0, 1, 5, 4],
[1, 2, 6, 5],
[2, 3, 7, 6],
[3, 0, 4, 7],
[0, 3, 2, 1],
[4, 5, 6, 7],
];
const EDGES: [[usize; 2]; 12] = [
[0, 1],
[1, 2],
[2, 3],
[3, 0],
[4, 5],
[5, 6],
[6, 7],
[7, 4],
[0, 4],
[1, 5],
[2, 6],
[3, 7],
];
const DIRECTIONS: [Direction<D>; 3] = [
Direction::const_from([1.0, 0.140_412_03, 0.092_153_88]),
Direction::const_from([0.097_153_2, 1.0, 0.131_771_4]),
Direction::const_from([0.123_456_7, 0.087_654_3, 1.0]),
];
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Class {
Inside,
Cut,
Outside,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Sign {
Inside,
On,
Outside,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Vertex {
Node(usize),
Crossing([usize; 2], usize),
}
pub struct Tables {
signs: HashMap<usize, Sign>,
crossings: HashMap<[usize; 2], Vec<Coordinate<D>>>,
faces: HashMap<[usize; 4], [usize; 4]>,
segments: HashMap<[usize; 4], Vec<[Vertex; 2]>>,
}
impl Tables {
pub fn signs(&self) -> &HashMap<usize, Sign> {
&self.signs
}
pub fn crossings(&self) -> &HashMap<[usize; 2], Vec<Coordinate<D>>> {
&self.crossings
}
pub fn faces(&self) -> &HashMap<[usize; 4], [usize; 4]> {
&self.faces
}
pub fn segments(&self) -> &HashMap<[usize; 4], Vec<[Vertex; 2]>> {
&self.segments
}
}
impl Tessellation {
pub fn dual_background(
&self,
balancing: Balancing,
scale: Scalar,
) -> Result<(Mesh<D>, Vec<Class>), &'static str> {
let sizing = Sizing::new(self, scale, CurvatureSizing::default(), PADDING);
let mesh = if sizing.fits::<u16>() {
let mut octree = Octree::<u16, NonZeroU32>::refine(&sizing)?;
octree.equilibrate(balancing, Pairing::Regular)?;
octree.dualize()
} else {
let mut octree = Octree::<u32, NonZeroU32>::refine(&sizing)?;
octree.equilibrate(balancing, Pairing::Regular)?;
octree.dualize()
};
let classes = self.classify(&mesh);
Ok((mesh, classes))
}
pub fn lattice_background(
&self,
spacing: Quantity<Length>,
) -> Result<(Mesh<D>, Vec<Class>), &'static str> {
Ok(self.lattice_cells(spacing)?.mesh())
}
pub fn octree_background(
&self,
balancing: Balancing,
scale: Scalar,
) -> Result<(Mesh<D>, Vec<Class>), &'static str> {
let sizing = Sizing::new(self, scale, CurvatureSizing::default(), PADDING);
let mesh = if sizing.fits::<u16>() {
let mut octree = Octree::<u16, NonZeroU32>::refine(&sizing)?;
octree.equilibrate(balancing, Pairing::Regular)?;
Mesh::from(octree)
} else {
let mut octree = Octree::<u32, NonZeroU32>::refine(&sizing)?;
octree.equilibrate(balancing, Pairing::Regular)?;
Mesh::from(octree)
};
let classes = self.classify(&mesh);
Ok((mesh, classes))
}
pub fn cut(&self, mesh: Mesh<D>, classes: &[Class]) -> Result<Mesh<D>, &'static str> {
if !contained(&mesh, classes) {
return Err("tessellation is not contained within the background mesh");
}
let (mesh, snapped) = self.snap(mesh, classes)?;
let tables = self.tables(&mesh, classes, &snapped)?;
self.assemble(&mesh, classes, &tables)
}
pub fn cut_polyhedral(
&self,
mesh: Mesh<D>,
classes: &[Class],
) -> Result<Mesh<D>, &'static str> {
if !contained(&mesh, classes) {
return Err("tessellation is not contained within the background mesh");
}
let (mesh, snapped) = self.snap_generic(mesh, classes)?;
let tables = self.tables_generic(&mesh, classes, &snapped)?;
self.assemble_generic(&mesh, classes, &tables)
}
}