Path: Refactor PathBoundary algorithm to independent class

Relocate code for PathBoundary algorithm into an independent class allowing for usage without the Dressup object creation.
This commit is contained in:
Russell Johnson
2021-05-09 22:08:10 -05:00
parent cfca2a790a
commit 30ea19a923

View File

@@ -77,7 +77,29 @@ class DressupPathBoundary(object):
obj.Stock = None
return True
def boundaryCommands(self, obj, begin, end, verticalFeed):
def execute(self, obj):
pb = PathBoundary(obj.Base, obj.Stock.Shape, obj.Inside)
obj.Path = pb.execute()
# Eclass
class PathBoundary:
"""class PathBoundary...
This class requires a base operation, boundary shape, and optional inside boolean (default is True).
The `execute()` method returns a Path object with path commands limited to cut paths inside or outside
the provided boundary shape.
"""
def __init__(self, baseOp, boundaryShape, inside=True):
self.baseOp = baseOp
self.boundary = boundaryShape
self.inside = inside
self.safeHeight = None
self.clearanceHeight = None
self.strG1ZsafeHeight = None
self.strG0ZclearanceHeight = None
def boundaryCommands(self, begin, end, verticalFeed):
PathLog.track(_vstr(begin), _vstr(end))
if end and PathGeom.pointsCoincide(begin, end):
return []
@@ -94,117 +116,117 @@ class DressupPathBoundary(object):
cmds.append(Path.Command('G1', {'Z': end.z, 'F': verticalFeed}))
return cmds
def execute(self, obj):
if not obj.Base or not obj.Base.isDerivedFrom('Path::Feature') or not obj.Base.Path:
return
def execute(self):
if not self.baseOp or not self.baseOp.isDerivedFrom('Path::Feature') or not self.baseOp.Path:
return None
tc = PathDressup.toolController(obj.Base)
if len(self.baseOp.Path.Commands) == 0:
PathLog.warning("No Path Commands for %s" % self.baseOp.Label)
return []
if len(obj.Base.Path.Commands) > 0:
self.safeHeight = float(PathUtil.opProperty(obj.Base, 'SafeHeight'))
self.clearanceHeight = float(PathUtil.opProperty(obj.Base, 'ClearanceHeight'))
self.strG1ZsafeHeight = Path.Command('G1', {'Z': self.safeHeight, 'F': tc.VertFeed.Value})
self.strG0ZclearanceHeight = Path.Command('G0', {'Z': self.clearanceHeight})
tc = PathDressup.toolController(self.baseOp)
boundary = obj.Stock.Shape
cmd = obj.Base.Path.Commands[0]
pos = cmd.Placement.Base # bogus m/c position to create first edge
bogusX = True
bogusY = True
commands = [cmd]
lastExit = None
for cmd in obj.Base.Path.Commands[1:]:
if cmd.Name in PathGeom.CmdMoveAll:
if bogusX == True :
bogusX = ( 'X' not in cmd.Parameters )
if bogusY :
bogusY = ( 'Y' not in cmd.Parameters )
edge = PathGeom.edgeForCmd(cmd, pos)
if edge:
inside = edge.common(boundary).Edges
outside = edge.cut(boundary).Edges
if not obj.Inside: # UI "inside boundary" param
tmp = inside
inside = outside
outside = tmp
# it's really a shame that one cannot trust the sequence and/or
# orientation of edges
if 1 == len(inside) and 0 == len(outside):
PathLog.track(_vstr(pos), _vstr(lastExit), ' + ', cmd)
# cmd fully included by boundary
if lastExit:
self.safeHeight = float(PathUtil.opProperty(self.baseOp, 'SafeHeight'))
self.clearanceHeight = float(PathUtil.opProperty(self.baseOp, 'ClearanceHeight'))
self.strG1ZsafeHeight = Path.Command('G1', {'Z': self.safeHeight, 'F': tc.VertFeed.Value})
self.strG0ZclearanceHeight = Path.Command('G0', {'Z': self.clearanceHeight})
cmd = self.baseOp.Path.Commands[0]
pos = cmd.Placement.Base # bogus m/c position to create first edge
bogusX = True
bogusY = True
commands = [cmd]
lastExit = None
for cmd in self.baseOp.Path.Commands[1:]:
if cmd.Name in PathGeom.CmdMoveAll:
if bogusX == True :
bogusX = ( 'X' not in cmd.Parameters )
if bogusY :
bogusY = ( 'Y' not in cmd.Parameters )
edge = PathGeom.edgeForCmd(cmd, pos)
if edge:
inside = edge.common(self.boundary).Edges
outside = edge.cut(self.boundary).Edges
if not self.inside: # UI "inside boundary" param
tmp = inside
inside = outside
outside = tmp
# it's really a shame that one cannot trust the sequence and/or
# orientation of edges
if 1 == len(inside) and 0 == len(outside):
PathLog.track(_vstr(pos), _vstr(lastExit), ' + ', cmd)
# cmd fully included by boundary
if lastExit:
if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position
commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value))
lastExit = None
commands.append(cmd)
pos = PathGeom.commandEndPoint(cmd, pos)
elif 0 == len(inside) and 1 == len(outside):
PathLog.track(_vstr(pos), _vstr(lastExit), ' - ', cmd)
# cmd fully excluded by boundary
if not lastExit:
lastExit = pos
pos = PathGeom.commandEndPoint(cmd, pos)
else:
PathLog.track(_vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd)
# cmd pierces boundary
while inside or outside:
ie = [e for e in inside if PathGeom.edgeConnectsTo(e, pos)]
PathLog.track(ie)
if ie:
e = ie[0]
LastPt = e.valueAt(e.LastParameter)
flip = PathGeom.pointsCoincide(pos, LastPt)
newPos = e.valueAt(e.FirstParameter) if flip else LastPt
# inside edges are taken at this point (see swap of inside/outside
# above - so we can just connect the dots ...
if lastExit:
if not ( bogusX or bogusY ) : commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value))
lastExit = None
PathLog.track(e, flip)
if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position
commands.extend(self.boundaryCommands(obj, lastExit, pos, tc.VertFeed.Value))
lastExit = None
commands.append(cmd)
pos = PathGeom.commandEndPoint(cmd, pos)
elif 0 == len(inside) and 1 == len(outside):
PathLog.track(_vstr(pos), _vstr(lastExit), ' - ', cmd)
# cmd fully excluded by boundary
if not lastExit:
lastExit = pos
pos = PathGeom.commandEndPoint(cmd, pos)
else:
PathLog.track(_vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd)
# cmd pierces boundary
while inside or outside:
ie = [e for e in inside if PathGeom.edgeConnectsTo(e, pos)]
PathLog.track(ie)
if ie:
e = ie[0]
LastPt = e.valueAt(e.LastParameter)
flip = PathGeom.pointsCoincide(pos, LastPt)
newPos = e.valueAt(e.FirstParameter) if flip else LastPt
# inside edges are taken at this point (see swap of inside/outside
# above - so we can just connect the dots ...
if lastExit:
if not ( bogusX or bogusY ) : commands.extend(self.boundaryCommands(obj, lastExit, pos, tc.VertFeed.Value))
lastExit = None
PathLog.track(e, flip)
if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position
commands.extend(PathGeom.cmdsForEdge(e, flip, False, 50, tc.HorizFeed.Value, tc.VertFeed.Value))
inside.remove(e)
commands.extend(PathGeom.cmdsForEdge(e, flip, False, 50, tc.HorizFeed.Value, tc.VertFeed.Value))
inside.remove(e)
pos = newPos
lastExit = newPos
else:
oe = [e for e in outside if PathGeom.edgeConnectsTo(e, pos)]
PathLog.track(oe)
if oe:
e = oe[0]
ptL = e.valueAt(e.LastParameter)
flip = PathGeom.pointsCoincide(pos, ptL)
newPos = e.valueAt(e.FirstParameter) if flip else ptL
# outside edges are never taken at this point (see swap of
# inside/outside above) - so just move along ...
outside.remove(e)
pos = newPos
lastExit = newPos
else:
oe = [e for e in outside if PathGeom.edgeConnectsTo(e, pos)]
PathLog.track(oe)
if oe:
e = oe[0]
ptL = e.valueAt(e.LastParameter)
flip = PathGeom.pointsCoincide(pos, ptL)
newPos = e.valueAt(e.FirstParameter) if flip else ptL
# outside edges are never taken at this point (see swap of
# inside/outside above) - so just move along ...
outside.remove(e)
pos = newPos
else:
PathLog.error('huh?')
import Part
Part.show(Part.Vertex(pos), 'pos')
for e in inside:
Part.show(e, 'ei')
for e in outside:
Part.show(e, 'eo')
raise Exception('This is not supposed to happen')
# Eif
PathLog.error('huh?')
import Part
Part.show(Part.Vertex(pos), 'pos')
for e in inside:
Part.show(e, 'ei')
for e in outside:
Part.show(e, 'eo')
raise Exception('This is not supposed to happen')
# Eif
# Ewhile
# Eif
# pos = PathGeom.commandEndPoint(cmd, pos)
# Eif
# Ewhile
# Eif
else:
PathLog.track('no-move', cmd)
commands.append(cmd)
if lastExit:
commands.extend(self.boundaryCommands(obj, lastExit, None, tc.VertFeed.Value))
lastExit = None
else:
PathLog.warning("No Path Commands for %s" % obj.Base.Label)
commands = []
PathLog.track(commands)
obj.Path = Path.Path(commands)
# pos = PathGeom.commandEndPoint(cmd, pos)
# Eif
else:
PathLog.track('no-move', cmd)
commands.append(cmd)
if lastExit:
commands.extend(self.boundaryCommands(lastExit, None, tc.VertFeed.Value))
lastExit = None
PathLog.track(commands)
return Path.Path(commands)
# Eclass
def Create(base, name='DressupPathBoundary'):
'''Create(base, name='DressupPathBoundary') ... creates a dressup limiting base's Path to a boundary.'''