use crate::cube::corner_offset;
pub const TET_COUNT: usize = 6;
pub const TET_EDGE_COUNT: usize = 6;
pub const MAX_TET_TRIANGLES: usize = 2;
pub static TETS: [[u8; 4]; TET_COUNT] = build_tets();
const fn build_tets() -> [[u8; 4]; TET_COUNT] {
let orders = [
[0u8, 1, 2],
[0, 2, 1],
[1, 0, 2],
[1, 2, 0],
[2, 0, 1],
[2, 1, 0],
];
let mut out = [[0u8; 4]; TET_COUNT];
let mut t = 0usize;
while t < TET_COUNT {
let mut corner = 0u8;
out[t][0] = 0;
let mut step = 0usize;
while step < 3 {
corner |= 1 << orders[t][step];
out[t][step + 1] = corner;
step += 1;
}
t += 1;
}
out
}
pub const TET_EDGES: [[u8; 2]; TET_EDGE_COUNT] = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]];
#[must_use]
pub const fn tet_edge_corners(t: usize, edge: usize) -> [u8; 2] {
let [a, b] = TET_EDGES[edge];
[TETS[t][a as usize], TETS[t][b as usize]]
}
#[derive(Clone, Copy, Debug)]
pub struct TetCase {
pub count: u8,
pub triangles: [[u8; 3]; MAX_TET_TRIANGLES],
}
pub static TET_CASES: [[TetCase; 16]; TET_COUNT] = build_tet_cases();
const fn build_tet_cases() -> [[TetCase; 16]; TET_COUNT] {
let empty = TetCase {
count: 0,
triangles: [[0u8; 3]; MAX_TET_TRIANGLES],
};
let mut out = [[empty; 16]; TET_COUNT];
let mut t = 0usize;
while t < TET_COUNT {
let mut case = 0usize;
while case < 16 {
out[t][case] = build_tet_case(t, case as u8);
case += 1;
}
t += 1;
}
out
}
const fn edge_midpoint_x2(t: usize, edge: usize) -> [i32; 3] {
let [a, b] = tet_edge_corners(t, edge);
let pa = corner_offset(a);
let pb = corner_offset(b);
[
(pa[0] + pb[0]) as i32,
(pa[1] + pb[1]) as i32,
(pa[2] + pb[2]) as i32,
]
}
const fn build_tet_case(t: usize, case: u8) -> TetCase {
let mut triangles = [[0u8; 3]; MAX_TET_TRIANGLES];
let mut count = 0usize;
let mut cut = [false; TET_EDGE_COUNT];
let mut cut_count = 0usize;
let mut e = 0usize;
while e < TET_EDGE_COUNT {
let [a, b] = TET_EDGES[e];
let a_in = case & (1 << a) != 0;
let b_in = case & (1 << b) != 0;
cut[e] = a_in != b_in;
if cut[e] {
cut_count += 1;
}
e += 1;
}
if cut_count == 3 {
let mut tri = [0u8; 3];
let mut n = 0usize;
let mut e = 0usize;
while e < TET_EDGE_COUNT {
if cut[e] {
tri[n] = e as u8;
n += 1;
}
e += 1;
}
triangles[0] = orient(t, tri, case);
count = 1;
} else if cut_count == 4 {
let mut quad = [0u8; 4];
let mut n = 0usize;
let mut e = 0usize;
while e < TET_EDGE_COUNT {
if cut[e] {
quad[n] = e as u8;
n += 1;
}
e += 1;
}
let quad = order_quad(quad);
triangles[0] = orient(t, [quad[0], quad[1], quad[2]], case);
triangles[1] = orient(t, [quad[0], quad[2], quad[3]], case);
count = 2;
}
TetCase {
count: count as u8,
triangles,
}
}
const fn edges_share_a_corner(x: u8, y: u8) -> bool {
let [xa, xb] = TET_EDGES[x as usize];
let [ya, yb] = TET_EDGES[y as usize];
xa == ya || xa == yb || xb == ya || xb == yb
}
const fn order_quad(quad: [u8; 4]) -> [u8; 4] {
let mut out = [quad[0], 0, 0, 0];
let mut used = [true, false, false, false];
let mut n = 1usize;
while n < 4 {
let mut i = 1usize;
while i < 4 {
if !used[i] && edges_share_a_corner(out[n - 1], quad[i]) {
out[n] = quad[i];
used[i] = true;
break;
}
i += 1;
}
n += 1;
}
out
}
const fn orient(t: usize, tri: [u8; 3], case: u8) -> [u8; 3] {
let p0 = edge_midpoint_x2(t, tri[0] as usize);
let p1 = edge_midpoint_x2(t, tri[1] as usize);
let p2 = edge_midpoint_x2(t, tri[2] as usize);
let u = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]];
let v = [p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]];
let normal = [
u[1] * v[2] - u[2] * v[1],
u[2] * v[0] - u[0] * v[2],
u[0] * v[1] - u[1] * v[0],
];
let mut inside = 0usize;
while inside < 4 {
if case & (1 << inside) != 0 {
break;
}
inside += 1;
}
let c = corner_offset(TETS[t][inside]);
let from_inside = [
p0[0] - 2 * c[0] as i32,
p0[1] - 2 * c[1] as i32,
p0[2] - 2 * c[2] as i32,
];
let facing =
normal[0] * from_inside[0] + normal[1] * from_inside[1] + normal[2] * from_inside[2];
if facing >= 0 {
tri
} else {
[tri[0], tri[2], tri[1]]
}
}