use axiolid_core::Point2;
use axiolid_guarantees::Sign;
use axiolid_predicates::orient2d;
use crate::mesh::{Triangulation, TriangulationError, NO_HALFEDGE};
use crate::recover::recover_constraints;
use crate::{decided, in_circumcircle, turns_left, Constraint};
pub fn triangulate(
points: &[Point2],
constraints: &[Constraint],
) -> Result<Triangulation, TriangulationError> {
if points.len() < 3 {
return Err(TriangulationError::TooFewPoints);
}
let count = u32::try_from(points.len()).unwrap_or(u32::MAX);
for c in constraints {
if c.a >= count || c.b >= count {
let index = if c.a >= count { c.a } else { c.b };
return Err(TriangulationError::ConstraintOutOfRange { index });
}
}
if !has_non_collinear_triple(points) {
return Err(TriangulationError::AllPointsCollinear);
}
let mut state = Builder::new(points);
state.insert_all()?;
let mut triangulation = state.finish(points, constraints);
recover_constraints(&mut triangulation)?;
Ok(triangulation)
}
fn has_non_collinear_triple(points: &[Point2]) -> bool {
let a = points[0];
let Some(b) = points.iter().copied().find(|p| *p != a) else {
return false;
};
points
.iter()
.any(|&c| turns_left(a, b, c) || turns_left(b, a, c))
}
struct Builder {
points: Vec<Point2>,
triangles: Vec<u32>,
halfedges: Vec<u32>,
real_count: usize,
}
impl Builder {
fn new(points: &[Point2]) -> Self {
let mut all = points.to_vec();
let (min, max) = bounds(points);
let dx = max.x - min.x;
let dy = max.y - min.y;
let span = if dx > dy { dx } else { dy };
let span = if span > 0.0 { span } else { 1.0 };
let cx = (min.x + max.x) * 0.5;
let cy = (min.y + max.y) * 0.5;
let far = span * 32.0;
all.push(Point2::new(cx - far, cy - far));
all.push(Point2::new(cx + far, cy - far));
all.push(Point2::new(cx, cy + far));
let n = points.len() as u32;
Self {
points: all,
triangles: vec![n, n + 1, n + 2],
halfedges: vec![NO_HALFEDGE, NO_HALFEDGE, NO_HALFEDGE],
real_count: points.len(),
}
}
fn insert_all(&mut self) -> Result<(), TriangulationError> {
for v in 0..self.real_count as u32 {
self.insert_point(v)?;
}
Ok(())
}
fn insert_point(&mut self, v: u32) -> Result<(), TriangulationError> {
let Some(t) = self.locate(self.points[v as usize]) else {
return Err(TriangulationError::VertexUnplaceable { index: v });
};
let (a, b, c) = (
self.triangles[3 * t],
self.triangles[3 * t + 1],
self.triangles[3 * t + 2],
);
let p = self.points[v as usize];
for i in 0..3 {
let (u, w) = (
self.triangles[3 * t + i],
self.triangles[3 * t + (i + 1) % 3],
);
if decided(orient2d(
self.points[u as usize],
self.points[w as usize],
p,
)) != Sign::Zero
{
continue;
}
let twin = self.halfedges[3 * t + i];
if twin == NO_HALFEDGE {
return Err(TriangulationError::VertexUnplaceable { index: v });
}
let x = self.triangles[3 * t + (i + 2) % 3];
let ot = twin as usize / 3;
let oi = twin as usize % 3;
let y = self.triangles[3 * ot + (oi + 2) % 3];
let t1 = self.triangles.len() / 3;
let t2 = t1 + 1;
self.triangles[3 * t] = x;
self.triangles[3 * t + 1] = u;
self.triangles[3 * t + 2] = v;
self.triangles[3 * ot] = y;
self.triangles[3 * ot + 1] = w;
self.triangles[3 * ot + 2] = v;
self.triangles.extend_from_slice(&[w, x, v]);
self.triangles.extend_from_slice(&[u, y, v]);
self.halfedges.extend_from_slice(&[NO_HALFEDGE; 6]);
self.rebuild_adjacency();
for tri in [t, ot, t1, t2] {
self.legalize(3 * tri);
}
return Ok(());
}
let t1 = self.triangles.len() / 3;
let t2 = t1 + 1;
self.triangles[3 * t] = a;
self.triangles[3 * t + 1] = b;
self.triangles[3 * t + 2] = v;
self.triangles.extend_from_slice(&[b, c, v]);
self.triangles.extend_from_slice(&[c, a, v]);
self.halfedges.extend_from_slice(&[NO_HALFEDGE; 6]);
self.rebuild_adjacency();
self.legalize(3 * t);
self.legalize(3 * t1);
self.legalize(3 * t2);
Ok(())
}
fn rebuild_adjacency(&mut self) {
use std::collections::HashMap;
let count = self.triangles.len();
self.halfedges.clear();
self.halfedges.resize(count, NO_HALFEDGE);
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 locate(&self, p: Point2) -> Option<usize> {
let mut t = self.triangles.len() / 3 - 1;
for _ in 0..self.triangles.len() {
let (a, b, c) = (
self.points[self.triangles[3 * t] as usize],
self.points[self.triangles[3 * t + 1] as usize],
self.points[self.triangles[3 * t + 2] as usize],
);
let mut moved = false;
for (i, (u, w)) in [(a, b), (b, c), (c, a)].into_iter().enumerate() {
let right_of = decided(orient2d(u, w, p)) == Sign::Negative;
if right_of {
let twin = self.halfedges[3 * t + i];
if twin == NO_HALFEDGE {
return None;
}
t = twin as usize / 3;
moved = true;
break;
}
}
if !moved {
return Some(t);
}
}
None
}
fn legalize(&mut self, edge: usize) {
let mut stack = vec![edge];
let mut budget = 4 * self.triangles.len() + 64;
while let Some(e) = stack.pop() {
if budget == 0 {
break;
}
budget -= 1;
let twin = self.halfedges[e];
if twin == NO_HALFEDGE {
continue;
}
let t = e / 3;
let ti = e % 3;
let o = twin as usize;
let ot = o / 3;
let oi = o % 3;
let p0 = self.triangles[3 * t + ti];
let p1 = self.triangles[3 * t + (ti + 1) % 3];
let apex = self.triangles[3 * t + (ti + 2) % 3];
let other = self.triangles[3 * ot + (oi + 2) % 3];
if !in_circumcircle(
self.points[p0 as usize],
self.points[p1 as usize],
self.points[apex as usize],
self.points[other as usize],
) {
continue;
}
let q = |k: u32| self.points[k as usize];
if !(turns_left(q(apex), q(p0), q(other)) && turns_left(q(other), q(p1), q(apex))) {
continue;
}
self.triangles[3 * t + (ti + 1) % 3] = other;
self.triangles[3 * ot + (oi + 1) % 3] = apex;
self.rebuild_adjacency();
stack.push(3 * t + ti);
stack.push(3 * ot + (oi + 2) % 3);
}
}
fn finish(self, original: &[Point2], constraints: &[Constraint]) -> Triangulation {
let limit = self.real_count as u32;
let mut triangles = Vec::with_capacity(self.triangles.len());
for t in self.triangles.chunks_exact(3) {
if t.iter().all(|&v| v < limit) {
triangles.extend_from_slice(t);
}
}
let mut sorted = constraints.to_vec();
sorted.sort_unstable();
sorted.dedup();
let mut out = Triangulation {
points: original.to_vec(),
triangles,
halfedges: Vec::new(),
constraints: sorted,
};
out.rebuild_halfedges();
out
}
}
fn bounds(points: &[Point2]) -> (Point2, Point2) {
let mut min = points[0];
let mut max = points[0];
for p in &points[1..] {
min.x = min.x.min(p.x);
min.y = min.y.min(p.y);
max.x = max.x.max(p.x);
max.y = max.y.max(p.y);
}
(min, max)
}