use super::*;
use crate::linalg::{mat4_to_mat3x4, translation_matrix, Vec3};
use crate::properties::Property;
#[test]
fn test_compose_meshes_disjoint_cubes() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(3.0, 0.0, 0.0))));
let c = compose_meshes(&[a, b]);
assert_eq!(c.num_tri(), 24);
assert_eq!(c.num_vert(), 16);
}
#[test]
fn test_boolean_add_disjoint() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(3.0, 0.0, 0.0))));
let c = boolean(&a, &b, OpType::Add);
assert_eq!(c.num_tri(), 24);
assert_eq!(c.num_vert(), 16);
}
#[test]
fn test_boolean_intersect_disjoint_empty() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(3.0, 0.0, 0.0))));
let c = boolean(&a, &b, OpType::Intersect);
assert!(c.is_empty());
}
#[test]
fn test_boolean_subtract_disjoint() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(3.0, 0.0, 0.0))));
let c = boolean(&a, &b, OpType::Subtract);
assert_eq!(c.num_tri(), 12);
}
#[test]
fn test_boolean3_no_overlap() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(3.0, 0.0, 0.0))));
let bool3 = Boolean3::new(&a, &b, OpType::Add);
assert!(bool3.valid);
assert!(bool3.xv12.p1q2.is_empty());
assert!(bool3.xv21.p1q2.is_empty());
assert_eq!(bool3.w03.len(), a.num_vert());
assert_eq!(bool3.w30.len(), b.num_vert());
assert!(bool3.w03.iter().all(|&w| w == 0));
assert!(bool3.w30.iter().all(|&w| w == 0));
}
#[test]
fn test_boolean3_overlapping_cubes() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.5, 0.3, 0.2))));
let bool3 = Boolean3::new(&a, &b, OpType::Add);
assert!(bool3.valid);
assert!(
!bool3.xv12.p1q2.is_empty() || !bool3.xv21.p1q2.is_empty(),
"Overlapping cubes should produce intersections"
);
}
#[test]
fn test_cube_has_vert_normals() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
eprintln!("Cube has {} vert_normals for {} verts", a.vert_normal.len(), a.num_vert());
for (i, n) in a.vert_normal.iter().enumerate() {
eprintln!(" normal[{}] = ({:.4}, {:.4}, {:.4})", i, n.x, n.y, n.z);
}
assert_eq!(a.vert_normal.len(), a.num_vert(), "vert_normal should be populated");
}
#[test]
fn test_boolean_union_overlapping_cubes() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.5, 0.3, 0.2))));
let result = boolean(&a, &b, OpType::Add);
let expected_vol = 2.0 - 0.5 * 0.7 * 0.8; assert!(!result.is_empty(), "Union should not be empty");
let vol = result.get_property(Property::Volume).abs();
assert!(
(vol - expected_vol).abs() < 0.05,
"Union volume should be ~{:.3}, got {}",
expected_vol,
vol
);
}
#[test]
fn test_boolean_intersect_overlapping_cubes() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.5, 0.3, 0.2))));
let result = boolean(&a, &b, OpType::Intersect);
let expected_vol = 0.5 * 0.7 * 0.8;
assert!(!result.is_empty(), "Intersection should not be empty");
let vol = result.get_property(Property::Volume).abs();
assert!(
(vol - expected_vol).abs() < 0.05,
"Intersection volume should be ~{:.3}, got {}",
expected_vol,
vol
);
}
#[test]
fn test_boolean_subtract_overlapping_cubes() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.5, 0.3, 0.2))));
let result = boolean(&a, &b, OpType::Subtract);
let expected_vol = 1.0 - 0.5 * 0.7 * 0.8;
assert!(!result.is_empty(), "Difference should not be empty");
let vol = result.get_property(Property::Volume).abs();
assert!(
(vol - expected_vol).abs() < 0.05,
"Difference volume should be ~{:.3}, got {}",
expected_vol,
vol
);
}
#[test]
fn test_boolean_union_same_position() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let result = boolean(&a, &b, OpType::Add);
let vol = result.get_property(Property::Volume).abs();
assert!(
(vol - 1.0).abs() < 0.1,
"Union of identical cubes should have volume ~1.0, got {}",
vol
);
}
#[test]
fn test_boolean_intersect_touching() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(1.0, 0.0, 0.0))));
let result = boolean(&a, &b, OpType::Intersect);
let vol = result.get_property(Property::Volume).abs();
assert!(
vol < 0.01,
"Intersection of touching cubes should have ~0 volume, got {}",
vol
);
}
#[test]
fn test_boolean_intersect_disjoint_returns_empty() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(5.0, 0.0, 0.0))));
let result = boolean(&a, &b, OpType::Intersect);
assert!(result.is_empty(), "Intersection of disjoint cubes should be empty");
}
#[test]
fn test_boolean_union_small_overlap() {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.9, 0.0, 0.0))));
let result = boolean(&a, &b, OpType::Add);
let expected_vol = 2.0 - 0.1; assert!(!result.is_empty(), "Union should not be empty");
let vol = result.get_property(Property::Volume).abs();
assert!(
(vol - expected_vol).abs() < 0.1,
"Union volume should be ~{:.3}, got {}",
expected_vol,
vol
);
}
#[test]
fn test_boolean_intersect_various_offsets() {
for &offset in &[0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0, 1.5] {
let a = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(0.0, 0.0, 0.0))));
let b = ManifoldImpl::cube(&mat4_to_mat3x4(translation_matrix(Vec3::new(offset, 0.0, 0.0))));
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
boolean(&a, &b, OpType::Intersect)
}));
match result {
Ok(r) => {
let vol = r.get_property(Property::Volume).abs();
eprintln!("Intersect offset={}: vol={:.4} verts={} tris={} empty={}",
offset, vol, r.num_vert(), r.num_tri(), r.is_empty());
}
Err(e) => {
eprintln!("Intersect offset={}: PANIC {:?}", offset, e.downcast_ref::<String>());
}
}
}
}
#[test]
fn test_boolean_tetra() {
use crate::manifold::Manifold;
let tetra = Manifold::tetrahedron();
assert!(!tetra.is_empty());
let tetra2 = tetra.translate(Vec3::splat(0.5));
let result = tetra2.difference(&tetra);
assert_eq!(result.num_vert(), 8);
assert_eq!(result.num_tri(), 12);
}
#[test]
fn test_boolean_mirrored() {
use crate::manifold::Manifold;
let cube = Manifold::cube(Vec3::splat(1.0), false).scale(Vec3::new(1.0, -1.0, 1.0));
assert!(cube.matches_tri_normals(), "Mirrored cube should match tri normals");
let cube2 = Manifold::cube(Vec3::splat(1.0), false).scale(Vec3::new(0.5, -1.0, 0.5));
let result = cube.difference(&cube2);
assert!((result.volume() - 0.75).abs() < 1e-5,
"Volume should be 0.75, got {}", result.volume());
assert!((result.surface_area() - 5.5).abs() < 1e-5,
"Surface area should be 5.5, got {}", result.surface_area());
assert_eq!(result.genus(), 0);
assert!(result.num_vert() <= 14);
assert!(result.num_tri() <= 24);
}
#[test]
fn test_boolean_cubes() {
use crate::manifold::Manifold;
let mut result = Manifold::cube(Vec3::new(1.2, 1.0, 1.0), true)
.translate(Vec3::new(0.0, -0.5, 0.5));
result = result.union(
&Manifold::cube(Vec3::new(1.0, 0.8, 0.5), false)
.translate(Vec3::new(-0.5, 0.0, 0.5)),
);
result = result.union(
&Manifold::cube(Vec3::new(1.2, 0.1, 0.5), false)
.translate(Vec3::new(-0.6, -0.1, 0.0)),
);
assert!(result.matches_tri_normals());
assert!(result.num_degenerate_tris() <= 0);
assert!((result.volume() - 1.6).abs() < 0.001);
assert!((result.surface_area() - 9.2).abs() < 0.01);
}
#[test]
fn test_boolean_no_retained_verts() {
use crate::manifold::Manifold;
let cube = Manifold::cube(Vec3::splat(1.0), true);
let oct = Manifold::sphere(1.0, 4);
assert!((cube.volume() - 1.0).abs() < 0.001);
assert!((oct.volume() - 1.333).abs() < 0.001);
let result = cube.intersection(&oct);
assert!((result.volume() - 0.833).abs() < 0.001);
}
#[test]
fn test_boolean_self_subtract() {
use crate::manifold::Manifold;
let cube = Manifold::cube(Vec3::splat(1.0), false);
let empty = cube.difference(&cube);
assert!(empty.is_empty());
assert!((empty.volume()).abs() < 1e-10);
assert!((empty.surface_area()).abs() < 1e-10);
}
#[test]
fn test_boolean_union_difference() {
use crate::manifold::Manifold;
let block = Manifold::cube(Vec3::splat(1.0), true)
.difference(&Manifold::cylinder(1.0, 0.5, 0.5, 32));
let result = block.union(&block.translate(Vec3::new(0.0, 0.0, 1.0)));
let result_vol = result.volume();
let block_vol = block.volume();
assert!(
(result_vol - block_vol * 2.0).abs() < 0.0001,
"Expected union of two identical blocks to be 2x volume: got {} vs {}",
result_vol,
block_vol * 2.0
);
}
#[test]
fn test_boolean_tree_transforms() {
use crate::manifold::Manifold;
let c = Manifold::cube(Vec3::splat(1.0), false);
let a = c.union(&c).translate(Vec3::new(1.0, 0.0, 0.0));
let b = c.union(&c);
let vol = a.union(&b).volume();
assert!((vol - 2.0).abs() < 1e-5, "Expected volume 2.0, got {}", vol);
}
#[test]
fn test_boolean_face_union() {
use crate::manifold::Manifold;
let cubes = Manifold::cube(Vec3::splat(1.0), false)
.union(&Manifold::cube(Vec3::splat(1.0), false).translate(Vec3::new(1.0, 0.0, 0.0)));
assert_eq!(cubes.genus(), 0);
assert_eq!(cubes.num_vert(), 12);
assert_eq!(cubes.num_tri(), 20);
assert!((cubes.volume() - 2.0).abs() < 1e-5);
assert!((cubes.surface_area() - 10.0).abs() < 1e-5);
}
#[test]
fn test_boolean_edge_union() {
use crate::manifold::Manifold;
let cubes = Manifold::cube(Vec3::splat(1.0), false)
.union(&Manifold::cube(Vec3::splat(1.0), false).translate(Vec3::new(1.0, 1.0, 0.0)));
assert_eq!(cubes.volume(), 2.0);
}
#[test]
fn test_boolean_corner_union() {
use crate::manifold::Manifold;
let cubes = Manifold::cube(Vec3::splat(1.0), false)
.union(&Manifold::cube(Vec3::splat(1.0), false).translate(Vec3::new(1.0, 1.0, 1.0)));
assert_eq!(cubes.volume(), 2.0);
}
#[test]
fn test_boolean_coplanar() {
use crate::manifold::Manifold;
let cyl = Manifold::cylinder(1.0, 1.0, 1.0, 32);
let cyl2 = cyl.scale(Vec3::new(0.8, 0.8, 1.0)).rotate(0.0, 0.0, 185.0);
let out = cyl.difference(&cyl2);
assert_eq!(out.num_degenerate_tris(), 0);
assert_eq!(out.genus(), 1);
}
#[test]
fn test_boolean_multi_coplanar() {
use crate::manifold::Manifold;
let cube = Manifold::cube(Vec3::splat(1.0), false);
let first = cube.difference(&cube.translate(Vec3::new(0.3, 0.3, 0.0)));
let cube2 = cube.translate(Vec3::new(-0.3, -0.3, 0.0));
let out = first.difference(&cube2);
assert_eq!(out.genus(), -1);
assert!((out.volume() - 0.18).abs() < 1e-5);
assert!((out.surface_area() - 2.76).abs() < 1e-5);
}
#[test]
fn test_boolean_empty() {
use crate::manifold::Manifold;
let cube = Manifold::cube(Vec3::splat(1.0), false);
let cube_vol = cube.volume();
let empty = Manifold::empty();
assert!((cube.union(&empty).volume() - cube_vol).abs() < 1e-10);
assert!((cube.difference(&empty).volume() - cube_vol).abs() < 1e-10);
assert!(empty.difference(&cube).is_empty());
assert!(cube.intersection(&empty).is_empty());
}
#[test]
fn test_boolean_non_intersecting() {
use crate::manifold::Manifold;
let cube1 = Manifold::cube(Vec3::splat(1.0), false);
let vol1 = cube1.volume();
let cube2 = cube1.scale(Vec3::splat(2.0)).translate(Vec3::new(3.0, 0.0, 0.0));
let vol2 = cube2.volume();
assert!((cube1.union(&cube2).volume() - (vol1 + vol2)).abs() < 1e-5);
assert!((cube1.difference(&cube2).volume() - vol1).abs() < 1e-5);
assert!(cube1.intersection(&cube2).is_empty());
}
#[test]
fn test_boolean_perturb() {
use crate::manifold::Manifold;
let tetra = Manifold::tetrahedron();
let empty = tetra.difference(&tetra);
assert!(empty.is_empty());
assert!((empty.volume()).abs() < 1e-10);
assert!((empty.surface_area()).abs() < 1e-10);
}
#[test]
fn test_boolean_complex_sphere() {
use crate::manifold::Manifold;
let sphere = Manifold::sphere(1.0, 12);
let sphere2 = sphere.translate(Vec3::splat(0.5));
let result = sphere.difference(&sphere2);
assert_eq!(result.num_degenerate_tris(), 0);
assert!(result.num_vert() > 0);
assert!(result.num_tri() > 0);
assert!(result.volume() > 0.0);
}
#[test]
fn test_boolean_volumes() {
use crate::manifold::Manifold;
let m1 = Manifold::cube(Vec3::new(1.0, 1.0, 1.0), false);
let m2 = Manifold::cube(Vec3::new(2.0, 1.0, 1.0), false)
.translate(Vec3::new(1.0, 0.0, 0.0));
let m4 = Manifold::cube(Vec3::new(4.0, 1.0, 1.0), false)
.translate(Vec3::new(3.0, 0.0, 0.0));
let m3 = Manifold::cube(Vec3::new(3.0, 1.0, 1.0), false);
let m7 = Manifold::cube(Vec3::new(7.0, 1.0, 1.0), false);
assert!((m1.intersection(&m2).volume()).abs() < 1e-5, "m1^m2 should be 0");
assert!((m1.union(&m2).union(&m4).volume() - 7.0).abs() < 1e-5, "m1+m2+m4 should be 7");
assert!((m1.union(&m2).difference(&m4).volume() - 3.0).abs() < 1e-5, "m1+m2-m4 should be 3");
assert!((m1.union(&m2.intersection(&m4)).volume() - 1.0).abs() < 1e-5, "m1+(m2^m4) should be 1");
assert!((m7.intersection(&m4).volume() - 4.0).abs() < 1e-5, "m7^m4 should be 4");
assert!((m7.intersection(&m3).intersection(&m1).volume() - 1.0).abs() < 1e-5, "m7^m3^m1 should be 1");
assert!((m7.intersection(&m1.union(&m2)).volume() - 3.0).abs() < 1e-5, "m7^(m1+m2) should be 3");
assert!((m7.difference(&m4).volume() - 3.0).abs() < 1e-5, "m7-m4 should be 3");
assert!((m7.difference(&m4).difference(&m2).volume() - 1.0).abs() < 1e-5, "m7-m4-m2 should be 1");
assert!((m7.difference(&m7.difference(&m1)).volume() - 1.0).abs() < 1e-5, "m7-(m7-m1) should be 1");
assert!((m7.difference(&m1.union(&m2)).volume() - 4.0).abs() < 1e-5, "m7-(m1+m2) should be 4");
}
#[test]
fn test_boolean_spiral() {
use crate::manifold::Manifold;
let d = 2.0;
fn spiral(rec: i32, r: f64, add: f64, d: f64) -> Manifold {
let rot = 360.0 / (std::f64::consts::PI * r * 2.0) * d;
let r_next = r + add / 360.0 * rot;
let cube = Manifold::cube(Vec3::splat(1.0), true)
.translate(Vec3::new(0.0, r, 0.0));
if rec > 0 {
spiral(rec - 1, r_next, add, d).rotate(0.0, 0.0, rot).union(&cube)
} else {
cube
}
}
let result = spiral(10, 25.0, 2.0, d);
assert_eq!(result.genus(), -10);
}
#[test]
fn test_boolean_almost_coplanar() {
use crate::manifold::Manifold;
let tet = Manifold::tetrahedron();
let result = tet
.union(&tet.rotate(0.001, -0.08472872823860228, 0.055910459615905288))
.union(&tet);
assert!(result.num_vert() >= 20 && result.num_vert() <= 22,
"Expected ~20 verts, got {}", result.num_vert());
assert!(result.num_tri() >= 36 && result.num_tri() <= 40,
"Expected ~36 tris, got {}", result.num_tri());
assert!(result.volume() > 0.0, "Result should not be empty");
assert_eq!(result.genus(), 0);
}
#[test]
fn test_boolean_perturb1() {
use crate::manifold::Manifold;
use crate::linalg::Vec2;
type Polygons = Vec<Vec<Vec2>>;
let big_polys: Polygons = vec![
vec![Vec2::new(0.0, 2.0), Vec2::new(2.0, 0.0), Vec2::new(4.0, 2.0), Vec2::new(2.0, 4.0)],
vec![Vec2::new(1.0, 2.0), Vec2::new(2.0, 3.0), Vec2::new(3.0, 2.0), Vec2::new(2.0, 1.0)],
];
let big = Manifold::extrude(&big_polys, 1.0, 0, 0.0, Vec2::new(1.0, 1.0));
let little_polys: Polygons = vec![
vec![Vec2::new(2.0, 1.0), Vec2::new(3.0, 2.0), Vec2::new(2.0, 3.0), Vec2::new(1.0, 2.0)],
];
let little = Manifold::extrude(&little_polys, 1.0, 0, 0.0, Vec2::new(1.0, 1.0))
.translate(Vec3::new(0.0, 0.0, 1.0));
let punch_polys: Polygons = vec![
vec![Vec2::new(1.0, 2.0), Vec2::new(2.0, 2.0), Vec2::new(2.0, 3.0)],
];
let punch_hole = Manifold::extrude(&punch_polys, 1.0, 0, 0.0, Vec2::new(1.0, 1.0))
.translate(Vec3::new(0.0, 0.0, 1.0));
let result = big.union(&little).difference(&punch_hole);
assert_eq!(result.num_degenerate_tris(), 0);
assert_eq!(result.num_vert(), 24, "verts: {}", result.num_vert());
assert!((result.volume() - 7.5).abs() < 1e-5, "volume: {}", result.volume());
assert!((result.surface_area() - 38.2).abs() < 0.1, "SA: {}", result.surface_area());
}
#[test]
fn test_boolean_complex_subtract() {
use crate::manifold::Manifold;
use crate::types::MeshGL;
let mut first_mesh = MeshGL::default();
first_mesh.num_prop = 3;
first_mesh.vert_properties = vec![
0.0, 0.0, 0.0,
1540.0, 0.0, 0.0,
1540.0, 70.0, 0.0,
0.0, 70.0, 0.0,
0.0, 0.0, -278.282,
1540.0, 70.0, -278.282,
1540.0, 0.0, -278.282,
0.0, 70.0, -278.282,
];
first_mesh.tri_verts = vec![
0, 1, 2,
2, 3, 0,
4, 5, 6,
5, 4, 7,
6, 2, 1,
6, 5, 2,
5, 3, 2,
5, 7, 3,
7, 0, 3,
7, 4, 0,
4, 1, 0,
4, 6, 1,
];
let mut second_mesh = MeshGL::default();
second_mesh.num_prop = 3;
second_mesh.vert_properties = vec![
2.04636e-12, 70.0, 50000.0,
2.04636e-12, -1.27898e-13, 50000.0,
1470.0, -1.27898e-13, 50000.0,
1540.0, 70.0, 50000.0,
2.04636e-12, 70.0, -28.2818,
1470.0, -1.27898e-13, 0.0,
2.04636e-12, -1.27898e-13, 0.0,
1540.0, 70.0, -28.2818,
];
second_mesh.tri_verts = vec![
0, 1, 2,
2, 3, 0,
4, 5, 6,
5, 4, 7,
6, 2, 1,
6, 5, 2,
5, 3, 2,
5, 7, 3,
7, 0, 3,
7, 4, 0,
4, 1, 0,
4, 6, 1,
];
let first = Manifold::from_mesh_gl(&first_mesh);
let second = Manifold::from_mesh_gl(&second_mesh);
let result = first.difference(&second);
let _ = result.get_mesh_gl(0);
assert_eq!(result.status(), crate::types::Error::NoError);
}
#[test]
fn test_boolean_precision2() {
use crate::manifold::Manifold;
let k_precision: f64 = 1e-12;
let scale = 1000.0;
let cube = Manifold::cube(Vec3::splat(scale), false);
let distance = scale * (1.0 - k_precision / 2.0);
let cube2 = cube.translate(Vec3::splat(-distance));
let intersection = cube.intersection(&cube2);
assert!(intersection.volume() < 1e-6,
"Near-precision intersection volume should be tiny: {}", intersection.volume());
}
#[test]
fn test_boolean_cubes_complex() {
use crate::manifold::Manifold;
let mut result = Manifold::cube(Vec3::new(1.2, 1.0, 1.0), true)
.translate(Vec3::new(0.0, -0.5, 0.5));
result = result.union(
&Manifold::cube(Vec3::new(1.0, 0.8, 0.5), false)
.translate(Vec3::new(-0.5, 0.0, 0.5)),
);
result = result.union(
&Manifold::cube(Vec3::new(1.2, 0.1, 0.5), false)
.translate(Vec3::new(-0.6, -0.1, 0.0)),
);
assert!(result.matches_tri_normals());
assert!(result.num_degenerate_tris() <= 0);
assert!((result.volume() - 1.6).abs() < 0.001, "volume: {}", result.volume());
assert!((result.surface_area() - 9.2).abs() < 0.01, "SA: {}", result.surface_area());
}
#[test]
fn test_boolean_union_difference_stacked() {
use crate::manifold::Manifold;
let block = Manifold::cube(Vec3::splat(1.0), true)
.difference(&Manifold::cylinder(1.0, 0.5, 0.5, 32));
let result = block.union(&block.translate(Vec3::new(0.0, 0.0, 1.0)));
let blocksize = block.volume();
assert!((result.volume() - blocksize * 2.0).abs() < 0.0001,
"Stacked union volume: {} expected: {}", result.volume(), blocksize * 2.0);
}
#[test]
fn test_boolean_coplanar_cylinder() {
use crate::manifold::Manifold;
let cylinder = Manifold::cylinder(1.0, 1.0, 1.0, 32);
let cylinder2 = cylinder.scale(Vec3::new(0.8, 0.8, 1.0))
.rotate(0.0, 0.0, 185.0);
let out = cylinder.difference(&cylinder2);
assert_eq!(out.num_degenerate_tris(), 0);
assert_eq!(out.genus(), 1);
}
#[test]
fn test_boolean_multi_coplanar_complex() {
use crate::manifold::Manifold;
let cube = Manifold::cube(Vec3::splat(1.0), false);
let first = cube.difference(&cube.translate(Vec3::new(0.3, 0.3, 0.0)));
let cube2 = cube.translate(Vec3::new(-0.3, -0.3, 0.0));
let out = first.difference(&cube2);
assert_eq!(out.genus(), -1);
assert!((out.volume() - 0.18).abs() < 1e-5, "volume: {}", out.volume());
assert!((out.surface_area() - 2.76).abs() < 1e-5, "SA: {}", out.surface_area());
}