A FreeCAD workbench that reverse-engineers STL meshes of prismatic parts into editable PartDesign bodies.
Status: 0.18.x. The pipeline is well tested on machined-style prismatic parts — see the limitations section for what it does not attempt. Bug reports with the offending STL attached are very welcome.
Point it at a triangle mesh of a plate-like machined part and it will:
- Segment & recognize the mesh into analytic surfaces (planes, cylinders, cones, spheres, and tori) by region-growing + geometric fitting,
- Snap the fitted parameters to inferred design intent — unify near-parallel directions to canonical axes, merge coaxial cylinders, equalize near-equal radii, round values to grid-friendly numbers — with a full audit trail of every snap decision in the report view,
- Detect features — holes, counterbores, pockets, steps, pads, patterns — and infer a plausible build history,
- Rebuild the part as a native PartDesign Body: real sketches, pads, pockets, and holes you can edit parametrically.
Recognized and rebuilt on the current release:
- Base solid from the part footprint (arbitrary polygonal outline, including interior cutouts), on parts in any orientation (the working frame is detected, not assumed axis-aligned).
- Terraces / stepped pockets at multiple depths, including adjacent regions at different depths, curved (polygonal-approximated) boundaries, and raised islands inside pockets.
- Drilled holes: through and blind, including grid patterns (detected and labeled, e.g. "8x counterbored hole, grid 4x2").
- Counterbored holes, including bores whose mouth opens below the part's top face (e.g. on a plate under a raised deck).
- Countersunk holes: the conical entry of a flat-head-screw hole is
rebuilt as a
PartDesign::Holecountersink (mouth diameter + included angle), through or blind, on the top or bottom face. - Counterdrilled holes: a cylindrical recess with a conical
transition down to the drill (a flat-head screw seated in a bore) is
rebuilt as a single
PartDesign::HolewithHoleCutType = Counterdrill. - Conical pockets: tapered recesses, conical seats, and tapered
through holes, rebuilt as
PartDesign::SubtractiveConeprimitives. - Tapered bosses (convex cones): conical studs and draft-angled
bosses, truncated or pointed, rebuilt as
PartDesign::AdditiveConeprimitives standing on their base face. - Spherical bosses (domes) and spherical seats (sockets): a
full-revolution spherical cap standing on a face is rebuilt as a
PartDesign::Revolutionand a concave cap cut into a face as aPartDesign::Groove(PartDesign has no sphere primitive, so the cap is a revolved circular-arc profile, the apex placed exactly). Smooth domes, truncated (flat-topped) domes, and smooth sockets are supported; full spheres and flat-bottomed sockets are out of scope. - Rounded ring bands (tori): a full-revolution ring band standing on
a face is a rounded ring BOSS (a donut blend the fillet rule cannot
claim), and one cut into a face is a ring SEAT (groove). PartDesign has
no torus primitive, so the band is revolve-built — a circular-arc
profile on a plane through the major axis, spun 360 deg as a
PartDesign::Revolution(additive boss) orPartDesign::Groove(subtractive seat). Standalone donuts and partial-revolution bands are out of scope. - Cross-axis holes (horizontal holes), both through and blind (a blind side hole is rebuilt as a depth-limited pocket).
- Vertical bosses and lateral pads — flanges, rails, and gusseted or beveled wedges protruding sideways off a wall, with slanted undersides reproduced at true slope.
- Fillet detection with partial rebuild support, and chamfer
detection and rebuild as
PartDesign::Chamfer. - Robust executor: FreeCAD/OCC boolean failures on individual features are retried, deferred to the end of the build, and — only if unrecoverable — skipped with a loud, named report instead of silently degrading the part.
- Diagnostics:
freecad/meshtofeatures_wb/scripts/probe_bore_placement.pyruns the full pipeline headlessly inside FreeCAD's Python console and prints quantitative per-feature checks (useful when reporting bugs).
- Prismatic parts only. The reconstruction targets parts made of planes, cylinders, and cones (plates, brackets, housings, fixtures), plus conical countersinks, conical pockets, tapered bosses, spherical caps (domes and sockets), and rounded ring bands (tori). Organic / sculpted / scanned shapes will not reconstruct meaningfully. Full spheres and standalone donuts are fitted by the core but not yet rebuilt as features.
- Freeform transition geometry is carried by the correction pass. Some parts blend a wall into a base through a swept, freeform transition band (the Thingiverse corner bracket is the canonical case). No analytic primitive fits that surface, so the parametric feature tree approximates it (~1–2% volume under) while the terminal deviation-correction feature carries the mesh-accurate shape — hybrid modelling: edit the tree, measure the corrected feature. Fillets and chamfers detected ON such a band are dressed geometrically at the detected positions (labelled "(geometric)" in the tree) when the parametric body has no edge there; the Report view says so explicitly.
- Dimensional fidelity is bounded by the mesh. Snapping tolerance is ~0.1% of the part diagonal; a coarse tessellation limits what can be recovered. Some internal recess boundaries are intentionally oversized by ~0.15% of the part diagonal to avoid degenerate zero-thickness shells in OCC booleans — within mesh tolerance, but worth knowing if you measure the result.
- Input mesh quality matters. Best results on watertight, single-solid, CAD-exported STLs. Noisy 3D-scan meshes are untested and expected to perform poorly in this release.
- FreeCAD 1.1+ is the tested target (developed and field-tested on 1.1.x). Older versions may work but are unverified.
- Very large meshes (millions of faces) have not been performance-tuned.
The geometry core needs a few packages available inside FreeCAD's Python interpreter (not just your system Python):
numpy scipy trimesh shapely rtree
(rtree is a spatial index that some trimesh versions require at
runtime.) The Addon Manager offers to install the declared dependencies,
but sandboxed installs can prevent that — on Flatpak, install them
through the flatpak's own Python instead:
flatpak run --command=python3 org.freecad.FreeCAD -m pip install --user numpy scipy trimesh shapely rtree
If a rebuild produces no PartDesign body, check the Report view first: missing packages are named there with the exact install command.
See docs/VERIFY.md for per-platform instructions (including snap/AppImage and Flatpak paths) and a verification checklist.
Via the Addon Manager (custom repository): Edit → Preferences →
Addon Manager → add https://github.com/MasoudMiM/MeshToFeatures as a
custom repository, then install MeshToFeatures from the Addon Manager
and restart FreeCAD.
Manually: download the release zip and extract so that the folder lands at:
~/.local/share/FreeCAD/v1-1/Mod/MeshToFeatures (Linux)
%APPDATA%/FreeCAD/Mod/MeshToFeatures (Windows)
~/Library/Application Support/FreeCAD/Mod/... (macOS)
Restart FreeCAD fully after installing or upgrading (it caches Python modules for the whole session).
- Import your STL (File → Open, or drag-and-drop).
- Switch to the MeshToFeatures workbench.
- Select the mesh object, then run one of:
- Rebuild as PartDesign body — the full pipeline; creates a
Rebuilt_<name>Body plus aReconstruction of <name>group with the recognized surfaces. - Reconstruct surfaces (snapped) — recognition + snapping only, with the audit trail in the Report view.
- Reconstruct surfaces (raw fits) — recognition without snapping, for comparing against the snapped result.
- MeshToFeatures panel… — task panel with options and progress.
- Rebuild as PartDesign body — the full pipeline; creates a
- Watch the Report view (View → Panels → Report view): it lists every recognized pattern/feature, every snap decision, and any feature the executor had to retry or skip.
freecad/meshtofeatures_wb/ the FreeCAD workbench: commands, task
panel, and the PartDesign executor
core/ geometry core (FreeCAD-free: numpy/scipy/
trimesh/shapely) — fitting, segmentation,
snapping, feature detection, build planning
scripts/ headless diagnostics (probe + smoke test)
tests/ pytest suite (500+ tests, runs without FreeCAD)
docs/ design notes + verification guide
ROADMAP.md phased plan (executor CI, feature depth,
deviation report, sketching)
The core is deliberately FreeCAD-free so the entire planning pipeline is unit-testable anywhere; only the thin executor layer touches FreeCAD.
The suite needs the test-only extras manifold3d, mapbox-earcut, and
pytest in addition to the runtime packages (pip install -e .[dev]
provides everything).
pip install numpy scipy trimesh shapely manifold3d mapbox-earcut rtree pytest
python -m pytest freecad/meshtofeatures_wb/tests/ -qCI (.github/workflows/ci.yml) runs two jobs on every push and PR: the
headless suite above on a Python 3.10/3.11/3.12 matrix, and the
in-FreeCAD executor smoke test (scripts/freecad_smoke_test.py, run with
freecadcmd inside a FreeCAD 1.1.x AppImage) — the part of the pipeline
only a real FreeCAD build can exercise.
Issues and PRs are welcome. For reconstruction bugs, please attach the STL (or a minimal reproduction), your FreeCAD version, and the Report view output — the probe script's output is even better.
LGPL-2.1-or-later. See LICENSE.