use super::*;
#[test]
fn test_cpp_hull_tictac() {
let tictac_rad = 100.0;
let tictac_height = 500.0;
let tictac_seg = 500;
let tictac_mid = tictac_height - 2.0 * tictac_rad;
let sphere = Manifold::sphere(tictac_rad, tictac_seg);
let spheres = vec![
sphere.clone(),
sphere.translate(Vec3::new(0.0, 0.0, tictac_mid)),
];
let tictac = Manifold::hull_manifolds(&spheres);
assert!(
(tictac.num_vert() as i64 - (sphere.num_vert() as i64 + tictac_seg as i64)).abs() <= 1,
"Tictac: {} verts, expected ~{}", tictac.num_vert(), sphere.num_vert() + tictac_seg as usize
);
}
#[test]
fn test_cpp_hull_failing_test1() {
let pts = vec![
Vec3::new(-24.983196259, -43.272167206, 52.710712433),
Vec3::new(-25.0, -12.7726717, 49.907142639),
Vec3::new(-23.016393661, 39.865562439, 79.083930969),
Vec3::new(-24.983196259, -40.272167206, 52.710712433),
Vec3::new(-4.5177311897, -28.633184433, 50.405872345),
Vec3::new(11.176083565, -22.357545853, 45.275596619),
Vec3::new(-25.0, 21.885698318, 49.907142639),
Vec3::new(-17.633232117, -17.341972351, 89.96282196),
Vec3::new(26.922552109, 10.344738007, 57.146999359),
Vec3::new(-24.949174881, 1.5, 54.598075867),
Vec3::new(9.2058267593, -23.47851944, 55.334011078),
Vec3::new(13.26748085, -19.979951859, 28.117856979),
Vec3::new(-18.286884308, 31.673814774, 2.1749999523),
Vec3::new(18.419618607, -18.215343475, 52.450099945),
Vec3::new(-24.983196259, 43.272167206, 52.710712433),
Vec3::new(-1.6232370138, -29.794223785, 48.394889832),
Vec3::new(49.865573883, -0.0, 55.507141113),
Vec3::new(-18.627283096, -39.544368744, 55.507141113),
Vec3::new(-20.442623138, -35.407661438, 8.2749996185),
Vec3::new(10.229375839, -14.717799187, 10.508025169),
];
let hull = Manifold::hull(&pts);
assert!(!hull.is_empty(), "FailingTest1 hull should not be empty");
assert!(hull.volume() > 0.0, "FailingTest1 hull should have positive volume");
}
#[test]
fn test_cpp_hull_failing_test2() {
let pts = vec![
Vec3::new(174.17001343, -12.022000313, 29.562002182),
Vec3::new(174.51400757, -10.858000755, -3.3340001106),
Vec3::new(187.50801086, 22.826000214, 23.486001968),
Vec3::new(172.42800903, 12.018000603, 28.120000839),
Vec3::new(180.98001099, -26.866001129, 6.9100003242),
Vec3::new(172.42800903, -12.022000313, 28.120000839),
Vec3::new(174.17001343, 19.498001099, 29.562002182),
Vec3::new(213.96600342, 2.9400000572, -11.100000381),
Vec3::new(182.53001404, -22.49200058, 23.644001007),
Vec3::new(175.89401245, 19.900001526, 16.118000031),
Vec3::new(211.38601685, 3.0200002193, -14.250000954),
Vec3::new(183.7440033, 12.018000603, 18.090000153),
Vec3::new(210.51000977, 2.5040001869, -11.100000381),
Vec3::new(204.13601685, 34.724002838, -11.250000954),
Vec3::new(193.23400879, -24.704000473, 17.768001556),
Vec3::new(171.62800598, -19.502000809, 27.320001602),
Vec3::new(189.67401123, 8.486000061, -5.4080004692),
Vec3::new(193.23800659, 24.704000473, 17.758001328),
Vec3::new(165.36801147, -6.5600004196, -14.250000954),
Vec3::new(174.17001343, -19.502000809, 29.562002182),
Vec3::new(190.06401062, -0.81000006199, -14.250000954),
];
let hull = Manifold::hull(&pts);
assert!(!hull.is_empty(), "FailingTest2 hull should not be empty");
assert!(hull.volume() > 0.0, "FailingTest2 hull should have positive volume");
}
#[test]
fn test_cpp_hull_disabled_face_test() {
let pts = vec![
Vec3::new(65.398902893, 58.303115845, 58.765388489),
Vec3::new(42.147319794, 44.512584686, 75.703102112),
Vec3::new(89.208251953, 97.092460632, 41.632453918),
Vec3::new(69.860748291, 69.860748291, 56.492958069),
Vec3::new(45.375354767, 39.067985535, 64.844772339),
Vec3::new(26.555616379, 18.671405792, 81.067504883),
Vec3::new(88.179382324, 81.083595276, 43.981628418),
Vec3::new(51.823883057, 50.247039795, 70.359062195),
Vec3::new(58.489616394, 72.681190491, 51.274829865),
Vec3::new(110.0, 10.0, 65.0),
Vec3::new(29.590316772, 20.917686462, 73.143547058),
Vec3::new(101.61526489, 98.461585999, 30.909877777),
];
let hull = Manifold::hull(&pts);
assert!(!hull.is_empty(), "DisabledFaceTest hull should not be empty");
assert!(hull.volume() > 0.0);
}
#[test]
fn test_cpp_hull_degenerate_2d() {
let hull = Manifold::hull(&[
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(0.5, 0.0, 0.0),
Vec3::new(0.5, 0.0, 0.0),
Vec3::new(0.5, 0.0, 1.0),
]);
assert!(!hull.is_empty(), "Degenerate2D hull should not be empty");
let bb = hull.bounding_box();
assert!((bb.min.x - 0.0).abs() < 1e-6);
assert!((bb.min.y - 0.0).abs() < 1e-6);
assert!((bb.min.z - 0.0).abs() < 1e-6);
assert!((bb.max.x - 0.5).abs() < 1e-6);
assert!((bb.max.y - 0.0).abs() < 1e-6);
assert!((bb.max.z - 1.0).abs() < 1e-6);
assert!((hull.volume()).abs() < 1e-10, "Degenerate2D hull volume should be 0");
}
#[test]
fn test_cpp_hull_degenerate_1d() {
let hull = Manifold::hull(&[
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.5, 0.0, 0.0),
Vec3::new(0.5, 0.0, 0.0),
Vec3::new(0.5, 0.0, 0.0),
]);
assert!(hull.volume().abs() < 1e-10, "Degenerate1D hull volume should be 0, got {}", hull.volume());
}
#[test]
fn test_cpp_hull_not_enough_points() {
let hull = Manifold::hull(&[
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.5, 0.0, 0.0),
]);
assert!(hull.volume().abs() < 1e-10, "NotEnoughPoints hull volume should be 0, got {}", hull.volume());
}
#[test]
fn test_cpp_hull_empty_hull() {
let hull = Manifold::hull(&[]);
assert!(hull.is_empty(), "EmptyHull should be empty");
}
fn menger_sponge(n: i32) -> Manifold {
let result = Manifold::cube(Vec3::splat(1.0), true);
let mut holes: Vec<Manifold> = Vec::new();
fractal(&mut holes, &result, 1.0, Vec2::new(0.0, 0.0), 1, n);
let hole = Manifold::batch_boolean(&holes, OpType::Add);
let result = result.difference(&hole);
let hole = hole.rotate(90.0, 0.0, 0.0);
let result = result.difference(&hole);
let hole = hole.rotate(0.0, 0.0, 90.0);
result.difference(&hole)
}
fn fractal(holes: &mut Vec<Manifold>, hole: &Manifold, w: f64, position: Vec2, depth: i32, max_depth: i32) {
let w = w / 3.0;
holes.push(hole.scale(Vec3::new(w, w, 1.0)).translate(Vec3::new(position.x, position.y, 0.0)));
if depth == max_depth { return; }
let offsets = [
Vec2::new(-w, -w), Vec2::new(-w, 0.0), Vec2::new(-w, w), Vec2::new(0.0, w),
Vec2::new(w, w), Vec2::new(w, 0.0), Vec2::new(w, -w), Vec2::new(0.0, -w),
];
for off in &offsets {
fractal(holes, hole, w, position + *off, depth + 1, max_depth);
}
}
#[test]
fn test_cpp_hull_menger_sponge_depth2() {
let sponge = menger_sponge(2).rotate(10.0, 20.0, 30.0);
let hull = sponge.convex_hull();
assert_eq!(hull.num_tri(), 12, "MengerSponge(2) hull tris={}", hull.num_tri());
assert!((hull.surface_area() - 6.0).abs() < 1e-4,
"MengerSponge(2) hull sa={}", hull.surface_area());
assert!((hull.volume() - 1.0).abs() < 1e-4,
"MengerSponge(2) hull vol={}", hull.volume());
}
#[test]
#[ignore = "Slow: depth-4 CSG (~400k tris). 19.9s release sequential (parity with \
sequential C++ at 19.1s), 9.6s with `--features parallel`; debug takes \
minutes. Kept ignored purely for suite speed — passes in release."]
fn test_cpp_hull_menger_sponge() {
let sponge = menger_sponge(4).rotate(10.0, 20.0, 30.0);
let hull = sponge.convex_hull();
assert_eq!(hull.num_tri(), 12, "MengerSponge hull tris={}", hull.num_tri());
assert!((hull.surface_area() - 6.0).abs() < 1e-4,
"MengerSponge hull sa={}", hull.surface_area());
assert!((hull.volume() - 1.0).abs() < 1e-4,
"MengerSponge hull vol={}", hull.volume());
}
#[test]
#[ignore = "Slow: 1500-segment sphere hull"]
fn test_cpp_hull_sphere() {
let sphere = Manifold::sphere(1.0, 1500).translate(Vec3::new(0.5, 0.5, 0.5));
let hull = Manifold::hull_manifolds(&[sphere.clone()]);
assert_eq!(hull.num_tri(), sphere.num_tri());
assert!((hull.volume() - sphere.volume()).abs() < 1e-4,
"Hull sphere volume {} vs sphere {}", hull.volume(), sphere.volume());
}