use honeycomb_core::{
cmap::{CMap2, DartIdType, EdgeIdType, NULL_DART_ID, NULL_EDGE_ID, SewError},
geometry::CoordsFloat,
stm::{Transaction, TransactionClosureResult, abort, try_or_coerce},
};
use crate::utils::{FaceAnchor, VertexAnchor};
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
pub enum EdgeSwapError {
#[error("core operation failed: {0}")]
FailedCoreOp(#[from] SewError),
#[error("cannot swap edge due to constraints: {0}")]
NotSwappable(&'static str),
#[error("cannot swap null edge")]
NullEdge,
#[error("cannot swap an edge adjacent to a single cell")]
IncompleteEdge,
#[error("cannot swap an edge adjacent to a non-triangular cell")]
BadTopology,
}
#[inline]
pub fn swap_edge<T: CoordsFloat>(
t: &mut Transaction,
map: &CMap2<T>,
e: EdgeIdType,
) -> TransactionClosureResult<(), EdgeSwapError> {
if e == NULL_EDGE_ID {
abort(EdgeSwapError::NullEdge)?;
}
let (l, r) = (e as DartIdType, map.beta_tx::<2>(t, e as DartIdType)?);
if r == NULL_DART_ID {
abort(EdgeSwapError::IncompleteEdge)?;
}
let (l_a, r_a) = if map.contains_attribute::<FaceAnchor>() {
let l_fid = map.face_id_tx(t, l)?;
let r_fid = map.face_id_tx(t, r)?;
let l_a = map.remove_attribute_tx::<FaceAnchor>(t, l_fid)?;
let r_a = map.remove_attribute_tx::<FaceAnchor>(t, r_fid)?;
if l_a != r_a {
abort(EdgeSwapError::NotSwappable(
"edge separates two distinct surfaces",
))?;
}
(l_a, r_a)
} else {
(None, None)
};
let (b1l, b1r) = (map.beta_tx::<1>(t, l)?, map.beta_tx::<1>(t, r)?);
let (b0l, b0r) = (map.beta_tx::<0>(t, l)?, map.beta_tx::<0>(t, r)?);
if map.beta_tx::<1>(t, b1l)? != b0l || map.beta_tx::<1>(t, b1r)? != b0r {
abort(EdgeSwapError::BadTopology)?;
}
try_or_coerce!(map.unsew_tx::<1>(t, l), EdgeSwapError);
try_or_coerce!(map.unsew_tx::<1>(t, r), EdgeSwapError);
try_or_coerce!(map.unsew_tx::<1>(t, b0l), EdgeSwapError);
try_or_coerce!(map.unsew_tx::<1>(t, b0r), EdgeSwapError);
try_or_coerce!(map.unsew_tx::<1>(t, b1l), EdgeSwapError);
try_or_coerce!(map.unsew_tx::<1>(t, b1r), EdgeSwapError);
let l_vid = map.vertex_id_tx(t, l)?;
let r_vid = map.vertex_id_tx(t, r)?;
let _ = map.remove_vertex_tx(t, l_vid)?;
let _ = map.remove_vertex_tx(t, r_vid)?;
if map.contains_attribute::<VertexAnchor>() {
map.remove_attribute_tx::<VertexAnchor>(t, l_vid)?;
map.remove_attribute_tx::<VertexAnchor>(t, r_vid)?;
}
try_or_coerce!(map.sew_tx::<1>(t, l, b0r), EdgeSwapError);
try_or_coerce!(map.sew_tx::<1>(t, b0r, b1l), EdgeSwapError);
try_or_coerce!(map.sew_tx::<1>(t, b1l, l), EdgeSwapError);
try_or_coerce!(map.sew_tx::<1>(t, r, b0l), EdgeSwapError);
try_or_coerce!(map.sew_tx::<1>(t, b0l, b1r), EdgeSwapError);
try_or_coerce!(map.sew_tx::<1>(t, b1r, r), EdgeSwapError);
match (l_a, r_a) {
(Some(l_a), Some(r_a)) => {
let l_fid = map.face_id_tx(t, l)?;
let r_fid = map.face_id_tx(t, r)?;
map.write_attribute_tx(t, l_fid, l_a)?;
map.write_attribute_tx(t, r_fid, r_a)?;
}
(Some(_), None) | (None, Some(_)) => unreachable!(),
(None, None) => {}
}
Ok(())
}