diff --git a/ARFrames.py b/ARFrames.py
new file mode 100644
index 0000000..8f8b827
--- /dev/null
+++ b/ARFrames.py
@@ -0,0 +1,724 @@
+import FreeCAD
+import ARTools
+if FreeCAD.GuiUp:
+ import FreeCADGui
+ from pivy import coin
+ from PySide import QtCore, QtGui, QtSvg
+ import Part
+ import os
+
+__title__ = "ARFrames"
+__author__ = "Mathias Hauan Arbo"
+__workbenchname__ = "ARBench"
+__version__ = "0.1"
+__url__ = "https://github.com/mahaarbo/ARBench"
+__doc__ = """"""
+
+
+############################################################
+# Frame Objects
+############################################################
+
+
+class Frame(object):
+ """Basic freestanding frame"""
+ def __init__(self, obj):
+ obj.addProperty("App::PropertyPlacement",
+ "Placement", "Base",
+ "Placement of the frame")
+ obj.Placement = FreeCAD.Placement()
+ obj.Proxy = self
+
+ def onChanged(self, fp, prop):
+ pass
+
+ def execute(self, obj):
+ pass
+
+ def __getstate__(self):
+ return None
+
+ def __setstate__(self, state):
+ return None
+
+
+class PartFrame(Frame):
+ """Frame rigidly attached to a part frame.
+ Inherits the base placement from the part's frame, and placement is
+ relative to the part frame.
+ """
+ def __init__(self, obj, partobj):
+ Frame.__init__(self, obj)
+ obj.addProperty("App::PropertyPlacementLink",
+ "Part", "Parent",
+ "The part to attach to.")
+ obj.Part = partobj
+ obj.setEditorMode("Part", 1)
+
+ def execute(self, obj):
+ if FreeCAD.GuiUp:
+ obj.ViewObject.Proxy.updateData(obj, "Placement")
+
+
+class FeatureFrame(PartFrame):
+ """Frame rigidly attached to a feature.
+ The feature frame is attached to a feature on a part. It gives both the
+ placement of the feature w.r.t. the part, and the placement from the
+ feature."""
+ def __init__(self, obj, partobj, featurePlacement):
+ PartFrame.__init__(self, obj, partobj)
+ obj.addProperty("App::PropertyPlacement",
+ "FeaturePlacement", "Feature",
+ "The frame attached to the feature.")
+ obj.addProperty("App::PropertyString",
+ "PrimitiveType", "Feature",
+ "The primitive type of the feature.")
+ obj.addProperty("App::PropertyString",
+ "ShapeType", "Feature",
+ "The shape type of the feature.")
+ obj.addProperty("App::PropertyString",
+ "Positioning", "Feature",
+ "The type of positioning used during creation.")
+ obj.FeaturePlacement = featurePlacement
+
+
+############################################################
+# ViewProvider to the frames
+############################################################
+
+
+class ViewProviderFrame(object):
+ """ViewProvider for the basic frame.
+ Uses the SOAxiscrosskit to create axises with constant length regardless
+ of zoom. Updates position when placement is changed.
+ """
+ def __init__(self, vobj):
+ vobj.addProperty("App::PropertyFloat", "Scale")
+ vobj.Scale = 0.12
+ vobj.addProperty("App::PropertyFloat", "HeadSize")
+ vobj.HeadSize = 3.0
+ vobj.addProperty("App::PropertyFloat", "LineWidth")
+ vobj.LineWidth = 2.0
+ vobj.Proxy = self
+
+ def attach(self, vobj):
+ # We only have a shaded visual group
+ self.shaded = coin.SoGroup()
+
+ # Takes heavily from SoAxisCrosskit.h,
+ # and Toggle_DH_Frames by galou_breizh on the forums
+ self.vframe = coin.SoType.fromName("SoShapeScale").createInstance()
+ self.vframe.setPart("shape", coin.SoType.fromName("SoAxisCrossKit").createInstance())
+ self.vframe.scaleFactor.setValue(0.1)
+ ax = self.vframe.getPart("shape", 0)
+ cone = ax.getPart("xHead.shape", 0)
+ cone.bottomRadius.setValue(vobj.HeadSize)
+ cone = ax.getPart("yHead.shape", 0)
+ cone.bottomRadius.setValue(vobj.HeadSize)
+ cone = ax.getPart("zHead.shape", 0)
+ cone.bottomRadius.setValue(vobj.HeadSize)
+ lwstring = "lineWidth {0}".format(vobj.LineWidth)
+ ax.set("xAxis.appearance.drawStyle", lwstring)
+ ax.set("yAxis.appearance.drawStyle", lwstring)
+ ax.set("zAxis.appearance.drawStyle", lwstring)
+ ax.set("xAxis.pickStyle", "style SHAPE")
+ ax.set("yAxis.pickStyle", "style SHAPE")
+ ax.set("zAxis.pickStyle", "style SHAPE")
+
+ # Then remember to make it selectable in the viewer
+ selectionNode = coin.SoType.fromName("SoFCSelection").createInstance()
+ selectionNode.documentName.setValue(FreeCAD.ActiveDocument.Name)
+ selectionNode.objectName.setValue(vobj.Object.Name)
+ selectionNode.subElementName.setValue("Frame")
+ selectionNode.addChild(self.vframe)
+
+ # We would like to place it where we want
+ self.transform = coin.SoTransform()
+ self.shaded.addChild(self.transform)
+ self.shaded.addChild(self.vframe)
+ self.shaded.addChild(selectionNode)
+ vobj.addDisplayMode(self.shaded, "Shaded")
+
+ def updateData(self, fp, prop):
+ if prop == "Placement":
+ pl = fp.getPropertyByName("Placement")
+ self.transform.translation = (pl.Base.x,
+ pl.Base.y,
+ pl.Base.Z)
+ self.transform.rotation = pl.Rotation.Q
+
+ def getDisplayModes(self, vobj):
+ modes = ["Shaded"]
+ return modes
+
+ def getDefaultDisplayMode(self):
+ return "Shaded"
+
+ def getIcon(self):
+ icondir = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", __workbenchname__, "UI", "icons")
+ return str(os.path.join(icondir, "frame.svg"))
+
+ def onChanged(self, vp, prop):
+ if prop == "Scale":
+ s = vp.getPropertyByName("Scale")
+ self.vframe.scaleFactor.setValue(float(s))
+ elif prop == "HeadSize":
+ hs = vp.getPropertyByName("HeadSize")
+ xcone = self.vframe.getPart("shape", 0).getPart("xHead.shape", 0)
+ xcone.bottomRadius.setValue(float(hs))
+ ycone = self.vframe.getPart("shape", 0).getPart("yHead.shape", 0)
+ ycone.bottomRadius.setValue(float(hs))
+ zcone = self.vframe.getPart("shape", 0).getPart("zHead.shape", 0)
+ zcone.bottomRadius.setValue(float(hs))
+ elif prop == "LineWidth":
+ lw = vp.getPropertyByName("LineWidth")
+ lwstring = "lineWidth {0}".format(lw)
+ ax = self.vframe.getPart("shape", 0)
+ ax.set("xAxis.appearance.drawStyle", lwstring)
+ ax.set("yAxis.appearance.drawStyle", lwstring)
+ ax.set("zAxis.appearance.drawStyle", lwstring)
+
+ def __getstate__(self):
+ return None
+
+ def __setstate__(self, state):
+ pass
+
+
+class ViewProviderPartFrame(ViewProviderFrame):
+ """View provider to the part frame."""
+ def updateData(self, fp, prop):
+ if prop == "Placement":
+ parentpl = fp.getPropertyByName("Part").Placement
+ localpl = fp.Placement
+ pl = parentpl.multiply(localpl)
+ self.transform.translation = (pl.Base.x,
+ pl.Base.y,
+ pl.Base.z)
+ self.transform.rotation = pl.Rotation.Q
+
+
+class ViewProviderFeatureFrame(ViewProviderFrame):
+ """View provider to the feature frames."""
+ def updateData(self, fp, prop):
+ if prop == "Placement":
+ parentpl = fp.getPropertyByName("Part").Placement
+ featurepl = fp.getPropertyByName("FeaturePlacement")
+ localpl = fp.Placement
+ pl = parentpl.multiply(featurepl.multiply(localpl))
+ self.transform.translation = (pl.Base.x,
+ pl.Base.y,
+ pl.Base.z)
+ self.transform.rotation = pl.Rotation.Q
+
+
+###################################################################
+# Base functions
+###################################################################
+
+def makeFrame(placement=FreeCAD.Placement()):
+ obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "Frame")
+ Frame(obj)
+ if FreeCAD.GuiUp:
+ ViewProviderFrame(obj.ViewObject)
+ return obj
+
+
+def makePartFrame(part):
+ obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "PartFrame")
+ PartFrame(obj, part)
+ if FreeCAD.GuiUp:
+ ViewProviderPartFrame(obj.ViewObject)
+ return obj
+
+
+def makeFeatureFrame(part, featurepl):
+ obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "FeatureFrame")
+ FeatureFrame(obj, part, featurepl)
+ if FreeCAD.GuiUp:
+ ViewProviderFeatureFrame(obj.ViewObject)
+ return obj
+
+
+def makeAllPartFrames():
+ dc = FreeCAD.activeDocument()
+ for part in dc.Objects:
+ if isinstance(part, Part.Feature):
+ pf = makePartFrame(part)
+ pf.Label = "Frame"+str(part.Label)
+
+
+def spawnFeatureFrameCreator():
+ ffpanel = FeatureFramePanel()
+ FreeCADGui.Control.showDialog(ffpanel)
+
+
+###################################################################
+# GUI Related
+###################################################################
+uidir = os.path.join(FreeCAD.getUserAppDataDir(),
+ "Mod", __workbenchname__, "UI")
+icondir = os.path.join(uidir, "icons")
+
+ARTools.spawnClassCommand("FrameCommand",
+ makeFrame,
+ {"Pixmap": str(os.path.join(icondir, "frame.svg")),
+ "MenuText": "Make a free frame",
+ "ToolTip": "Make a freestanding reference frame."})
+
+ARTools.spawnClassCommand("AllPartFramesCommand",
+ makeAllPartFrames,
+ {"Pixmap": str(os.path.join(icondir, "allpartframes.svg")),
+ "MenuText": "All part frames",
+ "ToolTip": "Make all part frames."})
+ARTools.spawnClassCommand("FeatureFrameCommand",
+ spawnFeatureFrameCreator,
+ {"Pixmap": str(os.path.join(icondir, "featureframecreator.svg")),
+ "MenuText": "Feature frame creator",
+ "ToolTip": "Create a feature frame on selected primitive."})
+
+
+###################################################################
+# GUI buttons
+###################################################################
+class FeatureFramePanel:
+ """Spawn panel choices for a feature."""
+ def __init__(self):
+ selected = FreeCADGui.Selection.getSelectionEx()
+ # Check selection
+ if len(selected) == 1:
+ selected = selected[0]
+ self.selected = selected
+ else:
+ FreeCAD.Console.PrintError("Multipart selection not available.")
+ self.reject()
+
+ if not selected.HasSubObjects:
+ FreeCAD.Console.PrintError("Part selected not feature.")
+ self.reject()
+ elif not len(selected.SubObjects) == 1:
+ FreeCAD.Console.PrintError("Multifeature selection not available")
+ self.reject()
+
+ # Choices related to selection
+ so_desc = ARTools.describeSubObject(selected.SubObjects[0])
+ self.so_desc = so_desc
+ shape_choices = {
+ "Vertex": [],
+ "Edge": ["PointOnEdge"],
+ "Face": ["PointOnSurface"]
+ }
+ prim_choices = {
+ "ArcOfCircle": ["Center"],
+ "ArcOfEllipse": ["Center"],
+ "ArcOfHyperBola": ["Center"],
+ "ArcOfParabola": ["Center"],
+ "BSplineCurve": ["Center"],
+ "BezierCurve": ["Center"],
+ "Circle": ["Center"],
+ "Ellipse": ["Center"],
+ "Hyperbola": ["Center"],
+ "Parabola": ["Center"],
+ "Line": [],
+ "BSplineSurface": ["Center"],
+ "BezierSurface": ["Center"],
+ "Cylinder": ["PointOnCenterline"],
+ "Plane": ["Center"],
+ "Sphere": ["Center"],
+ "Toroid": ["Center"],
+ "Cone": ["PointOnCenterline"]
+ }
+ self.choices = ["PickedPoint"]
+ self.choices = self.choices + shape_choices[so_desc[1]]
+ self.choices = self.choices + prim_choices[so_desc[0]]
+ # Setting up QT form
+ uiform_path = os.path.join(uidir, "FeatureFrameCreator.ui")
+ self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
+ self.form.ChoicesBox.addItems(self.choices)
+ self.form.PickedTypeLabel.setText(so_desc[0])
+ QtCore.QObject.connect(self.form.ChoicesBox,
+ QtCore.SIGNAL("currentIndexChanged(QString)"),
+ self.choiceChanged)
+ # Setting up relevant illustrations
+ self.scenes = {}
+ for choice in self.choices:
+ sc = QtGui.QGraphicsScene()
+ icon = str(os.path.join(icondir, choice+".svg"))
+ sc.addItem(QtSvg.QGraphicsSvgItem(icon))
+ self.scenes[choice] = sc
+ self.choiceChanged(self.form.ChoicesBox.currentText())
+
+ def choiceChanged(self, choice):
+ if choice in self.scenes.keys():
+ self.form.Preview.setScene(self.scenes[choice])
+
+ def accept(self):
+ sel_choice = self.form.ChoicesBox.currentText()
+ paneldict = {"PickedPoint": PickedPointPanel,
+ "PointOnEdge": PointOnEdgePanel,
+ "PointOnSurface": PointOnSurfacePanel,
+ "Center": CenterPanel,
+ "PointOnCenterline": PointOnCenterlinePanel}
+ new_panel = paneldict[sel_choice](self.selected, self.so_desc)
+ FreeCADGui.Control.closeDialog()
+ FreeCADGui.Control.showDialog(new_panel)
+
+ def reject(self):
+ FreeCADGui.Control.closeDialog()
+
+
+class BaseFeaturePanel(object):
+ """Base feature panel to be inherited from."""
+ def __init__(self, selected, so_desc):
+ # Handle selected and FF placement
+ self.selected = selected
+ self.so_desc = so_desc
+ # Connect offset to spinboxes
+ QtCore.QObject.connect(self.form.XBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.offsetChanged)
+ QtCore.QObject.connect(self.form.YBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.offsetChanged)
+ QtCore.QObject.connect(self.form.ZBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.offsetChanged)
+ QtCore.QObject.connect(self.form.RollBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.offsetChanged)
+ QtCore.QObject.connect(self.form.PitchBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.offsetChanged)
+ QtCore.QObject.connect(self.form.YawBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.offsetChanged)
+ QtCore.QObject.connect(self.form.ScaleBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.scaleChanged)
+
+ def createFrame(self):
+ self.fframe = makeFeatureFrame(self.selected.Object, self.local_ffpl)
+ self.fframe.PrimitiveType = self.so_desc[0]
+ self.fframe.ShapeType = self.so_desc[1]
+
+ def scaleChanged(self):
+ scale = self.form.ScaleBox.value()
+ self.fframe.ViewObject.Scale = scale
+
+ def offsetChanged(self):
+ disp = FreeCAD.Vector(self.form.XBox.value(),
+ self.form.YBox.value(),
+ self.form.ZBox.value())
+ rot = FreeCAD.Rotation(self.form.YawBox.value(),
+ self.form.PitchBox.value(),
+ self.form.RollBox.value())
+ offset = FreeCAD.Placement(disp, rot)
+ self.fframe.Placement = offset
+
+ def accept(self):
+ framelabel = self.form.FrameLabelField.toPlainText()
+ if not len(framelabel) == 0:
+ self.fframe.Label = framelabel
+ # self.storeFeatureProperties()
+ FreeCADGui.Control.closeDialog()
+
+ def reject(self):
+ FreeCAD.activeDocument().removeObject(self.fframe.Name)
+ FreeCADGui.Control.closeDialog()
+
+
+class PickedPointPanel(BaseFeaturePanel):
+ """Create a feature frame at the picked point."""
+ # Not very clever. It just places the frame with default rotation.
+ def __init__(self, selected, so_desc):
+ uiform_path = os.path.join(uidir, "FramePlacer.ui")
+ self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
+ BaseFeaturePanel.__init__(self, selected, so_desc)
+ parent_pl = selected.Object.Placement
+ abs_pl = FreeCAD.Placement(selected.PickedPoints[0],
+ FreeCAD.Rotation())
+ self.local_ffpl = parent_pl.inverse().multiply(abs_pl)
+ self.createFrame()
+ self.fframe.Positioning = "PickedPoint"
+
+
+class PointOnEdgePanel(BaseFeaturePanel):
+ """Create a feature frame on an edge."""
+ def __init__(self, selected, so_desc):
+ uiform_path = os.path.join(uidir, "FramePlacer.ui")
+ self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
+ # Enable the first parameter
+ self.form.VLabel.setEnabled(True)
+ self.form.VLabel.setVisible(True)
+ self.form.VLabel.setText("u")
+ self.form.VBox.setEnabled(True)
+ self.form.VBox.setVisible(True)
+ QtCore.QObject.connect(self.form.VBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.parameterChanged)
+
+ # Enable percentage or param selection
+ self.form.OptionsLabel.setEnabled(True)
+ self.form.OptionsLabel.setVisible(True)
+ self.form.OptionsLabel.setText("Arc param.")
+ self.form.OptionsBox.setEnabled(True)
+ self.form.OptionsBox.setVisible(True)
+ self.form.OptionsBox.addItems(["mm", "%"])
+ QtCore.QObject.connect(self.form.OptionsBox,
+ QtCore.SIGNAL("currentIndexChanged(QString)"),
+ self.choiceChanged)
+ BaseFeaturePanel.__init__(self, selected, so_desc)
+
+ # Place the frame wherever the values are atm
+ self.local_ffpl = FreeCAD.Placement()
+ self.createFrame()
+ self.fframe.Positioning = "PointOnEdge"
+ self.choiceChanged(self.form.OptionsBox.currentText())
+ self.parameterChanged()
+
+ def parameterChanged(self):
+ value = self.form.VBox.value()
+ if self.form.OptionsBox.currentText() == "%":
+ value = self.p2mm(value)
+ edge = self.selected.SubObjects[0]
+ point = edge.valueAt(value)
+ tangentdir = edge.tangentAt(value)
+ rot = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0),
+ tangentdir)
+ abs_ffpl = FreeCAD.Placement(point, rot)
+ parent_pl = self.selected.Object.Placement
+ self.local_ffpl = parent_pl.inverse().multiply(abs_ffpl)
+ self.fframe.FeaturePlacement = self.local_ffpl
+ # force recompute of placement?
+ self.fframe.Placement = self.fframe.Placement
+
+ def choiceChanged(self, choice):
+ value = self.form.VBox.value()
+ if choice == "mm":
+ value = self.p2mm(value)
+ self.form.VBox.setSuffix("mm")
+ parameter_range = self.selected.SubObjects[0].ParameterRange
+ self.form.VBox.setRange(*parameter_range)
+ self.form.VBox.setSingleStep(0.1)
+ elif choice == "%":
+ value = self.mm2p(value)
+ self.form.VBox.setSuffix("%")
+ self.form.VBox.setRange(0, 100.0)
+ self.form.VBox.setSingleStep(1.0)
+ self.form.VBox.setValue(value)
+
+ def p2mm(self, value):
+ parameter_range = self.selected.SubObjects[0].ParameterRange
+ delta = parameter_range[1] - parameter_range[0]
+ return 0.01*value*delta + parameter_range[0]
+
+ def mm2p(self, value):
+ parameter_range = self.selected.SubObjects[0].ParameterRange
+ delta = parameter_range[1] - parameter_range[0]
+ return 100.0*(value - parameter_range[0])/delta
+
+
+class PointOnSurfacePanel(BaseFeaturePanel):
+ """Create a feature on a surface."""
+ def __init__(self, selected, so_desc):
+ uiform_path = os.path.join(uidir, "FramePlacer.ui")
+ self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
+ # Enable both parameters
+ self.form.ULabel.setVisible(True)
+ self.form.VLabel.setVisible(True)
+ self.form.UBox.setVisible(True)
+ self.form.VBox.setVisible(True)
+ QtCore.QObject.connect(self.form.VBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.parameterChanged)
+ QtCore.QObject.connect(self.form.UBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.parameterChanged)
+ # Enable percentage or param selection
+ self.form.OptionsLabel.setEnabled(True)
+ self.form.OptionsLabel.setVisible(True)
+ self.form.OptionsLabel.setText("Surf. param.")
+ self.form.OptionsBox.setEnabled(True)
+ self.form.OptionsBox.setVisible(True)
+ self.form.OptionsBox.addItems(["mm", "%"])
+ QtCore.QObject.connect(self.form.OptionsBox,
+ QtCore.SIGNAL("currentIndexChanged(QString)"),
+ self.choiceChanged)
+ BaseFeaturePanel.__init__(self, selected, so_desc)
+
+ # Place the frame wherever the values are atm
+ self.local_ffpl = FreeCAD.Placement()
+ self.createFrame()
+ self.fframe.Positioning = "PointOnSurface"
+ self.choiceChanged(self.form.OptionsBox.currentText())
+ self.parameterChanged()
+
+ def parameterChanged(self):
+ value = (self.form.UBox.value(), self.form.VBox.value())
+ if self.form.OptionsBox.currentText() == "%":
+ value = self.p2mm(value)
+ face = self.selected.SubObjects[0]
+ point = face.valueAt(*value)
+ normaldir = face.normalAt(*value)
+ rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1),
+ normaldir)
+ abs_ffpl = FreeCAD.Placement(point, rotation)
+ parent_pl = self.selected.Object.Placement
+ self.local_ffpl = parent_pl.inverse().multiply(abs_ffpl)
+ self.fframe.FeaturePlacement = self.local_ffpl
+ # Force recompute of placement
+ self.fframe.Placement = self.fframe.Placement
+
+ def choiceChanged(self, choice):
+ value = (self.form.UBox.value(), self.form.VBox.value())
+ if choice == "mm":
+ value = self.p2mm(value)
+ parameter_range = self.selected.SubObjects[0].ParameterRange
+ self.form.UBox.setRange(parameter_range[0], parameter_range[1])
+ self.form.UBox.setSuffix("mm")
+ self.form.UBox.setSingleStep(0.1)
+ self.form.VBox.setRange(parameter_range[2], parameter_range[3])
+ self.form.VBox.setSuffix("mm")
+ self.form.VBox.setSingleStep(0.1)
+ elif choice == "%":
+ value = self.mm2p(value)
+ self.form.UBox.setRange(0.0, 100.0)
+ self.form.UBox.setSuffix("%")
+ self.form.VBox.setRange(0.0, 100.0)
+ self.form.UBox.setSingleStep(1.0)
+ self.form.VBox.setSuffix("%")
+ self.form.VBox.setSingleStep(1.0)
+ self.form.UBox.setValue(value[0])
+ self.form.VBox.setValue(value[1])
+
+
+ def p2mm(self, value):
+ parameter_range = self.selected.SubObjects[0].ParameterRange
+ delta = [parameter_range[1] - parameter_range[0],
+ parameter_range[3] - parameter_range[2]]
+ u = 0.01*value[0]*delta[0] + parameter_range[0]
+ v = 0.01*value[1]*delta[1] + parameter_range[2]
+ return (u, v)
+
+ def mm2p(self, value):
+ parameter_range = self.selected.SubObjects[0].ParameterRange
+ delta = [parameter_range[1] - parameter_range[0],
+ parameter_range[3] - parameter_range[2]]
+ u = 100.0*(value[0] - parameter_range[0])/delta[0]
+ v = 100.0*(value[1] - parameter_range[2])/delta[1]
+ return (u, v)
+
+
+class CenterPanel(BaseFeaturePanel):
+ """Create a feature frame on center."""
+ def __init__(self, selected, so_desc):
+ uiform_path = os.path.join(uidir, "FramePlacer.ui")
+ self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
+ BaseFeaturePanel.__init__(self, selected, so_desc)
+ edge_curve_list = ["ArcOfCircle",
+ "ArcOfEllipse",
+ "ArcOfHyperbola",
+ "ArcOfParabola",
+ "Circle",
+ "Ellipse",
+ "Hyperbola",
+ "Parabola"]
+ face_surf_list = ["Sphere",
+ "Toroid"]
+ if so_desc[0] in edge_curve_list:
+ edge = selected.SubObjects[0]
+ axis = edge.Curve.Axis
+ rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1),
+ axis)
+ center_point = edge.Curve.Center
+ elif so_desc[0] in face_surf_list:
+ face = selected.SubObjects[0]
+ axis = face.Surface.Axis
+ rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1),
+ axis)
+ center_point = face.Surface.Center
+ else:
+ rotation = FreeCAD.Rotation()
+ center_point = selected.SubObjects[0].CenterOfMass
+ parent_pl = selected.Object.Placement
+ abs_pl = FreeCAD.Placement(center_point,
+ rotation)
+ self.local_ffpl = parent_pl.inverse().multiply(abs_pl)
+ self.createFrame()
+ self.fframe.Positioning = "Center"
+
+
+class PointOnCenterlinePanel(BaseFeaturePanel):
+ """Create a point on centerline of primitive."""
+ def __init__(self, selected, so_desc):
+ uiform_path = os.path.join(uidir, "FramePlacer.ui")
+ self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
+ BaseFeaturePanel.__init__(self, selected, so_desc)
+ # Enable the along line parameter
+ self.form.VLabel.setVisible(True)
+ self.form.VLabel.setText("u")
+ self.form.VBox.setVisible(True)
+ QtCore.QObject.connect(self.form.VBox,
+ QtCore.SIGNAL("valueChanged(double)"),
+ self.parameterChanged)
+ # Enable percentage of param selection
+ self.form.OptionsLabel.setVisible(True)
+ self.form.OptionsLabel.setText("Line param.")
+ self.form.OptionsBox.setVisible(True)
+ self.form.OptionsBox.addItems(["mm", "%"])
+ QtCore.QObject.connect(self.form.OptionsBox,
+ QtCore.SIGNAL("currentIndexChanged(QString)"),
+ self.choiceChanged)
+ # Place the frame wherever the values are atm
+ self.local_ffpl = FreeCAD.Placement()
+ self.createFrame()
+ self.fframe.Positioning = "PointOnCenterline"
+ self.parameterChanged()
+
+ def parameterChanged(self):
+ value = self.form.VBox.value()
+ if self.form.OptionsBox.currentText() == "%":
+ value = self.p2mm(value)
+ displacement_pl = FreeCAD.Placement(FreeCAD.Vector(0, 0, value),
+ FreeCAD.Rotation())
+ # Find the center
+ axis = self.selected.SubObjects[0].Surface.Axis
+ rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1),
+ axis)
+ center_point = self.selected.SubObjects[0].Surface.Center
+ center_pl = FreeCAD.Placement(center_point, rotation)
+ abs_ffpl = center_pl.multiply(displacement_pl)
+ parent_pl = self.selected.Object.Placement
+ self.local_ffpl = parent_pl.inverse().multiply(abs_ffpl)
+ self.fframe.FeaturePlacement = self.local_ffpl
+ # force recompute of placement
+ self.fframe.Placement = self.fframe.Placement
+ FreeCAD.Console.PrintMessage("paramChanged:"+str(value)+"\n")
+
+ def choiceChanged(self, choice):
+ FreeCAD.Console.PrintMessage("choiceChanged\n")
+ value = self.form.VBox.value()
+ FreeCAD.Console.PrintMessage("preval:"+str(value)+"\n")
+ if choice == "mm":
+ value = self.p2mm(value)
+ self.form.VBox.setSuffix("mm")
+ parameter_range = self.selected.SubObjects[0].ParameterRange[2:]
+ self.form.VBox.setRange(*parameter_range)
+ self.form.VBox.setSingleStep(0.1)
+ elif choice == "%":
+ value = self.mm2p(value)
+ self.form.VBox.setSuffix("%")
+ self.form.VBox.setRange(0, 100)
+ self.form.VBox.setSingleStep(1.0)
+ self.form.VBox.setValue(value)
+ FreeCAD.Console.PrintMessage("postval:"+str(value)+"\n")
+
+ def p2mm(self, value):
+ parameter_range = self.selected.SubObjects[0].ParameterRange[2:]
+ delta = parameter_range[1] - parameter_range[0]
+ return 0.01*value*delta + parameter_range[0]
+
+ def mm2p(self, value):
+ parameter_range = self.selected.SubObjects[0].ParameterRange[2:]
+ delta = parameter_range[1] - parameter_range[0]
+ return 100.0*(value - parameter_range[0])/delta
diff --git a/ARTools.py b/ARTools.py
new file mode 100644
index 0000000..64afad3
--- /dev/null
+++ b/ARTools.py
@@ -0,0 +1,119 @@
+import FreeCAD
+import Part
+if FreeCAD.GuiUp:
+ import FreeCADGui
+
+
+__title__ = "ARTools"
+__author__ = "Mathias Hauan Arbo"
+__workbenchname__ = "ARBench"
+__version__ = "0.1"
+__url__ = "https://github.com/mahaarbo/ARBench"
+__doc__ = """"""
+
+
+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."""
+ return [vec.x*scale, vec.y*scale, vec.z*scale]
+
+
+def matrix2list(mat, scale=1e-3):
+ """Gives the transformation matrix as a list, set scale 1 to get in mm."""
+ return [[mat.A11, mat.A12, mat.A13, mat.A14*scale],
+ [mat.A21, mat.A22, mat.A23, mat.A24*scale],
+ [mat.A31, mat.A32, mat.A33, mat.A34*scale],
+ [mat.A41, mat.A42, mat.A43, mat.A44]]
+
+
+def placement2axisvec(pl):
+ """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),
+ "rotation": {"axis": vector2list(pl.Rotation.Axis, scale=1),
+ "angle": pl.Rotation.Angle}}
+
+
+def describeSubObject(subobj):
+ """Returns PrimitiveType, ShapeType."""
+ if isinstance(subobj, Part.Vertex):
+ return "Vertex", "Vertex"
+ elif isinstance(subobj, Part.Edge):
+ if isinstance(subobj.Curve, Part.Arc):
+ return "Arc", "Edge"
+ elif isinstance(subobj.Curve, Part.ArcOfCircle):
+ return "ArcOfCircle", "Edge"
+ elif isinstance(subobj.Curve, Part.ArcOfEllipse):
+ return "ArcOfEllipse", "Edge"
+ elif isinstance(subobj.Curve, Part.ArcOfHyperbola):
+ return "ArcOfHyperbola", "Edge"
+ elif isinstance(subobj.Curve, Part.ArcOfParabola):
+ return "ArcOfParabola", "Edge"
+ elif isinstance(subobj.Curve, Part.BSplineCurve):
+ return "BSplineCurve", "Edge"
+ elif isinstance(subobj.Curve, Part.BezierCurve):
+ return "BezierCurve", "Edge"
+ elif isinstance(subobj.Curve, Part.Circle):
+ return "Circle", "Edge"
+ elif isinstance(subobj.Curve, Part.Ellipse):
+ return "Ellipse", "Edge"
+ elif isinstance(subobj.Curve, Part.Hyperbola):
+ return "Hyperbola", "Edge"
+ elif isinstance(subobj.Curve, Part.Line):
+ return "Line", "Edge"
+ elif isinstance(subobj.Curve, Part.Parabola):
+ return "Parabola", "Edge"
+ else:
+ FreeCAD.Console.PrintError("Unknown edge type")
+ elif isinstance(subobj, Part.Face):
+ if isinstance(subobj.Surface, Part.BSplineSurface):
+ return "BSplineSurface", "Face"
+ elif isinstance(subobj.Surface, Part.BezierSurface):
+ return "BezierSurface", "Face"
+ elif isinstance(subobj.Surface, Part.Cylinder):
+ return "Cylinder", "Face"
+ elif isinstance(subobj.Surface, Part.Plane):
+ return "Plane", "Face"
+ elif isinstance(subobj.Surface, Part.Sphere):
+ return "Sphere", "Face"
+ elif isinstance(subobj.Surface, Part.Toroid):
+ return "Toroid", "Face"
+ elif isinstance(subobj.Surface, Part.Cone):
+ return "Cone", "Face"
+ else:
+ FreeCAD.Console.PrintError("Unknown surface type")
+ # Better strategy desirable for the following:
+ elif isinstance(subobj, Part.Wire):
+ return "Wire", "Wire"
+ elif isinstance(subobj, Part.Shell):
+ return "Shell", "Shell"
+ elif isinstance(subobj, Part.Solid):
+ return "Solid", "Solid"
+ elif isinstance(subobj, Part.Compsolid):
+ return "Compsolid", "Compsolid"
+ elif isinstance(subobj, Part.Compound):
+ return "Compound", "Compound"
+ else:
+ FreeCAD.Console.PrintError("Unable to identify subobject.")
+
+
+def closeToZero(a, tol=1e-10):
+ return abs(a) < tol
+
+
+def spawnClassCommand(classname, function, resources):
+ """
+ Commands, or buttons, are tedious to write. So this function spawns
+ one if the function to be executed takes no arguments.
+ Example usage:
+ spawnClassCommand("testcommand", testfunc,
+ {"Pixmap":"", "MenuText":"menutext","ToolTip":"tooltiptext"})
+ then add "testcommand" to commandlist in InitGui.py"""
+ def Activated(s):
+ function()
+
+ def GetResources(s):
+ return resources
+ CommandClass = type("classname", (object,), {"Activated": Activated,
+ "GetResources": GetResources})
+ FreeCADGui.addCommand(classname, CommandClass())
diff --git a/Init.py b/Init.py
new file mode 100644
index 0000000..2b677df
--- /dev/null
+++ b/Init.py
@@ -0,0 +1 @@
+#I made this?
diff --git a/InitGui.py b/InitGui.py
new file mode 100644
index 0000000..f4b984c
--- /dev/null
+++ b/InitGui.py
@@ -0,0 +1,36 @@
+class ARBench(Workbench):
+ MenuText = "ARBench"
+ ToolTip = "Annotation for Robotics workbench"
+ Icon = """"""
+
+ def __init__(self):
+ import os
+ self.Icon = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", "ARBench", "UI", "icons", "frame.svg")
+
+ def Initialize(self):
+ """This function is executed when FreeCAD starts"""
+ import ARFrames
+ self.framecommands = ["FrameCommand",
+ "AllPartFramesCommand",
+ "FeatureFrameCommand"]
+ self.appendToolbar("AR Frames", self.framecommands)
+
+ def Activated(self):
+ """This function is executed when the workbench is activated."""
+ #
+ return
+
+ def Deactivated(self):
+ """This function is executed when the workbench is deactivated."""
+ #
+ return
+
+ def ContextMenu(self, recipient):
+ """This is execcuted whenever the user right-clicks on screen."""
+ pass
+
+ def GetClassName(self):
+ #This function is mandatory if this is a full python workbench
+ return "Gui::PythonWorkbench"
+
+Gui.addWorkbench(ARBench())
diff --git a/UI/FeatureFrameCreator.ui b/UI/FeatureFrameCreator.ui
new file mode 100644
index 0000000..1eb41b6
--- /dev/null
+++ b/UI/FeatureFrameCreator.ui
@@ -0,0 +1,73 @@
+
+
+ FeatureFrameCreator
+
+
+
+ 0
+ 0
+ 235
+ 238
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 235
+ 238
+
+
+
+
+ 235
+ 238
+
+
+
+ Feature Frame
+
+
+
+
+ 30
+ 29
+ 170
+ 151
+
+
+
+
+
+
+ 30
+ 190
+ 171
+ 27
+
+
+
+
+
+
+ 35
+ 10
+ 161
+ 20
+
+
+
+ TextLabel
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
diff --git a/UI/FramePlacer.ui b/UI/FramePlacer.ui
new file mode 100644
index 0000000..335f3a7
--- /dev/null
+++ b/UI/FramePlacer.ui
@@ -0,0 +1,520 @@
+
+
+ dialog
+
+
+
+ 0
+ 0
+ 299
+ 292
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 299
+ 292
+
+
+
+ Part Frame Creator
+
+
+
+
+ 52
+ 85
+ 103
+ 27
+
+
+
+ Offset in part frame x direction
+
+
+ mm
+
+
+ -1000.000000000000000
+
+
+ 1000.000000000000000
+
+
+
+
+
+ 52
+ 118
+ 103
+ 27
+
+
+
+ offset in part frame z direction
+
+
+ mm
+
+
+ -1000.000000000000000
+
+
+ 1000.000000000000000
+
+
+
+
+
+ 52
+ 151
+ 103
+ 27
+
+
+
+ offset in part frame z direction
+
+
+ mm
+
+
+ 2
+
+
+ -1000.000000000000000
+
+
+ 1000.000000000000000
+
+
+
+
+
+ 9
+ 85
+ 16
+ 17
+
+
+
+ x
+
+
+ XBox
+
+
+
+
+
+ 9
+ 118
+ 16
+ 17
+
+
+
+ y
+
+
+ YBox
+
+
+
+
+
+ 9
+ 151
+ 16
+ 17
+
+
+
+ z
+
+
+ ZBox
+
+
+
+
+
+ 161
+ 85
+ 24
+ 17
+
+
+
+ roll offset from part frame
+
+
+ roll
+
+
+ RollBox
+
+
+
+
+
+ 161
+ 118
+ 35
+ 17
+
+
+
+ pitch offset from part frame
+
+
+ pitch
+
+
+ PitchBox
+
+
+
+
+
+ 161
+ 151
+ 29
+ 17
+
+
+
+ yaw offset from part frame
+
+
+ yaw
+
+
+ YawBox
+
+
+
+
+
+ 228
+ 85
+ 61
+ 27
+
+
+
+ roll offset from part frame
+
+
+ °
+
+
+ 1
+
+
+ 360.000000000000000
+
+
+
+
+
+ 228
+ 118
+ 61
+ 27
+
+
+
+ pitch offset from part frame
+
+
+ °
+
+
+ 1
+
+
+ 360.000000000000000
+
+
+
+
+
+ 228
+ 151
+ 61
+ 27
+
+
+
+ yaw offset from part frame
+
+
+ °
+
+
+ 1
+
+
+ 360.000000000000000
+
+
+
+
+
+ 94
+ 9
+ 195
+ 31
+
+
+
+
+ 0
+ 0
+
+
+
+ Label of the frame
+
+
+ Label of the new frame
+
+
+ Label of the new frame, must be unique
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+
+
+
+ 9
+ 9
+ 79
+ 31
+
+
+
+ Frame label
+
+
+
+
+
+ 9
+ 48
+ 281
+ 31
+
+
+
+ Define an offset from the part frame if needed
+
+
+ Offset from part frame
+
+
+ Qt::AlignCenter
+
+
+
+
+
+ 160
+ 180
+ 71
+ 21
+
+
+
+ Scale of the axis arrows
+
+
+ Scale of the axis arrows
+
+
+ Scale of the axis arrows
+
+
+ Axis scale
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+
+
+ 230
+ 180
+ 60
+ 27
+
+
+
+ Scale of the axis arrows
+
+
+ Scale of the axis arrows
+
+
+ Scale of the axis arrows
+
+
+ 0.100000000000000
+
+
+ 2.000000000000000
+
+
+ 0.010000000000000
+
+
+ 0.110000000000000
+
+
+
+
+ true
+
+
+
+ 20
+ 220
+ 66
+ 31
+
+
+
+ false
+
+
+ Options
+
+
+ OptionsBox
+
+
+
+
+ true
+
+
+
+ 210
+ 220
+ 78
+ 27
+
+
+
+ false
+
+
+
+
+ true
+
+
+
+ 100
+ 250
+ 62
+ 27
+
+
+
+ false
+
+
+
+
+ true
+
+
+
+ 220
+ 250
+ 62
+ 27
+
+
+
+ false
+
+
+
+
+ true
+
+
+
+ 80
+ 250
+ 16
+ 31
+
+
+
+ false
+
+
+ u
+
+
+ UBox
+
+
+
+
+ true
+
+
+
+ 200
+ 250
+ 16
+ 31
+
+
+
+ false
+
+
+ v
+
+
+ VBox
+
+
+
+
+ true
+
+
+
+ 20
+ 256
+ 51
+ 21
+
+
+
+ false
+
+
+ Coords:
+
+
+
+
+
+
diff --git a/UI/InsertTaskCreator.ui b/UI/InsertTaskCreator.ui
new file mode 100644
index 0000000..f2830c3
--- /dev/null
+++ b/UI/InsertTaskCreator.ui
@@ -0,0 +1,262 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 323
+ 325
+
+
+
+
+ 316
+ 325
+
+
+
+ Dialog
+
+
+
+
+ 10
+ 20
+ 71
+ 21
+
+
+
+ Task Label
+
+
+
+
+
+ 150
+ 290
+ 161
+ 27
+
+
+
+ Get from selection
+
+
+
+
+
+ 100
+ 20
+ 211
+ 27
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ 10
+ 50
+ 31
+ 21
+
+
+
+ Hole
+
+
+
+
+
+ 10
+ 130
+ 31
+ 21
+
+
+
+ Peg
+
+
+
+
+
+ 50
+ 70
+ 41
+ 21
+
+
+
+ Part:
+
+
+
+
+
+ 50
+ 90
+ 41
+ 21
+
+
+
+ Face:
+
+
+
+
+
+ 50
+ 150
+ 41
+ 21
+
+
+
+ Part:
+
+
+
+
+
+ 50
+ 170
+ 41
+ 21
+
+
+
+ Face:
+
+
+
+
+
+ 100
+ 70
+ 231
+ 21
+
+
+
+ PartLabel
+
+
+
+
+
+ 100
+ 90
+ 231
+ 21
+
+
+
+ FaceID
+
+
+
+
+
+ 100
+ 170
+ 231
+ 21
+
+
+
+ FaceID
+
+
+
+
+
+ 100
+ 150
+ 231
+ 21
+
+
+
+ PartLabel
+
+
+
+
+
+ 100
+ 110
+ 231
+ 21
+
+
+
+ Feature Description
+
+
+
+
+
+ 30
+ 110
+ 61
+ 21
+
+
+
+ Feature:
+
+
+
+
+
+ 100
+ 190
+ 231
+ 21
+
+
+
+ Feature Description
+
+
+
+
+
+ 30
+ 190
+ 61
+ 21
+
+
+
+ Feature:
+
+
+
+
+
+ 10
+ 290
+ 131
+ 27
+
+
+
+ Animate
+
+
+
+
+
+
diff --git a/UI/InsertTaskCreator.ui~ b/UI/InsertTaskCreator.ui~
new file mode 100644
index 0000000..3338ac2
--- /dev/null
+++ b/UI/InsertTaskCreator.ui~
@@ -0,0 +1,401 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 323
+ 325
+
+
+
+
+ 316
+ 325
+
+
+
+ Dialog
+
+
+
+
+ 10
+ 20
+ 71
+ 21
+
+
+
+ Task Label
+
+
+
+
+
+ 150
+ 290
+ 161
+ 27
+
+
+
+ Get from selection
+
+
+
+
+
+ 100
+ 20
+ 211
+ 27
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ 10
+ 50
+ 31
+ 21
+
+
+
+ Hole
+
+
+
+
+
+ 10
+ 130
+ 31
+ 21
+
+
+
+ Peg
+
+
+
+
+
+ 50
+ 70
+ 41
+ 21
+
+
+
+ Part:
+
+
+
+
+
+ 50
+ 90
+ 41
+ 21
+
+
+
+ Face:
+
+
+
+
+
+ 50
+ 150
+ 41
+ 21
+
+
+
+ Part:
+
+
+
+
+
+ 50
+ 170
+ 41
+ 21
+
+
+
+ Face:
+
+
+
+
+
+ 100
+ 70
+ 231
+ 21
+
+
+
+ PartLabel
+
+
+
+
+
+ 100
+ 90
+ 231
+ 21
+
+
+
+ FaceID
+
+
+
+
+
+ 100
+ 170
+ 231
+ 21
+
+
+
+ FaceID
+
+
+
+
+
+ 100
+ 150
+ 231
+ 21
+
+
+
+ PartLabel
+
+
+
+
+
+ 100
+ 110
+ 231
+ 21
+
+
+
+ Feature Description
+
+
+
+
+
+ 30
+ 110
+ 61
+ 21
+
+
+
+ Feature:
+
+
+
+
+
+ 100
+ 190
+ 231
+ 21
+
+
+
+ Feature Description
+
+
+
+
+
+ 30
+ 190
+ 61
+ 21
+
+
+
+ Feature:
+
+
+
+
+
+ 80
+ 250
+ 16
+ 17
+
+
+
+ y
+
+
+ YBox
+
+
+
+
+
+ 80
+ 230
+ 16
+ 17
+
+
+
+ x
+
+
+ XBox
+
+
+
+
+
+ 80
+ 270
+ 16
+ 17
+
+
+
+ z
+
+
+ ZBox
+
+
+
+
+
+ 100
+ 250
+ 61
+ 21
+
+
+
+ offset in part frame z direction
+
+
+
+
+
+ -1.000000000000000
+
+
+ 1.000000000000000
+
+
+ 0.100000000000000
+
+
+
+
+
+ 100
+ 230
+ 61
+ 20
+
+
+
+ Offset in part frame x direction
+
+
+
+
+
+ -1.000000000000000
+
+
+ 1.000000000000000
+
+
+ 0.100000000000000
+
+
+
+
+
+ 100
+ 270
+ 61
+ 20
+
+
+
+ offset in part frame z direction
+
+
+
+
+
+ 2
+
+
+ -1.000000000000000
+
+
+ 1.000000000000000
+
+
+ 0.100000000000000
+
+
+
+
+
+ 10
+ 210
+ 121
+ 21
+
+
+
+ Assembly Axis
+
+
+
+
+
+ 10
+ 290
+ 131
+ 27
+
+
+
+ Animate
+
+
+
+
+
+
diff --git a/UI/PartFrameCreator.ui b/UI/PartFrameCreator.ui
new file mode 100644
index 0000000..335f3a7
--- /dev/null
+++ b/UI/PartFrameCreator.ui
@@ -0,0 +1,520 @@
+
+
+ dialog
+
+
+
+ 0
+ 0
+ 299
+ 292
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 299
+ 292
+
+
+
+ Part Frame Creator
+
+
+
+
+ 52
+ 85
+ 103
+ 27
+
+
+
+ Offset in part frame x direction
+
+
+ mm
+
+
+ -1000.000000000000000
+
+
+ 1000.000000000000000
+
+
+
+
+
+ 52
+ 118
+ 103
+ 27
+
+
+
+ offset in part frame z direction
+
+
+ mm
+
+
+ -1000.000000000000000
+
+
+ 1000.000000000000000
+
+
+
+
+
+ 52
+ 151
+ 103
+ 27
+
+
+
+ offset in part frame z direction
+
+
+ mm
+
+
+ 2
+
+
+ -1000.000000000000000
+
+
+ 1000.000000000000000
+
+
+
+
+
+ 9
+ 85
+ 16
+ 17
+
+
+
+ x
+
+
+ XBox
+
+
+
+
+
+ 9
+ 118
+ 16
+ 17
+
+
+
+ y
+
+
+ YBox
+
+
+
+
+
+ 9
+ 151
+ 16
+ 17
+
+
+
+ z
+
+
+ ZBox
+
+
+
+
+
+ 161
+ 85
+ 24
+ 17
+
+
+
+ roll offset from part frame
+
+
+ roll
+
+
+ RollBox
+
+
+
+
+
+ 161
+ 118
+ 35
+ 17
+
+
+
+ pitch offset from part frame
+
+
+ pitch
+
+
+ PitchBox
+
+
+
+
+
+ 161
+ 151
+ 29
+ 17
+
+
+
+ yaw offset from part frame
+
+
+ yaw
+
+
+ YawBox
+
+
+
+
+
+ 228
+ 85
+ 61
+ 27
+
+
+
+ roll offset from part frame
+
+
+ °
+
+
+ 1
+
+
+ 360.000000000000000
+
+
+
+
+
+ 228
+ 118
+ 61
+ 27
+
+
+
+ pitch offset from part frame
+
+
+ °
+
+
+ 1
+
+
+ 360.000000000000000
+
+
+
+
+
+ 228
+ 151
+ 61
+ 27
+
+
+
+ yaw offset from part frame
+
+
+ °
+
+
+ 1
+
+
+ 360.000000000000000
+
+
+
+
+
+ 94
+ 9
+ 195
+ 31
+
+
+
+
+ 0
+ 0
+
+
+
+ Label of the frame
+
+
+ Label of the new frame
+
+
+ Label of the new frame, must be unique
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+
+
+
+ 9
+ 9
+ 79
+ 31
+
+
+
+ Frame label
+
+
+
+
+
+ 9
+ 48
+ 281
+ 31
+
+
+
+ Define an offset from the part frame if needed
+
+
+ Offset from part frame
+
+
+ Qt::AlignCenter
+
+
+
+
+
+ 160
+ 180
+ 71
+ 21
+
+
+
+ Scale of the axis arrows
+
+
+ Scale of the axis arrows
+
+
+ Scale of the axis arrows
+
+
+ Axis scale
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+
+
+ 230
+ 180
+ 60
+ 27
+
+
+
+ Scale of the axis arrows
+
+
+ Scale of the axis arrows
+
+
+ Scale of the axis arrows
+
+
+ 0.100000000000000
+
+
+ 2.000000000000000
+
+
+ 0.010000000000000
+
+
+ 0.110000000000000
+
+
+
+
+ true
+
+
+
+ 20
+ 220
+ 66
+ 31
+
+
+
+ false
+
+
+ Options
+
+
+ OptionsBox
+
+
+
+
+ true
+
+
+
+ 210
+ 220
+ 78
+ 27
+
+
+
+ false
+
+
+
+
+ true
+
+
+
+ 100
+ 250
+ 62
+ 27
+
+
+
+ false
+
+
+
+
+ true
+
+
+
+ 220
+ 250
+ 62
+ 27
+
+
+
+ false
+
+
+
+
+ true
+
+
+
+ 80
+ 250
+ 16
+ 31
+
+
+
+ false
+
+
+ u
+
+
+ UBox
+
+
+
+
+ true
+
+
+
+ 200
+ 250
+ 16
+ 31
+
+
+
+ false
+
+
+ v
+
+
+ VBox
+
+
+
+
+ true
+
+
+
+ 20
+ 256
+ 51
+ 21
+
+
+
+ false
+
+
+ Coords:
+
+
+
+
+
+
diff --git a/UI/ScrewTaskCreator.ui b/UI/ScrewTaskCreator.ui
new file mode 100644
index 0000000..fc20be0
--- /dev/null
+++ b/UI/ScrewTaskCreator.ui
@@ -0,0 +1,239 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 337
+ 322
+
+
+
+
+ 337
+ 322
+
+
+
+ Dialog
+
+
+
+
+ 20
+ 50
+ 31
+ 21
+
+
+
+ Hole
+
+
+
+
+
+ 60
+ 150
+ 41
+ 21
+
+
+
+ Part:
+
+
+
+
+
+ 60
+ 170
+ 41
+ 21
+
+
+
+ Face:
+
+
+
+
+
+ 110
+ 170
+ 231
+ 21
+
+
+
+ FaceID
+
+
+
+
+
+ 20
+ 20
+ 71
+ 21
+
+
+
+ Task Label
+
+
+
+
+
+ 110
+ 20
+ 211
+ 27
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ 20
+ 290
+ 131
+ 27
+
+
+
+ Animate
+
+
+
+
+
+ 160
+ 290
+ 161
+ 27
+
+
+
+ Get from selection
+
+
+
+
+
+ 110
+ 90
+ 231
+ 21
+
+
+
+ FaceID
+
+
+
+
+
+ 60
+ 70
+ 41
+ 21
+
+
+
+ Part:
+
+
+
+
+
+ 60
+ 90
+ 41
+ 21
+
+
+
+ Face:
+
+
+
+
+
+ 20
+ 130
+ 41
+ 21
+
+
+
+ Screw
+
+
+
+
+
+ 110
+ 70
+ 231
+ 21
+
+
+
+ PartLabel
+
+
+
+
+
+ 110
+ 150
+ 231
+ 21
+
+
+
+ PartLabel
+
+
+
+
+
+ 30
+ 190
+ 66
+ 17
+
+
+
+ Type:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ 110
+ 190
+ 161
+ 17
+
+
+
+ ThreadType
+
+
+
+
+
+
diff --git a/UI/icons/Center.svg b/UI/icons/Center.svg
new file mode 100644
index 0000000..aeddb82
--- /dev/null
+++ b/UI/icons/Center.svg
@@ -0,0 +1,237 @@
+
+
+
+
diff --git a/UI/icons/PickedPoint.svg b/UI/icons/PickedPoint.svg
new file mode 100644
index 0000000..6e0c4b3
--- /dev/null
+++ b/UI/icons/PickedPoint.svg
@@ -0,0 +1,152 @@
+
+
+
+
diff --git a/UI/icons/PointOnCenterline.svg b/UI/icons/PointOnCenterline.svg
new file mode 100644
index 0000000..501db60
--- /dev/null
+++ b/UI/icons/PointOnCenterline.svg
@@ -0,0 +1,275 @@
+
+
+
+
diff --git a/UI/icons/PointOnEdge.svg b/UI/icons/PointOnEdge.svg
new file mode 100644
index 0000000..0edfb5e
--- /dev/null
+++ b/UI/icons/PointOnEdge.svg
@@ -0,0 +1,181 @@
+
+
+
+
diff --git a/UI/icons/PointOnSurface.svg b/UI/icons/PointOnSurface.svg
new file mode 100644
index 0000000..62c8edc
--- /dev/null
+++ b/UI/icons/PointOnSurface.svg
@@ -0,0 +1,44 @@
+
+
+
+
diff --git a/UI/icons/allpartframes.svg b/UI/icons/allpartframes.svg
new file mode 100644
index 0000000..11d63fa
--- /dev/null
+++ b/UI/icons/allpartframes.svg
@@ -0,0 +1,60 @@
+
+
+
+
diff --git a/UI/icons/allpartgroups.svg b/UI/icons/allpartgroups.svg
new file mode 100644
index 0000000..c12ed92
--- /dev/null
+++ b/UI/icons/allpartgroups.svg
@@ -0,0 +1,141 @@
+
+
+
+
diff --git a/UI/icons/featureframecreator.svg b/UI/icons/featureframecreator.svg
new file mode 100644
index 0000000..a5d9ebe
--- /dev/null
+++ b/UI/icons/featureframecreator.svg
@@ -0,0 +1,254 @@
+
+
+
+
diff --git a/UI/icons/frame.svg b/UI/icons/frame.svg
new file mode 100644
index 0000000..6e0c4b3
--- /dev/null
+++ b/UI/icons/frame.svg
@@ -0,0 +1,152 @@
+
+
+
+
diff --git a/UI/icons/inserttask.svg b/UI/icons/inserttask.svg
new file mode 100644
index 0000000..bc562cf
--- /dev/null
+++ b/UI/icons/inserttask.svg
@@ -0,0 +1,49 @@
+
+
+
+
diff --git a/UI/icons/partframe.svg b/UI/icons/partframe.svg
new file mode 100644
index 0000000..4a7acc5
--- /dev/null
+++ b/UI/icons/partframe.svg
@@ -0,0 +1,384 @@
+
+
+
+
diff --git a/UI/icons/parttojson.svg b/UI/icons/parttojson.svg
new file mode 100644
index 0000000..2531c40
--- /dev/null
+++ b/UI/icons/parttojson.svg
@@ -0,0 +1,262 @@
+
+
+
+
diff --git a/UI/icons/placetask.svg b/UI/icons/placetask.svg
new file mode 100644
index 0000000..b99e61b
--- /dev/null
+++ b/UI/icons/placetask.svg
@@ -0,0 +1,66 @@
+
+
diff --git a/UI/icons/plasetask.svg b/UI/icons/plasetask.svg
new file mode 100644
index 0000000..c0ac862
--- /dev/null
+++ b/UI/icons/plasetask.svg
@@ -0,0 +1,66 @@
+
+
diff --git a/UI/icons/screwtask.svg b/UI/icons/screwtask.svg
new file mode 100644
index 0000000..f307cc8
--- /dev/null
+++ b/UI/icons/screwtask.svg
@@ -0,0 +1,48 @@
+
+
diff --git a/UI/icons/taskcreator.svg b/UI/icons/taskcreator.svg
new file mode 100644
index 0000000..49c923c
--- /dev/null
+++ b/UI/icons/taskcreator.svg
@@ -0,0 +1,49 @@
+
+