From 37095fbf7868daf1580636f10ddb671dbc28700c Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Mon, 28 Feb 2022 21:06:16 -0800 Subject: [PATCH] Consistent black formatting of all Path python files --- src/Mod/Path/Init.py | 52 +- src/Mod/Path/PathScripts/PathAreaOp.py | 8 +- src/Mod/Path/PathScripts/PathArray.py | 20 +- .../PathScripts/PathCircularHoleBaseGui.py | 46 +- src/Mod/Path/PathScripts/PathDeburrGui.py | 4 +- src/Mod/Path/PathScripts/PathDressup.py | 28 +- .../Path/PathScripts/PathDressupAxisMap.py | 1 - .../Path/PathScripts/PathDressupDogbone.py | 21 +- .../Path/PathScripts/PathDressupDragknife.py | 1 - .../Path/PathScripts/PathDressupLeadInOut.py | 1 - .../PathScripts/PathDressupPathBoundaryGui.py | 1 - .../Path/PathScripts/PathDressupRampEntry.py | 1 - src/Mod/Path/PathScripts/PathDressupTag.py | 4 +- src/Mod/Path/PathScripts/PathDressupTagGui.py | 1 - .../Path/PathScripts/PathFeatureExtensions.py | 4 +- .../PathScripts/PathFeatureExtensionsGui.py | 12 +- src/Mod/Path/PathScripts/PathGetPoint.py | 105 ++- src/Mod/Path/PathScripts/PathGuiInit.py | 6 +- src/Mod/Path/PathScripts/PathJob.py | 1 + src/Mod/Path/PathScripts/PathJobCmd.py | 7 +- src/Mod/Path/PathScripts/PathJobDlg.py | 4 +- src/Mod/Path/PathScripts/PathLog.py | 73 +- src/Mod/Path/PathScripts/PathOp.py | 8 +- src/Mod/Path/PathScripts/PathOpGui.py | 6 +- src/Mod/Path/PathScripts/PathPostProcessor.py | 27 +- .../PathScripts/PathPreferencesPathJob.py | 252 ++++-- src/Mod/Path/PathScripts/PathProfile.py | 16 +- .../Path/PathScripts/PathProfileContour.py | 9 +- src/Mod/Path/PathScripts/PathProfileEdges.py | 9 +- src/Mod/Path/PathScripts/PathProfileFaces.py | 9 +- src/Mod/Path/PathScripts/PathProperty.py | 73 +- src/Mod/Path/PathScripts/PathPropertyBag.py | 4 +- src/Mod/Path/PathScripts/PathSelection.py | 8 +- .../PathScripts/PathSetupSheetOpPrototype.py | 75 +- src/Mod/Path/PathScripts/PathSimulatorGui.py | 20 +- src/Mod/Path/PathScripts/PathSurfaceGui.py | 2 +- .../Path/PathScripts/PathThreadMillingGui.py | 4 +- src/Mod/Path/PathScripts/PathToolBit.py | 7 +- .../Path/PathScripts/PathToolControllerGui.py | 1 - src/Mod/Path/PathScripts/PathToolEdit.py | 165 ++-- src/Mod/Path/PathScripts/PathUtils.py | 8 +- src/Mod/Path/PathScripts/PathUtilsGui.py | 19 +- src/Mod/Path/PathScripts/PathWaterline.py | 1 - src/Mod/Path/PathScripts/PostUtils.py | 4 +- .../Path/PathScripts/post/KineticNC_post.py | 191 ++-- .../Path/PathScripts/post/centroid_post.py | 146 ++-- .../Path/PathScripts/post/comparams_post.py | 63 +- src/Mod/Path/PathScripts/post/dumper_post.py | 23 +- .../Path/PathScripts/post/dynapath_post.py | 131 +-- src/Mod/Path/PathScripts/post/example_post.py | 12 +- src/Mod/Path/PathScripts/post/example_pre.py | 6 +- src/Mod/Path/PathScripts/post/fablin_post.py | 95 +- src/Mod/Path/PathScripts/post/fanuc_post.py | 356 +++++--- src/Mod/Path/PathScripts/post/grbl_post.py | 2 +- .../Path/PathScripts/post/heidenhain_post.py | 796 ++++++++++------- src/Mod/Path/PathScripts/post/jtech_post.py | 180 ++-- .../Path/PathScripts/post/linuxcnc_post.py | 215 +++-- .../Path/PathScripts/post/mach3_mach4_post.py | 272 ++++-- src/Mod/Path/PathScripts/post/nccad_post.py | 45 +- src/Mod/Path/PathScripts/post/opensbp_post.py | 103 ++- src/Mod/Path/PathScripts/post/opensbp_pre.py | 103 ++- src/Mod/Path/PathScripts/post/philips_post.py | 380 +++++--- src/Mod/Path/PathScripts/post/rml_post.py | 162 ++-- src/Mod/Path/PathScripts/post/rrf_post.py | 542 ++++++------ src/Mod/Path/PathScripts/post/slic3r_pre.py | 9 +- .../Path/PathScripts/post/smoothie_post.py | 162 ++-- src/Mod/Path/PathScripts/post/uccnc_post.py | 258 +++--- src/Mod/Path/PathTests/PathTestUtils.py | 62 +- src/Mod/Path/PathTests/TestPathAdaptive.py | 230 +++-- src/Mod/Path/PathTests/TestPathCore.py | 122 +-- src/Mod/Path/PathTests/TestPathDeburr.py | 31 +- .../Path/PathTests/TestPathDressupDogbone.py | 104 ++- .../PathTests/TestPathDressupHoldingTags.py | 11 +- src/Mod/Path/PathTests/TestPathGeom.py | 819 +++++++++++++----- src/Mod/Path/PathTests/TestPathHelix.py | 63 +- src/Mod/Path/PathTests/TestPathLog.py | 83 +- src/Mod/Path/PathTests/TestPathOpTools.py | 401 ++++++--- src/Mod/Path/PathTests/TestPathPost.py | 13 +- src/Mod/Path/PathTests/TestPathPreferences.py | 41 +- src/Mod/Path/PathTests/TestPathPropertyBag.py | 57 +- src/Mod/Path/PathTests/TestPathSetupSheet.py | 256 ++++-- src/Mod/Path/PathTests/TestPathStock.py | 98 ++- .../Path/PathTests/TestPathThreadMilling.py | 24 +- src/Mod/Path/PathTests/TestPathTool.py | 40 +- src/Mod/Path/PathTests/TestPathToolBit.py | 95 +- .../Path/PathTests/TestPathToolController.py | 53 +- src/Mod/Path/PathTests/TestPathTooltable.py | 28 +- src/Mod/Path/PathTests/TestPathUtil.py | 48 +- src/Mod/Path/PathTests/TestPathVcarve.py | 74 +- src/Mod/Path/PathTests/TestPathVoronoi.py | 129 ++- src/Mod/Path/TestPathApp.py | 2 + src/Mod/Path/Tools/toolbit-attributes.py | 64 +- src/Mod/Path/libarea/kurve/test.py | 6 +- 93 files changed, 5216 insertions(+), 3118 deletions(-) diff --git a/src/Mod/Path/Init.py b/src/Mod/Path/Init.py index 076cb16a4b..138a54ed40 100644 --- a/src/Mod/Path/Init.py +++ b/src/Mod/Path/Init.py @@ -1,32 +1,32 @@ -#*************************************************************************** -#* Copyright (c) 2014 Yorik van Havre * -#* * -#* This file is part of the FreeCAD CAx development system. * -#* * -#* 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. * -#* * -#* FreeCAD 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 Lesser General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * -#* License along with FreeCAD; 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 file is part of the FreeCAD CAx development system. * +# * * +# * 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. * +# * * +# * FreeCAD 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 Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** # Get the Parameter Group of this module ParGrp = App.ParamGet("System parameter:Modules").GetGroup("Path") # Set the needed information -ParGrp.SetString("HelpIndex", "Path/Help/index.html") -ParGrp.SetString("WorkBenchName", "Path") -ParGrp.SetString("WorkBenchModule", "PathWorkbench.py") +ParGrp.SetString("HelpIndex", "Path/Help/index.html") +ParGrp.SetString("WorkBenchName", "Path") +ParGrp.SetString("WorkBenchModule", "PathWorkbench.py") -FreeCAD.__unit_test__ += [ "TestPathApp" ] +FreeCAD.__unit_test__ += ["TestPathApp"] diff --git a/src/Mod/Path/PathScripts/PathAreaOp.py b/src/Mod/Path/PathScripts/PathAreaOp.py index 799ae60a41..fc6900103a 100644 --- a/src/Mod/Path/PathScripts/PathAreaOp.py +++ b/src/Mod/Path/PathScripts/PathAreaOp.py @@ -227,9 +227,7 @@ class ObjectOp(PathOp.ObjectOp): area.setPlane(PathUtils.makeWorkplane(baseobject)) area.add(baseobject) - areaParams = self.areaOpAreaParams( - obj, isHole - ) + areaParams = self.areaOpAreaParams(obj, isHole) heights = [i for i in self.depthparams] PathLog.debug("depths: {}".format(heights)) @@ -245,9 +243,7 @@ class ObjectOp(PathOp.ObjectOp): shapelist = [sec.getShape() for sec in sections] PathLog.debug("shapelist = %s" % shapelist) - pathParams = self.areaOpPathParams( - obj, isHole - ) + pathParams = self.areaOpPathParams(obj, isHole) pathParams["shapes"] = shapelist pathParams["feedrate"] = self.horizFeed pathParams["feedrate_v"] = self.vertFeed diff --git a/src/Mod/Path/PathScripts/PathArray.py b/src/Mod/Path/PathScripts/PathArray.py index e90088c8c8..f179b52e13 100644 --- a/src/Mod/Path/PathScripts/PathArray.py +++ b/src/Mod/Path/PathScripts/PathArray.py @@ -118,11 +118,11 @@ class ObjectArray: QT_TRANSLATE_NOOP("App::Property", "Maximum random offset of copies"), ) obj.addProperty( - "App::PropertyInteger", - "JitterSeed", - "Path", - QT_TRANSLATE_NOOP("App::Property","Seed value for jitter randomness"), - ) + "App::PropertyInteger", + "JitterSeed", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Seed value for jitter randomness"), + ) obj.addProperty( "App::PropertyLink", "ToolController", @@ -165,8 +165,14 @@ class ObjectArray: copiesXMode = copiesYMode = offsetMode = swapDirectionMode = 2 if not hasattr(obj, "JitterSeed"): - obj.addProperty("App::PropertyInteger", "JitterSeed", - "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","Seed value for jitter randomness")) + obj.addProperty( + "App::PropertyInteger", + "JitterSeed", + "Path", + QtCore.QT_TRANSLATE_NOOP( + "App::Property", "Seed value for jitter randomness" + ), + ) obj.JitterSeed = 0 obj.setEditorMode("Angle", angleMode) diff --git a/src/Mod/Path/PathScripts/PathCircularHoleBaseGui.py b/src/Mod/Path/PathScripts/PathCircularHoleBaseGui.py index 166f24ee13..4f75193f31 100644 --- a/src/Mod/Path/PathScripts/PathCircularHoleBaseGui.py +++ b/src/Mod/Path/PathScripts/PathCircularHoleBaseGui.py @@ -22,7 +22,7 @@ import FreeCAD import FreeCADGui -import PathGui as PGui # ensure Path/Gui/Resources are loaded +import PathGui as PGui # ensure Path/Gui/Resources are loaded import PathScripts.PathLog as PathLog import PathScripts.PathOpGui as PathOpGui @@ -41,27 +41,28 @@ if LOGLEVEL: else: PathLog.setLevel(PathLog.Level.NOTICE, PathLog.thisModule()) + class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): - '''Controller class to be used for the BaseGeomtery page. + """Controller class to be used for the BaseGeomtery page. Circular holes don't just display the feature, they also add a column displaying the radius the feature describes. This page provides that - UI and functionality for all circular hole based operations.''' + UI and functionality for all circular hole based operations.""" DataFeatureName = QtCore.Qt.ItemDataRole.UserRole - DataObject = QtCore.Qt.ItemDataRole.UserRole + 1 - DataObjectSub = QtCore.Qt.ItemDataRole.UserRole + 2 + DataObject = QtCore.Qt.ItemDataRole.UserRole + 1 + DataObjectSub = QtCore.Qt.ItemDataRole.UserRole + 2 InitBase = False def getForm(self): - '''getForm() ... load and return page''' + """getForm() ... load and return page""" return FreeCADGui.PySideUic.loadUi(":/panels/PageBaseHoleGeometryEdit.ui") def initPage(self, obj): self.updating = False def setFields(self, obj): - '''setFields(obj) ... fill form with values from obj''' + """setFields(obj) ... fill form with values from obj""" PathLog.track() self.form.baseList.blockSignals(True) self.form.baseList.clearContents() @@ -80,7 +81,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): item.setData(self.DataFeatureName, name) item.setData(self.DataObject, base) item.setData(self.DataObjectSub, sub) - self.form.baseList.setItem(self.form.baseList.rowCount()-1, 0, item) + self.form.baseList.setItem(self.form.baseList.rowCount() - 1, 0, item) dia = obj.Proxy.holeDiameter(obj, base, sub) item = QtGui.QTableWidgetItem("{:.3f}".format(dia)) @@ -88,7 +89,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): item.setData(self.DataObject, base) item.setData(self.DataObjectSub, sub) item.setTextAlignment(QtCore.Qt.AlignHCenter) - self.form.baseList.setItem(self.form.baseList.rowCount()-1, 1, item) + self.form.baseList.setItem(self.form.baseList.rowCount() - 1, 1, item) self.form.baseList.resizeColumnToContents(0) self.form.baseList.blockSignals(False) @@ -96,7 +97,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): self.itemActivated() def itemActivated(self): - '''itemActivated() ... callback when item in table is selected''' + """itemActivated() ... callback when item in table is selected""" PathLog.track() if self.form.baseList.selectedItems(): self.form.deleteBase.setEnabled(True) @@ -117,21 +118,23 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): self.form.deleteBase.setEnabled(False) def deleteBase(self): - '''deleteBase() ... callback for push button''' + """deleteBase() ... callback for push button""" PathLog.track() - selected = [self.form.baseList.row(item) for item in self.form.baseList.selectedItems()] + selected = [ + self.form.baseList.row(item) for item in self.form.baseList.selectedItems() + ] self.form.baseList.blockSignals(True) for row in sorted(list(set(selected)), key=lambda row: -row): self.form.baseList.removeRow(row) self.updateBase() self.form.baseList.resizeColumnToContents(0) self.form.baseList.blockSignals(False) - #self.obj.Proxy.execute(self.obj) + # self.obj.Proxy.execute(self.obj) FreeCAD.ActiveDocument.recompute() self.setFields(self.obj) def updateBase(self): - '''updateBase() ... helper function to transfer current table to obj''' + """updateBase() ... helper function to transfer current table to obj""" PathLog.track() newlist = [] for i in range(self.form.baseList.rowCount()): @@ -147,7 +150,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): self.updating = False def checkedChanged(self): - '''checkeChanged() ... callback when checked status of a base feature changed''' + """checkeChanged() ... callback when checked status of a base feature changed""" PathLog.track() disabled = [] for i in range(0, self.form.baseList.rowCount()): @@ -158,7 +161,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): FreeCAD.ActiveDocument.recompute() def registerSignalHandlers(self, obj): - '''registerSignalHandlers(obj) ... setup signal handlers''' + """registerSignalHandlers(obj) ... setup signal handlers""" self.form.baseList.itemSelectionChanged.connect(self.itemActivated) self.form.addBase.clicked.connect(self.addBase) self.form.deleteBase.clicked.connect(self.deleteBase) @@ -166,7 +169,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): self.form.baseList.itemChanged.connect(self.checkedChanged) def resetBase(self): - '''resetBase() ... push button callback''' + """resetBase() ... push button callback""" self.obj.Base = [] self.obj.Disabled = [] self.obj.Proxy.findAllHoles(self.obj) @@ -175,13 +178,14 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): FreeCAD.ActiveDocument.recompute() def updateData(self, obj, prop): - '''updateData(obj, prop) ... callback whenever a property of the model changed''' - if not self.updating and prop in ['Base', 'Disabled']: + """updateData(obj, prop) ... callback whenever a property of the model changed""" + if not self.updating and prop in ["Base", "Disabled"]: self.setFields(obj) + class TaskPanelOpPage(PathOpGui.TaskPanelPage): - '''Base class for circular hole based operation's page controller.''' + """Base class for circular hole based operation's page controller.""" def taskPanelBaseGeometryPage(self, obj, features): - '''taskPanelBaseGeometryPage(obj, features) ... Return circular hole specific page controller for Base Geometry.''' + """taskPanelBaseGeometryPage(obj, features) ... Return circular hole specific page controller for Base Geometry.""" return TaskPanelHoleGeometryPage(obj, features) diff --git a/src/Mod/Path/PathScripts/PathDeburrGui.py b/src/Mod/Path/PathScripts/PathDeburrGui.py index ebe19ef33c..2222721d2a 100644 --- a/src/Mod/Path/PathScripts/PathDeburrGui.py +++ b/src/Mod/Path/PathScripts/PathDeburrGui.py @@ -73,9 +73,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.opImagePath = "{}Mod/Path/Images/Ops/{}".format( FreeCAD.getHomePath(), "chamfer.svg" ) - self.opImage = QtGui.QPixmap( - self.opImagePath - ) + self.opImage = QtGui.QPixmap(self.opImagePath) self.form.opImage.setPixmap(self.opImage) iconMiter = QtGui.QIcon(":/icons/edge-join-miter-not.svg") iconMiter.addFile(":/icons/edge-join-miter.svg", state=QtGui.QIcon.On) diff --git a/src/Mod/Path/PathScripts/PathDressup.py b/src/Mod/Path/PathScripts/PathDressup.py index 6efdf60174..cec69a65fc 100644 --- a/src/Mod/Path/PathScripts/PathDressup.py +++ b/src/Mod/Path/PathScripts/PathDressup.py @@ -23,30 +23,38 @@ import FreeCAD import PathScripts.PathJob as PathJob + def selection(): - '''isActive() ... return True if a dressup command is possible.''' + """isActive() ... return True if a dressup command is possible.""" if FreeCAD.ActiveDocument and FreeCAD.GuiUp: import FreeCADGui + sel = FreeCADGui.Selection.getSelectionEx() - if len(sel) == 1 and sel[0].Object.isDerivedFrom("Path::Feature") and PathJob.Instances(): + if ( + len(sel) == 1 + and sel[0].Object.isDerivedFrom("Path::Feature") + and PathJob.Instances() + ): return sel[0].Object return None + def hasEntryMethod(path): - '''hasEntryDressup(path) ... returns true if the given object already has an entry method attached.''' - if 'RampEntry' in path.Name or 'LeadInOut' in path.Name: + """hasEntryDressup(path) ... returns true if the given object already has an entry method attached.""" + if "RampEntry" in path.Name or "LeadInOut" in path.Name: return True - if 'Dressup' in path.Name and hasattr(path, 'Base'): + if "Dressup" in path.Name and hasattr(path, "Base"): return hasEntryMethod(path.Base) return False + def baseOp(path): - '''baseOp(path) ... return the base operation underlying the given path''' - if 'Dressup' in path.Name: + """baseOp(path) ... return the base operation underlying the given path""" + if "Dressup" in path.Name: return baseOp(path.Base) return path -def toolController(path): - '''toolController(path) ... return the tool controller from the base op.''' - return baseOp(path).ToolController +def toolController(path): + """toolController(path) ... return the tool controller from the base op.""" + return baseOp(path).ToolController diff --git a/src/Mod/Path/PathScripts/PathDressupAxisMap.py b/src/Mod/Path/PathScripts/PathDressupAxisMap.py index b019cd7900..83a1871a36 100644 --- a/src/Mod/Path/PathScripts/PathDressupAxisMap.py +++ b/src/Mod/Path/PathScripts/PathDressupAxisMap.py @@ -260,7 +260,6 @@ class ViewProviderDressup: class CommandPathDressup: - def GetResources(self): return { "Pixmap": "Path_Dressup", diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index c25740d6ae..0a22d29ad5 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -391,9 +391,7 @@ class Bone(object): # c = distance # b = self.toolRadius # beta = fabs(boneAngle - theta) - beta = math.fabs( - addAngle(boneAngle, -theta) - ) + beta = math.fabs(addAngle(boneAngle, -theta)) D = (distance / toolRadius) * math.sin(beta) if D > 1: # no intersection PathLog.debug("adaptive - no intersection - no bone") @@ -432,9 +430,7 @@ class ObjectDressup(object): "App::PropertyEnumeration", "Side", "Dressup", - QT_TRANSLATE_NOOP( - "App::Property", "The side of path to insert bones" - ), + QT_TRANSLATE_NOOP("App::Property", "The side of path to insert bones"), ) obj.Side = [Side.Left, Side.Right] obj.Side = Side.Right @@ -450,9 +446,7 @@ class ObjectDressup(object): "App::PropertyIntegerList", "BoneBlacklist", "Dressup", - QT_TRANSLATE_NOOP( - "App::Property", "Bones that aren't dressed up" - ), + QT_TRANSLATE_NOOP("App::Property", "Bones that aren't dressed up"), ) obj.BoneBlacklist = [] obj.setEditorMode("BoneBlacklist", 2) # hide this one @@ -470,9 +464,7 @@ class ObjectDressup(object): "App::PropertyFloat", "Custom", "Dressup", - QT_TRANSLATE_NOOP( - "App::Property", "Dressup length if Incision == custom" - ), + QT_TRANSLATE_NOOP("App::Property", "Dressup length if Incision == custom"), ) obj.Custom = 0.0 obj.Proxy = self @@ -1367,13 +1359,10 @@ def Create(base, name="DogboneDressup"): class CommandDressupDogbone(object): - def GetResources(self): return { "Pixmap": "Path_Dressup", - "MenuText": QT_TRANSLATE_NOOP( - "Path_DressupDogbone", "Dogbone Dress-up" - ), + "MenuText": QT_TRANSLATE_NOOP("Path_DressupDogbone", "Dogbone Dress-up"), "ToolTip": QT_TRANSLATE_NOOP( "Path_DressupDogbone", "Creates a Dogbone Dress-up object from a selected path", diff --git a/src/Mod/Path/PathScripts/PathDressupDragknife.py b/src/Mod/Path/PathScripts/PathDressupDragknife.py index b57e6aa1d5..5f7defb6f5 100644 --- a/src/Mod/Path/PathScripts/PathDressupDragknife.py +++ b/src/Mod/Path/PathScripts/PathDressupDragknife.py @@ -596,7 +596,6 @@ class ViewProviderDressup: class CommandDressupDragknife: - def GetResources(self): return { "Pixmap": "Path_Dressup", diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index c8e8482e67..ec2b5228c6 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -663,7 +663,6 @@ class ViewProviderDressup: class CommandPathDressupLeadInOut: - def GetResources(self): return { "Pixmap": "Path_Dressup", diff --git a/src/Mod/Path/PathScripts/PathDressupPathBoundaryGui.py b/src/Mod/Path/PathScripts/PathDressupPathBoundaryGui.py index 12a9ae139e..f2893ebb55 100644 --- a/src/Mod/Path/PathScripts/PathDressupPathBoundaryGui.py +++ b/src/Mod/Path/PathScripts/PathDressupPathBoundaryGui.py @@ -257,7 +257,6 @@ def Create(base, name="DressupPathBoundary"): class CommandPathDressupPathBoundary: - def GetResources(self): return { "Pixmap": "Path_Dressup", diff --git a/src/Mod/Path/PathScripts/PathDressupRampEntry.py b/src/Mod/Path/PathScripts/PathDressupRampEntry.py index 15234064f5..962b49ee10 100644 --- a/src/Mod/Path/PathScripts/PathDressupRampEntry.py +++ b/src/Mod/Path/PathScripts/PathDressupRampEntry.py @@ -894,7 +894,6 @@ class ViewProviderDressup: class CommandPathDressupRampEntry: - def GetResources(self): return { "Pixmap": "Path_Dressup", diff --git a/src/Mod/Path/PathScripts/PathDressupTag.py b/src/Mod/Path/PathScripts/PathDressupTag.py index e555800699..6a9487adea 100644 --- a/src/Mod/Path/PathScripts/PathDressupTag.py +++ b/src/Mod/Path/PathScripts/PathDressupTag.py @@ -251,9 +251,7 @@ class ObjectDressup: self.solids = [self.masterSolid.cloneAt(pos) for pos in self.obj.Positions] self.tagSolid = Part.Compound(self.solids) - self.wire, rapid = PathGeom.wireForPath( - obj.Base.Path - ) + self.wire, rapid = PathGeom.wireForPath(obj.Base.Path) self.edges = self.wire.Edges maxTagZ = minZ + obj.Height.Value diff --git a/src/Mod/Path/PathScripts/PathDressupTagGui.py b/src/Mod/Path/PathScripts/PathDressupTagGui.py index a100b827aa..0dba024a52 100644 --- a/src/Mod/Path/PathScripts/PathDressupTagGui.py +++ b/src/Mod/Path/PathScripts/PathDressupTagGui.py @@ -560,7 +560,6 @@ def Create(baseObject, name="DressupTag"): class CommandPathDressupTag: - def GetResources(self): return { "Pixmap": "Path_Dressup", diff --git a/src/Mod/Path/PathScripts/PathFeatureExtensions.py b/src/Mod/Path/PathScripts/PathFeatureExtensions.py index ff6c12816b..6036efc5f8 100644 --- a/src/Mod/Path/PathScripts/PathFeatureExtensions.py +++ b/src/Mod/Path/PathScripts/PathFeatureExtensions.py @@ -84,9 +84,7 @@ def selectOffsetWire(feature, wires): closest = None for w in wires: dist = feature.distToShape(w)[0] - if ( - closest is None or dist > closest[0] - ): + if closest is None or dist > closest[0]: closest = (dist, w) if closest is not None: diff --git a/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py b/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py index cb24685d80..bc8f88d865 100644 --- a/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py +++ b/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py @@ -208,9 +208,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.obj.ViewObject.RootNode.addChild(self.switch) self.switch.whichChild = coin.SO_SWITCH_ALL - self.model = QtGui.QStandardItemModel( - self.form.extensionTree - ) + self.model = QtGui.QStandardItemModel(self.form.extensionTree) self.model.setHorizontalHeaderLabels(["Base", "Extension"]) """ @@ -261,9 +259,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): def updateProxyExtensions(self, obj): PathLog.debug("updateProxyExtensions()") - self.extensions = ( - self.currentExtensions() - ) + self.extensions = self.currentExtensions() FeatureExtensions.setExtensions(obj, self.extensions) def getFields(self, obj): @@ -634,9 +630,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.model.itemChanged.connect(self.updateItemEnabled) - self.selectionModel = ( - self.form.extensionTree.selectionModel() - ) + self.selectionModel = self.form.extensionTree.selectionModel() self.selectionModel.selectionChanged.connect(self.selectionChanged) self.selectionChanged() diff --git a/src/Mod/Path/PathScripts/PathGetPoint.py b/src/Mod/Path/PathScripts/PathGetPoint.py index 9f0291ac06..54e908eae0 100644 --- a/src/Mod/Path/PathScripts/PathGetPoint.py +++ b/src/Mod/Path/PathScripts/PathGetPoint.py @@ -26,7 +26,8 @@ import PathScripts.PathLog as PathLog # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader -Draft = LazyLoader('Draft', globals(), 'Draft') + +Draft = LazyLoader("Draft", globals(), "Draft") from PySide import QtCore, QtGui from pivy import coin @@ -37,17 +38,19 @@ __url__ = "https://www.freecadweb.org" __doc__ = "Helper class to use FreeCADGUi.Snapper to let the user enter arbitrary points while the task panel is active." PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.track(PathLog.thisModule()) +# PathLog.track(PathLog.thisModule()) + class TaskPanel: - '''Use an instance of this class in another TaskPanel to invoke the snapper. + """Use an instance of this class in another TaskPanel to invoke the snapper. Create the instance in the TaskPanel's constructors and invoke getPoint(whenDone, start) whenever a new point is required or an existing point needs to be edited. The receiver is expected to have the same lifespan as the form provided in the constructor. The (only) public API function other than the constructor is getPoint(whenDone, start). - ''' + """ + def __init__(self, form, onPath=False): - '''__init___(form) ... form will be replaced by PointEdit.ui while the Snapper is active.''' + """__init___(form) ... form will be replaced by PointEdit.ui while the Snapper is active.""" self.formOrig = form self.formPoint = FreeCADGui.PySideUic.loadUi(":/panels/PointEdit.ui") @@ -69,7 +72,7 @@ class TaskPanel: self.view = None def setupUi(self): - '''setupUi() ... internal function - do not call.''' + """setupUi() ... internal function - do not call.""" self.formPoint.buttonBox.accepted.connect(self.pointAccept) self.formPoint.buttonBox.rejected.connect(self.pointReject) @@ -77,44 +80,52 @@ class TaskPanel: self.formPoint.globalY.editingFinished.connect(self.updatePoint) self.formPoint.globalZ.editingFinished.connect(self.updatePoint) - self.formPoint.globalX.setProperty('unit', FreeCAD.Units.MilliMetre.getUserPreferred()[2]) - self.formPoint.globalY.setProperty('unit', FreeCAD.Units.MilliMetre.getUserPreferred()[2]) - self.formPoint.globalZ.setProperty('unit', FreeCAD.Units.MilliMetre.getUserPreferred()[2]) + self.formPoint.globalX.setProperty( + "unit", FreeCAD.Units.MilliMetre.getUserPreferred()[2] + ) + self.formPoint.globalY.setProperty( + "unit", FreeCAD.Units.MilliMetre.getUserPreferred()[2] + ) + self.formPoint.globalZ.setProperty( + "unit", FreeCAD.Units.MilliMetre.getUserPreferred()[2] + ) def addEscapeShortcut(self): - '''addEscapeShortcut() ... internal function - do not call.''' + """addEscapeShortcut() ... internal function - do not call.""" # The only way I could get to intercept the escape key, or really any key was # by creating an action with a shortcut ..... self.escape = QtGui.QAction(self.formPoint) - self.escape.setText('Done') - self.escape.setShortcut(QtGui.QKeySequence.fromString('Esc')) - QtCore.QObject.connect(self.escape, QtCore.SIGNAL('triggered()'), self.pointDone) + self.escape.setText("Done") + self.escape.setShortcut(QtGui.QKeySequence.fromString("Esc")) + QtCore.QObject.connect( + self.escape, QtCore.SIGNAL("triggered()"), self.pointDone + ) self.formPoint.addAction(self.escape) def removeEscapeShortcut(self): - '''removeEscapeShortcut() ... internal function - do not call.''' + """removeEscapeShortcut() ... internal function - do not call.""" if self.escape: self.formPoint.removeAction(self.escape) self.escape = None def getPoint(self, whenDone, start=None): - '''getPoint(whenDone, start=None) ... invoke Snapper and call whenDone when a point is entered or the user cancels the operation. + """getPoint(whenDone, start=None) ... invoke Snapper and call whenDone when a point is entered or the user cancels the operation. whenDone(point, obj) is called either with a point and the object on which the point lies if the user set the point, or None and None if the user cancelled the operation. start is an optional Vector indicating from where to start Snapper. This is mostly used when editing existing points. Snapper also creates a dotted line indicating from where the original point started from. If start is specified the Snapper UI is closed on the first point the user enters. If start remains None, then Snapper is kept open - until the user explicitly closes Snapper. This lets the user enter multiple points in quick succession.''' + until the user explicitly closes Snapper. This lets the user enter multiple points in quick succession.""" # there's no get point without Snapper, if it's not loaded, need to do that explicitly - if not hasattr(FreeCADGui, 'Snapper'): + if not hasattr(FreeCADGui, "Snapper"): import DraftTools def displayPoint(p): self.point = p - self.formPoint.globalX.setProperty('rawValue', p.x) - self.formPoint.globalY.setProperty('rawValue', p.y) - self.formPoint.globalZ.setProperty('rawValue', p.z) + self.formPoint.globalX.setProperty("rawValue", p.x) + self.formPoint.globalY.setProperty("rawValue", p.y) + self.formPoint.globalZ.setProperty("rawValue", p.z) self.formPoint.globalX.setFocus() self.formPoint.globalX.selectAll() @@ -129,10 +140,10 @@ class TaskPanel: screenpos = tuple(pos.getValue()) snapInfo = Draft.get3DView().getObjectInfo(screenpos) if snapInfo: - obj = FreeCAD.ActiveDocument.getObject(snapInfo['Object']) - if hasattr(obj, 'Path'): + obj = FreeCAD.ActiveDocument.getObject(snapInfo["Object"]) + if hasattr(obj, "Path"): self.obj = obj - p = FreeCAD.Vector(snapInfo['x'], snapInfo['y'], snapInfo['z']) + p = FreeCAD.Vector(snapInfo["x"], snapInfo["y"], snapInfo["z"]) self.pt = p else: self.obj = None @@ -140,7 +151,9 @@ class TaskPanel: # Snapper handles regular objects just fine cntrl = event.wasCtrlDown() shift = event.wasShiftDown() - self.pt = FreeCADGui.Snapper.snap(pos, lastpoint=start, active=cntrl, constrain=shift) + self.pt = FreeCADGui.Snapper.snap( + pos, lastpoint=start, active=cntrl, constrain=shift + ) plane = FreeCAD.DraftWorkingPlane p = plane.getLocalCoords(self.pt) self.obj = FreeCADGui.Snapper.lastSnappedObject @@ -150,7 +163,10 @@ class TaskPanel: def click(cb): event = cb.getEvent() - if event.getButton() == 1 and event.getState() == coin.SoMouseButtonEvent.DOWN: + if ( + event.getButton() == 1 + and event.getState() == coin.SoMouseButtonEvent.DOWN + ): if self.obj: accept() @@ -173,16 +189,20 @@ class TaskPanel: displayPoint(FreeCAD.Vector(0, 0, 0)) self.view = Draft.get3DView() - self.pointCbClick = self.view.addEventCallbackPivy(coin.SoMouseButtonEvent.getClassTypeId(), click) - self.pointCbMove = self.view.addEventCallbackPivy(coin.SoLocation2Event.getClassTypeId(), mouseMove) + self.pointCbClick = self.view.addEventCallbackPivy( + coin.SoMouseButtonEvent.getClassTypeId(), click + ) + self.pointCbMove = self.view.addEventCallbackPivy( + coin.SoLocation2Event.getClassTypeId(), mouseMove + ) if self.buttonBox: self.buttonBox.setEnabled(False) - FreeCADGui.Snapper.forceGridOff=True + FreeCADGui.Snapper.forceGridOff = True - def pointFinish(self, ok, cleanup = True): - '''pointFinish(ok, cleanup=True) ... internal function - do not call.''' + def pointFinish(self, ok, cleanup=True): + """pointFinish(ok, cleanup=True) ... internal function - do not call.""" if cleanup: self.removeGlobalCallbacks() @@ -201,34 +221,38 @@ class TaskPanel: self.pointWhenDone(None, None) def pointDone(self): - '''pointDone() ... internal function - do not call.''' + """pointDone() ... internal function - do not call.""" self.pointFinish(False) def pointReject(self): - '''pointReject() ... internal function - do not call.''' + """pointReject() ... internal function - do not call.""" self.pointFinish(False) def pointAccept(self): - '''pointAccept() ... internal function - do not call.''' + """pointAccept() ... internal function - do not call.""" self.pointFinish(True) def pointAcceptAndContinue(self): - '''pointAcceptAndContinue() ... internal function - do not call.''' + """pointAcceptAndContinue() ... internal function - do not call.""" self.pointFinish(True, False) def removeGlobalCallbacks(self): - '''removeGlobalCallbacks() ... internal function - do not call.''' - if hasattr(self, 'view') and self.view: + """removeGlobalCallbacks() ... internal function - do not call.""" + if hasattr(self, "view") and self.view: if self.pointCbClick: - self.view.removeEventCallbackPivy(coin.SoMouseButtonEvent.getClassTypeId(), self.pointCbClick) + self.view.removeEventCallbackPivy( + coin.SoMouseButtonEvent.getClassTypeId(), self.pointCbClick + ) self.pointCbClick = None if self.pointCbMove: - self.view.removeEventCallbackPivy(coin.SoLocation2Event.getClassTypeId(), self.pointCbMove) + self.view.removeEventCallbackPivy( + coin.SoLocation2Event.getClassTypeId(), self.pointCbMove + ) self.pointCbMove = None self.view = None - def updatePoint(self, usePoint = True): - '''updatePoint() ... internal function - do not call.''' + def updatePoint(self, usePoint=True): + """updatePoint() ... internal function - do not call.""" if usePoint and self.point: self.pt = self.point else: @@ -236,4 +260,3 @@ class TaskPanel: y = FreeCAD.Units.Quantity(self.formPoint.globalY.text()).Value z = FreeCAD.Units.Quantity(self.formPoint.globalZ.text()).Value self.pt = FreeCAD.Vector(x, y, z) - diff --git a/src/Mod/Path/PathScripts/PathGuiInit.py b/src/Mod/Path/PathScripts/PathGuiInit.py index 295c0c8d01..593648e229 100644 --- a/src/Mod/Path/PathScripts/PathGuiInit.py +++ b/src/Mod/Path/PathScripts/PathGuiInit.py @@ -32,10 +32,11 @@ else: Processed = False + def Startup(): global Processed if not Processed: - PathLog.debug('Initializing PathGui') + PathLog.debug("Initializing PathGui") from PathScripts import PathAdaptiveGui from PathScripts import PathArray from PathScripts import PathComment @@ -75,6 +76,7 @@ def Startup(): from PathScripts import PathToolLibraryManager from PathScripts import PathUtilsGui from PathScripts import PathVcarveGui + Processed = True else: - PathLog.debug('Skipping PathGui initialisation') + PathLog.debug("Skipping PathGui initialisation") diff --git a/src/Mod/Path/PathScripts/PathJob.py b/src/Mod/Path/PathScripts/PathJob.py index d536ad497a..de7992b8d0 100644 --- a/src/Mod/Path/PathScripts/PathJob.py +++ b/src/Mod/Path/PathScripts/PathJob.py @@ -51,6 +51,7 @@ translate = FreeCAD.Qt.translate class JobTemplate: """Attribute and sub element strings for template export/import.""" + Description = "Desc" GeometryTolerance = "Tolerance" Job = "Job" diff --git a/src/Mod/Path/PathScripts/PathJobCmd.py b/src/Mod/Path/PathScripts/PathJobCmd.py index 9f20533619..830eb23aef 100644 --- a/src/Mod/Path/PathScripts/PathJobCmd.py +++ b/src/Mod/Path/PathScripts/PathJobCmd.py @@ -58,9 +58,7 @@ class CommandJobCreate: "Pixmap": "Path_Job", "MenuText": QT_TRANSLATE_NOOP("Path_Job", "Job"), "Accel": "P, J", - "ToolTip": QT_TRANSLATE_NOOP( - "Path_Job", "Creates a Path Job object" - ), + "ToolTip": QT_TRANSLATE_NOOP("Path_Job", "Creates a Path Job object"), } def IsActive(self): @@ -104,7 +102,8 @@ class CommandJobTemplateExport: "Pixmap": "Path_ExportTemplate", "MenuText": QT_TRANSLATE_NOOP("Path_ExportTemplate", "Export Template"), "ToolTip": QT_TRANSLATE_NOOP( - "Path_ExportTemplate", "Exports Path Job as a template to be used for other jobs" + "Path_ExportTemplate", + "Exports Path Job as a template to be used for other jobs", ), } diff --git a/src/Mod/Path/PathScripts/PathJobDlg.py b/src/Mod/Path/PathScripts/PathJobDlg.py index 0a92620279..d0acdfda70 100644 --- a/src/Mod/Path/PathScripts/PathJobDlg.py +++ b/src/Mod/Path/PathScripts/PathJobDlg.py @@ -260,9 +260,7 @@ class JobCreate: models = [] for i in range(self.itemsSolid.rowCount()): - for j in range( - self.itemsSolid.child(i, 1).data(QtCore.Qt.EditRole) - ): + for j in range(self.itemsSolid.child(i, 1).data(QtCore.Qt.EditRole)): models.append(self.itemsSolid.child(i).data(self.DataObject)) for i in range(self.items2D.rowCount()): diff --git a/src/Mod/Path/PathScripts/PathLog.py b/src/Mod/Path/PathScripts/PathLog.py index 6caf9816f7..0f2b436c1c 100644 --- a/src/Mod/Path/PathScripts/PathLog.py +++ b/src/Mod/Path/PathScripts/PathLog.py @@ -24,36 +24,47 @@ import FreeCAD import os import traceback + class Level: """Enumeration of log levels, used for setLevel and getLevel.""" - RESET = -1 - ERROR = 0 - WARNING = 1 - NOTICE = 2 - INFO = 3 - DEBUG = 4 - _names = { ERROR: 'ERROR', WARNING: 'WARNING', NOTICE: 'NOTICE', INFO: 'INFO', DEBUG: 'DEBUG' } + RESET = -1 + ERROR = 0 + WARNING = 1 + NOTICE = 2 + INFO = 3 + DEBUG = 4 + + _names = { + ERROR: "ERROR", + WARNING: "WARNING", + NOTICE: "NOTICE", + INFO: "INFO", + DEBUG: "DEBUG", + } @classmethod def toString(cls, level): - return cls._names.get(level, 'UNKNOWN') + return cls._names.get(level, "UNKNOWN") + _defaultLogLevel = Level.NOTICE -_moduleLogLevel = { } +_moduleLogLevel = {} _useConsole = True -_trackModule = { } +_trackModule = {} _trackAll = False + def logToConsole(yes): """(boolean) - if set to True (default behaviour) log messages are printed to the console. Otherwise they are printed to stdout.""" global _useConsole _useConsole = yes -def setLevel(level, module = None): + +def setLevel(level, module=None): """(level, module = None) - if no module is specified the default log level is set. - Otherwise the module specific log level is changed (use RESET to clear).""" + if no module is specified the default log level is set. + Otherwise the module specific log level is changed (use RESET to clear).""" global _defaultLogLevel global _moduleLogLevel if module: @@ -65,25 +76,29 @@ def setLevel(level, module = None): else: if level == Level.RESET: _defaultLogLevel = Level.NOTICE - _moduleLogLevel = { } + _moduleLogLevel = {} else: _defaultLogLevel = level -def getLevel(module = None): + +def getLevel(module=None): """(module = None) - return the global (None) or module specific log level.""" if module: return _moduleLogLevel.get(module, _defaultLogLevel) return _defaultLogLevel + def thisModule(): """returns the module id of the caller, can be used for setLevel, getLevel and trackModule.""" return _caller()[0] + def _caller(): """internal function to determine the calling module.""" filename, line, func, text = traceback.extract_stack(limit=3)[0] return os.path.splitext(os.path.basename(filename))[0], line, func + def _log(level, module_line_func, msg): """internal function to do the logging""" module, line, func = module_line_func @@ -104,37 +119,49 @@ def _log(level, module_line_func, msg): return message return None + def debug(msg): """(message)""" module, line, func = _caller() msg = "({}) - {}".format(line, msg) return _log(Level.DEBUG, _caller(), msg) + + def info(msg): """(message)""" return _log(Level.INFO, _caller(), msg) + + def notice(msg): """(message)""" return _log(Level.NOTICE, _caller(), msg) + + def warning(msg): """(message)""" return _log(Level.WARNING, _caller(), msg) + + def error(msg): """(message)""" return _log(Level.ERROR, _caller(), msg) + def trackAllModules(boolean): """(boolean) - if True all modules will be tracked, otherwise tracking is up to the module setting.""" global _trackAll _trackAll = boolean + def untrackAllModules(): """In addition to stop tracking all modules it also clears the tracking flag for all individual modules.""" global _trackAll global _trackModule _trackAll = False - _trackModule = { } + _trackModule = {} -def trackModule(module = None): + +def trackModule(module=None): """(module = None) - start tracking given module, current module if not set.""" global _trackModule if module: @@ -143,7 +170,8 @@ def trackModule(module = None): mod, line, func = _caller() _trackModule[mod] = True -def untrackModule(module = None): + +def untrackModule(module=None): """(module = None) - stop tracking given module, current module if not set.""" global _trackModule if module and _trackModule.get(module, None): @@ -153,15 +181,20 @@ def untrackModule(module = None): if _trackModule.get(mod, None): del _trackModule[mod] + def track(*args): """(....) - call with arguments of current function you want logged if tracking is enabled.""" module, line, func = _caller() if _trackAll or _trackModule.get(module, None): - message = "%s(%d).%s(%s)" % (module, line, func, ', '.join([str(arg) for arg in args])) + message = "%s(%d).%s(%s)" % ( + module, + line, + func, + ", ".join([str(arg) for arg in args]), + ) if _useConsole: FreeCAD.Console.PrintMessage(message + "\n") else: print(message) return message return None - diff --git a/src/Mod/Path/PathScripts/PathOp.py b/src/Mod/Path/PathScripts/PathOp.py index e9b18ade68..b39cc3dbe5 100644 --- a/src/Mod/Path/PathScripts/PathOp.py +++ b/src/Mod/Path/PathScripts/PathOp.py @@ -68,13 +68,15 @@ FeatureDiameters = 0x4000 # Turning Diameters FeatureBaseGeometry = FeatureBaseVertexes | FeatureBaseFaces | FeatureBaseEdges + class PathNoTCException(Exception): - '''PathNoTCException is raised when no TC was selected or matches the input + """PathNoTCException is raised when no TC was selected or matches the input criteria. This can happen intentionally by the user when they cancel the TC - selection dialog.''' + selection dialog.""" def __init__(self): - super().__init__('No Tool Controller found') + super().__init__("No Tool Controller found") + class ObjectOp(object): """ diff --git a/src/Mod/Path/PathScripts/PathOpGui.py b/src/Mod/Path/PathScripts/PathOpGui.py index ba5e222316..d8a5361ddf 100644 --- a/src/Mod/Path/PathScripts/PathOpGui.py +++ b/src/Mod/Path/PathScripts/PathOpGui.py @@ -1378,12 +1378,14 @@ def Create(res): obj.ViewObject.Document.setEdit(obj.ViewObject, 0) return obj except PathUtils.PathNoTCExistsException: - msg = translate('PathOp', 'No suitable tool controller found.\nAborting op creation') + msg = translate( + "PathOp", "No suitable tool controller found.\nAborting op creation" + ) diag = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Error", msg) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() except PathOp.PathNoTCException: - PathLog.warning(translate('PathOp', 'No tool controller, aborting op creation')) + PathLog.warning(translate("PathOp", "No tool controller, aborting op creation")) FreeCAD.ActiveDocument.abortTransaction() FreeCAD.ActiveDocument.recompute() diff --git a/src/Mod/Path/PathScripts/PathPostProcessor.py b/src/Mod/Path/PathScripts/PathPostProcessor.py index 80820581bb..c692032ba1 100644 --- a/src/Mod/Path/PathScripts/PathPostProcessor.py +++ b/src/Mod/Path/PathScripts/PathPostProcessor.py @@ -26,8 +26,8 @@ import sys PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -class PostProcessor: +class PostProcessor: @classmethod def exists(cls, processor): return processor in PathPreferences.allAvailablePostProcessors() @@ -43,9 +43,9 @@ class PostProcessor: postname = processor + "_post" namespace = {} - #can't modify function local scope with exec in python3 + # can't modify function local scope with exec in python3 exec("import %s as current_post" % postname, namespace) - current_post = namespace['current_post'] + current_post = namespace["current_post"] # make sure the script is reloaded if it was previously loaded # should the script have been imported for the first time above @@ -53,11 +53,12 @@ class PostProcessor: # resulting in 2 load messages if the script outputs one of those. try: # Python 2.7 - exec("reload(%s)" % 'current_post') + exec("reload(%s)" % "current_post") except NameError: # Python 3.4+ from importlib import reload - exec("reload(%s)" % 'current_post') + + exec("reload(%s)" % "current_post") sys.path = syspath @@ -72,14 +73,18 @@ class PostProcessor: instance.machineName = current_post.MACHINE_NAME if hasattr(current_post, "CORNER_MAX"): - instance.cornerMax = {'x': current_post.CORNER_MAX['x'], - 'y': current_post.CORNER_MAX['y'], - 'z': current_post.CORNER_MAX['z']} + instance.cornerMax = { + "x": current_post.CORNER_MAX["x"], + "y": current_post.CORNER_MAX["y"], + "z": current_post.CORNER_MAX["z"], + } if hasattr(current_post, "CORNER_MIN"): - instance.cornerMin = {'x': current_post.CORNER_MIN['x'], - 'y': current_post.CORNER_MIN['y'], - 'z': current_post.CORNER_MIN['z']} + instance.cornerMin = { + "x": current_post.CORNER_MIN["x"], + "y": current_post.CORNER_MIN["y"], + "z": current_post.CORNER_MIN["z"], + } if hasattr(current_post, "TOOLTIP"): instance.tooltip = current_post.TOOLTIP diff --git a/src/Mod/Path/PathScripts/PathPreferencesPathJob.py b/src/Mod/Path/PathScripts/PathPreferencesPathJob.py index 15eb42380d..3164438998 100644 --- a/src/Mod/Path/PathScripts/PathPreferencesPathJob.py +++ b/src/Mod/Path/PathScripts/PathPreferencesPathJob.py @@ -38,22 +38,27 @@ PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) class JobPreferencesPage: def __init__(self, parent=None): import FreeCADGui + self.form = FreeCADGui.PySideUic.loadUi(":preferences/PathJob.ui") - self.form.toolBox.setCurrentIndex(0) # Take that qt designer! + self.form.toolBox.setCurrentIndex(0) # Take that qt designer! self.postProcessorDefaultTooltip = self.form.defaultPostProcessor.toolTip() - self.postProcessorArgsDefaultTooltip = self.form.defaultPostProcessorArgs.toolTip() - self.processor = { } + self.postProcessorArgsDefaultTooltip = ( + self.form.defaultPostProcessorArgs.toolTip() + ) + self.processor = {} def saveSettings(self): filePath = self.form.leDefaultFilePath.text() jobTemplate = self.form.leDefaultJobTemplate.text() geometryTolerance = Units.Quantity(self.form.geometryTolerance.text()) curveAccuracy = Units.Quantity(self.form.curveAccuracy.text()) - PathPreferences.setJobDefaults(filePath, jobTemplate, geometryTolerance, curveAccuracy) + PathPreferences.setJobDefaults( + filePath, jobTemplate, geometryTolerance, curveAccuracy + ) if curveAccuracy: - Path.Area.setDefaultParams(Accuracy = curveAccuracy) + Path.Area.setDefaultParams(Accuracy=curveAccuracy) processor = str(self.form.defaultPostProcessor.currentText()) args = str(self.form.defaultPostProcessorArgs.text()) @@ -73,41 +78,79 @@ class JobPreferencesPage: def saveStockSettings(self): if self.form.stockGroup.isChecked(): attrs = {} - attrs['version'] = 1 - typ = [PathStock.StockType.CreateBox, PathStock.StockType.CreateCylinder, PathStock.StockType.FromBase][self.form.stock.currentIndex()] - attrs['create'] = typ + attrs["version"] = 1 + typ = [ + PathStock.StockType.CreateBox, + PathStock.StockType.CreateCylinder, + PathStock.StockType.FromBase, + ][self.form.stock.currentIndex()] + attrs["create"] = typ if typ == PathStock.StockType.CreateBox: - attrs['length'] = FreeCAD.Units.Quantity(self.form.stockBoxLength.text()).UserString - attrs['width'] = FreeCAD.Units.Quantity(self.form.stockBoxWidth.text()).UserString - attrs['height'] = FreeCAD.Units.Quantity(self.form.stockBoxHeight.text()).UserString + attrs["length"] = FreeCAD.Units.Quantity( + self.form.stockBoxLength.text() + ).UserString + attrs["width"] = FreeCAD.Units.Quantity( + self.form.stockBoxWidth.text() + ).UserString + attrs["height"] = FreeCAD.Units.Quantity( + self.form.stockBoxHeight.text() + ).UserString if typ == PathStock.StockType.CreateCylinder: - attrs['radius'] = FreeCAD.Units.Quantity(self.form.stockCylinderRadius.text()).UserString - attrs['height'] = FreeCAD.Units.Quantity(self.form.stockCylinderHeight.text()).UserString + attrs["radius"] = FreeCAD.Units.Quantity( + self.form.stockCylinderRadius.text() + ).UserString + attrs["height"] = FreeCAD.Units.Quantity( + self.form.stockCylinderHeight.text() + ).UserString if typ == PathStock.StockType.FromBase: - attrs['xneg'] = FreeCAD.Units.Quantity(self.form.stockExtXneg.text()).UserString - attrs['xpos'] = FreeCAD.Units.Quantity(self.form.stockExtXpos.text()).UserString - attrs['yneg'] = FreeCAD.Units.Quantity(self.form.stockExtYneg.text()).UserString - attrs['ypos'] = FreeCAD.Units.Quantity(self.form.stockExtYpos.text()).UserString - attrs['zneg'] = FreeCAD.Units.Quantity(self.form.stockExtZneg.text()).UserString - attrs['zpos'] = FreeCAD.Units.Quantity(self.form.stockExtZpos.text()).UserString + attrs["xneg"] = FreeCAD.Units.Quantity( + self.form.stockExtXneg.text() + ).UserString + attrs["xpos"] = FreeCAD.Units.Quantity( + self.form.stockExtXpos.text() + ).UserString + attrs["yneg"] = FreeCAD.Units.Quantity( + self.form.stockExtYneg.text() + ).UserString + attrs["ypos"] = FreeCAD.Units.Quantity( + self.form.stockExtYpos.text() + ).UserString + attrs["zneg"] = FreeCAD.Units.Quantity( + self.form.stockExtZneg.text() + ).UserString + attrs["zpos"] = FreeCAD.Units.Quantity( + self.form.stockExtZpos.text() + ).UserString if self.form.stockPlacementGroup.isChecked(): angle = FreeCAD.Units.Quantity(self.form.stockAngle.text()).Value - axis = FreeCAD.Vector(self.form.stockAxisX.value(), self.form.stockAxisY.value(), self.form.stockAxisZ.value()) + axis = FreeCAD.Vector( + self.form.stockAxisX.value(), + self.form.stockAxisY.value(), + self.form.stockAxisZ.value(), + ) rot = FreeCAD.Rotation(axis, angle) - attrs['rotX'] = rot.Q[0] - attrs['rotY'] = rot.Q[1] - attrs['rotZ'] = rot.Q[2] - attrs['rotW'] = rot.Q[3] - attrs['posX'] = FreeCAD.Units.Quantity(self.form.stockPositionX.text()).Value - attrs['posY'] = FreeCAD.Units.Quantity(self.form.stockPositionY.text()).Value - attrs['posZ'] = FreeCAD.Units.Quantity(self.form.stockPositionZ.text()).Value + attrs["rotX"] = rot.Q[0] + attrs["rotY"] = rot.Q[1] + attrs["rotZ"] = rot.Q[2] + attrs["rotW"] = rot.Q[3] + attrs["posX"] = FreeCAD.Units.Quantity( + self.form.stockPositionX.text() + ).Value + attrs["posY"] = FreeCAD.Units.Quantity( + self.form.stockPositionY.text() + ).Value + attrs["posZ"] = FreeCAD.Units.Quantity( + self.form.stockPositionZ.text() + ).Value PathPreferences.setDefaultStockTemplate(json.dumps(attrs)) else: - PathPreferences.setDefaultStockTemplate('') + PathPreferences.setDefaultStockTemplate("") def saveToolsSettings(self): - PathPreferences.setToolsSettings(self.form.toolsUseLegacy.isChecked(), - self.form.toolsAbsolutePaths.isChecked()) + PathPreferences.setToolsSettings( + self.form.toolsUseLegacy.isChecked(), + self.form.toolsAbsolutePaths.isChecked(), + ) def selectComboEntry(self, widget, text): index = widget.findText(text, QtCore.Qt.MatchFixedString) @@ -125,17 +168,19 @@ class JobPreferencesPage: item = self.form.postProcessorList.item(i) if item.checkState() == QtCore.Qt.CheckState.Checked: self.form.defaultPostProcessor.addItem(item.text()) - if item.text() == processor : + if item.text() == processor: defaultIsValid = True # if we get here the default processor was disabled if not defaultIsValid: - self.form.defaultPostProcessorArgs.setText('') - processor = '' + self.form.defaultPostProcessorArgs.setText("") + processor = "" self.selectComboEntry(self.form.defaultPostProcessor, processor) self.form.defaultPostProcessor.blockSignals(False) def verifyAndUpdateDefaultPostProcessor(self): - self.verifyAndUpdateDefaultPostProcessorWith(str(self.form.defaultPostProcessor.currentText())) + self.verifyAndUpdateDefaultPostProcessorWith( + str(self.form.defaultPostProcessor.currentText()) + ) def loadSettings(self): self.form.leDefaultFilePath.setText(PathPreferences.defaultFilePath()) @@ -148,24 +193,44 @@ class JobPreferencesPage: item.setCheckState(QtCore.Qt.CheckState.Unchecked) else: item.setCheckState(QtCore.Qt.CheckState.Checked) - item.setFlags( QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled | QtCore.Qt.ItemFlag.ItemIsUserCheckable) + item.setFlags( + QtCore.Qt.ItemFlag.ItemIsSelectable + | QtCore.Qt.ItemFlag.ItemIsEnabled + | QtCore.Qt.ItemFlag.ItemIsUserCheckable + ) self.form.postProcessorList.addItem(item) - self.verifyAndUpdateDefaultPostProcessorWith(PathPreferences.defaultPostProcessor()) + self.verifyAndUpdateDefaultPostProcessorWith( + PathPreferences.defaultPostProcessor() + ) - self.form.defaultPostProcessorArgs.setText(PathPreferences.defaultPostProcessorArgs()) + self.form.defaultPostProcessorArgs.setText( + PathPreferences.defaultPostProcessorArgs() + ) - geomTol = Units.Quantity(PathPreferences.defaultGeometryTolerance(), Units.Length) + geomTol = Units.Quantity( + PathPreferences.defaultGeometryTolerance(), Units.Length + ) self.form.geometryTolerance.setText(geomTol.UserString) - self.form.curveAccuracy.setText(Units.Quantity(PathPreferences.defaultLibAreaCurveAccuracy(), Units.Length).UserString) + self.form.curveAccuracy.setText( + Units.Quantity( + PathPreferences.defaultLibAreaCurveAccuracy(), Units.Length + ).UserString + ) self.form.leOutputFile.setText(PathPreferences.defaultOutputFile()) - self.selectComboEntry(self.form.cboOutputPolicy, PathPreferences.defaultOutputPolicy()) + self.selectComboEntry( + self.form.cboOutputPolicy, PathPreferences.defaultOutputPolicy() + ) self.form.tbDefaultFilePath.clicked.connect(self.browseDefaultFilePath) self.form.tbDefaultJobTemplate.clicked.connect(self.browseDefaultJobTemplate) self.form.postProcessorList.itemEntered.connect(self.setProcessorListTooltip) - self.form.postProcessorList.itemChanged.connect(self.verifyAndUpdateDefaultPostProcessor) - self.form.defaultPostProcessor.currentIndexChanged.connect(self.updateDefaultPostProcessorToolTip) + self.form.postProcessorList.itemChanged.connect( + self.verifyAndUpdateDefaultPostProcessor + ) + self.form.defaultPostProcessor.currentIndexChanged.connect( + self.updateDefaultPostProcessorToolTip + ) self.form.tbOutputFile.clicked.connect(self.browseOutputFile) self.loadStockSettings() @@ -176,8 +241,8 @@ class JobPreferencesPage: index = -1 if stock: attrs = json.loads(stock) - if attrs.get('version') and 1 == int(attrs['version']): - stockType = attrs.get('create') + if attrs.get("version") and 1 == int(attrs["version"]): + stockType = attrs.get("create") if stockType == PathStock.StockType.FromBase: index = 2 elif stockType == PathStock.StockType.CreateBox: @@ -194,26 +259,34 @@ class JobPreferencesPage: self.form.stock.setCurrentIndex(index) # this either sets the default value or the value from the template for each field - self.form.stockExtXneg.setText(attrs.get('xneg', '1 mm')) - self.form.stockExtXpos.setText(attrs.get('xpos', '1 mm')) - self.form.stockExtYneg.setText(attrs.get('yneg', '1 mm')) - self.form.stockExtYpos.setText(attrs.get('ypos', '1 mm')) - self.form.stockExtZneg.setText(attrs.get('zneg', '1 mm')) - self.form.stockExtZpos.setText(attrs.get('zpos', '1 mm')) - self.form.stockBoxLength.setText(attrs.get('length', '10 mm')) - self.form.stockBoxWidth.setText(attrs.get('width', '10 mm')) - self.form.stockBoxHeight.setText(attrs.get('height', '10 mm')) - self.form.stockCylinderRadius.setText(attrs.get('radius', '5 mm')) - self.form.stockCylinderHeight.setText(attrs.get('height', '10 mm')) + self.form.stockExtXneg.setText(attrs.get("xneg", "1 mm")) + self.form.stockExtXpos.setText(attrs.get("xpos", "1 mm")) + self.form.stockExtYneg.setText(attrs.get("yneg", "1 mm")) + self.form.stockExtYpos.setText(attrs.get("ypos", "1 mm")) + self.form.stockExtZneg.setText(attrs.get("zneg", "1 mm")) + self.form.stockExtZpos.setText(attrs.get("zpos", "1 mm")) + self.form.stockBoxLength.setText(attrs.get("length", "10 mm")) + self.form.stockBoxWidth.setText(attrs.get("width", "10 mm")) + self.form.stockBoxHeight.setText(attrs.get("height", "10 mm")) + self.form.stockCylinderRadius.setText(attrs.get("radius", "5 mm")) + self.form.stockCylinderHeight.setText(attrs.get("height", "10 mm")) - posX = attrs.get('posX') - posY = attrs.get('posY') - posZ = attrs.get('posZ') - rotX = attrs.get('rotX') - rotY = attrs.get('rotY') - rotZ = attrs.get('rotZ') - rotW = attrs.get('rotW') - if posX is not None and posY is not None and posZ is not None and rotX is not None and rotY is not None and rotZ is not None and rotW is not None: + posX = attrs.get("posX") + posY = attrs.get("posY") + posZ = attrs.get("posZ") + rotX = attrs.get("rotX") + rotY = attrs.get("rotY") + rotZ = attrs.get("rotZ") + rotW = attrs.get("rotW") + if ( + posX is not None + and posY is not None + and posZ is not None + and rotX is not None + and rotY is not None + and rotZ is not None + and rotW is not None + ): pos = FreeCAD.Vector(float(posX), float(posY), float(posZ)) rot = FreeCAD.Rotation(float(rotX), float(rotY), float(rotZ), float(rotW)) placement = FreeCAD.Placement(pos, rot) @@ -222,13 +295,21 @@ class JobPreferencesPage: placement = FreeCAD.Placement() self.form.stockPlacementGroup.setChecked(False) - self.form.stockAngle.setText(FreeCAD.Units.Quantity("%f rad" % placement.Rotation.Angle).UserString) + self.form.stockAngle.setText( + FreeCAD.Units.Quantity("%f rad" % placement.Rotation.Angle).UserString + ) self.form.stockAxisX.setValue(placement.Rotation.Axis.x) self.form.stockAxisY.setValue(placement.Rotation.Axis.y) self.form.stockAxisZ.setValue(placement.Rotation.Axis.z) - self.form.stockPositionX.setText(FreeCAD.Units.Quantity(placement.Base.x, FreeCAD.Units.Length).UserString) - self.form.stockPositionY.setText(FreeCAD.Units.Quantity(placement.Base.y, FreeCAD.Units.Length).UserString) - self.form.stockPositionZ.setText(FreeCAD.Units.Quantity(placement.Base.z, FreeCAD.Units.Length).UserString) + self.form.stockPositionX.setText( + FreeCAD.Units.Quantity(placement.Base.x, FreeCAD.Units.Length).UserString + ) + self.form.stockPositionY.setText( + FreeCAD.Units.Quantity(placement.Base.y, FreeCAD.Units.Length).UserString + ) + self.form.stockPositionZ.setText( + FreeCAD.Units.Quantity(placement.Base.z, FreeCAD.Units.Length).UserString + ) self.setupStock(index) self.form.stock.currentIndexChanged.connect(self.setupStock) @@ -249,7 +330,9 @@ class JobPreferencesPage: def loadToolSettings(self): self.form.toolsUseLegacy.setChecked(PathPreferences.toolsUseLegacyTools()) - self.form.toolsAbsolutePaths.setChecked(PathPreferences.toolsStoreAbsolutePaths()) + self.form.toolsAbsolutePaths.setChecked( + PathPreferences.toolsStoreAbsolutePaths() + ) def getPostProcessor(self, name): if not name in self.processor.keys(): @@ -266,20 +349,26 @@ class JobPreferencesPage: widget.setToolTip(default) def setProcessorListTooltip(self, item): - self.setPostProcessorTooltip(self.form.postProcessorList, item.text(), '') + self.setPostProcessorTooltip(self.form.postProcessorList, item.text(), "") def updateDefaultPostProcessorToolTip(self): name = str(self.form.defaultPostProcessor.currentText()) if name: - self.setPostProcessorTooltip(self.form.defaultPostProcessor, name, self.postProcessorDefaultTooltip) + self.setPostProcessorTooltip( + self.form.defaultPostProcessor, name, self.postProcessorDefaultTooltip + ) processor = self.getPostProcessor(name) if processor.tooltipArgs: self.form.defaultPostProcessorArgs.setToolTip(processor.tooltipArgs) else: - self.form.defaultPostProcessorArgs.setToolTip(self.postProcessorArgsDefaultTooltip) + self.form.defaultPostProcessorArgs.setToolTip( + self.postProcessorArgsDefaultTooltip + ) else: self.form.defaultPostProcessor.setToolTip(self.postProcessorDefaultTooltip) - self.form.defaultPostProcessorArgs.setToolTip(self.postProcessorArgsDefaultTooltip) + self.form.defaultPostProcessorArgs.setToolTip( + self.postProcessorArgsDefaultTooltip + ) def bestGuessForFilePath(self): path = self.form.leDefaultFilePath.text() @@ -291,25 +380,24 @@ class JobPreferencesPage: path = self.form.leDefaultJobTemplate.text() if not path: path = self.bestGuessForFilePath() - foo = QtGui.QFileDialog.getOpenFileName(QtGui.QApplication.activeWindow(), - "Path - Job Template", - path, - "job_*.json")[0] + foo = QtGui.QFileDialog.getOpenFileName( + QtGui.QApplication.activeWindow(), "Path - Job Template", path, "job_*.json" + )[0] if foo: self.form.leDefaultJobTemplate.setText(foo) - def browseDefaultFilePath(self): path = self.bestGuessForFilePath() - foo = QtGui.QFileDialog.getExistingDirectory(QtGui.QApplication.activeWindow(), "Path - External File Directory", path) + foo = QtGui.QFileDialog.getExistingDirectory( + QtGui.QApplication.activeWindow(), "Path - External File Directory", path + ) if foo: self.form.leDefaultFilePath.setText(foo) def browseOutputFile(self): path = self.form.leOutputFile.text() - foo = QtGui.QFileDialog.getExistingDirectory(QtGui.QApplication.activeWindow(), "Path - Output File/Directory", path) + foo = QtGui.QFileDialog.getExistingDirectory( + QtGui.QApplication.activeWindow(), "Path - Output File/Directory", path + ) if foo: self.form.leOutputFile.setText(foo) - - - diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py index 7e79c2a0f8..03ff482ba5 100644 --- a/src/Mod/Path/PathScripts/PathProfile.py +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -332,8 +332,8 @@ class ObjectProfile(PathAreaOp.ObjectOp): params["MiterLimit"] = obj.MiterLimit if obj.SplitArcs: - params['Explode'] = True - params['FitArcs'] = False + params["Explode"] = True + params["FitArcs"] = False return params @@ -376,7 +376,8 @@ class ObjectProfile(PathAreaOp.ObjectOp): shapes = [] remainingObjBaseFeatures = [] self.isDebug = True if PathLog.getLevel(PathLog.thisModule()) == 4 else False - self.inaccessibleMsg = translate("PathProfile", + self.inaccessibleMsg = translate( + "PathProfile", "The selected edge(s) are inaccessible. If multiple, re-ordering selection might work.", ) self.offsetExtra = obj.OffsetExtra.Value @@ -480,7 +481,9 @@ class ObjectProfile(PathAreaOp.ObjectOp): ) except Exception as ee: # PathUtils.getEnvelope() failed to return an object. - msg = translate("PathProfile", "Unable to create path for face(s).") + msg = translate( + "PathProfile", "Unable to create path for face(s)." + ) PathLog.error(msg + "\n{}".format(ee)) cont = False @@ -623,7 +626,10 @@ class ObjectProfile(PathAreaOp.ObjectOp): shapes.append(tup) else: if zDiff < self.JOB.GeometryTolerance.Value: - msg = translate("PathProfile", "Check edge selection and Final Depth requirements for profiling open edge(s).") + msg = translate( + "PathProfile", + "Check edge selection and Final Depth requirements for profiling open edge(s).", + ) PathLog.error(msg) else: PathLog.error(self.inaccessibleMsg) diff --git a/src/Mod/Path/PathScripts/PathProfileContour.py b/src/Mod/Path/PathScripts/PathProfileContour.py index 47bce461ee..3d2ee44d6d 100644 --- a/src/Mod/Path/PathScripts/PathProfileContour.py +++ b/src/Mod/Path/PathScripts/PathProfileContour.py @@ -32,9 +32,12 @@ __doc__ = "Implementation of the Contour operation (depreciated)." class ObjectContour(PathProfile.ObjectProfile): - '''Pseudo class for Profile operation, - allowing for backward compatibility with pre-existing "Contour" operations.''' + """Pseudo class for Profile operation, + allowing for backward compatibility with pre-existing "Contour" operations.""" + pass + + # Eclass @@ -43,7 +46,7 @@ def SetupProperties(): def Create(name, obj=None, parentJob=None): - '''Create(name) ... Creates and returns a Profile operation.''' + """Create(name) ... Creates and returns a Profile operation.""" if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) obj.Proxy = ObjectContour(obj, name, parentJob) diff --git a/src/Mod/Path/PathScripts/PathProfileEdges.py b/src/Mod/Path/PathScripts/PathProfileEdges.py index 000fa9055a..12f487942f 100644 --- a/src/Mod/Path/PathScripts/PathProfileEdges.py +++ b/src/Mod/Path/PathScripts/PathProfileEdges.py @@ -33,9 +33,12 @@ __contributors__ = "russ4262 (Russell Johnson)" class ObjectProfile(PathProfile.ObjectProfile): - '''Pseudo class for Profile operation, - allowing for backward compatibility with pre-existing "Profile Edges" operations.''' + """Pseudo class for Profile operation, + allowing for backward compatibility with pre-existing "Profile Edges" operations.""" + pass + + # Eclass @@ -44,7 +47,7 @@ def SetupProperties(): def Create(name, obj=None, parentJob=None): - '''Create(name) ... Creates and returns a Profile operation.''' + """Create(name) ... Creates and returns a Profile operation.""" if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) obj.Proxy = ObjectProfile(obj, name, parentJob) diff --git a/src/Mod/Path/PathScripts/PathProfileFaces.py b/src/Mod/Path/PathScripts/PathProfileFaces.py index f92fba81d5..3de022ff57 100644 --- a/src/Mod/Path/PathScripts/PathProfileFaces.py +++ b/src/Mod/Path/PathScripts/PathProfileFaces.py @@ -34,9 +34,12 @@ __contributors__ = "Schildkroet" class ObjectProfile(PathProfile.ObjectProfile): - '''Pseudo class for Profile operation, - allowing for backward compatibility with pre-existing "Profile Faces" operations.''' + """Pseudo class for Profile operation, + allowing for backward compatibility with pre-existing "Profile Faces" operations.""" + pass + + # Eclass @@ -45,7 +48,7 @@ def SetupProperties(): def Create(name, obj=None, parentJob=None): - '''Create(name) ... Creates and returns a Profile operation.''' + """Create(name) ... Creates and returns a Profile operation.""" if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) obj.Proxy = ObjectProfile(obj, name, parentJob) diff --git a/src/Mod/Path/PathScripts/PathProperty.py b/src/Mod/Path/PathScripts/PathProperty.py index e6c8fe3877..eea7c61745 100644 --- a/src/Mod/Path/PathScripts/PathProperty.py +++ b/src/Mod/Path/PathScripts/PathProperty.py @@ -29,10 +29,12 @@ __doc__ = "Prototype objects to allow extraction of setup sheet values and editi PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) + class Property(object): - '''Base class for all prototype properties''' + """Base class for all prototype properties""" + def __init__(self, name, propType, category, info): self.name = name self.propType = propType @@ -43,6 +45,7 @@ class Property(object): def setValue(self, value): self.value = value + def getValue(self): return self.value @@ -52,7 +55,7 @@ class Property(object): def displayString(self): if self.value is None: t = self.typeString() - p = 'an' if t[0] in ['A', 'E', 'I', 'O', 'U'] else 'a' + p = "an" if t[0] in ["A", "E", "I", "O", "U"] else "a" return "%s %s" % (p, t) return self.value @@ -77,6 +80,7 @@ class Property(object): def valueFromString(self, string): return string + class PropertyEnumeration(Property): def typeString(self): return "Enumeration" @@ -93,28 +97,34 @@ class PropertyEnumeration(Property): def initProperty(self, obj, name): setattr(obj, name, self.enums) + class PropertyQuantity(Property): def displayString(self): if self.value is None: return Property.displayString(self) return self.value.getUserPreferred()[0] + class PropertyAngle(PropertyQuantity): def typeString(self): return "Angle" + class PropertyDistance(PropertyQuantity): def typeString(self): return "Distance" + class PropertyLength(PropertyQuantity): def typeString(self): return "Length" + class PropertyPercent(Property): def typeString(self): return "Percent" + class PropertyFloat(Property): def typeString(self): return "Float" @@ -122,6 +132,7 @@ class PropertyFloat(Property): def valueFromString(self, string): return float(string) + class PropertyInteger(Property): def typeString(self): return "Integer" @@ -129,6 +140,7 @@ class PropertyInteger(Property): def valueFromString(self, string): return int(string) + class PropertyBool(Property): def typeString(self): return "Bool" @@ -136,10 +148,12 @@ class PropertyBool(Property): def valueFromString(self, string): return bool(string) + class PropertyString(Property): def typeString(self): return "String" + class PropertyMap(Property): def typeString(self): return "Map" @@ -147,31 +161,32 @@ class PropertyMap(Property): def displayString(self, value): return str(value) + class OpPrototype(object): PropertyType = { - 'App::PropertyAngle': PropertyAngle, - 'App::PropertyBool': PropertyBool, - 'App::PropertyDistance': PropertyDistance, - 'App::PropertyEnumeration': PropertyEnumeration, - 'App::PropertyFile': PropertyString, - 'App::PropertyFloat': PropertyFloat, - 'App::PropertyFloatConstraint': Property, - 'App::PropertyFloatList': Property, - 'App::PropertyInteger': PropertyInteger, - 'App::PropertyIntegerList': PropertyInteger, - 'App::PropertyLength': PropertyLength, - 'App::PropertyLink': Property, - 'App::PropertyLinkList': Property, - 'App::PropertyLinkSubListGlobal': Property, - 'App::PropertyMap': PropertyMap, - 'App::PropertyPercent': PropertyPercent, - 'App::PropertyString': PropertyString, - 'App::PropertyStringList': Property, - 'App::PropertyVectorDistance': Property, - 'App::PropertyVectorList': Property, - 'Part::PropertyPartShape': Property, - } + "App::PropertyAngle": PropertyAngle, + "App::PropertyBool": PropertyBool, + "App::PropertyDistance": PropertyDistance, + "App::PropertyEnumeration": PropertyEnumeration, + "App::PropertyFile": PropertyString, + "App::PropertyFloat": PropertyFloat, + "App::PropertyFloatConstraint": Property, + "App::PropertyFloatList": Property, + "App::PropertyInteger": PropertyInteger, + "App::PropertyIntegerList": PropertyInteger, + "App::PropertyLength": PropertyLength, + "App::PropertyLink": Property, + "App::PropertyLinkList": Property, + "App::PropertyLinkSubListGlobal": Property, + "App::PropertyMap": PropertyMap, + "App::PropertyPercent": PropertyPercent, + "App::PropertyString": PropertyString, + "App::PropertyStringList": Property, + "App::PropertyVectorDistance": Property, + "App::PropertyVectorList": Property, + "Part::PropertyPartShape": Property, + } def __init__(self, name): self.Label = name @@ -180,13 +195,13 @@ class OpPrototype(object): self.Proxy = None def __setattr__(self, name, val): - if name in ['Label', 'DoNotSetDefaultValues', 'properties', 'Proxy']: - if name == 'Proxy': - val = None # make sure the proxy is never set + if name in ["Label", "DoNotSetDefaultValues", "properties", "Proxy"]: + if name == "Proxy": + val = None # make sure the proxy is never set return super(OpPrototype, self).__setattr__(name, val) self.properties[name].setValue(val) - def addProperty(self, typeString, name, category, info = None): + def addProperty(self, typeString, name, category, info=None): prop = self.PropertyType[typeString](name, typeString, category, info) self.properties[name] = prop return self diff --git a/src/Mod/Path/PathScripts/PathPropertyBag.py b/src/Mod/Path/PathScripts/PathPropertyBag.py index 27fef993c5..7238ec099b 100644 --- a/src/Mod/Path/PathScripts/PathPropertyBag.py +++ b/src/Mod/Path/PathScripts/PathPropertyBag.py @@ -72,9 +72,7 @@ class PropertyBag(object): "App::PropertyStringList", self.CustomPropertyGroups, "Base", - QT_TRANSLATE_NOOP( - "App::Property", "List of custom property groups" - ), + QT_TRANSLATE_NOOP("App::Property", "List of custom property groups"), ) self.onDocumentRestored(obj) diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index 730fd06c1f..37bec946fe 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -382,7 +382,7 @@ def turnselect(): def select(op): opsel = {} - opsel["Contour"] = contourselect # deprecated + opsel["Contour"] = contourselect # deprecated opsel["Deburr"] = chamferselect opsel["Drilling"] = drillselect opsel["Engrave"] = engraveselect @@ -390,10 +390,10 @@ def select(op): opsel["MillFace"] = pocketselect opsel["Pocket"] = pocketselect opsel["Pocket 3D"] = pocketselect - opsel["Pocket3D"] = pocketselect # deprecated + opsel["Pocket3D"] = pocketselect # deprecated opsel["Pocket Shape"] = pocketselect - opsel["Profile Edges"] = eselect # deprecated - opsel["Profile Faces"] = fselect # deprecated + opsel["Profile Edges"] = eselect # deprecated + opsel["Profile Faces"] = fselect # deprecated opsel["Profile"] = profileselect opsel["Slot"] = slotselect opsel["Surface"] = surfaceselect diff --git a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py index 03c7fbdb18..09474e9eb9 100644 --- a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py +++ b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py @@ -29,10 +29,12 @@ __doc__ = "Prototype objects to allow extraction of setup sheet values and editi PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) + class Property(object): - '''Base class for all prototype properties''' + """Base class for all prototype properties""" + def __init__(self, name, propType, category, info): self.name = name self.propType = propType @@ -43,6 +45,7 @@ class Property(object): def setValue(self, value): self.value = value + def getValue(self): return self.value @@ -52,7 +55,7 @@ class Property(object): def displayString(self): if self.value is None: t = self.typeString() - p = 'an' if t[0] in ['A', 'E', 'I', 'O', 'U'] else 'a' + p = "an" if t[0] in ["A", "E", "I", "O", "U"] else "a" return "%s %s" % (p, t) return self.value @@ -77,6 +80,7 @@ class Property(object): def valueFromString(self, string): return string + class PropertyEnumeration(Property): def typeString(self): return "Enumeration" @@ -93,28 +97,34 @@ class PropertyEnumeration(Property): def initProperty(self, obj, name): setattr(obj, name, self.enums) + class PropertyQuantity(Property): def displayString(self): if self.value is None: return Property.displayString(self) return self.value.getUserPreferred()[0] + class PropertyAngle(PropertyQuantity): def typeString(self): return "Angle" + class PropertyDistance(PropertyQuantity): def typeString(self): return "Distance" + class PropertyLength(PropertyQuantity): def typeString(self): return "Length" + class PropertyPercent(Property): def typeString(self): return "Percent" + class PropertyFloat(Property): def typeString(self): return "Float" @@ -122,6 +132,7 @@ class PropertyFloat(Property): def valueFromString(self, string): return float(string) + class PropertyInteger(Property): def typeString(self): return "Integer" @@ -129,6 +140,7 @@ class PropertyInteger(Property): def valueFromString(self, string): return int(string) + class PropertyBool(Property): def typeString(self): return "Bool" @@ -136,10 +148,12 @@ class PropertyBool(Property): def valueFromString(self, string): return bool(string) + class PropertyString(Property): def typeString(self): return "String" + class PropertyMap(Property): def typeString(self): return "Map" @@ -147,32 +161,33 @@ class PropertyMap(Property): def displayString(self, value): return str(value) + class OpPrototype(object): PropertyType = { - 'App::PropertyAngle': PropertyAngle, - 'App::PropertyBool': PropertyBool, - 'App::PropertyDistance': PropertyDistance, - 'App::PropertyEnumeration': PropertyEnumeration, - 'App::PropertyFile': PropertyString, - 'App::PropertyFloat': PropertyFloat, - 'App::PropertyFloatConstraint': Property, - 'App::PropertyFloatList': Property, - 'App::PropertyInteger': PropertyInteger, - 'App::PropertyIntegerList': PropertyInteger, - 'App::PropertyLength': PropertyLength, - 'App::PropertyLink': Property, - 'App::PropertyLinkList': Property, - 'App::PropertyLinkSubListGlobal': Property, - 'App::PropertyMap': PropertyMap, - 'App::PropertyPercent': PropertyPercent, - 'App::PropertyString': PropertyString, - 'App::PropertyStringList': Property, - 'App::PropertyVectorDistance': Property, - 'App::PropertyVectorList': Property, - 'Part::PropertyPartShape': Property, - 'App::PropertyPythonObject': PropertyString, - } + "App::PropertyAngle": PropertyAngle, + "App::PropertyBool": PropertyBool, + "App::PropertyDistance": PropertyDistance, + "App::PropertyEnumeration": PropertyEnumeration, + "App::PropertyFile": PropertyString, + "App::PropertyFloat": PropertyFloat, + "App::PropertyFloatConstraint": Property, + "App::PropertyFloatList": Property, + "App::PropertyInteger": PropertyInteger, + "App::PropertyIntegerList": PropertyInteger, + "App::PropertyLength": PropertyLength, + "App::PropertyLink": Property, + "App::PropertyLinkList": Property, + "App::PropertyLinkSubListGlobal": Property, + "App::PropertyMap": PropertyMap, + "App::PropertyPercent": PropertyPercent, + "App::PropertyString": PropertyString, + "App::PropertyStringList": Property, + "App::PropertyVectorDistance": Property, + "App::PropertyVectorList": Property, + "Part::PropertyPartShape": Property, + "App::PropertyPythonObject": PropertyString, + } def __init__(self, name): self.Label = name @@ -181,13 +196,13 @@ class OpPrototype(object): self.Proxy = None def __setattr__(self, name, val): - if name in ['Label', 'DoNotSetDefaultValues', 'properties', 'Proxy']: - if name == 'Proxy': - val = None # make sure the proxy is never set + if name in ["Label", "DoNotSetDefaultValues", "properties", "Proxy"]: + if name == "Proxy": + val = None # make sure the proxy is never set return super(OpPrototype, self).__setattr__(name, val) self.properties[name].setValue(val) - def addProperty(self, typeString, name, category, info = None): + def addProperty(self, typeString, name, category, info=None): prop = self.PropertyType[typeString](name, typeString, category, info) self.properties[name] = prop return self diff --git a/src/Mod/Path/PathScripts/PathSimulatorGui.py b/src/Mod/Path/PathScripts/PathSimulatorGui.py index 1bbac7a243..f2cdb36c82 100644 --- a/src/Mod/Path/PathScripts/PathSimulatorGui.py +++ b/src/Mod/Path/PathScripts/PathSimulatorGui.py @@ -341,25 +341,25 @@ class PathSimulation: # for cmd in job.Path.Commands: if cmd.Name in ["G0", "G1", "G2", "G3"]: self.firstDrill = True - if cmd.Name in ['G2', 'G3'] and (cmd.k or 0) == 0: + if cmd.Name in ["G2", "G3"] and (cmd.k or 0) == 0: cx = self.curpos.Base.x + (cmd.i or 0) cy = self.curpos.Base.y + (cmd.j or 0) - a0 = math.atan2( self.curpos.Base.y - cy, self.curpos.Base.x - cx ) - a1 = math.atan2( cmd.y - cy, cmd.x - cx ) + a0 = math.atan2(self.curpos.Base.y - cy, self.curpos.Base.x - cx) + a1 = math.atan2(cmd.y - cy, cmd.x - cx) da = a1 - a0 - if cmd.Name == 'G3': + if cmd.Name == "G3": da = da % (2 * math.pi) else: da = -((-da) % (2 * math.pi)) - r = math.sqrt( (cmd.i or 0) ** 2 + (cmd.j or 0) ** 2 ) - n = math.ceil( math.sqrt( r / self.resolution * da * da ) ) + r = math.sqrt((cmd.i or 0) ** 2 + (cmd.j or 0) ** 2) + n = math.ceil(math.sqrt(r / self.resolution * da * da)) da = da / n dz = (cmd.z - self.curpos.Base.z) / n - cmd.Name = 'G1' - for i in range( n ): + cmd.Name = "G1" + for i in range(n): a0 += da - cmd.x = cx + r * math.cos( a0 ) - cmd.y = cy + r * math.sin( a0 ) + cmd.x = cx + r * math.cos(a0) + cmd.y = cy + r * math.sin(a0) cmd.z = self.curpos.Base.z + dz self.curpos = self.voxSim.ApplyCommand(self.curpos, cmd) else: diff --git a/src/Mod/Path/PathScripts/PathSurfaceGui.py b/src/Mod/Path/PathScripts/PathSurfaceGui.py index 83e98323cd..6068b5292c 100644 --- a/src/Mod/Path/PathScripts/PathSurfaceGui.py +++ b/src/Mod/Path/PathScripts/PathSurfaceGui.py @@ -51,7 +51,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.setTitle("3D Surface - " + obj.Label) # self.updateVisibility() # retrieve property enumerations - # self.propEnums = PathSurface.ObjectSurface.opPropertyEnumerations(False) + # self.propEnums = PathSurface.ObjectSurface.opPropertyEnumerations(False) self.propEnums = PathSurface.ObjectSurface.propertyEnumerations(False) def getForm(self): diff --git a/src/Mod/Path/PathScripts/PathThreadMillingGui.py b/src/Mod/Path/PathScripts/PathThreadMillingGui.py index 080835555b..7b9c0e3b82 100644 --- a/src/Mod/Path/PathScripts/PathThreadMillingGui.py +++ b/src/Mod/Path/PathScripts/PathThreadMillingGui.py @@ -72,9 +72,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): self.minorDia = PathGui.QuantitySpinBox( self.form.threadMinor, obj, "MinorDiameter" ) - self.pitch = PathGui.QuantitySpinBox( - self.form.threadPitch, obj, "Pitch" - ) + self.pitch = PathGui.QuantitySpinBox(self.form.threadPitch, obj, "Pitch") # setupCombo(self.form.threadOrientation, obj.Proxy.ThreadOrientations) # setupCombo(self.form.threadType, obj.Proxy.ThreadTypes) diff --git a/src/Mod/Path/PathScripts/PathToolBit.py b/src/Mod/Path/PathScripts/PathToolBit.py index f4adde8fbb..e657b7e315 100644 --- a/src/Mod/Path/PathScripts/PathToolBit.py +++ b/src/Mod/Path/PathScripts/PathToolBit.py @@ -369,8 +369,11 @@ class ToolBit(object): self._setupProperty(obj, prop, attributes) propNames.append(prop) if not propNames: - PathLog.error("Did not find a PropertyBag in {} - not a ToolBit shape?".format( - docName)) + PathLog.error( + "Did not find a PropertyBag in {} - not a ToolBit shape?".format( + docName + ) + ) # has to happen last because it could trigger op.execute evaluations obj.BitPropertyNames = propNames diff --git a/src/Mod/Path/PathScripts/PathToolControllerGui.py b/src/Mod/Path/PathScripts/PathToolControllerGui.py index fbc4fb92e0..72e4445c7b 100644 --- a/src/Mod/Path/PathScripts/PathToolControllerGui.py +++ b/src/Mod/Path/PathScripts/PathToolControllerGui.py @@ -138,7 +138,6 @@ def Create(name="Default Tool", tool=None, toolNumber=1): class CommandPathToolController(object): - def GetResources(self): return { "Pixmap": "Path_LengthOffset", diff --git a/src/Mod/Path/PathScripts/PathToolEdit.py b/src/Mod/Path/PathScripts/PathToolEdit.py index 0b4bc95bea..8cb5b1afc9 100644 --- a/src/Mod/Path/PathScripts/PathToolEdit.py +++ b/src/Mod/Path/PathScripts/PathToolEdit.py @@ -29,11 +29,13 @@ import math from PySide import QtGui PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) + class ToolEditorDefault: - '''Generic Tool parameter editor for all Tools that don't have a customized edit function. - Let's the user enter the raw internal data. Not the best approach but this is the starting point.''' + """Generic Tool parameter editor for all Tools that don't have a customized edit function. + Let's the user enter the raw internal data. Not the best approach but this is the starting point.""" + def __init__(self, editor): self.editor = editor self.form = editor.form @@ -43,38 +45,72 @@ class ToolEditorDefault: self.form.paramGeneric.show() def updateUI(self): - self.form.toolDiameter.setText(FreeCAD.Units.Quantity(self.editor.tool.Diameter, FreeCAD.Units.Length).UserString) - self.form.toolFlatRadius.setText(FreeCAD.Units.Quantity(self.editor.tool.FlatRadius, FreeCAD.Units.Length).UserString) - self.form.toolCornerRadius.setText(FreeCAD.Units.Quantity(self.editor.tool.CornerRadius, FreeCAD.Units.Length).UserString) - self.form.toolCuttingEdgeHeight.setText(FreeCAD.Units.Quantity(self.editor.tool.CuttingEdgeHeight, FreeCAD.Units.Length).UserString) - self.form.toolCuttingEdgeAngle.setText(FreeCAD.Units.Quantity(self.editor.tool.CuttingEdgeAngle, FreeCAD.Units.Angle).UserString) + self.form.toolDiameter.setText( + FreeCAD.Units.Quantity( + self.editor.tool.Diameter, FreeCAD.Units.Length + ).UserString + ) + self.form.toolFlatRadius.setText( + FreeCAD.Units.Quantity( + self.editor.tool.FlatRadius, FreeCAD.Units.Length + ).UserString + ) + self.form.toolCornerRadius.setText( + FreeCAD.Units.Quantity( + self.editor.tool.CornerRadius, FreeCAD.Units.Length + ).UserString + ) + self.form.toolCuttingEdgeHeight.setText( + FreeCAD.Units.Quantity( + self.editor.tool.CuttingEdgeHeight, FreeCAD.Units.Length + ).UserString + ) + self.form.toolCuttingEdgeAngle.setText( + FreeCAD.Units.Quantity( + self.editor.tool.CuttingEdgeAngle, FreeCAD.Units.Angle + ).UserString + ) def updateTool(self): - self.editor.tool.Diameter = FreeCAD.Units.parseQuantity(self.form.toolDiameter.text()) - self.editor.tool.FlatRadius = FreeCAD.Units.parseQuantity(self.form.toolFlatRadius.text()) - self.editor.tool.CornerRadius = FreeCAD.Units.parseQuantity(self.form.toolCornerRadius.text()) - self.editor.tool.CuttingEdgeAngle = FreeCAD.Units.Quantity(self.form.toolCuttingEdgeAngle.text()) - self.editor.tool.CuttingEdgeHeight = FreeCAD.Units.parseQuantity(self.form.toolCuttingEdgeHeight.text()) + self.editor.tool.Diameter = FreeCAD.Units.parseQuantity( + self.form.toolDiameter.text() + ) + self.editor.tool.FlatRadius = FreeCAD.Units.parseQuantity( + self.form.toolFlatRadius.text() + ) + self.editor.tool.CornerRadius = FreeCAD.Units.parseQuantity( + self.form.toolCornerRadius.text() + ) + self.editor.tool.CuttingEdgeAngle = FreeCAD.Units.Quantity( + self.form.toolCuttingEdgeAngle.text() + ) + self.editor.tool.CuttingEdgeHeight = FreeCAD.Units.parseQuantity( + self.form.toolCuttingEdgeHeight.text() + ) + class ToolEditorImage(object): - '''Base implementation for all customized Tool parameter editors. - While not required it is simplest to subclass specific editors.''' - def __init__(self, editor, imageFile, hide='', disable=''): + """Base implementation for all customized Tool parameter editors. + While not required it is simplest to subclass specific editors.""" + + def __init__(self, editor, imageFile, hide="", disable=""): self.editor = editor self.form = editor.form - self.imagePath = "{}Mod/Path/Images/Tools/{}".format(FreeCAD.getHomePath(), imageFile) + self.imagePath = "{}Mod/Path/Images/Tools/{}".format( + FreeCAD.getHomePath(), imageFile + ) self.image = QtGui.QPixmap(self.imagePath) self.hide = hide self.disable = disable form = editor.form self.widgets = { - 'D' : (form.label_D, form.value_D), - 'd' : (form.label_d, form.value_d), - 'H' : (form.label_H, form.value_H), - 'a' : (form.label_a, form.value_a), - 'S' : (form.label_S, form.value_S) - } + "D": (form.label_D, form.value_D), + "d": (form.label_d, form.value_d), + "H": (form.label_H, form.value_H), + "a": (form.label_a, form.value_a), + "S": (form.label_S, form.value_S), + } def setupUI(self): PathLog.track() @@ -82,7 +118,7 @@ class ToolEditorImage(object): self.form.paramImage.show() for key, widgets in self.widgets.items(): - hide = key in self.hide + hide = key in self.hide disable = key in self.disable for w in widgets: w.setHidden(hide) @@ -102,22 +138,22 @@ class ToolEditorImage(object): def updateTool(self): PathLog.track() toolDefault = Path.Tool() - if 'D' in self.hide: + if "D" in self.hide: self.editor.tool.Diameter = toolDefault.Diameter else: self.editor.tool.Diameter = self.quantityDiameter(False) - if 'd' in self.hide: + if "d" in self.hide: self.editor.tool.FlatRadius = toolDefault.FlatRadius else: self.editor.tool.FlatRadius = self.quantityFlatRadius(False) - if 'a' in self.hide: + if "a" in self.hide: self.editor.tool.CuttingEdgeAngle = toolDefault.CuttingEdgeAngle else: self.editor.tool.CuttingEdgeAngle = self.quantityCuttingEdgeAngle(False) - if 'H' in self.hide: + if "H" in self.hide: self.editor.tool.CuttingEdgeHeight = toolDefault.CuttingEdgeHeight else: self.editor.tool.CuttingEdgeHeight = self.quantityCuttingEdgeHeight(False) @@ -126,48 +162,69 @@ class ToolEditorImage(object): def quantityDiameter(self, propertyToDisplay): if propertyToDisplay: - return FreeCAD.Units.Quantity(self.editor.tool.Diameter, FreeCAD.Units.Length) + return FreeCAD.Units.Quantity( + self.editor.tool.Diameter, FreeCAD.Units.Length + ) return FreeCAD.Units.parseQuantity(self.form.value_D.text()) def quantityFlatRadius(self, propertyToDisplay): if propertyToDisplay: - return FreeCAD.Units.Quantity(self.editor.tool.FlatRadius, FreeCAD.Units.Length) * 2 + return ( + FreeCAD.Units.Quantity( + self.editor.tool.FlatRadius, FreeCAD.Units.Length + ) + * 2 + ) return FreeCAD.Units.parseQuantity(self.form.value_d.text()) / 2 def quantityCuttingEdgeAngle(self, propertyToDisplay): if propertyToDisplay: - return FreeCAD.Units.Quantity(self.editor.tool.CuttingEdgeAngle, FreeCAD.Units.Angle) + return FreeCAD.Units.Quantity( + self.editor.tool.CuttingEdgeAngle, FreeCAD.Units.Angle + ) return FreeCAD.Units.parseQuantity(self.form.value_a.text()) def quantityCuttingEdgeHeight(self, propertyToDisplay): if propertyToDisplay: - return FreeCAD.Units.Quantity(self.editor.tool.CuttingEdgeHeight, FreeCAD.Units.Length) + return FreeCAD.Units.Quantity( + self.editor.tool.CuttingEdgeHeight, FreeCAD.Units.Length + ) return FreeCAD.Units.parseQuantity(self.form.value_H.text()) + class ToolEditorEndmill(ToolEditorImage): - '''Tool parameter editor for endmills.''' + """Tool parameter editor for endmills.""" + def __init__(self, editor): - super(ToolEditorEndmill, self).__init__(editor, 'endmill.svg', 'da', 'S') + super(ToolEditorEndmill, self).__init__(editor, "endmill.svg", "da", "S") + class ToolEditorReamer(ToolEditorImage): - '''Tool parameter editor for reamers.''' + """Tool parameter editor for reamers.""" + def __init__(self, editor): - super(ToolEditorReamer, self).__init__(editor, 'reamer.svg', 'da', 'S') + super(ToolEditorReamer, self).__init__(editor, "reamer.svg", "da", "S") + class ToolEditorDrill(ToolEditorImage): - '''Tool parameter editor for drills.''' + """Tool parameter editor for drills.""" + def __init__(self, editor): - super(ToolEditorDrill, self).__init__(editor, 'drill.svg', 'dS', '') + super(ToolEditorDrill, self).__init__(editor, "drill.svg", "dS", "") def quantityCuttingEdgeAngle(self, propertyToDisplay): if propertyToDisplay: - return FreeCAD.Units.Quantity(self.editor.tool.CuttingEdgeAngle, FreeCAD.Units.Angle) + return FreeCAD.Units.Quantity( + self.editor.tool.CuttingEdgeAngle, FreeCAD.Units.Angle + ) return FreeCAD.Units.parseQuantity(self.form.value_a.text()) + class ToolEditorEngrave(ToolEditorImage): - '''Tool parameter editor for v-bits.''' + """Tool parameter editor for v-bits.""" + def __init__(self, editor): - super(ToolEditorEngrave, self).__init__(editor, 'v-bit.svg', '', 'dS') + super(ToolEditorEngrave, self).__init__(editor, "v-bit.svg", "", "dS") def quantityCuttingEdgeHeight(self, propertyToDisplay): PathLog.track() @@ -175,8 +232,9 @@ class ToolEditorEngrave(ToolEditorImage): da = self.quantityCuttingEdgeAngle(False).Value return dr / math.tan(math.radians(da) / 2) + class ToolEditor: - '''UI and controller for editing a Tool. + """UI and controller for editing a Tool. The controller embeds the UI to the parentWidget which has to have a layout attached to it. The editor maintains two Tools, self.tool and self.Tool. The former is the one being edited and always reflects the current state. self.Tool on the other hand is the "official" Tool @@ -186,13 +244,14 @@ class ToolEditor: The editor uses instances of ToolEditorDefault and ToolEditorImage to deal with the changes of the actual parameters. For any ToolType not mapped in ToolTypeImage the editor uses an instance of ToolEditorDefault. - ''' + """ ToolTypeImage = { - 'EndMill': ToolEditorEndmill, - 'Drill': ToolEditorDrill, - 'Engraver': ToolEditorEngrave, - 'Reamer': ToolEditorReamer, } + "EndMill": ToolEditorEndmill, + "Drill": ToolEditorDrill, + "Engraver": ToolEditorEngrave, + "Reamer": ToolEditorReamer, + } def __init__(self, tool, parentWidget, parent=None): self.Parent = parent @@ -242,7 +301,11 @@ class ToolEditor: self.form.toolName.setText(self.tool.Name) self.form.toolType.setCurrentIndex(self.getType(self.tool.ToolType)) self.form.toolMaterial.setCurrentIndex(self.getMaterial(self.tool.Material)) - self.form.toolLengthOffset.setText(FreeCAD.Units.Quantity(self.tool.LengthOffset, FreeCAD.Units.Length).UserString) + self.form.toolLengthOffset.setText( + FreeCAD.Units.Quantity( + self.tool.LengthOffset, FreeCAD.Units.Length + ).UserString + ) self.editor.updateUI() @@ -257,7 +320,7 @@ class ToolEditor: def setupToolType(self, tt): PathLog.track() print("Tool type: %s" % (tt)) - if 'Undefined' == tt: + if "Undefined" == tt: tt = Path.Tool.getToolTypes(Path.Tool())[0] if tt in self.ToolTypeImage: self.editor = self.ToolTypeImage[tt](self) @@ -270,7 +333,9 @@ class ToolEditor: PathLog.track() self.tool.Name = str(self.form.toolName.text()) self.tool.Material = self.getMaterial(self.form.toolMaterial.currentIndex()) - self.tool.LengthOffset = FreeCAD.Units.parseQuantity(self.form.toolLengthOffset.text()) + self.tool.LengthOffset = FreeCAD.Units.parseQuantity( + self.form.toolLengthOffset.text() + ) self.editor.updateTool() def refresh(self): diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 1153ab2cde..419855bdb2 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -51,13 +51,15 @@ else: UserInput = None + class PathNoTCExistsException(Exception): - '''PathNoECExistsException is raised when no TC exists at all, or when all + """PathNoECExistsException is raised when no TC exists at all, or when all existing TCs are rejected by a given op. - This is typically an error because avery op requires a TC. ''' + This is typically an error because avery op requires a TC.""" def __init__(self): - super().__init__('No Tool Controllers exist') + super().__init__("No Tool Controllers exist") + def waiting_effects(function): def new_function(*args, **kwargs): diff --git a/src/Mod/Path/PathScripts/PathUtilsGui.py b/src/Mod/Path/PathScripts/PathUtilsGui.py index a5df387eb3..3290d75e3c 100644 --- a/src/Mod/Path/PathScripts/PathUtilsGui.py +++ b/src/Mod/Path/PathScripts/PathUtilsGui.py @@ -21,20 +21,22 @@ # *************************************************************************** import FreeCADGui -import PathGui as PGui # ensure Path/Gui/Resources are loaded +import PathGui as PGui # ensure Path/Gui/Resources are loaded import PathScripts import PathScripts.PathJobCmd as PathJobCmd import PathScripts.PathUtils as PathUtils -class PathUtilsUserInput(object): +class PathUtilsUserInput(object): def selectedToolController(self): tc = None # check if a user has selected a tool controller in the tree. # Return the first one and remove all from selection for sel in FreeCADGui.Selection.getSelectionEx(): - if hasattr(sel.Object, 'Proxy'): - if isinstance(sel.Object.Proxy, PathScripts.PathToolController.ToolController): + if hasattr(sel.Object, "Proxy"): + if isinstance( + sel.Object.Proxy, PathScripts.PathToolController.ToolController + ): if tc is None: tc = sel.Object FreeCADGui.Selection.removeSelection(sel.Object) @@ -47,7 +49,9 @@ class PathUtilsUserInput(object): r = form.exec_() if not r: return None - return [i for i in controllers if i.Label == form.uiToolController.currentText()][0] + return [ + i for i in controllers if i.Label == form.uiToolController.currentText() + ][0] def chooseJob(self, jobs): job = None @@ -79,7 +83,9 @@ class PathUtilsUserInput(object): if r is False or r == 0: return None else: - job = [j for j in jobs if j.Label == form.cboProject.currentText()][0] + job = [ + j for j in jobs if j.Label == form.cboProject.currentText() + ][0] return job def createJob(self): @@ -87,4 +93,3 @@ class PathUtilsUserInput(object): PathUtils.UserInput = PathUtilsUserInput() - diff --git a/src/Mod/Path/PathScripts/PathWaterline.py b/src/Mod/Path/PathScripts/PathWaterline.py index 0e54b52bf8..83db68725c 100644 --- a/src/Mod/Path/PathScripts/PathWaterline.py +++ b/src/Mod/Path/PathScripts/PathWaterline.py @@ -66,7 +66,6 @@ else: PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) - class ObjectWaterline(PathOp.ObjectOp): """Proxy object for Surfacing operation.""" diff --git a/src/Mod/Path/PathScripts/PostUtils.py b/src/Mod/Path/PathScripts/PostUtils.py index 60c0744ed8..c6a4aa36bd 100644 --- a/src/Mod/Path/PathScripts/PostUtils.py +++ b/src/Mod/Path/PathScripts/PostUtils.py @@ -198,6 +198,7 @@ def fcoms(string, commentsym): return string return comment + def splitArcs(path): """filters a path object and replaces at G2/G3 moves with discrete G1 returns a Path object""" @@ -217,7 +218,7 @@ def splitArcs(path): continue edge = edgeForCmd(command, machine.getPosition()) - pts =edge.discretize(Deflection=deflection) + pts = edge.discretize(Deflection=deflection) edges = [Part.makeLine(v1, v2) for v1, v2 in zip(pts, pts[1:])] for edge in edges: results.extend(cmdsForEdge(edge)) @@ -225,4 +226,3 @@ def splitArcs(path): machine.addCommand(command) return Path.Path(results) - diff --git a/src/Mod/Path/PathScripts/post/KineticNC_post.py b/src/Mod/Path/PathScripts/post/KineticNC_post.py index 2fdc98f4e2..10f6de3c4e 100644 --- a/src/Mod/Path/PathScripts/post/KineticNC_post.py +++ b/src/Mod/Path/PathScripts/post/KineticNC_post.py @@ -41,7 +41,7 @@ import shlex from PathScripts import PostUtils from PathScripts import PathUtils -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output real GCode suitable for a High-Z S-1000T 4 Axis Mill by CNC-Step (www.cnc-step.com) using the KineticNC Control Software. This postprocessor, once placed @@ -50,21 +50,45 @@ FreeCAD, via the GUI importer or via python scripts with: import KineticNC_post KineticNC_post.export(object,"/path/to/file.ncc","") -''' +""" now = datetime.datetime.now() -parser = argparse.ArgumentParser(prog='linuxcnc', add_help=False) -parser.add_argument('--no-header', action='store_true', help='suppress header output') -parser.add_argument('--no-comments', action='store_true', help='suppress comment output') -parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers') -parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output') -parser.add_argument('--precision', default='3', help='number of digits of precision, default=3') -parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"') -parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"') -parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)') -parser.add_argument('--modal', action='store_true', help='Output the Same G-command Name USE NonModal Mode') -parser.add_argument('--axis-modal', action='store_true', help='Output the Same Axis Value Mode') +parser = argparse.ArgumentParser(prog="linuxcnc", add_help=False) +parser.add_argument("--no-header", action="store_true", help="suppress header output") +parser.add_argument( + "--no-comments", action="store_true", help="suppress comment output" +) +parser.add_argument( + "--line-numbers", action="store_true", help="prefix with line numbers" +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) +parser.add_argument( + "--precision", default="3", help="number of digits of precision, default=3" +) +parser.add_argument( + "--preamble", + help='set commands to be issued before the first command, default="G17\nG90"', +) +parser.add_argument( + "--postamble", + help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"', +) +parser.add_argument( + "--inches", action="store_true", help="Convert output for US imperial mode (G20)" +) +parser.add_argument( + "--modal", + action="store_true", + help="Output the Same G-command Name USE NonModal Mode", +) +parser.add_argument( + "--axis-modal", action="store_true", help="Output the Same Axis Value Mode" +) TOOLTIP_ARGS = parser.format_help() @@ -74,44 +98,46 @@ OUTPUT_HEADER = True OUTPUT_LINE_NUMBERS = False SHOW_EDITOR = True MODAL = False # if true commands are suppressed if the same as previous line. -OUTPUT_DOUBLES = True # if false duplicate axis values are suppressed if the same as previous line. +OUTPUT_DOUBLES = ( + True # if false duplicate axis values are suppressed if the same as previous line. +) COMMAND_SPACE = " " LINENR = 100 # line number starting value # These globals will be reflected in the Machine configuration of the project UNITS = "G21" # G21 for metric, G20 for us standard -UNIT_SPEED_FORMAT = 'mm/min' -UNIT_FORMAT = 'mm' +UNIT_SPEED_FORMAT = "mm/min" +UNIT_FORMAT = "mm" MACHINE_NAME = "High-Z S-1000T" -CORNER_MIN = {'x': 0, 'y': 0, 'z': 0} -CORNER_MAX = {'x': 1000, 'y': 600, 'z': 300} +CORNER_MIN = {"x": 0, "y": 0, "z": 0} +CORNER_MAX = {"x": 1000, "y": 600, "z": 300} PRECISION = 3 # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''% +PREAMBLE = """% G17 G21 G40 G49 G80 G90 M08 -''' +""" # Postamble text will appear following the last operation. -POSTAMBLE = '''M05 M09 +POSTAMBLE = """M05 M09 G17 G90 G80 G40 M30 -''' +""" # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''M05 -M09''' +TOOL_CHANGE = """M05 +M09""" # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -146,14 +172,14 @@ def processArguments(argstring): if args.postamble is not None: POSTAMBLE = args.postamble if args.inches: - UNITS = 'G20' - UNIT_SPEED_FORMAT = 'in/min' - UNIT_FORMAT = 'in' + UNITS = "G20" + UNIT_SPEED_FORMAT = "in/min" + UNIT_FORMAT = "in" PRECISION = 4 if args.modal: MODAL = True if args.axis_modal: - print ('here') + print("here") OUTPUT_DOUBLES = False except: @@ -171,7 +197,11 @@ def export(objectslist, filename, argstring): for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return None print("postprocessing...") @@ -195,7 +225,7 @@ def export(objectslist, filename, argstring): # fetch machine details job = PathUtils.findParentJob(obj) - myMachine = 'not set' + myMachine = "not set" if hasattr(job, "MachineName"): myMachine = job.MachineName @@ -203,17 +233,20 @@ def export(objectslist, filename, argstring): if hasattr(job, "MachineUnits"): if job.MachineUnits == "Metric": UNITS = "G21" - UNIT_FORMAT = 'mm' - UNIT_SPEED_FORMAT = 'mm/min' + UNIT_FORMAT = "mm" + UNIT_SPEED_FORMAT = "mm/min" else: UNITS = "G20" - UNIT_FORMAT = 'in' - UNIT_SPEED_FORMAT = 'in/min' + UNIT_FORMAT = "in" + UNIT_SPEED_FORMAT = "in/min" # do the pre_op if OUTPUT_COMMENTS: gcode += linenumber() + "(begin operation: %s)\n" % obj.Label - gcode += linenumber() + "(machine: %s, %s)\n" % (myMachine, UNIT_SPEED_FORMAT) + gcode += linenumber() + "(machine: %s, %s)\n" % ( + myMachine, + UNIT_SPEED_FORMAT, + ) for line in PRE_OPERATION.splitlines(True): gcode += linenumber() + line @@ -244,7 +277,7 @@ def export(objectslist, filename, argstring): print("done postprocessing.") - if not filename == '-': + if not filename == "-": gfile = pythonopen(filename, "w") gfile.write(final) gfile.close() @@ -269,12 +302,30 @@ def parse(pathobj): out = "" lastcommand = None - precision_string = '.' + str(PRECISION) + 'f' + precision_string = "." + str(PRECISION) + "f" currLocation = {} # keep track for no doubles # the order of parameters # linuxcnc doesn't want K properties on XY plane Arcs need work. - params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P'] + params = [ + "X", + "Y", + "Z", + "A", + "B", + "C", + "I", + "J", + "F", + "S", + "T", + "Q", + "R", + "L", + "H", + "D", + "P", + ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters @@ -304,41 +355,64 @@ def parse(pathobj): if command == lastcommand: outstring.pop(0) - if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment + if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES): - if c.Name not in ["G0", "G00"]: # linuxcnc doesn't use rapid speeds - speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) + if param == "F" and ( + currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES + ): + if c.Name not in [ + "G0", + "G00", + ]: # linuxcnc doesn't use rapid speeds + speed = Units.Quantity( + c.Parameters["F"], FreeCAD.Units.Velocity + ) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: - outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) + outstring.append( + param + + format( + float(speed.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) else: continue - elif param == 'T': - outstring.append(param + str(int(c.Parameters['T']))) - elif param == 'H': - outstring.append(param + str(int(c.Parameters['H']))) - elif param == 'D': - outstring.append(param + str(int(c.Parameters['D']))) - elif param == 'S': - outstring.append(param + str(int(c.Parameters['S']))) + elif param == "T": + outstring.append(param + str(int(c.Parameters["T"]))) + elif param == "H": + outstring.append(param + str(int(c.Parameters["H"]))) + elif param == "D": + outstring.append(param + str(int(c.Parameters["D"]))) + elif param == "S": + outstring.append(param + str(int(c.Parameters["S"]))) else: - if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]): + if ( + (not OUTPUT_DOUBLES) + and (param in currLocation) + and (currLocation[param] == c.Parameters[param]) + ): continue else: - pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) + pos = Units.Quantity( + c.Parameters[param], FreeCAD.Units.Length + ) outstring.append( - param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), precision_string + ) + ) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: - if command == 'M6': + if command == "M6": # if OUTPUT_COMMENTS: # out += linenumber() + "(begin toolchange)\n" for line in TOOL_CHANGE.splitlines(True): @@ -362,4 +436,5 @@ def parse(pathobj): return out + print(__name__ + " gcode postprocessor loaded.") diff --git a/src/Mod/Path/PathScripts/post/centroid_post.py b/src/Mod/Path/PathScripts/post/centroid_post.py index 40f45bab02..4dae5462f0 100644 --- a/src/Mod/Path/PathScripts/post/centroid_post.py +++ b/src/Mod/Path/PathScripts/post/centroid_post.py @@ -30,7 +30,7 @@ import datetime import PathScripts import PathScripts.PostUtils as PostUtils -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output real GCode suitable for a centroid 3 axis mill. This postprocessor, once placed @@ -39,9 +39,9 @@ FreeCAD, via the GUI importer or via python scripts with: import centroid_post centroid_post.export(object,"/path/to/file.ncc","") -''' +""" -TOOLTIP_ARGS = ''' +TOOLTIP_ARGS = """ Arguments for centroid: --header,--no-header ... output headers (--header) --comments,--no-comments ... output comments (--comments) @@ -50,7 +50,7 @@ Arguments for centroid: --feed-precision=1 ... number of digits of precision for feed rate. Default=1 --axis-precision=4 ... number of digits of precision for axis moves. Default=4 --inches ... Convert output for US imperial mode (G20) -''' +""" now = datetime.datetime.now() # These globals set common customization preferences @@ -68,56 +68,58 @@ LINENR = 100 # line number starting value # These globals will be reflected in the Machine configuration of the project UNITS = "G21" # G21 for metric, G20 for us standard -UNIT_FORMAT = 'mm' -UNIT_SPEED_FORMAT = 'mm/min' +UNIT_FORMAT = "mm" +UNIT_SPEED_FORMAT = "mm/min" MACHINE_NAME = "Centroid" -CORNER_MIN = {'x': -609.6, 'y': -152.4, 'z': 0} # use metric for internal units -CORNER_MAX = {'x': 609.6, 'y': 152.4, 'z': 304.8} # use metric for internal units +CORNER_MIN = {"x": -609.6, "y": -152.4, "z": 0} # use metric for internal units +CORNER_MAX = {"x": 609.6, "y": 152.4, "z": 304.8} # use metric for internal units AXIS_PRECISION = 4 FEED_PRECISION = 1 SPINDLE_DECIMALS = 0 COMMENT = ";" -HEADER = ''' +HEADER = """ ;Exported by FreeCAD ;Post Processor: {} ;CAM file: {} ;Output Time: {} -'''.format(__name__, FreeCAD.ActiveDocument.FileName, str(now)) +""".format( + __name__, FreeCAD.ActiveDocument.FileName, str(now) +) # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''G53 G00 G17 -''' +PREAMBLE = """G53 G00 G17 +""" # Postamble text will appear following the last operation. -POSTAMBLE = '''M99 -''' +POSTAMBLE = """M99 +""" -TOOLRETURN = '''M5 +TOOLRETURN = """M5 M25 G49 H0 -''' # spindle off,height offset canceled,spindle retracted (M25 is a centroid command to retract spindle) +""" # spindle off,height offset canceled,spindle retracted (M25 is a centroid command to retract spindle) -ZAXISRETURN = '''G91 G28 X0 Z0 +ZAXISRETURN = """G91 G28 X0 Z0 G90 -''' +""" -SAFETYBLOCK = '''G90 G80 G40 G49 -''' +SAFETYBLOCK = """G90 G80 G40 G49 +""" # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__', 'io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -133,30 +135,30 @@ def processArguments(argstring): global UNITS for arg in argstring.split(): - if arg == '--header': + if arg == "--header": OUTPUT_HEADER = True - elif arg == '--no-header': + elif arg == "--no-header": OUTPUT_HEADER = False - elif arg == '--comments': + elif arg == "--comments": OUTPUT_COMMENTS = True - elif arg == '--no-comments': + elif arg == "--no-comments": OUTPUT_COMMENTS = False - elif arg == '--line-numbers': + elif arg == "--line-numbers": OUTPUT_LINE_NUMBERS = True - elif arg == '--no-line-numbers': + elif arg == "--no-line-numbers": OUTPUT_LINE_NUMBERS = False - elif arg == '--show-editor': + elif arg == "--show-editor": SHOW_EDITOR = True - elif arg == '--no-show-editor': + elif arg == "--no-show-editor": SHOW_EDITOR = False - elif arg.split('=')[0] == '--axis-precision': - AXIS_PRECISION = arg.split('=')[1] - elif arg.split('=')[0] == '--feed-precision': - FEED_PRECISION = arg.split('=')[1] - elif arg == '--inches': - UNITS = 'G20' - UNIT_SPEED_FORMAT = 'in/min' - UNIT_FORMAT = 'in' + elif arg.split("=")[0] == "--axis-precision": + AXIS_PRECISION = arg.split("=")[1] + elif arg.split("=")[0] == "--feed-precision": + FEED_PRECISION = arg.split("=")[1] + elif arg == "--inches": + UNITS = "G20" + UNIT_SPEED_FORMAT = "in/min" + UNIT_FORMAT = "in" def export(objectslist, filename, argstring): @@ -179,7 +181,9 @@ def export(objectslist, filename, argstring): # Write the preamble if OUTPUT_COMMENTS: for item in objectslist: - if hasattr(item, "Proxy") and isinstance(item.Proxy, PathScripts.PathToolController.ToolController): + if hasattr(item, "Proxy") and isinstance( + item.Proxy, PathScripts.PathToolController.ToolController + ): gcode += ";T{}={}\n".format(item.ToolNumber, item.Name) gcode += linenumber() + ";begin preamble\n" for line in PREAMBLE.splitlines(True): @@ -226,7 +230,7 @@ def export(objectslist, filename, argstring): print("done postprocessing.") - if not filename == '-': + if not filename == "-": gfile = pythonopen(filename, "w") gfile.write(final) gfile.close() @@ -245,12 +249,12 @@ def linenumber(): def parse(pathobj): out = "" lastcommand = None - axis_precision_string = '.' + str(AXIS_PRECISION) + 'f' - feed_precision_string = '.' + str(FEED_PRECISION) + 'f' + axis_precision_string = "." + str(AXIS_PRECISION) + "f" + feed_precision_string = "." + str(FEED_PRECISION) + "f" # params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control # the order of parameters # centroid doesn't want K properties on XY plane Arcs need work. - params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H'] + params = ["X", "Y", "Z", "A", "B", "I", "J", "F", "S", "T", "Q", "R", "L", "H"] if hasattr(pathobj, "Group"): # We have a compound or project. # if OUTPUT_COMMENTS: @@ -271,7 +275,7 @@ def parse(pathobj): commandlist = [] # list of elements in the command, code and params. command = c.Name # command M or G code or comment string - if command[0] == '(': + if command[0] == "(": command = PostUtils.fcoms(command, COMMENT) commandlist.append(command) @@ -284,32 +288,50 @@ def parse(pathobj): # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F': - if c.Name not in ["G0", "G00"]: # centroid doesn't use rapid speeds - speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) + if param == "F": + if c.Name not in [ + "G0", + "G00", + ]: # centroid doesn't use rapid speeds + speed = Units.Quantity( + c.Parameters["F"], FreeCAD.Units.Velocity + ) commandlist.append( - param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), feed_precision_string)) - elif param == 'H': - commandlist.append(param + str(int(c.Parameters['H']))) - elif param == 'S': - commandlist.append(param + PostUtils.fmt(c.Parameters['S'], SPINDLE_DECIMALS, "G21")) - elif param == 'T': - commandlist.append(param + str(int(c.Parameters['T']))) + param + + format( + float(speed.getValueAs(UNIT_SPEED_FORMAT)), + feed_precision_string, + ) + ) + elif param == "H": + commandlist.append(param + str(int(c.Parameters["H"]))) + elif param == "S": + commandlist.append( + param + + PostUtils.fmt(c.Parameters["S"], SPINDLE_DECIMALS, "G21") + ) + elif param == "T": + commandlist.append(param + str(int(c.Parameters["T"]))) else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) commandlist.append( - param + format(float(pos.getValueAs(UNIT_FORMAT)), axis_precision_string)) + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), + axis_precision_string, + ) + ) outstr = str(commandlist) - outstr = outstr.replace('[', '') - outstr = outstr.replace(']', '') - outstr = outstr.replace("'", '') - outstr = outstr.replace(",", '') + outstr = outstr.replace("[", "") + outstr = outstr.replace("]", "") + outstr = outstr.replace("'", "") + outstr = outstr.replace(",", "") # store the latest command lastcommand = command # Check for Tool Change: - if command == 'M6': + if command == "M6": # if OUTPUT_COMMENTS: # out += linenumber() + "(begin toolchange)\n" for line in TOOL_CHANGE.splitlines(True): diff --git a/src/Mod/Path/PathScripts/post/comparams_post.py b/src/Mod/Path/PathScripts/post/comparams_post.py index 2f31093e3d..aa5c275e98 100644 --- a/src/Mod/Path/PathScripts/post/comparams_post.py +++ b/src/Mod/Path/PathScripts/post/comparams_post.py @@ -24,25 +24,26 @@ import FreeCAD import Path import PathScripts.PostUtils as PostUtils -TOOLTIP = '''Example Post, using Path.Commands instead of Path.toGCode strings for Path gcode output.''' +TOOLTIP = """Example Post, using Path.Commands instead of Path.toGCode strings for Path gcode output.""" SHOW_EDITOR = True def fmt(num): fnum = "" - fnum += '%.3f' % (num) + fnum += "%.3f" % (num) return fnum def ffmt(num): fnum = "" - fnum += '%.1f' % (num) + fnum += "%.1f" % (num) return fnum class saveVals(object): - ''' save command info for modal output''' + """save command info for modal output""" + def __init__(self, command): self.com = command.Name self.params = command.Parameters @@ -57,55 +58,55 @@ def lineout(command, oldvals, modal): line += "" else: line += str(command.Name) - if command.Name == 'M6': - line += 'T' + str(int(command.Parameters['T'])) - if command.Name == 'M3': - line += 'S' + str(ffmt(command.Parameters['S'])) - if command.Name == 'M4': - line += 'S' + str(ffmt(command.Parameters['S'])) - if 'X' in command.Parameters: - line += "X" + str(fmt(command.Parameters['X'])) - if 'Y' in command.Parameters: - line += "Y" + str(fmt(command.Parameters['Y'])) - if 'Z' in command.Parameters: - line += "Z" + str(fmt(command.Parameters['Z'])) - if 'I' in command.Parameters: - line += "I" + str(fmt(command.Parameters['I'])) - if 'J' in command.Parameters: - line += "J" + str(fmt(command.Parameters['J'])) - if 'F' in command.Parameters: - line += "F" + str(ffmt(command.Parameters['F'])) + if command.Name == "M6": + line += "T" + str(int(command.Parameters["T"])) + if command.Name == "M3": + line += "S" + str(ffmt(command.Parameters["S"])) + if command.Name == "M4": + line += "S" + str(ffmt(command.Parameters["S"])) + if "X" in command.Parameters: + line += "X" + str(fmt(command.Parameters["X"])) + if "Y" in command.Parameters: + line += "Y" + str(fmt(command.Parameters["Y"])) + if "Z" in command.Parameters: + line += "Z" + str(fmt(command.Parameters["Z"])) + if "I" in command.Parameters: + line += "I" + str(fmt(command.Parameters["I"])) + if "J" in command.Parameters: + line += "J" + str(fmt(command.Parameters["J"])) + if "F" in command.Parameters: + line += "F" + str(ffmt(command.Parameters["F"])) return line def export(obj, filename, argstring): modal = True - gcode = '' - safetyblock1 = 'G90G40G49\n' + gcode = "" + safetyblock1 = "G90G40G49\n" gcode += safetyblock1 units = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units") - if units.GetInt('UserSchema') == 0: - firstcommand = Path.Command('G21') # metric mode + if units.GetInt("UserSchema") == 0: + firstcommand = Path.Command("G21") # metric mode else: - firstcommand = Path.Command('G20') # inch mode + firstcommand = Path.Command("G20") # inch mode oldvals = saveVals(firstcommand) # save first command for modal use fp = obj[0] gcode += firstcommand.Name if hasattr(fp, "Path"): for c in fp.Path.Commands: - gcode += lineout(c, oldvals, modal) + '\n' + gcode += lineout(c, oldvals, modal) + "\n" oldvals = saveVals(c) - gcode += 'M2\n' + gcode += "M2\n" gfile = open(filename, "w") gfile.write(gcode) gfile.close() else: - FreeCAD.Console.PrintError('Select a path object and try again\n') + FreeCAD.Console.PrintError("Select a path object and try again\n") if SHOW_EDITOR: - FreeCAD.Console.PrintMessage('Editor Activated\n') + FreeCAD.Console.PrintMessage("Editor Activated\n") dia = PostUtils.GCodeEditorDialog() dia.editor.setText(gcode) dia.exec_() diff --git a/src/Mod/Path/PathScripts/post/dumper_post.py b/src/Mod/Path/PathScripts/post/dumper_post.py index fdd88cbf38..a89d515d6b 100644 --- a/src/Mod/Path/PathScripts/post/dumper_post.py +++ b/src/Mod/Path/PathScripts/post/dumper_post.py @@ -23,12 +23,12 @@ from __future__ import print_function -TOOLTIP=''' +TOOLTIP = """ Dumper is an extremely simple postprocessor file for the Path workbench. It is used to dump the command list from one or more Path objects for simple inspection. This post doesn't do any manipulation of the path and doesn't write anything to disk. It just shows the dialog so you can see it. Useful for debugging, but not much else. -''' +""" import datetime from PathScripts import PostUtils @@ -36,25 +36,29 @@ now = datetime.datetime.now() SHOW_EDITOR = True # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open -def export(objectslist, filename,argstring): +def export(objectslist, filename, argstring): "called when freecad exports a list of objects" - output = '''(This output produced with the dump post processor) + output = """(This output produced with the dump post processor) (Dump is useful for inspecting the raw commands in your paths) (but is not useful for driving machines.) (Consider setting a default postprocessor in your project or ) (exporting your paths using a specific post that matches your machine) -''' +""" for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return print("postprocessing...") output += parse(obj) @@ -84,7 +88,9 @@ def parse(pathobj): return out else: # parsing simple path - if not hasattr(pathobj, "Path"): # groups might contain non-path things like stock. + if not hasattr( + pathobj, "Path" + ): # groups might contain non-path things like stock. return out out += "(Path: " + pathobj.Label + ")\n" @@ -93,4 +99,5 @@ def parse(pathobj): out += str(c) + "\n" return out + # print(__name__ + " gcode postprocessor loaded.") diff --git a/src/Mod/Path/PathScripts/post/dynapath_post.py b/src/Mod/Path/PathScripts/post/dynapath_post.py index 908fcca876..936d1f6b97 100644 --- a/src/Mod/Path/PathScripts/post/dynapath_post.py +++ b/src/Mod/Path/PathScripts/post/dynapath_post.py @@ -38,7 +38,7 @@ import datetime import shlex from PathScripts import PostUtils -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output real GCode suitable for a Tree Journyman 325 3 axis mill with Dynapath 20 @@ -70,26 +70,35 @@ spaces add / prior to comments import dynapath_post dynapath_post.export(object,"/path/to/file.ncc","") -''' +""" -parser = argparse.ArgumentParser(prog='dynapath_post', - add_help=False) -parser.add_argument('--no-header', action='store_true', - help='suppress header output') -parser.add_argument('--no-comments', action='store_true', - help='suppress comment output') -parser.add_argument('--line-numbers', action='store_true', - help='prefix with line numbers') -parser.add_argument('--no-show-editor', action='store_true', - help='don\'t pop up editor before writing output') -parser.add_argument('--precision', default='3', - help='number of digits of precision, default=3') -parser.add_argument('--preamble', - help='set commands to be issued before the first command, default="G17\nG90\nG80\nG40"') -parser.add_argument('--postamble', - help='set commands to be issued after the last command, default="M09\nM05\nG80\nG40\nG17\nG90\nM30"') -parser.add_argument('--inches', action='store_true', - help='Convert output for US imperial mode (G20)') +parser = argparse.ArgumentParser(prog="dynapath_post", add_help=False) +parser.add_argument("--no-header", action="store_true", help="suppress header output") +parser.add_argument( + "--no-comments", action="store_true", help="suppress comment output" +) +parser.add_argument( + "--line-numbers", action="store_true", help="prefix with line numbers" +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) +parser.add_argument( + "--precision", default="3", help="number of digits of precision, default=3" +) +parser.add_argument( + "--preamble", + help='set commands to be issued before the first command, default="G17\nG90\nG80\nG40"', +) +parser.add_argument( + "--postamble", + help='set commands to be issued after the last command, default="M09\nM05\nG80\nG40\nG17\nG90\nM30"', +) +parser.add_argument( + "--inches", action="store_true", help="Convert output for US imperial mode (G20)" +) TOOLTIP_ARGS = parser.format_help() @@ -105,46 +114,46 @@ MODAL = False # if true commands are suppressed if the same as previous line. COMMAND_SPACE = " " LINENR = 1 # line number starting value -UNIT_SPEED_FORMAT = 'mm/min' -UNIT_FORMAT = 'mm' +UNIT_SPEED_FORMAT = "mm/min" +UNIT_FORMAT = "mm" # These globals will be reflected in the Machine configuration of the project UNITS = "G21" # G21 for metric, G20 for us standard MACHINE_NAME = "Tree MM" -CORNER_MIN = {'x': -340, 'y': 0, 'z': 0} -CORNER_MAX = {'x': 340, 'y': -355, 'z': -150} +CORNER_MIN = {"x": -340, "y": 0, "z": 0} +CORNER_MAX = {"x": 340, "y": -355, "z": -150} # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''G17 +PREAMBLE = """G17 G90 ;G90.1 ;needed for simulation only G80 G40 -''' +""" # Postamble text will appear following the last operation. -POSTAMBLE = '''M09 +POSTAMBLE = """M09 M05 G80 G40 G17 G90 M30 -''' +""" # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__', 'io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -180,9 +189,9 @@ def processArguments(argstring): if args.postamble is not None: POSTAMBLE = args.postamble if args.inches: - UNITS = 'G20' - UNIT_SPEED_FORMAT = 'in/min' - UNIT_FORMAT = 'in' + UNITS = "G20" + UNIT_SPEED_FORMAT = "in/min" + UNIT_FORMAT = "in" PRECISION = 4 except Exception: @@ -200,7 +209,11 @@ def export(objectslist, filename, argstring): for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return print("postprocessing...") @@ -224,7 +237,7 @@ def export(objectslist, filename, argstring): if OUTPUT_HEADER: gcode += linenumber() + "(Exported by FreeCAD)\n" gcode += linenumber() + "(Post Processor: " + __name__ + ")\n" - gcode += linenumber() + "(Output Time:"+str(now)+")\n" + gcode += linenumber() + "(Output Time:" + str(now) + ")\n" # Write the preamble if OUTPUT_COMMENTS: @@ -256,7 +269,7 @@ def export(objectslist, filename, argstring): for line in POSTAMBLE.splitlines(True): gcode += linenumber() + line - print('show editor: {}'.format(SHOW_EDITOR)) + print("show editor: {}".format(SHOW_EDITOR)) if FreeCAD.GuiUp and SHOW_EDITOR: dia = PostUtils.GCodeEditorDialog() dia.editor.setText(gcode) @@ -291,10 +304,10 @@ def parse(pathobj): out = "" lastcommand = None - precision_string = '.' + str(PRECISION) + 'f' + precision_string = "." + str(PRECISION) + "f" # params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control the order of parameters - params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L'] + params = ["X", "Y", "Z", "A", "B", "I", "J", "F", "S", "T", "Q", "R", "L"] if hasattr(pathobj, "Group"): # We have a compound or project. if OUTPUT_COMMENTS: @@ -304,7 +317,9 @@ def parse(pathobj): return out else: # parsing simple path - if not hasattr(pathobj, "Path"): # groups might contain non-path things like stock. + if not hasattr( + pathobj, "Path" + ): # groups might contain non-path things like stock. return out if OUTPUT_COMMENTS: @@ -322,24 +337,40 @@ def parse(pathobj): # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F': - speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) + if param == "F": + speed = Units.Quantity( + c.Parameters["F"], FreeCAD.Units.Velocity + ) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: - outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) - elif param == 'S': - outstring.append(param + format(c.Parameters[param], precision_string)) - elif param == 'T': - outstring.append(param + format(c.Parameters['T'], precision_string)) + outstring.append( + param + + format( + float(speed.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) + elif param == "S": + outstring.append( + param + format(c.Parameters[param], precision_string) + ) + elif param == "T": + outstring.append( + param + format(c.Parameters["T"], precision_string) + ) else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outstring.append( - param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), precision_string + ) + ) # store the latest command lastcommand = command # Check for Tool Change: - if command == 'M6': + if command == "M6": if OUTPUT_COMMENTS: out += linenumber() + "(begin toolchange)\n" for line in TOOL_CHANGE.splitlines(True): diff --git a/src/Mod/Path/PathScripts/post/example_post.py b/src/Mod/Path/PathScripts/post/example_post.py index 49ae88db49..0d4ea51423 100644 --- a/src/Mod/Path/PathScripts/post/example_post.py +++ b/src/Mod/Path/PathScripts/post/example_post.py @@ -23,24 +23,25 @@ from __future__ import print_function -TOOLTIP=''' +TOOLTIP = """ This is an example postprocessor file for the Path workbench. It is used to save a list of FreeCAD Path objects to a file. Read the Path Workbench documentation to know how to convert Path objects to GCode. -''' +""" import datetime + now = datetime.datetime.now() # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open -def export(objectslist, filename,argstring): +def export(objectslist, filename, argstring): "called when freecad exports a list of objects" if len(objectslist) > 1: print("This script is unable to write more than one Path object") @@ -62,7 +63,7 @@ def parse(inputstring): output = "" # write some stuff first - output += "N10 ;time:"+str(now)+"\n" + output += "N10 ;time:" + str(now) + "\n" output += "N20 G17 G20 G80 G40 G90\n" output += "N30 (Exported by FreeCAD)\n" @@ -99,4 +100,5 @@ def parse(inputstring): print("done postprocessing.") return output + # print(__name__ + " gcode postprocessor loaded.") diff --git a/src/Mod/Path/PathScripts/post/example_pre.py b/src/Mod/Path/PathScripts/post/example_pre.py index 802764cfd1..9ac769a519 100644 --- a/src/Mod/Path/PathScripts/post/example_pre.py +++ b/src/Mod/Path/PathScripts/post/example_pre.py @@ -22,7 +22,7 @@ # *************************************************************************** -''' +""" This is an example preprocessor file for the Path workbench. Its aim is to open a gcode file, parse its contents, and create the appropriate objects in FreeCAD. This preprocessor will not add imported gcode to an existing @@ -30,7 +30,7 @@ job. For a more useful preprocessor, look at the gcode_pre.py file Read the Path Workbench documentation to know how to create Path objects from GCode. -''' +""" import os import Path @@ -46,7 +46,7 @@ if LEVEL == PathLog.Level.DEBUG: # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__', 'io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open diff --git a/src/Mod/Path/PathScripts/post/fablin_post.py b/src/Mod/Path/PathScripts/post/fablin_post.py index 2bb3ff935c..74b01710fc 100644 --- a/src/Mod/Path/PathScripts/post/fablin_post.py +++ b/src/Mod/Path/PathScripts/post/fablin_post.py @@ -26,23 +26,24 @@ import datetime from PathScripts import PostUtils + now = datetime.datetime.now() -''' +""" Generate g-code compatible with fablin from a Path. import fablin_post fablin_post.export(object,"/path/to/file.ncc") -''' +""" -TOOLTIP_ARGS = ''' +TOOLTIP_ARGS = """ Arguments for fablin: --rapids-feedrate ... feedrate to be used for rapids (e.g. --rapids-feedrate=300) --header,--no-header ... output headers (--header) --comments,--no-comments ... output comments (--comments) --line-numbers,--no-line-numbers ... prefix with line numbers (--no-lin-numbers) --show-editor, --no-show-editor ... pop up editor before writing output(--show-editor) -''' +""" # These globals set common customization preferences OUTPUT_COMMENTS = False # Fablin does not support parenthesis, it will echo the command complaining. As a side effect the spinner may turn at a very reduced speed (do not ask me why). @@ -58,38 +59,38 @@ LINENR = 100 # line number starting value # These globals will be reflected in the Machine configuration of the project UNITS = "" # only metric, G20/G21 is ignored MACHINE_NAME = "FABLIN" -CORNER_MIN = {'x': 0, 'y': 0, 'z': 0} -CORNER_MAX = {'x': 500, 'y': 300, 'z': 300} +CORNER_MIN = {"x": 0, "y": 0, "z": 0} +CORNER_MAX = {"x": 500, "y": 300, "z": 300} -RAPID_MOVES = ['G0', 'G00'] +RAPID_MOVES = ["G0", "G00"] RAPID_FEEDRATE = 10000 # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''G90 -''' +PREAMBLE = """G90 +""" # Postamble text will appear following the last operation. -POSTAMBLE = '''M5 +POSTAMBLE = """M5 G00 X-1.0 Y1.0 G90 -''' +""" # These commands are ignored by commenting them out -SUPPRESS_COMMANDS = ['G98', 'G80', 'M6', 'G17'] +SUPPRESS_COMMANDS = ["G98", "G80", "M6", "G17"] # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__', 'io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -100,26 +101,26 @@ def processArguments(argstring): global SHOW_EDITOR global RAPID_FEEDRATE for arg in argstring.split(): - if arg == '--header': + if arg == "--header": OUTPUT_HEADER = True - elif arg == '--no-header': + elif arg == "--no-header": OUTPUT_HEADER = False - elif arg == '--comments': + elif arg == "--comments": OUTPUT_COMMENTS = True - elif arg == '--no-comments': + elif arg == "--no-comments": OUTPUT_COMMENTS = False - elif arg == '--line-numbers': + elif arg == "--line-numbers": OUTPUT_LINE_NUMBERS = True - elif arg == '--no-line-numbers': + elif arg == "--no-line-numbers": OUTPUT_LINE_NUMBERS = False - elif arg == '--show-editor': + elif arg == "--show-editor": SHOW_EDITOR = True - elif arg == '--no-show-editor': + elif arg == "--no-show-editor": SHOW_EDITOR = False - params = arg.split('=') + params = arg.split("=") - if params[0] == '--rapids-feedrate': + if params[0] == "--rapids-feedrate": RAPID_FEEDRATE = params[1] @@ -128,7 +129,11 @@ def export(objectslist, filename, argstring): global UNITS for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return print("postprocessing...") @@ -216,7 +221,21 @@ def parse(pathobj): out = "" lastcommand = None - params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L'] # linuxcnc doesn't want K properties on XY plane Arcs need work. + params = [ + "X", + "Y", + "Z", + "A", + "B", + "I", + "J", + "F", + "S", + "T", + "Q", + "R", + "L", + ] # linuxcnc doesn't want K properties on XY plane Arcs need work. if hasattr(pathobj, "Group"): # We have a compound or project. if OUTPUT_COMMENTS: @@ -226,7 +245,9 @@ def parse(pathobj): return out else: # parsing simple path - if not hasattr(pathobj, "Path"): # groups might contain non-path things like stock. + if not hasattr( + pathobj, "Path" + ): # groups might contain non-path things like stock. return out if OUTPUT_COMMENTS: @@ -237,7 +258,7 @@ def parse(pathobj): command = c.Name # fablin does not support parenthesis syntax, so removing that (pocket) in the agnostic gcode - if command[0] == '(': + if command[0] == "(": if not OUTPUT_COMMENTS: pass else: @@ -251,22 +272,22 @@ def parse(pathobj): # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F': + if param == "F": if command not in RAPID_MOVES: - outstring.append(param + format(c.Parameters['F'], '.2f')) - elif param == 'T': - outstring.append(param + str(c.Parameters['T'])) + outstring.append(param + format(c.Parameters["F"], ".2f")) + elif param == "T": + outstring.append(param + str(c.Parameters["T"])) else: - outstring.append(param + format(c.Parameters[param], '.4f')) + outstring.append(param + format(c.Parameters[param], ".4f")) if command in RAPID_MOVES and command != lastcommand: - outstring.append('F' + format(RAPID_FEEDRATE)) + outstring.append("F" + format(RAPID_FEEDRATE)) # store the latest command lastcommand = command # Check for Tool Change: - if command == 'M6': + if command == "M6": if OUTPUT_COMMENTS: out += linenumber() + "(begin toolchange)\n" if not OUTPUT_TOOL_CHANGE: diff --git a/src/Mod/Path/PathScripts/post/fanuc_post.py b/src/Mod/Path/PathScripts/post/fanuc_post.py index cadfa9b098..b5829a1ed1 100644 --- a/src/Mod/Path/PathScripts/post/fanuc_post.py +++ b/src/Mod/Path/PathScripts/post/fanuc_post.py @@ -31,7 +31,7 @@ import shlex import os.path from PathScripts import PostUtils -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output real GCode suitable should be suitable for most Fanuc controllers. @@ -42,22 +42,50 @@ python scripts with: import fanuc_post fanuc_post.export(object,"/path/to/file.ncc","") -''' +""" now = datetime.datetime.now() -parser = argparse.ArgumentParser(prog='fanuc', add_help=False) -parser.add_argument('--no-header', action='store_true', help='suppress header output') -parser.add_argument('--no-comments', action='store_true', help='suppress comment output') -parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers') -parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output') -parser.add_argument('--precision', default='3', help='number of digits of precision, default=3') -parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"') -parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"') -parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)') -parser.add_argument('--no-modal', action='store_true', help='Don\'t output the Same G-command Name USE NonModal Mode') -parser.add_argument('--no-axis-modal', action='store_true', help='Don\'t output the Same Axis Value Mode') -parser.add_argument('--no-tlo', action='store_true', help='suppress tool length offset (G43) following tool changes') +parser = argparse.ArgumentParser(prog="fanuc", add_help=False) +parser.add_argument("--no-header", action="store_true", help="suppress header output") +parser.add_argument( + "--no-comments", action="store_true", help="suppress comment output" +) +parser.add_argument( + "--line-numbers", action="store_true", help="prefix with line numbers" +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) +parser.add_argument( + "--precision", default="3", help="number of digits of precision, default=3" +) +parser.add_argument( + "--preamble", + help='set commands to be issued before the first command, default="G17\nG90"', +) +parser.add_argument( + "--postamble", + help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"', +) +parser.add_argument( + "--inches", action="store_true", help="Convert output for US imperial mode (G20)" +) +parser.add_argument( + "--no-modal", + action="store_true", + help="Don't output the Same G-command Name USE NonModal Mode", +) +parser.add_argument( + "--no-axis-modal", action="store_true", help="Don't output the Same Axis Value Mode" +) +parser.add_argument( + "--no-tlo", + action="store_true", + help="suppress tool length offset (G43) following tool changes", +) TOOLTIP_ARGS = parser.format_help() @@ -67,19 +95,21 @@ OUTPUT_HEADER = True OUTPUT_LINE_NUMBERS = False SHOW_EDITOR = True MODAL = True # if true commands are suppressed if the same as previous line. -USE_TLO = True # if true G43 will be output following tool changes -OUTPUT_DOUBLES = False # if false duplicate axis values are suppressed if the same as previous line. +USE_TLO = True # if true G43 will be output following tool changes +OUTPUT_DOUBLES = ( + False # if false duplicate axis values are suppressed if the same as previous line. +) COMMAND_SPACE = " " LINENR = 100 # line number starting value # These globals will be reflected in the Machine configuration of the project UNITS = "G21" # G21 for metric, G20 for us standard -UNIT_SPEED_FORMAT = 'mm/min' -UNIT_FORMAT = 'mm' +UNIT_SPEED_FORMAT = "mm/min" +UNIT_FORMAT = "mm" MACHINE_NAME = "fanuc" -CORNER_MIN = {'x': 0, 'y': 0, 'z': 0} -CORNER_MAX = {'x': 500, 'y': 300, 'z': 300} +CORNER_MIN = {"x": 0, "y": 0, "z": 0} +CORNER_MAX = {"x": 500, "y": 300, "z": 300} PRECISION = 3 # this global is used to pass spindle speed from the tool command into the machining command for @@ -87,27 +117,27 @@ PRECISION = 3 tapSpeed = 0 # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''G17 G54 G40 G49 G80 G90 -''' +PREAMBLE = """G17 G54 G40 G49 G80 G90 +""" # Postamble text will appear following the last operation. -POSTAMBLE = '''M05 +POSTAMBLE = """M05 G17 G54 G90 G80 G40 M6 T0 M2 -''' +""" # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -143,9 +173,9 @@ def processArguments(argstring): if args.postamble is not None: POSTAMBLE = args.postamble if args.inches: - UNITS = 'G20' - UNIT_SPEED_FORMAT = 'in/min' - UNIT_FORMAT = 'in' + UNITS = "G20" + UNIT_SPEED_FORMAT = "in/min" + UNIT_FORMAT = "in" PRECISION = 4 if args.no_modal: MODAL = False @@ -171,7 +201,11 @@ def export(objectslist, filename, argstring): for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return None print("postprocessing...") @@ -181,8 +215,15 @@ def export(objectslist, filename, argstring): if OUTPUT_HEADER: gcode += "%\n" gcode += ";\n" - gcode += os.path.split(filename)[-1]+" ("+"FREECAD-FILENAME-GOES-HERE" + ", " + "JOB-NAME-GOES-HERE"+")\n" - gcode += linenumber() + "("+filename.upper()+",EXPORTED BY FREECAD!)\n" + gcode += ( + os.path.split(filename)[-1] + + " (" + + "FREECAD-FILENAME-GOES-HERE" + + ", " + + "JOB-NAME-GOES-HERE" + + ")\n" + ) + gcode += linenumber() + "(" + filename.upper() + ",EXPORTED BY FREECAD!)\n" gcode += linenumber() + "(POST PROCESSOR: " + __name__.upper() + ")\n" gcode += linenumber() + "(OUTPUT TIME:" + str(now).upper() + ")\n" @@ -196,23 +237,29 @@ def export(objectslist, filename, argstring): for obj in objectslist: # Skip inactive operations - if hasattr(obj, 'Active'): + if hasattr(obj, "Active"): if not obj.Active: continue - if hasattr(obj, 'Base') and hasattr(obj.Base, 'Active'): + if hasattr(obj, "Base") and hasattr(obj.Base, "Active"): if not obj.Base.Active: continue # do the pre_op if OUTPUT_COMMENTS: gcode += linenumber() + "(BEGIN OPERATION: %s)\n" % obj.Label.upper() - gcode += linenumber() + "(MACHINE UNITS: %s)\n" % (UNIT_SPEED_FORMAT.upper()) + gcode += linenumber() + "(MACHINE UNITS: %s)\n" % ( + UNIT_SPEED_FORMAT.upper() + ) for line in PRE_OPERATION.splitlines(True): gcode += linenumber() + line # get coolant mode - coolantMode = 'None' - if hasattr(obj, "CoolantMode") or hasattr(obj, 'Base') and hasattr(obj.Base, "CoolantMode"): + coolantMode = "None" + if ( + hasattr(obj, "CoolantMode") + or hasattr(obj, "Base") + and hasattr(obj.Base, "CoolantMode") + ): if hasattr(obj, "CoolantMode"): coolantMode = obj.CoolantMode else: @@ -220,12 +267,12 @@ def export(objectslist, filename, argstring): # turn coolant on if required if OUTPUT_COMMENTS: - if not coolantMode == 'None': - gcode += linenumber() + '(COOLANT ON:' + coolantMode.upper() + ')\n' - if coolantMode == 'Flood': - gcode += linenumber() + 'M8' + '\n' - if coolantMode == 'Mist': - gcode += linenumber() + 'M7' + '\n' + if not coolantMode == "None": + gcode += linenumber() + "(COOLANT ON:" + coolantMode.upper() + ")\n" + if coolantMode == "Flood": + gcode += linenumber() + "M8" + "\n" + if coolantMode == "Mist": + gcode += linenumber() + "M7" + "\n" # process the operation gcode gcode += parse(obj) @@ -237,10 +284,10 @@ def export(objectslist, filename, argstring): gcode += linenumber() + line # turn coolant off if required - if not coolantMode == 'None': + if not coolantMode == "None": if OUTPUT_COMMENTS: - gcode += linenumber() + '(COOLANT OFF:' + coolantMode.upper() + ')\n' - gcode += linenumber() +'M9' + '\n' + gcode += linenumber() + "(COOLANT OFF:" + coolantMode.upper() + ")\n" + gcode += linenumber() + "M9" + "\n" # do the post_amble if OUTPUT_COMMENTS: @@ -262,7 +309,7 @@ def export(objectslist, filename, argstring): print("done postprocessing.") - if not filename == '-': + if not filename == "-": gfile = pythonopen(filename, "w") gfile.write(final) gfile.close() @@ -288,14 +335,32 @@ def parse(pathobj): out = "" lastcommand = None - precision_string = '.' + str(PRECISION) + 'f' + precision_string = "." + str(PRECISION) + "f" currLocation = {} # keep track for no doubles print("Startup!") # the order of parameters # arcs need work. original code from mach3_4 doesn't want K properties on XY plane. Not sure # what fanuc does here. - params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P'] + params = [ + "X", + "Y", + "Z", + "A", + "B", + "C", + "I", + "J", + "F", + "S", + "T", + "Q", + "R", + "L", + "H", + "D", + "P", + ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters @@ -318,33 +383,47 @@ def parse(pathobj): opHorizRapid = 0 opVertRapid = 0 - if 'Adaptive' in pathobj.Name: + if "Adaptive" in pathobj.Name: adaptiveOp = True - if hasattr(pathobj, 'ToolController'): - if hasattr(pathobj.ToolController, 'HorizRapid') and pathobj.ToolController.HorizRapid > 0: - opHorizRapid = Units.Quantity(pathobj.ToolController.HorizRapid, FreeCAD.Units.Velocity) + if hasattr(pathobj, "ToolController"): + if ( + hasattr(pathobj.ToolController, "HorizRapid") + and pathobj.ToolController.HorizRapid > 0 + ): + opHorizRapid = Units.Quantity( + pathobj.ToolController.HorizRapid, FreeCAD.Units.Velocity + ) else: - FreeCAD.Console.PrintWarning('Tool Controller Horizontal Rapid Values are unset'+ '\n') + FreeCAD.Console.PrintWarning( + "Tool Controller Horizontal Rapid Values are unset" + "\n" + ) - if hasattr(pathobj.ToolController, 'VertRapid') and pathobj.ToolController.VertRapid > 0: - opVertRapid = Units.Quantity(pathobj.ToolController.VertRapid, FreeCAD.Units.Velocity) + if ( + hasattr(pathobj.ToolController, "VertRapid") + and pathobj.ToolController.VertRapid > 0 + ): + opVertRapid = Units.Quantity( + pathobj.ToolController.VertRapid, FreeCAD.Units.Velocity + ) else: - FreeCAD.Console.PrintWarning('Tool Controller Vertical Rapid Values are unset'+ '\n') + FreeCAD.Console.PrintWarning( + "Tool Controller Vertical Rapid Values are unset" + "\n" + ) - for index,c in enumerate(pathobj.Path.Commands): + for index, c in enumerate(pathobj.Path.Commands): outstring = [] command = c.Name - if index+1 == len(pathobj.Path.Commands): - nextcommand = "" + if index + 1 == len(pathobj.Path.Commands): + nextcommand = "" else: - nextcommand = pathobj.Path.Commands[index+1].Name + nextcommand = pathobj.Path.Commands[index + 1].Name if adaptiveOp and c.Name in ["G0", "G00"]: if opHorizRapid and opVertRapid: - command = 'G1' + command = "G1" else: - outstring.append('(TOOL CONTROLLER RAPID VALUES ARE UNSET)' + '\n') + outstring.append("(TOOL CONTROLLER RAPID VALUES ARE UNSET)" + "\n") # suppress moves in fixture selection if pathobj.Label == "Fixture": @@ -359,45 +438,82 @@ def parse(pathobj): # convert drill cycles to tap cycles if tool is a tap if command == "G81" or command == "G83": - if hasattr(pathobj, 'ToolController') and pathobj.ToolController.Tool.ToolType == "Tap": + if ( + hasattr(pathobj, "ToolController") + and pathobj.ToolController.Tool.ToolType == "Tap" + ): command = "G84" out += linenumber() + "G95\n" paramstring = "" - for param in [ "X", "Y" ]: + for param in ["X", "Y"]: if param in c.Parameters: - if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]): + if ( + (not OUTPUT_DOUBLES) + and (param in currLocation) + and (currLocation[param] == c.Parameters[param]) + ): continue else: - pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) - paramstring += " " + param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string) + pos = Units.Quantity( + c.Parameters[param], FreeCAD.Units.Length + ) + paramstring += ( + " " + + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), + precision_string, + ) + ) if paramstring != "": - out += linenumber() + "G00"+paramstring+"\n" + out += linenumber() + "G00" + paramstring + "\n" if "S" in c.Parameters: - tapSpeed = int(c.Parameters['S']) - out += "M29 S"+str(tapSpeed)+"\n" + tapSpeed = int(c.Parameters["S"]) + out += "M29 S" + str(tapSpeed) + "\n" - for param in [ "Z", "R" ]: + for param in ["Z", "R"]: if param in c.Parameters: - if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]): + if ( + (not OUTPUT_DOUBLES) + and (param in currLocation) + and (currLocation[param] == c.Parameters[param]) + ): continue else: - pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) - paramstring += " " + param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string) + pos = Units.Quantity( + c.Parameters[param], FreeCAD.Units.Length + ) + paramstring += ( + " " + + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), + precision_string, + ) + ) # in this mode, F is the distance per revolution of the thread (pitch) # P is the dwell time in seconds at the bottom of the thread # Q is the peck depth of the threading operation - for param in [ "F", "P", "Q" ]: + for param in ["F", "P", "Q"]: if param in c.Parameters: - value = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) - paramstring += " " + param + format(float(value.getValueAs(UNIT_FORMAT)), precision_string) + value = Units.Quantity( + c.Parameters[param], FreeCAD.Units.Length + ) + paramstring += ( + " " + + param + + format( + float(value.getValueAs(UNIT_FORMAT)), + precision_string, + ) + ) out += linenumber() + "G84" + paramstring + "\n" out += linenumber() + "G80\n" out += linenumber() + "G94\n" continue - outstring.append(command) # if modal: suppress the command if it is the same as the last one @@ -409,48 +525,83 @@ def parse(pathobj): if command == "G80" and lastcommand == nextcommand: continue - if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment + if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES): - if c.Name not in ["G0", "G00"]: # fanuc doesn't use rapid speeds - speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) + if param == "F" and ( + currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES + ): + if c.Name not in [ + "G0", + "G00", + ]: # fanuc doesn't use rapid speeds + speed = Units.Quantity( + c.Parameters["F"], FreeCAD.Units.Velocity + ) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: - outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) + outstring.append( + param + + format( + float(speed.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) else: continue - elif param == 'T': - outstring.append(param + str(int(c.Parameters['T']))) - elif param == 'H': - outstring.append(param + str(int(c.Parameters['H']))) - elif param == 'D': - outstring.append(param + str(int(c.Parameters['D']))) - elif param == 'S': - outstring.append(param + str(int(c.Parameters['S']))) + elif param == "T": + outstring.append(param + str(int(c.Parameters["T"]))) + elif param == "H": + outstring.append(param + str(int(c.Parameters["H"]))) + elif param == "D": + outstring.append(param + str(int(c.Parameters["D"]))) + elif param == "S": + outstring.append(param + str(int(c.Parameters["S"]))) else: - if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]): + if ( + (not OUTPUT_DOUBLES) + and (param in currLocation) + and (currLocation[param] == c.Parameters[param]) + ): continue else: - pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) + pos = Units.Quantity( + c.Parameters[param], FreeCAD.Units.Length + ) outstring.append( - param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), precision_string + ) + ) if adaptiveOp and c.Name in ["G0", "G00"]: if opHorizRapid and opVertRapid: - if 'Z' not in c.Parameters: - outstring.append('F' + format(float(opHorizRapid.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) + if "Z" not in c.Parameters: + outstring.append( + "F" + + format( + float(opHorizRapid.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) else: - outstring.append('F' + format(float(opVertRapid.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) + outstring.append( + "F" + + format( + float(opVertRapid.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: - if command == 'M6': + if command == "M6": # stop the spindle out += linenumber() + "M5\n" for line in TOOL_CHANGE.splitlines(True): @@ -458,7 +609,7 @@ def parse(pathobj): # add height offset if USE_TLO: - tool_height = '\nG43 H' + str(int(c.Parameters['T'])) + tool_height = "\nG43 H" + str(int(c.Parameters["T"])) outstring.append(tool_height) if command == "message": @@ -479,4 +630,5 @@ def parse(pathobj): return out + # print(__name__ + " gcode postprocessor loaded.") diff --git a/src/Mod/Path/PathScripts/post/grbl_post.py b/src/Mod/Path/PathScripts/post/grbl_post.py index 1d2c2f29ba..9c0eaa8011 100755 --- a/src/Mod/Path/PathScripts/post/grbl_post.py +++ b/src/Mod/Path/PathScripts/post/grbl_post.py @@ -580,7 +580,7 @@ def parse(pathobj): out += linenumber() + format_outstring(outstring) + "\n" # Check for comments containing machine-specific commands to pass literally to the controller - m = re.match(r'^\(MC_RUN_COMMAND: ([^)]+)\)$', command) + m = re.match(r"^\(MC_RUN_COMMAND: ([^)]+)\)$", command) if m: raw_command = m.group(1) out += linenumber() + raw_command + "\n" diff --git a/src/Mod/Path/PathScripts/post/heidenhain_post.py b/src/Mod/Path/PathScripts/post/heidenhain_post.py index 5443504566..9d94d002f5 100644 --- a/src/Mod/Path/PathScripts/post/heidenhain_post.py +++ b/src/Mod/Path/PathScripts/post/heidenhain_post.py @@ -1,22 +1,22 @@ # -*- coding: UTF-8 -*- -#*************************************************************************** -#* Copyright (C) 2020 Stefano Chiaro * -#* * -#* This library is free software; you can redistribute it and/or * -#* modify it under the terms of the GNU Lesser General Public * -#* License as published by the Free Software Foundation; either * -#* version 2.1 of the License, or (at your option) any later version. * -#* * -#* This library 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 * -#* Lesser General Public License for more details. * -#* * -#* You should have received a copy of the GNU Lesser General Public * -#* License along with this library; if not, write to the Free Software * -#* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * -#* 02110-1301 USA * -#*************************************************************************** +# *************************************************************************** +# * Copyright (C) 2020 Stefano Chiaro * +# * * +# * This library is free software; you can redistribute it and/or * +# * modify it under the terms of the GNU Lesser General Public * +# * License as published by the Free Software Foundation; either * +# * version 2.1 of the License, or (at your option) any later version. * +# * * +# * This library 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with this library; if not, write to the Free Software * +# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * +# * 02110-1301 USA * +# *************************************************************************** # HEDENHAIN Post-Processor for FreeCAD @@ -26,7 +26,7 @@ import PathScripts from PathScripts import PostUtils import math -#**************************************************************************# +# **************************************************************************# # USER EDITABLE STUFF HERE # # # # THESE VALUES SHOULD BE CHANGED TO FIT MACHINE TYPE AND LANGUAGE # @@ -43,38 +43,44 @@ MACHINE_USE_FMAX = False # Usage of FMAX # 'False' Print the value set on FEED_MAX_SPEED # Old machines don't accept FMAX and need a feed value -FEED_MAX_SPEED = 8000 # Max machine speed for FMAX +FEED_MAX_SPEED = 8000 # Max machine speed for FMAX # possible values: # integer >= 0 -AXIS_DECIMALS = 3 # machine axis precision +AXIS_DECIMALS = 3 # machine axis precision # possible values: # integer >= 0 -FEED_DECIMALS = 0 # machine feed precision +FEED_DECIMALS = 0 # machine feed precision # possible values: # integer >= 0 -SPINDLE_DECIMALS = 0 # machine spindle precision +SPINDLE_DECIMALS = 0 # machine spindle precision # possible values: # integer >= 0 -FIRST_LBL = 1 # first LBL number for LBLIZE function +FIRST_LBL = 1 # first LBL number for LBLIZE function # possible values: # integer >= 0 # TEMPLATES FOR CYCLE DEFINITION # MACHINE_CYCLE_DEF = { - 1: "CYCL DEF 1.0 FORATURA PROF." + "\n" - + "CYCL DEF 1.1 DIST" + "{DIST}\n" - + "CYCL DEF 1.2 PROF" + "{DEPTH}\n" - + "CYCL DEF 1.3 INCR" + "{INCR}\n" - + "CYCL DEF 1.4 SOSTA" + "{DWELL}\n" - + "CYCL DEF 1.5 F" + "{FEED}", + 1: "CYCL DEF 1.0 FORATURA PROF." + + "\n" + + "CYCL DEF 1.1 DIST" + + "{DIST}\n" + + "CYCL DEF 1.2 PROF" + + "{DEPTH}\n" + + "CYCL DEF 1.3 INCR" + + "{INCR}\n" + + "CYCL DEF 1.4 SOSTA" + + "{DWELL}\n" + + "CYCL DEF 1.5 F" + + "{FEED}", 2: "", - 7: "" - } + 7: "", +} # OPTIONAL COMPUTING # @@ -96,7 +102,7 @@ SKIP_WARNS = False # Skip post-processor warnings # STANDARD HEIDENHAIN VALUES FOR POSTPROCESSOR # -UNITS = 'MM' # post-processor units +UNITS = "MM" # post-processor units # possible values: # 'INCH' for inches # 'MM' for metric units @@ -117,62 +123,66 @@ LINENUMBER_INCREMENT = 1 # line number increment # POSTPROCESSOR VARIABLES AND CODE # -#**************************************************************************# +# **************************************************************************# # don't edit the stuff below this line unless you know what you're doing # -#**************************************************************************# +# **************************************************************************# # GCODE VARIABLES AND FUNCTIONS # -POSTGCODE = [] # Output string array +POSTGCODE = [] # Output string array G_FUNCTION_STORE = { - 'G90': False, 'G91': False, - 'G98': False, 'G99': False, - 'G81': False, 'G82': False, 'G83': False - } + "G90": False, + "G91": False, + "G98": False, + "G99": False, + "G81": False, + "G82": False, + "G83": False, +} # HEIDENHAIN MACHINE PARAMETERS # -MACHINE_WORK_AXIS = 2 # 0=X ; 1=Y ; 2=Z usually Z -MACHINE_SPINDLE_DIRECTION = 3 # CW = 3 ; CCW = 4 -MACHINE_LAST_POSITION = { # axis initial values to overwrite - 'X': 99999, - 'Y': 99999, - 'Z': 99999, - 'A': 99999, - 'B': 99999, - 'C': 99999 - } -MACHINE_LAST_CENTER = { # CC initial values to overwrite - 'X': 99999, - 'Y': 99999, - 'Z': 99999 - } -MACHINE_TRASL_ROT = [0, 0, 0, 0] # [X, Y, Z , Angle] !not implemented -MACHINE_STORED_PARAMS = ['', -1, ''] # Store R F M parameter to skip +MACHINE_WORK_AXIS = 2 # 0=X ; 1=Y ; 2=Z usually Z +MACHINE_SPINDLE_DIRECTION = 3 # CW = 3 ; CCW = 4 +MACHINE_LAST_POSITION = { # axis initial values to overwrite + "X": 99999, + "Y": 99999, + "Z": 99999, + "A": 99999, + "B": 99999, + "C": 99999, +} +MACHINE_LAST_CENTER = { # CC initial values to overwrite + "X": 99999, + "Y": 99999, + "Z": 99999, +} +MACHINE_TRASL_ROT = [0, 0, 0, 0] # [X, Y, Z , Angle] !not implemented +MACHINE_STORED_PARAMS = ["", -1, ""] # Store R F M parameter to skip # POSTPROCESSOR VARIABLES STORAGE # -STORED_COMPENSATED_OBJ = () # Store a copy of compensated path -STORED_CANNED_PARAMS = { # Store canned cycles for match - 'DIST': 99999, - 'DEPTH': 99999, - 'INCR': 99999, - 'DWELL': 99999, - 'FEED': 99999 - } -STORED_LBL = [] # Store array of LBL for match +STORED_COMPENSATED_OBJ = () # Store a copy of compensated path +STORED_CANNED_PARAMS = { # Store canned cycles for match + "DIST": 99999, + "DEPTH": 99999, + "INCR": 99999, + "DWELL": 99999, + "FEED": 99999, +} +STORED_LBL = [] # Store array of LBL for match # POSTPROCESSOR SPECIAL VARIABLES # -COMPENSATION_DIFF_STATUS = [False, True]# Check compensation, Check Diff -LBLIZE_ACTIVE = False # Check if search for LBL -LBLIZE_STAUS = False # Activated by path type -LBLIZE_PATH_LEVEL = 99999 # Save milling level of actual LBL +COMPENSATION_DIFF_STATUS = [False, True] # Check compensation, Check Diff +LBLIZE_ACTIVE = False # Check if search for LBL +LBLIZE_STAUS = False # Activated by path type +LBLIZE_PATH_LEVEL = 99999 # Save milling level of actual LBL # END OF POSTPROCESSOR VARIABLES # -#**************************************************************************# +# **************************************************************************# -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output real GCode suitable for a heidenhain 3 axis mill. This postprocessor, once placed @@ -181,26 +191,63 @@ FreeCAD, via the GUI importer or via python scripts with: import heidenhain_post heidenhain.export(object,"/path/to/file.ncc","") -''' +""" -parser = argparse.ArgumentParser(prog='heidenhain', add_help=False) -parser.add_argument('--skip-params', action='store_true', help='suppress R F M parameters where already stored') -parser.add_argument('--use-fmax', action='store_true', help='suppress feedrate and use FMAX instead with rapid movements') -parser.add_argument('--fmax-value', default='8000', help='feedrate to use instead of FMAX, default=8000') -parser.add_argument('--axis-decimals', default='3', help='number of digits of axis precision, default=3') -parser.add_argument('--feed-decimals', default='0', help='number of digits of feedrate precision, default=0') -parser.add_argument('--spindle-decimals', default='0', help='number of digits of spindle precision, default=0') -parser.add_argument('--solve-comp', action='store_true', help='try to get RL or RR real path where compensation is active') -parser.add_argument('--solve-lbl', action='store_true', help='try to replace repetitive movements with LBL') -parser.add_argument('--first-lbl', default='1', help='change the first LBL number, default=1') -parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output') -parser.add_argument('--no-warns', action='store_true', help='don\'t pop up post-processor warnings') +parser = argparse.ArgumentParser(prog="heidenhain", add_help=False) +parser.add_argument( + "--skip-params", + action="store_true", + help="suppress R F M parameters where already stored", +) +parser.add_argument( + "--use-fmax", + action="store_true", + help="suppress feedrate and use FMAX instead with rapid movements", +) +parser.add_argument( + "--fmax-value", default="8000", help="feedrate to use instead of FMAX, default=8000" +) +parser.add_argument( + "--axis-decimals", default="3", help="number of digits of axis precision, default=3" +) +parser.add_argument( + "--feed-decimals", + default="0", + help="number of digits of feedrate precision, default=0", +) +parser.add_argument( + "--spindle-decimals", + default="0", + help="number of digits of spindle precision, default=0", +) +parser.add_argument( + "--solve-comp", + action="store_true", + help="try to get RL or RR real path where compensation is active", +) +parser.add_argument( + "--solve-lbl", + action="store_true", + help="try to replace repetitive movements with LBL", +) +parser.add_argument( + "--first-lbl", default="1", help="change the first LBL number, default=1" +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) +parser.add_argument( + "--no-warns", action="store_true", help="don't pop up post-processor warnings" +) TOOLTIP_ARGS = parser.format_help() -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open + def processArguments(argstring): global MACHINE_SKIP_PARAMS global MACHINE_USE_FMAX @@ -243,6 +290,7 @@ def processArguments(argstring): return True + def export(objectslist, filename, argstring): if not processArguments(argstring): return None @@ -261,50 +309,65 @@ def export(objectslist, filename, argstring): Spindle_Active = False Compensation = "0" params = [ - 'X', 'Y', 'Z', - 'A', 'B', 'C', - 'I', 'J', 'K', - 'F', 'H', 'S', 'T', 'Q', 'R', 'L' - ] + "X", + "Y", + "Z", + "A", + "B", + "C", + "I", + "J", + "K", + "F", + "H", + "S", + "T", + "Q", + "R", + "L", + ] for obj in objectslist: if not hasattr(obj, "Path"): print( - "the object " + obj.Name + - " is not a path. Please select only path and Compounds." - ) + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return - POSTGCODE.append(HEIDEN_Begin(objectslist)) #add header + POSTGCODE.append(HEIDEN_Begin(objectslist)) # add header for obj in objectslist: - Cmd_Count = 0 # command line number + Cmd_Count = 0 # command line number LBLIZE_STAUS = False # useful to get idea of object kind - if isinstance (obj.Proxy, PathScripts.PathToolController.ToolController): + if isinstance(obj.Proxy, PathScripts.PathToolController.ToolController): Object_Kind = "TOOL" # like we go to change tool position - MACHINE_LAST_POSITION['X'] = 99999 - MACHINE_LAST_POSITION['Y'] = 99999 - MACHINE_LAST_POSITION['Z'] = 99999 - elif isinstance (obj.Proxy, PathScripts.PathProfileEdges.ObjectProfile): + MACHINE_LAST_POSITION["X"] = 99999 + MACHINE_LAST_POSITION["Y"] = 99999 + MACHINE_LAST_POSITION["Z"] = 99999 + elif isinstance(obj.Proxy, PathScripts.PathProfileEdges.ObjectProfile): Object_Kind = "PROFILE" - if LBLIZE_ACTIVE: LBLIZE_STAUS = True - elif isinstance (obj.Proxy, PathScripts.PathMillFace.ObjectFace): + if LBLIZE_ACTIVE: + LBLIZE_STAUS = True + elif isinstance(obj.Proxy, PathScripts.PathMillFace.ObjectFace): Object_Kind = "FACE" - if LBLIZE_ACTIVE: LBLIZE_STAUS = True - elif isinstance (obj.Proxy, PathScripts.PathHelix.ObjectHelix): + if LBLIZE_ACTIVE: + LBLIZE_STAUS = True + elif isinstance(obj.Proxy, PathScripts.PathHelix.ObjectHelix): Object_Kind = "HELIX" # If used compensated path, store, recompute and diff when asked - if hasattr(obj, 'UseComp') and SOLVE_COMPENSATION_ACTIVE: + if hasattr(obj, "UseComp") and SOLVE_COMPENSATION_ACTIVE: if obj.UseComp: - if hasattr(obj.Path, 'Commands') and Object_Kind == "PROFILE": + if hasattr(obj.Path, "Commands") and Object_Kind == "PROFILE": # Take a copy of compensated path STORED_COMPENSATED_OBJ = obj.Path.Commands # Find mill compensation - if hasattr(obj, 'Side') and hasattr(obj, 'Direction'): + if hasattr(obj, "Side") and hasattr(obj, "Direction"): if obj.Side == "Outside" and obj.Direction == "CW": Compensation = "L" elif obj.Side == "Outside" and obj.Direction == "CCW": @@ -318,35 +381,42 @@ def export(objectslist, filename, argstring): obj.recompute() # small edges could be skipped and movements joints can add edges NameStr = "" - if hasattr(obj, 'Label'): NameStr = str(obj.Label) + if hasattr(obj, "Label"): + NameStr = str(obj.Label) if len(obj.Path.Commands) != len(STORED_COMPENSATED_OBJ): # not same number of edges obj.UseComp = True obj.recompute() POSTGCODE.append("; MISSING EDGES UNABLE TO GET COMPENSATION") - if not SKIP_WARNS: ( - PostUtils.editor( - "--solve-comp command ACTIVE\n\n" + - "UNABLE to solve " + NameStr + " compensation\n\n" + - "Some edges are missing\n" + - "try to change Join Type to Miter or Square\n" + - "try to use a smaller Tool Diameter\n" + - "Internal Path could have too small corners\n\n" + - "use --no-warns to not prompt this message" + if not SKIP_WARNS: + ( + PostUtils.editor( + "--solve-comp command ACTIVE\n\n" + + "UNABLE to solve " + + NameStr + + " compensation\n\n" + + "Some edges are missing\n" + + "try to change Join Type to Miter or Square\n" + + "try to use a smaller Tool Diameter\n" + + "Internal Path could have too small corners\n\n" + + "use --no-warns to not prompt this message" + ) ) - ) else: - if not SKIP_WARNS: ( - PostUtils.editor( - "--solve-comp command ACTIVE\n\n" + - "BE CAREFUL with solved " + NameStr + " compensation\n\n" + - "USE AT YOUR OWN RISK\n" + - "Simulate it before use\n" + - "Offset Extra ignored use DR+ on TOOL CALL\n" + - "Path could be different and/or give tool radius errors\n\n" + - "use --no-warns to not prompt this message" + if not SKIP_WARNS: + ( + PostUtils.editor( + "--solve-comp command ACTIVE\n\n" + + "BE CAREFUL with solved " + + NameStr + + " compensation\n\n" + + "USE AT YOUR OWN RISK\n" + + "Simulate it before use\n" + + "Offset Extra ignored use DR+ on TOOL CALL\n" + + "Path could be different and/or give tool radius errors\n\n" + + "use --no-warns to not prompt this message" + ) ) - ) # we can try to solve compensation POSTGCODE.append("; COMPENSATION ACTIVE") COMPENSATION_DIFF_STATUS[0] = True @@ -354,81 +424,84 @@ def export(objectslist, filename, argstring): for c in obj.Path.Commands: Cmd_Count += 1 command = c.Name - if command != 'G0': - command = command.replace('G0','G') # normalize: G01 -> G1 + if command != "G0": + command = command.replace("G0", "G") # normalize: G01 -> G1 for param in params: if param in c.Parameters: - if param == 'F': - Feed = c.Parameters['F'] + if param == "F": + Feed = c.Parameters["F"] - if command == 'G90': - G_FUNCTION_STORE['G90'] = True - G_FUNCTION_STORE['G91'] = False + if command == "G90": + G_FUNCTION_STORE["G90"] = True + G_FUNCTION_STORE["G91"] = False - if command == 'G91': - G_FUNCTION_STORE['G91'] = True - G_FUNCTION_STORE['G90'] = False + if command == "G91": + G_FUNCTION_STORE["G91"] = True + G_FUNCTION_STORE["G90"] = False - if command == 'G98': - G_FUNCTION_STORE['G98'] = True - G_FUNCTION_STORE['G99'] = False + if command == "G98": + G_FUNCTION_STORE["G98"] = True + G_FUNCTION_STORE["G99"] = False - if command == 'G99': - G_FUNCTION_STORE['G99'] = True - G_FUNCTION_STORE['G98'] = False + if command == "G99": + G_FUNCTION_STORE["G99"] = True + G_FUNCTION_STORE["G98"] = False # Rapid movement - if command == 'G0': + if command == "G0": Spindle_Status = "" - if Spindle_Active == False: # At first rapid movement we turn on spindle - Spindle_Status += str(MACHINE_SPINDLE_DIRECTION) # Activate spindle + if ( + Spindle_Active == False + ): # At first rapid movement we turn on spindle + Spindle_Status += str(MACHINE_SPINDLE_DIRECTION) # Activate spindle Spindle_Active = True - else: # At last rapid movement we turn off spindle + else: # At last rapid movement we turn off spindle if Cmd_Count == len(obj.Path.Commands): - Spindle_Status += "5" # Deactivate spindle + Spindle_Status += "5" # Deactivate spindle Spindle_Active = False else: - Spindle_Status += "" # Spindle still active + Spindle_Status += "" # Spindle still active parsedElem = HEIDEN_Line( c.Parameters, Compensation, Feed, True, Spindle_Status, Cmd_Count - ) - if parsedElem != None: POSTGCODE.append(parsedElem) + ) + if parsedElem != None: + POSTGCODE.append(parsedElem) - # Linear movement - if command == 'G1': + # Linear movement + if command == "G1": parsedElem = HEIDEN_Line( c.Parameters, Compensation, Feed, False, "", Cmd_Count - ) - if parsedElem != None: POSTGCODE.append(parsedElem) + ) + if parsedElem != None: + POSTGCODE.append(parsedElem) # Arc movement - if command == 'G2' or command == 'G3': + if command == "G2" or command == "G3": parsedElem = HEIDEN_Arc( c.Parameters, command, Compensation, Feed, False, "", Cmd_Count - ) + ) if parsedElem != None: POSTGCODE.extend(parsedElem) - if command == 'G80': #Reset Canned Cycles - G_FUNCTION_STORE['G81'] = False - G_FUNCTION_STORE['G82'] = False - G_FUNCTION_STORE['G83'] = False + if command == "G80": # Reset Canned Cycles + G_FUNCTION_STORE["G81"] = False + G_FUNCTION_STORE["G82"] = False + G_FUNCTION_STORE["G83"] = False # Drilling, Dwell Drilling, Peck Drilling - if command == 'G81' or command == 'G82' or command == 'G83': - parsedElem = HEIDEN_Drill( - obj, c.Parameters, command, Feed - ) + if command == "G81" or command == "G82" or command == "G83": + parsedElem = HEIDEN_Drill(obj, c.Parameters, command, Feed) if parsedElem != None: POSTGCODE.extend(parsedElem) # Tool change - if command == 'M6': + if command == "M6": parsedElem = HEIDEN_ToolCall(obj) - if parsedElem != None: POSTGCODE.append(parsedElem) + if parsedElem != None: + POSTGCODE.append(parsedElem) - if COMPENSATION_DIFF_STATUS[0]: #Restore the compensation if removed + if COMPENSATION_DIFF_STATUS[0]: # Restore the compensation if removed obj.UseComp = True obj.recompute() COMPENSATION_DIFF_STATUS[0] = False @@ -438,26 +511,29 @@ def export(objectslist, filename, argstring): LBLIZE_STAUS = False if LBLIZE_ACTIVE: - if not SKIP_WARNS: ( - PostUtils.editor( - "--solve-lbl command ACTIVE\n\n" + - "BE CAREFUL with LBL replacements\n\n" + - "USE AT YOUR OWN RISK\n" + - "Simulate it before use\n" + - "Path could be different and/or give errors\n\n" + - "use --no-warns to not prompt this message" + if not SKIP_WARNS: + ( + PostUtils.editor( + "--solve-lbl command ACTIVE\n\n" + + "BE CAREFUL with LBL replacements\n\n" + + "USE AT YOUR OWN RISK\n" + + "Simulate it before use\n" + + "Path could be different and/or give errors\n\n" + + "use --no-warns to not prompt this message" + ) ) - ) - POSTGCODE.append(HEIDEN_End(objectslist)) #add footer - Program_Out = HEIDEN_Numberize(POSTGCODE) #add line number + POSTGCODE.append(HEIDEN_End(objectslist)) # add footer + Program_Out = HEIDEN_Numberize(POSTGCODE) # add line number - if SHOW_EDITOR: PostUtils.editor(Program_Out) + if SHOW_EDITOR: + PostUtils.editor(Program_Out) gfile = pythonopen(filename, "w") gfile.write(Program_Out) gfile.close() -def HEIDEN_Begin(ActualJob): #use Label for program name + +def HEIDEN_Begin(ActualJob): # use Label for program name global UNITS # JobParent = PathUtils.findParentJob(ActualJob[0]) # if hasattr(JobParent, "Label"): @@ -466,7 +542,8 @@ def HEIDEN_Begin(ActualJob): #use Label for program name # program_id = "NEW" return "BEGIN PGM {}".format(UNITS) -def HEIDEN_End(ActualJob): #use Label for program name + +def HEIDEN_End(ActualJob): # use Label for program name global UNITS # JobParent = PathUtils.findParentJob(ActualJob[0]) # if hasattr(JobParent, "Label"): @@ -475,9 +552,11 @@ def HEIDEN_End(ActualJob): #use Label for program name # program_id = "NEW" return "END PGM {}".format(UNITS) -#def HEIDEN_ToolDef(tool_id, tool_length, tool_radius): # old machines don't have tool table, need tooldef list + +# def HEIDEN_ToolDef(tool_id, tool_length, tool_radius): # old machines don't have tool table, need tooldef list # return "TOOL DEF " + tool_id + " R" + "{:.3f}".format(tool_length) + " L" + "{:.3f}".format(tool_radius) + def HEIDEN_ToolCall(tool_Params): global MACHINE_SPINDLE_DIRECTION global MACHINE_WORK_AXIS @@ -486,28 +565,36 @@ def HEIDEN_ToolCall(tool_Params): H_Tool_Speed = 0 H_Tool_Comment = "" - if hasattr(tool_Params, "Label"): # use Label as tool comment + if hasattr(tool_Params, "Label"): # use Label as tool comment H_Tool_Comment = tool_Params.Label - if hasattr(tool_Params, "SpindleDir"): # get spindle direction for this tool + if hasattr(tool_Params, "SpindleDir"): # get spindle direction for this tool if tool_Params.SpindleDir == "Forward": MACHINE_SPINDLE_DIRECTION = 3 else: MACHINE_SPINDLE_DIRECTION = 4 - if hasattr(tool_Params, "SpindleSpeed"): # get tool speed for spindle + if hasattr(tool_Params, "SpindleSpeed"): # get tool speed for spindle H_Tool_Speed = tool_Params.SpindleSpeed - if hasattr(tool_Params, "ToolNumber"): # use ToolNumber for tool id + if hasattr(tool_Params, "ToolNumber"): # use ToolNumber for tool id H_Tool_ID = tool_Params.ToolNumber if H_Tool_ID == "0" and H_Tool_Speed == 0 and H_Tool_Comment == "": return None else: - return( - "TOOL CALL " + str(H_Tool_ID) + " " + H_Tool_Axis[MACHINE_WORK_AXIS] + - HEIDEN_Format(" S", H_Tool_Speed) + " ;" + str(H_Tool_Comment) - ) + return ( + "TOOL CALL " + + str(H_Tool_ID) + + " " + + H_Tool_Axis[MACHINE_WORK_AXIS] + + HEIDEN_Format(" S", H_Tool_Speed) + + " ;" + + str(H_Tool_Comment) + ) + # create a linear movement -def HEIDEN_Line(line_Params, line_comp, line_feed, line_rapid, line_M_funct, Cmd_Number): +def HEIDEN_Line( + line_Params, line_comp, line_feed, line_rapid, line_M_funct, Cmd_Number +): global FEED_MAX_SPEED global COMPENSATION_DIFF_STATUS global G_FUNCTION_STORE @@ -516,51 +603,61 @@ def HEIDEN_Line(line_Params, line_comp, line_feed, line_rapid, line_M_funct, Cmd global MACHINE_STORED_PARAMS global MACHINE_SKIP_PARAMS global MACHINE_USE_FMAX - H_Line_New = { # axis initial values to overwrite - 'X': 99999, - 'Y': 99999, - 'Z': 99999, - 'A': 99999, - 'B': 99999, - 'C': 99999 - } + H_Line_New = { # axis initial values to overwrite + "X": 99999, + "Y": 99999, + "Z": 99999, + "A": 99999, + "B": 99999, + "C": 99999, + } H_Line = "L" - H_Line_Params = [['X', 'Y', 'Z'], ['R0', line_feed, line_M_funct]] + H_Line_Params = [["X", "Y", "Z"], ["R0", line_feed, line_M_funct]] # check and hide duplicated axis movements, not last, update with new ones for i in H_Line_New: - if G_FUNCTION_STORE['G91']: # incremental + if G_FUNCTION_STORE["G91"]: # incremental H_Line_New[i] = 0 if i in line_Params: - if line_Params[i] != 0 or line_M_funct != '': - H_Line += " I" + HEIDEN_Format(i, line_Params[i]) # print incremental + if line_Params[i] != 0 or line_M_funct != "": + H_Line += " I" + HEIDEN_Format( + i, line_Params[i] + ) # print incremental # update to absolute position H_Line_New[i] = MACHINE_LAST_POSITION[i] + H_Line_New[i] - else: #absolute + else: # absolute H_Line_New[i] = MACHINE_LAST_POSITION[i] if i in line_Params: - if line_Params[i] != H_Line_New[i] or line_M_funct != '': + if line_Params[i] != H_Line_New[i] or line_M_funct != "": H_Line += " " + HEIDEN_Format(i, line_Params[i]) H_Line_New[i] = line_Params[i] - if H_Line == "L": #No movements no line + if H_Line == "L": # No movements no line return None - if COMPENSATION_DIFF_STATUS[0]: # Diff from compensated ad not compensated path - if COMPENSATION_DIFF_STATUS[1]: # skip if already compensated, not active by now - Cmd_Number -= 1 # align + if COMPENSATION_DIFF_STATUS[0]: # Diff from compensated ad not compensated path + if COMPENSATION_DIFF_STATUS[ + 1 + ]: # skip if already compensated, not active by now + Cmd_Number -= 1 # align # initialize like true, set false if not same point compensated and not compensated i = True for j in H_Line_Params[0]: - if j in STORED_COMPENSATED_OBJ[Cmd_Number].Parameters and j in line_Params: - if STORED_COMPENSATED_OBJ[Cmd_Number].Parameters[j] != line_Params[j]: + if ( + j in STORED_COMPENSATED_OBJ[Cmd_Number].Parameters + and j in line_Params + ): + if ( + STORED_COMPENSATED_OBJ[Cmd_Number].Parameters[j] + != line_Params[j] + ): i = False if i == False: H_Line_Params[1][0] = "R" + line_comp -# we can skip this control if already in compensation -# COMPENSATION_DIFF_STATUS[1] = False + # we can skip this control if already in compensation + # COMPENSATION_DIFF_STATUS[1] = False else: - H_Line_Params[1][0] = "R" + line_comp # not used by now + H_Line_Params[1][0] = "R" + line_comp # not used by now # check if we need to skip already active parameters # R parameter @@ -569,11 +666,15 @@ def HEIDEN_Line(line_Params, line_comp, line_feed, line_rapid, line_M_funct, Cmd H_Line += " " + H_Line_Params[1][0] # F parameter (check rapid o feed) - if line_rapid == True: H_Line_Params[1][1] = FEED_MAX_SPEED + if line_rapid == True: + H_Line_Params[1][1] = FEED_MAX_SPEED if MACHINE_USE_FMAX == True and line_rapid == True: H_Line += " FMAX" else: - if MACHINE_SKIP_PARAMS == False or H_Line_Params[1][1] != MACHINE_STORED_PARAMS[1]: + if ( + MACHINE_SKIP_PARAMS == False + or H_Line_Params[1][1] != MACHINE_STORED_PARAMS[1] + ): MACHINE_STORED_PARAMS[1] = H_Line_Params[1][1] H_Line += HEIDEN_Format(" F", H_Line_Params[1][1]) @@ -597,8 +698,11 @@ def HEIDEN_Line(line_Params, line_comp, line_feed, line_rapid, line_M_funct, Cmd return H_Line + # create a arc movement -def HEIDEN_Arc(arc_Params, arc_direction, arc_comp, arc_feed, arc_rapid, arc_M_funct, Cmd_Number): +def HEIDEN_Arc( + arc_Params, arc_direction, arc_comp, arc_feed, arc_rapid, arc_M_funct, Cmd_Number +): global FEED_MAX_SPEED global COMPENSATION_DIFF_STATUS global G_FUNCTION_STORE @@ -613,20 +717,16 @@ def HEIDEN_Arc(arc_Params, arc_direction, arc_comp, arc_feed, arc_rapid, arc_M_f H_ArcIncr = "" H_ArcCenter = "CC " H_ArcPoint = "C" - H_Arc_Params = [['X', 'Y', 'Z'], ['R0', arc_feed, arc_M_funct], ['I', 'J', 'K']] - H_Arc_CC = { # CC initial values to overwrite - 'X': 99999, - 'Y': 99999, - 'Z': 99999 - } - H_Arc_P_NEW = { # end point initial values to overwrite - 'X': 99999, - 'Y': 99999, - 'Z': 99999 - } + H_Arc_Params = [["X", "Y", "Z"], ["R0", arc_feed, arc_M_funct], ["I", "J", "K"]] + H_Arc_CC = {"X": 99999, "Y": 99999, "Z": 99999} # CC initial values to overwrite + H_Arc_P_NEW = { # end point initial values to overwrite + "X": 99999, + "Y": 99999, + "Z": 99999, + } # get command values - if G_FUNCTION_STORE['G91']: # incremental + if G_FUNCTION_STORE["G91"]: # incremental H_ArcIncr = "I" for i in range(0, 3): a = H_Arc_Params[0][i] @@ -642,7 +742,7 @@ def HEIDEN_Arc(arc_Params, arc_direction, arc_comp, arc_feed, arc_rapid, arc_M_f H_Arc_CC[a] = arc_Params[b] else: H_Arc_CC[a] = 0 - else: # absolute + else: # absolute for i in range(0, 3): a = H_Arc_Params[0][i] b = H_Arc_Params[2][i] @@ -660,55 +760,74 @@ def HEIDEN_Arc(arc_Params, arc_direction, arc_comp, arc_feed, arc_rapid, arc_M_f def Axis_Select(a, b, c, incr): if a in arc_Params and b in arc_Params: _H_ArcCenter = ( - incr + HEIDEN_Format(a, H_Arc_CC[a]) + " " + - incr + HEIDEN_Format(b, H_Arc_CC[b]) - ) + incr + + HEIDEN_Format(a, H_Arc_CC[a]) + + " " + + incr + + HEIDEN_Format(b, H_Arc_CC[b]) + ) if c in arc_Params and arc_Params[c] != MACHINE_LAST_POSITION[c]: # if there are 3 axis movements it need to be polar arc _H_ArcPoint = HEIDEN_PolarArc( - H_Arc_CC[a], H_Arc_CC[b], H_Arc_P_NEW[a], H_Arc_P_NEW[b], arc_Params[c], c, incr - ) + H_Arc_CC[a], + H_Arc_CC[b], + H_Arc_P_NEW[a], + H_Arc_P_NEW[b], + arc_Params[c], + c, + incr, + ) else: _H_ArcPoint = ( - " " + incr + HEIDEN_Format(a, H_Arc_P_NEW[a]) + - " " + incr + HEIDEN_Format(b, H_Arc_P_NEW[b]) - ) + " " + + incr + + HEIDEN_Format(a, H_Arc_P_NEW[a]) + + " " + + incr + + HEIDEN_Format(b, H_Arc_P_NEW[b]) + ) return [_H_ArcCenter, _H_ArcPoint] else: return ["", ""] # set the right work plane based on tool direction - if MACHINE_WORK_AXIS == 0: # tool on X axis - Axis_Result = Axis_Select('Y', 'Z', 'X', H_ArcIncr) - elif MACHINE_WORK_AXIS == 1: # tool on Y axis - Axis_Result = Axis_Select('X', 'Z', 'Y', H_ArcIncr) - elif MACHINE_WORK_AXIS == 2: # tool on Z axis - Axis_Result = Axis_Select('X', 'Y', 'Z', H_ArcIncr) + if MACHINE_WORK_AXIS == 0: # tool on X axis + Axis_Result = Axis_Select("Y", "Z", "X", H_ArcIncr) + elif MACHINE_WORK_AXIS == 1: # tool on Y axis + Axis_Result = Axis_Select("X", "Z", "Y", H_ArcIncr) + elif MACHINE_WORK_AXIS == 2: # tool on Z axis + Axis_Result = Axis_Select("X", "Y", "Z", H_ArcIncr) # and fill with values H_ArcCenter += Axis_Result[0] H_ArcPoint += Axis_Result[1] - if H_ArcCenter == "CC ": #No movements no circle + if H_ArcCenter == "CC ": # No movements no circle return None - if arc_direction == "G2": # set the right arc direction + if arc_direction == "G2": # set the right arc direction H_ArcPoint += " DR-" else: H_ArcPoint += " DR+" - if COMPENSATION_DIFF_STATUS[0]: # Diff from compensated ad not compensated path - if COMPENSATION_DIFF_STATUS[1]: # skip if already compensated - Cmd_Number -= 1 # align + if COMPENSATION_DIFF_STATUS[0]: # Diff from compensated ad not compensated path + if COMPENSATION_DIFF_STATUS[1]: # skip if already compensated + Cmd_Number -= 1 # align i = True for j in H_Arc_Params[0]: - if j in STORED_COMPENSATED_OBJ[Cmd_Number].Parameters and j in arc_Params: - if STORED_COMPENSATED_OBJ[Cmd_Number].Parameters[j] != arc_Params[j]: + if ( + j in STORED_COMPENSATED_OBJ[Cmd_Number].Parameters + and j in arc_Params + ): + if ( + STORED_COMPENSATED_OBJ[Cmd_Number].Parameters[j] + != arc_Params[j] + ): i = False if i == False: H_Arc_Params[1][0] = "R" + arc_comp -# COMPENSATION_DIFF_STATUS[1] = False # we can skip this control if already in compensation + # COMPENSATION_DIFF_STATUS[1] = False # we can skip this control if already in compensation else: - H_Arc_Params[1][0] = "R" + arc_comp # not used by now + H_Arc_Params[1][0] = "R" + arc_comp # not used by now # check if we need to skip already active parameters @@ -718,11 +837,15 @@ def HEIDEN_Arc(arc_Params, arc_direction, arc_comp, arc_feed, arc_rapid, arc_M_f H_ArcPoint += " " + H_Arc_Params[1][0] # F parameter - if arc_rapid == True: H_Arc_Params[1][1] = FEED_MAX_SPEED + if arc_rapid == True: + H_Arc_Params[1][1] = FEED_MAX_SPEED if MACHINE_USE_FMAX == True and arc_rapid == True: H_ArcPoint += " FMAX" else: - if MACHINE_SKIP_PARAMS == False or H_Arc_Params[1][1] != MACHINE_STORED_PARAMS[1]: + if ( + MACHINE_SKIP_PARAMS == False + or H_Arc_Params[1][1] != MACHINE_STORED_PARAMS[1] + ): MACHINE_STORED_PARAMS[1] = H_Arc_Params[1][1] H_ArcPoint += HEIDEN_Format(" F", H_Arc_Params[1][1]) @@ -732,16 +855,16 @@ def HEIDEN_Arc(arc_Params, arc_direction, arc_comp, arc_feed, arc_rapid, arc_M_f H_ArcPoint += " M" + H_Arc_Params[1][2] # update values to absolute if are incremental before store - if G_FUNCTION_STORE['G91']: # incremental + if G_FUNCTION_STORE["G91"]: # incremental for i in H_Arc_Params[0]: H_Arc_P_NEW[i] = MACHINE_LAST_POSITION[i] + H_Arc_P_NEW[i] H_Arc_CC[i] = MACHINE_LAST_POSITION[i] + H_Arc_CC[i] # check if we can skip CC print - if( - MACHINE_LAST_CENTER['X'] == H_Arc_CC['X'] and - MACHINE_LAST_CENTER['Y'] == H_Arc_CC['Y'] and - MACHINE_LAST_CENTER['Z'] == H_Arc_CC['Z'] + if ( + MACHINE_LAST_CENTER["X"] == H_Arc_CC["X"] + and MACHINE_LAST_CENTER["Y"] == H_Arc_CC["Y"] + and MACHINE_LAST_CENTER["Z"] == H_Arc_CC["Z"] ): H_ArcSameCenter = True @@ -768,6 +891,7 @@ def HEIDEN_Arc(arc_Params, arc_direction, arc_comp, arc_feed, arc_rapid, arc_M_f else: return [H_ArcCenter, H_ArcPoint] + def HEIDEN_PolarArc(pol_cc_X, pol_cc_Y, pol_X, pol_Y, pol_Z, pol_Axis, pol_Incr): pol_Angle = 0 pol_Result = "" @@ -792,60 +916,73 @@ def HEIDEN_PolarArc(pol_cc_X, pol_cc_Y, pol_X, pol_Y, pol_Z, pol_Axis, pol_Incr) # set inside +0° +360° range if pol_Angle > 0: - if pol_Angle >= 360: pol_Angle = pol_Angle - 360 + if pol_Angle >= 360: + pol_Angle = pol_Angle - 360 elif pol_Angle < 0: pol_Angle = pol_Angle + 360 - if pol_Angle < 0: pol_Angle = pol_Angle + 360 + if pol_Angle < 0: + pol_Angle = pol_Angle + 360 - pol_Result = "P" + HEIDEN_Format(" PA+", pol_Angle) + " " + pol_Incr + HEIDEN_Format(pol_Axis, pol_Z) + pol_Result = ( + "P" + + HEIDEN_Format(" PA+", pol_Angle) + + " " + + pol_Incr + + HEIDEN_Format(pol_Axis, pol_Z) + ) return pol_Result -def HEIDEN_Drill(drill_Obj, drill_Params, drill_Type, drill_feed): # create a drill cycle and movement + +def HEIDEN_Drill( + drill_Obj, drill_Params, drill_Type, drill_feed +): # create a drill cycle and movement global FEED_MAX_SPEED global MACHINE_WORK_AXIS global MACHINE_LAST_POSITION global MACHINE_USE_FMAX global G_FUNCTION_STORE global STORED_CANNED_PARAMS - drill_Defs = {'DIST': 0, 'DEPTH': 0, 'INCR': 0, 'DWELL': 0, 'FEED': drill_feed} + drill_Defs = {"DIST": 0, "DEPTH": 0, "INCR": 0, "DWELL": 0, "FEED": drill_feed} drill_Output = [] drill_Surface = None drill_SafePoint = 0 drill_StartPoint = 0 # try to get the distance from Clearance Height and Start Depth - if hasattr(drill_Obj, 'StartDepth') and hasattr(drill_Obj.StartDepth, 'Value'): + if hasattr(drill_Obj, "StartDepth") and hasattr(drill_Obj.StartDepth, "Value"): drill_Surface = drill_Obj.StartDepth.Value # initialize - if 'R' in drill_Params: + if "R" in drill_Params: # SafePoint equals to R position - if G_FUNCTION_STORE['G91']: # incremental - drill_SafePoint = drill_Params['R'] + MACHINE_LAST_POSITION['Z'] + if G_FUNCTION_STORE["G91"]: # incremental + drill_SafePoint = drill_Params["R"] + MACHINE_LAST_POSITION["Z"] else: - drill_SafePoint = drill_Params['R'] + drill_SafePoint = drill_Params["R"] # Surface equals to theoric start point of drilling if drill_Surface != None and drill_SafePoint > drill_Surface: - drill_Defs['DIST'] = drill_Surface - drill_SafePoint + drill_Defs["DIST"] = drill_Surface - drill_SafePoint drill_StartPoint = drill_Surface else: - drill_Defs['DIST'] = 0 + drill_Defs["DIST"] = 0 drill_StartPoint = drill_SafePoint - if 'Z' in drill_Params: - if G_FUNCTION_STORE['G91']: # incremental - drill_Defs['DEPTH'] = drill_SafePoint + drill_Params['Z'] + if "Z" in drill_Params: + if G_FUNCTION_STORE["G91"]: # incremental + drill_Defs["DEPTH"] = drill_SafePoint + drill_Params["Z"] else: - drill_Defs['DEPTH'] = drill_Params['Z'] - drill_StartPoint + drill_Defs["DEPTH"] = drill_Params["Z"] - drill_StartPoint - if drill_Defs['DEPTH'] > 0: + if drill_Defs["DEPTH"] > 0: drill_Output.append("; WARNING START DEPTH LOWER THAN FINAL DEPTH") - drill_Defs['INCR'] = drill_Defs['DEPTH'] + drill_Defs["INCR"] = drill_Defs["DEPTH"] # overwrite - if 'P' in drill_Params: drill_Defs['DWELL'] = drill_Params['P'] - if 'Q' in drill_Params: drill_Defs['INCR'] = drill_Params['Q'] + if "P" in drill_Params: + drill_Defs["DWELL"] = drill_Params["P"] + if "Q" in drill_Params: + drill_Defs["INCR"] = drill_Params["Q"] # set the parameters for rapid movements if MACHINE_USE_FMAX: @@ -861,62 +998,65 @@ def HEIDEN_Drill(drill_Obj, drill_Params, drill_Type, drill_feed): # create a dr # move to drill location drill_Movement = "L" - if G_FUNCTION_STORE['G91']: # incremental + if G_FUNCTION_STORE["G91"]: # incremental # update Z value to R + actual_Z if first call if G_FUNCTION_STORE[drill_Type] == False: - MACHINE_LAST_POSITION['Z'] = drill_Defs['DIST'] + MACHINE_LAST_POSITION['Z'] - drill_Movement = "L" + HEIDEN_Format(" Z", MACHINE_LAST_POSITION['Z']) + drill_Rapid + MACHINE_LAST_POSITION["Z"] = drill_Defs["DIST"] + MACHINE_LAST_POSITION["Z"] + drill_Movement = ( + "L" + HEIDEN_Format(" Z", MACHINE_LAST_POSITION["Z"]) + drill_Rapid + ) drill_Output.append(drill_Movement) # update X and Y position - if 'X' in drill_Params and drill_Params['X'] != 0: - MACHINE_LAST_POSITION['X'] = drill_Params['X'] + MACHINE_LAST_POSITION['X'] - drill_Movement += HEIDEN_Format(" X", MACHINE_LAST_POSITION['X']) - if 'Y' in drill_Params and drill_Params['Y'] != 0: - MACHINE_LAST_POSITION['Y'] = drill_Params['Y'] + MACHINE_LAST_POSITION['Y'] - drill_Movement += HEIDEN_Format(" Y", MACHINE_LAST_POSITION['Y']) - if drill_Movement != "L": # same location + if "X" in drill_Params and drill_Params["X"] != 0: + MACHINE_LAST_POSITION["X"] = drill_Params["X"] + MACHINE_LAST_POSITION["X"] + drill_Movement += HEIDEN_Format(" X", MACHINE_LAST_POSITION["X"]) + if "Y" in drill_Params and drill_Params["Y"] != 0: + MACHINE_LAST_POSITION["Y"] = drill_Params["Y"] + MACHINE_LAST_POSITION["Y"] + drill_Movement += HEIDEN_Format(" Y", MACHINE_LAST_POSITION["Y"]) + if drill_Movement != "L": # same location drill_Output.append(drill_Movement + drill_Rapid) - else: # not incremental + else: # not incremental # check if R is higher than actual Z and move if needed - if drill_SafePoint > MACHINE_LAST_POSITION['Z']: + if drill_SafePoint > MACHINE_LAST_POSITION["Z"]: drill_Movement = "L" + HEIDEN_Format(" Z", drill_SafePoint) + drill_Rapid drill_Output.append(drill_Movement) - MACHINE_LAST_POSITION['Z'] = drill_SafePoint + MACHINE_LAST_POSITION["Z"] = drill_SafePoint # update X and Y position - if 'X' in drill_Params and drill_Params['X'] != MACHINE_LAST_POSITION['X']: - MACHINE_LAST_POSITION['X'] = drill_Params['X'] - drill_Movement += HEIDEN_Format(" X", MACHINE_LAST_POSITION['X']) - if 'Y' in drill_Params and drill_Params['Y'] != MACHINE_LAST_POSITION['Y']: - MACHINE_LAST_POSITION['Y'] = drill_Params['Y'] - drill_Movement += HEIDEN_Format(" Y", MACHINE_LAST_POSITION['Y']) - if drill_Movement != "L": # same location + if "X" in drill_Params and drill_Params["X"] != MACHINE_LAST_POSITION["X"]: + MACHINE_LAST_POSITION["X"] = drill_Params["X"] + drill_Movement += HEIDEN_Format(" X", MACHINE_LAST_POSITION["X"]) + if "Y" in drill_Params and drill_Params["Y"] != MACHINE_LAST_POSITION["Y"]: + MACHINE_LAST_POSITION["Y"] = drill_Params["Y"] + drill_Movement += HEIDEN_Format(" Y", MACHINE_LAST_POSITION["Y"]) + if drill_Movement != "L": # same location drill_Output.append(drill_Movement + drill_Rapid) # check if R is not than actual Z and move if needed - if drill_SafePoint != MACHINE_LAST_POSITION['Z']: + if drill_SafePoint != MACHINE_LAST_POSITION["Z"]: drill_Movement = "L" + HEIDEN_Format(" Z", drill_SafePoint) + drill_Rapid drill_Output.append(drill_Movement) - MACHINE_LAST_POSITION['Z'] = drill_SafePoint + MACHINE_LAST_POSITION["Z"] = drill_SafePoint # check if cycle is already stored i = True for j in drill_Defs: - if drill_Defs[j] != STORED_CANNED_PARAMS[j]: i = False + if drill_Defs[j] != STORED_CANNED_PARAMS[j]: + i = False - if i == False: # not same cycle, update and print + if i == False: # not same cycle, update and print for j in drill_Defs: STORED_CANNED_PARAMS[j] = drill_Defs[j] # get the DEF template and replace the strings drill_CycleDef = MACHINE_CYCLE_DEF[1].format( - DIST = str("{:.3f}".format(drill_Defs['DIST'])), - DEPTH = str("{:.3f}".format(drill_Defs['DEPTH'])), - INCR = str("{:.3f}".format(drill_Defs['INCR'])), - DWELL = str("{:.0f}".format(drill_Defs['DWELL'])), - FEED = str("{:.0f}".format(drill_Defs['FEED'])) - ) + DIST=str("{:.3f}".format(drill_Defs["DIST"])), + DEPTH=str("{:.3f}".format(drill_Defs["DEPTH"])), + INCR=str("{:.3f}".format(drill_Defs["INCR"])), + DWELL=str("{:.0f}".format(drill_Defs["DWELL"])), + FEED=str("{:.0f}".format(drill_Defs["FEED"])), + ) drill_Output.extend(drill_CycleDef.split("\n")) # add the cycle call to do the drill @@ -930,6 +1070,7 @@ def HEIDEN_Drill(drill_Obj, drill_Params, drill_Type, drill_feed): # create a dr return drill_Output + def HEIDEN_Format(formatType, formatValue): global SPINDLE_DECIMALS global FEED_DECIMALS @@ -938,27 +1079,29 @@ def HEIDEN_Format(formatType, formatValue): formatType = str(formatType) if formatType == "S" or formatType == " S": - returnString = '%.*f' % (SPINDLE_DECIMALS, formatValue) + returnString = "%.*f" % (SPINDLE_DECIMALS, formatValue) elif formatType == "F" or formatType == " F": - returnString = '%.*f' % (FEED_DECIMALS, formatValue) + returnString = "%.*f" % (FEED_DECIMALS, formatValue) else: - returnString = '%.*f' % (AXIS_DECIMALS, formatValue) + returnString = "%.*f" % (AXIS_DECIMALS, formatValue) return formatType + str(returnString) -def HEIDEN_Numberize(GCodeStrings): # add line numbers and concatenation + +def HEIDEN_Numberize(GCodeStrings): # add line numbers and concatenation global LINENUMBERS global STARTLINENR global LINENUMBER_INCREMENT if LINENUMBERS: linenr = STARTLINENR - result = '' + result = "" for s in GCodeStrings: result += str(linenr) + " " + s + "\n" linenr += LINENUMBER_INCREMENT return result else: - return '\n'.join(GCodeStrings) + return "\n".join(GCodeStrings) + def HEIDEN_LBL_Get(GetPoint=None, GetLevel=None, GetAddit=0): global POSTGCODE @@ -970,9 +1113,7 @@ def HEIDEN_LBL_Get(GetPoint=None, GetLevel=None, GetAddit=0): LBLIZE_PATH_LEVEL = GetLevel if len(STORED_LBL) != 0 and len(STORED_LBL[-1][-1]) == 0: STORED_LBL[-1].pop() - STORED_LBL.append( - [[len(POSTGCODE) + GetAddit, len(POSTGCODE) + GetAddit]] - ) + STORED_LBL.append([[len(POSTGCODE) + GetAddit, len(POSTGCODE) + GetAddit]]) else: if len(STORED_LBL[-1][-1]) == 0: STORED_LBL[-1][-1][0] = len(POSTGCODE) + GetAddit @@ -982,6 +1123,7 @@ def HEIDEN_LBL_Get(GetPoint=None, GetLevel=None, GetAddit=0): STORED_LBL[-1].append([]) return + def HEIDEN_LBL_Replace(): global POSTGCODE global STORED_LBL @@ -998,9 +1140,9 @@ def HEIDEN_LBL_Replace(): # from the last row in the last column of indexes Cols = len(STORED_LBL) - 1 if Cols > 0: - FIRST_LBL += len(STORED_LBL[0]) # last LBL number to print + FIRST_LBL += len(STORED_LBL[0]) # last LBL number to print for Col in range(Cols, 0, -1): - LBL_Shift = 0 # LBL number iterator + LBL_Shift = 0 # LBL number iterator Rows = len(STORED_LBL[0]) - 1 for Row in range(Rows, -1, -1): LBL_Shift += 1 @@ -1010,7 +1152,7 @@ def HEIDEN_LBL_Replace(): # get the indexes from first column RefStart = STORED_LBL[0][Row][1] i = 0 - Remove = True # initial value True, be False on error + Remove = True # initial value True, be False on error for j in range(CellStart, CellStop, -1): # diff from actual to first index column/row if POSTGCODE[j] != POSTGCODE[RefStart - i]: @@ -1022,7 +1164,9 @@ def HEIDEN_LBL_Replace(): for j in range(CellStart, CellStop, -1): POSTGCODE.pop(j) # add the LBL calls - POSTGCODE.insert(CellStop + 1, "CALL LBL " + str(FIRST_LBL - LBL_Shift)) + POSTGCODE.insert( + CellStop + 1, "CALL LBL " + str(FIRST_LBL - LBL_Shift) + ) if Gcode_Lenght != len(POSTGCODE): LBL_Shift = 0 Rows = len(STORED_LBL[0]) - 1 diff --git a/src/Mod/Path/PathScripts/post/jtech_post.py b/src/Mod/Path/PathScripts/post/jtech_post.py index e8a06a1ab6..f753357584 100644 --- a/src/Mod/Path/PathScripts/post/jtech_post.py +++ b/src/Mod/Path/PathScripts/post/jtech_post.py @@ -30,7 +30,7 @@ import datetime import shlex from PathScripts import PostUtils -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output real GCode suitable for a jtech photonics laser. This postprocessor, once placed @@ -39,22 +39,50 @@ FreeCAD, via the GUI importer or via python scripts with: import jtech_post jtech_post.export(object,"/path/to/file.ngc","") -''' +""" now = datetime.datetime.now() -parser = argparse.ArgumentParser(prog='jtech', add_help=False) -parser.add_argument('--no-header', action='store_true', help='suppress header output') -parser.add_argument('--no-comments', action='store_true', help='suppress comment output') -parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers') -parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output') -parser.add_argument('--precision', default='3', help='number of digits of precision, default=3') -parser.add_argument('--preamble', help='set commands to be issued before the first command, default="M05 S0\nG90"') -parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05 S0\nM2"') -parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)') -parser.add_argument('--modal', action='store_true', help='Output the Same G-command Name USE NonModal Mode') -parser.add_argument('--axis-modal', action='store_true', help='Output the Same Axis Value Mode') -parser.add_argument('--power-on-delay', default='255', help='milliseconds - Add a delay after laser on before moving to pre-heat material. Default=0') +parser = argparse.ArgumentParser(prog="jtech", add_help=False) +parser.add_argument("--no-header", action="store_true", help="suppress header output") +parser.add_argument( + "--no-comments", action="store_true", help="suppress comment output" +) +parser.add_argument( + "--line-numbers", action="store_true", help="prefix with line numbers" +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) +parser.add_argument( + "--precision", default="3", help="number of digits of precision, default=3" +) +parser.add_argument( + "--preamble", + help='set commands to be issued before the first command, default="M05 S0\nG90"', +) +parser.add_argument( + "--postamble", + help='set commands to be issued after the last command, default="M05 S0\nM2"', +) +parser.add_argument( + "--inches", action="store_true", help="Convert output for US imperial mode (G20)" +) +parser.add_argument( + "--modal", + action="store_true", + help="Output the Same G-command Name USE NonModal Mode", +) +parser.add_argument( + "--axis-modal", action="store_true", help="Output the Same Axis Value Mode" +) +parser.add_argument( + "--power-on-delay", + default="255", + help="milliseconds - Add a delay after laser on before moving to pre-heat material. Default=0", +) TOOLTIP_ARGS = parser.format_help() @@ -65,48 +93,50 @@ OUTPUT_HEADER = True OUTPUT_LINE_NUMBERS = False SHOW_EDITOR = True MODAL = False # if true commands are suppressed if the same as previous line. -OUTPUT_DOUBLES = True # if false duplicate axis values are suppressed if the same as previous line. +OUTPUT_DOUBLES = ( + True # if false duplicate axis values are suppressed if the same as previous line. +) COMMAND_SPACE = " " LINENR = 100 # line number starting value # These globals will be reflected in the Machine configuration of the project UNITS = "G21" # G21 for metric, G20 for us standard -UNIT_SPEED_FORMAT = 'mm/min' -UNIT_FORMAT = 'mm' +UNIT_SPEED_FORMAT = "mm/min" +UNIT_FORMAT = "mm" MACHINE_NAME = "JTECH Photonic Laser" PRECISION = 3 # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''M05 S0 +PREAMBLE = """M05 S0 G90 -''' +""" # Postamble text will appear following the last operation. -POSTAMBLE = '''M05 S0 +POSTAMBLE = """M05 S0 M2 -''' +""" -PRE_FEED = '''M03 +PRE_FEED = """M03 G4 P{} -''' +""" -POST_FEED = '''M05 -''' +POST_FEED = """M05 +""" # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" POWER_ON_DELAY = 0 # to distinguish python built-in open function from the one declared below -if open.__module__ == '__builtin__': +if open.__module__ == "__builtin__": pythonopen = open @@ -142,9 +172,9 @@ def processArguments(argstring): if args.postamble is not None: POSTAMBLE = args.postamble if args.inches: - UNITS = 'G20' - UNIT_SPEED_FORMAT = 'in/min' - UNIT_FORMAT = 'in' + UNITS = "G20" + UNIT_SPEED_FORMAT = "in/min" + UNIT_FORMAT = "in" PRECISION = 4 if args.modal: MODAL = True @@ -164,7 +194,11 @@ def export(objectslist, filename, argstring): for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return None print("postprocessing...") @@ -218,7 +252,7 @@ def export(objectslist, filename, argstring): print("done postprocessing.") - if not filename == '-': + if not filename == "-": gfile = pythonopen(filename, "wb") gfile.write(final) gfile.close() @@ -237,12 +271,30 @@ def linenumber(): def parse(pathobj): out = "" lastcommand = None - precision_string = '.' + str(PRECISION) + 'f' + precision_string = "." + str(PRECISION) + "f" currLocation = {} # keep track for no doubles RAPID_MOVES = ["G0", "G00"] FEED_MOVES = ["G1", "G01", "G2", "G02", "G3", "G03"] # the order of parameters - params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P'] + params = [ + "X", + "Y", + "Z", + "A", + "B", + "C", + "I", + "J", + "F", + "S", + "T", + "Q", + "R", + "L", + "H", + "D", + "P", + ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters @@ -276,41 +328,63 @@ def parse(pathobj): if command == lastcommand: outstring.pop(0) - if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment + if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES): - if c.Name not in RAPID_MOVES: # linuxcnc doesn't use rapid speeds - speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) + if param == "F" and ( + currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES + ): + if ( + c.Name not in RAPID_MOVES + ): # linuxcnc doesn't use rapid speeds + speed = Units.Quantity( + c.Parameters["F"], FreeCAD.Units.Velocity + ) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: - outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) + outstring.append( + param + + format( + float(speed.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) else: continue - elif param == 'T': - outstring.append(param + str(int(c.Parameters['T']))) - elif param == 'H': - outstring.append(param + str(int(c.Parameters['H']))) - elif param == 'D': - outstring.append(param + str(int(c.Parameters['D']))) - elif param == 'S': - outstring.append(param + str(int(c.Parameters['S']))) + elif param == "T": + outstring.append(param + str(int(c.Parameters["T"]))) + elif param == "H": + outstring.append(param + str(int(c.Parameters["H"]))) + elif param == "D": + outstring.append(param + str(int(c.Parameters["D"]))) + elif param == "S": + outstring.append(param + str(int(c.Parameters["S"]))) else: - if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]): + if ( + (not OUTPUT_DOUBLES) + and (param in currLocation) + and (currLocation[param] == c.Parameters[param]) + ): continue else: - pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) + pos = Units.Quantity( + c.Parameters[param], FreeCAD.Units.Length + ) outstring.append( - param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), precision_string + ) + ) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: - if command == 'M6': + if command == "M6": continue if command == "message": diff --git a/src/Mod/Path/PathScripts/post/linuxcnc_post.py b/src/Mod/Path/PathScripts/post/linuxcnc_post.py index 760d6a22ef..2d0f9e3d6d 100644 --- a/src/Mod/Path/PathScripts/post/linuxcnc_post.py +++ b/src/Mod/Path/PathScripts/post/linuxcnc_post.py @@ -30,7 +30,7 @@ import datetime import shlex from PathScripts import PostUtils -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output real GCode suitable for a linuxcnc 3 axis mill. This postprocessor, once placed @@ -39,22 +39,50 @@ FreeCAD, via the GUI importer or via python scripts with: import linuxcnc_post linuxcnc_post.export(object,"/path/to/file.ncc","") -''' +""" now = datetime.datetime.now() -parser = argparse.ArgumentParser(prog='linuxcnc', add_help=False) -parser.add_argument('--no-header', action='store_true', help='suppress header output') -parser.add_argument('--no-comments', action='store_true', help='suppress comment output') -parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers') -parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output') -parser.add_argument('--precision', default='3', help='number of digits of precision, default=3') -parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"') -parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"') -parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)') -parser.add_argument('--modal', action='store_true', help='Output the Same G-command Name USE NonModal Mode') -parser.add_argument('--axis-modal', action='store_true', help='Output the Same Axis Value Mode') -parser.add_argument('--no-tlo', action='store_true', help='suppress tool length offset (G43) following tool changes') +parser = argparse.ArgumentParser(prog="linuxcnc", add_help=False) +parser.add_argument("--no-header", action="store_true", help="suppress header output") +parser.add_argument( + "--no-comments", action="store_true", help="suppress comment output" +) +parser.add_argument( + "--line-numbers", action="store_true", help="prefix with line numbers" +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) +parser.add_argument( + "--precision", default="3", help="number of digits of precision, default=3" +) +parser.add_argument( + "--preamble", + help='set commands to be issued before the first command, default="G17\nG90"', +) +parser.add_argument( + "--postamble", + help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"', +) +parser.add_argument( + "--inches", action="store_true", help="Convert output for US imperial mode (G20)" +) +parser.add_argument( + "--modal", + action="store_true", + help="Output the Same G-command Name USE NonModal Mode", +) +parser.add_argument( + "--axis-modal", action="store_true", help="Output the Same Axis Value Mode" +) +parser.add_argument( + "--no-tlo", + action="store_true", + help="suppress tool length offset (G43) following tool changes", +) TOOLTIP_ARGS = parser.format_help() @@ -64,42 +92,44 @@ OUTPUT_HEADER = True OUTPUT_LINE_NUMBERS = False SHOW_EDITOR = True MODAL = False # if true commands are suppressed if the same as previous line. -USE_TLO = True # if true G43 will be output following tool changes -OUTPUT_DOUBLES = True # if false duplicate axis values are suppressed if the same as previous line. +USE_TLO = True # if true G43 will be output following tool changes +OUTPUT_DOUBLES = ( + True # if false duplicate axis values are suppressed if the same as previous line. +) COMMAND_SPACE = " " LINENR = 100 # line number starting value # These globals will be reflected in the Machine configuration of the project UNITS = "G21" # G21 for metric, G20 for us standard -UNIT_SPEED_FORMAT = 'mm/min' -UNIT_FORMAT = 'mm' +UNIT_SPEED_FORMAT = "mm/min" +UNIT_FORMAT = "mm" MACHINE_NAME = "LinuxCNC" -CORNER_MIN = {'x': 0, 'y': 0, 'z': 0} -CORNER_MAX = {'x': 500, 'y': 300, 'z': 300} +CORNER_MIN = {"x": 0, "y": 0, "z": 0} +CORNER_MAX = {"x": 500, "y": 300, "z": 300} PRECISION = 3 # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''G17 G54 G40 G49 G80 G90 -''' +PREAMBLE = """G17 G54 G40 G49 G80 G90 +""" # Postamble text will appear following the last operation. -POSTAMBLE = '''M05 +POSTAMBLE = """M05 G17 G54 G90 G80 G40 M2 -''' +""" # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -135,16 +165,16 @@ def processArguments(argstring): if args.postamble is not None: POSTAMBLE = args.postamble if args.inches: - UNITS = 'G20' - UNIT_SPEED_FORMAT = 'in/min' - UNIT_FORMAT = 'in' + UNITS = "G20" + UNIT_SPEED_FORMAT = "in/min" + UNIT_FORMAT = "in" PRECISION = 4 if args.modal: MODAL = True if args.no_tlo: USE_TLO = False if args.axis_modal: - print ('here') + print("here") OUTPUT_DOUBLES = False except Exception: @@ -152,6 +182,7 @@ def processArguments(argstring): return True + def export(objectslist, filename, argstring): if not processArguments(argstring): return None @@ -161,7 +192,11 @@ def export(objectslist, filename, argstring): for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return None print("postprocessing...") @@ -183,10 +218,10 @@ def export(objectslist, filename, argstring): for obj in objectslist: # Skip inactive operations - if hasattr(obj, 'Active'): + if hasattr(obj, "Active"): if not obj.Active: continue - if hasattr(obj, 'Base') and hasattr(obj.Base, 'Active'): + if hasattr(obj, "Base") and hasattr(obj.Base, "Active"): if not obj.Base.Active: continue @@ -198,8 +233,12 @@ def export(objectslist, filename, argstring): gcode += linenumber() + line # get coolant mode - coolantMode = 'None' - if hasattr(obj, "CoolantMode") or hasattr(obj, 'Base') and hasattr(obj.Base, "CoolantMode"): + coolantMode = "None" + if ( + hasattr(obj, "CoolantMode") + or hasattr(obj, "Base") + and hasattr(obj.Base, "CoolantMode") + ): if hasattr(obj, "CoolantMode"): coolantMode = obj.CoolantMode else: @@ -207,12 +246,12 @@ def export(objectslist, filename, argstring): # turn coolant on if required if OUTPUT_COMMENTS: - if not coolantMode == 'None': - gcode += linenumber() + '(Coolant On:' + coolantMode + ')\n' - if coolantMode == 'Flood': - gcode += linenumber() + 'M8' + '\n' - if coolantMode == 'Mist': - gcode += linenumber() + 'M7' + '\n' + if not coolantMode == "None": + gcode += linenumber() + "(Coolant On:" + coolantMode + ")\n" + if coolantMode == "Flood": + gcode += linenumber() + "M8" + "\n" + if coolantMode == "Mist": + gcode += linenumber() + "M7" + "\n" # process the operation gcode gcode += parse(obj) @@ -224,10 +263,10 @@ def export(objectslist, filename, argstring): gcode += linenumber() + line # turn coolant off if required - if not coolantMode == 'None': + if not coolantMode == "None": if OUTPUT_COMMENTS: - gcode += linenumber() + '(Coolant Off:' + coolantMode + ')\n' - gcode += linenumber() +'M9' + '\n' + gcode += linenumber() + "(Coolant Off:" + coolantMode + ")\n" + gcode += linenumber() + "M9" + "\n" # do the post_amble if OUTPUT_COMMENTS: @@ -250,7 +289,7 @@ def export(objectslist, filename, argstring): print("done postprocessing.") - if not filename == '-': + if not filename == "-": gfile = pythonopen(filename, "w") gfile.write(final) gfile.close() @@ -275,12 +314,30 @@ def parse(pathobj): out = "" lastcommand = None - precision_string = '.' + str(PRECISION) + 'f' + precision_string = "." + str(PRECISION) + "f" currLocation = {} # keep track for no doubles # the order of parameters # linuxcnc doesn't want K properties on XY plane Arcs need work. - params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P'] + params = [ + "X", + "Y", + "Z", + "A", + "B", + "C", + "I", + "J", + "F", + "S", + "T", + "Q", + "R", + "L", + "H", + "D", + "P", + ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters @@ -310,41 +367,64 @@ def parse(pathobj): if command == lastcommand: outstring.pop(0) - if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment + if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES): - if c.Name not in ["G0", "G00"]: # linuxcnc doesn't use rapid speeds - speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) + if param == "F" and ( + currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES + ): + if c.Name not in [ + "G0", + "G00", + ]: # linuxcnc doesn't use rapid speeds + speed = Units.Quantity( + c.Parameters["F"], FreeCAD.Units.Velocity + ) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: - outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) + outstring.append( + param + + format( + float(speed.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) else: continue - elif param == 'T': - outstring.append(param + str(int(c.Parameters['T']))) - elif param == 'H': - outstring.append(param + str(int(c.Parameters['H']))) - elif param == 'D': - outstring.append(param + str(int(c.Parameters['D']))) - elif param == 'S': - outstring.append(param + str(int(c.Parameters['S']))) + elif param == "T": + outstring.append(param + str(int(c.Parameters["T"]))) + elif param == "H": + outstring.append(param + str(int(c.Parameters["H"]))) + elif param == "D": + outstring.append(param + str(int(c.Parameters["D"]))) + elif param == "S": + outstring.append(param + str(int(c.Parameters["S"]))) else: - if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]): + if ( + (not OUTPUT_DOUBLES) + and (param in currLocation) + and (currLocation[param] == c.Parameters[param]) + ): continue else: - pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) + pos = Units.Quantity( + c.Parameters[param], FreeCAD.Units.Length + ) outstring.append( - param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), precision_string + ) + ) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: - if command == 'M6': + if command == "M6": # stop the spindle out += linenumber() + "M5\n" for line in TOOL_CHANGE.splitlines(True): @@ -352,7 +432,7 @@ def parse(pathobj): # add height offset if USE_TLO: - tool_height = '\nG43 H' + str(int(c.Parameters['T'])) + tool_height = "\nG43 H" + str(int(c.Parameters["T"])) outstring.append(tool_height) if command == "message": @@ -375,4 +455,5 @@ def parse(pathobj): return out + # print(__name__ + " gcode postprocessor loaded.") diff --git a/src/Mod/Path/PathScripts/post/mach3_mach4_post.py b/src/Mod/Path/PathScripts/post/mach3_mach4_post.py index f210b1dae1..9fd054fb5a 100644 --- a/src/Mod/Path/PathScripts/post/mach3_mach4_post.py +++ b/src/Mod/Path/PathScripts/post/mach3_mach4_post.py @@ -29,7 +29,7 @@ import datetime import shlex from PathScripts import PostUtils -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output real GCode suitable for a mach3_4 3 axis mill. This postprocessor, once placed @@ -38,22 +38,50 @@ FreeCAD, via the GUI importer or via python scripts with: import mach3_4_post mach3_4_post.export(object,"/path/to/file.ncc","") -''' +""" now = datetime.datetime.now() -parser = argparse.ArgumentParser(prog='mach3_4', add_help=False) -parser.add_argument('--no-header', action='store_true', help='suppress header output') -parser.add_argument('--no-comments', action='store_true', help='suppress comment output') -parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers') -parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output') -parser.add_argument('--precision', default='3', help='number of digits of precision, default=3') -parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"') -parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"') -parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)') -parser.add_argument('--modal', action='store_true', help='Output the Same G-command Name USE NonModal Mode') -parser.add_argument('--axis-modal', action='store_true', help='Output the Same Axis Value Mode') -parser.add_argument('--no-tlo', action='store_true', help='suppress tool length offset (G43) following tool changes') +parser = argparse.ArgumentParser(prog="mach3_4", add_help=False) +parser.add_argument("--no-header", action="store_true", help="suppress header output") +parser.add_argument( + "--no-comments", action="store_true", help="suppress comment output" +) +parser.add_argument( + "--line-numbers", action="store_true", help="prefix with line numbers" +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) +parser.add_argument( + "--precision", default="3", help="number of digits of precision, default=3" +) +parser.add_argument( + "--preamble", + help='set commands to be issued before the first command, default="G17\nG90"', +) +parser.add_argument( + "--postamble", + help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"', +) +parser.add_argument( + "--inches", action="store_true", help="Convert output for US imperial mode (G20)" +) +parser.add_argument( + "--modal", + action="store_true", + help="Output the Same G-command Name USE NonModal Mode", +) +parser.add_argument( + "--axis-modal", action="store_true", help="Output the Same Axis Value Mode" +) +parser.add_argument( + "--no-tlo", + action="store_true", + help="suppress tool length offset (G43) following tool changes", +) TOOLTIP_ARGS = parser.format_help() @@ -63,42 +91,44 @@ OUTPUT_HEADER = True OUTPUT_LINE_NUMBERS = False SHOW_EDITOR = True MODAL = False # if true commands are suppressed if the same as previous line. -USE_TLO = True # if true G43 will be output following tool changes -OUTPUT_DOUBLES = True # if false duplicate axis values are suppressed if the same as previous line. +USE_TLO = True # if true G43 will be output following tool changes +OUTPUT_DOUBLES = ( + True # if false duplicate axis values are suppressed if the same as previous line. +) COMMAND_SPACE = " " LINENR = 100 # line number starting value # These globals will be reflected in the Machine configuration of the project UNITS = "G21" # G21 for metric, G20 for us standard -UNIT_SPEED_FORMAT = 'mm/min' -UNIT_FORMAT = 'mm' +UNIT_SPEED_FORMAT = "mm/min" +UNIT_FORMAT = "mm" MACHINE_NAME = "mach3_4" -CORNER_MIN = {'x': 0, 'y': 0, 'z': 0} -CORNER_MAX = {'x': 500, 'y': 300, 'z': 300} +CORNER_MIN = {"x": 0, "y": 0, "z": 0} +CORNER_MAX = {"x": 500, "y": 300, "z": 300} PRECISION = 3 # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''G17 G54 G40 G49 G80 G90 -''' +PREAMBLE = """G17 G54 G40 G49 G80 G90 +""" # Postamble text will appear following the last operation. -POSTAMBLE = '''M05 +POSTAMBLE = """M05 G17 G54 G90 G80 G40 M2 -''' +""" # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -134,16 +164,16 @@ def processArguments(argstring): if args.postamble is not None: POSTAMBLE = args.postamble if args.inches: - UNITS = 'G20' - UNIT_SPEED_FORMAT = 'in/min' - UNIT_FORMAT = 'in' + UNITS = "G20" + UNIT_SPEED_FORMAT = "in/min" + UNIT_FORMAT = "in" PRECISION = 4 if args.modal: MODAL = True if args.no_tlo: USE_TLO = False if args.axis_modal: - print ('here') + print("here") OUTPUT_DOUBLES = False except Exception: @@ -161,7 +191,11 @@ def export(objectslist, filename, argstring): for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return None print("postprocessing...") @@ -183,23 +217,30 @@ def export(objectslist, filename, argstring): for obj in objectslist: # Skip inactive operations - if hasattr(obj, 'Active'): + if hasattr(obj, "Active"): if not obj.Active: continue - if hasattr(obj, 'Base') and hasattr(obj.Base, 'Active'): + if hasattr(obj, "Base") and hasattr(obj.Base, "Active"): if not obj.Base.Active: continue # do the pre_op if OUTPUT_COMMENTS: gcode += linenumber() + "(begin operation: %s)\n" % obj.Label - gcode += linenumber() + "(machine: %s, %s)\n" % (MACHINE_NAME, UNIT_SPEED_FORMAT) + gcode += linenumber() + "(machine: %s, %s)\n" % ( + MACHINE_NAME, + UNIT_SPEED_FORMAT, + ) for line in PRE_OPERATION.splitlines(True): gcode += linenumber() + line # get coolant mode - coolantMode = 'None' - if hasattr(obj, "CoolantMode") or hasattr(obj, 'Base') and hasattr(obj.Base, "CoolantMode"): + coolantMode = "None" + if ( + hasattr(obj, "CoolantMode") + or hasattr(obj, "Base") + and hasattr(obj.Base, "CoolantMode") + ): if hasattr(obj, "CoolantMode"): coolantMode = obj.CoolantMode else: @@ -207,12 +248,12 @@ def export(objectslist, filename, argstring): # turn coolant on if required if OUTPUT_COMMENTS: - if not coolantMode == 'None': - gcode += linenumber() + '(Coolant On:' + coolantMode + ')\n' - if coolantMode == 'Flood': - gcode += linenumber() + 'M8' + '\n' - if coolantMode == 'Mist': - gcode += linenumber() + 'M7' + '\n' + if not coolantMode == "None": + gcode += linenumber() + "(Coolant On:" + coolantMode + ")\n" + if coolantMode == "Flood": + gcode += linenumber() + "M8" + "\n" + if coolantMode == "Mist": + gcode += linenumber() + "M7" + "\n" # process the operation gcode gcode += parse(obj) @@ -224,10 +265,10 @@ def export(objectslist, filename, argstring): gcode += linenumber() + line # turn coolant off if required - if not coolantMode == 'None': + if not coolantMode == "None": if OUTPUT_COMMENTS: - gcode += linenumber() + '(Coolant Off:' + coolantMode + ')\n' - gcode += linenumber() +'M9' + '\n' + gcode += linenumber() + "(Coolant Off:" + coolantMode + ")\n" + gcode += linenumber() + "M9" + "\n" # do the post_amble if OUTPUT_COMMENTS: @@ -248,7 +289,7 @@ def export(objectslist, filename, argstring): print("done postprocessing.") - if not filename == '-': + if not filename == "-": gfile = pythonopen(filename, "w") gfile.write(final) gfile.close() @@ -273,12 +314,30 @@ def parse(pathobj): out = "" lastcommand = None - precision_string = '.' + str(PRECISION) + 'f' + precision_string = "." + str(PRECISION) + "f" currLocation = {} # keep track for no doubles # the order of parameters # mach3_4 doesn't want K properties on XY plane Arcs need work. - params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P'] + params = [ + "X", + "Y", + "Z", + "A", + "B", + "C", + "I", + "J", + "F", + "S", + "T", + "Q", + "R", + "L", + "H", + "D", + "P", + ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters @@ -301,18 +360,32 @@ def parse(pathobj): opHorizRapid = 0 opVertRapid = 0 - if 'Adaptive' in pathobj.Name: + if "Adaptive" in pathobj.Name: adaptiveOp = True - if hasattr(pathobj, 'ToolController'): - if hasattr(pathobj.ToolController, 'HorizRapid') and pathobj.ToolController.HorizRapid > 0: - opHorizRapid = Units.Quantity(pathobj.ToolController.HorizRapid, FreeCAD.Units.Velocity) + if hasattr(pathobj, "ToolController"): + if ( + hasattr(pathobj.ToolController, "HorizRapid") + and pathobj.ToolController.HorizRapid > 0 + ): + opHorizRapid = Units.Quantity( + pathobj.ToolController.HorizRapid, FreeCAD.Units.Velocity + ) else: - FreeCAD.Console.PrintWarning('Tool Controller Horizontal Rapid Values are unset'+ '\n') + FreeCAD.Console.PrintWarning( + "Tool Controller Horizontal Rapid Values are unset" + "\n" + ) - if hasattr(pathobj.ToolController, 'VertRapid') and pathobj.ToolController.VertRapid > 0: - opVertRapid = Units.Quantity(pathobj.ToolController.VertRapid, FreeCAD.Units.Velocity) + if ( + hasattr(pathobj.ToolController, "VertRapid") + and pathobj.ToolController.VertRapid > 0 + ): + opVertRapid = Units.Quantity( + pathobj.ToolController.VertRapid, FreeCAD.Units.Velocity + ) else: - FreeCAD.Console.PrintWarning('Tool Controller Vertical Rapid Values are unset'+ '\n') + FreeCAD.Console.PrintWarning( + "Tool Controller Vertical Rapid Values are unset" + "\n" + ) for c in pathobj.Path.Commands: @@ -321,10 +394,9 @@ def parse(pathobj): if adaptiveOp and c.Name in ["G0", "G00"]: if opHorizRapid and opVertRapid: - command = 'G1' + command = "G1" else: - outstring.append('(Tool Controller Rapid Values are unset)' + '\n') - + outstring.append("(Tool Controller Rapid Values are unset)" + "\n") outstring.append(command) @@ -333,48 +405,83 @@ def parse(pathobj): if command == lastcommand: outstring.pop(0) - if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment + if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES): - if c.Name not in ["G0", "G00"]: # mach3_4 doesn't use rapid speeds - speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) + if param == "F" and ( + currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES + ): + if c.Name not in [ + "G0", + "G00", + ]: # mach3_4 doesn't use rapid speeds + speed = Units.Quantity( + c.Parameters["F"], FreeCAD.Units.Velocity + ) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: - outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) + outstring.append( + param + + format( + float(speed.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) else: continue - elif param == 'T': - outstring.append(param + str(int(c.Parameters['T']))) - elif param == 'H': - outstring.append(param + str(int(c.Parameters['H']))) - elif param == 'D': - outstring.append(param + str(int(c.Parameters['D']))) - elif param == 'S': - outstring.append(param + str(int(c.Parameters['S']))) + elif param == "T": + outstring.append(param + str(int(c.Parameters["T"]))) + elif param == "H": + outstring.append(param + str(int(c.Parameters["H"]))) + elif param == "D": + outstring.append(param + str(int(c.Parameters["D"]))) + elif param == "S": + outstring.append(param + str(int(c.Parameters["S"]))) else: - if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]): + if ( + (not OUTPUT_DOUBLES) + and (param in currLocation) + and (currLocation[param] == c.Parameters[param]) + ): continue else: - pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) + pos = Units.Quantity( + c.Parameters[param], FreeCAD.Units.Length + ) outstring.append( - param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), precision_string + ) + ) if adaptiveOp and c.Name in ["G0", "G00"]: if opHorizRapid and opVertRapid: - if 'Z' not in c.Parameters: - outstring.append('F' + format(float(opHorizRapid.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) + if "Z" not in c.Parameters: + outstring.append( + "F" + + format( + float(opHorizRapid.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) else: - outstring.append('F' + format(float(opVertRapid.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) + outstring.append( + "F" + + format( + float(opVertRapid.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: - if command == 'M6': + if command == "M6": # stop the spindle out += linenumber() + "M5\n" for line in TOOL_CHANGE.splitlines(True): @@ -382,7 +489,7 @@ def parse(pathobj): # add height offset if USE_TLO: - tool_height = '\nG43 H' + str(int(c.Parameters['T'])) + tool_height = "\nG43 H" + str(int(c.Parameters["T"])) outstring.append(tool_height) if command == "message": @@ -403,4 +510,5 @@ def parse(pathobj): return out + # print(__name__ + " gcode postprocessor loaded.") diff --git a/src/Mod/Path/PathScripts/post/nccad_post.py b/src/Mod/Path/PathScripts/post/nccad_post.py index 07273403f4..f931e58db0 100644 --- a/src/Mod/Path/PathScripts/post/nccad_post.py +++ b/src/Mod/Path/PathScripts/post/nccad_post.py @@ -26,7 +26,7 @@ from PathScripts import PostUtils import datetime -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment output by a Path object and output real GCode suitable for the Max Computer GmbH nccad9 Computer Numeric Control. @@ -41,33 +41,35 @@ Supported features: import nccad_post nccad_post.export([object], "/path/to/file.knc", "") -''' +""" -MACHINE_NAME = '''Max Computer GmbH nccad9 MCS/KOSY''' +MACHINE_NAME = """Max Computer GmbH nccad9 MCS/KOSY""" # gCode for changing tools # M01 ; Displays and waits for user interaction -TOOL_CHANGE = '''G77 ; Move to release position +TOOL_CHANGE = """G77 ; Move to release position M10 O6.0 ; Stop spindle M01 Insert tool TOOL G76 ; Move to reference point to ensure correct coordinates after tool change -M10 O6.1 ; Start spindle''' +M10 O6.1 ; Start spindle""" # gCode finishing the program -POSTAMBLE = '''G77 ; Move to release position -M10 O6.0 ; Stop spindle''' +POSTAMBLE = """G77 ; Move to release position +M10 O6.0 ; Stop spindle""" # gCode header with information about CAD-software, post-processor # and date/time -HEADER = ''';Exported by FreeCAD +HEADER = """;Exported by FreeCAD ;Post Processor: {} ;CAM file: {} ;Output Time: {} -'''.format(__name__, App.ActiveDocument.FileName, str(datetime.datetime.now())) +""".format( + __name__, App.ActiveDocument.FileName, str(datetime.datetime.now()) +) def export(objectslist, filename, argstring): @@ -86,14 +88,15 @@ def export(objectslist, filename, argstring): for obj in objectslist: for command in obj.Path.Commands: # Manipulate tool change commands - if 'M6' == command.Name: - gcode += TOOL_CHANGE.replace('TOOL', - str(int(command.Parameters['T']))) - elif 'M3' == command.Name: + if "M6" == command.Name: + gcode += TOOL_CHANGE.replace("TOOL", str(int(command.Parameters["T"]))) + elif "M3" == command.Name: # Convert spindle speed (rpm) command to comment - gcode += ('M01 Set spindle speed to ' - + str(int(command.Parameters['S'])) - + ' rounds per minute') + gcode += ( + "M01 Set spindle speed to " + + str(int(command.Parameters["S"])) + + " rounds per minute" + ) else: # Add other commands gcode += command.Name @@ -102,15 +105,15 @@ def export(objectslist, filename, argstring): for parameter, value in command.Parameters.items(): # Multiply F parameter value by 10, # FreeCAD = mm/s, nccad = 1/10 mm/s - if 'F' == parameter: + if "F" == parameter: value *= 10 # Add command parameters and values and round float # as nccad9 does not support exponents - gcode += ' ' + parameter + str(round(value, 5)) + gcode += " " + parameter + str(round(value, 5)) - gcode += '\n' + gcode += "\n" - gcode += POSTAMBLE + '\n' + gcode += POSTAMBLE + "\n" # Open editor window if App.GuiUp: @@ -121,7 +124,7 @@ def export(objectslist, filename, argstring): gcode = dia.editor.toPlainText() # Save to file - if filename != '-': + if filename != "-": gfile = open(filename, "w") gfile.write(gcode) gfile.close() diff --git a/src/Mod/Path/PathScripts/post/opensbp_post.py b/src/Mod/Path/PathScripts/post/opensbp_post.py index e58613a7cd..e76eeedc97 100644 --- a/src/Mod/Path/PathScripts/post/opensbp_post.py +++ b/src/Mod/Path/PathScripts/post/opensbp_post.py @@ -26,7 +26,7 @@ import datetime from PathScripts import PostUtils -TOOLTIP = ''' +TOOLTIP = """ This is an postprocessor file for the Path workbench. It will output path data in a format suitable for OpenSBP controllers like shopbot. This postprocessor, once placed in the appropriate PathScripts folder, can be used directly from @@ -34,9 +34,9 @@ inside FreeCAD, via the GUI importer or via python scripts with: import Path Path.write(object,"/path/to/file.ncc","post_opensbp") -''' +""" -''' +""" DONE: uses native commands handles feed and jog moves @@ -48,15 +48,15 @@ ToDo drilling. Haven't looked at it. many other things -''' +""" -TOOLTIP_ARGS = ''' +TOOLTIP_ARGS = """ Arguments for opensbp: --comments ... insert comments - mostly for debugging --inches ... convert output to inches --no-header ... suppress header output --no-show-editor ... don't show editor, just save result -''' +""" now = datetime.datetime.now() @@ -66,21 +66,21 @@ SHOW_EDITOR = True COMMAND_SPACE = "," # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''''' +PREAMBLE = """""" # Postamble text will appear following the last operation. -POSTAMBLE = '''''' +POSTAMBLE = """""" # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__', 'io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open CurrentState = {} @@ -105,13 +105,13 @@ def export(objectslist, filename, argstring): global GetValue for arg in argstring.split(): - if arg == '--comments': + if arg == "--comments": OUTPUT_COMMENTS = True - if arg == '--inches': + if arg == "--inches": GetValue = getImperialValue - if arg == '--no-header': + if arg == "--no-header": OUTPUT_HEADER = False - if arg == '--no-show-editor': + if arg == "--no-show-editor": SHOW_EDITOR = False for obj in objectslist: @@ -122,8 +122,15 @@ def export(objectslist, filename, argstring): return CurrentState = { - 'X': 0, 'Y': 0, 'Z': 0, 'F': 0, 'S': 0, - 'JSXY': 0, 'JSZ': 0, 'MSXY': 0, 'MSZ': 0 + "X": 0, + "Y": 0, + "Z": 0, + "F": 0, + "S": 0, + "JSXY": 0, + "JSZ": 0, + "MSXY": 0, + "MSZ": 0, } print("postprocessing...") gcode = "" @@ -188,26 +195,26 @@ def move(command): # txt += feedrate(command) axis = "" - for p in ['X', 'Y', 'Z']: + for p in ["X", "Y", "Z"]: if p in command.Parameters: if command.Parameters[p] != CurrentState[p]: axis += p - if 'F' in command.Parameters: - speed = command.Parameters['F'] - if command.Name in ['G1', 'G01']: # move + if "F" in command.Parameters: + speed = command.Parameters["F"] + if command.Name in ["G1", "G01"]: # move movetype = "MS" else: # jog movetype = "JS" zspeed = "" xyspeed = "" - if 'Z' in axis: + if "Z" in axis: speedKey = "{}Z".format(movetype) speedVal = GetValue(speed) if CurrentState[speedKey] != speedVal: CurrentState[speedKey] = speedVal zspeed = "{:f}".format(speedVal) - if ('X' in axis) or ('Y' in axis): + if ("X" in axis) or ("Y" in axis): speedKey = "{}XY".format(movetype) speedVal = GetValue(speed) if CurrentState[speedKey] != speedVal: @@ -216,45 +223,45 @@ def move(command): if zspeed or xyspeed: txt += "{},{},{}\n".format(movetype, xyspeed, zspeed) - if command.Name in ['G0', 'G00']: + if command.Name in ["G0", "G00"]: pref = "J" else: pref = "M" if axis == "X": txt += pref + "X" - txt += "," + format(GetValue(command.Parameters["X"]), '.4f') + txt += "," + format(GetValue(command.Parameters["X"]), ".4f") txt += "\n" elif axis == "Y": txt += pref + "Y" - txt += "," + format(GetValue(command.Parameters["Y"]), '.4f') + txt += "," + format(GetValue(command.Parameters["Y"]), ".4f") txt += "\n" elif axis == "Z": txt += pref + "Z" - txt += "," + format(GetValue(command.Parameters["Z"]), '.4f') + txt += "," + format(GetValue(command.Parameters["Z"]), ".4f") txt += "\n" elif axis == "XY": txt += pref + "2" - txt += "," + format(GetValue(command.Parameters["X"]), '.4f') - txt += "," + format(GetValue(command.Parameters["Y"]), '.4f') + txt += "," + format(GetValue(command.Parameters["X"]), ".4f") + txt += "," + format(GetValue(command.Parameters["Y"]), ".4f") txt += "\n" elif axis == "XZ": txt += pref + "3" - txt += "," + format(GetValue(command.Parameters["X"]), '.4f') + txt += "," + format(GetValue(command.Parameters["X"]), ".4f") txt += "," - txt += "," + format(GetValue(command.Parameters["Z"]), '.4f') + txt += "," + format(GetValue(command.Parameters["Z"]), ".4f") txt += "\n" elif axis == "XYZ": txt += pref + "3" - txt += "," + format(GetValue(command.Parameters["X"]), '.4f') - txt += "," + format(GetValue(command.Parameters["Y"]), '.4f') - txt += "," + format(GetValue(command.Parameters["Z"]), '.4f') + txt += "," + format(GetValue(command.Parameters["X"]), ".4f") + txt += "," + format(GetValue(command.Parameters["Y"]), ".4f") + txt += "," + format(GetValue(command.Parameters["Z"]), ".4f") txt += "\n" elif axis == "YZ": txt += pref + "3" txt += "," - txt += "," + format(GetValue(command.Parameters["Y"]), '.4f') - txt += "," + format(GetValue(command.Parameters["Z"]), '.4f') + txt += "," + format(GetValue(command.Parameters["Y"]), ".4f") + txt += "," + format(GetValue(command.Parameters["Z"]), ".4f") txt += "\n" elif axis == "": print("warning: skipping duplicate move.") @@ -267,15 +274,15 @@ def move(command): def arc(command): - if command.Name == 'G2': # CW + if command.Name == "G2": # CW dirstring = "1" else: # G3 means CCW dirstring = "-1" txt = "CG,," - txt += format(GetValue(command.Parameters['X']), '.4f') + "," - txt += format(GetValue(command.Parameters['Y']), '.4f') + "," - txt += format(GetValue(command.Parameters['I']), '.4f') + "," - txt += format(GetValue(command.Parameters['J']), '.4f') + "," + txt += format(GetValue(command.Parameters["X"]), ".4f") + "," + txt += format(GetValue(command.Parameters["Y"]), ".4f") + "," + txt += format(GetValue(command.Parameters["I"]), ".4f") + "," + txt += format(GetValue(command.Parameters["J"]), ".4f") + "," txt += "T" + "," txt += dirstring txt += "\n" @@ -288,9 +295,9 @@ def tool_change(command): txt += "'a tool change happens now\n" for line in TOOL_CHANGE.splitlines(True): txt += line - txt += "&ToolName=" + str(int(command.Parameters['T'])) + txt += "&ToolName=" + str(int(command.Parameters["T"])) txt += "\n" - txt += "&Tool=" + str(int(command.Parameters['T'])) + txt += "&Tool=" + str(int(command.Parameters["T"])) txt += "\n" return txt @@ -306,7 +313,7 @@ def spindle(command): pass else: pass - txt += "TR," + str(command.Parameters['S']) + "\n" + txt += "TR," + str(command.Parameters["S"]) + "\n" txt += "C6\n" txt += "PAUSE 2\n" return txt @@ -326,7 +333,7 @@ scommands = { "G03": arc, "M06": tool_change, "M03": spindle, - "message": comment + "message": comment, } @@ -351,10 +358,10 @@ def parse(pathobj): output += scommands[command](c) if c.Parameters: CurrentState.update(c.Parameters) - elif command[0] == '(': + elif command[0] == "(": output += "' " + command + "\n" else: - print("I don't know what the hell the command: ", end='') + print("I don't know what the hell the command: ", end="") print(command + " means. Maybe I should support it.") return output diff --git a/src/Mod/Path/PathScripts/post/opensbp_pre.py b/src/Mod/Path/PathScripts/post/opensbp_pre.py index 4b8e58542e..3bd4117d69 100644 --- a/src/Mod/Path/PathScripts/post/opensbp_pre.py +++ b/src/Mod/Path/PathScripts/post/opensbp_pre.py @@ -22,7 +22,7 @@ # * * # *************************************************************************** -''' +""" This is a preprocessor file for the Path workbench. Its aim is to parse the contents of a given OpenSBP file, and transform it to make it suitable for use in a Path object. This preprocessor, once placed in the @@ -46,18 +46,24 @@ if operations are preceded by a comment ('New Path ...) They are split into mul TODO Many other OpenSBP commands not handled -''' +""" from __future__ import print_function import FreeCAD import PathScripts.PathUtil as PathUtil import os import Path -AXIS = 'X', 'Y', 'Z', 'A', 'B' # OpenSBP always puts multiaxis move parameters in this order -SPEEDS = 'XY', 'Z', 'A', 'B' +AXIS = ( + "X", + "Y", + "Z", + "A", + "B", +) # OpenSBP always puts multiaxis move parameters in this order +SPEEDS = "XY", "Z", "A", "B" # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__', 'io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -69,9 +75,9 @@ def open(filename): def insert(filename, docname): - '''called when freecad imports a file + """called when freecad imports a file This insert expects parse to return a list of strings - each string will become a separate path''' + each string will become a separate path""" gfile = pythonopen(filename) gcode = gfile.read() gfile.close() @@ -90,10 +96,20 @@ def parse(inputstring): lines = inputstring.split("\n") return_output = [] output = "" - last = {'X': None, 'Y': None, 'Z': None, 'A': None, 'B': None} - lastrapidspeed = {'XY': "50", 'Z': "50", 'A': "50", 'B': "50"} # set default rapid speeds - lastfeedspeed = {'XY': "50", 'Z': "50", 'A': "50", 'B': "50"} # set default feed speed - movecommand = ['G1', 'G0', 'G02', 'G03'] + last = {"X": None, "Y": None, "Z": None, "A": None, "B": None} + lastrapidspeed = { + "XY": "50", + "Z": "50", + "A": "50", + "B": "50", + } # set default rapid speeds + lastfeedspeed = { + "XY": "50", + "Z": "50", + "A": "50", + "B": "50", + } # set default feed speed + movecommand = ["G1", "G0", "G02", "G03"] for line in lines: # remove any leftover trailing and preceding spaces @@ -105,22 +121,33 @@ def parse(inputstring): # discard comment and other non strictly gcode lines if line[0:9] == "'New Path": # starting new path - if any(x in output for x in movecommand): # make sure the path has at least one move command. + if any( + x in output for x in movecommand + ): # make sure the path has at least one move command. return_output.append(output) output = "" continue words = [a.strip() for a in line.split(",")] words[0] = words[0].upper() - if words[0] in ["J2", "J3", "J4", "J5", "M2", "M3", "M4", "M5"]: # multi-axis jogs and moves - if words[0][0] == 'J': # jog move + if words[0] in [ + "J2", + "J3", + "J4", + "J5", + "M2", + "M3", + "M4", + "M5", + ]: # multi-axis jogs and moves + if words[0][0] == "J": # jog move s = "G0 " - else: # feed move + else: # feed move s = "G1 " speed = lastfeedspeed["XY"] for i in range(1, len(words)): - if words[i] == '': + if words[i] == "": if last[AXIS[i - 1]] is None: continue else: @@ -128,19 +155,30 @@ def parse(inputstring): else: s += AXIS[i - 1] + words[i] last[AXIS[i - 1]] = words[i] - output += s + " F" + speed + '\n' + output += s + " F" + speed + "\n" - if words[0] in ["JA", "JB", "JX", "JY", "JZ", "MA", "MB", "MX", "MY", "MZ"]: # single axis jogs and moves - if words[0][0] == 'J': # jog move + if words[0] in [ + "JA", + "JB", + "JX", + "JY", + "JZ", + "MA", + "MB", + "MX", + "MY", + "MZ", + ]: # single axis jogs and moves + if words[0][0] == "J": # jog move s = "G0 " - if words[0][1] in ['X', 'Y']: + if words[0][1] in ["X", "Y"]: speed = lastrapidspeed["XY"] else: speed = lastrapidspeed[words[0][1]] - else: # feed move + else: # feed move s = "G1 " - if words[0][1] in ['X', 'Y']: + if words[0][1] in ["X", "Y"]: speed = lastfeedspeed["XY"] else: speed = lastfeedspeed[words[0][1]] @@ -153,7 +191,7 @@ def parse(inputstring): if words[0] in ["JS"]: # set jog speed for i in range(1, len(words)): - if words[i] == '': + if words[i] == "": continue else: lastrapidspeed[SPEEDS[i - 1]] = words[i] @@ -166,7 +204,7 @@ def parse(inputstring): continue if words[0] in ["MS"]: # set move speed for i in range(1, len(words)): - if words[i] == '': + if words[i] == "": continue else: lastfeedspeed[SPEEDS[i - 1]] = words[i] @@ -180,7 +218,7 @@ def parse(inputstring): else: s = "M3 S" s += str(abs(float(words[1]))) - output += s + '\n' + output += s + "\n" if words[0] in ["CG"]: # Gcode circle/arc if words[1] != "": # diameter mode @@ -193,8 +231,19 @@ def parse(inputstring): else: # CCW s = "G3" - s += " X" + words[2] + " Y" + words[3] + " I" + words[4] + " J" + words[5] + " F" + str(lastfeedspeed["XY"]) - output += s + '\n' + s += ( + " X" + + words[2] + + " Y" + + words[3] + + " I" + + words[4] + + " J" + + words[5] + + " F" + + str(lastfeedspeed["XY"]) + ) + output += s + "\n" last["X"] = words[2] last["Y"] = words[3] diff --git a/src/Mod/Path/PathScripts/post/philips_post.py b/src/Mod/Path/PathScripts/post/philips_post.py index 4444b7b069..d001ada2a5 100644 --- a/src/Mod/Path/PathScripts/post/philips_post.py +++ b/src/Mod/Path/PathScripts/post/philips_post.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- -#*************************************************************************** -#* Copyright (c) 2016 Christoph Blaue * -#* * -#* 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 Christoph Blaue * +# * * +# * 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 * +# * * +# *************************************************************************** # 03-24-2021 Sliptonic: I've removed the PathUtils import and job lookup # post processors shouldn't be reaching back to the job. This can cause a @@ -30,7 +30,7 @@ import time from PathScripts import PostUtils import math -TOOLTIP = '''Post processor for Maho M 600E mill +TOOLTIP = """Post processor for Maho M 600E mill Machines with Philips or Heidenhain control should be very easy to adapt. @@ -39,18 +39,18 @@ No programming experience required. This can make a generated g-code program more readable and since older machines have very limited memory it seems sensible to reduce the number of commands and parameters, like e.g. suppress the units in the header and at every hop. -''' +""" -#*************************************************************************** +# *************************************************************************** # user editable stuff here COMMAND_SPACE = " " -MACHINE_NAME = 'Maho 600E' -CORNER_MIN = {'x': -51.877, 'y': 0, 'z': 0} # use metric for internal units -CORNER_MAX = {'x': 591.5, 'y': 391.498, 'z': 391.5} # use metric for internal units +MACHINE_NAME = "Maho 600E" +CORNER_MIN = {"x": -51.877, "y": 0, "z": 0} # use metric for internal units +CORNER_MAX = {"x": 591.5, "y": 391.498, "z": 391.5} # use metric for internal units -UNITS = 'G21' # use metric units +UNITS = "G21" # use metric units # possible values: # 'G20' for inches, # 'G21' for metric units. @@ -63,7 +63,7 @@ UNITS_INCLUDED = False # do not include the units in the GCode program # usually the units to be used are defined in the machine constants and almost never change, # so this can be set to False. -COMMENT = '' +COMMENT = "" # possible values: # ';' centroid or sinumerik comment symbol, # '' leave blank for bracketed comments style "(comment comment comment)" @@ -108,7 +108,7 @@ MODAL = True # G1 X15 Y30 # suppress these parameters if they haven't changed -MODALPARAMS = ['X', 'Y', 'Z', 'S', 'F'] +MODALPARAMS = ["X", "Y", "Z", "S", "F"] # possible values: # any list of GCode parameters # if a parameter doesn't change from one line to the next ( or even further) it is suppressed. @@ -126,7 +126,9 @@ SWAP_G2_G3 = True # some machines have the sign of the X-axis swapped, so they # this might be special with some maho machines or even with mine and # might be changed in the machine constants as well -SWAP_Y_Z = True # machines with an angle milling head do not switch axes, so we do it here +SWAP_Y_Z = ( + True # machines with an angle milling head do not switch axes, so we do it here +) # possible values: # True if Y and Z values have to be swapped # False do not swap @@ -161,8 +163,12 @@ RADIUS_COMMENT = True # and never with the comment symbol, because the radius might appear in # the middle of a line. -GCODE_MAP = {'M1': 'M0', 'M6': 'M66', 'G20': 'G70', - 'G21': 'G71'} # cb: this could be used to swap G2/G3 +GCODE_MAP = { + "M1": "M0", + "M6": "M66", + "G20": "G70", + "G21": "G71", +} # cb: this could be used to swap G2/G3 # possible values: # Comma separated list of values of the form 'sourceGCode':'targetGCode' # @@ -200,42 +206,56 @@ SUPPRESS_ZERO_FEED = True # def mkHeader(selection): # return '' -parser = argparse.ArgumentParser(prog='philips', add_help=False) -parser.add_argument('--header', action='store_true', help='create header output') -parser.add_argument('--no-header', action='store_true', help='suppress header output') +parser = argparse.ArgumentParser(prog="philips", add_help=False) +parser.add_argument("--header", action="store_true", help="create header output") +parser.add_argument("--no-header", action="store_true", help="suppress header output") -parser.add_argument('--comments', action='store_true', help='create comment output') -parser.add_argument('--no-comments', action='store_true', help='suppress comment output') +parser.add_argument("--comments", action="store_true", help="create comment output") +parser.add_argument( + "--no-comments", action="store_true", help="suppress comment output" +) -parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers') -parser.add_argument('--no-line-numbers', action='store_true', help='omit line number prefixes') +parser.add_argument( + "--line-numbers", action="store_true", help="prefix with line numbers" +) +parser.add_argument( + "--no-line-numbers", action="store_true", help="omit line number prefixes" +) -parser.add_argument('--show-editor', action='store_true', help='pop up editor before writing output') -parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output') +parser.add_argument( + "--show-editor", action="store_true", help="pop up editor before writing output" +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) TOOLTIP_ARGS = parser.format_help() + def processArguments(argstring): global LINENUMBERS global SHOW_EDITOR for arg in argstring.split(): - if arg == '--line-numbers': + if arg == "--line-numbers": LINENUMBERS = True - elif arg == '--no-line-numbers': + elif arg == "--no-line-numbers": LINENUMBERS = False - elif arg == '--show-editor': + elif arg == "--show-editor": SHOW_EDITOR = True - elif arg == '--no-show-editor': + elif arg == "--no-show-editor": SHOW_EDITOR = False + def mkHeader(selection): # job = PathUtils.findParentJob(selection[0]) - # this is within a function, because otherwise filename and time don't change when changing the FreeCAD project + # this is within a function, because otherwise filename and time don't change when changing the FreeCAD project # now = datetime.datetime.now() now = time.strftime("%Y-%m-%d %H:%M") originfile = FreeCAD.ActiveDocument.FileName - headerNoNumber = "%PM\n" # this line gets no linenumber + headerNoNumber = "%PM\n" # this line gets no linenumber # if hasattr(job, "Description"): # description = job.Description # else: @@ -244,18 +264,19 @@ def mkHeader(selection): # this line gets no linenumber, it is already a specially numbered headerNoNumber += "N9XXX (" + description + ", " + now + ")\n" header = "" -# header += "(Output Time:" + str(now) + ")\n" + # header += "(Output Time:" + str(now) + ")\n" header += "(" + originfile + ")\n" -# header += "(Exported by FreeCAD)\n" + # header += "(Exported by FreeCAD)\n" header += "(Post Processor: " + __name__ + ")\n" -# header += "(Target machine: " + MACHINE_NAME + ")\n" - header += "G18\n" # Select XY plane - header += "G90\n" # Absolute coordinates - header += "G51\n" # Reset Zero - header += "G52 (ersetze G55-G59)" # set zero + # header += "(Target machine: " + MACHINE_NAME + ")\n" + header += "G18\n" # Select XY plane + header += "G90\n" # Absolute coordinates + header += "G51\n" # Reset Zero + header += "G52 (ersetze G55-G59)" # set zero return headerNoNumber + linenumberify(header) -GCODE_HEADER = "" # do not terminate with a newline, it is inserted by linenumberify + +GCODE_HEADER = "" # do not terminate with a newline, it is inserted by linenumberify # GCODE_HEADER = "G40 G90" # do not terminate with a newline, it is inserted by linenumberify # possible values: # any sequence of GCode, multiple lines are welcome @@ -270,11 +291,11 @@ GCODE_FOOTER = "M30" # linenumbers are inserted automatically if LINENUMBERS is True # don't edit with the stuff below the next line unless you know what you're doing :) -#*************************************************************************** +# *************************************************************************** linenr = 0 # variable has to be global because it is used by linenumberify and export -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -286,8 +307,21 @@ def angleUnder180(command, lastX, lastY, x, y, i, j): # (or on) the connection line middleOfLineY = (lastY + y) / 2 centerY = lastY + j - if ((command == 'G2' and ((lastX == x and ((lastY < y and i >= 0) or (lastY > y and i <= 0))) or (lastX < x and centerY <= middleOfLineY) or (lastX > x and centerY >= middleOfLineY))) - or (command == 'G3' and ((lastX == x and ((lastY < y and i <= 0) or (lastY > y and i >= 0))) or (lastX < x and centerY >= middleOfLineY) or (lastX > x and centerY <= middleOfLineY)))): + if ( + command == "G2" + and ( + (lastX == x and ((lastY < y and i >= 0) or (lastY > y and i <= 0))) + or (lastX < x and centerY <= middleOfLineY) + or (lastX > x and centerY >= middleOfLineY) + ) + ) or ( + command == "G3" + and ( + (lastX == x and ((lastY < y and i <= 0) or (lastY > y and i >= 0))) + or (lastX < x and centerY >= middleOfLineY) + or (lastX > x and centerY <= middleOfLineY) + ) + ): return True else: return False @@ -299,10 +333,10 @@ def mapGCode(command): else: mappedCommand = command if SWAP_G2_G3: - if command == 'G2': - mappedCommand = 'G3' - elif command == 'G3': - mappedCommand = 'G2' + if command == "G2": + mappedCommand = "G3" + elif command == "G3": + mappedCommand = "G2" return mappedCommand @@ -312,7 +346,7 @@ def linenumberify(GCodeString): if not LINENUMBERS: result = GCodeString + "\n" else: - result = '' + result = "" strList = GCodeString.split("\n") for s in strList: if s: @@ -325,6 +359,7 @@ def linenumberify(GCodeString): result += s + "\n" return result + def export(objectslist, filename, argstring): global UNITS global linenr @@ -333,14 +368,32 @@ def export(objectslist, filename, argstring): lastX = 0 lastY = 0 lastZ = 0 - params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'H', 'S', 'T', - 'Q', 'R', 'L'] # Using XY plane most of the time so skipping K + params = [ + "X", + "Y", + "Z", + "A", + "B", + "I", + "J", + "F", + "H", + "S", + "T", + "Q", + "R", + "L", + ] # Using XY plane most of the time so skipping K modalParamsDict = dict() for mp in MODALPARAMS: modalParamsDict[mp] = None for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return myMachine = None for pathobj in objectslist: @@ -354,23 +407,23 @@ def export(objectslist, filename, argstring): if myMachine is None: print("philips_post: No machine found in this selection") - gcode = '' + gcode = "" gcode += mkHeader(objectslist) gcode += linenumberify(GCODE_HEADER) if UNITS_INCLUDED: gcode += linenumberify(mapGCode(UNITS)) lastcommand = None for obj in objectslist: - if hasattr(obj, 'Comment'): - gcode += linenumberify('(' + obj.Comment + ')') + if hasattr(obj, "Comment"): + gcode += linenumberify("(" + obj.Comment + ")") for c in obj.Path.Commands: outstring = [] command = c.Name - if command != 'G0': - command = command.replace('G0','G') # normalize: G01 -> G1 + if command != "G0": + command = command.replace("G0", "G") # normalize: G01 -> G1 - if (command != UNITS or UNITS_INCLUDED): - if command[0] == '(': + if command != UNITS or UNITS_INCLUDED: + if command[0] == "(": command = PostUtils.fcoms(command, COMMENT) # the mapping is done for output only! For internal things we # still use the old value. @@ -378,63 +431,90 @@ def export(objectslist, filename, argstring): if not MODAL or command != lastcommand: outstring.append(mappedCommand) -# if MODAL == True: -# #\better: append iff MODAL == False -# if command == lastcommand: -# outstring.pop(0) + # if MODAL == True: + # #\better: append iff MODAL == False + # if command == lastcommand: + # outstring.pop(0) if len(c.Parameters) >= 1: for param in params: # test print("param: " + param + ", command: " + command) if param in c.Parameters: - if (param in MODALPARAMS) and (modalParamsDict[str(param)] == c.Parameters[str(param)]): + if (param in MODALPARAMS) and ( + modalParamsDict[str(param)] == c.Parameters[str(param)] + ): # do nothing or append white space - outstring.append(' ') - elif param == 'F': - feed = c.Parameters['F'] + outstring.append(" ") + elif param == "F": + feed = c.Parameters["F"] if SUPPRESS_ZERO_FEED and feed == 0: pass else: outstring.append( - param + PostUtils.fmt(feed, FEED_DECIMALS, UNITS)) - elif param == 'H': - outstring.append( - param + str(int(c.Parameters['H']))) - elif param == 'S': + param + + PostUtils.fmt(feed, FEED_DECIMALS, UNITS) + ) + elif param == "H": + outstring.append(param + str(int(c.Parameters["H"]))) + elif param == "S": # rpm is unitless-therefore I had to 'fake it # out' by using metric units which don't get # converted from entered value outstring.append( - param + PostUtils.fmt(c.Parameters['S'], SPINDLE_DECIMALS, 'G21')) - elif param == 'T': - outstring.append( - param + str(int(c.Parameters['T']))) - elif param == 'I' and (command == 'G2' or command == 'G3'): - #test print("param = 'I'") + param + + PostUtils.fmt( + c.Parameters["S"], SPINDLE_DECIMALS, "G21" + ) + ) + elif param == "T": + outstring.append(param + str(int(c.Parameters["T"]))) + elif param == "I" and (command == "G2" or command == "G3"): + # test print("param = 'I'") # this is the special case for circular paths, # where relative coordinates have to be changed # to absolute - i = c.Parameters['I'] + i = c.Parameters["I"] # calculate the radius r - j = c.Parameters['J'] - r = math.sqrt(i**2 + j**2) - if USE_RADIUS_IF_POSSIBLE and angleUnder180(command, lastX, lastY, c.Parameters['X'], c.Parameters['Y'], i, j): + j = c.Parameters["J"] + r = math.sqrt(i ** 2 + j ** 2) + if USE_RADIUS_IF_POSSIBLE and angleUnder180( + command, + lastX, + lastY, + c.Parameters["X"], + c.Parameters["Y"], + i, + j, + ): outstring.append( - 'R' + PostUtils.fmt(r, AXIS_DECIMALS, UNITS)) + "R" + PostUtils.fmt(r, AXIS_DECIMALS, UNITS) + ) else: if RADIUS_COMMENT: outstring.append( - '(R' + PostUtils.fmt(r, AXIS_DECIMALS, UNITS) + ')') + "(R" + + PostUtils.fmt(r, AXIS_DECIMALS, UNITS) + + ")" + ) if ABSOLUTE_CIRCLE_CENTER: i += lastX outstring.append( - param + PostUtils.fmt(i, AXIS_DECIMALS, UNITS)) - elif param == 'J' and (command == 'G2' or command == 'G3'): + param + PostUtils.fmt(i, AXIS_DECIMALS, UNITS) + ) + elif param == "J" and (command == "G2" or command == "G3"): # this is the special case for circular paths, # where incremental center has to be changed to # absolute center - i = c.Parameters['I'] - j = c.Parameters['J'] - if USE_RADIUS_IF_POSSIBLE and angleUnder180(command, lastX, lastY, c.Parameters['X'], c.Parameters['Y'], i, j): + i = c.Parameters["I"] + j = c.Parameters["J"] + if USE_RADIUS_IF_POSSIBLE and angleUnder180( + command, + lastX, + lastY, + c.Parameters["X"], + c.Parameters["Y"], + i, + j, + ): # R is handled with the I parameter, here: # do nothing at all, keep the structure as # with I command @@ -445,19 +525,36 @@ def export(objectslist, filename, argstring): if SWAP_Y_Z: # we have to swap j and k as well outstring.append( - 'K' + PostUtils.fmt(j, AXIS_DECIMALS, UNITS)) + "K" + PostUtils.fmt(j, AXIS_DECIMALS, UNITS) + ) else: outstring.append( - param + PostUtils.fmt(j, AXIS_DECIMALS, UNITS)) - elif param == 'K' and (command == 'G2' or command == 'G3'): + param + + PostUtils.fmt(j, AXIS_DECIMALS, UNITS) + ) + elif param == "K" and (command == "G2" or command == "G3"): # this is the special case for circular paths, # where incremental center has to be changed to # absolute center outstring.append( - '(' + param + PostUtils.fmt(c.Parameters[param], AXIS_DECIMALS, UNITS) + ')') - z = c.Parameters['Z'] - k = c.Parameters['K'] - if USE_RADIUS_IF_POSSIBLE and angleUnder180(command, lastX, lastY, c.Parameters['X'], c.Parameters['Y'], i, j): + "(" + + param + + PostUtils.fmt( + c.Parameters[param], AXIS_DECIMALS, UNITS + ) + + ")" + ) + z = c.Parameters["Z"] + k = c.Parameters["K"] + if USE_RADIUS_IF_POSSIBLE and angleUnder180( + command, + lastX, + lastY, + c.Parameters["X"], + c.Parameters["Y"], + i, + j, + ): # R is handled with the I parameter, here: # do nothing at all, keep the structure as # with I command @@ -468,47 +565,60 @@ def export(objectslist, filename, argstring): if SWAP_Y_Z: # we have to swap j and k as well outstring.append( - 'J' + PostUtils.fmt(j, AXIS_DECIMALS, UNITS)) + "J" + PostUtils.fmt(j, AXIS_DECIMALS, UNITS) + ) else: outstring.append( - param + PostUtils.fmt(j, AXIS_DECIMALS, UNITS)) - elif param == 'Y' and SWAP_Y_Z: + param + PostUtils.fmt(j, AXIS_DECIMALS, UNITS) + ) + elif param == "Y" and SWAP_Y_Z: outstring.append( - 'Z' + PostUtils.fmt(c.Parameters[param], AXIS_DECIMALS, UNITS)) - elif param == 'Z' and SWAP_Y_Z: + "Z" + + PostUtils.fmt( + c.Parameters[param], AXIS_DECIMALS, UNITS + ) + ) + elif param == "Z" and SWAP_Y_Z: outstring.append( - 'Y' + PostUtils.fmt(c.Parameters[param], AXIS_DECIMALS, UNITS)) + "Y" + + PostUtils.fmt( + c.Parameters[param], AXIS_DECIMALS, UNITS + ) + ) else: -# To Do: suppress unknown commands, if this is done here, all X parameters are suppressed + # To Do: suppress unknown commands, if this is done here, all X parameters are suppressed # this is an unknown command, don't create GCode for it -# print("parameter " + param + " for command " + command + " ignored") + # print("parameter " + param + " for command " + command + " ignored") outstring.append( - param + PostUtils.fmt(c.Parameters[param], AXIS_DECIMALS, UNITS)) + param + + PostUtils.fmt( + c.Parameters[param], AXIS_DECIMALS, UNITS + ) + ) if param in MODALPARAMS: - modalParamsDict[str(param)] = c.Parameters[ - param] + modalParamsDict[str(param)] = c.Parameters[param] # save the last X, Y, Z values - if 'X' in c.Parameters: - lastX = c.Parameters['X'] - if 'Y' in c.Parameters: - lastY = c.Parameters['Y'] - if 'Z' in c.Parameters: - lastZ = c.Parameters['Z'] + if "X" in c.Parameters: + lastX = c.Parameters["X"] + if "Y" in c.Parameters: + lastY = c.Parameters["Y"] + if "Z" in c.Parameters: + lastZ = c.Parameters["Z"] - outstr = '' + outstr = "" for w in outstring: outstr += w + COMMAND_SPACE - outstr = outstr.replace(']', '') - outstr = outstr.replace('[', '') - outstr = outstr.replace("'", '') - outstr = outstr.replace(",", '.') + outstr = outstr.replace("]", "") + outstr = outstr.replace("[", "") + outstr = outstr.replace("'", "") + outstr = outstr.replace(",", ".") if LINENUMBERS: gcode += "N" + str(linenr) + " " linenr += LINENUMBER_INCREMENT - gcode += outstr + '\n' + gcode += outstr + "\n" lastcommand = c.Name - gcode = gcode.replace("_","-") + gcode = gcode.replace("_", "-") gcode += linenumberify(GCODE_FOOTER) if SHOW_EDITOR: PostUtils.editor(gcode) diff --git a/src/Mod/Path/PathScripts/post/rml_post.py b/src/Mod/Path/PathScripts/post/rml_post.py index 3e5e135868..62fa837737 100644 --- a/src/Mod/Path/PathScripts/post/rml_post.py +++ b/src/Mod/Path/PathScripts/post/rml_post.py @@ -1,28 +1,28 @@ -#*************************************************************************** -#* Copyright (c) 2015 Jon Nordby * -#* * -#* This file is part of the FreeCAD CAx development system. * -#* * -#* 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. * -#* * -#* FreeCAD 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 Lesser General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * -#* License along with FreeCAD; if not, write to the Free Software * -#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * -#*************************************************************************** +# *************************************************************************** +# * Copyright (c) 2015 Jon Nordby * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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. * +# * * +# * FreeCAD 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 Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** -TOOLTIP=''' +TOOLTIP = """ FreeCAD Path post-processor to output code for the Roland Modela MDX-## machines. The machine speaks RML-1, specified in 'Roland RML-1 Programming Guidelines' @@ -32,14 +32,14 @@ http://altlab.org/d/content/m/pangelo/ideas/rml_command_guide_en_v100.pdf The format has some overlap with HPGL: https://en.wikipedia.org/wiki/HPGL http://paulbourke.net/dataformats/hpgl/ -''' +""" import FreeCAD import Part import PathScripts.PostUtils as PostUtils # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -51,34 +51,46 @@ def export(objectslist, filename, argstring): for obj in objectslist: code += convertobject(obj) - gfile = pythonopen(filename,"w") + gfile = pythonopen(filename, "w") gfile.write(code) gfile.close() + def convertobject(obj): gcode = obj.Path.toGCode() gcode = parse(gcode) return gcode + def motoron(): - return [ "!MC1;" ] + return ["!MC1;"] + + def motoroff(): - return [ "!MC0;" ] + return ["!MC0;"] + + def home(): - return [ "H;" ] + return ["H;"] + def setjog(): # "!PZ%d,%d;",iz_down,iz_up); // set z down, jog return "" + def addheader(): - return [ "PA;PA;" ] # absolute positioning + return ["PA;PA;"] # absolute positioning + + def addfooter(): return [] + def mm2cord(mm): mm = float(mm) - return int(40.0*mm) + return int(40.0 * mm) + def feed(x=None, y=None, z=None, state=None): c = [] @@ -87,13 +99,13 @@ def feed(x=None, y=None, z=None, state=None): if x is not None: x = float(x) - state['X'] = x + state["X"] = x if y is not None: y = float(y) - state['Y'] = y + state["Y"] = y if z is not None: z = float(z) - state['Z'] = z + state["Z"] = z if x is not None and y is not None and z is not None: # 3d motion @@ -105,6 +117,7 @@ def feed(x=None, y=None, z=None, state=None): pass return c + def jog(x=None, y=None, z=None, state=None): c = [] if state is None: @@ -112,22 +125,23 @@ def jog(x=None, y=None, z=None, state=None): if x is not None and y is not None: x, y = float(x), float(y) c.append("PU%d,%d;" % (mm2cord(x), mm2cord(y))) - state['X'] = x - state['Y'] = y + state["X"] = x + state["Y"] = y if z is not None: z = float(z) c.append("PU;") - state['Z'] = z + state["Z"] = z return c + def xyarc(args, state): # no native support in RML/Modela, convert to linear line segments c = [] - lastPoint = FreeCAD.Vector(state['X'], state['Y']) - newPoint = FreeCAD.Vector(float(args['X']), float(args['Y'])) - centerOffset = FreeCAD.Vector(float(args['I']), float(args['J'])) + lastPoint = FreeCAD.Vector(state["X"], state["Y"]) + newPoint = FreeCAD.Vector(float(args["X"]), float(args["Y"])) + centerOffset = FreeCAD.Vector(float(args["I"]), float(args["J"])) center = lastPoint + centerOffset radius = (center - lastPoint).Length xyNormal = FreeCAD.Vector(0, 0, 1) @@ -135,14 +149,15 @@ def xyarc(args, state): p0 = circle.parameter(lastPoint) p1 = circle.parameter(newPoint) arc = Part.ArcOfCircle(circle, p0, p1) - steps = 64 # specify max error instead? + steps = 64 # specify max error instead? points = arc.discretize(steps) # consider direction? - #print('p = Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(%f, %f), FreeCAD.Vector(0, 0, 1), %f), %f, %f)' % (center.x, center.y, radius, p0, p1)) + # print('p = Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(%f, %f), FreeCAD.Vector(0, 0, 1), %f), %f, %f)' % (center.x, center.y, radius, p0, p1)) for p in points: - c += feed(p.x, p.y, state['Z'], state) + c += feed(p.x, p.y, state["Z"], state) return c + def speed(xy=None, z=None, state=None): c = [] if state is None: @@ -150,66 +165,67 @@ def speed(xy=None, z=None, state=None): print(xy, z, state) if xy is not None: xy = float(xy) - if xy > 0.0 and xy != state['XYspeed']: + if xy > 0.0 and xy != state["XYspeed"]: c.append("VS%.1f;" % xy) - state['XYspeed'] = xy + state["XYspeed"] = xy if z is not None: z = float(z) - if z > 0.0 and z != state['Zspeed']: + if z > 0.0 and z != state["Zspeed"]: c.append("!VZ%.1f;" % z) - state['Zspeed'] = z + state["Zspeed"] = z return c + def convertgcode(cmd, args, state): """Convert a single gcode command to equivalent Roland code""" - if cmd == 'G0': + if cmd == "G0": # jog - return jog(args['X'], args['Y'], args['Z'], state) - elif cmd == 'G1': + return jog(args["X"], args["Y"], args["Z"], state) + elif cmd == "G1": # linear feed c = [] # feedrate - c += speed(xy=args['F'], z=args['F'], state=state) + c += speed(xy=args["F"], z=args["F"], state=state) # motion - c += feed(args['X'], args['Y'], args['Z'], state) + c += feed(args["X"], args["Y"], args["Z"], state) return c - elif cmd == 'G2' or cmd == 'G3': + elif cmd == "G2" or cmd == "G3": # arc feed c = [] # feedrate - c += speed(xy=args['F'], state=state) + c += speed(xy=args["F"], state=state) # motion - if args['X'] and args['Y'] and args['Z']: + if args["X"] and args["Y"] and args["Z"]: # helical motion pass - elif args['X'] and args['Y']: + elif args["X"] and args["Y"]: # arc in plane c += xyarc(args, state) return c - elif cmd == 'G20': + elif cmd == "G20": # inches mode raise ValueError("rml_post: Inches mode not supported") - elif cmd == 'G21': + elif cmd == "G21": # millimeter mode return "" - elif cmd == 'G40': + elif cmd == "G40": # tool compensation off return "" - elif cmd == 'G80': + elif cmd == "G80": # cancel all cycles (drill normally) return "PU;" - elif cmd == 'G81': + elif cmd == "G81": c = [] # feedrate - c += speed(z=args['F'], state=state) + c += speed(z=args["F"], state=state) # motion - c += jog(args['X'], args['Y'], state=state) - c += feed(args['X'], args['Y'], args['Z'], state) + c += jog(args["X"], args["Y"], state=state) + c += feed(args["X"], args["Y"], args["Z"], state) return c - elif cmd == 'G90': + elif cmd == "G90": # absolute mode? return "" - elif cmd == 'G98': + elif cmd == "G98": # feedrate return "" else: @@ -219,14 +235,14 @@ def convertgcode(cmd, args, state): def parse(inputstring): "parse(inputstring): returns a parsed output string" - state = { 'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'XYspeed': -1.0, 'Zspeed': -1.0 } + state = {"X": 0.0, "Y": 0.0, "Z": 0.0, "XYspeed": -1.0, "Zspeed": -1.0} output = [] # header output += addheader() output += motoron() - output += speed(2.0, 1.0, state) # defaults + output += speed(2.0, 1.0, state) # defaults # respect clearance height? @@ -236,13 +252,13 @@ def parse(inputstring): if not line: continue parsed = PostUtils.stringsplit(line) - command = parsed['command'] - print('cmd', line) + command = parsed["command"] + print("cmd", line) try: if command: code = convertgcode(command, parsed, state) if not isinstance(code, list): - code = [ code ] + code = [code] if len(code) and code[0]: output += code except NotImplementedError as e: @@ -253,7 +269,7 @@ def parse(inputstring): output += home() output += addfooter() - return '\n'.join(output) + return "\n".join(output) + # print (__name__ + " gcode postprocessor loaded.") - diff --git a/src/Mod/Path/PathScripts/post/rrf_post.py b/src/Mod/Path/PathScripts/post/rrf_post.py index 9bd976018c..176b2af0db 100644 --- a/src/Mod/Path/PathScripts/post/rrf_post.py +++ b/src/Mod/Path/PathScripts/post/rrf_post.py @@ -34,7 +34,7 @@ from FreeCAD import Units import PathScripts.PathUtil as PathUtil import PathScripts.PostUtils as PostUtils -Revised = '2021-10-21' # Revision date for this file. +Revised = "2021-10-21" # Revision date for this file. # ***************************************************************************** # * Due to the fundamentals of the FreeCAD pre-processor, * @@ -46,154 +46,145 @@ Revised = '2021-10-21' # Revision date for this file. # ***************************************************************************** -TOOLTIP = ''' +TOOLTIP = """ Generate g-code from a Path that is compatible with the Duet controller (RepRapFirmware). import rrf_post rrf_post.export(object, "/path/to/file.nc") -''' +""" # ***************************************************************************** # * Initial configuration, not changeable * # ***************************************************************************** -MOTION_MODE = 'G90' # G90 only, for absolute moves -WORK_PLANE = 'G17' # G17 only, XY plane, for vertical milling -UNITS = 'G21' # G21 only, for metric -UNIT_FORMAT = 'mm' -UNIT_FEED_FORMAT = 'mm/min' +MOTION_MODE = "G90" # G90 only, for absolute moves +WORK_PLANE = "G17" # G17 only, XY plane, for vertical milling +UNITS = "G21" # G21 only, for metric +UNIT_FORMAT = "mm" +UNIT_FEED_FORMAT = "mm/min" # ***************************************************************************** # * Initial configuration, changeable via command line arguments * # ***************************************************************************** -PRECISION = 3 # Decimal places displayed for metric -DRILL_RETRACT_MODE = 'G98' # End of drill-cycle retractation type. G99 +PRECISION = 3 # Decimal places displayed for metric +DRILL_RETRACT_MODE = "G98" # End of drill-cycle retractation type. G99 # is the alternative. -TRANSLATE_DRILL_CYCLES = True # If true, G81, G82, and G83 are translated +TRANSLATE_DRILL_CYCLES = True # If true, G81, G82, and G83 are translated # into G0/G1 moves -RETURN_TO = None # None = No movement at end of program -SPINDLE_WAIT = 3 # 0 == No waiting after M3 / M4 -MODAL = False # True: Commands are suppressed if they are +RETURN_TO = None # None = No movement at end of program +SPINDLE_WAIT = 3 # 0 == No waiting after M3 / M4 +MODAL = False # True: Commands are suppressed if they are # the same as the previous line -LINENR = 100 # Line number starting value -LINEINCR = 10 # Line number increment -PRE_OPERATION = '''''' # Pre operation text will be inserted before +LINENR = 100 # Line number starting value +LINEINCR = 10 # Line number increment +PRE_OPERATION = """""" # Pre operation text will be inserted before # every operation -POST_OPERATION = '''''' # Post operation text will be inserted after +POST_OPERATION = """""" # Post operation text will be inserted after # every operation -TOOL_CHANGE = '''''' # Tool Change commands will be inserted +TOOL_CHANGE = """""" # Tool Change commands will be inserted # before a tool change # ***************************************************************************** # * Initial gcode output options, changeable via command line arguments * # ***************************************************************************** -OUTPUT_HEADER = True # Output header in output gcode file -OUTPUT_COMMENTS = True # Comments in output gcode file -OUTPUT_FINISH = False # Include an operation finished comment -OUTPUT_PATH = False # Include a Path: comment -OUTPUT_RRF_CONFIG = True # Display expected #defines for RRF config -OUTPUT_LINE_NUMBERS = False # Output line numbers in output gcode file -OUTPUT_BCNC = False # Add bCNC operation block headers in output +OUTPUT_HEADER = True # Output header in output gcode file +OUTPUT_COMMENTS = True # Comments in output gcode file +OUTPUT_FINISH = False # Include an operation finished comment +OUTPUT_PATH = False # Include a Path: comment +OUTPUT_RRF_CONFIG = True # Display expected #defines for RRF config +OUTPUT_LINE_NUMBERS = False # Output line numbers in output gcode file +OUTPUT_BCNC = False # Add bCNC operation block headers in output # gcode file -SHOW_EDITOR = True # Display the resulting gcode file +SHOW_EDITOR = True # Display the resulting gcode file OUTPUT_TOOL_CHANGE = True # ***************************************************************************** # * Command line arguments * # ***************************************************************************** -parser = argparse.ArgumentParser(prog='rrf', add_help=False) +parser = argparse.ArgumentParser(prog="rrf", add_help=False) +parser.add_argument("--header", action="store_true", help="output headers (default)") +parser.add_argument("--no-header", action="store_true", help="suppress header output") +parser.add_argument("--comments", action="store_true", help="output comment (default)") parser.add_argument( - '--header', - action='store_true', - help='output headers (default)') + "--no-comments", action="store_true", help="suppress comment output" +) parser.add_argument( - '--no-header', - action='store_true', - help='suppress header output') + "--finish-comments", action="store_true", help="output finish-comment" +) parser.add_argument( - '--comments', - action='store_true', - help='output comment (default)') + "--no-finish-comments", + action="store_true", + help="suppress finish-comment output (default)", +) +parser.add_argument("--path-comments", action="store_true", help="output path-comment") parser.add_argument( - '--no-comments', - action='store_true', - help='suppress comment output') + "--no-path-comments", + action="store_true", + help="suppress path-comment output (default)", +) +parser.add_argument("--rrf-config", action="store_true", help="output #defines for RRF") parser.add_argument( - '--finish-comments', - action='store_true', - help='output finish-comment') + "--no-rrf-config", + action="store_true", + help="suppress output #defines for RRF (default)", +) parser.add_argument( - '--no-finish-comments', - action='store_true', - help='suppress finish-comment output (default)') + "--line-numbers", action="store_true", help="prefix with line numbers" +) parser.add_argument( - '--path-comments', - action='store_true', - help='output path-comment') + "--no-line-numbers", + action="store_true", + help="do not prefix with line numbers (default)", +) parser.add_argument( - '--no-path-comments', - action='store_true', - help='suppress path-comment output (default)') + "--show-editor", + action="store_true", + help="pop up editor before writing output (default)", +) parser.add_argument( - '--rrf-config', - action='store_true', - help='output #defines for RRF') + "--no-show-editor", + action="store_true", + help="do not pop up editor before writing output", +) parser.add_argument( - '--no-rrf-config', - action='store_true', - help='suppress output #defines for RRF (default)') + "--precision", default="3", help="number of digits of precision, default=3" +) parser.add_argument( - '--line-numbers', - action='store_true', - help='prefix with line numbers') + "--translate_drill", + action="store_true", + help="translate drill cycles G81, G82, G83 into G0/G1 movements (default)", +) parser.add_argument( - '--no-line-numbers', - action='store_true', - help='do not prefix with line numbers (default)') + "--no-translate_drill", + action="store_true", + help="do not translate drill cycles G81, G82, G83 into G0/G1 movements", +) parser.add_argument( - '--show-editor', - action='store_true', - help='pop up editor before writing output (default)') + "--preamble", help='set commands to be issued before the first command, default=""' +) parser.add_argument( - '--no-show-editor', - action='store_true', - help='do not pop up editor before writing output') + "--postamble", help='set commands to be issued after the last command, default="M5"' +) parser.add_argument( - '--precision', - default='3', - help='number of digits of precision, default=3') + "--tool-change", action="store_true", help="Insert M6 for all tool changes" +) parser.add_argument( - '--translate_drill', - action='store_true', - help='translate drill cycles G81, G82, G83 into G0/G1 movements (default)') -parser.add_argument( - '--no-translate_drill', - action='store_true', - help='do not translate drill cycles G81, G82, G83 into G0/G1 movements') -parser.add_argument( - '--preamble', - help='set commands to be issued before the first command, default=""') -parser.add_argument( - '--postamble', - help='set commands to be issued after the last command, default="M5"') -parser.add_argument( - '--tool-change', action='store_true', - help='Insert M6 for all tool changes') -parser.add_argument( - '--wait-for-spindle', + "--wait-for-spindle", type=int, default=3, - help='Wait for spindle to reach desired speed after M3 or M4, default=0') + help="Wait for spindle to reach desired speed after M3 or M4, default=0", +) parser.add_argument( - '--return-to', - default='', - help='When done, move to, e.g. --return-to="3.175, 4.702, 50.915"') + "--return-to", + default="", + help='When done, move to, e.g. --return-to="3.175, 4.702, 50.915"', +) parser.add_argument( - '--bcnc', - action='store_true', - help='Add Job operations as bCNC block headers. \ - Consider suppressing existing comments: Add argument --no-comments') + "--bcnc", + action="store_true", + help="Add Job operations as bCNC block headers. \ + Consider suppressing existing comments: Add argument --no-comments", +) parser.add_argument( - '--no-bcnc', - action='store_true', - help='suppress bCNC block header output (default)') + "--no-bcnc", action="store_true", help="suppress bCNC block header output (default)" +) TOOLTIP_ARGS = parser.format_help() # ***************************************************************************** @@ -207,19 +198,19 @@ TOOLTIP_ARGS = parser.format_help() # ***************************************************************************** # Default preamble text will appear at the beginning of the gcode output file. -PREAMBLE = '''''' +PREAMBLE = """""" # Default postamble text will appear following the last operation. -POSTAMBLE = '''M5 -''' +POSTAMBLE = """M5 +""" # ***************************************************************************** # * Internal global variables * # ***************************************************************************** -MOTION_COMMANDS = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03'] -RAPID_MOVES = ['G0', 'G00'] # Rapid moves gcode commands definition +MOTION_COMMANDS = ["G0", "G00", "G1", "G01", "G2", "G02", "G3", "G03"] +RAPID_MOVES = ["G0", "G00"] # Rapid moves gcode commands definition SUPPRESS_COMMANDS = [] # These commands are ignored by commenting them out -COMMAND_SPACE = ' ' +COMMAND_SPACE = " " # Global variables storing current position (Use None for safety.) CURRENT_X = None CURRENT_Y = None @@ -288,9 +279,9 @@ def processArguments(argstring): OUTPUT_TOOL_CHANGE = True if args.return_to: RETURN_TO = args.return_to - if RETURN_TO.find(',') == -1: + if RETURN_TO.find(",") == -1: RETURN_TO = None - print('--return-to coordinates must be specified as:') + print("--return-to coordinates must be specified as:") print('--return-to "x.n,y.n,z.n"') if args.bcnc: OUTPUT_BCNC = True @@ -309,14 +300,14 @@ def processArguments(argstring): def dump(obj): for attr in dir(obj): try: - if attr.startswith('__'): + if attr.startswith("__"): continue - print('>' + attr + '<') + print(">" + attr + "<") attr_text = "%s = %s" % (attr, getattr(obj, attr)) - if attr in ['HorizFeed', 'VertFeed']: - print('==============\n', attr_text) - if 'mm/s' in attr_text: - print('===> metric values <===') + if attr in ["HorizFeed", "VertFeed"]: + print("==============\n", attr_text) + if "mm/s" in attr_text: + print("===> metric values <===") except Exception: # Insignificant errors # print('==>', obj, attr) pass @@ -332,123 +323,129 @@ def export(objectslist, filename, argstring): global MOTION_MODE global SUPPRESS_COMMANDS - print('Post Processor: ' + __name__ + ' postprocessing...') - gcode = '' + print("Post Processor: " + __name__ + " postprocessing...") + gcode = "" # Write header: if OUTPUT_HEADER: - gcode += linenumber() + '(Exported by FreeCAD)\n' - gcode += linenumber() + '(Post Processor: ' + __name__ - gcode += '.py, version: ' + Revised + ')\n' - gcode += linenumber() + '(Output Time:' + str(datetime.now()) + ')\n' + gcode += linenumber() + "(Exported by FreeCAD)\n" + gcode += linenumber() + "(Post Processor: " + __name__ + gcode += ".py, version: " + Revised + ")\n" + gcode += linenumber() + "(Output Time:" + str(datetime.now()) + ")\n" # Suppress drill-cycle commands: if TRANSLATE_DRILL_CYCLES: - SUPPRESS_COMMANDS += ['G80', 'G98', 'G99'] + SUPPRESS_COMMANDS += ["G80", "G98", "G99"] # Write the preamble: if OUTPUT_COMMENTS: - gcode += linenumber() + '(Begin preamble)\n' + gcode += linenumber() + "(Begin preamble)\n" for line in PREAMBLE.splitlines(True): gcode += linenumber() + line # Write these settings AFTER the preamble, # to prevent the preamble from changing these: if OUTPUT_COMMENTS: - gcode += linenumber() + '(Default Configuration)\n' - gcode += linenumber() + MOTION_MODE + '\n' - gcode += linenumber() + UNITS + '\n' - gcode += linenumber() + WORK_PLANE + '\n' + gcode += linenumber() + "(Default Configuration)\n" + gcode += linenumber() + MOTION_MODE + "\n" + gcode += linenumber() + UNITS + "\n" + gcode += linenumber() + WORK_PLANE + "\n" for obj in objectslist: # Debug... # print('\n' + '*'*70 + '\n') # dump(obj) # print('\n' + '*'*70 + '\n') - if not hasattr(obj, 'Path'): - print('The object ' + obj.Name + - ' is not a path. Please select only path and Compounds.') + if not hasattr(obj, "Path"): + print( + "The object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return # Skip inactive operations: - if PathUtil.opProperty(obj, 'Active') is False: + if PathUtil.opProperty(obj, "Active") is False: continue # Do the pre_op: if OUTPUT_BCNC: - gcode += linenumber() + '(Block-name: ' + obj.Label + ')\n' - gcode += linenumber() + '(Block-expand: 0)\n' - gcode += linenumber() + '(Block-enable: 1)\n' + gcode += linenumber() + "(Block-name: " + obj.Label + ")\n" + gcode += linenumber() + "(Block-expand: 0)\n" + gcode += linenumber() + "(Block-enable: 1)\n" if OUTPUT_COMMENTS: - gcode += linenumber() + '(Begin operation: ' + obj.Label + ')\n' + gcode += linenumber() + "(Begin operation: " + obj.Label + ")\n" for line in PRE_OPERATION.splitlines(True): gcode += linenumber() + line # Get coolant mode: - coolantMode = 'None' # None is the word returned from the operation - if hasattr(obj, 'CoolantMode') or hasattr(obj, 'Base') and \ - hasattr(obj.Base, 'CoolantMode'): - if hasattr(obj, 'CoolantMode'): + coolantMode = "None" # None is the word returned from the operation + if ( + hasattr(obj, "CoolantMode") + or hasattr(obj, "Base") + and hasattr(obj.Base, "CoolantMode") + ): + if hasattr(obj, "CoolantMode"): coolantMode = obj.CoolantMode else: coolantMode = obj.Base.CoolantMode # Turn coolant on if required: if OUTPUT_COMMENTS: - if not coolantMode == 'None': - gcode += linenumber() + '(Coolant On:' + coolantMode + ')\n' - if coolantMode == 'Flood': - gcode += linenumber() + 'M8\n' - if coolantMode == 'Mist': - gcode += linenumber() + 'M7\n' + if not coolantMode == "None": + gcode += linenumber() + "(Coolant On:" + coolantMode + ")\n" + if coolantMode == "Flood": + gcode += linenumber() + "M8\n" + if coolantMode == "Mist": + gcode += linenumber() + "M7\n" # Parse the op: gcode += parse(obj) # Do the post_op: if OUTPUT_COMMENTS and OUTPUT_FINISH: - gcode += linenumber() + '(Finish operation: ' + obj.Label + ')\n' + gcode += linenumber() + "(Finish operation: " + obj.Label + ")\n" for line in POST_OPERATION.splitlines(True): gcode += linenumber() + line # Turn coolant off if previously enabled: - if not coolantMode == 'None': + if not coolantMode == "None": if OUTPUT_COMMENTS: - gcode += linenumber() + '(Coolant Off:' + coolantMode + ')\n' - gcode += linenumber() + 'M9\n' + gcode += linenumber() + "(Coolant Off:" + coolantMode + ")\n" + gcode += linenumber() + "M9\n" # Do the post_amble: if OUTPUT_BCNC: - gcode += linenumber() + '(Block-name: post_amble)\n' - gcode += linenumber() + '(Block-expand: 0)\n' - gcode += linenumber() + '(Block-enable: 1)\n' + gcode += linenumber() + "(Block-name: post_amble)\n" + gcode += linenumber() + "(Block-expand: 0)\n" + gcode += linenumber() + "(Block-enable: 1)\n" if OUTPUT_COMMENTS: - gcode += linenumber() + '(Begin postamble)\n' + gcode += linenumber() + "(Begin postamble)\n" for line in POSTAMBLE.splitlines(True): gcode += linenumber() + line # Optionally add a final XYZ position to the end of the gcode: if RETURN_TO: - first_comma = RETURN_TO.find(',') - last_comma = RETURN_TO.rfind(',') # == first_comma if only one comma - ref_X = ' X' + RETURN_TO[0: first_comma].strip() + first_comma = RETURN_TO.find(",") + last_comma = RETURN_TO.rfind(",") # == first_comma if only one comma + ref_X = " X" + RETURN_TO[0:first_comma].strip() # Z is optional: if last_comma != first_comma: - ref_Z = ' Z' + RETURN_TO[last_comma + 1:].strip() - ref_Y = ' Y' + RETURN_TO[first_comma + 1:last_comma].strip() + ref_Z = " Z" + RETURN_TO[last_comma + 1 :].strip() + ref_Y = " Y" + RETURN_TO[first_comma + 1 : last_comma].strip() else: - ref_Z = '' - ref_Y = ' Y' + RETURN_TO[first_comma + 1:].strip() + ref_Z = "" + ref_Y = " Y" + RETURN_TO[first_comma + 1 :].strip() - gcode += linenumber() + 'G0' + ref_X + ref_Y + ref_Z + '\n' + gcode += linenumber() + "G0" + ref_X + ref_Y + ref_Z + "\n" # Optionally add recommended RRF configuration to gcode file: if OUTPUT_RRF_CONFIG: - gcode += linenumber() + '(RRF Configuration)\n' - gcode += linenumber() + '(The following should be enabled in)\n' - gcode += linenumber() + '(the config.g)\n' - gcode += linenumber() + '(M453)\n' + gcode += linenumber() + "(RRF Configuration)\n" + gcode += linenumber() + "(The following should be enabled in)\n" + gcode += linenumber() + "(the config.g)\n" + gcode += linenumber() + "(M453)\n" # Show the gcode result dialog: if FreeCAD.GuiUp and SHOW_EDITOR: @@ -462,26 +459,26 @@ def export(objectslist, filename, argstring): else: final = gcode - print('Done postprocessing.') + print("Done postprocessing.") # Write the file: - with open(filename, 'w') as fp: + with open(filename, "w") as fp: fp.write(final) def linenumber(): if not OUTPUT_LINE_NUMBERS: - return '' + return "" global LINENR global LINEINCR LINENR += LINEINCR - return 'N' + str(LINENR) + ' ' + return "N" + str(LINENR) + " " def format_outlist(strTable): # construct the line for the final output global COMMAND_SPACE - s = '' + s = "" for w in strTable: s += w + COMMAND_SPACE return s.strip() @@ -494,27 +491,46 @@ def parse(pathobj): global CURRENT_Y global CURRENT_Z - out = '' + out = "" lastcommand = None - precision_string = '.' + str(PRECISION) + 'f' + precision_string = "." + str(PRECISION) + "f" - params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V', 'W', 'I', 'J', 'K', 'F', - 'S', 'T', 'Q', 'R', 'L', 'P'] + params = [ + "X", + "Y", + "Z", + "A", + "B", + "C", + "U", + "V", + "W", + "I", + "J", + "K", + "F", + "S", + "T", + "Q", + "R", + "L", + "P", + ] - if hasattr(pathobj, 'Group'): # We have a compound or project. + if hasattr(pathobj, "Group"): # We have a compound or project. if OUTPUT_COMMENTS: - out += linenumber() + '(Compound: ' + pathobj.Label + ')\n' + out += linenumber() + "(Compound: " + pathobj.Label + ")\n" for p in pathobj.Group: out += parse(p) return out else: # Parsing simple path # groups might contain non-path things like stock. - if not hasattr(pathobj, 'Path'): + if not hasattr(pathobj, "Path"): return out if OUTPUT_COMMENTS and OUTPUT_PATH: - out += linenumber() + '(Path: ' + pathobj.Label + ')\n' + out += linenumber() + "(Path: " + pathobj.Label + ")\n" for c in pathobj.Path.Commands: outlist = [] @@ -531,107 +547,112 @@ def parse(pathobj): # Add the remaining parameters in order: for param in params: if param in c.Parameters: - if param == 'F': + if param == "F": if command not in RAPID_MOVES: feedRate = Units.Quantity( - c.Parameters['F'], FreeCAD.Units.Velocity) + c.Parameters["F"], FreeCAD.Units.Velocity + ) if feedRate.getValueAs(UNIT_FEED_FORMAT) > 0.0: - outlist.append(param + format(float( - feedRate.getValueAs(UNIT_FEED_FORMAT)), - precision_string)) - elif param =='T': - outlist.append(param + str(int(c.Parameters[param]))) - - elif param in ['H', 'D', 'S', 'P', 'L']: + outlist.append( + param + + format( + float(feedRate.getValueAs(UNIT_FEED_FORMAT)), + precision_string, + ) + ) + elif param == "T": + outlist.append(param + str(int(c.Parameters[param]))) + + elif param in ["H", "D", "S", "P", "L"]: outlist.append(param + str(c.Parameters[param])) - elif param in ['A', 'B', 'C']: - outlist.append(param + format( - c.Parameters[param], precision_string)) + elif param in ["A", "B", "C"]: + outlist.append( + param + format(c.Parameters[param], precision_string) + ) # [X, Y, Z, U, V, W, I, J, K, R, Q] else: - pos = Units.Quantity( - c.Parameters[param], FreeCAD.Units.Length) - outlist.append(param + format(float( - pos.getValueAs(UNIT_FORMAT)), precision_string)) + pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) + outlist.append( + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), precision_string + ) + ) # Store the latest command: lastcommand = command # Capture the current position for subsequent calculations: if command in MOTION_COMMANDS: - if 'X' in c.Parameters: - CURRENT_X = Units.Quantity( - c.Parameters['X'], FreeCAD.Units.Length) - if 'Y' in c.Parameters: - CURRENT_Y = Units.Quantity( - c.Parameters['Y'], FreeCAD.Units.Length) - if 'Z' in c.Parameters: - CURRENT_Z = Units.Quantity( - c.Parameters['Z'], FreeCAD.Units.Length) + if "X" in c.Parameters: + CURRENT_X = Units.Quantity(c.Parameters["X"], FreeCAD.Units.Length) + if "Y" in c.Parameters: + CURRENT_Y = Units.Quantity(c.Parameters["Y"], FreeCAD.Units.Length) + if "Z" in c.Parameters: + CURRENT_Z = Units.Quantity(c.Parameters["Z"], FreeCAD.Units.Length) - if command in ('G98', 'G99'): + if command in ("G98", "G99"): DRILL_RETRACT_MODE = command if TRANSLATE_DRILL_CYCLES: - if command in ('G81', 'G82', 'G83'): + if command in ("G81", "G82", "G83"): out += drill_translate(outlist, command, c.Parameters) # Erase the line just translated: outlist = [] if SPINDLE_WAIT > 0: - if command in ('M3', 'M03', 'M4', 'M04'): - out += linenumber() + format_outlist(outlist) + '\n' + if command in ("M3", "M03", "M4", "M04"): + out += linenumber() + format_outlist(outlist) + "\n" # RRF: P for milliseconds, S for seconds, change P to S out += linenumber() - out += format_outlist(['G4', 'S%s' % SPINDLE_WAIT]) - out += '\n' + out += format_outlist(["G4", "S%s" % SPINDLE_WAIT]) + out += "\n" outlist = [] - # Check for Tool Change: - if command in ('M6', 'M06'): + if command in ("M6", "M06"): if OUTPUT_COMMENTS: - out += linenumber() + '(Begin toolchange)\n' + out += linenumber() + "(Begin toolchange)\n" if OUTPUT_TOOL_CHANGE: for line in TOOL_CHANGE.splitlines(True): - out += linenumber() + line + '\n' - outlist[0] = ' ' - outlist[-1] =('T' + str(int(c.Parameters['T']))) + out += linenumber() + line + "\n" + outlist[0] = " " + outlist[-1] = "T" + str(int(c.Parameters["T"])) if not OUTPUT_TOOL_CHANGE and OUTPUT_COMMENTS: -# next 2 lines could also be replaced by a single line as "outlist = []" - outlist[0] = ' ' - outlist[-1] = ' ' + # next 2 lines could also be replaced by a single line as "outlist = []" + outlist[0] = " " + outlist[-1] = " " if not OUTPUT_TOOL_CHANGE and not OUTPUT_COMMENTS: outlist = [] - if command == 'message': + if command == "message": if OUTPUT_COMMENTS: outlist.pop(0) # remove the command else: out = [] if command in SUPPRESS_COMMANDS: - outlist[0] = '(' + outlist[0] - outlist[-1] = outlist[-1] + ')' + outlist[0] = "(" + outlist[0] + outlist[-1] = outlist[-1] + ")" # Remove embedded comments: if not OUTPUT_COMMENTS: tmplist = [] list_index = 0 while list_index < len(outlist): - left_index = outlist[list_index].find('(') + left_index = outlist[list_index].find("(") if left_index == -1: # Not a comment tmplist.append(outlist[list_index]) else: # This line contains a comment, and possibly more - right_index = outlist[list_index].find(')') - comment_area = outlist[list_index][ - left_index: right_index + 1] - line_minus_comment = outlist[list_index].replace( - comment_area, '').strip() + right_index = outlist[list_index].find(")") + comment_area = outlist[list_index][left_index : right_index + 1] + line_minus_comment = ( + outlist[list_index].replace(comment_area, "").strip() + ) if line_minus_comment: # Line contained more than just a comment tmplist.append(line_minus_comment) @@ -641,7 +662,7 @@ def parse(pathobj): # Prepend a line number and append a newline if len(outlist) >= 1: - out += linenumber() + format_outlist(outlist) + '\n' + out += linenumber() + format_outlist(outlist) + "\n" return out @@ -662,61 +683,56 @@ def drill_translate(outlist, cmd, params): global UNIT_FEED_FORMAT class Drill: # Using a class is necessary for the nested functions. - gcode = '' + gcode = "" - strFormat = '.' + str(PRECISION) + 'f' + strFormat = "." + str(PRECISION) + "f" if OUTPUT_COMMENTS: # Comment the original command - outlist[0] = '(' + outlist[0] - outlist[-1] = outlist[-1] + ')' - Drill.gcode += linenumber() + format_outlist(outlist) + '\n' + outlist[0] = "(" + outlist[0] + outlist[-1] = outlist[-1] + ")" + Drill.gcode += linenumber() + format_outlist(outlist) + "\n" # Cycle conversion only converts the cycles in the XY plane (G17). # --> ZX (G18) and YZ (G19) planes produce false gcode. - drill_X = Units.Quantity(params['X'], FreeCAD.Units.Length) - drill_Y = Units.Quantity(params['Y'], FreeCAD.Units.Length) - drill_Z = Units.Quantity(params['Z'], FreeCAD.Units.Length) - drill_R = Units.Quantity(params['R'], FreeCAD.Units.Length) - drill_F = Units.Quantity(params['F'], FreeCAD.Units.Velocity) - if cmd == 'G82': - drill_DwellTime = params['P'] - elif cmd == 'G83': - drill_Step = Units.Quantity(params['Q'], FreeCAD.Units.Length) + drill_X = Units.Quantity(params["X"], FreeCAD.Units.Length) + drill_Y = Units.Quantity(params["Y"], FreeCAD.Units.Length) + drill_Z = Units.Quantity(params["Z"], FreeCAD.Units.Length) + drill_R = Units.Quantity(params["R"], FreeCAD.Units.Length) + drill_F = Units.Quantity(params["F"], FreeCAD.Units.Velocity) + if cmd == "G82": + drill_DwellTime = params["P"] + elif cmd == "G83": + drill_Step = Units.Quantity(params["Q"], FreeCAD.Units.Length) # R less than Z is error if drill_R < drill_Z: - Drill.gcode += linenumber() + '(drill cycle error: R less than Z )\n' + Drill.gcode += linenumber() + "(drill cycle error: R less than Z )\n" return Drill.gcode # Z height to retract to when drill cycle is done: - if DRILL_RETRACT_MODE == 'G98' and CURRENT_Z > drill_R: + if DRILL_RETRACT_MODE == "G98" and CURRENT_Z > drill_R: RETRACT_Z = CURRENT_Z else: RETRACT_Z = drill_R # Z motion nested functions: def rapid_Z_to(new_Z): - Drill.gcode += linenumber() + 'G0 Z' - Drill.gcode += format( - float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + '\n' + Drill.gcode += linenumber() + "G0 Z" + Drill.gcode += format(float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n" def feed_Z_to(new_Z): - Drill.gcode += linenumber() + 'G1 Z' - Drill.gcode += format( - float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + ' F' - Drill.gcode += format( - float(drill_F.getValueAs(UNIT_FEED_FORMAT)), '.2f') + '\n' + Drill.gcode += linenumber() + "G1 Z" + Drill.gcode += format(float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + " F" + Drill.gcode += format(float(drill_F.getValueAs(UNIT_FEED_FORMAT)), ".2f") + "\n" # Make sure that Z is not below RETRACT_Z: if CURRENT_Z < RETRACT_Z: rapid_Z_to(RETRACT_Z) # Rapid to hole position XY: - Drill.gcode += linenumber() + 'G0 X' - Drill.gcode += format( - float(drill_X.getValueAs(UNIT_FORMAT)), strFormat) + ' Y' - Drill.gcode += format( - float(drill_Y.getValueAs(UNIT_FORMAT)), strFormat) + '\n' + Drill.gcode += linenumber() + "G0 X" + Drill.gcode += format(float(drill_X.getValueAs(UNIT_FORMAT)), strFormat) + " Y" + Drill.gcode += format(float(drill_Y.getValueAs(UNIT_FORMAT)), strFormat) + "\n" # Rapid to R: rapid_Z_to(drill_R) @@ -732,13 +748,13 @@ def drill_translate(outlist, cmd, params): # * G99 After the hole has been drilled, retract to R height * # * Select G99 only if safe to move from hole to hole at the R height * # ************************************************************************* - if cmd in ('G81', 'G82'): + if cmd in ("G81", "G82"): feed_Z_to(drill_Z) # Drill hole in one step - if cmd == 'G82': # Dwell time delay at the bottom of the hole - Drill.gcode += linenumber() + 'G4 S' + str(drill_DwellTime) + '\n' + if cmd == "G82": # Dwell time delay at the bottom of the hole + Drill.gcode += linenumber() + "G4 S" + str(drill_DwellTime) + "\n" # RRF uses P for milliseconds, S for seconds, change P to S - elif cmd == 'G83': # Peck drill cycle: + elif cmd == "G83": # Peck drill cycle: chip_Space = drill_Step * 0.5 next_Stop_Z = drill_R - drill_Step while next_Stop_Z >= drill_Z: @@ -772,5 +788,3 @@ def drill_translate(outlist, cmd, params): # PEP8 format passed using: http://pep8online.com/, which primarily covers # indentation and line length. Some other aspects of PEP8 which have not # been applied yet may be applied in future updates. - - diff --git a/src/Mod/Path/PathScripts/post/slic3r_pre.py b/src/Mod/Path/PathScripts/post/slic3r_pre.py index f9544f9c57..69084e74f7 100644 --- a/src/Mod/Path/PathScripts/post/slic3r_pre.py +++ b/src/Mod/Path/PathScripts/post/slic3r_pre.py @@ -21,16 +21,16 @@ # * * # *************************************************************************** -''' +""" This is an preprocessor to read gcode files produced from slic3r. -''' +""" import os import Path import FreeCAD # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__','io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -93,5 +93,4 @@ def parse(inputstring): return output -print (__name__ + " gcode preprocessor loaded.") - +print(__name__ + " gcode preprocessor loaded.") diff --git a/src/Mod/Path/PathScripts/post/smoothie_post.py b/src/Mod/Path/PathScripts/post/smoothie_post.py index 6b96609db6..85f42a1506 100644 --- a/src/Mod/Path/PathScripts/post/smoothie_post.py +++ b/src/Mod/Path/PathScripts/post/smoothie_post.py @@ -30,7 +30,7 @@ import FreeCAD from FreeCAD import Units import shlex -TOOLTIP = ''' +TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output real GCode suitable for a smoothieboard. This postprocessor, once placed @@ -39,25 +39,55 @@ FreeCAD, via the GUI importer or via python scripts with: import smoothie_post smoothie_post.export(object,"/path/to/file.ncc","") -''' +""" now = datetime.datetime.now() -parser = argparse.ArgumentParser(prog='linuxcnc', add_help=False) -parser.add_argument('--header', action='store_true', help='output headers (default)') -parser.add_argument('--no-header', action='store_true', help='suppress header output') -parser.add_argument('--comments', action='store_true', help='output comment (default)') -parser.add_argument('--no-comments', action='store_true', help='suppress comment output') -parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers') -parser.add_argument('--no-line-numbers', action='store_true', help='don\'t prefix with line numbers (default)') -parser.add_argument('--show-editor', action='store_true', help='pop up editor before writing output (default)') -parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output') -parser.add_argument('--precision', default='4', help='number of digits of precision, default=4') -parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"') -parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"') -parser.add_argument('--IP_ADDR', help='IP Address for machine target machine') -parser.add_argument('--verbose', action='store_true', help='verbose output for debugging, default="False"') -parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)') +parser = argparse.ArgumentParser(prog="linuxcnc", add_help=False) +parser.add_argument("--header", action="store_true", help="output headers (default)") +parser.add_argument("--no-header", action="store_true", help="suppress header output") +parser.add_argument("--comments", action="store_true", help="output comment (default)") +parser.add_argument( + "--no-comments", action="store_true", help="suppress comment output" +) +parser.add_argument( + "--line-numbers", action="store_true", help="prefix with line numbers" +) +parser.add_argument( + "--no-line-numbers", + action="store_true", + help="don't prefix with line numbers (default)", +) +parser.add_argument( + "--show-editor", + action="store_true", + help="pop up editor before writing output (default)", +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) +parser.add_argument( + "--precision", default="4", help="number of digits of precision, default=4" +) +parser.add_argument( + "--preamble", + help='set commands to be issued before the first command, default="G17\nG90"', +) +parser.add_argument( + "--postamble", + help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"', +) +parser.add_argument("--IP_ADDR", help="IP Address for machine target machine") +parser.add_argument( + "--verbose", + action="store_true", + help='verbose output for debugging, default="False"', +) +parser.add_argument( + "--inches", action="store_true", help="Convert output for US imperial mode (G20)" +) TOOLTIP_ARGS = parser.format_help() @@ -80,38 +110,38 @@ LINENR = 100 # line number starting value # These globals will be reflected in the Machine configuration of the project UNITS = "G21" # G21 for metric, G20 for us standard -UNIT_SPEED_FORMAT = 'mm/min' -UNIT_FORMAT = 'mm' +UNIT_SPEED_FORMAT = "mm/min" +UNIT_FORMAT = "mm" MACHINE_NAME = "SmoothieBoard" -CORNER_MIN = {'x': 0, 'y': 0, 'z': 0} -CORNER_MAX = {'x': 500, 'y': 300, 'z': 300} +CORNER_MIN = {"x": 0, "y": 0, "z": 0} +CORNER_MAX = {"x": 500, "y": 300, "z": 300} # Preamble text will appear at the beginning of the GCODE output file. -PREAMBLE = '''G17 G90 -''' +PREAMBLE = """G17 G90 +""" # Postamble text will appear following the last operation. -POSTAMBLE = '''M05 +POSTAMBLE = """M05 G17 G90 M2 -''' +""" # Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" # Number of digits after the decimal point PRECISION = 5 # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__', 'io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open @@ -155,9 +185,9 @@ def processArguments(argstring): if args.postamble is not None: POSTAMBLE = args.postamble if args.inches: - UNITS = 'G20' - UNIT_SPEED_FORMAT = 'in/min' - UNIT_FORMAT = 'in' + UNITS = "G20" + UNIT_SPEED_FORMAT = "in/min" + UNIT_FORMAT = "in" IP_ADDR = args.IP_ADDR VERBOSE = args.verbose @@ -173,7 +203,11 @@ def export(objectslist, filename, argstring): global UNITS for obj in objectslist: if not hasattr(obj, "Path"): - FreeCAD.Console.PrintError("the object " + obj.Name + " is not a path. Please select only path and Compounds.\n") + FreeCAD.Console.PrintError( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds.\n" + ) return FreeCAD.Console.PrintMessage("postprocessing...\n") @@ -245,7 +279,7 @@ def export(objectslist, filename, argstring): sendToSmoothie(IP_ADDR, final, filename) else: - if not filename == '-': + if not filename == "-": gfile = pythonopen(filename, "w") gfile.write(final) gfile.close() @@ -260,7 +294,7 @@ def sendToSmoothie(ip, GCODE, fname): import os fname = os.path.basename(fname) - FreeCAD.Console.PrintMessage('sending to smoothie: {}\n'.format(fname)) + FreeCAD.Console.PrintMessage("sending to smoothie: {}\n".format(fname)) f = GCODE.rstrip() filesize = len(f) @@ -268,9 +302,9 @@ def sendToSmoothie(ip, GCODE, fname): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(4.0) s.connect((ip, 115)) - tn = s.makefile(mode='rw') + tn = s.makefile(mode="rw") -# read startup prompt + # read startup prompt ln = tn.readline() if not ln.startswith("+"): FreeCAD.Console.PrintMessage("Failed to connect with sftp: {}\n".format(ln)) @@ -279,7 +313,7 @@ def sendToSmoothie(ip, GCODE, fname): if VERBOSE: print("RSP: " + ln.strip()) -# Issue initial store command + # Issue initial store command tn.write("STOR OLD /sd/" + fname + "\n") tn.flush() @@ -291,7 +325,7 @@ def sendToSmoothie(ip, GCODE, fname): if VERBOSE: print("RSP: " + ln.strip()) -# send size of file + # send size of file tn.write("SIZE " + str(filesize) + "\n") tn.flush() @@ -304,13 +338,13 @@ def sendToSmoothie(ip, GCODE, fname): print("RSP: " + ln.strip()) cnt = 0 -# now send file + # now send file for line in f.splitlines(1): tn.write(line) if VERBOSE: cnt += len(line) print("SND: " + line.strip()) - print(str(cnt) + "/" + str(filesize) + "\r", end='') + print(str(cnt) + "/" + str(filesize) + "\r", end="") tn.flush() @@ -322,7 +356,7 @@ def sendToSmoothie(ip, GCODE, fname): if VERBOSE: print("RSP: " + ln.strip()) -# exit + # exit tn.write("DONE\n") tn.flush() tn.close() @@ -343,12 +377,12 @@ def parse(pathobj): out = "" lastcommand = None - precision_string = '.' + str(PRECISION) + 'f' + precision_string = "." + str(PRECISION) + "f" # params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control # the order of parameters # linuxcnc doesn't want K properties on XY plane Arcs need work. - params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L'] + params = ["X", "Y", "Z", "A", "B", "I", "J", "F", "S", "T", "Q", "R", "L"] if hasattr(pathobj, "Group"): # We have a compound or project. # if OUTPUT_COMMENTS: @@ -378,28 +412,42 @@ def parse(pathobj): # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F': - if c.Name not in ["G0", "G00"]: # linuxcnc doesn't use rapid speeds - speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) + if param == "F": + if c.Name not in [ + "G0", + "G00", + ]: # linuxcnc doesn't use rapid speeds + speed = Units.Quantity( + c.Parameters["F"], FreeCAD.Units.Velocity + ) outstring.append( - param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) - elif param == 'T': - outstring.append(param + str(c.Parameters['T'])) - elif param == 'S': - outstring.append(param + str(c.Parameters['S'])) - SPINDLE_SPEED = c.Parameters['S'] + param + + format( + float(speed.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) + elif param == "T": + outstring.append(param + str(c.Parameters["T"])) + elif param == "S": + outstring.append(param + str(c.Parameters["S"])) + SPINDLE_SPEED = c.Parameters["S"] else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outstring.append( - param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) - if command in ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']: - outstring.append('S' + str(SPINDLE_SPEED)) + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), precision_string + ) + ) + if command in ["G1", "G01", "G2", "G02", "G3", "G03"]: + outstring.append("S" + str(SPINDLE_SPEED)) # store the latest command lastcommand = command # Check for Tool Change: - if command == 'M6': + if command == "M6": # if OUTPUT_COMMENTS: # out += linenumber() + "(begin toolchange)\n" for line in TOOL_CHANGE.splitlines(True): diff --git a/src/Mod/Path/PathScripts/post/uccnc_post.py b/src/Mod/Path/PathScripts/post/uccnc_post.py index 855237207c..d150be5a6e 100644 --- a/src/Mod/Path/PathScripts/post/uccnc_post.py +++ b/src/Mod/Path/PathScripts/post/uccnc_post.py @@ -35,12 +35,13 @@ from FreeCAD import Units import Path import argparse import datetime + # import shlex from PathScripts import PostUtils VERSION = "0.0.4" -TOOLTIP = ''' Post processor for UC-CNC. +TOOLTIP = """ Post processor for UC-CNC. This is a postprocessor file for the Path workbench. It is used to take a pseudo-gcode fragment outputted by a Path object, and output @@ -55,57 +56,57 @@ This postprocessor was tested on UC-CNC v1.2111, an UC100 and a Stepcraft 420. It was tested on FreeCAD v0.17, v0.18 and v0.19 Other (Stepcraft) machines using UC-CNC and UC* controllers should be easy to adapt. -''' +""" # PREAMBLE_ possible values: # Multi line text with gcode. Preamble gcode # The preamble text will appear at the beginning of the GCODE output file. -PREAMBLE_DEFAULT = '''G17 (Default: XY-plane) +PREAMBLE_DEFAULT = """G17 (Default: XY-plane) G54 (Default: First coordinate system) G40 (Default: Cutter radius compensation none) G49 (Default: Tool Length Offsets: cancel tool length) G90 (Default: Absolute distance mode selection) G80 (Cancel canned cycle) -''' +""" -PREAMBLE_DEFAULT_NO_COMMENT = '''G17 +PREAMBLE_DEFAULT_NO_COMMENT = """G17 G54 G40 G49 G90 G80 -''' +""" # POSTAMBLE possible values: # Multi line text with gcode. Postable gcode # The postamble text will appear following the last operation. -POSTAMBLE_DEFAULT = '''M05 (stop spindle) +POSTAMBLE_DEFAULT = """M05 (stop spindle) G17 (Default: XY-plane) G54 (Default: First coordinate system) G40 (Default: Cutter radius compensation none) G90 (Default: Absolute distance mode selection) G80 (Cancel canned cycle) M30 (Stop program and rewind code) -''' +""" -POSTAMBLE_DEFAULT_NO_COMMENT = '''M05 +POSTAMBLE_DEFAULT_NO_COMMENT = """M05 G17 G54 G40 G90 G80 M30 -''' +""" # PRE_OPERATION: Pre operation text will be inserted before every operation -PRE_OPERATION = '''''' +PRE_OPERATION = """""" # POST_OPERATION: Post operation text will be inserted after every operation -POST_OPERATION = '''''' +POST_OPERATION = """""" # TOOL_CHANGE: Tool Change commands will be inserted before a tool change -TOOL_CHANGE = '''''' +TOOL_CHANGE = """""" ################################ # Other configuration settings # @@ -209,8 +210,8 @@ PRECISION = 3 # note: G20/G21 are not supported by UC-CNC, units are configured in a program profile. # In code G20/G21 commands are silently ignored by UC-CNC # UNITS is included in the post processor to mirror the profile settings. -UNITS_US_IMP = 'G20' -UNITS_METRIC = 'G21' +UNITS_US_IMP = "G20" +UNITS_METRIC = "G21" UNITS = UNITS_METRIC # UNIT_FORMAT possible values: (see UNITS) @@ -220,8 +221,8 @@ UNITS = UNITS_METRIC # note: G20/G21 are not supported by UC-CNC, units are configured in a program profile. # In code G20/G21 commands are silently ignored by UC-CNC # UNITS is included in the post processor to mirror the profile settings. -UNIT_FORMAT_US_IMP = 'in' -UNIT_FORMAT_METRIC = 'mm' +UNIT_FORMAT_US_IMP = "in" +UNIT_FORMAT_METRIC = "mm" UNIT_FORMAT = UNIT_FORMAT_METRIC # UNIT_SPEED_FORMAT possible values: (see UNITS) @@ -231,8 +232,8 @@ UNIT_FORMAT = UNIT_FORMAT_METRIC # note: G20/G21 are not supported by UC-CNC, units are configured in a program profile. # In code G20/G21 commands are silently ignored by UC-CNC # UNITS is included in the post processor to mirror the profile settings. -UNIT_SPEED_FORMAT_US_IMP = 'in/min' -UNIT_SPEED_FORMAT_METRIC = 'mm/min' +UNIT_SPEED_FORMAT_US_IMP = "in/min" +UNIT_SPEED_FORMAT_METRIC = "mm/min" UNIT_SPEED_FORMAT = UNIT_SPEED_FORMAT_METRIC ################################################## @@ -241,40 +242,49 @@ UNIT_SPEED_FORMAT = UNIT_SPEED_FORMAT_METRIC # see: https://docs.python.org/3/library/argparse.html parser = argparse.ArgumentParser(prog=__name__, add_help=False) -parser.add_argument('--name', - help='GCode program name') -parser.add_argument('--no-header', action='store_true', - help='suppress header output') -parser.add_argument('--no-comments', action='store_true', - help='suppress comment output') -parser.add_argument('--line-numbers', action='store_true', - help='suppress prefix with line numbers') -parser.add_argument('--no-show-editor', action='store_true', - help='don\'t pop up editor before writing output') -parser.add_argument('--precision', default='3', - help='number of digits of precision, default=3') -parser.add_argument('--preamble', - help='set commands to be issued before the first command, default="G17\nG90\nG54"') -parser.add_argument('--postamble', - help='set commands to be issued after the last command, default="M05\nM30"') -parser.add_argument('--inches', action='store_true', - help='lengths in [in], G20') -parser.add_argument('--metric', action='store_true', - help='lengths in [mm], G21') -parser.add_argument('--modal', action='store_true', - help='repeat/suppress repeated command arguments') -parser.add_argument('--tool-length-offset', action='store_true', - help='suppress tool length offset G43 following tool changes') -parser.add_argument('--repeat', action='store_true', - help='repeat axis arguments') +parser.add_argument("--name", help="GCode program name") +parser.add_argument("--no-header", action="store_true", help="suppress header output") +parser.add_argument( + "--no-comments", action="store_true", help="suppress comment output" +) +parser.add_argument( + "--line-numbers", action="store_true", help="suppress prefix with line numbers" +) +parser.add_argument( + "--no-show-editor", + action="store_true", + help="don't pop up editor before writing output", +) +parser.add_argument( + "--precision", default="3", help="number of digits of precision, default=3" +) +parser.add_argument( + "--preamble", + help='set commands to be issued before the first command, default="G17\nG90\nG54"', +) +parser.add_argument( + "--postamble", + help='set commands to be issued after the last command, default="M05\nM30"', +) +parser.add_argument("--inches", action="store_true", help="lengths in [in], G20") +parser.add_argument("--metric", action="store_true", help="lengths in [mm], G21") +parser.add_argument( + "--modal", action="store_true", help="repeat/suppress repeated command arguments" +) +parser.add_argument( + "--tool-length-offset", + action="store_true", + help="suppress tool length offset G43 following tool changes", +) +parser.add_argument("--repeat", action="store_true", help="repeat axis arguments") TOOLTIP_ARGS = parser.format_help() # to distinguish python built-in open function from the one declared below -if open.__module__ in ['__builtin__', 'io']: +if open.__module__ in ["__builtin__", "io"]: pythonopen = open # to distinguish python built-in open function from the one declared below -if open.__module__ == '__builtin__': +if open.__module__ == "__builtin__": pythonopen = open # debug option, trace to screen while processing to see where things break up. @@ -291,29 +301,29 @@ UNIT_DEFAULT_CHANGED = False warnings_count = 0 problems_count = 0 -HEADER = '''(Exported by FreeCAD for {}) +HEADER = """(Exported by FreeCAD for {}) (Post Processor: {}, version {}) (CAM file: {}) (Output Time: {}) -''' +""" def processArguments(argstring): - global SHOW_EDITOR # Show gcode before saving. - global PROG_NAME # Name of the G-Code program - global OUTPUT_HEADER # Use of a document header - global OUTPUT_COMMENTS # (Dont) use comments in output - global OUTPUT_LINE_NUMBERS # (Dont) use line numbers in output - global PREAMBLE # Preamble gcode - global POSTAMBLE # Postable gcode - global MODAL # Repeat/suppress repeated command arguments. - global USE_TLO # Set tool length offset - global PRECISION # Number of digits in feed and axis values - global UNITS # Code to switch to specific units - global UNIT_FORMAT # Text with specific units - global UNIT_SPEED_FORMAT # Text with specific units over time units - global UNIT_DEFAULT_CHANGED # tracing changes in UNIT settings. - global REPEAT_ARGUMENTS # Repeat or suppress axis values if the same as previous line. + global SHOW_EDITOR # Show gcode before saving. + global PROG_NAME # Name of the G-Code program + global OUTPUT_HEADER # Use of a document header + global OUTPUT_COMMENTS # (Dont) use comments in output + global OUTPUT_LINE_NUMBERS # (Dont) use line numbers in output + global PREAMBLE # Preamble gcode + global POSTAMBLE # Postable gcode + global MODAL # Repeat/suppress repeated command arguments. + global USE_TLO # Set tool length offset + global PRECISION # Number of digits in feed and axis values + global UNITS # Code to switch to specific units + global UNIT_FORMAT # Text with specific units + global UNIT_SPEED_FORMAT # Text with specific units over time units + global UNIT_DEFAULT_CHANGED # tracing changes in UNIT settings. + global REPEAT_ARGUMENTS # Repeat or suppress axis values if the same as previous line. try: UNIT_DEFAULT_CHANGED = False @@ -381,14 +391,14 @@ def processArguments(argstring): def append0(line): result = line - if (trace_gcode): + if trace_gcode: print("export: >>" + result) return result def append(line): result = linenumber() + line - if (trace_gcode): + if trace_gcode: print("export: >>" + result) return result @@ -407,7 +417,11 @@ def export(objectslist, filename, argstring): for obj in objectslist: if not hasattr(obj, "Path"): - print("the object " + obj.Name + " is not a path. Please select only path and Compounds.") + print( + "the object " + + obj.Name + + " is not a path. Please select only path and Compounds." + ) return None print("export: postprocessing...") @@ -419,16 +433,20 @@ def export(objectslist, filename, argstring): # write header if OUTPUT_HEADER: - for line in HEADER.format(GCODE_PROCESSOR, - __name__, VERSION, - FreeCAD.ActiveDocument.FileName, str(now)).splitlines(False): - if (line): + for line in HEADER.format( + GCODE_PROCESSOR, + __name__, + VERSION, + FreeCAD.ActiveDocument.FileName, + str(now), + ).splitlines(False): + if line: gcode += append(line + "\n") # Write the preamble # G20/G21 not supported by UC-CNC, *always* report the configured units. gcode += append("(Units: '" + UNIT_FORMAT + "' and '" + UNIT_SPEED_FORMAT + "')\n") - if (UNIT_DEFAULT_CHANGED): + if UNIT_DEFAULT_CHANGED: gcode += append("(WARNING: Units default changed, check your UC-CNC profile)\n") warnings_count += 1 @@ -455,12 +473,12 @@ def export(objectslist, filename, argstring): # turn coolant on if required if hasattr(obj, "CoolantMode"): coolantMode = obj.CoolantMode - if coolantMode == 'Mist': + if coolantMode == "Mist": if OUTPUT_COMMENTS: gcode += append("M7 (coolant: mist on)\n") else: gcode += append("M7\n") - if coolantMode == 'Flood': + if coolantMode == "Flood": if OUTPUT_COMMENTS: gcode += append("M8 (coolant: flood on)\n") else: @@ -480,7 +498,7 @@ def export(objectslist, filename, argstring): # turn coolant off if required if hasattr(obj, "CoolantMode"): coolantMode = obj.CoolantMode - if not coolantMode == 'None': + if not coolantMode == "None": if OUTPUT_COMMENTS: gcode += append("M9 (coolant: off)\n") else: @@ -509,12 +527,15 @@ def export(objectslist, filename, argstring): final = gcode if (0 < problems_count) or (0 < warnings_count): - print("export: postprocessing: done, warnings: {}, problems: {}, see GCode for details." - .format(warnings_count, problems_count)) + print( + "export: postprocessing: done, warnings: {}, problems: {}, see GCode for details.".format( + warnings_count, problems_count + ) + ) else: print("export: postprocessing: done (none of the problems detected).") - if not filename == '-': + if not filename == "-": print("export: writing to '{}'".format(filename)) gfile = pythonopen(filename, "w") gfile.write(final) @@ -526,7 +547,7 @@ def export(objectslist, filename, argstring): def linenumber(): global LINENR - if (LINENR <= 0): + if LINENR <= 0: LINENR = LINE_NUMBER_START if OUTPUT_LINE_NUMBERS is True: line = LINENR @@ -538,11 +559,28 @@ def linenumber(): def parse(pathobj): out = "" lastcommand = None - precision_string = '.' + str(PRECISION) + 'f' + precision_string = "." + str(PRECISION) + "f" currLocation = {} # keep track for no doubles # The params list control the order of parameters - params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'K', 'R', 'F', 'S', 'T', 'H', 'L', 'Q'] + params = [ + "X", + "Y", + "Z", + "A", + "B", + "C", + "I", + "J", + "K", + "R", + "F", + "S", + "T", + "H", + "L", + "Q", + ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters @@ -574,49 +612,67 @@ def parse(pathobj): if command == lastcommand: commandlist.pop(0) - if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment + if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: - if param == 'F' and (currLocation[param] != c.Parameters[param] or REPEAT_ARGUMENTS): + if param == "F" and ( + currLocation[param] != c.Parameters[param] or REPEAT_ARGUMENTS + ): if c.Name not in ["G0", "G00"]: # No F in G0 - speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) + speed = Units.Quantity( + c.Parameters["F"], FreeCAD.Units.Velocity + ) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: commandlist.append( - param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), - precision_string)) + param + + format( + float(speed.getValueAs(UNIT_SPEED_FORMAT)), + precision_string, + ) + ) else: continue - elif param == 'T': - commandlist.append(param + str(int(c.Parameters['T']))) - elif param == 'H': - commandlist.append(param + str(int(c.Parameters['H']))) - elif param == 'D': - commandlist.append(param + str(int(c.Parameters['D']))) - elif param == 'S': - commandlist.append(param + str(int(c.Parameters['S']))) + elif param == "T": + commandlist.append(param + str(int(c.Parameters["T"]))) + elif param == "H": + commandlist.append(param + str(int(c.Parameters["H"]))) + elif param == "D": + commandlist.append(param + str(int(c.Parameters["D"]))) + elif param == "S": + commandlist.append(param + str(int(c.Parameters["S"]))) else: - if (not REPEAT_ARGUMENTS) and (param in currLocation) and (currLocation[param] == c.Parameters[param]): + if ( + (not REPEAT_ARGUMENTS) + and (param in currLocation) + and (currLocation[param] == c.Parameters[param]) + ): continue else: - pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) - commandlist.append(param + format(float(pos.getValueAs(UNIT_FORMAT)), - precision_string)) + pos = Units.Quantity( + c.Parameters[param], FreeCAD.Units.Length + ) + commandlist.append( + param + + format( + float(pos.getValueAs(UNIT_FORMAT)), precision_string + ) + ) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: - if command == 'M6': + if command == "M6": for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line # add height offset if USE_TLO: - tool_height = '\nG43 H' + str(int(c.Parameters['T'])) + tool_height = "\nG43 H" + str(int(c.Parameters["T"])) commandlist.append(tool_height) if command == "message": @@ -633,7 +689,7 @@ def parse(pathobj): # append the line to the final output for w in commandlist: out += w.strip() + COMMAND_SPACE - if (trace_gcode): + if trace_gcode: print("parse : >>{}".format(out)) out = out.strip() + "\n" diff --git a/src/Mod/Path/PathTests/PathTestUtils.py b/src/Mod/Path/PathTests/PathTestUtils.py index c753d83f35..492d44f77a 100644 --- a/src/Mod/Path/PathTests/PathTestUtils.py +++ b/src/Mod/Path/PathTests/PathTestUtils.py @@ -28,6 +28,7 @@ import unittest from FreeCAD import Vector + class PathTestBase(unittest.TestCase): """Base test class with some additional asserts.""" @@ -50,7 +51,9 @@ class PathTestBase(unittest.TestCase): def assertLine(self, edge, pt1, pt2): """Verify that edge is a line from pt1 to pt2.""" # Depending on the setting of LineOld .... - self.assertTrue(type(edge.Curve) is Part.Line or type(edge.Curve) is Part.LineSegment) + self.assertTrue( + type(edge.Curve) is Part.Line or type(edge.Curve) is Part.LineSegment + ) self.assertCoincide(pt1, edge.valueAt(edge.FirstParameter)) self.assertCoincide(pt2, edge.valueAt(edge.LastParameter)) @@ -62,16 +65,16 @@ class PathTestBase(unittest.TestCase): self.assertEqual(len(edges), len(points) - 1) for i in range(0, len(edges)): - self.assertLine(edges[i], points[i], points[i+1]) + self.assertLine(edges[i], points[i], points[i + 1]) - def assertArc(self, edge, pt1, pt2, direction = 'CW'): + def assertArc(self, edge, pt1, pt2, direction="CW"): """Verify that edge is an arc between pt1 and pt2 with the given direction.""" self.assertIs(type(edge.Curve), Part.Circle) self.assertCoincide(edge.valueAt(edge.FirstParameter), pt1) self.assertCoincide(edge.valueAt(edge.LastParameter), pt2) - ptm = edge.valueAt((edge.LastParameter + edge.FirstParameter)/2) + ptm = edge.valueAt((edge.LastParameter + edge.FirstParameter) / 2) side = PathGeom.Side.of(pt2 - pt1, ptm - pt1) - if 'CW' == direction: + if "CW" == direction: self.assertEqual(side, PathGeom.Side.Left) else: self.assertEqual(side, PathGeom.Side.Right) @@ -83,57 +86,62 @@ class PathTestBase(unittest.TestCase): self.assertCoincide(curve.Center, Vector(pt.x, pt.y, pt.z)) self.assertRoughly(curve.Radius, r) - def assertCurve(self, edge, p1, p2, p3): """Verify that the edge goes through the given 3 points, representing start, mid and end point respectively.""" self.assertCoincide(edge.valueAt(edge.FirstParameter), p1) self.assertCoincide(edge.valueAt(edge.LastParameter), p3) - self.assertCoincide(edge.valueAt((edge.FirstParameter + edge.LastParameter)/2), p2) + self.assertCoincide( + edge.valueAt((edge.FirstParameter + edge.LastParameter) / 2), p2 + ) def assertCylinderAt(self, solid, pt, r, h): """Verify that solid is a cylinder at the specified location.""" self.assertEqual(len(solid.Edges), 3) - lid = solid.Edges[0] + lid = solid.Edges[0] hull = solid.Edges[1] base = solid.Edges[2] - self.assertCircle(lid, Vector(pt.x, pt.y, pt.z+h), r) - self.assertLine(hull, Vector(pt.x+r, pt.y, pt.z), Vector(pt.x+r, pt.y, pt.z+h)) + self.assertCircle(lid, Vector(pt.x, pt.y, pt.z + h), r) + self.assertLine( + hull, Vector(pt.x + r, pt.y, pt.z), Vector(pt.x + r, pt.y, pt.z + h) + ) self.assertCircle(base, Vector(pt.x, pt.y, pt.z), r) def assertConeAt(self, solid, pt, r1, r2, h): """Verify that solid is a cone at the specified location.""" self.assertEqual(len(solid.Edges), 3) - lid = solid.Edges[0] + lid = solid.Edges[0] hull = solid.Edges[1] base = solid.Edges[2] - self.assertCircle(lid, Vector(pt.x, pt.y, pt.z+h), r2) - self.assertLine(hull, Vector(pt.x+r1, pt.y, pt.z), Vector(pt.x+r2, pt.y, pt.z+h)) + self.assertCircle(lid, Vector(pt.x, pt.y, pt.z + h), r2) + self.assertLine( + hull, Vector(pt.x + r1, pt.y, pt.z), Vector(pt.x + r2, pt.y, pt.z + h) + ) self.assertCircle(base, Vector(pt.x, pt.y, pt.z), r1) def assertCommandEqual(self, c1, c2): """Verify that the 2 commands are equivalent.""" self.assertEqual(c1.Name, c2.Name) - self.assertRoughly(c1.Parameters.get('X', 0), c2.Parameters.get('X', 0)) - self.assertRoughly(c1.Parameters.get('Y', 0), c2.Parameters.get('Y', 0)) - self.assertRoughly(c1.Parameters.get('Z', 0), c2.Parameters.get('Z', 0)) + self.assertRoughly(c1.Parameters.get("X", 0), c2.Parameters.get("X", 0)) + self.assertRoughly(c1.Parameters.get("Y", 0), c2.Parameters.get("Y", 0)) + self.assertRoughly(c1.Parameters.get("Z", 0), c2.Parameters.get("Z", 0)) - self.assertRoughly(c1.Parameters.get('I', 0), c2.Parameters.get('I', 0)) - self.assertRoughly(c1.Parameters.get('J', 0), c2.Parameters.get('J', 0)) - self.assertRoughly(c1.Parameters.get('K', 0), c2.Parameters.get('K', 0)) + self.assertRoughly(c1.Parameters.get("I", 0), c2.Parameters.get("I", 0)) + self.assertRoughly(c1.Parameters.get("J", 0), c2.Parameters.get("J", 0)) + self.assertRoughly(c1.Parameters.get("K", 0), c2.Parameters.get("K", 0)) - def assertEqualLocale(self,s1,s2): + def assertEqualLocale(self, s1, s2): """Verify that the 2 strings are equivalent, but converts eventual , into . for the first string that may be affected by locale.""" - #self.assertEqual(s1.replace(",","."), s2) - q1=FreeCAD.Units.Quantity(s1) - q2=FreeCAD.Units.Quantity(s2) + # self.assertEqual(s1.replace(",","."), s2) + q1 = FreeCAD.Units.Quantity(s1) + q2 = FreeCAD.Units.Quantity(s2) self.assertEqual(q1.UserString, q2.UserString) - def assertEdgeShapesMatch(self,e1,e2): + def assertEdgeShapesMatch(self, e1, e2): """Verify that 2 edges have the same shape, regardless of orientation.""" self.assertEqual(type(e1.Curve), type(e2.Curve)) self.assertEqual(len(e1.Vertexes), len(e2.Vertexes)) @@ -148,8 +156,11 @@ class PathTestBase(unittest.TestCase): self.assertCoincide(e1.Curve.Center, e2.Curve.Center) self.assertCoincide(e1.Curve.Axis, -e2.Curve.Axis) else: + def valueAt(e, fraction): - return e.valueAt(e.FirstParameter + (e.LastParameter - e.FirstParameter)*fraction) + return e.valueAt( + e.FirstParameter + (e.LastParameter - e.FirstParameter) * fraction + ) if PathGeom.pointsCoincide(e1.Vertexes[0].Point, e2.Vertexes[0].Point): self.assertCoincide(e1.Vertexes[-1].Point, e2.Vertexes[-1].Point) @@ -184,4 +195,3 @@ class PathTestBase(unittest.TestCase): self.assertEqual(len(pts0), len(pts1)) for i in range(len(pts0)): self.assertCoincide(pts0[i], pts1[i]) - diff --git a/src/Mod/Path/PathTests/TestPathAdaptive.py b/src/Mod/Path/PathTests/TestPathAdaptive.py index 57e9c9531e..caac814be1 100644 --- a/src/Mod/Path/PathTests/TestPathAdaptive.py +++ b/src/Mod/Path/PathTests/TestPathAdaptive.py @@ -28,30 +28,33 @@ import PathScripts.PathJob as PathJob import PathScripts.PathAdaptive as PathAdaptive import PathScripts.PathGeom as PathGeom from PathTests.PathTestUtils import PathTestBase + if FreeCAD.GuiUp: import PathScripts.PathAdaptiveGui as PathAdaptiveGui import PathScripts.PathJobGui as PathJobGui class TestPathAdaptive(PathTestBase): - '''Unit tests for the Adaptive operation.''' + """Unit tests for the Adaptive operation.""" @classmethod def setUpClass(cls): - '''setUpClass()... + """setUpClass()... This method is called upon instantiation of this test class. Add code and objects here that are needed for the duration of the test() methods in this class. In other words, set up the 'global' test environment here; use the `setUp()` method to set up a 'local' - test environment. + test environment. This method does not have access to the class `self` reference, but it is able to call static methods within this same class. - ''' + """ # Open existing FreeCAD document with test geometry - doc = FreeCAD.open(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_adaptive.fcstd') + doc = FreeCAD.open( + FreeCAD.getHomePath() + "Mod/Path/PathTests/test_adaptive.fcstd" + ) # Create Job object, adding geometry objects from file opened above - job = PathJob.Create('Job', [doc.Fusion], None) + job = PathJob.Create("Job", [doc.Fusion], None) job.GeometryTolerance.Value = 0.001 if FreeCAD.GuiUp: job.ViewObject.Proxy = PathJobGui.ViewProvider(job.ViewObject) @@ -66,12 +69,12 @@ class TestPathAdaptive(PathTestBase): @classmethod def tearDownClass(cls): - '''tearDownClass()... + """tearDownClass()... This method is called prior to destruction of this test class. Add code and objects here that cleanup the test environment after the test() methods in this class have been executed. This method does not have access to the class `self` reference. This method is able to call static methods within this same class. - ''' + """ # FreeCAD.Console.PrintMessage("TestPathAdaptive.tearDownClass()\n") # Close geometry document without saving @@ -79,30 +82,30 @@ class TestPathAdaptive(PathTestBase): # Setup and tear down methods called before and after each unit test def setUp(self): - '''setUp()... + """setUp()... This method is called prior to each `test()` method. Add code and objects here that are needed for multiple `test()` methods. - ''' + """ self.doc = FreeCAD.ActiveDocument self.con = FreeCAD.Console def tearDown(self): - '''tearDown()... + """tearDown()... This method is called after each test() method. Add cleanup instructions here. Such cleanup instructions will likely undo those in the setUp() method. - ''' + """ pass # Unit tests def test00(self): - '''test00() Empty test.''' + """test00() Empty test.""" return def test01(self): - '''test01() Verify path generated on Face3.''' + """test01() Verify path generated on Face3.""" # Instantiate a Adaptive operation and set Base Geometry - adaptive = PathAdaptive.Create('Adaptive') + adaptive = PathAdaptive.Create("Adaptive") adaptive.Base = [(self.doc.Fusion, ["Face3"])] # (base, subs_list) adaptive.Label = "test01+" adaptive.Comment = "test01() Verify path generated on Face3." @@ -115,8 +118,10 @@ class TestPathAdaptive(PathTestBase): adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False - adaptive.setExpression('StepDown', None) - adaptive.StepDown.Value = 20.0 # Have to set expression to None before numerical value assignment + adaptive.setExpression("StepDown", None) + adaptive.StepDown.Value = ( + 20.0 # Have to set expression to None before numerical value assignment + ) _addViewProvider(adaptive) self.doc.recompute() @@ -127,13 +132,15 @@ class TestPathAdaptive(PathTestBase): # self.assertTrue(expected_moves_test01 == operationMoves, # "expected_moves_test01: {}\noperationMoves: {}".format(expected_moves_test01, operationMoves)) - self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.") + self.assertTrue( + len(adaptive.Path.Commands) > 100, "Command count not greater than 100." + ) def test02(self): - '''test02() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different.''' + """test02() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different.""" # Instantiate a Adaptive operation and set Base Geometry - adaptive = PathAdaptive.Create('Adaptive') + adaptive = PathAdaptive.Create("Adaptive") adaptive.Base = [(self.doc.Fusion, ["Face3", "Face10"])] # (base, subs_list) adaptive.Label = "test02+" adaptive.Comment = "test02() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different." @@ -146,19 +153,23 @@ class TestPathAdaptive(PathTestBase): adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False - adaptive.setExpression('StepDown', None) - adaptive.StepDown.Value = 20.0 # Have to set expression to None before numerical value assignment + adaptive.setExpression("StepDown", None) + adaptive.StepDown.Value = ( + 20.0 # Have to set expression to None before numerical value assignment + ) _addViewProvider(adaptive) self.doc.recompute() - self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.") + self.assertTrue( + len(adaptive.Path.Commands) > 100, "Command count not greater than 100." + ) def test03(self): - '''test03() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different.''' + """test03() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different.""" # Instantiate a Adaptive operation and set Base Geometry - adaptive = PathAdaptive.Create('Adaptive') + adaptive = PathAdaptive.Create("Adaptive") adaptive.Base = [(self.doc.Fusion, ["Face3", "Face10"])] # (base, subs_list) adaptive.Label = "test03+" adaptive.Comment = "test03() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different." @@ -171,20 +182,38 @@ class TestPathAdaptive(PathTestBase): adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = True - adaptive.setExpression('StepDown', None) - adaptive.StepDown.Value = 20.0 # Have to set expression to None before numerical value assignment + adaptive.setExpression("StepDown", None) + adaptive.StepDown.Value = ( + 20.0 # Have to set expression to None before numerical value assignment + ) _addViewProvider(adaptive) self.doc.recompute() - self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.") + self.assertTrue( + len(adaptive.Path.Commands) > 100, "Command count not greater than 100." + ) def test04(self): - '''test04() Verify path generated non-closed edges with differing Z-heights that are closed with Z=1 projection: "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".''' + """test04() Verify path generated non-closed edges with differing Z-heights that are closed with Z=1 projection: "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".""" # Instantiate a Adaptive operation and set Base Geometry - adaptive = PathAdaptive.Create('Adaptive') - adaptive.Base = [(self.doc.Fusion, ["Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19"])] # (base, subs_list) + adaptive = PathAdaptive.Create("Adaptive") + adaptive.Base = [ + ( + self.doc.Fusion, + [ + "Edge9", + "Edge2", + "Edge8", + "Edge15", + "Edge30", + "Edge31", + "Edge29", + "Edge19", + ], + ) + ] # (base, subs_list) adaptive.Label = "test04+" adaptive.Comment = 'test04() Verify path generated non-closed edges with differing Z-heights that are closed with Z=1 projection: "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".' @@ -196,20 +225,40 @@ class TestPathAdaptive(PathTestBase): adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False - adaptive.setExpression('StepDown', None) - adaptive.StepDown.Value = 20.0 # Have to set expression to None before numerical value assignment + adaptive.setExpression("StepDown", None) + adaptive.StepDown.Value = ( + 20.0 # Have to set expression to None before numerical value assignment + ) _addViewProvider(adaptive) self.doc.recompute() - self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.") + self.assertTrue( + len(adaptive.Path.Commands) > 100, "Command count not greater than 100." + ) def test05(self): - '''test05() Verify path generated closed wire with differing Z-heights: "Edge13", "Edge7", "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".''' + """test05() Verify path generated closed wire with differing Z-heights: "Edge13", "Edge7", "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".""" # Instantiate a Adaptive operation and set Base Geometry - adaptive = PathAdaptive.Create('Adaptive') - adaptive.Base = [(self.doc.Fusion, ["Edge13", "Edge7", "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19"])] # (base, subs_list) + adaptive = PathAdaptive.Create("Adaptive") + adaptive.Base = [ + ( + self.doc.Fusion, + [ + "Edge13", + "Edge7", + "Edge9", + "Edge2", + "Edge8", + "Edge15", + "Edge30", + "Edge31", + "Edge29", + "Edge19", + ], + ) + ] # (base, subs_list) adaptive.Label = "test05+" adaptive.Comment = 'test05() Verify path generated closed wire with differing Z-heights: "Edge13", "Edge7", "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".' @@ -221,20 +270,40 @@ class TestPathAdaptive(PathTestBase): adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False - adaptive.setExpression('StepDown', None) - adaptive.StepDown.Value = 20.0 # Have to set expression to None before numerical value assignment + adaptive.setExpression("StepDown", None) + adaptive.StepDown.Value = ( + 20.0 # Have to set expression to None before numerical value assignment + ) _addViewProvider(adaptive) self.doc.recompute() - self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.") + self.assertTrue( + len(adaptive.Path.Commands) > 100, "Command count not greater than 100." + ) def test06(self): - '''test06() Verify path generated with outer and inner edge loops at same Z height: "Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33".''' + """test06() Verify path generated with outer and inner edge loops at same Z height: "Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33".""" # Instantiate a Adaptive operation and set Base Geometry - adaptive = PathAdaptive.Create('Adaptive') - adaptive.Base = [(self.doc.Fusion, ["Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33"])] # (base, subs_list) + adaptive = PathAdaptive.Create("Adaptive") + adaptive.Base = [ + ( + self.doc.Fusion, + [ + "Edge15", + "Edge30", + "Edge31", + "Edge29", + "Edge19", + "Edge18", + "Edge35", + "Edge32", + "Edge34", + "Edge33", + ], + ) + ] # (base, subs_list) adaptive.Label = "test06+" adaptive.Comment = 'test06() Verify path generated with outer and inner edge loops at same Z height: "Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33".' @@ -246,18 +315,25 @@ class TestPathAdaptive(PathTestBase): adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False - adaptive.setExpression('StepDown', None) - adaptive.StepDown.Value = 20.0 # Have to set expression to None before numerical value assignment + adaptive.setExpression("StepDown", None) + adaptive.StepDown.Value = ( + 20.0 # Have to set expression to None before numerical value assignment + ) _addViewProvider(adaptive) self.doc.recompute() # Check command count - self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.") + self.assertTrue( + len(adaptive.Path.Commands) > 100, "Command count not greater than 100." + ) # Check if any paths originate inside inner hole of donut. They should not. isInBox = False - edges = [self.doc.Fusion.Shape.getElement(e) for e in ["Edge35", "Edge32", "Edge33", "Edge34"]] + edges = [ + self.doc.Fusion.Shape.getElement(e) + for e in ["Edge35", "Edge32", "Edge33", "Edge34"] + ] square = Part.Wire(edges) sqrBB = square.BoundBox minPoint = FreeCAD.Vector(sqrBB.XMin, sqrBB.YMin, 0.0) @@ -269,10 +345,10 @@ class TestPathAdaptive(PathTestBase): self.assertFalse(isInBox, "Paths originating within the inner hole.") def test07(self): - '''test07() Verify path generated on donut-shaped Face10.''' + """test07() Verify path generated on donut-shaped Face10.""" # Instantiate a Adaptive operation and set Base Geometry - adaptive = PathAdaptive.Create('Adaptive') + adaptive = PathAdaptive.Create("Adaptive") adaptive.Base = [(self.doc.Fusion, ["Face10"])] # (base, subs_list) adaptive.Label = "test07+" adaptive.Comment = "test07() Verify path generated on donut-shaped Face10." @@ -285,17 +361,24 @@ class TestPathAdaptive(PathTestBase): adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False - adaptive.setExpression('StepDown', None) - adaptive.StepDown.Value = 20.0 # Have to set expression to None before numerical value assignment + adaptive.setExpression("StepDown", None) + adaptive.StepDown.Value = ( + 20.0 # Have to set expression to None before numerical value assignment + ) _addViewProvider(adaptive) self.doc.recompute() - self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.") + self.assertTrue( + len(adaptive.Path.Commands) > 100, "Command count not greater than 100." + ) # Check if any paths originate inside inner hole of donut. They should not. isInBox = False - edges = [self.doc.Fusion.Shape.getElement(e) for e in ["Edge35", "Edge32", "Edge33", "Edge34"]] + edges = [ + self.doc.Fusion.Shape.getElement(e) + for e in ["Edge35", "Edge32", "Edge33", "Edge34"] + ] square = Part.Wire(edges) sqrBB = square.BoundBox minPoint = FreeCAD.Vector(sqrBB.XMin, sqrBB.YMin, 0.0) @@ -312,7 +395,10 @@ class TestPathAdaptive(PathTestBase): # Check if any paths originate inside inner hole of donut. They should not. isInBox = False - edges = [self.doc.Fusion.Shape.getElement(e) for e in ["Edge35", "Edge32", "Edge33", "Edge34"]] + edges = [ + self.doc.Fusion.Shape.getElement(e) + for e in ["Edge35", "Edge32", "Edge33", "Edge34"] + ] square = Part.Wire(edges) sqrBB = square.BoundBox minPoint = FreeCAD.Vector(sqrBB.XMin, sqrBB.YMin, 0.0) @@ -322,20 +408,22 @@ class TestPathAdaptive(PathTestBase): isInBox = True break self.assertTrue(isInBox, "No paths originating within the inner hole.") + + # Eclass def setDepthsAndHeights(op, strDep=20.0, finDep=0.0): - '''setDepthsAndHeights(op, strDep=20.0, finDep=0.0)... Sets default depths and heights for `op` passed to it''' + """setDepthsAndHeights(op, strDep=20.0, finDep=0.0)... Sets default depths and heights for `op` passed to it""" # Set start and final depth in order to eliminate effects of stock (and its default values) - op.setExpression('StartDepth', None) + op.setExpression("StartDepth", None) op.StartDepth.Value = strDep - op.setExpression('FinalDepth', None) + op.setExpression("FinalDepth", None) op.FinalDepth.Value = finDep # Set step down so as to only produce one layer path - op.setExpression('StepDown', None) + op.setExpression("StepDown", None) op.StepDown.Value = 20.0 # Set Heights @@ -343,9 +431,9 @@ def setDepthsAndHeights(op, strDep=20.0, finDep=0.0): def getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=True): - '''getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=True)... + """getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=True)... Accepts command dict and returns point string coordinate. - ''' + """ gcode_list = list() last = FreeCAD.Vector(0.0, 0.0, 0.0) for c in cmdList: @@ -358,13 +446,13 @@ def getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=Tr z = last.z if p.get("X"): x = round(p["X"], 2) - gcode += " X" + str(x) + gcode += " X" + str(x) if p.get("Y"): y = round(p["Y"], 2) - gcode += " Y" + str(y) + gcode += " Y" + str(y) if p.get("Z"): z = round(p["Z"], 2) - gcode += " Z" + str(z) + gcode += " Z" + str(z) last.x = x last.y = y last.z = z @@ -376,13 +464,13 @@ def getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=Tr z = last.z if p.get("X"): x = round(p["X"], 2) - gcode += " X" + str(x) + gcode += " X" + str(x) if p.get("Y"): y = round(p["Y"], 2) - gcode += " Y" + str(y) + gcode += " Y" + str(y) if p.get("Z"): z = round(p["Z"], 2) - gcode += " Z" + str(z) + gcode += " Z" + str(z) last.x = x last.y = y last.z = z @@ -407,13 +495,13 @@ def getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=Tr if p.get("X"): x = round(p["X"], 2) - gcode += " X" + str(x) + gcode += " X" + str(x) if p.get("Y"): y = round(p["Y"], 2) - gcode += " Y" + str(y) + gcode += " Y" + str(y) if p.get("Z"): z = round(p["Z"], 2) - gcode += " Z" + str(z) + gcode += " Z" + str(z) gcode_list.append(gcode) last.x = x @@ -438,7 +526,9 @@ def _addViewProvider(adaptiveOp): if FreeCAD.GuiUp: PathOpGui = PathAdaptiveGui.PathOpGui cmdRes = PathAdaptiveGui.Command.res - adaptiveOp.ViewObject.Proxy = PathOpGui.ViewProvider(adaptiveOp.ViewObject, cmdRes) + adaptiveOp.ViewObject.Proxy = PathOpGui.ViewProvider( + adaptiveOp.ViewObject, cmdRes + ) # Example string literal of expected path moves from an operation diff --git a/src/Mod/Path/PathTests/TestPathCore.py b/src/Mod/Path/PathTests/TestPathCore.py index 34d84d6477..e27cb26a55 100644 --- a/src/Mod/Path/PathTests/TestPathCore.py +++ b/src/Mod/Path/PathTests/TestPathCore.py @@ -24,79 +24,86 @@ import FreeCAD import Path from PathTests.PathTestUtils import PathTestBase -class TestPathCore(PathTestBase): +class TestPathCore(PathTestBase): def test00(self): """Test Path command core functionality""" - #create empty command - c=Path.Command() + # create empty command + c = Path.Command() self.assertIsInstance(c, Path.Command) - #change name + # change name c.Name = "G1" self.assertEqual(c.Name, "G1") - #Assign Parameters - c.Parameters= {"X":1,"Y":0} - self.assertEqual(c.Parameters, {'Y': 0.0, 'X': 1.0}) + # Assign Parameters + c.Parameters = {"X": 1, "Y": 0} + self.assertEqual(c.Parameters, {"Y": 0.0, "X": 1.0}) - #change parameters - c.Parameters= {"X":1,"Y":0.5} - self.assertEqual(c.Parameters, {'Y': 0.5, 'X': 1}) + # change parameters + c.Parameters = {"X": 1, "Y": 0.5} + self.assertEqual(c.Parameters, {"Y": 0.5, "X": 1}) - #output gcode - self.assertEqual(c.toGCode(), 'G1 X1.000000 Y0.500000') + # output gcode + self.assertEqual(c.toGCode(), "G1 X1.000000 Y0.500000") - #create and assign name in one - c2=Path.Command("G2") + # create and assign name in one + c2 = Path.Command("G2") self.assertEqual(c2.Name, "G2") - #Create Path and parameters in one - c3=Path.Command("G1",{"X":34,"Y":1.2}) - self.assertEqual(str(c3), 'Command G1 [ X:34 Y:1.2 ]') + # Create Path and parameters in one + c3 = Path.Command("G1", {"X": 34, "Y": 1.2}) + self.assertEqual(str(c3), "Command G1 [ X:34 Y:1.2 ]") c4 = Path.Command("G1X4Y5") - self.assertEqual(str(c4), 'Command G1 [ X:4 Y:5 ]') + self.assertEqual(str(c4), "Command G1 [ X:4 Y:5 ]") - #use placement - self.assertEqual( str(c3.Placement), 'Placement [Pos=(34,1.2,0), Yaw-Pitch-Roll=(0,0,0)]') - self.assertEqual( c3.toGCode(), 'G1 X34.000000 Y1.200000') + # use placement + self.assertEqual( + str(c3.Placement), "Placement [Pos=(34,1.2,0), Yaw-Pitch-Roll=(0,0,0)]" + ) + self.assertEqual(c3.toGCode(), "G1 X34.000000 Y1.200000") p1 = FreeCAD.Placement() - p1.Base = FreeCAD.Vector(3,2,1) - self.assertEqual(str(p1), 'Placement [Pos=(3,2,1), Yaw-Pitch-Roll=(0,0,0)]') - c5=Path.Command("g1",p1) - self.assertEqual(str(c5), 'Command G1 [ X:3 Y:2 Z:1 ]') - p2=FreeCAD.Placement() - p2.Base = FreeCAD.Vector(5,0,0) + p1.Base = FreeCAD.Vector(3, 2, 1) + self.assertEqual(str(p1), "Placement [Pos=(3,2,1), Yaw-Pitch-Roll=(0,0,0)]") + c5 = Path.Command("g1", p1) + self.assertEqual(str(c5), "Command G1 [ X:3 Y:2 Z:1 ]") + p2 = FreeCAD.Placement() + p2.Base = FreeCAD.Vector(5, 0, 0) - #overwrite placement - c5.Placement=p2 - self.assertEqual(str(c5), 'Command G1 [ X:5 ]') + # overwrite placement + c5.Placement = p2 + self.assertEqual(str(c5), "Command G1 [ X:5 ]") self.assertEqual(c5.x, 5.0) - #overwrite individual parameters - c5.x=10 + # overwrite individual parameters + c5.x = 10 self.assertEqual(c5.x, 10.0) - c5.y=2 - self.assertEqual (str(c5), 'Command G1 [ X:10 Y:2 ]') + c5.y = 2 + self.assertEqual(str(c5), "Command G1 [ X:10 Y:2 ]") - #set from gcode + # set from gcode c3.setFromGCode("G1X1Y0") - self.assertEqual(str(c3), 'Command G1 [ X:1 Y:0 ]') + self.assertEqual(str(c3), "Command G1 [ X:1 Y:0 ]") def test10(self): """Test Path Object core functionality""" - c1=Path.Command("g1",{"x":1,"y":0}) - c2=Path.Command("g1",{"x":0,"y":2}) - p=Path.Path([c1,c2]) - self.assertAlmostEqual(str(p), 'Path [ size:2 length:3.2361 ]', places=4) + c1 = Path.Command("g1", {"x": 1, "y": 0}) + c2 = Path.Command("g1", {"x": 0, "y": 2}) + p = Path.Path([c1, c2]) + self.assertAlmostEqual(str(p), "Path [ size:2 length:3.2361 ]", places=4) - self.assertEqual(str(p.Commands), '[Command G1 [ X:1 Y:0 ], Command G1 [ X:0 Y:2 ]]') - self.assertAlmostEqual( p.Length, 3.2361, places=4) + self.assertEqual( + str(p.Commands), "[Command G1 [ X:1 Y:0 ], Command G1 [ X:0 Y:2 ]]" + ) + self.assertAlmostEqual(p.Length, 3.2361, places=4) p.addCommands(c1) - self.assertEqual(p.toGCode(), 'G1 X1.000000 Y0.000000\nG1 X0.000000 Y2.000000\nG1 X1.000000 Y0.000000\n') + self.assertEqual( + p.toGCode(), + "G1 X1.000000 Y0.000000\nG1 X0.000000 Y2.000000\nG1 X1.000000 Y0.000000\n", + ) - lines = ''' + lines = """ G0X-0.5905Y-0.3937S3000M03 G0Z0.125 G1Z-0.004F3 @@ -105,9 +112,9 @@ G1X0.9842Y0.433 G1X-0.5905Y0.433 G1X-0.5905Y-0.3937 G0Z0.5 -''' +""" - output = '''G0 S3000.000000 X-0.590500 Y-0.393700 + output = """G0 S3000.000000 X-0.590500 Y-0.393700 M03 G0 Z0.125000 G1 F3.000000 Z-0.004000 @@ -116,18 +123,17 @@ G1 X0.984200 Y0.433000 G1 X-0.590500 Y0.433000 G1 X-0.590500 Y-0.393700 G0 Z0.500000 -''' +""" - - #create a path directly form a piece of gcode. + # create a path directly form a piece of gcode. p = Path.Path() p.setFromGCode(lines) - self.assertEqual (p.toGCode(), output) + self.assertEqual(p.toGCode(), output) def test20(self): """Test Path Tool and ToolTable object core functionality""" - t1=Path.Tool() + t1 = Path.Tool() self.assertIsInstance(t1, Path.Tool) t1.Name = "12.7mm Drill Bit" @@ -135,7 +141,7 @@ G0 Z0.500000 self.assertEqual(t1.ToolType, "Undefined") t1.ToolType = "Drill" self.assertEqual(t1.ToolType, "Drill") - t1.Diameter= 12.7 + t1.Diameter = 12.7 t1.LengthOffset = 127 t1.CuttingEdgeAngle = 59 t1.CuttingEdgeHeight = 50.8 @@ -145,7 +151,7 @@ G0 Z0.500000 self.assertEqual(t1.CuttingEdgeAngle, 59) self.assertEqual(t1.CuttingEdgeHeight, 50.8) - t2 = Path.Tool("my other tool",tooltype="EndMill",diameter=10) + t2 = Path.Tool("my other tool", tooltype="EndMill", diameter=10) table = Path.Tooltable() self.assertIsInstance(table, Path.Tooltable) table.addTools(t1) @@ -153,14 +159,16 @@ G0 Z0.500000 self.assertEqual(len(table.Tools), 2) # gcc7 build needs some special treatment (makes 1L out of a 1) ... - if str(table.Tools) != '{1L: Tool 12.7mm Drill Bit, 2L: Tool my other tool}': - self.assertEqual(str(table.Tools), '{1: Tool 12.7mm Drill Bit, 2: Tool my other tool}') + if str(table.Tools) != "{1L: Tool 12.7mm Drill Bit, 2L: Tool my other tool}": + self.assertEqual( + str(table.Tools), "{1: Tool 12.7mm Drill Bit, 2: Tool my other tool}" + ) def test50(self): """Test Path.Length calculation""" commands = [] - commands.append(Path.Command("G1",{"X":1})) - commands.append(Path.Command("G1",{"Y":1})) + commands.append(Path.Command("G1", {"X": 1})) + commands.append(Path.Command("G1", {"Y": 1})) path = Path.Path(commands) self.assertEqual(path.Length, 2) diff --git a/src/Mod/Path/PathTests/TestPathDeburr.py b/src/Mod/Path/PathTests/TestPathDeburr.py index 0127ec46d2..f21bd569b3 100644 --- a/src/Mod/Path/PathTests/TestPathDeburr.py +++ b/src/Mod/Path/PathTests/TestPathDeburr.py @@ -26,13 +26,12 @@ import PathScripts.PathLog as PathLog import PathTests.PathTestUtils as PathTestUtils PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) class TestPathDeburr(PathTestUtils.PathTestBase): - def test00(self): - '''Verify chamfer depth and offset for an end mill.''' + """Verify chamfer depth and offset for an end mill.""" tool = Path.Tool() tool.Diameter = 20 tool.FlatRadius = 0 @@ -52,7 +51,7 @@ class TestPathDeburr(PathTestUtils.PathTestBase): self.assertFalse(info) def test01(self): - '''Verify chamfer depth and offset for a 90 deg v-bit.''' + """Verify chamfer depth and offset for a 90 deg v-bit.""" tool = Path.Tool() tool.FlatRadius = 0 tool.CuttingEdgeAngle = 90 @@ -68,7 +67,7 @@ class TestPathDeburr(PathTestUtils.PathTestBase): self.assertFalse(info) def test02(self): - '''Verify chamfer depth and offset for a 90 deg v-bit with non 0 flat radius.''' + """Verify chamfer depth and offset for a 90 deg v-bit with non 0 flat radius.""" tool = Path.Tool() tool.FlatRadius = 0.3 tool.CuttingEdgeAngle = 90 @@ -84,7 +83,7 @@ class TestPathDeburr(PathTestUtils.PathTestBase): self.assertFalse(info) def test03(self): - '''Verify chamfer depth and offset for a 60 deg v-bit with non 0 flat radius.''' + """Verify chamfer depth and offset for a 60 deg v-bit with non 0 flat radius.""" tool = Path.Tool() tool.FlatRadius = 10 tool.CuttingEdgeAngle = 60 @@ -102,7 +101,7 @@ class TestPathDeburr(PathTestUtils.PathTestBase): self.assertFalse(info) def test10(self): - '''Verify missing cutting edge angle info prints only once.''' + """Verify missing cutting edge angle info prints only once.""" class FakeEndmill(object): def __init__(self, dia): @@ -113,17 +112,21 @@ class TestPathDeburr(PathTestUtils.PathTestBase): self.assertRoughly(0.1, depth) self.assertRoughly(4, offset) self.assertTrue(info) - (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset( + 1, 0.1, tool, not info + ) self.assertRoughly(0.1, depth) self.assertRoughly(4, offset) self.assertTrue(info) - (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset( + 1, 0.1, tool, not info + ) self.assertRoughly(0.1, depth) self.assertRoughly(4, offset) self.assertTrue(info) def test11(self): - '''Verify missing tip diameter info prints only once.''' + """Verify missing tip diameter info prints only once.""" class FakePointyBit(object): def __init__(self, dia, angle): @@ -135,11 +138,15 @@ class TestPathDeburr(PathTestUtils.PathTestBase): self.assertRoughly(1.1, depth) self.assertRoughly(0.1, offset) self.assertTrue(info) - (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset( + 1, 0.1, tool, not info + ) self.assertRoughly(1.1, depth) self.assertRoughly(0.1, offset) self.assertTrue(info) - (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset( + 1, 0.1, tool, not info + ) self.assertRoughly(1.1, depth) self.assertRoughly(0.1, offset) self.assertTrue(info) diff --git a/src/Mod/Path/PathTests/TestPathDressupDogbone.py b/src/Mod/Path/PathTests/TestPathDressupDogbone.py index a0f919c1f9..7f160c4c24 100644 --- a/src/Mod/Path/PathTests/TestPathDressupDogbone.py +++ b/src/Mod/Path/PathTests/TestPathDressupDogbone.py @@ -28,14 +28,15 @@ import PathScripts.PathProfileFaces as PathProfileFaces from PathTests.PathTestUtils import PathTestBase -class TestProfile: +class TestProfile: def __init__(self, side, direction, path): self.Side = side self.Direction = direction self.Path = Path.Path(path) - self.ToolController = None # default tool 5mm - self.Name = 'Profile' + self.ToolController = None # default tool 5mm + self.Name = "Profile" + class TestFeature: def __init__(self): @@ -47,6 +48,7 @@ class TestFeature: def setEditorMode(self, prop, mode): pass + class TestDressupDogbone(PathTestBase): """Unit tests for the Dogbone dressup.""" @@ -54,8 +56,11 @@ class TestDressupDogbone(PathTestBase): return "%d: (%.2f, %.2f)" % (bone[0], bone[1][0], bone[1][1]) def test00(self): - '''Verify bones are inserted for simple moves.''' - base = TestProfile('Inside', 'CW', ''' + """Verify bones are inserted for simple moves.""" + base = TestProfile( + "Inside", + "CW", + """ G0 X10 Y10 Z10 G1 Z0 G1 Y100 @@ -63,7 +68,8 @@ class TestDressupDogbone(PathTestBase): G1 Y10 G1 X10 G1 Z10 - ''') + """, + ) obj = TestFeature() db = PathDressupDogbone.ObjectDressup(obj, base) db.setup(obj, True) @@ -75,8 +81,11 @@ class TestDressupDogbone(PathTestBase): self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3])) def test01(self): - '''Verify bones are inserted if hole ends with rapid move out.''' - base = TestProfile('Inside', 'CW', ''' + """Verify bones are inserted if hole ends with rapid move out.""" + base = TestProfile( + "Inside", + "CW", + """ G0 X10 Y10 Z10 G1 Z0 G1 Y100 @@ -84,7 +93,8 @@ class TestDressupDogbone(PathTestBase): G1 Y10 G1 X10 G0 Z10 - ''') + """, + ) obj = TestFeature() db = PathDressupDogbone.ObjectDressup(obj, base) db.setup(obj, True) @@ -96,40 +106,42 @@ class TestDressupDogbone(PathTestBase): self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3])) def test02(self): - '''Verify bones are correctly generated for a Profile.''' + """Verify bones are correctly generated for a Profile.""" doc = FreeCAD.newDocument("TestDressupDogbone") # This is a real world test to make sure none of the tool chain broke - box0 = doc.addObject('Part::Box', 'Box') + box0 = doc.addObject("Part::Box", "Box") box0.Width = 100 box0.Length = 100 box0.Height = 10 - box1 = doc.addObject('Part::Box', 'Box') + box1 = doc.addObject("Part::Box", "Box") box1.Width = 50 box1.Length = 50 box1.Height = 20 - box1.Placement = FreeCAD.Placement(FreeCAD.Vector(25,25,-5), FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0)) + box1.Placement = FreeCAD.Placement( + FreeCAD.Vector(25, 25, -5), FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) + ) doc.recompute() - cut = doc.addObject('Part::Cut', 'Cut') + cut = doc.addObject("Part::Cut", "Cut") cut.Base = box0 cut.Tool = box1 doc.recompute() for i in range(11): - face = "Face%d" % (i+1) + face = "Face%d" % (i + 1) f = cut.Shape.getElement(face) - if f.Surface.Axis == FreeCAD.Vector(0,0,1) and f.Orientation == 'Forward': + if f.Surface.Axis == FreeCAD.Vector(0, 0, 1) and f.Orientation == "Forward": break - PathJob.Create('Job', [cut], None) + PathJob.Create("Job", [cut], None) - profile = PathProfileFaces.Create('Profile Faces') + profile = PathProfileFaces.Create("Profile Faces") profile.Base = (cut, face) profile.StepDown = 5 # set start and final depth in order to eliminate effects of stock (and its default values) - profile.setExpression('StartDepth', None) + profile.setExpression("StartDepth", None) profile.StartDepth = 10 - profile.setExpression('FinalDepth', None) + profile.setExpression("FinalDepth", None) profile.FinalDepth = 0 profile.processHoles = True @@ -140,7 +152,9 @@ class TestDressupDogbone(PathTestBase): doc.recompute() dog = dogbone.Proxy - locs = sorted([bone[1] for bone in dog.bones], key=lambda xy: xy[0] * 1000 + xy[1]) + locs = sorted( + [bone[1] for bone in dog.bones], key=lambda xy: xy[0] * 1000 + xy[1] + ) def formatBoneLoc(pt): return "(%.2f, %.2f)" % (pt[0], pt[1]) @@ -160,8 +174,11 @@ class TestDressupDogbone(PathTestBase): FreeCAD.closeDocument("TestDressupDogbone") def test03(self): - '''Verify no bone is inserted for straight move interrupted by plunge.''' - base = TestProfile('Inside', 'CW', ''' + """Verify no bone is inserted for straight move interrupted by plunge.""" + base = TestProfile( + "Inside", + "CW", + """ G0 X10 Y10 Z10 G1 Z0 G1 X0 ( start) @@ -170,7 +187,8 @@ class TestDressupDogbone(PathTestBase): G1 Y10 G1 X10 ( straight line move to start) G0 Z10 - ''') + """, + ) obj = TestFeature() db = PathDressupDogbone.ObjectDressup(obj, base) db.setup(obj, True) @@ -178,8 +196,11 @@ class TestDressupDogbone(PathTestBase): self.assertEqual(len(db.bones), 0) def test04(self): - '''Verify can handle comments between moves''' - base = TestProfile('Inside', 'CW', ''' + """Verify can handle comments between moves""" + base = TestProfile( + "Inside", + "CW", + """ G0 X10 Y10 Z10 G1 Z0 G1 X20 @@ -187,7 +208,8 @@ class TestDressupDogbone(PathTestBase): G1 X10 G1 Y10 G1 Z10 - ''') + """, + ) obj = TestFeature() db = PathDressupDogbone.ObjectDressup(obj, base) db.setup(obj, True) @@ -198,7 +220,10 @@ class TestDressupDogbone(PathTestBase): self.assertEqual("3: (10.00, 0.00)", self.formatBone(db.bones[2])) self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3])) - base = TestProfile('Inside', 'CW', ''' + base = TestProfile( + "Inside", + "CW", + """ G0 X10 Y10 Z10 G1 Z0 G1 X20 @@ -207,7 +232,8 @@ class TestDressupDogbone(PathTestBase): (some comment or other should not change the output) G1 Y10 G1 Z10 - ''') + """, + ) obj = TestFeature() db = PathDressupDogbone.ObjectDressup(obj, base) db.setup(obj, True) @@ -218,10 +244,12 @@ class TestDressupDogbone(PathTestBase): self.assertEqual("3: (10.00, 0.00)", self.formatBone(db.bones[2])) self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3])) - def test05(self): - '''Verify can handle noops between moves''' - base = TestProfile('Inside', 'CW', ''' + """Verify can handle noops between moves""" + base = TestProfile( + "Inside", + "CW", + """ G0 X10 Y10 Z10 G1 Z0 G1 X20 @@ -229,7 +257,8 @@ class TestDressupDogbone(PathTestBase): G1 X10 G1 Y10 G1 Z10 - ''') + """, + ) obj = TestFeature() db = PathDressupDogbone.ObjectDressup(obj, base) db.setup(obj, True) @@ -240,7 +269,10 @@ class TestDressupDogbone(PathTestBase): self.assertEqual("3: (10.00, 0.00)", self.formatBone(db.bones[2])) self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3])) - base = TestProfile('Inside', 'CW', ''' + base = TestProfile( + "Inside", + "CW", + """ G0 X10 Y10 Z10 G1 Z0 G1 X20 @@ -249,7 +281,8 @@ class TestDressupDogbone(PathTestBase): G1 X10 G1 Y10 G1 Z10 - ''') + """, + ) obj = TestFeature() db = PathDressupDogbone.ObjectDressup(obj, base) db.setup(obj, True) @@ -259,4 +292,3 @@ class TestDressupDogbone(PathTestBase): self.assertEqual("2: (20.00, 0.00)", self.formatBone(db.bones[1])) self.assertEqual("3: (10.00, 0.00)", self.formatBone(db.bones[2])) self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3])) - diff --git a/src/Mod/Path/PathTests/TestPathDressupHoldingTags.py b/src/Mod/Path/PathTests/TestPathDressupHoldingTags.py index 6d1c406f62..103e9f56fa 100644 --- a/src/Mod/Path/PathTests/TestPathDressupHoldingTags.py +++ b/src/Mod/Path/PathTests/TestPathDressupHoldingTags.py @@ -26,6 +26,7 @@ import math from FreeCAD import Vector from PathScripts.PathDressupHoldingTags import Tag + class TestHoldingTags(PathTestUtils.PathTestBase): """Unit tests for the HoldingTags dressup.""" @@ -34,7 +35,6 @@ class TestHoldingTags(PathTestUtils.PathTestBase): tag = Tag(0, 77, 13, 4, 5, 90, True) self.assertCoincide(tag.originAt(3), Vector(77, 13, 3)) - def test01(self): """Verify solid for a 90 degree tag is a cylinder.""" tag = Tag(0, 100, 200, 4, 5, 90, 0, True) @@ -49,7 +49,7 @@ class TestHoldingTags(PathTestUtils.PathTestBase): tag.createSolidsAt(0, 0) self.assertIsNotNone(tag.solid) - self.assertConeAt(tag.solid, Vector(0,0,-0.05), 9, 3.95, 5.05) + self.assertConeAt(tag.solid, Vector(0, 0, -0.05), 9, 3.95, 5.05) def test03(self): """Verify pointy cone shape of tag with pointy end if width, angle and height match up.""" @@ -57,14 +57,13 @@ class TestHoldingTags(PathTestUtils.PathTestBase): tag.createSolidsAt(0, 0) self.assertIsNotNone(tag.solid) h = 5 * 1.01 - self.assertConeAt(tag.solid, Vector(0,0,-h * 0.01), 5, 0, h) + self.assertConeAt(tag.solid, Vector(0, 0, -h * 0.01), 5, 0, h) def test04(self): """Verify height adjustment if tag isn't wide eough for angle.""" tag = Tag(0, 0, 0, 5, 17, 60, 0, True) tag.createSolidsAt(0, 0) self.assertIsNotNone(tag.solid) - h = 2.5 * math.tan((60/180.0)*math.pi) * 1.01 + h = 2.5 * math.tan((60 / 180.0) * math.pi) * 1.01 print(h) - self.assertConeAt(tag.solid, Vector(0,0,-h * 0.01), 2.5, 0, h) - + self.assertConeAt(tag.solid, Vector(0, 0, -h * 0.01), 2.5, 0, h) diff --git a/src/Mod/Path/PathTests/TestPathGeom.py b/src/Mod/Path/PathTests/TestPathGeom.py index 896698c485..87c93f5852 100644 --- a/src/Mod/Path/PathTests/TestPathGeom.py +++ b/src/Mod/Path/PathTests/TestPathGeom.py @@ -28,115 +28,219 @@ import math from FreeCAD import Vector from PathTests.PathTestUtils import PathTestBase + class TestPathGeom(PathTestBase): - '''Test Path <-> Wire conversion.''' + """Test Path <-> Wire conversion.""" def test00(self): - '''Verify getAngle functionality.''' + """Verify getAngle functionality.""" self.assertRoughly(PathGeom.getAngle(Vector(1, 0, 0)), 0) - self.assertRoughly(PathGeom.getAngle(Vector(1, 1, 0)), math.pi/4) - self.assertRoughly(PathGeom.getAngle(Vector(0, 1, 0)), math.pi/2) - self.assertRoughly(PathGeom.getAngle(Vector(-1, 1, 0)), 3*math.pi/4) + self.assertRoughly(PathGeom.getAngle(Vector(1, 1, 0)), math.pi / 4) + self.assertRoughly(PathGeom.getAngle(Vector(0, 1, 0)), math.pi / 2) + self.assertRoughly(PathGeom.getAngle(Vector(-1, 1, 0)), 3 * math.pi / 4) self.assertRoughly(PathGeom.getAngle(Vector(-1, 0, 0)), math.pi) - self.assertRoughly(PathGeom.getAngle(Vector(-1, -1, 0)), -3*math.pi/4) - self.assertRoughly(PathGeom.getAngle(Vector(0, -1, 0)), -math.pi/2) - self.assertRoughly(PathGeom.getAngle(Vector(1, -1, 0)), -math.pi/4) + self.assertRoughly(PathGeom.getAngle(Vector(-1, -1, 0)), -3 * math.pi / 4) + self.assertRoughly(PathGeom.getAngle(Vector(0, -1, 0)), -math.pi / 2) + self.assertRoughly(PathGeom.getAngle(Vector(1, -1, 0)), -math.pi / 4) def test01(self): - '''Verify diffAngle functionality.''' - self.assertRoughly(PathGeom.diffAngle(0, +0*math.pi/4, 'CW') / math.pi, 0/4.) - self.assertRoughly(PathGeom.diffAngle(0, +3*math.pi/4, 'CW') / math.pi, 5/4.) - self.assertRoughly(PathGeom.diffAngle(0, -3*math.pi/4, 'CW') / math.pi, 3/4.) - self.assertRoughly(PathGeom.diffAngle(0, +4*math.pi/4, 'CW') / math.pi, 4/4.) - self.assertRoughly(PathGeom.diffAngle(0, +0*math.pi/4, 'CCW')/ math.pi, 0/4.) - self.assertRoughly(PathGeom.diffAngle(0, +3*math.pi/4, 'CCW')/ math.pi, 3/4.) - self.assertRoughly(PathGeom.diffAngle(0, -3*math.pi/4, 'CCW')/ math.pi, 5/4.) - self.assertRoughly(PathGeom.diffAngle(0, +4*math.pi/4, 'CCW')/ math.pi, 4/4.) + """Verify diffAngle functionality.""" + self.assertRoughly( + PathGeom.diffAngle(0, +0 * math.pi / 4, "CW") / math.pi, 0 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(0, +3 * math.pi / 4, "CW") / math.pi, 5 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(0, -3 * math.pi / 4, "CW") / math.pi, 3 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(0, +4 * math.pi / 4, "CW") / math.pi, 4 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(0, +0 * math.pi / 4, "CCW") / math.pi, 0 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(0, +3 * math.pi / 4, "CCW") / math.pi, 3 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(0, -3 * math.pi / 4, "CCW") / math.pi, 5 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(0, +4 * math.pi / 4, "CCW") / math.pi, 4 / 4.0 + ) - self.assertRoughly(PathGeom.diffAngle(+math.pi/4, +0*math.pi/4, 'CW') / math.pi, 1/4.) - self.assertRoughly(PathGeom.diffAngle(+math.pi/4, +3*math.pi/4, 'CW') / math.pi, 6/4.) - self.assertRoughly(PathGeom.diffAngle(+math.pi/4, -1*math.pi/4, 'CW') / math.pi, 2/4.) - self.assertRoughly(PathGeom.diffAngle(-math.pi/4, +0*math.pi/4, 'CW') / math.pi, 7/4.) - self.assertRoughly(PathGeom.diffAngle(-math.pi/4, +3*math.pi/4, 'CW') / math.pi, 4/4.) - self.assertRoughly(PathGeom.diffAngle(-math.pi/4, -1*math.pi/4, 'CW') / math.pi, 0/4.) + self.assertRoughly( + PathGeom.diffAngle(+math.pi / 4, +0 * math.pi / 4, "CW") / math.pi, 1 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(+math.pi / 4, +3 * math.pi / 4, "CW") / math.pi, 6 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(+math.pi / 4, -1 * math.pi / 4, "CW") / math.pi, 2 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(-math.pi / 4, +0 * math.pi / 4, "CW") / math.pi, 7 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(-math.pi / 4, +3 * math.pi / 4, "CW") / math.pi, 4 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(-math.pi / 4, -1 * math.pi / 4, "CW") / math.pi, 0 / 4.0 + ) - self.assertRoughly(PathGeom.diffAngle(+math.pi/4, +0*math.pi/4, 'CCW') / math.pi, 7/4.) - self.assertRoughly(PathGeom.diffAngle(+math.pi/4, +3*math.pi/4, 'CCW') / math.pi, 2/4.) - self.assertRoughly(PathGeom.diffAngle(+math.pi/4, -1*math.pi/4, 'CCW') / math.pi, 6/4.) - self.assertRoughly(PathGeom.diffAngle(-math.pi/4, +0*math.pi/4, 'CCW') / math.pi, 1/4.) - self.assertRoughly(PathGeom.diffAngle(-math.pi/4, +3*math.pi/4, 'CCW') / math.pi, 4/4.) - self.assertRoughly(PathGeom.diffAngle(-math.pi/4, -1*math.pi/4, 'CCW') / math.pi, 0/4.) + self.assertRoughly( + PathGeom.diffAngle(+math.pi / 4, +0 * math.pi / 4, "CCW") / math.pi, 7 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(+math.pi / 4, +3 * math.pi / 4, "CCW") / math.pi, 2 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(+math.pi / 4, -1 * math.pi / 4, "CCW") / math.pi, 6 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(-math.pi / 4, +0 * math.pi / 4, "CCW") / math.pi, 1 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(-math.pi / 4, +3 * math.pi / 4, "CCW") / math.pi, 4 / 4.0 + ) + self.assertRoughly( + PathGeom.diffAngle(-math.pi / 4, -1 * math.pi / 4, "CCW") / math.pi, 0 / 4.0 + ) def test02(self): - '''Verify isVertical/isHorizontal for Vector''' + """Verify isVertical/isHorizontal for Vector""" self.assertTrue(PathGeom.isVertical(Vector(0, 0, 1))) self.assertTrue(PathGeom.isVertical(Vector(0, 0, -1))) self.assertFalse(PathGeom.isVertical(Vector(1, 0, 1))) self.assertFalse(PathGeom.isVertical(Vector(1, 0, -1))) - self.assertTrue(PathGeom.isHorizontal(Vector( 1, 0, 0))) - self.assertTrue(PathGeom.isHorizontal(Vector(-1, 0, 0))) - self.assertTrue(PathGeom.isHorizontal(Vector( 0, 1, 0))) - self.assertTrue(PathGeom.isHorizontal(Vector( 0, -1, 0))) - self.assertTrue(PathGeom.isHorizontal(Vector( 1, 1, 0))) - self.assertTrue(PathGeom.isHorizontal(Vector(-1, 1, 0))) - self.assertTrue(PathGeom.isHorizontal(Vector( 1, -1, 0))) + self.assertTrue(PathGeom.isHorizontal(Vector(1, 0, 0))) + self.assertTrue(PathGeom.isHorizontal(Vector(-1, 0, 0))) + self.assertTrue(PathGeom.isHorizontal(Vector(0, 1, 0))) + self.assertTrue(PathGeom.isHorizontal(Vector(0, -1, 0))) + self.assertTrue(PathGeom.isHorizontal(Vector(1, 1, 0))) + self.assertTrue(PathGeom.isHorizontal(Vector(-1, 1, 0))) + self.assertTrue(PathGeom.isHorizontal(Vector(1, -1, 0))) self.assertTrue(PathGeom.isHorizontal(Vector(-1, -1, 0))) - self.assertFalse(PathGeom.isHorizontal(Vector(0, 1, 1))) - self.assertFalse(PathGeom.isHorizontal(Vector(0, -1, 1))) - self.assertFalse(PathGeom.isHorizontal(Vector(0, 1, -1))) + self.assertFalse(PathGeom.isHorizontal(Vector(0, 1, 1))) + self.assertFalse(PathGeom.isHorizontal(Vector(0, -1, 1))) + self.assertFalse(PathGeom.isHorizontal(Vector(0, 1, -1))) self.assertFalse(PathGeom.isHorizontal(Vector(0, -1, -1))) def test03(self): - '''Verify isVertical/isHorizontal for Edges''' + """Verify isVertical/isHorizontal for Edges""" # lines - self.assertTrue(PathGeom.isVertical(Part.Edge(Part.LineSegment(Vector(-1, -1, -1), Vector(-1, -1, 8))))) - self.assertFalse(PathGeom.isVertical(Part.Edge(Part.LineSegment(Vector(-1, -1, -1), Vector(1, -1, 8))))) - self.assertFalse(PathGeom.isVertical(Part.Edge(Part.LineSegment(Vector(-1, -1, -1), Vector(-1, 1, 8))))) + self.assertTrue( + PathGeom.isVertical( + Part.Edge(Part.LineSegment(Vector(-1, -1, -1), Vector(-1, -1, 8))) + ) + ) + self.assertFalse( + PathGeom.isVertical( + Part.Edge(Part.LineSegment(Vector(-1, -1, -1), Vector(1, -1, 8))) + ) + ) + self.assertFalse( + PathGeom.isVertical( + Part.Edge(Part.LineSegment(Vector(-1, -1, -1), Vector(-1, 1, 8))) + ) + ) - self.assertTrue(PathGeom.isHorizontal(Part.Edge(Part.LineSegment(Vector(1, -1, -1), Vector(-1, -1, -1))))) - self.assertTrue(PathGeom.isHorizontal(Part.Edge(Part.LineSegment(Vector(-1, 1, -1), Vector(-1, -1, -1))))) - self.assertTrue(PathGeom.isHorizontal(Part.Edge(Part.LineSegment(Vector(1, 1, -1), Vector(-1, -1, -1))))) - self.assertFalse(PathGeom.isHorizontal(Part.Edge(Part.LineSegment(Vector(1, -1, -1), Vector(1, -1, 8))))) - self.assertFalse(PathGeom.isHorizontal(Part.Edge(Part.LineSegment(Vector(-1, 1, -1), Vector(-1, 1, 8))))) + self.assertTrue( + PathGeom.isHorizontal( + Part.Edge(Part.LineSegment(Vector(1, -1, -1), Vector(-1, -1, -1))) + ) + ) + self.assertTrue( + PathGeom.isHorizontal( + Part.Edge(Part.LineSegment(Vector(-1, 1, -1), Vector(-1, -1, -1))) + ) + ) + self.assertTrue( + PathGeom.isHorizontal( + Part.Edge(Part.LineSegment(Vector(1, 1, -1), Vector(-1, -1, -1))) + ) + ) + self.assertFalse( + PathGeom.isHorizontal( + Part.Edge(Part.LineSegment(Vector(1, -1, -1), Vector(1, -1, 8))) + ) + ) + self.assertFalse( + PathGeom.isHorizontal( + Part.Edge(Part.LineSegment(Vector(-1, 1, -1), Vector(-1, 1, 8))) + ) + ) # circles - self.assertTrue(PathGeom.isVertical(Part.Edge(Part.makeCircle(4, Vector(), Vector(0, 1, 0))))) - self.assertTrue(PathGeom.isVertical(Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 0, 0))))) - self.assertTrue(PathGeom.isVertical(Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 1, 0))))) - self.assertFalse(PathGeom.isVertical(Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 1, 1))))) + self.assertTrue( + PathGeom.isVertical( + Part.Edge(Part.makeCircle(4, Vector(), Vector(0, 1, 0))) + ) + ) + self.assertTrue( + PathGeom.isVertical( + Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 0, 0))) + ) + ) + self.assertTrue( + PathGeom.isVertical( + Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 1, 0))) + ) + ) + self.assertFalse( + PathGeom.isVertical( + Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 1, 1))) + ) + ) - self.assertTrue(PathGeom.isHorizontal(Part.Edge(Part.makeCircle(4, Vector(), Vector(0, 0, 1))))) - self.assertFalse(PathGeom.isHorizontal(Part.Edge(Part.makeCircle(4, Vector(), Vector(0, 1, 1))))) - self.assertFalse(PathGeom.isHorizontal(Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 0, 1))))) - self.assertFalse(PathGeom.isHorizontal(Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 1, 1))))) + self.assertTrue( + PathGeom.isHorizontal( + Part.Edge(Part.makeCircle(4, Vector(), Vector(0, 0, 1))) + ) + ) + self.assertFalse( + PathGeom.isHorizontal( + Part.Edge(Part.makeCircle(4, Vector(), Vector(0, 1, 1))) + ) + ) + self.assertFalse( + PathGeom.isHorizontal( + Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 0, 1))) + ) + ) + self.assertFalse( + PathGeom.isHorizontal( + Part.Edge(Part.makeCircle(4, Vector(), Vector(1, 1, 1))) + ) + ) # bezier curves # ml: I know nothing about bezier curves, so this might be bollocks # and now I disable the tests because they seem to fail on OCE - #bezier = Part.BezierCurve() - #bezier.setPoles([Vector(), Vector(1,1,0), Vector(2,1,0), Vector(2,2,0)]) - #self.assertTrue(PathGeom.isHorizontal(Part.Edge(bezier))) - #self.assertFalse(PathGeom.isVertical(Part.Edge(bezier))) - #bezier.setPoles([Vector(), Vector(1,1,1), Vector(2,1,0), Vector(2,2,0)]) - #self.assertFalse(PathGeom.isHorizontal(Part.Edge(bezier))) - #self.assertFalse(PathGeom.isVertical(Part.Edge(bezier))) - #bezier.setPoles([Vector(), Vector(1,1,0), Vector(2,1,1), Vector(2,2,0)]) - #self.assertFalse(PathGeom.isHorizontal(Part.Edge(bezier))) - #self.assertFalse(PathGeom.isVertical(Part.Edge(bezier))) - #bezier.setPoles([Vector(), Vector(1,1,0), Vector(2,1,0), Vector(2,2,1)]) - #self.assertFalse(PathGeom.isHorizontal(Part.Edge(bezier))) - #self.assertFalse(PathGeom.isVertical(Part.Edge(bezier))) + # bezier = Part.BezierCurve() + # bezier.setPoles([Vector(), Vector(1,1,0), Vector(2,1,0), Vector(2,2,0)]) + # self.assertTrue(PathGeom.isHorizontal(Part.Edge(bezier))) + # self.assertFalse(PathGeom.isVertical(Part.Edge(bezier))) + # bezier.setPoles([Vector(), Vector(1,1,1), Vector(2,1,0), Vector(2,2,0)]) + # self.assertFalse(PathGeom.isHorizontal(Part.Edge(bezier))) + # self.assertFalse(PathGeom.isVertical(Part.Edge(bezier))) + # bezier.setPoles([Vector(), Vector(1,1,0), Vector(2,1,1), Vector(2,2,0)]) + # self.assertFalse(PathGeom.isHorizontal(Part.Edge(bezier))) + # self.assertFalse(PathGeom.isVertical(Part.Edge(bezier))) + # bezier.setPoles([Vector(), Vector(1,1,0), Vector(2,1,0), Vector(2,2,1)]) + # self.assertFalse(PathGeom.isHorizontal(Part.Edge(bezier))) + # self.assertFalse(PathGeom.isVertical(Part.Edge(bezier))) # - #bezier.setPoles([Vector(), Vector(1,1,1), Vector(2,2,2), Vector(0,0,3)]) - #self.assertFalse(PathGeom.isHorizontal(Part.Edge(bezier))) - #self.assertTrue(PathGeom.isVertical(Part.Edge(bezier))) - + # bezier.setPoles([Vector(), Vector(1,1,1), Vector(2,2,2), Vector(0,0,3)]) + # self.assertFalse(PathGeom.isHorizontal(Part.Edge(bezier))) + # self.assertTrue(PathGeom.isVertical(Part.Edge(bezier))) def test04(self): - '''Verify isVertical/isHorizontal for faces''' + """Verify isVertical/isHorizontal for faces""" # planes xPlane = Part.makePlane(100, 100, Vector(), Vector(1, 0, 0)) @@ -161,12 +265,36 @@ class TestPathGeom(PathTestBase): self.assertFalse(PathGeom.isHorizontal(yzPlane)) # cylinders - xCylinder = [f for f in Part.makeCylinder(1, 1, Vector(), Vector(1, 0, 0)).Faces if type(f.Surface) == Part.Cylinder][0] - yCylinder = [f for f in Part.makeCylinder(1, 1, Vector(), Vector(0, 1, 0)).Faces if type(f.Surface) == Part.Cylinder][0] - zCylinder = [f for f in Part.makeCylinder(1, 1, Vector(), Vector(0, 0, 1)).Faces if type(f.Surface) == Part.Cylinder][0] - xyCylinder = [f for f in Part.makeCylinder(1, 1, Vector(), Vector(1, 1, 0)).Faces if type(f.Surface) == Part.Cylinder][0] - xzCylinder = [f for f in Part.makeCylinder(1, 1, Vector(), Vector(1, 0, 1)).Faces if type(f.Surface) == Part.Cylinder][0] - yzCylinder = [f for f in Part.makeCylinder(1, 1, Vector(), Vector(0, 1, 1)).Faces if type(f.Surface) == Part.Cylinder][0] + xCylinder = [ + f + for f in Part.makeCylinder(1, 1, Vector(), Vector(1, 0, 0)).Faces + if type(f.Surface) == Part.Cylinder + ][0] + yCylinder = [ + f + for f in Part.makeCylinder(1, 1, Vector(), Vector(0, 1, 0)).Faces + if type(f.Surface) == Part.Cylinder + ][0] + zCylinder = [ + f + for f in Part.makeCylinder(1, 1, Vector(), Vector(0, 0, 1)).Faces + if type(f.Surface) == Part.Cylinder + ][0] + xyCylinder = [ + f + for f in Part.makeCylinder(1, 1, Vector(), Vector(1, 1, 0)).Faces + if type(f.Surface) == Part.Cylinder + ][0] + xzCylinder = [ + f + for f in Part.makeCylinder(1, 1, Vector(), Vector(1, 0, 1)).Faces + if type(f.Surface) == Part.Cylinder + ][0] + yzCylinder = [ + f + for f in Part.makeCylinder(1, 1, Vector(), Vector(0, 1, 1)).Faces + if type(f.Surface) == Part.Cylinder + ][0] self.assertTrue(PathGeom.isHorizontal(xCylinder)) self.assertTrue(PathGeom.isHorizontal(yCylinder)) @@ -176,214 +304,412 @@ class TestPathGeom(PathTestBase): self.assertFalse(PathGeom.isHorizontal(yzCylinder)) def test07(self): - '''Verify speed interpolation works for different pitches''' + """Verify speed interpolation works for different pitches""" # horizontal - self.assertRoughly(100, PathGeom.speedBetweenPoints(Vector(), Vector(1,1,0), 100, 50)) - self.assertRoughly(100, PathGeom.speedBetweenPoints(Vector(1,1,0), Vector(), 100, 50)) + self.assertRoughly( + 100, PathGeom.speedBetweenPoints(Vector(), Vector(1, 1, 0), 100, 50) + ) + self.assertRoughly( + 100, PathGeom.speedBetweenPoints(Vector(1, 1, 0), Vector(), 100, 50) + ) # vertical - self.assertRoughly( 50, PathGeom.speedBetweenPoints(Vector(), Vector(0,0,1), 100, 50)) - self.assertRoughly( 50, PathGeom.speedBetweenPoints(Vector(0,0,1), Vector(), 100, 50)) + self.assertRoughly( + 50, PathGeom.speedBetweenPoints(Vector(), Vector(0, 0, 1), 100, 50) + ) + self.assertRoughly( + 50, PathGeom.speedBetweenPoints(Vector(0, 0, 1), Vector(), 100, 50) + ) # 45° - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(), Vector(1,0,1), 100, 50)) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(), Vector(0,1,1), 100, 50)) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(), Vector(0.707,0.707,1), 100, 50), 0.01) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(1,0,1), Vector(), 100, 50)) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(0,1,1), Vector(), 100, 50)) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(0.707,0.707,1), Vector(), 100, 50), 0.01) + self.assertRoughly( + 75, PathGeom.speedBetweenPoints(Vector(), Vector(1, 0, 1), 100, 50) + ) + self.assertRoughly( + 75, PathGeom.speedBetweenPoints(Vector(), Vector(0, 1, 1), 100, 50) + ) + self.assertRoughly( + 75, + PathGeom.speedBetweenPoints(Vector(), Vector(0.707, 0.707, 1), 100, 50), + 0.01, + ) + self.assertRoughly( + 75, PathGeom.speedBetweenPoints(Vector(1, 0, 1), Vector(), 100, 50) + ) + self.assertRoughly( + 75, PathGeom.speedBetweenPoints(Vector(0, 1, 1), Vector(), 100, 50) + ) + self.assertRoughly( + 75, + PathGeom.speedBetweenPoints(Vector(0.707, 0.707, 1), Vector(), 100, 50), + 0.01, + ) # 30° - self.assertRoughly( 66.66, PathGeom.speedBetweenPoints(Vector(), Vector(0.5774,0,1), 100, 50), 0.01) - self.assertRoughly( 66.66, PathGeom.speedBetweenPoints(Vector(), Vector(0,0.5774,1), 100, 50), 0.01) - self.assertRoughly( 66.66, PathGeom.speedBetweenPoints(Vector(0.5774,0,1), Vector(), 100, 50), 0.01) - self.assertRoughly( 66.66, PathGeom.speedBetweenPoints(Vector(0,0.5774,1), Vector(), 100, 50), 0.01) + self.assertRoughly( + 66.66, + PathGeom.speedBetweenPoints(Vector(), Vector(0.5774, 0, 1), 100, 50), + 0.01, + ) + self.assertRoughly( + 66.66, + PathGeom.speedBetweenPoints(Vector(), Vector(0, 0.5774, 1), 100, 50), + 0.01, + ) + self.assertRoughly( + 66.66, + PathGeom.speedBetweenPoints(Vector(0.5774, 0, 1), Vector(), 100, 50), + 0.01, + ) + self.assertRoughly( + 66.66, + PathGeom.speedBetweenPoints(Vector(0, 0.5774, 1), Vector(), 100, 50), + 0.01, + ) # 60° - self.assertRoughly( 83.33, PathGeom.speedBetweenPoints(Vector(), Vector(1,0,0.5774), 100, 50), 0.01) - self.assertRoughly( 83.33, PathGeom.speedBetweenPoints(Vector(), Vector(0,1,0.5774), 100, 50), 0.01) - self.assertRoughly( 83.33, PathGeom.speedBetweenPoints(Vector(1,0,0.5774), Vector(), 100, 50), 0.01) - self.assertRoughly( 83.33, PathGeom.speedBetweenPoints(Vector(0,1,0.5774), Vector(), 100, 50), 0.01) + self.assertRoughly( + 83.33, + PathGeom.speedBetweenPoints(Vector(), Vector(1, 0, 0.5774), 100, 50), + 0.01, + ) + self.assertRoughly( + 83.33, + PathGeom.speedBetweenPoints(Vector(), Vector(0, 1, 0.5774), 100, 50), + 0.01, + ) + self.assertRoughly( + 83.33, + PathGeom.speedBetweenPoints(Vector(1, 0, 0.5774), Vector(), 100, 50), + 0.01, + ) + self.assertRoughly( + 83.33, + PathGeom.speedBetweenPoints(Vector(0, 1, 0.5774), Vector(), 100, 50), + 0.01, + ) def test08(self): - '''Verify speed interpolation works for different pitches if vSpeed > hSpeed''' + """Verify speed interpolation works for different pitches if vSpeed > hSpeed""" # horizontal - self.assertRoughly( 50, PathGeom.speedBetweenPoints(Vector(), Vector(1,1,0), 50, 100)) - self.assertRoughly( 50, PathGeom.speedBetweenPoints(Vector(1,1,0), Vector(), 50, 100)) + self.assertRoughly( + 50, PathGeom.speedBetweenPoints(Vector(), Vector(1, 1, 0), 50, 100) + ) + self.assertRoughly( + 50, PathGeom.speedBetweenPoints(Vector(1, 1, 0), Vector(), 50, 100) + ) # vertical - self.assertRoughly(100, PathGeom.speedBetweenPoints(Vector(), Vector(0,0,1), 50, 100)) - self.assertRoughly(100, PathGeom.speedBetweenPoints(Vector(0,0,1), Vector(), 50, 100)) + self.assertRoughly( + 100, PathGeom.speedBetweenPoints(Vector(), Vector(0, 0, 1), 50, 100) + ) + self.assertRoughly( + 100, PathGeom.speedBetweenPoints(Vector(0, 0, 1), Vector(), 50, 100) + ) # 45° - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(), Vector(1,0,1), 50, 100)) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(), Vector(0,1,1), 50, 100)) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(), Vector(0.707,0.707,1), 50, 100), 0.01) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(1,0,1), Vector(), 50, 100)) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(0,1,1), Vector(), 50, 100)) - self.assertRoughly( 75, PathGeom.speedBetweenPoints(Vector(0.707,0.707,1), Vector(), 50, 100), 0.01) + self.assertRoughly( + 75, PathGeom.speedBetweenPoints(Vector(), Vector(1, 0, 1), 50, 100) + ) + self.assertRoughly( + 75, PathGeom.speedBetweenPoints(Vector(), Vector(0, 1, 1), 50, 100) + ) + self.assertRoughly( + 75, + PathGeom.speedBetweenPoints(Vector(), Vector(0.707, 0.707, 1), 50, 100), + 0.01, + ) + self.assertRoughly( + 75, PathGeom.speedBetweenPoints(Vector(1, 0, 1), Vector(), 50, 100) + ) + self.assertRoughly( + 75, PathGeom.speedBetweenPoints(Vector(0, 1, 1), Vector(), 50, 100) + ) + self.assertRoughly( + 75, + PathGeom.speedBetweenPoints(Vector(0.707, 0.707, 1), Vector(), 50, 100), + 0.01, + ) # 30° - self.assertRoughly( 83.33, PathGeom.speedBetweenPoints(Vector(), Vector(0.5774,0,1), 50, 100), 0.01) - self.assertRoughly( 83.33, PathGeom.speedBetweenPoints(Vector(), Vector(0,0.5774,1), 50, 100), 0.01) - self.assertRoughly( 83.33, PathGeom.speedBetweenPoints(Vector(0.5774,0,1), Vector(), 50, 100), 0.01) - self.assertRoughly( 83.33, PathGeom.speedBetweenPoints(Vector(0,0.5774,1), Vector(), 50, 100), 0.01) + self.assertRoughly( + 83.33, + PathGeom.speedBetweenPoints(Vector(), Vector(0.5774, 0, 1), 50, 100), + 0.01, + ) + self.assertRoughly( + 83.33, + PathGeom.speedBetweenPoints(Vector(), Vector(0, 0.5774, 1), 50, 100), + 0.01, + ) + self.assertRoughly( + 83.33, + PathGeom.speedBetweenPoints(Vector(0.5774, 0, 1), Vector(), 50, 100), + 0.01, + ) + self.assertRoughly( + 83.33, + PathGeom.speedBetweenPoints(Vector(0, 0.5774, 1), Vector(), 50, 100), + 0.01, + ) # 60° - self.assertRoughly( 66.66, PathGeom.speedBetweenPoints(Vector(), Vector(1,0,0.5774), 50, 100), 0.01) - self.assertRoughly( 66.66, PathGeom.speedBetweenPoints(Vector(), Vector(0,1,0.5774), 50, 100), 0.01) - self.assertRoughly( 66.66, PathGeom.speedBetweenPoints(Vector(1,0,0.5774), Vector(), 50, 100), 0.01) - self.assertRoughly( 66.66, PathGeom.speedBetweenPoints(Vector(0,1,0.5774), Vector(), 50, 100), 0.01) + self.assertRoughly( + 66.66, + PathGeom.speedBetweenPoints(Vector(), Vector(1, 0, 0.5774), 50, 100), + 0.01, + ) + self.assertRoughly( + 66.66, + PathGeom.speedBetweenPoints(Vector(), Vector(0, 1, 0.5774), 50, 100), + 0.01, + ) + self.assertRoughly( + 66.66, + PathGeom.speedBetweenPoints(Vector(1, 0, 0.5774), Vector(), 50, 100), + 0.01, + ) + self.assertRoughly( + 66.66, + PathGeom.speedBetweenPoints(Vector(0, 1, 0.5774), Vector(), 50, 100), + 0.01, + ) def test10(self): - '''Verify proper geometry objects for G1 and G01 commands are created.''' - spt = Vector(1,2,3) - self.assertLine(PathGeom.edgeForCmd(Path.Command('G1', {'X': 7, 'Y': 2, 'Z': 3}), spt), spt, Vector(7, 2, 3)) - self.assertLine(PathGeom.edgeForCmd(Path.Command('G01', {'X': 1, 'Y': 3, 'Z': 5}), spt), spt, Vector(1, 3, 5)) + """Verify proper geometry objects for G1 and G01 commands are created.""" + spt = Vector(1, 2, 3) + self.assertLine( + PathGeom.edgeForCmd(Path.Command("G1", {"X": 7, "Y": 2, "Z": 3}), spt), + spt, + Vector(7, 2, 3), + ) + self.assertLine( + PathGeom.edgeForCmd(Path.Command("G01", {"X": 1, "Y": 3, "Z": 5}), spt), + spt, + Vector(1, 3, 5), + ) def test20(self): - '''Verify proper geometry for arcs in the XY-plane are created.''' + """Verify proper geometry for arcs in the XY-plane are created.""" p1 = Vector(0, -1, 2) p2 = Vector(-1, 0, 2) self.assertArc( - PathGeom.edgeForCmd( - Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 0, 'J': 1, 'K': 0}), p1), - p1, p2, 'CW') + PathGeom.edgeForCmd( + Path.Command( + "G2", {"X": p2.x, "Y": p2.y, "Z": p2.z, "I": 0, "J": 1, "K": 0} + ), + p1, + ), + p1, + p2, + "CW", + ) self.assertArc( - PathGeom.edgeForCmd( - Path.Command('G3', {'X': p1.x, 'Y': p1.y, 'z': p1.z, 'I': -1, 'J': 0, 'K': 0}), p2), - p2, p1, 'CCW') + PathGeom.edgeForCmd( + Path.Command( + "G3", {"X": p1.x, "Y": p1.y, "z": p1.z, "I": -1, "J": 0, "K": 0} + ), + p2, + ), + p2, + p1, + "CCW", + ) def test30(self): - '''Verify proper geometry for arcs with rising and fall ing Z-axis are created.''' - #print("------ rising helix -------") + """Verify proper geometry for arcs with rising and fall ing Z-axis are created.""" + # print("------ rising helix -------") p1 = Vector(0, 1, 0) p2 = Vector(1, 0, 2) self.assertCurve( - PathGeom.edgeForCmd( - Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 0, 'J': -1, 'K': 1}), p1), - p1, Vector(1/math.sqrt(2), 1/math.sqrt(2), 1), p2) + PathGeom.edgeForCmd( + Path.Command( + "G2", {"X": p2.x, "Y": p2.y, "Z": p2.z, "I": 0, "J": -1, "K": 1} + ), + p1, + ), + p1, + Vector(1 / math.sqrt(2), 1 / math.sqrt(2), 1), + p2, + ) p1 = Vector(-1, 0, 0) p2 = Vector(0, -1, 2) self.assertCurve( - PathGeom.edgeForCmd( - Path.Command('G3', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 1, 'J': 0, 'K': 1}), p1), - p1, Vector(-1/math.sqrt(2), -1/math.sqrt(2), 1), p2) + PathGeom.edgeForCmd( + Path.Command( + "G3", {"X": p2.x, "Y": p2.y, "Z": p2.z, "I": 1, "J": 0, "K": 1} + ), + p1, + ), + p1, + Vector(-1 / math.sqrt(2), -1 / math.sqrt(2), 1), + p2, + ) - #print("------ falling helix -------") + # print("------ falling helix -------") p1 = Vector(0, -1, 2) p2 = Vector(-1, 0, 0) self.assertCurve( - PathGeom.edgeForCmd( - Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 0, 'J': 1, 'K': -1}), p1), - p1, Vector(-1/math.sqrt(2), -1/math.sqrt(2), 1), p2) + PathGeom.edgeForCmd( + Path.Command( + "G2", {"X": p2.x, "Y": p2.y, "Z": p2.z, "I": 0, "J": 1, "K": -1} + ), + p1, + ), + p1, + Vector(-1 / math.sqrt(2), -1 / math.sqrt(2), 1), + p2, + ) p1 = Vector(-1, 0, 2) p2 = Vector(0, -1, 0) self.assertCurve( - PathGeom.edgeForCmd( - Path.Command('G3', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 1, 'J': 0, 'K': -1}), p1), - p1, Vector(-1/math.sqrt(2), -1/math.sqrt(2), 1), p2) + PathGeom.edgeForCmd( + Path.Command( + "G3", {"X": p2.x, "Y": p2.y, "Z": p2.z, "I": 1, "J": 0, "K": -1} + ), + p1, + ), + p1, + Vector(-1 / math.sqrt(2), -1 / math.sqrt(2), 1), + p2, + ) def test40(self): - '''Verify arc results in proper G2/3 command.''' - p1 = Vector( 0, -10, 0) - p2 = Vector(-10, 0, 0) - p3 = Vector( 0, +10, 0) - p4 = Vector(+10, 0, 0) + """Verify arc results in proper G2/3 command.""" + p1 = Vector(0, -10, 0) + p2 = Vector(-10, 0, 0) + p3 = Vector(0, +10, 0) + p4 = Vector(+10, 0, 0) def cmds(pa, pb, pc, flip): return PathGeom.cmdsForEdge(Part.Edge(Part.Arc(pa, pb, pc)), flip)[0] + def cmd(g, end, off): - return Path.Command(g, {'X': end.x, 'Y': end.y, 'Z': end.z, 'I': off.x, 'J': off.y, 'K': off.z}) + return Path.Command( + g, + { + "X": end.x, + "Y": end.y, + "Z": end.z, + "I": off.x, + "J": off.y, + "K": off.z, + }, + ) - self.assertCommandEqual(cmds(p1, p2, p3, False), cmd('G2', p3, Vector(0, 10, 0))) - self.assertCommandEqual(cmds(p1, p4, p3, False), cmd('G3', p3, Vector(0, 10, 0))) + self.assertCommandEqual( + cmds(p1, p2, p3, False), cmd("G2", p3, Vector(0, 10, 0)) + ) + self.assertCommandEqual( + cmds(p1, p4, p3, False), cmd("G3", p3, Vector(0, 10, 0)) + ) - self.assertCommandEqual(cmds(p1, p2, p3, True), cmd('G3', p1, Vector(0, -10, 0))) - self.assertCommandEqual(cmds(p1, p4, p3, True), cmd('G2', p1, Vector(0, -10, 0))) + self.assertCommandEqual( + cmds(p1, p2, p3, True), cmd("G3", p1, Vector(0, -10, 0)) + ) + self.assertCommandEqual( + cmds(p1, p4, p3, True), cmd("G2", p1, Vector(0, -10, 0)) + ) def test41(self): - '''Verify circle results in proper G2/G3 commands.''' + """Verify circle results in proper G2/G3 commands.""" - def cmds(center, radius, up = True): + def cmds(center, radius, up=True): norm = Vector(0, 0, 1) if up else Vector(0, 0, -1) return PathGeom.cmdsForEdge(Part.Edge(Part.Circle(center, norm, radius)))[0] + def cmd(g, end, off): - return Path.Command(g, {'X': end.x, 'Y': end.y, 'Z': end.z, 'I': off.x, 'J': off.y, 'K': off.z}) + return Path.Command( + g, + { + "X": end.x, + "Y": end.y, + "Z": end.z, + "I": off.x, + "J": off.y, + "K": off.z, + }, + ) center = Vector(10, 10, 0) radius = 5 - self.assertCommandEqual(cmds(center, radius), cmd('G3', Vector(15, 10, 0), Vector(-5, 0, 0))) + self.assertCommandEqual( + cmds(center, radius), cmd("G3", Vector(15, 10, 0), Vector(-5, 0, 0)) + ) def test42(self): - '''Verify ellipsis results in a proper segmentation of G1 commands.''' + """Verify ellipsis results in a proper segmentation of G1 commands.""" ellipse = Part.Edge(Part.Ellipse()) cmds = PathGeom.cmdsForEdge(ellipse) # let's make sure all commands are G1 and there are more than 20 of those self.assertGreater(len(cmds), 20) - self.assertTrue(all([cmd.Name == 'G1' for cmd in cmds])) + self.assertTrue(all([cmd.Name == "G1" for cmd in cmds])) def test50(self): - '''Verify proper wire(s) aggregation from a Path.''' + """Verify proper wire(s) aggregation from a Path.""" commands = [] - commands.append(Path.Command('G1', {'X': 1})) - commands.append(Path.Command('G1', {'Y': 1})) - commands.append(Path.Command('G0', {'X': 0})) - commands.append(Path.Command('G1', {'Y': 0})) + commands.append(Path.Command("G1", {"X": 1})) + commands.append(Path.Command("G1", {"Y": 1})) + commands.append(Path.Command("G0", {"X": 0})) + commands.append(Path.Command("G1", {"Y": 0})) - wire,rapid = PathGeom.wireForPath(Path.Path(commands)) + wire, rapid = PathGeom.wireForPath(Path.Path(commands)) self.assertEqual(len(wire.Edges), 4) - self.assertLine(wire.Edges[0], Vector(0,0,0), Vector(1,0,0)) - self.assertLine(wire.Edges[1], Vector(1,0,0), Vector(1,1,0)) - self.assertLine(wire.Edges[2], Vector(1,1,0), Vector(0,1,0)) - self.assertLine(wire.Edges[3], Vector(0,1,0), Vector(0,0,0)) + self.assertLine(wire.Edges[0], Vector(0, 0, 0), Vector(1, 0, 0)) + self.assertLine(wire.Edges[1], Vector(1, 0, 0), Vector(1, 1, 0)) + self.assertLine(wire.Edges[2], Vector(1, 1, 0), Vector(0, 1, 0)) + self.assertLine(wire.Edges[3], Vector(0, 1, 0), Vector(0, 0, 0)) self.assertEqual(len(rapid), 1) self.assertTrue(PathGeom.edgesMatch(rapid[0], wire.Edges[2])) wires = PathGeom.wiresForPath(Path.Path(commands)) self.assertEqual(len(wires), 2) self.assertEqual(len(wires[0].Edges), 2) - self.assertLine(wires[0].Edges[0], Vector(0,0,0), Vector(1,0,0)) - self.assertLine(wires[0].Edges[1], Vector(1,0,0), Vector(1,1,0)) + self.assertLine(wires[0].Edges[0], Vector(0, 0, 0), Vector(1, 0, 0)) + self.assertLine(wires[0].Edges[1], Vector(1, 0, 0), Vector(1, 1, 0)) self.assertEqual(len(wires[1].Edges), 1) - self.assertLine(wires[1].Edges[0], Vector(0,1,0), Vector(0,0,0)) - + self.assertLine(wires[1].Edges[0], Vector(0, 1, 0), Vector(0, 0, 0)) def test60(self): - '''Verify arcToHelix returns proper helix.''' - p1 = Vector(10,-10,0) - p2 = Vector(0,0,0) - p3 = Vector(10,10,0) + """Verify arcToHelix returns proper helix.""" + p1 = Vector(10, -10, 0) + p2 = Vector(0, 0, 0) + p3 = Vector(10, 10, 0) e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p1, p2, p3)), 0, 2) - self.assertCurve(e, p1, p2 + Vector(0,0,1), p3 + Vector(0,0,2)) + self.assertCurve(e, p1, p2 + Vector(0, 0, 1), p3 + Vector(0, 0, 2)) e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p1, p2, p3)), 3, 7) - self.assertCurve(e, p1 + Vector(0,0,3), p2 + Vector(0,0,5), p3 + Vector(0,0,7)) + self.assertCurve( + e, p1 + Vector(0, 0, 3), p2 + Vector(0, 0, 5), p3 + Vector(0, 0, 7) + ) e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p1, p2, p3)), 9, 1) - self.assertCurve(e, p1 + Vector(0,0,9), p2 + Vector(0,0,5), p3 + Vector(0,0,1)) + self.assertCurve( + e, p1 + Vector(0, 0, 9), p2 + Vector(0, 0, 5), p3 + Vector(0, 0, 1) + ) - dz = Vector(0,0,3) + dz = Vector(0, 0, 3) p11 = p1 + dz p12 = p2 + dz p13 = p3 + dz e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p11, p12, p13)), 0, 8) - self.assertCurve(e, p1, p2 + Vector(0,0,4), p3 + Vector(0,0,8)) + self.assertCurve(e, p1, p2 + Vector(0, 0, 4), p3 + Vector(0, 0, 8)) e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p11, p12, p13)), 2, -2) - self.assertCurve(e, p1 + Vector(0,0,2), p2, p3 + Vector(0,0,-2)) + self.assertCurve(e, p1 + Vector(0, 0, 2), p2, p3 + Vector(0, 0, -2)) p1 = Vector(10, -10, 1) - p2 = Vector(10 - 10*math.sin(math.pi/4), -10*math.cos(math.pi/4), 1) + p2 = Vector(10 - 10 * math.sin(math.pi / 4), -10 * math.cos(math.pi / 4), 1) p3 = Vector(0, 0, 1) e = PathGeom.arcToHelix(Part.Edge(Part.Arc(p1, p2, p3)), 0, 5) - self.assertCurve(e, Vector(10,-10,0), Vector(p2.x,p2.y,2.5), Vector(0, 0, 5)) - + self.assertCurve( + e, Vector(10, -10, 0), Vector(p2.x, p2.y, 2.5), Vector(0, 0, 5) + ) def test62(self): - '''Verify splitArcAt returns proper subarcs.''' - p1 = Vector(10,-10,0) - p2 = Vector(0,0,0) - p3 = Vector(10,10,0) + """Verify splitArcAt returns proper subarcs.""" + p1 = Vector(10, -10, 0) + p2 = Vector(0, 0, 0) + p3 = Vector(10, 10, 0) arc = Part.Edge(Part.Arc(p1, p2, p3)) - o = 10*math.sin(math.pi/4) + o = 10 * math.sin(math.pi / 4) p12 = Vector(10 - o, -o, 0) p23 = Vector(10 - o, +o, 0) @@ -391,51 +717,55 @@ class TestPathGeom(PathTestBase): self.assertCurve(e[0], p1, p12, p2) self.assertCurve(e[1], p2, p23, p3) - p34 = Vector(10 - 10*math.sin(1*math.pi/8), -10*math.cos(1*math.pi/8), 0) - p45 = Vector(10 - 10*math.sin(5*math.pi/8), -10*math.cos(5*math.pi/8), 0) + p34 = Vector( + 10 - 10 * math.sin(1 * math.pi / 8), -10 * math.cos(1 * math.pi / 8), 0 + ) + p45 = Vector( + 10 - 10 * math.sin(5 * math.pi / 8), -10 * math.cos(5 * math.pi / 8), 0 + ) e = PathGeom.splitArcAt(arc, p12) self.assertCurve(e[0], p1, p34, p12) self.assertCurve(e[1], p12, p45, p3) - def test65(self): - '''Verify splitEdgeAt.''' + """Verify splitEdgeAt.""" # split a line segment - e = PathGeom.splitEdgeAt(Part.Edge(Part.LineSegment(Vector(), Vector(2, 4, 6))), Vector(1, 2, 3)) - self.assertLine(e[0], Vector(), Vector(1,2,3)) - self.assertLine(e[1], Vector(1,2,3), Vector(2,4,6)) + e = PathGeom.splitEdgeAt( + Part.Edge(Part.LineSegment(Vector(), Vector(2, 4, 6))), Vector(1, 2, 3) + ) + self.assertLine(e[0], Vector(), Vector(1, 2, 3)) + self.assertLine(e[1], Vector(1, 2, 3), Vector(2, 4, 6)) # split an arc - p1 = Vector(10,-10,1) - p2 = Vector(0,0,1) - p3 = Vector(10,10,1) + p1 = Vector(10, -10, 1) + p2 = Vector(0, 0, 1) + p3 = Vector(10, 10, 1) arc = Part.Edge(Part.Arc(p1, p2, p3)) e = PathGeom.splitEdgeAt(arc, p2) - o = 10*math.sin(math.pi/4) + o = 10 * math.sin(math.pi / 4) p12 = Vector(10 - o, -o, 1) p23 = Vector(10 - o, +o, 1) self.assertCurve(e[0], p1, p12, p2) self.assertCurve(e[1], p2, p23, p3) - # split a helix - p1 = Vector(10,-10,0) - p2 = Vector(0,0,5) - p3 = Vector(10,10,10) + p1 = Vector(10, -10, 0) + p2 = Vector(0, 0, 5) + p3 = Vector(10, 10, 10) h = PathGeom.arcToHelix(arc, 0, 10) self.assertCurve(h, p1, p2, p3) e = PathGeom.splitEdgeAt(h, p2) - o = 10*math.sin(math.pi/4) + o = 10 * math.sin(math.pi / 4) p12 = Vector(10 - o, -o, 2.5) p23 = Vector(10 - o, +o, 7.5) self.assertCurve(e[0], p1, p12, p2) self.assertCurve(e[1], p2, p23, p3) def test66(self): - '''Split arc real world sample''' + """Split arc real world sample""" af = Vector(421.55, 378.41, 1) am = Vector(459.51, 372.61, 1) @@ -447,13 +777,19 @@ class TestPathGeom(PathTestBase): head, tail = PathGeom.splitEdgeAt(arc, s) # make sure the arcs connect as they should - self.assertCoincide(arc.valueAt(arc.FirstParameter), head.valueAt(head.FirstParameter), 0.005) + self.assertCoincide( + arc.valueAt(arc.FirstParameter), head.valueAt(head.FirstParameter), 0.005 + ) self.assertCoincide(s, head.valueAt(head.LastParameter), 0.005) self.assertCoincide(s, tail.valueAt(tail.FirstParameter), 0.005) i = arc.valueAt(arc.LastParameter) j = tail.valueAt(tail.LastParameter) - print("(%.2f, %.2f, %.2f) vs. (%.2f, %.2f, %.2f)" % (i.x, i.y, i.z, j.x, j.y, j.z)) - self.assertCoincide(arc.valueAt(arc.LastParameter), tail.valueAt(tail.LastParameter), 0.005) + print( + "(%.2f, %.2f, %.2f) vs. (%.2f, %.2f, %.2f)" % (i.x, i.y, i.z, j.x, j.y, j.z) + ) + self.assertCoincide( + arc.valueAt(arc.LastParameter), tail.valueAt(tail.LastParameter), 0.005 + ) # make sure the radii match self.assertRoughly(arc.Curve.Radius, head.Curve.Radius, 0.001) @@ -464,24 +800,24 @@ class TestPathGeom(PathTestBase): self.assertCoincide(arc.Curve.Center, tail.Curve.Center, 0.001) def test70(self): - '''Flip a line.''' - edge = Part.Edge(Part.Line(Vector(0,0,0), Vector(3, 2, 1))) + """Flip a line.""" + edge = Part.Edge(Part.Line(Vector(0, 0, 0), Vector(3, 2, 1))) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) - edge = Part.Edge(Part.Line(Vector(0,0,0), Vector(-3, -2, -1))) + edge = Part.Edge(Part.Line(Vector(0, 0, 0), Vector(-3, -2, -1))) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) def test71(self): - '''Flip a line segment.''' - edge = Part.Edge(Part.LineSegment(Vector(0,0,0), Vector(3, 2, 1))) + """Flip a line segment.""" + edge = Part.Edge(Part.LineSegment(Vector(0, 0, 0), Vector(3, 2, 1))) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) - edge = Part.Edge(Part.LineSegment(Vector(4,2,1), Vector(-3, -7, 9))) + edge = Part.Edge(Part.LineSegment(Vector(4, 2, 1), Vector(-3, -7, 9))) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) - edge = Part.makeLine(Vector(1,0,3), Vector(3, 2, 1)) + edge = Part.makeLine(Vector(1, 0, 3), Vector(3, 2, 1)) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) def test72(self): - '''Flip a circle''' + """Flip a circle""" edge = Part.makeCircle(3, Vector(1, 3, 2), Vector(0, 0, 1)) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) @@ -489,7 +825,7 @@ class TestPathGeom(PathTestBase): self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) def test73(self): - '''Flip an arc''' + """Flip an arc""" # make sure all 4 quadrants work edge = Part.makeCircle(3, Vector(1, 3, 2), Vector(0, 0, 1), 45, 90) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) @@ -514,32 +850,53 @@ class TestPathGeom(PathTestBase): self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) def test74(self): - '''Flip a rotated arc''' + """Flip a rotated arc""" # oh yes ... edge = Part.makeCircle(3, Vector(1, 3, 2), Vector(0, 0, 1), 45, 90) edge.rotate(edge.Curve.Center, Vector(0, 0, 1), -90) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) def test75(self): - '''Flip a B-spline''' + """Flip a B-spline""" spline = Part.BSplineCurve() - spline.interpolate([Vector(1,2,3), Vector(-3,0,7), Vector(-3,1,9), Vector(1, 3, 5)]) + spline.interpolate( + [Vector(1, 2, 3), Vector(-3, 0, 7), Vector(-3, 1, 9), Vector(1, 3, 5)] + ) edge = Part.Edge(spline) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) - edge = Part.Edge(Part.BSplineCurve([Vector(-8,4,0), Vector(1,-5,0), Vector(5,11,0), Vector(12,-5,0)], weights=[2,3,5,7])) + edge = Part.Edge( + Part.BSplineCurve( + [ + Vector(-8, 4, 0), + Vector(1, -5, 0), + Vector(5, 11, 0), + Vector(12, -5, 0), + ], + weights=[2, 3, 5, 7], + ) + ) self.assertEdgeShapesMatch(edge, PathGeom.flipEdge(edge)) def test76(self): - '''Flip an offset wire''' + """Flip an offset wire""" - e0 = Part.Edge(Part.BSplineCurve([Vector(-8,4,0), Vector(1,-5,0), Vector(5,11,0), Vector(12,-5,0)], weights=[2,3,5,7])) - e1 = Part.Edge(Part.LineSegment(Vector(12,-5,0), Vector(0,-7,0))) - e2 = Part.Edge(Part.LineSegment(Vector(0,-7,0), Vector(-8,4,0))) + e0 = Part.Edge( + Part.BSplineCurve( + [ + Vector(-8, 4, 0), + Vector(1, -5, 0), + Vector(5, 11, 0), + Vector(12, -5, 0), + ], + weights=[2, 3, 5, 7], + ) + ) + e1 = Part.Edge(Part.LineSegment(Vector(12, -5, 0), Vector(0, -7, 0))) + e2 = Part.Edge(Part.LineSegment(Vector(0, -7, 0), Vector(-8, 4, 0))) w0 = Part.Wire([e0, e1, e2]) w1 = w0.makeOffset2D(1) w2 = PathGeom.flipWire(w1) # do some sanity checks self.assertTrue(w2.isValid()) self.assertTrue(w2.isClosed()) - diff --git a/src/Mod/Path/PathTests/TestPathHelix.py b/src/Mod/Path/PathTests/TestPathHelix.py index a3dc545156..c3512571d7 100644 --- a/src/Mod/Path/PathTests/TestPathHelix.py +++ b/src/Mod/Path/PathTests/TestPathHelix.py @@ -28,7 +28,7 @@ import PathScripts.PathLog as PathLog import PathTests.PathTestUtils as PathTestUtils PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) class TestPathHelix(PathTestUtils.PathTestBase): @@ -36,35 +36,39 @@ class TestPathHelix(PathTestUtils.PathTestBase): def setUp(self): self.clone = None - self.doc = FreeCAD.open(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_holes00.fcstd') - self.job = PathJob.Create('Job', [self.doc.Body]) + self.doc = FreeCAD.open( + FreeCAD.getHomePath() + "Mod/Path/PathTests/test_holes00.fcstd" + ) + self.job = PathJob.Create("Job", [self.doc.Body]) def tearDown(self): FreeCAD.closeDocument(self.doc.Name) def test00(self): - '''Verify Helix does not throw an exception.''' + """Verify Helix does not throw an exception.""" - op = PathHelix.Create('Helix') + op = PathHelix.Create("Helix") op.Proxy.execute(op) def test01(self): - '''Verify Helix generates proper holes from model''' + """Verify Helix generates proper holes from model""" - op = PathHelix.Create('Helix') + op = PathHelix.Create("Helix") proxy = op.Proxy for base in op.Base: model = base[0] for sub in base[1]: pos = proxy.holePosition(op, model, sub) - self.assertRoughly(round(pos.Length / 10, 0), proxy.holeDiameter(op, model, sub)) + self.assertRoughly( + round(pos.Length / 10, 0), proxy.holeDiameter(op, model, sub) + ) def test02(self): - '''Verify Helix generates proper holes for rotated model''' + """Verify Helix generates proper holes for rotated model""" self.job.Tools.Group[0].Tool.Diameter = 0.5 - op = PathHelix.Create('Helix') + op = PathHelix.Create("Helix") proxy = op.Proxy model = self.job.Model.Group[0] @@ -74,22 +78,25 @@ class TestPathHelix(PathTestUtils.PathTestBase): model = base[0] for sub in base[1]: pos = proxy.holePosition(op, model, sub) - #PathLog.track(deg, pos, pos.Length) - self.assertRoughly(round(pos.Length / 10, 0), proxy.holeDiameter(op, model, sub)) - + # PathLog.track(deg, pos, pos.Length) + self.assertRoughly( + round(pos.Length / 10, 0), proxy.holeDiameter(op, model, sub) + ) def test03(self): - '''Verify Helix generates proper holes for rotated base model''' + """Verify Helix generates proper holes for rotated base model""" for deg in range(self.RotateBy, 360, self.RotateBy): self.tearDown() - self.doc = FreeCAD.open(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_holes00.fcstd') + self.doc = FreeCAD.open( + FreeCAD.getHomePath() + "Mod/Path/PathTests/test_holes00.fcstd" + ) self.doc.Body.Placement.Rotation = FreeCAD.Rotation(deg, 0, 0) - self.job = PathJob.Create('Job', [self.doc.Body]) + self.job = PathJob.Create("Job", [self.doc.Body]) self.job.Tools.Group[0].Tool.Diameter = 0.5 - op = PathHelix.Create('Helix') + op = PathHelix.Create("Helix") proxy = op.Proxy model = self.job.Model.Group[0] @@ -97,21 +104,25 @@ class TestPathHelix(PathTestUtils.PathTestBase): model = base[0] for sub in base[1]: pos = proxy.holePosition(op, model, sub) - #PathLog.track(deg, pos, pos.Length) - self.assertRoughly(round(pos.Length / 10, 0), proxy.holeDiameter(op, model, sub)) + # PathLog.track(deg, pos, pos.Length) + self.assertRoughly( + round(pos.Length / 10, 0), proxy.holeDiameter(op, model, sub) + ) def test04(self): - '''Verify Helix generates proper holes for rotated clone base model''' + """Verify Helix generates proper holes for rotated clone base model""" for deg in range(self.RotateBy, 360, self.RotateBy): self.tearDown() - self.doc = FreeCAD.open(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_holes00.fcstd') + self.doc = FreeCAD.open( + FreeCAD.getHomePath() + "Mod/Path/PathTests/test_holes00.fcstd" + ) self.clone = Draft.clone(self.doc.Body) self.clone.Placement.Rotation = FreeCAD.Rotation(deg, 0, 0) - self.job = PathJob.Create('Job', [self.clone]) + self.job = PathJob.Create("Job", [self.clone]) self.job.Tools.Group[0].Tool.Diameter = 0.5 - op = PathHelix.Create('Helix') + op = PathHelix.Create("Helix") proxy = op.Proxy model = self.job.Model.Group[0] @@ -119,5 +130,7 @@ class TestPathHelix(PathTestUtils.PathTestBase): model = base[0] for sub in base[1]: pos = proxy.holePosition(op, model, sub) - #PathLog.track(deg, pos, pos.Length) - self.assertRoughly(round(pos.Length / 10, 0), proxy.holeDiameter(op, model, sub)) + # PathLog.track(deg, pos, pos.Length) + self.assertRoughly( + round(pos.Length / 10, 0), proxy.holeDiameter(op, model, sub) + ) diff --git a/src/Mod/Path/PathTests/TestPathLog.py b/src/Mod/Path/PathTests/TestPathLog.py index bb85f78a63..a18fe4abc1 100644 --- a/src/Mod/Path/PathTests/TestPathLog.py +++ b/src/Mod/Path/PathTests/TestPathLog.py @@ -23,10 +23,11 @@ import PathScripts.PathLog as PathLog import unittest + class TestPathLog(unittest.TestCase): """Some basic tests for the logging framework.""" - MODULE = 'TestPathLog' # file name without extension + MODULE = "TestPathLog" # file name without extension def setUp(self): PathLog.setLevel(PathLog.Level.RESET) @@ -34,8 +35,10 @@ class TestPathLog(unittest.TestCase): def callerFile(self): return PathLog._caller()[0] + def callerLine(self): return PathLog._caller()[1] + def callerFunc(self): return PathLog._caller()[2] @@ -45,7 +48,7 @@ class TestPathLog(unittest.TestCase): def test01(self): """Check for proper function extraction.""" - self.assertEqual(self.callerFunc(), 'test01') + self.assertEqual(self.callerFunc(), "test01") def test10(self): """Verify default log levels is NOTICE.""" @@ -69,7 +72,7 @@ class TestPathLog(unittest.TestCase): def test13(self): """Verify setting other modul's log level doesn't change this one's.""" # if this test fails then most likely the global RESET is broken - PathLog.setLevel(PathLog.Level.DEBUG, 'SomeOtherModule') + PathLog.setLevel(PathLog.Level.DEBUG, "SomeOtherModule") self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE) self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.NOTICE) @@ -100,93 +103,93 @@ class TestPathLog(unittest.TestCase): def test30(self): """Verify log level ERROR.""" PathLog.setLevel(PathLog.Level.ERROR) - self.assertIsNone(PathLog.debug('something')) - self.assertIsNone(PathLog.info('something')) - self.assertIsNone(PathLog.notice('something')) - self.assertIsNone(PathLog.warning('something')) - self.assertIsNotNone(PathLog.error('something')) + self.assertIsNone(PathLog.debug("something")) + self.assertIsNone(PathLog.info("something")) + self.assertIsNone(PathLog.notice("something")) + self.assertIsNone(PathLog.warning("something")) + self.assertIsNotNone(PathLog.error("something")) def test31(self): """Verify log level WARNING.""" PathLog.setLevel(PathLog.Level.WARNING) - self.assertIsNone(PathLog.debug('something')) - self.assertIsNone(PathLog.info('something')) - self.assertIsNone(PathLog.notice('something')) - self.assertIsNotNone(PathLog.warning('something')) - self.assertIsNotNone(PathLog.error('something')) + self.assertIsNone(PathLog.debug("something")) + self.assertIsNone(PathLog.info("something")) + self.assertIsNone(PathLog.notice("something")) + self.assertIsNotNone(PathLog.warning("something")) + self.assertIsNotNone(PathLog.error("something")) def test32(self): """Verify log level NOTICE.""" PathLog.setLevel(PathLog.Level.NOTICE) - self.assertIsNone(PathLog.debug('something')) - self.assertIsNone(PathLog.info('something')) - self.assertIsNotNone(PathLog.notice('something')) - self.assertIsNotNone(PathLog.warning('something')) - self.assertIsNotNone(PathLog.error('something')) + self.assertIsNone(PathLog.debug("something")) + self.assertIsNone(PathLog.info("something")) + self.assertIsNotNone(PathLog.notice("something")) + self.assertIsNotNone(PathLog.warning("something")) + self.assertIsNotNone(PathLog.error("something")) def test33(self): """Verify log level INFO.""" PathLog.setLevel(PathLog.Level.INFO) - self.assertIsNone(PathLog.debug('something')) - self.assertIsNotNone(PathLog.info('something')) - self.assertIsNotNone(PathLog.notice('something')) - self.assertIsNotNone(PathLog.warning('something')) - self.assertIsNotNone(PathLog.error('something')) + self.assertIsNone(PathLog.debug("something")) + self.assertIsNotNone(PathLog.info("something")) + self.assertIsNotNone(PathLog.notice("something")) + self.assertIsNotNone(PathLog.warning("something")) + self.assertIsNotNone(PathLog.error("something")) def test34(self): """Verify log level DEBUG.""" PathLog.setLevel(PathLog.Level.DEBUG) - self.assertIsNotNone(PathLog.debug('something')) - self.assertIsNotNone(PathLog.info('something')) - self.assertIsNotNone(PathLog.notice('something')) - self.assertIsNotNone(PathLog.warning('something')) - self.assertIsNotNone(PathLog.error('something')) + self.assertIsNotNone(PathLog.debug("something")) + self.assertIsNotNone(PathLog.info("something")) + self.assertIsNotNone(PathLog.notice("something")) + self.assertIsNotNone(PathLog.warning("something")) + self.assertIsNotNone(PathLog.error("something")) def test50(self): """Verify no tracking by default.""" - self.assertIsNone(PathLog.track('this', 'and', 'that')) + self.assertIsNone(PathLog.track("this", "and", "that")) def test51(self): """Verify enabling tracking for module results in tracking.""" PathLog.trackModule() # Don't want to rely on the line number matching - still want some # indication that track does the right thing .... - msg = PathLog.track('this', 'and', 'that') + msg = PathLog.track("this", "and", "that") self.assertTrue(msg.startswith(self.MODULE)) - self.assertTrue(msg.endswith('test51(this, and, that)')) + self.assertTrue(msg.endswith("test51(this, and, that)")) def test52(self): """Verify untracking stops tracking.""" PathLog.trackModule() - self.assertIsNotNone(PathLog.track('this', 'and', 'that')) + self.assertIsNotNone(PathLog.track("this", "and", "that")) PathLog.untrackModule() - self.assertIsNone(PathLog.track('this', 'and', 'that')) + self.assertIsNone(PathLog.track("this", "and", "that")) def test53(self): """Verify trackAllModules works correctly.""" PathLog.trackAllModules(True) - self.assertIsNotNone(PathLog.track('this', 'and', 'that')) + self.assertIsNotNone(PathLog.track("this", "and", "that")) PathLog.trackAllModules(False) - self.assertIsNone(PathLog.track('this', 'and', 'that')) + self.assertIsNone(PathLog.track("this", "and", "that")) PathLog.trackAllModules(True) PathLog.trackModule() - self.assertIsNotNone(PathLog.track('this', 'and', 'that')) + self.assertIsNotNone(PathLog.track("this", "and", "that")) PathLog.trackAllModules(False) - self.assertIsNotNone(PathLog.track('this', 'and', 'that')) + self.assertIsNotNone(PathLog.track("this", "and", "that")) def test60(self): """Verify track handles no argument.""" PathLog.trackModule() msg = PathLog.track() self.assertTrue(msg.startswith(self.MODULE)) - self.assertTrue(msg.endswith('test60()')) + self.assertTrue(msg.endswith("test60()")) def test61(self): """Verify track handles arbitrary argument types correctly.""" PathLog.trackModule() - msg = PathLog.track('this', None, 1, 18.25) + msg = PathLog.track("this", None, 1, 18.25) self.assertTrue(msg.startswith(self.MODULE)) - self.assertTrue(msg.endswith('test61(this, None, 1, 18.25)')) + self.assertTrue(msg.endswith("test61(this, None, 1, 18.25)")) def testzz(self): """Restoring environment after tests.""" diff --git a/src/Mod/Path/PathTests/TestPathOpTools.py b/src/Mod/Path/PathTests/TestPathOpTools.py index eed0eee99e..34dfc09c5a 100644 --- a/src/Mod/Path/PathTests/TestPathOpTools.py +++ b/src/Mod/Path/PathTests/TestPathOpTools.py @@ -31,11 +31,13 @@ import math from FreeCAD import Vector PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) + def getWire(obj, nr=0): return obj.Tip.Profile[0].Shape.Wires[nr] + def getWireInside(obj): w1 = getWire(obj, 0) w2 = getWire(obj, 1) @@ -43,6 +45,7 @@ def getWireInside(obj): return w1 return w2 + def getWireOutside(obj): w1 = getWire(obj, 0) w2 = getWire(obj, 1) @@ -50,16 +53,20 @@ def getWireOutside(obj): return w2 return w1 + def getPositiveShape(obj): return obj.Tool.Shape + def getNegativeShape(obj): return obj.Shape + doc = None triangle = None shape = None + def makeWire(pts): edges = [] first = pts[0] @@ -70,6 +77,7 @@ def makeWire(pts): edges.append(Part.Edge(Part.LineSegment(last, first))) return Part.Wire(edges) + def wireMarkers(wire): pts = [wire.Edges[0].valueAt(wire.Edges[0].FirstParameter)] for edge in wire.Edges: @@ -78,18 +86,19 @@ def wireMarkers(wire): class TestPathOpTools(PathTestUtils.PathTestBase): - @classmethod def setUpClass(cls): global doc - doc = FreeCAD.openDocument(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_geomop.fcstd') + doc = FreeCAD.openDocument( + FreeCAD.getHomePath() + "Mod/Path/PathTests/test_geomop.fcstd" + ) @classmethod def tearDownClass(cls): FreeCAD.closeDocument("test_geomop") def test00(self): - '''Verify isWireClockwise for polygon wires.''' + """Verify isWireClockwise for polygon wires.""" pa = Vector(1, 1, 0) pb = Vector(1, 5, 0) pc = Vector(5, 5, 0) @@ -99,43 +108,51 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertFalse(PathOpTools.isWireClockwise(makeWire([pa, pd, pc, pb]))) def test01(self): - '''Verify isWireClockwise for single edge circle wires.''' - self.assertTrue(PathOpTools.isWireClockwise(Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1)))) - self.assertFalse(PathOpTools.isWireClockwise(Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1)))) + """Verify isWireClockwise for single edge circle wires.""" + self.assertTrue( + PathOpTools.isWireClockwise( + Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1)) + ) + ) + self.assertFalse( + PathOpTools.isWireClockwise( + Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1)) + ) + ) def test02(self): - '''Verify isWireClockwise for two half circle wires.''' - e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180) + """Verify isWireClockwise for two half circle wires.""" + e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180) e1 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 180, 360) self.assertTrue(PathOpTools.isWireClockwise(Part.Wire([e0, e1]))) - e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180) + e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180) e1 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 180, 360) self.assertFalse(PathOpTools.isWireClockwise(Part.Wire([e0, e1]))) def test03(self): - '''Verify isWireClockwise for two edge wires with an arc.''' - e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180) + """Verify isWireClockwise for two edge wires with an arc.""" + e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180) e2 = Part.makeLine(e0.valueAt(e0.LastParameter), e0.valueAt(e0.FirstParameter)) self.assertTrue(PathOpTools.isWireClockwise(Part.Wire([e0, e2]))) - e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180) + e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180) e2 = Part.makeLine(e0.valueAt(e0.LastParameter), e0.valueAt(e0.FirstParameter)) self.assertFalse(PathOpTools.isWireClockwise(Part.Wire([e0, e2]))) def test04(self): - '''Verify isWireClockwise for unoriented wires.''' - e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180) + """Verify isWireClockwise for unoriented wires.""" + e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180) e3 = Part.makeLine(e0.valueAt(e0.FirstParameter), e0.valueAt(e0.LastParameter)) self.assertTrue(PathOpTools.isWireClockwise(Part.Wire([e0, e3]))) - e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180) + e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180) e3 = Part.makeLine(e0.valueAt(e0.FirstParameter), e0.valueAt(e0.LastParameter)) self.assertFalse(PathOpTools.isWireClockwise(Part.Wire([e0, e3]))) def test11(self): - '''Check offsetting a circular hole.''' - obj = doc.getObjectsByLabel('offset-circle')[0] + """Check offsetting a circular hole.""" + obj = doc.getObjectsByLabel("offset-circle")[0] small = getWireInside(obj) self.assertRoughly(10, small.Edges[0].Curve.Radius) @@ -153,8 +170,8 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, 1), wire.Edges[0].Curve.Axis) def test12(self): - '''Check offsetting a circular hole by the radius or more makes the hole vanish.''' - obj = doc.getObjectsByLabel('offset-circle')[0] + """Check offsetting a circular hole by the radius or more makes the hole vanish.""" + obj = doc.getObjectsByLabel("offset-circle")[0] small = getWireInside(obj) self.assertRoughly(10, small.Edges[0].Curve.Radius) @@ -165,8 +182,8 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertIsNone(wire) def test13(self): - '''Check offsetting a cylinder succeeds.''' - obj = doc.getObjectsByLabel('offset-circle')[0] + """Check offsetting a cylinder succeeds.""" + obj = doc.getObjectsByLabel("offset-circle")[0] big = getWireOutside(obj) self.assertRoughly(20, big.Edges[0].Curve.Radius) @@ -184,10 +201,15 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, -1), wire.Edges[0].Curve.Axis) def test14(self): - '''Check offsetting a hole with Placement.''' - obj = doc.getObjectsByLabel('offset-placement')[0] + """Check offsetting a hole with Placement.""" + obj = doc.getObjectsByLabel("offset-placement")[0] - wires = [w for w in obj.Shape.Wires if 1 == len(w.Edges) and PathGeom.isRoughly(0, w.Edges[0].Vertexes[0].Point.z)] + wires = [ + w + for w in obj.Shape.Wires + if 1 == len(w.Edges) + and PathGeom.isRoughly(0, w.Edges[0].Vertexes[0].Point.z) + ] self.assertEqual(2, len(wires)) w = wires[1] if wires[0].BoundBox.isInside(wires[1].BoundBox) else wires[0] @@ -203,10 +225,15 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, 1), wire.Edges[0].Curve.Axis) def test15(self): - '''Check offsetting a cylinder with Placement.''' - obj = doc.getObjectsByLabel('offset-placement')[0] + """Check offsetting a cylinder with Placement.""" + obj = doc.getObjectsByLabel("offset-placement")[0] - wires = [w for w in obj.Shape.Wires if 1 == len(w.Edges) and PathGeom.isRoughly(0, w.Edges[0].Vertexes[0].Point.z)] + wires = [ + w + for w in obj.Shape.Wires + if 1 == len(w.Edges) + and PathGeom.isRoughly(0, w.Edges[0].Vertexes[0].Point.z) + ] self.assertEqual(2, len(wires)) w = wires[0] if wires[0].BoundBox.isInside(wires[1].BoundBox) else wires[1] @@ -222,14 +249,23 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, -1), wire.Edges[0].Curve.Axis) def test20(self): - '''Check offsetting hole wire succeeds.''' - obj = doc.getObjectsByLabel('offset-edge')[0] + """Check offsetting hole wire succeeds.""" + obj = doc.getObjectsByLabel("offset-edge")[0] small = getWireInside(obj) # sanity check y = 10 - x = 10 * math.cos(math.pi/6) - self.assertLines(small.Edges, False, [Vector(0, y, 0), Vector(-x, -y/2, 0), Vector(x, -y/2, 0), Vector(0, y, 0)]) + x = 10 * math.cos(math.pi / 6) + self.assertLines( + small.Edges, + False, + [ + Vector(0, y, 0), + Vector(-x, -y / 2, 0), + Vector(x, -y / 2, 0), + Vector(0, y, 0), + ], + ) wire = PathOpTools.offsetWire(small, obj.Shape, 3, True) self.assertIsNotNone(wire) @@ -238,30 +274,52 @@ class TestPathOpTools(PathTestUtils.PathTestBase): # for holes processing "forward" means CCW self.assertFalse(PathOpTools.isWireClockwise(wire)) y = 4 # offset works in both directions - x = 4 * math.cos(math.pi/6) - self.assertLines(wire.Edges, False, [Vector(0, 4, 0), Vector(-x, -2, 0), Vector(x, -2, 0), Vector(0, 4, 0)]) + x = 4 * math.cos(math.pi / 6) + self.assertLines( + wire.Edges, + False, + [Vector(0, 4, 0), Vector(-x, -2, 0), Vector(x, -2, 0), Vector(0, 4, 0)], + ) def test21(self): - '''Check offsetting hole wire for more than it's size makes hole vanish.''' - obj = doc.getObjectsByLabel('offset-edge')[0] + """Check offsetting hole wire for more than it's size makes hole vanish.""" + obj = doc.getObjectsByLabel("offset-edge")[0] small = getWireInside(obj) # sanity check y = 10 - x = 10 * math.cos(math.pi/6) - self.assertLines(small.Edges, False, [Vector(0, y, 0), Vector(-x, -y/2, 0), Vector(x, -y/2, 0), Vector(0, y, 0)]) + x = 10 * math.cos(math.pi / 6) + self.assertLines( + small.Edges, + False, + [ + Vector(0, y, 0), + Vector(-x, -y / 2, 0), + Vector(x, -y / 2, 0), + Vector(0, y, 0), + ], + ) wire = PathOpTools.offsetWire(small, obj.Shape, 5, True) self.assertIsNone(wire) def test22(self): - '''Check offsetting a body wire succeeds.''' - obj = doc.getObjectsByLabel('offset-edge')[0] + """Check offsetting a body wire succeeds.""" + obj = doc.getObjectsByLabel("offset-edge")[0] big = getWireOutside(obj) # sanity check y = 20 - x = 20 * math.cos(math.pi/6) - self.assertLines(big.Edges, False, [Vector(0, y, 0), Vector(-x, -y/2, 0), Vector(x, -y/2, 0), Vector(0, y, 0)]) + x = 20 * math.cos(math.pi / 6) + self.assertLines( + big.Edges, + False, + [ + Vector(0, y, 0), + Vector(-x, -y / 2, 0), + Vector(x, -y / 2, 0), + Vector(0, y, 0), + ], + ) wire = PathOpTools.offsetWire(big, obj.Shape, 5, True) self.assertIsNotNone(wire) @@ -275,10 +333,12 @@ class TestPathOpTools(PathTestUtils.PathTestBase): else: self.assertRoughly(34.641, e.Length, 0.001) begin = e.Vertexes[0].Point - end = e.Vertexes[1].Point + end = e.Vertexes[1].Point v = end - begin angle = PathGeom.getAngle(v) - if PathGeom.isRoughly(0, angle) or PathGeom.isRoughly(math.pi, math.fabs(angle)): + if PathGeom.isRoughly(0, angle) or PathGeom.isRoughly( + math.pi, math.fabs(angle) + ): if lastAngle: self.assertRoughly(-refAngle, lastAngle) elif PathGeom.isRoughly(+refAngle, angle): @@ -293,8 +353,8 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertTrue(PathOpTools.isWireClockwise(wire)) def test31(self): - '''Check offsetting a cylinder.''' - obj = doc.getObjectsByLabel('circle-cut')[0] + """Check offsetting a cylinder.""" + obj = doc.getObjectsByLabel("circle-cut")[0] wire = PathOpTools.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True) self.assertEqual(1, len(wire.Edges)) @@ -304,22 +364,25 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertRoughly(33, edge.Curve.Radius) # the other way around everything's the same except the axis is negative - wire = PathOpTools.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False) + wire = PathOpTools.offsetWire( + getWire(obj.Tool), getPositiveShape(obj), 3, False + ) self.assertEqual(1, len(wire.Edges)) edge = wire.Edges[0] self.assertCoincide(Vector(), edge.Curve.Center) self.assertCoincide(Vector(0, 0, +1), edge.Curve.Axis) self.assertRoughly(33, edge.Curve.Radius) - def test32(self): - '''Check offsetting a box.''' - obj = doc.getObjectsByLabel('square-cut')[0] + """Check offsetting a box.""" + obj = doc.getObjectsByLabel("square-cut")[0] wire = PathOpTools.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True) self.assertEqual(8, len(wire.Edges)) self.assertEqual(4, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) - self.assertEqual(4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)])) + self.assertEqual( + 4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]) + ) for e in wire.Edges: if Part.Line == type(e.Curve): if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x): @@ -332,10 +395,14 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertTrue(PathOpTools.isWireClockwise(wire)) # change offset orientation - wire = PathOpTools.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False) + wire = PathOpTools.offsetWire( + getWire(obj.Tool), getPositiveShape(obj), 3, False + ) self.assertEqual(8, len(wire.Edges)) self.assertEqual(4, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) - self.assertEqual(4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)])) + self.assertEqual( + 4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]) + ) for e in wire.Edges: if Part.Line == type(e.Curve): if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x): @@ -347,15 +414,16 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis) self.assertFalse(PathOpTools.isWireClockwise(wire)) - def test33(self): - '''Check offsetting a triangle.''' - obj = doc.getObjectsByLabel('triangle-cut')[0] + """Check offsetting a triangle.""" + obj = doc.getObjectsByLabel("triangle-cut")[0] wire = PathOpTools.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True) self.assertEqual(6, len(wire.Edges)) self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) - self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)])) + self.assertEqual( + 3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]) + ) length = 60 * math.sin(math.radians(60)) for e in wire.Edges: if Part.Line == type(e.Curve): @@ -365,10 +433,14 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis) # change offset orientation - wire = PathOpTools.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False) + wire = PathOpTools.offsetWire( + getWire(obj.Tool), getPositiveShape(obj), 3, False + ) self.assertEqual(6, len(wire.Edges)) self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) - self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)])) + self.assertEqual( + 3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]) + ) for e in wire.Edges: if Part.Line == type(e.Curve): self.assertRoughly(length, e.Length) @@ -377,13 +449,15 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis) def test34(self): - '''Check offsetting a shape.''' - obj = doc.getObjectsByLabel('shape-cut')[0] + """Check offsetting a shape.""" + obj = doc.getObjectsByLabel("shape-cut")[0] wire = PathOpTools.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True) self.assertEqual(6, len(wire.Edges)) self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) - self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)])) + self.assertEqual( + 3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]) + ) length = 40 radius = 20 + 3 for e in wire.Edges: @@ -394,10 +468,14 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis) # change offset orientation - wire = PathOpTools.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False) + wire = PathOpTools.offsetWire( + getWire(obj.Tool), getPositiveShape(obj), 3, False + ) self.assertEqual(6, len(wire.Edges)) self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) - self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)])) + self.assertEqual( + 3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]) + ) for e in wire.Edges: if Part.Line == type(e.Curve): self.assertRoughly(length, e.Length) @@ -406,8 +484,8 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis) def test35(self): - '''Check offsetting a cylindrical hole.''' - obj = doc.getObjectsByLabel('circle-cut')[0] + """Check offsetting a cylindrical hole.""" + obj = doc.getObjectsByLabel("circle-cut")[0] wire = PathOpTools.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True) self.assertEqual(1, len(wire.Edges)) @@ -417,17 +495,18 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertRoughly(27, edge.Curve.Radius) # the other way around everything's the same except the axis is negative - wire = PathOpTools.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False) + wire = PathOpTools.offsetWire( + getWire(obj.Tool), getNegativeShape(obj), 3, False + ) self.assertEqual(1, len(wire.Edges)) edge = wire.Edges[0] self.assertCoincide(Vector(), edge.Curve.Center) self.assertCoincide(Vector(0, 0, -1), edge.Curve.Axis) self.assertRoughly(27, edge.Curve.Radius) - def test36(self): - '''Check offsetting a square hole.''' - obj = doc.getObjectsByLabel('square-cut')[0] + """Check offsetting a square hole.""" + obj = doc.getObjectsByLabel("square-cut")[0] wire = PathOpTools.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True) self.assertEqual(4, len(wire.Edges)) @@ -440,7 +519,9 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertFalse(PathOpTools.isWireClockwise(wire)) # change offset orientation - wire = PathOpTools.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False) + wire = PathOpTools.offsetWire( + getWire(obj.Tool), getNegativeShape(obj), 3, False + ) self.assertEqual(4, len(wire.Edges)) self.assertEqual(4, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) for e in wire.Edges: @@ -450,10 +531,9 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertRoughly(54, e.Length) self.assertTrue(PathOpTools.isWireClockwise(wire)) - def test37(self): - '''Check offsetting a triangular holee.''' - obj = doc.getObjectsByLabel('triangle-cut')[0] + """Check offsetting a triangular holee.""" + obj = doc.getObjectsByLabel("triangle-cut")[0] wire = PathOpTools.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True) self.assertEqual(3, len(wire.Edges)) @@ -464,7 +544,9 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertFalse(PathOpTools.isWireClockwise(wire)) # change offset orientation - wire = PathOpTools.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False) + wire = PathOpTools.offsetWire( + getWire(obj.Tool), getNegativeShape(obj), 3, False + ) self.assertEqual(3, len(wire.Edges)) self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) for e in wire.Edges: @@ -472,13 +554,15 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertTrue(PathOpTools.isWireClockwise(wire)) def test38(self): - '''Check offsetting a shape hole.''' - obj = doc.getObjectsByLabel('shape-cut')[0] + """Check offsetting a shape hole.""" + obj = doc.getObjectsByLabel("shape-cut")[0] wire = PathOpTools.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True) self.assertEqual(6, len(wire.Edges)) self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) - self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)])) + self.assertEqual( + 3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]) + ) length = 40 radius = 20 - 3 for e in wire.Edges: @@ -489,10 +573,14 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis) # change offset orientation - wire = PathOpTools.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False) + wire = PathOpTools.offsetWire( + getWire(obj.Tool), getNegativeShape(obj), 3, False + ) self.assertEqual(6, len(wire.Edges)) self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)])) - self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)])) + self.assertEqual( + 3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]) + ) for e in wire.Edges: if Part.Line == type(e.Curve): self.assertRoughly(length, e.Length) @@ -500,18 +588,21 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertRoughly(radius, e.Curve.Radius) self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis) - def test40(self): - '''Check offsetting a single outside edge forward.''' - obj = doc.getObjectsByLabel('offset-edge')[0] + """Check offsetting a single outside edge forward.""" + obj = doc.getObjectsByLabel("offset-edge")[0] w = getWireOutside(obj) - length = 40 * math.cos(math.pi/6) + length = 40 * math.cos(math.pi / 6) for e in w.Edges: self.assertRoughly(length, e.Length) # let's offset the horizontal edge for starters - hEdges = [e for e in w.Edges if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)] + hEdges = [ + e + for e in w.Edges + if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) + ] x = length / 2 y = -10 @@ -537,16 +628,20 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point) def test41(self): - '''Check offsetting a single outside edge not forward.''' - obj = doc.getObjectsByLabel('offset-edge')[0] + """Check offsetting a single outside edge not forward.""" + obj = doc.getObjectsByLabel("offset-edge")[0] w = getWireOutside(obj) - length = 40 * math.cos(math.pi/6) + length = 40 * math.cos(math.pi / 6) for e in w.Edges: self.assertRoughly(length, e.Length) # let's offset the horizontal edge for starters - hEdges = [e for e in w.Edges if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)] + hEdges = [ + e + for e in w.Edges + if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) + ] x = length / 2 y = -10 @@ -571,22 +666,26 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point) def test42(self): - '''Check offsetting multiple outside edges.''' - obj = doc.getObjectsByLabel('offset-edge')[0] + """Check offsetting multiple outside edges.""" + obj = doc.getObjectsByLabel("offset-edge")[0] obj.Shape.tessellate(0.01) doc.recompute() w = getWireOutside(obj) - length = 40 * math.cos(math.pi/6) + length = 40 * math.cos(math.pi / 6) # let's offset the other two legs - lEdges = [e for e in w.Edges if not PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)] + lEdges = [ + e + for e in w.Edges + if not PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) + ] self.assertEqual(2, len(lEdges)) wire = PathOpTools.offsetWire(Part.Wire(lEdges), obj.Shape, 2, True) - x = length/2 + 2 * math.cos(math.pi/6) - y = -10 + 2 * math.sin(math.pi/6) + x = length / 2 + 2 * math.cos(math.pi / 6) + y = -10 + 2 * math.sin(math.pi / 6) self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point) @@ -597,7 +696,7 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center) self.assertCoincide(Vector(0, 0, -1), rEdges[0].Curve.Axis) - #offset the other way + # offset the other way wire = PathOpTools.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False) self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) @@ -610,23 +709,27 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, +1), rEdges[0].Curve.Axis) def test43(self): - '''Check offsetting multiple backwards outside edges.''' + """Check offsetting multiple backwards outside edges.""" # This is exactly the same as test32, except that the wire is flipped to make # sure the input orientation doesn't matter - obj = doc.getObjectsByLabel('offset-edge')[0] + obj = doc.getObjectsByLabel("offset-edge")[0] w = getWireOutside(obj) - length = 40 * math.cos(math.pi/6) + length = 40 * math.cos(math.pi / 6) # let's offset the other two legs - lEdges = [e for e in w.Edges if not PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)] + lEdges = [ + e + for e in w.Edges + if not PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) + ] self.assertEqual(2, len(lEdges)) w = PathGeom.flipWire(Part.Wire(lEdges)) wire = PathOpTools.offsetWire(w, obj.Shape, 2, True) - x = length/2 + 2 * math.cos(math.pi/6) - y = -10 + 2 * math.sin(math.pi/6) + x = length / 2 + 2 * math.cos(math.pi / 6) + y = -10 + 2 * math.sin(math.pi / 6) self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point) @@ -637,7 +740,7 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center) self.assertCoincide(Vector(0, 0, -1), rEdges[0].Curve.Axis) - #offset the other way + # offset the other way wire = PathOpTools.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False) self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) @@ -650,16 +753,20 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(0, 0, +1), rEdges[0].Curve.Axis) def test44(self): - '''Check offsetting a single inside edge forward.''' - obj = doc.getObjectsByLabel('offset-edge')[0] + """Check offsetting a single inside edge forward.""" + obj = doc.getObjectsByLabel("offset-edge")[0] w = getWireInside(obj) - length = 20 * math.cos(math.pi/6) + length = 20 * math.cos(math.pi / 6) for e in w.Edges: self.assertRoughly(length, e.Length) # let's offset the horizontal edge for starters - hEdges = [e for e in w.Edges if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)] + hEdges = [ + e + for e in w.Edges + if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) + ] x = length / 2 y = -5 @@ -685,16 +792,20 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point) def test45(self): - '''Check offsetting a single inside edge not forward.''' - obj = doc.getObjectsByLabel('offset-edge')[0] + """Check offsetting a single inside edge not forward.""" + obj = doc.getObjectsByLabel("offset-edge")[0] w = getWireInside(obj) - length = 20 * math.cos(math.pi/6) + length = 20 * math.cos(math.pi / 6) for e in w.Edges: self.assertRoughly(length, e.Length) # let's offset the horizontal edge for starters - hEdges = [e for e in w.Edges if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)] + hEdges = [ + e + for e in w.Edges + if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) + ] x = length / 2 y = -5 @@ -720,20 +831,24 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point) def test46(self): - '''Check offsetting multiple inside edges.''' - obj = doc.getObjectsByLabel('offset-edge')[0] + """Check offsetting multiple inside edges.""" + obj = doc.getObjectsByLabel("offset-edge")[0] w = getWireInside(obj) - length = 20 * math.cos(math.pi/6) + length = 20 * math.cos(math.pi / 6) # let's offset the other two legs - lEdges = [e for e in w.Edges if not PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)] + lEdges = [ + e + for e in w.Edges + if not PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) + ] self.assertEqual(2, len(lEdges)) wire = PathOpTools.offsetWire(Part.Wire(lEdges), obj.Shape, 2, True) - x = length/2 - 2 * math.cos(math.pi/6) - y = -5 - 2 * math.sin(math.pi/6) + x = length / 2 - 2 * math.cos(math.pi / 6) + y = -5 - 2 * math.sin(math.pi / 6) self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point) @@ -741,7 +856,7 @@ class TestPathOpTools(PathTestUtils.PathTestBase): rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)] self.assertEqual(0, len(rEdges)) - #offset the other way + # offset the other way wire = PathOpTools.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False) self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) @@ -751,23 +866,27 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertEqual(0, len(rEdges)) def test47(self): - '''Check offsetting multiple backwards inside edges.''' + """Check offsetting multiple backwards inside edges.""" # This is exactly the same as test36 except that the wire is flipped to make # sure it's orientation doesn't matter - obj = doc.getObjectsByLabel('offset-edge')[0] + obj = doc.getObjectsByLabel("offset-edge")[0] w = getWireInside(obj) - length = 20 * math.cos(math.pi/6) + length = 20 * math.cos(math.pi / 6) # let's offset the other two legs - lEdges = [e for e in w.Edges if not PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)] + lEdges = [ + e + for e in w.Edges + if not PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) + ] self.assertEqual(2, len(lEdges)) w = PathGeom.flipWire(Part.Wire(lEdges)) wire = PathOpTools.offsetWire(w, obj.Shape, 2, True) - x = length/2 - 2 * math.cos(math.pi/6) - y = -5 - 2 * math.sin(math.pi/6) + x = length / 2 - 2 * math.cos(math.pi / 6) + y = -5 - 2 * math.sin(math.pi / 6) self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point) @@ -775,7 +894,7 @@ class TestPathOpTools(PathTestUtils.PathTestBase): rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)] self.assertEqual(0, len(rEdges)) - #offset the other way + # offset the other way wire = PathOpTools.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False) self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) @@ -785,7 +904,7 @@ class TestPathOpTools(PathTestUtils.PathTestBase): self.assertEqual(0, len(rEdges)) def test50(self): - '''Orient an already oriented wire''' + """Orient an already oriented wire""" p0 = Vector() p1 = Vector(1, 2, 3) p2 = Vector(2, 3, 4) @@ -794,13 +913,13 @@ class TestPathOpTools(PathTestUtils.PathTestBase): e0 = Part.Edge(Part.LineSegment(p0, p1)) e1 = Part.Edge(Part.LineSegment(p1, p2)) - wire = PathOpTools.orientWire(Part.Wire([e0, e1])) + wire = PathOpTools.orientWire(Part.Wire([e0, e1])) wirePts = wireMarkers(wire) self.assertPointsMatch(wirePts, pts) def test51(self): - '''Orient a potentially misoriented wire''' + """Orient a potentially misoriented wire""" p0 = Vector() p1 = Vector(1, 2, 3) p2 = Vector(2, 3, 4) @@ -811,20 +930,20 @@ class TestPathOpTools(PathTestUtils.PathTestBase): e1p = Part.Edge(Part.LineSegment(p1, p2)) e1m = Part.Edge(Part.LineSegment(p2, p1)) - wire = PathOpTools.orientWire(Part.Wire([e0p, e1p])) + wire = PathOpTools.orientWire(Part.Wire([e0p, e1p])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0p, e1m])) + wire = PathOpTools.orientWire(Part.Wire([e0p, e1m])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0m, e1p])) + wire = PathOpTools.orientWire(Part.Wire([e0m, e1p])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0m, e1m])) + wire = PathOpTools.orientWire(Part.Wire([e0m, e1m])) self.assertPointsMatch(wireMarkers(wire), pts) def test52(self): - '''Orient a potentially misoriented longer wire''' + """Orient a potentially misoriented longer wire""" p0 = Vector() p1 = Vector(1, 2, 3) p2 = Vector(4, 5, 6) @@ -838,26 +957,26 @@ class TestPathOpTools(PathTestUtils.PathTestBase): e2p = Part.Edge(Part.LineSegment(p2, p3)) e2m = Part.Edge(Part.LineSegment(p3, p2)) - wire = PathOpTools.orientWire(Part.Wire([e0p, e1p, e2p])) + wire = PathOpTools.orientWire(Part.Wire([e0p, e1p, e2p])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0p, e1m, e2p])) + wire = PathOpTools.orientWire(Part.Wire([e0p, e1m, e2p])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0m, e1p, e2p])) + wire = PathOpTools.orientWire(Part.Wire([e0m, e1p, e2p])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0m, e1m, e2p])) + wire = PathOpTools.orientWire(Part.Wire([e0m, e1m, e2p])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0p, e1p, e2m])) + wire = PathOpTools.orientWire(Part.Wire([e0p, e1p, e2m])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0p, e1m, e2m])) + wire = PathOpTools.orientWire(Part.Wire([e0p, e1m, e2m])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0m, e1p, e2m])) + wire = PathOpTools.orientWire(Part.Wire([e0m, e1p, e2m])) self.assertPointsMatch(wireMarkers(wire), pts) - wire = PathOpTools.orientWire(Part.Wire([e0m, e1m, e2m])) + wire = PathOpTools.orientWire(Part.Wire([e0m, e1m, e2m])) self.assertPointsMatch(wireMarkers(wire), pts) diff --git a/src/Mod/Path/PathTests/TestPathPost.py b/src/Mod/Path/PathTests/TestPathPost.py index 9beb5772a0..468e2f2316 100644 --- a/src/Mod/Path/PathTests/TestPathPost.py +++ b/src/Mod/Path/PathTests/TestPathPost.py @@ -143,13 +143,12 @@ class TestPathPostUtils(unittest.TestCase): testpath = Path.Path(commands) self.assertTrue(len(testpath.Commands) == 9) - self.assertTrue(len([c for c in testpath.Commands if c.Name in ['G2', 'G3']]) == 4) + self.assertTrue( + len([c for c in testpath.Commands if c.Name in ["G2", "G3"]]) == 4 + ) results = PostUtils.splitArcs(testpath) # self.assertTrue(len(results.Commands) == 117) - self.assertTrue(len([c for c in results.Commands if c.Name in ['G2', 'G3']]) == 0) - - - - - + self.assertTrue( + len([c for c in results.Commands if c.Name in ["G2", "G3"]]) == 0 + ) diff --git a/src/Mod/Path/PathTests/TestPathPreferences.py b/src/Mod/Path/PathTests/TestPathPreferences.py index 4abff53527..589c6ee64b 100644 --- a/src/Mod/Path/PathTests/TestPathPreferences.py +++ b/src/Mod/Path/PathTests/TestPathPreferences.py @@ -23,36 +23,45 @@ import PathScripts.PathPreferences as PathPreferences import PathTests.PathTestUtils as PathTestUtils -class TestPathPreferences(PathTestUtils.PathTestBase): +class TestPathPreferences(PathTestUtils.PathTestBase): def test00(self): - '''There is at least one search path.''' + """There is at least one search path.""" paths = PathPreferences.searchPaths() self.assertGreater(len(paths), 0) def test01(self): - '''PathScripts is part of the posts search path.''' + """PathScripts is part of the posts search path.""" paths = PathPreferences.searchPathsPost() - self.assertEqual(len([p for p in paths if p.endswith('/PathScripts/')]), 1) + self.assertEqual(len([p for p in paths if p.endswith("/PathScripts/")]), 1) def test02(self): - '''PathScripts/post is part of the posts search path.''' + """PathScripts/post is part of the posts search path.""" paths = PathPreferences.searchPathsPost() - self.assertEqual(len([p for p in paths if p.endswith('/PathScripts/post/')]), 1) + self.assertEqual(len([p for p in paths if p.endswith("/PathScripts/post/")]), 1) def test03(self): - '''Available post processors include linuxcnc, grbl and opensbp.''' + """Available post processors include linuxcnc, grbl and opensbp.""" posts = PathPreferences.allAvailablePostProcessors() - self.assertTrue('linuxcnc' in posts) - self.assertTrue('grbl' in posts) - self.assertTrue('opensbp' in posts) - + self.assertTrue("linuxcnc" in posts) + self.assertTrue("grbl" in posts) + self.assertTrue("opensbp" in posts) def test10(self): - '''Default paths for tools are resolved correctly''' + """Default paths for tools are resolved correctly""" - self.assertTrue(PathPreferences.pathDefaultToolsPath().endswith('/Path/Tools/')) - self.assertTrue(PathPreferences.pathDefaultToolsPath('Bit').endswith('/Path/Tools/Bit')) - self.assertTrue(PathPreferences.pathDefaultToolsPath('Library').endswith('/Path/Tools/Library')) - self.assertTrue(PathPreferences.pathDefaultToolsPath('Template').endswith('/Path/Tools/Template')) + self.assertTrue(PathPreferences.pathDefaultToolsPath().endswith("/Path/Tools/")) + self.assertTrue( + PathPreferences.pathDefaultToolsPath("Bit").endswith("/Path/Tools/Bit") + ) + self.assertTrue( + PathPreferences.pathDefaultToolsPath("Library").endswith( + "/Path/Tools/Library" + ) + ) + self.assertTrue( + PathPreferences.pathDefaultToolsPath("Template").endswith( + "/Path/Tools/Template" + ) + ) diff --git a/src/Mod/Path/PathTests/TestPathPropertyBag.py b/src/Mod/Path/PathTests/TestPathPropertyBag.py index 974ca3bd6c..71019dfb27 100644 --- a/src/Mod/Path/PathTests/TestPathPropertyBag.py +++ b/src/Mod/Path/PathTests/TestPathPropertyBag.py @@ -24,53 +24,58 @@ import FreeCAD import PathScripts.PathPropertyBag as PathPropertyBag import PathTests.PathTestUtils as PathTestUtils -class TestPathPropertyBag(PathTestUtils.PathTestBase): +class TestPathPropertyBag(PathTestUtils.PathTestBase): def setUp(self): - self.doc = FreeCAD.newDocument('test-property-bag') + self.doc = FreeCAD.newDocument("test-property-bag") def tearDown(self): FreeCAD.closeDocument(self.doc.Name) def test00(self): - '''basic PropertyBag creation and access test''' + """basic PropertyBag creation and access test""" bag = PathPropertyBag.Create() - self.assertTrue(hasattr(bag, 'Proxy')) + self.assertTrue(hasattr(bag, "Proxy")) self.assertEqual(bag.Proxy.getCustomProperties(), []) - self.assertEqual(bag.CustomPropertyGroups, []) + self.assertEqual(bag.CustomPropertyGroups, []) def test01(self): - '''adding properties to a PropertyBag is tracked properly''' + """adding properties to a PropertyBag is tracked properly""" bag = PathPropertyBag.Create() proxy = bag.Proxy - proxy.addCustomProperty('App::PropertyString', 'Title', 'Address', 'Some description') - self.assertTrue(hasattr(bag, 'Title')) - bag.Title = 'Madame' - self.assertEqual(bag.Title, 'Madame') - self.assertEqual(bag.Proxy.getCustomProperties(), ['Title']) - self.assertEqual(bag.CustomPropertyGroups, ['Address']) + proxy.addCustomProperty( + "App::PropertyString", "Title", "Address", "Some description" + ) + self.assertTrue(hasattr(bag, "Title")) + bag.Title = "Madame" + self.assertEqual(bag.Title, "Madame") + self.assertEqual(bag.Proxy.getCustomProperties(), ["Title"]) + self.assertEqual(bag.CustomPropertyGroups, ["Address"]) def test02(self): - '''refreshCustomPropertyGroups deletes empty groups''' + """refreshCustomPropertyGroups deletes empty groups""" bag = PathPropertyBag.Create() proxy = bag.Proxy - proxy.addCustomProperty('App::PropertyString', 'Title', 'Address', 'Some description') - bag.Title = 'Madame' - bag.removeProperty('Title') + proxy.addCustomProperty( + "App::PropertyString", "Title", "Address", "Some description" + ) + bag.Title = "Madame" + bag.removeProperty("Title") proxy.refreshCustomPropertyGroups() self.assertEqual(bag.Proxy.getCustomProperties(), []) - self.assertEqual(bag.CustomPropertyGroups, []) + self.assertEqual(bag.CustomPropertyGroups, []) def test03(self): - '''refreshCustomPropertyGroups does not delete non-empty groups''' + """refreshCustomPropertyGroups does not delete non-empty groups""" bag = PathPropertyBag.Create() proxy = bag.Proxy - proxy.addCustomProperty('App::PropertyString', 'Title', 'Address', 'Some description') - proxy.addCustomProperty('App::PropertyString', 'Gender', 'Attributes') - bag.Title = 'Madame' - bag.Gender = 'Female' - bag.removeProperty('Gender') + proxy.addCustomProperty( + "App::PropertyString", "Title", "Address", "Some description" + ) + proxy.addCustomProperty("App::PropertyString", "Gender", "Attributes") + bag.Title = "Madame" + bag.Gender = "Female" + bag.removeProperty("Gender") proxy.refreshCustomPropertyGroups() - self.assertEqual(bag.Proxy.getCustomProperties(), ['Title']) - self.assertEqual(bag.CustomPropertyGroups, ['Address']) - + self.assertEqual(bag.Proxy.getCustomProperties(), ["Title"]) + self.assertEqual(bag.CustomPropertyGroups, ["Address"]) diff --git a/src/Mod/Path/PathTests/TestPathSetupSheet.py b/src/Mod/Path/PathTests/TestPathSetupSheet.py index 9be3499326..9b74a12301 100644 --- a/src/Mod/Path/PathTests/TestPathSetupSheet.py +++ b/src/Mod/Path/PathTests/TestPathSetupSheet.py @@ -29,6 +29,7 @@ PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) from PathTests.PathTestUtils import PathTestBase + def refstring(string): if sys.version_info.major < 3: return string @@ -36,7 +37,6 @@ def refstring(string): class TestPathSetupSheet(PathTestBase): - def setUp(self): self.doc = FreeCAD.newDocument("TestPathSetupSheet") @@ -44,32 +44,42 @@ class TestPathSetupSheet(PathTestBase): FreeCAD.closeDocument(self.doc.Name) def test00(self): - '''Verify SetupSheet templateAttributes''' + """Verify SetupSheet templateAttributes""" ss = PathSetupSheet.Create().Proxy self.doc.recompute() attrs = ss.templateAttributes(True, True) - self.assertEqualLocale(attrs[PathSetupSheet.Template.HorizRapid], '0.00 mm/s') - self.assertEqualLocale(attrs[PathSetupSheet.Template.VertRapid], '0.00 mm/s') - self.assertEqualLocale(attrs[PathSetupSheet.Template.SafeHeightOffset], '3.00 mm') - self.assertEqual(attrs[PathSetupSheet.Template.SafeHeightExpression], 'OpStockZMax+SetupSheet.SafeHeightOffset') - self.assertEqualLocale(attrs[PathSetupSheet.Template.ClearanceHeightOffset], '5.00 mm') - self.assertEqual(attrs[PathSetupSheet.Template.ClearanceHeightExpression], 'OpStockZMax+SetupSheet.ClearanceHeightOffset') + self.assertEqualLocale(attrs[PathSetupSheet.Template.HorizRapid], "0.00 mm/s") + self.assertEqualLocale(attrs[PathSetupSheet.Template.VertRapid], "0.00 mm/s") + self.assertEqualLocale( + attrs[PathSetupSheet.Template.SafeHeightOffset], "3.00 mm" + ) + self.assertEqual( + attrs[PathSetupSheet.Template.SafeHeightExpression], + "OpStockZMax+SetupSheet.SafeHeightOffset", + ) + self.assertEqualLocale( + attrs[PathSetupSheet.Template.ClearanceHeightOffset], "5.00 mm" + ) + self.assertEqual( + attrs[PathSetupSheet.Template.ClearanceHeightExpression], + "OpStockZMax+SetupSheet.ClearanceHeightOffset", + ) def test01(self): - '''Verify SetupSheet template attributes roundtrip.''' + """Verify SetupSheet template attributes roundtrip.""" o1 = PathSetupSheet.Create() self.doc.recompute() - o1.VertRapid = '10 mm/s' - o1.HorizRapid = '22 mm/s' - o1.SafeHeightOffset = '18 mm' - o1.SafeHeightExpression = 'Hugo+Olga' - o1.ClearanceHeightOffset = '23 mm' - o1.ClearanceHeightExpression = 'Peter+Paul' - o1.StartDepthExpression = 'Alpha' - o1.FinalDepthExpression = 'Omega' - o1.StepDownExpression = '1' + o1.VertRapid = "10 mm/s" + o1.HorizRapid = "22 mm/s" + o1.SafeHeightOffset = "18 mm" + o1.SafeHeightExpression = "Hugo+Olga" + o1.ClearanceHeightOffset = "23 mm" + o1.ClearanceHeightExpression = "Peter+Paul" + o1.StartDepthExpression = "Alpha" + o1.FinalDepthExpression = "Omega" + o1.StepDownExpression = "1" o2 = PathSetupSheet.Create() self.doc.recompute() @@ -83,14 +93,16 @@ class TestPathSetupSheet(PathTestBase): self.assertEqual(o1.HorizRapid.UserString, o2.HorizRapid.UserString) self.assertEqual(o1.SafeHeightOffset.UserString, o2.SafeHeightOffset.UserString) self.assertEqual(o1.SafeHeightExpression, o2.SafeHeightExpression) - self.assertEqual(o1.ClearanceHeightOffset.UserString, o2.ClearanceHeightOffset.UserString) + self.assertEqual( + o1.ClearanceHeightOffset.UserString, o2.ClearanceHeightOffset.UserString + ) self.assertEqual(o1.ClearanceHeightExpression, o2.ClearanceHeightExpression) self.assertEqual(o1.StartDepthExpression, o2.StartDepthExpression) self.assertEqual(o1.FinalDepthExpression, o2.FinalDepthExpression) self.assertEqual(o1.StepDownExpression, o2.StepDownExpression) def test02(self): - '''Verify default value detection logic.''' + """Verify default value detection logic.""" obj = PathSetupSheet.Create() ss = obj.Proxy @@ -98,121 +110,195 @@ class TestPathSetupSheet(PathTestBase): self.assertTrue(ss.hasDefaultOperationHeights()) self.assertTrue(ss.hasDefaultOperationDepths()) - obj.VertRapid = '1 mm/s' + obj.VertRapid = "1 mm/s" self.assertFalse(ss.hasDefaultToolRapids()) - obj.VertRapid = '0 mm/s' + obj.VertRapid = "0 mm/s" self.assertTrue(ss.hasDefaultToolRapids()) - obj.HorizRapid = '1 mm/s' + obj.HorizRapid = "1 mm/s" self.assertFalse(ss.hasDefaultToolRapids()) - obj.HorizRapid = '0 mm/s' + obj.HorizRapid = "0 mm/s" self.assertTrue(ss.hasDefaultToolRapids()) - obj.SafeHeightOffset = '0 mm' + obj.SafeHeightOffset = "0 mm" self.assertFalse(ss.hasDefaultOperationHeights()) - obj.SafeHeightOffset = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultSafeHeightOffset) + obj.SafeHeightOffset = ss.decodeAttributeString( + PathSetupSheet.SetupSheet.DefaultSafeHeightOffset + ) self.assertTrue(ss.hasDefaultOperationHeights()) - obj.ClearanceHeightOffset = '0 mm' + obj.ClearanceHeightOffset = "0 mm" self.assertFalse(ss.hasDefaultOperationHeights()) - obj.ClearanceHeightOffset = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultClearanceHeightOffset) + obj.ClearanceHeightOffset = ss.decodeAttributeString( + PathSetupSheet.SetupSheet.DefaultClearanceHeightOffset + ) self.assertTrue(ss.hasDefaultOperationHeights()) - obj.SafeHeightExpression = '0 mm' + obj.SafeHeightExpression = "0 mm" self.assertFalse(ss.hasDefaultOperationHeights()) - obj.SafeHeightExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultSafeHeightExpression) + obj.SafeHeightExpression = ss.decodeAttributeString( + PathSetupSheet.SetupSheet.DefaultSafeHeightExpression + ) self.assertTrue(ss.hasDefaultOperationHeights()) - obj.ClearanceHeightExpression = '0 mm' + obj.ClearanceHeightExpression = "0 mm" self.assertFalse(ss.hasDefaultOperationHeights()) - obj.ClearanceHeightExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultClearanceHeightExpression) + obj.ClearanceHeightExpression = ss.decodeAttributeString( + PathSetupSheet.SetupSheet.DefaultClearanceHeightExpression + ) self.assertTrue(ss.hasDefaultOperationHeights()) - obj.StartDepthExpression = '' + obj.StartDepthExpression = "" self.assertFalse(ss.hasDefaultOperationDepths()) - obj.StartDepthExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultStartDepthExpression) + obj.StartDepthExpression = ss.decodeAttributeString( + PathSetupSheet.SetupSheet.DefaultStartDepthExpression + ) self.assertTrue(ss.hasDefaultOperationDepths()) - obj.FinalDepthExpression = '' + obj.FinalDepthExpression = "" self.assertFalse(ss.hasDefaultOperationDepths()) - obj.FinalDepthExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultFinalDepthExpression) + obj.FinalDepthExpression = ss.decodeAttributeString( + PathSetupSheet.SetupSheet.DefaultFinalDepthExpression + ) self.assertTrue(ss.hasDefaultOperationDepths()) - obj.StepDownExpression = '' + obj.StepDownExpression = "" self.assertFalse(ss.hasDefaultOperationDepths()) - obj.StepDownExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultStepDownExpression) + obj.StepDownExpression = ss.decodeAttributeString( + PathSetupSheet.SetupSheet.DefaultStepDownExpression + ) self.assertTrue(ss.hasDefaultOperationDepths()) def test10(self): - '''Verify template attributes encoding/decoding of floats.''' + """Verify template attributes encoding/decoding of floats.""" ss = PathSetupSheet.Create().Proxy - self.assertEqual(ss.expressionReference(), 'SetupSheet') + self.assertEqual(ss.expressionReference(), "SetupSheet") - self.assertEqual(str(ss.encodeTemplateAttributes({'00': 13.00})), "{'00': 13.0}") - self.assertEqual(str(ss.decodeTemplateAttributes({'00': 13.00})), "{'00': 13.0}") + self.assertEqual( + str(ss.encodeTemplateAttributes({"00": 13.00})), "{'00': 13.0}" + ) + self.assertEqual( + str(ss.decodeTemplateAttributes({"00": 13.00})), "{'00': 13.0}" + ) def test11(self): - '''Verify template attributes encoding/decoding of strings.''' + """Verify template attributes encoding/decoding of strings.""" ss = PathSetupSheet.Create().Proxy - self.assertEqual(str(ss.encodeTemplateAttributes({'00': 'hugo'})), refstring("{'00': u'hugo'}")) - self.assertEqual(str(ss.encodeTemplateAttributes({'00': 'SetupSheet'})), refstring("{'00': u'${SetupSheet}'}")) - self.assertEqual(str(ss.encodeTemplateAttributes({'00': 'SetupSheet.y'})), refstring("{'00': u'${SetupSheet}.y'}")) - self.assertEqual(str(ss.encodeTemplateAttributes({'00': '${hugo}'})), refstring("{'00': u'${hugo}'}")) + self.assertEqual( + str(ss.encodeTemplateAttributes({"00": "hugo"})), + refstring("{'00': u'hugo'}"), + ) + self.assertEqual( + str(ss.encodeTemplateAttributes({"00": "SetupSheet"})), + refstring("{'00': u'${SetupSheet}'}"), + ) + self.assertEqual( + str(ss.encodeTemplateAttributes({"00": "SetupSheet.y"})), + refstring("{'00': u'${SetupSheet}.y'}"), + ) + self.assertEqual( + str(ss.encodeTemplateAttributes({"00": "${hugo}"})), + refstring("{'00': u'${hugo}'}"), + ) - self.assertEqual(str(ss.decodeTemplateAttributes({'00': 'hugo'})), refstring("{'00': u'hugo'}")) - self.assertEqual(str(ss.decodeTemplateAttributes({'00': '${SetupSheet}'})), refstring("{'00': u'SetupSheet'}")) - self.assertEqual(str(ss.decodeTemplateAttributes({'00': '${SetupSheet}.y'})), refstring("{'00': u'SetupSheet.y'}")) - self.assertEqual(str(ss.decodeTemplateAttributes({'00': '${SetupSheet}.y - ${SetupSheet}.z'})), refstring("{'00': u'SetupSheet.y - SetupSheet.z'}")) + self.assertEqual( + str(ss.decodeTemplateAttributes({"00": "hugo"})), + refstring("{'00': u'hugo'}"), + ) + self.assertEqual( + str(ss.decodeTemplateAttributes({"00": "${SetupSheet}"})), + refstring("{'00': u'SetupSheet'}"), + ) + self.assertEqual( + str(ss.decodeTemplateAttributes({"00": "${SetupSheet}.y"})), + refstring("{'00': u'SetupSheet.y'}"), + ) + self.assertEqual( + str( + ss.decodeTemplateAttributes({"00": "${SetupSheet}.y - ${SetupSheet}.z"}) + ), + refstring("{'00': u'SetupSheet.y - SetupSheet.z'}"), + ) def test12(self): - '''Verify template attributes encoding/decoding of dictionaries.''' + """Verify template attributes encoding/decoding of dictionaries.""" ss = PathSetupSheet.Create().Proxy - self.assertEqual(str(ss.encodeTemplateAttributes({'00': {'01': 'hugo'}})), refstring("{'00': {'01': u'hugo'}}")) - self.assertEqual(str(ss.encodeTemplateAttributes({'00': {'01': 'SetupSheet.y - SetupSheet.z'}})), refstring("{'00': {'01': u'${SetupSheet}.y - ${SetupSheet}.z'}}")) + self.assertEqual( + str(ss.encodeTemplateAttributes({"00": {"01": "hugo"}})), + refstring("{'00': {'01': u'hugo'}}"), + ) + self.assertEqual( + str( + ss.encodeTemplateAttributes( + {"00": {"01": "SetupSheet.y - SetupSheet.z"}} + ) + ), + refstring("{'00': {'01': u'${SetupSheet}.y - ${SetupSheet}.z'}}"), + ) - self.assertEqual(str(ss.decodeTemplateAttributes({'00': {'01': 'hugo'}})), refstring("{'00': {'01': u'hugo'}}")) - self.assertEqual(str(ss.decodeTemplateAttributes({'00': {'01': '${SetupSheet}.y - ${SetupSheet}.z'}})), refstring("{'00': {'01': u'SetupSheet.y - SetupSheet.z'}}")) + self.assertEqual( + str(ss.decodeTemplateAttributes({"00": {"01": "hugo"}})), + refstring("{'00': {'01': u'hugo'}}"), + ) + self.assertEqual( + str( + ss.decodeTemplateAttributes( + {"00": {"01": "${SetupSheet}.y - ${SetupSheet}.z"}} + ) + ), + refstring("{'00': {'01': u'SetupSheet.y - SetupSheet.z'}}"), + ) def test13(self): - '''Verify template attributes encoding/decoding of lists.''' + """Verify template attributes encoding/decoding of lists.""" ss = PathSetupSheet.Create().Proxy attrs = {} - attrs['00'] = 'x.SetupSheet' - attrs['01'] = [{'10': 'SetupSheet', '11': 'SetupSheet.y'}, {'20': 'SetupSheet'}] - attrs['02'] = [{'a': [{'b': 'SetupSheet'}, {'c': 'SetupSheet'}], 'b': [{'b': 'SetupSheet'}]}] + attrs["00"] = "x.SetupSheet" + attrs["01"] = [{"10": "SetupSheet", "11": "SetupSheet.y"}, {"20": "SetupSheet"}] + attrs["02"] = [ + { + "a": [{"b": "SetupSheet"}, {"c": "SetupSheet"}], + "b": [{"b": "SetupSheet"}], + } + ] encoded = ss.encodeTemplateAttributes(attrs) - self.assertEqual(encoded['00'], 'x.${SetupSheet}') - self.assertEqual(len(encoded['01']), 2) - self.assertEqual(encoded['01'][0]['10'], '${SetupSheet}') - self.assertEqual(encoded['01'][0]['11'], '${SetupSheet}.y') - self.assertEqual(str(encoded['01'][1]), refstring("{'20': u'${SetupSheet}'}")) - self.assertEqual(len(encoded['02']), 1) - self.assertEqual(len(encoded['02'][0]['a']), 2) - self.assertEqual(str(encoded['02'][0]['a'][0]), refstring("{'b': u'${SetupSheet}'}")) - self.assertEqual(str(encoded['02'][0]['a'][1]), refstring("{'c': u'${SetupSheet}'}")) - self.assertEqual(len(encoded['02'][0]['b']), 1) - self.assertEqual(str(encoded['02'][0]['b'][0]), refstring("{'b': u'${SetupSheet}'}")) + self.assertEqual(encoded["00"], "x.${SetupSheet}") + self.assertEqual(len(encoded["01"]), 2) + self.assertEqual(encoded["01"][0]["10"], "${SetupSheet}") + self.assertEqual(encoded["01"][0]["11"], "${SetupSheet}.y") + self.assertEqual(str(encoded["01"][1]), refstring("{'20': u'${SetupSheet}'}")) + self.assertEqual(len(encoded["02"]), 1) + self.assertEqual(len(encoded["02"][0]["a"]), 2) + self.assertEqual( + str(encoded["02"][0]["a"][0]), refstring("{'b': u'${SetupSheet}'}") + ) + self.assertEqual( + str(encoded["02"][0]["a"][1]), refstring("{'c': u'${SetupSheet}'}") + ) + self.assertEqual(len(encoded["02"][0]["b"]), 1) + self.assertEqual( + str(encoded["02"][0]["b"][0]), refstring("{'b': u'${SetupSheet}'}") + ) decoded = ss.decodeTemplateAttributes(encoded) self.assertEqual(len(decoded), len(attrs)) - self.assertEqual(decoded['00'], attrs['00']) - self.assertEqual(len(decoded['01']), len(attrs['01'])) - self.assertEqual(decoded['01'][0]['10'], attrs['01'][0]['10']) - self.assertEqual(decoded['01'][0]['11'], attrs['01'][0]['11']) - self.assertEqual(decoded['01'][1]['20'], attrs['01'][1]['20']) - self.assertEqual(len(decoded['02']), len(attrs['02'])) - self.assertEqual(len(decoded['02'][0]['a']), len(attrs['02'][0]['a'])) - self.assertEqual(decoded['02'][0]['a'][0]['b'], attrs['02'][0]['a'][0]['b']) - self.assertEqual(decoded['02'][0]['a'][1]['c'], attrs['02'][0]['a'][1]['c']) - self.assertEqual(len(decoded['02'][0]['b']), len(attrs['02'][0]['b'])) - self.assertEqual(decoded['02'][0]['b'][0]['b'], attrs['02'][0]['b'][0]['b']) + self.assertEqual(decoded["00"], attrs["00"]) + self.assertEqual(len(decoded["01"]), len(attrs["01"])) + self.assertEqual(decoded["01"][0]["10"], attrs["01"][0]["10"]) + self.assertEqual(decoded["01"][0]["11"], attrs["01"][0]["11"]) + self.assertEqual(decoded["01"][1]["20"], attrs["01"][1]["20"]) + self.assertEqual(len(decoded["02"]), len(attrs["02"])) + self.assertEqual(len(decoded["02"][0]["a"]), len(attrs["02"][0]["a"])) + self.assertEqual(decoded["02"][0]["a"][0]["b"], attrs["02"][0]["a"][0]["b"]) + self.assertEqual(decoded["02"][0]["a"][1]["c"], attrs["02"][0]["a"][1]["c"]) + self.assertEqual(len(decoded["02"][0]["b"]), len(attrs["02"][0]["b"])) + self.assertEqual(decoded["02"][0]["b"][0]["b"], attrs["02"][0]["b"][0]["b"]) # just to be safe ... s2 = PathSetupSheet.Create().Proxy self.doc.recompute() s2.setFromTemplate(ss.templateAttributes()) - self.assertEqual(s2.expressionReference(), 'SetupSheet001') + self.assertEqual(s2.expressionReference(), "SetupSheet001") dec = s2.decodeTemplateAttributes(encoded) # pick one - self.assertEqual(dec['01'][0]['11'], 'SetupSheet001.y') - + self.assertEqual(dec["01"][0]["11"], "SetupSheet001.y") diff --git a/src/Mod/Path/PathTests/TestPathStock.py b/src/Mod/Path/PathTests/TestPathStock.py index af2471d611..e7293e9092 100644 --- a/src/Mod/Path/PathTests/TestPathStock.py +++ b/src/Mod/Path/PathTests/TestPathStock.py @@ -25,23 +25,24 @@ import PathScripts.PathStock as PathStock from PathTests.PathTestUtils import PathTestBase + class FakeJobProxy: def baseObject(self, obj): return obj.Base + R = 223.606798 / 2 class TestPathStock(PathTestBase): - def setUp(self): self.doc = FreeCAD.newDocument("TestPathStock") - self.base = self.doc.addObject('Part::Box', 'Box') + self.base = self.doc.addObject("Part::Box", "Box") self.base.Length = 100 - self.base.Width = 200 + self.base.Width = 200 self.base.Height = 300 - self.job = self.doc.addObject('App::FeaturePython', 'Job') - self.job.addProperty('App::PropertyLink', 'Model') + self.job = self.doc.addObject("App::FeaturePython", "Job") + self.job.addProperty("App::PropertyLink", "Model") model = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "Model") model.addObject(self.base) self.job.Model = model @@ -51,12 +52,12 @@ class TestPathStock(PathTestBase): FreeCAD.closeDocument("TestPathStock") def test00(self): - '''Test CreateBox''' + """Test CreateBox""" stock = PathStock.CreateBox(self.job) - self.assertTrue(hasattr(stock, 'Length')) - self.assertTrue(hasattr(stock, 'Width')) - self.assertTrue(hasattr(stock, 'Height')) + self.assertTrue(hasattr(stock, "Length")) + self.assertTrue(hasattr(stock, "Width")) + self.assertTrue(hasattr(stock, "Height")) self.assertEqual(100, stock.Length) self.assertEqual(200, stock.Width) self.assertEqual(300, stock.Height) @@ -67,7 +68,9 @@ class TestPathStock(PathTestBase): self.assertEqual(13, stock.Width) self.assertEqual(77, stock.Height) - placement = FreeCAD.Placement(FreeCAD.Vector(-3, 88, 4), FreeCAD.Vector(0, 0, 1), 180) + placement = FreeCAD.Placement( + FreeCAD.Vector(-3, 88, 4), FreeCAD.Vector(0, 0, 1), 180 + ) stock = PathStock.CreateBox(self.job, extent, placement) self.assertEqual(17, stock.Length) self.assertEqual(13, stock.Width) @@ -75,11 +78,11 @@ class TestPathStock(PathTestBase): self.assertPlacement(placement, stock.Placement) def test01(self): - '''Test CreateCylinder''' + """Test CreateCylinder""" stock = PathStock.CreateCylinder(self.job) - self.assertTrue(hasattr(stock, 'Radius')) - self.assertTrue(hasattr(stock, 'Height')) + self.assertTrue(hasattr(stock, "Radius")) + self.assertTrue(hasattr(stock, "Height")) self.assertRoughly(R, stock.Radius.Value) self.assertEqual(300, stock.Height) @@ -87,27 +90,32 @@ class TestPathStock(PathTestBase): self.assertEqual(37, stock.Radius) self.assertEqual(24, stock.Height) - placement = FreeCAD.Placement(FreeCAD.Vector(3, 8, -4), FreeCAD.Vector(0, 0, 1), -90) + placement = FreeCAD.Placement( + FreeCAD.Vector(3, 8, -4), FreeCAD.Vector(0, 0, 1), -90 + ) stock = PathStock.CreateCylinder(self.job, 1, 88, placement) self.assertEqual(1, stock.Radius) self.assertEqual(88, stock.Height) self.assertPlacement(placement, stock.Placement) - def test10(self): - '''Verify FromTemplate box creation.''' + """Verify FromTemplate box creation.""" extent = FreeCAD.Vector(17, 13, 77) - placement = FreeCAD.Placement(FreeCAD.Vector(3, 8, -4), FreeCAD.Vector(0, 0, 1), -90) + placement = FreeCAD.Placement( + FreeCAD.Vector(3, 8, -4), FreeCAD.Vector(0, 0, 1), -90 + ) orig = PathStock.CreateBox(self.job, extent, placement) # collect full template template = PathStock.TemplateAttributes(orig) stock = PathStock.CreateFromTemplate(self.job, template) - self.assertEqual(PathStock.StockType.CreateBox, PathStock.StockType.FromStock(stock)) + self.assertEqual( + PathStock.StockType.CreateBox, PathStock.StockType.FromStock(stock) + ) self.assertEqual(orig.Length, stock.Length) - self.assertEqual(orig.Width, stock.Width) + self.assertEqual(orig.Width, stock.Width) self.assertEqual(orig.Height, stock.Height) self.assertPlacement(orig.Placement, stock.Placement) @@ -115,9 +123,11 @@ class TestPathStock(PathTestBase): template = PathStock.TemplateAttributes(orig, False, True) stock = PathStock.CreateFromTemplate(self.job, template) - self.assertEqual(PathStock.StockType.CreateBox, PathStock.StockType.FromStock(stock)) + self.assertEqual( + PathStock.StockType.CreateBox, PathStock.StockType.FromStock(stock) + ) self.assertEqual(100, stock.Length) - self.assertEqual(200, stock.Width) + self.assertEqual(200, stock.Width) self.assertEqual(300, stock.Height) self.assertPlacement(orig.Placement, stock.Placement) @@ -125,24 +135,30 @@ class TestPathStock(PathTestBase): template = PathStock.TemplateAttributes(orig, True, False) stock = PathStock.CreateFromTemplate(self.job, template) - self.assertEqual(PathStock.StockType.CreateBox, PathStock.StockType.FromStock(stock)) + self.assertEqual( + PathStock.StockType.CreateBox, PathStock.StockType.FromStock(stock) + ) self.assertEqual(orig.Length, stock.Length) - self.assertEqual(orig.Width, stock.Width) + self.assertEqual(orig.Width, stock.Width) self.assertEqual(orig.Height, stock.Height) self.assertPlacement(FreeCAD.Placement(), stock.Placement) def test11(self): - '''Verify FromTemplate cylinder creation.''' + """Verify FromTemplate cylinder creation.""" radius = 7 height = 12 - placement = FreeCAD.Placement(FreeCAD.Vector(99, 88, 77), FreeCAD.Vector(1, 1, 1), 123) + placement = FreeCAD.Placement( + FreeCAD.Vector(99, 88, 77), FreeCAD.Vector(1, 1, 1), 123 + ) orig = PathStock.CreateCylinder(self.job, radius, height, placement) # full template template = PathStock.TemplateAttributes(orig) stock = PathStock.CreateFromTemplate(self.job, template) - self.assertEqual(PathStock.StockType.CreateCylinder, PathStock.StockType.FromStock(stock)) + self.assertEqual( + PathStock.StockType.CreateCylinder, PathStock.StockType.FromStock(stock) + ) self.assertEqual(orig.Radius, stock.Radius) self.assertEqual(orig.Height, stock.Height) self.assertPlacement(orig.Placement, stock.Placement) @@ -151,7 +167,9 @@ class TestPathStock(PathTestBase): template = PathStock.TemplateAttributes(orig, False, True) stock = PathStock.CreateFromTemplate(self.job, template) - self.assertEqual(PathStock.StockType.CreateCylinder, PathStock.StockType.FromStock(stock)) + self.assertEqual( + PathStock.StockType.CreateCylinder, PathStock.StockType.FromStock(stock) + ) self.assertRoughly(R, stock.Radius.Value) self.assertEqual(300, stock.Height) self.assertPlacement(orig.Placement, stock.Placement) @@ -160,7 +178,9 @@ class TestPathStock(PathTestBase): template = PathStock.TemplateAttributes(orig, True, False) stock = PathStock.CreateFromTemplate(None, template) - self.assertEqual(PathStock.StockType.CreateCylinder, PathStock.StockType.FromStock(stock)) + self.assertEqual( + PathStock.StockType.CreateCylinder, PathStock.StockType.FromStock(stock) + ) self.assertEqual(orig.Radius, stock.Radius) self.assertEqual(orig.Height, stock.Height) self.assertPlacement(FreeCAD.Placement(), stock.Placement) @@ -169,26 +189,33 @@ class TestPathStock(PathTestBase): template = PathStock.TemplateAttributes(orig, True, False) stock = PathStock.CreateFromTemplate(self.job, template) - self.assertEqual(PathStock.StockType.CreateCylinder, PathStock.StockType.FromStock(stock)) + self.assertEqual( + PathStock.StockType.CreateCylinder, PathStock.StockType.FromStock(stock) + ) self.assertEqual(orig.Radius, stock.Radius) self.assertEqual(orig.Height, stock.Height) - self.assertPlacement(FreeCAD.Placement(FreeCAD.Vector(50, 100, 0), FreeCAD.Rotation()), stock.Placement) + self.assertPlacement( + FreeCAD.Placement(FreeCAD.Vector(50, 100, 0), FreeCAD.Rotation()), + stock.Placement, + ) def test12(self): - '''Verify FromTemplate from Base creation.''' - neg = FreeCAD.Vector(1,2,3) - pos = FreeCAD.Vector(9,8,7) + """Verify FromTemplate from Base creation.""" + neg = FreeCAD.Vector(1, 2, 3) + pos = FreeCAD.Vector(9, 8, 7) orig = PathStock.CreateFromBase(self.job, neg, pos) # Make sure we have a different base object for the creation self.base.Length = 11 - self.base.Width = 12 + self.base.Width = 12 self.base.Height = 13 # full template template = PathStock.TemplateAttributes(orig) stock = PathStock.CreateFromTemplate(self.job, template) - self.assertEqual(PathStock.StockType.FromBase, PathStock.StockType.FromStock(stock)) + self.assertEqual( + PathStock.StockType.FromBase, PathStock.StockType.FromStock(stock) + ) self.assertEqual(orig.ExtXneg, stock.ExtXneg) self.assertEqual(orig.ExtXpos, stock.ExtXpos) self.assertEqual(orig.ExtYneg, stock.ExtYneg) @@ -200,4 +227,3 @@ class TestPathStock(PathTestBase): self.assertEqual(neg.x + pos.x + 11, bb.XLength) self.assertEqual(neg.y + pos.y + 12, bb.YLength) self.assertEqual(neg.z + pos.z + 13, bb.ZLength) - diff --git a/src/Mod/Path/PathTests/TestPathThreadMilling.py b/src/Mod/Path/PathTests/TestPathThreadMilling.py index aebca937cf..051c594f57 100644 --- a/src/Mod/Path/PathTests/TestPathThreadMilling.py +++ b/src/Mod/Path/PathTests/TestPathThreadMilling.py @@ -26,34 +26,40 @@ import math from PathTests.PathTestUtils import PathTestBase + def radii(major, minor, toolDia, toolCrest): - '''test radii function for simple testing''' + """test radii function for simple testing""" return (minor, major) + class TestPathThreadMilling(PathTestBase): - '''Test thread milling basics.''' + """Test thread milling basics.""" def assertRadii(self, have, want): self.assertRoughly(have[0], want[0]) self.assertRoughly(have[1], want[1]) def assertList(self, have, want): - self.assertEqual(len(have), len(want)); + self.assertEqual(len(have), len(want)) for i in range(len(have)): self.assertRoughly(have[i], want[i]) def test00(self): - '''Verify internal radii.''' + """Verify internal radii.""" self.assertRadii(PathThreadMilling.radiiInternal(20, 18, 2, 0), (8, 9.2)) self.assertRadii(PathThreadMilling.radiiInternal(20, 19, 2, 0), (8.5, 9.1)) def test01(self): - '''Verify internal radii with tool crest.''' + """Verify internal radii with tool crest.""" self.assertRadii(PathThreadMilling.radiiInternal(20, 18, 2, 0.1), (8, 9.113397)) def test10(self): - '''Verify thread passes.''' + """Verify thread passes.""" self.assertList(PathThreadMilling.threadPasses(1, radii, 10, 9, 0, 0), [10]) - self.assertList(PathThreadMilling.threadPasses(2, radii, 10, 9, 0, 0), [9.5, 10]) - self.assertList(PathThreadMilling.threadPasses(5, radii, 10, 9, 0, 0), [9.2, 9.4, 9.6, 9.8, 10]) - + self.assertList( + PathThreadMilling.threadPasses(2, radii, 10, 9, 0, 0), [9.5, 10] + ) + self.assertList( + PathThreadMilling.threadPasses(5, radii, 10, 9, 0, 0), + [9.2, 9.4, 9.6, 9.8, 10], + ) diff --git a/src/Mod/Path/PathTests/TestPathTool.py b/src/Mod/Path/PathTests/TestPathTool.py index a457118e36..130f16e473 100644 --- a/src/Mod/Path/PathTests/TestPathTool.py +++ b/src/Mod/Path/PathTests/TestPathTool.py @@ -24,20 +24,20 @@ import Path from PathTests.PathTestUtils import PathTestBase + class TestPathTool(PathTestBase): - def test00(self): - '''Verify templateAttrs''' + """Verify templateAttrs""" - name = 'tool 1' - mat = 'Carbide' - typ = 'EndMill' - dia = 1.7 - flat = 7.2 + name = "tool 1" + mat = "Carbide" + typ = "EndMill" + dia = 1.7 + flat = 7.2 offset = 3.2 corner = 4 height = 45.3 - angle = 118 + angle = 118 tool = Path.Tool() tool.Name = name @@ -51,20 +51,19 @@ class TestPathTool(PathTestBase): tool.CuttingEdgeHeight = height attrs = tool.templateAttrs() - self.assertEqual(attrs['name'], name) - self.assertEqual(attrs['diameter'], dia) - self.assertEqual(attrs['material'], mat) - self.assertEqual(attrs['tooltype'], typ) - self.assertEqual(attrs['lengthOffset'], offset) - self.assertEqual(attrs['flatRadius'], flat) - self.assertEqual(attrs['cornerRadius'], corner) - self.assertEqual(attrs['cuttingEdgeAngle'], angle) - self.assertEqual(attrs['cuttingEdgeHeight'], height) + self.assertEqual(attrs["name"], name) + self.assertEqual(attrs["diameter"], dia) + self.assertEqual(attrs["material"], mat) + self.assertEqual(attrs["tooltype"], typ) + self.assertEqual(attrs["lengthOffset"], offset) + self.assertEqual(attrs["flatRadius"], flat) + self.assertEqual(attrs["cornerRadius"], corner) + self.assertEqual(attrs["cuttingEdgeAngle"], angle) + self.assertEqual(attrs["cuttingEdgeHeight"], height) return tool - def test01(self): - '''Verify template roundtrip''' + """Verify template roundtrip""" t0 = self.test00() t1 = Path.Tool() @@ -81,7 +80,7 @@ class TestPathTool(PathTestBase): self.assertEqual(t0.CuttingEdgeHeight, t1.CuttingEdgeHeight) def test02(self): - '''Verify template dictionary construction''' + """Verify template dictionary construction""" t0 = self.test00() t1 = Path.Tool(t0.templateAttrs()) @@ -95,4 +94,3 @@ class TestPathTool(PathTestBase): self.assertEqual(t0.CornerRadius, t1.CornerRadius) self.assertEqual(t0.CuttingEdgeAngle, t1.CuttingEdgeAngle) self.assertEqual(t0.CuttingEdgeHeight, t1.CuttingEdgeHeight) - diff --git a/src/Mod/Path/PathTests/TestPathToolBit.py b/src/Mod/Path/PathTests/TestPathToolBit.py index 7a684b3697..71252c2a11 100644 --- a/src/Mod/Path/PathTests/TestPathToolBit.py +++ b/src/Mod/Path/PathTests/TestPathToolBit.py @@ -25,35 +25,41 @@ import PathTests.PathTestUtils as PathTestUtils import glob import os -TestToolDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'Tools') -TestInvalidDir = os.path.join(TestToolDir, 'some', 'silly', 'path', 'that', 'should', 'not', 'exist') +TestToolDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "Tools") +TestInvalidDir = os.path.join( + TestToolDir, "some", "silly", "path", "that", "should", "not", "exist" +) -TestToolBitName = 'test-path-tool-bit-bit-00.fctb' -TestToolShapeName = 'test-path-tool-bit-shape-00.fcstd' -TestToolLibraryName = 'test-path-tool-bit-library-00.fctl' +TestToolBitName = "test-path-tool-bit-bit-00.fctb" +TestToolShapeName = "test-path-tool-bit-shape-00.fcstd" +TestToolLibraryName = "test-path-tool-bit-library-00.fctl" -def testToolShape(path = TestToolDir, name = TestToolShapeName): - return os.path.join(path, 'Shape', name) -def testToolBit(path = TestToolDir, name = TestToolBitName): - return os.path.join(path, 'Bit', name) +def testToolShape(path=TestToolDir, name=TestToolShapeName): + return os.path.join(path, "Shape", name) + + +def testToolBit(path=TestToolDir, name=TestToolBitName): + return os.path.join(path, "Bit", name) + + +def testToolLibrary(path=TestToolDir, name=TestToolLibraryName): + return os.path.join(path, "Library", name) -def testToolLibrary(path = TestToolDir, name = TestToolLibraryName): - return os.path.join(path, 'Library', name) def printTree(path, indent): print("{} {}".format(indent, os.path.basename(path))) if os.path.isdir(path): - if os.path.basename(path).startswith('__'): + if os.path.basename(path).startswith("__"): print("{} ...".format(indent)) else: - for foo in sorted(glob.glob(os.path.join(path, '*'))): + for foo in sorted(glob.glob(os.path.join(path, "*"))): printTree(foo, "{} ".format(indent)) -class TestPathToolBit(PathTestUtils.PathTestBase): +class TestPathToolBit(PathTestUtils.PathTestBase): def test(self): - '''Log test setup directory structure''' + """Log test setup directory structure""" # Enable this test if there are errors showing up in the build system with the # paths that work OK locally. It'll print out the directory tree, and if it # doesn't look right you know where to look for it @@ -64,101 +70,84 @@ class TestPathToolBit(PathTestUtils.PathTestBase): printTree(os.path.dirname(os.path.realpath(__file__)), " :") def test00(self): - '''Find a tool shape from file name''' - path = PathToolBit.findToolShape('endmill.fcstd') + """Find a tool shape from file name""" + path = PathToolBit.findToolShape("endmill.fcstd") self.assertIsNot(path, None) - self.assertNotEqual(path, 'endmill.fcstd') - + self.assertNotEqual(path, "endmill.fcstd") def test01(self): - '''Not find a relative path shape if not stored in default location''' + """Not find a relative path shape if not stored in default location""" path = PathToolBit.findToolShape(TestToolShapeName) self.assertIsNone(path) - def test02(self): - '''Find a relative path shape if it's local to a bit path''' + """Find a relative path shape if it's local to a bit path""" path = PathToolBit.findToolShape(TestToolShapeName, testToolBit()) self.assertIsNot(path, None) self.assertEqual(path, testToolShape()) - def test03(self): - '''Not find a tool shape from an invalid absolute path.''' + """Not find a tool shape from an invalid absolute path.""" path = PathToolBit.findToolShape(testToolShape(TestInvalidDir)) self.assertIsNone(path) - def test04(self): - '''Find a tool shape from a valid absolute path.''' + """Find a tool shape from a valid absolute path.""" path = PathToolBit.findToolShape(testToolShape()) self.assertIsNot(path, None) self.assertEqual(path, testToolShape()) - def test10(self): - '''Find a tool bit from file name''' - path = PathToolBit.findToolBit('5mm_Endmill.fctb') + """Find a tool bit from file name""" + path = PathToolBit.findToolBit("5mm_Endmill.fctb") self.assertIsNot(path, None) - self.assertNotEqual(path, '5mm_Endmill.fctb') - + self.assertNotEqual(path, "5mm_Endmill.fctb") def test11(self): - '''Not find a relative path bit if not stored in default location''' + """Not find a relative path bit if not stored in default location""" path = PathToolBit.findToolBit(TestToolBitName) self.assertIsNone(path) - def test12(self): - '''Find a relative path bit if it's local to a library path''' + """Find a relative path bit if it's local to a library path""" path = PathToolBit.findToolBit(TestToolBitName, testToolLibrary()) self.assertIsNot(path, None) self.assertEqual(path, testToolBit()) - def test13(self): - '''Not find a tool bit from an invalid absolute path.''' + """Not find a tool bit from an invalid absolute path.""" path = PathToolBit.findToolBit(testToolBit(TestInvalidDir)) self.assertIsNone(path) - def test14(self): - '''Find a tool bit from a valid absolute path.''' + """Find a tool bit from a valid absolute path.""" path = PathToolBit.findToolBit(testToolBit()) self.assertIsNot(path, None) self.assertEqual(path, testToolBit()) - - def test20(self): - '''Find a tool library from file name''' - path = PathToolBit.findToolLibrary('Default.fctl') + """Find a tool library from file name""" + path = PathToolBit.findToolLibrary("Default.fctl") self.assertIsNot(path, None) - self.assertNotEqual(path, 'Default.fctl') - + self.assertNotEqual(path, "Default.fctl") def test21(self): - '''Not find a relative path library if not stored in default location''' + """Not find a relative path library if not stored in default location""" path = PathToolBit.findToolLibrary(TestToolLibraryName) self.assertIsNone(path) - def test22(self): - '''[skipped] Find a relative path library if it's local to ''' + """[skipped] Find a relative path library if it's local to """ # this is not a valid test for libraries because t self.assertTrue(True) - def test23(self): - '''Not find a tool library from an invalid absolute path.''' + """Not find a tool library from an invalid absolute path.""" path = PathToolBit.findToolLibrary(testToolLibrary(TestInvalidDir)) self.assertIsNone(path) - def test24(self): - '''Find a tool library from a valid absolute path.''' + """Find a tool library from a valid absolute path.""" path = PathToolBit.findToolBit(testToolBit()) self.assertIsNot(path, None) self.assertEqual(path, testToolBit()) - - diff --git a/src/Mod/Path/PathTests/TestPathToolController.py b/src/Mod/Path/PathTests/TestPathToolController.py index 67d2507ec6..afa2291e10 100644 --- a/src/Mod/Path/PathTests/TestPathToolController.py +++ b/src/Mod/Path/PathTests/TestPathToolController.py @@ -28,55 +28,60 @@ import PathScripts.PathToolController as PathToolController from PathTests.PathTestUtils import PathTestBase -class TestPathToolController(PathTestBase): +class TestPathToolController(PathTestBase): def setUp(self): self.doc = FreeCAD.newDocument("TestPathToolController") def tearDown(self): FreeCAD.closeDocument(self.doc.Name) - def createTool(self, name='t1', diameter=1.75): + def createTool(self, name="t1", diameter=1.75): if PathPreferences.toolsUseLegacyTools(): return Path.Tool(name=name, diameter=diameter) - attrs = {'shape': None, 'name': name, 'parameter': {'Diameter': diameter}, 'attribute': []} + attrs = { + "shape": None, + "name": name, + "parameter": {"Diameter": diameter}, + "attribute": [], + } return PathToolBit.Factory.CreateFromAttrs(attrs, name) def test00(self): - '''Verify ToolController templateAttrs''' - t = self.createTool('T1') - tc = PathToolController.Create('TC0', t) + """Verify ToolController templateAttrs""" + t = self.createTool("T1") + tc = PathToolController.Create("TC0", t) - tc.Label = 'ToolController' + tc.Label = "ToolController" tc.ToolNumber = 7 - tc.VertFeed = '3 in/s' + tc.VertFeed = "3 in/s" tc.VertFeed = round(tc.VertFeed, 1) - tc.HorizFeed = '10 mm/s' + tc.HorizFeed = "10 mm/s" tc.VertRapid = 40 tc.HorizRapid = 28 - tc.SpindleDir = 'Reverse' + tc.SpindleDir = "Reverse" tc.SpindleSpeed = 12000 attrs = tc.Proxy.templateAttrs(tc) - self.assertEqual(attrs['name'], 'TC0') - self.assertEqual(attrs['label'], 'ToolController') - self.assertEqual(attrs['nr'], 7) - self.assertEqual(attrs['vfeed'], '76.2 mm/s') - self.assertEqual(attrs['hfeed'], '10.0 mm/s') - self.assertEqual(attrs['vrapid'], '40.0 mm/s') - self.assertEqual(attrs['hrapid'], '28.0 mm/s') - self.assertEqual(attrs['dir'], 'Reverse') - self.assertEqual(attrs['speed'], 12000) + self.assertEqual(attrs["name"], "TC0") + self.assertEqual(attrs["label"], "ToolController") + self.assertEqual(attrs["nr"], 7) + self.assertEqual(attrs["vfeed"], "76.2 mm/s") + self.assertEqual(attrs["hfeed"], "10.0 mm/s") + self.assertEqual(attrs["vrapid"], "40.0 mm/s") + self.assertEqual(attrs["hrapid"], "28.0 mm/s") + self.assertEqual(attrs["dir"], "Reverse") + self.assertEqual(attrs["speed"], 12000) if PathPreferences.toolsUseLegacyTools(): - self.assertEqual(attrs['tool'], t.templateAttrs()) + self.assertEqual(attrs["tool"], t.templateAttrs()) else: - self.assertEqual(attrs['tool'], t.Proxy.templateAttrs(t)) + self.assertEqual(attrs["tool"], t.Proxy.templateAttrs(t)) return tc def test01(self): - '''Verify ToolController template roundtrip.''' + """Verify ToolController template roundtrip.""" tc0 = self.test00() tc1 = PathToolController.FromTemplate(tc0.Proxy.templateAttrs(tc0)) @@ -92,6 +97,6 @@ class TestPathToolController(PathTestBase): self.assertRoughly(tc0.SpindleSpeed, tc1.SpindleSpeed) # These are not valid because the name & label get adjusted if there # is a conflict. No idea how this could work with the C implementation - #self.assertEqual(tc0.Tool.Name, tc1.Tool.Name) - #self.assertEqual(tc0.Tool.Label, tc1.Tool.Label) + # self.assertEqual(tc0.Tool.Name, tc1.Tool.Name) + # self.assertEqual(tc0.Tool.Label, tc1.Tool.Label) self.assertRoughly(tc0.Tool.Diameter, tc1.Tool.Diameter) diff --git a/src/Mod/Path/PathTests/TestPathTooltable.py b/src/Mod/Path/PathTests/TestPathTooltable.py index 1dcf8938e8..9752bc6263 100644 --- a/src/Mod/Path/PathTests/TestPathTooltable.py +++ b/src/Mod/Path/PathTests/TestPathTooltable.py @@ -24,14 +24,14 @@ import Path from PathTests.PathTestUtils import PathTestBase + class TestPathTooltable(PathTestBase): - def test00(self): - '''Verify templateAttrs''' + """Verify templateAttrs""" - t = Path.Tool(name='t', diameter=1.2) - u = Path.Tool(name='u', diameter=3.4) - v = Path.Tool(name='v', diameter=5.6) + t = Path.Tool(name="t", diameter=1.2) + u = Path.Tool(name="u", diameter=3.4) + v = Path.Tool(name="v", diameter=5.6) tt = Path.Tooltable() tt.setTool(3, t) @@ -45,26 +45,24 @@ class TestPathTooltable(PathTestBase): self.assertTrue(3 in attrs) self.assertTrue(4 in attrs) - self.assertEqual(attrs[1]['name'], 'u') - self.assertEqual(attrs[1]['diameter'], 3.4) - self.assertEqual(attrs[3]['name'], 't') - self.assertEqual(attrs[3]['diameter'], 1.2) - self.assertEqual(attrs[4]['name'], 'v') - self.assertEqual(attrs[4]['diameter'], 5.6) + self.assertEqual(attrs[1]["name"], "u") + self.assertEqual(attrs[1]["diameter"], 3.4) + self.assertEqual(attrs[3]["name"], "t") + self.assertEqual(attrs[3]["diameter"], 1.2) + self.assertEqual(attrs[4]["name"], "v") + self.assertEqual(attrs[4]["diameter"], 5.6) return tt def test01(self): - '''Verify setFromTemplate roundtrip.''' + """Verify setFromTemplate roundtrip.""" tt = self.test00() uu = Path.Tooltable() uu.setFromTemplate(tt.templateAttrs()) self.assertEqual(tt.Content, uu.Content) - def test02(self): - '''Verify template constructor.''' + """Verify template constructor.""" tt = self.test00() uu = Path.Tooltable(tt.templateAttrs()) self.assertEqual(tt.Content, uu.Content) - diff --git a/src/Mod/Path/PathTests/TestPathUtil.py b/src/Mod/Path/PathTests/TestPathUtil.py index 5ed2d04afb..04ae92ce58 100644 --- a/src/Mod/Path/PathTests/TestPathUtil.py +++ b/src/Mod/Path/PathTests/TestPathUtil.py @@ -26,8 +26,8 @@ import TestSketcherApp from PathTests.PathTestUtils import PathTestBase -class TestPathUtil(PathTestBase): +class TestPathUtil(PathTestBase): def setUp(self): self.doc = FreeCAD.newDocument("TestPathUtils") @@ -35,47 +35,50 @@ class TestPathUtil(PathTestBase): FreeCAD.closeDocument("TestPathUtils") def test00(self): - '''Check that isValidBaseObject detects solids.''' - box = self.doc.addObject('Part::Box', 'Box') - cylinder = self.doc.addObject('Part::Cylinder', 'Cylinder') + """Check that isValidBaseObject detects solids.""" + box = self.doc.addObject("Part::Box", "Box") + cylinder = self.doc.addObject("Part::Cylinder", "Cylinder") self.doc.recompute() self.assertTrue(PathUtil.isValidBaseObject(box)) self.assertTrue(PathUtil.isValidBaseObject(cylinder)) def test01(self): - '''Check that isValidBaseObject detects PDs.''' - body = self.doc.addObject('PartDesign::Body', 'Body') - box = self.doc.addObject('PartDesign::AdditiveBox', 'Box') + """Check that isValidBaseObject detects PDs.""" + body = self.doc.addObject("PartDesign::Body", "Body") + box = self.doc.addObject("PartDesign::AdditiveBox", "Box") body.addObject(box) self.doc.recompute() self.assertTrue(PathUtil.isValidBaseObject(body)) def test02(self): - '''Check that isValidBaseObject detects compounds.''' - box = self.doc.addObject('Part::Box', 'Box') + """Check that isValidBaseObject detects compounds.""" + box = self.doc.addObject("Part::Box", "Box") box.Length = 10 box.Width = 10 box.Height = 1 - box.Placement = FreeCAD.Placement(FreeCAD.Vector(-5,-5,0), FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0)) - cyl = self.doc.addObject('Part::Cylinder', 'Cylinder') + box.Placement = FreeCAD.Placement( + FreeCAD.Vector(-5, -5, 0), FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) + ) + cyl = self.doc.addObject("Part::Cylinder", "Cylinder") cyl.Radius = 1 cyl.Height = 10 - box.Placement = FreeCAD.Placement(FreeCAD.Vector(0,0,-5), FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0)) - cut = self.doc.addObject('Part::Cut', 'Cut') + box.Placement = FreeCAD.Placement( + FreeCAD.Vector(0, 0, -5), FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) + ) + cut = self.doc.addObject("Part::Cut", "Cut") cut.Base = box cut.Tool = cyl self.doc.recompute() self.assertTrue(PathUtil.isValidBaseObject(cut)) - def test03(self): - '''Check that isValidBaseObject ignores sketches.''' - body = self.doc.addObject('PartDesign::Body', 'Body') - sketch = self.doc.addObject('Sketcher::SketchObject', 'Sketch') + """Check that isValidBaseObject ignores sketches.""" + body = self.doc.addObject("PartDesign::Body", "Body") + sketch = self.doc.addObject("Sketcher::SketchObject", "Sketch") body.addObject(sketch) TestSketcherApp.CreateSlotPlateSet(sketch) self.doc.recompute() - pad = self.doc.addObject('PartDesign::Pad', 'Pad') + pad = self.doc.addObject("PartDesign::Pad", "Pad") body.addObject(pad) pad.Profile = sketch self.doc.recompute() @@ -90,19 +93,19 @@ class TestPathUtil(PathTestBase): self.assertFalse(PathUtil.isValidBaseObject(sketch)) def test04(self): - '''Check that Part is handled correctly.''' - part = self.doc.addObject('App::Part', 'Part') + """Check that Part is handled correctly.""" + part = self.doc.addObject("App::Part", "Part") # an empty part is not a valid base object self.assertFalse(PathUtil.isValidBaseObject(part)) # a non-empty part where none of the objects have a shape, is no good either - fp = self.doc.addObject('App::FeaturePython', 'Feature') + fp = self.doc.addObject("App::FeaturePython", "Feature") part.addObject(fp) self.assertFalse(PathUtil.isValidBaseObject(part)) # create a valid base object - box = self.doc.addObject("Part::Box","Box") + box = self.doc.addObject("Part::Box", "Box") self.doc.recompute() self.assertTrue(PathUtil.isValidBaseObject(box)) @@ -112,4 +115,3 @@ class TestPathUtil(PathTestBase): # however, the object itself is no longer valid self.assertFalse(PathUtil.isValidBaseObject(box)) - diff --git a/src/Mod/Path/PathTests/TestPathVcarve.py b/src/Mod/Path/PathTests/TestPathVcarve.py index f248d006cd..51bb80678e 100644 --- a/src/Mod/Path/PathTests/TestPathVcarve.py +++ b/src/Mod/Path/PathTests/TestPathVcarve.py @@ -28,85 +28,87 @@ import math from PathTests.PathTestUtils import PathTestBase + class VbitTool(object): - '''Faked out vcarve tool''' + """Faked out vcarve tool""" def __init__(self, dia, angle, tipDia): - self.Diameter = FreeCAD.Units.Quantity(dia, FreeCAD.Units.Length) + self.Diameter = FreeCAD.Units.Quantity(dia, FreeCAD.Units.Length) self.CuttingEdgeAngle = FreeCAD.Units.Quantity(angle, FreeCAD.Units.Angle) - self.TipDiameter = FreeCAD.Units.Quantity(tipDia, FreeCAD.Units.Length) + self.TipDiameter = FreeCAD.Units.Quantity(tipDia, FreeCAD.Units.Length) + Scale45 = 2.414214 Scale60 = math.sqrt(3) + class TestPathVcarve(PathTestBase): - '''Test Vcarve milling basics.''' + """Test Vcarve milling basics.""" def test00(self): - '''Verify 90 deg depth calculation''' + """Verify 90 deg depth calculation""" tool = VbitTool(10, 90, 0) geom = PathVcarve._Geometry.FromTool(tool, 0, -10) - self.assertRoughly(geom.start, 0) - self.assertRoughly(geom.stop, -5) - self.assertRoughly(geom.scale, 1) + self.assertRoughly(geom.start, 0) + self.assertRoughly(geom.stop, -5) + self.assertRoughly(geom.scale, 1) def test01(self): - '''Verify 90 deg depth limit''' + """Verify 90 deg depth limit""" tool = VbitTool(10, 90, 0) geom = PathVcarve._Geometry.FromTool(tool, 0, -3) - self.assertRoughly(geom.start, 0) - self.assertRoughly(geom.stop, -3) - self.assertRoughly(geom.scale, 1) + self.assertRoughly(geom.start, 0) + self.assertRoughly(geom.stop, -3) + self.assertRoughly(geom.scale, 1) def test02(self): - '''Verify 60 deg depth calculation''' + """Verify 60 deg depth calculation""" tool = VbitTool(10, 60, 0) geom = PathVcarve._Geometry.FromTool(tool, 0, -10) - self.assertRoughly(geom.start, 0) + self.assertRoughly(geom.start, 0) self.assertRoughly(geom.stop, -5 * Scale60) - self.assertRoughly(geom.scale, Scale60) + self.assertRoughly(geom.scale, Scale60) def test03(self): - '''Verify 60 deg depth limit''' + """Verify 60 deg depth limit""" tool = VbitTool(10, 60, 0) geom = PathVcarve._Geometry.FromTool(tool, 0, -3) - self.assertRoughly(geom.start, 0) - self.assertRoughly(geom.stop, -3) - self.assertRoughly(geom.scale, Scale60) + self.assertRoughly(geom.start, 0) + self.assertRoughly(geom.stop, -3) + self.assertRoughly(geom.scale, Scale60) def test10(self): - '''Verify 90 deg with tip dia depth calculation''' + """Verify 90 deg with tip dia depth calculation""" tool = VbitTool(10, 90, 2) geom = PathVcarve._Geometry.FromTool(tool, 0, -10) # in order for the width to be correct the height needs to be shifted - self.assertRoughly(geom.start, 1) - self.assertRoughly(geom.stop, -4) - self.assertRoughly(geom.scale, 1) + self.assertRoughly(geom.start, 1) + self.assertRoughly(geom.stop, -4) + self.assertRoughly(geom.scale, 1) def test11(self): - '''Verify 90 deg with tip dia depth limit calculation''' + """Verify 90 deg with tip dia depth limit calculation""" tool = VbitTool(10, 90, 2) geom = PathVcarve._Geometry.FromTool(tool, 0, -3) # in order for the width to be correct the height needs to be shifted - self.assertRoughly(geom.start, 1) - self.assertRoughly(geom.stop, -3) - self.assertRoughly(geom.scale, 1) + self.assertRoughly(geom.start, 1) + self.assertRoughly(geom.stop, -3) + self.assertRoughly(geom.scale, 1) def test12(self): - '''Verify 45 deg with tip dia depth calculation''' + """Verify 45 deg with tip dia depth calculation""" tool = VbitTool(10, 45, 2) geom = PathVcarve._Geometry.FromTool(tool, 0, -10) # in order for the width to be correct the height needs to be shifted - self.assertRoughly(geom.start, Scale45) - self.assertRoughly(geom.stop, -4 * Scale45) - self.assertRoughly(geom.scale, Scale45) + self.assertRoughly(geom.start, Scale45) + self.assertRoughly(geom.stop, -4 * Scale45) + self.assertRoughly(geom.scale, Scale45) def test13(self): - '''Verify 45 deg with tip dia depth limit calculation''' + """Verify 45 deg with tip dia depth limit calculation""" tool = VbitTool(10, 45, 2) geom = PathVcarve._Geometry.FromTool(tool, 0, -3) # in order for the width to be correct the height needs to be shifted - self.assertRoughly(geom.start, Scale45) - self.assertRoughly(geom.stop, -3) - self.assertRoughly(geom.scale, Scale45) - + self.assertRoughly(geom.start, Scale45) + self.assertRoughly(geom.stop, -3) + self.assertRoughly(geom.scale, Scale45) diff --git a/src/Mod/Path/PathTests/TestPathVoronoi.py b/src/Mod/Path/PathTests/TestPathVoronoi.py index 45713d806a..90125677ca 100644 --- a/src/Mod/Path/PathTests/TestPathVoronoi.py +++ b/src/Mod/Path/PathTests/TestPathVoronoi.py @@ -29,16 +29,30 @@ import sys vd = None + def initVD(): global vd if vd is None: - pts = [(0,0), (3.5,0), (3.5,1), (1,1), (1,2), (2.5,2), (2.5,3), (1,3), (1,4), (3.5, 4), (3.5,5), (0,5)] + pts = [ + (0, 0), + (3.5, 0), + (3.5, 1), + (1, 1), + (1, 2), + (2.5, 2), + (2.5, 3), + (1, 3), + (1, 4), + (3.5, 4), + (3.5, 5), + (0, 5), + ] ptv = [FreeCAD.Vector(p[0], p[1]) for p in pts] ptv.append(ptv[0]) vd = Path.Voronoi() for i in range(len(pts)): - vd.addSegment(ptv[i], ptv[i+1]) + vd.addSegment(ptv[i], ptv[i + 1]) vd.construct() @@ -54,46 +68,44 @@ def initVD(): class TestPathVoronoi(PathTestUtils.PathTestBase): - def setUp(self): initVD() def test00(self): - '''Check vertex comparison''' + """Check vertex comparison""" - self.assertTrue( vd.Vertices[0] == vd.Vertices[0]) - self.assertTrue( vd.Vertices[1] == vd.Vertices[1]) - self.assertTrue( vd.Vertices[0] != vd.Vertices[1]) - self.assertEqual( vd.Vertices[0], vd.Vertices[0]) - self.assertEqual( vd.Vertices[1], vd.Vertices[1]) + self.assertTrue(vd.Vertices[0] == vd.Vertices[0]) + self.assertTrue(vd.Vertices[1] == vd.Vertices[1]) + self.assertTrue(vd.Vertices[0] != vd.Vertices[1]) + self.assertEqual(vd.Vertices[0], vd.Vertices[0]) + self.assertEqual(vd.Vertices[1], vd.Vertices[1]) self.assertNotEqual(vd.Vertices[0], vd.Vertices[1]) self.assertNotEqual(vd.Vertices[1], vd.Vertices[0]) def test10(self): - '''Check edge comparison''' + """Check edge comparison""" - self.assertTrue( vd.Edges[0] == vd.Edges[0]) - self.assertTrue( vd.Edges[1] == vd.Edges[1]) - self.assertTrue( vd.Edges[0] != vd.Edges[1]) - self.assertEqual( vd.Edges[0], vd.Edges[0]) - self.assertEqual( vd.Edges[1], vd.Edges[1]) + self.assertTrue(vd.Edges[0] == vd.Edges[0]) + self.assertTrue(vd.Edges[1] == vd.Edges[1]) + self.assertTrue(vd.Edges[0] != vd.Edges[1]) + self.assertEqual(vd.Edges[0], vd.Edges[0]) + self.assertEqual(vd.Edges[1], vd.Edges[1]) self.assertNotEqual(vd.Edges[0], vd.Edges[1]) self.assertNotEqual(vd.Edges[1], vd.Edges[0]) - def test20(self): - '''Check cell comparison''' + """Check cell comparison""" - self.assertTrue( vd.Cells[0] == vd.Cells[0]) - self.assertTrue( vd.Cells[1] == vd.Cells[1]) - self.assertTrue( vd.Cells[0] != vd.Cells[1]) - self.assertEqual( vd.Cells[0], vd.Cells[0]) - self.assertEqual( vd.Cells[1], vd.Cells[1]) + self.assertTrue(vd.Cells[0] == vd.Cells[0]) + self.assertTrue(vd.Cells[1] == vd.Cells[1]) + self.assertTrue(vd.Cells[0] != vd.Cells[1]) + self.assertEqual(vd.Cells[0], vd.Cells[0]) + self.assertEqual(vd.Cells[1], vd.Cells[1]) self.assertNotEqual(vd.Cells[0], vd.Cells[1]) self.assertNotEqual(vd.Cells[1], vd.Cells[0]) def test50(self): - '''Check toShape for linear edges''' + """Check toShape for linear edges""" edges = [e for e in vd.Edges if e.Color == 0 and e.isLinear()] self.assertNotEqual(len(edges), 0) @@ -101,12 +113,16 @@ class TestPathVoronoi(PathTestUtils.PathTestBase): e = e0.toShape() self.assertTrue(type(e.Curve) == Part.LineSegment or type(e.Curve) == Part.Line) - self.assertFalse(PathGeom.pointsCoincide(e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter))) + self.assertFalse( + PathGeom.pointsCoincide( + e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter) + ) + ) self.assertRoughly(e.valueAt(e.FirstParameter).z, 0) - self.assertRoughly(e.valueAt(e.LastParameter).z, 0) + self.assertRoughly(e.valueAt(e.LastParameter).z, 0) def test51(self): - '''Check toShape for linear edges with set z''' + """Check toShape for linear edges with set z""" edges = [e for e in vd.Edges if e.Color == 0 and e.isLinear()] self.assertNotEqual(len(edges), 0) @@ -114,12 +130,16 @@ class TestPathVoronoi(PathTestUtils.PathTestBase): e = e0.toShape(13.7) self.assertTrue(type(e.Curve) == Part.LineSegment or type(e.Curve) == Part.Line) - self.assertFalse(PathGeom.pointsCoincide(e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter))) + self.assertFalse( + PathGeom.pointsCoincide( + e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter) + ) + ) self.assertRoughly(e.valueAt(e.FirstParameter).z, 13.7) - self.assertRoughly(e.valueAt(e.LastParameter).z, 13.7) + self.assertRoughly(e.valueAt(e.LastParameter).z, 13.7) def test52(self): - '''Check toShape for linear edges with varying z''' + """Check toShape for linear edges with varying z""" edges = [e for e in vd.Edges if e.Color == 0 and e.isLinear()] self.assertNotEqual(len(edges), 0) @@ -127,46 +147,67 @@ class TestPathVoronoi(PathTestUtils.PathTestBase): e = e0.toShape(2.37, 5.14) self.assertTrue(type(e.Curve) == Part.LineSegment or type(e.Curve) == Part.Line) - self.assertFalse(PathGeom.pointsCoincide(e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter))) + self.assertFalse( + PathGeom.pointsCoincide( + e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter) + ) + ) self.assertRoughly(e.valueAt(e.FirstParameter).z, 2.37) - self.assertRoughly(e.valueAt(e.LastParameter).z, 5.14) + self.assertRoughly(e.valueAt(e.LastParameter).z, 5.14) def test60(self): - '''Check toShape for curved edges''' + """Check toShape for curved edges""" edges = [e for e in vd.Edges if e.Color == 0 and e.isCurved()] self.assertNotEqual(len(edges), 0) e0 = edges[0] e = e0.toShape() - self.assertTrue(type(e.Curve) == Part.Parabola or type(e.Curve) == Part.BSplineCurve) - self.assertFalse(PathGeom.pointsCoincide(e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter))) + self.assertTrue( + type(e.Curve) == Part.Parabola or type(e.Curve) == Part.BSplineCurve + ) + self.assertFalse( + PathGeom.pointsCoincide( + e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter) + ) + ) self.assertRoughly(e.valueAt(e.FirstParameter).z, 0) - self.assertRoughly(e.valueAt(e.LastParameter).z, 0) + self.assertRoughly(e.valueAt(e.LastParameter).z, 0) def test61(self): - '''Check toShape for curved edges with set z''' + """Check toShape for curved edges with set z""" edges = [e for e in vd.Edges if e.Color == 0 and e.isCurved()] self.assertNotEqual(len(edges), 0) e0 = edges[0] e = e0.toShape(13.7) - self.assertTrue(type(e.Curve) == Part.Parabola or type(e.Curve) == Part.BSplineCurve) - self.assertFalse(PathGeom.pointsCoincide(e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter))) + self.assertTrue( + type(e.Curve) == Part.Parabola or type(e.Curve) == Part.BSplineCurve + ) + self.assertFalse( + PathGeom.pointsCoincide( + e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter) + ) + ) self.assertRoughly(e.valueAt(e.FirstParameter).z, 13.7) - self.assertRoughly(e.valueAt(e.LastParameter).z, 13.7) + self.assertRoughly(e.valueAt(e.LastParameter).z, 13.7) def test62(self): - '''Check toShape for curved edges with varying z''' + """Check toShape for curved edges with varying z""" edges = [e for e in vd.Edges if e.Color == 0 and e.isCurved()] self.assertNotEqual(len(edges), 0) e0 = edges[0] e = e0.toShape(2.37, 5.14) - self.assertTrue(type(e.Curve) == Part.Parabola or type(e.Curve) == Part.BSplineCurve) - self.assertFalse(PathGeom.pointsCoincide(e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter))) + self.assertTrue( + type(e.Curve) == Part.Parabola or type(e.Curve) == Part.BSplineCurve + ) + self.assertFalse( + PathGeom.pointsCoincide( + e.valueAt(e.FirstParameter), e.valueAt(e.LastParameter) + ) + ) self.assertRoughly(e.valueAt(e.FirstParameter).z, 2.37) - self.assertRoughly(e.valueAt(e.LastParameter).z, 5.14) - + self.assertRoughly(e.valueAt(e.LastParameter).z, 5.14) diff --git a/src/Mod/Path/TestPathApp.py b/src/Mod/Path/TestPathApp.py index dab2a245dc..9465b2eeb6 100644 --- a/src/Mod/Path/TestPathApp.py +++ b/src/Mod/Path/TestPathApp.py @@ -31,11 +31,13 @@ from PathTests.TestPathDressupHoldingTags import TestHoldingTags from PathTests.TestPathDrillable import TestPathDrillable from PathTests.TestPathDrillGenerator import TestPathDrillGenerator from PathTests.TestPathGeom import TestPathGeom + # from PathTests.TestPathHelix import TestPathHelix from PathTests.TestPathHelpers import TestPathHelpers from PathTests.TestPathHelixGenerator import TestPathHelixGenerator from PathTests.TestPathLog import TestPathLog from PathTests.TestPathOpTools import TestPathOpTools + # from PathTests.TestPathPost import PathPostTestCases from PathTests.TestPathPost import TestPathPostUtils from PathTests.TestPathPreferences import TestPathPreferences diff --git a/src/Mod/Path/Tools/toolbit-attributes.py b/src/Mod/Path/Tools/toolbit-attributes.py index 2f04ed6a58..92f91d70fd 100755 --- a/src/Mod/Path/Tools/toolbit-attributes.py +++ b/src/Mod/Path/Tools/toolbit-attributes.py @@ -61,15 +61,31 @@ import os import sys parser = argparse.ArgumentParser() -parser.add_argument('path', nargs='+', help='Shape file to process') -parser.add_argument('--move', metavar=':', help='Move attributes from group 1 into group 2') -parser.add_argument('--delete', metavar='prop', help='Delete the given attribute') -parser.add_argument('--set', metavar='prop=value', help='Set property value') -parser.add_argument('--print', action='store_true', help='If set attributes are printed as discovered') -parser.add_argument('--print-all', action='store_true', help='If set Shape attributes are also printed') -parser.add_argument('--print-groups', action='store_true', help='If set all custom property groups are printed') -parser.add_argument('--save-changes', action='store_true', help='Unless specified the file is not saved') -parser.add_argument('--freecad', help='Directory FreeCAD binaries (libFreeCAD.so) if not installed') +parser.add_argument("path", nargs="+", help="Shape file to process") +parser.add_argument( + "--move", + metavar=":", + help="Move attributes from group 1 into group 2", +) +parser.add_argument("--delete", metavar="prop", help="Delete the given attribute") +parser.add_argument("--set", metavar="prop=value", help="Set property value") +parser.add_argument( + "--print", action="store_true", help="If set attributes are printed as discovered" +) +parser.add_argument( + "--print-all", action="store_true", help="If set Shape attributes are also printed" +) +parser.add_argument( + "--print-groups", + action="store_true", + help="If set all custom property groups are printed", +) +parser.add_argument( + "--save-changes", action="store_true", help="Unless specified the file is not saved" +) +parser.add_argument( + "--freecad", help="Directory FreeCAD binaries (libFreeCAD.so) if not installed" +) args = parser.parse_args() if args.freecad: @@ -80,19 +96,19 @@ import Path import PathScripts.PathPropertyBag as PathPropertyBag import PathScripts.PathUtil as PathUtil -set_var=None -set_val=None +set_var = None +set_val = None GroupMap = {} if args.move: - g = args.move.split(':') + g = args.move.split(":") if len(g) != 2: print("ERROR: {} not a valid group mapping".format(args.move)) sys.exit(1) GroupMap[g[0]] = g[1] if args.set: - s = args.set.split('=') + s = args.set.split("=") if len(s) != 2: print("ERROR: {} not a valid group mapping".format(args.move)) sys.exit(1) @@ -100,7 +116,7 @@ if args.set: set_val = s[1] for i, fname in enumerate(args.path): - #print(fname) + # print(fname) doc = FreeCAD.openDocument(fname, False) print("{}:".format(doc.Name)) for o in doc.Objects: @@ -115,11 +131,11 @@ for i, fname in enumerate(args.path): ttp = PathPropertyBag.getPropertyTypeName(typ) val = PathUtil.getProperty(o, p) dsc = o.getDocumentationOfProperty(p) - enm = '' + enm = "" enum = [] - if ttp == 'Enumeration': + if ttp == "Enumeration": enum = o.getEnumerationsOfProperty(p) - enm = "{}".format(','.join(enum)) + enm = "{}".format(",".join(enum)) if GroupMap.get(grp): group = GroupMap.get(grp) print("move: {}.{} -> {}".format(grp, p, group)) @@ -131,22 +147,26 @@ for i, fname in enumerate(args.path): PathUtil.setProperty(o, p, val) if p == set_var: print("set {}.{} = {}".format(grp, p, set_val)) - if ttp == 'Enumeration' and set_val[0] == '[': - enum = set_val[1:-1].split(',') + if ttp == "Enumeration" and set_val[0] == "[": + enum = set_val[1:-1].split(",") setattr(o, p, enum) else: PathUtil.setProperty(o, p, set_val) if p == args.delete: print("delete {}.{}".format(grp, p)) o.removeProperty(p) - if not args.print_all and grp == 'Shape': + if not args.print_all and grp == "Shape": continue if args.print or args.print_all: - print(" {:10} {:20} {:20} {:10} {}".format(grp, p, ttp, str(val), enm)) + print( + " {:10} {:20} {:20} {:10} {}".format( + grp, p, ttp, str(val), enm + ) + ) o.Proxy.refreshCustomPropertyGroups() if args.save_changes: doc.recompute() doc.save() FreeCAD.closeDocument(doc.Name) -print('-done-') +print("-done-") diff --git a/src/Mod/Path/libarea/kurve/test.py b/src/Mod/Path/libarea/kurve/test.py index 4b3d49ace6..d06e4e918e 100644 --- a/src/Mod/Path/libarea/kurve/test.py +++ b/src/Mod/Path/libarea/kurve/test.py @@ -1,9 +1,9 @@ import area -p = area.Point(0,0) +p = area.Point(0, 0) -m = area.Matrix([1,0,0,12, 0,1,0,0, 0,0,1,0, 0,0,0,1]) +m = area.Matrix([1, 0, 0, 12, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) p.Transform(m) -print (p.x, p.y) +print(p.x, p.y)