Code to create interlocked geometries for multimaterial 3D printing.
Given two solids that overlap, Interlock3DP splits the shared region between them so that the two printed parts lock together. The result is two watertight, non-overlapping solids, ready to be printed in two materials.
This is the reference implementation for:
A. Chambers, A. Krishnamurthy, "Designing Interlocking Interfaces of Complex Geometries for Multimaterial 3D Printing", Manufacturing Letters 49 (2026) 1215–1225 (NAMRC 54).
- Overlap. The intersection Ω of the two input solids is computed and voxelized.
- Cut each layer. In every voxel layer, an A* path between the two farthest boundary points divides the layer's overlap. A flood fill gives one side to each part, and a continuity weight keeps the sides consistent from one layer to the next.
- Stamp the interface. Each cell on the path is replaced by an interlocking stamp: a flat (T-shaped) stamp on straight runs and a corner stamp at turns. Each stamp has a complementary half, so the two parts' teeth fit together within the layer.
- Assemble. Each part is its input solid minus the overlap, plus its share of the overlap.
VERIFICATION.md maps each step of the paper's Algorithm 1 to the function that implements it.
Use Python 3.12 with NumPy 1.x. trimesh 4.1.4 does not work with NumPy 2, and NumPy 1.x
does not run on Python 3.13.
conda create -p ./py312 python=3.12
./py312/python -m pip install -r requirements.txtrequirements.txt pins the versions the verification was run with.
python main.py <input1> <input2> <output_dir> <feature_size> [flat flatAlt corner cornerAlt]input1,input2: the two solids, in any mesh format trimesh reads (STL, OBJ, ...). They must overlap.output_dir: created if it does not exist.feature_size: the stamp feature size, in model units; pick it near your nozzle diameter. One voxel is 5 ×feature_size, and each stamp is a 5 × 5 × 5 pattern of features.flat flatAlt corner cornerAlt(optional, all four or none): custom stamps replacing the built-in ones. Each is a watertight mesh in the unit cube [0, 1]³, which is scaled to one voxel;flatAltandcornerAltmust be the complements offlatandcornerin that cube.
python main.py -h prints this summary.
| File | Contents |
|---|---|
d1.obj, d2.obj |
Input 1 and input 2 with the overlap removed |
i1.obj, i2.obj |
The two interlocking halves of the overlap |
S1.stl, S2.stl |
The final parts to print: d1 ∪ i1 and d2 ∪ i2 |
The output is deterministic: the same inputs give byte-identical files on every run.
python -c "import trimesh; trimesh.creation.box(bounds=[[0,0,0],[10,10,10]]).export('a.stl'); trimesh.creation.box(bounds=[[5,4,2],[15,14,12]]).export('b.stl')"
python main.py a.stl b.stl out 0.2
python verification/check_outputs.py out/ a.stl b.stlThis takes about a second. out/S1.stl and out/S2.stl are each one watertight body, and they do
not overlap.
VERIFICATION.md compares the code with the paper and records the test runs. The two scripts in
verification/ reproduce them:
| Script | Checks |
|---|---|
check_outputs.py <out/> <a> <b> |
Watertightness, number of bodies, overlap between the parts, volume conservation; rewrites S1.stl and S2.stl |
dense_sample.py <out/> <feature_size> <a> <b> |
Samples the overlap densely to look for gaps and doubly claimed points, and counts the stamp teeth (label changes along z) |
Both scripts join paths by concatenation, so pass the output directory with a trailing /.
This repository is the reference for the paper, so the method is kept as published. These
departures from the paper's text are deliberate and described in VERIFICATION.md:
- the farthest boundary pair maximizes √|dx| + √|dy| rather than the grid distance;
- the continuity weights are a product rather than the sum in Eq. 3–4;
- on rectangular overlaps the cut follows an edge, so one part can get little of the overlap (17 % in the narrow-overlap test).
MIT; see LICENSE.