use axiolid_core::Point2;
use crate::mesh::{Triangulation, TriangulationError, NO_HALFEDGE};
use crate::{in_circumcircle, turns_left, Constraint};
impl Triangulation {
pub(crate) fn rebuild_halfedges(&mut self) {
use std::collections::HashMap;
let count = self.triangles.len();
self.halfedges = vec![NO_HALFEDGE; count];
let mut seen: HashMap<(u32, u32), u32> = HashMap::with_capacity(count);
for e in 0..count {
let t = e / 3;
let i = e % 3;
let from = self.triangles[3 * t + i];
let to = self.triangles[3 * t + (i + 1) % 3];
if let Some(&twin) = seen.get(&(to, from)) {
self.halfedges[e] = twin;
self.halfedges[twin as usize] = e as u32;
} else {
seen.insert((from, to), e as u32);
}
}
}
fn has_edge(&self, a: u32, b: u32) -> bool {
self.triangles
.chunks_exact(3)
.any(|t| (t[0] == a || t[1] == a || t[2] == a) && (t[0] == b || t[1] == b || t[2] == b))
}
}
pub(crate) fn recover_constraints(tri: &mut Triangulation) -> Result<(), TriangulationError> {
let constraints = tri.constraints.clone();
for c in constraints {
if tri.has_edge(c.a, c.b) {
continue;
}
recover_one(tri, c)?;
}
tri.rebuild_halfedges();
restore_delaunay(tri);
Ok(())
}
fn restore_delaunay(tri: &mut Triangulation) {
let mut budget = tri.triangles.len() * tri.triangles.len() + 64;
loop {
let mut changed = false;
for t in 0..tri.triangle_count() {
for i in 0..3 {
let twin = tri.halfedges[3 * t + i];
if twin == NO_HALFEDGE {
continue;
}
let (u, v) = (tri.triangles[3 * t + i], tri.triangles[3 * t + (i + 1) % 3]);
if tri.is_constrained(u, v) {
continue;
}
let apex = tri.triangles[3 * t + (i + 2) % 3];
let other = tri.triangles[3 * (twin as usize / 3) + (twin as usize % 3 + 2) % 3];
let p = |k: u32| tri.points[k as usize];
if in_circumcircle(p(u), p(v), p(apex), p(other)) && flip(tri, t, i) {
changed = true;
budget = budget.saturating_sub(1);
}
}
}
if !changed || budget == 0 {
break;
}
}
}
fn recover_one(tri: &mut Triangulation, c: Constraint) -> Result<(), TriangulationError> {
use std::collections::VecDeque;
let pa = tri.points[c.a as usize];
let pb = tri.points[c.b as usize];
let blocked = || TriangulationError::CrossingConstraints { a: c.a, b: c.b };
let mut queue: VecDeque<(u32, u32)> = find_crossing(tri, pa, pb, c)
.into_iter()
.map(|(t, i)| (tri.triangles[3 * t + i], tri.triangles[3 * t + (i + 1) % 3]))
.collect();
if queue.is_empty() && !tri.has_edge(c.a, c.b) {
return Err(blocked());
}
let mut idle = 0usize;
let mut budget = tri.triangles.len() * tri.triangles.len() + 64;
while let Some((u, v)) = queue.pop_front() {
budget = budget.checked_sub(1).ok_or_else(blocked)?;
let Some((t, i)) = halfedge(tri, u, v) else {
continue;
};
if !flip(tri, t, i) {
queue.push_back((u, v));
idle += 1;
if idle > queue.len() {
return Err(blocked());
}
continue;
}
idle = 0;
let (x, y) = (tri.triangles[3 * t], tri.triangles[3 * t + 1]);
let shares = x == c.a || x == c.b || y == c.a || y == c.b;
if !shares
&& segments_properly_cross(pa, pb, tri.points[x as usize], tri.points[y as usize])
{
queue.push_back((x, y));
}
}
if tri.has_edge(c.a, c.b) {
Ok(())
} else {
Err(blocked())
}
}
fn halfedge(tri: &Triangulation, u: u32, v: u32) -> Option<(usize, usize)> {
for t in 0..tri.triangle_count() {
for i in 0..3 {
let (a, b) = (tri.triangles[3 * t + i], tri.triangles[3 * t + (i + 1) % 3]);
if (a == u && b == v) || (a == v && b == u) {
return Some((t, i));
}
}
}
None
}
fn find_crossing(
tri: &Triangulation,
pa: Point2,
pb: Point2,
c: Constraint,
) -> Vec<(usize, usize)> {
let mut out = Vec::new();
for t in 0..tri.triangle_count() {
for i in 0..3 {
let u = tri.triangles[3 * t + i];
let v = tri.triangles[3 * t + (i + 1) % 3];
if u == c.a || u == c.b || v == c.a || v == c.b {
continue;
}
if tri.is_constrained(u, v) {
continue;
}
let pu = tri.points[u as usize];
let pv = tri.points[v as usize];
if segments_properly_cross(pa, pb, pu, pv) {
out.push((t, i));
}
}
}
out
}
fn segments_properly_cross(a: Point2, b: Point2, c: Point2, d: Point2) -> bool {
let d1 = turns_left(a, b, c);
let d2 = turns_left(a, b, d);
let d3 = turns_left(c, d, a);
let d4 = turns_left(c, d, b);
d1 != d2 && d3 != d4
}
fn flip(tri: &mut Triangulation, t: usize, i: usize) -> bool {
let twin = tri.halfedges[3 * t + i];
if twin == NO_HALFEDGE {
return false;
}
let ot = twin as usize / 3;
let oi = twin as usize % 3;
let a = tri.triangles[3 * t + i];
let b = tri.triangles[3 * t + (i + 1) % 3];
let apex = tri.triangles[3 * t + (i + 2) % 3];
let other = tri.triangles[3 * ot + (oi + 2) % 3];
let (pa, pb) = (tri.points[a as usize], tri.points[b as usize]);
let (pap, pot) = (tri.points[apex as usize], tri.points[other as usize]);
if !(turns_left(pap, pa, pot) && turns_left(pot, pb, pap)) {
return false;
}
tri.triangles[3 * t] = apex;
tri.triangles[3 * t + 1] = other;
tri.triangles[3 * t + 2] = b;
tri.triangles[3 * ot] = other;
tri.triangles[3 * ot + 1] = apex;
tri.triangles[3 * ot + 2] = a;
tri.rebuild_halfedges();
true
}