diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc
index 184bbb6266..ca09e140b8 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 0000000000..36d58f0c6f
--- /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 5a49d4bc8a..2be8337f4b 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 @@ 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
+ obj.addProperty("Part::PropertyPartShape", "interpSurface", "Path")
+ obj.setEditorMode('interpSurface', 2) # hide
def __getstate__(self):
return None
@@ -64,88 +72,61 @@ class ObjectDressup:
def onChanged(self, fp, prop):
if str(prop) == "probefile":
- self._loadFile(fp.probefile)
+ self._loadFile(fp, fp.probefile)
- def _bilinearInterpolate(self, x, y):
- # local lookups
- x_index, y_index, values = self.x_index, self.y_index, self.values
+ def _bilinearInterpolate(self, surface, x, y):
- i = bisect_left(x_index, x) - 1
- j = bisect_left(y_index, y) - 1
+ print ('xval:{}, yval:{}'.format(x, y))
+ p1 = FreeCAD.Vector(x, y, 100.0)
+ p2 = FreeCAD.Vector(x, y, -100.0)
- # 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))
+ vertical_line = Part.Line(p1, p2)
+ points, curves = vertical_line.intersectCS(surface)
+ return points[0].Z
- def _loadFile(self, filename):
+ def _loadFile(self, obj, filename):
+ if filename == "":
+ return
+
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))
+ 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)
- xcolpos = list(sorted(set(cols[0])))
- #ycolpos = list(sorted(set(cols[1])))
- zdict = {x:[] for x in xcolpos}
+ pointlist.append([xval, yval,zval])
- for (x, y, z) in pointlist:
- zdict[x].append(z)
+ cols = list(zip(*pointlist))
+ yindex = list(sorted(set(cols[1])))
- 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])))
+ 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)
- # sanity check
- x_length = len(self.x_index)
- y_length = len(self.y_index)
+ intSurf = Part.BSplineSurface()
+ intSurf.interpolate(array)
- 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
+ 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 @@ class ObjectDressup:
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 @@ class ViewProviderDressup:
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 eb409d833d..c8ff986950 100644
--- a/src/Mod/Path/PathScripts/PathProbe.py
+++ b/src/Mod/Path/PathScripts/PathProbe.py
@@ -83,8 +83,8 @@ class ObjectProbing(PathOp.ObjectOp):
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}))