From 59a9ab9bc06a83c40e70e01bc6bdd3075679e7ed Mon Sep 17 00:00:00 2001 From: sliptonic Date: Sun, 16 Sep 2018 16:04:26 -0500 Subject: [PATCH 1/7] Path: Z depth correction from probe data --- src/Mod/Path/CMakeLists.txt | 3 + src/Mod/Path/Gui/Resources/Path.qrc | 2 + .../Path/Gui/Resources/icons/Path-Probe.svg | 666 ++++++++++++++++++ .../Gui/Resources/panels/PageOpProbeEdit.ui | 93 +++ src/Mod/Path/InitGui.py | 2 +- .../Path/PathScripts/PathDressupZCorrect.py | 261 +++++++ src/Mod/Path/PathScripts/PathProbe.py | 107 +++ src/Mod/Path/PathScripts/PathProbeGui.py | 70 ++ src/Mod/Path/PathScripts/PathSelection.py | 11 +- 9 files changed, 1213 insertions(+), 2 deletions(-) create mode 100644 src/Mod/Path/Gui/Resources/icons/Path-Probe.svg create mode 100644 src/Mod/Path/Gui/Resources/panels/PageOpProbeEdit.ui create mode 100644 src/Mod/Path/PathScripts/PathDressupZCorrect.py create mode 100644 src/Mod/Path/PathScripts/PathProbe.py create mode 100644 src/Mod/Path/PathScripts/PathProbeGui.py diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index 098bb114dd97..1a51ebc8729c 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -46,6 +46,7 @@ SET(PathScripts_SRCS PathScripts/PathDressupTag.py PathScripts/PathDressupTagGui.py PathScripts/PathDressupTagPreferences.py + PathScripts/PathDressupZCorrect.py PathScripts/PathDrilling.py PathScripts/PathDrillingGui.py PathScripts/PathEngrave.py @@ -82,6 +83,8 @@ SET(PathScripts_SRCS PathScripts/PathPreferences.py PathScripts/PathPreferencesPathDressup.py PathScripts/PathPreferencesPathJob.py + PathScripts/PathProbe.py + PathScripts/PathProbeGui.py PathScripts/PathProfileBase.py PathScripts/PathProfileBaseGui.py PathScripts/PathProfileContour.py diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index b461c8cdf6f4..184bbb62666b 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -37,6 +37,7 @@ icons/Path-Plane.svg icons/Path-Pocket.svg icons/Path-Post.svg + icons/Path-Probe.svg icons/Path-Profile-Edges.svg icons/Path-Profile-Face.svg icons/Path-Profile.svg @@ -102,6 +103,7 @@ panels/PageOpHelixEdit.ui panels/PageOpPocketExtEdit.ui panels/PageOpPocketFullEdit.ui + panels/PageOpProbeEdit.ui panels/PageOpProfileFullEdit.ui panels/PageOpSurfaceEdit.ui panels/PathEdit.ui diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Probe.svg b/src/Mod/Path/Gui/Resources/icons/Path-Probe.svg new file mode 100644 index 000000000000..63eae06c30fe --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Probe.svg @@ -0,0 +1,666 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + Path-Drilling + 2015-07-04 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Drilling.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpProbeEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpProbeEdit.ui new file mode 100644 index 000000000000..c2c9a2730952 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/panels/PageOpProbeEdit.ui @@ -0,0 +1,93 @@ + + + Form + + + + 0 + 0 + 400 + 140 + + + + Form + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + + + ToolController + + + + + + + <html><head/><body><p>The tool and its settings to be used for this operation.</p></body></html> + + + + + + + + + + + + + Algorithm + + + + + + + + OCL Dropcutter + + + + + OCL Waterline + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 0657fac577dd..64a48bc54a90 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -92,7 +92,7 @@ def Initialize(self): threedopcmdlist = ["Path_Pocket_3D"] engravecmdlist = ["Path_Engrave", "Path_Deburr"] modcmdlist = ["Path_OperationCopy", "Path_Array", "Path_SimpleCopy" ] - dressupcmdlist = ["Path_DressupAxisMap", "Path_DressupPathBoundary", "Path_DressupDogbone", "Path_DressupDragKnife", "Path_DressupLeadInOut", "Path_DressupRampEntry", "Path_DressupTag"] + dressupcmdlist = ["Path_DressupAxisMap", "Path_DressupPathBoundary", "Path_DressupDogbone", "Path_DressupDragKnife", "Path_DressupLeadInOut", "Path_DressupRampEntry", "Path_DressupTag", "Path_DressupZCorrect"] extracmdlist = [] #modcmdmore = ["Path_Hop",] #remotecmdlist = ["Path_Remote"] diff --git a/src/Mod/Path/PathScripts/PathDressupZCorrect.py b/src/Mod/Path/PathScripts/PathDressupZCorrect.py new file mode 100644 index 000000000000..ecb9dc43e390 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathDressupZCorrect.py @@ -0,0 +1,261 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2018 sliptonic * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +import FreeCAD +import FreeCADGui +import Path +import PathScripts.PathUtils as PathUtils +from bisect import bisect_left + +from PySide import QtCore, QtGui + +"""Z Depth Correction Dressup. This dressup takes a probe file as input and does bilinear interpolation of the Zdepths to correct for a surface which is not parallel to the milling table/bed. The probe file should conform to the format specified by the linuxcnc G38 probe logging: 9-number coordinate consisting of XYZABCUVW http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g38 +""" + +# Qt tanslation handling +def translate(context, text, disambig=None): + return QtCore.QCoreApplication.translate(context, text, disambig) + +movecommands = ['G1', 'G01', 'G2', 'G02', 'G3', 'G03'] +rapidcommands = ['G0', 'G00'] +arccommands = ['G2', 'G3', 'G02', 'G03'] + +class ObjectDressup: + x_index = 0 + y_index = 0 + values = None + x_length = 0 + y_length = 0 + + def __init__(self, obj): + obj.addProperty("App::PropertyLink", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupAxisMap", "The base path to modify")) + obj.addProperty("App::PropertyFile", "probefile", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "The point file from the surface probing.")) + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + def onChanged(self, fp, prop): + if str(prop) == "probefile": + self._loadFile(fp.probefile) + + def _bilinearInterpolate(self, x, y): + # local lookups + x_index, y_index, values = self.x_index, self.y_index, self.values + + i = bisect_left(x_index, x) - 1 + j = bisect_left(y_index, y) - 1 + + if True: #self.extrapolate: + # fix x index + if i == -1: + x_slice = slice(None, 2) + elif i == self.x_length - 1: + x_slice = slice(-2, None) + else: + x_slice = slice(i, i + 2) + # fix y index + if j == -1: + j = 0 + y_slice = slice(None, 2) + elif j == self.y_length - 1: + j = -2 + y_slice = slice(-2, None) + else: + y_slice = slice(j, j + 2) + else: + if i == -1 or i == self.x_length - 1: + raise ValueError("Extrapolation not allowed!") + if j == -1 or j == self.y_length - 1: + raise ValueError("Extrapolation not allowed!") + + x1, x2 = x_index[x_slice] + y1, y2 = y_index[y_slice] + z11, z12 = values[j][x_slice] + z21, z22 = values[j + 1][x_slice] + + return (z11 * (x2 - x) * (y2 - y) + + z21 * (x - x1) * (y2 - y) + + z12 * (x2 - x) * (y - y1) + + z22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1)) + + + def _loadFile(self, filename): + f1 = open(filename, 'r') + + pointlist = [] + for line in f1.readlines(): + w = line.split() + xval = round(float(w[0]), 3) + yval = round(float(w[1]), 3) + zval = round(float(w[2]), 3) + pointlist.append((xval, yval,zval)) + cols = list(zip(*pointlist)) + + xcolpos = list(sorted(set(cols[0]))) + #ycolpos = list(sorted(set(cols[1]))) + zdict = {x:[] for x in xcolpos} + + for (x, y, z) in pointlist: + zdict[x].append(z) + + self.values = tuple(tuple(x) for x in [zdict[x] for x in sorted(xcolpos)]) + self.x_index = tuple(sorted(set(cols[0]))) + self.y_index = tuple(sorted(set(cols[1]))) + + # sanity check + x_length = len(self.x_index) + y_length = len(self.y_index) + + if x_length < 2 or y_length < 2: + raise ValueError("Probe grid must be at least 2x2.") + if y_length != len(self.values): + raise ValueError("Probe grid data must have equal number of rows to y_index.") + if any(x2 - x1 <= 0 for x1, x2 in zip(self.x_index, self.x_index[1:])): + raise ValueError("x_index must be in strictly ascending order!") + if any(y2 - y1 <= 0 for y1, y2 in zip(self.y_index, self.y_index[1:])): + raise ValueError("y_index must be in strictly ascending order!") + + self.x_length = x_length + self.y_length = y_length + + + def execute(self, obj): + if self.values is None: #No valid probe data. return unchanged path + obj.Path = obj.Base.Path + return + + if obj.Base: + if obj.Base.isDerivedFrom("Path::Feature"): + if obj.Base.Path: + if obj.Base.Path.Commands: + pp = obj.Base.Path.Commands + # process the path + + pathlist = pp + newcommandlist = [] + currLocation = {'X':0,'Y':0,'Z':0, 'F': 0} + + for c in pathlist: #obj.Base.Path.Commands: + newparams = dict(c.Parameters) + currLocation.update(newparams) + remapvar = newparams.pop("Z", None) + if remapvar is not None: + offset = self._bilinearInterpolate(currLocation['X'], currLocation['Y']) + newparams["Z"] = remapvar + offset + newcommand = Path.Command(c.Name, newparams) + newcommandlist.append(newcommand) + currLocation.update(newparams) + else: + newcommandlist.append(c) + + path = Path.Path(newcommandlist) + obj.Path = path + + +class ViewProviderDressup: + + def __init__(self, vobj): + vobj.Proxy = self + + def attach(self, vobj): + self.obj = vobj.Object + if self.obj and self.obj.Base: + for i in self.obj.Base.InList: + if hasattr(i, "Group"): + group = i.Group + for g in group: + if g.Name == self.obj.Base.Name: + group.remove(g) + i.Group = group + # FreeCADGui.ActiveDocument.getObject(obj.Base.Name).Visibility = False + return + + def claimChildren(self): + return [self.obj.Base] + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + def onDelete(self, arg1=None, arg2=None): + '''this makes sure that the base operation is added back to the project and visible''' + FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True + job = PathUtils.findParentJob(arg1.Object) + job.Proxy.addOperation(arg1.Object.Base) + arg1.Object.Base = None + return True + +class CommandPathDressup: + + def GetResources(self): + return {'Pixmap': 'Path-Dressup', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "Z Depth Correction Dress-up"), + 'Accel': "", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "Use Probe Map to correct Z depth")} + + def IsActive(self): + if FreeCAD.ActiveDocument is not None: + for o in FreeCAD.ActiveDocument.Objects: + if o.Name[:3] == "Job": + return True + return False + + def Activated(self): + + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelection() + if len(selection) != 1: + FreeCAD.Console.PrintError(translate("Path_Dressup", "Please select one path object\n")) + return + if not selection[0].isDerivedFrom("Path::Feature"): + FreeCAD.Console.PrintError(translate("Path_Dressup", "The selected object is not a path\n")) + return + if selection[0].isDerivedFrom("Path::FeatureCompoundPython"): + FreeCAD.Console.PrintError(translate("Path_Dressup", "Please select a Path object")) + return + + # everything ok! + FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupZCorrect", "Create Dress-up")) + FreeCADGui.addModule("PathScripts.PathDressupZCorrect") + FreeCADGui.addModule("PathScripts.PathUtils") + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "ZCorrectDressup")') + FreeCADGui.doCommand('PathScripts.PathDressupZCorrect.ObjectDressup(obj)') + FreeCADGui.doCommand('obj.Base = FreeCAD.ActiveDocument.' + selection[0].Name) + FreeCADGui.doCommand('PathScripts.PathDressupZCorrect.ViewProviderDressup(obj.ViewObject)') + FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)') + FreeCADGui.doCommand('Gui.ActiveDocument.getObject(obj.Base.Name).Visibility = False') + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_DressupZCorrect', CommandPathDressup()) + +FreeCAD.Console.PrintLog("Loading PathDressup... done\n") diff --git a/src/Mod/Path/PathScripts/PathProbe.py b/src/Mod/Path/PathScripts/PathProbe.py new file mode 100644 index 000000000000..4bb66b126165 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathProbe.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2018 sliptonic * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +from __future__ import print_function + +import FreeCAD +import Path +import PathScripts.PathLog as PathLog +import PathScripts.PathOp as PathOp +import PathScripts.PathUtils as PathUtils + +#from PathScripts.PathUtils import waiting_effects +from PySide import QtCore + +__title__ = "Path Probing Operation" +__author__ = "sliptonic (Brad Collette)" +__url__ = "http://www.freecadweb.org" +__doc__ = "Path Probing operation." + +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) + + +# Qt tanslation handling +def translate(context, text, disambig=None): + return QtCore.QCoreApplication.translate(context, text, disambig) + + +class ObjectProbing(PathOp.ObjectOp): + '''Proxy object for Probing operation.''' + + def opFeatures(self, obj): + '''opFeatures(obj) ... Probing works on the stock object.''' + return PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureTool + + def initOperation(self, obj): + obj.addProperty("App::PropertyLength", "Xoffset", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "X offset between tool and probe")) + obj.addProperty("App::PropertyLength", "Yoffset", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Y offset between tool and probe")) + obj.addProperty("App::PropertyInteger", "PointCountX", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in X direction")).PointCountX=3 + obj.addProperty("App::PropertyInteger", "PointCountY", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in Y direction")).PointCountY=3 + + def drange(self, start=1.0, stop=5.0, step=1.0): + r = start + while r <= stop: + yield r + r += step + + def opExecute(self, obj): + '''opExecute(obj) ... generate probe locations.''' + PathLog.track() + self.commandlist.append(Path.Command("(Begin Probing)")) + + stock = PathUtils.findParentJob(obj).Stock + bb = stock.Shape.BoundBox + + xdist = (bb.XMax - bb.XMin)/ (obj.PointCountX - 1) + ydist = (bb.YMax - bb.YMin)/ (obj.PointCountY - 1) + + self.commandlist.append(Path.Command("(PROBEOPEN probe_points.txt)")) + + self.commandlist.append(Path.Command("G0", {"X":bb.XMin, "Y":bb.YMin, "Z":obj.SafeHeight.Value})) + for x in self.drange(bb.XMin, bb.XMax, xdist): + for y in self.drange(bb.YMin, bb.YMax, ydist): + self.commandlist.append(Path.Command("G0", {"X":x + obj.Xoffset.Value, "Y":y + obj.Yoffset.Value, "Z":obj.SafeHeight.Value})) + self.commandlist.append(Path.Command("G38.2",{"Z":obj.FinalDepth.Value, "F":obj.ToolController.VertFeed.Value})) + self.commandlist.append(Path.Command("G0", {"Z":obj.SafeHeight.Value})) + + self.commandlist.append(Path.Command("(PROBECLOSE)")) + + + def opSetDefaultValues(self, obj, job): + '''opSetDefaultValues(obj, job) ... set default value for RetractHeight''' + +def SetupProperties(): + setup = [] + return setup + +def Create(name, obj = None): + '''Create(name) ... Creates and returns a Probing operation.''' + if obj is None: + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) + proxy = ObjectProbing(obj, name) + return obj diff --git a/src/Mod/Path/PathScripts/PathProbeGui.py b/src/Mod/Path/PathScripts/PathProbeGui.py new file mode 100644 index 000000000000..eba39965176b --- /dev/null +++ b/src/Mod/Path/PathScripts/PathProbeGui.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2017 sliptonic * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +import FreeCAD +import FreeCADGui +import PathScripts.PathProbe as PathProbe +import PathScripts.PathOpGui as PathOpGui + +from PySide import QtCore + +__title__ = "Path Probing Operation UI" +__author__ = "sliptonic (Brad Collette)" +__url__ = "http://www.freecadweb.org" +__doc__ = "Probing operation page controller and command implementation." + +class TaskPanelOpPage(PathOpGui.TaskPanelPage): + '''Page controller class for the Probing operation.''' + + def getForm(self): + '''getForm() ... returns UI''' + return FreeCADGui.PySideUic.loadUi(":/panels/PageOpProbeEdit.ui") + + def getFields(self, obj): + '''getFields(obj) ... transfers values from UI to obj's proprties''' + # if obj.StartVertex != self.form.startVertex.value(): + # obj.StartVertex = self.form.startVertex.value() + self.updateToolController(obj, self.form.toolController) + + def setFields(self, obj): + '''setFields(obj) ... transfers obj's property values to UI''' + #self.form.startVertex.setValue(obj.StartVertex) + self.setupToolController(obj, self.form.toolController) + + def getSignalsForUpdate(self, obj): + '''getSignalsForUpdate(obj) ... return list of signals for updating obj''' + signals = [] + #signals.append(self.form.startVertex.editingFinished) + signals.append(self.form.toolController.currentIndexChanged) + return signals + +Command = PathOpGui.SetupOperation('Probe', + PathProbe.Create, + TaskPanelOpPage, + 'Path-Probe', + QtCore.QT_TRANSLATE_NOOP("Probe", "Probe"), + QtCore.QT_TRANSLATE_NOOP("Probe", "Create a Probing Grid from a job stock"))# PathProbe.SetupProperties) + +FreeCAD.Console.PrintLog("Loading PathProbeGui... done\n") + diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index acb9c473e295..386ff1c29c88 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -167,13 +167,17 @@ def allow(self, doc, obj, sub): # pylint: disable=unused-argument obj = obj.Shape except Exception: # pylint: disable=broad-except return False - + return adaptive class CONTOURGate(PathBaseGate): def allow(self, doc, obj, sub): # pylint: disable=unused-argument pass +class PROBEGate: + def allow(self, doc, obj, sub): + pass + def contourselect(): FreeCADGui.Selection.addSelectionGate(CONTOURGate()) FreeCAD.Console.PrintWarning("Contour Select Mode\n") @@ -215,6 +219,10 @@ def surfaceselect(): # FreeCADGui.Selection.addSelectionGate(PROFILEGate()) # Added for face selection FreeCAD.Console.PrintWarning("Surfacing Select Mode\n") +def probeselect(): + FreeCADGui.Selection.addSelectionGate(PROBEGate()) + FreeCAD.Console.PrintWarning("Probe Select Mode\n") + def select(op): opsel = {} opsel['Contour'] = contourselect @@ -230,6 +238,7 @@ def select(op): opsel['Profile Faces'] = profileselect opsel['Surface'] = surfaceselect opsel['Adaptive'] = adaptiveselect + opsel['Probe'] = probeselect return opsel[op] def clear(): From 70a52a8eda7681ca4b1597576a686c5607d665b2 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 17 Sep 2018 10:53:23 -0500 Subject: [PATCH 2/7] Path: Layout op panel for probe --- .../Gui/Resources/panels/PageOpProbeEdit.ui | 131 +++++++++++++++--- src/Mod/Path/PathScripts/PathProbe.py | 11 +- src/Mod/Path/PathScripts/PathProbeGui.py | 29 +++- 3 files changed, 144 insertions(+), 27 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpProbeEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpProbeEdit.ui index c2c9a2730952..f2220803796a 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpProbeEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpProbeEdit.ui @@ -6,8 +6,8 @@ 0 0 - 400 - 140 + 424 + 376 @@ -47,27 +47,113 @@ - - - - + + + Probe Grid Points + + + + + + 3 + + + 1000 + + + + + + + 3 + + + 1000 + + + + + - Algorithm + X: + + + + + + + Y: + + + + + + + + + + Probe + + + + + + Y Offset + + + + + + + - - - - - OCL Dropcutter - - - - - OCL Waterline - - + + + + X Offset + + + + + + + + + + + + + + + + + Output + + + + + + File Name + + + + + + + <html><head/><body><p>Enter the filename where the probe points should be written.</p></body></html> + + + ProbePoints.txt + + + + + + + ... + @@ -88,6 +174,13 @@ + + + Gui::InputField + QLineEdit +
Gui/InputField.h
+
+
diff --git a/src/Mod/Path/PathScripts/PathProbe.py b/src/Mod/Path/PathScripts/PathProbe.py index 4bb66b126165..9e27b534118b 100644 --- a/src/Mod/Path/PathScripts/PathProbe.py +++ b/src/Mod/Path/PathScripts/PathProbe.py @@ -60,9 +60,9 @@ def opFeatures(self, obj): def initOperation(self, obj): obj.addProperty("App::PropertyLength", "Xoffset", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "X offset between tool and probe")) obj.addProperty("App::PropertyLength", "Yoffset", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Y offset between tool and probe")) - obj.addProperty("App::PropertyInteger", "PointCountX", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in X direction")).PointCountX=3 - obj.addProperty("App::PropertyInteger", "PointCountY", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in Y direction")).PointCountY=3 - + obj.addProperty("App::PropertyInteger", "PointCountX", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in X direction")) + obj.addProperty("App::PropertyInteger", "PointCountY", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in Y direction")) + obj.addProperty("App::PropertyFile", "OutputFileName", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The output location for the probe data to be written")) def drange(self, start=1.0, stop=5.0, step=1.0): r = start while r <= stop: @@ -80,7 +80,8 @@ def opExecute(self, obj): xdist = (bb.XMax - bb.XMin)/ (obj.PointCountX - 1) ydist = (bb.YMax - bb.YMin)/ (obj.PointCountY - 1) - self.commandlist.append(Path.Command("(PROBEOPEN probe_points.txt)")) + openstring = '(PROBEOPEN {})'.format(obj.OutputFileName) + self.commandlist.append(Path.Command(openstring)) self.commandlist.append(Path.Command("G0", {"X":bb.XMin, "Y":bb.YMin, "Z":obj.SafeHeight.Value})) for x in self.drange(bb.XMin, bb.XMax, xdist): @@ -96,7 +97,7 @@ def opSetDefaultValues(self, obj, job): '''opSetDefaultValues(obj, job) ... set default value for RetractHeight''' def SetupProperties(): - setup = [] + setup = ['Xoffset', 'Yoffset', 'PointCountX', 'PointCountY'] return setup def Create(name, obj = None): diff --git a/src/Mod/Path/PathScripts/PathProbeGui.py b/src/Mod/Path/PathScripts/PathProbeGui.py index eba39965176b..2ae4730924eb 100644 --- a/src/Mod/Path/PathScripts/PathProbeGui.py +++ b/src/Mod/Path/PathScripts/PathProbeGui.py @@ -26,8 +26,9 @@ import FreeCADGui import PathScripts.PathProbe as PathProbe import PathScripts.PathOpGui as PathOpGui +import PathScripts.PathGui as PathGui -from PySide import QtCore +from PySide import QtCore, QtGui __title__ = "Path Probing Operation UI" __author__ = "sliptonic (Brad Collette)" @@ -46,25 +47,47 @@ def getFields(self, obj): # if obj.StartVertex != self.form.startVertex.value(): # obj.StartVertex = self.form.startVertex.value() self.updateToolController(obj, self.form.toolController) + PathGui.updateInputField(obj, 'Xoffset', self.form.Xoffset) + PathGui.updateInputField(obj, 'Yoffset', self.form.Yoffset) + obj.PointCountX = self.form.PointCountX.value() + obj.PointCountY = self.form.PointCountY.value() + obj.OutputFileName = str(self.form.OutputFileName.text()) def setFields(self, obj): '''setFields(obj) ... transfers obj's property values to UI''' #self.form.startVertex.setValue(obj.StartVertex) self.setupToolController(obj, self.form.toolController) + self.form.Xoffset.setText(FreeCAD.Units.Quantity(obj.Xoffset.Value, FreeCAD.Units.Length).UserString) + self.form.Yoffset.setText(FreeCAD.Units.Quantity(obj.Yoffset.Value, FreeCAD.Units.Length).UserString) + self.form.OutputFileName.setText(obj.OutputFileName) + self.form.PointCountX.setValue(obj.PointCountX) + self.form.PointCountY.setValue(obj.PointCountY) def getSignalsForUpdate(self, obj): '''getSignalsForUpdate(obj) ... return list of signals for updating obj''' signals = [] - #signals.append(self.form.startVertex.editingFinished) signals.append(self.form.toolController.currentIndexChanged) + signals.append(self.form.PointCountX.valueChanged) + signals.append(self.form.PointCountY.valueChanged) + signals.append(self.form.OutputFileName.editingFinished) + signals.append(self.form.Xoffset.valueChanged) + signals.append(self.form.Yoffset.valueChanged) + signals.append(self.form.SetOutputFileName.clicked) return signals + # def SetOutputFileName(self): + # filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("Path_Probe", "Select Output File"), None, translate("Path_Probe", "All Files (*.*)")) + # if filename and filename[0]: + # self.obj.OutputFileName = str(filename[0]) + # self.setFields() + Command = PathOpGui.SetupOperation('Probe', PathProbe.Create, TaskPanelOpPage, 'Path-Probe', QtCore.QT_TRANSLATE_NOOP("Probe", "Probe"), - QtCore.QT_TRANSLATE_NOOP("Probe", "Create a Probing Grid from a job stock"))# PathProbe.SetupProperties) + QtCore.QT_TRANSLATE_NOOP("Probe", "Create a Probing Grid from a job stock"), + PathProbe.SetupProperties) FreeCAD.Console.PrintLog("Loading PathProbeGui... done\n") From 4b0f4e1ac006037575c99af8a4c4fe4624fe9cfe Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 17 Sep 2018 23:37:40 -0500 Subject: [PATCH 3/7] Path: make setupsheet handle PropertyFile --- .../Path/PathScripts/PathSetupSheetOpPrototypeGui.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototypeGui.py b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototypeGui.py index f89b7b40ed96..d648ca6a6292 100644 --- a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototypeGui.py +++ b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototypeGui.py @@ -188,6 +188,18 @@ def setEditorData(self, widget): def setModelData(self, widget): self.prop.setValue(widget.value()) +class _PropertyFileEditor(_PropertyEditor): + + def widget(self, parent): + return QtGui.QLineEdit(parent) + + def setEditorData(self, widget): + text = '' if self.prop.getValue() is None else self.prop.getValue() + widget.setText(text) + + def setModelData(self, widget): + self.prop.setValue(widget.text()) + _EditorFactory = { PathSetupSheetOpPrototype.Property: None, PathSetupSheetOpPrototype.PropertyAngle: _PropertyAngleEditor, From a8483c764e9422fb74a3b17123d869eedf7202c3 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Tue, 18 Sep 2018 09:46:04 -0500 Subject: [PATCH 4/7] Path: Finish GUI & Cleanup --- .../Path/PathScripts/PathDressupZCorrect.py | 42 +++++++++---------- src/Mod/Path/PathScripts/PathProbe.py | 23 +++++----- src/Mod/Path/PathScripts/PathProbeGui.py | 20 +++++---- 3 files changed, 42 insertions(+), 43 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupZCorrect.py b/src/Mod/Path/PathScripts/PathDressupZCorrect.py index ecb9dc43e390..5a49d4bc8a6b 100644 --- a/src/Mod/Path/PathScripts/PathDressupZCorrect.py +++ b/src/Mod/Path/PathScripts/PathDressupZCorrect.py @@ -20,6 +20,10 @@ # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * # * USA * # * * +# * Bilinear interpolation code modified heavily from the interpolation * +# * library https://github.com/pmav99/interpolation * +# * Copyright (c) 2013 by Panagiotis Mavrogiorgos * +# * * # *************************************************************************** import FreeCAD import FreeCADGui @@ -27,7 +31,7 @@ import PathScripts.PathUtils as PathUtils from bisect import bisect_left -from PySide import QtCore, QtGui +from PySide import QtCore """Z Depth Correction Dressup. This dressup takes a probe file as input and does bilinear interpolation of the Zdepths to correct for a surface which is not parallel to the milling table/bed. The probe file should conform to the format specified by the linuxcnc G38 probe logging: 9-number coordinate consisting of XYZABCUVW http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g38 """ @@ -69,28 +73,22 @@ def _bilinearInterpolate(self, x, y): i = bisect_left(x_index, x) - 1 j = bisect_left(y_index, y) - 1 - if True: #self.extrapolate: - # fix x index - if i == -1: - x_slice = slice(None, 2) - elif i == self.x_length - 1: - x_slice = slice(-2, None) - else: - x_slice = slice(i, i + 2) - # fix y index - if j == -1: - j = 0 - y_slice = slice(None, 2) - elif j == self.y_length - 1: - j = -2 - y_slice = slice(-2, None) - else: - y_slice = slice(j, j + 2) + # fix x index + if i == -1: + x_slice = slice(None, 2) + elif i == self.x_length - 1: + x_slice = slice(-2, None) + else: + x_slice = slice(i, i + 2) + # fix y index + if j == -1: + j = 0 + y_slice = slice(None, 2) + elif j == self.y_length - 1: + j = -2 + y_slice = slice(-2, None) else: - if i == -1 or i == self.x_length - 1: - raise ValueError("Extrapolation not allowed!") - if j == -1 or j == self.y_length - 1: - raise ValueError("Extrapolation not allowed!") + y_slice = slice(j, j + 2) x1, x2 = x_index[x_slice] y1, y2 = y_index[y_slice] diff --git a/src/Mod/Path/PathScripts/PathProbe.py b/src/Mod/Path/PathScripts/PathProbe.py index 9e27b534118b..eb409d833d41 100644 --- a/src/Mod/Path/PathScripts/PathProbe.py +++ b/src/Mod/Path/PathScripts/PathProbe.py @@ -63,11 +63,13 @@ def initOperation(self, obj): obj.addProperty("App::PropertyInteger", "PointCountX", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in X direction")) obj.addProperty("App::PropertyInteger", "PointCountY", "Probe", QtCore.QT_TRANSLATE_NOOP("App::Property", "Number of points to probe in Y direction")) obj.addProperty("App::PropertyFile", "OutputFileName", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The output location for the probe data to be written")) - def drange(self, start=1.0, stop=5.0, step=1.0): - r = start - while r <= stop: - yield r - r += step + + def nextpoint(self, startpoint=0.0, endpoint=0.0, count=3): + curstep = 0 + dist = (endpoint - startpoint) / (count - 1) + while curstep <= count-1: + yield startpoint + (curstep * dist) + curstep += 1 def opExecute(self, obj): '''opExecute(obj) ... generate probe locations.''' @@ -77,15 +79,12 @@ def opExecute(self, obj): stock = PathUtils.findParentJob(obj).Stock bb = stock.Shape.BoundBox - xdist = (bb.XMax - bb.XMin)/ (obj.PointCountX - 1) - ydist = (bb.YMax - bb.YMin)/ (obj.PointCountY - 1) - openstring = '(PROBEOPEN {})'.format(obj.OutputFileName) self.commandlist.append(Path.Command(openstring)) + self.commandlist.append(Path.Command("G0", {"Z":obj.ClearanceHeight.Value})) - self.commandlist.append(Path.Command("G0", {"X":bb.XMin, "Y":bb.YMin, "Z":obj.SafeHeight.Value})) - for x in self.drange(bb.XMin, bb.XMax, xdist): - for y in self.drange(bb.YMin, bb.YMax, ydist): + for x in self.nextpoint(bb.XMin, bb.XMax, obj.PointCountX): + for y in self.nextpoint(bb.YMin, bb.YMax, obj.PointCountY): self.commandlist.append(Path.Command("G0", {"X":x + obj.Xoffset.Value, "Y":y + obj.Yoffset.Value, "Z":obj.SafeHeight.Value})) self.commandlist.append(Path.Command("G38.2",{"Z":obj.FinalDepth.Value, "F":obj.ToolController.VertFeed.Value})) self.commandlist.append(Path.Command("G0", {"Z":obj.SafeHeight.Value})) @@ -97,7 +96,7 @@ def opSetDefaultValues(self, obj, job): '''opSetDefaultValues(obj, job) ... set default value for RetractHeight''' def SetupProperties(): - setup = ['Xoffset', 'Yoffset', 'PointCountX', 'PointCountY'] + setup = ['Xoffset', 'Yoffset', 'PointCountX', 'PointCountY', 'OutputFileName'] return setup def Create(name, obj = None): diff --git a/src/Mod/Path/PathScripts/PathProbeGui.py b/src/Mod/Path/PathScripts/PathProbeGui.py index 2ae4730924eb..767bd1c57330 100644 --- a/src/Mod/Path/PathScripts/PathProbeGui.py +++ b/src/Mod/Path/PathScripts/PathProbeGui.py @@ -35,6 +35,10 @@ __url__ = "http://www.freecadweb.org" __doc__ = "Probing operation page controller and command implementation." +# Qt tanslation handling +def translate(context, text, disambig=None): + return QtCore.QCoreApplication.translate(context, text, disambig) + class TaskPanelOpPage(PathOpGui.TaskPanelPage): '''Page controller class for the Probing operation.''' @@ -44,8 +48,6 @@ def getForm(self): def getFields(self, obj): '''getFields(obj) ... transfers values from UI to obj's proprties''' - # if obj.StartVertex != self.form.startVertex.value(): - # obj.StartVertex = self.form.startVertex.value() self.updateToolController(obj, self.form.toolController) PathGui.updateInputField(obj, 'Xoffset', self.form.Xoffset) PathGui.updateInputField(obj, 'Yoffset', self.form.Yoffset) @@ -55,7 +57,6 @@ def getFields(self, obj): def setFields(self, obj): '''setFields(obj) ... transfers obj's property values to UI''' - #self.form.startVertex.setValue(obj.StartVertex) self.setupToolController(obj, self.form.toolController) self.form.Xoffset.setText(FreeCAD.Units.Quantity(obj.Xoffset.Value, FreeCAD.Units.Length).UserString) self.form.Yoffset.setText(FreeCAD.Units.Quantity(obj.Yoffset.Value, FreeCAD.Units.Length).UserString) @@ -72,14 +73,15 @@ def getSignalsForUpdate(self, obj): signals.append(self.form.OutputFileName.editingFinished) signals.append(self.form.Xoffset.valueChanged) signals.append(self.form.Yoffset.valueChanged) - signals.append(self.form.SetOutputFileName.clicked) + #signals.append(self.form.SetOutputFileName.clicked) + self.form.SetOutputFileName.clicked.connect(self.SetOutputFileName) return signals - # def SetOutputFileName(self): - # filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("Path_Probe", "Select Output File"), None, translate("Path_Probe", "All Files (*.*)")) - # if filename and filename[0]: - # self.obj.OutputFileName = str(filename[0]) - # self.setFields() + def SetOutputFileName(self): + filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("Path_Probe", "Select Output File"), None, translate("Path_Probe", "All Files (*.*)")) + if filename and filename[0]: + self.obj.OutputFileName = str(filename[0]) + self.setFields(self.obj) Command = PathOpGui.SetupOperation('Probe', PathProbe.Create, From 71da4906889fdacc32a192643aa6073c62aaa415 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 24 Sep 2018 12:37:50 -0500 Subject: [PATCH 5/7] Path: adding dressup task panel --- src/Mod/Path/Gui/Resources/Path.qrc | 1 + .../Path/Gui/Resources/panels/ZCorrectEdit.ui | 92 ++++++++ .../Path/PathScripts/PathDressupZCorrect.py | 219 +++++++++++------- src/Mod/Path/PathScripts/PathProbe.py | 4 +- 4 files changed, 234 insertions(+), 82 deletions(-) create mode 100644 src/Mod/Path/Gui/Resources/panels/ZCorrectEdit.ui diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index 184bbb62666b..ca09e140b83c 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -116,6 +116,7 @@ panels/ToolEditor.ui panels/ToolLibraryEditor.ui panels/TaskPathSimulator.ui + panels/ZCorrectEdit.ui preferences/PathDressupHoldingTags.ui preferences/PathJob.ui translations/Path_af.qm diff --git a/src/Mod/Path/Gui/Resources/panels/ZCorrectEdit.ui b/src/Mod/Path/Gui/Resources/panels/ZCorrectEdit.ui new file mode 100644 index 000000000000..36d58f0c6f95 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/panels/ZCorrectEdit.ui @@ -0,0 +1,92 @@ + + + TaskPanel + + + + 0 + 0 + 376 + 387 + + + + Z Depth Correction + + + + + + QFrame::NoFrame + + + 0 + + + + + 0 + 0 + 358 + 340 + + + + Dressup + + + + + + Probe Points File + + + + + + ... + + + + + + + File Name + + + + + + + <html><head/><body><p>Enter the filename containing the probe data</p></body></html> + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/PathScripts/PathDressupZCorrect.py b/src/Mod/Path/PathScripts/PathDressupZCorrect.py index 5a49d4bc8a6b..2be8337f4b7e 100644 --- a/src/Mod/Path/PathScripts/PathDressupZCorrect.py +++ b/src/Mod/Path/PathScripts/PathDressupZCorrect.py @@ -27,15 +27,26 @@ # *************************************************************************** import FreeCAD import FreeCADGui +import Part import Path +import PathScripts.PathLog as PathLog import PathScripts.PathUtils as PathUtils -from bisect import bisect_left +#from bisect import bisect_left -from PySide import QtCore +from PySide import QtCore, QtGui """Z Depth Correction Dressup. This dressup takes a probe file as input and does bilinear interpolation of the Zdepths to correct for a surface which is not parallel to the milling table/bed. The probe file should conform to the format specified by the linuxcnc G38 probe logging: 9-number coordinate consisting of XYZABCUVW http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g38 """ +LOG_MODULE = PathLog.thisModule() + +if False: + PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE) + PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE) +else: + PathLog.setLevel(PathLog.Level.NOTICE, LOG_MODULE) + + # Qt tanslation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) @@ -45,16 +56,13 @@ def translate(context, text, disambig=None): arccommands = ['G2', 'G3', 'G02', 'G03'] class ObjectDressup: - x_index = 0 - y_index = 0 - values = None - x_length = 0 - y_length = 0 def __init__(self, obj): obj.addProperty("App::PropertyLink", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupAxisMap", "The base path to modify")) obj.addProperty("App::PropertyFile", "probefile", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "The point file from the surface probing.")) obj.Proxy = self + obj.addProperty("Part::PropertyPartShape", "interpSurface", "Path") + obj.setEditorMode('interpSurface', 2) # hide def __getstate__(self): return None @@ -64,88 +72,61 @@ def __setstate__(self, state): def onChanged(self, fp, prop): if str(prop) == "probefile": - self._loadFile(fp.probefile) - - def _bilinearInterpolate(self, x, y): - # local lookups - x_index, y_index, values = self.x_index, self.y_index, self.values - - i = bisect_left(x_index, x) - 1 - j = bisect_left(y_index, y) - 1 - - # fix x index - if i == -1: - x_slice = slice(None, 2) - elif i == self.x_length - 1: - x_slice = slice(-2, None) - else: - x_slice = slice(i, i + 2) - # fix y index - if j == -1: - j = 0 - y_slice = slice(None, 2) - elif j == self.y_length - 1: - j = -2 - y_slice = slice(-2, None) - else: - y_slice = slice(j, j + 2) - - x1, x2 = x_index[x_slice] - y1, y2 = y_index[y_slice] - z11, z12 = values[j][x_slice] - z21, z22 = values[j + 1][x_slice] - - return (z11 * (x2 - x) * (y2 - y) + - z21 * (x - x1) * (y2 - y) + - z12 * (x2 - x) * (y - y1) + - z22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1)) - - - def _loadFile(self, filename): - f1 = open(filename, 'r') + self._loadFile(fp, fp.probefile) + + def _bilinearInterpolate(self, surface, x, y): + + print ('xval:{}, yval:{}'.format(x, y)) + p1 = FreeCAD.Vector(x, y, 100.0) + p2 = FreeCAD.Vector(x, y, -100.0) + + vertical_line = Part.Line(p1, p2) + points, curves = vertical_line.intersectCS(surface) + return points[0].Z + - pointlist = [] - for line in f1.readlines(): - w = line.split() - xval = round(float(w[0]), 3) - yval = round(float(w[1]), 3) - zval = round(float(w[2]), 3) - pointlist.append((xval, yval,zval)) - cols = list(zip(*pointlist)) + def _loadFile(self, obj, filename): + if filename == "": + return + + f1 = open(filename, 'r') - xcolpos = list(sorted(set(cols[0]))) - #ycolpos = list(sorted(set(cols[1]))) - zdict = {x:[] for x in xcolpos} + try: + pointlist = [] + for line in f1.readlines(): + w = line.split() + xval = round(float(w[0]), 2) + yval = round(float(w[1]), 2) + zval = round(float(w[2]), 2) - for (x, y, z) in pointlist: - zdict[x].append(z) + pointlist.append([xval, yval,zval]) - self.values = tuple(tuple(x) for x in [zdict[x] for x in sorted(xcolpos)]) - self.x_index = tuple(sorted(set(cols[0]))) - self.y_index = tuple(sorted(set(cols[1]))) + cols = list(zip(*pointlist)) + yindex = list(sorted(set(cols[1]))) - # sanity check - x_length = len(self.x_index) - y_length = len(self.y_index) + array = [] + for y in yindex: + points = sorted([p for p in pointlist if p[1] == y]) + inner = [] + for p in points: + inner.append(FreeCAD.Vector(p[0],p[1],p[2])) + array.append(inner) - if x_length < 2 or y_length < 2: - raise ValueError("Probe grid must be at least 2x2.") - if y_length != len(self.values): - raise ValueError("Probe grid data must have equal number of rows to y_index.") - if any(x2 - x1 <= 0 for x1, x2 in zip(self.x_index, self.x_index[1:])): - raise ValueError("x_index must be in strictly ascending order!") - if any(y2 - y1 <= 0 for y1, y2 in zip(self.y_index, self.y_index[1:])): - raise ValueError("y_index must be in strictly ascending order!") + intSurf = Part.BSplineSurface() + intSurf.interpolate(array) - self.x_length = x_length - self.y_length = y_length + obj.interpSurface = intSurf.toShape() + except: + raise ValueError("File does not contain appropriate point data") def execute(self, obj): - if self.values is None: #No valid probe data. return unchanged path + if obj.interpSurface.isNull(): #No valid probe data. return unchanged path obj.Path = obj.Base.Path return + surface = obj.interpSurface.toNurbs().Faces[0].Surface + if obj.Base: if obj.Base.isDerivedFrom("Path::Feature"): if obj.Base.Path: @@ -159,20 +140,92 @@ def execute(self, obj): for c in pathlist: #obj.Base.Path.Commands: newparams = dict(c.Parameters) + zval = newparams.pop("Z", currLocation['Z']) currLocation.update(newparams) - remapvar = newparams.pop("Z", None) - if remapvar is not None: - offset = self._bilinearInterpolate(currLocation['X'], currLocation['Y']) + if c.Name in movecommands: + remapvar = currLocation['Z'] + offset = self._bilinearInterpolate(surface, currLocation['X'], currLocation['Y']) newparams["Z"] = remapvar + offset newcommand = Path.Command(c.Name, newparams) newcommandlist.append(newcommand) currLocation.update(newparams) + currLocation['Z'] = zval else: newcommandlist.append(c) path = Path.Path(newcommandlist) obj.Path = path +class TaskPanel: + + def __init__(self, obj): + self.obj = obj + self.form = FreeCADGui.PySideUic.loadUi(":/panels/ZCorrectEdit.ui") + FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupZCorrect", "Edit Z Correction Dress-up")) + self.interpshape = FreeCAD.ActiveDocument.addObject("Part::Feature", "InterpolationSurface") + self.interpshape.Shape = obj.interpSurface + self.interpshape.ViewObject.Transparency = 60 + self.interpshape.ViewObject.ShapeColor = (1.00000,1.00000,0.01961) + self.interpshape.ViewObject.Selectable = False + stock = PathUtils.findParentJob(obj).Stock + self.interpshape.Placement.Base.z = stock.Shape.BoundBox.ZMax + def reject(self): + FreeCAD.ActiveDocument.abortTransaction() + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + + def accept(self): + self.getFields() + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.removeObject(self.interpshape.Name) + FreeCADGui.ActiveDocument.resetEdit() + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + FreeCAD.ActiveDocument.recompute() + + def getFields(self): + self.obj.Proxy.execute(self.obj) + + + def updateUI(self): + + if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG: + for obj in FreeCAD.ActiveDocument.Objects: + if obj.Name.startswith('Shape'): + FreeCAD.ActiveDocument.removeObject(obj.Name) + print('object name %s' % self.obj.Name) + if hasattr(self.obj.Proxy, "shapes"): + PathLog.info("showing shapes attribute") + for shapes in self.obj.Proxy.shapes.itervalues(): + for shape in shapes: + Part.show(shape) + else: + PathLog.info("no shapes attribute found") + + def updateModel(self): + self.getFields() + self.updateUI() + FreeCAD.ActiveDocument.recompute() + + def setFields(self): + self.form.ProbePointFileName.setText(self.obj.probefile) + + self.updateUI() + + def open(self): + pass + def setupUi(self): + self.setFields() + # now that the form is filled, setup the signal handlers + self.form.ProbePointFileName.editingFinished.connect(self.updateModel) + self.form.SetProbePointFileName.clicked.connect(self.SetProbePointFileName) + + def SetProbePointFileName(self): + filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("Path_Probe", "Select Probe Point File"), None, translate("Path_Probe", "All Files (*.*)")) + if filename and filename[0]: + self.obj.probefile = str(filename[0]) + self.setFields() + class ViewProviderDressup: @@ -189,12 +242,18 @@ def attach(self, vobj): if g.Name == self.obj.Base.Name: group.remove(g) i.Group = group - # FreeCADGui.ActiveDocument.getObject(obj.Base.Name).Visibility = False return def claimChildren(self): return [self.obj.Base] + def setEdit(self, vobj, mode=0): + FreeCADGui.Control.closeDialog() + panel = TaskPanel(vobj.Object) + FreeCADGui.Control.showDialog(panel) + panel.setupUi() + return True + def __getstate__(self): return None diff --git a/src/Mod/Path/PathScripts/PathProbe.py b/src/Mod/Path/PathScripts/PathProbe.py index eb409d833d41..c8ff9869504d 100644 --- a/src/Mod/Path/PathScripts/PathProbe.py +++ b/src/Mod/Path/PathScripts/PathProbe.py @@ -83,8 +83,8 @@ def opExecute(self, obj): self.commandlist.append(Path.Command(openstring)) self.commandlist.append(Path.Command("G0", {"Z":obj.ClearanceHeight.Value})) - for x in self.nextpoint(bb.XMin, bb.XMax, obj.PointCountX): - for y in self.nextpoint(bb.YMin, bb.YMax, obj.PointCountY): + for y in self.nextpoint(bb.YMin, bb.YMax, obj.PointCountY): + for x in self.nextpoint(bb.XMin, bb.XMax, obj.PointCountX): self.commandlist.append(Path.Command("G0", {"X":x + obj.Xoffset.Value, "Y":y + obj.Yoffset.Value, "Z":obj.SafeHeight.Value})) self.commandlist.append(Path.Command("G38.2",{"Z":obj.FinalDepth.Value, "F":obj.ToolController.VertFeed.Value})) self.commandlist.append(Path.Command("G0", {"Z":obj.SafeHeight.Value})) From 040324c6ae4c0da5b6cbe3b313978436808c3d1a Mon Sep 17 00:00:00 2001 From: sliptonic Date: Sun, 7 Oct 2018 17:19:05 -0500 Subject: [PATCH 6/7] discretizing --- .../Path/PathScripts/PathDressupZCorrect.py | 55 ++++++++++++------- src/Mod/Path/PathScripts/PathGeom.py | 7 +++ src/Mod/Path/PathScripts/PathGuiInit.py | 2 + 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupZCorrect.py b/src/Mod/Path/PathScripts/PathDressupZCorrect.py index 2be8337f4b7e..65388caa216a 100644 --- a/src/Mod/Path/PathScripts/PathDressupZCorrect.py +++ b/src/Mod/Path/PathScripts/PathDressupZCorrect.py @@ -29,6 +29,7 @@ import FreeCADGui import Part import Path +import PathScripts.PathGeom as PathGeom import PathScripts.PathLog as PathLog import PathScripts.PathUtils as PathUtils #from bisect import bisect_left @@ -40,7 +41,7 @@ LOG_MODULE = PathLog.thisModule() -if False: +if True: PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE) PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE) else: @@ -59,11 +60,14 @@ class ObjectDressup: def __init__(self, obj): obj.addProperty("App::PropertyLink", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupAxisMap", "The base path to modify")) - obj.addProperty("App::PropertyFile", "probefile", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "The point file from the surface probing.")) + obj.addProperty("App::PropertyFile", "probefile", "ProbeData", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "The point file from the surface probing.")) obj.Proxy = self obj.addProperty("Part::PropertyPartShape", "interpSurface", "Path") obj.setEditorMode('interpSurface', 2) # hide - + obj.addProperty("App::PropertyDistance", "ArcInterpolate", "Interpolate", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrect", "Deflection distance for arc interpolation")) + obj.addProperty("App::PropertyDistance", "SegInterpolate", "Interpolate", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrectp", "break segments into smaller segments of this length.")) + obj.ArcInterpolate = 0.1 + obj.SegInterpolate = 1.0 def __getstate__(self): return None @@ -76,7 +80,6 @@ def onChanged(self, fp, prop): def _bilinearInterpolate(self, surface, x, y): - print ('xval:{}, yval:{}'.format(x, y)) p1 = FreeCAD.Vector(x, y, 100.0) p2 = FreeCAD.Vector(x, y, -100.0) @@ -84,7 +87,6 @@ def _bilinearInterpolate(self, surface, x, y): points, curves = vertical_line.intersectCS(surface) return points[0].Z - def _loadFile(self, obj, filename): if filename == "": return @@ -100,9 +102,12 @@ def _loadFile(self, obj, filename): zval = round(float(w[2]), 2) pointlist.append([xval, yval,zval]) + PathLog.debug(pointlist) cols = list(zip(*pointlist)) + PathLog.debug("cols: {}".format(cols)) yindex = list(sorted(set(cols[1]))) + PathLog.debug("yindex: {}".format(yindex)) array = [] for y in yindex: @@ -121,6 +126,10 @@ def _loadFile(self, obj, filename): def execute(self, obj): + + sampleD = obj.SegInterpolate.Value + curveD = obj.ArcInterpolate.Value + if obj.interpSurface.isNull(): #No valid probe data. return unchanged path obj.Path = obj.Base.Path return @@ -131,28 +140,36 @@ def execute(self, obj): if obj.Base.isDerivedFrom("Path::Feature"): if obj.Base.Path: if obj.Base.Path.Commands: - pp = obj.Base.Path.Commands - # process the path + pathlist = obj.Base.Path.Commands - pathlist = pp newcommandlist = [] currLocation = {'X':0,'Y':0,'Z':0, 'F': 0} for c in pathlist: #obj.Base.Path.Commands: + PathLog.debug(c) + PathLog.debug(" curLoc:{}".format(currLocation)) newparams = dict(c.Parameters) - zval = newparams.pop("Z", currLocation['Z']) - currLocation.update(newparams) + zval = newparams.get("Z", currLocation['Z']) if c.Name in movecommands: - remapvar = currLocation['Z'] - offset = self._bilinearInterpolate(surface, currLocation['X'], currLocation['Y']) - newparams["Z"] = remapvar + offset - newcommand = Path.Command(c.Name, newparams) - newcommandlist.append(newcommand) - currLocation.update(newparams) - currLocation['Z'] = zval + curVec = FreeCAD.Vector(currLocation['X'], currLocation['Y'], currLocation['Z']) + arcwire = PathGeom.edgeForCmd(c, curVec) + if arcwire is None: + continue + if c.Name in arccommands: + pointlist = arcwire.discretize(Deflection=curveD) + else: + pointlist = arcwire.discretize(Number=int(arcwire.Length / sampleD)) + for point in pointlist: + offset = self._bilinearInterpolate(surface, point.x, point.y) + newcommand = Path.Command("G1", {'X':point.x, 'Y':point.y, 'Z':point.z + offset}) + newcommandlist.append(newcommand) + currLocation.update(newcommand.Parameters) + currLocation['Z'] = zval + else: + # Non Feed Command newcommandlist.append(c) - + currLocation.update(c.Parameters) path = Path.Path(newcommandlist) obj.Path = path @@ -162,7 +179,7 @@ def __init__(self, obj): self.obj = obj self.form = FreeCADGui.PySideUic.loadUi(":/panels/ZCorrectEdit.ui") FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupZCorrect", "Edit Z Correction Dress-up")) - self.interpshape = FreeCAD.ActiveDocument.addObject("Part::Feature", "InterpolationSurface") + self.interpshape = FreeCAD.ActiveDocument.addObject("Part::Feature", "InterpolationSurface") self.interpshape.Shape = obj.interpSurface self.interpshape.ViewObject.Transparency = 60 self.interpshape.ViewObject.ShapeColor = (1.00000,1.00000,0.01961) diff --git a/src/Mod/Path/PathScripts/PathGeom.py b/src/Mod/Path/PathScripts/PathGeom.py index 2fbaae4a4084..fba8d1ddb1f6 100644 --- a/src/Mod/Path/PathScripts/PathGeom.py +++ b/src/Mod/Path/PathScripts/PathGeom.py @@ -313,6 +313,9 @@ def edgeForCmd(cmd, startPoint): """edgeForCmd(cmd, startPoint). Returns an Edge representing the given command, assuming a given startPoint.""" + PathLog.debug("cmd: {}".format(cmd)) + PathLog.debug("startpoint {}".format(startPoint)) + endPoint = commandEndPoint(cmd, startPoint) if (cmd.Name in CmdMoveStraight) or (cmd.Name in CmdMoveRapid): if pointsCoincide(startPoint, endPoint): @@ -343,6 +346,10 @@ def edgeForCmd(cmd, startPoint): if isRoughly(startPoint.z, endPoint.z): midPoint = center + Vector(math.cos(angle), math.sin(angle), 0) * R PathLog.debug("arc: (%.2f, %.2f) -> (%.2f, %.2f) -> (%.2f, %.2f)" % (startPoint.x, startPoint.y, midPoint.x, midPoint.y, endPoint.x, endPoint.y)) + PathLog.debug("StartPoint:{}".format(startPoint)) + PathLog.debug("MidPoint:{}".format(midPoint)) + PathLog.debug("EndPoint:{}".format(endPoint)) + return Part.Edge(Part.Arc(startPoint, midPoint, endPoint)) # It's a Helix diff --git a/src/Mod/Path/PathScripts/PathGuiInit.py b/src/Mod/Path/PathScripts/PathGuiInit.py index 57f302fc3a28..29272c0382e5 100644 --- a/src/Mod/Path/PathScripts/PathGuiInit.py +++ b/src/Mod/Path/PathScripts/PathGuiInit.py @@ -51,6 +51,7 @@ def Startup(): from PathScripts import PathDressupPathBoundaryGui from PathScripts import PathDressupTagGui from PathScripts import PathDressupLeadInOut + from PathScripts import PathDressupZCorrect from PathScripts import PathDrillingGui from PathScripts import PathEngraveGui from PathScripts import PathFixture @@ -61,6 +62,7 @@ def Startup(): from PathScripts import PathPocketGui from PathScripts import PathPocketShapeGui from PathScripts import PathPost + from PathScripts import PathProbeGui from PathScripts import PathProfileContourGui from PathScripts import PathProfileEdgesGui from PathScripts import PathProfileFacesGui From 4e91020c1f25cc88bb3435d5b90693ff75974da1 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Fri, 6 Mar 2020 13:20:50 -0600 Subject: [PATCH 7/7] fix error when discretize N=1 cleanup InitGui to add Path_Probe command Probe setupsheet --- src/Mod/Path/InitGui.py | 2 +- .../Path/PathScripts/PathDressupZCorrect.py | 38 +++++++++++-------- src/Mod/Path/PathScripts/PathProbe.py | 14 +++---- src/Mod/Path/PathScripts/PathProbeGui.py | 17 ++++----- .../PathScripts/PathSetupSheetOpPrototype.py | 1 + 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 64a48bc54a90..dc638a929952 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -87,7 +87,7 @@ def Initialize(self): # build commands list projcmdlist = ["Path_Job", "Path_Post"] toolcmdlist = ["Path_Inspect", "Path_Simulator", "Path_ToolLibraryEdit", "Path_SelectLoop", "Path_OpActiveToggle"] - prepcmdlist = ["Path_Fixture", "Path_Comment", "Path_Stop", "Path_Custom"] + prepcmdlist = ["Path_Fixture", "Path_Comment", "Path_Stop", "Path_Custom", "Path_Probe"] twodopcmdlist = ["Path_Contour", "Path_Profile_Faces", "Path_Profile_Edges", "Path_Pocket_Shape", "Path_Drilling", "Path_MillFace", "Path_Helix", "Path_Adaptive" ] threedopcmdlist = ["Path_Pocket_3D"] engravecmdlist = ["Path_Engrave", "Path_Deburr"] diff --git a/src/Mod/Path/PathScripts/PathDressupZCorrect.py b/src/Mod/Path/PathScripts/PathDressupZCorrect.py index 65388caa216a..c0354518297e 100644 --- a/src/Mod/Path/PathScripts/PathDressupZCorrect.py +++ b/src/Mod/Path/PathScripts/PathDressupZCorrect.py @@ -32,7 +32,6 @@ import PathScripts.PathGeom as PathGeom import PathScripts.PathLog as PathLog import PathScripts.PathUtils as PathUtils -#from bisect import bisect_left from PySide import QtCore, QtGui @@ -41,7 +40,7 @@ LOG_MODULE = PathLog.thisModule() -if True: +if False: PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE) PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE) else: @@ -52,10 +51,12 @@ def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) + movecommands = ['G1', 'G01', 'G2', 'G02', 'G3', 'G03'] rapidcommands = ['G0', 'G00'] arccommands = ['G2', 'G3', 'G02', 'G03'] + class ObjectDressup: def __init__(self, obj): @@ -68,6 +69,7 @@ def __init__(self, obj): obj.addProperty("App::PropertyDistance", "SegInterpolate", "Interpolate", QtCore.QT_TRANSLATE_NOOP("Path_DressupZCorrectp", "break segments into smaller segments of this length.")) obj.ArcInterpolate = 0.1 obj.SegInterpolate = 1.0 + def __getstate__(self): return None @@ -91,7 +93,7 @@ def _loadFile(self, obj, filename): if filename == "": return - f1 = open(filename, 'r') + f1 = open(filename, 'r') try: pointlist = [] @@ -101,7 +103,7 @@ def _loadFile(self, obj, filename): yval = round(float(w[1]), 2) zval = round(float(w[2]), 2) - pointlist.append([xval, yval,zval]) + pointlist.append([xval, yval, zval]) PathLog.debug(pointlist) cols = list(zip(*pointlist)) @@ -114,23 +116,22 @@ def _loadFile(self, obj, filename): points = sorted([p for p in pointlist if p[1] == y]) inner = [] for p in points: - inner.append(FreeCAD.Vector(p[0],p[1],p[2])) + inner.append(FreeCAD.Vector(p[0], p[1], p[2])) array.append(inner) intSurf = Part.BSplineSurface() intSurf.interpolate(array) obj.interpSurface = intSurf.toShape() - except: + except Exception: raise ValueError("File does not contain appropriate point data") - def execute(self, obj): sampleD = obj.SegInterpolate.Value curveD = obj.ArcInterpolate.Value - if obj.interpSurface.isNull(): #No valid probe data. return unchanged path + if obj.interpSurface.isNull(): # No valid probe data. return unchanged path obj.Path = obj.Base.Path return @@ -143,9 +144,9 @@ def execute(self, obj): pathlist = obj.Base.Path.Commands newcommandlist = [] - currLocation = {'X':0,'Y':0,'Z':0, 'F': 0} + currLocation = {'X': 0, 'Y': 0, 'Z': 0, 'F': 0} - for c in pathlist: #obj.Base.Path.Commands: + for c in pathlist: PathLog.debug(c) PathLog.debug(" curLoc:{}".format(currLocation)) newparams = dict(c.Parameters) @@ -156,12 +157,16 @@ def execute(self, obj): if arcwire is None: continue if c.Name in arccommands: - pointlist = arcwire.discretize(Deflection=curveD) + pointlist = arcwire.discretize(Deflection=curveD) else: - pointlist = arcwire.discretize(Number=int(arcwire.Length / sampleD)) + disc_number = int(arcwire.Length / sampleD) + if disc_number > 1: + pointlist = arcwire.discretize(Number=int(arcwire.Length / sampleD)) + else: + pointlist = [v.Point for v in arcwire.Vertexes] for point in pointlist: offset = self._bilinearInterpolate(surface, point.x, point.y) - newcommand = Path.Command("G1", {'X':point.x, 'Y':point.y, 'Z':point.z + offset}) + newcommand = Path.Command("G1", {'X': point.x, 'Y': point.y, 'Z': point.z + offset}) newcommandlist.append(newcommand) currLocation.update(newcommand.Parameters) currLocation['Z'] = zval @@ -173,6 +178,7 @@ def execute(self, obj): path = Path.Path(newcommandlist) obj.Path = path + class TaskPanel: def __init__(self, obj): @@ -182,10 +188,11 @@ def __init__(self, obj): self.interpshape = FreeCAD.ActiveDocument.addObject("Part::Feature", "InterpolationSurface") self.interpshape.Shape = obj.interpSurface self.interpshape.ViewObject.Transparency = 60 - self.interpshape.ViewObject.ShapeColor = (1.00000,1.00000,0.01961) + self.interpshape.ViewObject.ShapeColor = (1.00000, 1.00000, 0.01961) self.interpshape.ViewObject.Selectable = False stock = PathUtils.findParentJob(obj).Stock self.interpshape.Placement.Base.z = stock.Shape.BoundBox.ZMax + def reject(self): FreeCAD.ActiveDocument.abortTransaction() FreeCADGui.Control.closeDialog() @@ -203,7 +210,6 @@ def accept(self): def getFields(self): self.obj.Proxy.execute(self.obj) - def updateUI(self): if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG: @@ -231,6 +237,7 @@ def setFields(self): def open(self): pass + def setupUi(self): self.setFields() # now that the form is filled, setup the signal handlers @@ -285,6 +292,7 @@ def onDelete(self, arg1=None, arg2=None): arg1.Object.Base = None return True + class CommandPathDressup: def GetResources(self): diff --git a/src/Mod/Path/PathScripts/PathProbe.py b/src/Mod/Path/PathScripts/PathProbe.py index c8ff9869504d..c5e8b34f81b9 100644 --- a/src/Mod/Path/PathScripts/PathProbe.py +++ b/src/Mod/Path/PathScripts/PathProbe.py @@ -30,7 +30,6 @@ import PathScripts.PathOp as PathOp import PathScripts.PathUtils as PathUtils -#from PathScripts.PathUtils import waiting_effects from PySide import QtCore __title__ = "Path Probing Operation" @@ -81,25 +80,26 @@ def opExecute(self, obj): openstring = '(PROBEOPEN {})'.format(obj.OutputFileName) self.commandlist.append(Path.Command(openstring)) - self.commandlist.append(Path.Command("G0", {"Z":obj.ClearanceHeight.Value})) + self.commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) for y in self.nextpoint(bb.YMin, bb.YMax, obj.PointCountY): for x in self.nextpoint(bb.XMin, bb.XMax, obj.PointCountX): - self.commandlist.append(Path.Command("G0", {"X":x + obj.Xoffset.Value, "Y":y + obj.Yoffset.Value, "Z":obj.SafeHeight.Value})) - self.commandlist.append(Path.Command("G38.2",{"Z":obj.FinalDepth.Value, "F":obj.ToolController.VertFeed.Value})) - self.commandlist.append(Path.Command("G0", {"Z":obj.SafeHeight.Value})) + self.commandlist.append(Path.Command("G0", {"X": x + obj.Xoffset.Value, "Y": y + obj.Yoffset.Value, "Z": obj.SafeHeight.Value})) + self.commandlist.append(Path.Command("G38.2", {"Z": obj.FinalDepth.Value, "F": obj.ToolController.VertFeed.Value})) + self.commandlist.append(Path.Command("G0", {"Z": obj.SafeHeight.Value})) self.commandlist.append(Path.Command("(PROBECLOSE)")) - def opSetDefaultValues(self, obj, job): '''opSetDefaultValues(obj, job) ... set default value for RetractHeight''' + def SetupProperties(): setup = ['Xoffset', 'Yoffset', 'PointCountX', 'PointCountY', 'OutputFileName'] return setup -def Create(name, obj = None): + +def Create(name, obj=None): '''Create(name) ... Creates and returns a Probing operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) diff --git a/src/Mod/Path/PathScripts/PathProbeGui.py b/src/Mod/Path/PathScripts/PathProbeGui.py index 767bd1c57330..9da563fa4887 100644 --- a/src/Mod/Path/PathScripts/PathProbeGui.py +++ b/src/Mod/Path/PathScripts/PathProbeGui.py @@ -35,10 +35,12 @@ __url__ = "http://www.freecadweb.org" __doc__ = "Probing operation page controller and command implementation." + # Qt tanslation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) + class TaskPanelOpPage(PathOpGui.TaskPanelPage): '''Page controller class for the Probing operation.''' @@ -73,7 +75,6 @@ def getSignalsForUpdate(self, obj): signals.append(self.form.OutputFileName.editingFinished) signals.append(self.form.Xoffset.valueChanged) signals.append(self.form.Yoffset.valueChanged) - #signals.append(self.form.SetOutputFileName.clicked) self.form.SetOutputFileName.clicked.connect(self.SetOutputFileName) return signals @@ -83,13 +84,11 @@ def SetOutputFileName(self): self.obj.OutputFileName = str(filename[0]) self.setFields(self.obj) -Command = PathOpGui.SetupOperation('Probe', - PathProbe.Create, - TaskPanelOpPage, - 'Path-Probe', - QtCore.QT_TRANSLATE_NOOP("Probe", "Probe"), - QtCore.QT_TRANSLATE_NOOP("Probe", "Create a Probing Grid from a job stock"), - PathProbe.SetupProperties) -FreeCAD.Console.PrintLog("Loading PathProbeGui... done\n") +Command = PathOpGui.SetupOperation('Probe', PathProbe.Create, TaskPanelOpPage, + 'Path-Probe', + QtCore.QT_TRANSLATE_NOOP("Probe", "Probe"), + QtCore.QT_TRANSLATE_NOOP("Probe", "Create a Probing Grid from a job stock"), + PathProbe.SetupProperties) +FreeCAD.Console.PrintLog("Loading PathProbeGui... done\n") diff --git a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py index fa99cbc72b4f..d089dd7f7af6 100644 --- a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py +++ b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py @@ -149,6 +149,7 @@ class OpPrototype(object): 'App::PropertyBool': PropertyBool, 'App::PropertyDistance': PropertyDistance, 'App::PropertyEnumeration': PropertyEnumeration, + 'App::PropertyFile': PropertyString, 'App::PropertyFloat': PropertyFloat, 'App::PropertyFloatConstraint': Property, 'App::PropertyFloatList': Property,