From 2f17f1104989cda47827f49dec9ea90b1b798cc8 Mon Sep 17 00:00:00 2001 From: brad Date: Fri, 22 Apr 2016 15:55:43 -0500 Subject: [PATCH] PEP8 cleanup --- src/Mod/Path/PathScripts/PathDrilling.py | 193 +++--- src/Mod/Path/PathScripts/PathEngrave.py | 187 +++--- src/Mod/Path/PathScripts/PathLoadTool.py | 183 +++--- src/Mod/Path/PathScripts/PathPocket.py | 676 ++++++++-------------- src/Mod/Path/PathScripts/PathProfile.py | 8 +- src/Mod/Path/PathScripts/PathRemote.py | 229 ++++---- src/Mod/Path/PathScripts/PathSelection.py | 5 + src/Mod/Path/PathScripts/PathUtils.py | 504 +++++++++++----- 8 files changed, 994 insertions(+), 991 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index 702c86141..054d42db3 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -1,29 +1,30 @@ # -*- coding: utf-8 -*- -#*************************************************************************** -#* * -#* Copyright (c) 2014 Yorik van Havre * -#* * -#* 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 * -#* * -#*************************************************************************** +# *************************************************************************** +# * * +# * Copyright (c) 2014 Yorik van Havre * +# * * +# * 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,Path -from PySide import QtCore,QtGui +import FreeCAD +import Path +from PySide import QtCore, QtGui from PathScripts import PathUtils FreeCADGui = None @@ -35,6 +36,7 @@ if FreeCAD.GuiUp: # Qt tanslation handling try: _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: @@ -44,47 +46,46 @@ except AttributeError: class ObjectDrilling: + def __init__(self, obj): + obj.addProperty("App::PropertyLinkSubList", "Base","Path", translate("PathProject", "The base geometry of this toolpath")) + obj.addProperty("App::PropertyBool", "Active", "Path", translate("PathProject", "Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyString", "Comment", "Path", translate("PathProject", "An optional comment for this profile")) - def __init__(self,obj): - obj.addProperty("App::PropertyLinkSubList","Base","Path",translate("PathProject","The base geometry of this toolpath")) - obj.addProperty("App::PropertyBool","Active","Path",translate("PathProject","Make False, to prevent operation from generating code")) - obj.addProperty("App::PropertyString","Comment","Path",translate("PathProject","An optional comment for this profile")) + obj.addProperty("App::PropertyLength", "PeckDepth", "Depth", translate("PathProject", "Incremental Drill depth before retracting to clear chips")) + obj.addProperty("App::PropertyLength", "StartDepth", "Depth", translate("PathProject", "Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("PathProject", "The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("PathProject", "Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("PathProject", "Height to clear top of materil")) + obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth", translate("PathProject", "The height where feed starts and height during retract tool when path is finished")) - obj.addProperty("App::PropertyLength", "PeckDepth", "Depth", translate("PathProject","Incremental Drill depth before retracting to clear chips")) - obj.addProperty("App::PropertyLength", "StartDepth", "Depth", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) - obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("PathProject","The height needed to clear clamps and obstructions")) - obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("PathProject","Final Depth of Tool- lowest value in Z")) - obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("PathProject","Height to clear top of materil")) - obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth", translate("PathProject","The height where feed starts and height during retract tool when path is finished")) - - #Tool Properties - obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) - obj.ToolNumber = (0,0,1000,1) - obj.setEditorMode('ToolNumber',1) #make this read only + # Tool Properties + obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", translate("PathProfile", "The tool number in use")) + obj.ToolNumber = (0, 0, 1000, 1) + obj.setEditorMode('ToolNumber', 1) # make this read only obj.Proxy = self def __getstate__(self): return None - def __setstate__(self,state): + def __setstate__(self, state): return None - def execute(self,obj): + def execute(self, obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad == None: + if toolLoad is None: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 - obj.ToolNumber= 0 + obj.ToolNumber = 0 else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value - obj.ToolNumber= toolLoad.ToolNumber + obj.ToolNumber = toolLoad.ToolNumber tool = PathUtils.getTool(obj, toolLoad.ToolNumber) - if tool == None: + if tool is None: self.radius = 0.25 else: self.radius = tool.Diameter/2 @@ -94,11 +95,11 @@ class ObjectDrilling: for loc in obj.Base: if "Face" in loc[1] or "Edge" in loc[1]: - s = getattr(loc[0].Shape,loc[1]) + s = getattr(loc[0].Shape, loc[1]) else: s = loc[0].Shape - if s.ShapeType in ['Face', 'Wire', 'Edge' ]: + if s.ShapeType in ['Face', 'Wire', 'Edge']: X = s.Edges[0].Curve.Center.x Y = s.Edges[0].Curve.Center.y Z = s.Edges[0].Curve.Center.z @@ -107,40 +108,43 @@ class ObjectDrilling: Y = s.Point.y Z = s.Point.z - locations.append(FreeCAD.Vector(X,Y,Z)) + locations.append(FreeCAD.Vector(X, Y, Z)) output = "G90 G98\n" # rapid to clearance height output += "G0 Z" + str(obj.ClearanceHeight.Value) # rapid to first hole location, with spindle still retracted: p0 = locations[0] - output += "G0 X"+str(p0.x) + " Y" + str(p0.y)+ "\n" + output += "G0 X" + str(p0.x) + " Y" + str(p0.y) + "\n" # move tool to clearance plane output += "G0 Z" + str(obj.ClearanceHeight.Value) + "\n" if obj.PeckDepth.Value > 0: cmd = "G83" - qword = " Q"+ str(obj.PeckDepth.Value) + qword = " Q" + str(obj.PeckDepth.Value) else: cmd = "G81" qword = "" for p in locations: - - output += cmd + " X" + str(p.x) + " Y" + str(p.y) + " Z" + str(obj.FinalDepth.Value) + qword + " R" + str(obj.RetractHeight.Value) + " F" + str(self.vertFeed) + "\n" + output += cmd + \ + " X" + str(p.x) + \ + " Y" + str(p.y) + \ + " Z" + str(obj.FinalDepth.Value) + qword + \ + " R" + str(obj.RetractHeight.Value) + \ + " F" + str(self.vertFeed) + "\n" \ output += "G80\n" path = Path.Path(output) obj.Path = path - def addDrillableLocation(self, obj, ss, sub=""): baselist = obj.Base item = (ss, sub) - if len(baselist) == 0: #When adding the first base object, guess at heights + if len(baselist) == 0: # When adding the first base object, guess at heights try: - bb = ss.Shape.BoundBox #parent boundbox + bb = ss.Shape.BoundBox # parent boundbox subobj = ss.Shape.getElement(sub) - fbb = subobj.BoundBox #feature boundbox + fbb = subobj.BoundBox # feature boundbox obj.StartDepth = bb.ZMax obj.ClearanceHeight = bb.ZMax + 5.0 obj.SafeHeight = bb.ZMax + 3.0 @@ -157,34 +161,35 @@ class ObjectDrilling: obj.RetractHeight = 6.0 if item in baselist: - FreeCAD.Console.PrintWarning("Drillable location already in the list"+ "\n") + FreeCAD.Console.PrintWarning("Drillable location already in the list" + "\n") else: - baselist.append (item) + baselist.append(item) obj.Base = baselist self.execute(obj) + class _ViewProviderDrill: - def __init__(self,obj): #mandatory + def __init__(self, obj): obj.Proxy = self - def __getstate__(self): #mandatory + def __getstate__(self): return None - def __setstate__(self,state): #mandatory + def __setstate__(self, state): return None - def getIcon(self): #optional + def getIcon(self): return ":/icons/Path-Drilling.svg" - def onChanged(self,obj,prop): #optional + def onChanged(self, obj, prop): # this is executed when a property of the VIEW PROVIDER changes pass - def updateData(self,obj,prop): #optional + def updateData(self, obj, prop): # this is executed when a property of the APP OBJECT changes pass - def setEdit(self,vobj,mode=0): + def setEdit(self, vobj, mode=0): FreeCADGui.Control.closeDialog() taskd = TaskPanel() taskd.obj = vobj.Object @@ -192,28 +197,28 @@ class _ViewProviderDrill: taskd.setupUi() return True - - def unsetEdit(self,vobj,mode): #optional + def unsetEdit(self, vobj, mode): # this is executed when the user cancels or terminates edit mode pass + class CommandPathDrilling: def GetResources(self): - return {'Pixmap' : 'Path-Drilling', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Drilling","Drilling"), + return {'Pixmap': 'Path-Drilling', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Drilling", "Drilling"), 'Accel': "P, D", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Drilling","Creates a Path Drilling object")} + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Drilling", "Creates a Path Drilling object")} def IsActive(self): - return not FreeCAD.ActiveDocument is None + return FreeCAD.ActiveDocument is not None def Activated(self): # if everything is ok, execute and register the transaction in the undo/redo stack - FreeCAD.ActiveDocument.openTransaction(translate("Path_Drilling","Create Drilling")) + FreeCAD.ActiveDocument.openTransaction(translate("Path_Drilling", "Create Drilling")) FreeCADGui.addModule("PathScripts.PathDrilling") - FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Drilling")') + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Drilling")') FreeCADGui.doCommand('PathScripts.PathDrilling.ObjectDrilling(obj)') FreeCADGui.doCommand('obj.Active = True') FreeCADGui.doCommand('PathScripts.PathDrilling._ViewProviderDrill(obj.ViewObject)') @@ -229,9 +234,9 @@ class CommandPathDrilling: FreeCAD.ActiveDocument.recompute() FreeCADGui.doCommand('obj.ViewObject.startEditing()') + class TaskPanel: def __init__(self): - #self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DrillingEdit.ui") self.form = FreeCADGui.PySideUic.loadUi(":/panels/DrillingEdit.ui") def accept(self): @@ -249,32 +254,31 @@ class TaskPanel: def getFields(self): if self.obj: - if hasattr(self.obj,"StartDepth"): + if hasattr(self.obj, "StartDepth"): self.obj.StartDepth = self.form.startDepth.text() - if hasattr(self.obj,"FinalDepth"): + if hasattr(self.obj, "FinalDepth"): self.obj.FinalDepth = self.form.finalDepth.text() - if hasattr(self.obj,"PeckDepth"): + if hasattr(self.obj, "PeckDepth"): self.obj.PeckDepth = self.form.peckDepth.text() - if hasattr(self.obj,"SafeHeight"): + if hasattr(self.obj, "SafeHeight"): self.obj.SafeHeight = self.form.safeHeight.text() - if hasattr(self.obj,"ClearanceHeight"): + if hasattr(self.obj, "ClearanceHeight"): self.obj.ClearanceHeight = self.form.clearanceHeight.text() - if hasattr(self.obj,"RetractHeight"): + if hasattr(self.obj, "RetractHeight"): self.obj.RetractHeight = self.form.retractHeight.text() self.obj.Proxy.execute(self.obj) def open(self): - self.s =SelObserver() - # install the function mode resident + self.s = SelObserver() FreeCADGui.Selection.addObserver(self.s) def addBase(self): - # check that the selection contains exactly what we want + # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelectionEx() if not len(selection) >= 1: - FreeCAD.Console.PrintError(translate("PathProject","Please select at least one Drillable Location\n")) + FreeCAD.Console.PrintError(translate("PathProject", "Please select at least one Drillable Location\n")) return for s in selection: if s.HasSubObjects: @@ -283,19 +287,18 @@ class TaskPanel: else: self.obj.Proxy.addDrillableLocation(self.obj, s.Object) - self.setupUi() #defaults may have changed. Reload. + self.setupUi() # defaults may have changed. Reload. self.form.baseList.clear() for i in self.obj.Base: self.form.baseList.addItem(i[0].Name + "." + i[1]) - def deleteBase(self): dlist = self.form.baseList.selectedItems() for d in dlist: newlist = [] for i in self.obj.Base: if not i[0].Name == d.text().partition(".")[0]: - newlist.append (i) + newlist.append(i) self.obj.Base = newlist self.form.baseList.takeItem(self.form.baseList.row(d)) # self.obj.Proxy.execute(self.obj) @@ -307,9 +310,9 @@ class TaskPanel: for i in slist: objstring = i.text().partition(".") obj = FreeCAD.ActiveDocument.getObject(objstring[0]) - # sub = o.Shape.getElement(objstring[2]) + # sub = o.Shape.getElement(objstring[2]) if objstring[2] != "": - FreeCADGui.Selection.addSelection(obj,objstring[2]) + FreeCADGui.Selection.addSelection(obj, objstring[2]) else: FreeCADGui.Selection.addSelection(obj) @@ -324,7 +327,7 @@ class TaskPanel: obj = FreeCAD.ActiveDocument.getObject(objstring[0]) item = (obj, str(objstring[2])) newlist.append(item) - self.obj.Base=newlist + self.obj.Base = newlist self.obj.Proxy.execute(self.obj) FreeCAD.ActiveDocument.recompute() @@ -340,12 +343,11 @@ class TaskPanel: self.form.clearanceHeight.setText(str(self.obj.ClearanceHeight.Value)) self.form.retractHeight.setText(str(self.obj.RetractHeight.Value)) - for i in self.obj.Base: self.form.baseList.addItem(i[0].Name + "." + i[1]) - #Connect Signals and Slots - self.form.startDepth.editingFinished.connect(self.getFields) #This is newer syntax + # Connect Signals and Slots + self.form.startDepth.editingFinished.connect(self.getFields) self.form.finalDepth.editingFinished.connect(self.getFields) self.form.safeHeight.editingFinished.connect(self.getFields) self.form.clearanceHeight.editingFinished.connect(self.getFields) @@ -356,6 +358,7 @@ class TaskPanel: self.form.baseList.itemSelectionChanged.connect(self.itemActivated) + class SelObserver: def __init__(self): import PathScripts.PathSelection as PST @@ -365,13 +368,13 @@ class SelObserver: import PathScripts.PathSelection as PST PST.clear() - def addSelection(self,doc,obj,sub,pnt): # Selection object - FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj +')') + 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_Drilling',CommandPathDrilling()) + FreeCADGui.addCommand('Path_Drilling', CommandPathDrilling()) FreeCAD.Console.PrintLog("Loading PathDrilling... done\n") diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index 2865ff686..cbbb0e3d6 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -1,30 +1,33 @@ # -*- coding: utf-8 -*- -#*************************************************************************** -#* * -#* Copyright (c) 2014 Yorik van Havre * -#* * -#* 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 * -#* * -#*************************************************************************** +# *************************************************************************** +# * * +# * Copyright (c) 2014 Yorik van Havre * +# * * +# * 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,FreeCADGui,Path,Draft +import FreeCAD +import FreeCADGui +import Path +import Draft -from PySide import QtCore,QtGui +from PySide import QtCore, QtGui from PathScripts import PathUtils """Path Engrave object and FreeCAD command""" @@ -32,6 +35,7 @@ from PathScripts import PathUtils # Qt tanslation handling try: _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: @@ -41,26 +45,25 @@ except AttributeError: class ObjectPathEngrave: - def __init__(self,obj): - obj.addProperty("App::PropertyLinkSubList","Base","Path","The base geometry of this object") - obj.addProperty("App::PropertyBool","Active","Path",translate("Path","Make False, to prevent operation from generating code")) - obj.addProperty("App::PropertyString","Comment","Path",translate("Path","An optional comment for this profile")) + obj.addProperty("App::PropertyLinkSubList", "Base", "Path", "The base geometry of this object") + obj.addProperty("App::PropertyBool", "Active", "Path", translate("Path", "Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyString", "Comment", "Path", translate("Path", "An optional comment for this profile")) - obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm",translate("Path", "The library or Algorithm used to generate the path")) + obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm", translate("Path", "The library or Algorithm used to generate the path")) obj.Algorithm = ['OCC Native'] - #Tool Properties - obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("Path","The tool number in use")) - obj.ToolNumber = (0,0,1000,1) - obj.setEditorMode('ToolNumber',1) #make this read only + # Tool Properties + obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", translate("Path", "The tool number in use")) + obj.ToolNumber = (0, 0, 1000, 1) + obj.setEditorMode('ToolNumber', 1) # make this read only - #Depth Properties - obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Path","The height needed to clear clamps and obstructions")) - obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("Path","Rapid Safety Height between locations.")) - obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("Path","Starting Depth of Tool- first cut depth in Z")) - obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Path","Final Depth of Tool- lowest value in Z")) - obj.addProperty("App::PropertyInteger","StartVertex","Path","The vertex index to start the path from") + # Depth Properties + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Path", "The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("Path", "Rapid Safety Height between locations.")) + obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("Path", "Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Path", "Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyInteger", "StartVertex", "Path", "The vertex index to start the path from") if FreeCAD.GuiUp: _ViewProviderEngrave(obj.ViewObject) @@ -70,25 +73,25 @@ class ObjectPathEngrave: def __getstate__(self): return None - def __setstate__(self,state): + def __setstate__(self, state): return None - def execute(self,obj): + def execute(self, obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad == None: + if toolLoad is None: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 - obj.ToolNumber= 0 + obj.ToolNumber = 0 else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value - obj.ToolNumber= toolLoad.ToolNumber + obj.ToolNumber = toolLoad.ToolNumber tool = PathUtils.getTool(obj, toolLoad.ToolNumber) - if tool == None: + if tool is None: self.radius = 0.25 else: self.radius = tool.Diameter/2 @@ -102,14 +105,15 @@ class ObjectPathEngrave: if obj.Algorithm == "OCC Native": output += self.buildpathocc(obj, wires) - #print output + # print output if output == "": - output +="G0" + output += "G0" path = Path.Path(output) obj.Path = path def buildpathocc(self, obj, wires): - import Part,DraftGeomUtils + import Part + import DraftGeomUtils output = "G90\nG21\nG40\n" output += "G0 Z" + str(obj.ClearanceHeight.Value) @@ -119,7 +123,7 @@ class ObjectPathEngrave: offset = wire # reorder the wire - offset = DraftGeomUtils.rebaseWire(offset,obj.StartVertex) + offset = DraftGeomUtils.rebaseWire(offset, obj.StartVertex) # we create the path from the offset shape last = None @@ -127,11 +131,11 @@ class ObjectPathEngrave: if not last: # we set the first move to our first point last = edge.Vertexes[0].Point - output += "G0" + " X" + str("%f" % last.x) + " Y" + str("%f" % last.y) #Rapid sto starting position - output += "G1" + " Z" + str("%f" % last.z) +"F " + str(self.vertFeed)+ "\n" #Vertical feed to depth - if isinstance(edge.Curve,Part.Circle): + output += "G0" + " X" + str("%f" % last.x) + " Y" + str("%f" % last.y) # Rapid sto starting position + output += "G1" + " Z" + str("%f" % last.z) + "F " + str(self.vertFeed) + "\n" # Vertical feed to depth + if isinstance(edge.Curve, Part.Circle): point = edge.Vertexes[-1].Point - if point == last: # edges can come flipped + if point == last: # edges can come flipped point = edge.Vertexes[0].Point center = edge.Curve.Center relcenter = center.sub(last) @@ -143,27 +147,27 @@ class ObjectPathEngrave: output += "G3" output += " X" + str("%f" % point.x) + " Y" + str("%f" % point.y) + " Z" + str("%f" % point.z) output += " I" + str("%f" % relcenter.x) + " J" + str("%f" % relcenter.y) + " K" + str("%f" % relcenter.z) - output += " F " + str(self.horizFeed) + output += " F " + str(self.horizFeed) output += "\n" last = point else: point = edge.Vertexes[-1].Point - if point == last: # edges can come flipped + if point == last: # edges can come flipped point = edge.Vertexes[0].Point output += "G1 X" + str("%f" % point.x) + " Y" + str("%f" % point.y) + " Z" + str("%f" % point.z) - output += " F " + str(self.horizFeed) - output += "\n" + output += " F " + str(self.horizFeed) + output += "\n" last = point output += "G0 Z " + str(obj.SafeHeight.Value) return output def addShapeString(self, obj, ss): baselist = obj.Base - if len(baselist) == 0: #When adding the first base object, guess at heights + if len(baselist) == 0: # When adding the first base object, guess at heights try: - bb = ss.Shape.BoundBox #parent boundbox + bb = ss.Shape.BoundBox # parent boundbox subobj = ss.Shape.getElement(sub) - fbb = subobj.BoundBox #feature boundbox + fbb = subobj.BoundBox # feature boundbox obj.StartDepth = bb.ZMax obj.ClearanceHeight = bb.ZMax + 5.0 obj.SafeHeight = bb.ZMax + 3.0 @@ -179,24 +183,24 @@ class ObjectPathEngrave: item = (ss, "") if item in baselist: - FreeCAD.Console.PrintWarning("ShapeString already in the Engraving list"+ "\n") + FreeCAD.Console.PrintWarning("ShapeString already in the Engraving list" + "\n") else: - baselist.append (item) + baselist.append(item) obj.Base = baselist self.execute(obj) class _ViewProviderEngrave: - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self - def attach(self,vobj): + def attach(self, vobj): self.Object = vobj.Object return - def setEdit(self,vobj,mode=0): + def setEdit(self, vobj, mode=0): FreeCADGui.Control.closeDialog() taskd = TaskPanel() taskd.obj = vobj.Object @@ -210,43 +214,42 @@ class _ViewProviderEngrave: def __getstate__(self): return None - def __setstate__(self,state): + def __setstate__(self, state): return None + class CommandPathEngrave: def GetResources(self): - return {'Pixmap' : 'Path-Engrave', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Engrave","ShapeString Engrave"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Engrave","Creates an Engraving Path around a Draft ShapeString")} + return {'Pixmap': 'Path-Engrave', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Engrave", "ShapeString Engrave"), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Engrave", "Creates an Engraving Path around a Draft ShapeString")} def IsActive(self): - return not FreeCAD.ActiveDocument is None + return FreeCAD.ActiveDocument is not None def Activated(self): # if everything is ok, execute and register the transaction in the undo/redo stack - FreeCAD.ActiveDocument.openTransaction("Create Engrave Path") FreeCADGui.addModule("PathScripts.PathFaceProfile") FreeCADGui.addModule("PathScripts.PathUtils") - FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","PathEngrave")') + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "PathEngrave")') FreeCADGui.doCommand('PathScripts.PathEngrave.ObjectPathEngrave(obj)') FreeCADGui.doCommand('obj.ClearanceHeight = 10') FreeCADGui.doCommand('obj.StartDepth= 0') - FreeCADGui.doCommand('obj.FinalDepth= -0.1' ) - FreeCADGui.doCommand('obj.SafeHeight= 5.0' ) + FreeCADGui.doCommand('obj.FinalDepth= -0.1') + FreeCADGui.doCommand('obj.SafeHeight= 5.0') - #FreeCADGui.doCommand('obj.ViewObject.Proxy = 0') FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') 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/EngraveEdit.ui") self.form = FreeCADGui.PySideUic.loadUi(":/panels/EngraveEdit.ui") def accept(self): @@ -264,33 +267,32 @@ class TaskPanel: def getFields(self): if self.obj: - if hasattr(self.obj,"StartDepth"): + if hasattr(self.obj, "StartDepth"): self.obj.StartDepth = self.form.startDepth.text() - if hasattr(self.obj,"FinalDepth"): + if hasattr(self.obj, "FinalDepth"): self.obj.FinalDepth = self.form.finalDepth.text() - if hasattr(self.obj,"SafeHeight"): + if hasattr(self.obj, "SafeHeight"): self.obj.SafeHeight = self.form.safeHeight.text() - if hasattr(self.obj,"ClearanceHeight"): + if hasattr(self.obj, "ClearanceHeight"): self.obj.ClearanceHeight = self.form.clearanceHeight.text() - self.obj.Proxy.execute(self.obj) def open(self): - self.s =SelObserver() + 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 + # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelectionEx() if not len(selection) >= 1: - FreeCAD.Console.PrintError(translate("Path_Engrave","Please select at least one ShapeString\n")) + FreeCAD.Console.PrintError(translate("Path_Engrave", "Please select at least one ShapeString\n")) return for s in selection: if not Draft.getType(s.Object) == "ShapeString": - FreeCAD.Console.PrintError(translate("Path_Engrave","Please select at least one ShapeString\n")) + FreeCAD.Console.PrintError(translate("Path_Engrave", "Please select at least one ShapeString\n")) return self.obj.Proxy.addShapeString(self.obj, s.Object) @@ -304,11 +306,9 @@ class TaskPanel: newlist = [] for i in self.obj.Base: if not i[0].Name == d.text(): - newlist.append (i) + newlist.append(i) self.obj.Base = newlist self.form.baseList.takeItem(self.form.baseList.row(d)) - # self.obj.Proxy.execute(self.obj) - # FreeCAD.ActiveDocument.recompute() def itemActivated(self): FreeCADGui.Selection.clearSelection() @@ -321,10 +321,10 @@ class TaskPanel: def reorderBase(self): newlist = [] for i in range(self.form.baseList.count()): - s = self.form.baseList.item(i).text() + s = self.form.baseList.item(i).text() obj = FreeCAD.ActiveDocument.getObject(s) newlist.append(obj) - self.obj.Base=newlist + self.obj.Base = newlist self.obj.Proxy.execute(self.obj) FreeCAD.ActiveDocument.recompute() @@ -340,7 +340,7 @@ class TaskPanel: for i in self.obj.Base: self.form.baseList.addItem(i[0].Name) - #Connect Signals and Slots + # Connect Signals and Slots self.form.startDepth.editingFinished.connect(self.getFields) self.form.finalDepth.editingFinished.connect(self.getFields) self.form.safeHeight.editingFinished.connect(self.getFields) @@ -362,10 +362,9 @@ class SelObserver: import PathScripts.PathSelection as PST PST.clear() - def addSelection(self,doc,obj,sub,pnt): # Selection object - FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj +')') + 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_Engrave',CommandPathEngrave()) + FreeCADGui.addCommand('Path_Engrave', CommandPathEngrave()) diff --git a/src/Mod/Path/PathScripts/PathLoadTool.py b/src/Mod/Path/PathScripts/PathLoadTool.py index a622a5bd7..ecaa721fd 100644 --- a/src/Mod/Path/PathScripts/PathLoadTool.py +++ b/src/Mod/Path/PathScripts/PathLoadTool.py @@ -1,142 +1,144 @@ # -*- coding: utf-8 -*- -#*************************************************************************** -#* * -#* Copyright (c) 2015 Dan Falck * -#* * -#* 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 * -#* * -#*************************************************************************** +# *************************************************************************** +# * * +# * Copyright (c) 2015 Dan Falck * +# * * +# * 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 * +# * * +# *************************************************************************** ''' Used for CNC machine to load cutting Tool ie M6T3''' -import FreeCAD,FreeCADGui,Path,PathGui -import PathScripts, PathUtils -from PathScripts import PathProject -from PySide import QtCore,QtGui +import FreeCAD +import FreeCADGui +import Path +# import PathGui +import PathScripts +import PathUtils +# from PathScripts import PathProject +from PySide import QtCore, QtGui # 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 LoadTool: - def __init__(self,obj): - obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber","Tool","The active tool") - obj.ToolNumber = (0,0,10000,1) - obj.addProperty("App::PropertyFloat", "SpindleSpeed", "Tool","The speed of the cutting spindle in RPM") - obj.addProperty("App::PropertyEnumeration", "SpindleDir", "Tool","Direction of spindle rotation") - obj.SpindleDir = ['Forward','Reverse'] - obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Path","Feed rate for vertical moves in Z")) - obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Path","Feed rate for horizontal moves")) + def __init__(self, obj): + obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", translate("Tool Number", "The active tool")) + obj.ToolNumber = (0, 0, 10000, 1) + obj.addProperty("App::PropertyFloat", "SpindleSpeed", "Tool", translate("Spindle Speed", "The speed of the cutting spindle in RPM")) + obj.addProperty("App::PropertyEnumeration", "SpindleDir", "Tool", translate("Spindle Dir", "Direction of spindle rotation")) + obj.SpindleDir = ['Forward', 'Reverse'] + obj.addProperty("App::PropertySpeed", "VertFeed", "Feed", translate("Path", "Feed rate for vertical moves in Z")) + obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed", translate("Path", "Feed rate for horizontal moves")) obj.Proxy = self mode = 2 - obj.setEditorMode('Placement',mode) + obj.setEditorMode('Placement', mode) - - - def execute(self,obj): + def execute(self, obj): commands = "" commands = 'M6T'+str(obj.ToolNumber)+'\n' - if obj.SpindleDir =='Forward': - commands +='M3S'+str(obj.SpindleSpeed)+'\n' + if obj.SpindleDir == 'Forward': + commands += 'M3S' + str(obj.SpindleSpeed) + '\n' else: - commands +='M4S'+str(obj.SpindleSpeed)+'\n' + commands += 'M4S' + str(obj.SpindleSpeed) + '\n' obj.Path = Path.Path(commands) obj.Label = "Tool"+str(obj.ToolNumber) - - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): mode = 2 - obj.setEditorMode('Placement',mode) - #if prop == "ToolNumber": - proj = PathUtils.findProj() + obj.setEditorMode('Placement', mode) + # if prop == "ToolNumber": + proj = PathUtils.findProj() for g in proj.Group: if not(isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool)): - g.touch() - - + g.touch() + + class _ViewProviderLoadTool: - def __init__(self,vobj): #mandatory + def __init__(self, vobj): vobj.Proxy = self mode = 2 - vobj.setEditorMode('LineWidth',mode) - vobj.setEditorMode('MarkerColor',mode) - vobj.setEditorMode('NormalColor',mode) - vobj.setEditorMode('ShowFirstRapid',mode) - vobj.setEditorMode('DisplayMode',mode) - vobj.setEditorMode('BoundingBox',mode) - vobj.setEditorMode('Selectable',mode) - vobj.setEditorMode('ShapeColor',mode) - vobj.setEditorMode('Transparency',mode) - vobj.setEditorMode('Visibility',mode) + vobj.setEditorMode('LineWidth', mode) + vobj.setEditorMode('MarkerColor', mode) + vobj.setEditorMode('NormalColor', mode) + vobj.setEditorMode('ShowFirstRapid', mode) + vobj.setEditorMode('DisplayMode', mode) + vobj.setEditorMode('BoundingBox', mode) + vobj.setEditorMode('Selectable', mode) + vobj.setEditorMode('ShapeColor', mode) + vobj.setEditorMode('Transparency', mode) + vobj.setEditorMode('Visibility', mode) - def __getstate__(self): #mandatory + def __getstate__(self): return None - def __setstate__(self,state): #mandatory + def __setstate__(self, state): return None - def getIcon(self): #optional + def getIcon(self): return ":/icons/Path-LoadTool.svg" - def onChanged(self,vobj,prop): #optional + def onChanged(self, vobj, prop): mode = 2 - vobj.setEditorMode('LineWidth',mode) - vobj.setEditorMode('MarkerColor',mode) - vobj.setEditorMode('NormalColor',mode) - vobj.setEditorMode('ShowFirstRapid',mode) - vobj.setEditorMode('DisplayMode',mode) - vobj.setEditorMode('BoundingBox',mode) - vobj.setEditorMode('Selectable',mode) - #vobj.setEditorMode('ShapeColor',mode) - #vobj.setEditorMode('Transparency',mode) - #vobj.setEditorMode('Visibility',mode) + vobj.setEditorMode('LineWidth', mode) + vobj.setEditorMode('MarkerColor', mode) + vobj.setEditorMode('NormalColor', mode) + vobj.setEditorMode('ShowFirstRapid', mode) + vobj.setEditorMode('DisplayMode', mode) + vobj.setEditorMode('BoundingBox', mode) + vobj.setEditorMode('Selectable', mode) - def updateData(self,vobj,prop): #optional + def updateData(self, vobj, prop): # this is executed when a property of the APP OBJECT changes pass - def setEdit(self,vobj,mode): #optional + def setEdit(self, vobj, mode): # this is executed when the object is double-clicked in the tree pass - def unsetEdit(self,vobj,mode): #optional + def unsetEdit(self, vobj, mode): # this is executed when the user cancels or terminates edit mode pass + class CommandPathLoadTool: def GetResources(self): - return {'Pixmap' : 'Path-LoadTool', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_LoadTool","Tool Number to Load"), + return {'Pixmap': 'Path-LoadTool', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_LoadTool", "Tool Number to Load"), 'Accel': "P, T", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_LoadTool","Tool Number to Load")} + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_LoadTool", "Tool Number to Load")} def IsActive(self): - return not FreeCAD.ActiveDocument is None + return FreeCAD.ActiveDocument is not None + def Activated(self): - FreeCAD.ActiveDocument.openTransaction(translate("Current Tool","Tool Number to Load")) + FreeCAD.ActiveDocument.openTransaction(translate("Current Tool", "Tool Number to Load")) snippet = ''' import Path, PathScripts from PathScripts import PathUtils, PathLoadTool @@ -153,23 +155,20 @@ PathUtils.addToProject(obj) @staticmethod def Create(): - #FreeCADGui.addModule("PathScripts.PathLoadTool") - import Path, PathScripts, PathUtils + # FreeCADGui.addModule("PathScripts.PathLoadTool") + # import Path + import PathScripts + import PathUtils - obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Tool") + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Tool") PathScripts.PathLoadTool.LoadTool(obj) PathScripts.PathLoadTool._ViewProviderLoadTool(obj.ViewObject) PathUtils.addToProject(obj) - -if FreeCAD.GuiUp: + +if FreeCAD.GuiUp: # register the FreeCAD command FreeCADGui.addCommand('Path_LoadTool', CommandPathLoadTool()) - + FreeCAD.Console.PrintLog("Loading PathLoadTool... done\n") - - - - - diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py index d1a82c1a3..1e9cac892 100644 --- a/src/Mod/Path/PathScripts/PathPocket.py +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -1,31 +1,31 @@ # -*- coding: utf-8 -*- -#*************************************************************************** -#* * -#* Copyright (c) 2014 Yorik van Havre * -#* * -#* 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 * -#* * -#*************************************************************************** +# *************************************************************************** +# * * +# * Copyright (c) 2014 Yorik van Havre * +# * * +# * 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,Path -from PySide import QtCore,QtGui +import FreeCAD +import Path +from PySide import QtCore, QtGui from PathScripts import PathUtils -import math FreeCADGui = None if FreeCAD.GuiUp: @@ -36,6 +36,7 @@ if FreeCAD.GuiUp: # Qt tanslation handling try: _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: @@ -45,82 +46,82 @@ except AttributeError: class ObjectPocket: - def __init__(self,obj): - obj.addProperty("App::PropertyLinkSubList","Base","Path",translate("PathProject","The base geometry of this object")) - obj.addProperty("App::PropertyBool","Active","Path",translate("PathProject","Make False, to prevent operation from generating code")) - obj.addProperty("App::PropertyString","Comment","Path",translate("PathProject","An optional comment for this profile")) + def __init__(self, obj): + obj.addProperty("App::PropertyLinkSubList", "Base", "Path", translate("PathProject", "The base geometry of this object")) + obj.addProperty("App::PropertyBool", "Active", "Path", translate("PathProject", "Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyString", "Comment", "Path", translate("PathProject", "An optional comment for this profile")) - obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm",translate("PathProject", "The library to use to generate the path")) - obj.Algorithm = ['OCC Native','libarea'] + obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm", translate("PathProject", "The library to use to generate the path")) + obj.Algorithm = ['OCC Native', 'libarea'] - #Tool Properties - obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) + # Tool Properties + obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", translate("PathProfile", "The tool number in use")) obj.ToolNumber = (0, 0, 1000, 0) - obj.setEditorMode('ToolNumber',1) #make this read only + obj.setEditorMode('ToolNumber', 1) # make this read only - #Depth Properties - obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("PathProject","The height needed to clear clamps and obstructions")) - obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("PathProject","Rapid Safety Height between locations.")) - obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", translate("PathProject","Incremental Step Down of Tool")) + # Depth Properties + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("PathProject", "The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("PathProject", "Rapid Safety Height between locations.")) + obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", translate("PathProject", "Incremental Step Down of Tool")) obj.StepDown = (0.0, 0.01, 100.0, 0.5) - obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) - obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("PathProject","Final Depth of Tool- lowest value in Z")) - obj.addProperty("App::PropertyDistance", "FinishDepth", "Depth", translate("PathProject","Maximum material removed on final pass.")) + obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("PathProject", "Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("PathProject", "Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyDistance", "FinishDepth", "Depth", translate("PathProject", "Maximum material removed on final pass.")) - #Pocket Properties - obj.addProperty("App::PropertyEnumeration", "CutMode", "Pocket",translate("PathProject", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) - obj.CutMode = ['Climb','Conventional'] - obj.addProperty("App::PropertyDistance", "MaterialAllowance", "Pocket", translate("PathProject","Amount of material to leave")) - obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket",translate("PathProject", "Start pocketing at center or boundary")) + # Pocket Properties + obj.addProperty("App::PropertyEnumeration", "CutMode", "Pocket", translate("PathProject", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) + obj.CutMode = ['Climb', 'Conventional'] + obj.addProperty("App::PropertyDistance", "MaterialAllowance", "Pocket", translate("PathProject", "Amount of material to leave")) + obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket", translate("PathProject", "Start pocketing at center or boundary")) obj.StartAt = ['Center', 'Edge'] - obj.addProperty("App::PropertyFloatConstraint","StepOver","Pocket",translate("PathProject","Amount to step over on each pass")) + obj.addProperty("App::PropertyFloatConstraint", "StepOver", "Pocket", translate("PathProject", "Amount to step over on each pass")) obj.StepOver = (0.0, 0.01, 100.0, 0.5) - obj.addProperty("App::PropertyBool","KeepToolDown","Pocket",translate("PathProject","Attempts to avoid unnecessary retractions.")) - obj.addProperty("App::PropertyBool","ZigUnidirectional","Pocket",translate("PathProject","Lifts tool at the end of each pass to respect cut mode.")) - obj.addProperty("App::PropertyBool","UseZigZag","Pocket",translate("PathProject","Use Zig Zag pattern to clear area.")) - obj.addProperty("App::PropertyFloat","ZigZagAngle","Pocket",translate("PathProject","Angle of the zigzag pattern")) + obj.addProperty("App::PropertyBool", "KeepToolDown", "Pocket", translate("PathProject", "Attempts to avoid unnecessary retractions.")) + obj.addProperty("App::PropertyBool", "ZigUnidirectional", "Pocket", translate("PathProject", "Lifts tool at the end of each pass to respect cut mode.")) + obj.addProperty("App::PropertyBool", "UseZigZag", "Pocket", translate("PathProject", "Use Zig Zag pattern to clear area.")) + obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Pocket", translate("PathProject", "Angle of the zigzag pattern")) - #Entry Properties - obj.addProperty("App::PropertyBool","UseEntry","Entry",translate("PathProject","Allow Cutter enter material with a straight plunge.")) - obj.addProperty("App::PropertyFloatConstraint", "RampSize", "Entry", translate("PathProject","The minimum fraction of tool diameter to use for ramp length")) + # Entry Properties + obj.addProperty("App::PropertyBool", "UseEntry", "Entry", translate("PathProject", "Allow Cutter enter material with a straight plunge.")) + obj.addProperty("App::PropertyFloatConstraint", "RampSize", "Entry", translate("PathProject", "The minimum fraction of tool diameter to use for ramp length")) obj.RampSize = (0.0, 0.01, 100.0, 0.5) - obj.addProperty("App::PropertyFloatConstraint", "HelixSize", "Entry", translate("PathProject","The fraction of tool diameter to use for calculating helix size.")) + obj.addProperty("App::PropertyFloatConstraint", "HelixSize", "Entry", translate("PathProject", "The fraction of tool diameter to use for calculating helix size.")) obj.HelixSize = (0.0, 0.01, 100.0, 0.5) - obj.addProperty("App::PropertyFloatConstraint", "RampAngle", "Entry", translate("PathProject","The Angle of the ramp entry.")) + obj.addProperty("App::PropertyFloatConstraint", "RampAngle", "Entry", translate("PathProject", "The Angle of the ramp entry.")) obj.RampAngle = (0.0, 0.01, 100.0, 0.5) - #Start Point Properties - obj.addProperty("App::PropertyVector","StartPoint","Start Point",translate("PathProject","The start point of this path")) - obj.addProperty("App::PropertyBool","UseStartPoint","Start Point",translate("PathProject","make True, if specifying a Start Point")) + # Start Point Properties + obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", translate("PathProject", "The start point of this path")) + obj.addProperty("App::PropertyBool", "UseStartPoint", "Start Point", translate("PathProject", "make True, if specifying a Start Point")) obj.Proxy = self - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): if prop == "UseEntry": if obj.UseEntry: - obj.setEditorMode('HelixSize',0) #make this visible - obj.setEditorMode('RampAngle',0) #make this visible - obj.setEditorMode('RampSize',0) #make this visible + obj.setEditorMode('HelixSize', 0) # make this visible + obj.setEditorMode('RampAngle', 0) # make this visible + obj.setEditorMode('RampSize', 0) # make this visible else: - obj.setEditorMode('HelixSize',2) #make this hidden - obj.setEditorMode('RampAngle',2) #make this hidden - obj.setEditorMode('RampSize',2) #make this hidden + obj.setEditorMode('HelixSize', 2) # make this hidden + obj.setEditorMode('RampAngle', 2) # make this hidden + obj.setEditorMode('RampSize', 2) # make this hidden def __getstate__(self): return None - def __setstate__(self,state): + def __setstate__(self, state): return None def addpocketbase(self, obj, ss, sub=""): baselist = obj.Base - if baselist == None: + if baselist is None: baselist = [] - if len(baselist) == 0: #When adding the first base object, guess at heights + if len(baselist) == 0: # When adding the first base object, guess at heights try: - bb = ss.Shape.BoundBox #parent boundbox + bb = ss.Shape.BoundBox # parent boundbox subobj = ss.Shape.getElement(sub) - fbb = subobj.BoundBox #feature boundbox + fbb = subobj.BoundBox # feature boundbox obj.StartDepth = bb.ZMax obj.ClearanceHeight = bb.ZMax + 5.0 obj.SafeHeight = bb.ZMax + 3.0 @@ -136,19 +137,19 @@ class ObjectPocket: item = (ss, sub) if item in baselist: - FreeCAD.Console.PrintWarning("this object already in the list"+ "\n") + FreeCAD.Console.PrintWarning(translate("Path", "this object already in the list" + "\n")) else: - baselist.append (item) + baselist.append(item) obj.Base = baselist print "this base is: " + str(baselist) self.execute(obj) - def getStock(self,obj): - "retrieves a stock object from hosting project if any" + def getStock(self, obj): + """find and return a stock object from hosting project if any""" for o in obj.InList: - if hasattr(o,"Group"): + if hasattr(o, "Group"): for g in o.Group: - if hasattr(g,"Height_Allowance"): + if hasattr(g, "Height_Allowance"): return o # not found? search one level up for o in obj.InList: @@ -156,13 +157,19 @@ class ObjectPocket: return None def buildpathlibarea(self, obj, a): + """Build the pocket path using libarea algorithm""" import PathScripts.PathAreaUtils as PathAreaUtils from PathScripts.PathUtils import depth_params - import area - FreeCAD.Console.PrintMessage(translate("PathPocket","Generating toolpath with libarea offsets.\n")) + FreeCAD.Console.PrintMessage(translate("PathPocket", "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) + 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 = obj.StepOver @@ -179,268 +186,97 @@ class ObjectPocket: PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed) if obj.UseStartPoint: - start_point = (obj.StartPoint.x,obj.StartPoint.y) + 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) - #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,\ + 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() - #@do_cprofile def buildpathocc(self, obj, shape): - import Part, DraftGeomUtils - FreeCAD.Console.PrintMessage(translate("PathPocket","Generating toolpath with OCC native offsets.\n")) + """Build pocket Path using Native OCC algorithm.""" + import Part + import DraftGeomUtils + from PathScripts.PathUtils import fmt, helicalPlunge, rampPlunge - def prnt(vlu): return str("%.4f" % round(vlu, 4)) + FreeCAD.Console.PrintMessage(translate("PathPocket", "Generating toolpath with OCC native offsets.\n")) - def rapid(x=None, y=None, z=None): - #Returns gcode to perform a rapid move - retstr = "G00" - if (x != None) or (y != None) or (z != None): - if (x != None): - retstr += " X" + str("%.4f" % x) - if (y != None): - retstr += " Y" + str("%.4f" % y) - if (z != None): - retstr += " Z" + str("%.4f" % z) - else: - return "" - return retstr + "\n" - - def feed(x=None, y=None, z=None): - #Returns gcode to perform a linear feed - global feedxy - retstr = "G01 F" - if(x == None) and (y == None): - retstr += str("%.4f" % self.horizFeed) - else: - retstr += str("%.4f" % self.vertFeed) - - if (x != None) or (y != None) or (z != None): - if (x != None): - retstr += " X" + str("%.4f" % x) - if (y != None): - retstr += " Y" + str("%.4f" % y) - if (z != None): - retstr += " Z" + str("%.4f" % z) - else: - return "" - return retstr + "\n" - - def arc(cx, cy, sx, sy, ex, ey, ez=None, ccw=False): - #Returns gcode to perform an arc - #Assumes XY plane or helix around Z - #Don't worry about starting Z- assume that's dealt with elsewhere - #If start/end radii aren't within eps, abort - eps = 0.01 - if (math.sqrt((cx - sx)**2 + (cy - sy)**2) - math.sqrt((cx - ex)**2 + (cy - ey)**2)) >= eps: - print "ERROR: Illegal arc: Stand and end radii not equal" - return "" - - #Set [C]CW and feed - retstr = "" - if ccw: - retstr += "G03 F" - else: - retstr += "G02 F" - retstr += str(self.horizFeed) - - #End location - retstr += " X" + str("%.4f" % ex) + " Y" + str("%.4f" % ey) - - #Helix if requested - if ez != None: - retstr += " Z" + str("%.4f" % ez) - - #Append center offsets - retstr += " I" + str("%.4f" % (cx - sx)) + " J" + str("%.4f" % (cy - sy)) - - return retstr + "\n" - - def helicalPlunge(plungePos, rampangle, destZ, startZ): - #Returns gcode to helically plunge - #destZ is the milling level - #startZ is the height we can safely feed down to before helix-ing - toold = self.radius * 2 - - helixCmds = "(START HELICAL PLUNGE)\n" - if(plungePos == None): - raise Exception("Helical plunging requires a position!") - return None - - helixX = plungePos.x + toold/2 * plungeR - helixY = plungePos.y; - - helixCirc = math.pi * toold * plungeR - dzPerRev = math.sin(rampangle/180. * math.pi) * helixCirc - - #Go to the start of the helix position - helixCmds += rapid(helixX, helixY) - helixCmds += rapid(z=startZ) - - #Helix as required to get to the requested depth - lastZ = startZ - curZ = max(startZ-dzPerRev, destZ) - done = False - while not done: - done = (curZ == destZ) - #NOTE: FreeCAD doesn't render this, but at least LinuxCNC considers it valid - #helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX, helixY, ez = curZ, ccw=True) - - #Use two half-helixes; FreeCAD renders that correctly, - #and it fits with the other code breaking up 360-degree arcs - helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX - toold * plungeR, helixY, ez = (curZ + lastZ)/2., ccw=True) - helixCmds += arc(plungePos.x, plungePos.y, helixX - toold * plungeR, helixY, helixX, helixY, ez = curZ, ccw=True) - lastZ = curZ - curZ = max(curZ - dzPerRev, destZ) - - return helixCmds - - def rampPlunge(edge, rampangle, destZ, startZ): - #Returns commands to linearly ramp into a cut - #FIXME: This ramps along the first edge, assuming it's long - #enough, NOT just wiggling back and forth by ~0.75 * toolD. - #Not sure if that's any worse, but it's simpler - # I think this should be changed to be limited to a maximum ramp size. Otherwise machine time will get longer than it needs to be. - - rampCmds = "(START RAMP PLUNGE)\n" - if(edge == None): - raise Exception("Ramp plunging requires an edge!") - return None - - sPoint = edge.Vertexes[0].Point - ePoint = edge.Vertexes[1].Point - #Evidently edges can get flipped- pick the right one in this case - #FIXME: This is iffy code, based on what already existed in the "for vpos ..." loop below - if ePoint == sPoint: - #print "FLIP" - ePoint = edge.Vertexes[-1].Point - - rampDist = edge.Length - rampDZ = math.sin(rampangle/180. * math.pi) * rampDist - - rampCmds += rapid(sPoint.x, sPoint.y) - rampCmds += rapid(z=startZ) - - #Ramp down to the requested depth - #FIXME: This might be an arc, so handle that as well - - curZ = max(startZ-rampDZ, destZ) - done = False - while not done: - done = (curZ == destZ) - - #If it's an arc, handle it! - if isinstance(edge.Curve,Part.Circle): - raise Exception("rampPlunge: Screw it, not handling an arc.") - #Straight feed! Easy! - else: - rampCmds += feed(ePoint.x, ePoint.y, curZ) - rampCmds += feed(sPoint.x, sPoint.y) - - curZ = max(curZ - rampDZ, destZ) - - return rampCmds - - -#### Start Processing Path Data here. - - ###Build up the offset loops + # Build up the offset loops output = "" offsets = [] nextradius = self.radius - result = DraftGeomUtils.pocket2d(shape,nextradius) + result = DraftGeomUtils.pocket2d(shape, nextradius) while result: - #print "Adding " + str(len(result)) + " wires" + # print "Adding " + str(len(result)) + " wires" offsets.extend(result) nextradius += self.radius - result = DraftGeomUtils.pocket2d(shape,nextradius) + result = DraftGeomUtils.pocket2d(shape, nextradius) # revert the list so we start with the outer wires if obj.StartAt != 'Edge': offsets.reverse() - ####loops built + if obj.UseEntry: + # Try to find an entry location + plungePos = None + toold = self.radius*2 + helixBounds = DraftGeomUtils.pocket2d(shape, self.radius * (1 + obj.HelixSize)) - # first move will be rapid, subsequent will be at feed rate - first = True - startPoint = None + if helixBounds: + rampD = obj.RampSize + if obj.StartAt == 'Edge': + plungePos = helixBounds[0].Edges[0].Vertexes[0].Point + else: + plungePos = offsets[0].Edges[0].Vertexes[0].Point - ##This should probably just be clearanceheight. - fastZPos = max(obj.StartDepth.Value + 2, obj.ClearanceHeight.Value) + # If it turns out this is invalid for some reason, nuke plungePos + [perp, idx] = DraftGeomUtils.findPerpendicular(plungePos, shape.Edges) + if not perp or perp.Length < self.radius * (1 + obj.HelixSize): + plungePos = None + FreeCAD.Console.PrintError(translate("PathPocket", "Helical Entry location not found.\n")) + # FIXME: Really need to do a point-in-polygon operation to make sure this is within helixBounds + # Or some math to prove that it has to be (doubt that's true) + # Maybe reverse helixBounds and pick off that? + if plungePos is None: # If we didn't find a place to helix, how about a ramp? + rampEdge = None + FreeCAD.Console.PrintMessage(translate("PathPocket", "Attempting ramp entry.\n")) + if (offsets[0].Edges[0].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[0].Curve, Part.Circle)): + rampEdge = offsets[0].Edges[0] + # The last edge also connects with the starting location- try that + elif (offsets[0].Edges[-1].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[-1].Curve, Part.Circle)): + rampEdge = offsets[0].Edges[-1] + else: + FreeCAD.Console.PrintError(translate("PathPocket", "Ramp Entry location not found.\n")) + # print "Neither edge works: " + str(offsets[0].Edges[0]) + ", " + str(offsets[0].Edges[-1]) + # FIXME: There's got to be a smarter way to find a place to ramp - #Fraction of tool radius our plunge helix is to be - plungeR = obj.HelixSize + fastZPos = obj.ClearanceHeight.Value - #(minimum) Fraction of tool DIAMETER to go back and forth while ramp-plunging - #FIXME: The ramp plunging should maybe even be limited to this distance; I don't know what's best - rampD = obj.RampSize - - #Total offset from the desired pocket edge is tool radius plus the plunge helix radius - #Any point on these curves could be the center of a plunge - helixBounds = DraftGeomUtils.pocket2d(shape, self.radius * (1 + plungeR)) - - - #Try to find a location to nicely plunge, starting with a helix, then ramp - #Can't do it without knowledge of a tool - plungePos = None - rampEdge = None - - - #Since we're going to start machining either the inner-most - #edge or the outer (depending on StartAt setting), try to - #plunge near that location - - if helixBounds and obj.UseEntry: - #Edge is easy- pick a point on helixBounds and go with it - if obj.StartAt == 'Edge': - plungePos = helixBounds[0].Edges[0].Vertexes[0].Point - #Center is harder- use a point from the first offset, check if it works - else: - plungePos = offsets[0].Edges[0].Vertexes[0].Point - - #If it turns out this is invalid for some reason, nuke plungePos - [perp,idx] = DraftGeomUtils.findPerpendicular(plungePos, shape.Edges) - if not perp or perp.Length < self.radius * (1 + plungeR): - plungePos = None - #FIXME: Really need to do a point-in-polygon operation to make sure this is within helixBounds - #Or some math to prove that it has to be (doubt that's true) - #Maybe reverse helixBounds and pick off that? - - #If we didn't find a place to helix, how about a ramp? - if not plungePos and obj.UseEntry: - #Check first edge of our offsets - if (offsets[0].Edges[0].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[0].Curve, Part.Circle)): - rampEdge = offsets[0].Edges[0] - #The last edge also connects with the starting location- try that - elif (offsets[0].Edges[-1].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[-1].Curve, Part.Circle)): - rampEdge = offsets[0].Edges[-1] - else: - print "Neither edge works: " + str(offsets[0].Edges[0]) + ", " + str(offsets[0].Edges[-1]) - #FIXME: There's got to be a smarter way to find a place to ramp - - #For helix-ing/ramping, know where we were last time - #FIXME: Can probably get this from the "machine"? + # For helix-ing/ramping, know where we were last time + # FIXME: Can probably get this from the "machine"? lastZ = fastZPos - for vpos in PathUtils.frange(obj.StartDepth.Value, obj.FinalDepth.Value, obj.StepDown, obj.FinishDepth.Value): - #Every for every depth we should helix down + startPoint = None + + for vpos in PathUtils.frange(obj.StartDepth.Value, + obj.FinalDepth.Value, obj.StepDown, + obj.FinishDepth.Value): first = True # loop over successive wires for currentWire in offsets: @@ -449,31 +285,31 @@ class ObjectPocket: if not last: # we set the base GO to our fast move to our starting pos if first: - #If we can helix, do so + # If we can helix, do so if plungePos: - output += helicalPlunge(plungePos, obj.RampAngle, vpos, lastZ) - #print output + output += helicalPlunge(plungePos, obj.RampAngle, vpos, lastZ, self.radius*2, obj.HelixSize, self.horizFeed) + # print output lastZ = vpos - #Otherwise, see if we can ramp - #FIXME: This could be a LOT smarter (eg, searching for a longer leg of the edge to ramp along) + # Otherwise, see if we can ramp + # FIXME: This could be a LOT smarter (eg, searching for a longer leg of the edge to ramp along) elif rampEdge: output += rampPlunge(rampEdge, obj.RampAngle, vpos, lastZ) lastZ = vpos - #Otherwise, straight plunge... Don't want to, but sometimes you might not have a choice. - #FIXME: At least not with the lazy ramp programming above... + # Otherwise, straight plunge... Don't want to, but sometimes you might not have a choice. + # FIXME: At least not with the lazy ramp programming above... else: print "WARNING: Straight-plunging... probably not good, but we didn't find a place to helix or ramp" startPoint = edge.Vertexes[0].Point - output += "G0 X" + prnt(startPoint.x) + " Y" + prnt(startPoint.y) +\ - " Z" + prnt(fastZPos) + "\n" + output += "G0 X" + fmt(startPoint.x) + " Y" + fmt(startPoint.y) +\ + " Z" + fmt(fastZPos) + "\n" first = False - #then move slow down to our starting point for our profile + # then move slow down to our starting point for our profile last = edge.Vertexes[0].Point - output += "G1 X" + prnt(last.x) + " Y" + prnt(last.y) + " Z" + prnt(vpos) + "\n" - #if isinstance(edge.Curve,Part.Circle): + output += "G1 X" + fmt(last.x) + " Y" + fmt(last.y) + " Z" + fmt(vpos) + "\n" + # if isinstance(edge.Curve,Part.Circle): if DraftGeomUtils.geomType(edge) == "Circle": point = edge.Vertexes[-1].Point - if point == last: # edges can come flipped + if point == last: # edges can come flipped point = edge.Vertexes[0].Point center = edge.Curve.Center relcenter = center.sub(last) @@ -483,73 +319,72 @@ class ObjectPocket: output += "G2" else: output += "G3" - output += " X" + prnt(point.x) + " Y" + prnt(point.y) + " Z" + prnt(vpos) - output += " I" + prnt(relcenter.x) + " J" +prnt(relcenter.y) + " K" + prnt(relcenter.z) + output += " X" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos) + output += " I" + fmt(relcenter.x) + " J" + fmt(relcenter.y) + " K" + fmt(relcenter.z) output += "\n" last = point else: point = edge.Vertexes[-1].Point - if point == last: # edges can come flipped + if point == last: # edges can come flipped point = edge.Vertexes[0].Point - output += "G1 X" + prnt(point.x) + " Y" + prnt(point.y) + " Z" + prnt(vpos) + "\n" + output += "G1 X" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos) + "\n" last = point - #move back up - output += "G1 Z" + prnt(fastZPos) + "\n" + # move back up + output += "G1 Z" + fmt(fastZPos) + "\n" return output - - #To reload this from FreeCAD, use: import PathScripts.PathPocket; reload(PathScripts.PathPocket) - def execute(self,obj): + # To reload this from FreeCAD, use: import PathScripts.PathPocket; reload(PathScripts.PathPocket) + def execute(self, obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad == None: + if toolLoad is None: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 - obj.ToolNumber= 0 + obj.ToolNumber = 0 else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value - obj.ToolNumber= toolLoad.ToolNumber + obj.ToolNumber = toolLoad.ToolNumber tool = PathUtils.getTool(obj, toolLoad.ToolNumber) - if tool == None: + if tool is None: self.radius = 0.25 else: self.radius = tool.Diameter/2 - if obj.Base: for b in obj.Base: print "object base: " + str(b) - import Part, PathScripts.PathKurveUtils + import Part + import PathScripts.PathKurveUtils if "Face" in b[1]: print "inside" - shape = getattr(b[0].Shape,b[1]) + shape = getattr(b[0].Shape, b[1]) wire = shape.OuterWire edges = wire.Edges else: print "in else" - edges = [getattr(b[0].Shape,sub) for sub in b[1]] + edges = [getattr(b[0].Shape, sub) for sub in b[1]] print "myedges: " + str(edges) wire = Part.Wire(edges) shape = None # output = "" if obj.Algorithm == "OCC Native": - if shape == None: + if shape is None: shape = wire output += self.buildpathocc(obj, shape) else: try: import area except: - FreeCAD.Console.PrintError(translate("PathKurve","libarea needs to be installed for this command to work.\n")) + FreeCAD.Console.PrintError(translate("PathKurve", "libarea needs to be installed for this command to work.\n")) return a = area.Area() - if shape == None: + if shape is None: c = PathScripts.PathKurveUtils.makeAreaCurve(wire.Edges, 'CW') a.append(c) else: @@ -568,14 +403,13 @@ class ObjectPocket: a.append(c) ######## - ##This puts out some interesting information from libarea + # This puts out some interesting information from libarea print a.text() ######## a.Reorder() output += self.buildpathlibarea(obj, a) - if obj.Active: path = Path.Path(output) obj.Path = path @@ -585,33 +419,35 @@ class ObjectPocket: obj.Path = path obj.ViewObject.Visibility = False + class _CommandSetPocketStartPoint: def GetResources(self): - return {'Pixmap' : 'Path-StartPoint', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPocket","Pick Start Point"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPocket","Pick Start Point")} + return {'Pixmap': 'Path-StartPoint', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Pick Start Point"), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Pick Start Point")} def IsActive(self): - return not FreeCAD.ActiveDocument is None + return FreeCAD.ActiveDocument is not None - def setpoint(self,point,o): - obj=FreeCADGui.Selection.getSelection()[0] + def setpoint(self, point, o): + obj = FreeCADGui.Selection.getSelection()[0] obj.StartPoint.x = point.x obj.StartPoint.y = point.y - def Activated(self): + def Activated(self): FreeCADGui.Snapper.getPoint(callback=self.setpoint) + class ViewProviderPocket: - def __init__(self,vobj): + def __init__(self, vobj): vobj.Proxy = self - def attach(self,vobj): + def attach(self, vobj): self.Object = vobj.Object return - def setEdit(self,vobj,mode=0): + def setEdit(self, vobj, mode=0): FreeCADGui.Control.closeDialog() taskd = TaskPanel() taskd.obj = vobj.Object @@ -625,97 +461,57 @@ class ViewProviderPocket: def __getstate__(self): return None - def __setstate__(self,state): + def __setstate__(self, state): return None class CommandPathPocket: - def GetResources(self): - return {'Pixmap' : 'Path-Pocket', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPocket","Pocket"), + return {'Pixmap': 'Path-Pocket', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Pocket"), 'Accel': "P, O", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPocket","Creates a Path Pocket object from a loop of edges or a face")} + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Creates a Path Pocket object from a loop of edges or a face")} def IsActive(self): - return not FreeCAD.ActiveDocument is None + return FreeCAD.ActiveDocument is not None def Activated(self): - # check that the selection contains exactly what we want - # selection = FreeCADGui.Selection.getSelectionEx() - # if len(selection) != 1: - # FreeCAD.Console.PrintError(translate("PathPocket","Please select an edges loop from one object, or a single face\n")) - # return - # if len(selection[0].SubObjects) == 0: - # FreeCAD.Console.PrintError(translate("PathPocket","Please select an edges loop from one object, or a single face\n")) - # return - # for s in selection[0].SubObjects: - # if s.ShapeType != "Edge": - # if (s.ShapeType != "Face") or (len(selection[0].SubObjects) != 1): - # FreeCAD.Console.PrintError(translate("PathPocket","Please select only edges or a single face\n")) - # return - # if selection[0].SubObjects[0].ShapeType == "Edge": - # try: - # import Part - # w = Part.Wire(selection[0].SubObjects) - # if w.isClosed() == False: - # FreeCAD.Console.PrintError(translate("PathPocket","The selected edges don't form a loop\n")) - # return - - # except: - # FreeCAD.Console.PrintError(translate("PathPocket","The selected edges don't form a loop\n")) - # return - - # Take a guess at some reasonable values for Finish depth. - # bb = selection[0].Object.Shape.BoundBox #parent boundbox - # fbb = selection[0].SubObjects[0].BoundBox #feature boundbox - # if fbb.ZMax < bb.ZMax: - # zbottom = fbb.ZMax - # else: - # zbottom = bb.ZMin - zbottom = 0.0 ztop = 10.0 # if everything is ok, execute and register the transaction in the undo/redo stack - FreeCAD.ActiveDocument.openTransaction(translate("PathPocket","Create Pocket")) + FreeCAD.ActiveDocument.openTransaction(translate("PathPocket", "Create Pocket")) FreeCADGui.addModule("PathScripts.PathPocket") - - FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Pocket")') + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Pocket")') FreeCADGui.doCommand('PathScripts.PathPocket.ObjectPocket(obj)') - FreeCADGui.doCommand('obj.Active = True') - FreeCADGui.doCommand('PathScripts.PathPocket.ViewProviderPocket(obj.ViewObject)') - FreeCADGui.doCommand('from PathScripts import PathUtils') - FreeCADGui.doCommand('obj.StepOver = 1.0') - FreeCADGui.doCommand('obj.ClearanceHeight = 10')# + str(bb.ZMax + 2.0)) + FreeCADGui.doCommand('obj.ClearanceHeight = 10') # + str(bb.ZMax + 2.0)) FreeCADGui.doCommand('obj.StepDown = 1.0') - FreeCADGui.doCommand('obj.StartDepth= ' + str(ztop)) - FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom)) - FreeCADGui.doCommand('obj.ZigZagAngle=45') - FreeCADGui.doCommand('obj.UseEntry=True') + FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop)) + FreeCADGui.doCommand('obj.FinalDepth =' + str(zbottom)) + FreeCADGui.doCommand('obj.ZigZagAngle = 45') + FreeCADGui.doCommand('obj.UseEntry = True') FreeCADGui.doCommand('obj.RampAngle = 3.0') FreeCADGui.doCommand('obj.RampSize = 0.75') FreeCADGui.doCommand('obj.HelixSize = 0.75') - FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') - 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/PocketEdit.ui") + # self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/PocketEdit.ui") self.form = FreeCADGui.PySideUic.loadUi(":/panels/PocketEdit.ui") self.updating = False - def accept(self): self.getFields() @@ -731,37 +527,37 @@ class TaskPanel: def getFields(self): if self.obj: - if hasattr(self.obj,"StartDepth"): + if hasattr(self.obj, "StartDepth"): self.obj.StartDepth = self.form.startDepth.text() - if hasattr(self.obj,"FinalDepth"): + if hasattr(self.obj, "FinalDepth"): self.obj.FinalDepth = self.form.finalDepth.text() - if hasattr(self.obj,"SafeHeight"): + if hasattr(self.obj, "SafeHeight"): self.obj.SafeHeight = self.form.safeHeight.text() - if hasattr(self.obj,"ClearanceHeight"): + if hasattr(self.obj, "ClearanceHeight"): self.obj.ClearanceHeight = self.form.clearanceHeight.text() - if hasattr(self.obj,"StepDown"): + if hasattr(self.obj, "StepDown"): self.obj.StepDown = self.form.stepDown.value() - if hasattr(self.obj,"MaterialAllowance"): + if hasattr(self.obj, "MaterialAllowance"): self.obj.MaterialAllowance = self.form.extraOffset.value() - if hasattr(self.obj,"UseStartPoint"): + if hasattr(self.obj, "UseStartPoint"): self.obj.UseStartPoint = self.form.useStartPoint.isChecked() - if hasattr(self.obj,"Algorithm"): + if hasattr(self.obj, "Algorithm"): self.obj.Algorithm = str(self.form.algorithmSelect.currentText()) - if hasattr(self.obj,"CutMode"): + if hasattr(self.obj, "CutMode"): self.obj.CutMode = str(self.form.cutMode.currentText()) self.obj.Proxy.execute(self.obj) def open(self): - self.s =SelObserver() + 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 + # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelectionEx() if not len(selection) >= 1: - FreeCAD.Console.PrintError(translate("PathProject","Please select at least one profileable object\n")) + FreeCAD.Console.PrintError(translate("PathProject", "Please select at least one profileable object\n")) return for s in selection: if s.HasSubObjects: @@ -770,7 +566,7 @@ class TaskPanel: else: self.obj.Proxy.addpocketbase(self.obj, s.Object) - self.setupUi() #defaults may have changed. Reload. + self.setupUi() # defaults may have changed. Reload. self.form.baseList.clear() for i in self.obj.Base: self.form.baseList.addItem(i[0].Name + "." + i[1]) @@ -780,8 +576,8 @@ class TaskPanel: 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) + 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) @@ -793,9 +589,8 @@ class TaskPanel: for i in slist: objstring = i.text().partition(".") obj = FreeCAD.ActiveDocument.getObject(objstring[0]) - # sub = o.Shape.getElement(objstring[2]) if objstring[2] != "": - FreeCADGui.Selection.addSelection(obj,objstring[2]) + FreeCADGui.Selection.addSelection(obj, objstring[2]) else: FreeCADGui.Selection.addSelection(obj) @@ -810,7 +605,7 @@ class TaskPanel: obj = FreeCAD.ActiveDocument.getObject(objstring[0]) item = (obj, str(objstring[2])) newlist.append(item) - self.obj.Base=newlist + self.obj.Base = newlist self.obj.Proxy.execute(self.obj) FreeCAD.ActiveDocument.recompute() @@ -818,8 +613,7 @@ class TaskPanel: def getStandardButtons(self): return int(QtGui.QDialogButtonBox.Ok) - - def edit(self,item,column): + def edit(self, item, column): if not self.updating: self.resetObject() @@ -839,23 +633,23 @@ class TaskPanel: for i in self.obj.Base: self.form.baseList.addItem(i[0].Name + "." + i[1]) - #Connect Signals and Slots - #Base Controls + # 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 + # Depths self.form.startDepth.editingFinished.connect(self.getFields) self.form.finalDepth.editingFinished.connect(self.getFields) self.form.stepDown.editingFinished.connect(self.getFields) - #Heights + # Heights self.form.safeHeight.editingFinished.connect(self.getFields) self.form.clearanceHeight.editingFinished.connect(self.getFields) - #operation + # operation self.form.algorithmSelect.currentIndexChanged.connect(self.getFields) self.form.cutMode.currentIndexChanged.connect(self.getFields) self.form.useStartPoint.clicked.connect(self.getFields) @@ -871,14 +665,14 @@ class SelObserver: import PathScripts.PathSelection as PST PST.clear() - def addSelection(self,doc,obj,sub,pnt): # Selection object - FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj +')') + 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_Pocket',CommandPathPocket()) - FreeCADGui.addCommand('Set_PocketStartPoint',_CommandSetPocketStartPoint()) + FreeCADGui.addCommand('Path_Pocket', CommandPathPocket()) + FreeCADGui.addCommand('Set_PocketStartPoint', _CommandSetPocketStartPoint()) FreeCAD.Console.PrintLog("Loading PathPocket... done\n") diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py index 032b15281..b9005afce 100644 --- a/src/Mod/Path/PathScripts/PathProfile.py +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -142,7 +142,7 @@ class ObjectProfile: item = (ss, sub) if item in baselist: - FreeCAD.Console.PrintWarning("this object already in the list" + "\n") + FreeCAD.Console.PrintWarning(translate("Path", "this object already in the list" + "\n")) else: baselist.append(item) obj.Base = baselist @@ -247,6 +247,7 @@ print "y - " + str(point.y) self.horizFeed = 100 self.radius = 0.25 obj.ToolNumber = 0 + FreeCAD.Console.PrintWarning(translate("Path", "No tool found. Using default values for now." + "\n")) else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value @@ -444,6 +445,9 @@ class TaskPanel: self.form = FreeCADGui.PySideUic.loadUi(":/panels/ProfileEdit.ui") self.updating = False + def __del__(self): + FreeCADGui.Selection.removeObserver(self.s) + def accept(self): self.getFields() @@ -725,7 +729,7 @@ class SelObserver: PST.clear() def addSelection(self, doc, obj, sub, pnt): - FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')') + #FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')') FreeCADGui.updateGui() diff --git a/src/Mod/Path/PathScripts/PathRemote.py b/src/Mod/Path/PathScripts/PathRemote.py index 9583663d3..961a825b4 100644 --- a/src/Mod/Path/PathScripts/PathRemote.py +++ b/src/Mod/Path/PathScripts/PathRemote.py @@ -1,28 +1,29 @@ # -*- 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 * -#* * -#*************************************************************************** +# *************************************************************************** +# * * +# * 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,Path +import FreeCAD +import Path from PathScripts import PathUtils import urllib2 import json @@ -31,12 +32,11 @@ if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui from DraftTools import translate - from pivy import coin else: - def translate(ctxt,txt): + def translate(ctxt, txt): return txt -__title__="Path Remote Operation" +__title__ = "Path Remote Operation" __author__ = "sliptonic (Brad Collette)" __url__ = "http://www.freecadweb.org" @@ -45,6 +45,7 @@ __url__ = "http://www.freecadweb.org" # Qt tanslation handling try: _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: @@ -54,39 +55,39 @@ except AttributeError: class ObjectRemote: - def __init__(self,obj): + def __init__(self, obj): - obj.addProperty("App::PropertyLinkSubList","Base","Path",translate("Parent Object(s)","The base geometry of this toolpath")) - obj.addProperty("App::PropertyBool","Active","Path",translate("Active","Make False, to prevent operation from generating code")) - obj.addProperty("App::PropertyString","Comment","Path",translate("PathProject","An optional comment for this profile")) + obj.addProperty("App::PropertyLinkSubList", "Base", "Path", translate("Parent Object(s)", "The base geometry of this toolpath")) + obj.addProperty("App::PropertyBool", "Active", "Path", translate("Active", "Make False, to prevent operation from generating code")) + obj.addProperty("App::PropertyString", "Comment", "Path", translate("PathProject", "An optional comment for this profile")) - obj.addProperty("App::PropertyString","URL", "API", translate("RemotePath", "The Base URL of the remote path service")) - obj.addProperty("App::PropertyStringList", "proplist","Path",translate("Path","list of remote properties")) - obj.setEditorMode('proplist',2) #make this hidden + obj.addProperty("App::PropertyString", "URL", "API", translate("RemotePath", "The Base URL of the remote path service")) + obj.addProperty("App::PropertyStringList", "proplist", "Path", translate("Path", "list of remote properties")) + obj.setEditorMode('proplist', 2) # make this hidden - #Tool Properties - obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) + # Tool Properties + obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", translate("PathProfile", "The tool number in use")) obj.ToolNumber = (0, 0, 1000, 0) - obj.setEditorMode('ToolNumber',1) #make this read only + obj.setEditorMode('ToolNumber', 1) # make this read only - #Depth Properties - obj.addProperty("App::PropertyFloat", "ClearanceHeight", "Depth", translate("PathProject","The height needed to clear clamps and obstructions")) - obj.addProperty("App::PropertyFloat", "SafeHeight", "Depth", translate("PathProject","Rapid Safety Height between locations.")) - obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Step", translate("PathProject","Incremental Step Down of Tool")) + # Depth Properties + obj.addProperty("App::PropertyFloat", "ClearanceHeight", "Depth", translate("PathProject", "The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyFloat", "SafeHeight", "Depth", translate("PathProject", "Rapid Safety Height between locations.")) + obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Step", translate("PathProject", "Incremental Step Down of Tool")) obj.StepDown = (0.0, 0.01, 100.0, 0.5) - obj.addProperty("App::PropertyFloat", "StartDepth", "Depth", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) - obj.addProperty("App::PropertyFloat", "FinalDepth", "Depth", translate("PathProject","Final Depth of Tool- lowest value in Z")) - obj.addProperty("App::PropertyFloat", "FinishDepth", "Depth", translate("PathProject","Maximum material removed on final pass.")) + obj.addProperty("App::PropertyFloat", "StartDepth", "Depth", translate("PathProject", "Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyFloat", "FinalDepth", "Depth", translate("PathProject", "Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyFloat", "FinishDepth", "Depth", translate("PathProject", "Maximum material removed on final pass.")) obj.Proxy = self def addbaseobject(self, obj, ss, sub=""): baselist = obj.Base - if len(baselist) == 0: #When adding the first base object, guess at heights + if len(baselist) == 0: # When adding the first base object, guess at heights try: - bb = ss.Shape.BoundBox #parent boundbox + bb = ss.Shape.BoundBox # parent boundbox subobj = ss.Shape.getElement(sub) - fbb = subobj.BoundBox #feature boundbox + fbb = subobj.BoundBox # feature boundbox obj.StartDepth = bb.ZMax obj.ClearanceHeight = bb.ZMax + 5.0 obj.SafeHeight = bb.ZMax + 3.0 @@ -102,16 +103,16 @@ class ObjectRemote: item = (ss, sub) if item in baselist: - FreeCAD.Console.PrintWarning("this object already in the list"+ "\n") + FreeCAD.Console.PrintWarning("this object already in the list" + "\n") else: - baselist.append (item) + baselist.append(item) obj.Base = baselist self.execute(obj) def __getstate__(self): return None - def __setstate__(self,state): + def __setstate__(self, state): return None def onChanged(self, obj, prop): @@ -143,54 +144,58 @@ class ObjectRemote: pl = obj.proplist pl = [] for prop in properties: - obj.addProperty(prop['type'], prop['propertyname'], "Remote", prop['description']) + obj.addProperty( + prop['type'], + prop['propertyname'], + "Remote", + prop['description']) pl.append(prop['propertyname']) print "adding: " + str(prop) obj.proplist = pl - def execute(self,obj): + def execute(self, obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad == None: + if toolLoad is None: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 - obj.ToolNumber= 0 + obj.ToolNumber = 0 else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value tool = PathUtils.getTool(obj, toolLoad.ToolNumber) self.radius = tool.Diameter/2 - obj.ToolNumber= toolLoad.ToolNumber + obj.ToolNumber = toolLoad.ToolNumber output += "(remote gcode goes here)" path = Path.Path(output) obj.Path = path + class ViewProviderRemote: - def __init__(self,obj): #mandatory -# obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property") + def __init__(self, obj): obj.Proxy = self - def __getstate__(self): #mandatory + def __getstate__(self): return None - def __setstate__(self,state): #mandatory + def __setstate__(self, state): return None - def getIcon(self): #optional + def getIcon(self): return ":/icons/Path-Remote.svg" - def onChanged(self,obj,prop): #optional + def onChanged(self, obj, prop): # this is executed when a property of the VIEW PROVIDER changes pass - def updateData(self,obj,prop): #optional + def updateData(self, obj, prop): # optional # this is executed when a property of the APP OBJECT changes pass - def setEdit(self,vobj,mode=0): + def setEdit(self, vobj, mode=0): FreeCADGui.Control.closeDialog() taskd = TaskPanel() taskd.obj = vobj.Object @@ -198,37 +203,37 @@ class ViewProviderRemote: taskd.setupUi() return True - def unsetEdit(self,vobj,mode): #optional + def unsetEdit(self, vobj, mode): # this is executed when the user cancels or terminates edit mode pass + class _RefreshRemotePath: def GetResources(self): - return {'Pixmap' : 'Path-Refresh', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Remote","Refresh Remote Path Data"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Remote","Refresh Remote Path Data")} + return {'Pixmap': 'Path-Refresh', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Remote", "Refresh Remote Path Data"), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Remote", "Refresh Remote Path Data")} def IsActive(self): - return not FreeCAD.ActiveDocument is None + return FreeCAD.ActiveDocument is not None def refresh(self): - obj=FreeCADGui.Selection.getSelection()[0] + obj = FreeCADGui.Selection.getSelection()[0] values = {} for i in obj.PropertiesList: if obj.getGroupOfProperty(i) in ["Remote"]: - values.update({i : obj.getPropertyByName(i)}) + values.update({i: obj.getPropertyByName(i)}) if obj.getGroupOfProperty(i) in ["Depth"]: print str(i) - values.update({i : obj.getPropertyByName(i)}) + values.update({i: obj.getPropertyByName(i)}) if obj.getGroupOfProperty(i) in ["Step"]: - values.update({i : obj.getPropertyByName(i)}) - + values.update({i: obj.getPropertyByName(i)}) if obj.getGroupOfProperty(i) in ["Tool"]: - tool = PathUtils.getTool(obj,obj.ToolNumber) + tool = PathUtils.getTool(obj, obj.ToolNumber) if tool: tradius = tool.Diameter/2 tlength = tool.LengthOffset @@ -238,10 +243,9 @@ class _RefreshRemotePath: tlength = 1 ttype = "undefined" - values.update({"tool_diameter" : tradius}) - values.update({"tool_length" : tlength}) - values.update({"tool_type" : ttype}) - + values.update({"tool_diameter": tradius}) + values.update({"tool_length": tlength}) + values.update({"tool_type": ttype}) payload = json.dumps(values) @@ -256,7 +260,6 @@ class _RefreshRemotePath: print "service not defined or not responding" return - path = data['path'] output = "" for command in path: @@ -264,33 +267,31 @@ class _RefreshRemotePath: path = Path.Path(output) obj.Path = path - def Activated(self): self.refresh() + class CommandPathRemote: - def GetResources(self): - return {'Pixmap' : 'Path-Remote', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Remote","Remote"), + return {'Pixmap': 'Path-Remote', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Remote", "Remote"), 'Accel': "P, R", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Remote","Request a Path from a remote cloud service")} + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Remote", "Request a Path from a remote cloud service")} def IsActive(self): - return not FreeCAD.ActiveDocument is None + return FreeCAD.ActiveDocument is not None def Activated(self): ztop = 10.0 zbottom = 0.0 - FreeCAD.ActiveDocument.openTransaction(translate("Path_Remote","Create remote path operation")) + FreeCAD.ActiveDocument.openTransaction(translate("Path_Remote", "Create remote path operation")) FreeCADGui.addModule("PathScripts.PathRemote") - FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Remote")') + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Remote")') FreeCADGui.doCommand('PathScripts.PathRemote.ObjectRemote(obj)') FreeCADGui.doCommand('obj.Active = True') FreeCADGui.doCommand('PathScripts.PathRemote.ViewProviderRemote(obj.ViewObject)') - #FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.'+selection[0].ObjectName+',[])') FreeCADGui.doCommand('from PathScripts import PathUtils') FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(ztop + 2)) FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop)) @@ -304,9 +305,9 @@ class CommandPathRemote: FreeCAD.ActiveDocument.recompute() FreeCADGui.doCommand('obj.ViewObject.startEditing()') + class TaskPanel: def __init__(self): - #self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/RemoteEdit.ui") self.form = FreeCADGui.PySideUic.loadUi(":/panels/RemoteEdit.ui") def accept(self): @@ -327,41 +328,31 @@ class TaskPanel: self.obj.URL = self.form.remoteURL.text() print "getRemote:320" - #self.form.label_a = QtGui.QLabel(self.form.remoteProperties) - #self.form.label_a.setObjectName("label_a") - #self.form.formLayoutREMOTE.setWidget(0, QtGui.QFormLayout.LabelRole, self.form.label_a) - #self.form.sampleLE = QtGui.QLineEdit(self.form.remoteProperties) - #self.form.sampleLE.setObjectName("sampleLE") - #self.form.formLayoutREMOTE.setWidget(0, QtGui.QFormLayout.FieldRole, self.form.sampleLE) - ##self.formLayout_2.setWidget(1, QtGui.QFormLayout.SpanningRole, self.remoteProperties) - - def getFields(self): if self.obj: - if hasattr(self.obj,"StartDepth"): + if hasattr(self.obj, "StartDepth"): self.obj.StartDepth = float(self.form.startDepth.text()) - if hasattr(self.obj,"FinalDepth"): + if hasattr(self.obj, "FinalDepth"): self.obj.FinalDepth = float(self.form.finalDepth.text()) - if hasattr(self.obj,"SafeHeight"): + if hasattr(self.obj, "SafeHeight"): self.obj.SafeHeight = float(self.form.safeHeight.text()) - if hasattr(self.obj,"ClearanceHeight"): + if hasattr(self.obj, "ClearanceHeight"): self.obj.ClearanceHeight = float(self.form.clearanceHeight.text()) - if hasattr(self.obj,"StepDown"): + if hasattr(self.obj, "StepDown"): self.obj.StepDown = float(self.form.stepDown.value()) self.obj.Proxy.execute(self.obj) def open(self): - self.s =SelObserver() - # install the function mode resident + self.s = SelObserver() FreeCADGui.Selection.addObserver(self.s) def addBase(self): - # check that the selection contains exactly what we want + # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelectionEx() if not len(selection) >= 1: - FreeCAD.Console.PrintError(translate("PathProject","Please select at least one suitable object\n")) + FreeCAD.Console.PrintError(translate("PathProject", "Please select at least one suitable object\n")) return for s in selection: if s.HasSubObjects: @@ -370,7 +361,7 @@ class TaskPanel: else: self.obj.Proxy.addbaseobject(self.obj, s.Object) - self.setupUi() #defaults may have changed. Reload. + self.setupUi() # defaults may have changed. Reload. self.form.baseList.clear() for i in self.obj.Base: self.form.baseList.addItem(i[0].Name + "." + i[1]) @@ -381,7 +372,7 @@ class TaskPanel: newlist = [] for i in self.obj.Base: if not i[0].Name == d.text(): - newlist.append (i) + newlist.append(i) self.obj.Base = newlist self.form.baseList.takeItem(self.form.baseList.row(d)) self.obj.Proxy.execute(self.obj) @@ -398,10 +389,10 @@ class TaskPanel: def reorderBase(self): newlist = [] for i in range(self.form.baseList.count()): - s = self.form.baseList.item(i).text() + s = self.form.baseList.item(i).text() obj = FreeCAD.ActiveDocument.getObject(s) newlist.append(obj) - self.obj.Base=newlist + self.obj.Base = newlist self.obj.Proxy.execute(self.obj) FreeCAD.ActiveDocument.recompute() @@ -411,16 +402,15 @@ class TaskPanel: def changeURL(self): from urlparse import urlparse t = self.form.remoteURL.text() - if t == '' and self.obj.URL != '': #if the url was deleted, cleanup. + if t == '' and self.obj.URL != '': # if the url was deleted, cleanup. self.obj.URL = '' - if urlparse(t).scheme != '' and t != self.obj.URL: #validate new url. + if urlparse(t).scheme != '' and t != self.obj.URL: # validate new url. self.obj.URL = t - #next make sure the property fields reflect the current attached service + # next make sure the property fields reflect the current attached service for p in self.obj.proplist: print p - def setupUi(self): self.form.startDepth.setText(str(self.obj.StartDepth)) self.form.finalDepth.setText(str(self.obj.FinalDepth)) @@ -431,7 +421,7 @@ class TaskPanel: for i in self.obj.Base: self.form.baseList.addItem(i[0].Name) - #Connect Signals and Slots + # Connect Signals and Slots self.form.startDepth.editingFinished.connect(self.getFields) self.form.finalDepth.editingFinished.connect(self.getFields) self.form.safeHeight.editingFinished.connect(self.getFields) @@ -443,24 +433,23 @@ class TaskPanel: self.form.reorderBase.clicked.connect(self.reorderBase) self.form.remoteURL.editingFinished.connect(self.changeURL) - #self.form.remoteURL.returnPressed.connect(self.testOne) + class SelObserver: def __init__(self): import PathScripts.PathSelection as PST - #PST.surfaceselect() def __del__(self): import PathScripts.PathSelection as PST PST.clear() - def addSelection(self,doc,obj,sub,pnt): # Selection object - FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj +')') + 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_Remote',CommandPathRemote()) - FreeCADGui.addCommand('Refresh_Path',_RefreshRemotePath()) + FreeCADGui.addCommand('Path_Remote', CommandPathRemote()) + FreeCADGui.addCommand('Refresh_Path', _RefreshRemotePath()) FreeCAD.Console.PrintLog("Loading PathRemote... done\n") diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index d4c1a671d..6f2b24ce6 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -125,6 +125,11 @@ class PROFILEGate: elif obj.ShapeType == 'Compound': profileable = True + if sub[0:4] == 'Face': + profileable = True + + if sub[0:4] == 'Edge': + profileable = True elif obj.ShapeType == 'Face': profileable = True diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 3e497fdd6..2b5c6e3a9 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -1,65 +1,61 @@ # -*- coding: utf-8 -*- -#*************************************************************************** -#* * -#* Copyright (c) 2014 Dan Falck * -#* * -#* 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 * -#* * -#*************************************************************************** +# *************************************************************************** +# * * +# * Copyright (c) 2014 Dan Falck * +# * * +# * 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 * +# * * +# *************************************************************************** '''PathUtils -common functions used in PathScripts for filterig, sorting, and generating gcode toolpath data ''' import FreeCAD import Part -from FreeCAD import Vector -import FreeCADGui import math -import DraftGeomUtils from DraftGeomUtils import geomType import DraftVecUtils import PathScripts from PathScripts import PathProject - -def cleanedges(splines,precision): +def cleanedges(splines, precision): '''cleanedges([splines],precision). Convert BSpline curves, Beziers, to arcs that can be used for cnc paths. Returns Lines as is. Filters Circle and Arcs for over 180 degrees. Discretizes Ellipses. Ignores other geometry. ''' edges = [] for spline in splines: - if geomType(spline)=="BSplineCurve": + if geomType(spline) == "BSplineCurve": arcs = spline.Curve.toBiArcs(precision) for i in arcs: edges.append(Part.Edge(i)) - elif geomType(spline)=="BezierCurve": - newspline=spline.Curve.toBSpline() + elif geomType(spline) == "BezierCurve": + newspline = spline.Curve.toBSpline() arcs = newspline.toBiArcs(precision) for i in arcs: edges.append(Part.Edge(i)) - elif geomType(spline)=="Ellipse": - edges = curvetowire(spline, 1.0) #fixme hardcoded value + elif geomType(spline) == "Ellipse": + edges = curvetowire(spline, 1.0) # fixme hardcoded value - elif geomType(spline)=="Circle": - arcs=filterArcs(spline) + elif geomType(spline) == "Circle": + arcs = filterArcs(spline) for i in arcs: edges.append(Part.Edge(i)) - elif geomType(spline)=="Line": + elif geomType(spline) == "Line": edges.append(spline) else: @@ -67,71 +63,80 @@ def cleanedges(splines,precision): return edges -def curvetowire(obj,steps): + +def curvetowire(obj, steps): '''adapted from DraftGeomUtils, because the discretize function changed a bit ''' - points = obj.copy().discretize(Distance = eval('steps')) + points = obj.copy().discretize(Distance=eval('steps')) p0 = points[0] edgelist = [] for p in points[1:]: - edge = Part.makeLine((p0.x,p0.y,p0.z),(p.x,p.y,p.z)) + edge = Part.makeLine((p0.x, p0.y, p0.z), (p.x, p.y, p.z)) edgelist.append(edge) p0 = p return edgelist -def fmt(val): return format(val, '.4f') #fixme set at 4 decimal places for testing -def isSameEdge(e1,e2): +# fixme set at 4 decimal places for testing +def fmt(val): return format(val, '.4f') + + +def isSameEdge(e1, e2): """isSameEdge(e1,e2): return True if the 2 edges are both lines or arcs/circles and have the same points - inspired by Yorik's function isSameLine""" - if not (isinstance(e1.Curve,Part.Line) or isinstance(e1.Curve,Part.Circle)): + if not (isinstance(e1.Curve, Part.Line) or isinstance(e1.Curve, Part.Circle)): return False - if not (isinstance(e2.Curve,Part.Line) or isinstance(e2.Curve,Part.Circle)): + if not (isinstance(e2.Curve, Part.Line) or isinstance(e2.Curve, Part.Circle)): return False - if type(e1.Curve) <> type(e2.Curve): + if type(e1.Curve) != type(e2.Curve): return False - if isinstance(e1.Curve,Part.Line): - if (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[0].Point)) and \ - (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[-1].Point)): + if isinstance(e1.Curve, Part.Line): + if (DraftVecUtils.equals(e1.Vertexes[0].Point, e2.Vertexes[0].Point)) and \ + (DraftVecUtils.equals(e1.Vertexes[-1].Point, e2.Vertexes[-1].Point)): return True - elif (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[0].Point)) and \ - (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[-1].Point)): + elif (DraftVecUtils.equals(e1.Vertexes[-1].Point, e2.Vertexes[0].Point)) and \ + (DraftVecUtils.equals(e1.Vertexes[0].Point, e2.Vertexes[-1].Point)): return True - if isinstance(e1.Curve,Part.Circle): - center = False; radius= False; endpts=False + if isinstance(e1.Curve, Part.Circle): + center = False + radius = False + endpts = False if e1.Curve.Center == e2.Curve.Center: center = True if e1.Curve.Radius == e2.Curve.Radius: radius = True - if (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[0].Point)) and \ - (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[-1].Point)): + if (DraftVecUtils.equals(e1.Vertexes[0].Point, e2.Vertexes[0].Point)) and \ + (DraftVecUtils.equals(e1.Vertexes[-1].Point, e2.Vertexes[-1].Point)): endpts = True - elif (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[0].Point)) and \ - (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[-1].Point)): + elif (DraftVecUtils.equals(e1.Vertexes[-1].Point, e2.Vertexes[0].Point)) and \ + (DraftVecUtils.equals(e1.Vertexes[0].Point, e2.Vertexes[-1].Point)): endpts = True if (center and radius and endpts): return True return False + def segments(poly): ''' A sequence of (x,y) numeric coordinates pairs ''' return zip(poly, poly[1:] + [poly[0]]) + def check_clockwise(poly): ''' check_clockwise(poly) a function for returning a boolean if the selected wire is clockwise or counter clockwise based on point order. poly = [(x1,y1),(x2,y2),(x3,y3)] ''' clockwise = False - if (sum(x0*y1 - x1*y0 for ((x0, y0), (x1, y1)) in segments(poly))) < 0: + if (sum(x0 * y1 - x1 * y0 for ((x0, y0), (x1, y1)) in segments(poly))) < 0: clockwise = not clockwise return clockwise + def filterArcs(arcEdge): '''filterArcs(Edge) -used to split arcs that over 180 degrees. Returns list ''' s = arcEdge - if isinstance(s.Curve,Part.Circle): - splitlist =[] - angle = abs(s.LastParameter-s.FirstParameter) + if isinstance(s.Curve, Part.Circle): + splitlist = [] + angle = abs(s.LastParameter - s.FirstParameter) overhalfcircle = False goodarc = False if (angle > math.pi): @@ -139,14 +144,17 @@ def filterArcs(arcEdge): else: goodarc = True if not goodarc: - arcstpt = s.valueAt(s.FirstParameter) - arcmid = s.valueAt((s.LastParameter-s.FirstParameter)*0.5+s.FirstParameter) - arcquad1 = s.valueAt((s.LastParameter-s.FirstParameter)*0.25+s.FirstParameter)#future midpt for arc1 - arcquad2 = s.valueAt((s.LastParameter-s.FirstParameter)*0.75+s.FirstParameter) #future midpt for arc2 + arcstpt = s.valueAt(s.FirstParameter) + arcmid = s.valueAt( + (s.LastParameter - s.FirstParameter) * 0.5 + s.FirstParameter) + arcquad1 = s.valueAt( + (s.LastParameter - s.FirstParameter) * 0.25 + s.FirstParameter) # future midpt for arc1 + arcquad2 = s.valueAt( + (s.LastParameter - s.FirstParameter) * 0.75 + s.FirstParameter) # future midpt for arc2 arcendpt = s.valueAt(s.LastParameter) # reconstruct with 2 arcs - arcseg1 = Part.ArcOfCircle(arcstpt,arcquad1,arcmid) - arcseg2 = Part.ArcOfCircle(arcmid,arcquad2,arcendpt) + arcseg1 = Part.ArcOfCircle(arcstpt, arcquad1, arcmid) + arcseg2 = Part.ArcOfCircle(arcmid, arcquad2, arcendpt) eseg1 = arcseg1.toShape() eseg2 = arcseg2.toShape() @@ -154,43 +162,50 @@ def filterArcs(arcEdge): splitlist.append(eseg2) else: splitlist.append(s) - elif isinstance(s.Curve,Part.Line): + elif isinstance(s.Curve, Part.Line): pass return splitlist + def reverseEdge(e): if geomType(e) == "Circle": - arcstpt = e.valueAt(e.FirstParameter) - arcmid = e.valueAt((e.LastParameter-e.FirstParameter)*0.5+e.FirstParameter) + arcstpt = e.valueAt(e.FirstParameter) + arcmid = e.valueAt( + (e.LastParameter - e.FirstParameter) * 0.5 + e.FirstParameter) arcendpt = e.valueAt(e.LastParameter) - arcofCirc = Part.ArcOfCircle(arcendpt,arcmid,arcstpt) + arcofCirc = Part.ArcOfCircle(arcendpt, arcmid, arcstpt) newedge = arcofCirc.toShape() elif geomType(e) == "Line": stpt = e.valueAt(e.FirstParameter) endpt = e.valueAt(e.LastParameter) - newedge = Part.makeLine(endpt,stpt) + newedge = Part.makeLine(endpt, stpt) return newedge -def convert(toolpath,Side,radius,clockwise=False,Z=0.0,firstedge=None,vf=1.0,hf=2.0): + +def convert( + toolpath, Side, radius, clockwise=False, Z=0.0, firstedge=None, + vf=1.0, hf=2.0): '''convert(toolpath,Side,radius,clockwise=False,Z=0.0,firstedge=None) Converts lines and arcs to G1,G2,G3 moves. Returns a string.''' last = None output = "" # create the path from the offset shape for edge in toolpath: if not last: - #set the first point + # set the first point last = edge.Vertexes[0].Point - #FreeCAD.Console.PrintMessage("last pt= " + str(last)+ "\n") - output += "G1 X"+str(fmt(last.x))+" Y"+str(fmt(last.y))+" Z"+str(fmt(Z))+" F"+str(vf)+"\n" - if isinstance(edge.Curve,Part.Circle): - #FreeCAD.Console.PrintMessage("arc\n") + # FreeCAD.Console.PrintMessage("last pt= " + str(last)+ "\n") + output += "G1 X" + str(fmt(last.x)) + " Y" + str(fmt(last.y)) + \ + " Z" + str(fmt(Z)) + " F" + str(vf) + "\n" + if isinstance(edge.Curve, Part.Circle): + # FreeCAD.Console.PrintMessage("arc\n") arcstartpt = edge.valueAt(edge.FirstParameter) - midpt = edge.valueAt((edge.FirstParameter+edge.LastParameter)*0.5) + midpt = edge.valueAt( + (edge.FirstParameter + edge.LastParameter) * 0.5) arcendpt = edge.valueAt(edge.LastParameter) - arcchkpt=edge.valueAt(edge.LastParameter*.99) + # arcchkpt = edge.valueAt(edge.LastParameter * .99) - if DraftVecUtils.equals(last,arcstartpt): + if DraftVecUtils.equals(last, arcstartpt): startpt = arcstartpt endpt = arcendpt else: @@ -198,54 +213,59 @@ def convert(toolpath,Side,radius,clockwise=False,Z=0.0,firstedge=None,vf=1.0,hf= endpt = arcstartpt center = edge.Curve.Center relcenter = center.sub(last) - #FreeCAD.Console.PrintMessage("arc startpt= " + str(startpt)+ "\n") - #FreeCAD.Console.PrintMessage("arc midpt= " + str(midpt)+ "\n") - #FreeCAD.Console.PrintMessage("arc endpt= " + str(endpt)+ "\n") - arc_cw = check_clockwise([(startpt.x,startpt.y),(midpt.x,midpt.y),(endpt.x,endpt.y)]) - #FreeCAD.Console.PrintMessage("arc_cw="+ str(arc_cw)+"\n") + # FreeCAD.Console.PrintMessage("arc startpt= " + str(startpt)+ "\n") + # FreeCAD.Console.PrintMessage("arc midpt= " + str(midpt)+ "\n") + # FreeCAD.Console.PrintMessage("arc endpt= " + str(endpt)+ "\n") + arc_cw = check_clockwise( + [(startpt.x, startpt.y), (midpt.x, midpt.y), (endpt.x, endpt.y)]) + # FreeCAD.Console.PrintMessage("arc_cw="+ str(arc_cw)+"\n") if arc_cw: output += "G2" else: output += "G3" - output += " X"+str(fmt(endpt.x))+" Y"+str(fmt(endpt.y))+" Z"+str(fmt(Z))+" F"+str(hf) - output += " I" + str(fmt(relcenter.x)) + " J" + str(fmt(relcenter.y)) + " K" + str(fmt(relcenter.z)) + output += " X" + str(fmt(endpt.x)) + " Y" + \ + str(fmt(endpt.y)) + " Z" + str(fmt(Z)) + " F" + str(hf) + output += " I" + str(fmt(relcenter.x)) + " J" + \ + str(fmt(relcenter.y)) + " K" + str(fmt(relcenter.z)) output += "\n" last = endpt - #FreeCAD.Console.PrintMessage("last pt arc= " + str(last)+ "\n") + # FreeCAD.Console.PrintMessage("last pt arc= " + str(last)+ "\n") else: point = edge.Vertexes[-1].Point - if DraftVecUtils.equals(point , last): # edges can come flipped + if DraftVecUtils.equals(point, last): # edges can come flipped point = edge.Vertexes[0].Point - output += "G1 X"+str(fmt(point.x))+" Y"+str(fmt(point.y))+" Z"+str(fmt(Z))+" F"+str(hf)+"\n" + output += "G1 X" + str(fmt(point.x)) + " Y" + str(fmt(point.y)) + \ + " Z" + str(fmt(Z)) + " F" + str(hf) + "\n" last = point - #FreeCAD.Console.PrintMessage("line\n") - #FreeCAD.Console.PrintMessage("last pt line= " + str(last)+ "\n") + # FreeCAD.Console.PrintMessage("line\n") + # FreeCAD.Console.PrintMessage("last pt line= " + str(last)+ "\n") return output -def SortPath(wire,Side,radius,clockwise,firstedge=None,SegLen =0.5): + +def SortPath(wire, Side, radius, clockwise, firstedge=None, SegLen=0.5): '''SortPath(wire,Side,radius,clockwise,firstedge=None,SegLen =0.5) Sorts the wire and reverses it, if needed. Splits arcs over 180 degrees in two. Returns the reordered offset of the wire. ''' if firstedge: edgelist = wire.Edges[:] if wire.isClosed(): elindex = None - n=0 + n = 0 for e in edgelist: - if isSameEdge(e,firstedge): -# FreeCAD.Console.PrintMessage('found first edge\n') + if isSameEdge(e, firstedge): + # FreeCAD.Console.PrintMessage('found first edge\n') elindex = n - n=n+1 + n = n + 1 l1 = edgelist[:elindex] l2 = edgelist[elindex:] - newedgelist = l2+l1 + newedgelist = l2 + l1 if clockwise: newedgelist.reverse() last = newedgelist.pop(-1) newedgelist.insert(0, last) - preoffset= [] + preoffset = [] for e in newedgelist: - if clockwise: + if clockwise: r = reverseEdge(e) preoffset.append(r) else: @@ -266,16 +286,16 @@ def SortPath(wire,Side,radius,clockwise,firstedge=None,SegLen =0.5): elif geomType(e) == "Line": edgelist.append(e) elif geomType(e) == "BSplineCurve" or \ - geomType(e) == "BezierCurve" or \ - geomType(e) == "Ellipse": - edgelist.append(Part.Wire(curvetowire(e,(SegLen)))) + geomType(e) == "BezierCurve" or \ + geomType(e) == "Ellipse": + edgelist.append(Part.Wire(curvetowire(e, (SegLen)))) newwire = Part.Wire(edgelist) if Side == 'Left': - # we use the OCC offset feature - offset = newwire.makeOffset(radius)#tool is outside line + # we use the OCC offset feature + offset = newwire.makeOffset(radius) # tool is outside line elif Side == 'Right': - offset = newwire.makeOffset(-radius)#tool is inside line + offset = newwire.makeOffset(-radius) # tool is inside line else: if wire.isClosed(): offset = newwire.makeOffset(0.0) @@ -284,48 +304,57 @@ def SortPath(wire,Side,radius,clockwise,firstedge=None,SegLen =0.5): return offset -def MakePath(wire,Side,radius,clockwise,ZClearance,StepDown,ZStart,ZFinalDepth,firstedge=None,PathClosed=True,SegLen =0.5,VertFeed=1.0,HorizFeed=2.0): + +def MakePath(wire, Side, radius, clockwise, ZClearance, StepDown, ZStart, ZFinalDepth, firstedge=None, PathClosed=True, SegLen=0.5, VertFeed=1.0, HorizFeed=2.0): ''' makes the path - just a simple profile for now ''' - offset = SortPath(wire,Side,radius,clockwise,firstedge,SegLen=0.5) + offset = SortPath(wire, Side, radius, clockwise, firstedge, SegLen=0.5) toolpath = offset.Edges[:] paths = "" - paths += "G0 Z" + str(ZClearance)+"\n" + paths += "G0 Z" + str(ZClearance) + "\n" first = toolpath[0].Vertexes[0].Point - paths += "G0 X"+str(fmt(first.x))+"Y"+str(fmt(first.y))+"\n" - ZCurrent = ZStart- StepDown + paths += "G0 X" + str(fmt(first.x)) + "Y" + str(fmt(first.y)) + "\n" + ZCurrent = ZStart - StepDown if PathClosed: while ZCurrent > ZFinalDepth: - paths += convert(toolpath,Side,radius,clockwise,ZCurrent,firstedge,VertFeed,HorizFeed) - ZCurrent = ZCurrent-abs(StepDown) - paths += convert(toolpath,Side,radius,clockwise,ZFinalDepth,firstedge,VertFeed,HorizFeed) + paths += convert(toolpath, Side, radius, clockwise, + ZCurrent, firstedge, VertFeed, HorizFeed) + ZCurrent = ZCurrent - abs(StepDown) + paths += convert(toolpath, Side, radius, clockwise, + ZFinalDepth, firstedge, VertFeed, HorizFeed) paths += "G0 Z" + str(ZClearance) else: while ZCurrent > ZFinalDepth: - paths += convert(toolpath,Side,radius,clockwise,ZCurrent,firstedge,VertFeed,HorizFeed) + paths += convert(toolpath, Side, radius, clockwise, + ZCurrent, firstedge, VertFeed, HorizFeed) paths += "G0 Z" + str(ZClearance) - paths += "G0 X"+str(fmt(first.x))+"Y"+str(fmt(first.y))+"\n" - ZCurrent = ZCurrent-abs(StepDown) - paths += convert(toolpath,Side,radius,clockwise,ZFinalDepth,firstedge,VertFeed,HorizFeed) + paths += "G0 X" + str(fmt(first.x)) + "Y" + \ + str(fmt(first.y)) + "\n" + ZCurrent = ZCurrent - abs(StepDown) + paths += convert(toolpath, Side, radius, clockwise, + ZFinalDepth, firstedge, VertFeed, HorizFeed) paths += "G0 Z" + str(ZClearance) return paths -# the next two functions are for automatically populating tool numbers/height offset numbers based on previously active toolnumbers +# the next two functions are for automatically populating tool +# numbers/height offset numbers based on previously active toolnumbers -def changeTool(obj,proj): + +def changeTool(obj, proj): tlnum = 0 for p in proj.Group: - if not hasattr(p,"Group"): - if isinstance(p.Proxy,PathScripts.PathLoadTool.LoadTool) and p.ToolNumber > 0: + if not hasattr(p, "Group"): + if isinstance(p.Proxy, PathScripts.PathLoadTool.LoadTool) and p.ToolNumber > 0: tlnum = p.ToolNumber if p == obj: return tlnum - elif hasattr(p,"Group"): + elif hasattr(p, "Group"): for g in p.Group: - if isinstance(g.Proxy,PathScripts.PathLoadTool.LoadTool): + if isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool): tlnum = g.ToolNumber if g == obj: return tlnum + def getLastTool(obj): toolNum = obj.ToolNumber if obj.ToolNumber == 0: @@ -334,11 +363,13 @@ def getLastTool(obj): toolNum = changeTool(obj, proj) return getTool(obj, toolNum) + def getLastToolLoad(obj): - #This walks up the hierarchy and tries to find the closest preceding toolchange. + # This walks up the hierarchy and tries to find the closest preceding + # ToolLoadOject (tlo). import PathScripts - tc = None + tlo = None lastfound = None try: @@ -347,42 +378,43 @@ def getLastToolLoad(obj): except: parent = None - while parent != None: - + while parent is not None: sibs = parent.Group for g in sibs: - if isinstance(g.Proxy,PathScripts.PathLoadTool.LoadTool): + if isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool): lastfound = g if g == child: - tc = lastfound + tlo = lastfound - if tc == None: + if tlo is None: try: child = parent - parent = parent.InList[0] + parent = child.InList[0] except: parent = None else: - return tc + return tlo - if tc == None: - for g in FreeCAD.ActiveDocument.Objects: #top level object - if isinstance(g.Proxy,PathScripts.PathLoadTool.LoadTool): - lastfound = g + if tlo is None: + for g in FreeCAD.ActiveDocument.Objects: # Look in top level + if hasattr(g, "Proxy"): + if isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool): + lastfound = g if g == obj: - tc = lastfound - return tc + tlo = lastfound + return tlo -def getTool(obj,number=0): + +def getTool(obj, number=0): "retrieves a tool from a hosting object with a tooltable, if any" for o in obj.InList: if o.TypeId == "Path::FeatureCompoundPython": for m in o.Group: - if hasattr(m,"Tooltable"): + if hasattr(m, "Tooltable"): return m.Tooltable.getTool(number) # not found? search one level up for o in obj.InList: - return getTool(o,number) + return getTool(o, number) return None @@ -392,6 +424,7 @@ def findProj(): if isinstance(o.Proxy, PathProject.ObjectPathProject): return o + def findMachine(): '''find machine object for the tooltable editor ''' for o in FreeCAD.ActiveDocument.Objects: @@ -399,10 +432,11 @@ def findMachine(): if isinstance(o.Proxy, PathScripts.PathMachine.Machine): return o + def addToProject(obj): """Adds a path obj to this document, if no PathParoject exists it's created on the fly""" p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Path") - if p.GetBool("pathAutoProject",True): + if p.GetBool("pathAutoProject", True): project = findProj() if not project: project = PathProject.CommandProject.Create() @@ -420,9 +454,10 @@ def getLastZ(obj): for c in g.Path.Commands: for n in c.Parameters: if n == 'Z': - lastZ= c.Parameters['Z'] + lastZ = c.Parameters['Z'] return lastZ + def frange(start, stop, step, finish): x = [] curdepth = start @@ -435,7 +470,8 @@ def frange(start, stop, step, finish): curdepth = stop + finish x.append(curdepth) - # we might have to do a last pass or else finish round might be too far away + # we might have to do a last pass or else finish round might be too far + # away if curdepth - stop > finish: x.append(stop + finish) @@ -444,10 +480,182 @@ def frange(start, stop, step, finish): curdepth = stop x.append(curdepth) - return x +def rapid(x=None, y=None, z=None): + """ Returns gcode string to perform a rapid move.""" + retstr = "G00" + if (x is not None) or (y is not None) or (z is not None): + if (x is not None): + retstr += " X" + str("%.4f" % x) + if (y is not None): + retstr += " Y" + str("%.4f" % y) + if (z is not None): + retstr += " Z" + str("%.4f" % z) + else: + return "" + return retstr + "\n" + +def feed(x=None, y=None, z=None, horizFeed=0, vertFeed=0): + """ Return gcode string to perform a linear feed.""" + global feedxy + retstr = "G01 F" + if(x is None) and (y is None): + retstr += str("%.4f" % horizFeed) + else: + retstr += str("%.4f" % vertFeed) + + if (x is not None) or (y is not None) or (z is not None): + if (x is not None): + retstr += " X" + str("%.4f" % x) + if (y is not None): + retstr += " Y" + str("%.4f" % y) + if (z is not None): + retstr += " Z" + str("%.4f" % z) + else: + return "" + return retstr + "\n" + +def arc(cx, cy, sx, sy, ex, ey, horizFeed=0, ez=None, ccw=False): + """ + Return gcode string to perform an arc. + + Assumes XY plane or helix around Z + Don't worry about starting Z- assume that's dealt with elsewhere + If start/end radii aren't within eps, abort. + + cx, cy -- arc center coordinates + sx, sy -- arc start coordinates + ex, ey -- arc end coordinates + ez -- ending Z coordinate. None unless helix. + horizFeed -- horiz feed speed + ccw -- arc direction + """ + + eps = 0.01 + if (math.sqrt((cx - sx)**2 + (cy - sy)**2) - math.sqrt((cx - ex)**2 + (cy - ey)**2)) >= eps: + print "ERROR: Illegal arc: Start and end radii not equal" + return "" + + retstr = "" + if ccw: + retstr += "G03 F" + str(horizFeed) + else: + retstr += "G02 F" + str(horizFeed) + + retstr += " X" + str("%.4f" % ex) + " Y" + str("%.4f" % ey) + + if ez is not None: + retstr += " Z" + str("%.4f" % ez) + + retstr += " I" + str("%.4f" % (cx - sx)) + " J" + str("%.4f" % (cy - sy)) + + return retstr + "\n" + +def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed): + """ + Return gcode string to perform helical entry move. + + plungePos -- vector of the helical entry location + destZ -- the lowest Z position or milling level + startZ -- Starting Z position for helical move + rampangle -- entry angle + toold -- tool diameter + plungeR -- the radius of the entry helix + """ + # toold = self.radius * 2 + + helixCmds = "(START HELICAL PLUNGE)\n" + if(plungePos is None): + raise Exception("Helical plunging requires a position!") + return None + + helixX = plungePos.x + toold/2 * plungeR + helixY = plungePos.y + + helixCirc = math.pi * toold * plungeR + dzPerRev = math.sin(rampangle/180. * math.pi) * helixCirc + + # Go to the start of the helix position + helixCmds += rapid(helixX, helixY) + helixCmds += rapid(z=startZ) + + # Helix as required to get to the requested depth + lastZ = startZ + curZ = max(startZ-dzPerRev, destZ) + done = False + while not done: + done = (curZ == destZ) + # NOTE: FreeCAD doesn't render this, but at least LinuxCNC considers it valid + # helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX, helixY, ez = curZ, ccw=True) + + # Use two half-helixes; FreeCAD renders that correctly, + # and it fits with the other code breaking up 360-degree arcs + helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX - toold * plungeR, helixY, horizFeed, ez=(curZ + lastZ)/2., ccw=True) + helixCmds += arc(plungePos.x, plungePos.y, helixX - toold * plungeR, helixY, helixX, helixY, horizFeed, ez=curZ, ccw=True) + lastZ = curZ + curZ = max(curZ - dzPerRev, destZ) + + return helixCmds + +def rampPlunge(edge, rampangle, destZ, startZ): + """ + Return gcode string to linearly ramp down to milling level. + + edge -- edge to follow + rampangle -- entry angle + destZ -- Final Z depth + startZ -- Starting Z depth + + FIXME: This ramps along the first edge, assuming it's long + enough, NOT just wiggling back and forth by ~0.75 * toolD. + Not sure if that's any worse, but it's simpler + I think this should be changed to be limited to a maximum ramp size. Otherwise machine time will get longer than it needs to be. + """ + + rampCmds = "(START RAMP PLUNGE)\n" + if(edge is None): + raise Exception("Ramp plunging requires an edge!") + return None + + sPoint = edge.Vertexes[0].Point + ePoint = edge.Vertexes[1].Point + # Evidently edges can get flipped- pick the right one in this case + # FIXME: This is iffy code, based on what already existed in the "for vpos ..." loop below + if ePoint == sPoint: + # print "FLIP" + ePoint = edge.Vertexes[-1].Point + + rampDist = edge.Length + rampDZ = math.sin(rampangle/180. * math.pi) * rampDist + + rampCmds += rapid(sPoint.x, sPoint.y) + rampCmds += rapid(z=startZ) + + # Ramp down to the requested depth + # FIXME: This might be an arc, so handle that as well + + curZ = max(startZ-rampDZ, destZ) + done = False + while not done: + done = (curZ == destZ) + + # If it's an arc, handle it! + if isinstance(edge.Curve, Part.Circle): + raise Exception("rampPlunge: Screw it, not handling an arc.") + # Straight feed! Easy! + else: + rampCmds += feed(ePoint.x, ePoint.y, curZ) + rampCmds += feed(sPoint.x, sPoint.y) + + curZ = max(curZ - rampDZ, destZ) + + return rampCmds + + + class depth_params: + def __init__(self, clearance_height, rapid_safety_space, start_depth, step_down, z_finish_depth, final_depth, user_depths=None): self.clearance_height = clearance_height self.rapid_safety_space = math.fabs(rapid_safety_space) @@ -459,17 +667,19 @@ class depth_params: def get_depths(self): depths = [] - if self.user_depths != None: + if self.user_depths is not None: depths = self.user_depths else: depth = self.final_depth depths = [depth] depth += self.z_finish_depth if depth + 0.0000001 < self.start_depth: - if self.z_finish_depth > 0.0000001: depths.insert(0, depth) - layer_count = int((self.start_depth - depth) / self.step_down - 0.0000001) + 1 + if self.z_finish_depth > 0.0000001: + depths.insert(0, depth) + layer_count = int((self.start_depth - depth) / + self.step_down - 0.0000001) + 1 if layer_count > 0: - layer_depth = (self.start_depth - depth)/layer_count + layer_depth = (self.start_depth - depth) / layer_count for i in range(1, layer_count): depth += layer_depth depths.append(depth)