Merge pull request #3629 from gwicke/line_simplification
[path] Implement Ramer-Douglas-Peucker line simplification
This commit is contained in:
@@ -1094,7 +1094,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
|
||||
def _planarSinglepassProcess(self, obj, points):
|
||||
if obj.OptimizeLinearPaths:
|
||||
points = self._optimizeLinearSegments(points)
|
||||
points = PathUtils.simplify3dLine(points,
|
||||
tolerance=obj.LinearDeflection.Value)
|
||||
# Begin processing ocl points list into gcode
|
||||
commands = []
|
||||
for pnt in points:
|
||||
@@ -2100,28 +2101,14 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
PathLog.warning("Defaulting cutter to standard end mill.")
|
||||
return ocl.CylCutter(diam_1, (CEH + lenOfst))
|
||||
|
||||
def _optimizeLinearSegments(self, line):
|
||||
"""Eliminate collinear interior segments"""
|
||||
if len(line) > 2:
|
||||
prv, pnt = line[0:2]
|
||||
pts = [prv]
|
||||
for nxt in line[2:]:
|
||||
if not pnt.isOnLineSegment(prv, nxt):
|
||||
pts.append(pnt)
|
||||
prv = pnt
|
||||
pnt = nxt
|
||||
pts.append(line[-1])
|
||||
return pts
|
||||
else:
|
||||
return line
|
||||
|
||||
def _getTransitionLine(self, pdc, p1, p2, obj):
|
||||
"""Use an OCL PathDropCutter to generate a safe transition path between
|
||||
two points in the x/y plane."""
|
||||
p1xy, p2xy = ((p1.x, p1.y), (p2.x, p2.y))
|
||||
pdcLine = self._planarDropCutScan(pdc, p1xy, p2xy)
|
||||
if obj.OptimizeLinearPaths:
|
||||
pdcLine = self._optimizeLinearSegments(pdcLine)
|
||||
pdcLine = PathUtils.simplify3dLine(
|
||||
pdcLine, tolerance=obj.LinearDeflection.Value)
|
||||
zs = [obj.z for obj in pdcLine]
|
||||
# PDC z values are based on the model, and do not take into account
|
||||
# any remaining stock / multi layer paths. Adjust raw PDC z values to
|
||||
|
||||
@@ -871,3 +871,41 @@ class depth_params(object):
|
||||
return depths
|
||||
else:
|
||||
return [stop] + depths
|
||||
|
||||
|
||||
def simplify3dLine(line, tolerance=1e-4):
|
||||
"""Simplify a line defined by a list of App.Vectors, while keeping the
|
||||
maximum deviation from the original line within the defined tolerance.
|
||||
Implementation of
|
||||
https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm"""
|
||||
stack = [(0, len(line) - 1)]
|
||||
results = []
|
||||
|
||||
def processRange(start, end):
|
||||
"""Internal worker. Process a range of Vector indices within the
|
||||
line."""
|
||||
if end - start < 2:
|
||||
results.extend(line[start:end])
|
||||
return
|
||||
# Find point with maximum distance
|
||||
maxIndex, maxDistance = 0, 0.0
|
||||
startPoint, endPoint = (line[start], line[end])
|
||||
for i in range(start + 1, end):
|
||||
v = line[i]
|
||||
distance = v.distanceToLineSegment(startPoint, endPoint).Length
|
||||
if distance > maxDistance:
|
||||
maxDistance = distance
|
||||
maxIndex = i
|
||||
if maxDistance > tolerance:
|
||||
# Push second branch first, to be executed last
|
||||
stack.append((maxIndex, end))
|
||||
stack.append((start, maxIndex))
|
||||
else:
|
||||
results.append(line[start])
|
||||
|
||||
while len(stack):
|
||||
processRange(*stack.pop())
|
||||
# Each segment only appended its start point to the final result, so fill in
|
||||
# the last point.
|
||||
results.append(line[-1])
|
||||
return results
|
||||
|
||||
Reference in New Issue
Block a user