use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, OrbitPolicy};
use honeycomb_core::geometry::{CoordsFloat, Vertex2};
use honeycomb_core::stm::{Transaction, TransactionClosureResult, abort, try_or_coerce};
use smallvec::SmallVec;
use crate::triangulation::{TriangulateError, check_requirements};
#[allow(clippy::missing_panics_doc)]
pub fn earclip_cell_countercw<T: CoordsFloat>(
t: &mut Transaction,
cmap: &CMap2<T>,
face_id: FaceIdType,
new_darts: &[DartIdType],
) -> TransactionClosureResult<(), TriangulateError> {
process_cell(t, cmap, face_id, new_darts, |v1, v2, v3| {
Vertex2::cross_product_from_vertices(v1, v2, v3) > T::zero()
})
}
#[allow(clippy::missing_panics_doc)]
pub fn earclip_cell_cw<T: CoordsFloat>(
t: &mut Transaction,
cmap: &CMap2<T>,
face_id: FaceIdType,
new_darts: &[DartIdType],
) -> TransactionClosureResult<(), TriangulateError> {
process_cell(t, cmap, face_id, new_darts, |v1, v2, v3| {
Vertex2::cross_product_from_vertices(v1, v2, v3) < T::zero()
})
}
fn process_cell<T: CoordsFloat>(
t: &mut Transaction,
cmap: &CMap2<T>,
face_id: FaceIdType,
new_darts: &[DartIdType],
is_inside_fn: impl FnOnce(&Vertex2<T>, &Vertex2<T>, &Vertex2<T>) -> bool + Copy,
) -> TransactionClosureResult<(), TriangulateError> {
let mut darts: SmallVec<DartIdType, 16> = SmallVec::new();
let mut vertices: SmallVec<Vertex2<T>, 16> = SmallVec::new();
for d in cmap.orbit_tx(t, OrbitPolicy::FaceLinear, face_id as DartIdType) {
darts.push(d?);
}
for &d in &darts {
let vid = cmap.vertex_id_tx(t, d)?;
let v = if let Some(val) = cmap.read_vertex_tx(t, vid)? {
val
} else {
abort(TriangulateError::UndefinedFace(
"one or more undefined vertices",
))?
};
vertices.push(v);
}
if let Err(e) = check_requirements(darts.len(), new_darts.len()) {
abort(e)?;
}
let mut darts = darts.clone();
let mut vertices = vertices.clone();
let mut n = darts.len();
for sl in new_darts.chunks_exact(2) {
let &[nd1, nd2] = sl else { unreachable!() };
let Some(ear) = (0..n).find(|idx| {
let v1 = &vertices[*idx]; let v2 = &vertices[(*idx + 1) % n]; let v3 = &vertices[(*idx + 2) % n];
let is_inside = is_inside_fn(v1, v2, v3);
let no_overlap = vertices
.iter()
.filter(|v| (**v != *v1) && (**v != *v2) && (**v != *v3))
.all(|v| {
let sig12v = Vertex2::cross_product_from_vertices(v1, v2, v);
let sig23v = Vertex2::cross_product_from_vertices(v2, v3, v);
let sig31v = Vertex2::cross_product_from_vertices(v3, v1, v);
let has_pos =
(sig12v > T::zero()) || (sig23v > T::zero()) || (sig31v > T::zero());
let has_neg =
(sig12v < T::zero()) || (sig23v < T::zero()) || (sig31v < T::zero());
has_pos && has_neg
});
is_inside && no_overlap
}) else {
abort(TriangulateError::NoEar)?
};
let d_ear1 = darts[ear];
let d_ear2 = darts[(ear + 1) % n];
let b0_d_ear1 = cmap.beta_tx::<0>(t, d_ear1)?;
let b1_d_ear2 = cmap.beta_tx::<1>(t, d_ear2)?;
try_or_coerce!(cmap.unsew_tx::<1>(t, b0_d_ear1), TriangulateError);
try_or_coerce!(cmap.unsew_tx::<1>(t, d_ear2), TriangulateError);
try_or_coerce!(cmap.sew_tx::<1>(t, d_ear2, nd1), TriangulateError);
try_or_coerce!(cmap.sew_tx::<1>(t, nd1, d_ear1), TriangulateError);
try_or_coerce!(cmap.sew_tx::<1>(t, b0_d_ear1, nd2), TriangulateError);
try_or_coerce!(cmap.sew_tx::<1>(t, nd2, b1_d_ear2), TriangulateError);
try_or_coerce!(cmap.sew_tx::<2>(t, nd1, nd2), TriangulateError);
darts.remove((ear + 1) % n);
darts.push(nd2);
darts.swap_remove(ear);
vertices.remove((ear + 1) % n);
n -= 1;
}
assert_eq!(n, 3, "E: unreachable");
Ok(())
}