#[cfg(test)]
mod test;
use crate::{
geometry::{
Coordinate, CoordinatesRef,
mesh::{
Mesh,
tessellation::{D, Tessellation},
},
},
math::{Scalar, Tensor},
};
use std::thread::{available_parallelism, scope};
const GRAZING_TOLERANCE: Scalar = 1.0e-4;
const TRIM_RATIO: Scalar = 0.1;
const DIRECTIONS: [Coordinate<D>; 3] = [
Coordinate::const_from([1.0, 0.140_412_03, 0.092_153_88]),
Coordinate::const_from([0.097_153_2, 1.0, 0.131_771_4]),
Coordinate::const_from([0.123_456_7, 0.087_654_3, 1.0]),
];
impl Tessellation {
pub fn trim(&self, mesh: &mut Mesh<D>) -> Result<(), &'static str> {
let bvh = self.bvh();
let surface = self.mesh();
let surface_coordinates = surface.coordinates();
let elements: Vec<&[usize]> = surface.connectivities().iter().flatten().collect();
let normals: CoordinatesRef<'_, D> = self.normals().iter().flatten().collect();
let directions = DIRECTIONS.map(|direction| direction.normalized());
let coordinates = mesh.coordinates();
let number_of_nodes = coordinates.len();
let mut signed = vec![Scalar::NEG_INFINITY; number_of_nodes];
let threads = available_parallelism().map_or(1, |threads| threads.get());
let chunk_size = number_of_nodes.div_ceil(threads).max(1);
scope(|scope| {
let (elements, normals, directions) = (&elements, &normals, &directions);
signed
.chunks_mut(chunk_size)
.enumerate()
.for_each(|(chunk, distances)| {
scope.spawn(move || {
let offset = chunk * chunk_size;
distances
.iter_mut()
.enumerate()
.for_each(|(local, distance)| {
let point = &coordinates[offset + local];
let inside = directions
.iter()
.find_map(|direction| {
let ray = (point.clone(), direction.clone()).into();
match bvh.intersect(&ray, surface_coordinates, elements) {
None => Some(false),
Some(hit) => {
let normal = &normals[hit.index()];
let cosine = (direction * normal) / normal.norm();
(cosine.abs() > GRAZING_TOLERANCE)
.then_some(cosine > 0.0)
}
}
})
.unwrap_or(false);
if let Some((closest, _)) =
bvh.closest_point(point, surface_coordinates, elements)
{
let magnitude = (&closest - point).norm();
*distance = if inside { magnitude } else { -magnitude };
}
});
});
});
});
mesh.keep_hexes(|_, hex, _| {
let (minimum, maximum) = hex.iter().fold(
(Scalar::INFINITY, Scalar::NEG_INFINITY),
|(minimum, maximum), &node| (minimum.min(signed[node]), maximum.max(signed[node])),
);
minimum + TRIM_RATIO * maximum >= 0.0
})
}
}