Skip to content

Commit

Permalink
Path: Improve 4th-axis rotation analysis and application
Browse files Browse the repository at this point in the history
Fix incorrect depth calculations for envelopes.
Added solid-based model orientation check developed in PathProfileEdges open edges upgrade.
Clean-up some blank indentations.
Down-grade some messaging levels.
Add a few debug comments.
  • Loading branch information
Russ4262 committed Mar 26, 2020
1 parent 60329f8 commit 0564739
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 36 deletions.
39 changes: 27 additions & 12 deletions src/Mod/Path/PathScripts/PathPocketShape.py
Expand Up @@ -42,7 +42,7 @@
__url__ = "http://www.freecadweb.org"
__doc__ = "Class and implementation of shape based Pocket operation."

PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())


Expand Down Expand Up @@ -435,7 +435,7 @@ def clasifySub(self, bs, sub):
if obj.Base:
PathLog.debug('Processing... obj.Base')
self.removalshapes = [] # pylint: disable=attribute-defined-outside-init
# ----------------------------------------------------------------------

if obj.EnableRotation == 'Off':
stock = PathUtils.findParentJob(obj).Stock
for (base, subList) in obj.Base:
Expand All @@ -450,11 +450,11 @@ def clasifySub(self, bs, sub):
(isLoop, norm, surf) = self.checkForFacesLoop(base, subsList)

