Last active
May 8, 2021 01:03
-
-
Save hyOzd/2b38adff6a04e1613622 to your computer and use it in GitHub Desktop.
An experimental FreeCAD macro that will export visible objects as X3D
This file contains 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
# | |
# This is an experimental FreeCAD macro that will export current scene | |
# as X3D file. Only the visible objects that can be converted to mesh | |
# will be exported with their color. | |
# | |
import FreeCAD | |
import FreeCADGui | |
import xml.etree.ElementTree as et | |
from PySide.QtGui import QFileDialog | |
import os | |
def getShapeNode(vertices, faces, diffuseColor=None): | |
"""Returns a <Shape> node for given mesh data. | |
vertices: list of vertice coordinates as `Vector` type | |
faces: list of tuple of vertice indexes ex: (1, 2, 3) | |
diffuseColor: tuple in the form of (R, G, B)""" | |
shapeNode = et.Element('Shape') | |
faceNode = et.SubElement(shapeNode, 'IndexedFaceSet') | |
faceNode.set('coordIndex', ' '.join(["%d %d %d -1" % face for face in faces])) | |
coordinateNode = et.SubElement(faceNode, 'Coordinate') | |
coordinateNode.set('point', | |
' '.join(["%f %f %f" % (p.x, p.y, p.z) for p in vertices])) | |
if diffuseColor: | |
appearanceNode = et.SubElement(shapeNode, 'Appearance') | |
materialNode = et.SubElement(appearanceNode, 'Material') | |
materialNode.set('diffuseColor', "%f %f %f" % diffuseColor) | |
return shapeNode | |
def exportX3D(objects, filepath): | |
"""Export given list of objects to a X3D file. | |
Each object is a dictionary in this form: | |
{ | |
points : [Vector, Vector...], | |
faces : [(pi, pi, pi), ...], # pi: point index | |
color : (R, G, B) # number range is 0-1.0 | |
}""" | |
fileNode = et.Element('X3D') | |
fileNode.set('profile', 'Interchange') | |
fileNode.set('version', '3.3') | |
sceneNode = et.SubElement(fileNode, 'Scene') | |
for o in objects: | |
shapeNode = getShapeNode(o["points"], o["faces"], o["color"]) | |
sceneNode.append(shapeNode) | |
with open(filepath, "wr") as f: | |
f.write(et.tostring(fileNode)) | |
def getDocumentDir(doc): | |
"""Returns directory for given document. `None` if the file is not | |
saved yet.""" | |
if doc.FileName: | |
return os.path.dirname(doc.FileName) | |
else: | |
return None | |
def run(): | |
doc = FreeCAD.activeDocument() | |
objects = [] | |
for o in doc.Objects: | |
if o.ViewObject.Visibility: | |
if hasattr(o, "Shape"): | |
mesh = o.Shape.tessellate(1) | |
if (not mesh[0]) or (not mesh[1]): | |
continue # some objects (such as Part:Circle) | |
# generate empty mesh, skip them | |
objects.append({ | |
"points": mesh[0], | |
"faces": mesh[1], | |
"color": o.ViewObject.ShapeColor[0:3] | |
}) | |
if objects: | |
savefile = QFileDialog.getSaveFileName( | |
parent = FreeCADGui.getMainWindow(), | |
caption = "Export X3D file", | |
dir = getDocumentDir(doc))[0] | |
exportX3D(objects, savefile) | |
else: | |
raise Exception("There is nothing to export!") | |
if __name__ == "__main__": | |
run() |
I didn't bother adding a license to this. But I can add GPL license. Does this work for you?
@hyOzd Sorry, I never got a notification about your answer. Adding a license would be great, but you can also just let me know if it's ok to evaluate working this into CadQuery. I'm not sure if I'll get it done, but I'm going to experiment with X3D support a bit. Thanks.
@jmwright no problem at all. About the notifications, github doesn't send notifications on gist comments :/
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Works for me. Well done. What's the license on this? I'd be interested in seeing if I could get this integrated into CadQuery as an export option at some point.