From e1fad4e76f58d622dcf959bc92a0c9e6e8c4f581 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Wed, 2 Nov 2016 13:39:41 -0500 Subject: [PATCH 1/8] remove unneeded base geometry tab --- .../Path/Gui/Resources/panels/ContourEdit.ui | 46 +++++-------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui index d68a4c97d9..04dfd1f13e 100644 --- a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui @@ -23,43 +23,19 @@ - 4 + 3 - - - true - - - - 0 - 0 - 334 - 288 - - - - Contour works on shape outer loop but maybe could work on something else - - - - :/icons/Path-BaseGeometry.svg:/icons/Path-BaseGeometry.svg - - - Base Geometry - - - 0 0 334 - 288 + 318 - + :/icons/Path-Depths.svg:/icons/Path-Depths.svg @@ -121,12 +97,12 @@ 0 0 - 334 - 288 + 165 + 70 - + :/icons/Path-Heights.svg:/icons/Path-Heights.svg @@ -172,7 +148,7 @@ 0 0 334 - 288 + 318 @@ -230,11 +206,11 @@ 0 0 334 - 288 + 318 - + :/icons/Path-OperationB.svg:/icons/Path-OperationB.svg @@ -358,8 +334,8 @@ - - + + From bbf4e2954abbaeb283b3d61e13a2c0a98cf4e265 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Thu, 3 Nov 2016 19:00:43 -0500 Subject: [PATCH 2/8] Initial commit of facing op --- .../Path/Gui/Resources/panels/MillFaceEdit.ui | 422 +++++++++++++ src/Mod/Path/InitGui.py | 3 +- src/Mod/Path/PathScripts/PathMillFace.py | 581 ++++++++++++++++++ src/Mod/Path/PathScripts/PathUtils.py | 20 +- 4 files changed, 1024 insertions(+), 2 deletions(-) create mode 100644 src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui create mode 100644 src/Mod/Path/PathScripts/PathMillFace.py 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 0000000000..480d22d1ca --- /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 +
Gui/InputField.h
+
+
+ + + + + +
diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 9894b22017..0a9545e51f 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 0000000000..f2b49ab4c2 --- /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 62e8fd08eb..6e625a66c3 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): From 50bfaffcd1715859bf24f0badeb3b655d3c5a545 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Tue, 25 Oct 2016 18:13:23 -0500 Subject: [PATCH 3/8] PathJob chooser needs to use dialog in resource file --- src/Mod/Path/PathScripts/PathUtils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 6e625a66c3..c1e7cce99f 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -609,7 +609,9 @@ def addToJob(obj, jobname = None): elif len(jobs) == 1: job = jobs[0] else: - form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui") + #form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui") + form = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobChooser.ui") + mylist = [i.Name for i in jobs] form.cboProject.addItems(mylist) r = form.exec_() From fe11942afb60e25fb456ea73c163f9ff8ffce4e0 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Sat, 5 Nov 2016 19:24:56 -0500 Subject: [PATCH 4/8] Toolchange output fixed --- src/Mod/Path/PathScripts/PathLoadTool.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Mod/Path/PathScripts/PathLoadTool.py b/src/Mod/Path/PathScripts/PathLoadTool.py index e4a5407d17..a115a099c2 100644 --- a/src/Mod/Path/PathScripts/PathLoadTool.py +++ b/src/Mod/Path/PathScripts/PathLoadTool.py @@ -26,6 +26,7 @@ import FreeCAD import FreeCADGui import PathUtils +import Path import Part import PathScripts from PySide import QtCore, QtGui @@ -74,6 +75,15 @@ class LoadTool(): else: commands += 'M4S' + str(obj.SpindleSpeed) + '\n' + # print output + if commands == "": + commands += "(No commands processed)" + + + path = Path.Path(commands) + obj.Path = path + obj.ViewObject.Visibility = True + def onChanged(self, obj, prop): mode = 2 obj.setEditorMode('Placement', mode) From 18ea91cc2544a0423fc7a4769d6c934662d72749 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Sun, 6 Nov 2016 17:42:50 -0600 Subject: [PATCH 5/8] fix for drilling in Compound objects. Better check of hole face ordering. --- src/Mod/Path/PathScripts/PathDrilling.py | 17 ++++++++++++++--- src/Mod/Path/PathScripts/PathSelection.py | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index 79f348f4bd..0cd13d2c6f 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -189,15 +189,26 @@ class ObjectDrilling: drillable = False if obj.ShapeType == 'Vertex': drillable = True - elif obj.ShapeType == 'Solid': + elif obj.ShapeType in['Solid', 'Compound']: if sub[0:4] == 'Face': subobj = obj.getElement(sub) if isinstance(subobj.Edges[0].Curve, Part.Circle): drillable = True if str(subobj.Surface) == "": drillable = True - if len(subobj.Edges[0].Vertexes) > 1: - drillable = False + if len(subobj.Edges) == 3: + cedge = [] + ledge = [] + for e in subobj.Edges: + if isinstance (e.Curve, Part.Circle): + cedge.append(e) + elif isinstance (e.Curve, Part.Line): + ledge.append(e) + if len(cedge) == 2 and len(ledge) == 1: + drillable = True + else: + #if len(subobj.Edges[0].Vertexes) > 1: + drillable = False if sub[0:4] == 'Edge': o = obj.getElement(sub) if isinstance(o.Curve, Part.Circle) and len(o.Vertexes) == 1: diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index a2e3fb4556..a6566e9258 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -100,7 +100,7 @@ class DRILLGate: return False if obj.ShapeType == 'Vertex': drillable = True - elif obj.ShapeType == 'Solid': + elif obj.ShapeType in['Solid', 'Compound']: if sub[0:4] == 'Face': subobj = obj.getElement(sub) drillable = isinstance(subobj.Edges[0].Curve, Part.Circle) From 7930db4e5cc1694d5907642add0bc398eee5eb24 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 7 Nov 2016 14:48:35 -0600 Subject: [PATCH 6/8] icons --- src/Mod/Path/Gui/Resources/Path.qrc | 1 + .../Path/Gui/Resources/icons/Path-Face.svg | 926 ++++++++++++++++++ 2 files changed, 927 insertions(+) create mode 100644 src/Mod/Path/Gui/Resources/icons/Path-Face.svg diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index 3269fd0653..da55dd99b4 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -45,6 +45,7 @@ icons/Path-Profile-Edges.svg icons/Path-Profile-Face.svg icons/Path-SelectLoop.svg + icons/Path-Face.svg translations/Path_de.qm translations/Path_af.qm translations/Path_zh-CN.qm diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Face.svg b/src/Mod/Path/Gui/Resources/icons/Path-Face.svg new file mode 100644 index 0000000000..a9c190f11e --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Face.svg @@ -0,0 +1,926 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + From a38006b5d501c502b7d049fc061de048dd8fe864 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Thu, 10 Nov 2016 17:43:50 -0600 Subject: [PATCH 7/8] incorporating TechDraw findShapeOutline --- src/Mod/Path/CMakeLists.txt | 1 + src/Mod/Path/Gui/Resources/Path.qrc | 1 + .../Path/Gui/Resources/panels/MillFaceEdit.ui | 112 +++++++++++------- src/Mod/Path/PathScripts/PathContour.py | 4 +- src/Mod/Path/PathScripts/PathMillFace.py | 69 ++++++++--- 5 files changed, 127 insertions(+), 60 deletions(-) diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index e6cc466f60..c4cf6bd043 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -33,6 +33,7 @@ SET(PathScripts_SRCS PathScripts/PathProfile.py PathScripts/PathProfileEdges.py PathScripts/PathContour.py + PathScripts/PathMillFace.py PathScripts/PathPocket.py PathScripts/PathDrilling.py PathScripts/PathDressup.py diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index da55dd99b4..27353e6759 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -85,6 +85,7 @@ panels/ToolEdit.ui panels/DlgJobChooser.ui panels/ContourEdit.ui + panels/MillFaceEdit.ui panels/ProfileEdgesEdit.ui panels/DogboneEdit.ui panels/DlgSelectPostProcessor.ui diff --git a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui index 480d22d1ca..7f8f49f822 100644 --- a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui @@ -6,8 +6,8 @@ 0 0 - 352 - 525 + 351 + 520 @@ -33,12 +33,12 @@ 0 0 - 334 - 329 + 333 + 353 - + :/icons/Path-BaseGeometry.svg:/icons/Path-BaseGeometry.svg @@ -111,12 +111,12 @@ 0 0 - 334 - 329 + 333 + 324 - + :/icons/Path-Depths.svg:/icons/Path-Depths.svg @@ -192,12 +192,12 @@ 0 0 - 334 - 329 + 333 + 324 - + :/icons/Path-Heights.svg:/icons/Path-Heights.svg @@ -237,27 +237,13 @@
- - - - 0 - 0 - 334 - 329 - - - - Entry - - - 0 0 - 334 - 329 + 333 + 353 @@ -330,22 +316,19 @@ 0 0 - 334 - 329 + 333 + 353 - + :/icons/Path-OperationB.svg:/icons/Path-OperationB.svg Operation - - - QFormLayout::AllNonFixedFieldsGrow - - + + @@ -372,7 +355,7 @@ - + @@ -385,22 +368,69 @@ - + - Material Allowance + Pass Extension - + + + -99.000000000000000 + + + 99.000000000000000 + + + + + + + + + Boundary Shape + + + + + + + + Model Perimeter + + + + + Model Boundbox + + + + + + + + + + + Qt::Vertical + + + + 20 + 116 + + + + @@ -415,8 +445,8 @@ - - + + diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index 5eedfaaa2c..0bb61ebf8c 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -25,6 +25,7 @@ import FreeCAD import Path from FreeCAD import Vector +import TechDraw from PathScripts import PathUtils from PathScripts.PathUtils import depth_params @@ -246,8 +247,7 @@ class ObjectContour: baseobject = parentJob.Base if baseobject is None: return - print "base object: " + baseobject.Name - contourwire = PathUtils.silhouette(baseobject) + contourwire = TechDraw.findShapeOutline(baseobject.Shape,1, Vector(0,0,1)) edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index f2b49ab4c2..d25ef20563 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -29,6 +29,11 @@ from PathScripts import PathUtils import Part import PathScripts.PathKurveUtils import area +import TechDraw +import Drawing +import DraftGeomUtils +from FreeCAD import Vector +import Arch FreeCADGui = None if FreeCAD.GuiUp: @@ -74,7 +79,7 @@ class ObjectFace: # 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::PropertyDistance", "PassExtension", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","How far the cutter should extend past the boundary")) 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")) @@ -83,6 +88,8 @@ class ObjectFace: 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")) + obj.addProperty("App::PropertyEnumeration", "BoundaryShape", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Shape to use for calculating Boundary")) + obj.BoundaryShape = ['Perimeter', 'BoundBox'] # Start Point Properties @@ -170,7 +177,7 @@ class ObjectFace: obj.FinishDepth.Value, obj.FinalDepth.Value) - extraoffset = obj.MaterialAllowance.Value + extraoffset = 1 - obj.PassExtension.Value stepover = (self.radius * 2) * (float(obj.StepOver)/100) use_zig_zag = obj.UseZigZag zig_angle = obj.ZigZagAngle @@ -246,15 +253,22 @@ class ObjectFace: #Facing is done either against base object if obj.Base: + faces = [] 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 + shape = getattr(b[0].Shape, sub) + if isinstance (shape, Part.Face): + groups = Drawing.project(shape, Vector(0,0,1)) + if len(groups[0].Edges) > 0: + p = DraftGeomUtils.superWire(groups[0].Edges, closed=True) + w = [] + w.append(p) + faces.append(Arch.makeFace(w)) else: + print ('falling out') return + contourwire = TechDraw.findShapeOutline(faces[0].multiFuse(faces[1:]).removeSplitter(), 1, Vector(0,0,1)) + print ('269: contourwire', contourwire) #If no base object, do planing of top surface of entire model else: @@ -264,10 +278,20 @@ class ObjectFace: 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) + # print "Plane base object: " + baseobject.Name + contourwire = TechDraw.findShapeOutline(baseobject.Shape, 1, Vector(0,0,1)) + print ('281: contourwire', contourwire) + + if obj.BoundaryShape == 'BoundBox': + print 'boundbox' + bb = contourwire.BoundBox + bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0,0,1)) + contourwire = TechDraw.findShapeOutline(bbperim, 1, Vector(0,0,1)) + + else: + print 'perimeter' + edgelist = contourwire.Edges + edgelist = Part.__sortEdges__(edgelist) #use libarea to build the pattern a = area.Area() @@ -312,8 +336,8 @@ class ViewProviderFace: FreeCADGui.Control.closeDialog() taskd = TaskPanel() taskd.obj = vobj.Object - FreeCADGui.Control.showDialog(taskd) taskd.setupUi() + FreeCADGui.Control.showDialog(taskd) return True def getIcon(self): @@ -385,8 +409,8 @@ else: 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.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/MillFaceEdit.ui") + self.form = FreeCADGui.PySideUic.loadUi(":/panels/MillFaceEdit.ui") self.updating = False def accept(self): @@ -408,14 +432,16 @@ class TaskPanel: self.obj.StartDepth = self.form.startDepth.text() if hasattr(self.obj, "FinalDepth"): self.obj.FinalDepth = self.form.finalDepth.text() + if hasattr(self.obj, "FinishDepth"): + self.obj.FinishDepth = self.form.finishDepth.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, "PassExtensioon"): + self.obj.PassExtension = self.form.extraOffset.value() if hasattr(self.obj, "UseStartPoint"): self.obj.UseStartPoint = self.form.useStartPoint.isChecked() if hasattr(self.obj, "CutMode"): @@ -434,10 +460,11 @@ class TaskPanel: def setFields(self): self.form.startDepth.setText(str(self.obj.StartDepth.Value)) self.form.finalDepth.setText(str(self.obj.FinalDepth.Value)) + self.form.finishDepth.setText(str(self.obj.FinishDepth.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.extraOffset.setValue(self.obj.PassExtension.Value) self.form.useStartPoint.setChecked(self.obj.UseStartPoint) self.form.useZigZag.setChecked(self.obj.UseZigZag) self.form.zigZagUnidirectional.setChecked(self.obj.ZigUnidirectional) @@ -523,6 +550,12 @@ class TaskPanel: if not self.updating: self.resetObject() + def resetObject(self, remove=None): + "transfers the values from the widget to the object" + + self.obj.touch() + FreeCAD.ActiveDocument.recompute() + def setupUi(self): # Connect Signals and Slots @@ -535,6 +568,7 @@ class TaskPanel: # Depths self.form.startDepth.editingFinished.connect(self.getFields) self.form.finalDepth.editingFinished.connect(self.getFields) + self.form.finishDepth.editingFinished.connect(self.getFields) self.form.stepDown.editingFinished.connect(self.getFields) # Heights @@ -545,6 +579,7 @@ class TaskPanel: self.form.cutMode.currentIndexChanged.connect(self.getFields) self.form.useStartPoint.clicked.connect(self.getFields) self.form.extraOffset.editingFinished.connect(self.getFields) + self.form.boundaryShape.currentIndexChanged.connect(self.getFields) # Pattern self.form.stepOverPercent.editingFinished.connect(self.getFields) From 7c2c541d7afbd2f9900ee26302b86bf75a23bc8c Mon Sep 17 00:00:00 2001 From: sliptonic Date: Sun, 13 Nov 2016 14:45:34 -0600 Subject: [PATCH 8/8] Holding tag bugs Removed OCC from profile Add option to suppress profile of perim. --- .../Path/Gui/Resources/panels/ContourEdit.ui | 25 +- .../Path/Gui/Resources/panels/MillFaceEdit.ui | 258 +++++++--------- .../Gui/Resources/panels/ProfileEdgesEdit.ui | 14 +- .../Path/Gui/Resources/panels/ProfileEdit.ui | 281 +++++++++--------- src/Mod/Path/InitGui.py | 2 +- src/Mod/Path/PathScripts/PathContour.py | 4 +- src/Mod/Path/PathScripts/PathKurveUtils.py | 32 +- src/Mod/Path/PathScripts/PathMillFace.py | 93 +++--- src/Mod/Path/PathScripts/PathProfile.py | 126 ++------ src/Mod/Path/PathScripts/PathProfileEdges.py | 6 +- 10 files changed, 386 insertions(+), 455 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui index 04dfd1f13e..f80fdf98c6 100644 --- a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui @@ -23,7 +23,7 @@ - 3 + 0 @@ -42,13 +42,9 @@ Depths - - - - mm - - - + + QFormLayout::AllNonFixedFieldsGrow + @@ -90,6 +86,13 @@ + + + + mm + + + @@ -97,8 +100,8 @@ 0 0 - 165 - 70 + 334 + 318 @@ -169,7 +172,7 @@ - + diff --git a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui index 7f8f49f822..2795ac19c6 100644 --- a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui @@ -6,8 +6,8 @@ 0 0 - 351 - 520 + 374 + 453 @@ -33,8 +33,8 @@ 0 0 - 333 - 353 + 340 + 277 @@ -111,8 +111,8 @@ 0 0 - 333 - 324 + 356 + 124 @@ -192,8 +192,8 @@ 0 0 - 333 - 324 + 356 + 95 @@ -237,87 +237,13 @@ - - - - 0 - 0 - 333 - 353 - - - - Pattern - - - - - - 1 - - - 100 - - - 10 - - - 100 - - - - - - - Step Over Percent - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Use ZigZag - - - - - - - ZigZag Unidirectional - - - - - - - - - - ZigZag Angle - - - - - - - - 0 0 - 333 - 353 + 356 + 296 @@ -327,50 +253,10 @@ Operation - - - - - - - - Cut Mode - - - - - - - - Climb - - - - - Conventional - - - - - - - - - - - - - - Use Start Point - - - - - - - + + - + @@ -388,29 +274,109 @@ + + + + Step Over Percent + + + + + + + 1 + + + 100 + + + 10 + + + 100 + + + - - - + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + Use ZigZag + + + + + + + ZigZag Angle + + + + + + + + + + ZigZag Unidirectional + + + + + + + + + + + + + Cut Mode + + + + Boundary Shape - + - Model Perimeter + Perimeter - Model Boundbox + Boundbox + + + + + + + + + Climb + + + + + Conventional @@ -418,23 +384,23 @@ - - - - Qt::Vertical - - - - 20 - 116 - - - - + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/Mod/Path/Gui/Resources/panels/ProfileEdgesEdit.ui b/src/Mod/Path/Gui/Resources/panels/ProfileEdgesEdit.ui index 4732c2b253..80ad39a545 100644 --- a/src/Mod/Path/Gui/Resources/panels/ProfileEdgesEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/ProfileEdgesEdit.ui @@ -111,8 +111,8 @@ 0 0 - 334 - 319 + 168 + 99 @@ -178,8 +178,8 @@ 0 0 - 334 - 319 + 165 + 70 @@ -250,7 +250,7 @@ - + @@ -286,8 +286,8 @@ 0 0 - 334 - 319 + 322 + 306 diff --git a/src/Mod/Path/Gui/Resources/panels/ProfileEdit.ui b/src/Mod/Path/Gui/Resources/panels/ProfileEdit.ui index 1161856f91..8238fc6856 100644 --- a/src/Mod/Path/Gui/Resources/panels/ProfileEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/ProfileEdit.ui @@ -6,8 +6,8 @@ 0 0 - 352 - 525 + 399 + 519 @@ -33,8 +33,8 @@ 0 0 - 334 - 358 + 381 + 353 @@ -45,22 +45,6 @@ Base Geometry - - - - Drag to reorder, then update. - - - QAbstractItemView::DragDrop - - - Qt::MoveAction - - - false - - - @@ -104,6 +88,22 @@ + + + + Drag to reorder, then update. + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + false + + + @@ -111,8 +111,8 @@ 0 0 - 334 - 358 + 381 + 353 @@ -178,8 +178,8 @@ 0 0 - 334 - 358 + 381 + 353 @@ -228,14 +228,28 @@ 0 0 - 334 - 358 + 381 + 353 Holding + + + + Add New + + + + + + + Delete + + + @@ -265,20 +279,6 @@ - - - - Add New - - - - - - - Delete - - - @@ -286,8 +286,8 @@ 0 0 - 334 - 358 + 381 + 353 @@ -301,28 +301,107 @@ QFormLayout::AllNonFixedFieldsGrow - - - - Algorithm - + + + + + + + Use End Point + + + + + + + Use Start Point + + + + + + + Use Compensation + + + + + + + Process Holes + + + + + + + Process Perimeter + + + + + + + + + + + Extra Offset + + + + + + + + + + Segment Length + + + + + + + + + + + + + Roll Radius + + + + + + + Plunge Angle + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + - - - - OCC Native - - - - - libarea - - - - - @@ -375,86 +454,6 @@ - - - - - - - Use Start Point - - - - - - - Use Compensation - - - - - - - Use End Point - - - - - - - Process Holes - - - - - - - - - - - - - Extra Offset - - - - - - - - - - Segment Length - - - - - - - - - - Roll Radius - - - - - - - - - - Plunge Angle - - - - - - - - - diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 0a9545e51f..468f07fa97 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -136,7 +136,7 @@ class PathWorkbench (Workbench): if len(FreeCADGui.Selection.getSelection()) == 1: if FreeCADGui.Selection.getSelection()[0].isDerivedFrom("Path::Feature"): self.appendContextMenu("", ["Path_Inspect"]) - if "Profile" in FreeCADGui.Selection.getSelection()[0].Name: + if "Profile" or "Contour" in FreeCADGui.Selection.getSelection()[0].Name: self.appendContextMenu("", ["Add_Tag"]) self.appendContextMenu("", ["Set_StartPoint"]) self.appendContextMenu("", ["Set_EndPoint"]) diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index 0bb61ebf8c..a8386299ab 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -419,7 +419,6 @@ class TaskPanel: self.updating = False def accept(self): - print "removed" self.getFields() FreeCADGui.ActiveDocument.resetEdit() @@ -428,7 +427,6 @@ class TaskPanel: FreeCAD.ActiveDocument.recompute() def reject(self): - print "removed1" FreeCADGui.Control.closeDialog() FreeCADGui.Selection.removeObserver(self.s) FreeCAD.ActiveDocument.recompute() @@ -482,6 +480,7 @@ class TaskPanel: if index >= 0: self.form.direction.setCurrentIndex(index) + self.form.tagTree.blockSignals(True) for i in range(len(self.obj.locs)): item = QtGui.QTreeWidgetItem(self.form.tagTree) item.setText(0, str(i+1)) @@ -492,6 +491,7 @@ class TaskPanel: item.setText(4, str(self.obj.angles[i])) item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) item.setTextAlignment(0, QtCore.Qt.AlignLeft) + self.form.tagTree.blockSignals(False) def open(self): self.s = SelObserver() diff --git a/src/Mod/Path/PathScripts/PathKurveUtils.py b/src/Mod/Path/PathScripts/PathKurveUtils.py index 97f4e5f4c9..4fcf5c8b6a 100644 --- a/src/Mod/Path/PathScripts/PathKurveUtils.py +++ b/src/Mod/Path/PathScripts/PathKurveUtils.py @@ -302,26 +302,26 @@ def profile2(curve, direction="on", radius=1.0, vertfeed=0.0, extend_at_start=0.0, extend_at_end=0.0, lead_in_line_len=0.0, lead_out_line_len=0.0): - print "direction: " + str(direction) - print "radius: " + str(radius) - print "vertfeed: " + str(vertfeed) - print "horizfeed: " + str(horizfeed) - print "offset_extra: " + str(offset_extra) - print "roll_radius: " + str(roll_radius) - print "roll_on: " + str(roll_on) - print "roll_off: " + str(roll_off) - print "depthparams: " + str(depthparams) - print "extend_at_start: " + str(extend_at_start) - print "extend_at_end: " + str(extend_at_end) - print "lead_in_line_len: " + str(lead_in_line_len) - print "lead_out_line_len: " + str(lead_out_line_len) - print "in profile2: 318" + # print "direction: " + str(direction) + # print "radius: " + str(radius) + # print "vertfeed: " + str(vertfeed) + # print "horizfeed: " + str(horizfeed) + # print "offset_extra: " + str(offset_extra) + # print "roll_radius: " + str(roll_radius) + # print "roll_on: " + str(roll_on) + # print "roll_off: " + str(roll_off) + # print "depthparams: " + str(depthparams) + # print "extend_at_start: " + str(extend_at_start) + # print "extend_at_end: " + str(extend_at_end) + # print "lead_in_line_len: " + str(lead_in_line_len) + # print "lead_out_line_len: " + str(lead_out_line_len) + # print "in profile2: 318" global tags direction = direction.lower() offset_curve = area.Curve(curve) - print "curve: " , str(curve) - print "result curve: ", offset_curve.__dict__ + # print "curve: " , str(curve) + # print "result curve: ", offset_curve.__dict__ if direction == "on": use_CRC() == False diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index d25ef20563..b799cf5f4b 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -30,10 +30,7 @@ import Part import PathScripts.PathKurveUtils import area import TechDraw -import Drawing -import DraftGeomUtils from FreeCAD import Vector -import Arch FreeCADGui = None if FreeCAD.GuiUp: @@ -83,13 +80,13 @@ class ObjectFace: 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.StepOver = (1, 1, 100, 1) 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")) obj.addProperty("App::PropertyEnumeration", "BoundaryShape", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Shape to use for calculating Boundary")) - obj.BoundaryShape = ['Perimeter', 'BoundBox'] + obj.BoundaryShape = ['Perimeter', 'Boundbox'] # Start Point Properties @@ -102,6 +99,9 @@ class ObjectFace: if prop == "UserLabel": obj.Label = obj.UserLabel + " :" + obj.ToolDescription + if prop == "StepOver": + if obj.StepOver == 0: + obj.StepOver = 1 def __getstate__(self): return None @@ -144,10 +144,11 @@ class ObjectFace: item = (ss, sub) if item in baselist: FreeCAD.Console.PrintWarning(translate("Path", "this object already in the list" + "\n")) + elif PathUtils.findParentJob(obj).Base.Name != ss.Name: + FreeCAD.Console.PrintWarning(translate("Path", "Please select features from the Job model object" +"\n")) else: baselist.append(item) obj.Base = baselist - print "this base is: " + str(baselist) self.execute(obj) def getStock(self, obj): @@ -193,8 +194,6 @@ class ObjectFace: 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, @@ -251,24 +250,18 @@ class ObjectFace: else: obj.Label = obj.UserLabel + " :" + obj.ToolDescription - #Facing is done either against base object + #Facing is done either against base objects if obj.Base: faces = [] for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance (shape, Part.Face): - groups = Drawing.project(shape, Vector(0,0,1)) - if len(groups[0].Edges) > 0: - p = DraftGeomUtils.superWire(groups[0].Edges, closed=True) - w = [] - w.append(p) - faces.append(Arch.makeFace(w)) + faces.append(shape) else: print ('falling out') return - contourwire = TechDraw.findShapeOutline(faces[0].multiFuse(faces[1:]).removeSplitter(), 1, Vector(0,0,1)) - print ('269: contourwire', contourwire) + planeshape = Part.makeCompound(faces) #If no base object, do planing of top surface of entire model else: @@ -278,18 +271,16 @@ class ObjectFace: baseobject = parentJob.Base if baseobject is None: return - # print "Plane base object: " + baseobject.Name - contourwire = TechDraw.findShapeOutline(baseobject.Shape, 1, Vector(0,0,1)) - print ('281: contourwire', contourwire) + planeshape = baseobject.Shape - if obj.BoundaryShape == 'BoundBox': - print 'boundbox' - bb = contourwire.BoundBox + #if user wants the boundbox, calculate that + if obj.BoundaryShape == 'Boundbox': + bb = planeshape.BoundBox bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0,0,1)) contourwire = TechDraw.findShapeOutline(bbperim, 1, Vector(0,0,1)) - else: - print 'perimeter' + contourwire = TechDraw.findShapeOutline(planeshape, 1, Vector(0,0,1)) + edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) @@ -378,7 +369,6 @@ class CommandPathMillFace: 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') @@ -440,10 +430,10 @@ class TaskPanel: self.obj.ClearanceHeight = self.form.clearanceHeight.text() if hasattr(self.obj, "StepDown"): self.obj.StepDown = self.form.stepDown.value() - if hasattr(self.obj, "PassExtensioon"): + if hasattr(self.obj, "PassExtension"): self.obj.PassExtension = self.form.extraOffset.value() - if hasattr(self.obj, "UseStartPoint"): - self.obj.UseStartPoint = self.form.useStartPoint.isChecked() + # 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"): @@ -454,6 +444,8 @@ class TaskPanel: self.obj.ZigZagAngle = self.form.zigZagAngle.value() if hasattr(self.obj, "StepOver"): self.obj.StepOver = self.form.stepOverPercent.value() + if hasattr(self.obj, "BoundaryShape"): + self.obj.BoundaryShape = str(self.form.boundaryShape.currentText()) self.obj.Proxy.execute(self.obj) @@ -461,15 +453,31 @@ class TaskPanel: self.form.startDepth.setText(str(self.obj.StartDepth.Value)) self.form.finalDepth.setText(str(self.obj.FinalDepth.Value)) self.form.finishDepth.setText(str(self.obj.FinishDepth.Value)) + self.form.stepDown.setValue(self.obj.StepDown) 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.PassExtension.Value) - self.form.useStartPoint.setChecked(self.obj.UseStartPoint) + self.form.stepOverPercent.setValue(self.obj.StepOver) 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) + #self.form.useStartPoint.setChecked(self.obj.UseStartPoint) + self.form.extraOffset.setValue(self.obj.PassExtension.Value) + + index = self.form.cutMode.findText( + self.obj.CutMode, QtCore.Qt.MatchFixedString) + if index >=0: + + self.form.cutMode.blockSignals(True) + self.form.cutMode.setCurrentIndex(index) + self.form.cutMode.blockSignals(False) + + index = self.form.boundaryShape.findText( + self.obj.BoundaryShape, QtCore.Qt.MatchFixedString) + if index >=0: + self.form.boundaryShape.blockSignals(True) + self.form.boundaryShape.setCurrentIndex(index) + self.form.boundaryShape.blockSignals(False) + for i in self.obj.Base: for sub in i[1]: @@ -508,8 +516,20 @@ class TaskPanel: dlist = self.form.baseList.selectedItems() newlist = [] for d in dlist: + deletebase = d.text().partition(".")[0] + deletesub = d.text().partition(".")[2] + for i in self.obj.Base: - if i[0].Name != d.text().partition(".")[0] or i[1] != d.text().partition(".")[2]: + sublist = [] + #baseobj = i[0] + basesubs = i[1] + for sub in basesubs: + if sub != deletesub: + sublist.append(sub) + if len(sublist) >= 1: + newlist.append ((deletebase, tuple(sublist))) + + if i[0].Name != d.text().partition(".")[0] and d.text().partition(".")[2] not in i[1]: newlist.append(i) self.form.baseList.takeItem(self.form.baseList.row(d)) self.obj.Base = newlist @@ -517,6 +537,7 @@ class TaskPanel: FreeCAD.ActiveDocument.recompute() def itemActivated(self): + print self.form.baseList.selectedItems()[0].text() FreeCADGui.Selection.clearSelection() slist = self.form.baseList.selectedItems() for i in slist: @@ -577,11 +598,9 @@ class TaskPanel: # operation self.form.cutMode.currentIndexChanged.connect(self.getFields) - self.form.useStartPoint.clicked.connect(self.getFields) + #self.form.useStartPoint.clicked.connect(self.getFields) self.form.extraOffset.editingFinished.connect(self.getFields) self.form.boundaryShape.currentIndexChanged.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) diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py index 12d14f6fa9..72e04326e2 100644 --- a/src/Mod/Path/PathScripts/PathProfile.py +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -25,6 +25,7 @@ import FreeCAD import Path import numpy +import TechDraw from FreeCAD import Vector from PathScripts import PathUtils from PathScripts.PathUtils import depth_params @@ -58,14 +59,11 @@ class ObjectProfile: 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")) - obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm", QtCore.QT_TRANSLATE_NOOP("App::Property","The library or algorithm used to generate the path")) - obj.Algorithm = ['OCC Native', 'libarea'] - obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", "The tool number in use") obj.ToolNumber = (0, 0, 1000, 1) obj.setEditorMode('ToolNumber', 1) # make this read only obj.addProperty("App::PropertyString", "ToolDescription", "Tool", "The description of the tool ") - obj.setEditorMode('ToolDescription', 1) # make this read onlyt + 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")) @@ -98,7 +96,8 @@ class ObjectProfile: obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Extra value to stay away from final profile- good for roughing toolpath")) obj.addProperty("App::PropertyLength", "SegLen", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Tesselation value for tool paths made from beziers, bsplines, and ellipses")) obj.addProperty("App::PropertyAngle", "PlungeAngle", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Plunge angle with which the tool enters the work piece. Straight down is 90 degrees, if set small enough or zero the tool will descent exactly one layer depth down per turn")) - obj.addProperty("App::PropertyBool", "processHoles", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Handl holes as well as the outline")) + obj.addProperty("App::PropertyBool", "processHoles", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Profile holes as well as the outline")) + obj.addProperty("App::PropertyBool", "processPerimeter", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Profile the outline")) obj.addProperty("App::PropertyVectorList", "locs", "Tags", QtCore.QT_TRANSLATE_NOOP("App::Property","List of holding tag locations")) @@ -114,7 +113,6 @@ class ObjectProfile: obj.angles = angles obj.lengths = lengths obj.heights = heights - #obj.ToolDescription = "UNDEFINED" obj.Proxy = self @@ -167,29 +165,6 @@ class ObjectProfile: obj.Base = baselist self.execute(obj) - def _buildPathOCC(self, obj, wire): - import DraftGeomUtils - output = "" - if obj.Comment != "": - output += '(' + str(obj.Comment)+')\n' - - if obj.Direction == 'CCW': - clockwise = False - else: - clockwise = True - - FirstEdge = None - PathClosed = DraftGeomUtils.isReallyClosed(wire) - - output += PathUtils.MakePath( - wire, obj.Side, self.radius, clockwise, - obj.ClearanceHeight.Value, obj.StepDown, obj.StartDepth.Value, - obj.FinalDepth.Value, FirstEdge, PathClosed, obj.SegLen.Value, - self.vertFeed, self.horizFeed, self.vertRapid, self.horizRapid, - PlungeAngle=obj.PlungeAngle.Value) - - return output - def _buildPathLibarea(self, obj, edgelist, isHole): import PathScripts.PathKurveUtils as PathKurveUtils import math @@ -228,7 +203,6 @@ print "y - " + str(point.y) start location CRC or probably other features in heekscnc''' - # output += PathKurveUtils.profile(curve, side, radius, vf, hf, offset_extra, rapid_safety_space, clearance, start_depth, step_down, final_depth, use_CRC) '''The following calls the original procedure from h toolLoad = obj.activeTCeekscnc profile function. This, in turn, calls many other procedures to modify the profile. @@ -273,8 +247,6 @@ print "y - " + str(point.y) output = "" toolLoad = PathUtils.getLastToolLoad(obj) - # obj.ToolController = PathUtils.getToolControllers(obj) - # toolLoad = PathUtils.getToolLoad(obj, obj.ToolController) if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 @@ -309,62 +281,32 @@ print "y - " + str(point.y) output += "(Uncompensated Tool Path)" if obj.Base: - hfaces = [] - vfaces = [] - wires = [] holes = [] - + faces = [] for b in obj.Base: for sub in b[1]: - # we only consider the outer wire if this is a Face - # Horizontal and vertical faces are handled differently shape = getattr(b[0].Shape, sub) - if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face - hfaces.append(shape) + if isinstance (shape, Part.Face): + faces.append(shape) + if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face + holes += shape.Wires[1:] - elif numpy.isclose(shape.normalAt(0, 0).z, 0): # vertical face - vfaces.append(shape) else: - FreeCAD.Console.PrintError(translate("Path", "Face doesn't appear to be parallel or perpendicular to the XY plane. No path will be generated for: \n")) - FreeCAD.Console.PrintError(b[0].Name + "." + sub + "\n") + print ("found a base object which is not a face. Can't continue.") + return + profileshape = Part.makeCompound(faces) + profilewire = TechDraw.findShapeOutline(profileshape, 1, Vector(0,0,1)) if obj.processHoles: - for h in hfaces: - holes += h.Wires[1:] - - for h in hfaces: - wires.append(h.OuterWire) - - tempshell = Part.makeShell(vfaces) - slices = tempshell.slice(FreeCAD.Base.Vector(0, 0, 1), tempshell.CenterOfMass.z ) - - wires = wires + slices - - for wire in holes: - if obj.Algorithm == "OCC Native": - output += self._buildPathOCC(obj, wire) - else: - try: - import area - except: - FreeCAD.Console.PrintError(translate("Path", "libarea needs to be installed for this command to work.\n")) - return + for wire in holes: edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) output += self._buildPathLibarea(obj, edgelist, True) - - for wire in wires: - if obj.Algorithm == "OCC Native": - output += self._buildPathOCC(obj, wire) - else: - try: - import area - except: - FreeCAD.Console.PrintError(translate("Path", "libarea needs to be installed for this command to work.\n")) - return - edgelist = wire.Edges - edgelist = Part.__sortEdges__(edgelist) - output += self._buildPathLibarea(obj, edgelist, False) + + if obj.processPerimeter: + edgelist = profilewire.Edges + edgelist = Part.__sortEdges__(edgelist) + output += self._buildPathLibarea(obj, edgelist, False) if obj.Active: path = Path.Path(output) @@ -500,12 +442,9 @@ class CommandPathProfile: FreeCADGui.addModule("PathScripts.PathProfile") FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Profile")') FreeCADGui.doCommand('PathScripts.PathProfile.ObjectProfile(obj)') - FreeCADGui.doCommand('PathScripts.PathProfile._ViewProviderProfile(obj.ViewObject)') FreeCADGui.doCommand('obj.Active = True') - FreeCADGui.doCommand('obj.Algorithm = "libarea"') - FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(ztop + 10.0)) FreeCADGui.doCommand('obj.StepDown = 1.0') FreeCADGui.doCommand('obj.StartDepth= ' + str(ztop)) @@ -514,11 +453,12 @@ class CommandPathProfile: FreeCADGui.doCommand('obj.SafeHeight = ' + str(ztop + 2.0)) FreeCADGui.doCommand('obj.Side = "Left"') FreeCADGui.doCommand('obj.OffsetExtra = 0.0') - FreeCADGui.doCommand('obj.Direction = "CW"') + FreeCADGui.doCommand('obj.Direction = "CCW"') FreeCADGui.doCommand('obj.UseComp = False') - FreeCADGui.doCommand('obj.processHoles = False') + FreeCADGui.doCommand('obj.processHoles = False') + FreeCADGui.doCommand('obj.processPerimeter = True') FreeCADGui.doCommand('obj.PlungeAngle = 90.0') - #FreeCADGui.doCommand('obj.ActiveTC = None') + FreeCADGui.doCommand('PathScripts.PathProfile._ViewProviderProfile(obj.ViewObject)') FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)') FreeCAD.ActiveDocument.commitTransaction() @@ -570,14 +510,14 @@ class TaskPanel: self.obj.UseStartPoint = self.form.useStartPoint.isChecked() if hasattr(self.obj, "UseEndPoint"): self.obj.UseEndPoint = self.form.useEndPoint.isChecked() - if hasattr(self.obj, "Algorithm"): - self.obj.Algorithm = str(self.form.algorithmSelect.currentText()) if hasattr(self.obj, "Side"): self.obj.Side = str(self.form.cutSide.currentText()) if hasattr(self.obj, "Direction"): self.obj.Direction = str(self.form.direction.currentText()) if hasattr(self.obj, "processHoles"): self.obj.processHoles = self.form.processHoles.isChecked() + if hasattr(self.obj, "processPerimeter"): + self.obj.processPerimeter = self.form.processPerimeter.isChecked() self.obj.Proxy.execute(self.obj) def setFields(self): @@ -594,13 +534,7 @@ class TaskPanel: self.form.useStartPoint.setChecked(self.obj.UseStartPoint) self.form.useEndPoint.setChecked(self.obj.UseEndPoint) self.form.processHoles.setChecked(self.obj.processHoles) - - index = self.form.algorithmSelect.findText( - self.obj.Algorithm, QtCore.Qt.MatchFixedString) - if index >= 0: - self.form.algorithmSelect.blockSignals(True) - self.form.algorithmSelect.setCurrentIndex(index) - self.form.algorithmSelect.blockSignals(False) + self.form.processPerimeter.setChecked(self.obj.processPerimeter) index = self.form.cutSide.findText( self.obj.Side, QtCore.Qt.MatchFixedString) @@ -616,10 +550,13 @@ class TaskPanel: self.form.direction.setCurrentIndex(index) self.form.direction.blockSignals(False) + self.form.baseList.blockSignals(True) for i in self.obj.Base: for sub in i[1]: self.form.baseList.addItem(i[0].Name + "." + sub) + self.form.baseList.blockSignals(False) + self.form.tagTree.blockSignals(True) for i in range(len(self.obj.locs)): item = QtGui.QTreeWidgetItem(self.form.tagTree) item.setText(0, str(i+1)) @@ -630,6 +567,9 @@ class TaskPanel: item.setText(4, str(self.obj.angles[i])) item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) item.setTextAlignment(0, QtCore.Qt.AlignLeft) + self.form.tagTree.blockSignals(False) + self.form.update() + def open(self): self.s = SelObserver() @@ -807,7 +747,6 @@ class TaskPanel: self.form.clearanceHeight.editingFinished.connect(self.getFields) # operation - self.form.algorithmSelect.currentIndexChanged.connect(self.getFields) self.form.cutSide.currentIndexChanged.connect(self.getFields) self.form.direction.currentIndexChanged.connect(self.getFields) self.form.useCompensation.clicked.connect(self.getFields) @@ -817,6 +756,7 @@ class TaskPanel: self.form.segLen.editingFinished.connect(self.getFields) self.form.rollRadius.editingFinished.connect(self.getFields) self.form.processHoles.clicked.connect(self.getFields) + self.form.processPerimeter.clicked.connect(self.getFields) # Tag Form QtCore.QObject.connect( diff --git a/src/Mod/Path/PathScripts/PathProfileEdges.py b/src/Mod/Path/PathScripts/PathProfileEdges.py index cd13dcf9b6..a56a21440e 100644 --- a/src/Mod/Path/PathScripts/PathProfileEdges.py +++ b/src/Mod/Path/PathScripts/PathProfileEdges.py @@ -528,11 +528,13 @@ class TaskPanel: self.obj.Direction, QtCore.Qt.MatchFixedString) if index >= 0: self.form.direction.setCurrentIndex(index) - + self.form.baseList.blockSignals(True) for i in self.obj.Base: for sub in i[1]: self.form.baseList.addItem(i[0].Name + "." + sub) + self.form.baseList.blockSignals(False) + self.form.tagTree.blockSignals(True) for i in range(len(self.obj.locs)): item = QtGui.QTreeWidgetItem(self.form.tagTree) item.setText(0, str(i+1)) @@ -543,6 +545,8 @@ class TaskPanel: item.setText(4, str(self.obj.angles[i])) item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) item.setTextAlignment(0, QtCore.Qt.AlignLeft) + self.form.tagTree.blockSignals(False) + def open(self): self.s = SelObserver()