use super::exact::approx::{incircle_a, incircle_a_exact, orient2d_a, orient2d_a_exact};
use super::exact::backend::{denom, numer, Rational, ToPrimitive};
use super::exact::predicates::{homog2_of, incircle_h, orient2d_h, Homog2, TriLoc};
use super::exact::rational::{rat_to_f64, R2};
use super::exact::Sign;
#[derive(Clone, Debug)]
struct Tri {
v: [usize; 3],
adj: [i32; 3],
con: [bool; 3],
alive: bool,
}
struct Cdt {
pts: Vec<Homog2>,
apts: Vec<[f64; 2]>,
exact: Vec<bool>,
tris: Vec<Tri>,
suspects: Vec<usize>,
vert_tri: Vec<u32>,
rank: Vec<u32>,
}
pub fn triangulate(points: &[R2], constraints: &[(usize, usize)]) -> Vec<[usize; 3]> {
triangulate_with_token(points, constraints, None)
.expect("uncancellable triangulate cannot cancel")
}
pub fn triangulate_with_token(
points: &[R2],
constraints: &[(usize, usize)],
token: Option<&crate::cancel::CancelToken>,
) -> Option<Vec<[usize; 3]>> {
let (apts, exact) = translated_filter_inputs(points);
triangulate_with_apts(points, constraints, apts, exact, token)
}
fn translated_filter_inputs(points: &[R2]) -> (Vec<[f64; 2]>, Vec<bool>) {
let origin = &points[0];
let (mut apts, mut exact) = (
Vec::with_capacity(points.len()),
Vec::with_capacity(points.len()),
);
for p in points {
let t = p.sub(origin);
apts.push([rat_to_f64(&t.x), rat_to_f64(&t.y)]);
exact.push(is_exact(&t.x) && is_exact(&t.y));
}
(apts, exact)
}
pub(crate) fn triangulate_with_apts(
points: &[R2],
constraints: &[(usize, usize)],
apts: Vec<[f64; 2]>,
exact: Vec<bool>,
token: Option<&crate::cancel::CancelToken>,
) -> Option<Vec<[usize; 3]>> {
assert!(points.len() >= 3, "need the three corner points");
debug_assert_eq!(apts.len(), points.len(), "one filter input per point");
debug_assert_eq!(exact.len(), points.len(), "one exactness flag per point");
let hom: Vec<Homog2> = points.iter().map(homog2_of).collect();
let mut corners = [0usize, 1, 2];
let orient = orient2d_h(&hom[0], &hom[1], &hom[2]);
assert!(
orient != Sign::Zero,
"degenerate base triangle in CDT input"
);
if orient == Sign::Neg {
corners.swap(1, 2);
}
let mut by_coord: Vec<usize> = (0..points.len()).collect();
by_coord.sort_by(|&i, &j| {
points[i]
.x
.cmp(&points[j].x)
.then_with(|| points[i].y.cmp(&points[j].y))
});
let mut rank = vec![0u32; points.len()];
for (r, &i) in by_coord.iter().enumerate() {
rank[i] = r as u32;
}
let mut cdt = Cdt {
pts: hom,
apts,
exact,
tris: vec![Tri {
v: corners,
adj: [-1, -1, -1],
con: [false, false, false],
alive: true,
}],
suspects: Vec::new(),
vert_tri: vec![0; points.len()],
rank,
};
for p in 3..cdt.pts.len() {
if p % 64 == 0 && crate::cancel::is_cancelled(token) {
return None;
}
let first_new = cdt.tris.len();
cdt.insert_point(p);
cdt.seed_suspects(first_new);
cdt.legalize_suspects();
}
for &(a, b) in constraints {
if crate::cancel::is_cancelled(token) {
return None;
}
debug_assert_ne!(a, b, "zero-length constraint");
let first_new = cdt.tris.len();
cdt.insert_constraint(a, b);
cdt.seed_suspects(first_new);
cdt.legalize_suspects();
}
Some(cdt.tris.iter().filter(|t| t.alive).map(|t| t.v).collect())
}
#[inline]
fn is_exact(r: &Rational) -> bool {
match (numer(r).to_i64(), denom(r).to_u64()) {
(Some(n), Some(d)) => d.is_power_of_two() && n.unsigned_abs() < (1u64 << 53),
_ => false,
}
}
impl Cdt {
#[inline]
fn o2(&self, i: usize, j: usize, k: usize) -> Sign {
if self.exact[i] && self.exact[j] && self.exact[k] {
if let Some(s) = orient2d_a_exact(self.apts[i], self.apts[j], self.apts[k]) {
return s;
}
}
orient2d_a(self.apts[i], self.apts[j], self.apts[k])
.unwrap_or_else(|| orient2d_h(&self.pts[i], &self.pts[j], &self.pts[k]))
}
#[inline]
fn incircle(&self, tri: [usize; 3], d: usize) -> Sign {
if self.exact[tri[0]] && self.exact[tri[1]] && self.exact[tri[2]] && self.exact[d] {
if let Some(s) = incircle_a_exact(
self.apts[tri[0]],
self.apts[tri[1]],
self.apts[tri[2]],
self.apts[d],
) {
return s;
}
}
match incircle_a(
self.apts[tri[0]],
self.apts[tri[1]],
self.apts[tri[2]],
self.apts[d],
) {
Some(s) => s,
None => incircle_h(
&self.pts[tri[0]],
&self.pts[tri[1]],
&self.pts[tri[2]],
&self.pts[d],
),
}
}
#[inline]
fn nondelaunay(&self, tri: [usize; 3], d: usize) -> bool {
self.incircle(tri, d) == Sign::Pos
}
#[inline]
fn diag_key(&self, u: usize, v: usize) -> (u32, u32) {
let (ru, rv) = (self.rank[u], self.rank[v]);
(ru.min(rv), ru.max(rv))
}
fn loc_in_tri(&self, p: usize, v: [usize; 3]) -> TriLoc {
let s0 = self.o2(v[0], v[1], p);
let s1 = self.o2(v[1], v[2], p);
let s2 = self.o2(v[2], v[0], p);
if s0 == Sign::Neg || s1 == Sign::Neg || s2 == Sign::Neg {
return TriLoc::Outside;
}
match (s0 == Sign::Zero, s1 == Sign::Zero, s2 == Sign::Zero) {
(false, false, false) => TriLoc::Inside,
(true, false, false) => TriLoc::OnEdge(0),
(false, true, false) => TriLoc::OnEdge(1),
(false, false, true) => TriLoc::OnEdge(2),
(true, false, true) => TriLoc::OnVertex(0),
(true, true, false) => TriLoc::OnVertex(1),
(false, true, true) => TriLoc::OnVertex(2),
(true, true, true) => TriLoc::Outside,
}
}
#[inline]
fn record(&mut self, t: usize) {
for v in self.tris[t].v {
self.vert_tri[v] = t as u32;
}
}
fn live_tri_with(&self, a: usize) -> usize {
let cand = self.vert_tri[a] as usize;
if cand < self.tris.len() && self.tris[cand].alive && self.tris[cand].v.contains(&a) {
return cand;
}
debug_assert!(false, "vert_tri invariant broken for vertex {a}");
(0..self.tris.len())
.rev()
.find(|&i| self.tris[i].alive && self.tris[i].v.contains(&a))
.expect("vertex not in any live triangle")
}
fn rotate_around<T>(&self, a: usize, mut f: impl FnMut(usize) -> Option<T>) -> Option<T> {
let seed = self.live_tri_with(a);
for dirn in 0..2 {
let mut cur = seed;
loop {
if dirn == 0 || cur != seed {
if let Some(out) = f(cur) {
return Some(out);
}
}
let t = &self.tris[cur];
let ia = (0..3)
.position(|k| t.v[k] == a)
.expect("rotation left the vertex fan");
let e_step = if dirn == 0 { ia } else { (ia + 2) % 3 };
let n = t.adj[e_step];
if n < 0 || n as usize == seed {
break;
}
cur = n as usize;
}
}
None
}
fn shared_edge(&self, t: usize, n: usize) -> usize {
(0..3)
.find(|&e| self.tris[t].adj[e] == n as i32)
.expect("adjacency tables out of sync")
}
fn rewire(&mut self, t: usize, e: usize, new_t: usize) {
let n = self.tris[t].adj[e];
if n >= 0 {
let ne = self.shared_edge(n as usize, t);
self.tris[n as usize].adj[ne] = new_t as i32;
}
}
fn insert_point(&mut self, p: usize) {
let (t, loc) = self.locate(p);
match loc {
TriLoc::Inside => self.split_interior(t, p),
TriLoc::OnEdge(e) => self.split_edge(t, e as usize, p),
TriLoc::OnVertex(_) => {
debug_assert!(false, "duplicate point reached CDT: {p}");
}
TriLoc::Outside => unreachable!("point outside base triangle"),
}
}
fn locate(&self, p: usize) -> (usize, TriLoc) {
if let Some(start) = (0..self.tris.len()).rev().find(|&i| self.tris[i].alive) {
let mut cur = start;
let mut steps = 0usize;
let cap = 4 * self.tris.len() + 16;
'walk: while steps < cap {
steps += 1;
let t = &self.tris[cur];
let loc = self.loc_in_tri(p, t.v);
if loc != TriLoc::Outside {
return (cur, loc);
}
for e in 0..3 {
if self.o2(t.v[e], t.v[(e + 1) % 3], p) == Sign::Neg {
let n = t.adj[e];
if n >= 0 {
cur = n as usize;
continue 'walk;
}
}
}
break; }
}
for (i, t) in self.tris.iter().enumerate() {
if !t.alive {
continue;
}
if self.loc_in_tri(p, t.v) != TriLoc::Outside {
return (i, self.loc_in_tri(p, t.v));
}
}
unreachable!("point {p} not located in any live triangle");
}
fn split_interior(&mut self, t: usize, p: usize) {
let Tri { v, adj, con, .. } = self.tris[t].clone();
let base = self.tris.len();
for k in 0..3 {
let next = base + (k + 1) % 3;
let prev = base + (k + 2) % 3;
self.tris.push(Tri {
v: [v[k], v[(k + 1) % 3], p],
adj: [adj[k], next as i32, prev as i32],
con: [con[k], false, false],
alive: true,
});
}
for k in 0..3 {
self.rewire(t, k, base + k);
self.record(base + k);
}
self.tris[t].alive = false;
}
fn split_edge(&mut self, t: usize, e: usize, p: usize) {
let n = self.tris[t].adj[e];
self.split_edge_one_side(t, e, p);
if n >= 0 {
let ne = self.shared_edge(n as usize, t);
self.split_edge_one_side(n as usize, ne, p);
self.fix_split_pairs(p);
}
}
fn split_edge_one_side(&mut self, t: usize, e: usize, p: usize) {
let Tri { v, adj, con, .. } = self.tris[t].clone();
let (a, b, c) = (v[e], v[(e + 1) % 3], v[(e + 2) % 3]);
let (adj_ab, adj_bc, adj_ca) = (adj[e], adj[(e + 1) % 3], adj[(e + 2) % 3]);
let (con_ab, con_bc, con_ca) = (con[e], con[(e + 1) % 3], con[(e + 2) % 3]);
let t1 = self.tris.len();
let t2 = t1 + 1;
self.tris.push(Tri {
v: [a, p, c],
adj: [adj_ab, t2 as i32, adj_ca],
con: [con_ab, false, con_ca],
alive: true,
});
self.tris.push(Tri {
v: [p, b, c],
adj: [adj_ab, adj_bc, t1 as i32],
con: [con_ab, con_bc, false],
alive: true,
});
self.rewire(t, (e + 1) % 3, t2);
self.rewire(t, (e + 2) % 3, t1);
self.record(t1);
self.record(t2);
self.tris[t].alive = false;
}
fn fix_split_pairs(&mut self, p: usize) {
let first = self.tris.len().saturating_sub(4);
let mut dangling: Vec<(usize, usize)> = Vec::new();
for i in first..self.tris.len() {
if !self.tris[i].alive {
continue;
}
for e in 0..3 {
let n = self.tris[i].adj[e];
if n >= 0 && !self.tris[n as usize].alive {
debug_assert!(
self.tris[i].v[e] == p || self.tris[i].v[(e + 1) % 3] == p,
"dangling edge must touch the split point"
);
dangling.push((i, e));
}
}
}
debug_assert_eq!(
dangling.len(),
(0..first)
.filter(|&i| self.tris[i].alive)
.flat_map(|i| (0..3).map(move |e| (i, e)))
.filter(|&(i, e)| {
let n = self.tris[i].adj[e];
n >= 0 && !self.tris[n as usize].alive
})
.count()
+ dangling.len(),
"dangling adjacency outside the four freshly split halves"
);
for i in 0..dangling.len() {
for j in (i + 1)..dangling.len() {
let (ti, ei) = dangling[i];
let (tj, ej) = dangling[j];
let vi = [self.tris[ti].v[ei], self.tris[ti].v[(ei + 1) % 3]];
let vj = [self.tris[tj].v[ej], self.tris[tj].v[(ej + 1) % 3]];
if vi[0] == vj[1] && vi[1] == vj[0] {
self.tris[ti].adj[ei] = tj as i32;
self.tris[tj].adj[ej] = ti as i32;
}
}
}
}
fn seed_suspects(&mut self, first_new: usize) {
for t in first_new..self.tris.len() {
self.suspects.push(t);
}
}
fn legalize_suspects(&mut self) {
while let Some(t) = self.suspects.pop() {
if t >= self.tris.len() || !self.tris[t].alive {
continue;
}
for e in 0..3 {
if self.try_flip(t, e) {
let n = self.tris.len();
self.suspects.push(n - 2);
self.suspects.push(n - 1);
break;
}
}
}
}
fn try_flip(&mut self, t: usize, e: usize) -> bool {
if self.tris[t].con[e] {
return false;
}
let n = self.tris[t].adj[e];
if n < 0 {
return false;
}
let n = n as usize;
let ne = self.shared_edge(n, t);
let (a, b, c) = (
self.tris[t].v[e],
self.tris[t].v[(e + 1) % 3],
self.tris[t].v[(e + 2) % 3],
);
let d = self.tris[n].v[(ne + 2) % 3];
match self.incircle(self.tris[t].v, d) {
Sign::Neg => return false,
Sign::Zero => {
if self.diag_key(c, d) >= self.diag_key(a, b) {
return false;
}
}
Sign::Pos => {}
}
if self.o2(a, d, c) != Sign::Pos || self.o2(d, b, c) != Sign::Pos {
return false;
}
self.flip(t, e, n, ne);
true
}
fn flip(&mut self, t: usize, e: usize, n: usize, ne: usize) {
let (a, b, c) = (
self.tris[t].v[e],
self.tris[t].v[(e + 1) % 3],
self.tris[t].v[(e + 2) % 3],
);
let d = self.tris[n].v[(ne + 2) % 3];
debug_assert_eq!(self.tris[n].v[ne], b);
debug_assert_eq!(self.tris[n].v[(ne + 1) % 3], a);
let adj_bc = self.tris[t].adj[(e + 1) % 3];
let con_bc = self.tris[t].con[(e + 1) % 3];
let adj_ca = self.tris[t].adj[(e + 2) % 3];
let con_ca = self.tris[t].con[(e + 2) % 3];
let adj_ad = self.tris[n].adj[(ne + 1) % 3];
let con_ad = self.tris[n].con[(ne + 1) % 3];
let adj_db = self.tris[n].adj[(ne + 2) % 3];
let con_db = self.tris[n].con[(ne + 2) % 3];
let t1 = self.tris.len();
let t2 = t1 + 1;
self.tris.push(Tri {
v: [a, d, c],
adj: [adj_ad, t2 as i32, adj_ca],
con: [con_ad, false, con_ca],
alive: true,
});
self.tris.push(Tri {
v: [d, b, c],
adj: [adj_db, adj_bc, t1 as i32],
con: [con_db, con_bc, false],
alive: true,
});
self.rewire(t, (e + 1) % 3, t2);
self.rewire(t, (e + 2) % 3, t1);
self.rewire(n, (ne + 1) % 3, t1);
self.rewire(n, (ne + 2) % 3, t2);
self.record(t1);
self.record(t2);
self.tris[t].alive = false;
self.tris[n].alive = false;
}
}
#[path = "cdt_constraints.rs"]
mod constraints;
#[cfg(test)]
#[path = "cdt_tests.rs"]
mod tests;