Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/usr/bin/python3
"""
This script renders different kinds of CAD files as PNG files using
FreeCAD. If the given PNG image path already exists, and was created
from the same file (same MD5 hash), no new rendering is performed.
Usage: cad2png.py [OPTIONS] path/to/someFile.FCStd path/to/somefile.png
Options:
-w, --width : the width of the png image, default 800
-h, --height : the height of the png image, default 600
-b, --background : the background color in hex format, default #FFFFFF
-n, --nocache : discard caching and force-recompute image
Example:
cad2png.py -w 800 -h 600 /path/to/file.FCStd /path/to/image.png
Supported file types:
- FreeCAD files (*.FCStd)
- STEP files (*.stp, *.step)
- IGES files (*.igs, *.iges)
- OpenCasCade's BREP files (*.brp, *.brep)
- Any other filetype supported by FreeCAD (obj, stl, ifc, etc...) is
supported as well, but they are mesh formats, and there might be much
faster viewers for them
Instructions:
- make sure you have the latest version of FreeCAD installed
- make sure you have the pivy (python bindongs for coin3D) package
installed
- make sure FreeCAD is importable from python (use the commented line
below if needed). Another way, more system-independent, is to symlink
your FreeCAD.so file to .local/lib/Python3.7/site-packages (user-wide)
or /usr/lib/python3.7/dis-packages (system-wide) (using your version
of python3 instead of 3.7 in the links above if applicable)
- on linux, the X server must have indirect rendering enabled in order
to be able to do offline PNG rendering. Unfortunatley, this is turned
off by default on most recent distros. The easiest way I found is to
edit (or create if inexistant) /etc/X11/xorg.conf and add this:
Section "ServerFlags"
Option "AllowIndirectGLX" "on"
Option "IndirectGLX" "on"
EndSection
(if your xorg.conf already has a "ServerFlags" section, just add the
two options in it). There might be other ways for other distros
though, google for "enable indirect glx" with your distro name and
version
"""
# builtin Python modules
import os # builtin python lib
import sys # builtin python lib
import getopt # command-line options parser
import hashlib # builtin python lib
from PIL.PngImagePlugin import PngImageFile, PngInfo # PNG metadata io
# FreeCAD modules
# uncomment and adapt next line if needed - path to your FreeCAD.so (or FreeCAD.pyd on windows)
# sys.path.append("/path/to/FreeCAD.so")
import FreeCAD # main freecad lib, to be imported before any other FreeCAD component
import OfflineRenderingUtils # the offline rendering utilities module
def convert(freecadfile,outputpath,width=800,height=600,background="#FFFFFF",usecache=True):
# first check if the image already exists and was made with the exact same file
with open(freecadfile, "rb") as f:
file_hash = hashlib.md5()
while chunk := f.read(8192):
file_hash.update(chunk)
filehash = str(file_hash.hexdigest())
if usecache and os.path.exists(outputpath):
target = PngImageFile(outputpath)
if hasattr(target,"text") and ("fchash" in target.text):
if target.text["fchash"] == filehash:
print(outputpath,"is up to date")
return
# convert background string to tuple
background = tuple(int(background[i:i + 2], 16) / 255. for i in (1, 3, 5))
# open FreeCAD file (this works with any filetype supported by FreeCAD
FreeCAD.loadFile(freecadfile)
doc = FreeCAD.ActiveDocument
if freecadfile.lower().endswith(".fcstd"):
# build color dict
colors = OfflineRenderingUtils.getColors(freecadfile)
# get the camera data from the file (used in some functions below)
camera = OfflineRenderingUtils.getCamera(freecadfile)
else:
colors = {}
camera = None
# build coin scene
scene = OfflineRenderingUtils.buildScene(doc.Objects,colors)
# export to PNG
OfflineRenderingUtils.render(outputpath,
scene,
camera,
width=width,
height=height,
background=background)
# write hash info as metadata
target = PngImageFile(outputpath)
metadata = PngInfo()
metadata.add_text("fchash",filehash)
target.save(outputpath, pnginfo=metadata)
if __name__ == "__main__":
opts, args = getopt.getopt(sys.argv[1:], "h:w:b:n", ["height=", "width=","background=","nocache"])
if len(args) == 2:
infile = args[0]
outfile = args[1]
width = 800
height = 600
background = "#FFFFFF"
usecache = True
for opt in opts:
if opt[0] in ("-h","--height"):
height = int(opt[1])
elif opt[0] in ("-w","--width"):
width = int(opt[1])
elif opt[0] in ("-b","--background"):
background = opt[1]
elif opt[0] in ("-n","--nocache"):
usecache = False
convert(infile, outfile, width, height, background, usecache)
else:
print(__doc__)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.