#![forbid(unsafe_code)]
use crate::core::collections::SimplexKeyBuffer;
use crate::core::tds::{SimplexKey, Tds, VertexKey};
use crate::core::vertex::Vertex;
use slotmap::Key;
use std::fmt;
use thiserror::Error;
#[derive(Copy, Clone, Debug, Error, PartialEq, Eq)]
#[non_exhaustive]
pub enum EdgeKeyError {
#[error("Edge endpoints must be distinct, got {endpoint:?} twice")]
DuplicateEndpoint {
endpoint: VertexKey,
},
#[error("Edge endpoint {endpoint:?} is not present in the TDS")]
MissingEndpoint {
endpoint: VertexKey,
},
#[error("Vertices {v0:?} and {v1:?} are not joined by an edge in the TDS")]
EdgeNotFound {
v0: VertexKey,
v1: VertexKey,
},
#[error("Vertex incidence index does not list any simplex containing edge {v0:?}-{v1:?}")]
MissingEdgeIncidence {
v0: VertexKey,
v1: VertexKey,
},
#[error("Vertex incidence index for {vertex_key:?} references missing simplex {simplex_key:?}")]
DanglingVertexIncidence {
vertex_key: VertexKey,
simplex_key: SimplexKey,
},
#[error("Vertex incidence index for {vertex_key:?} is missing simplex {simplex_key:?}")]
MissingVertexIncidence {
vertex_key: VertexKey,
simplex_key: SimplexKey,
},
#[error(
"Vertex incidence index for {vertex_key:?} references simplex {simplex_key:?}, but the simplex does not contain that vertex"
)]
VertexIncidenceMismatch {
vertex_key: VertexKey,
simplex_key: SimplexKey,
},
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct EdgeKey {
v0: VertexKey,
v1: VertexKey,
}
impl EdgeKey {
pub fn try_new<U, V, const D: usize>(
tds: &Tds<U, V, D>,
a: VertexKey,
b: VertexKey,
) -> Result<Self, EdgeKeyError> {
if a == b {
return Err(EdgeKeyError::DuplicateEndpoint { endpoint: a });
}
if tds.vertex(a).is_none() {
return Err(EdgeKeyError::MissingEndpoint { endpoint: a });
}
if tds.vertex(b).is_none() {
return Err(EdgeKeyError::MissingEndpoint { endpoint: b });
}
let key = Self::from_validated_endpoints(a, b);
let _validated_edge_star = Self::parse_edge_star(tds, key)?;
Ok(key)
}
fn parse_edge_star<U, V, const D: usize>(
tds: &Tds<U, V, D>,
key: Self,
) -> Result<SimplexKeyBuffer, EdgeKeyError> {
let (v0, v1) = key.endpoints();
let v0_degree = tds.vertex_to_simplices_index().number_of_simplices(v0);
let v1_degree = tds.vertex_to_simplices_index().number_of_simplices(v1);
let (primary, secondary) = if v0_degree <= v1_degree {
(v0, v1)
} else {
(v1, v0)
};
let primary_edge_simplices = Self::edge_simplices_in_star(tds, primary, secondary)?;
let secondary_edge_simplices = Self::edge_simplices_in_star(tds, secondary, primary)?;
for &simplex_key in primary_edge_simplices.as_slice() {
if !secondary_edge_simplices.contains(&simplex_key) {
return Err(EdgeKeyError::MissingVertexIncidence {
vertex_key: secondary,
simplex_key,
});
}
}
for &simplex_key in secondary_edge_simplices.as_slice() {
if !primary_edge_simplices.contains(&simplex_key) {
return Err(EdgeKeyError::MissingVertexIncidence {
vertex_key: primary,
simplex_key,
});
}
}
if !primary_edge_simplices.is_empty() {
return Ok(primary_edge_simplices);
}
if EdgeView::endpoints_share_stored_simplex(tds, v0, v1) {
return Err(EdgeKeyError::MissingEdgeIncidence { v0, v1 });
}
Err(EdgeKeyError::EdgeNotFound { v0, v1 })
}
fn edge_simplices_in_star<U, V, const D: usize>(
tds: &Tds<U, V, D>,
source: VertexKey,
target: VertexKey,
) -> Result<SimplexKeyBuffer, EdgeKeyError> {
let mut edge_simplices = SimplexKeyBuffer::new();
for simplex_key in tds.simplex_keys_containing_vertex(source) {
let simplex =
tds.simplex(simplex_key)
.ok_or(EdgeKeyError::DanglingVertexIncidence {
vertex_key: source,
simplex_key,
})?;
if !simplex.contains_vertex(source) {
return Err(EdgeKeyError::VertexIncidenceMismatch {
vertex_key: source,
simplex_key,
});
}
if simplex.contains_vertex(target) {
edge_simplices.push(simplex_key);
}
}
Ok(edge_simplices)
}
#[inline]
#[must_use]
pub(crate) fn from_validated_endpoints(a: VertexKey, b: VertexKey) -> Self {
let a_raw = a.data().as_ffi();
let b_raw = b.data().as_ffi();
if a_raw <= b_raw {
Self { v0: a, v1: b }
} else {
Self { v0: b, v1: a }
}
}
#[inline]
#[must_use]
pub const fn v0(self) -> VertexKey {
self.v0
}
#[inline]
#[must_use]
pub const fn v1(self) -> VertexKey {
self.v1
}
#[inline]
#[must_use]
pub const fn endpoints(self) -> (VertexKey, VertexKey) {
(self.v0, self.v1)
}
pub fn view<U, V, const D: usize>(
self,
tds: &Tds<U, V, D>,
) -> Result<EdgeView<'_, U, V, D>, EdgeKeyError> {
EdgeView::try_new(tds, self)
}
}
#[must_use]
pub struct EdgeView<'tds, U, V, const D: usize> {
tds: &'tds Tds<U, V, D>,
key: EdgeKey,
vertices: (&'tds Vertex<U, D>, &'tds Vertex<U, D>),
incident_simplices: SimplexKeyBuffer,
}
impl<'tds, U, V, const D: usize> EdgeView<'tds, U, V, D> {
pub fn try_new(tds: &'tds Tds<U, V, D>, key: EdgeKey) -> Result<Self, EdgeKeyError> {
let (v0, v1) = key.endpoints();
if v0 == v1 {
return Err(EdgeKeyError::DuplicateEndpoint { endpoint: v0 });
}
let first = tds
.vertex(v0)
.ok_or(EdgeKeyError::MissingEndpoint { endpoint: v0 })?;
let second = tds
.vertex(v1)
.ok_or(EdgeKeyError::MissingEndpoint { endpoint: v1 })?;
let key = EdgeKey::from_validated_endpoints(v0, v1);
let incident_simplices = EdgeKey::parse_edge_star(tds, key)?;
Ok(Self {
tds,
key,
vertices: (first, second),
incident_simplices,
})
}
#[inline]
#[must_use]
pub const fn key(&self) -> EdgeKey {
self.key
}
#[inline]
#[must_use]
pub(crate) const fn tds(&self) -> &'tds Tds<U, V, D> {
self.tds
}
#[inline]
#[must_use]
pub const fn endpoint_keys(&self) -> (VertexKey, VertexKey) {
self.key.endpoints()
}
#[inline]
#[must_use]
pub const fn vertices(&self) -> (&'tds Vertex<U, D>, &'tds Vertex<U, D>) {
self.vertices
}
#[must_use]
pub fn incident_simplices(&self) -> &[SimplexKey] {
self.incident_simplices.as_slice()
}
fn endpoints_share_stored_simplex(tds: &Tds<U, V, D>, v0: VertexKey, v1: VertexKey) -> bool {
tds.simplices().any(|(_simplex_key, simplex)| {
simplex.contains_vertex(v0) && simplex.contains_vertex(v1)
})
}
}
impl<U, V, const D: usize> fmt::Debug for EdgeView<'_, U, V, D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EdgeView")
.field("key", &self.key)
.field("vertices", &self.key.endpoints())
.field("incident_simplices", &self.incident_simplices)
.field("dimension", &D)
.finish()
}
}
impl<U, V, const D: usize> Clone for EdgeView<'_, U, V, D> {
fn clone(&self) -> Self {
Self {
tds: self.tds,
key: self.key,
vertices: self.vertices,
incident_simplices: self.incident_simplices.clone(),
}
}
}
impl<U, V, const D: usize> PartialEq for EdgeView<'_, U, V, D> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.tds, other.tds) && self.key == other.key
}
}
impl<U, V, const D: usize> Eq for EdgeView<'_, U, V, D> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::simplex::Simplex;
use crate::prelude::DelaunayTriangulationBuilder;
use crate::vertex;
use std::{
collections::{BTreeSet, HashSet},
ptr,
};
fn with_triangle_tds(test: impl FnOnce(&Tds<(), (), 2>, [VertexKey; 3])) {
let vertices = [
vertex!([0.0, 0.0]).unwrap(),
vertex!([1.0, 0.0]).unwrap(),
vertex!([0.0, 1.0]).unwrap(),
];
let dt = DelaunayTriangulationBuilder::new(&vertices)
.build()
.unwrap();
let simplex = dt.simplices().next().unwrap().1;
let vertices = simplex.vertices();
let simplex_vertices = [vertices[0], vertices[1], vertices[2]];
test(dt.tds(), simplex_vertices);
}
#[test]
fn edge_key_is_canonical() {
with_triangle_tds(|tds, [a, b, _c]| {
let e1 = EdgeKey::try_new(tds, a, b).unwrap();
let e2 = EdgeKey::try_new(tds, b, a).unwrap();
assert_eq!(e1, e2);
assert!(e1.v0().data().as_ffi() <= e1.v1().data().as_ffi());
});
}
#[test]
fn edge_key_endpoints_roundtrip() {
with_triangle_tds(|tds, [a, b, _c]| {
let e = EdgeKey::try_new(tds, b, a).unwrap();
let (v0, v1) = e.endpoints();
assert_eq!(v0, e.v0());
assert_eq!(v1, e.v1());
assert!(v0.data().as_ffi() <= v1.data().as_ffi());
assert_eq!(EdgeKey::try_new(tds, a, b).unwrap(), e);
});
}
#[test]
fn edge_key_rejects_duplicate_endpoint() {
with_triangle_tds(|tds, [a, _b, _c]| {
assert_eq!(
EdgeKey::try_new(tds, a, a),
Err(EdgeKeyError::DuplicateEndpoint { endpoint: a })
);
});
}
#[test]
fn edge_key_rejects_missing_endpoint() {
with_triangle_tds(|tds, [a, _b, _c]| {
let missing = VertexKey::default();
assert_eq!(
EdgeKey::try_new(tds, a, missing),
Err(EdgeKeyError::MissingEndpoint { endpoint: missing })
);
});
}
#[test]
fn edge_key_rejects_missing_first_endpoint() {
with_triangle_tds(|tds, [_a, b, _c]| {
let missing = VertexKey::default();
assert_eq!(
EdgeKey::try_new(tds, missing, b),
Err(EdgeKeyError::MissingEndpoint { endpoint: missing })
);
});
}
#[test]
fn edge_key_rejects_live_vertices_without_edge() {
let vertices = [
vertex!([0.0, 0.0]).unwrap(),
vertex!([1.0, 0.0]).unwrap(),
vertex!([1.0, 1.0]).unwrap(),
vertex!([0.0, 1.0]).unwrap(),
];
let dt = DelaunayTriangulationBuilder::new(&vertices)
.build()
.unwrap();
let keys: Vec<VertexKey> = dt.tds().vertex_keys().collect();
let missing_edge = keys.iter().enumerate().find_map(|(i, &a)| {
keys.iter().skip(i + 1).copied().find_map(|b| {
matches!(
EdgeKey::try_new(dt.tds(), a, b),
Err(EdgeKeyError::EdgeNotFound { .. })
)
.then_some((a, b))
})
});
let (a, b) = missing_edge.expect("square triangulation should have one missing diagonal");
assert_eq!(
EdgeKey::try_new(dt.tds(), a, b),
Err(EdgeKeyError::EdgeNotFound { v0: a, v1: b })
);
}
#[test]
fn edge_key_is_hashable_and_orderable() {
with_triangle_tds(|tds, [a, b, c]| {
let mut hash_set: HashSet<EdgeKey> = HashSet::new();
hash_set.insert(EdgeKey::try_new(tds, a, b).unwrap());
hash_set.insert(EdgeKey::try_new(tds, b, a).unwrap());
hash_set.insert(EdgeKey::try_new(tds, a, c).unwrap());
assert_eq!(hash_set.len(), 2);
let mut btree_set: BTreeSet<EdgeKey> = BTreeSet::new();
btree_set.insert(EdgeKey::try_new(tds, a, b).unwrap());
btree_set.insert(EdgeKey::try_new(tds, b, a).unwrap());
btree_set.insert(EdgeKey::try_new(tds, a, c).unwrap());
assert_eq!(btree_set.len(), 2);
});
}
#[test]
fn edge_view_exposes_endpoint_vertices_and_key() {
with_triangle_tds(|tds, [a, b, _c]| {
let key = EdgeKey::try_new(tds, a, b).unwrap();
let view = key.view(tds).unwrap();
let (first, second) = view.vertices();
assert_eq!(view.key(), key);
assert!(ptr::eq(view.tds(), tds));
assert_eq!(view.endpoint_keys(), key.endpoints());
assert_eq!(first.uuid(), tds.vertex(view.key().v0()).unwrap().uuid());
assert_eq!(second.uuid(), tds.vertex(view.key().v1()).unwrap().uuid());
});
}
#[test]
fn edge_view_clone_debug_and_equality_use_owner_and_key() {
with_triangle_tds(|tds, [a, b, c]| {
let first = EdgeKey::try_new(tds, a, b).unwrap().view(tds).unwrap();
let first_clone = first.clone();
let second = EdgeKey::try_new(tds, a, c).unwrap().view(tds).unwrap();
assert_eq!(first, first_clone);
assert_ne!(first, second);
let debug = format!("{first:?}");
assert!(debug.contains("EdgeView"));
assert!(debug.contains("incident_simplices"));
});
}
#[test]
fn edge_view_enumerates_incident_simplices_from_incidence_index() {
let mut tds: Tds<(), (), 2> = Tds::empty();
let v0 = tds
.insert_vertex_with_mapping(vertex!([0.0, 0.0]).unwrap())
.unwrap();
let v1 = tds
.insert_vertex_with_mapping(vertex!([1.0, 0.0]).unwrap())
.unwrap();
let v2 = tds
.insert_vertex_with_mapping(vertex!([0.0, 1.0]).unwrap())
.unwrap();
let v3 = tds
.insert_vertex_with_mapping(vertex!([1.0, 1.0]).unwrap())
.unwrap();
let c0 = tds
.insert_simplex_with_mapping(Simplex::try_new(vec![v0, v1, v2]).unwrap())
.unwrap();
let c1 = tds
.insert_simplex_with_mapping(Simplex::try_new(vec![v1, v0, v3]).unwrap())
.unwrap();
let _only_v0 = tds
.insert_simplex_with_mapping(Simplex::try_new(vec![v0, v2, v3]).unwrap())
.unwrap();
let edge = EdgeKey::try_new(&tds, v1, v0).unwrap();
let incident: HashSet<_> = edge
.view(&tds)
.unwrap()
.incident_simplices()
.iter()
.copied()
.collect();
assert_eq!(incident, HashSet::from([c0, c1]));
}
#[test]
fn edge_key_rejects_partial_missing_reverse_vertex_incidence() {
let mut tds: Tds<(), (), 2> = Tds::empty();
let v0 = tds
.insert_vertex_with_mapping(vertex!([0.0, 0.0]).unwrap())
.unwrap();
let v1 = tds
.insert_vertex_with_mapping(vertex!([1.0, 0.0]).unwrap())
.unwrap();
let v2 = tds
.insert_vertex_with_mapping(vertex!([0.0, 1.0]).unwrap())
.unwrap();
let v3 = tds
.insert_vertex_with_mapping(vertex!([1.0, 1.0]).unwrap())
.unwrap();
let retained_simplex = tds
.insert_simplex_with_mapping(Simplex::try_new(vec![v0, v1, v2]).unwrap())
.unwrap();
let missing_simplex = tds
.insert_simplex_with_mapping(Simplex::try_new(vec![v1, v0, v3]).unwrap())
.unwrap();
let edge = EdgeKey::from_validated_endpoints(v0, v1);
let (_first, second) = edge.endpoints();
tds.clear_vertex_incidence_for_test(second);
tds.add_simplex_to_vertex_incidence_for_test(second, retained_simplex);
let expected_error = EdgeKeyError::MissingVertexIncidence {
vertex_key: second,
simplex_key: missing_simplex,
};
assert_eq!(EdgeKey::try_new(&tds, v0, v1), Err(expected_error));
assert_eq!(edge.view(&tds), Err(expected_error));
}
#[test]
fn edge_view_rejects_stale_vertex_incidence() {
let mut tds: Tds<(), (), 3> = Tds::empty();
let v0 = tds
.insert_vertex_with_mapping(vertex!([0.0, 0.0, 0.0]).unwrap())
.unwrap();
let v1 = tds
.insert_vertex_with_mapping(vertex!([1.0, 0.0, 0.0]).unwrap())
.unwrap();
let v2 = tds
.insert_vertex_with_mapping(vertex!([0.0, 1.0, 0.0]).unwrap())
.unwrap();
let v3 = tds
.insert_vertex_with_mapping(vertex!([0.0, 0.0, 1.0]).unwrap())
.unwrap();
let stale_simplex = tds
.insert_simplex_with_mapping(Simplex::try_new(vec![v0, v1, v2, v3]).unwrap())
.unwrap();
tds.remove_simplex_storage_only_for_test(stale_simplex);
let edge = EdgeKey::from_validated_endpoints(v0, v1);
assert_eq!(
EdgeKey::try_new(&tds, v0, v1),
Err(EdgeKeyError::DanglingVertexIncidence {
vertex_key: edge.v0(),
simplex_key: stale_simplex
})
);
assert_eq!(
edge.view(&tds),
Err(EdgeKeyError::DanglingVertexIncidence {
vertex_key: edge.v0(),
simplex_key: stale_simplex
})
);
}
#[test]
fn edge_view_rejects_missing_reverse_vertex_incidence() {
let mut tds: Tds<(), (), 2> = Tds::empty();
let v0 = tds
.insert_vertex_with_mapping(vertex!([0.0, 0.0]).unwrap())
.unwrap();
let v1 = tds
.insert_vertex_with_mapping(vertex!([1.0, 0.0]).unwrap())
.unwrap();
let v2 = tds
.insert_vertex_with_mapping(vertex!([0.0, 1.0]).unwrap())
.unwrap();
let simplex_key = tds
.insert_simplex_with_mapping(Simplex::try_new(vec![v0, v1, v2]).unwrap())
.unwrap();
let edge = EdgeKey::from_validated_endpoints(v0, v1);
let (_first, second) = edge.endpoints();
tds.clear_vertex_incidence_for_test(second);
assert_eq!(
EdgeKey::try_new(&tds, v0, v1),
Err(EdgeKeyError::MissingVertexIncidence {
vertex_key: second,
simplex_key
})
);
assert_eq!(
edge.view(&tds),
Err(EdgeKeyError::MissingVertexIncidence {
vertex_key: second,
simplex_key
})
);
}
#[test]
fn edge_view_rejects_missing_forward_vertex_incidence() {
let mut tds: Tds<(), (), 2> = Tds::empty();
let v0 = tds
.insert_vertex_with_mapping(vertex!([0.0, 0.0]).unwrap())
.unwrap();
let v1 = tds
.insert_vertex_with_mapping(vertex!([1.0, 0.0]).unwrap())
.unwrap();
let v2 = tds
.insert_vertex_with_mapping(vertex!([0.0, 1.0]).unwrap())
.unwrap();
let simplex_key = tds
.insert_simplex_with_mapping(Simplex::try_new(vec![v0, v1, v2]).unwrap())
.unwrap();
let edge = EdgeKey::from_validated_endpoints(v0, v1);
let (first, _second) = edge.endpoints();
tds.clear_vertex_incidence_for_test(first);
assert_eq!(
EdgeKey::try_new(&tds, v0, v1),
Err(EdgeKeyError::MissingVertexIncidence {
vertex_key: first,
simplex_key
})
);
assert_eq!(
edge.view(&tds),
Err(EdgeKeyError::MissingVertexIncidence {
vertex_key: first,
simplex_key
})
);
}
#[test]
fn edge_view_rejects_live_endpoints_without_stored_edge() {
let mut tds: Tds<(), (), 2> = Tds::empty();
let v0 = tds
.insert_vertex_with_mapping(vertex!([0.0, 0.0]).unwrap())
.unwrap();
let v1 = tds
.insert_vertex_with_mapping(vertex!([1.0, 0.0]).unwrap())
.unwrap();
let edge = EdgeKey::from_validated_endpoints(v0, v1);
let (v0, v1) = edge.endpoints();
assert_eq!(edge.view(&tds), Err(EdgeKeyError::EdgeNotFound { v0, v1 }));
}
#[test]
fn edge_view_rejects_stale_endpoint_handles() {
with_triangle_tds(|_tds, [a, b, _c]| {
let stale = EdgeKey::from_validated_endpoints(a, b);
let empty: Tds<(), (), 2> = Tds::empty();
assert_eq!(
stale.view(&empty),
Err(EdgeKeyError::MissingEndpoint {
endpoint: stale.v0()
})
);
});
}
}