Add missing C API bindings (#1633)

Add missing C API bindings for Manifold methods and MeshGL fields

New bindings:
- manifold_get_tolerance, manifold_set_tolerance, manifold_simplify,
  manifold_num_prop_vert
- manifold_meshgl{,64}_tolerance, _run_flags_length, _run_flags,
  _num_run, _update_normals

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Zach Loafman
2026-04-10 19:10:33 +12:00
committed by GitHub
co-authored by Claude Opus 4.6
parent a503665378
commit 93c1b4bf3c
3 changed files with 217 additions and 0 deletions
+16
View File
@@ -173,6 +173,10 @@ ManifoldManifold* manifold_refine_to_length(void* mem, ManifoldManifold* m,
double length);
ManifoldManifold* manifold_refine_to_tolerance(void* mem, ManifoldManifold* m,
double tolerance);
ManifoldManifold* manifold_set_tolerance(void* mem, ManifoldManifold* m,
double tolerance);
ManifoldManifold* manifold_simplify(void* mem, ManifoldManifold* m,
double tolerance);
// Manifold Shapes / Constructors
@@ -216,6 +220,8 @@ size_t manifold_num_tri(ManifoldManifold* m);
size_t manifold_num_prop(ManifoldManifold* m);
ManifoldBox* manifold_bounding_box(void* mem, ManifoldManifold* m);
double manifold_epsilon(ManifoldManifold* m);
double manifold_get_tolerance(ManifoldManifold* m);
size_t manifold_num_prop_vert(ManifoldManifold* m);
int manifold_genus(ManifoldManifold* m);
double manifold_surface_area(ManifoldManifold* m);
double manifold_volume(ManifoldManifold* m);
@@ -411,6 +417,11 @@ uint32_t* manifold_meshgl_run_original_id(void* mem, ManifoldMeshGL* m);
float* manifold_meshgl_run_transform(void* mem, ManifoldMeshGL* m);
uint32_t* manifold_meshgl_face_id(void* mem, ManifoldMeshGL* m);
float* manifold_meshgl_halfedge_tangent(void* mem, ManifoldMeshGL* m);
float manifold_meshgl_tolerance(ManifoldMeshGL* m);
size_t manifold_meshgl_run_flags_length(ManifoldMeshGL* m);
uint8_t* manifold_meshgl_run_flags(void* mem, ManifoldMeshGL* m);
size_t manifold_meshgl_num_run(ManifoldMeshGL* m);
void manifold_meshgl_update_normals(ManifoldMeshGL* m, int normal_idx);
size_t manifold_meshgl64_num_prop(ManifoldMeshGL64* m);
size_t manifold_meshgl64_num_vert(ManifoldMeshGL64* m);
@@ -432,6 +443,11 @@ uint32_t* manifold_meshgl64_run_original_id(void* mem, ManifoldMeshGL64* m);
double* manifold_meshgl64_run_transform(void* mem, ManifoldMeshGL64* m);
uint64_t* manifold_meshgl64_face_id(void* mem, ManifoldMeshGL64* m);
double* manifold_meshgl64_halfedge_tangent(void* mem, ManifoldMeshGL64* m);
double manifold_meshgl64_tolerance(ManifoldMeshGL64* m);
size_t manifold_meshgl64_run_flags_length(ManifoldMeshGL64* m);
uint8_t* manifold_meshgl64_run_flags(void* mem, ManifoldMeshGL64* m);
size_t manifold_meshgl64_num_run(ManifoldMeshGL64* m);
void manifold_meshgl64_update_normals(ManifoldMeshGL64* m, int normal_idx);
// Triangulation
+60
View File
@@ -321,6 +321,18 @@ ManifoldManifold* manifold_refine_to_tolerance(void* mem, ManifoldManifold* m,
return to_c(new (mem) Manifold(refined));
}
ManifoldManifold* manifold_set_tolerance(void* mem, ManifoldManifold* m,
double tolerance) {
auto result = from_c(m)->SetTolerance(tolerance);
return to_c(new (mem) Manifold(result));
}
ManifoldManifold* manifold_simplify(void* mem, ManifoldManifold* m,
double tolerance) {
auto simplified = from_c(m)->Simplify(tolerance);
return to_c(new (mem) Manifold(simplified));
}
ManifoldManifold* manifold_empty(void* mem) {
return to_c(new (mem) Manifold());
}
@@ -652,6 +664,26 @@ float* manifold_meshgl_halfedge_tangent(void* mem, ManifoldMeshGL* m) {
return copy_data(mem, from_c(m)->halfedgeTangent);
}
float manifold_meshgl_tolerance(ManifoldMeshGL* m) {
return from_c(m)->tolerance;
}
size_t manifold_meshgl_run_flags_length(ManifoldMeshGL* m) {
return from_c(m)->runFlags.size();
}
uint8_t* manifold_meshgl_run_flags(void* mem, ManifoldMeshGL* m) {
return copy_data(mem, from_c(m)->runFlags);
}
size_t manifold_meshgl_num_run(ManifoldMeshGL* m) {
return from_c(m)->NumRun();
}
void manifold_meshgl_update_normals(ManifoldMeshGL* m, int normal_idx) {
from_c(m)->UpdateNormals(normal_idx);
}
size_t manifold_meshgl64_num_prop(ManifoldMeshGL64* m) {
return from_c(m)->numProp;
}
@@ -713,6 +745,26 @@ double* manifold_meshgl64_halfedge_tangent(void* mem, ManifoldMeshGL64* m) {
return copy_data(mem, from_c(m)->halfedgeTangent);
}
double manifold_meshgl64_tolerance(ManifoldMeshGL64* m) {
return from_c(m)->tolerance;
}
size_t manifold_meshgl64_run_flags_length(ManifoldMeshGL64* m) {
return from_c(m)->runFlags.size();
}
uint8_t* manifold_meshgl64_run_flags(void* mem, ManifoldMeshGL64* m) {
return copy_data(mem, from_c(m)->runFlags);
}
size_t manifold_meshgl64_num_run(ManifoldMeshGL64* m) {
return from_c(m)->NumRun();
}
void manifold_meshgl64_update_normals(ManifoldMeshGL64* m, int normal_idx) {
from_c(m)->UpdateNormals(normal_idx);
}
ManifoldManifold* manifold_as_original(void* mem, ManifoldManifold* m) {
auto orig = from_c(m)->AsOriginal();
return to_c(new (mem) Manifold(orig));
@@ -747,6 +799,14 @@ ManifoldBox* manifold_bounding_box(void* mem, ManifoldManifold* m) {
double manifold_epsilon(ManifoldManifold* m) { return from_c(m)->GetEpsilon(); }
double manifold_get_tolerance(ManifoldManifold* m) {
return from_c(m)->GetTolerance();
}
size_t manifold_num_prop_vert(ManifoldManifold* m) {
return from_c(m)->NumPropVert();
}
uint32_t manifold_reserve_ids(uint32_t n) { return Manifold::ReserveIDs(n); }
ManifoldManifold* manifold_set_properties(
+141
View File
@@ -524,3 +524,144 @@ TEST(CBIND, meshgl64_merge_returns_mem) {
free(original);
free(cube);
}
TEST(CBIND, tolerance) {
ManifoldManifold* sphere = manifold_sphere(alloc_manifold_buffer(), 1.0, 100);
// GetTolerance should return a non-negative value.
double tol = manifold_get_tolerance(sphere);
EXPECT_GE(tol, 0.0);
// SetTolerance should be reflected by GetTolerance.
ManifoldManifold* with_tol =
manifold_set_tolerance(alloc_manifold_buffer(), sphere, 0.5);
EXPECT_EQ(manifold_get_tolerance(with_tol), 0.5);
// Simplify should produce a valid manifold with fewer or equal triangles.
ManifoldManifold* simplified =
manifold_simplify(alloc_manifold_buffer(), sphere, 0.1);
EXPECT_EQ(manifold_status(simplified), MANIFOLD_NO_ERROR);
EXPECT_LE(manifold_num_tri(simplified), manifold_num_tri(sphere));
manifold_destruct_manifold(sphere);
manifold_destruct_manifold(with_tol);
manifold_destruct_manifold(simplified);
free(sphere);
free(with_tol);
free(simplified);
}
TEST(CBIND, num_prop_vert) {
ManifoldManifold* cube =
manifold_cube(alloc_manifold_buffer(), 1.0, 1.0, 1.0, 0);
// A cube has 8 geometric vertices but more property vertices (due to
// duplicated normals at sharp edges).
EXPECT_EQ(manifold_num_vert(cube), 8);
EXPECT_GE(manifold_num_prop_vert(cube), manifold_num_vert(cube));
manifold_destruct_manifold(cube);
free(cube);
}
TEST(CBIND, meshgl_run_accessors) {
// Create two shapes with original IDs so the boolean result has 2 runs.
ManifoldManifold* cube_tmp =
manifold_cube(alloc_manifold_buffer(), 1, 1, 1, 0);
ManifoldManifold* cube =
manifold_as_original(alloc_manifold_buffer(), cube_tmp);
ManifoldManifold* sphere_tmp =
manifold_sphere(alloc_manifold_buffer(), 0.6, 32);
ManifoldManifold* sphere_trans =
manifold_translate(alloc_manifold_buffer(), sphere_tmp, 0.5, 0.5, 0.5);
ManifoldManifold* sphere =
manifold_as_original(alloc_manifold_buffer(), sphere_trans);
ManifoldManifold* result =
manifold_union(alloc_manifold_buffer(), cube, sphere);
EXPECT_EQ(manifold_status(result), MANIFOLD_NO_ERROR);
// MeshGL
ManifoldMeshGL* mesh = manifold_get_meshgl(alloc_meshgl_buffer(), result);
EXPECT_GE(manifold_meshgl_tolerance(mesh), 0.0f);
EXPECT_EQ(manifold_meshgl_num_run(mesh), 2);
EXPECT_EQ(manifold_meshgl_run_flags_length(mesh),
manifold_meshgl_num_run(mesh));
size_t flags_len = manifold_meshgl_run_flags_length(mesh);
uint8_t* flags = (uint8_t*)manifold_meshgl_run_flags(malloc(flags_len), mesh);
// Just verify we got data without crashing.
EXPECT_NE(flags, (uint8_t*)NULL);
free(flags);
// MeshGL64
ManifoldMeshGL64* mesh64 =
manifold_get_meshgl64(alloc_meshgl64_buffer(), result);
EXPECT_GE(manifold_meshgl64_tolerance(mesh64), 0.0);
EXPECT_EQ(manifold_meshgl64_num_run(mesh64), 2);
EXPECT_EQ(manifold_meshgl64_run_flags_length(mesh64),
manifold_meshgl64_num_run(mesh64));
size_t flags64_len = manifold_meshgl64_run_flags_length(mesh64);
uint8_t* flags64 =
(uint8_t*)manifold_meshgl64_run_flags(malloc(flags64_len), mesh64);
EXPECT_NE(flags64, (uint8_t*)NULL);
free(flags64);
manifold_destruct_meshgl(mesh);
manifold_destruct_meshgl64(mesh64);
manifold_destruct_manifold(result);
manifold_destruct_manifold(sphere);
manifold_destruct_manifold(sphere_trans);
manifold_destruct_manifold(sphere_tmp);
manifold_destruct_manifold(cube);
manifold_destruct_manifold(cube_tmp);
free(mesh);
free(mesh64);
free(result);
free(sphere);
free(sphere_trans);
free(sphere_tmp);
free(cube);
free(cube_tmp);
}
TEST(CBIND, meshgl_update_normals) {
// Get a mesh with normals, then verify UpdateNormals doesn't corrupt it.
ManifoldManifold* cube =
manifold_cube(alloc_manifold_buffer(), 1.0, 1.0, 1.0, 0);
ManifoldManifold* with_normals =
manifold_calculate_normals(alloc_manifold_buffer(), cube, 3, 60.0);
EXPECT_EQ(manifold_status(with_normals), MANIFOLD_NO_ERROR);
// MeshGL with normals at channel 3
ManifoldMeshGL* mesh =
manifold_get_meshgl_w_normals(alloc_meshgl_buffer(), with_normals, 3);
size_t tri_before = manifold_meshgl_num_tri(mesh);
manifold_meshgl_update_normals(mesh, 3);
EXPECT_EQ(manifold_meshgl_num_tri(mesh), tri_before);
// Verify the mesh is still valid by constructing a Manifold from it.
ManifoldManifold* rebuilt = manifold_of_meshgl(alloc_manifold_buffer(), mesh);
EXPECT_EQ(manifold_status(rebuilt), MANIFOLD_NO_ERROR);
// MeshGL64 same test
ManifoldMeshGL64* mesh64 =
manifold_get_meshgl64_w_normals(alloc_meshgl64_buffer(), with_normals, 3);
manifold_meshgl64_update_normals(mesh64, 3);
ManifoldManifold* rebuilt64 =
manifold_of_meshgl64(alloc_manifold_buffer(), mesh64);
EXPECT_EQ(manifold_status(rebuilt64), MANIFOLD_NO_ERROR);
manifold_destruct_manifold(rebuilt64);
manifold_destruct_meshgl64(mesh64);
manifold_destruct_manifold(rebuilt);
manifold_destruct_meshgl(mesh);
manifold_destruct_manifold(with_normals);
manifold_destruct_manifold(cube);
free(rebuilt64);
free(mesh64);
free(rebuilt);
free(mesh);
free(with_normals);
free(cube);
}