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.
This commit is contained in:
Russell Johnson
2019-06-03 03:33:06 -05:00
parent 4960cfcd22
commit 72dc30c218

View File

@@ -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