Misc. fixes
Clean up code, improve comments, improve user feedback. Improve temp clone management. Correct depth issue with self.finDep Add comment descriptions for new methods Clean up comments and contribution information Initiate recognition of type()==SurfaceOfExtrusion Adopt standard PathGeom.isRoughly() and .Tolerance rather than idependent precision settings
This commit is contained in:
@@ -21,22 +21,31 @@
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Additional modifications and contributions beginning 2019 *
|
||||
# * Focus: 4th-axis integration *
|
||||
# * by Russell Johnson <russ4262@gmail.com> *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
# SCRIPT NOTES:
|
||||
# - FUTURE: Relocate rotational calculations to Job setup tool, creating a Machine section
|
||||
# with axis & rotation toggles and associated min/max values
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import Draft
|
||||
import math
|
||||
|
||||
# from PathScripts.PathUtils import waiting_effects
|
||||
from PySide import QtCore
|
||||
import math
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
|
||||
|
||||
__title__ = "Base class for PathArea based operations."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
@@ -44,8 +53,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__ = "2f testing"
|
||||
__lastModified__ = "2019-06-12 14:12 CST"
|
||||
__scriptVersion__ = "2g testing"
|
||||
__lastModified__ = "2019-06-12 23:29 CST"
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
@@ -189,7 +198,8 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
if PathOp.FeatureDepths & self.opFeatures(obj):
|
||||
try:
|
||||
shape = self.areaOpShapeForDepths(obj, job)
|
||||
except:
|
||||
except Exception as ee:
|
||||
PathLog.error(ee)
|
||||
shape = None
|
||||
|
||||
# Set initial start and final depths
|
||||
@@ -316,10 +326,8 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
areaOpShapes(obj) ... the shape for path area to process
|
||||
areaOpUseProjection(obj) ... return true if operation can use projection
|
||||
instead.'''
|
||||
PathLog.debug("\n\n----- opExecute() in PathAreaOp.py")
|
||||
PathLog.track()
|
||||
self.endVector = None
|
||||
PathLog.debug("\n\n----- opExecute() in PathAreaOp.py")
|
||||
PathLog.info("\n----- opExecute() in PathAreaOp.py")
|
||||
# PathLog.debug("OpDepths are Start: {}, and Final: {}".format(obj.OpStartDepth.Value, obj.OpFinalDepth.Value))
|
||||
# PathLog.debug("Depths are Start: {}, and Final: {}".format(obj.StartDepth.Value, obj.FinalDepth.Value))
|
||||
# PathLog.debug("initOpDepths are Start: {}, and Final: {}".format(self.initOpStartDepth, self.initOpFinalDepth))
|
||||
@@ -331,6 +339,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
self.cloneNames = []
|
||||
self.guiMsgs = [] # list of message tuples (title, msg) to be displayed in GUI
|
||||
self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox
|
||||
self.useTempJobClones('Delete') # Clear temporary group and recreate for temp job clones
|
||||
|
||||
# Import OpFinalDepth from pre-existing operation for recompute() scenarios
|
||||
if self.defValsSet is True:
|
||||
@@ -347,7 +356,6 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
self.defValsSet = False
|
||||
|
||||
if obj.EnableRotation != 'Off':
|
||||
self.useRotJobClones('Start')
|
||||
# Calculate operation heights based upon rotation radii
|
||||
opHeights = self.opDetermineRotationRadii(obj)
|
||||
(self.xRotRad, self.yRotRad, self.zRotRad) = opHeights[0]
|
||||
@@ -360,16 +368,11 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
self.strDep = self.yRotRad
|
||||
else:
|
||||
self.strDep = max(self.xRotRad, self.yRotRad)
|
||||
self.finDep = -1 * self.strDep
|
||||
self.finDep = -1 * self.strDep
|
||||
|
||||
obj.ClearanceHeight.Value = self.strDep + self.safOfset
|
||||
obj.SafeHeight.Value = self.strDep + self.safOfst
|
||||
|
||||
# Set axial feed rates based upon horizontal feed rates
|
||||
safeCircum = 2 * math.pi * obj.SafeHeight.Value
|
||||
self.axialFeed = 360 / safeCircum * self.horizFeed
|
||||
self.axialRapid = 360 / safeCircum * self.horizRapid
|
||||
|
||||
if self.initWithRotation == False:
|
||||
if obj.FinalDepth.Value == obj.OpFinalDepth.Value:
|
||||
obj.FinalDepth.Value = self.finDep
|
||||
@@ -383,6 +386,11 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
self.strDep = obj.StartDepth.Value
|
||||
self.finDep = obj.FinalDepth.Value
|
||||
|
||||
# Set axial feed rates based upon horizontal feed rates
|
||||
safeCircum = 2 * math.pi * obj.SafeHeight.Value
|
||||
self.axialFeed = 360 / safeCircum * self.horizFeed
|
||||
self.axialRapid = 360 / safeCircum * self.horizRapid
|
||||
|
||||
# Initiate depthparams and calculate operation heights for rotational operation
|
||||
finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0
|
||||
self.depthparams = PathUtils.depth_params(
|
||||
@@ -408,7 +416,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
if len(shp) == 2:
|
||||
(fc, iH) = shp
|
||||
# fc, iH, sub, angle, axis
|
||||
tup = fc, iH, 'otherOp', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
tup = fc, iH, 'otherOp', 0.0, 'S', obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
shapes.append(tup)
|
||||
else:
|
||||
shapes.append(shp)
|
||||
@@ -427,12 +435,13 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
# PathLog.debug("Pre_path depths are Start: {}, and Final: {}".format(obj.StartDepth.Value, obj.FinalDepth.Value))
|
||||
sims = []
|
||||
numShapes = len(shapes)
|
||||
|
||||
if numShapes == 1:
|
||||
nextAxis = shapes[0][4]
|
||||
elif numShapes > 1:
|
||||
nextAxis = shapes[1][4]
|
||||
else:
|
||||
nextAxis = 'X'
|
||||
nextAxis = 'L'
|
||||
|
||||
for ns in range(0, numShapes):
|
||||
(shape, isHole, sub, angle, axis, strDep, finDep) = shapes[ns]
|
||||
@@ -460,26 +469,33 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
ppCmds = pp.Commands
|
||||
if obj.EnableRotation != 'Off' and self.rotateFlag is True:
|
||||
# Rotate model to index for cut
|
||||
axisOfRot = 'A'
|
||||
if axis == 'Y':
|
||||
if axis == 'X':
|
||||
axisOfRot = 'A'
|
||||
elif axis == 'Y':
|
||||
axisOfRot = 'B'
|
||||
# Reverse angle temporarily to match model. Error in FreeCAD render of B axis rotations
|
||||
if obj.B_AxisErrorOverride is True:
|
||||
angle = -1 * angle
|
||||
elif axis == 'Z':
|
||||
axisOfRot = 'C'
|
||||
else:
|
||||
axisOfRot = 'A'
|
||||
# Rotate Model to correct angle
|
||||
ppCmds.insert(0, Path.Command('G1', {axisOfRot: angle, 'F': self.axialFeed}))
|
||||
ppCmds.insert(0, Path.Command('N100', {}))
|
||||
|
||||
# Raise cutter to safe depth and return index to starting position
|
||||
ppCmds.insert(0, Path.Command('N200', {}))
|
||||
ppCmds.append(Path.Command('G1', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
|
||||
# ppCmds.append(Path.Command('G0', {axisOfRot: 0.0, 'F': self.axialRapid}))
|
||||
ppCmds.append(Path.Command('N200', {}))
|
||||
ppCmds.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
|
||||
if axis != nextAxis:
|
||||
ppCmds.append(Path.Command('G0', {axisOfRot: 0.0, 'F': self.axialRapid}))
|
||||
# Eif
|
||||
|
||||
# Save gcode commands to object command list
|
||||
self.commandlist.extend(ppCmds)
|
||||
sims.append(sim)
|
||||
|
||||
# Eif
|
||||
|
||||
if self.areaOpRetractTool(obj):
|
||||
self.endVector = None
|
||||
|
||||
@@ -489,10 +505,9 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
self.commandlist.append(Path.Command('G0', {'A': 0.0, 'F': self.axialRapid}))
|
||||
self.commandlist.append(Path.Command('G0', {'B': 0.0, 'F': self.axialRapid}))
|
||||
|
||||
self.useTempJobClones('Delete') # Delete temp job clone group and contents
|
||||
self.guiMessage('title', None, show=True) # Process GUI messages to user
|
||||
PathLog.debug("obj.Name: " + str(obj.Name))
|
||||
if obj.EnableRotation != 'Off':
|
||||
self.useRotJobClones('Delete')
|
||||
self.guiMessage('title', None, show=True)
|
||||
return sims
|
||||
|
||||
def areaOpRetractTool(self, obj):
|
||||
@@ -522,7 +537,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
return False
|
||||
|
||||
def opDetermineRotationRadii(self, obj):
|
||||
'''opDetermineRotationRadii(self, obj)
|
||||
'''opDetermineRotationRadii(obj)
|
||||
Determine rotational radii for 4th-axis rotations, for clearance/safe heights '''
|
||||
|
||||
parentJob = PathUtils.findParentJob(obj)
|
||||
@@ -561,36 +576,40 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
return [(xRotRad, yRotRad, zRotRad), (clrOfst, safOfst)]
|
||||
|
||||
def faceRotationAnalysis(self, obj, norm, surf):
|
||||
'''faceRotationAnalysis(self, obj, norm, surf)
|
||||
'''faceRotationAnalysis(obj, norm, surf)
|
||||
Determine X and Y independent rotation necessary to make normalAt = Z=1 (0,0,1) '''
|
||||
PathLog.track()
|
||||
|
||||
praInfo = "faceRotationAnalysis() in PathAreaOp.py"
|
||||
rtn = False
|
||||
rtn = True
|
||||
axis = 'X'
|
||||
orientation = 'X'
|
||||
angle = 500.0
|
||||
precision = 6
|
||||
|
||||
def roundRoughValues(val):
|
||||
zTol = 1.0E-9
|
||||
rndTol = 1.0 - zTol
|
||||
for i in range(0, 13):
|
||||
if PathGeom.Tolerance * (i * 10) == 1.0:
|
||||
precision = i
|
||||
break
|
||||
|
||||
def roundRoughValues(precision, val):
|
||||
# Convert VALxe-15 numbers to zero
|
||||
if math.fabs(val) <= zTol:
|
||||
if PathGeom.isRoughly(0.0, val) is True:
|
||||
return 0.0
|
||||
# Convert VAL.99999999 to next integer
|
||||
elif math.fabs(val % 1) > rndTol:
|
||||
elif math.fabs(val % 1) > 1.0 - PathGeom.Tolerance:
|
||||
return round(val)
|
||||
else:
|
||||
return val
|
||||
return round(val, precision)
|
||||
|
||||
nX = roundRoughValues(norm.x)
|
||||
nY = roundRoughValues(norm.y)
|
||||
nZ = roundRoughValues(norm.z)
|
||||
nX = roundRoughValues(precision, norm.x)
|
||||
nY = roundRoughValues(precision, norm.y)
|
||||
nZ = roundRoughValues(precision, norm.z)
|
||||
praInfo += "\n -normalAt(0,0): " + str(nX) + ", " + str(nY) + ", " + str(nZ)
|
||||
|
||||
saX = roundRoughValues(surf.x)
|
||||
saY = roundRoughValues(surf.y)
|
||||
saZ = roundRoughValues(surf.z)
|
||||
saX = roundRoughValues(precision, surf.x)
|
||||
saY = roundRoughValues(precision, surf.y)
|
||||
saZ = roundRoughValues(precision, surf.z)
|
||||
praInfo += "\n -Surface.Axis: " + str(saX) + ", " + str(saY) + ", " + str(saZ)
|
||||
|
||||
# Determine rotation needed and current orientation
|
||||
@@ -653,15 +672,20 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
# Enforce enabled rotation in settings
|
||||
if orientation == 'Y':
|
||||
axis = 'X'
|
||||
if obj.EnableRotation == 'B(y)': # Axis disabled
|
||||
angle = 500.0
|
||||
if obj.EnableRotation == 'B(y)': # Required axis disabled
|
||||
rtn = False
|
||||
else:
|
||||
axis = 'Y'
|
||||
if obj.EnableRotation == 'A(x)': # Axis disabled
|
||||
angle = 500.0
|
||||
if obj.EnableRotation == 'A(x)': # Required axis disabled
|
||||
rtn = False
|
||||
|
||||
if angle != 500.0 and angle != 0.0:
|
||||
praInfo += "\n - ... rotation triggered"
|
||||
if angle == 500.0:
|
||||
rtn = False
|
||||
|
||||
if angle == 0.0:
|
||||
rtn = False
|
||||
|
||||
if rtn is True:
|
||||
self.rotateFlag = True
|
||||
rtn = True
|
||||
if obj.ReverseDirection is True:
|
||||
@@ -669,7 +693,11 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
angle = angle + 180.0
|
||||
else:
|
||||
angle = angle - 180.0
|
||||
praInfo += "\n -Suggested rotation: angle: " + str(angle) + ", axis: " + str(axis)
|
||||
angle = round(angle, precision)
|
||||
|
||||
praInfo += "\n -Rotation analysis: angle: " + str(angle) + ", axis: " + str(axis)
|
||||
if rtn is True:
|
||||
praInfo += "\n - ... rotation triggered"
|
||||
else:
|
||||
praInfo += "\n - ... NO rotation triggered"
|
||||
|
||||
@@ -678,6 +706,8 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
return (rtn, angle, axis, praInfo)
|
||||
|
||||
def guiMessage(self, title, msg, show=False):
|
||||
'''guiMessage(title, msg, show=False)
|
||||
Handle op related GUI messages to user'''
|
||||
if msg is not None:
|
||||
self.guiMsgs.append((title, msg))
|
||||
if show is True:
|
||||
@@ -712,6 +742,10 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
return False
|
||||
|
||||
def visualAxis(self):
|
||||
'''visualAxis()
|
||||
Create visual X & Y axis for use in orientation of rotational operations
|
||||
Triggered only for PathLog.debug'''
|
||||
|
||||
if not FreeCAD.ActiveDocument.getObject('xAxCyl'):
|
||||
xAx = 'xAxCyl'
|
||||
yAx = 'yAxCyl'
|
||||
@@ -760,20 +794,20 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
cylGui.Visibility = False
|
||||
vaGrp.addObject(cyl)
|
||||
|
||||
|
||||
def useRotJobClones(self, cloneName):
|
||||
def useTempJobClones(self, cloneName):
|
||||
'''useTempJobClones(cloneName)
|
||||
Manage use of temporary model clones for rotational operation calculations.
|
||||
Clones are stored in 'rotJobClones' group.'''
|
||||
if FreeCAD.ActiveDocument.getObject('rotJobClones'):
|
||||
if cloneName == 'Delete':
|
||||
if cloneName == 'Start':
|
||||
if PathLog.getLevel(PathLog.thisModule()) < 4:
|
||||
for cln in FreeCAD.ActiveDocument.getObject('rotJobClones').Group:
|
||||
FreeCAD.ActiveDocument.removeObject(cln.Name)
|
||||
elif cloneName == 'Delete':
|
||||
if PathLog.getLevel(PathLog.thisModule()) < 4:
|
||||
for cln in FreeCAD.ActiveDocument.getObject('rotJobClones').Group:
|
||||
FreeCAD.ActiveDocument.removeObject(cln.Name)
|
||||
FreeCAD.ActiveDocument.removeObject('rotJobClones')
|
||||
return
|
||||
if cloneName == 'Start':
|
||||
for cln in FreeCAD.ActiveDocument.getObject('rotJobClones').Group:
|
||||
FreeCAD.ActiveDocument.removeObject(cln.Name)
|
||||
FreeCAD.ActiveDocument.removeObject('rotJobClones')
|
||||
return
|
||||
else:
|
||||
FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","rotJobClones")
|
||||
FreeCADGui.ActiveDocument.getObject('rotJobClones').Visibility = False
|
||||
@@ -783,6 +817,9 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
FreeCADGui.ActiveDocument.getObject(cloneName).Visibility = False
|
||||
|
||||
def cloneBaseAndStock(self, obj, base, angle, axis, subCount):
|
||||
'''cloneBaseAndStock(obj, base, angle, axis, subCount)
|
||||
Method called to create a temporary clone of the base and parent Job stock.
|
||||
Clones are destroyed after usage for calculations related to rotational operations.'''
|
||||
# Create a temporary clone and stock of model for rotational use.
|
||||
rndAng = round(angle, 8)
|
||||
if rndAng < 0.0: # neg sign is converted to underscore in clone name creation.
|
||||
@@ -794,19 +831,23 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
if clnNm not in self.cloneNames:
|
||||
self.cloneNames.append(clnNm)
|
||||
self.cloneNames.append(stckClnNm)
|
||||
if FreeCAD.ActiveDocument.getObject(clnNm):
|
||||
FreeCAD.ActiveDocument.removeObject(clnNm)
|
||||
if FreeCAD.ActiveDocument.getObject(stckClnNm):
|
||||
FreeCAD.ActiveDocument.removeObject(stckClnNm)
|
||||
FreeCAD.ActiveDocument.addObject('Part::Feature', clnNm).Shape = base.Shape
|
||||
FreeCAD.ActiveDocument.addObject('Part::Feature', stckClnNm).Shape = PathUtils.findParentJob(obj).Stock.Shape
|
||||
FreeCADGui.ActiveDocument.getObject(stckClnNm).Transparency=90
|
||||
FreeCADGui.ActiveDocument.getObject(clnNm).ShapeColor = (1.000,0.667,0.000)
|
||||
self.useRotJobClones(clnNm)
|
||||
self.useRotJobClones(stckClnNm)
|
||||
self.useTempJobClones(clnNm)
|
||||
self.useTempJobClones(stckClnNm)
|
||||
clnBase = FreeCAD.ActiveDocument.getObject(clnNm)
|
||||
clnStock = FreeCAD.ActiveDocument.getObject(stckClnNm)
|
||||
tag = base.Name + '_' + tag
|
||||
return (clnBase, clnStock, tag)
|
||||
|
||||
def getFaceNormAndSurf(self, face):
|
||||
'''getFaceNormAndSurf(self, face)
|
||||
'''getFaceNormAndSurf(face)
|
||||
Return face.normalAt(0,0) or face.normal(0,0) and face.Surface.Axis vectors
|
||||
'''
|
||||
norm = FreeCAD.Vector(0.0, 0.0, 0.0)
|
||||
@@ -829,7 +870,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
return (norm, surf)
|
||||
|
||||
def applyRotationalAnalysis(self, obj, base, angle, axis, subCount):
|
||||
'''applyRotationalAnalysis(self, obj, base, angle, axis, subCount)
|
||||
'''applyRotationalAnalysis(obj, base, angle, axis, subCount)
|
||||
Create temp clone and stock and apply rotation to both.
|
||||
Return new rotated clones
|
||||
'''
|
||||
@@ -852,7 +893,9 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
clnStock.purgeTouched()
|
||||
return (clnBase, angle, clnStock, tag)
|
||||
|
||||
def applyInverseAngle(self, obj, clnBase, clnStock, axis, angle):
|
||||
def applyInverseAngle(self, obj, clnBase, clnStock, axis, angle):
|
||||
'''applyInverseAngle(obj, clnBase, clnStock, axis, angle)
|
||||
Apply rotations to incoming base and stock objects.'''
|
||||
if axis == 'X':
|
||||
vect = FreeCAD.Vector(1, 0, 0)
|
||||
elif axis == 'Y':
|
||||
@@ -864,15 +907,15 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
clnStock.purgeTouched()
|
||||
# Update property and angle values
|
||||
obj.InverseAngle = True
|
||||
obj.AttemptInverseAngle = False
|
||||
angle = -1 * angle
|
||||
|
||||
PathLog.info(translate("Path", "Rotated to inverse angle."))
|
||||
return (clnBase, clnStock, angle)
|
||||
|
||||
def calculateStartFinalDepths(self, obj, shape, stock):
|
||||
'''calculateStartFinalDepths(self, obj, shape, stock)
|
||||
Calculate correct start and final depths for the face
|
||||
'''
|
||||
'''calculateStartFinalDepths(obj, shape, stock)
|
||||
Calculate correct start and final depths for the shape(face) object provided.'''
|
||||
finDep = max(obj.FinalDepth.Value, shape.BoundBox.ZMin)
|
||||
stockTop = stock.Shape.BoundBox.ZMax
|
||||
if obj.EnableRotation == 'Off':
|
||||
@@ -883,14 +926,12 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
strDep = min(obj.StartDepth.Value, stockTop)
|
||||
if strDep <= finDep:
|
||||
strDep = stockTop # self.strDep
|
||||
msg = "Start depth <= face depth.\nIncreased to stock top."
|
||||
# msg = translate('Path', msg + "\nFace depth is {} mm.".format(face.BoundBox.ZMax)
|
||||
msg = translate('Path', msg)
|
||||
msg = translate('Path', "Start depth <= face depth.\nIncreased to stock top.")
|
||||
PathLog.error(msg)
|
||||
return (strDep, finDep)
|
||||
|
||||
def sortTuplesByIndex(self, TupleList, tagIdx):
|
||||
'''sortTuplesByIndex(self, TupleList, tagIdx)
|
||||
'''sortTuplesByIndex(TupleList, tagIdx)
|
||||
sort list of tuples based on tag index provided
|
||||
return (TagList, GroupList)
|
||||
'''
|
||||
@@ -914,5 +955,14 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
GroupList.pop(0)
|
||||
return (TagList, GroupList)
|
||||
|
||||
|
||||
|
||||
def warnDisabledAxis(self, obj, axis):
|
||||
'''warnDisabledAxis(self, obj, axis)
|
||||
Provide user feedback if required axis is disabled'''
|
||||
if axis == 'X' and obj.EnableRotation == 'B(y)':
|
||||
PathLog.warning(translate('Path', "Part feature is inaccessible. Selected feature(s) require 'A(x)' for access."))
|
||||
return True
|
||||
elif axis == 'Y' and obj.EnableRotation == 'A(x)':
|
||||
PathLog.warning(translate('Path', "Part feature is inaccessible. Selected feature(s) require 'B(y)' for access."))
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -21,16 +21,17 @@
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Additional modifications and contributions beginning 2019 *
|
||||
# * Focus: 4th-axis integration *
|
||||
# * by Russell Johnson <russ4262@gmail.com> *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
# SCRIPT NOTES:
|
||||
# - Need test models for testing vertical faces scenarios.
|
||||
# - Need to group VERTICAL faces per axis_angle tag just like horizontal faces.
|
||||
# Then, need to run each grouping through
|
||||
# PathGeom.combineConnectedShapes(vertical) algorithm grouping
|
||||
# - Need to add face boundbox analysis code to vertical axis_angle
|
||||
# section to identify highest zMax for all faces included in group
|
||||
# - FUTURE: Re-iterate PathAreaOp.py need to relocate rotational settings
|
||||
# to Job setup, under Machine settings tab
|
||||
# - FUTURE: PathAreaOp.py need to relocate rotational settings
|
||||
# to Job setup, under Machine settings tab
|
||||
|
||||
import FreeCAD
|
||||
import Part
|
||||
@@ -50,8 +51,8 @@ __url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Class and implementation of shape based Pocket operation."
|
||||
__contributors__ = "mlampert [FreeCAD], russ4262 (Russell Johnson)"
|
||||
__created__ = "2017"
|
||||
__scriptVersion__ = "2f testing"
|
||||
__lastModified__ = "2019-06-12 14:12 CST"
|
||||
__scriptVersion__ = "2g testing"
|
||||
__lastModified__ = "2019-06-12 23:29 CST"
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
@@ -173,8 +174,9 @@ class Extension(object):
|
||||
tangent = e0.tangentAt(midparam)
|
||||
try:
|
||||
normal = tangent.cross(FreeCAD.Vector(0, 0, 1)).normalize()
|
||||
except:
|
||||
except Exception as e:
|
||||
PathLog.error('getDirection(): tangent.cross(FreeCAD.Vector(0, 0, 1)).normalize()')
|
||||
PathLog.error(e)
|
||||
return None
|
||||
else:
|
||||
poffPlus = e0.valueAt(midparam) + 0.01 * normal
|
||||
@@ -241,22 +243,27 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
'''areaOpShapes(obj) ... return shapes representing the solids to be removed.'''
|
||||
PathLog.track()
|
||||
PathLog.debug("----- areaOpShapes() in PathPocketShape.py")
|
||||
|
||||
import Draft
|
||||
|
||||
baseSubsTuples = []
|
||||
subCount = 0
|
||||
allTuples = []
|
||||
self.tempNameList = []
|
||||
self.delTempNameList = 0
|
||||
finalDepths = []
|
||||
|
||||
def clasifySub(self, bs, sub):
|
||||
face = bs.Shape.getElement(sub)
|
||||
|
||||
def planarFaceFromExtrusionEdges(clsd):
|
||||
useFace = 'useFaceName'
|
||||
minArea = 0.0
|
||||
fCnt = 0
|
||||
def planarFaceFromExtrusionEdges(face, trans):
|
||||
useFace = 'useFaceName'
|
||||
minArea = 0.0
|
||||
fCnt = 0
|
||||
clsd = []
|
||||
planar = False
|
||||
# Identify closed edges
|
||||
for edg in face.Edges:
|
||||
if edg.isClosed():
|
||||
PathLog.debug(' -e.isClosed()')
|
||||
clsd.append(edg)
|
||||
planar = True
|
||||
# Attempt to create planar faces and select that with smallest area for use as pocket base
|
||||
if planar is True:
|
||||
planar = False
|
||||
for edg in clsd:
|
||||
fCnt += 1
|
||||
fName = sub + '_face_' + str(fCnt)
|
||||
@@ -265,7 +272,10 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
if mFF.isNull():
|
||||
PathLog.debug('Face(Part.Wire()) failed')
|
||||
else:
|
||||
mFF.translate(FreeCAD.Vector(0, 0, face.BoundBox.ZMin - mFF.BoundBox.ZMin))
|
||||
if trans is True:
|
||||
mFF.translate(FreeCAD.Vector(0, 0, face.BoundBox.ZMin - mFF.BoundBox.ZMin))
|
||||
if FreeCAD.ActiveDocument.getObject(fName):
|
||||
FreeCAD.ActiveDocument.removeObject(fName)
|
||||
tmpFace = FreeCAD.ActiveDocument.addObject('Part::Feature', fName).Shape = mFF
|
||||
tmpFace = FreeCAD.ActiveDocument.getObject(fName)
|
||||
tmpFace.purgeTouched()
|
||||
@@ -279,7 +289,12 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
useFace = fName
|
||||
else:
|
||||
FreeCAD.ActiveDocument.removeObject(fName)
|
||||
return (planar, useFace)
|
||||
if useFace != 'useFaceName':
|
||||
self.useTempJobClones(useFace)
|
||||
return (planar, useFace)
|
||||
|
||||
def clasifySub(self, bs, sub):
|
||||
face = bs.Shape.getElement(sub)
|
||||
|
||||
if type(face.Surface) == Part.Plane:
|
||||
PathLog.debug('type() == Part.Plane')
|
||||
@@ -292,7 +307,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
PathLog.debug(' -isHorizontal()')
|
||||
self.vert.append(face)
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
elif type(face.Surface) == Part.Cylinder and PathGeom.isVertical(face.Surface.Axis):
|
||||
PathLog.debug('type() == Part.Cylinder')
|
||||
# vertical cylinder wall
|
||||
@@ -312,36 +328,22 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
elif type(face.Surface) == Part.SurfaceOfExtrusion:
|
||||
# extrusion wall
|
||||
PathLog.debug('type() == Part.SurfaceOfExtrusion')
|
||||
clsd = []
|
||||
planar = False
|
||||
# Identify closed edges
|
||||
for edg in face.Edges:
|
||||
if edg.isClosed():
|
||||
PathLog.debug(' -e.isClosed()')
|
||||
clsd.append(edg)
|
||||
planar = True
|
||||
# Attempt to create planar faces and select that with smallest area for use as pocket base
|
||||
if planar is True:
|
||||
planar = False
|
||||
(planar, useFace) = planarFaceFromExtrusionEdges(clsd)
|
||||
# Attempt to extract planar face from surface of extrusion
|
||||
(planar, useFace) = planarFaceFromExtrusionEdges(face, trans=True)
|
||||
# Save face object to self.horiz for processing or display error
|
||||
if planar is True:
|
||||
self.tempNameList.append(useFace)
|
||||
self.delTempNameList += 1
|
||||
uFace = FreeCAD.ActiveDocument.getObject(useFace)
|
||||
self.horiz.append(uFace.Shape.Faces[0])
|
||||
msg = "<b>Verify depth of pocket for '{}'.</b>".format(sub)
|
||||
msg += "\n<br>Pocket is based on extruded surface."
|
||||
msg += "\n<br>Bottom of pocket might be non-planar and/or not normal to spindle axis."
|
||||
msg += "\n<br>\n<br><i>3D pocket bottom is NOT available in this operation</i>."
|
||||
msg = translate('Path', msg)
|
||||
msg = translate('Path', "<b>Verify depth of pocket for '{}'.</b>".format(sub))
|
||||
msg += translate('Path', "\n<br>Pocket is based on extruded surface.")
|
||||
msg += translate('Path', "\n<br>Bottom of pocket might be non-planar and/or not normal to spindle axis.")
|
||||
msg += translate('Path', "\n<br>\n<br><i>3D pocket bottom is NOT available in this operation</i>.")
|
||||
PathLog.info(msg)
|
||||
title = translate('Path', 'Depth Warning')
|
||||
self.guiMessage(title, msg, False)
|
||||
else:
|
||||
PathLog.error(translate("Path", "Failed to create a planar face from edges in {}.".format(sub)))
|
||||
else:
|
||||
PathLog.debug('type() == OTHER')
|
||||
PathLog.debug(' -type(face.Surface): {}'.format(type(face.Surface)))
|
||||
return False
|
||||
|
||||
@@ -356,19 +358,23 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
else:
|
||||
for p in range(0, len(obj.Base)):
|
||||
(base, subsList) = obj.Base[p]
|
||||
go = False
|
||||
isLoop = False
|
||||
|
||||
# First, check all subs collectively for loop of faces
|
||||
if len(subsList) > 2:
|
||||
(go, norm, surf) = self.checkForFacesLoop(base, subsList)
|
||||
if go is True:
|
||||
PathLog.debug("Common Surface.Axis or normalAt() value found for loop faces.")
|
||||
(isLoop, norm, surf) = self.checkForFacesLoop(base, subsList)
|
||||
if isLoop is True:
|
||||
PathLog.info("Common Surface.Axis or normalAt() value found for loop faces.")
|
||||
rtn = False
|
||||
subCount += 1
|
||||
(rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf)
|
||||
PathLog.info("angle: {}; axis: {}".format(angle, axis))
|
||||
|
||||
if rtn is True:
|
||||
(clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, subCount)
|
||||
faceNums = ""
|
||||
for f in subsList:
|
||||
faceNums += '_' + f.replace('Face', '')
|
||||
(clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, faceNums)
|
||||
|
||||
# Verify faces are correctly oriented - InverseAngle might be necessary
|
||||
PathLog.debug("Checking if faces are oriented correctly after rotation...")
|
||||
@@ -386,7 +392,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
|
||||
tup = clnBase, subsList, angle, axis, clnStock
|
||||
else:
|
||||
PathLog.debug("No rotation used")
|
||||
if self.warnDisabledAxis(obj, axis) is False:
|
||||
PathLog.debug("No rotation used")
|
||||
axis = 'X'
|
||||
angle = 0.0
|
||||
stock = PathUtils.findParentJob(obj).Stock
|
||||
@@ -396,7 +403,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
baseSubsTuples.append(tup)
|
||||
# Eif
|
||||
|
||||
if go is False:
|
||||
if isLoop is False:
|
||||
PathLog.debug(translate('Path', "Processing subs individually ..."))
|
||||
for sub in subsList:
|
||||
subCount += 1
|
||||
@@ -405,11 +412,28 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
|
||||
PathLog.debug(translate('Path', "Base Geometry sub: {}".format(sub)))
|
||||
face = base.Shape.getElement(sub)
|
||||
|
||||
# --------------------------------------------------------
|
||||
if type(face.Surface) == Part.SurfaceOfExtrusion:
|
||||
# extrusion wall
|
||||
PathLog.debug('analyzing type() == Part.SurfaceOfExtrusion')
|
||||
# Attempt to extract planar face from surface of extrusion
|
||||
(planar, useFace) = planarFaceFromExtrusionEdges(face, trans=False)
|
||||
# Save face object to self.horiz for processing or display error
|
||||
if planar is True:
|
||||
base = FreeCAD.ActiveDocument.getObject(useFace)
|
||||
sub = 'Face1'
|
||||
PathLog.debug(' -successful face crated: {}'.format(useFace))
|
||||
else:
|
||||
PathLog.error(translate("Path", "Failed to create a planar face from edges in {}.".format(sub)))
|
||||
# --------------------------------------------------------
|
||||
|
||||
(norm, surf) = self.getFaceNormAndSurf(face)
|
||||
(rtn, angle, axis, praInfo)= self.faceRotationAnalysis(obj, norm, surf)
|
||||
|
||||
if rtn is True:
|
||||
(clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, subCount)
|
||||
faceNum = sub.replace('Face', '')
|
||||
(clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, faceNum)
|
||||
# Verify faces are correctly oriented - InverseAngle might be necessary
|
||||
faceIA = clnBase.Shape.getElement(sub)
|
||||
(norm, surf) = self.getFaceNormAndSurf(faceIA)
|
||||
@@ -425,7 +449,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
|
||||
tup = clnBase, [sub], angle, axis, clnStock
|
||||
else:
|
||||
PathLog.debug(str(sub) + ": No rotation used")
|
||||
if self.warnDisabledAxis(obj, axis) is False:
|
||||
PathLog.debug(str(sub) + ": No rotation used")
|
||||
axis = 'X'
|
||||
angle = 0.0
|
||||
stock = PathUtils.findParentJob(obj).Stock
|
||||
@@ -460,40 +485,44 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
self.horiz = []
|
||||
self.vert = []
|
||||
subBase = o[0]
|
||||
subsList = o[1]
|
||||
angle = o[2]
|
||||
axis = o[3]
|
||||
stock = o[4]
|
||||
|
||||
for sub in o[1]:
|
||||
for sub in subsList:
|
||||
if 'Face' in sub:
|
||||
if clasifySub(self, subBase, sub) is False:
|
||||
PathLog.error(translate('PathPocket', 'Pocket does not support shape %s.%s') % (subBase.Label, sub))
|
||||
if obj.EnableRotation != 'Off':
|
||||
PathLog.info(translate('PathPocket', 'Face might not be within rotation accessibility limits.'))
|
||||
|
||||
# Determine final depth as highest value of bottom boundbox of vertical face,
|
||||
# in case of uneven faces on bottom
|
||||
vFinDep = self.vert[0].BoundBox.ZMin
|
||||
for vFace in self.vert:
|
||||
if vFace.BoundBox.ZMin > vFinDep:
|
||||
vFinDep = vFace.BoundBox.ZMin
|
||||
# Determine if vertical faces for a loop: Extract planar loop wire as new horizontal face.
|
||||
self.vertical = PathGeom.combineConnectedShapes(self.vert)
|
||||
self.vWires = [TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) for shape in self.vertical]
|
||||
for wire in self.vWires:
|
||||
w = PathGeom.removeDuplicateEdges(wire)
|
||||
face = Part.Face(w)
|
||||
# face.tessellate(0.1)
|
||||
if PathGeom.isRoughly(face.Area, 0):
|
||||
msg = translate('PathPocket', 'Vertical faces do not form a loop - ignoring')
|
||||
PathLog.error(msg)
|
||||
# title = translate("Path", "Face Selection Warning")
|
||||
# self.guiMessage(title, msg, True)
|
||||
else:
|
||||
face.translate(FreeCAD.Vector(0, 0, vFinDep - face.BoundBox.ZMin))
|
||||
self.horiz.append(face)
|
||||
msg = translate('Path', 'Verify final depth of pocket shaped by vertical faces.')
|
||||
PathLog.error(msg)
|
||||
title = translate('Path', 'Depth Warning')
|
||||
self.guiMessage(title, msg, False)
|
||||
if len(self.vert) > 0:
|
||||
vFinDep = self.vert[0].BoundBox.ZMin
|
||||
for vFace in self.vert:
|
||||
if vFace.BoundBox.ZMin > vFinDep:
|
||||
vFinDep = vFace.BoundBox.ZMin
|
||||
# Determine if vertical faces for a loop: Extract planar loop wire as new horizontal face.
|
||||
self.vertical = PathGeom.combineConnectedShapes(self.vert)
|
||||
self.vWires = [TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) for shape in self.vertical]
|
||||
for wire in self.vWires:
|
||||
w = PathGeom.removeDuplicateEdges(wire)
|
||||
face = Part.Face(w)
|
||||
# face.tessellate(0.1)
|
||||
if PathGeom.isRoughly(face.Area, 0):
|
||||
msg = translate('PathPocket', 'Vertical faces do not form a loop - ignoring')
|
||||
PathLog.error(msg)
|
||||
# title = translate("Path", "Face Selection Warning")
|
||||
# self.guiMessage(title, msg, True)
|
||||
else:
|
||||
face.translate(FreeCAD.Vector(0, 0, vFinDep - face.BoundBox.ZMin))
|
||||
self.horiz.append(face)
|
||||
msg = translate('Path', 'Verify final depth of pocket shaped by vertical faces.')
|
||||
PathLog.error(msg)
|
||||
title = translate('Path', 'Depth Warning')
|
||||
self.guiMessage(title, msg, False)
|
||||
|
||||
# add faces for extensions
|
||||
self.exts = []
|
||||
@@ -524,15 +553,19 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
for face in self.horizontal:
|
||||
# extrude all faces up to StartDepth and those are the removal shapes
|
||||
(strDep, finDep) = self.calculateStartFinalDepths(obj, face, stock)
|
||||
finalDepths.append(finDep)
|
||||
extent = FreeCAD.Vector(0, 0, strDep - finDep)
|
||||
self.removalshapes.append((face.removeSplitter().extrude(extent), False, 'pathPocketShape', angle, axis, strDep, finDep))
|
||||
PathLog.debug("Extent depths are str: {}, and fin: {}".format(strDep, finDep))
|
||||
# Efor face
|
||||
|
||||
# Adjust obj.FinalDepth.Value as needed.
|
||||
if subCount == 1:
|
||||
obj.FinalDepth.Value = finDep
|
||||
else: # process the job base object as a whole
|
||||
if len(finalDepths) > 0:
|
||||
finalDep = min(finalDepths)
|
||||
if subCount == 1:
|
||||
obj.FinalDepth.Value = finDep
|
||||
else:
|
||||
# process the job base object as a whole
|
||||
PathLog.debug(translate("Path", 'Processing model as a whole ...'))
|
||||
finDep = obj.FinalDepth.Value
|
||||
strDep = obj.StartDepth.Value
|
||||
@@ -554,9 +587,10 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
if self.removalshapes:
|
||||
obj.removalshape = self.removalshapes[0][0]
|
||||
|
||||
if self.delTempNameList > 0:
|
||||
for tmpNm in self.tempNameList:
|
||||
FreeCAD.ActiveDocument.removeObject(tmpNm)
|
||||
#if PathLog.getLevel(PathLog.thisModule()) != 4:
|
||||
#if self.delTempNameList > 0:
|
||||
# for tmpNm in self.tempNameList:
|
||||
# FreeCAD.ActiveDocument.removeObject(tmpNm)
|
||||
|
||||
return self.removalshapes
|
||||
|
||||
@@ -590,7 +624,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
obj.ExtensionFeature = [(ext.obj, ext.getSubLink()) for ext in extensions]
|
||||
|
||||
def checkForFacesLoop(self, base, subsList):
|
||||
'''checkForFacesLoop(self, base, subsList)...
|
||||
'''checkForFacesLoop(base, subsList)...
|
||||
Accepts a list of face names for the given base.
|
||||
Checks to determine if they are looped together.
|
||||
'''
|
||||
@@ -603,6 +637,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
saSum = FreeCAD.Vector(0.0, 0.0, 0.0)
|
||||
norm = FreeCAD.Vector(0.0, 0.0, 0.0)
|
||||
surf = FreeCAD.Vector(0.0, 0.0, 0.0)
|
||||
precision = 6
|
||||
|
||||
def makeTempExtrusion(base, sub, fCnt):
|
||||
extName = 'tmpExtrude' + str(fCnt)
|
||||
@@ -632,17 +667,21 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
tmpWire.purgeTouched()
|
||||
return (True, tmpWire, tmpExt)
|
||||
|
||||
def roundValue(val):
|
||||
zTol = 1.0E-8
|
||||
rndTol = 1.0 - zTol
|
||||
def roundValue(precision, val):
|
||||
# Convert VALxe-15 numbers to zero
|
||||
if math.fabs(val) <= zTol:
|
||||
if PathGeom.isRoughly(0.0, val) is True:
|
||||
return 0.0
|
||||
# Convert VAL.99999999 to next integer
|
||||
elif math.fabs(val % 1) > rndTol:
|
||||
elif math.fabs(val % 1) > 1.0 - PathGeom.Tolerance:
|
||||
return round(val)
|
||||
else:
|
||||
return round(val, 8)
|
||||
return round(val, precision)
|
||||
|
||||
# Determine precision from Tolerance
|
||||
for i in range(0, 13):
|
||||
if PathGeom.Tolerance * (i * 10) == 1.0:
|
||||
precision = i
|
||||
break
|
||||
|
||||
# Sub Surface.Axis values of faces
|
||||
# Vector of (0, 0, 0) will suggests a loop
|
||||
@@ -692,21 +731,21 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
if len(matchList) == 0:
|
||||
for fc in lastExtrusion.Shape.Faces:
|
||||
(norm, raw) = self.getFaceNormAndSurf(fc)
|
||||
rnded = FreeCAD.Vector(roundValue(raw.x), roundValue(raw.y), roundValue(raw.z))
|
||||
rnded = FreeCAD.Vector(roundValue(precision, raw.x), roundValue(precision, raw.y), roundValue(precision, raw.z))
|
||||
if rnded.x == 0.0 or rnded.y == 0.0 or rnded.z == 0.0:
|
||||
for fc2 in tmpExt.Shape.Faces:
|
||||
(norm2, raw2) = self.getFaceNormAndSurf(fc2)
|
||||
rnded2 = FreeCAD.Vector(roundValue(raw2.x), roundValue(raw2.y), roundValue(raw2.z))
|
||||
rnded2 = FreeCAD.Vector(roundValue(precision, raw2.x), roundValue(precision, raw2.y), roundValue(precision, raw2.z))
|
||||
if rnded == rnded2:
|
||||
matchList.append(fc2)
|
||||
go = True
|
||||
else:
|
||||
for m in matchList:
|
||||
(norm, raw) = self.getFaceNormAndSurf(m)
|
||||
rnded = FreeCAD.Vector(roundValue(raw.x), roundValue(raw.y), roundValue(raw.z))
|
||||
rnded = FreeCAD.Vector(roundValue(precision, raw.x), roundValue(precision, raw.y), roundValue(precision, raw.z))
|
||||
for fc2 in tmpExt.Shape.Faces:
|
||||
(norm2, raw2) = self.getFaceNormAndSurf(fc2)
|
||||
rnded2 = FreeCAD.Vector(roundValue(raw2.x), roundValue(raw2.y), roundValue(raw2.z))
|
||||
rnded2 = FreeCAD.Vector(roundValue(precision, raw2.x), roundValue(precision, raw2.y), roundValue(precision, raw2.z))
|
||||
if rnded.x == 0.0 or rnded.y == 0.0 or rnded.z == 0.0:
|
||||
if rnded == rnded2:
|
||||
go = True
|
||||
@@ -722,7 +761,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
saTotal = FreeCAD.Vector(0.0, 0.0, 0.0)
|
||||
for fc in matchList:
|
||||
(norm, raw) = self.getFaceNormAndSurf(fc)
|
||||
rnded = FreeCAD.Vector(roundValue(raw.x), roundValue(raw.y), roundValue(raw.z))
|
||||
rnded = FreeCAD.Vector(roundValue(precision, raw.x), roundValue(precision, raw.y), roundValue(precision, raw.z))
|
||||
if (rnded.y > 0.0 or rnded.z > 0.0) and vertLoopFace is None:
|
||||
vertLoopFace = fc
|
||||
saTotal = saTotal.add(rnded)
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Additional modifications and contributions beginning 2019 *
|
||||
# * Focus: 4th-axis integration *
|
||||
# * by Russell Johnson <russ4262@gmail.com> *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import ArchPanel
|
||||
import FreeCAD
|
||||
@@ -40,9 +46,10 @@ __title__ = "Path Profile Faces Operation"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Path Profile operation based on faces."
|
||||
__contributors__ = "russ4262 (Russell Johnson)"
|
||||
__created__ = "2014"
|
||||
__scriptVersion__ = "2f testing"
|
||||
__lastModified__ = "2019-06-12 14:12 CST"
|
||||
__scriptVersion__ = "2g testing"
|
||||
__lastModified__ = "2019-06-12 23:29 CST"
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
@@ -136,7 +143,8 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
|
||||
|
||||
tup = clnBase, sub, tag, angle, axis, clnStock
|
||||
else:
|
||||
PathLog.debug(str(sub) + ": no rotation used")
|
||||
if self.warnDisabledAxis(obj, axis) is False:
|
||||
PathLog.debug(str(sub) + ": No rotation used")
|
||||
axis = 'X'
|
||||
angle = 0.0
|
||||
tag = base.Name + '_' + axis + str(angle).replace('.', '_')
|
||||
@@ -148,9 +156,8 @@ class ObjectProfile(PathProfileBase.ObjectProfile):
|
||||
# Efor
|
||||
# Efor
|
||||
if subCount > 1:
|
||||
msg = "Multiple faces in Base Geometry."
|
||||
msg += " Depth settings will be applied to all faces."
|
||||
msg = translate("Path", msg)
|
||||
msg = translate('Path', "Multiple faces in Base Geometry.") + " "
|
||||
msg += translate('Path', "Depth settings will be applied to all faces.")
|
||||
PathLog.warning(msg)
|
||||
#title = translate("Path", "Depth Warning")
|
||||
#self.guiMessage(title, msg)
|
||||
|
||||
Reference in New Issue
Block a user