use alloc::vec;
use alloc::vec::Vec;
use super::{FaceAmbiguity, face_is_joined, joined_mask};
use crate::cube::{face_corners, is_inside};
use crate::marching_cubes::table::{AMBIGUOUS_FACES, face_bit};
#[test]
fn a_dominant_inside_diagonal_joins() {
assert!(face_is_joined([-2.0f64, 1.0, -2.0, 1.0]));
assert!(face_is_joined([1.0f64, -2.0, 1.0, -2.0]));
}
#[test]
fn a_dominant_outside_diagonal_separates() {
assert!(!face_is_joined([-1.0f64, 2.0, -1.0, 2.0]));
assert!(!face_is_joined([2.0f64, -1.0, 2.0, -1.0]));
}
#[test]
fn a_degenerate_saddle_separates() {
assert!(!face_is_joined([-1.0f64, 1.0, -1.0, 1.0]));
assert!(!face_is_joined([-2.0f64, 1.0, -0.5, 1.0]));
}
#[test]
fn a_zero_on_the_outside_diagonal_joins() {
assert!(face_is_joined([-1.0f64, 0.0, -1.0, 3.0]));
assert!(face_is_joined([-1.0f64, 0.0, -1.0, 0.0]));
}
#[test]
fn the_decider_is_invariant_under_rotation_and_reflection() {
for v in ambiguous_quadruples() {
let expected = face_is_joined(v);
for r in 0..4usize {
let rotated = [v[r], v[(r + 1) % 4], v[(r + 2) % 4], v[(r + 3) % 4]];
assert_eq!(face_is_joined(rotated), expected, "rotation {r} of {v:?}");
let reflected = [rotated[0], rotated[3], rotated[2], rotated[1]];
assert_eq!(
face_is_joined(reflected),
expected,
"reflected rotation {r} of {v:?}"
);
}
}
}
#[test]
fn the_decider_agrees_with_the_bilinear_interpolant() {
const N: usize = 256;
const TOL: f64 = 2e-3;
let mut checked = 0usize;
let mut skipped = 0usize;
let mut joined_seen = 0usize;
for v in ambiguous_quadruples() {
let a = v[0] - v[1] + v[2] - v[3];
let saddle = (v[0] * v[2] - v[1] * v[3]) / (v[0] + v[2] - v[1] - v[3]);
if saddle.abs() <= TOL * a.abs() {
skipped += 1;
continue;
}
let truth = negative_corners_are_connected::<N>(v);
let decided = face_is_joined(v);
assert_eq!(
decided, truth,
"values {v:?}: decider says joined={decided}, dense sampling says {truth} \
(saddle {saddle})"
);
checked += 1;
joined_seen += usize::from(truth);
}
assert!(checked > 100, "only {checked} quadruples resolved");
assert!(
joined_seen > 0 && joined_seen < checked,
"joined={joined_seen} of {checked}"
);
assert!(
skipped * 4 < checked,
"{skipped} skipped against {checked} checked — the tolerance is eating the test"
);
}
#[test]
fn joined_mask_reads_only_the_faces_it_is_told_to() {
let corner_value = [-4.0f64, 0.5, 0.5, -4.0, 0.5, -4.0, -4.0, 0.5];
let case = case_of(&corner_value);
let ambiguous = AMBIGUOUS_FACES[case as usize];
assert_ne!(ambiguous, 0, "fixture has no ambiguous face");
assert_eq!(joined_mask(&corner_value, ambiguous), ambiguous);
assert_eq!(joined_mask(&corner_value, 0), 0);
for axis in 0..3usize {
for side in 0..2u8 {
let bit = face_bit(axis, side);
if ambiguous & bit == 0 {
continue;
}
assert_eq!(joined_mask(&corner_value, bit), bit);
}
}
}
#[test]
fn the_default_is_separate() {
assert_eq!(FaceAmbiguity::default(), FaceAmbiguity::Separate);
}
fn ambiguous_quadruples() -> Vec<[f64; 4]> {
const LADDER: [f64; 7] = [-4.0, -1.0, -0.25, 0.0, 0.25, 1.0, 4.0];
let mut out = Vec::new();
for &v0 in &LADDER {
for &v1 in &LADDER {
for &v2 in &LADDER {
for &v3 in &LADDER {
let v = [v0, v1, v2, v3];
if is_inside(v0) == is_inside(v2)
&& is_inside(v1) == is_inside(v3)
&& is_inside(v0) != is_inside(v1)
{
out.push(v);
}
}
}
}
}
out
}
fn negative_corners_are_connected<const N: usize>(v: [f64; 4]) -> bool {
let stride = N + 1;
let at = |i: usize, j: usize| -> f64 {
let s = i as f64 / N as f64;
let t = j as f64 / N as f64;
v[0] * (1.0 - s) * (1.0 - t) + v[1] * s * (1.0 - t) + v[2] * s * t + v[3] * (1.0 - s) * t
};
let index = |i: usize, j: usize| j * stride + i;
let (from, to) = if is_inside(v[0]) {
((0, 0), (N, N)) } else {
((N, 0), (0, N)) };
let mut seen = vec![false; stride * stride];
let mut stack = vec![from];
seen[index(from.0, from.1)] = true;
while let Some((i, j)) = stack.pop() {
if (i, j) == to {
return true;
}
let push = |i: usize, j: usize, stack: &mut Vec<(usize, usize)>, seen: &mut [bool]| {
let k = index(i, j);
if !seen[k] && is_inside(at(i, j)) {
seen[k] = true;
stack.push((i, j));
}
};
if i > 0 {
push(i - 1, j, &mut stack, &mut seen);
}
if i < N {
push(i + 1, j, &mut stack, &mut seen);
}
if j > 0 {
push(i, j - 1, &mut stack, &mut seen);
}
if j < N {
push(i, j + 1, &mut stack, &mut seen);
}
}
false
}
fn case_of(corner_value: &[f64; 8]) -> u8 {
let mut case = 0u8;
for (c, &v) in corner_value.iter().enumerate() {
if is_inside(v) {
case |= 1 << c;
}
}
case
}
#[test]
fn the_diagonals_are_corners_zero_two_and_one_three() {
for axis in 0..3usize {
for side in 0..2u8 {
let c = face_corners(axis, side);
assert_eq!((c[0] ^ c[2]).count_ones(), 2, "axis {axis} side {side}");
assert_eq!((c[1] ^ c[3]).count_ones(), 2, "axis {axis} side {side}");
}
}
}