diff --git a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui
new file mode 100644
index 000000000..480d22d1c
--- /dev/null
+++ b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui
@@ -0,0 +1,422 @@
+
+
+ TaskPanel
+
+
+
+ 0
+ 0
+ 352
+ 525
+
+
+
+
+ 0
+ 400
+
+
+
+ Mill Facing
+
+
+ -
+
+
+ 0
+
+
+
+ true
+
+
+
+ 0
+ 0
+ 334
+ 329
+
+
+
+
+ :/icons/Path-BaseGeometry.svg:/icons/Path-BaseGeometry.svg
+
+
+ Base Geometry
+
+
+
-
+
+
+ Drag to reorder, then update.
+
+
+ QAbstractItemView::DragDrop
+
+
+ Qt::MoveAction
+
+
+ false
+
+
+
+ -
+
+
+ Add item selected in window.
+
+
+ add
+
+
+
+ -
+
+
+ Remove Item selected in list, then update.
+
+
+ Remove
+
+
+
+ -
+
+
+ Update the path with the removed and reordered items.
+
+
+ Update
+
+
+
+ -
+
+
+ All objects will be processed using the same operation properties.
+
+
+ Qt::AutoText
+
+
+ true
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 334
+ 329
+
+
+
+
+ :/icons/Path-Depths.svg:/icons/Path-Depths.svg
+
+
+ Depths
+
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Start Depth
+
+
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Final Depth
+
+
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Finish Depth
+
+
+
+ -
+
+
+ 3
+
+
+ 0.100000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ Step Down
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 334
+ 329
+
+
+
+
+ :/icons/Path-Heights.svg:/icons/Path-Heights.svg
+
+
+ Heights
+
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Safe Height
+
+
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Clearance Height
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 334
+ 329
+
+
+
+ Entry
+
+
+
+
+
+
+ 0
+ 0
+ 334
+ 329
+
+
+
+ Pattern
+
+
+ -
+
+
+ 1
+
+
+ 100
+
+
+ 10
+
+
+ 100
+
+
+
+ -
+
+
+ Step Over Percent
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ Use ZigZag
+
+
+
+ -
+
+
+ ZigZag Unidirectional
+
+
+
+ -
+
+
+ -
+
+
+ ZigZag Angle
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 334
+ 329
+
+
+
+
+ :/icons/Path-OperationB.svg:/icons/Path-OperationB.svg
+
+
+ Operation
+
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+ -
+
+
+
-
+
+
+ Cut Mode
+
+
+
+ -
+
+
-
+
+ Climb
+
+
+ -
+
+ Conventional
+
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+ Use Start Point
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+ Material Allowance
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Gui::InputField
+ QLineEdit
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py
index 9894b2201..0a9545e51 100644
--- a/src/Mod/Path/InitGui.py
+++ b/src/Mod/Path/InitGui.py
@@ -74,13 +74,14 @@ class PathWorkbench (Workbench):
from PathScripts import PathContour
from PathScripts import PathProfileEdges
from PathScripts import DogboneDressup
+ from PathScripts import PathMillFace
import PathCommands
# build commands list
projcmdlist = ["Path_Job", "Path_Post", "Path_Inspect", "Path_Sanity"]
toolcmdlist = ["Path_ToolLibraryEdit", "Path_LoadTool"]
prepcmdlist = ["Path_Plane", "Path_Fixture", "Path_ToolLenOffset", "Path_Comment", "Path_Stop", "Path_FaceProfile", "Path_FacePocket", "Path_Custom", "Path_FromShape"]
- twodopcmdlist = ["Path_Contour", "Path_Profile", "Path_Profile_Edges", "Path_Pocket", "Path_Drilling", "Path_Engrave"]
+ twodopcmdlist = ["Path_Contour", "Path_Profile", "Path_Profile_Edges", "Path_Pocket", "Path_Drilling", "Path_Engrave", "Path_MillFace"]
threedopcmdlist = ["Path_Surfacing"]
modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ]
dressupcmdlist = ["Dogbone_Dressup", "DragKnife_Dressup"]
diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py
new file mode 100644
index 000000000..f2b49ab4c
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathMillFace.py
@@ -0,0 +1,581 @@
+# -*- coding: utf-8 -*-
+
+# ***************************************************************************
+# * *
+# * Copyright (c) 2016 sliptonic *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * This program is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with this program; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import FreeCAD
+import Path
+from PySide import QtCore, QtGui
+from PathScripts import PathUtils
+import Part
+import PathScripts.PathKurveUtils
+import area
+
+FreeCADGui = None
+if FreeCAD.GuiUp:
+ import FreeCADGui
+
+"""Path Face object and FreeCAD command"""
+
+# Qt tanslation handling
+try:
+ _encoding = QtGui.QApplication.UnicodeUTF8
+
+ def translate(context, text, disambig=None):
+ return QtGui.QApplication.translate(context, text, disambig, _encoding)
+except AttributeError:
+ def translate(context, text, disambig=None):
+ return QtGui.QApplication.translate(context, text, disambig)
+
+
+class ObjectFace:
+
+ def __init__(self, obj):
+ obj.addProperty("App::PropertyLinkSubList", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","The base geometry of this object"))
+ obj.addProperty("App::PropertyBool", "Active", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","Make False, to prevent operation from generating code"))
+ obj.addProperty("App::PropertyString", "Comment", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","An optional comment for this profile"))
+ obj.addProperty("App::PropertyString", "UserLabel", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","User Assigned Label"))
+
+ # Tool Properties
+ obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", QtCore.QT_TRANSLATE_NOOP("App::Property","The tool number in use"))
+ obj.ToolNumber = (0, 0, 1000, 0)
+ obj.setEditorMode('ToolNumber', 1) # make this read only
+ obj.addProperty("App::PropertyString", "ToolDescription", "Tool", QtCore.QT_TRANSLATE_NOOP("App::Property","The description of the tool "))
+ obj.setEditorMode('ToolDescription', 1) # make this read only
+
+ # Depth Properties
+ obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","The height needed to clear clamps and obstructions"))
+ obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Rapid Safety Height between locations."))
+ obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Incremental Step Down of Tool"))
+ obj.StepDown = (0.0, 0.01, 100.0, 0.5)
+ obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Starting Depth of Tool- first cut depth in Z"))
+ obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Final Depth of Tool- lowest value in Z"))
+ obj.addProperty("App::PropertyDistance", "FinishDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Maximum material removed on final pass."))
+
+ # Face Properties
+ obj.addProperty("App::PropertyEnumeration", "CutMode", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW"))
+ obj.CutMode = ['Climb', 'Conventional']
+ obj.addProperty("App::PropertyDistance", "MaterialAllowance", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Amount of material to leave"))
+ obj.addProperty("App::PropertyEnumeration", "StartAt", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Start Faceing at center or boundary"))
+ obj.StartAt = ['Center', 'Edge']
+ obj.addProperty("App::PropertyPercent", "StepOver", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Percent of cutter diameter to step over on each pass"))
+ # obj.StepOver = (0.0, 0.01, 100.0, 0.5)
+ obj.addProperty("App::PropertyBool", "KeepToolDown", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Attempts to avoid unnecessary retractions."))
+ obj.addProperty("App::PropertyBool", "ZigUnidirectional", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Lifts tool at the end of each pass to respect cut mode."))
+ obj.addProperty("App::PropertyBool", "UseZigZag", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Use Zig Zag pattern to clear area."))
+ obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Angle of the zigzag pattern"))
+
+
+ # Start Point Properties
+ obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property","The start point of this path"))
+ obj.addProperty("App::PropertyBool", "UseStartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property","make True, if specifying a Start Point"))
+
+ obj.Proxy = self
+
+ def onChanged(self, obj, prop):
+
+ if prop == "UserLabel":
+ obj.Label = obj.UserLabel + " :" + obj.ToolDescription
+
+ def __getstate__(self):
+ return None
+
+ def __setstate__(self, state):
+ return None
+
+ def _guessDepths(self, obj, ss, sub=""):
+ try:
+ bb = ss.Shape.BoundBox # parent boundbox
+ subobj = ss.Shape.getElement(sub)
+ fbb = subobj.BoundBox # feature boundbox
+ obj.StartDepth = bb.ZMax
+ obj.ClearanceHeight = bb.ZMax + 5.0
+ obj.SafeHeight = bb.ZMax + 3.0
+
+ if fbb.ZMax == fbb.ZMin and fbb.ZMax == bb.ZMax: # top face
+ obj.FinalDepth = bb.ZMin
+ elif fbb.ZMax > fbb.ZMin and fbb.ZMax == bb.ZMax: # vertical face, full cut
+ obj.FinalDepth = fbb.ZMin
+ elif fbb.ZMax > fbb.ZMin and fbb.ZMin > bb.ZMin: # internal vertical wall
+ obj.FinalDepth = fbb.ZMin
+ elif fbb.ZMax == fbb.ZMin and fbb.ZMax > bb.ZMin: # face/shelf
+ obj.FinalDepth = fbb.ZMin
+ else: # catch all
+ obj.FinalDepth = bb.ZMin
+ except:
+ obj.StartDepth = 5.0
+ obj.ClearanceHeight = 10.0
+ obj.SafeHeight = 8.0
+
+
+ def addFacebase(self, obj, ss, sub=""):
+ baselist = obj.Base
+ if baselist is None:
+ baselist = []
+ if len(baselist) == 0: # When adding the first base object, guess at heights
+ self._guessDepths(obj, ss, sub)
+
+ item = (ss, sub)
+ if item in baselist:
+ FreeCAD.Console.PrintWarning(translate("Path", "this object already in the list" + "\n"))
+ else:
+ baselist.append(item)
+ obj.Base = baselist
+ print "this base is: " + str(baselist)
+ self.execute(obj)
+
+ def getStock(self, obj):
+ """find and return a stock object from hosting project if any"""
+ for o in obj.InList:
+ if hasattr(o, "Group"):
+ for g in o.Group:
+ if hasattr(g, "Height_Allowance"):
+ return o
+ # not found? search one level up
+ for o in obj.InList:
+ return self.getStock(o)
+ return None
+
+ def buildpathlibarea(self, obj, a):
+ """Build the face path using libarea algorithm"""
+ import PathScripts.PathAreaUtils as PathAreaUtils
+ from PathScripts.PathUtils import depth_params
+
+ FreeCAD.Console.PrintMessage(translate("PathFace", "Generating toolpath with libarea offsets.\n"))
+
+ depthparams = depth_params(
+ obj.ClearanceHeight.Value,
+ obj.SafeHeight.Value,
+ obj.StartDepth.Value,
+ obj.StepDown,
+ obj.FinishDepth.Value,
+ obj.FinalDepth.Value)
+
+ extraoffset = obj.MaterialAllowance.Value
+ stepover = (self.radius * 2) * (float(obj.StepOver)/100)
+ use_zig_zag = obj.UseZigZag
+ zig_angle = obj.ZigZagAngle
+ from_center = (obj.StartAt == "Center")
+ keep_tool_down = obj.KeepToolDown
+ zig_unidirectional = obj.ZigUnidirectional
+ start_point = None
+ cut_mode = obj.CutMode
+
+ PathAreaUtils.flush_nc()
+ PathAreaUtils.output('mem')
+ PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed)
+ if obj.UseStartPoint:
+ start_point = (obj.StartPoint.x, obj.StartPoint.y)
+
+ # print "a," + str(self.radius) + "," + str(extraoffset) + "," + str(stepover) + ",depthparams, " + str(from_center) + "," + str(keep_tool_down) + "," + str(use_zig_zag) + "," + str(zig_angle) + "," + str(zig_unidirectional) + "," + str(start_point) + "," + str(cut_mode)
+
+ PathAreaUtils.pocket(
+ a,
+ self.radius,
+ extraoffset,
+ stepover,
+ depthparams,
+ from_center,
+ keep_tool_down,
+ use_zig_zag,
+ zig_angle,
+ zig_unidirectional,
+ start_point,
+ cut_mode)
+ return PathAreaUtils.retrieve_gcode()
+
+ # To reload this from FreeCAD, use: import PathScripts.PathFace; reload(PathScripts.PathFace)
+ def execute(self, obj):
+
+ if not obj.Active:
+ path = Path.Path("(inactive operation)")
+ obj.Path = path
+ obj.ViewObject.Visibility = False
+ return
+
+ #Tool may have changed. Refresh data
+ toolLoad = PathUtils.getLastToolLoad(obj)
+ if toolLoad is None or toolLoad.ToolNumber == 0:
+ self.vertFeed = 100
+ self.horizFeed = 100
+ self.vertRapid = 100
+ self.horiRrapid = 100
+ self.radius = 0.25
+ obj.ToolNumber = 0
+ obj.ToolDescription = "UNDEFINED"
+ else:
+ self.vertFeed = toolLoad.VertFeed.Value
+ self.horizFeed = toolLoad.HorizFeed.Value
+ self.vertRapid = toolLoad.VertRapid.Value
+ self.horizRapid = toolLoad.HorizRapid.Value
+ tool = PathUtils.getTool(obj, toolLoad.ToolNumber)
+ if tool.Diameter == 0:
+ self.radius = 0.25
+ else:
+ self.radius = tool.Diameter/2
+ obj.ToolNumber = toolLoad.ToolNumber
+ obj.ToolDescription = toolLoad.Name
+
+ #Build preliminary comments
+ output = ""
+ output += "(" + obj.Label + ")"
+
+ if obj.UserLabel == "":
+ obj.Label = obj.Name + " :" + obj.ToolDescription
+ else:
+ obj.Label = obj.UserLabel + " :" + obj.ToolDescription
+
+ #Facing is done either against base object
+ if obj.Base:
+ for b in obj.Base:
+ print (b)
+ for sub in b[1]:
+ if "Face" in sub:
+ shape = getattr(b[0].Shape, sub)
+ wire = shape.OuterWire
+ edgelist = wire.Edges
+ else:
+ return
+
+ #If no base object, do planing of top surface of entire model
+ else:
+ parentJob = PathUtils.findParentJob(obj)
+ if parentJob is None:
+ return
+ baseobject = parentJob.Base
+ if baseobject is None:
+ return
+ print "Plane base object: " + baseobject.Name
+ contourwire = PathUtils.silhouette(baseobject)
+ edgelist = contourwire.Edges
+ edgelist = Part.__sortEdges__(edgelist)
+
+ #use libarea to build the pattern
+ a = area.Area()
+ c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW')
+ a.append(c)
+ a.Reorder()
+ output += self.buildpathlibarea(obj, a)
+
+ path = Path.Path(output)
+ obj.Path = path
+ obj.ViewObject.Visibility = True
+
+
+class _CommandSetFaceStartPoint:
+ def GetResources(self):
+ return {'Pixmap': 'Path-StartPoint',
+ 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathFace", "Pick Start Point"),
+ 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathFace", "Pick Start Point")}
+
+ def IsActive(self):
+ return FreeCAD.ActiveDocument is not None
+
+ def setpoint(self, point, o):
+ obj = FreeCADGui.Selection.getSelection()[0]
+ obj.StartPoint.x = point.x
+ obj.StartPoint.y = point.y
+
+ def Activated(self):
+ FreeCADGui.Snapper.getPoint(callback=self.setpoint)
+
+
+class ViewProviderFace:
+
+ def __init__(self, vobj):
+ vobj.Proxy = self
+
+ def attach(self, vobj):
+ self.Object = vobj.Object
+ return
+
+ def setEdit(self, vobj, mode=0):
+ FreeCADGui.Control.closeDialog()
+ taskd = TaskPanel()
+ taskd.obj = vobj.Object
+ FreeCADGui.Control.showDialog(taskd)
+ taskd.setupUi()
+ return True
+
+ def getIcon(self):
+ return ":/icons/Path-Face.svg"
+
+ def __getstate__(self):
+ return None
+
+ def __setstate__(self, state):
+ return None
+
+
+class CommandPathMillFace:
+
+ def GetResources(self):
+ return {'Pixmap': 'Path-Face',
+ 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathFace", "Face"),
+ 'Accel': "P, O",
+ 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathFace", "Create a Facing Operation from a model or face")}
+
+ def IsActive(self):
+ if FreeCAD.ActiveDocument is not None:
+ for o in FreeCAD.ActiveDocument.Objects:
+ if o.Name[:3] == "Job":
+ return True
+ return False
+
+ def Activated(self):
+
+ # zbottom = 0.0
+ ztop = 10.0
+
+ # if everything is ok, execute and register the transaction in the undo/redo stack
+ FreeCAD.ActiveDocument.openTransaction(translate("PathFace", "Create Face"))
+ FreeCADGui.addModule("PathScripts.PathMillFace")
+ FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Face")')
+ FreeCADGui.doCommand('PathScripts.PathMillFace.ObjectFace(obj)')
+ FreeCADGui.doCommand('obj.Active = True')
+ FreeCADGui.doCommand('PathScripts.PathMillFace.ViewProviderFace(obj.ViewObject)')
+ FreeCADGui.doCommand('from PathScripts import PathUtils')
+ #FreeCADGui.doCommand('obj.Algorithm = "libarea"')
+ FreeCADGui.doCommand('obj.StepOver = 50')
+ FreeCADGui.doCommand('obj.ClearanceHeight = 10') # + str(bb.ZMax + 2.0))
+ FreeCADGui.doCommand('obj.StepDown = 1.0')
+ FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop + 1))
+ FreeCADGui.doCommand('obj.FinalDepth =' + str(ztop))
+ FreeCADGui.doCommand('obj.ZigZagAngle = 0.0')
+ FreeCADGui.doCommand('obj.UseZigZag = True')
+ FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
+ snippet = '''
+parentJob = PathUtils.findParentJob(obj)
+if parentJob is None:
+ pass
+else:
+ baseobject = parentJob.Base
+ if baseobject is None:
+ pass
+ else:
+ obj.StartDepth = str(baseobject.Shape.BoundBox.ZMax + 1)
+ obj.FinalDepth = str(baseobject.Shape.BoundBox.ZMax)
+'''
+ FreeCADGui.doCommand(snippet)
+
+ FreeCAD.ActiveDocument.commitTransaction()
+
+ FreeCAD.ActiveDocument.recompute()
+ FreeCADGui.doCommand('obj.ViewObject.startEditing()')
+
+
+class TaskPanel:
+ def __init__(self):
+ self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/MillFaceEdit.ui")
+ #self.form = FreeCADGui.PySideUic.loadUi(":/panels/MillFaceEdit.ui")
+ self.updating = False
+
+ def accept(self):
+ self.getFields()
+
+ FreeCADGui.ActiveDocument.resetEdit()
+ FreeCADGui.Control.closeDialog()
+ FreeCAD.ActiveDocument.recompute()
+ FreeCADGui.Selection.removeObserver(self.s)
+
+ def reject(self):
+ FreeCADGui.Control.closeDialog()
+ FreeCAD.ActiveDocument.recompute()
+ FreeCADGui.Selection.removeObserver(self.s)
+
+ def getFields(self):
+ if self.obj:
+ if hasattr(self.obj, "StartDepth"):
+ self.obj.StartDepth = self.form.startDepth.text()
+ if hasattr(self.obj, "FinalDepth"):
+ self.obj.FinalDepth = self.form.finalDepth.text()
+ if hasattr(self.obj, "SafeHeight"):
+ self.obj.SafeHeight = self.form.safeHeight.text()
+ if hasattr(self.obj, "ClearanceHeight"):
+ self.obj.ClearanceHeight = self.form.clearanceHeight.text()
+ if hasattr(self.obj, "StepDown"):
+ self.obj.StepDown = self.form.stepDown.value()
+ if hasattr(self.obj, "MaterialAllowance"):
+ self.obj.MaterialAllowance = self.form.extraOffset.value()
+ if hasattr(self.obj, "UseStartPoint"):
+ self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
+ if hasattr(self.obj, "CutMode"):
+ self.obj.CutMode = str(self.form.cutMode.currentText())
+ if hasattr(self.obj, "UseZigZag"):
+ self.obj.UseZigZag = self.form.useZigZag.isChecked()
+ if hasattr(self.obj, "ZigUnidirectional"):
+ self.obj.ZigUnidirectional = self.form.zigZagUnidirectional.isChecked()
+ if hasattr(self.obj, "ZigZagAngle"):
+ self.obj.ZigZagAngle = self.form.zigZagAngle.value()
+ if hasattr(self.obj, "StepOver"):
+ self.obj.StepOver = self.form.stepOverPercent.value()
+
+ self.obj.Proxy.execute(self.obj)
+
+ def setFields(self):
+ self.form.startDepth.setText(str(self.obj.StartDepth.Value))
+ self.form.finalDepth.setText(str(self.obj.FinalDepth.Value))
+ self.form.safeHeight.setText(str(self.obj.SafeHeight.Value))
+ self.form.clearanceHeight.setText(str(self.obj.ClearanceHeight.Value))
+ self.form.stepDown.setValue(self.obj.StepDown)
+ self.form.extraOffset.setValue(self.obj.MaterialAllowance.Value)
+ self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
+ self.form.useZigZag.setChecked(self.obj.UseZigZag)
+ self.form.zigZagUnidirectional.setChecked(self.obj.ZigUnidirectional)
+ self.form.zigZagAngle.setValue(self.obj.ZigZagAngle)
+ self.form.stepOverPercent.setValue(self.obj.StepOver)
+
+ for i in self.obj.Base:
+ for sub in i[1]:
+ self.form.baseList.addItem(i[0].Name + "." + sub)
+
+ def open(self):
+ self.s = SelObserver()
+ # install the function mode resident
+ FreeCADGui.Selection.addObserver(self.s)
+
+ def addBase(self):
+ # check that the selection contains exactly what we want
+ selection = FreeCADGui.Selection.getSelectionEx()
+
+ if len(selection) != 1:
+ FreeCAD.Console.PrintError(translate("PathProject", "Please select only faces from one solid\n"))
+ return
+ sel = selection[0]
+ if not sel.HasSubObjects:
+ FreeCAD.Console.PrintError(translate("PathProject", "Please select faces from one solid\n"))
+ return
+ if not selection[0].SubObjects[0].ShapeType == "Face":
+ FreeCAD.Console.PrintError(translate("PathProject", "Please select faces from one solid\n"))
+ return
+ for i in sel.SubElementNames:
+ self.obj.Proxy.addFacebase(self.obj, sel.Object, i)
+
+ self.setFields() # defaults may have changed. Reload.
+ self.form.baseList.clear()
+
+ for i in self.obj.Base:
+ for sub in i[1]:
+ self.form.baseList.addItem(i[0].Name + "." + sub)
+
+ def deleteBase(self):
+ dlist = self.form.baseList.selectedItems()
+ newlist = []
+ for d in dlist:
+ for i in self.obj.Base:
+ if i[0].Name != d.text().partition(".")[0] or i[1] != d.text().partition(".")[2]:
+ newlist.append(i)
+ self.form.baseList.takeItem(self.form.baseList.row(d))
+ self.obj.Base = newlist
+ self.obj.Proxy.execute(self.obj)
+ FreeCAD.ActiveDocument.recompute()
+
+ def itemActivated(self):
+ FreeCADGui.Selection.clearSelection()
+ slist = self.form.baseList.selectedItems()
+ for i in slist:
+ objstring = i.text().partition(".")
+ obj = FreeCAD.ActiveDocument.getObject(objstring[0])
+ if objstring[2] != "":
+ FreeCADGui.Selection.addSelection(obj, objstring[2])
+ else:
+ FreeCADGui.Selection.addSelection(obj)
+
+ FreeCADGui.updateGui()
+
+ def reorderBase(self):
+ newlist = []
+ for i in range(self.form.baseList.count()):
+ s = self.form.baseList.item(i).text()
+ objstring = s.partition(".")
+
+ obj = FreeCAD.ActiveDocument.getObject(objstring[0])
+ item = (obj, str(objstring[2]))
+ newlist.append(item)
+ self.obj.Base = newlist
+
+ self.obj.Proxy.execute(self.obj)
+ FreeCAD.ActiveDocument.recompute()
+
+ def getStandardButtons(self):
+ return int(QtGui.QDialogButtonBox.Ok)
+
+ def edit(self, item, column):
+ if not self.updating:
+ self.resetObject()
+
+ def setupUi(self):
+
+ # Connect Signals and Slots
+ # Base Controls
+ self.form.baseList.itemSelectionChanged.connect(self.itemActivated)
+ self.form.addBase.clicked.connect(self.addBase)
+ self.form.deleteBase.clicked.connect(self.deleteBase)
+ self.form.reorderBase.clicked.connect(self.reorderBase)
+
+ # Depths
+ self.form.startDepth.editingFinished.connect(self.getFields)
+ self.form.finalDepth.editingFinished.connect(self.getFields)
+ self.form.stepDown.editingFinished.connect(self.getFields)
+
+ # Heights
+ self.form.safeHeight.editingFinished.connect(self.getFields)
+ self.form.clearanceHeight.editingFinished.connect(self.getFields)
+
+ # operation
+ self.form.cutMode.currentIndexChanged.connect(self.getFields)
+ self.form.useStartPoint.clicked.connect(self.getFields)
+ self.form.extraOffset.editingFinished.connect(self.getFields)
+
+ # Pattern
+ self.form.stepOverPercent.editingFinished.connect(self.getFields)
+ self.form.useZigZag.clicked.connect(self.getFields)
+ self.form.zigZagUnidirectional.clicked.connect(self.getFields)
+ self.form.zigZagAngle.editingFinished.connect(self.getFields)
+
+ self.setFields()
+
+ sel = FreeCADGui.Selection.getSelectionEx()
+ if len(sel) != 0 and sel[0].HasSubObjects:
+ self.addBase()
+
+
+class SelObserver:
+ def __init__(self):
+ import PathScripts.PathSelection as PST
+ PST.pocketselect()
+
+ def __del__(self):
+ import PathScripts.PathSelection as PST
+ PST.clear()
+
+ def addSelection(self, doc, obj, sub, pnt):
+ FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')')
+ FreeCADGui.updateGui()
+
+if FreeCAD.GuiUp:
+ # register the FreeCAD command
+ FreeCADGui.addCommand('Path_MillFace', CommandPathMillFace())
+ FreeCADGui.addCommand('Set_FaceStartPoint', _CommandSetFaceStartPoint())
+
+
+FreeCAD.Console.PrintLog("Loading PathMillFace... done\n")
diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py
index 62e8fd08e..6e625a66c 100644
--- a/src/Mod/Path/PathScripts/PathUtils.py
+++ b/src/Mod/Path/PathScripts/PathUtils.py
@@ -84,8 +84,26 @@ def curvetowire(obj, steps):
# fixme set at 4 decimal places for testing
def fmt(val): return format(val, '.4f')
+
+def getProjected(shape,direction):
+ "returns projected edges from a shape and a direction"
+ import Part,Drawing
+ edges = []
+ groups = Drawing.projectEx(shape,direction)
+ for g in groups[0:5]:
+ if g:
+ edges.append(g)
+ # if hasattr(obj,"Tessellation") and obj.Tessellation:
+ # return DraftGeomUtils.cleanProjection(Part.makeCompound(edges),obj.Tessellation,obj.SegmentLength)
+ # else:
+ return Part.makeCompound(edges)
+
+
def silhouette(obj):
- w = TechDraw.findOuterWire(obj.Shape.Edges)
+ from FreeCAD import Vector
+ s = getProjected(obj.Shape, Vector(0,0,1))
+ print s
+ w = TechDraw.findOuterWire(s.Edges)
return w
def isSameEdge(e1, e2):