Merge pull request #3565 from Russ4262/3D_Surface_vertical_face_fix

[Path] 3D Surface: various fixes
This commit is contained in:
sliptonic
2020-06-05 09:08:37 -05:00
committed by GitHub
2 changed files with 88 additions and 58 deletions

View File

@@ -418,6 +418,20 @@ class ObjectSurface(PathOp.ObjectOp):
obj.AvoidLastX_Faces = 100
PathLog.error(translate('PathSurface', 'AvoidLastX_Faces: Avoid last X faces count limited to 100.'))
def opUpdateDepths(self, obj):
if hasattr(obj, 'Base') and obj.Base:
base, sublist = obj.Base[0]
fbb = base.Shape.getElement(sublist[0]).BoundBox
zmin = fbb.ZMax
for base, sublist in obj.Base:
for sub in sublist:
try:
fbb = base.Shape.getElement(sub).BoundBox
zmin = min(zmin, fbb.ZMin)
except Part.OCCError as e:
PathLog.error(e)
obj.OpFinalDepth = zmin
def opExecute(self, obj):
'''opExecute(obj) ... process surface operation'''
PathLog.track()
@@ -661,7 +675,7 @@ class ObjectSurface(PathOp.ObjectOp):
else:
exTime = str(round(execTime, 5)) + ' sec.'
msg = translate('PathSurface', 'operation time is')
FreeCAD.Console.PrintMessage('3D Surface ' + msg + '{}\n'.format(exTime))
FreeCAD.Console.PrintMessage('3D Surface ' + msg + ' {}\n'.format(exTime))
if self.cancelOperation:
FreeCAD.ActiveDocument.openTransaction(translate("PathSurface", "Canceled 3D Surface operation."))

View File

