This is what happens when I get pissed off and bored at the same time.
As many threads have noted here, converting STL to STEP for CAD work involves plenty of roadblocks. The best online tool I’ve found so fare is Convert STL to STEP Online - Free & Fast | AnyConv But even that one often pukes on STLs larger than 10,000 vertices. FreeCAD 1.0 was released in 2024 and after 24 years in development, it finally got out of BETA. Better late than never!!! Before this release it was truly a science project, it still is but the STL to STEP conversion is pretty robust if not complicated though.
So I had AI generate a Python script to my specs and I am using it as a FreeCAD macro. Over the last three years, I have learned that if you can clearly articulate a technical problem, using the AI is like having a smart college intern working for me. My coding skills are rusty, but my computer science fundamentals are intact, so this is a pretty effective arrangement. That said, you still need to understand the AI intern’s limitations and as the normal disclaimer goes: Use at your own risk!!!
Problem #1:
STL to STEP conversion. A lot of online converters choke on high-poly meshes and/or messy geometry (non-manifold edges, self-intersections, etc.).
Solution:
FreeCAD 1.0+ for Windows has a very robust mesh-to-shape / mesh-to-solid workflow. For complex meshes it can take a long time (minutes to hours). It may look frozen. I have let it run overnight and ended up with a valid solid STEP. As always, your computer and mileage may vary.
Problem #2:
Even when conversion succeeds, importing into OnShape (my preferred CAD program) can put the part a mile away from the origin - because STL is just triangles in whatever coordinate space the author used. Sure, I can transform it manually, but I would rather make the computer do the boring part.
Solution:
A FreeCAD macro that:
normalizes the mesh placement,
converts mesh → shape → solid, and
exports a STEP that is centered at the origin.
For a FreeCAD installation tutorial, consult YouTube. Here’s a short one that walks you through it. I’ve forwarded the video to 0:56 to speed things up: https://youtu.be/9TrUSYzOHUM?t=56
Fair question. No, this is not like what Fusion360 advertises in their paid version with Prismatic conversion. I say “advertises” because I am unwilling to pay for the license just to experiment with that feature. Based on the demo videos I’ve seen, they imply it can convert planar surfaces and other geometry back into the original solid body. I’m skeptical, but I would welcome someone else trying it and reporting back.
With the FreeCAD conversion, a planar face is often still divided into 2 triangles, and you still have to go in and rebuild some features. While in my example below that did not happen, I recognize that when it does it can be time-consuming, but as anyone who has tried operating directly on an imported STL mesh can tell you, it is still far easier than working on the raw mesh itself.
Here’s a visual example of an STL and its STEP file import after it was converted.
FreeCAD converted and imported into OnShape
But as stated previously, the frustrations I was trying to overcome were the following import issues.
Non-Solid body - Making it very difficult to operate on.
Placing the model at the origin. I find It makes it easier to constrain the geometry when one corner of the model is at the origin rather than having to transform it manually.
Loss of Parametric Planar information.
Other minor “creature comforts” and “quality of life” issues and conveniences in addition import at origin, naming the object consistently upon import.
Note: For those unfamiliar with the distinction between when an item is a mesh or solid, it illustrated with the difference in icons upon import to OnShape or FreeCAD. Other CAD programs have similar restrictions and ICON notations.
As I stated in my opening post, a lot of this is personal nitpicking but since I so often find the need to modify an STL I find on some of the repositories, it becomes a real time saver to import correct, the first time out.
As we all know, ultimately when it comes to importing into the slicer, everything is converted to a mesh anyway but it’s those operations outside the slicer that matter.
yeah I see, thanks for sharing. It is an interesting topic due the noise of STL, I think it would be (poorly) solved by some AI because discrete mathematical stuff will always fail, like you could see some objects being 3.999999999998 where you as a human rebuilder will turn to 4, or that sphere for example, but you will also notice if is wrong to turn to sphere based on other conditions and such…
Quicksurface is an interesting tool to explore too, at least they have a fully functional trial. I often end using the STL for references only, and redrawing it.
I’m always looking for a better STL conversion tool. I just checked their website and, honestly, the experience was off-putting. An obfuscated site that took seven clicks and about 20 pages of scrolling just to find a price of €480 is not a great first impression. Not listing pricing in USD also suggests they’re not particularly interested in my business. Did you end up paying for it, and if so, what was the actual price?
By comparison, Fusion 360’s ~$680/year price is already about $630 beyond what I’m willing to pay for a single feature that may or may not work as advertised (prismatic conversion).
For what it’s worth, until I rediscovered FreeCAD after 1.0 release, over the past year I’ve leaned more toward Meshmixer as a conversion aid. That said, it’s no longer supported by Autodesk and could disappear at any time. It also requires far too many manual steps, which means babysitting the process for hours on complex models. If Meshmixer had a true macro or batch mode where I could set it up, walk away, and come back to a finished result, it would be much more compelling.
Yeah, that’s my approach as well in similar cases (well, 96% of cases… 4% are dropped), though I do confess that, like Olias, I’d love having a tool that would correctly import a STL to work on it.
Time ago I had a 3d scan and explored some tools (meshmixer, meshlab, etc) and others which normally are insanely expensive for this task. Quicksurface trial was very helpful to extract something useful because it is so interactive: you do few clicks and tell “this is a plane” and “this is a sphere” and it fits however it can, even stuff you wont imagine like extrusions and such, so if I had to do this for anything commercial I would probably use it.
I have no idea they had a complicated website, I remember clicking get trial and that was most of it, maybe annoying signup? I dont remember.
So, I do use free cad and have done a couple file type conversions using it but never used a macro. My question is to use the example of a macro you have here, how do i implement it on an existing file, as in how do i run it on my existing stl just to see it in action.
There are two ways. The hotkey is the easiest method. Once you define a macro, it is automatically assigned a hotkey. That’s the quickest way you invoke the script. It is designed to prompt you for a file name and will output to the same folder. You can find out what is assigned through the recent macros menu.
If for some reason a hotkey is not assigned, the second method is via the macro menu then once the macro execute macro dialogue box opens, select the macro and click the “execute” button.
Yes, I came across that after I made this original post and added it to my version but was too lazy to post it here. Although it’s a useful parameter, so far in my limited testing, I’ve only come across one shape where I could see a difference in the face count. But I can definitely see the value which is why I added it later to my private version, again, not trying to hold back just too lazy to post.
At any rate, for the sake of completeness even though you already posted the edit, here is the complete updated macro with “refine shape” option included:
Click here for a copy and paste window to open
# FreeCAD 1.0+ macro
# STL -> mesh-to-shape (sew ON, tol=0.10) -> solid -> center at origin -> REFINE -> STEP export
# Prompts for STL
# - Final object name = STL base name, truncated to 24 chars
# - Output STEP name = <base>_Origin_Placement.STEP (same folder as STL)
import FreeCAD as App
import Mesh
import Part
import Import
import os
from PySide import QtWidgets
TOLERANCE = 0.10
SEW_SHAPE = True
MAX_OBJ_NAME_LEN = 24
def prompt_for_stl():
dlg = QtWidgets.QFileDialog()
dlg.setWindowTitle("Select STL file")
dlg.setNameFilter("STL files (*.stl *.STL)")
dlg.setFileMode(QtWidgets.QFileDialog.ExistingFile)
if dlg.exec_():
files = dlg.selectedFiles()
if files:
return files[0]
return None
def sanitize_object_name(name, max_len):
clean = "".join(c if c.isalnum() or c == "_" else "_" for c in name)
if clean and clean[0].isdigit():
clean = "_" + clean
return clean[:max_len] if clean else "STL_Object"
def translate_mesh_to_min_corner_at_origin(mesh):
bb = mesh.BoundBox
dx = -bb.XMin
dy = -bb.YMin
dz = -bb.ZMin
mesh.translate(dx, dy, dz) # Mesh.Mesh.translate requires 3 scalars
return App.Vector(dx, dy, dz)
def make_shape_from_mesh(mesh, tol=0.10, sew=True):
shape = Part.Shape()
shape.makeShapeFromMesh(mesh.Topology, tol)
if sew:
try:
sewn = shape.sewShape(tol)
if sewn:
shape = sewn
except Exception:
pass
return shape
def make_solid_from_shape(shape):
if shape.ShapeType == "Solid":
return shape
if shape.ShapeType == "Shell":
return Part.makeSolid(shape)
if hasattr(shape, "Shells") and shape.Shells:
return Part.makeSolid(shape.Shells[0])
raise RuntimeError(f"Cannot create solid from ShapeType={shape.ShapeType}")
def recenter_to_origin_by_bbox_center(shape):
bb = shape.BoundBox
center = App.Vector(
(bb.XMin + bb.XMax) / 2.0,
(bb.YMin + bb.YMax) / 2.0,
(bb.ZMin + bb.ZMax) / 2.0
)
moved = shape.copy()
moved.translate(-center)
return moved, center
def refine_shape(shape):
# Equivalent intent to Part -> Create a copy -> Refine shape
try:
return shape.removeSplitter()
except Exception:
# Fallback: return original shape if refine is unavailable for this shape/build
return shape
def main():
stl_path = prompt_for_stl()
if not stl_path:
App.Console.PrintMessage("No STL selected. Aborted.\n")
return
folder = os.path.dirname(stl_path)
base_name = os.path.splitext(os.path.basename(stl_path))[0]
obj_name = sanitize_object_name(base_name, MAX_OBJ_NAME_LEN)
step_filename = f"{base_name}_Origin_Placement.STEP"
step_path = os.path.join(folder, step_filename)
doc = App.ActiveDocument or App.newDocument("STL_to_STEP")
# 1) Load mesh and move so MIN corner is at (0,0,0)
mesh = Mesh.Mesh(stl_path)
v_min_to_origin = translate_mesh_to_min_corner_at_origin(mesh)
mesh_obj = doc.addObject("Mesh::Feature", f"{obj_name}_Mesh")
mesh_obj.Mesh = mesh
# 2) Mesh -> Shape (sew ON, tol=0.10)
shape = make_shape_from_mesh(mesh, TOLERANCE, SEW_SHAPE)
shape_obj = doc.addObject("Part::Feature", f"{obj_name}_Shape")
shape_obj.Shape = shape
# 3) Shape -> Solid
solid = make_solid_from_shape(shape)
solid_obj = doc.addObject("Part::Feature", f"{obj_name}_Solid")
solid_obj.Shape = solid
doc.recompute()
# 4) Center solid at origin (bbox center)
centered, old_center = recenter_to_origin_by_bbox_center(solid_obj.Shape)
# 5) Refine shape (remove splitter edges), then write final object with desired name
refined = refine_shape(centered)
final_obj = doc.addObject("Part::Feature", obj_name)
final_obj.Shape = refined
doc.recompute()
# Export refined, centered solid
Import.export([final_obj], step_path)
App.Console.PrintMessage("Done.\n")
App.Console.PrintMessage(f"STL: {stl_path}\n")
App.Console.PrintMessage(f"Object name: {obj_name}\n")
App.Console.PrintMessage(f"Move (min corner -> origin): {v_min_to_origin}\n")
App.Console.PrintMessage(f"Recentered by old bbox center: {old_center}\n")
App.Console.PrintMessage("Refine shape: applied (removeSplitter)\n")
App.Console.PrintMessage(f"STEP exported: {step_path}\n")
main()
thank you, i appreciate the information and soem more insight. Its odd i keep getting a double “drawing” when i use the macro, i did try the updated version as well
That’s not odd and that’s not you. That’s an “undocumented feature” – yeah… that’s it, undocumented -- that I decided to deliberately leave in. I’ll explain.
So what you’re seeing is the the model in all of the stages that it progress through. I chose not to have the macro delete the prior stage just in case I wanted to go back and try tweaking the parameters manual.
Stage 1 - STL Mesh - The imported model
Stage 2 - Conversion from a mesh to shape
Stage 3 - Conversion from shape to a solid shape - this is very important if you miss this step, you will get a hollow body and if you cut that body in the slicer, you will get an “open manifold” error.
Stage 4 - Final Solid model with “refined” shape and the STEP file moved to the 0,0,0 origin which was the second goal of this macro. This was the step I left out in v1.0 which you noted above and I included in v1.1.
For those not familiar with FreeCad, you can toggle the visibility of objects by clicking the eyeball icon in the tree menu.
So what your seeing is actually four versions of the model. As stated, they were left there deliberately so that I could go back a step and manipulate further. Here’s an example of what I mean. Here I used the default “Sew shape” which is what stitched the mesh into a solid. However, with objects with fewer curves, it produces a STEP file output with fewer faces - which is highly desirable - if you increase the value to let’s say five. But this is at the sacrifice of curves which in turn come out very “polygonish” in appearance with fewer and courser facets.