mesh2cad turns an STL back into CAD: a STEP file with real planes, cylinders and cones, plus a CadQuery or OpenSCAD script when the part is simple enough. I test it on six real Original Prusa i3 MK3S printer parts, because synthetic test shapes lied to me before.
After 0.10.0, two of the six came back as exact STEP solids. The other four fell back to a faceted solid (valid, but just triangles glued into faces). The tool reported a small "unfitted" area on each, and I wrote in the README that these were probably edge rounds: torus fillets and sphere corners that the fitter didn't know yet.
That was wrong, and finding out how it was wrong is what made 0.11.0 work.
Check the diagnosis against ground truth
These Prusa parts are open source, and the STEP files the STLs were exported from are in the same repository at the same commit. So instead of writing a torus fitter, I first listed the surface types in those STEP files.
There were no tori and no spheres at all. The surfaces were planes, cylinders, 45 degree cones (chamfers), and a few small B-spline patches for the belt tooth profile, about 0.1 to 0.2% of the area.
So the "unfitted" regions were not a missing surface type. They were segmentation mistakes:
- Faces glued across a real edge. A region grown across a 45 or 90 degree edge contains two planes, so no single surface fits it, and the whole patch was marked unfitted.
- Short chamfers rejected. A 45 degree chamfer 0.6 mm tall has so few vertices that the cone test threw it out.
- Cylinders left behind after a chamfer was carved out of them. The hole was fine, but its band was split into pieces that each looked too small.
- Flat polygons whose corners lie on a circle. On the belt idler, eight bands with radius 11.1 mm looked like a cylinder. Every vertex is exactly on that circle, but the face centres are about 7 mm off it and the angle between neighbours goes up to 90 degrees. They are planes. A fitter that only checks vertices will call a polygonal boss a cylinder or even a sphere.
What changed in 0.11.0
All of these only run when the mesh is a clean CAD tessellation, never on a noisy scan:
- A region that crosses an edge sharper than about 35 degrees is split and each piece is refit.
- A cylinder or sphere candidate must also match its face centres, not just its vertices, so polygons stay planes.
- A short 45 degree chamfer is kept when its vertices lie on the cone to roundoff.
- A ring of single-facet planes whose centres lie on one full cylinder is merged back into that cylinder.
- A hole circle that ends on several coplanar pads that don't touch is sewn as arcs of one circle.
- A plane corner whose three-plane intersection is unstable uses the mesh vertex, if that vertex lies on all three planes.
A fillet between two planes is deliberately left alone.
Numbers
From python benchmarks/real_parts.py (H is Hausdorff distance from the mesh, in mm):
| part | 0.10.0 | 0.11.0 |
|---|---|---|
| y-belt-idler | faceted-hybrid, H 0.28, 12.3 s | exact: 197 planes, 6 cylinders, 3 cones, H 0.0082, 5.1 s |
| y-belt-holder | faceted, 890 faces, 21.0 s | exact: 210 planes, 4 cylinders, H 0.16, 6.0 s |
| y-motor-holder | faceted-hybrid, H 0.27, 13.4 s | exact: 396 planes, 6 cylinders, 2 cones, H 0.10, 12.2 s |
| y-rod-holder | faceted, 38.6 s | still faceted, 34.2 s |
So five of six real parts are now exact STEP. The cylinders are the real screw and bearing holes (R 2.80, 1.65, 1.60, 1.50 mm) and the cones are the real chamfers. The 0.16 mm and 0.10 mm Hausdorff on the holder and motor holder come from the belt profile and a partial R=11.25 mm round that meets a chamfer, which stay as planes.
What still doesn't work
- y-rod-holder doesn't sew. Every region is now fitted (144 planes, 8 cylinders, 11 cones), but the R=4 mm rod bore is one loop that meets several chamfers, and the solid doesn't close. It falls back to the raw mesh: valid, Hausdorff 0, but not editable.
- Parametric scripts are still only 2 of 6. plug-aligner and endstop-block get OpenSCAD and CadQuery scripts with real parameters. The idler, motor holder and rod holder are refused with "a countersink, chamfer or draft on a combined extrusion is not supported yet". The belt holder is refused because the combined extrusion would deviate by 24 mm. The tool says this instead of writing a wrong script.
- B-spline belt profiles stay planes. That is fine for printing and measuring, less nice for editing.
The lesson
When a geometry tool fails on real data, the tempting move is to add the next surface type. Here the missing surface type didn't exist. If you have the original CAD for some of your test inputs, compare against it before writing new fitting code. It saved me writing a torus fitter nobody needed.
Try it:
pip install mesh2cad
mesh2cad convert part.stl -o part.step
mesh2cad convert part.stl --openscad part.scad
It also runs as an MCP server (mesh2cad mcp) so an AI assistant can convert and edit parts for you. Next up are chamfers on combined extrusions, which would make the idler and motor holder parametric, and sewing the rod holder bore. Bug reports with STL files that fail are the most useful thing you can send: github.com/emirmrkaljevic/mesh2cad.













