From 59a9ab9bc06a83c40e70e01bc6bdd3075679e7ed Mon Sep 17 00:00:00 2001 From: sliptonic Date: Sun, 16 Sep 2018 16:04:26 -0500 Subject: [PATCH] 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 098bb114dd..1a51ebc872 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 b461c8cdf6..184bbb6266 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 0000000000..63eae06c30 --- /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 0000000000..c2c9a27309 --- /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 0657fac577..64a48bc54a 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -92,7 +92,7 @@ class PathWorkbench (Workbench): 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 0000000000..ecb9dc43e3 --- /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 0000000000..4bb66b1261 --- /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 0000000000..eba3996517 --- /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 acb9c473e2..386ff1c29c 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -167,13 +167,17 @@ class ADAPTIVEGate(PathBaseGate): 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():