diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui index 6b5878a9ff..a7630a0a94 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui @@ -24,7 +24,7 @@ - + Tool Controller @@ -38,7 +38,7 @@ - + Coolant Mode @@ -64,12 +64,17 @@ - Perimeter + Boundbox - Boundbox + Face Region + + + + + Perimeter @@ -80,7 +85,7 @@ - + Boundary Shape @@ -96,7 +101,7 @@ QFormLayout::AllNonFixedFieldsGrow - + Cut Mode @@ -120,7 +125,7 @@ - + Pattern @@ -172,7 +177,7 @@ - + Angle @@ -186,7 +191,7 @@ - + Step Over Percent @@ -212,7 +217,7 @@ - + Material Allowance @@ -226,7 +231,7 @@ - + Enable Rotation diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index b2f2f699c3..09b93959cc 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -41,13 +41,10 @@ __author__ = "sliptonic (Brad Collette)" __url__ = "http://www.freecadweb.org" __doc__ = "Class and implementation of Mill Facing operation." __contributors__ = "russ4262 (Russell Johnson)" -__created__ = "2014" -__scriptVersion__ = "1d usable" -__lastModified__ = "2019-07-22 23:49 CST" PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) # Qt translation handling @@ -61,12 +58,12 @@ class ObjectFace(PathPocketBase.ObjectPocket): def initPocketOp(self, obj): '''initPocketOp(obj) ... create facing specific properties''' obj.addProperty("App::PropertyEnumeration", "BoundaryShape", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Shape to use for calculating Boundary")) - obj.BoundaryShape = ['Perimeter', 'Boundbox', 'Stock'] obj.addProperty("App::PropertyBool", "ClearEdges", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Clear edges of surface (Only applicable to BoundBox)")) - if not hasattr(obj, 'ExcludeRaisedAreas'): obj.addProperty("App::PropertyBool", "ExcludeRaisedAreas", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Exclude milling raised areas inside the face.")) + obj.BoundaryShape = ['Boundbox', 'Face Region', 'Perimeter', 'Stock'] + def pocketInvertExtraOffset(self): return True @@ -103,6 +100,8 @@ class ObjectFace(PathPocketBase.ObjectPocket): # Facing is done either against base objects holeShape = None + PathLog.debug('depthparams: {}'.format([i for i in self.depthparams])) + if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) faces = [] @@ -119,20 +118,22 @@ class ObjectFace(PathPocketBase.ObjectPocket): faces.append(shape) if shape.BoundBox.ZMin < minHeight: minHeight = shape.BoundBox.ZMin + # Limit to one model base per operation if oneBase[0] is not b[0]: oneBase[1] = False if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face + # Analyze internal closed wires to determine if raised or a recess for wire in shape.Wires[1:]: - if obj.ExcludeRaisedAreas is True: + if obj.ExcludeRaisedAreas: ip = self.isPocket(b[0], shape, wire) if ip is False: holes.append((b[0].Shape, wire)) else: holes.append((b[0].Shape, wire)) else: - PathLog.error('The base subobject, "{0}," is not a face. Ignoring "{0}."'.format(sub)) + PathLog.warning('The base subobject, "{0}," is not a face. Ignoring "{0}."'.format(sub)) - if obj.ExcludeRaisedAreas is True and len(holes) > 0: + if obj.ExcludeRaisedAreas and len(holes) > 0: for shape, wire in holes: f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(shape, subshape=f, depthparams=self.depthparams) @@ -152,8 +153,8 @@ class ObjectFace(PathPocketBase.ObjectPocket): bb = planeshape.BoundBox # Apply offset for clearing edges - offset = 0; - if obj.ClearEdges == True: + offset = 0 + if obj.ClearEdges: offset = self.radius + 0.1 bb.XMin = bb.XMin - offset @@ -164,7 +165,7 @@ class ObjectFace(PathPocketBase.ObjectPocket): if obj.BoundaryShape == 'Boundbox': bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1)) env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams) - if obj.ExcludeRaisedAreas is True and oneBase[1] is True: + if obj.ExcludeRaisedAreas and oneBase[1]: includedFaces = self.getAllIncludedFaces(oneBase[0], env, faceZ=minHeight) if len(includedFaces) > 0: includedShape = Part.makeCompound(includedFaces) @@ -174,23 +175,43 @@ class ObjectFace(PathPocketBase.ObjectPocket): stock = PathUtils.findParentJob(obj).Stock.Shape env = stock - if obj.ExcludeRaisedAreas is True and oneBase[1] is True: + if obj.ExcludeRaisedAreas and oneBase[1]: includedFaces = self.getAllIncludedFaces(oneBase[0], stock, faceZ=minHeight) if len(includedFaces) > 0: stockEnv = PathUtils.getEnvelope(partshape=stock, depthparams=self.depthparams) includedShape = Part.makeCompound(includedFaces) includedEnv = PathUtils.getEnvelope(oneBase[0].Shape, subshape=includedShape, depthparams=self.depthparams) env = stockEnv.cut(includedEnv) - else: - env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) + elif obj.BoundaryShape == 'Perimeter': + if obj.ClearEdges: + psZMin = planeshape.BoundBox.ZMin + ofstShape = PathSurfaceSupport.extractFaceOffset(planeshape, self.radius * 1.25, planeshape) + ofstShape.translate(FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin)) + env = PathUtils.getEnvelope(partshape=ofstShape, depthparams=self.depthparams) + else: + env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) + elif obj.BoundaryShape == 'Face Region': + import PathScripts.PathSurfaceSupport as PathSurfaceSupport + baseShape = oneBase[0].Shape + psZMin = planeshape.BoundBox.ZMin + ofstShape = PathSurfaceSupport.extractFaceOffset(planeshape, self.tool.Diameter * 1.1, planeshape) + ofstShape.translate(FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin)) + # custDepthparams = self._customDepthParams(obj, obj.StartDepth.Value, obj.FinalDepth.Value) # only an envelope + # ofstShapeEnv = PathUtils.getEnvelope(partshape=ofstShape, depthparams=self.depthparams) + # ofstShapeEnv.translate(FreeCAD.Vector(0.0, 0.0, -0.5)) + custDepthparams = self._customDepthParams(obj, obj.StartDepth.Value + 0.1, obj.FinalDepth.Value - 0.1) # only an envelope + ofstShapeEnv = PathUtils.getEnvelope(partshape=ofstShape, depthparams=custDepthparams) + env = ofstShapeEnv.cut(baseShape) - if holeShape is not None: - PathLog.info("Processing holes...") + if holeShape: + PathLog.debug("Processing holes and face ...") holeEnv = PathUtils.getEnvelope(partshape=holeShape, depthparams=self.depthparams) newEnv = env.cut(holeEnv) - return [(newEnv, False)] + tup = newEnv, False, 'pathMillFace', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value else: - return [(env, False)] + PathLog.debug("Processing solid face ...") + tup = env, False, 'pathMillFace', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value + return [tup] def areaOpSetDefaultValues(self, obj, job): '''areaOpSetDefaultValues(obj, job) ... initialize mill facing properties''' @@ -215,7 +236,7 @@ class ObjectFace(PathPocketBase.ObjectPocket): face = b.Shape.Faces[fi] for ei in range(0, len(face.Edges)): edge = face.Edges[ei] - if e.isSame(edge) is True: + if e.isSame(edge): if f is face: # Alternative: run loop to see if all edges are same pass # same source face, look for another @@ -226,7 +247,7 @@ class ObjectFace(PathPocketBase.ObjectPocket): def getAllIncludedFaces(self, base, env, faceZ): included = [] - + eXMin = env.BoundBox.XMin eXMax = env.BoundBox.XMax eYMin = env.BoundBox.YMin @@ -257,12 +278,12 @@ class ObjectFace(PathPocketBase.ObjectPocket): fYMax = face.BoundBox.YMax # fZMin = face.BoundBox.ZMin fZMax = face.BoundBox.ZMax - + if fZMax > eZMin: - if isOverlap(fXMin, fXMax, eXMin, eXMax) is True: - if isOverlap(fYMin, fYMax, eYMin, eYMax) is True: + if isOverlap(fXMin, fXMax, eXMin, eXMax): + if isOverlap(fYMin, fYMax, eYMin, eYMax): incl = True - if incl is True: + if incl: included.append(face) return included diff --git a/src/Mod/Path/PathScripts/PathPocketBase.py b/src/Mod/Path/PathScripts/PathPocketBase.py index 702ba5b8d4..62a9c70bf1 100644 --- a/src/Mod/Path/PathScripts/PathPocketBase.py +++ b/src/Mod/Path/PathScripts/PathPocketBase.py @@ -71,17 +71,18 @@ class ObjectPocket(PathAreaOp.ObjectOp): # Pocket Properties obj.addProperty("App::PropertyEnumeration", "CutMode", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise (CW) or CounterClockWise (CCW)")) - obj.CutMode = ['Climb', 'Conventional'] obj.addProperty("App::PropertyDistance", "ExtraOffset", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra offset to apply to the operation. Direction is operation dependent.")) obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Start pocketing at center or boundary")) - obj.StartAt = ['Center', 'Edge'] obj.addProperty("App::PropertyPercent", "StepOver", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Percent of cutter diameter to step over on each pass")) obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern")) obj.addProperty("App::PropertyEnumeration", "OffsetPattern", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Clearing pattern to use")) - obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle'] obj.addProperty("App::PropertyBool", "MinTravel", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Use 3D Sorting of Path")) obj.addProperty("App::PropertyBool", "KeepToolDown", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Attempts to avoid unnecessary retractions.")) + obj.CutMode = ['Climb', 'Conventional'] + obj.StartAt = ['Center', 'Edge'] + obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle'] + self.initPocketOp(obj) def areaOpRetractTool(self, obj): @@ -141,4 +142,7 @@ def SetupProperties(): setup.append('StepOver') setup.append('ZigZagAngle') setup.append('OffsetPattern') + setup.append('StartAt') + setup.append('MinTravel') + setup.append('KeepToolDown') return setup