use nalgebra::Point2;
use super::classify::EdgeClass;
use super::delaunay::Triangulation;
#[derive(Clone, Copy, Debug)]
pub(super) struct Quad {
pub(super) vertices: [usize; 4],
}
impl Quad {
pub(super) fn perimeter_edges(&self) -> [(usize, usize); 4] {
[
(self.vertices[0], self.vertices[1]),
(self.vertices[1], self.vertices[2]),
(self.vertices[2], self.vertices[3]),
(self.vertices[3], self.vertices[0]),
]
}
}
fn unique_diagonal_edge(kinds: &[EdgeClass], t: usize) -> Option<usize> {
let base = 3 * t;
let mut diag_idx: Option<usize> = None;
for k in 0..3 {
let e = base + k;
match kinds[e] {
EdgeClass::Diagonal => {
if diag_idx.is_some() {
return None;
}
diag_idx = Some(k);
}
EdgeClass::Grid => {}
EdgeClass::Spurious => return None,
}
}
let k = diag_idx?;
for kk in 0..3 {
if kk == k {
continue;
}
if kinds[base + kk] != EdgeClass::Grid {
return None;
}
}
Some(k)
}
fn build_quad(verts: [usize; 4], positions: &[Point2<f32>]) -> Quad {
let pts = verts.map(|v| positions[v]);
let cx = (pts[0].x + pts[1].x + pts[2].x + pts[3].x) / 4.0;
let cy = (pts[0].y + pts[1].y + pts[2].y + pts[3].y) / 4.0;
let mut indexed: [(usize, f32); 4] = [
(verts[0], (pts[0].y - cy).atan2(pts[0].x - cx)),
(verts[1], (pts[1].y - cy).atan2(pts[1].x - cx)),
(verts[2], (pts[2].y - cy).atan2(pts[2].x - cx)),
(verts[3], (pts[3].y - cy).atan2(pts[3].x - cx)),
];
indexed.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
let mut tl_idx = 0usize;
for k in 1..4 {
let (vk, _) = indexed[k];
let (v_tl, _) = indexed[tl_idx];
let pk = positions[vk];
let p_tl = positions[v_tl];
if pk.y < p_tl.y || (pk.y == p_tl.y && pk.x < p_tl.x) {
tl_idx = k;
}
}
let mut out = [0usize; 4];
for k in 0..4 {
out[k] = indexed[(tl_idx + k) % 4].0;
}
Quad { vertices: out }
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(
level = "debug",
skip_all,
fields(num_triangles = triangulation.num_tri()),
)
)]
pub(super) fn merge_triangle_pairs(
triangulation: &Triangulation,
kinds: &[EdgeClass],
positions: &[Point2<f32>],
) -> Vec<Quad> {
let mut out = Vec::new();
for t in 0..triangulation.num_tri() {
let Some(k) = unique_diagonal_edge(kinds, t) else {
continue;
};
let e = 3 * t + k;
let e_buddy = triangulation.halfedges[e];
if e_buddy == delaunator::EMPTY {
continue;
}
let t_other = Triangulation::tri_of(e_buddy);
if t_other <= t {
continue;
}
let Some(k_other) = unique_diagonal_edge(kinds, t_other) else {
continue;
};
if 3 * t_other + k_other != e_buddy {
continue;
}
let mut verts = [usize::MAX; 4];
let mut count = 0;
for &v in &triangulation.triangles[3 * t..3 * t + 3] {
verts[count] = v;
count += 1;
}
for &v in &triangulation.triangles[3 * t_other..3 * t_other + 3] {
if !verts[..count].contains(&v) {
if count >= 4 {
break;
}
verts[count] = v;
count += 1;
}
}
if count != 4 {
continue;
}
out.push(build_quad(verts, positions));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
fn pt(x: f32, y: f32) -> Point2<f32> {
Point2::new(x, y)
}
#[test]
fn quad_ordering_is_cw_from_top_left() {
let positions = vec![pt(0.0, 0.0), pt(1.0, 0.0), pt(1.0, 1.0), pt(0.0, 1.0)];
let q = build_quad([2, 0, 3, 1], &positions);
assert_eq!(q.vertices, [0, 1, 2, 3]);
}
}