use honeycomb_core::{
attributes::AttributeError,
cmap::{CMap2, DartIdType, EdgeIdType, LinkError, NULL_DART_ID, SewError},
geometry::{CoordsFloat, Vertex2},
stm::{Transaction, TransactionClosureResult, abort, try_or_coerce},
};
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
pub enum VertexInsertionError {
#[error("core operation failed: {0}")]
FailedCoreOp(#[from] SewError),
#[error("vertex placement for split is not in ]0;1[")]
VertexBound,
#[error("edge isn't defined correctly")]
UndefinedEdge,
#[error("passed darts should be free & non-null - {0}")]
InvalidDarts(&'static str),
#[error("wrong # of darts - expected `{0}`, got {1}")]
WrongAmountDarts(usize, usize),
}
impl From<LinkError> for VertexInsertionError {
fn from(value: LinkError) -> Self {
Self::FailedCoreOp(value.into())
}
}
impl From<AttributeError> for VertexInsertionError {
fn from(value: AttributeError) -> Self {
Self::FailedCoreOp(value.into())
}
}
#[allow(clippy::too_many_lines)]
pub fn insert_vertex_on_edge<T: CoordsFloat>(
cmap: &CMap2<T>,
t: &mut Transaction,
edge_id: EdgeIdType,
new_darts: (DartIdType, DartIdType), midpoint_vertex: Option<T>,
) -> TransactionClosureResult<(), VertexInsertionError> {
if midpoint_vertex.is_some_and(|p| (p >= T::one()) | (p <= T::zero())) {
abort(VertexInsertionError::VertexBound)?;
}
let base_dart1 = edge_id as DartIdType;
let base_dart2 = cmap.beta_tx::<2>(t, base_dart1)?;
if new_darts.0 == NULL_DART_ID || !cmap.is_free_tx(t, new_darts.0)? {
abort(VertexInsertionError::InvalidDarts(
"first dart is null or not free",
))?;
}
if base_dart2 != NULL_DART_ID
&& (new_darts.1 == NULL_DART_ID || !cmap.is_free_tx(t, new_darts.1)?)
{
abort(VertexInsertionError::InvalidDarts(
"second dart is null or not free",
))?;
}
let base_dart2 = cmap.beta_tx::<2>(t, base_dart1)?;
if base_dart2 == NULL_DART_ID {
let b1d1_old = cmap.beta_tx::<1>(t, base_dart1)?;
let b1d1_new = new_darts.0;
let (vid1, vid2) = (
cmap.vertex_id_tx(t, base_dart1)?,
cmap.vertex_id_tx(t, b1d1_old)?,
);
let (Some(v1), Some(v2)) = (cmap.read_vertex_tx(t, vid1)?, cmap.read_vertex_tx(t, vid2)?)
else {
abort(VertexInsertionError::UndefinedEdge)?
};
if b1d1_old != NULL_DART_ID {
try_or_coerce!(cmap.unlink_tx::<1>(t, base_dart1), VertexInsertionError);
}
try_or_coerce!(
cmap.link_tx::<1>(t, base_dart1, b1d1_new),
VertexInsertionError
);
try_or_coerce!(
cmap.link_tx::<1>(t, b1d1_new, b1d1_old),
VertexInsertionError
);
let seg = v2 - v1;
let vnew = cmap.vertex_id_tx(t, b1d1_new)?;
cmap.write_vertex_tx(
t,
vnew,
midpoint_vertex.map_or(Vertex2::average(&v1, &v2), |t| v1 + seg * t),
)?;
Ok(())
} else {
let b1d1_old = cmap.beta_tx::<1>(t, base_dart1)?;
let b1d2_old = cmap.beta_tx::<1>(t, base_dart2)?;
let (b1d1_new, b1d2_new) = new_darts;
let (vid1, vid2) = (
cmap.vertex_id_tx(t, base_dart1)?,
cmap.vertex_id_tx(t, base_dart2)?,
);
let (Some(v1), Some(v2)) = (cmap.read_vertex_tx(t, vid1)?, cmap.read_vertex_tx(t, vid2)?)
else {
abort(VertexInsertionError::UndefinedEdge)?
};
if b1d1_old != NULL_DART_ID {
try_or_coerce!(cmap.unlink_tx::<1>(t, base_dart1), VertexInsertionError);
}
if b1d2_old != NULL_DART_ID {
try_or_coerce!(cmap.unlink_tx::<1>(t, base_dart2), VertexInsertionError);
}
try_or_coerce!(cmap.unlink_tx::<2>(t, base_dart1), VertexInsertionError);
try_or_coerce!(
cmap.link_tx::<1>(t, base_dart1, b1d1_new),
VertexInsertionError
);
if b1d1_old != NULL_DART_ID {
try_or_coerce!(
cmap.link_tx::<1>(t, b1d1_new, b1d1_old),
VertexInsertionError
);
}
try_or_coerce!(
cmap.link_tx::<1>(t, base_dart2, b1d2_new),
VertexInsertionError
);
if b1d2_old != NULL_DART_ID {
try_or_coerce!(
cmap.link_tx::<1>(t, b1d2_new, b1d2_old),
VertexInsertionError
);
}
try_or_coerce!(
cmap.link_tx::<2>(t, base_dart1, b1d2_new),
VertexInsertionError
);
try_or_coerce!(
cmap.link_tx::<2>(t, base_dart2, b1d1_new),
VertexInsertionError
);
let seg = v2 - v1;
let vnew = cmap.vertex_id_tx(t, b1d1_new)?;
cmap.write_vertex_tx(
t,
vnew,
midpoint_vertex.map_or(Vertex2::average(&v1, &v2), |t| v1 + seg * t),
)?;
Ok(())
}
}
#[allow(clippy::missing_errors_doc)]
pub fn insert_vertices_on_edge<T: CoordsFloat>(
cmap: &CMap2<T>,
t: &mut Transaction,
edge_id: EdgeIdType,
new_darts: &[DartIdType],
midpoint_vertices: &[T],
) -> TransactionClosureResult<(), VertexInsertionError> {
let n_t = midpoint_vertices.len();
let n_d = new_darts.len();
if n_d != 2 * n_t {
abort(VertexInsertionError::WrongAmountDarts(2 * n_t, n_d))?;
}
for d in new_darts {
if !cmap.is_free_tx(t, *d)? {
abort(VertexInsertionError::InvalidDarts("one dart is not free"))?;
}
}
let darts_fh = &new_darts[..n_t];
let darts_sh = &new_darts[n_t..];
let base_dart1 = edge_id as DartIdType;
let base_dart2 = cmap.beta_tx::<2>(t, base_dart1)?;
if darts_fh.contains(&NULL_DART_ID) {
abort(VertexInsertionError::InvalidDarts(
"one dart of the first half is null",
))?;
}
if base_dart2 != NULL_DART_ID && darts_sh.contains(&NULL_DART_ID) {
abort(VertexInsertionError::InvalidDarts(
"one dart of the second half is null",
))?;
}
if midpoint_vertices
.iter()
.any(|p| (*p >= T::one()) | (*p <= T::zero()))
{
abort(VertexInsertionError::VertexBound)?;
}
let base_dart2 = cmap.beta_tx::<2>(t, base_dart1)?;
let b1d1_old = cmap.beta_tx::<1>(t, base_dart1)?;
let (vid1, vid2) = (
cmap.vertex_id_tx(t, base_dart1)?,
cmap.vertex_id_tx(
t,
if b1d1_old != NULL_DART_ID {
b1d1_old
} else if base_dart2 != NULL_DART_ID {
base_dart2
} else {
abort(VertexInsertionError::UndefinedEdge)?
},
)?,
);
let (Some(v1), Some(v2)) = (cmap.read_vertex_tx(t, vid1)?, cmap.read_vertex_tx(t, vid2)?)
else {
abort(VertexInsertionError::UndefinedEdge)?
};
let seg = v2 - v1;
if b1d1_old != NULL_DART_ID {
try_or_coerce!(cmap.unlink_tx::<1>(t, base_dart1), VertexInsertionError);
}
if base_dart2 != NULL_DART_ID {
try_or_coerce!(cmap.unlink_tx::<2>(t, base_dart1), VertexInsertionError);
}
let mut prev_d = base_dart1;
for (&p, &new_d) in midpoint_vertices.iter().zip(darts_fh.iter()) {
let new_v = v1 + seg * p;
try_or_coerce!(cmap.link_tx::<1>(t, prev_d, new_d), VertexInsertionError);
cmap.write_vertex_tx(t, new_d, new_v)?;
prev_d = new_d;
}
try_or_coerce!(cmap.link_tx::<1>(t, prev_d, b1d1_old), VertexInsertionError);
if base_dart2 != NULL_DART_ID {
let b1d2_old = cmap.beta_tx::<1>(t, base_dart2)?;
if b1d2_old != NULL_DART_ID {
try_or_coerce!(cmap.unlink_tx::<1>(t, base_dart2), VertexInsertionError);
}
let mut prev_d = base_dart2;
for (d, new_d) in darts_fh.iter().rev().zip(darts_sh.iter()) {
try_or_coerce!(cmap.link_tx::<2>(t, prev_d, *d), VertexInsertionError);
try_or_coerce!(cmap.link_tx::<1>(t, prev_d, *new_d), VertexInsertionError);
prev_d = *new_d;
}
if b1d2_old != NULL_DART_ID {
try_or_coerce!(cmap.link_tx::<1>(t, prev_d, b1d2_old), VertexInsertionError);
}
try_or_coerce!(
cmap.link_tx::<2>(t, prev_d, base_dart1),
VertexInsertionError
);
}
Ok(())
}