Path: Add ExpandProfile feature
Two new properties, `ExpandProfile` (length) and `ExpandProfileStepOver` (percent) added for new feature. New feature converts the normal single offset profile into a compound profile containing multiple increasing/decreasing offsets. The new feature clears a each layer completely before stepping down to the next layer. Adjust sorting procedure in PathAreaOp due to `ExpandProfile` modifications to open edges code.
This commit is contained in:
@@ -29,14 +29,13 @@ import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import math
|
||||
from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Draft = LazyLoader('Draft', globals(), 'Draft')
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
# from PathScripts.PathUtils import waiting_effects
|
||||
from PySide import QtCore
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
|
||||
@@ -234,6 +233,8 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
area.add(baseobject)
|
||||
|
||||
areaParams = self.areaOpAreaParams(obj, isHole) # pylint: disable=assignment-from-no-return
|
||||
if hasattr(obj, 'ExpandProfile') and obj.ExpandProfile != 0:
|
||||
areaParams = self.areaOpAreaParamsExpandProfile(obj, isHole) # pylint: disable=assignment-from-no-return
|
||||
|
||||
heights = [i for i in self.depthparams]
|
||||
PathLog.debug('depths: {}'.format(heights))
|
||||
@@ -283,8 +284,8 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
|
||||
return pp, simobj
|
||||
|
||||
def _buildProfileOpenEdges(self, obj, baseShape, isHole, start, getsim):
|
||||
'''_buildPathArea(obj, baseShape, isHole, start, getsim) ... internal function.'''
|
||||
def _buildProfileOpenEdges(self, obj, edgeList, isHole, start, getsim):
|
||||
'''_buildPathArea(obj, edgeList, isHole, start, getsim) ... internal function.'''
|
||||
# pylint: disable=unused-argument
|
||||
PathLog.track()
|
||||
|
||||
@@ -293,35 +294,36 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
PathLog.debug('depths: {}'.format(heights))
|
||||
lstIdx = len(heights) - 1
|
||||
for i in range(0, len(heights)):
|
||||
hWire = Part.Wire(Part.__sortEdges__(baseShape.Edges))
|
||||
hWire.translate(FreeCAD.Vector(0, 0, heights[i] - hWire.BoundBox.ZMin))
|
||||
for baseShape in edgeList:
|
||||
hWire = Part.Wire(Part.__sortEdges__(baseShape.Edges))
|
||||
hWire.translate(FreeCAD.Vector(0, 0, heights[i] - hWire.BoundBox.ZMin))
|
||||
|
||||
pathParams = {} # pylint: disable=assignment-from-no-return
|
||||
pathParams['shapes'] = [hWire]
|
||||
pathParams['feedrate'] = self.horizFeed
|
||||
pathParams['feedrate_v'] = self.vertFeed
|
||||
pathParams['verbose'] = True
|
||||
pathParams['resume_height'] = obj.SafeHeight.Value
|
||||
pathParams['retraction'] = obj.ClearanceHeight.Value
|
||||
pathParams['return_end'] = True
|
||||
# Note that emitting preambles between moves breaks some dressups and prevents path optimization on some controllers
|
||||
pathParams['preamble'] = False
|
||||
pathParams = {} # pylint: disable=assignment-from-no-return
|
||||
pathParams['shapes'] = [hWire]
|
||||
pathParams['feedrate'] = self.horizFeed
|
||||
pathParams['feedrate_v'] = self.vertFeed
|
||||
pathParams['verbose'] = True
|
||||
pathParams['resume_height'] = obj.SafeHeight.Value
|
||||
pathParams['retraction'] = obj.ClearanceHeight.Value
|
||||
pathParams['return_end'] = True
|
||||
# Note that emitting preambles between moves breaks some dressups and prevents path optimization on some controllers
|
||||
pathParams['preamble'] = False
|
||||
|
||||
if self.endVector is None:
|
||||
V = hWire.Wires[0].Vertexes
|
||||
lv = len(V) - 1
|
||||
pathParams['start'] = FreeCAD.Vector(V[0].X, V[0].Y, V[0].Z)
|
||||
if obj.Direction == 'CCW':
|
||||
pathParams['start'] = FreeCAD.Vector(V[lv].X, V[lv].Y, V[lv].Z)
|
||||
else:
|
||||
pathParams['start'] = self.endVector
|
||||
if self.endVector is None:
|
||||
V = hWire.Wires[0].Vertexes
|
||||
lv = len(V) - 1
|
||||
pathParams['start'] = FreeCAD.Vector(V[0].X, V[0].Y, V[0].Z)
|
||||
if obj.Direction == 'CCW':
|
||||
pathParams['start'] = FreeCAD.Vector(V[lv].X, V[lv].Y, V[lv].Z)
|
||||
else:
|
||||
pathParams['start'] = self.endVector
|
||||
|
||||
obj.PathParams = str({key: value for key, value in pathParams.items() if key != 'shapes'})
|
||||
PathLog.debug("Path with params: {}".format(obj.PathParams))
|
||||
obj.PathParams = str({key: value for key, value in pathParams.items() if key != 'shapes'})
|
||||
PathLog.debug("Path with params: {}".format(obj.PathParams))
|
||||
|
||||
(pp, end_vector) = Path.fromShapes(**pathParams)
|
||||
paths.extend(pp.Commands)
|
||||
PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector))
|
||||
(pp, end_vector) = Path.fromShapes(**pathParams)
|
||||
paths.extend(pp.Commands)
|
||||
PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector))
|
||||
|
||||
self.endVector = end_vector # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
@@ -410,11 +412,17 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
shapes.append(shp)
|
||||
|
||||
if len(shapes) > 1:
|
||||
jobs = [{
|
||||
'x': s[0].BoundBox.XMax,
|
||||
'y': s[0].BoundBox.YMax,
|
||||
'shape': s
|
||||
} for s in shapes]
|
||||
jobs = list()
|
||||
for s in shapes:
|
||||
if s[2] == 'OpenEdge':
|
||||
shp = Part.makeCompound(s[0])
|
||||
else:
|
||||
shp = s[0]
|
||||
jobs.append({
|
||||
'x': shp.BoundBox.XMax,
|
||||
'y': shp.BoundBox.YMax,
|
||||
'shape': s
|
||||
})
|
||||
|
||||
jobs = PathUtils.sort_jobs(jobs, ['x', 'y'])
|
||||
|
||||
|
||||
@@ -101,6 +101,10 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
return [
|
||||
("App::PropertyEnumeration", "Direction", "Profile",
|
||||
QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise (CW) or CounterClockWise (CCW)")),
|
||||
("App::PropertyLength", "ExpandProfile", "Profile",
|
||||
QtCore.QT_TRANSLATE_NOOP("App::Property", "Extend the profile clearing beyond the Extra Offset.")),
|
||||
("App::PropertyPercent", "ExpandProfileStepOver", "Profile",
|
||||
QtCore.QT_TRANSLATE_NOOP("App::Property", "Set the stepover percentage, based on the tool's diameter.")),
|
||||
("App::PropertyEnumeration", "HandleMultipleFeatures", "Profile",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathPocket", "Choose how to process multiple Base Geometry features.")),
|
||||
("App::PropertyEnumeration", "JoinType", "Profile",
|
||||
@@ -147,6 +151,8 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
return {
|
||||
'AttemptInverseAngle': True,
|
||||
'Direction': 'CW',
|
||||
'ExpandProfile': 0.0,
|
||||
'ExpandProfileStepOver': 100,
|
||||
'HandleMultipleFeatures': 'Individually',
|
||||
'InverseAngle': False,
|
||||
'JoinType': 'Round',
|
||||
@@ -257,6 +263,29 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
|
||||
return params
|
||||
|
||||
def areaOpAreaParamsExpandProfile(self, obj, isHole):
|
||||
'''areaOpPathParamsExpandProfile(obj, isHole) ... return dictionary with area parameters for expaned profile'''
|
||||
params = {}
|
||||
|
||||
params['Fill'] = 1
|
||||
params['Coplanar'] = 0
|
||||
params['PocketMode'] = 1
|
||||
params['SectionCount'] = -1
|
||||
# params['Angle'] = obj.ZigZagAngle
|
||||
# params['FromCenter'] = (obj.StartAt == "Center")
|
||||
params['PocketStepover'] = self.tool.Diameter * (float(obj.ExpandProfileStepOver) / 100.0)
|
||||
extraOffset = obj.OffsetExtra.Value
|
||||
if False: # self.pocketInvertExtraOffset(): # Method simply returns False
|
||||
extraOffset = 0.0 - extraOffset
|
||||
params['PocketExtraOffset'] = extraOffset
|
||||
params['ToolRadius'] = self.radius
|
||||
|
||||
# Pattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle']
|
||||
params['PocketMode'] = 2 # Pattern.index(obj.OffsetPattern) + 1
|
||||
params['JoinType'] = 0 # jointype = ['Round', 'Square', 'Miter']
|
||||
|
||||
return params
|
||||
|
||||
def areaOpPathParams(self, obj, isHole):
|
||||
'''areaOpPathParams(obj, isHole) ... returns dictionary with path parameters.
|
||||
Do not overwrite.'''
|
||||
@@ -283,7 +312,9 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
|
||||
def areaOpUseProjection(self, obj):
|
||||
'''areaOpUseProjection(obj) ... returns True'''
|
||||
return True
|
||||
if obj.ExpandProfile.Value == 0.0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def opUpdateDepths(self, obj):
|
||||
if hasattr(obj, 'Base') and obj.Base.__len__() == 0:
|
||||
@@ -300,8 +331,8 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
edgeFaces = list()
|
||||
subCount = 0
|
||||
self.inaccessibleMsg = translate('PathProfile', 'The selected edge(s) are inaccessible. If multiple, re-ordering selection might work.')
|
||||
self.profileshape = list() # pylint: disable=attribute-defined-outside-init
|
||||
self.offsetExtra = obj.OffsetExtra.Value # abs(obj.OffsetExtra.Value)
|
||||
self.offsetExtra = obj.OffsetExtra.Value
|
||||
self.expandProfile = None
|
||||
|
||||
if PathLog.getLevel(PathLog.thisModule()) == 4:
|
||||
for grpNm in ['tmpDebugGrp', 'tmpDebugGrp001']:
|
||||
@@ -313,6 +344,11 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
tmpGrpNm = self.tmpGrp.Name
|
||||
self.JOB = PathUtils.findParentJob(obj)
|
||||
|
||||
if obj.ExpandProfile.Value != 0.0:
|
||||
import PathScripts.PathSurfaceSupport as PathSurfaceSupport
|
||||
self.PathSurfaceSupport = PathSurfaceSupport
|
||||
self.expandProfile = True
|
||||
|
||||
if obj.UseComp:
|
||||
self.useComp = True
|
||||
self.ofstRadius = self.radius + self.offsetExtra
|
||||
@@ -389,56 +425,93 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
|
||||
startDepths.append(strDep)
|
||||
self.depthparams = self._customDepthParams(obj, strDep, finDep)
|
||||
|
||||
for shape, wire in holes:
|
||||
f = Part.makeFace(wire, 'Part::FaceMakerSimple')
|
||||
drillable = PathUtils.isDrillable(shape, wire)
|
||||
if (drillable and obj.processCircles) or (not drillable and obj.processHoles):
|
||||
env = PathUtils.getEnvelope(shape, subshape=f, depthparams=self.depthparams)
|
||||
tup = env, True, 'pathProfileFaces', angle, axis, strDep, finDep
|
||||
shapes.append(tup)
|
||||
drillable = PathUtils.isDrillable(baseShape, wire)
|
||||
ot = self._openingType(obj, baseShape, f, strDep, finDep)
|
||||
|
||||
if len(faces) > 0:
|
||||
profileshape = Part.makeCompound(faces)
|
||||
self.profileshape.append(profileshape)
|
||||
if obj.processCircles:
|
||||
if drillable:
|
||||
if ot < 1:
|
||||
cont = True
|
||||
if obj.processHoles:
|
||||
if not drillable:
|
||||
if ot < 1:
|
||||
cont = True
|
||||
if cont:
|
||||
if self.expandProfile:
|
||||
shapeEnv = self._getExpandedProfileEnvelope(obj, f, True, obj.StartDepth.Value, finDep)
|
||||
else:
|
||||
shapeEnv = PathUtils.getEnvelope(baseShape, subshape=f, depthparams=self.depthparams)
|
||||
|
||||
if shapeEnv:
|
||||
self._addDebugObject('HoleShapeEnvelope', shapeEnv)
|
||||
# env = PathUtils.getEnvelope(baseShape, subshape=f, depthparams=self.depthparams)
|
||||
tup = shapeEnv, True, 'pathProfile', angle, axis, strDep, finDep
|
||||
shapes.append(tup)
|
||||
|
||||
if obj.processPerimeter:
|
||||
if obj.HandleMultipleFeatures == 'Collectively':
|
||||
custDepthparams = self.depthparams
|
||||
cont = True
|
||||
|
||||
if len(faces) > 0:
|
||||
profileshape = Part.makeCompound(faces)
|
||||
|
||||
if obj.LimitDepthToFace is True and obj.EnableRotation != 'Off':
|
||||
if profileshape.BoundBox.ZMin > obj.FinalDepth.Value:
|
||||
finDep = profileshape.BoundBox.ZMin
|
||||
envDepthparams = self._customDepthParams(obj, strDep + 0.5, finDep) # only an envelope
|
||||
custDepthparams = self._customDepthParams(obj, strDep + 0.5, finDep) # only an envelope
|
||||
|
||||
try:
|
||||
env = PathUtils.getEnvelope(profileshape, depthparams=envDepthparams)
|
||||
# env = PathUtils.getEnvelope(profileshape, depthparams=custDepthparams)
|
||||
if self.expandProfile:
|
||||
shapeEnv = self._getExpandedProfileEnvelope(obj, shape, False, obj.StartDepth.Value, finDep)
|
||||
else:
|
||||
shapeEnv = PathUtils.getEnvelope(profileshape, depthparams=custDepthparams)
|
||||
except Exception as ee: # pylint: disable=broad-except
|
||||
# PathUtils.getEnvelope() failed to return an object.
|
||||
msg = translate('Path', 'Unable to create path for face(s).')
|
||||
PathLog.error(msg + '\n{}'.format(ee))
|
||||
else:
|
||||
tup = env, False, 'pathProfileFaces', angle, axis, strDep, finDep
|
||||
cont = False
|
||||
|
||||
if cont:
|
||||
self._addDebugObject('CollectCutShapeEnv', shapeEnv)
|
||||
tup = shapeEnv, False, 'pathProfile', angle, axis, strDep, finDep
|
||||
shapes.append(tup)
|
||||
|
||||
elif obj.HandleMultipleFeatures == 'Individually':
|
||||
for shape in faces:
|
||||
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 + 0.5, finalDep)
|
||||
|
||||
env = PathUtils.getEnvelope(shape, depthparams=custDepthparams)
|
||||
tup = env, False, 'pathProfileFaces', angle, axis, strDep, finalDep
|
||||
shapes.append(tup)
|
||||
if self.expandProfile:
|
||||
shapeEnv = self._getExpandedProfileEnvelope(obj, shape, False, obj.StartDepth.Value, finalDep)
|
||||
else:
|
||||
shapeEnv = PathUtils.getEnvelope(shape, depthparams=custDepthparams)
|
||||
|
||||
else: # Try to build targets from the job base
|
||||
if shapeEnv:
|
||||
self._addDebugObject('IndivCutShapeEnv', shapeEnv)
|
||||
tup = shapeEnv, False, 'pathProfile', angle, axis, strDep, finalDep
|
||||
shapes.append(tup)
|
||||
|
||||
else: # Try to build targets from the job models
|
||||
# No base geometry selected, so treating operation like a exterior contour operation
|
||||
self.opUpdateDepths(obj)
|
||||
obj.Side = 'Outside' # Force outside for whole model profile
|
||||
|
||||
if 1 == len(self.model) and hasattr(self.model[0], "Proxy"):
|
||||
if isinstance(self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet
|
||||
# Cancel ExpandProfile feature. Unavailable for ArchPanels.
|
||||
if obj.ExpandProfile.Value != 0.0:
|
||||
obj.ExpandProfile.Value == 0.0
|
||||
msg = translate('PathProfile', 'No ExpandProfile support for ArchPanel models.')
|
||||
FreeCAD.Console.PrintWarning(msg + '\n')
|
||||
modelProxy = self.model[0].Proxy
|
||||
# Process circles and holes if requested by user
|
||||
if obj.processCircles or obj.processHoles:
|
||||
@@ -457,12 +530,15 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
for wire in shape.Wires:
|
||||
f = Part.makeFace(wire, 'Part::FaceMakerSimple')
|
||||
env = PathUtils.getEnvelope(self.model[0].Shape, subshape=f, depthparams=self.depthparams)
|
||||
tup = env, False, 'pathProfileFaces', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
tup = env, False, 'pathProfile', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
shapes.append(tup)
|
||||
else:
|
||||
shapes.extend([(PathUtils.getEnvelope(partshape=base.Shape, subshape=None, depthparams=self.depthparams), False) for base in self.model if hasattr(base, 'Shape')])
|
||||
# shapes.extend([(PathUtils.getEnvelope(partshape=base.Shape, subshape=None, depthparams=self.depthparams), False) for base in self.model if hasattr(base, 'Shape')])
|
||||
PathLog.debug('Single model processed.')
|
||||
shapes.extend(self._processEachModel(obj))
|
||||
else:
|
||||
shapes.extend([(PathUtils.getEnvelope(partshape=base.Shape, subshape=None, depthparams=self.depthparams), False) for base in self.model if hasattr(base, 'Shape')])
|
||||
# shapes.extend([(PathUtils.getEnvelope(partshape=base.Shape, subshape=None, depthparams=self.depthparams), False) for base in self.model if hasattr(base, 'Shape')])
|
||||
shapes.extend(self._processEachModel(obj))
|
||||
|
||||
self.removalshapes = shapes # pylint: disable=attribute-defined-outside-init
|
||||
PathLog.debug("%d shapes" % len(shapes))
|
||||
@@ -529,6 +605,106 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
|
||||
return tup
|
||||
|
||||
def _openingType(self, obj, baseShape, face, strDep, finDep):
|
||||
# Test if solid geometry above opening
|
||||
extDistPos = strDep - face.BoundBox.ZMin
|
||||
if extDistPos > 0:
|
||||
extFacePos = face.extrude(FreeCAD.Vector(0.0, 0.0, extDistPos))
|
||||
cmnPos = baseShape.common(extFacePos)
|
||||
if cmnPos.Volume > 0:
|
||||
# Signifies solid protrusion above,
|
||||
# or overhang geometry above opening
|
||||
return 1
|
||||
# Test if solid geometry below opening
|
||||
extDistNeg = finDep - face.BoundBox.ZMin
|
||||
if extDistNeg < 0:
|
||||
extFaceNeg = face.extrude(FreeCAD.Vector(0.0, 0.0, extDistNeg))
|
||||
cmnNeg = baseShape.common(extFaceNeg)
|
||||
if cmnNeg.Volume == 0:
|
||||
# No volume below signifies
|
||||
# an unobstructed/nonconstricted opening through baseShape
|
||||
return 0
|
||||
else:
|
||||
# Could be a pocket,
|
||||
# or a constricted/narrowing hole through baseShape
|
||||
return -1
|
||||
msg = translate('PathProfile', 'failed to return opening type.')
|
||||
PathLog.debug('_openingType() ' + msg)
|
||||
return -2
|
||||
|
||||
# Method for expanded profile
|
||||
def _getExpandedProfileEnvelope(self, obj, faceShape, isHole, strDep, finalDep):
|
||||
shapeZ = faceShape.BoundBox.ZMin
|
||||
|
||||
def calculateOffsetValue(obj, isHole):
|
||||
offset = obj.ExpandProfile.Value + obj.OffsetExtra.Value # 0.0
|
||||
if obj.UseComp:
|
||||
offset = obj.OffsetExtra.Value + self.tool.Diameter
|
||||
offset += obj.ExpandProfile.Value
|
||||
if isHole:
|
||||
if obj.Side == 'Outside':
|
||||
offset = 0 - offset
|
||||
else:
|
||||
if obj.Side == 'Inside':
|
||||
offset = 0 - offset
|
||||
return offset
|
||||
|
||||
faceEnv = self.PathSurfaceSupport.getShapeEnvelope(faceShape)
|
||||
# newFace = self.PathSurfaceSupport.getSliceFromEnvelope(faceEnv)
|
||||
newFace = self.PathSurfaceSupport.getShapeSlice(faceEnv)
|
||||
# Compute necessary offset
|
||||
offsetVal = calculateOffsetValue(obj, isHole)
|
||||
expandedFace = self.PathSurfaceSupport.extractFaceOffset(newFace, offsetVal, newFace)
|
||||
if expandedFace:
|
||||
if shapeZ != 0.0:
|
||||
expandedFace.translate(FreeCAD.Vector(0.0, 0.0, shapeZ))
|
||||
newFace.translate(FreeCAD.Vector(0.0, 0.0, shapeZ))
|
||||
|
||||
if isHole:
|
||||
if obj.Side == 'Outside':
|
||||
newFace = newFace.cut(expandedFace)
|
||||
else:
|
||||
newFace = expandedFace.cut(newFace)
|
||||
else:
|
||||
if obj.Side == 'Inside':
|
||||
newFace = newFace.cut(expandedFace)
|
||||
else:
|
||||
newFace = expandedFace.cut(newFace)
|
||||
|
||||
if finalDep - shapeZ != 0:
|
||||
newFace.translate(FreeCAD.Vector(0.0, 0.0, finalDep - shapeZ))
|
||||
|
||||
if strDep - finalDep != 0:
|
||||
if newFace.Area > 0:
|
||||
return newFace.extrude(FreeCAD.Vector(0.0, 0.0, strDep - finalDep))
|
||||
else:
|
||||
PathLog.debug('No expanded profile face shape.\n')
|
||||
return False
|
||||
else:
|
||||
PathLog.debug(translate('PathProfile', 'Failed to extract offset(s) for expanded profile.') + '\n')
|
||||
|
||||
PathLog.debug(translate('PathProfile', 'Failed to expand profile.') + '\n')
|
||||
return False
|
||||
|
||||
# Method to handle each model as a whole, when no faces are selected
|
||||
# It includes ExpandProfile implementation
|
||||
def _processEachModel(self, obj):
|
||||
shapeTups = list()
|
||||
for base in self.model:
|
||||
if hasattr(base, 'Shape'):
|
||||
env = PathUtils.getEnvelope(partshape=base.Shape, subshape=None, depthparams=self.depthparams)
|
||||
if self.expandProfile:
|
||||
eSlice = self.PathSurfaceSupport.getCrossSection(env) # getSliceFromEnvelope(env)
|
||||
eSlice.translate(FreeCAD.Vector(0.0, 0.0, base.Shape.BoundBox.ZMin - env.BoundBox.ZMin))
|
||||
self._addDebugObject('ModelSlice', eSlice)
|
||||
shapeEnv = self._getExpandedProfileEnvelope(obj, eSlice, False, obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
else:
|
||||
shapeEnv = env
|
||||
|
||||
if shapeEnv:
|
||||
shapeTups.append((shapeEnv, False))
|
||||
return shapeTups
|
||||
|
||||
# Edges pre-processing
|
||||
def _processEdges(self, obj):
|
||||
import DraftGeomUtils
|
||||
@@ -536,6 +712,8 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
basewires = list()
|
||||
delPairs = list()
|
||||
ezMin = None
|
||||
self.cutOut = self.tool.Diameter * (float(obj.ExpandProfileStepOver) / 100.0)
|
||||
|
||||
for p in range(0, len(obj.Base)):
|
||||
(base, subsList) = obj.Base[p]
|
||||
tmpSubs = list()
|
||||
@@ -570,10 +748,15 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
zShift = ezMin - f.BoundBox.ZMin
|
||||
newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), f.Placement.Rotation)
|
||||
f.Placement = newPlace
|
||||
env = PathUtils.getEnvelope(base.Shape, subshape=f, depthparams=self.depthparams)
|
||||
# shapes.append((env, False))
|
||||
tup = env, False, 'Profile', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
shapes.append(tup)
|
||||
|
||||
if self.expandProfile:
|
||||
shapeEnv = self._getExpandedProfileEnvelope(obj, Part.Face(f), False, obj.StartDepth.Value, ezMin)
|
||||
else:
|
||||
shapeEnv = PathUtils.getEnvelope(base.Shape, subshape=f, depthparams=self.depthparams)
|
||||
|
||||
if shapeEnv:
|
||||
tup = shapeEnv, False, 'Profile', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
shapes.append(tup)
|
||||
else:
|
||||
PathLog.error(self.inaccessibleMsg)
|
||||
else:
|
||||
@@ -583,28 +766,39 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
msg += translate('PathProfile', 'Please set to an acceptable value greater than zero.')
|
||||
PathLog.error(msg)
|
||||
else:
|
||||
cutWireObjs = False
|
||||
flattened = self._flattenWire(obj, wire, obj.FinalDepth.Value)
|
||||
if flattened:
|
||||
cutWireObjs = False
|
||||
openEdges = list()
|
||||
passOffsets = [self.ofstRadius]
|
||||
(origWire, flatWire) = flattened
|
||||
if PathLog.getLevel(PathLog.thisModule()) == 4:
|
||||
os = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpFlatWire')
|
||||
os.Shape = flatWire
|
||||
os.purgeTouched()
|
||||
self.tmpGrp.addObject(os)
|
||||
if self.expandProfile:
|
||||
# Identify list of pass offset values for expanded profile paths
|
||||
regularOfst = self.ofstRadius
|
||||
targetOfst = regularOfst + obj.ExpandProfile.Value
|
||||
while regularOfst < targetOfst:
|
||||
regularOfst += self.cutOut
|
||||
passOffsets.insert(0, regularOfst)
|
||||
|
||||
cutShp = self._getCutAreaCrossSection(obj, base, origWire, flatWire)
|
||||
if cutShp:
|
||||
cutWireObjs = self._extractPathWire(obj, base, flatWire, cutShp)
|
||||
for po in passOffsets:
|
||||
self.ofstRadius = po
|
||||
cutShp = self._getCutAreaCrossSection(obj, base, origWire, flatWire)
|
||||
if cutShp:
|
||||
cutWireObjs = self._extractPathWire(obj, base, flatWire, cutShp)
|
||||
|
||||
if cutWireObjs:
|
||||
for cW in cutWireObjs:
|
||||
# shapes.append((cW, False))
|
||||
# self.profileEdgesIsOpen = True
|
||||
tup = cW, False, 'OpenEdge', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
shapes.append(tup)
|
||||
else:
|
||||
PathLog.error(self.inaccessibleMsg)
|
||||
if cutWireObjs:
|
||||
for cW in cutWireObjs:
|
||||
openEdges.append(cW)
|
||||
else:
|
||||
PathLog.error(self.inaccessibleMsg)
|
||||
|
||||
tup = openEdges, False, 'OpenEdge', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
shapes.append(tup)
|
||||
else:
|
||||
PathLog.error(self.inaccessibleMsg)
|
||||
# Eif
|
||||
|
||||
Reference in New Issue
Block a user