Restore manual override of FinalDepth values
Manual entries of FinalDepth values are now respected, even if below the calculated bottom of selected geometry. Affected modules include PathPocketShape, PathPocket, PathCircularHoleBase, and PathAreaOp-based operations. Fix format syntax issues
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -50,8 +50,8 @@ __url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Base class an implementation for operations on circular holes."
|
||||
__contributors__ = "russ4262 (Russell Johnson)"
|
||||
__created__ = "2017"
|
||||
__scriptVersion__ = "1e testing"
|
||||
__lastModified__ = "2019-07-26 14:15 CST"
|
||||
__scriptVersion__ = "2b"
|
||||
__lastModified__ = "2020-02-13 17:11 CST"
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
@@ -60,7 +60,7 @@ def translate(context, text, disambig=None):
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
|
||||
class ObjectOp(PathOp.ObjectOp):
|
||||
@@ -75,7 +75,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
def opFeatures(self, obj):
|
||||
'''opFeatures(obj) ... calls circularHoleFeatures(obj) and ORs in the standard features required for processing circular holes.
|
||||
Do not overwrite, implement circularHoleFeatures(obj) instead'''
|
||||
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureBaseFaces | self.circularHoleFeatures(obj) | PathOp.FeatureCoolant
|
||||
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureBaseFaces | self.circularHoleFeatures(obj) | PathOp.FeatureCoolant
|
||||
|
||||
def circularHoleFeatures(self, obj):
|
||||
'''circularHoleFeatures(obj) ... overwrite to add operations specific features.
|
||||
@@ -136,15 +136,14 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
|
||||
if shape.ShapeType == 'Edge' and type(shape.Curve) == Part.Circle:
|
||||
return shape.Curve.Radius * 2
|
||||
|
||||
|
||||
if shape.ShapeType == 'Face':
|
||||
for i in range(len(shape.Edges)):
|
||||
if (type(shape.Edges[i].Curve) == Part.Circle and
|
||||
shape.Edges[i].Curve.Radius * 2 < shape.BoundBox.XLength*1.1 and
|
||||
if (type(shape.Edges[i].Curve) == Part.Circle and
|
||||
shape.Edges[i].Curve.Radius * 2 < shape.BoundBox.XLength*1.1 and
|
||||
shape.Edges[i].Curve.Radius * 2 > shape.BoundBox.XLength*0.9):
|
||||
return shape.Edges[i].Curve.Radius * 2
|
||||
|
||||
|
||||
|
||||
# for all other shapes the diameter is just the dimension in X. This may be inaccurate as the BoundBox is calculated on the tessellated geometry
|
||||
PathLog.warning(translate("Path", "Hole diameter may be inaccurate due to tessellation on face. Consider selecting hole edge."))
|
||||
return shape.BoundBox.XLength
|
||||
@@ -207,7 +206,6 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
self.safeHeight = obj.SafeHeight.Value # pylint: disable=attribute-defined-outside-init
|
||||
self.axialFeed = 0.0 # pylint: disable=attribute-defined-outside-init
|
||||
self.axialRapid = 0.0 # pylint: disable=attribute-defined-outside-init
|
||||
trgtDep = None
|
||||
|
||||
def haveLocations(self, obj):
|
||||
if PathOp.FeatureLocations & self.opFeatures(obj):
|
||||
@@ -324,38 +322,29 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
pos = self.holePosition(obj, base, sub)
|
||||
if pos:
|
||||
# Default is treat selection as 'Face' shape
|
||||
finDep = base.Shape.getElement(sub).BoundBox.ZMin
|
||||
holeBtm = base.Shape.getElement(sub).BoundBox.ZMin
|
||||
if base.Shape.getElement(sub).ShapeType == 'Edge':
|
||||
msg = translate("Path", "Verify Final Depth of holes based on edges. {} depth is: {} mm".format(sub, round(finDep, 4))) + " "
|
||||
msg = translate("Path", "Verify Final Depth of holes based on edges. {} depth is: {} mm".format(sub, round(holeBtm, 4))) + " "
|
||||
msg += translate("Path", "Always select the bottom edge of the hole when using an edge.")
|
||||
PathLog.warning(msg)
|
||||
|
||||
# If user has not adjusted Final Depth value, attempt to determine from sub
|
||||
trgtDep = obj.FinalDepth.Value
|
||||
if obj.OpFinalDepth.Value == obj.FinalDepth.Value:
|
||||
trgtDep = finDep
|
||||
if obj.FinalDepth.Value < finDep:
|
||||
msg = translate("Path", "Final Depth setting is below the hole bottom for {}.".format(sub))
|
||||
# Warn user if Final Depth set lower than bottom of hole
|
||||
if finDep < holeBtm:
|
||||
msg = translate("Path", "Final Depth setting is below the hole bottom for {}.".format(sub)) + ' '
|
||||
msg += translate("Path", "{} depth is calculated at {} mm".format(sub, round(holeBtm, 4)))
|
||||
PathLog.warning(msg)
|
||||
|
||||
holes.append({'x': pos.x, 'y': pos.y, 'r': self.holeDiameter(obj, base, sub),
|
||||
'angle': angle, 'axis': axis, 'trgtDep': trgtDep,
|
||||
'angle': angle, 'axis': axis, 'trgtDep': finDep,
|
||||
'stkTop': stock.Shape.BoundBox.ZMax})
|
||||
|
||||
if haveLocations(self, obj):
|
||||
for location in obj.Locations:
|
||||
# holes.append({'x': location.x, 'y': location.y, 'r': 0, 'angle': 0.0, 'axis': 'X', 'finDep': obj.FinalDepth.Value})
|
||||
trgtDep = obj.FinalDepth.Value
|
||||
# holes.append({'x': location.x, 'y': location.y, 'r': 0, 'angle': 0.0, 'axis': 'X', 'holeBtm': obj.FinalDepth.Value})
|
||||
holes.append({'x': location.x, 'y': location.y, 'r': 0,
|
||||
'angle': 0.0, 'axis': 'X', 'trgtDep': trgtDep,
|
||||
'angle': 0.0, 'axis': 'X', 'trgtDep': finDep,
|
||||
'stkTop': PathUtils.findParentJob(obj).stock.Shape.BoundBox.ZMax})
|
||||
|
||||
# If all holes based upon edges, set post-operation Final Depth to highest edge height
|
||||
if obj.OpFinalDepth.Value == obj.FinalDepth.Value:
|
||||
if len(holes) == 1:
|
||||
PathLog.info(translate('Path', "Single-hole operation. Saving Final Depth determined from hole base."))
|
||||
obj.FinalDepth.Value = trgtDep
|
||||
|
||||
if len(holes) > 0:
|
||||
self.circularHoleExecute(obj, holes) # circularHoleExecute() located in PathDrilling.py
|
||||
|
||||
@@ -822,23 +811,6 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
angle = -1 * angle
|
||||
return (clnBase, clnStock, angle)
|
||||
|
||||
def calculateStartFinalDepths(self, obj, shape, stock):
|
||||
'''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':
|
||||
strDep = obj.StartDepth.Value
|
||||
if strDep <= finDep:
|
||||
strDep = stockTop
|
||||
else:
|
||||
strDep = min(obj.StartDepth.Value, stockTop)
|
||||
if strDep <= finDep:
|
||||
strDep = stockTop
|
||||
msg = translate('Path', "Start depth <= face depth.\nIncreased to stock top.")
|
||||
PathLog.error(msg)
|
||||
return (strDep, finDep)
|
||||
|
||||
def sortTuplesByIndex(self, TupleList, tagIdx):
|
||||
'''sortTuplesByIndex(TupleList, tagIdx)
|
||||
sort list of tuples based on tag index provided
|
||||
|
||||
@@ -37,11 +37,11 @@ __url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Class and implementation of the 3D Pocket operation."
|
||||
__contributors__ = "russ4262 (Russell Johnson)"
|
||||
__created__ = "2014"
|
||||
__scriptVersion__ = "2g testing"
|
||||
__lastModified__ = "2019-07-20 22:02 CST"
|
||||
__scriptVersion__ = "2e"
|
||||
__lastModified__ = "2020-02-13 17:22 CST"
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
@@ -102,8 +102,11 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
allSubsFaceType = False
|
||||
|
||||
if allSubsFaceType is True and obj.HandleMultipleFeatures == 'Collectively':
|
||||
(fzmin, fzmax) = self.getMinMaxOfFaces(Faces)
|
||||
if obj.FinalDepth.Value < fzmin:
|
||||
PathLog.warning(translate('PathPocket', 'Final depth set below ZMin of face(s) selected.'))
|
||||
'''
|
||||
if obj.OpFinalDepth == obj.FinalDepth:
|
||||
(fzmin, fzmax) = self.getMinMaxOfFaces(Faces)
|
||||
obj.FinalDepth.Value = fzmin
|
||||
finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0
|
||||
self.depthparams = PathUtils.depth_params(
|
||||
@@ -115,6 +118,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
final_depth=fzmin,
|
||||
user_depths=None)
|
||||
PathLog.info("Updated obj.FinalDepth.Value and self.depthparams to zmin: {}".format(fzmin))
|
||||
'''
|
||||
|
||||
if obj.AdaptivePocketStart is True or obj.AdaptivePocketFinish is True:
|
||||
pocketTup = self.calculateAdaptivePocket(obj, base, subObjTups)
|
||||
@@ -148,8 +152,9 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
PathLog.debug("processing the whole job base object")
|
||||
strDep = obj.StartDepth.Value
|
||||
finDep = obj.FinalDepth.Value
|
||||
recomputeDepthparams = False
|
||||
# recomputeDepthparams = False
|
||||
for base in self.model:
|
||||
'''
|
||||
if obj.OpFinalDepth == obj.FinalDepth:
|
||||
if base.Shape.BoundBox.ZMin < obj.FinalDepth.Value:
|
||||
obj.FinalDepth.Value = base.Shape.BoundBox.ZMin
|
||||
@@ -173,11 +178,13 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
final_depth=obj.FinalDepth.Value,
|
||||
user_depths=None)
|
||||
recomputeDepthparams = False
|
||||
'''
|
||||
|
||||
if obj.ProcessStockArea is True:
|
||||
job = PathUtils.findParentJob(obj)
|
||||
finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0
|
||||
|
||||
'''
|
||||
finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0
|
||||
depthparams = PathUtils.depth_params(
|
||||
clearance_height=obj.ClearanceHeight.Value,
|
||||
safe_height=obj.SafeHeight.Value,
|
||||
@@ -187,6 +194,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
final_depth=base.Shape.BoundBox.ZMin,
|
||||
user_depths=None)
|
||||
stockEnvShape = PathUtils.getEnvelope(job.Stock.Shape, subshape=None, depthparams=depthparams)
|
||||
'''
|
||||
stockEnvShape = PathUtils.getEnvelope(job.Stock.Shape, subshape=None, depthparams=self.depthparams)
|
||||
|
||||
obj.removalshape = stockEnvShape.cut(base.Shape)
|
||||
obj.removalshape.tessellate(0.1)
|
||||
@@ -225,7 +234,6 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
tryNonPlanar = False
|
||||
isHighFacePlanar = True
|
||||
isLowFacePlanar = True
|
||||
faceType = 0
|
||||
|
||||
for (sub, face) in subObjTups:
|
||||
Faces.append(face)
|
||||
|
||||
@@ -40,11 +40,11 @@ __url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Class and implementation of shape based Pocket operation."
|
||||
__contributors__ = "russ4262 (Russell Johnson)"
|
||||
__created__ = "2017"
|
||||
__scriptVersion__ = "2i usable"
|
||||
__lastModified__ = "2019-09-07 08:32 CST"
|
||||
__scriptVersion__ = "2i"
|
||||
__lastModified__ = "2020-02-13 17:01 CST"
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
@@ -65,11 +65,12 @@ def endPoints(edgeOrWire):
|
||||
unique.append(p)
|
||||
return unique
|
||||
pfirst = edgeOrWire.valueAt(edgeOrWire.FirstParameter)
|
||||
plast = edgeOrWire.valueAt(edgeOrWire.LastParameter)
|
||||
plast = edgeOrWire.valueAt(edgeOrWire.LastParameter)
|
||||
if PathGeom.pointsCoincide(pfirst, plast):
|
||||
return None
|
||||
return [pfirst, plast]
|
||||
|
||||
|
||||
def includesPoint(p, pts):
|
||||
'''includesPoint(p, pts) ... answer True if the collection of pts includes the point p'''
|
||||
for pt in pts:
|
||||
@@ -83,7 +84,7 @@ def selectOffsetWire(feature, wires):
|
||||
closest = None
|
||||
for w in wires:
|
||||
dist = feature.distToShape(w)[0]
|
||||
if closest is None or dist > closest[0]: # pylint: disable=unsubscriptable-object
|
||||
if closest is None or dist > closest[0]: # pylint: disable=unsubscriptable-object
|
||||
closest = (dist, w)
|
||||
if closest is not None:
|
||||
return closest[1]
|
||||
@@ -116,6 +117,7 @@ def extendWire(feature, wire, length):
|
||||
return Part.Wire(edges)
|
||||
return None
|
||||
|
||||
|
||||
class Extension(object):
|
||||
DirectionNormal = 0
|
||||
DirectionX = 1
|
||||
@@ -163,7 +165,7 @@ class Extension(object):
|
||||
return [self.obj.Shape.getElement(sub) for sub in self._getEdgeNames()]
|
||||
|
||||
def _getDirectedNormal(self, p0, normal):
|
||||
poffPlus = p0 + 0.01 * normal
|
||||
poffPlus = p0 + 0.01 * normal
|
||||
poffMinus = p0 - 0.01 * normal
|
||||
if not self.obj.Shape.isInside(poffPlus, 0.005, True):
|
||||
return normal
|
||||
@@ -281,7 +283,6 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
baseSubsTuples = []
|
||||
subCount = 0
|
||||
allTuples = []
|
||||
finalDepths = []
|
||||
|
||||
def planarFaceFromExtrusionEdges(face, trans):
|
||||
useFace = 'useFaceName'
|
||||
@@ -383,7 +384,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
|
||||
if obj.Base:
|
||||
PathLog.debug('Processing... obj.Base')
|
||||
self.removalshapes = [] # pylint: disable=attribute-defined-outside-init
|
||||
self.removalshapes = [] # pylint: disable=attribute-defined-outside-init
|
||||
# ----------------------------------------------------------------------
|
||||
if obj.EnableRotation == 'Off':
|
||||
stock = PathUtils.findParentJob(obj).Stock
|
||||
@@ -401,14 +402,14 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
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) # pylint: disable=unused-variable
|
||||
(rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
|
||||
PathLog.info("angle: {}; axis: {}".format(angle, axis))
|
||||
|
||||
if rtn is True:
|
||||
faceNums = ""
|
||||
for f in subsList:
|
||||
faceNums += '_' + f.replace('Face', '')
|
||||
(clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, faceNums) # pylint: disable=unused-variable
|
||||
(clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, faceNums) # pylint: disable=unused-variable
|
||||
|
||||
# Verify faces are correctly oriented - InverseAngle might be necessary
|
||||
PathLog.debug("Checking if faces are oriented correctly after rotation...")
|
||||
@@ -458,7 +459,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
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) # pylint: disable=unused-variable
|
||||
(rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
|
||||
|
||||
if rtn is True:
|
||||
faceNum = sub.replace('Face', '')
|
||||
@@ -466,7 +467,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
# Verify faces are correctly oriented - InverseAngle might be necessary
|
||||
faceIA = clnBase.Shape.getElement(sub)
|
||||
(norm, surf) = self.getFaceNormAndSurf(faceIA)
|
||||
(rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
|
||||
(rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
|
||||
if rtn is True:
|
||||
PathLog.debug("Face not aligned after initial rotation.")
|
||||
if obj.AttemptInverseAngle is True and obj.InverseAngle is False:
|
||||
@@ -492,8 +493,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
PathLog.error(translate('Path', "Selected feature is not a Face. Ignoring: {}".format(ignoreSub)))
|
||||
|
||||
for o in baseSubsTuples:
|
||||
self.horiz = [] # pylint: disable=attribute-defined-outside-init
|
||||
self.vert = [] # pylint: disable=attribute-defined-outside-init
|
||||
self.horiz = [] # pylint: disable=attribute-defined-outside-init
|
||||
self.vert = [] # pylint: disable=attribute-defined-outside-init
|
||||
subBase = o[0]
|
||||
subsList = o[1]
|
||||
angle = o[2]
|
||||
@@ -545,7 +546,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
|
||||
# move all horizontal faces to FinalDepth
|
||||
for f in self.horiz:
|
||||
finDep = max(obj.FinalDepth.Value, f.BoundBox.ZMin)
|
||||
finDep = obj.FinalDepth.Value # max(obj.FinalDepth.Value, f.BoundBox.ZMin)
|
||||
f.translate(FreeCAD.Vector(0, 0, finDep - f.BoundBox.ZMin))
|
||||
|
||||
# check all faces and see if they are touching/overlapping and combine those into a compound
|
||||
@@ -560,20 +561,17 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
else:
|
||||
self.horizontal.append(shape)
|
||||
|
||||
# extrude all faces up to StartDepth and those are the removal shapes
|
||||
sD = obj.StartDepth.Value
|
||||
fD = obj.FinalDepth.Value
|
||||
extent = FreeCAD.Vector(0, 0, sD - fD)
|
||||
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))
|
||||
self.removalshapes.append((face.removeSplitter().extrude(extent),
|
||||
False, 'pathPocketShape', angle, axis, sD, fD))
|
||||
PathLog.debug("Extent depths are str: {}, and fin: {}".format(sD, fD))
|
||||
# Efor face
|
||||
# Efor
|
||||
|
||||
# Adjust obj.FinalDepth.Value as needed.
|
||||
if len(finalDepths) > 0:
|
||||
finalDep = min(finalDepths)
|
||||
if subCount == 1:
|
||||
obj.FinalDepth.Value = finalDep
|
||||
else:
|
||||
# process the job base object as a whole
|
||||
PathLog.debug(translate("Path", 'Processing model as a whole ...'))
|
||||
|
||||
Reference in New Issue
Block a user