Path: Respect meshing tolerance settings for 3d surfaces

Meshing tolerance settings for 3d surface operations were ignored since
switching to directly using facets from the view tesselation. On complex
3d shapes, this could cause serious meshing artifacts in the generated
paths, serious enough to show up even in path previews.

Additionally, there were occasionally segfaults when a model was not
viewed and thus implicitly tessellated before starting an OCL operation.

This patch switches from using view tessellations to explicitly calling
`tessellate()`. While this is usually more expensive than using the
existing view tessellation, recent changes to parallelize tessellation
have reduced this additional cost significantly.

Note that this diff only restores the use of LinearDeflection, since
that is the main thing we care about in OCL use cases. Angular
deflection is still ignored, but I think this is a good thing for this
case since a highly refined mesh in tiny but heavily curved areas below
the linear deflection threshold just adds unnecessary cost. If there is
agreement on this point, then we can remove the preference for
AngularDeflection from the UI in a follow-up.
This commit is contained in:
Gabriel Wicke
2020-05-23 14:07:29 -07:00
parent 6b0b237441
commit 1acaa5eadf

View File

@@ -1139,33 +1139,14 @@ def extractFaceOffset(fcShape, offset, wpc, makeComp=True):
return ofstFace # offsetShape
# Functions for making model STLs
def _prepareModelSTLs(self, JOB, obj, m, ocl):
"""Tessellate model shapes or copy existing meshes into ocl.STLSurf
objects"""
PathLog.debug('_prepareModelSTLs()')
import MeshPart
if self.modelSTLs[m] is True:
M = JOB.Model.Group[m]
# PathLog.debug(f" -self.modelTypes[{m}] == 'M'")
if self.modelTypes[m] == 'M':
# TODO: test if this works
facets = M.Mesh.Facets.Points
else:
facets = Part.getFacets(M.Shape)
# mesh = MeshPart.meshFromShape(Shape=M.Shape,
# LinearDeflection=obj.LinearDeflection.Value,
# AngularDeflection=obj.AngularDeflection.Value,
# Relative=False)
stl = ocl.STLSurf()
for tri in facets:
t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]),
ocl.Point(tri[1][0], tri[1][1], tri[1][2]),
ocl.Point(tri[2][0], tri[2][1], tri[2][2]))
stl.addTriangle(t)
self.modelSTLs[m] = stl
return
model = JOB.Model.Group[m]
if self.modelSTLs[m] is True:
self.modelSTLs[m] = _makeSTL(model, obj, ocl, self.modelTypes[m])
def _makeSafeSTL(self, JOB, obj, mdlIdx, faceShapes, voidShapes, ocl):
@@ -1242,20 +1223,32 @@ def _makeSafeSTL(self, JOB, obj, mdlIdx, faceShapes, voidShapes, ocl):
T.purgeTouched()
self.tempGroup.addObject(T)
facets = Part.getFacets(fused)
# mesh = MeshPart.meshFromShape(Shape=fused,
# LinearDeflection=obj.LinearDeflection.Value,
# AngularDeflection=obj.AngularDeflection.Value,
# Relative=False)
self.safeSTLs[mdlIdx] = _makeSTL(fused, obj, ocl)
def _makeSTL(model, obj, ocl, model_type=None):
"""Convert a mesh or shape into an OCL STL, using the tessellation
tolerance specified in obj.LinearDeflection.
Returns an ocl.STLSurf()."""
if model_type == 'M':
facets = model.Mesh.Facets.Points
else:
if hasattr(model, 'Shape'):
shape = model.Shape
else:
shape = model
vertices, facet_indices = shape.tessellate(
obj.LinearDeflection.Value)
facets = ((vertices[f[0]], vertices[f[1]], vertices[f[2]])
for f in facet_indices)
stl = ocl.STLSurf()
for tri in facets:
t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]),
ocl.Point(tri[1][0], tri[1][1], tri[1][2]),
ocl.Point(tri[2][0], tri[2][1], tri[2][2]))
v1, v2, v3 = tri
t = ocl.Triangle(ocl.Point(v1[0], v1[1], v1[2]),
ocl.Point(v2[0], v2[1], v2[2]),
ocl.Point(v3[0], v3[1], v3[2]))
stl.addTriangle(t)
self.safeSTLs[mdlIdx] = stl
return stl
# Functions to convert path geometry into line/arc segments for OCL input or directly to g-code
@@ -1332,7 +1325,7 @@ def pathGeomToLinesPointSet(obj, compGeoShp, cutClimb, toolDiam, closedGap, gaps
gaps.insert(0, gap)
gaps.pop()
inLine.append(tup)
# Efor
lnCnt += 1
if cutClimb is True:
@@ -2003,7 +1996,7 @@ class FindUnifiedRegions:
def faceIndex(tup):
return tup[3]
def faceArea(face):
return face.Area
@@ -2057,7 +2050,7 @@ class FindUnifiedRegions:
notConnected = False
# Save loop components
LOOPS.append(connectedEdges)
# reset connected variables and re-assess
# reset connected variables and re-assess
connectedEdges = []
connectedIndexes = []
connectedCnt = 0
@@ -2086,7 +2079,7 @@ class FindUnifiedRegions:
if idxCnt == 0:
cont = False
# Ewhile
if len(LOOPS) > 0:
FACES = list()
for Edges in LOOPS:
@@ -2283,4 +2276,4 @@ class FindUnifiedRegions:
return self.INTERNALS
FreeCAD.Console.PrintError('getUnifiedRegions() must be called before getInternalFeatures().\n')
return False
# Eclass
# Eclass