Added export functions

This commit is contained in:
Mathias Hauan Arbo 2018-01-16 14:38:16 +01:00
parent 22679e592b
commit ef7790d76a

View file

@ -1,17 +1,23 @@
import FreeCAD
import Part
import json # For exporting part infos
import os # for safer path handling
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtGui
__title__ = "ARTools"
__author__ = "Mathias Hauan Arbo"
__workbenchname__ = "ARBench"
__version__ = "0.1"
__url__ = "https://github.com/mahaarbo/ARBench"
__doc__ = """"""
__doc__ = """
Useful tools for the Annotations for Robotics workbench."""
###################################################################
# Module functions
###################################################################
def vector2list(vec, scale=1e-3):
"""Gives the vector as a list, set scale for scaling factor.
default scale = 1e-3 for units in m."""
@ -26,14 +32,31 @@ def matrix2list(mat, scale=1e-3):
[mat.A41, mat.A42, mat.A43, mat.A44]]
def placement2axisvec(pl):
def placement2axisvec(pl, scale=1e-3):
"""Gives the placement as an dictionary of origin and rotation.
origin: [x,y,z], rotation:{axis:[ax,ay,az], angle:ang}"""
return {"origin": vector2list(pl.Base),
return {"origin": vector2list(pl.Base, scale),
"rotation": {"axis": vector2list(pl.Rotation.Axis, scale=1),
"angle": pl.Rotation.Angle}}
def boundingBox2list(bb, scale=1e-3):
"""Gives the bounding box as a list in m instead of mm"""
return [bb.XMin*scale, bb.XMax*scale,
bb.YMin*scale, bb.YMax*scale,
bb.ZMin*scale, bb.ZMax*scale]
def principalProperties2dict(pp, scale=1e-3):
npp = {}
for key, value in pp.iteritems():
if type(value) is FreeCAD.Vector:
npp[key.lower()] = vector2list(value, scale=1e-3)
else:
npp[key.lower()] = value
return npp
def describeSubObject(subobj):
"""Returns PrimitiveType, ShapeType."""
if isinstance(subobj, Part.Vertex):
@ -108,7 +131,8 @@ def spawnClassCommand(classname, function, resources):
Example usage:
spawnClassCommand("testcommand", testfunc,
{"Pixmap":"", "MenuText":"menutext","ToolTip":"tooltiptext"})
then add "testcommand" to commandlist in InitGui.py"""
then add "testcommand" to commandlist in InitGui.py
"""
def Activated(s):
function()
@ -117,3 +141,364 @@ def spawnClassCommand(classname, function, resources):
CommandClass = type("classname", (object,), {"Activated": Activated,
"GetResources": GetResources})
FreeCADGui.addCommand(classname, CommandClass())
def getLocalPartProps(obj):
old_placement = obj.Placement
obj.Placement = FreeCAD.Placement()
# Part properties
partprops = {
"label": obj.Label,
"placement": placement2axisvec(old_placement),
"boundingbox": boundingBox2list(obj.Shape.BoundBox),
"volume": obj.Shape.Volume*1e-9,
"centerofmass": vector2list(obj.Shape.CenterOfMass),
"principalproperties": principalProperties2dict(obj.Shape.PrincipalProperties)
}
obj.Placement = old_placement
return partprops
###################################################################
# Export functions
###################################################################
def exportPartInfo(obj, ofile):
"""
Exports part info to a new json file.
The part info includes:
Placement relative to world frame, bounding box, volume, center of mass,
principal properties.
For more information on principal properties, see TopoShape in OCCT
documentation.
"""
# File path stuff
odir, of = os.path.split(ofile)
if not os.path.exists(odir):
os.makedirs(odir)
if not of.lower().endswith(".json"):
ofile = ofile + ".json"
partprops = getLocalPartProps(obj)
with open(ofile, "wb") as propfile:
json.dump(partprops, propfile, indent=1, separators=(',', ': '))
return True
def appendPartInfo(obj, ofile):
"""Rewrites/appends part info to an existing json file.
The part info includes:
Placement relative to world frame, bounding box, volume, center of mass,
principal properties.
For more information on principal properties, see TopoShape in OCCT
documentation.
"""
with open(ofile, "rb") as propfile:
partprops = json.load(propfile)
new_props = getLocalPartProps(obj)
partprops.update(new_props)
with open(ofile, "wb") as propfile:
json.dump(partprops, propfile, indent=1, separators=(',', ': '))
return True
def exportFeatureFrames(obj, ofile):
"""Exports feature frames attached to a part."""
# Get the feature frames
import ARFrames
ff_check = lambda x: isinstance(x.Proxy, ARFrames.FeatureFrame)
ff_list = filter(ff_check, obj.InList)
ff_named = {ff.Label: ff.Proxy.getDict() for ff in ff_list}
feature_dict = {"features": ff_named}
# File stuff
odir, of = os.path.split(ofile)
if not os.path.exists(odir):
os.makedirs(odir)
if not of.lower().endswith(".json"):
ofile = ofile + ".json"
with open(ofile, "wb") as propfile:
json.dump(feature_dict, indent=1, separators=(',', ': '))
return True
def appendFeatureFrames(obj, ofile):
"""Rewrites/appends featureframes attached to a part to an existing json
file."""
# Get the feature frames
import ARFrames
with open(ofile, "rb") as propfile:
partprops = json.load(propfile)
ff_check = lambda x: isinstance(x.Proxy, ARFrames.FeatureFrame)
ff_list = filter(ff_check, obj.InList)
ff_named = {ff.Label: ff.Proxy.getDict() for ff in ff_list}
feature_dict = {"features": ff_named}
if "features" not in partprops.keys():
partprops.update(feature_dict)
else:
partprops["features"].update(feature_dict["features"])
with open(ofile, "wb") as propfile:
json.dump(partprops, indent=1, separators=(',', ': '))
return True
def exportPartInfoDialogue():
"""Spawns a dialogue window for part info exporting"""
# Select only true parts
s = FreeCADGui.Selection.getSelection()
FreeCADGui.Selection.clearSelection()
if len(s) == 0:
FreeCAD.Console.PrintError("No part selected.")
return False
unique_selected = []
for item in s:
if item not in unique_selected and isinstance(item, Part.Feature):
# Ensuring that we are parts
unique_selected.append(item)
FreeCADGui.Selection.addSelection(item)
# Fix wording
textprompt = "Save the properties of the part"
if len(unique_selected) > 1:
textprompt = textprompt + "s"
opts = QtGui.QFileDialog.DontConfirmOverwrite
# Create file dialog
ofile, filt = QtGui.QFileDialog.getSaveFileName(None, textprompt,
"*.json", options=opts)
if ofile == "":
# User cancelled
return False
if os.path.exists(ofile):
msgbox = QtGui.QMessageBox()
msgbox.setText("File already exists. We can overwrite the file, or add the information/rewrite only relevant sections.")
append_button = msgbox.addButton(unicode("Append"),
QtGui.QMessageBox.YesRole)
overwrite_button = msgbox.addButton(unicode("Overwrite"),
QtGui.QMessageBox.NoRole)
msgbox.exec_()
if msgbox.clickedButton() == append_button:
NEWFILE = False
elif msgbox.clickedButton() == overwrite_button:
NEWFILE = True
else:
return False
else:
NEWFILE = True
if NEWFILE:
exportPartInfo(unique_selected[0], ofile)
else:
appendPartInfo(unique_selected[0], ofile)
if len(unique_selected) > 1:
FreeCAD.Console.PrintWarning("Multi-part export not yet supported\n")
FreeCAD.Console.PrintMessage("Properties exported to "+str(ofile)+"\n")
def exportFeatureFramesDialogue():
"""Spawns a dialogue window for a part's feature frames to be exported."""
# Select only true parts
s = FreeCADGui.Selection.getSelection()
FreeCADGui.Selection.clearSelection()
if len(s) == 0:
FreeCAD.Console.PrintError("No part selected.")
return False
unique_selected = []
for item in s:
if item not in unique_selected and isinstance(item, Part.Feature):
# Ensuring that we are parts
unique_selected.append(item)
FreeCADGui.Selection.addSelection(item)
# Fix wording
textprompt = "Save the feature frames attached to the part"
if len(unique_selected) > 1:
textprompt = textprompt + "s"
opts = QtGui.QFileDialog.DontConfirmOverwrite
# Create file dialog
ofile, filt = QtGui.QFileDialog.getSaveFileName(None, textprompt,
"*.json", options=opts)
if ofile == "":
# User cancelled
return False
if os.path.exists(ofile):
msgbox = QtGui.QMessageBox()
msgbox.setText("File already exists. We can overwrite the file, or add the information/rewrite only relevant sections.")
append_button = msgbox.addButton(unicode("Append"),
QtGui.QMessageBox.YesRole)
overwrite_button = msgbox.addButton(unicode("Overwrite"),
QtGui.QMessageBox.NoRole)
msgbox.exec_()
if msgbox.clickedButton() == append_button:
NEWFILE = False
elif msgbox.clickedButton() == overwrite_button:
NEWFILE = True
else:
return False
else:
NEWFILE = True
if NEWFILE:
exportFeatureFrames(unique_selected[0], ofile)
else:
appendFeatureFrames(unique_selected[0], ofile)
if len(unique_selected) > 1:
FreeCAD.Console.PrintWarning("Multi-part export not yet supported\n")
FreeCAD.Console.PrintMessage("Feature frames of " + str(unique_selected[0]) + "exported to " + str(ofile) + "\n")
def exportPartInfoAndFeaturesDialogue():
"""Spawns a dialogue window for exporting both."""
s = FreeCADGui.Selection.getSelection()
FreeCADGui.Selection.clearSelection()
if len(s) == 0:
FreeCAD.Console.PrintError("No part selected.")
return False
unique_selected = []
for item in s:
if item not in unique_selected and isinstance(item, Part.Feature):
# Ensuring that we are parts
unique_selected.append(item)
FreeCADGui.Selection.addSelection(item)
# Fix wording
textprompt = "Save the part info and feature frames attached to the part"
if len(unique_selected) > 1:
textprompt = textprompt + "s"
opts = QtGui.QFileDialog.DontConfirmOverwrite
# Create file dialog
ofile, filt = QtGui.QFileDialog.getSaveFileName(None, textprompt,
"*.json", options=opts)
if ofile == "":
# User cancelled
return False
if os.path.exists(ofile):
msgbox = QtGui.QMessageBox()
msgbox.setText("File already exists. We can overwrite the file, or add the information/rewrite only relevant sections.")
append_button = msgbox.addButton(unicode("Append"),
QtGui.QMessageBox.YesRole)
overwrite_button = msgbox.addButton(unicode("Overwrite"),
QtGui.QMessageBox.NoRole)
msgbox.exec_()
if msgbox.clickedButton() == append_button:
NEWFILE = False
elif msgbox.clickedButton() == overwrite_button:
NEWFILE = True
else:
return False
else:
NEWFILE = True
if NEWFILE:
exportPartInfo(unique_selected[0], ofile)
appendFeatureFrames(unique_selected[0], ofile)
else:
appendPartInfo(unique_selected[0], ofile)
appendFeatureFrames(unique_selected[0], ofile)
if len(unique_selected) > 1:
FreeCAD.Console.PrintWarning("Multi-part export not yet supported.\n")
FreeCAD.Console.PrintMessage("Feature frames of " + str(unique_selected[0]) + "exported to " + str(ofile) + "\n")
###################################################################
# GUI Commands
###################################################################
uidir = os.path.join(FreeCAD.getUserAppDataDir(),
"Mod", __workbenchname__, "UI")
icondir = os.path.join(uidir, "icons")
spawnClassCommand("ExportPartInfoAndFeaturesDialogueCommand",
exportPartInfoAndFeaturesDialogue,
{"Pixmap": str(os.path.join(icondir, "parttojson.svg")),
"MenuText": "Export info and featureframes",
"ToolTip": "Export part properties (placement, C.O.M) and feature frames"})
###################################################################
# Information from primitive type
###################################################################
def getPrimitiveInfo(prim_type, subobj, scale=1e-3):
"""returns a dictionary of the primitive's specific information."""
d = {}
if prim_type == "ArcOfCircle":
d["Radius"] = scale*subobj.Curve.Radius
d["Center"] = vector2list(subobj.Curve.Center, scale)
d["Axis"] = vector2list(subobj.Curve.Axis, scale=1)
d["ParamerRange"] = subobj.ParameterRange
elif prim_type == "ArcOfEllipse":
d["Center"] = vector2list(subobj.Curve.Center, scale)
d["Axis"] = vector2list(subobj.Curve.Axis, scale=1)
d["MajorRadius"] = scale*subobj.Curve.MajorRadius
d["MinorRadius"] = scale*subobj.Curve.MinorRadius
d["ParameterRange"] = subobj.ParameterRange
elif prim_type == "ArcOfHyperBola":
d["AngleXU"] = subobj.Curve.AngleXU
d["Axis"] = vector2list(subobj.Curve.Axis, scale=1)
d["Center"] = vector2list(subobj.Curve.Center, scale)
d["MajorRadius"] = scale*subobj.Curve.MajorRadius
d["MinorRadius"] = scale*subobj.Curve.MinorRadius
d["ParameterRange"] = subobj.ParameterRange
elif prim_type == "ArcOfParabola":
d["AngleXU"] = subobj.Curve.AngleXU
d["Axis"] = vector2list(subobj.Curve.Axis, scale=1)
d["Center"] = vector2list(subobj.Curve.Center, scale)
d["Focal"] = scale*subobj.Curve.Focal
elif prim_type == "BSplineCurve":
FreeCAD.Console.PrintWarning("getPrimitiveInfo of BSpline incomplete.")
elif prim_type == "BezierCurve":
FreeCAD.Console.PrintWarning("getPrimitiveInfo of Bezier incomplete.")
elif prim_type == "Circle":
d["Radius"] = scale*subobj.Curve.Radius
d["Center"] = vector2list(subobj.Curve.Center, scale)
d["Axis"] = vector2list(subobj.Curve.Axis, scale=1)
d["ParamerRange"] = subobj.ParameterRange
elif prim_type == "Ellipse":
d["Center"] = vector2list(subobj.Curve.Center, scale)
d["Axis"] = vector2list(subobj.Curve.Axis, scale=1)
d["MajorRadius"] = scale*subobj.Curve.MajorRadius
d["MinorRadius"] = scale*subobj.Curve.MinorRadius
d["ParameterRange"] = subobj.ParameterRange
elif prim_type == "Hyperbola":
d["AngleXU"] = subobj.Curve.AngleXU
d["Axis"] = vector2list(subobj.Curve.Axis, scale=1)
d["Center"] = vector2list(subobj.Curve.Center, scale)
d["MajorRadius"] = scale*subobj.Curve.MajorRadius
d["MinorRadius"] = scale*subobj.Curve.MinorRadius
d["ParameterRange"] = subobj.ParameterRange
elif prim_type == "Parabola":
d["AngleXU"] = subobj.Curve.AngleXU
d["Axis"] = vector2list(subobj.Curve.Axis, scale=1)
d["Center"] = vector2list(subobj.Curve.Center, scale)
d["Focal"] = scale*subobj.Curve.Focal
elif prim_type == "Line":
if not subobj.Curve.Infinite:
d["StartPoint"] = vector2list(subobj.Curve.StartPoint)
d["EndPoint"] = vector2list(subobj.Curve.EndPoint)
d["Infinite"] = subobj.Curve.Infinite
elif prim_type == "BSplineSurface":
FreeCAD.Console.PrintWarning("getPrimitiveInfo of BSpline incomplete.")
elif prim_type == "BezierSurface":
FreeCAD.Console.PrintWarning("getPrimitiveInfo of Bezier incomplete.")
elif prim_type == "Cylinder":
d["Axis"] = subobj.Surface.Axis
d["Radius"] = scale*subobj.Surface.Radius
d["Center"] = vector2list(subobj.Surface.Center)
PR = list(subobj.ParameterRange)
PR[2] = PR[2]*scale
PR[3] = PR[3]*scale
d["ParameterRange"] = PR
elif prim_type == "Plane":
d["Axis"] = subobj.Surface.Axis
d["Position"] = vector2list(subobj.Surface.Position, scale)
d["ParameterRange"] = [scale*i for i in subobj.ParameterRange]
elif prim_type == "Sphere":
d["Axis"] = vector2list(subobj.Surface.Axis, scale=1)
d["Center"] = vector2list(subobj.Surface.Center, scale)
d["Radius"] = scale*subobj.Surface.Radius
d["ParameterRange"] = subobj.ParameterRange
elif prim_type == "Toroid":
d["Axis"] = vector2list(subobj.Surface.Axis, scale=1)
d["Center"] = vector2list(subobj.Surface.Center, scale)
d["MajorRadius"] = scale*subobj.Surface.MajorRadius
d["MinorRadius"] = scale*subobj.Surface.MinorRadius
d["ParameterRange"] = subobj.Surface.ParameterRange
elif prim_type == "Cone":
d["Axis"] = vector2list(subobj.Surface.Axis, scale=1)
d["Center"] = vector2list(subobj.Surface.Center, scale)
d["Radius"] = scale*subobj.Surface.Radius
d["SemiAngle"] = subobj.Surface.SemiAngle
d["ParameterRange"] = subobj.ParameterRange
FreeCAD.Console.PrintWarning("getPrimitiveInfo of Cone may have wrong ParameterRange.")
return d