if isLoop is True:
PathLog.info("Common Surface.Axis or normalAt() value found for loop faces.")
PathLog.debug("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
PathLog.info("angle: {}; axis: {}".format(angle, axis))
PathLog.debug("angle: {}; axis: {}".format(angle, axis))

if rtn is True:
faceNums = ""
Expand All @@ -471,13 +471,15 @@ def clasifySub(self, bs, sub):
rtn = False
PathLog.warning(translate("PathPocketShape", "Face appears to NOT be horizontal AFTER rotation applied."))
break

if rtn is False:
PathLog.debug(translate("Path", "Face appears misaligned after initial rotation."))
if obj.InverseAngle is False:
if obj.AttemptInverseAngle is True:
PathLog.debug("Applying the inverse angle.")
(clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
else:
PathLog.warning(translate("Path", "Consider toggling the InverseAngle property and recomputing the operation."))
msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
PathLog.warning(msg)

if angle < -180.0:
angle += 360.0
Expand Down Expand Up @@ -518,27 +520,39 @@ def clasifySub(self, bs, sub):

(norm, surf) = self.getFaceNormAndSurf(face)
(rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
PathLog.debug("initial {}".format(praInfo))

if rtn is True:
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)
(rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
(rtn, praAngle, praAxis, praInfo2) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
PathLog.debug("follow-up {}".format(praInfo2))

if praAxis == axis and abs(praAngle) == 180.0:
rtn = False
if self.isFaceUp(clnBase, faceIA) is False:
PathLog.debug('isFaceUp is False')
angle -= 180.0

if rtn is True:
PathLog.debug("Face not aligned after initial rotation.")
PathLog.debug(translate("Path", "Face appears misaligned after initial rotation."))
if obj.InverseAngle is False:
if obj.AttemptInverseAngle is True:
PathLog.debug("Applying the inverse angle.")
(clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
else:
PathLog.warning(translate("Path", "Consider toggling the InverseAngle property and recomputing the operation."))
msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
PathLog.warning(msg)

if self.isFaceUp(clnBase, faceIA) is False:
PathLog.debug('isFaceUp is False')
angle += 180.0
else:
PathLog.debug("Face appears to be oriented correctly.")

if angle < -180.0:
if angle < 0.0:
angle += 360.0

tup = clnBase, [sub], angle, axis, clnStock
Expand Down Expand Up @@ -650,8 +664,9 @@ def clasifySub(self, bs, sub):
if shpZMin > obj.FinalDepth.Value:
afD = shpZMin
if sD <= afD:
PathLog.error('Start Depth is lower than face depth.')
sD = afD + 1.0
msg = translate('PathPocketShape', 'Start Depth is lower than face depth. Setting to ')
PathLog.warning(msg + ' {} mm.'.format(sD))
else:
face.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - shpZMin))

Expand Down
71 changes: 47 additions & 24 deletions src/Mod/Path/PathScripts/PathProfileFaces.py
Expand Up @@ -43,6 +43,7 @@

PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())


# Qt translation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
Expand Down Expand Up @@ -132,22 +133,43 @@ def areaOpShapes(self, obj):
rtn = False
(norm, surf) = self.getFaceNormAndSurf(shape)
(rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
PathLog.debug("initial faceRotationAnalysis: {}".format(praInfo))
if rtn is True:
(clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis(obj, base, angle, axis, subCount)
# Verify faces are correctly oriented - InverseAngle might be necessary
faceIA = getattr(clnBase.Shape, sub)
(norm, surf) = self.getFaceNormAndSurf(faceIA)
(rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
(rtn, praAngle, praAxis, praInfo2) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable
PathLog.debug("follow-up faceRotationAnalysis: {}".format(praInfo2))

if praAxis == axis and abs(praAngle) == 180.0:
rtn = False
if self.isFaceUp(clnBase, faceIA) is False:
PathLog.debug('isFaceUp is False')
angle -= 180.0

if rtn is True:
PathLog.error(translate("Path", "Face appears misaligned after initial rotation."))
if obj.AttemptInverseAngle is True and obj.InverseAngle is False:
(clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
PathLog.debug(translate("Path", "Face appears misaligned after initial rotation."))
if obj.InverseAngle is False:
if obj.AttemptInverseAngle is True:
(clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle)
clnBase.recompute()
else:
msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
PathLog.warning(msg)

if self.isFaceUp(clnBase, faceIA) is False:
PathLog.debug('isFaceUp is False')
angle += 180.0
else:
msg = translate("Path", "Consider toggling the 'InverseAngle' property and recomputing.")
PathLog.error(msg)
PathLog.debug(' isFaceUp')

else:
PathLog.debug("Face appears to be oriented correctly.")

if angle < 0.0:
angle += 360.0

tup = clnBase, sub, tag, angle, axis, clnStock
else:
if self.warnDisabledAxis(obj, axis) is False:
Expand All @@ -157,21 +179,21 @@ def areaOpShapes(self, obj):
tag = base.Name + '_' + axis + str(angle).replace('.', '_')
stock = PathUtils.findParentJob(obj).Stock
tup = base, sub, tag, angle, axis, stock

allTuples.append(tup)

if subCount > 1:
msg = translate('Path', "Multiple faces in Base Geometry.") + " "
msg += translate('Path', "Depth settings will be applied to all faces.")
PathLog.warning(msg)

(Tags, Grps) = self.sortTuplesByIndex(allTuples, 2) # return (TagList, GroupList)
subList = []
for o in range(0, len(Tags)):
subList = []
for (base, sub, tag, angle, axis, stock) in Grps[o]:
subList.append(sub)

pair = base, subList, angle, axis, stock
baseSubsTuples.append(pair)
# Efor
Expand All @@ -196,7 +218,7 @@ def areaOpShapes(self, obj):
if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face
for wire in shape.Wires[1:]:
holes.append((base.Shape, wire))

# Add face depth to list
faceDepths.append(shape.BoundBox.ZMin)
else:
Expand All @@ -205,13 +227,12 @@ def areaOpShapes(self, obj):
PathLog.error(msg)
FreeCAD.Console.PrintWarning(msg)


# Set initial Start and Final Depths and recalculate depthparams
finDep = obj.FinalDepth.Value
strDep = obj.StartDepth.Value
if strDep > stock.Shape.BoundBox.ZMax:
strDep = stock.Shape.BoundBox.ZMax

startDepths.append(strDep)
self.depthparams = self._customDepthParams(obj, strDep, finDep)

Expand All @@ -230,43 +251,46 @@ def areaOpShapes(self, obj):
if obj.processPerimeter:
if obj.HandleMultipleFeatures == 'Collectively':
custDepthparams = self.depthparams

if obj.LimitDepthToFace is True and obj.EnableRotation != 'Off':
if profileshape.BoundBox.ZMin > obj.FinalDepth.Value:
finDep = profileshape.BoundBox.ZMin
custDepthparams = self._customDepthParams(obj, strDep, finDep - 0.5) # only an envelope
envDepthparams = self._customDepthParams(obj, strDep + 0.5, finDep) # only an envelope
try:
env = PathUtils.getEnvelope(base.Shape, subshape=profileshape, depthparams=custDepthparams)
# env = PathUtils.getEnvelope(base.Shape, subshape=profileshape, depthparams=envDepthparams)
env = PathUtils.getEnvelope(profileshape, depthparams=envDepthparams)
except Exception: # pylint: disable=broad-except
# PathUtils.getEnvelope() failed to return an object.
PathLog.error(translate('Path', 'Unable to create path for face(s).'))
else:
tup = env, False, 'pathProfileFaces', angle, axis, strDep, finDep
shapes.append(tup)

elif obj.HandleMultipleFeatures == 'Individually':
for shape in faces:
profShape = Part.makeCompound([shape])
# profShape = Part.makeCompound([shape])
finalDep = obj.FinalDepth.Value
custDepthparams = self.depthparams
if obj.Side == 'Inside':
if finalDep < shape.BoundBox.ZMin:
# Recalculate depthparams
finalDep = shape.BoundBox.ZMin
custDepthparams = self._customDepthParams(obj, strDep, finalDep - 0.5)

env = PathUtils.getEnvelope(base.Shape, subshape=profShape, depthparams=custDepthparams)
custDepthparams = self._customDepthParams(obj, strDep + 0.5, finalDep)

# env = PathUtils.getEnvelope(base.Shape, subshape=profShape, depthparams=custDepthparams)
env = PathUtils.getEnvelope(shape, depthparams=custDepthparams)
tup = env, False, 'pathProfileFaces', angle, axis, strDep, finalDep
shapes.append(tup)

# Lower high Start Depth to top of Stock
startDepth = max(startDepths)
if obj.StartDepth.Value > startDepth:
obj.StartDepth.Value = startDepth

else: # Try to build targets from the job base
if 1 == len(self.model):
if hasattr(self.model[0], "Proxy"):
PathLog.info("hasattr() Proxy")
PathLog.debug("hasattr() Proxy")
if isinstance(self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet
if obj.processCircles or obj.processHoles:
for shape in self.model[0].Proxy.getHoles(self.model[0], transform=True):
Expand Down Expand Up @@ -302,7 +326,7 @@ def areaOpSetDefaultValues(self, obj, job):
obj.InverseAngle = False
obj.AttemptInverseAngle = True
obj.LimitDepthToFace = True
obj.HandleMultipleFeatures = 'Collectively'
obj.HandleMultipleFeatures = 'Individually'


def SetupProperties():
Expand All @@ -321,6 +345,5 @@ def Create(name, obj=None):
'''Create(name) ... Creates and returns a Profile based on faces operation.'''
if obj is None:
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)

obj.Proxy = ObjectProfile(obj, name)
return obj

0 comments on commit 0564739

Please sign in to comment.