Skip to content

Instantly share code, notes, and snippets.

@hyOzd
Last active May 8, 2021 01:03
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hyOzd/2b38adff6a04e1613622 to your computer and use it in GitHub Desktop.
Save hyOzd/2b38adff6a04e1613622 to your computer and use it in GitHub Desktop.
An experimental FreeCAD macro that will export visible objects as X3D
#
# 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()
@jmwright
Copy link

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.

@hyOzd
Copy link
Author

hyOzd commented May 30, 2015

I didn't bother adding a license to this. But I can add GPL license. Does this work for you?

@jmwright
Copy link

jmwright commented Mar 1, 2016

@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.

@hyOzd
Copy link
Author

hyOzd commented Mar 27, 2016

@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