Triangle-mesh tooling for slicers, CAD utilities and 3D-printing pipelines: indexed STL I/O, repair (welding, hole filling, orientation, degenerate removal), and boolean operations (union, intersection, difference, symmetric difference) with exact-arithmetic predicates.
Written as a permissive (MIT OR Apache-2.0) alternative to the popular
GPL/LGPL mesh-processing stack — admesh, libigl/copyleft/cgal and
mcut — so it can be linked into commercial / closed-source pipelines
without the copyleft sweep.
The standard open-source mesh-processing stack has copyleft strings:
| Component | License | Replaced by |
|---|---|---|
admesh |
GPL-2.0+ | mesh_repair |
libigl/copyleft/cgal (boolean) |
GPL-3.0 | mesh_boolean |
mcut |
LGPL-3.0 / GPL | mesh_boolean |
Linking any of these into a commercial product drags the whole binary
under copyleft. M56kMeshTools rebuilds the same functionality from
published mathematical literature only (see Provenance) and
ships under permissive licensing, with a cxx-bridge static library for
direct C++ consumption.
mesh_core— sharedMesh/Facetypes, half-edge topology, uniform spatial grid, BVH, self-intersection detector, exact predicates (Shewchuk expansion arithmetic, Edelsbrunner-Mücke SoS), 2D polygon clipping (Sutherland-Hodgman).mesh_io— binary / ASCII STL reader & writer overmesh_core::Mesh; OBJ stub.mesh_repair— configurable repair pipeline: vertex welding, degenerate-facet removal, connected-component pruning, orientation propagation, hole filling, optional self-intersection report. Ships a CLImesh_repairbinary.mesh_boolean— mesh boolean operations: union, intersection, difference, symmetric difference. BVH broad phase, exact-arithmetic segment endpoints, SoS-resolved degeneracies, coplanar overlap via Sutherland-Hodgman, retri + classify + stitch + weld.mesh_ffi—cxx-bridge wrapping the four crates above into a single static library (libmesh_ffi.a/mesh_ffi.lib) with a generatedm56k_meshC++ namespace header.
All four library crates ship with #![forbid(unsafe_code)] at the workspace
level; mesh_ffi opts in only because cxx::bridge expands to internal
unsafe extern "C" blocks.
use mesh_repair::{load_stl, save_stl, repair, RepairConfig};
let mut m = load_stl(std::path::Path::new("model.stl"))?;
let outcome = repair(&mut m, &RepairConfig::default());
println!("{:?}", outcome);
save_stl(&m, std::path::Path::new("model_fixed.stl"))?;use mesh_boolean::{boolean, Config, Op};
use mesh_core::Mesh;
let result: Mesh = boolean(&a, &b, Op::Union, &Config::default())?;cargo build --release -p mesh_ffi produces:
target/release/libmesh_ffi.a(Unix) ortarget/release/mesh_ffi.lib(MSVC)target/cxxbridge/mesh_ffi/src/lib.rs.h— namespacem56k_mesh
#include "m56k_mesh/lib.rs.h"
using namespace m56k_mesh;
auto mesh = load_stl_file("input.stl");
auto cfg = default_repair_config();
auto outcome = repair_mesh(mesh, cfg);
save_stl_file(mesh, "output.stl");
auto u = boolean_meshes(a, b, COp::Union);Minimal CMake integration on the C++ host's side:
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/mesh_ffi/libmesh_ffi.a
COMMAND cargo build --release -p mesh_ffi
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/third_party/M56kMeshTools)
add_library(mesh_ffi STATIC IMPORTED GLOBAL)
set_target_properties(mesh_ffi PROPERTIES
IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/third_party/M56kMeshTools/target/release/libmesh_ffi.a
INTERFACE_INCLUDE_DIRECTORIES
${CMAKE_SOURCE_DIR}/third_party/M56kMeshTools/target/cxxbridge)For an idiomatic, maintained Cargo↔CMake bridge see
corrosion.
mesh_repair <input.stl> <output.stl> [options]
Options:
--tolerance F Vertex-merge distance (default 1e-6)
--max-iterations N Outer-loop cap (default 4)
--component-threshold F Min component size fraction (default 0.1)
--no-fix-normals Disable orientation stage
--no-fill-holes Disable hole filling
--no-remove-unconnected Keep every connected component
--no-merge Disable vertex welding
--detect-self-intersections Report intersecting pairs
--ascii Write ASCII STL
--analyze-only Print diagnosis without mutating the input
--quiet Suppress stats
Algorithms ported from open published literature only; no GPL/LGPL sources consulted at any stage.
- Botsch et al., Polygon Mesh Processing, AK Peters / CRC Press, 2010 (half-edge data structure, hole filling, orientation propagation).
- Attene M., "A lightweight approach to repairing digitized polygon meshes", Visual Computer, 2010 (welding, degenerate-facet removal).
- Bernstein G., "Fast, Exact, Linear Booleans", SIGGRAPH 2009 (mesh-boolean pipeline outline).
- Shewchuk J.R., "Adaptive Precision Floating-Point Arithmetic and Fast
Robust Geometric Predicates", Discrete & Computational Geometry 18(3),
1997 (
mesh_core::expansion&mesh_core::exact_isect). - Edelsbrunner H., Mücke E.P., "Simulation of Simplicity: A Technique to
Cope with Degenerate Cases in Geometric Algorithms", ACM TOG 9(1), 1990
(
mesh_core::sos). - Sutherland I.E., Hodgman G.W., "Reentrant Polygon Clipping", CACM 17(1),
1974 (
mesh_core::clip, coplanar-overlap branch inmesh_boolean::driver). - Möller T., Trumbore B., "Fast, Minimum Storage Ray/Triangle Intersection", Journal of Graphics Tools 2(1), 1997 (ray-cast inside / outside test).
The robust-orient3d primitive is delegated to the third-party
robust crate (MIT/Apache-2.0).
Requires Rust 1.75+ (workspace MSRV).
cargo build --workspace --release
cargo test --workspace --release
cargo clippy --workspace --all-targets --releaseOptional integration corpus (Thingi10K) — point THINGI10K_DIR at a
directory of STL files; cargo test -p mesh_repair --release thingi10k
will iterate up to 200 of them. Without the env var the test silently
skips.
cargo bench -p mesh_boolean / cargo bench -p mesh_repair. Indicative
numbers on x86_64 (single-thread release build, lto = "thin"):
| Benchmark | Time |
|---|---|
cube_union_translated 12 faces |
~470 µs |
cube_union_translated 48 faces |
~2.2 ms |
cube_union_translated 192 faces |
~10 ms |
sphere_intersect_cube (icosphere lvl 0) |
~0.4 ms |
sphere_intersect_cube (icosphere lvl 2) |
~6 ms |
tetra_pairwise_100 (sequential union) |
~30 ms |
repair_bench small (200 tris) |
~0.4 ms |
repair_bench medium (5 000 tris) |
~25 ms |
repair_bench large (80 000 tris) |
~600 ms |
| Item | State |
|---|---|
| All five crates compile clean on Rust 1.75+ | ✓ |
cargo test --workspace --release (132 tests) |
✓ |
cargo clippy --workspace --all-targets --release zero warns |
✓ |
| Property tests (4 × 4 096 cases) on boolean | ✓ |
| Stress: 1 000 random tetrahedra sequential union | ✓ |
| Stress: 5 000 random tetrahedra sequential union | ✓ |
Reference corpus (5 cases incl. 1 xfail) |
4/5 |
unsafe outside mesh_ffi |
0 |
cargo miri test clean |
* |
* Miri requires a separate nightly run; the workspace contains no unsafe
or FFI outside mesh_ffi, so Miri runs trivially on the four pure-Rust crates.
- Vertex-on-face degeneracies —
corpus_vertex_on_edge_degenerate.jsonisxfail. The boolean returns the correct volume but the surface is not strictly manifold (some edges incident to ≠ 2 faces). Resolving this needs explicit Steiner-vertex insertion in the stitcher; not yet implemented. - OBJ I/O —
mesh_io::objis a stub returningMeshError::Parse. STL is the supported path for now. - Stress reset rate — roughly 4 % of pairwise unions in the 5 000-tet
stress run produce a non-closed result and the harness resets the
accumulator to a fresh tetrahedron. This is below the Phase 5 spec
ceiling (no
OpenMesherrors, no panics, no non-finite volumes — all hold) but indicates remaining work on the stitcher's handling of high-multiplicity degeneracies.
Workspace-wide [lints.rust] unsafe_code = "forbid" keeps every pure-Rust
crate unsafe-free at the compiler level. The only crate that opts out is
mesh_ffi, where cxx::bridge macro-expands into internal
unsafe extern "C" blocks; the surface API exposed to either Rust or C++
callers is safe.
Dual-licensed under your choice of:
Copyright (c) 2026 Sarapin Vasiliy Dmitrievich.
Vasiliy Sarapin (DevMercenary).
Copyright (c) 2026 Sarapin Vasiliy Dmitrievich.
Issues and pull requests are welcome.