@@ -101,12 +101,6 @@ class PathGeometryGenerator:
self._prepareConstants()
def _prepareConstants(self):
# Apply drop cutter extra offset and set the max and min XY area of the operation
# xmin = self.shape.BoundBox.XMin
# xmax = self.shape.BoundBox.XMax
# ymin = self.shape.BoundBox.YMin
# ymax = self.shape.BoundBox.YMax
# Compute weighted center of mass of all faces combined
if self.pattern in ['Circular', 'CircularZigZag', 'Spiral']:
if self.obj.PatternCenterAt == 'CenterOfMass':
@@ -235,20 +229,6 @@ class PathGeometryGenerator:
centRot = FreeCAD.Vector(0.0, 0.0, 0.0) # Bottom left corner of face/selection/model
cAng = math.atan(self.deltaX / self.deltaY) # BoundaryBox angle
# Determine end points and create top lines
# x1 = centRot.x - self.halfDiag
# x2 = centRot.x + self.halfDiag
# diag = None
# if self.obj.CutPatternAngle == 0 or self.obj.CutPatternAngle == 180:
# diag = self.deltaY
# elif self.obj.CutPatternAngle == 90 or self.obj.CutPatternAngle == 270:
# diag = self.deltaX
# else:
# perpDist = math.cos(cAng - math.radians(self.obj.CutPatternAngle)) * self.deltaC
# diag = perpDist
# y1 = centRot.y + diag
# y2 = y1
# Create end points for set of lines to intersect with cross-section face
pntTuples = list()
for lc in range((-1 * (self.halfPasses - 1)), self.halfPasses + 1):
@@ -576,16 +556,16 @@ class ProcessSelectedFaces:
FreeCAD.Console.PrintError(msg)
else:
(fcShp, prflShp) = pPEB
if fcShp is not False:
if fcShp:
if fcShp is True:
PathLog.debug(' -fcShp is True.')
fShapes[m] = True
else:
fShapes[m] = [fcShp]
if prflShp is not False:
if fcShp is not False:
if prflShp:
if fcShp:
PathLog.debug('vShapes[{}]: {}'.format(m, vShapes[m]))
if vShapes[m] is not False:
if vShapes[m]:
PathLog.debug(' -Cutting void from base profile shape.')
adjPS = prflShp.cut(vShapes[m][0])
self.profileShapes[m] = [adjPS]
@@ -696,7 +676,7 @@ class ProcessSelectedFaces:
PathLog.debug('.. include Profile Edge')
ofstVal = self._calculateOffsetValue(isHole)
psOfst = extractFaceOffset(cfsL, ofstVal, self.wpc)
if psOfst is not False:
if psOfst:
mPS = [psOfst]
if self.profileEdges == 'Only':
mFS = True
@@ -721,7 +701,7 @@ class ProcessSelectedFaces:
if cont:
lenIfL = len(ifL)
if self.obj.InternalFeaturesCut is False:
if not self.obj.InternalFeaturesCut:
if lenIfL == 0:
PathLog.debug(' -No internal features saved.')
else:
@@ -737,7 +717,6 @@ class ProcessSelectedFaces:
ofstVal = self._calculateOffsetValue(isHole=True)
intOfstShp = extractFaceOffset(casL, ofstVal, self.wpc)
mIFS.append(intOfstShp)
# faceOfstShp = faceOfstShp.cut(intOfstShp)
mFS = [faceOfstShp]
# Eif
@@ -753,17 +732,19 @@ class ProcessSelectedFaces:
FUR = FindUnifiedRegions([(fcshp, fcIdx)], self.JOB.GeometryTolerance.Value)
if self.showDebugObjects:
FUR.setTempGroup(self.tempGroup)
outerFace = FUR.getUnifiedRegions()[0]
if not self.obj.InternalFeaturesCut:
ifL = FUR.getInternalFeatures()
gUR = FUR.getUnifiedRegions()
if len(gUR) > 0:
outerFace = gUR[0]
if not self.obj.InternalFeaturesCut:
ifL = FUR.getInternalFeatures()
if outerFace is not False:
if outerFace:
PathLog.debug('Attempting to create offset face of Face{}'.format(fNum))
if self.profileEdges != 'None':
ofstVal = self._calculateOffsetValue(isHole)
psOfst = extractFaceOffset(outerFace, ofstVal, self.wpc)
if psOfst is not False:
if psOfst:
if mPS is False:
mPS = list()
mPS.append(psOfst)
@@ -805,7 +786,7 @@ class ProcessSelectedFaces:
for ifs in mIFS:
mVS.append(ifs)
if VDS is not False:
if VDS:
PathLog.debug('Processing avoid faces.')
cont = True
isHole = False
@@ -908,7 +889,7 @@ class ProcessSelectedFaces:
PathLog.debug(' -Attempting profile geometry for model base.')
ofstVal = self._calculateOffsetValue(isHole)
psOfst = extractFaceOffset(csFaceShape, ofstVal, self.wpc)
if psOfst is not False:
if psOfst:
if self.profileEdges == 'Only':
return (True, psOfst)
prflShp = psOfst
@@ -1220,7 +1201,7 @@ def _makeSafeSTL(self, JOB, obj, mdlIdx, faceShapes, voidShapes, ocl):
B = Part.makeBox(bL, bW, bH, crnr, FreeCAD.Vector(0, 0, 1))
fuseShapes.append(B)
if voidShapes is not False:
if voidShapes:
voidComp = Part.makeCompound(voidShapes)
voidEnv = PathUtils.getEnvelope(partshape=voidComp, depthparams=self.depthParams) # Produces .Shape
fuseShapes.append(voidEnv)
@@ -1608,7 +1589,7 @@ def pathGeomToCircularPointSet(obj, compGeoShp, cutClimb, toolDiam, closedGap, g
chkGap = False
lst = None
if CONN is not False:
if CONN:
(iE, iS) = CONN
v1 = compGeoShp.Edges[iE].Vertexes[0]
v2 = compGeoShp.Edges[iS].Vertexes[1]
@@ -1883,6 +1864,7 @@ class FindUnifiedRegions:
count[1] += 1
def _groupEdgesByLength(self):
PathLog.debug('_groupEdgesByLength()')
cont = True
threshold = self.geomToler
grp = list()
@@ -1933,6 +1915,7 @@ class FindUnifiedRegions:
self.idGroups.append(grp)
def _identifySharedEdgesByLength(self, grp):
PathLog.debug('_identifySharedEdgesByLength()')
holds = list()
cont = True
specialIndexes = []
@@ -1992,6 +1975,7 @@ class FindUnifiedRegions:
self.noSharedEdges = False
def _extractWiresFromEdges(self):
PathLog.debug('_extractWiresFromEdges()')
DATA = self.edgeData
holds = list()
lastEdge = None
@@ -2090,16 +2074,34 @@ class FindUnifiedRegions:
cont = False
# Ewhile
if len(LOOPS) > 0:
numLoops = len(LOOPS)
PathLog.debug(' -numLoops: {}.'.format(numLoops))
if numLoops > 0:
FACES = list()
for Edges in LOOPS:
for li in range(0, numLoops):
Edges = LOOPS[li]
#for e in Edges:
# self._showShape(e, 'Loop_{}_Edge'.format(li))
wire = Part.Wire(Part.__sortEdges__(Edges))
if wire.isClosed():
face = Part.Face(wire)
# This simple Part.Face() method fails to catch
# wires with tangent closed wires, or an external
# wire with one or more internal tangent wires.
# face = Part.Face(wire)
# This method works with the complex tangent
# closed wires mentioned above.
extWire = wire.extrude(FreeCAD.Vector(0.0, 0.0, 2.0))
wireSolid = Part.makeSolid(extWire)
extdBBFace1 = makeExtendedBoundBox(wireSolid.BoundBox, 5.0, wireSolid.BoundBox.ZMin + 1.0)
extdBBFace2 = makeExtendedBoundBox(wireSolid.BoundBox, 5.0, wireSolid.BoundBox.ZMin + 1.0)
inverse = extdBBFace1.cut(wireSolid)
face = extdBBFace2.cut(inverse)
self.REGIONS.append(face)
self.REGIONS.sort(key=faceArea, reverse=True)
def _identifyInternalFeatures(self):
PathLog.debug('_identifyInternalFeatures()')
remList = list()
for (top, fcIdx) in self.topFaces:
@@ -2121,6 +2123,7 @@ class FindUnifiedRegions:
self.REGIONS.pop(ri)
def _processNestedRegions(self):
PathLog.debug('_processNestedRegions()')
cont = True
hold = list()
Ids = list()
@@ -2222,6 +2225,7 @@ class FindUnifiedRegions:
def getUnifiedRegions(self):
'''getUnifiedRegions()... Returns a list of unified regions from list
of tuples (faceShape, faceIndex) received at instantiation of the class object.'''
PathLog.debug('getUnifiedRegions()')
self.INTERNALS = list()
if len(self.FACES) == 0:
msg = translate('PathSurfaceSupport',
@@ -2231,7 +2235,6 @@ class FindUnifiedRegions:
self._extractTopFaces()
lenFaces = len(self.topFaces)
PathLog.debug('getUnifiedRegions() lenFaces: {}.'.format(lenFaces))
if lenFaces == 0:
return []
@@ -2243,28 +2246,26 @@ class FindUnifiedRegions:
lenWrs = len(topFace.Wires)
if lenWrs > 1:
for w in range(1, lenWrs):
# Any internal wires need to be flattened
# before appending to self.INTERNALS
# A problem exists that inner wires are not all recognized as wires.
# Some are single circular edges.
# Face.Edges.__len_() - Face.OuterWire.Edges.__len__() = edges for inner wire(s)
# extWire = getExtrudedShape(wr)
# wCS = getCrossSection(extWire)
# wCS.translate(FreeCAD.Vector(0.0, 0.0, wr.BoundBox.ZMin))
wr = topFace.Wires[w]
self.INTERNALS.append(Part.Face(wr))
# Flatten face and extract outer wire, then convert to face
extWire = getExtrudedShape(topFace)
wCS = getCrossSection(extWire)
wCS.translate(FreeCAD.Vector(0.0, 0.0, topFace.BoundBox.ZMin))
face = Part.Face(wCS)
return [face]
if wCS:
face = Part.Face(wCS)
return [face]
else:
(faceShp, fcIdx) = self.FACES[0]
msg = translate('PathSurfaceSupport',
'Failed to identify a horizontal cross-section for Face')
msg += '{}.\n'.format(fcIdx + 1)
FreeCAD.Console.PrintWarning(msg)
return []
# process multiple top faces, unifying if possible
self._fuseTopFaces()
# for F in self.fusedFaces.Faces:
# self._showShape(F, 'TopFaceFused')
for F in self.fusedFaces.Faces:
self._showShape(F, 'TopFaceFused')
self._getEdgesData()
self._groupEdgesByLength()
@@ -2272,7 +2273,7 @@ class FindUnifiedRegions:
self._identifySharedEdgesByLength(grp)
if self.noSharedEdges:
PathLog.debug('No shared edges by length detected.\n')
PathLog.debug('No shared edges by length detected.')
return [topFace for (topFace, fcIdx) in self.topFaces]
else:
# Delete shared edges from edgeData list
@@ -2283,8 +2284,8 @@ class FindUnifiedRegions:
self._extractWiresFromEdges()
self._identifyInternalFeatures()
self._processNestedRegions()
for ri in range(0, len(self.REGIONS)):
self._showShape(self.REGIONS[ri], 'UnifiedRegion_{}'.format(ri))
# for ri in range(0, len(self.REGIONS)):
# self._showShape(self.REGIONS[ri], 'UnifiedRegion_{}'.format(ri))
return self.REGIONS
@@ -2298,3 +2299,18 @@ class FindUnifiedRegions:
FreeCAD.Console.PrintError(msg + '\n')
return False
# Eclass
# Support functions
def makeExtendedBoundBox(wBB, bbBfr, zDep):
PathLog.debug('makeExtendedBoundBox()')
p1 = FreeCAD.Vector(wBB.XMin - bbBfr, wBB.YMin - bbBfr, zDep)
p2 = FreeCAD.Vector(wBB.XMax + bbBfr, wBB.YMin - bbBfr, zDep)
p3 = FreeCAD.Vector(wBB.XMax + bbBfr, wBB.YMax + bbBfr, zDep)
p4 = FreeCAD.Vector(wBB.XMin - bbBfr, wBB.YMax + bbBfr, zDep)
L1 = Part.makeLine(p1, p2)
L2 = Part.makeLine(p2, p3)
L3 = Part.makeLine(p3, p4)
L4 = Part.makeLine(p4, p1)
return Part.Face(Part.Wire([L1, L2, L3, L4]))