use super::*;
#[test]
fn test_cpp_complex_self_intersect() {
let m1 = read_test_obj("self_intersectA.obj");
let m2 = read_test_obj("self_intersectB.obj");
let res = m1.union(&m2);
res.get_mesh_gl(0); }
#[test]
#[ignore = "Slow in debug only — passes in release (~18s; C++ takes ~50s). No longer hangs \
after the RecursiveEdgeSwap FormLoop / edge<0 fix in edge_op.rs. Kept ignored \
for debug-suite speed, like sdf_blobs/hull_sphere."]
fn test_cpp_complex_generic_twin_7081() {
let m1 = read_test_obj("Generic_Twin_7081.1.t0_left.obj");
let m2 = read_test_obj("Generic_Twin_7081.1.t0_right.obj");
let res = m1.union(&m2);
res.get_mesh_gl(0); }
#[test]
fn test_cpp_complex_generic_twin_7863() {
let m1 = read_test_obj("Generic_Twin_7863.1.t0_left.obj");
let m2 = read_test_obj("Generic_Twin_7863.1.t0_right.obj");
let res = m1.union(&m2);
res.get_mesh_gl(0); }
#[test]
fn test_cpp_complex_havocglass8() {
let m1 = read_test_obj("Havocglass8_left.obj");
let m2 = read_test_obj("Havocglass8_right.obj");
let res = m1.union(&m2);
res.get_mesh_gl(0); }
#[test]
fn test_cpp_complex_hull_mask() {
let body = read_test_obj("hull-body.obj");
let mask = read_test_obj("hull-mask.obj");
let ret = body.difference(&mask);
ret.get_mesh_gl(0);
}
#[test]
fn test_cpp_complex_offset_triangulation_failure() {
let a = read_test_obj("Offset1.obj");
let b = read_test_obj("Offset2.obj");
let result = a.union(&b);
assert_eq!(result.status(), Error::NoError,
"OffsetTriangulationFailure: status {:?}", result.status());
}
#[test]
fn test_cpp_complex_offset_self_intersect() {
let a = read_test_obj("Offset3.obj");
let b = read_test_obj("Offset4.obj");
let result = a.union(&b);
assert_eq!(result.status(), Error::NoError,
"OffsetSelfIntersect: status {:?}", result.status());
}
#[test]
fn test_cpp_complex_subtract() {
let first_verts: Vec<f32> = 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,
];
let first_tris: Vec<u32> = 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 first_mesh = MeshGL::default();
first_mesh.num_prop = 3;
first_mesh.vert_properties = first_verts;
first_mesh.tri_verts = first_tris;
let second_verts: Vec<f32> = 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,
];
let second_tris: Vec<u32> = 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 = second_verts;
second_mesh.tri_verts = second_tris;
let mut first = Manifold::from_mesh_gl(&first_mesh);
let second = Manifold::from_mesh_gl(&second_mesh);
first = first.difference(&second);
first.get_mesh_gl(0);
assert_eq!(first.status(), Error::NoError);
}
#[test]
fn test_cpp_complex_cylinders() {
let rod = Manifold::cylinder(1.0, 0.4, -1.0, 12);
let arrays1: Vec<[f64; 12]> = vec![
[0.0, 0.0, 1.0, 3.0, -1.0, 0.0, 0.0, 3.0, 0.0, -1.0, 0.0, 6.0],
[0.0, 0.0, 1.0, 2.0, -1.0, 0.0, 0.0, 3.0, 0.0, -1.0, 0.0, 8.0],
[0.0, 0.0, 1.0, 1.0, -1.0, 0.0, 0.0, 2.0, 0.0, -1.0, 0.0, 7.0],
[1.0, 0.0, 0.0, 3.0, 0.0, 1.0, 0.0, 2.0, 0.0, 0.0, 1.0, 6.0],
[0.0, 0.0, 1.0, 3.0, -1.0, 0.0, 0.0, 3.0, 0.0, -1.0, 0.0, 7.0],
[0.0, 0.0, 1.0, 1.0, -1.0, 0.0, 0.0, 3.0, 0.0, -1.0, 0.0, 7.0],
[1.0, 0.0, 0.0, 3.0, 0.0, 0.0, 1.0, 4.0, 0.0, -1.0, 0.0, 6.0],
[1.0, 0.0, 0.0, 4.0, 0.0, 0.0, 1.0, 4.0, 0.0, -1.0, 0.0, 6.0],
];
let arrays2: Vec<[f64; 12]> = vec![
[1.0, 0.0, 0.0, 3.0, 0.0, 0.0, 1.0, 2.0, 0.0, -1.0, 0.0, 6.0],
[1.0, 0.0, 0.0, 4.0, 0.0, 1.0, 0.0, 3.0, 0.0, 0.0, 1.0, 6.0],
[0.0, 0.0, 1.0, 2.0, -1.0, 0.0, 0.0, 2.0, 0.0, -1.0, 0.0, 7.0],
[1.0, 0.0, 0.0, 3.0, 0.0, 1.0, 0.0, 3.0, 0.0, 0.0, 1.0, 7.0],
[1.0, 0.0, 0.0, 2.0, 0.0, 1.0, 0.0, 3.0, 0.0, 0.0, 1.0, 7.0],
[1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 3.0, 0.0, 0.0, 1.0, 7.0],
[1.0, 0.0, 0.0, 3.0, 0.0, 1.0, 0.0, 4.0, 0.0, 0.0, 1.0, 7.0],
[1.0, 0.0, 0.0, 3.0, 0.0, 1.0, 0.0, 5.0, 0.0, 0.0, 1.0, 6.0],
[0.0, 0.0, 1.0, 3.0, -1.0, 0.0, 0.0, 4.0, 0.0, -1.0, 0.0, 6.0],
];
let make_mat = |array: &[f64; 12]| -> Mat3x4 {
Mat3x4::from_cols(
Vec3::new(array[0], array[4], array[8]),
Vec3::new(array[1], array[5], array[9]),
Vec3::new(array[2], array[6], array[10]),
Vec3::new(array[3], array[7], array[11]),
)
};
let mut m1 = Manifold::empty();
for array in &arrays1 {
let mat = make_mat(array);
m1 = m1.union(&rod.transform(&mat));
}
let mut m2 = Manifold::empty();
for array in &arrays2 {
let mat = make_mat(array);
m2 = m2.union(&rod.transform(&mat));
}
m1 = m1.union(&m2);
assert!(m1.matches_tri_normals(), "Cylinders: should match tri normals");
assert!(m1.num_degenerate_tris() <= 12,
"Cylinders: {} degenerate tris, expected <= 12", m1.num_degenerate_tris());
}
#[test]
fn test_cpp_complex_close() {
let r = 10.0;
let a = Manifold::sphere(r, 256);
let mut result = a.clone();
for i in 0..10 {
result = result.intersection(&a.translate(Vec3::new(
a.get_tolerance() / 10.0 * i as f64,
0.0,
0.0,
)));
}
let pi = std::f64::consts::PI;
let tol = 0.004;
assert!(
(result.volume() - (4.0 / 3.0) * pi * r * r * r).abs() < tol * r * r * r,
"Close volume: {} expected ~{}", result.volume(), (4.0 / 3.0) * pi * r * r * r
);
assert!(
(result.surface_area() - 4.0 * pi * r * r).abs() < tol * r * r,
"Close area: {} expected ~{}", result.surface_area(), 4.0 * pi * r * r
);
}
#[test]
fn test_cpp_complex_lazy_collider() {
let ele1 = Manifold::cylinder(50.0, 50.0, -1.0, 0);
let ele2 = Manifold::cylinder(60.0, 30.0, -1.0, 0);
let ele4 = ele1.union(&ele2).mirror(Vec3::new(0.0, 0.0, 1.0));
assert!(
(ele4.volume() - 418839.0).abs() < 2.0,
"LazyCollider ele4 volume: {} expected ~418839", ele4.volume()
);
let r1 = ele4.difference(&ele2.translate(Vec3::new(0.0, 0.0, -20.0)));
assert!(
(r1.volume() - 362577.0).abs() < 2.0,
"LazyCollider r1 volume: {} expected ~362577", r1.volume()
);
let ele3 = Manifold::cylinder(60.0, 40.0, -1.0, 0).mirror(Vec3::new(0.0, 0.0, 1.0));
let r2 = ele4.translate(Vec3::new(0.0, 0.0, 1.0)).difference(&ele3);
assert!(
(r2.volume() - 145656.0).abs() < 2.0,
"LazyCollider r2 volume: {} expected ~145656", r2.volume()
);
}
#[test]
fn test_cpp_perturb2() {
let cube = Manifold::cube(Vec3::splat(2.0), true);
let cube_gl = cube.get_mesh_gl(0);
let mut result = cube.rotate(5.0, 10.0, 15.0);
let num_tri = cube_gl.tri_verts.len() / 3;
for tri in 0..num_tri {
let mut prism_verts: Vec<f32> = Vec::new();
let mut prism_tris: Vec<u32> = vec![4, 2, 0, 1, 3, 5];
for v0 in 0..3usize {
let v1 = (v0 + 1) % 3;
let v_in0 = cube_gl.tri_verts[3 * tri + v0] as usize;
let v_in1 = cube_gl.tri_verts[3 * tri + v1] as usize;
if v_in1 > v_in0 {
prism_tris.extend_from_slice(&[
2 * v0 as u32, 2 * v1 as u32, 2 * v1 as u32 + 1,
2 * v0 as u32, 2 * v1 as u32 + 1, 2 * v0 as u32 + 1,
]);
} else {
prism_tris.extend_from_slice(&[
2 * v0 as u32, 2 * v1 as u32, 2 * v0 as u32 + 1,
2 * v1 as u32, 2 * v1 as u32 + 1, 2 * v0 as u32 + 1,
]);
}
let np = cube_gl.num_prop as usize;
for j in 0..3 {
prism_verts.push(cube_gl.vert_properties[np * v_in0 + j]);
}
for j in 0..3 {
prism_verts.push(2.0 * cube_gl.vert_properties[np * v_in0 + j]);
}
}
let mut mesh = MeshGL::default();
mesh.num_prop = 3;
mesh.vert_properties = prism_verts;
mesh.tri_verts = prism_tris;
result = result + Manifold::from_mesh_gl(&mesh).rotate(5.0, 10.0, 15.0);
}
assert_eq!(result.num_degenerate_tris(), 0);
assert_eq!(result.num_vert(), 8, "Perturb2: {} verts expected 8", result.num_vert());
assert!((result.volume() - 64.0).abs() < 1e-4, "Perturb2 volume: {} expected 64", result.volume());
assert!((result.surface_area() - 96.0).abs() < 1e-4, "Perturb2 area: {} expected 96", result.surface_area());
}
#[test]
#[ignore = "Gear pattern requires BatchBoolean precision improvements"]
fn test_cpp_perturb3() {
let n = 16;
let alpha = 90.0 / n as f64;
let cube = Manifold::cube(Vec3::splat(1.0), true);
let mut outer_cubes = Vec::new();
for i in 0..n {
outer_cubes.push(cube.rotate(0.0, 0.0, alpha * i as f64));
}
let gear = Manifold::batch_boolean(&outer_cubes, OpType::Add);
let outer_gear = gear.scale(Vec3::new(2.0, 2.0, 1.0));
let nasty_gear = outer_gear.difference(&gear);
let expected_volume = outer_gear.volume() - gear.volume();
assert_eq!(nasty_gear.status(), Error::NoError);
assert!(!nasty_gear.is_empty());
assert_eq!(nasty_gear.genus(), 1, "Perturb3 genus: {} expected 1", nasty_gear.genus());
assert!((nasty_gear.volume() - expected_volume).abs() < 1e-5,
"Perturb3 volume: {} expected {}", nasty_gear.volume(), expected_volume);
assert!((nasty_gear.surface_area() - 26.972).abs() < 1e-3,
"Perturb3 area: {} expected 26.972", nasty_gear.surface_area());
}
#[test]
fn test_cpp_meshgl_round_trip() {
let cube = Manifold::cube(Vec3::splat(2.0), false);
assert!(cube.original_id() >= 0, "Cube should have positive originalID");
let _original = cube.get_mesh_gl(0);
let result = cube.clone() + cube.translate(Vec3::new(1.0, 1.0, 0.0));
assert!(result.original_id() < 0, "Boolean result should have negative originalID");
assert!(result.num_vert() > 0);
assert!(result.num_tri() > 0);
let in_gl = result.get_mesh_gl(0);
assert_eq!(in_gl.run_original_id.len(), 2,
"MeshGLRoundTrip: expected 2 runs, got {}", in_gl.run_original_id.len());
let result2 = Manifold::from_mesh_gl(&in_gl);
assert!(result2.original_id() < 0);
assert!(result2.num_vert() > 0);
assert!(result2.num_tri() > 0);
let out_gl = result2.get_mesh_gl(0);
assert_eq!(out_gl.run_original_id.len(), 2,
"MeshGLRoundTrip: roundtrip should preserve 2 runs, got {}", out_gl.run_original_id.len());
}
#[test]
fn test_cpp_complex_craycloud() {
let m1 = read_test_obj("Cray_left.obj");
let m2 = read_test_obj("Cray_right.obj");
let res = m1 - m2;
assert_eq!(res.status(), Error::NoError);
assert!(!res.is_empty(), "CraycloudBool: difference should not be empty");
let simplified = res.as_original().simplify(0.0);
assert!(simplified.is_empty(),
"CraycloudBool: AsOriginal().Simplify() should produce empty mesh, got {} tris",
simplified.num_tri());
}
#[test]
fn test_cpp_complex_boolean_volumes() {
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);
let eps = 1e-4;
assert!(((m1.clone() ^ m2.clone()).volume() - 0.0).abs() < eps, "m1^m2");
assert!(((m1.clone() + m2.clone() + m4.clone()).volume() - 7.0).abs() < eps, "m1+m2+m4");
assert!(((m1.clone() + m2.clone() - m4.clone()).volume() - 3.0).abs() < eps, "m1+m2-m4");
assert!(((m1.clone() + (m2.clone() ^ m4.clone())).volume() - 1.0).abs() < eps, "m1+(m2^m4)");
assert!(((m7.clone() ^ m4.clone()).volume() - 4.0).abs() < eps, "m7^m4");
assert!(((m7.clone() ^ m3.clone() ^ m1.clone()).volume() - 1.0).abs() < eps, "m7^m3^m1");
assert!(((m7.clone() ^ (m1.clone() + m2.clone())).volume() - 3.0).abs() < eps, "m7^(m1+m2)");
assert!(((m7.clone() - m4.clone()).volume() - 3.0).abs() < eps, "m7-m4");
assert!(((m7.clone() - m4.clone() - m2.clone()).volume() - 1.0).abs() < eps, "m7-m4-m2");
assert!(((m7.clone() - (m7.clone() - m1.clone())).volume() - 1.0).abs() < eps, "m7-(m7-m1)");
assert!(((m7.clone() - (m1.clone() + m2.clone())).volume() - 4.0).abs() < eps, "m7-(m1+m2)");
}
#[test]
fn test_cpp_complex_spiral() {
let d = 2.0f64;
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) + cube
} else {
cube
}
}
let result = spiral(120, 25.0, 2.0, d);
assert_eq!(result.genus(), -120, "Spiral genus should be -120, got {}", result.genus());
}
#[test]
fn test_cpp_openscad_crash() {
let m = read_test_obj("openscad-nonmanifold-crash.obj");
assert!(!m.is_empty(), "OBJ should load as non-empty manifold, status={:?}", m.status());
let m2 = m.clone() + m.translate(Vec3::new(0.0, 0.6, 0.0));
assert!(!m2.is_empty(), "Boolean union should not be empty, status={:?}", m2.status());
}
#[test]
fn test_cpp_meshgl_round_trip2() {
let cylinder = Manifold::cylinder(2.0, 1.0, -1.0, 0);
assert!(cylinder.original_id() >= 0);
let in_gl = cylinder.get_mesh_gl(0);
let cylinder2 = Manifold::from_mesh_gl(&in_gl);
let out_gl = cylinder2.get_mesh_gl(0);
assert_eq!(in_gl.run_original_id.len(), 1, "Input should have 1 run");
assert_eq!(out_gl.run_original_id.len(), 1, "Output should have 1 run");
assert_eq!(out_gl.run_original_id[0], in_gl.run_original_id[0],
"Original ID should be preserved through round-trip");
}
#[test]
fn test_cpp_complex_sphere_boolean() {
let sphere = Manifold::sphere(1.0, 12)
.set_properties(3, |new_prop, pos, _old| {
new_prop[0] = pos.x;
new_prop[1] = pos.y;
new_prop[2] = pos.z;
});
let sphere2 = sphere.translate(Vec3::splat(0.5));
let result = sphere.clone() - sphere2;
assert!(!result.is_empty(), "Sphere difference should not be empty");
assert_eq!(result.status(), Error::NoError);
assert!(result.num_tri() > 0, "Should have triangles");
assert_eq!(result.num_prop(), 3, "Should have 3 extra properties");
let refined = result.refine(4);
assert!(!refined.is_empty(), "Refined should not be empty");
assert_eq!(refined.num_prop(), 3, "Refined should have 3 props");
}
#[test]
fn test_cpp_complex_mesh_relation() {
let gyroid_src = super::with_position_colors(&super::gyroid());
let gyroid_gl = gyroid_src.get_mesh_gl(0);
let gyroid = gyroid_src.simplify(0.0);
let gyroid2 = gyroid.translate(Vec3::splat(2.0));
assert!(!gyroid.is_empty(), "MeshRelation: gyroid not empty");
assert!(gyroid.matches_tri_normals(), "MeshRelation: matches_tri_normals");
assert!(gyroid.num_degenerate_tris() <= 0, "MeshRelation: num_degenerate_tris <= 0");
let result = gyroid.union(&gyroid2).refine_to_length(0.1);
assert!(result.matches_tri_normals(), "MeshRelation: result matches_tri_normals");
assert!(result.num_degenerate_tris() <= 12, "MeshRelation: num_degenerate_tris <= 12");
assert_eq!(result.decompose().len(), 1, "MeshRelation: 1 component");
assert!((result.volume() - 226.0).abs() < 1.0, "MeshRelation: vol={}", result.volume());
assert!((result.surface_area() - 387.0).abs() < 1.0,
"MeshRelation: sa={}", result.surface_area());
super::related_gl(&result, &[&gyroid_gl]);
}
#[test]
fn test_cpp_complex_sweep() {
use std::f64::consts::PI;
let k_two_pi = 2.0 * PI;
let fillet_radius: f64 = 2.5;
let fillet_width: f64 = 5.0;
let num_arc_points: i32 = 10;
let arc_cp = Vec2::new(fillet_width - fillet_radius, fillet_radius);
let mut profile: Vec<Vec2> = vec![
Vec2::new(0.0, 0.0),
Vec2::new(fillet_width - fillet_radius, 0.0),
];
for i in 0..num_arc_points {
let angle = i as f64 * PI / num_arc_points as f64;
let y = arc_cp.y - angle.cos() * fillet_radius;
let x = arc_cp.x + angle.sin() * fillet_radius;
profile.push(Vec2::new(x, y));
}
profile.push(Vec2::new(0.0, fillet_width));
let profile_polys: Polygons = vec![profile];
let min_pos_angle = |angle: f64| -> f64 {
let div = angle / k_two_pi;
let whole = div.floor();
angle - whole * k_two_pi
};
let partial_revolve = |start_angle: f64, end_angle: f64, n_segments_per_rotation: i32| -> Manifold {
let pos_end = min_pos_angle(end_angle);
let total = if start_angle < 0.0 && end_angle < 0.0 && start_angle < end_angle {
end_angle - start_angle
} else {
pos_end - start_angle
};
let mut n_segments = (total / k_two_pi * n_segments_per_rotation as f64 + 1.0).ceil() as i32;
if n_segments < 2 { n_segments = 2; }
let angle_step = total / (n_segments - 1) as f64;
let n_segments_f = (n_segments - 1) as f64;
Manifold::extrude(&profile_polys, n_segments_f, n_segments - 2, 0.0, Vec2::new(1.0, 1.0))
.warp(move |v: &mut Vec3| {
let z_index = n_segments_f - v.z;
let angle = z_index * angle_step + start_angle;
let old_x = v.x;
let old_y = v.y;
v.z = old_y;
v.y = old_x * angle.sin();
v.x = old_x * angle.cos();
})
};
let det = |a: Vec2, b: Vec2| -> f64 { a.x * b.y - a.y * b.x };
let cutter_primitives = |p1: Vec2, p2: Vec2, p3: Vec2| -> Vec<Manifold> {
let diff = p2 - p1;
let v1 = p1 - p2;
let v2 = p3 - p2;
let determinant = det(v1, v2);
let start_angle = v1.x.atan2(-v1.y);
let end_angle = (-v2.x).atan2(v2.y);
let round = partial_revolve(start_angle, end_angle, 20)
.translate(Vec3::new(p2.x, p2.y, 0.0));
let distance = (diff.x * diff.x + diff.y * diff.y).sqrt();
let angle = diff.y.atan2(diff.x);
let extrusion = Manifold::extrude(&profile_polys, distance, 0, 0.0, Vec2::new(1.0, 1.0))
.rotate(90.0, 0.0, -90.0)
.translate(Vec3::new(distance, 0.0, 0.0))
.rotate(0.0, 0.0, angle * 180.0 / PI)
.translate(Vec3::new(p1.x, p1.y, 0.0));
if determinant < 0.0 { vec![round, extrusion] } else { vec![extrusion] }
};
let path_points_raw: [(f64, f64); 90] = [
(-21.707751473606564, 10.04202769267855),
(-21.840846948218307, 9.535474475521578),
(-21.940954413815387, 9.048287386171369),
(-22.005569458385835, 8.587741145234093),
(-22.032187669917704, 8.16111047331591),
(-22.022356960178296, 7.755456475810721),
(-21.9823319178086, 7.356408291345673),
(-21.91208498286602, 6.964505631629036),
(-21.811437268778267, 6.579251589515578),
(-21.68020988897306, 6.200149257860059),
(-21.51822395687812, 5.82670172951726),
(-21.254086890521585, 5.336709200579579),
(-21.01963533308061, 4.974523796623895),
(-20.658228140926262, 4.497743844638198),
(-20.350337020134603, 4.144115181723373),
(-19.9542029967, 3.7276501717684054),
(-20.6969129296381, 3.110639833377638),
(-21.026318197401537, 2.793796378245609),
(-21.454710558515973, 2.3418076758544806),
(-21.735944543382722, 2.014266362004704),
(-21.958999535447845, 1.7205197644485681),
(-22.170169612837164, 1.3912359628761894),
(-22.376940405634056, 1.0213515348242117),
(-22.62545385249271, 0.507889651991388),
(-22.77620002102207, 0.13973666928102288),
(-22.8689989640578, -0.135962138067232),
(-22.974385239894364, -0.5322784681448909),
(-23.05966775687304, -0.9551466941218276),
(-23.102914137841445, -1.2774406685179822),
(-23.14134824916783, -1.8152432718003662),
(-23.152085124298473, -2.241104719188421),
(-23.121576743285054, -2.976332948223073),
(-23.020491352156856, -3.6736813934577914),
(-22.843552165110886, -4.364810769710428),
(-22.60334013490563, -5.033012850282157),
(-22.305015243491663, -5.67461444847819),
(-21.942709324216615, -6.330962778427178),
(-21.648491707764062, -6.799117771996025),
(-21.15330508818782, -7.496539096945377),
(-21.10687739725184, -7.656798276710632),
(-21.01253055778545, -8.364144493707382),
(-20.923211927856293, -8.782280691344269),
(-20.771325204062215, -9.258087073404687),
(-20.554404009259198, -9.72613360625344),
(-20.384050989017144, -9.985885743112847),
(-20.134404839253612, -10.263023004626703),
(-19.756998832033442, -10.613109670467736),
(-18.83161393127597, -15.68768837402245),
(-19.155593463785983, -17.65410871259763),
(-17.930304365744544, -19.005810988385562),
(-16.893408103100064, -19.50558228186199),
(-16.27514960757635, -19.8288501942628),
(-15.183033464853374, -20.47781203017123),
(-14.906850387751492, -20.693472553142833),
(-14.585198957236713, -21.015257964547136),
(-11.013839210807205, -34.70394287828328),
(-8.79778020674896, -36.17434400175442),
(-7.850491148257242, -36.48835987119041),
(-6.982497182376991, -36.74546968896842),
(-6.6361688522576, -36.81653354539242),
(-6.0701080598244035, -36.964332993204),
(-5.472439187922815, -37.08824838436714),
(-4.802871164820756, -37.20127157090685),
(-3.6605994233344745, -37.34427653957914),
(-1.7314396363710867, -37.46415201430501),
(-0.7021130485987349, -37.5),
(0.01918509410483974, -37.49359541901704),
(1.2107837650065625, -37.45093992812552),
(3.375529069920302, 32.21823383780513),
(1.9041980552754056, 32.89839543047101),
(1.4107184651094313, 33.16556804736585),
(1.1315552947605065, 33.34344755450097),
(0.8882931135353977, 33.52377699790175),
(0.6775397019893341, 33.708817857198056),
(0.49590284067753837, 33.900831612019715),
(0.2291596803839543, 34.27380625039597),
(0.03901816126171688, 34.66402375075138),
(-0.02952797094655369, 34.8933309389416),
(-0.0561772851849209, 35.044928843125824),
(-0.067490756643705, 35.27129875796868),
(-0.05587453990569748, 35.42204271802184),
(0.013497378362074697, 35.72471438137191),
(0.07132375113026912, 35.877348797053145),
(0.18708820875448923, 36.108917464873215),
(0.39580614140195136, 36.424415957998825),
(0.8433687814267005, 36.964365016108914),
(0.7078417131710703, 37.172455373435916),
(0.5992848016685662, 37.27482757003058),
(0.40594743344375905, 37.36664006036318),
(0.1397973410299913, 37.434752779117005),
];
let path_points: Vec<Vec2> = path_points_raw.iter()
.map(|&(x, y)| Vec2::new(x, y) * 0.9)
.collect();
let n = path_points.len();
let mut primitives: Vec<Manifold> = Vec::new();
for i in 0..n {
let prims = cutter_primitives(
path_points[i],
path_points[(i + 1) % n],
path_points[(i + 2) % n],
);
primitives.extend(prims);
}
let shape = Manifold::batch_boolean(&primitives, crate::types::OpType::Add);
assert!((shape.volume() - 3757.0).abs() < 1.0,
"Sweep: vol={}, expected ~3757", shape.volume());
}
#[test]
fn test_cpp_interpolated_normals() {
let src = read_cpp_test_source("boolean_complex_test.cpp");
let mut a = MeshGL::default();
a.num_prop = 8;
a.vert_properties = cpp_inline_array(&src, "a.vertProperties = ", 0)
.into_iter().map(|v| v as f32).collect();
a.tri_verts = cpp_inline_array_u32(&src, "a.triVerts = ", 0);
a.merge_from_vert = cpp_inline_array_u32(&src, "a.mergeFromVert = ", 0);
a.merge_to_vert = cpp_inline_array_u32(&src, "a.mergeToVert = ", 0);
let mut b = MeshGL::default();
b.num_prop = 8;
b.vert_properties = cpp_inline_array(&src, "b.vertProperties = ", 0)
.into_iter().map(|v| v as f32).collect();
b.tri_verts = cpp_inline_array_u32(&src, "b.triVerts = ", 0);
b.merge_from_vert = cpp_inline_array_u32(&src, "b.mergeFromVert = ", 0);
b.merge_to_vert = cpp_inline_array_u32(&src, "b.mergeToVert = ", 0);
a.run_original_id = vec![Manifold::reserve_ids(1)];
b.run_original_id = vec![Manifold::reserve_ids(1)];
let a_manifold = Manifold::from_mesh_gl(&a);
let b_manifold = Manifold::from_mesh_gl(&b);
let a_minus_b = a_manifold - b_manifold;
super::related_gl_check_normals(&a_minus_b, &[&a, &b]);
}
fn ring_mesh(src: &str, occurrence: usize) -> MeshGL {
let mut m = MeshGL::default();
m.num_prop = 3;
m.vert_properties = cpp_inline_array(src, "m.vertProperties = ", occurrence)
.into_iter().map(|v| v as f32).collect();
m.tri_verts = cpp_inline_array_u32(src, "m.triVerts = ", occurrence);
m.run_index = cpp_inline_array_u32(src, "m.runIndex = ", occurrence);
m.run_original_id = cpp_inline_array_u32(src, "m.runOriginalID = ", occurrence);
m.face_id = cpp_inline_array_u32(src, "m.faceID = ", occurrence);
m
}
#[test]
fn test_cpp_ring() {
let src = read_cpp_test_source("boolean_complex_test.cpp");
let arg0 = Manifold::from_mesh_gl(&ring_mesh(&src, 0));
let arg1 = Manifold::from_mesh_gl(&ring_mesh(&src, 1));
let result = arg0.clone() - arg1.clone();
assert_eq!(arg0.status(), crate::types::Error::NoError, "arg0 status");
assert_eq!(arg1.status(), crate::types::Error::NoError, "arg1 status");
assert_eq!(result.status(), crate::types::Error::NoError, "result status");
}