-
-
Save mdeweerd/d14274ac53b64f23d983b5fdabed8f9e to your computer and use it in GitHub Desktop.
Convert OpenSCAD files to STEP or IGES using FreeCAD python library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env /cygdrive/c/Program Files/FreeCAD 0.21/bin/python | |
#!/usr/bin/env /cygdrive/c/Program Files/FreeCAD 0.20/bin/python | |
#!/usr/bin/env python3 | |
#!/usr/bin/env /cygdrive/c/Program Files/FreeCAD 0.20/bin/FreeCAD | |
#!/usr/bin/env /cygdrive/c/Program Files/FreeCAD 0.20/bin/FreeCADCmd | |
#!/usr/bin/freecad | |
#!/usr/bin/python | |
# (Options above for reminder, easy exchange of lines to adapt to local platform). | |
# /!\ Launch this script using the python provided/used by FreeCAD | |
# (Select/add the appropriate line above) | |
# | |
# | |
# "Quick" and dirty script to convert from one format to the other, | |
# including OpenSCAD to STEP conversion | |
# For STEP output with color, this script must be run using FreeCAD, not FreeCADCmd, not python . | |
# Also works with the latter two, but without color in STEP output. | |
# | |
# Original https://gist.github.com/slazav/4853bd36669bb9313ddb83f51ee1cb82 - slazav/convert_shape.py | |
# Fork (this version): https://gist.github.com/d14274ac53b64f23d983b5fdabed8f9e | |
# | |
# | |
import atexit | |
import os | |
import re | |
import sys | |
# Exit this program when we can catch an error | |
def at_exit(): | |
sys.exit(3) | |
# Register the exit script (does not catch everything though). | |
atexit.register(at_exit) | |
# Says if this was run with the FreeCAD Gui available. | |
hasGui = False | |
guiIsPossible = False | |
# Check if running from the Gui, cleanup sys.argv . | |
freecadPattern = re.compile(r"FreeCAD(Cmd)?(\.exe)?$", re.IGNORECASE) | |
freecadCmdPattern = re.compile(r"freecadcmd(\.exe)?$", re.IGNORECASE) | |
if freecadPattern.search(sys.argv[0]): | |
if not freecadCmdPattern.search(sys.argv[0]): | |
hasGui = True | |
# Remove freecad's path | |
sys.argv.pop(0) | |
# TODO: remove other arguments (-c, --console, etc) - workaround: do not use them | |
else: | |
paths = [ | |
"/usr/lib64/freecad/lib", | |
"C:/Program Files/FreeCAD 0.21/bin", | |
"C:/Program Files/FreeCAD 0.20/bin", | |
"C:/Program Files/FreeCAD 0.19/bin", | |
"C:/Program Files/FreeCAD 0.18/bin", | |
] | |
for p in paths: | |
if os.path.exists(p): | |
FREECADPATH = p | |
break | |
if FREECADPATH is not None: | |
print("Using FreeCAD.so from " + FREECADPATH) | |
sys.path.insert(0, FREECADPATH) | |
os.environ["PYTHON_PATH"] = FREECADPATH | |
guiIsPossible = True | |
author = "MDW" | |
company = "https://gist.github.com/d14274ac53b64f23d983b5fdabed8f9e" | |
if os.getenv("AUTHOR") is not None: | |
author = os.getenv("AUTHOR") | |
if os.getenv("COMPANY") is not None: | |
company = os.getenv("COMPANY") | |
# Check if started with appropriate parameters | |
if len(sys.argv) < 3: | |
print("Usage: %s <in_file> <out_file>" % (sys.argv[0]),) | |
sys.exit(1) | |
iname = sys.argv[1] | |
oname = sys.argv[2] | |
import FreeCAD | |
# Further checking of the parameters, especially the output | |
isMesh = False | |
# determine format from extension | |
if oname[-5:] == ".iges": | |
file_type = "iges" | |
elif oname[-5:] == ".step": | |
file_type = "step" | |
if guiIsPossible: | |
import FreeCADGui | |
FreeCADGui.showMainWindow() | |
hasGui = True | |
elif oname[-4:] == ".dae": | |
file_type = "dae" | |
elif oname[-4:] == ".wrl": | |
file_type = "wrl" | |
isMesh = True | |
else: | |
print("Output file should have .step, .dae, .wrl or .iges extension") | |
sys.exit(1) | |
# Export the parameters to a file (debug): | |
# p=FreeCAD.ParamGet("User parameter:BaseApp") | |
# p.Export("e.tmp") | |
if iname[-5:] == ".scad": | |
# OpenSCAD import settings according to | |
# https://forum.lulzbot.com/viewtopic.php?t=243 | |
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD") | |
p.SetBool("useViewProviderTree", True) | |
p.SetBool("useMultmatrixFeature", True) | |
# For some reason conversion does not work with cylinders created from | |
# extruded 2d circles. | |
# So I set MaxFN large enough and use smaller $fn in my step files to | |
# export such cylinders as polygons. | |
# If you use only normal cylinders, no need to use so large number here. | |
p.SetInt("useMaxFN", 50) | |
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/STEP") | |
p.SetString("Author", author) | |
p.SetString("Company", company) | |
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") | |
p.SetBool("ReadShapeCompoundMode", False) | |
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import") | |
p.SetBool("ExportLegacy", False) | |
p.SetBool("ExpandCompound", False) | |
p.SetInt("ImportMode", 0) | |
# Set a scheme for the STEP output: | |
# p.SetString("Scheme","AP203") | |
# p.SetString("Scheme", "AP214CD") | |
# p.SetString("Scheme","AP214DIS") | |
# p.SetString("Scheme","AP214IS") | |
p.SetString("Scheme", "AP242DIS") | |
# Export modified parameters to a file: | |
# p=FreeCAD.ParamGet("User parameter:BaseApp") | |
# p.Export("final.tmp") | |
# This should read any type of file | |
FreeCAD.loadFile(iname) | |
data = None | |
if False: | |
for p in App.ActiveDocument.RootObjects: | |
# find root object and export the shape | |
if len(p.InList) == 0: | |
print(p.__class__.__name__) | |
# iterate through all objects | |
allObjects = [] | |
for o in App.ActiveDocument.RootObjects: | |
# find root object and export the shape | |
if len(o.InList) == 0 and hasattr(o, "Shape") and o.Visibility: | |
# print(o.__class__.__name__) | |
# print (o.Visibility) | |
if data is None: | |
data = o | |
allObjects.append(o) | |
# print(allObjects) | |
# Transform meshes (not perfect, WIP) | |
mesh = None | |
if data is not None and isMesh: | |
import Import | |
import MeshPart | |
# shape = data[0][0].Shape | |
shape = data.Shape | |
mesh = MeshPart.meshFromShape(Shape=shape, LinearDeflection=0.1, Segments=True) | |
if 0: | |
shape_colors = data[0][1] | |
face_colors = [(0, 0, 0)] * mesh.CountFacets | |
for i in range(mesh.countSegments()): | |
color = shape_colors[i] | |
segm = mesh.getSegment(i) | |
for j in segm: | |
face_colors[j] = color | |
# mesh.write(Filename="new_example.obj", Material=face_colors, Format="obj") | |
# | |
# Generate the output | |
if data is None: | |
print("Error: can't find any object") | |
sys.exit(1) | |
else: | |
# print("Trying to write '{}'".format(oname)) | |
if file_type == "step": | |
done = False | |
if hasGui: | |
# Try our best option first! | |
try: | |
import ImportGui | |
ImportGui.export( | |
allObjects, name=oname, keepPlacement=True | |
) # , legacy=True, keepPlacement=True, exportHidden=True) | |
done = True | |
except Exception as e: | |
print("export using ImportGui did not work", e) | |
if not done: | |
try: | |
data.Shape.exportStep(oname) | |
done = True | |
except Exception as e: | |
print("exportStep on shape did not work", e) | |
if not done: | |
try: | |
import Import | |
Import.export( | |
obj=[data], | |
name=oname, | |
legacy=True, | |
keepPlacement=True, | |
exportHidden=True, | |
) | |
done = True | |
except Exception as e: | |
print("export using Import did not work", e) | |
elif file_type == "iges": | |
data.Shape.exportIges(oname) | |
elif file_type == "dae": | |
import importDAE | |
# mesh.write(Filename=oname) | |
importDAE.export(data, oname) # .write(Filename=oname) | |
elif file_type == "wrl": | |
mesh.write(Filename=oname) | |
sys.exit(0) | |
sys.exit(2) |
@mdeweerd Thanks for your reply, I just confused about the same function but not have same output(ImportGui.export
and Import.export
), and I will try to use the ImportGui.export
to do it until I found anymore solution. ❤️
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I checked - it's been a while since I used this.
Do note that there are 2 "issues:
While I managed to generate STEP with colors with older versions of FreeCAD, they were not (always) viewed in color in FreeCAD.
However, I could view them in color in KiCAD and they also in other tools.
I just tested FreeCAD 0.21.1: It generates more efficient STEP files and the STEP files with color are shown with color.
Not that the script tries several methods, the first one is ImportGui.export - in my case (on windows), FreeCAD opens a window which is closed almost immediately - so I think it works with the ImportGui.export method.
I am not 100% sure, but I think the ImportGui.Export provided better results - I would have prioritized the non-gui method if it didn't.
EDIT: I updated the script to call 0.21 (first line), add the 0.21 library as a library search path (windows), fix a spelling error, and also apply python formatting (black, flake, etc).