From 72dc30c218a301f7c6b43f505b38f89f40137f62 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Mon, 3 Jun 2019 03:33:06 -0500 Subject: [PATCH] Improve 4th-axis integration Improvements to 4th-axis readiness for other PathAreaOp based tools in PathWB. Add guiMessage() method for rendering GUI messages to user. --- src/Mod/Path/PathScripts/PathAreaOp.py | 99 +++++++++++++++++--------- 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathAreaOp.py b/src/Mod/Path/PathScripts/PathAreaOp.py index a139b0eac3..8dba641a11 100644 --- a/src/Mod/Path/PathScripts/PathAreaOp.py +++ b/src/Mod/Path/PathScripts/PathAreaOp.py @@ -23,8 +23,8 @@ # *************************************************************************** # SCRIPT NOTES: -# - Need to add "UseRotation" property to task window UI, and attach appropriate onChange event handler -# - Consult FC community about wording for "UseRotation" property +# - Need to add "EnableRotation" property to task window UI, and attach appropriate onChange event handler +# - Consult FC community about wording for "EnableRotation" property # - FUTURE: Relocate rotational calculations to Job setup tool, creating a Machine section # with axis & rotation toggles and associated min/max values @@ -44,8 +44,8 @@ __url__ = "http://www.freecadweb.org" __doc__ = "Base class and properties for Path.Area based operations." __contributors__ = "mlampert [FreeCAD], russ4262 (Russell Johnson)" __createdDate__ = "2017" -__scriptVersion__ = "1h testing" -__lastModified__ = "2019-05-03 10:52 CST" +__scriptVersion__ = "2b testing" +__lastModified__ = "2019-06-03 03:18 CST" if False: PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) @@ -77,6 +77,7 @@ class ObjectOp(PathOp.ObjectOp): '''opFeatures(obj) ... returns the base features supported by all Path.Area based operations. The standard feature list is OR'ed with the return value of areaOpFeatures(). Do not overwrite, implement areaOpFeatures(obj) instead.''' + # return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureStepDown | PathOp.FeatureHeights | PathOp.FeatureStartPoint | self.areaOpFeatures(obj) | PathOp.FeatureRotation return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureStepDown | PathOp.FeatureHeights | PathOp.FeatureStartPoint | self.areaOpFeatures(obj) def areaOpFeatures(self, obj): @@ -102,9 +103,9 @@ class ObjectOp(PathOp.ObjectOp): self.initAreaOp(obj) def setupAdditionalProperties(self, obj): - if not hasattr(obj, 'UseRotation'): - obj.addProperty("App::PropertyEnumeration", "UseRotation", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Use rotation to gain access to pockets/areas.")) - obj.UseRotation = ['Off', 'A(x)', 'B(y)', 'A & B'] + if not hasattr(obj, 'EnableRotation'): + obj.addProperty("App::PropertyEnumeration", "EnableRotation", "Rotation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Enable rotation to gain access to pockets/areas not normal to Z axis.")) + obj.EnableRotation = ['Off', 'A(x)', 'B(y)', 'A & B'] def initAreaOp(self, obj): '''initAreaOp(obj) ... overwrite if the receiver class needs initialisation. @@ -182,7 +183,7 @@ class ObjectOp(PathOp.ObjectOp): maxDep = 1.0 minDep = 0.0 - if obj.UseRotation == 'Off': + if obj.EnableRotation == 'Off': bb = job.Stock.Shape.BoundBox maxDep = bb.ZMax minDep = bb.ZMin @@ -211,7 +212,12 @@ class ObjectOp(PathOp.ObjectOp): PathLog.debug("-initFinalDepth" + str(self.initFinalDepth)) PathLog.debug("-initOpFinalDepth" + str(self.initOpFinalDepth)) - obj.UseRotation = 'Off' + # Initial setting for EnableRotation is taken from Job settings/SetupSheet + # User may override on per-operation basis as needed. + if hasattr(job.SetupSheet, 'SetupEnableRotation'): + obj.EnableRotation = job.SetupSheet.SetupEnableRotation + else: + obj.EnableRotation = 'Off' self.areaOpSetDefaultValues(obj, job) @@ -292,8 +298,9 @@ class ObjectOp(PathOp.ObjectOp): # Instantiate class variables for operation reference self.rotateFlag = False - self.modelName = None self.leadIn = 2.0 # safOfset / 2.0 + self.cloneNames = [] + self.guiMsgs = [] # list of message tuples (title, msg) to be displayed in GUI # Initialize depthparams finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0 @@ -307,7 +314,7 @@ class ObjectOp(PathOp.ObjectOp): user_depths=None) # Recalculate operation heights for rotational operation - if obj.UseRotation != 'Off': + if obj.EnableRotation != 'Off': # Calculate operation heights based upon rotation radii opHeights = self.opDetermineRotationRadii(obj) # return is [(xRotRad, yRotRad, zRotRad), (clrOfst, safOfst)] (xRotRad, yRotRad, zRotRad) = opHeights[0] @@ -339,6 +346,7 @@ class ObjectOp(PathOp.ObjectOp): for shp in aOS: if len(shp) == 2: (fc, iH) = shp + # fc, iH, sub, angle, axis tup = fc, iH, 'notPocket', 0.0, 'X' shapes.append(tup) else: @@ -375,7 +383,7 @@ class ObjectOp(PathOp.ObjectOp): try: (pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim) ppCmds = pp.Commands - if obj.UseRotation != 'Off' and self.rotateFlag is True: + if obj.EnableRotation != 'Off' and self.rotateFlag is True: # Rotate model to index for cut axisOfRot = 'A' if axis == 'Y': @@ -403,9 +411,15 @@ class ObjectOp(PathOp.ObjectOp): self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) self.commandlist.append(Path.Command('G0', {'A': 0.0, 'F': self.axialFeed})) self.commandlist.append(Path.Command('G0', {'B': 0.0, 'F': self.axialFeed})) - FreeCAD.ActiveDocument.getObject(self.modelName).purgeTouched() + + if len(self.cloneNames) > 0: + for cn in self.cloneNames: + if FreeCAD.ActiveDocument.getObject(cn): + FreeCAD.ActiveDocument.removeObject(cn) + PathLog.debug("Removed temp clone: " + cn) PathLog.debug("obj.Name: " + str(obj.Name)) + self.guiMessage('title', None, show=True) return sims def areaOpRetractTool(self, obj): @@ -450,14 +464,14 @@ class ObjectOp(PathOp.ObjectOp): else: zlim = bb.ZMax - if obj.UseRotation != 'B(y)': + if obj.EnableRotation != 'B(y)': # Rotation is around X-axis, cutter moves along same axis if math.fabs(bb.YMin) > math.fabs(bb.YMax): ylim = bb.YMin else: ylim = bb.YMax - if obj.UseRotation != 'A(x)': + if obj.EnableRotation != 'A(x)': # Rotation is around Y-axis, cutter moves along same axis if math.fabs(bb.XMin) > math.fabs(bb.XMax): xlim = bb.XMin @@ -473,9 +487,11 @@ class ObjectOp(PathOp.ObjectOp): return [(xRotRad, yRotRad, zRotRad), (clrOfst, safOfst)] - def pocketRotationAnalysis(self, obj, objRef, sub, prnt): + def pocketRotationAnalysis(self, obj, face, prnt): + # def pocketRotationAnalysis(self, obj, objRef, sub, prnt): '''pocketRotationAnalysis(self, obj, objRef, sub, prnt) Determine X and Y independent rotation necessary to make normalAt = Z=1 (0,0,1) ''' + PathLog.track() rtn = False axis = 'X' @@ -483,7 +499,7 @@ class ObjectOp(PathOp.ObjectOp): angle = 500.0 zTol = 1.0E-9 rndTol = 1.0 - zTol - testId = "pocketRotationAnalysis() in PathAreaOp.py" + praInfo = "pocketRotationAnalysis() in PathAreaOp.py" def roundRoughValues(val, zTol, rndTol): # Convert VALxe-15 numbers to zero @@ -495,19 +511,19 @@ class ObjectOp(PathOp.ObjectOp): else: return val - face = objRef.Shape.getElement(sub) + # face = objRef.Shape.getElement(sub) norm = face.normalAt(0, 0) nX = roundRoughValues(norm.x, zTol, rndTol) nY = roundRoughValues(norm.y, zTol, rndTol) nZ = roundRoughValues(norm.z, zTol, rndTol) - testId += "\n -normalAt(0,0): " + str(nX) + ", " + str(nY) + ", " + str(nZ) + praInfo += "\n -normalAt(0,0): " + str(nX) + ", " + str(nY) + ", " + str(nZ) surf = face.Surface.Axis saX = roundRoughValues(surf.x, zTol, rndTol) saY = roundRoughValues(surf.y, zTol, rndTol) saZ = roundRoughValues(surf.z, zTol, rndTol) - testId += "\n -Surface.Axis: " + str(saX) + ", " + str(saY) + ", " + str(saZ) + praInfo += "\n -Surface.Axis: " + str(saX) + ", " + str(saY) + ", " + str(saZ) # Determine rotation needed and current orientation if saX == 0.0: @@ -518,7 +534,7 @@ class ObjectOp(PathOp.ObjectOp): elif saZ == -1.0: angle = -180.0 else: - testId += "_else_X" + str(saZ) + praInfo += "_else_X" + str(saZ) elif saY == 1.0: orientation = "Y" angle = 90.0 @@ -537,16 +553,16 @@ class ObjectOp(PathOp.ObjectOp): elif saX == -1.0: angle = 90.0 else: - testId += "_else_X" + str(saX) + praInfo += "_else_X" + str(saX) else: orientation = "X" ratio = saX / saZ angle = math.degrees(math.atan(ratio)) if ratio < 0.0: - testId += " NEG-ratio" - angle -= 90 + praInfo += " NEG-ratio" + # angle -= 90 else: - testId += " POS-ratio" + praInfo += " POS-ratio" angle = -1 * angle if saX < 0.0: angle = angle + 180.0 @@ -566,16 +582,18 @@ class ObjectOp(PathOp.ObjectOp): if nX != 0.0: angle = -1 * angle + # Enforce enabled rotation in settings if orientation == 'Y': axis = 'X' - if obj.UseRotation == 'B(y)': # Axis disabled + if obj.EnableRotation == 'B(y)': # Axis disabled angle = 500.0 else: axis = 'Y' - if obj.UseRotation == 'A(x)': # Axis disabled + if obj.EnableRotation == 'A(x)': # Axis disabled angle = 500.0 if angle != 500.0 and angle != 0.0: + praInfo += "\n - ... rotation triggered" self.rotateFlag = True rtn = True if obj.ReverseDirection is True: @@ -583,11 +601,28 @@ class ObjectOp(PathOp.ObjectOp): angle = angle + 180.0 else: angle = angle - 180.0 - testId += "\n - ... rotation triggered" else: - testId += "\n - ... NO rotation triggered" + praInfo += "\n - ... NO rotation triggered" - testId += "\n -Suggested rotation: angle: " + str(angle) + ", axis: " + str(axis) + praInfo += "\n -Suggested rotation: angle: " + str(angle) + ", axis: " + str(axis) if prnt is True: - PathLog.debug("testId: " + testId) - return (rtn, angle, axis) + # PathLog.info("praInfo: " + str(praInfo)) + PathLog.debug("praInfo: " + str(praInfo)) + return (rtn, angle, axis, praInfo) + + def guiMessage(self, title, msg, show=False): + if msg is not None: + self.guiMsgs.append((title, msg)) + if show is True: + if FreeCAD.GuiUp and len(self.guiMsgs) > 0: + # self.guiMsgs.pop(0) # remove formatted place holder. + from PySide.QtGui import QMessageBox + # from PySide import QtGui + for entry in self.guiMsgs: + (title, msg) = entry + QMessageBox.warning(None, title, msg) + # QtGui.QMessageBox.warning(None, title, msg) + self.guiMsgs = [] # Reset messages + return True + return False +