#![forbid(unsafe_code)]
use super::model::UnverifiedTds;
use super::{
SimplexKey, Tds, TdsConstructionError, TdsError, TdsMutationError, TdsRollbackSavepoint,
VertexKey,
};
use crate::core::collections::SimplexVertexKeyBuffer;
use crate::core::simplex::Simplex;
use crate::core::simplex::SimplexValidationError;
use crate::core::vertex::Vertex;
use crate::refinement::RefinementError;
use thiserror::Error;
#[derive(Clone, Debug, Error, PartialEq)]
#[non_exhaustive]
pub enum TdsDraftInsertionError {
#[error("the explicit simplex specification is invalid: {source}")]
SimplexCreation {
#[source]
source: SimplexValidationError,
},
#[error("the explicit simplex could not be inserted into the TDS draft: {source}")]
SimplexInsertion {
#[source]
source: Box<TdsConstructionError>,
},
}
#[derive(Clone, Debug, Error, PartialEq)]
#[non_exhaustive]
pub enum TdsDraftError {
#[error("neighbor assignment failed while publishing a TDS draft: {source}")]
NeighborAssignment {
#[source]
source: Box<TdsError>,
},
#[error("incident-simplex assignment failed while publishing a TDS draft: {source}")]
IncidentAssignment {
#[source]
source: Box<TdsMutationError>,
},
#[error("orientation normalization failed while publishing a TDS draft: {source}")]
OrientationNormalization {
#[source]
source: Box<TdsError>,
},
#[error("Levels 1-2 validation failed while publishing a TDS draft: {source}")]
Validation {
#[source]
source: Box<TdsError>,
},
}
#[derive(Clone, Debug)]
pub struct TdsDraft<U, V, const D: usize> {
storage: UnverifiedTds<U, V, D>,
}
impl<U, V, const D: usize> TdsDraft<U, V, D> {
#[must_use]
pub fn new() -> Self {
Self {
storage: UnverifiedTds::empty_unpublished(),
}
}
pub fn insert_vertex(
&mut self,
vertex: Vertex<U, D>,
) -> Result<VertexKey, TdsConstructionError> {
self.storage.insert_vertex_with_mapping(vertex)
}
pub fn insert_simplex(
&mut self,
vertices: impl IntoIterator<Item = VertexKey>,
) -> Result<SimplexKey, TdsDraftInsertionError> {
self.insert_simplex_with_data(vertices, None)
}
pub fn insert_simplex_with_data(
&mut self,
vertices: impl IntoIterator<Item = VertexKey>,
data: Option<V>,
) -> Result<SimplexKey, TdsDraftInsertionError> {
let vertex_keys: SimplexVertexKeyBuffer = vertices.into_iter().collect();
let simplex = Simplex::try_new_with_data(vertex_keys, data)
.map_err(|source| TdsDraftInsertionError::SimplexCreation { source })?;
self.storage
.insert_simplex_with_mapping(simplex)
.map_err(|source| TdsDraftInsertionError::SimplexInsertion {
source: Box::new(source),
})
}
#[must_use]
pub fn number_of_vertices(&self) -> usize {
self.storage.number_of_vertices()
}
#[must_use]
pub fn number_of_simplices(&self) -> usize {
self.storage.number_of_simplices()
}
#[must_use]
pub fn dim(&self) -> i32 {
self.storage.dim()
}
#[must_use]
pub fn vertex(&self, key: VertexKey) -> Option<&Vertex<U, D>> {
self.storage.vertex(key)
}
pub fn vertices(&self) -> impl Iterator<Item = (VertexKey, &Vertex<U, D>)> {
self.storage.vertices()
}
#[must_use]
pub fn is_coherently_oriented(&self) -> bool {
self.storage.is_coherently_oriented()
}
pub fn validate_structure(&self) -> Result<(), TdsError> {
self.storage.validate()
}
pub(crate) fn insert_simplex_prechecked_topology(
&mut self,
simplex: Simplex<V, D>,
) -> Result<SimplexKey, TdsConstructionError> {
self.storage
.insert_simplex_with_mapping_prechecked_topology(simplex)
}
pub fn finish(self) -> Result<Tds<U, V, D>, TdsDraftError> {
self.finish_recoverable()
.map_err(RefinementError::into_reason)
}
pub(crate) fn begin_rollback_savepoint(&mut self) -> TdsRollbackSavepoint {
self.storage.begin_rollback_savepoint()
}
pub(crate) fn rollback_savepoint(&mut self, savepoint: TdsRollbackSavepoint) {
self.storage.rollback_savepoint(savepoint);
}
pub(crate) fn commit_savepoint(&mut self, savepoint: TdsRollbackSavepoint) {
self.storage.commit_savepoint(savepoint);
}
pub(crate) fn finish_recoverable(
mut self,
) -> Result<Tds<U, V, D>, RefinementError<Self, TdsDraftError>> {
if let Err(source) = self.storage.assign_neighbors() {
return Err(RefinementError::new(
self,
TdsDraftError::NeighborAssignment {
source: Box::new(source),
},
));
}
if let Err(source) = self.storage.assign_incident_simplices() {
return Err(RefinementError::new(
self,
TdsDraftError::IncidentAssignment {
source: Box::new(source),
},
));
}
if let Err(source) = self.storage.normalize_coherent_orientation() {
return Err(RefinementError::new(
self,
TdsDraftError::OrientationNormalization {
source: Box::new(source),
},
));
}
self.storage.publish_recoverable().map_err(|failure| {
let (storage, source) = (*failure).into_parts();
RefinementError::new(
Self { storage },
TdsDraftError::Validation {
source: Box::new(source),
},
)
})
}
pub(crate) const fn from_rolled_back_storage(storage: Tds<U, V, D>) -> Self {
Self {
storage: UnverifiedTds { storage },
}
}
}
impl<U, V, const D: usize> Default for TdsDraft<U, V, D> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod test_support {
use super::super::TopologyOwnerId;
use super::*;
impl<U, V, const D: usize> TdsDraft<U, V, D> {
pub(crate) fn topology_owner_id(&self) -> TopologyOwnerId {
self.storage.topology_owner_id()
}
pub(crate) fn topology_generation(&self) -> u64 {
self.storage.generation()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::vertex;
use std::assert_matches;
#[test]
fn empty_draft_publishes_as_the_verified_empty_complex() {
let draft: TdsDraft<(), (), 2> = TdsDraft::new();
let tds = draft.finish().unwrap();
assert_eq!(tds.number_of_vertices(), 0);
assert_eq!(tds.number_of_simplices(), 0);
assert_matches!(
tds.construction_state(),
crate::core::tds::TriangulationConstructionState::Constructed
);
}
#[test]
fn partial_bootstrap_draft_cannot_publish() {
let mut draft: TdsDraft<(), (), 2> = TdsDraft::new();
draft.insert_vertex(vertex![0.0, 0.0].unwrap()).unwrap();
let error = draft.finish().unwrap_err();
assert_matches!(
error,
TdsDraftError::Validation {
source
} if matches!(
*source,
TdsError::IncompleteConstruction {
dimension: 2,
vertex_count: 1,
simplex_count: 0,
}
)
);
}
#[test]
fn duplicate_simplex_rejection_preserves_the_staged_payload_and_topology() {
let mut draft: TdsDraft<(), usize, 2> = TdsDraft::default();
assert_eq!(draft.number_of_vertices(), 0);
assert_eq!(draft.number_of_simplices(), 0);
assert_eq!(draft.dim(), -1);
let v0 = draft.insert_vertex(vertex![0.0, 0.0].unwrap()).unwrap();
let v1 = draft.insert_vertex(vertex![1.0, 0.0].unwrap()).unwrap();
let v2 = draft.insert_vertex(vertex![0.0, 1.0].unwrap()).unwrap();
let simplex_key = draft
.insert_simplex_with_data([v0, v1, v2], Some(7))
.unwrap();
assert_eq!(draft.number_of_vertices(), 3);
assert_eq!(draft.number_of_simplices(), 1);
assert_eq!(draft.dim(), 2);
assert_matches!(
draft.insert_simplex([v0, v1, v2]),
Err(TdsDraftInsertionError::SimplexInsertion { source })
if matches!(
source.as_ref(),
TdsConstructionError::ValidationError {
source: TdsError::DuplicateSimplices { .. }
}
)
);
let tds = draft.finish().unwrap();
assert_eq!(tds.number_of_simplices(), 1);
assert_eq!(tds.simplex(simplex_key).unwrap().data(), Some(&7));
assert!(tds.validate().is_ok());
}
#[test]
fn explicit_connectivity_publishes_without_a_geometry_specific_algorithm() {
let mut draft: TdsDraft<(), (), 2> = TdsDraft::new();
let v0 = draft.insert_vertex(vertex![0.0, 0.0].unwrap()).unwrap();
let v1 = draft.insert_vertex(vertex![1.0, 0.0].unwrap()).unwrap();
let v2 = draft.insert_vertex(vertex![0.0, 1.0].unwrap()).unwrap();
draft.insert_simplex([v0, v1, v2]).unwrap();
let tds = draft.finish().unwrap();
assert_eq!(tds.number_of_vertices(), 3);
assert_eq!(tds.number_of_simplices(), 1);
assert!(tds.validate().is_ok());
}
}