use crate::connectivity::{
CellConnectivity, Connectivity, ConnectivityMut, Hex20Connectivity, Hex27Connectivity, Hex8Connectivity,
Quad4d2Connectivity, Quad9d2Connectivity, Tet10Connectivity, Tet20Connectivity, Tet4Connectivity,
Tri3d2Connectivity, Tri3d3Connectivity, Tri6d2Connectivity,
};
use crate::geometry::{AxisAlignedBoundingBox, BoundedGeometry, GeometryCollection};
use crate::Real;
use fenris_nested_vec::NestedVec;
use nalgebra::allocator::Allocator;
use nalgebra::{DefaultAllocator, DimName, OPoint, OVector, Scalar, U2, U3};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::iter::once;
pub mod procedural;
pub mod reorder;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))]
pub struct Mesh<T: Scalar, D, Connectivity>
where
D: DimName,
DefaultAllocator: Allocator<T, D>,
{
#[serde(bound(
serialize = "<DefaultAllocator as Allocator<T, D>>::Buffer: Serialize",
deserialize = "<DefaultAllocator as Allocator<T, D>>::Buffer: Deserialize<'de>"
))]
vertices: Vec<OPoint<T, D>>,
#[serde(bound(
serialize = "Connectivity: Serialize",
deserialize = "Connectivity: Deserialize<'de>"
))]
connectivity: Vec<Connectivity>,
}
pub type Mesh2d<T, Connectivity> = Mesh<T, U2, Connectivity>;
pub type Mesh3d<T, Connectivity> = Mesh<T, U3, Connectivity>;
pub type TriangleMesh2d<T> = Mesh2d<T, Tri3d2Connectivity>;
pub type Tri6Mesh2d<T> = Mesh2d<T, Tri6d2Connectivity>;
pub type QuadMesh2d<T> = Mesh2d<T, Quad4d2Connectivity>;
pub type Quad9Mesh2d<T> = Mesh2d<T, Quad9d2Connectivity>;
pub type TriangleMesh3d<T> = Mesh3d<T, Tri3d3Connectivity>;
pub type HexMesh<T> = Mesh3d<T, Hex8Connectivity>;
pub type Hex20Mesh<T> = Mesh3d<T, Hex20Connectivity>;
pub type Hex27Mesh<T> = Mesh3d<T, Hex27Connectivity>;
pub type Tet4Mesh<T> = Mesh3d<T, Tet4Connectivity>;
pub type Tet10Mesh<T> = Mesh3d<T, Tet10Connectivity>;
pub type Tet20Mesh<T> = Mesh3d<T, Tet20Connectivity>;
impl<T, D, Connectivity> Mesh<T, D, Connectivity>
where
T: Scalar,
D: DimName,
DefaultAllocator: Allocator<T, D>,
{
pub fn vertices_mut(&mut self) -> &mut [OPoint<T, D>] {
&mut self.vertices
}
pub fn vertices(&self) -> &[OPoint<T, D>] {
&self.vertices
}
pub fn connectivity(&self) -> &[Connectivity] {
&self.connectivity
}
pub fn from_vertices_and_connectivity(vertices: Vec<OPoint<T, D>>, connectivity: Vec<Connectivity>) -> Self {
Self { vertices, connectivity }
}
}
impl<T, D, Connectivity> Mesh<T, D, Connectivity>
where
T: Scalar,
D: DimName,
DefaultAllocator: Allocator<T, D>,
Connectivity: CellConnectivity<T, D>,
{
pub fn get_cell(&self, index: usize) -> Option<Connectivity::Cell> {
self.connectivity()
.get(index)
.and_then(|conn| conn.cell(self.vertices()))
}
pub fn cell_iter<'a>(&'a self) -> impl 'a + Iterator<Item = Connectivity::Cell> {
self.connectivity().iter().map(move |connectivity| {
connectivity
.cell(&self.vertices)
.expect("Mesh2d is not allowed to contain cells with indices out of bounds.")
})
}
}
impl<T, D, C> Mesh<T, D, C>
where
T: Scalar,
D: DimName,
C: Connectivity,
C::FaceConnectivity: Connectivity,
DefaultAllocator: Allocator<T, D>,
{
pub fn find_boundary_cells(&self) -> Vec<usize> {
let mut cells: Vec<_> = self
.find_boundary_faces()
.into_iter()
.map(|(_, cell_index, _)| cell_index)
.collect();
cells.sort_unstable();
cells.dedup();
cells
}
pub fn find_boundary_faces(&self) -> Vec<(C::FaceConnectivity, usize, usize)> {
let mut sorted_slices = NestedVec::new();
let mut face_info = Vec::new();
for (conn_idx, cell_conn) in self.connectivity.iter().enumerate() {
let num_faces = cell_conn.num_faces();
for local_idx in 0..num_faces {
let face_conn = cell_conn.get_face_connectivity(local_idx).unwrap();
sorted_slices.push(face_conn.vertex_indices());
let indices = sorted_slices.last_mut().unwrap();
indices.sort_unstable();
face_info.push((face_conn, conn_idx, local_idx));
}
}
let mut slice_counts = BTreeMap::new();
let num_slices = sorted_slices.len();
for i in 0..num_slices {
slice_counts
.entry(sorted_slices.get(i).unwrap())
.and_modify(|(_, count)| *count += 1)
.or_insert((i, 1));
}
slice_counts
.into_iter()
.map(|(_key, value)| value)
.filter(|&(_, count)| count == 1)
.map(move |(i, _)| face_info[i].clone())
.collect()
}
pub fn find_boundary_vertices(&self) -> Vec<usize> {
let mut indices = Vec::new();
for (connectivity, _, _) in self.find_boundary_faces() {
indices.extend(connectivity.vertex_indices());
}
indices.sort_unstable();
indices.dedup();
indices
}
}
impl<T, D, Connectivity> BoundedGeometry<T> for Mesh<T, D, Connectivity>
where
T: Real,
D: DimName,
DefaultAllocator: Allocator<T, D>,
Connectivity: CellConnectivity<T, D>,
Connectivity::Cell: BoundedGeometry<T, Dimension = D>,
{
type Dimension = D;
fn bounding_box(&self) -> AxisAlignedBoundingBox<T, D> {
let mut bbs = self.cell_iter().map(|cell| cell.bounding_box());
bbs.next()
.map(|first_bb| bbs.fold(first_bb, |bb1, bb2| bb1.enclose(&bb2)))
.unwrap_or_else(|| AxisAlignedBoundingBox::new(OVector::zeros(), OVector::zeros()))
}
}
impl<T, D, C> Mesh<T, D, C>
where
T: Real,
D: DimName,
DefaultAllocator: Allocator<T, D>,
{
pub fn translate(&mut self, translation: &OVector<T, D>) {
self.transform_vertices(|p| *p += translation);
}
pub fn translated(mut self, translation: &OVector<T, D>) -> Self {
self.translate(&translation);
self
}
pub fn transform_vertices<F>(&mut self, mut transformation: F)
where
F: FnMut(&mut OPoint<T, D>),
{
for p in &mut self.vertices {
transformation(p);
}
}
pub fn transform_all_vertices<F>(&mut self, mut transformation: F)
where
F: FnMut(&mut [OPoint<T, D>]),
{
transformation(&mut self.vertices);
}
}
impl<T> QuadMesh2d<T>
where
T: Real,
{
pub fn split_into_triangles(self) -> TriangleMesh2d<T> {
let triangles = self
.connectivity()
.iter()
.flat_map(|connectivity| {
let Quad4d2Connectivity(c) = connectivity;
let quad = connectivity
.cell(self.vertices())
.expect("Indices must be in bounds");
let (tri1, tri2) = quad.split_into_triangle_connectivities();
let tri1_global = Tri3d2Connectivity([c[tri1[0]], c[tri1[1]], c[tri1[2]]]);
let tri2_global = Tri3d2Connectivity([c[tri2[0]], c[tri2[1]], c[tri2[2]]]);
once(tri1_global).chain(once(tri2_global))
})
.collect();
TriangleMesh2d::from_vertices_and_connectivity(self.vertices, triangles)
}
}
impl<T, D, C> Mesh<T, D, C>
where
T: Scalar,
D: DimName,
C: ConnectivityMut,
DefaultAllocator: Allocator<T, D>,
{
pub fn keep_cells(&self, cell_indices: &[usize]) -> Self {
let vertex_keep_table = {
let mut table = vec![false; self.vertices.len()];
for cell_index in cell_indices {
let cell_connectivity = &self.connectivity[*cell_index];
for vertex_index in cell_connectivity.vertex_indices() {
table[*vertex_index] = true;
}
}
table
};
let old_to_new_label_map = {
let mut label_map = HashMap::new();
let mut next_label = 0;
for (i, keep) in vertex_keep_table.iter().enumerate() {
if *keep {
label_map.insert(i, next_label);
next_label += 1;
}
}
label_map
};
let relabeled_cells: Vec<_> = cell_indices
.iter()
.map(|i| self.connectivity()[*i].clone())
.map(|mut cell| {
for index in cell.vertex_indices_mut() {
*index = *old_to_new_label_map
.get(index)
.expect("Index must be in map");
}
cell
})
.collect();
let relabeled_vertices: Vec<_> = vertex_keep_table
.iter()
.enumerate()
.filter_map(|(i, should_keep)| if *should_keep { Some(i) } else { None })
.map(|index| self.vertices[index].clone())
.collect();
Mesh::from_vertices_and_connectivity(relabeled_vertices, relabeled_cells)
}
}
impl<T, D, C> Mesh<T, D, C>
where
T: Scalar,
D: DimName,
C: Connectivity,
C::FaceConnectivity: ConnectivityMut,
DefaultAllocator: Allocator<T, D>,
{
pub fn extract_surface_mesh(&self) -> Mesh<T, D, C::FaceConnectivity> {
let connectivity = self
.find_boundary_faces()
.into_iter()
.map(|(face, _, _)| face)
.collect();
let new_mesh = Mesh::from_vertices_and_connectivity(self.vertices.clone(), connectivity);
let cells_to_keep: Vec<_> = (0..new_mesh.connectivity().len()).collect();
new_mesh.keep_cells(&cells_to_keep)
}
}
impl<'a, T, D, C> GeometryCollection<'a> for Mesh<T, D, C>
where
T: Scalar,
D: DimName,
C: CellConnectivity<T, D>,
DefaultAllocator: Allocator<T, D>,
{
type Geometry = C::Cell;
fn num_geometries(&self) -> usize {
self.connectivity.len()
}
fn get_geometry(&'a self, index: usize) -> Option<Self::Geometry> {
self.connectivity()
.get(index)
.map(|conn| conn.cell(self.vertices()).unwrap())
}
}