diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui index d1a81ae2c1..de4c2d5f77 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui @@ -131,6 +131,12 @@ <html><head/><body><p>Pattern the tool bit is moved in to clear the material.</p></body></html> + + + + + ZigZag + ZigZag @@ -138,7 +144,7 @@ - Offset + Offset @@ -222,6 +228,37 @@ + + + + Enable Rotation + + + + + + + + Off + + + + + A(x) + + + + + B(y) + + + + + A & B + + + + diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpProfileFullEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpProfileFullEdit.ui index dba10c387d..5a5ca84a85 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpProfileFullEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpProfileFullEdit.ui @@ -56,6 +56,9 @@ + + + @@ -125,6 +128,37 @@ + + + + Enable Rotation + + + + + + + + Off + + + + + A(x) + + + + + B(y) + + + + + A & B + + + + diff --git a/src/Mod/Path/PathScripts/PathAreaOp.py b/src/Mod/Path/PathScripts/PathAreaOp.py index 965e5d89e5..b63d7f5974 100644 --- a/src/Mod/Path/PathScripts/PathAreaOp.py +++ b/src/Mod/Path/PathScripts/PathAreaOp.py @@ -3,6 +3,7 @@ # *************************************************************************** # * * # * Copyright (c) 2017 sliptonic * +# * Copyright (c) 2020 russ4262 (Russell Johnson) * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -392,15 +393,7 @@ class ObjectOp(PathOp.ObjectOp): self.axialRapid = 360 / safeCircum * self.horizRapid # pylint: disable=attribute-defined-outside-init # 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( # pylint: disable=attribute-defined-outside-init - clearance_height=obj.ClearanceHeight.Value, - safe_height=obj.SafeHeight.Value, - start_depth=obj.StartDepth.Value, - step_down=obj.StepDown.Value, - z_finish_step=finish_step, - final_depth=obj.FinalDepth.Value, - user_depths=None) + self.depthparams = self._customDepthParams(obj, obj.StartDepth.Value, obj.FinalDepth.Value) # Set start point if PathOp.FeatureStartPoint & self.opFeatures(obj) and obj.UseStartPoint: @@ -446,15 +439,7 @@ class ObjectOp(PathOp.ObjectOp): else: nextAxis = 'L' - finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0 - self.depthparams = PathUtils.depth_params( # pylint: disable=attribute-defined-outside-init - clearance_height=obj.ClearanceHeight.Value, - safe_height=obj.SafeHeight.Value, - start_depth=strDep, # obj.StartDepth.Value, - step_down=obj.StepDown.Value, - z_finish_step=finish_step, - final_depth=finDep, # obj.FinalDepth.Value, - user_depths=None) + self.depthparams = self._customDepthParams(obj, strDep, finDep) try: if self.profileEdgesIsOpen is True: @@ -475,19 +460,14 @@ class ObjectOp(PathOp.ObjectOp): 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', {})) + ppCmds.insert(0, Path.Command('G0', {axisOfRot: angle, 'F': self.axialRapid})) # Raise cutter to safe depth and return index to starting position - 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})) @@ -761,7 +741,7 @@ class ObjectOp(PathOp.ObjectOp): xAx = 'xAxCyl' yAx = 'yAxCyl' # zAx = 'zAxCyl' - FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "visualAxis") + VA = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "visualAxis") if FreeCAD.GuiUp: FreeCADGui.ActiveDocument.getObject('visualAxis').Visibility = False vaGrp = FreeCAD.ActiveDocument.getObject("visualAxis") @@ -793,6 +773,7 @@ class ObjectOp(PathOp.ObjectOp): cylGui.Transparency = 85 cylGui.Visibility = False vaGrp.addObject(cyl) + VA.purgeTouched() def useTempJobClones(self, cloneName): '''useTempJobClones(cloneName) @@ -808,6 +789,8 @@ class ObjectOp(PathOp.ObjectOp): for cln in FreeCAD.ActiveDocument.getObject('rotJobClones').Group: FreeCAD.ActiveDocument.removeObject(cln.Name) FreeCAD.ActiveDocument.removeObject('rotJobClones') + else: + FreeCAD.ActiveDocument.getObject('rotJobClones').purgeTouched() else: FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "rotJobClones") if FreeCAD.GuiUp: @@ -917,7 +900,7 @@ class ObjectOp(PathOp.ObjectOp): obj.AttemptInverseAngle = False angle = -1 * angle - PathLog.info(translate("Path", "Rotated to inverse angle.")) + PathLog.debug(translate("Path", "Rotated to inverse angle.")) return (clnBase, clnStock, angle) def sortTuplesByIndex(self, TupleList, tagIdx): @@ -960,3 +943,28 @@ class ObjectOp(PathOp.ObjectOp): return True else: return False + + def isFaceUp(self, base, face): + up = face.extrude(FreeCAD.Vector(0.0, 0.0, 5.0)) + dwn = face.extrude(FreeCAD.Vector(0.0, 0.0, -5.0)) + upCmn = base.Shape.common(up) + dwnCmn = base.Shape.common(dwn) + if upCmn.Volume == 0.0: + return True + elif dwnCmn.Volume == 0.0: + return False + if dwnCmn.Volume > upCmn.Volume: + return True + return False + + def _customDepthParams(self, obj, strDep, finDep): + finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0 + cdp = PathUtils.depth_params( + clearance_height=obj.ClearanceHeight.Value, + safe_height=obj.SafeHeight.Value, + start_depth=strDep, + step_down=obj.StepDown.Value, + z_finish_step=finish_step, + final_depth=finDep, + user_depths=None) + return cdp diff --git a/src/Mod/Path/PathScripts/PathCircularHoleBase.py b/src/Mod/Path/PathScripts/PathCircularHoleBase.py index 986c79d539..eea0deda3b 100644 --- a/src/Mod/Path/PathScripts/PathCircularHoleBase.py +++ b/src/Mod/Path/PathScripts/PathCircularHoleBase.py @@ -333,6 +333,7 @@ class ObjectOp(PathOp.ObjectOp): 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) + finDep = holeBtm holes.append({'x': pos.x, 'y': pos.y, 'r': self.holeDiameter(obj, base, sub), 'angle': angle, 'axis': axis, 'trgtDep': finDep, @@ -440,7 +441,7 @@ class ObjectOp(PathOp.ObjectOp): zlim = 0.0 xRotRad = 0.01 yRotRad = 0.01 - xRotRad = 0.01 + #xRotRad = 0.01 # Determine boundbox radius based upon xzy limits data if math.fabs(self.stockBB.ZMin) > math.fabs(self.stockBB.ZMax): diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index 99d3b8ac74..96d6a4b637 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -3,6 +3,8 @@ # *************************************************************************** # * * # * Copyright (c) 2014 Yorik van Havre * +# * Copyright (c) 2020 russ4262 (Russell Johnson) * +# * Copyright (c) 2020 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -43,13 +45,9 @@ __title__ = "Path Drilling Operation" __author__ = "sliptonic (Brad Collette)" __url__ = "http://www.freecadweb.org" __doc__ = "Path Drilling operation." -__contributors__ = "russ4262 (Russell Johnson), IMBack!" -__created__ = "2014" -__scriptVersion__ = "1c testing" -__lastModified__ = "2019-06-25 14:49 CST" PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) # Qt translation handling @@ -86,8 +84,6 @@ class ObjectDrilling(PathCircularHoleBase.ObjectOp): obj.addProperty('App::PropertyBool', 'ReverseDirection', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Reverse direction of pocket operation.')) if not hasattr(obj, 'InverseAngle'): obj.addProperty('App::PropertyBool', 'InverseAngle', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Inverse the angle. Example: -22.5 -> 22.5 degrees.')) - if not hasattr(obj, 'B_AxisErrorOverride'): - obj.addProperty('App::PropertyBool', 'B_AxisErrorOverride', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Match B rotations to model (error in FreeCAD rendering).')) if not hasattr(obj, 'AttemptInverseAngle'): obj.addProperty('App::PropertyBool', 'AttemptInverseAngle', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Attempt the inverse angle for face access if original rotation fails.')) @@ -113,7 +109,7 @@ class ObjectDrilling(PathCircularHoleBase.ObjectOp): holes = PathUtils.sort_jobs(holes, ['x', 'y']) self.commandlist.append(Path.Command('G90')) self.commandlist.append(Path.Command(obj.ReturnLevel)) - + for p in holes: cmd = "G81" cmdParams = {} @@ -139,9 +135,6 @@ class ObjectDrilling(PathCircularHoleBase.ObjectOp): 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: @@ -163,13 +156,13 @@ class ObjectDrilling(PathCircularHoleBase.ObjectOp): self.commandlist.append(Path.Command('G0', {axisOfRot: angle, 'F': self.axialRapid})) self.commandlist.append(Path.Command('G0', {'X': p['x'], 'Y': p['y'], 'F': self.horizRapid})) self.commandlist.append(Path.Command('G1', {'Z': p['stkTop'], 'F': self.vertFeed})) - - # Perform and cancel canned drilling cycle + + # Perform canned drilling cycle self.commandlist.append(Path.Command(cmd, params)) + + # Cancel canned drilling cycle self.commandlist.append(Path.Command('G80')) self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value})) - - # shift axis and angle values if obj.EnableRotation != 'Off': @@ -180,30 +173,28 @@ class ObjectDrilling(PathCircularHoleBase.ObjectOp): self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) self.commandlist.append(Path.Command('G0', {lastAxis: 0.0, 'F': self.axialRapid})) - def opSetDefaultValues(self, obj, job): '''opSetDefaultValues(obj, job) ... set default value for RetractHeight''' - + parentJob = PathUtils.findParentJob(obj) if hasattr(parentJob.SetupSheet, 'RetractHeight'): obj.RetractHeight = parentJob.SetupSheet.RetractHeight elif self.applyExpression(obj, 'RetractHeight', 'OpStartDepth+1mm'): obj.RetractHeight = 10 - + if hasattr(parentJob.SetupSheet, 'PeckDepth'): obj.PeckDepth = parentJob.SetupSheet.PeckDepth elif self.applyExpression(obj, 'PeckDepth', 'OpToolDiameter*0.75'): obj.PeckDepth = 1 - + if hasattr(parentJob.SetupSheet, 'DwellTime'): obj.DwellTime = parentJob.SetupSheet.DwellTime else: obj.DwellTime = 1 - + obj.ReverseDirection = False obj.InverseAngle = False - obj.B_AxisErrorOverride = False obj.AttemptInverseAngle = False obj.ExtraOffset = "None" @@ -228,7 +219,6 @@ def SetupProperties(): setup.append("EnableRotation") setup.append("ReverseDirection") setup.append("InverseAngle") - setup.append("B_AxisErrorOverride") setup.append("AttemptInverseAngle") return setup @@ -237,7 +227,9 @@ def Create(name, obj=None): '''Create(name) ... Creates and returns a Drilling operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) + obj.Proxy = ObjectDrilling(obj, name) if obj.Proxy: obj.Proxy.findAllHoles(obj) + return obj diff --git a/src/Mod/Path/PathScripts/PathPocketBase.py b/src/Mod/Path/PathScripts/PathPocketBase.py index c3ac4512af..ba3582f6b8 100644 --- a/src/Mod/Path/PathScripts/PathPocketBase.py +++ b/src/Mod/Path/PathScripts/PathPocketBase.py @@ -3,6 +3,7 @@ # *************************************************************************** # * * # * Copyright (c) 2017 sliptonic * +# * Copyright (c) 2020 russ4262 (Russell Johnson) * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -34,12 +35,14 @@ __url__ = "http://www.freecadweb.org" __doc__ = "Base class and implementation for Path pocket operations." PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) + # Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) + class ObjectPocket(PathAreaOp.ObjectOp): '''Base class for proxy objects of all pocket operations.''' @@ -130,6 +133,7 @@ class ObjectPocket(PathAreaOp.ObjectOp): params['threshold'] = self.radius * 2 return params + def SetupProperties(): setup = [] setup.append('CutMode') diff --git a/src/Mod/Path/PathScripts/PathPocketBaseGui.py b/src/Mod/Path/PathScripts/PathPocketBaseGui.py index ad8dda997c..e9aae91d9d 100644 --- a/src/Mod/Path/PathScripts/PathPocketBaseGui.py +++ b/src/Mod/Path/PathScripts/PathPocketBaseGui.py @@ -104,6 +104,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): obj.StepOver = self.form.stepOverPercent.value() if obj.OffsetPattern != str(self.form.offsetPattern.currentText()): obj.OffsetPattern = str(self.form.offsetPattern.currentText()) + if obj.EnableRotation != str(self.form.enableRotation.currentText()): + obj.EnableRotation = str(self.form.enableRotation.currentText()) PathGui.updateInputField(obj, 'ExtraOffset', self.form.extraOffset) self.updateToolController(obj, self.form.toolController) @@ -143,6 +145,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.selectInComboBox(obj.CutMode, self.form.cutMode) self.setupToolController(obj, self.form.toolController) self.setupCoolant(obj, self.form.coolantController) + self.selectInComboBox(obj.EnableRotation, self.form.enableRotation) if FeatureFacing & self.pocketFeatures(): self.selectInComboBox(obj.BoundaryShape, self.form.boundaryShape) @@ -162,6 +165,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): signals.append(self.form.useOutline.clicked) signals.append(self.form.minTravel.clicked) signals.append(self.form.coolantController.currentIndexChanged) + signals.append(self.form.enableRotation.currentIndexChanged) if FeatureFacing & self.pocketFeatures(): signals.append(self.form.boundaryShape.currentIndexChanged) diff --git a/src/Mod/Path/PathScripts/PathPocketShape.py b/src/Mod/Path/PathScripts/PathPocketShape.py index f89a613870..16fb8035dc 100644 --- a/src/Mod/Path/PathScripts/PathPocketShape.py +++ b/src/Mod/Path/PathScripts/PathPocketShape.py @@ -3,6 +3,8 @@ # *************************************************************************** # * * # * Copyright (c) 2017 sliptonic * +# * Copyright (c) 2020 russ4262 (Russell Johnson) * +# * Copyright (c) 2020 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -31,6 +33,7 @@ import PathScripts.PathPocketBase as PathPocketBase import PathScripts.PathUtils as PathUtils import TechDraw import math +import Draft from PySide import QtCore @@ -38,12 +41,8 @@ __title__ = "Path Pocket Shape Operation" __author__ = "sliptonic (Brad Collette)" __url__ = "http://www.freecadweb.org" __doc__ = "Class and implementation of shape based Pocket operation." -__contributors__ = "russ4262 (Russell Johnson)" -__created__ = "2017" -__scriptVersion__ = "2i" -__lastModified__ = "2020-02-13 17:01 CST" -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) +PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) # PathLog.trackModule(PathLog.thisModule()) @@ -63,11 +62,14 @@ def endPoints(edgeOrWire): cnt = len([p2 for p2 in pts if PathGeom.pointsCoincide(p, p2)]) if 1 == cnt: unique.append(p) + return unique + pfirst = edgeOrWire.valueAt(edgeOrWire.FirstParameter) plast = edgeOrWire.valueAt(edgeOrWire.LastParameter) if PathGeom.pointsCoincide(pfirst, plast): return None + return [pfirst, plast] @@ -76,6 +78,7 @@ def includesPoint(p, pts): for pt in pts: if PathGeom.pointsCoincide(p, pt): return True + return False @@ -86,8 +89,10 @@ def selectOffsetWire(feature, wires): dist = feature.distToShape(w)[0] if closest is None or dist > closest[0]: # pylint: disable=unsubscriptable-object closest = (dist, w) + if closest is not None: return closest[1] + return None @@ -114,6 +119,7 @@ def extendWire(feature, wire, length): edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[1]))) + return Part.Wire(edges) return None @@ -148,6 +154,7 @@ class Extension(object): wire = Part.Wire([e0, e1, e2, e3]) self.wire = wire return wire + return extendWire(feature, Part.Wire([e0]), self.length.Value) def _getEdgeNumbers(self): @@ -155,6 +162,7 @@ class Extension(object): numbers = [nr for nr in self.sub[5:-1].split(',')] else: numbers = [self.sub[4:]] + PathLog.debug("_getEdgeNumbers() -> %s" % numbers) return numbers @@ -169,8 +177,10 @@ class Extension(object): poffMinus = p0 - 0.01 * normal if not self.obj.Shape.isInside(poffPlus, 0.005, True): return normal + if not self.obj.Shape.isInside(poffMinus, 0.005, True): return normal.negative() + return None def _getDirection(self, wire): @@ -181,6 +191,7 @@ class Extension(object): normal = tangent.cross(FreeCAD.Vector(0, 0, 1)) if PathGeom.pointsCoincide(normal, FreeCAD.Vector(0, 0, 0)): return None + return self._getDirectedNormal(e0.valueAt(midparam), normal.normalize()) def getWire(self): @@ -209,6 +220,7 @@ class Extension(object): r = circle.Radius - self.length.Value else: r = circle.Radius + self.length.Value + # assuming the offset produces a valid circle - go for it if r > 0: e3 = Part.makeCircle(r, circle.Center, circle.Axis, edge.FirstParameter * 180 / math.pi, edge.LastParameter * 180 / math.pi) @@ -217,7 +229,9 @@ class Extension(object): e0 = Part.makeLine(edge.valueAt(edge.FirstParameter), e3.valueAt(e3.FirstParameter)) e2 = Part.makeLine(edge.valueAt(edge.LastParameter), e3.valueAt(e3.LastParameter)) return Part.Wire([e0, edge, e2, e3]) + return Part.Wire([e3]) + # the extension is bigger than the hole - so let's just cover the whole hole if endPoints(edge): # if the resulting arc is smaller than the radius, create a pie slice @@ -226,6 +240,7 @@ class Extension(object): e0 = Part.makeLine(center, edge.valueAt(edge.FirstParameter)) e2 = Part.makeLine(edge.valueAt(edge.LastParameter), center) return Part.Wire([e0, edge, e2]) + PathLog.track() return Part.Wire([edge]) @@ -234,8 +249,10 @@ class Extension(object): direction = self._getDirection(sub) if direction is None: return None + # return self._extendEdge(feature, edge, direction) return self._extendEdge(feature, edges[0], direction) + return extendWire(feature, sub, self.length.Value) @@ -257,20 +274,41 @@ class ObjectPocket(PathPocketBase.ObjectPocket): obj.addProperty('App::PropertyBool', 'ExtensionCorners', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'When enabled connected extension edges are combined to wires.')) obj.ExtensionCorners = True + obj.setEditorMode('ExtensionFeature', 2) + self.initRotationOp(obj) + + def initRotationOp(self, obj): + '''initRotationOp(obj) ... setup receiver for rotation''' if not hasattr(obj, 'ReverseDirection'): obj.addProperty('App::PropertyBool', 'ReverseDirection', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Reverse direction of pocket operation.')) if not hasattr(obj, 'InverseAngle'): obj.addProperty('App::PropertyBool', 'InverseAngle', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Inverse the angle. Example: -22.5 -> 22.5 degrees.')) - if not hasattr(obj, 'B_AxisErrorOverride'): - obj.addProperty('App::PropertyBool', 'B_AxisErrorOverride', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Match B rotations to model (error in FreeCAD rendering).')) if not hasattr(obj, 'AttemptInverseAngle'): obj.addProperty('App::PropertyBool', 'AttemptInverseAngle', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Attempt the inverse angle for face access if original rotation fails.')) + if not hasattr(obj, 'LimitDepthToFace'): + obj.addProperty('App::PropertyBool', 'LimitDepthToFace', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Enforce the Z-depth of the selected face as the lowest value for final depth. Higher user values will be observed.')) - obj.setEditorMode('ExtensionFeature', 2) + def areaOpOnChanged(self, obj, prop): + '''areaOpOnChanged(obj, porp) ... process operation specific changes to properties.''' + if prop == 'EnableRotation': + self.setEditorProperties(obj) + + def setEditorProperties(self, obj): + if obj.EnableRotation == 'Off': + obj.setEditorMode('ReverseDirection', 2) + obj.setEditorMode('InverseAngle', 2) + obj.setEditorMode('AttemptInverseAngle', 2) + obj.setEditorMode('LimitDepthToFace', 2) + else: + obj.setEditorMode('ReverseDirection', 0) + obj.setEditorMode('InverseAngle', 0) + obj.setEditorMode('AttemptInverseAngle', 0) + obj.setEditorMode('LimitDepthToFace', 0) def areaOpOnDocumentRestored(self, obj): - '''opOnDocumentRestored(obj) ... adds the UseOutline property if it doesn't exist.''' + '''opOnDocumentRestored(obj) ... adds the UseOutline property, others, if they doesn't exist.''' self.initPocketOp(obj) + self.setEditorProperties(obj) def pocketInvertExtraOffset(self): return False @@ -296,6 +334,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): 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 @@ -309,11 +348,14 @@ class ObjectPocket(PathPocketBase.ObjectPocket): else: 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() + if minArea == 0.0: minArea = tmpFace.Shape.Face1.Area useFace = fName @@ -324,8 +366,10 @@ class ObjectPocket(PathPocketBase.ObjectPocket): useFace = fName else: FreeCAD.ActiveDocument.removeObject(fName) + if useFace != 'useFaceName': self.useTempJobClones(useFace) + return (planar, useFace) def clasifySub(self, bs, sub): @@ -338,12 +382,15 @@ class ObjectPocket(PathPocketBase.ObjectPocket): # it's a flat horizontal face self.horiz.append(face) return True + elif PathGeom.isHorizontal(face.Surface.Axis): PathLog.debug(' -isHorizontal()') self.vert.append(face) return True + else: return False + elif type(face.Surface) == Part.Cylinder and PathGeom.isVertical(face.Surface.Axis): PathLog.debug('type() == Part.Cylinder') # vertical cylinder wall @@ -355,11 +402,13 @@ class ObjectPocket(PathPocketBase.ObjectPocket): disk.translate(FreeCAD.Vector(0, 0, face.BoundBox.ZMin - disk.BoundBox.ZMin)) self.horiz.append(disk) return True + else: PathLog.debug(' -none isClosed()') # partial cylinder wall self.vert.append(face) return True + elif type(face.Surface) == Part.SurfaceOfExtrusion: # extrusion wall PathLog.debug('type() == Part.SurfaceOfExtrusion') @@ -378,6 +427,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): # 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(face.Surface): {}'.format(type(face.Surface))) return False @@ -398,6 +448,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): # First, check all subs collectively for loop of faces if len(subsList) > 2: (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 @@ -418,12 +469,18 @@ class ObjectPocket(PathPocketBase.ObjectPocket): if type(face.Surface) == Part.Plane: if not PathGeom.isHorizontal(face.Surface.Axis): rtn = False + PathLog.warning(translate("PathPocketShape", "Face appears to NOT be horizontal AFTER rotation applied.")) break if rtn is False: - if obj.AttemptInverseAngle is True and obj.InverseAngle is False: - (clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle) - else: - PathLog.info(translate("Path", "Consider toggling the InverseAngle property and recomputing the operation.")) + 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.")) + + if angle < -180.0: + angle += 360.0 tup = clnBase, subsList, angle, axis, clnStock else: @@ -434,6 +491,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): stock = PathUtils.findParentJob(obj).Stock tup = base, subsList, angle, axis, stock # Eif + allTuples.append(tup) baseSubsTuples.append(tup) # Eif @@ -468,15 +526,21 @@ class ObjectPocket(PathPocketBase.ObjectPocket): faceIA = clnBase.Shape.getElement(sub) (norm, surf) = self.getFaceNormAndSurf(faceIA) (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: - (clnBase, clnStock, angle) = self.applyInverseAngle(obj, clnBase, clnStock, axis, angle) - else: - PathLog.info(translate("Path", "Consider toggling the InverseAngle property and recomputing the operation.")) + 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.")) else: PathLog.debug("Face appears to be oriented correctly.") + if angle < -180.0: + angle += 360.0 + tup = clnBase, [sub], angle, axis, clnStock else: if self.warnDisabledAxis(obj, axis) is False: @@ -506,7 +570,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): 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.')) + PathLog.warning(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 @@ -545,30 +609,56 @@ class ObjectPocket(PathPocketBase.ObjectPocket): self.exts.append(face) # move all horizontal faces to FinalDepth - for f in self.horiz: - finDep = obj.FinalDepth.Value # max(obj.FinalDepth.Value, f.BoundBox.ZMin) - f.translate(FreeCAD.Vector(0, 0, finDep - f.BoundBox.ZMin)) + # for f in self.horiz: + # f.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - f.BoundBox.ZMin)) # check all faces and see if they are touching/overlapping and combine those into a compound self.horizontal = [] # pylint: disable=attribute-defined-outside-init for shape in PathGeom.combineConnectedShapes(self.horiz): shape.sewShape() # shape.tessellate(0.1) + shpZMin = shape.BoundBox.ZMin + PathLog.debug('PathGeom.combineConnectedShapes shape.BoundBox.ZMin: {}'.format(shape.BoundBox.ZMin)) if obj.UseOutline: wire = TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) - wire.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - wire.BoundBox.ZMin)) - self.horizontal.append(Part.Face(wire)) + wFace = Part.Face(wire) + if wFace.BoundBox.ZMin != shpZMin: + wFace.translate(FreeCAD.Vector(0, 0, shpZMin - wFace.BoundBox.ZMin)) + self.horizontal.append(wFace) + PathLog.debug('PathGeom.combineConnectedShapes shape.BoundBox.ZMin: {}'.format(wFace.BoundBox.ZMin)) 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) + clrnc = 0.5 for face in self.horizontal: - self.removalshapes.append((face.removeSplitter().extrude(extent), - False, 'pathPocketShape', angle, axis, sD, fD)) - PathLog.debug("Extent depths are str: {}, and fin: {}".format(sD, fD)) + afD = fD + useAngle = angle + shpZMin = face.BoundBox.ZMin + PathLog.debug('self.horizontal shpZMin: {}'.format(shpZMin)) + if self.isFaceUp(subBase, face) is False: + useAngle += 180.0 + invZ = (-2 * shpZMin) - clrnc + face.translate(FreeCAD.Vector(0.0, 0.0, invZ)) + shpZMin = -1 * shpZMin + else: + face.translate(FreeCAD.Vector(0.0, 0.0, -1 * clrnc)) + + if obj.LimitDepthToFace is True and obj.EnableRotation != 'Off': + if shpZMin > obj.FinalDepth.Value: + afD = shpZMin + if sD <= afD: + PathLog.error('Start Depth is lower than face depth.') + sD = afD + 1.0 + else: + face.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - shpZMin)) + + extent = FreeCAD.Vector(0, 0, sD - afD + clrnc) + extShp = face.removeSplitter().extrude(extent) + self.removalshapes.append((extShp, False, 'pathPocketShape', useAngle, axis, sD, afD)) + PathLog.debug("Extent values are strDep: {}, finDep: {}, extrd: {}".format(sD, afD, extent)) # Efor face # Efor @@ -604,8 +694,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket): obj.UseOutline = False obj.ReverseDirection = False obj.InverseAngle = False - obj.B_AxisErrorOverride = False obj.AttemptInverseAngle = True + obj.LimitDepthToFace = True obj.setExpression('ExtensionLengthDefault', 'OpToolDiameter / 2') def createExtension(self, obj, extObj, extFeature, extSub): @@ -792,8 +882,8 @@ def SetupProperties(): setup.append('ExtensionCorners') setup.append("ReverseDirection") setup.append("InverseAngle") - setup.append("B_AxisErrorOverride") setup.append("AttemptInverseAngle") + setup.append("LimitDepthToFace") return setup diff --git a/src/Mod/Path/PathScripts/PathProfileBase.py b/src/Mod/Path/PathScripts/PathProfileBase.py index b406dd127b..07d7cb793d 100644 --- a/src/Mod/Path/PathScripts/PathProfileBase.py +++ b/src/Mod/Path/PathScripts/PathProfileBase.py @@ -3,6 +3,8 @@ # *************************************************************************** # * * # * Copyright (c) 2017 sliptonic * +# * Copyright (c) 2020 Schildkroet * +# * Copyright (c) 2020 russ4262 (Russell Johnson) * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -28,12 +30,13 @@ import PathScripts.PathLog as PathLog from PySide import QtCore __title__ = "Base Path Profile Operation" -__author__ = "sliptonic (Brad Collette)" +__author__ = "sliptonic (Brad Collette), Schildkroet" __url__ = "http://www.freecadweb.org" __doc__ = "Base class and implementation for Path profile operations." PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) + # Qt translation handling def translate(context, text, disambig=None): @@ -74,9 +77,23 @@ class ObjectProfile(PathAreaOp.ObjectOp): else: obj.setEditorMode('MiterLimit', 2) + self.extraOpOnChanged(obj, prop) + + def extraOpOnChanged(self, obj, prop): + '''otherOpOnChanged(obj, porp) ... overwrite to process onChange() events. + Can safely be overwritten by subclasses.''' + pass # pylint: disable=unnecessary-pass + + def setOpEditorProperties(self, obj): + '''setOpEditorProperties(obj, porp) ... overwrite to process operation specific changes to properties. + Can safely be overwritten by subclasses.''' + pass # pylint: disable=unnecessary-pass + def areaOpOnDocumentRestored(self, obj): for prop in ['UseComp', 'JoinType']: self.areaOpOnChanged(obj, prop) + + self.setOpEditorProperties(obj) def areaOpAreaParams(self, obj, isHole): '''areaOpAreaParams(obj, isHole) ... returns dictionary with area parameters. @@ -118,6 +135,7 @@ class ObjectProfile(PathAreaOp.ObjectOp): params['orientation'] = 0 else: params['orientation'] = 1 + if not obj.UseComp: if direction == 'CCW': params['orientation'] = 1 @@ -140,6 +158,7 @@ class ObjectProfile(PathAreaOp.ObjectOp): obj.JoinType = "Round" obj.MiterLimit = 0.1 + def SetupProperties(): setup = [] setup.append('Side') diff --git a/src/Mod/Path/PathScripts/PathProfileBaseGui.py b/src/Mod/Path/PathScripts/PathProfileBaseGui.py index 22cdcd7699..350bef44fc 100644 --- a/src/Mod/Path/PathScripts/PathProfileBaseGui.py +++ b/src/Mod/Path/PathScripts/PathProfileBaseGui.py @@ -78,6 +78,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): obj.UseStartPoint = self.form.useStartPoint.isChecked() if obj.Direction != str(self.form.direction.currentText()): obj.Direction = str(self.form.direction.currentText()) + if obj.EnableRotation != str(self.form.enableRotation.currentText()): + obj.EnableRotation = str(self.form.enableRotation.currentText()) self.updateToolController(obj, self.form.toolController) self.updateCoolant(obj, self.form.coolantController) @@ -103,6 +105,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.selectInComboBox(obj.Direction, self.form.direction) self.setupToolController(obj, self.form.toolController) self.setupCoolant(obj, self.form.coolantController) + self.selectInComboBox(obj.EnableRotation, self.form.enableRotation) if FeatureSide & self.profileFeatures(): self.selectInComboBox(obj.Side, self.form.cutSide) @@ -121,6 +124,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): signals.append(self.form.extraOffset.editingFinished) signals.append(self.form.toolController.currentIndexChanged) signals.append(self.form.coolantController.currentIndexChanged) + signals.append(self.form.enableRotation.currentIndexChanged) if FeatureSide & self.profileFeatures(): signals.append(self.form.cutSide.currentIndexChanged) diff --git a/src/Mod/Path/PathScripts/PathProfileFaces.py b/src/Mod/Path/PathScripts/PathProfileFaces.py index 620a6196e9..ee57887332 100644 --- a/src/Mod/Path/PathScripts/PathProfileFaces.py +++ b/src/Mod/Path/PathScripts/PathProfileFaces.py @@ -3,6 +3,8 @@ # *************************************************************************** # * * # * Copyright (c) 2014 Yorik van Havre * +# * Copyright (c) 2020 russ4262 (Russell Johnson) * +# * Copyright (c) 2020 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -35,13 +37,9 @@ import numpy from PySide import QtCore __title__ = "Path Profile Faces Operation" -__author__ = "sliptonic (Brad Collette)" +__author__ = "sliptonic (Brad Collette), Schildkroet" __url__ = "http://www.freecadweb.org" __doc__ = "Path Profile operation based on faces." -__contributors__ = "russ4262 (Russell Johnson, russ4262@gmail.com)" -__created__ = "2014" -__scriptVersion__ = "2j usable" -__lastModified__ = "2019-07-25 14:48 CST" PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) @@ -71,20 +69,42 @@ class ObjectProfile(PathProfileBase.ObjectProfile): obj.addProperty("App::PropertyBool", "processPerimeter", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Profile the outline")) obj.addProperty("App::PropertyBool", "processCircles", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Profile round holes")) + if not hasattr(obj, 'HandleMultipleFeatures'): + obj.addProperty('App::PropertyEnumeration', 'HandleMultipleFeatures', 'Profile', QtCore.QT_TRANSLATE_NOOP('PathPocket', 'Choose how to process multiple Base Geometry features.')) + + obj.HandleMultipleFeatures = ['Collectively', 'Individually'] + + self.initRotationOp(obj) + self.baseObject().initAreaOp(obj) + self.setOpEditorProperties(obj) + + def initRotationOp(self, obj): + '''initRotationOp(obj) ... setup receiver for rotation''' if not hasattr(obj, 'ReverseDirection'): obj.addProperty('App::PropertyBool', 'ReverseDirection', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Reverse direction of pocket operation.')) if not hasattr(obj, 'InverseAngle'): obj.addProperty('App::PropertyBool', 'InverseAngle', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Inverse the angle. Example: -22.5 -> 22.5 degrees.')) - if not hasattr(obj, 'B_AxisErrorOverride'): - obj.addProperty('App::PropertyBool', 'B_AxisErrorOverride', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Match B rotations to model (error in FreeCAD rendering).')) if not hasattr(obj, 'AttemptInverseAngle'): obj.addProperty('App::PropertyBool', 'AttemptInverseAngle', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Attempt the inverse angle for face access if original rotation fails.')) + if not hasattr(obj, 'LimitDepthToFace'): + obj.addProperty('App::PropertyBool', 'LimitDepthToFace', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Enforce the Z-depth of the selected face as the lowest value for final depth. Higher user values will be observed.')) - if not hasattr(obj, 'HandleMultipleFeatures'): - obj.addProperty('App::PropertyEnumeration', 'HandleMultipleFeatures', 'Profile', QtCore.QT_TRANSLATE_NOOP('PathPocket', 'Choose how to process multiple Base Geometry features.')) - obj.HandleMultipleFeatures = ['Collectively', 'Individually'] + def extraOpOnChanged(self, obj, prop): + '''extraOpOnChanged(obj, porp) ... process operation specific changes to properties.''' + if prop == 'EnableRotation': + self.setOpEditorProperties(obj) - self.baseObject().initAreaOp(obj) + def setOpEditorProperties(self, obj): + if obj.EnableRotation == 'Off': + obj.setEditorMode('ReverseDirection', 2) + obj.setEditorMode('InverseAngle', 2) + obj.setEditorMode('AttemptInverseAngle', 2) + obj.setEditorMode('LimitDepthToFace', 2) + else: + obj.setEditorMode('ReverseDirection', 0) + obj.setEditorMode('InverseAngle', 0) + obj.setEditorMode('AttemptInverseAngle', 0) + obj.setEditorMode('LimitDepthToFace', 0) def areaOpShapes(self, obj): '''areaOpShapes(obj) ... returns envelope for all base shapes or wires for Arch.Panels.''' @@ -138,17 +158,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 @@ -173,6 +197,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: @@ -181,23 +206,15 @@ class ObjectProfile(PathProfileBase.ObjectProfile): PathLog.error(msg) FreeCAD.Console.PrintWarning(msg) - # Raise FinalDepth to lowest face in list on Inside profile ops - finDep = obj.FinalDepth.Value + # 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) - - # Recalculate depthparams - self.depthparams = PathUtils.depth_params( # pylint: disable=attribute-defined-outside-init - clearance_height=obj.ClearanceHeight.Value, - safe_height=obj.SafeHeight.Value, - start_depth=strDep, # obj.StartDepth.Value, - step_down=obj.StepDown.Value, - z_finish_step=finish_step, - final_depth=finDep, # obj.FinalDepth.Value, - user_depths=None) + self.depthparams = self._customDepthParams(obj, strDep, finDep) for shape, wire in holes: f = Part.makeFace(wire, 'Part::FaceMakerSimple') @@ -213,14 +230,20 @@ 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 try: - env = PathUtils.getEnvelope(base.Shape, subshape=profileshape, depthparams=self.depthparams) + env = PathUtils.getEnvelope(base.Shape, subshape=profileshape, depthparams=custDepthparams) 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]) @@ -230,14 +253,8 @@ class ObjectProfile(PathProfileBase.ObjectProfile): if finalDep < shape.BoundBox.ZMin: # Recalculate depthparams finalDep = shape.BoundBox.ZMin - custDepthparams = PathUtils.depth_params( - clearance_height=obj.ClearanceHeight.Value, - safe_height=obj.SafeHeight.Value, - start_depth=strDep, # obj.StartDepth.Value, - step_down=obj.StepDown.Value, - z_finish_step=finish_step, - final_depth=finalDep, # obj.FinalDepth.Value, - user_depths=None) + custDepthparams = self._customDepthParams(obj, strDep, finalDep - 0.5) + env = PathUtils.getEnvelope(base.Shape, subshape=profShape, depthparams=custDepthparams) tup = env, False, 'pathProfileFaces', angle, axis, strDep, finalDep shapes.append(tup) @@ -246,6 +263,7 @@ 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"): @@ -284,7 +302,7 @@ class ObjectProfile(PathProfileBase.ObjectProfile): obj.ReverseDirection = False obj.InverseAngle = False obj.AttemptInverseAngle = True - obj.B_AxisErrorOverride = False + obj.LimitDepthToFace = True obj.HandleMultipleFeatures = 'Collectively' @@ -295,7 +313,6 @@ def SetupProperties(): setup.append("processCircles") setup.append("ReverseDirection") setup.append("InverseAngle") - setup.append("B_AxisErrorOverride") setup.append("AttemptInverseAngle") setup.append("HandleMultipleFeatures") return setup @@ -305,5 +322,6 @@ 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