use crate::index::*;
use crate::mesh::topology::*;
use crate::mesh::{attrib::*, PolyMesh, TetMeshExt, TriMesh};
use crate::Real;
pub trait Connectivity<Src: ElementIndex<usize>, Via: ElementIndex<usize>> {
type Topo: Default;
fn precompute_topo(&self) -> Self::Topo {
Default::default()
}
fn num_elements(&self) -> usize;
fn push_neighbours<T: Default + PartialEq>(
&self,
index: Src,
stack: &mut Vec<Src>,
topo: &Self::Topo,
attribute: Option<&[T]>,
);
fn connectivity(&self) -> (Vec<usize>, usize) {
self.connectivity_via_attrib_fn::<(), _>(|| None)
}
fn connectivity_via_attrib<T>(&self, attrib: Option<&str>) -> (Vec<usize>, usize)
where
Self: Attrib,
Src: AttribIndex<Self>,
T: Default + PartialEq + 'static,
{
self.connectivity_via_attrib_fn::<T, _>(|| {
attrib.and_then(|name| self.attrib_as_slice::<T, Src>(name).ok())
})
}
fn connectivity_via_attrib_fn<'a, T, F>(&self, f: F) -> (Vec<usize>, usize)
where
T: Default + PartialEq + 'a,
F: FnOnce() -> Option<&'a [T]>,
{
let mut cur_component_id = 0;
let mut stack: Vec<Src> = Vec::new();
let num_element_indices = self.num_elements();
let mut component_ids = vec![Index::INVALID; num_element_indices];
let data = self.precompute_topo();
let attrib_data = f();
for elem in 0..num_element_indices {
if component_ids[elem].is_valid() {
continue;
}
stack.push(elem.into());
while let Some(elem) = stack.pop() {
let elem_idx: usize = elem.into();
if !component_ids[elem_idx].is_valid() {
component_ids[elem_idx] = cur_component_id.into();
self.push_neighbours(elem, &mut stack, &data, attrib_data);
}
}
cur_component_id += 1;
}
debug_assert!(component_ids.iter().all(|&x| x.is_valid()));
(bytemuck::cast_vec(component_ids), cur_component_id)
}
}
impl<M: VertexCell + CellVertex + NumVertices> Connectivity<VertexIndex, CellIndex> for M {
type Topo = ();
fn num_elements(&self) -> usize {
self.num_vertices()
}
fn push_neighbours<T: Default + PartialEq>(
&self,
index: VertexIndex,
stack: &mut Vec<VertexIndex>,
_: &(),
_: Option<&[T]>,
) {
for which_cell in 0..self.num_cells_at_vertex(index) {
let cell = self.vertex_to_cell(index, which_cell).unwrap();
for which_vtx in 0..self.num_vertices_at_cell(cell) {
let neigh_vtx = self.cell_to_vertex(cell, which_vtx).unwrap();
if neigh_vtx != index {
stack.push(neigh_vtx);
}
}
}
}
}
impl<M: FaceVertex + NumVertices + NumFaces> Connectivity<VertexIndex, FaceIndex> for M {
type Topo = (Vec<usize>, Vec<usize>);
fn precompute_topo(&self) -> Self::Topo {
self.reverse_topo()
}
fn num_elements(&self) -> usize {
self.num_vertices()
}
fn push_neighbours<T: Default + PartialEq>(
&self,
index: VertexIndex,
stack: &mut Vec<VertexIndex>,
topo: &Self::Topo,
_: Option<&[T]>,
) {
let (face_indices, face_offsets) = topo;
let idx = usize::from(index);
for face in (face_offsets[idx]..face_offsets[idx + 1]).map(|i| face_indices[i]) {
for which_vtx in 0..self.num_vertices_at_face(face) {
let neigh_vtx = self.face_to_vertex(face, which_vtx).unwrap();
if neigh_vtx != index {
stack.push(neigh_vtx);
}
}
}
}
}
impl<M: FaceVertex + NumFaces + NumVertices> Connectivity<FaceVertexIndex, VertexIndex> for M {
type Topo = (Vec<usize>, Vec<usize>);
fn precompute_topo(&self) -> Self::Topo {
self.reverse_source_topo() }
fn num_elements(&self) -> usize {
self.num_face_vertices()
}
fn push_neighbours<T: Default + PartialEq>(
&self,
index: FaceVertexIndex,
stack: &mut Vec<FaceVertexIndex>,
topo: &Self::Topo,
attrib: Option<&[T]>,
) {
let (fv_indices, fv_offsets) = topo;
let vtx_idx = usize::from(self.vertex(index));
let idx = usize::from(index);
let def_val = T::default();
let primary_attrib_val = attrib.map(|a| &a[idx]).unwrap_or_else(|| &def_val);
for face_vertex in (fv_offsets[vtx_idx]..fv_offsets[vtx_idx + 1]).map(|i| fv_indices[i]) {
let neigh_attrib_val = attrib.map(|a| &a[face_vertex]).unwrap_or_else(|| &def_val);
if primary_attrib_val == neigh_attrib_val {
stack.push(face_vertex.into());
}
}
}
}
impl<T: Real> TriMesh<T> {
pub fn vertex_connectivity(&self) -> (Vec<usize>, usize) {
Connectivity::<VertexIndex, FaceIndex>::connectivity(self)
}
}
impl<T: Real> PolyMesh<T> {
pub fn vertex_connectivity(&self) -> (Vec<usize>, usize) {
Connectivity::<VertexIndex, FaceIndex>::connectivity(self)
}
}
impl<T: Real> TetMeshExt<T> {
pub fn vertex_connectivity(&self) -> (Vec<usize>, usize) {
Connectivity::<VertexIndex, CellIndex>::connectivity(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mesh::{TetMeshExt, TriMesh};
#[test]
fn tetmesh_connectivity() {
let verts = vec![[0.0; 3]; 12];
let indices = vec![[0, 1, 2, 3], [1, 2, 3, 4], [5, 6, 7, 8], [8, 9, 10, 11]];
let tetmesh = TetMeshExt::new(verts, indices);
assert_eq!(
tetmesh.connectivity(),
(vec![0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], 2)
);
}
#[test]
fn trimesh_connectivity() {
let verts = vec![[0.0; 3]; 7];
let indices = vec![[0, 1, 2], [1, 2, 3], [4, 5, 6]];
let trimesh = TriMesh::new(verts, indices);
assert_eq!(
trimesh.vertex_connectivity(),
(vec![0, 0, 0, 0, 1, 1, 1], 2)
);
}
}