From 4145e287c04739f07049f0cc6d197ea3e81f074f Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Sat, 21 Mar 2020 15:46:42 -0500 Subject: [PATCH] Path: Improve 4th-axis rotation analysis and application 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. --- src/Mod/Path/PathScripts/PathPocketShape.py | 39 +++++++---- src/Mod/Path/PathScripts/PathProfileFaces.py | 71 +++++++++++++------- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathPocketShape.py b/src/Mod/Path/PathScripts/PathPocketShape.py index 0271809bb5..56e238b007 100644 --- a/src/Mod/Path/PathScripts/PathPocketShape.py +++ b/src/Mod/Path/PathScripts/PathPocketShape.py @@ -42,7 +42,7 @@ __author__ = "sliptonic (Brad Collette)" __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()) @@ -435,7 +435,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): 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: @@ -450,11 +450,11 @@ class ObjectPocket(PathPocketBase.ObjectPocket): (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 = "" @@ -471,13 +471,15 @@ class ObjectPocket(PathPocketBase.ObjectPocket): 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 @@ -518,6 +520,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): (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', '') @@ -525,20 +528,31 @@ 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, 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 @@ -650,8 +664,9 @@ class ObjectPocket(PathPocketBase.ObjectPocket): 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)) diff --git a/src/Mod/Path/PathScripts/PathProfileFaces.py b/src/Mod/Path/PathScripts/PathProfileFaces.py index 506d5a1d00..68fa413cd5 100644 --- a/src/Mod/Path/PathScripts/PathProfileFaces.py +++ b/src/Mod/Path/PathScripts/PathProfileFaces.py @@ -43,6 +43,7 @@ __doc__ = "Path Profile operation based on faces." PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) + # Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) @@ -132,22 +133,43 @@ class ObjectProfile(PathProfileBase.ObjectProfile): 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: @@ -157,21 +179,21 @@ class ObjectProfile(PathProfileBase.ObjectProfile): 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 @@ -196,7 +218,7 @@ class ObjectProfile(PathProfileBase.ObjectProfile): 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: @@ -205,13 +227,12 @@ class ObjectProfile(PathProfileBase.ObjectProfile): 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) @@ -230,31 +251,34 @@ class ObjectProfile(PathProfileBase.ObjectProfile): 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) @@ -262,11 +286,11 @@ class ObjectProfile(PathProfileBase.ObjectProfile): 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): @@ -302,7 +326,7 @@ class ObjectProfile(PathProfileBase.ObjectProfile): obj.InverseAngle = False obj.AttemptInverseAngle = True obj.LimitDepthToFace = True - obj.HandleMultipleFeatures = 'Collectively' + obj.HandleMultipleFeatures = 'Individually' def SetupProperties(): @@ -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