#![forbid(unsafe_code)]
use crate::core::collections::{
FastHashMap, MAX_PRACTICAL_DIMENSION_SIZE, SmallBuffer, VertexToCellsMap,
};
use crate::core::edge::EdgeKey;
use crate::core::triangulation_data_structure::{CellKey, VertexKey};
use thiserror::Error;
#[derive(Clone, Debug, Error, PartialEq, Eq)]
#[non_exhaustive]
pub enum AdjacencyIndexBuildError {
#[error("Cell {cell_key:?} references missing vertex key {vertex_key:?}")]
MissingVertexKey {
cell_key: CellKey,
vertex_key: VertexKey,
},
#[error("Cell {cell_key:?} references missing neighbor cell {neighbor_key:?}")]
MissingNeighborCell {
cell_key: CellKey,
neighbor_key: CellKey,
},
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct AdjacencyIndex {
pub vertex_to_edges: FastHashMap<VertexKey, SmallBuffer<EdgeKey, MAX_PRACTICAL_DIMENSION_SIZE>>,
pub vertex_to_cells: VertexToCellsMap,
pub cell_to_neighbors: FastHashMap<CellKey, SmallBuffer<CellKey, MAX_PRACTICAL_DIMENSION_SIZE>>,
}
impl AdjacencyIndex {
#[must_use = "this iterator is lazy and does nothing unless consumed"]
#[inline]
pub fn adjacent_cells(&self, v: VertexKey) -> impl Iterator<Item = CellKey> + '_ {
self.vertex_to_cells
.get(&v)
.into_iter()
.flat_map(|cells| cells.iter().copied())
}
#[must_use]
#[inline]
pub fn number_of_adjacent_cells(&self, v: VertexKey) -> usize {
self.vertex_to_cells.get(&v).map_or(0, SmallBuffer::len)
}
#[must_use = "this iterator is lazy and does nothing unless consumed"]
#[inline]
pub fn incident_edges(&self, v: VertexKey) -> impl Iterator<Item = EdgeKey> + '_ {
self.vertex_to_edges
.get(&v)
.into_iter()
.flat_map(|edges| edges.iter().copied())
}
#[must_use]
#[inline]
pub fn number_of_incident_edges(&self, v: VertexKey) -> usize {
self.vertex_to_edges.get(&v).map_or(0, SmallBuffer::len)
}
#[must_use = "this iterator is lazy and does nothing unless consumed"]
#[inline]
pub fn cell_neighbors(&self, c: CellKey) -> impl Iterator<Item = CellKey> + '_ {
self.cell_to_neighbors
.get(&c)
.into_iter()
.flat_map(|neighbors| neighbors.iter().copied())
}
#[must_use]
#[inline]
pub fn number_of_cell_neighbors(&self, c: CellKey) -> usize {
self.cell_to_neighbors.get(&c).map_or(0, SmallBuffer::len)
}
#[must_use = "this iterator is lazy and does nothing unless consumed"]
pub fn edges(&self) -> impl Iterator<Item = EdgeKey> + '_ {
self.vertex_to_edges.iter().flat_map(|(vk, edges)| {
let vk = *vk;
edges.iter().copied().filter(move |edge| edge.v0() == vk)
})
}
#[must_use]
pub fn number_of_edges(&self) -> usize {
self.edges().count()
}
}
#[cfg(test)]
mod tests {
use super::*;
use slotmap::SlotMap;
#[test]
fn adjacency_index_build_error_display_is_informative() {
let mut vertices: SlotMap<VertexKey, ()> = SlotMap::with_key();
let mut cells: SlotMap<CellKey, ()> = SlotMap::with_key();
let vk = vertices.insert(());
let ck = cells.insert(());
let err = AdjacencyIndexBuildError::MissingVertexKey {
cell_key: ck,
vertex_key: vk,
};
let msg = err.to_string();
assert!(msg.contains("references missing vertex key"));
let err = AdjacencyIndexBuildError::MissingNeighborCell {
cell_key: ck,
neighbor_key: ck,
};
let msg = err.to_string();
assert!(msg.contains("references missing neighbor cell"));
}
#[test]
fn adjacency_index_is_send_sync_unpin() {
fn assert_auto_traits<T: Send + Sync + Unpin>() {}
assert_auto_traits::<AdjacencyIndex>();
}
#[test]
fn adjacency_index_query_helpers_are_consistent() {
use crate::core::delaunay_triangulation::DelaunayTriangulation;
use crate::vertex;
let vertices: Vec<_> = vec![
vertex!([0.0, 0.0, 0.0]),
vertex!([2.0, 0.0, 0.0]),
vertex!([1.0, 2.0, 0.0]),
vertex!([1.0, 0.7, 1.5]),
vertex!([1.0, 0.7, -1.5]),
];
let dt: DelaunayTriangulation<_, (), (), 3> =
DelaunayTriangulation::new(&vertices).unwrap();
let tri = dt.as_triangulation();
let index = tri.build_adjacency_index().unwrap();
let shared_vertex_key = tri
.vertices()
.find_map(|(vk, _)| (tri.vertex_coords(vk)? == [0.0, 0.0, 0.0]).then_some(vk))
.unwrap();
assert_eq!(
index.adjacent_cells(shared_vertex_key).count(),
index.number_of_adjacent_cells(shared_vertex_key)
);
assert_eq!(
index.incident_edges(shared_vertex_key).count(),
index.number_of_incident_edges(shared_vertex_key)
);
assert!(index.number_of_incident_edges(shared_vertex_key) > 0);
let cell_keys: Vec<_> = tri.cells().map(|(ck, _)| ck).collect();
for &ck in &cell_keys {
assert_eq!(
index.cell_neighbors(ck).count(),
index.number_of_cell_neighbors(ck)
);
assert_eq!(index.number_of_cell_neighbors(ck), 1);
}
let edges: std::collections::HashSet<_> = index.edges().collect();
assert_eq!(edges.len(), index.number_of_edges());
assert!(edges.iter().all(|e| e.v0() <= e.v1()));
assert_eq!(index.adjacent_cells(VertexKey::default()).count(), 0);
assert_eq!(index.number_of_adjacent_cells(VertexKey::default()), 0);
assert_eq!(index.incident_edges(VertexKey::default()).count(), 0);
assert_eq!(index.number_of_incident_edges(VertexKey::default()), 0);
assert_eq!(index.cell_neighbors(CellKey::default()).count(), 0);
assert_eq!(index.number_of_cell_neighbors(CellKey::default()), 0);
}
}