use super::*;
#[test]
fn test_manifold_cube_counts() {
let m = Manifold::cube(Vec3::new(1.0, 1.0, 1.0), false);
assert_eq!(m.num_vert(), 8);
assert_eq!(m.num_tri(), 12);
}
#[test]
fn test_manifold_transform_translate() {
let m = Manifold::cube(Vec3::new(1.0, 1.0, 1.0), false).translate(Vec3::new(2.0, 0.0, 0.0));
let out = m.get_mesh_gl(0);
let p = out.get_vert_pos(0);
assert!(p[0] >= 2.0);
}
#[test]
fn test_mesh_gl_roundtrip_basic() {
let m = Manifold::cube(Vec3::new(1.0, 1.0, 1.0), false);
let mesh = m.get_mesh_gl(0);
let rebuilt = Manifold::from_mesh_gl(&mesh);
assert_eq!(rebuilt.num_tri(), m.num_tri());
}
#[test]
fn test_calculate_curvature_keeps_mesh() {
let m = Manifold::cube(Vec3::new(1.0, 1.0, 1.0), false).calculate_curvature(0, 1);
let mesh = m.get_mesh_gl(0);
assert!(mesh.num_prop >= 5);
}
#[test]
fn test_refine_increases_triangles() {
let m = Manifold::cube(Vec3::new(1.0, 1.0, 1.0), false);
let r = m.refine(2);
assert_eq!(r.num_tri(), m.num_tri() * 4);
}
#[test]
fn test_hull_tetrahedron() {
let hull = Manifold::hull(&[
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
]);
assert_eq!(hull.num_vert(), 4);
assert_eq!(hull.num_tri(), 4);
}
#[test]
fn test_hull_cube() {
let hull = Manifold::hull(&[
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(1.0, 1.0, 0.0),
Vec3::new(1.0, 0.0, 1.0),
Vec3::new(0.0, 1.0, 1.0),
Vec3::new(1.0, 1.0, 1.0),
]);
assert_eq!(hull.num_vert(), 8);
assert_eq!(hull.num_tri(), 12);
}
#[test]
fn test_cpp_sphere_tri_count() {
let sphere = Manifold::sphere(1.0, 16);
assert_eq!(sphere.num_tri(), 128);
let sphere2 = Manifold::sphere(1.0, 32);
assert_eq!(sphere2.num_tri(), 512);
}
#[test]
fn test_cpp_cylinder_tri_count() {
let n = 10000i32;
let cyl = Manifold::cylinder(2.0, 2.0, 2.0, n);
assert_eq!(cyl.num_tri(), (4 * n - 4) as usize);
}
#[test]
fn test_cpp_revolve3() {
let circle = crate::cross_section::CrossSection::circle(1.0, 32);
let sphere = Manifold::revolve(&circle.to_polygons(), 32, 360.0);
let k_pi = std::f64::consts::PI;
assert!((sphere.volume() - 4.0 / 3.0 * k_pi).abs() < 0.1);
assert!((sphere.surface_area() - 4.0 * k_pi).abs() < 0.15);
}
#[test]
fn test_cpp_transform() {
let cube = Manifold::cube(Vec3::splat(1.0), false);
let translated = cube.translate(Vec3::new(1.0, 2.0, 3.0));
assert_eq!(translated.num_vert(), 8);
assert_eq!(translated.num_tri(), 12);
assert!((translated.volume() - 1.0).abs() < 1e-10);
}
#[test]
fn test_cpp_mirror_union() {
let cube = Manifold::cube(Vec3::new(5.0, 5.0, 5.0), false)
.translate(Vec3::new(0.0, 0.0, -3.0));
let mirrored = cube.scale(Vec3::new(1.0, 1.0, -1.0));
let result = cube.union(&mirrored);
assert_eq!(result.genus(), 0);
assert!((result.volume() - 5.0 * 5.0 * 6.0).abs() < 1e-5);
}
#[test]
fn test_cpp_empty() {
let empty = Manifold::empty();
assert!(empty.is_empty());
assert_eq!(empty.status(), Error::NoError);
}
#[test]
fn test_cpp_cylinder_zero_radius_low() {
let n = 256;
let h = 5.0;
let r = 3.0;
let cone_apex_bottom = Manifold::cylinder(h, 0.0, r, n);
let cone_apex_top = Manifold::cylinder(h, r, 0.0, n);
assert_eq!(cone_apex_bottom.status(), Error::NoError);
assert!(!cone_apex_bottom.is_empty());
let total_vol = cone_apex_top.volume();
assert!((cone_apex_bottom.volume() - total_vol).abs() < 1e-6,
"Cone volumes should match: {} vs {}", cone_apex_bottom.volume(), total_vol);
let slicer = Manifold::cube(
Vec3::new(2.0 * r + 1.0, 2.0 * r + 1.0, h / 2.0),
false,
).translate(Vec3::new(-(r + 0.5), -(r + 0.5), 0.0));
assert!((cone_apex_bottom.intersection(&slicer).volume() - total_vol / 8.0).abs() < 0.01,
"Apex-bottom cone bottom-half volume should be V/8");
assert!((cone_apex_top.intersection(&slicer).volume() - 7.0 * total_vol / 8.0).abs() < 0.01,
"Apex-top cone bottom-half volume should be 7V/8");
}
#[test]
fn test_cpp_extrude() {
let polys = square_hole(0.0);
let donut = Manifold::extrude(&polys, 1.0, 3, 0.0, Vec2::new(1.0, 1.0));
assert_eq!(donut.genus(), 1);
assert!((donut.volume() - 12.0).abs() < 1e-5, "volume: {}", donut.volume());
assert!((donut.surface_area() - 48.0).abs() < 1e-5, "SA: {}", donut.surface_area());
}
#[test]
fn test_cpp_extrude_cone() {
let polys = square_hole(0.0);
let donut = Manifold::extrude(&polys, 1.0, 0, 0.0, Vec2::new(0.0, 0.0));
assert_eq!(donut.genus(), 0);
assert!((donut.volume() - 4.0).abs() < 1e-5, "volume: {}", donut.volume());
}
#[test]
fn test_cpp_revolve() {
let polys = square_hole(0.0);
let k_pi = std::f64::consts::PI;
let vug = Manifold::revolve(&polys, 48, 360.0);
assert_eq!(vug.genus(), -1);
assert!((vug.volume() - 14.0 * k_pi).abs() < 0.2,
"volume: {} expected: {}", vug.volume(), 14.0 * k_pi);
assert!((vug.surface_area() - 30.0 * k_pi).abs() < 0.2,
"SA: {} expected: {}", vug.surface_area(), 30.0 * k_pi);
}
#[test]
fn test_cpp_revolve2() {
let polys = square_hole(2.0);
let k_pi = std::f64::consts::PI;
let donut_hole = Manifold::revolve(&polys, 48, 360.0);
assert_eq!(donut_hole.genus(), 0);
assert!((donut_hole.volume() - 48.0 * k_pi).abs() < 1.0,
"volume: {} expected: {}", donut_hole.volume(), 48.0 * k_pi);
assert!((donut_hole.surface_area() - 96.0 * k_pi).abs() < 1.0,
"SA: {} expected: {}", donut_hole.surface_area(), 96.0 * k_pi);
}
#[test]
fn test_cpp_revolve_clip() {
let polys: Polygons = vec![vec![
Vec2::new(-5.0, -10.0),
Vec2::new(5.0, 0.0),
Vec2::new(-5.0, 10.0),
]];
let clipped: Polygons = vec![vec![
Vec2::new(0.0, -5.0),
Vec2::new(5.0, 0.0),
Vec2::new(0.0, 5.0),
]];
let first = Manifold::revolve(&polys, 48, 360.0);
let second = Manifold::revolve(&clipped, 48, 360.0);
assert_eq!(first.genus(), second.genus());
assert!((first.volume() - second.volume()).abs() < 1e-10,
"volumes: {} vs {}", first.volume(), second.volume());
assert!((first.surface_area() - second.surface_area()).abs() < 1e-10,
"SAs: {} vs {}", first.surface_area(), second.surface_area());
}
#[test]
fn test_cpp_partial_revolve_on_y_axis() {
let polys = square_hole(2.0);
let k_pi = std::f64::consts::PI;
let revolute = Manifold::revolve(&polys, 48, 180.0);
assert_eq!(revolute.genus(), 1);
assert!((revolute.volume() - 24.0 * k_pi).abs() < 1.0,
"volume: {} expected: {}", revolute.volume(), 24.0 * k_pi);
let expected_sa = 48.0 * k_pi + 4.0 * 4.0 * 2.0 - 2.0 * 2.0 * 2.0;
assert!((revolute.surface_area() - expected_sa).abs() < 1.0,
"SA: {} expected: {}", revolute.surface_area(), expected_sa);
}
#[test]
fn test_cpp_partial_revolve_offset() {
let polys = square_hole(10.0);
let revolute = Manifold::revolve(&polys, 48, 180.0);
assert_eq!(revolute.genus(), 1);
assert!((revolute.surface_area() - 777.0).abs() < 1.0,
"SA: {} expected: 777", revolute.surface_area());
assert!((revolute.volume() - 376.0).abs() < 1.0,
"volume: {} expected: 376", revolute.volume());
}
#[test]
fn test_cpp_pinched_vert() {
let mut mesh = MeshGL::default();
mesh.num_prop = 3;
mesh.vert_properties = vec![
0.0, 0.0, 0.0,
1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-0.00001, 0.0, 0.0,
-1.0, -1.0, 0.0,
-1.0, 1.0, 0.0,
0.0, 0.0, 2.0,
0.0, 0.0, -2.0,
];
mesh.tri_verts = vec![
0, 2, 6,
2, 1, 6,
1, 0, 6,
4, 3, 6,
3, 5, 6,
5, 4, 6,
2, 0, 4,
0, 3, 4,
3, 0, 1,
3, 1, 5,
7, 2, 4,
7, 4, 5,
7, 5, 1,
7, 1, 2,
];
let touch = Manifold::from_mesh_gl(&mesh);
assert!(!touch.is_empty(), "PinchedVert mesh should not be empty");
assert_eq!(touch.status(), Error::NoError);
assert!(touch.genus() <= 1, "genus: {}", touch.genus());
}
#[test]
fn test_cpp_mirror_union2() {
let a = Manifold::cube(Vec3::splat(1.0), false);
let mirrored = a.scale(Vec3::new(-1.0, 1.0, 1.0));
assert!(mirrored.matches_tri_normals(), "Mirrored cube should match tri normals");
}
#[test]
fn test_cpp_opposite_face() {
let mut gl = MeshGL::default();
gl.num_prop = 3;
gl.vert_properties = vec![
0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 0.0, 0.0, 2.0, 1.0, 0.0, 2.0, 0.0, 1.0, 2.0, 1.0, 1.0, ];
gl.tri_verts = vec![
0, 1, 4,
0, 2, 3,
0, 3, 1,
0, 4, 2,
1, 3, 5,
1, 3, 9,
1, 5, 3,
1, 5, 4,
1, 8, 5,
1, 9, 8,
2, 4, 6,
2, 6, 7,
2, 7, 3,
3, 5, 7,
3, 7, 5,
3, 7, 11,
3, 11, 9,
4, 5, 6,
5, 7, 6,
5, 8, 10,
5, 10, 7,
7, 10, 11,
8, 9, 10,
9, 11, 10,
];
let man = Manifold::from_mesh_gl(&gl);
assert_eq!(man.status(), Error::NoError);
assert_eq!(man.num_vert(), 12);
assert!((man.volume() - 2.0).abs() < 1e-5, "volume: {}", man.volume());
}
#[test]
fn test_sphere_is_round() {
let m = Manifold::sphere(1.0, 24);
let vol = m.volume();
let expected = 4.0 * std::f64::consts::PI / 3.0;
assert!(
(vol - expected).abs() < 0.2,
"Sphere volume should be ~{:.3}, got {:.3}",
expected,
vol
);
let mesh = m.get_mesh_gl(0);
let num_prop = mesh.num_prop as usize;
let vert_count = if num_prop > 0 { mesh.vert_properties.len() / num_prop } else { 0 };
for i in 0..vert_count {
let x = mesh.vert_properties[i * num_prop] as f64;
let y = mesh.vert_properties[i * num_prop + 1] as f64;
let z = mesh.vert_properties[i * num_prop + 2] as f64;
let r = (x * x + y * y + z * z).sqrt();
assert!(
(r - 1.0).abs() < 0.01,
"Vertex {} at ({:.3},{:.3},{:.3}) has radius {:.4}, expected ~1.0",
i, x, y, z, r
);
}
}
#[test]
fn test_set_properties_roundtrip() {
let cube = Manifold::cube(Vec3::new(1.0, 1.0, 1.0), true);
let colored = cube.set_properties(3, |props, _pos, _old| {
props[0] = 1.0; props[1] = 0.0; props[2] = 0.0; });
let gl = colored.get_mesh_gl(0);
let num_prop = gl.num_prop as usize;
assert_eq!(num_prop, 6); let vert_count = gl.vert_properties.len() / num_prop;
assert!(vert_count > 0);
for i in 0..vert_count {
let r = gl.vert_properties[i * num_prop + 3];
let g = gl.vert_properties[i * num_prop + 4];
let b = gl.vert_properties[i * num_prop + 5];
assert!((r - 1.0).abs() < 1e-6, "Vertex {i} R={r}, expected 1.0");
assert!(g.abs() < 1e-6, "Vertex {i} G={g}, expected 0.0");
assert!(b.abs() < 1e-6, "Vertex {i} B={b}, expected 0.0");
}
}
#[test]
fn test_cpp_properties_measurements() {
let cube = Manifold::cube(Vec3::splat(1.0), false);
assert!((cube.volume() - 1.0).abs() < 1e-6, "cube volume: {}", cube.volume());
assert!((cube.surface_area() - 6.0).abs() < 1e-6, "cube area: {}", cube.surface_area());
let flipped = cube.scale(Vec3::splat(-1.0));
assert!((flipped.volume() - 1.0).abs() < 1e-6, "flipped cube volume: {}", flipped.volume());
assert!((flipped.surface_area() - 6.0).abs() < 1e-6, "flipped cube area: {}", flipped.surface_area());
}
#[test]
fn test_cpp_properties_epsilon() {
let k_precision: f64 = crate::types::K_PRECISION;
let cube = Manifold::cube(Vec3::splat(1.0), false);
assert!((cube.get_tolerance() - k_precision).abs() < k_precision * 0.1,
"unit cube epsilon: {} expected ~{}", cube.get_tolerance(), k_precision);
let scaled = cube.scale(Vec3::new(0.1, 1.0, 10.0));
assert!((scaled.get_tolerance() - 10.0 * k_precision).abs() < k_precision,
"scaled cube epsilon: {} expected ~{}", scaled.get_tolerance(), 10.0 * k_precision);
let translated = scaled.translate(Vec3::new(-100.0, -10.0, -1.0));
assert!((translated.get_tolerance() - 100.0 * k_precision).abs() < k_precision * 10.0,
"translated cube epsilon: {} expected ~{}", translated.get_tolerance(), 100.0 * k_precision);
}
#[test]
fn test_cpp_properties_epsilon2() {
let k_precision: f64 = crate::types::K_PRECISION;
let cube = Manifold::cube(Vec3::splat(1.0), false)
.translate(Vec3::new(-0.5, 0.0, 0.0))
.scale(Vec3::new(2.0, 1.0, 1.0));
assert!((cube.get_tolerance() - 2.0 * k_precision).abs() < k_precision,
"epsilon2: {} expected ~{}", cube.get_tolerance(), 2.0 * k_precision);
}
#[test]
fn test_cpp_properties_coplanar() {
let cube = Manifold::cube(Vec3::splat(1.0), false);
assert!(cube.matches_tri_normals(), "Cube should match tri normals");
assert_eq!(cube.num_degenerate_tris(), 0, "Cube should have no degenerate tris");
let tet = Manifold::tetrahedron();
assert!(tet.matches_tri_normals(), "Tetrahedron should match tri normals");
}
#[test]
fn test_cpp_mirror_union_full() {
let a = Manifold::cube(Vec3::splat(5.0), true);
let b = a.translate(Vec3::new(2.5, 2.5, 2.5));
let b_mirrored = b.mirror(Vec3::new(1.0, 1.0, 0.0));
let result = a.union(&b).union(&b_mirrored);
let vol_a = a.volume();
assert!((result.volume() - vol_a * 2.75).abs() < 1e-5,
"volume: {} expected: {}", result.volume(), vol_a * 2.75);
assert!(a.mirror(Vec3::new(0.0, 0.0, 0.0)).is_empty());
}
#[test]
fn test_cpp_invalid_constructors() {
use crate::types::Error;
assert_eq!(Manifold::sphere(0.0, 16).status(), Error::InvalidConstruction);
assert_eq!(Manifold::cylinder(0.0, 5.0, -1.0, 16).status(), Error::InvalidConstruction);
assert_eq!(Manifold::cylinder(2.0, -5.0, -1.0, 16).status(), Error::InvalidConstruction);
assert_eq!(Manifold::cylinder(2.0, 0.0, -1.0, 16).status(), Error::InvalidConstruction);
assert_eq!(Manifold::cylinder(2.0, 0.0, 0.0, 16).status(), Error::InvalidConstruction);
assert_eq!(Manifold::cube(Vec3::new(0.0, 0.0, 0.0), false).status(), Error::InvalidConstruction);
assert_eq!(Manifold::cube(Vec3::new(-1.0, 1.0, 1.0), false).status(), Error::InvalidConstruction);
let empty_poly: Vec<Vec<Vec2>> = vec![];
assert_eq!(Manifold::extrude(&empty_poly, 0.0, 0, 0.0, Vec2::new(1.0, 1.0)).status(), Error::InvalidConstruction);
}
#[test]
fn test_cpp_mesh_determinism() {
let cube1 = Manifold::cube(Vec3::new(2.0, 2.0, 2.0), true);
let cube2 = Manifold::cube(Vec3::new(2.0, 2.0, 2.0), true)
.translate(Vec3::new(-1.1091, 0.88509, 1.3099));
let result = cube1 - cube2;
let out = result.get_mesh_gl(3);
let expected_tri_verts: Vec<u32> = vec![
0, 2, 7, 0, 10, 1, 0, 6, 10, 0, 1, 2, 1, 3, 2,
1, 5, 3, 1, 11, 5, 0, 7, 6, 6, 7, 8, 6, 8, 13,
10, 12, 11, 1, 10, 11, 11, 13, 5, 6, 12, 10, 6, 13, 12,
13, 9, 5, 13, 8, 9, 11, 12, 13, 4, 2, 3, 4, 3, 5,
4, 7, 2, 4, 5, 8, 4, 8, 7, 9, 8, 5,
];
let expected_vert_props: Vec<f32> = vec![
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, -0.11491, 0.3099,
-1.0, -0.11491, 1.0,
-0.1091, -0.11491, 0.3099,
-0.1091, -0.11491, 1.0,
-1.0, 1.0, -1.0,
-1.0, 1.0, 0.3099,
-0.1091, 1.0, 0.3099,
-0.1091, 1.0, 1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
];
assert_eq!(out.tri_verts, expected_tri_verts,
"MeshDeterminism: triVerts mismatch");
assert_eq!(out.vert_properties.len(), expected_vert_props.len(),
"MeshDeterminism: vertProperties length mismatch: {} vs {}",
out.vert_properties.len(), expected_vert_props.len());
for (i, (&actual, &expected)) in out.vert_properties.iter().zip(expected_vert_props.iter()).enumerate() {
assert!((actual - expected).abs() < 1e-4,
"MeshDeterminism: vertProperties[{}] = {} expected {}", i, actual, expected);
}
}
#[test]
fn test_cpp_manifold_slice() {
let cube = Manifold::cube(Vec3::new(1.0, 1.0, 1.0), false);
let bottom = cube.slice(0.0);
let top = cube.slice(1.0);
assert_eq!(bottom.area(), 1.0, "Slice at z=0 should have area 1, got {}", bottom.area());
assert_eq!(top.area(), 0.0, "Slice at z=1 should have area 0, got {}", top.area());
}
#[test]
fn test_cpp_manifold_slice_empty_object() {
let empty = Manifold::empty();
assert!(empty.is_empty());
let bottom = empty.slice(0.0);
assert_eq!(bottom.area(), 0.0, "Slice of empty should have area 0");
}
#[test]
fn test_cpp_manifold_project() {
use crate::types::MeshGL;
let input = MeshGL {
num_prop: 3,
vert_properties: vec![
0.0, 0.0, 0.0,
-2.0, -0.7, -0.1,
-2.0, -0.7, 0.0,
-1.9, -0.7, -0.1,
-1.9, -0.6901, -0.1,
-1.9, -0.7, 0.0,
-1.9, -0.6901, 0.0,
-2.0, -1.0, 3.0,
-1.9, -1.0, 3.0,
-2.0, -1.0, 4.0,
-1.9, -1.0, 4.0,
-1.9, -0.6901, 3.0,
-1.9, -0.6901, 4.0,
-1.7, -0.6901, 3.0,
-1.7, -0.6901, 3.2,
-2.0, 0.0, -0.1,
-2.0, 0.0, 0.0,
-2.0, 0.0, 3.0,
-2.0, 0.0, 4.0,
-1.7, 0.0, 3.0,
-1.7, 0.0, 3.2,
-1.0, -0.6901, -0.1,
-1.0, -0.6901, 0.0,
-1.0, -0.6901, 3.2,
-1.0, -0.6901, 4.0,
-1.0, 0.0, -0.1,
-1.0, 0.0, 0.0,
-1.0, 0.0, 3.2,
-1.0, 0.0, 4.0,
],
tri_verts: vec![
1, 3, 2,
1, 4, 3,
2, 3, 5,
5, 6, 2,
3, 4, 6,
5, 3, 6,
6, 4, 21,
26, 22, 25,
21, 25, 22,
25, 15, 26,
26, 6, 22,
21, 4, 25,
21, 22, 6,
16, 26, 15,
16, 6, 26,
4, 15, 25,
15, 1, 16,
16, 2, 6,
4, 1, 15,
1, 2, 16,
12, 14, 23,
12, 13, 14,
12, 11, 13,
18, 9, 12,
11, 7, 17,
7, 9, 18,
17, 7, 18,
13, 11, 19,
17, 18, 20,
19, 11, 17,
19, 17, 20,
14, 13, 20,
18, 12, 24,
20, 13, 19,
20, 18, 27,
12, 10, 11,
24, 12, 23,
9, 10, 12,
9, 8, 10,
8, 11, 10,
8, 7, 11,
8, 9, 7,
14, 20, 27,
24, 28, 18,
27, 18, 28,
23, 14, 27,
24, 23, 28,
28, 23, 27,
],
..Default::default()
};
let m = Manifold::from_mesh_gl(&input);
let projected = m.project();
let area = projected.area();
assert!((area - 0.72).abs() < 0.01,
"Project area: {} expected ~0.72", area);
}
#[test]
fn test_cpp_manifold_get_mesh_gl() {
let m1 = Manifold::sphere(0.01, 0);
let mesh1 = m1.get_mesh_gl(3);
let m2 = Manifold::from_mesh_gl(&mesh1);
let mesh2 = m2.get_mesh_gl(3);
let nv1 = mesh1.vert_properties.len() / mesh1.num_prop as usize;
let nv2 = mesh2.vert_properties.len() / mesh2.num_prop as usize;
assert_eq!(nv1, nv2, "GetMeshGL: vertex count mismatch {} vs {}", nv1, nv2);
assert_eq!(mesh1.tri_verts.len(), mesh2.tri_verts.len(),
"GetMeshGL: triVerts length mismatch");
for i in 0..nv1 {
let p1 = mesh1.get_vert_pos(i);
let p2 = mesh2.get_vert_pos(i);
let dx = p1[0] - p2[0];
let dy = p1[1] - p2[1];
let dz = p1[2] - p2[2];
let dist = (dx * dx + dy * dy + dz * dz).sqrt();
assert!(dist <= 0.0001, "GetMeshGL: vertex {} distance {}", i, dist);
}
}
#[test]
fn test_cpp_manifold_warp_batch() {
let cube = Manifold::cube(Vec3::new(2.0, 3.0, 4.0), false);
let id = cube.original_id();
let shape1 = cube.warp(|v: &mut Vec3| {
v.x += v.z * v.z;
});
let shape2 = cube.warp_batch(|vecs: &mut [Vec3]| {
for v in vecs.iter_mut() {
v.x += v.z * v.z;
}
});
assert!(id >= 0, "WarpBatch: original ID should be >= 0");
assert_eq!(shape1.original_id(), -1, "WarpBatch: warped shape1 should have ID -1");
assert_eq!(shape2.original_id(), -1, "WarpBatch: warped shape2 should have ID -1");
let gl1 = shape1.get_mesh_gl(3);
assert_eq!(gl1.run_original_id.len(), 1, "WarpBatch: shape1 should have 1 run");
assert_eq!(gl1.run_original_id[0], id as u32, "WarpBatch: shape1 run ID mismatch");
let gl2 = shape2.get_mesh_gl(3);
assert_eq!(gl2.run_original_id.len(), 1, "WarpBatch: shape2 should have 1 run");
assert_eq!(gl2.run_original_id[0], id as u32, "WarpBatch: shape2 run ID mismatch");
assert_eq!(shape1.volume(), shape2.volume(), "WarpBatch: volumes differ");
assert_eq!(shape1.surface_area(), shape2.surface_area(), "WarpBatch: areas differ");
}
#[test]
fn test_cpp_manifold_warp2() {
use crate::cross_section::CrossSection;
let circle = CrossSection::circle(5.0, 20).translate(Vec2::new(10.0, 10.0));
let shape = Manifold::extrude(
&circle.to_polygons(), 2.0, 10, 0.0, Vec2::new(1.0, 1.0),
).warp(|v: &mut Vec3| {
let n_segments = 10;
let angle_step = 2.0 / 3.0 * std::f64::consts::PI / n_segments as f64;
let z_index = n_segments - 1 - v.z.round() as i32;
let angle = z_index as f64 * angle_step;
let new_z = v.y;
let new_y = v.x * angle.sin();
let new_x = v.x * angle.cos();
v.x = new_x;
v.y = new_y;
v.z = new_z;
});
let simplified = Manifold::batch_boolean(&[shape.clone()], OpType::Add);
assert!((shape.volume() - simplified.volume()).abs() < 0.0001,
"Warp2: volume mismatch {} vs {}", shape.volume(), simplified.volume());
assert!((shape.surface_area() - simplified.surface_area()).abs() < 0.0001,
"Warp2: area mismatch {} vs {}", shape.surface_area(), simplified.surface_area());
assert!((shape.volume() - 321.0).abs() < 1.0,
"Warp2: volume {} expected ~321", shape.volume());
}
#[test]
fn test_cpp_manifold_face_id_round_trip() {
let cube = Manifold::cube(Vec3::new(1.0, 1.0, 1.0), false);
assert!(cube.original_id() >= 0);
let mut in_gl = cube.get_mesh_gl(3);
let unique_in: std::collections::HashSet<u32> = in_gl.face_id.iter().copied().collect();
assert_eq!(unique_in.len(), 6, "FaceIDRoundTrip: expected 6 unique faceIDs, got {}", unique_in.len());
in_gl.face_id = vec![3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5];
let cube2 = Manifold::from_mesh_gl(&in_gl);
let out_gl = cube2.get_mesh_gl(3);
let unique_out: std::collections::HashSet<u32> = out_gl.face_id.iter().copied().collect();
assert_eq!(unique_out.len(), 2, "FaceIDRoundTrip: expected 2 unique faceIDs, got {}", unique_out.len());
}