use std::collections::HashSet;
use crate::linalg::Vec3;
use crate::robust::cells::VertTables;
use crate::robust::exact::rational::R3;
use super::plan_vertex_splits;
#[derive(Default)]
struct Verts {
pts: Vec<Vec3>,
}
impl Verts {
fn id(&mut self, x: f64, y: f64, z: f64) -> u32 {
let p = Vec3::new(x, y, z);
if let Some(i) = self.pts.iter().position(|q| *q == p) {
return i as u32;
}
self.pts.push(p);
(self.pts.len() - 1) as u32
}
fn tables(&self) -> (Vec<R3>, Vec<Vec3>) {
(
self.pts.iter().map(|p| R3::from_vec3(*p)).collect(),
self.pts.clone(),
)
}
}
fn box_tris(v: &mut Verts, lo: [f64; 3], hi: [f64; 3]) -> Vec<[u32; 3]> {
let c = [
v.id(lo[0], lo[1], lo[2]),
v.id(hi[0], lo[1], lo[2]),
v.id(hi[0], hi[1], lo[2]),
v.id(lo[0], hi[1], lo[2]),
v.id(lo[0], lo[1], hi[2]),
v.id(hi[0], lo[1], hi[2]),
v.id(hi[0], hi[1], hi[2]),
v.id(lo[0], hi[1], hi[2]),
];
[
[0, 3, 2],
[0, 2, 1], [4, 5, 6],
[4, 6, 7], [0, 1, 5],
[0, 5, 4], [1, 2, 6],
[1, 6, 5], [2, 3, 7],
[2, 7, 6], [3, 0, 4],
[3, 4, 7], ]
.iter()
.map(|t| [c[t[0]], c[t[1]], c[t[2]]])
.collect()
}
fn wedge_tris(v: &mut Verts, a: f64, b: f64) -> Vec<[u32; 3]> {
let pt = |deg: f64, z: f64| {
let r = deg.to_radians();
(r.cos(), r.sin(), z)
};
let mut ring = |z: f64| {
let (ax, ay, _) = (0.0, 0.0, z);
let (px, py, _) = pt(a, z);
let (qx, qy, _) = pt(b, z);
[v.id(ax, ay, z), v.id(px, py, z), v.id(qx, qy, z)]
};
let bot = ring(0.0);
let top = ring(1.0);
let mut tris = vec![
[bot[0], bot[2], bot[1]], [top[0], top[1], top[2]], ];
for i in 0..3 {
let j = (i + 1) % 3;
tris.push([bot[i], bot[j], top[j]]);
tris.push([bot[i], top[j], top[i]]);
}
tris
}
fn plan(tris: &[[u32; 3]], v: &Verts) -> Option<Vec<u32>> {
let (verts, verts_f64) = v.tables();
plan_vertex_splits(
tris,
VertTables {
verts: &verts,
verts_f64: &verts_f64,
},
)
}
fn copies_at(
tris: &[[u32; 3]],
plan: &[u32],
vid: u32,
group: impl Fn(usize) -> bool,
) -> HashSet<u32> {
let mut out = HashSet::new();
for (t, tri) in tris.iter().enumerate() {
if !group(t) {
continue;
}
for c in 0..3 {
if tri[c] == vid {
out.insert(plan[3 * t + c]);
}
}
}
out
}
#[test]
fn ordinary_closed_mesh_needs_no_split() {
let mut v = Verts::default();
let tris = box_tris(&mut v, [0.0; 3], [1.0; 3]);
assert_eq!(plan(&tris, &v), None);
}
#[test]
fn two_cubes_sharing_an_edge_pair_within_each_cube() {
let mut v = Verts::default();
let mut tris = box_tris(&mut v, [0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
let n_a = tris.len();
tris.extend(box_tris(&mut v, [-1.0, -1.0, 0.0], [0.0, 0.0, 1.0]));
let (lo, hi) = (v.id(0.0, 0.0, 0.0), v.id(0.0, 0.0, 1.0));
let plan = plan(&tris, &v).expect("four half-edges on the shared edge");
for shared in [lo, hi] {
let a = copies_at(&tris, &plan, shared, |t| t < n_a);
let b = copies_at(&tris, &plan, shared, |t| t >= n_a);
assert_eq!(a.len(), 1, "cube A's corners at {shared} must be one fan");
assert_eq!(b.len(), 1, "cube B's corners at {shared} must be one fan");
assert!(a != b, "the two cubes must not share a copy of {shared}");
}
for (t, tri) in tris.iter().enumerate() {
for c in 0..3 {
if tri[c] != lo && tri[c] != hi {
assert_eq!(plan[3 * t + c], 0, "unpinched vertex {} split", tri[c]);
}
}
}
}
#[test]
fn three_wedges_around_an_edge_pair_within_each_wedge() {
let mut v = Verts::default();
let mut tris = Vec::new();
let mut spans = Vec::new();
for (a, b) in [(0.0, 60.0), (120.0, 180.0), (240.0, 300.0)] {
let start = tris.len();
tris.extend(wedge_tris(&mut v, a, b));
spans.push(start..tris.len());
}
let (lo, hi) = (v.id(0.0, 0.0, 0.0), v.id(0.0, 0.0, 1.0));
let plan = plan(&tris, &v).expect("six half-edges on the axis edge");
for axis in [lo, hi] {
let mut seen = HashSet::new();
for span in &spans {
let c = copies_at(&tris, &plan, axis, |t| span.contains(&t));
assert_eq!(c.len(), 1, "a wedge's corners at {axis} must be one fan");
seen.extend(c);
}
assert_eq!(seen.len(), 3, "each wedge needs its own copy of {axis}");
}
}
#[test]
fn non_alternating_fan_is_rejected() {
let mut v = Verts::default();
let mut tris = box_tris(&mut v, [0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
for t in box_tris(&mut v, [-1.0, -1.0, 0.0], [0.0, 0.0, 1.0]) {
tris.push([t[0], t[2], t[1]]);
}
assert_eq!(plan(&tris, &v), None);
}
#[test]
fn odd_fan_is_rejected() {
let mut v = Verts::default();
let mut tris = box_tris(&mut v, [0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
tris.extend(box_tris(&mut v, [-1.0, -1.0, 0.0], [0.0, 0.0, 1.0]));
let (lo, hi) = (v.id(0.0, 0.0, 0.0), v.id(0.0, 0.0, 1.0));
let loose = v.id(2.0, 2.0, 2.0);
tris.push([lo, hi, loose]);
assert_eq!(plan(&tris, &v), None);
}