Skip to content

DevMercenary/M56kMeshTools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

M56kMeshTools

License: MIT OR Apache-2.0 Rust 1.75+ Tests unsafe forbidden

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.

Why this exists

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.

Crates

  • mesh_core — shared Mesh / Face types, 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 over mesh_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 CLI mesh_repair binary.
  • 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_fficxx-bridge wrapping the four crates above into a single static library (libmesh_ffi.a / mesh_ffi.lib) with a generated m56k_mesh C++ 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.

Quick start (Rust)

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())?;

Quick start (C++, via mesh_ffi)

cargo build --release -p mesh_ffi produces:

  • target/release/libmesh_ffi.a (Unix) or target/release/mesh_ffi.lib (MSVC)
  • target/cxxbridge/mesh_ffi/src/lib.rs.h — namespace m56k_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.

CLI

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

Provenance

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 in mesh_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).

Build

Requires Rust 1.75+ (workspace MSRV).

cargo build --workspace --release
cargo test  --workspace --release
cargo clippy --workspace --all-targets --release

Optional 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.

Benchmarks

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

Status

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.

Known limitations

  • Vertex-on-face degeneraciescorpus_vertex_on_edge_degenerate.json is xfail. 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/Omesh_io::obj is a stub returning MeshError::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 OpenMesh errors, no panics, no non-finite volumes — all hold) but indicates remaining work on the stitcher's handling of high-multiplicity degeneracies.

Safety

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.

License

Dual-licensed under your choice of:

Copyright (c) 2026 Sarapin Vasiliy Dmitrievich.

Author

Vasiliy Sarapin (DevMercenary).

Copyright (c) 2026 Sarapin Vasiliy Dmitrievich.

Issues and pull requests are welcome.

About

Clean-room, permissively-licensed (MIT OR Apache-2.0) triangle-mesh tooling: STL repair, boolean ops (union / intersection / difference / xor), exact-arithmetic predicates, Simulation of Simplicity. Drop-in replacement for admesh / libigl-cgal / mcut.

Topics

Resources

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages