Path: Implement new module PathSurfaceSupport; Add Spiral cut pattern
New module is shared with 3D Surface operation. Module contains PathGeometryGenerator class. More common methods can be moved into the new module.
This commit is contained in:
@@ -49,6 +49,7 @@ import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathSurfaceSupport as PathSurfaceSupport
|
||||
import time
|
||||
import math
|
||||
import Part
|
||||
@@ -1609,177 +1610,6 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
return final
|
||||
|
||||
# Methods for creating path geometry
|
||||
def _planarMakePathGeom(self, obj, faceShp):
|
||||
'''_planarMakePathGeom(obj, faceShp)...
|
||||
Creates the line/arc cut pattern geometry and returns the intersection with the received faceShp.
|
||||
The resulting intersecting line/arc geometries are then converted to lines or arcs for OCL.'''
|
||||
PathLog.debug('_planarMakePathGeom()')
|
||||
GeoSet = list()
|
||||
|
||||
# Apply drop cutter extra offset and set the max and min XY area of the operation
|
||||
xmin = faceShp.BoundBox.XMin
|
||||
xmax = faceShp.BoundBox.XMax
|
||||
ymin = faceShp.BoundBox.YMin
|
||||
ymax = faceShp.BoundBox.YMax
|
||||
zmin = faceShp.BoundBox.ZMin
|
||||
zmax = faceShp.BoundBox.ZMax
|
||||
|
||||
# Compute weighted center of mass of all faces combined
|
||||
fCnt = 0
|
||||
totArea = 0.0
|
||||
zeroCOM = FreeCAD.Vector(0.0, 0.0, 0.0)
|
||||
for F in faceShp.Faces:
|
||||
comF = F.CenterOfMass
|
||||
areaF = F.Area
|
||||
totArea += areaF
|
||||
fCnt += 1
|
||||
zeroCOM = zeroCOM.add(FreeCAD.Vector(comF.x, comF.y, 0.0).multiply(areaF))
|
||||
if fCnt == 0:
|
||||
PathLog.error(translate('PathWaterline', 'Cannot calculate the Center Of Mass. Using Center of Boundbox.'))
|
||||
zeroCOM = FreeCAD.Vector((xmin + xmax) / 2.0, (ymin + ymax) / 2.0, 0.0)
|
||||
else:
|
||||
avgArea = totArea / fCnt
|
||||
zeroCOM.multiply(1 / fCnt)
|
||||
zeroCOM.multiply(1 / avgArea)
|
||||
COM = FreeCAD.Vector(zeroCOM.x, zeroCOM.y, 0.0)
|
||||
|
||||
# get X, Y, Z spans; Compute center of rotation
|
||||
deltaX = abs(xmax-xmin)
|
||||
deltaY = abs(ymax-ymin)
|
||||
deltaC = math.sqrt(deltaX**2 + deltaY**2)
|
||||
lineLen = deltaC + (2.0 * self.cutter.getDiameter()) # Line length to span boundbox diag with 2x cutter diameter extra on each end
|
||||
halfLL = math.ceil(lineLen / 2.0)
|
||||
cutPasses = math.ceil(lineLen / self.cutOut) + 1 # Number of lines(passes) required to cover lineLen
|
||||
halfPasses = math.ceil(cutPasses / 2.0)
|
||||
bbC = faceShp.BoundBox.Center
|
||||
|
||||
# Generate the line/circle sets to be intersected with the cut-face-area
|
||||
if obj.CutPattern in ['ZigZag', 'Line']:
|
||||
centRot = FreeCAD.Vector(0.0, 0.0, 0.0) # Bottom left corner of face/selection/model
|
||||
cAng = math.atan(deltaX / deltaY) # BoundaryBox angle
|
||||
|
||||
# Determine end points and create top lines
|
||||
x1 = centRot.x - halfLL
|
||||
x2 = centRot.x + halfLL
|
||||
diag = None
|
||||
if obj.CutPatternAngle == 0 or obj.CutPatternAngle == 180:
|
||||
diag = deltaY
|
||||
elif obj.CutPatternAngle == 90 or obj.CutPatternAngle == 270:
|
||||
diag = deltaX
|
||||
else:
|
||||
perpDist = math.cos(cAng - math.radians(obj.CutPatternAngle)) * 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 * (halfPasses - 1)), halfPasses + 1):
|
||||
x1 = centRot.x - halfLL
|
||||
x2 = centRot.x + halfLL
|
||||
y1 = centRot.y + (lc * self.cutOut)
|
||||
# y2 = y1
|
||||
p1 = FreeCAD.Vector(x1, y1, 0.0)
|
||||
p2 = FreeCAD.Vector(x2, y1, 0.0)
|
||||
pntTuples.append( (p1, p2) )
|
||||
|
||||
# Convert end points to lines
|
||||
for (p1, p2) in pntTuples:
|
||||
line = Part.makeLine(p1, p2)
|
||||
GeoSet.append(line)
|
||||
elif obj.CutPattern in ['Circular', 'CircularZigZag']:
|
||||
zTgt = faceShp.BoundBox.ZMin
|
||||
axisRot = FreeCAD.Vector(0.0, 0.0, 1.0)
|
||||
cntr = FreeCAD.Placement()
|
||||
cntr.Rotation = FreeCAD.Rotation(axisRot, 0.0)
|
||||
|
||||
if obj.CircularCenterAt == 'CenterOfMass':
|
||||
cntr.Base = FreeCAD.Vector(COM.x, COM.y, zTgt) # COM # Use center of Mass
|
||||
elif obj.CircularCenterAt == 'CenterOfBoundBox':
|
||||
cent = faceShp.BoundBox.Center
|
||||
cntr.Base = FreeCAD.Vector(cent.x, cent.y, zTgt)
|
||||
elif obj.CircularCenterAt == 'XminYmin':
|
||||
cntr.Base = FreeCAD.Vector(faceShp.BoundBox.XMin, faceShp.BoundBox.YMin, zTgt)
|
||||
elif obj.CircularCenterAt == 'Custom':
|
||||
newCent = FreeCAD.Vector(obj.CircularCenterCustom.x, obj.CircularCenterCustom.y, zTgt)
|
||||
cntr.Base = newCent
|
||||
|
||||
# recalculate cutPasses value, if need be
|
||||
radialPasses = halfPasses
|
||||
if obj.CircularCenterAt != 'CenterOfBoundBox':
|
||||
# make 4 corners of boundbox in XY plane, find which is greatest distance to new circular center
|
||||
EBB = faceShp.BoundBox
|
||||
CORNERS = [
|
||||
FreeCAD.Vector(EBB.XMin, EBB.YMin, 0.0),
|
||||
FreeCAD.Vector(EBB.XMin, EBB.YMax, 0.0),
|
||||
FreeCAD.Vector(EBB.XMax, EBB.YMax, 0.0),
|
||||
FreeCAD.Vector(EBB.XMax, EBB.YMin, 0.0),
|
||||
]
|
||||
dMax = 0.0
|
||||
for c in range(0, 4):
|
||||
dist = CORNERS[c].sub(cntr.Base).Length
|
||||
if dist > dMax:
|
||||
dMax = dist
|
||||
lineLen = dMax + (2.0 * self.cutter.getDiameter()) # Line length to span boundbox diag with 2x cutter diameter extra on each end
|
||||
radialPasses = math.ceil(lineLen / self.cutOut) + 1 # Number of lines(passes) required to cover lineLen
|
||||
|
||||
# Update COM point and current CircularCenter
|
||||
if obj.CircularCenterAt != 'Custom':
|
||||
obj.CircularCenterCustom = cntr.Base
|
||||
|
||||
minRad = self.cutter.getDiameter() * 0.45
|
||||
siX3 = 3 * obj.SampleInterval.Value
|
||||
minRadSI = (siX3 / 2.0) / math.pi
|
||||
if minRad < minRadSI:
|
||||
minRad = minRadSI
|
||||
|
||||
# Make small center circle to start pattern
|
||||
if obj.StepOver > 50:
|
||||
circle = Part.makeCircle(minRad, cntr.Base)
|
||||
GeoSet.append(circle)
|
||||
|
||||
for lc in range(1, radialPasses + 1):
|
||||
rad = (lc * self.cutOut)
|
||||
if rad >= minRad:
|
||||
circle = Part.makeCircle(rad, cntr.Base)
|
||||
GeoSet.append(circle)
|
||||
# Efor
|
||||
COM = cntr.Base
|
||||
# Eif
|
||||
|
||||
if obj.CutPatternReversed is True:
|
||||
GeoSet.reverse()
|
||||
|
||||
if faceShp.BoundBox.ZMin != 0.0:
|
||||
faceShp.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - faceShp.BoundBox.ZMin))
|
||||
|
||||
# Create compound object to bind all lines in Lineset
|
||||
geomShape = Part.makeCompound(GeoSet)
|
||||
|
||||
# Position and rotate the Line and ZigZag geometry
|
||||
if obj.CutPattern in ['Line', 'ZigZag']:
|
||||
if obj.CutPatternAngle != 0.0:
|
||||
geomShape.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), obj.CutPatternAngle)
|
||||
geomShape.Placement.Base = FreeCAD.Vector(bbC.x, bbC.y, 0.0 - geomShape.BoundBox.ZMin)
|
||||
|
||||
if self.showDebugObjects is True:
|
||||
F = FreeCAD.ActiveDocument.addObject('Part::Feature','tmpGeometrySet')
|
||||
F.Shape = geomShape
|
||||
F.purgeTouched()
|
||||
self.tempGroup.addObject(F)
|
||||
|
||||
# Identify intersection of cross-section face and lineset
|
||||
cmnShape = faceShp.common(geomShape)
|
||||
|
||||
if self.showDebugObjects is True:
|
||||
F = FreeCAD.ActiveDocument.addObject('Part::Feature','tmpPathGeometry')
|
||||
F.Shape = cmnShape
|
||||
F.purgeTouched()
|
||||
self.tempGroup.addObject(F)
|
||||
|
||||
self.tmpCOM = FreeCAD.Vector(COM.x, COM.y, faceShp.BoundBox.ZMin)
|
||||
return cmnShape
|
||||
|
||||
def _pathGeomToLinesPointSet(self, obj, compGeoShp):
|
||||
'''_pathGeomToLinesPointSet(obj, compGeoShp)...
|
||||
Convert a compound set of sequential line segments to directionally-oriented collinear groupings.'''
|
||||
@@ -2209,14 +2039,59 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
|
||||
return ARCS
|
||||
|
||||
def _getExperimentalWaterlinePaths(self, obj, PNTSET, csHght):
|
||||
'''_getExperimentalWaterlinePaths(obj, PNTSET, csHght)...
|
||||
def _pathGeomToSpiralPointSet(self, obj, compGeoShp):
|
||||
'''_pathGeomToSpiralPointSet(obj, compGeoShp)...
|
||||
Convert a compound set of sequential line segments to directional, connected groupings.'''
|
||||
PathLog.debug('_pathGeomToSpiralPointSet()')
|
||||
# Extract intersection line segments for return value as list()
|
||||
LINES = list()
|
||||
inLine = list()
|
||||
lnCnt = 0
|
||||
ec = len(compGeoShp.Edges)
|
||||
start = 2
|
||||
|
||||
if obj.CutPatternReversed:
|
||||
edg1 = compGeoShp.Edges[0] # Skip first edge, as it is the closing edge: center to outer tail
|
||||
ec -= 1
|
||||
start = 1
|
||||
else:
|
||||
edg1 = compGeoShp.Edges[1] # Skip first edge, as it is the closing edge: center to outer tail
|
||||
p1 = FreeCAD.Vector(edg1.Vertexes[0].X, edg1.Vertexes[0].Y, 0.0)
|
||||
p2 = FreeCAD.Vector(edg1.Vertexes[1].X, edg1.Vertexes[1].Y, 0.0)
|
||||
tup = ((p1.x, p1.y), (p2.x, p2.y))
|
||||
inLine.append(tup)
|
||||
lst = p2
|
||||
|
||||
for ei in range(start, ec): # Skipped first edge, started with second edge above as edg1
|
||||
edg = compGeoShp.Edges[ei] # Get edge for vertexes
|
||||
sp = FreeCAD.Vector(edg.Vertexes[0].X, edg.Vertexes[0].Y, 0.0) # check point (first / middle point)
|
||||
ep = FreeCAD.Vector(edg.Vertexes[1].X, edg.Vertexes[1].Y, 0.0) # end point
|
||||
tup = ((sp.x, sp.y), (ep.x, ep.y))
|
||||
|
||||
if sp.sub(p2).Length < 0.000001:
|
||||
inLine.append(tup)
|
||||
else:
|
||||
LINES.append(inLine) # Save inLine segments
|
||||
lnCnt += 1
|
||||
inLine = list() # reset container
|
||||
inLine.append(tup)
|
||||
p1 = sp
|
||||
p2 = ep
|
||||
# Efor
|
||||
|
||||
lnCnt += 1
|
||||
LINES.append(inLine) # Save inLine segments
|
||||
|
||||
return LINES
|
||||
|
||||
def _getExperimentalWaterlinePaths(self, PNTSET, csHght, cutPattern):
|
||||
'''_getExperimentalWaterlinePaths(PNTSET, csHght, cutPattern)...
|
||||
Switching function for calling the appropriate path-geometry to OCL points conversion function
|
||||
for the various cut patterns.'''
|
||||
PathLog.debug('_getExperimentalWaterlinePaths()')
|
||||
SCANS = list()
|
||||
|
||||
if obj.CutPattern == 'Line':
|
||||
if cutPattern in ['Line', 'Spiral']:
|
||||
stpOvr = list()
|
||||
for D in PNTSET:
|
||||
for SEG in D:
|
||||
@@ -2230,7 +2105,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
stpOvr.append((P1, P2))
|
||||
SCANS.append(stpOvr)
|
||||
stpOvr = list()
|
||||
elif obj.CutPattern == 'ZigZag':
|
||||
elif cutPattern == 'ZigZag':
|
||||
stpOvr = list()
|
||||
for (dirFlg, LNS) in PNTSET:
|
||||
for SEG in LNS:
|
||||
@@ -2244,7 +2119,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
stpOvr.append((P1, P2))
|
||||
SCANS.append(stpOvr)
|
||||
stpOvr = list()
|
||||
elif obj.CutPattern in ['Circular', 'CircularZigZag']:
|
||||
elif cutPattern in ['Circular', 'CircularZigZag']:
|
||||
# PNTSET is list, by stepover.
|
||||
# Each stepover is a list containing arc/loop descriptions, (sp, ep, cp)
|
||||
for so in range(0, len(PNTSET)):
|
||||
@@ -2279,19 +2154,19 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
return SCANS
|
||||
|
||||
# Main planar scan functions
|
||||
def _stepTransitionCmds(self, obj, lstPnt, first, minSTH, tolrnc):
|
||||
def _stepTransitionCmds(self, obj, cutPattern, lstPnt, first, minSTH, tolrnc):
|
||||
cmds = list()
|
||||
rtpd = False
|
||||
horizGC = 'G0'
|
||||
hSpeed = self.horizRapid
|
||||
height = obj.SafeHeight.Value
|
||||
|
||||
if obj.CutPattern in ['Line', 'Circular']:
|
||||
if cutPattern in ['Line', 'Circular', 'Spiral']:
|
||||
if obj.OptimizeStepOverTransitions is True:
|
||||
height = minSTH + 2.0
|
||||
# if obj.LayerMode == 'Multi-pass':
|
||||
# rtpd = minSTH
|
||||
elif obj.CutPattern in ['ZigZag', 'CircularZigZag']:
|
||||
elif cutPattern in ['ZigZag', 'CircularZigZag']:
|
||||
if obj.OptimizeStepOverTransitions is True:
|
||||
zChng = first.z - lstPnt.z
|
||||
# PathLog.debug('first.z: {}'.format(first.z))
|
||||
@@ -2320,17 +2195,17 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
|
||||
return cmds
|
||||
|
||||
def _breakCmds(self, obj, lstPnt, first, minSTH, tolrnc):
|
||||
def _breakCmds(self, obj, cutPattern, lstPnt, first, minSTH, tolrnc):
|
||||
cmds = list()
|
||||
rtpd = False
|
||||
horizGC = 'G0'
|
||||
hSpeed = self.horizRapid
|
||||
height = obj.SafeHeight.Value
|
||||
|
||||
if obj.CutPattern in ['Line', 'Circular']:
|
||||
if cutPattern in ['Line', 'Circular', 'Spiral']:
|
||||
if obj.OptimizeStepOverTransitions is True:
|
||||
height = minSTH + 2.0
|
||||
elif obj.CutPattern in ['ZigZag', 'CircularZigZag']:
|
||||
elif cutPattern in ['ZigZag', 'CircularZigZag']:
|
||||
if obj.OptimizeStepOverTransitions is True:
|
||||
zChng = first.z - lstPnt.z
|
||||
if abs(zChng) < tolrnc: # transitions to same Z height
|
||||
@@ -2835,26 +2710,23 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
lastCsHght = csHght
|
||||
|
||||
# Clear layer as needed
|
||||
(useOfst, usePat, clearLastLayer) = self._clearLayer(obj, ca, lastCA, clearLastLayer)
|
||||
##if self.showDebugObjects is True and (usePat or useOfst):
|
||||
## OA = FreeCAD.ActiveDocument.addObject('Part::Feature', 'clearPatternArea_{}'.format(round(csHght, 2)))
|
||||
## OA.Shape = clearArea
|
||||
## OA.purgeTouched()
|
||||
## self.tempGroup.addObject(OA)
|
||||
if usePat:
|
||||
commands.extend(self._makeCutPatternLayerPaths(JOB, obj, clearArea, csHght))
|
||||
if useOfst:
|
||||
commands.extend(self._makeOffsetLayerPaths(JOB, obj, clearArea, csHght))
|
||||
(clrLyr, clearLastLayer) = self._clearLayer(obj, ca, lastCA, clearLastLayer)
|
||||
if clrLyr == 'Offset':
|
||||
commands.extend(self._makeOffsetLayerPaths(obj, clearArea, csHght))
|
||||
elif clrLyr:
|
||||
cutPattern = obj.CutPattern
|
||||
if clearLastLayer is False:
|
||||
cutPattern = obj.ClearLastLayer
|
||||
commands.extend(self._makeCutPatternLayerPaths(JOB, obj, clearArea, csHght, cutPattern))
|
||||
# Efor
|
||||
|
||||
if clearLastLayer:
|
||||
(useOfst, usePat, cLL) = self._clearLayer(obj, 1, 1, False)
|
||||
clearArea.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - lastClearArea.BoundBox.ZMin))
|
||||
if usePat:
|
||||
commands.extend(self._makeCutPatternLayerPaths(JOB, obj, lastClearArea, lastCsHght))
|
||||
|
||||
if useOfst:
|
||||
commands.extend(self._makeOffsetLayerPaths(JOB, obj, lastClearArea, lastCsHght))
|
||||
(clrLyr, cLL) = self._clearLayer(obj, 1, 1, False)
|
||||
lastClearArea.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - lastClearArea.BoundBox.ZMin))
|
||||
if clrLyr == 'Offset':
|
||||
commands.extend(self._makeOffsetLayerPaths(obj, lastClearArea, lastCsHght))
|
||||
elif clrLyr:
|
||||
commands.extend(self._makeCutPatternLayerPaths(JOB, obj, lastClearArea, lastCsHght, obj.ClearLastLayer))
|
||||
|
||||
PathLog.info("Waterline: All layer scans combined took " + str(time.time() - t_begin) + " s")
|
||||
return commands
|
||||
@@ -2928,7 +2800,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
|
||||
commands.append(Path.Command('N (Cut Area {}.)'.format(round(csHght, 2))))
|
||||
start = 1
|
||||
if ofstPlnrShp.BoundBox.ZMin < obj.IgnoreOuterAbove:
|
||||
if csHght < obj.IgnoreOuterAbove:
|
||||
start = 0
|
||||
for w in range(start, len(ofstPlnrShp.Wires)):
|
||||
wire = ofstPlnrShp.Wires[w]
|
||||
@@ -2946,90 +2818,97 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
|
||||
return commands
|
||||
|
||||
def _makeCutPatternLayerPaths(self, JOB, obj, clrAreaShp, csHght):
|
||||
def _makeCutPatternLayerPaths(self, JOB, obj, clrAreaShp, csHght, cutPattern):
|
||||
PathLog.debug('_makeCutPatternLayerPaths()')
|
||||
commands = []
|
||||
|
||||
clrAreaShp.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - clrAreaShp.BoundBox.ZMin))
|
||||
pathGeom = self._planarMakePathGeom(obj, clrAreaShp)
|
||||
pathGeom.translate(FreeCAD.Vector(0.0, 0.0, csHght - pathGeom.BoundBox.ZMin))
|
||||
# clrAreaShp.translate(FreeCAD.Vector(0.0, 0.0, csHght - clrAreaShp.BoundBox.ZMin))
|
||||
|
||||
if self.showDebugObjects is True:
|
||||
OA = FreeCAD.ActiveDocument.addObject('Part::Feature', 'pathGeom_{}'.format(round(csHght, 2)))
|
||||
OA.Shape = pathGeom
|
||||
OA.purgeTouched()
|
||||
self.tempGroup.addObject(OA)
|
||||
|
||||
# Convert pathGeom to gcode more efficiently
|
||||
if obj.CutPattern == 'Offset':
|
||||
commands.extend(self._makeOffsetLayerPaths(JOB, obj, clrAreaShp, csHght))
|
||||
if cutPattern == 'Offset':
|
||||
commands.extend(self._makeOffsetLayerPaths(obj, clrAreaShp, csHght))
|
||||
else:
|
||||
clrAreaShp.translate(FreeCAD.Vector(0.0, 0.0, csHght - clrAreaShp.BoundBox.ZMin))
|
||||
if obj.CutPattern == 'Line':
|
||||
# Request path geometry from external support class
|
||||
PGG = PathSurfaceSupport.PathGeometryGenerator(obj, clrAreaShp, cutPattern)
|
||||
if self.showDebugObjects:
|
||||
PGG.setDebugObjectsGroup(self.tempGroup)
|
||||
self.tmpCOM = PGG.getCenterOfMass()
|
||||
pathGeom = PGG.getPathGeometryGenerator()
|
||||
if not pathGeom:
|
||||
PathLog.warning('No path geometry generated.')
|
||||
return commands
|
||||
pathGeom.translate(FreeCAD.Vector(0.0, 0.0, csHght - pathGeom.BoundBox.ZMin))
|
||||
|
||||
if self.showDebugObjects is True:
|
||||
OA = FreeCAD.ActiveDocument.addObject('Part::Feature', 'pathGeom_{}'.format(round(csHght, 2)))
|
||||
OA.Shape = pathGeom
|
||||
OA.purgeTouched()
|
||||
self.tempGroup.addObject(OA)
|
||||
|
||||
if cutPattern == 'Line':
|
||||
pntSet = self._pathGeomToLinesPointSet(obj, pathGeom)
|
||||
elif obj.CutPattern == 'ZigZag':
|
||||
elif cutPattern == 'ZigZag':
|
||||
pntSet = self._pathGeomToZigzagPointSet(obj, pathGeom)
|
||||
elif obj.CutPattern in ['Circular', 'CircularZigZag']:
|
||||
elif cutPattern in ['Circular', 'CircularZigZag']:
|
||||
pntSet = self._pathGeomToArcPointSet(obj, pathGeom)
|
||||
stpOVRS = self._getExperimentalWaterlinePaths(obj, pntSet, csHght)
|
||||
# PathLog.debug('stpOVRS:\n{}'.format(stpOVRS))
|
||||
elif cutPattern == 'Spiral':
|
||||
pntSet = self._pathGeomToSpiralPointSet(obj, pathGeom)
|
||||
|
||||
stpOVRS = self._getExperimentalWaterlinePaths(pntSet, csHght, cutPattern)
|
||||
safePDC = False
|
||||
cmds = self._clearGeomToPaths(JOB, obj, safePDC, stpOVRS, csHght)
|
||||
cmds = self._clearGeomToPaths(JOB, obj, safePDC, stpOVRS, cutPattern)
|
||||
commands.extend(cmds)
|
||||
|
||||
return commands
|
||||
|
||||
def _makeOffsetLayerPaths(self, JOB, obj, clrAreaShp, csHght):
|
||||
def _makeOffsetLayerPaths(self, obj, clrAreaShp, csHght):
|
||||
PathLog.debug('_makeOffsetLayerPaths()')
|
||||
PathLog.warning('Using `Offset` for clearing bottom layer.')
|
||||
cmds = list()
|
||||
# ofst = obj.BoundaryAdjustment.Value
|
||||
ofst = 0.0 - self.cutOut # - self.cutter.getDiameter() # (self.radius + (tolrnc / 10.0))
|
||||
ofst = 0.0 - self.cutOut
|
||||
shape = clrAreaShp
|
||||
cont = True
|
||||
cnt = 0
|
||||
while cont:
|
||||
ofstArea = self._extractFaceOffset(shape, ofst, makeComp=True)
|
||||
if not ofstArea:
|
||||
PathLog.warning('No offset clearing area returned.')
|
||||
# PathLog.debug('No offset clearing area returned.')
|
||||
break
|
||||
for F in ofstArea.Faces:
|
||||
cmds.extend(self._wiresToWaterlinePath(obj, F, csHght))
|
||||
shape = ofstArea
|
||||
if cnt == 0:
|
||||
ofst = 0.0 - self.cutOut # self.cutter.Diameter()
|
||||
ofst = 0.0 - self.cutOut
|
||||
cnt += 1
|
||||
return cmds
|
||||
|
||||
def _clearGeomToPaths(self, JOB, obj, safePDC, SCANDATA, csHght):
|
||||
def _clearGeomToPaths(self, JOB, obj, safePDC, stpOVRS, cutPattern):
|
||||
PathLog.debug('_clearGeomToPaths()')
|
||||
|
||||
GCODE = [Path.Command('N (Beginning of Single-pass layer.)', {})]
|
||||
tolrnc = JOB.GeometryTolerance.Value
|
||||
lenSCANDATA = len(SCANDATA)
|
||||
lenstpOVRS = len(stpOVRS)
|
||||
gDIR = ['G3', 'G2']
|
||||
|
||||
if self.CutClimb is True:
|
||||
gDIR = ['G2', 'G3']
|
||||
|
||||
# Send cutter to x,y position of first point on first line
|
||||
first = SCANDATA[0][0][0] # [step][item][point]
|
||||
first = stpOVRS[0][0][0] # [step][item][point]
|
||||
GCODE.append(Path.Command('G0', {'X': first.x, 'Y': first.y, 'F': self.horizRapid}))
|
||||
|
||||
# Cycle through step-over sections (line segments or arcs)
|
||||
odd = True
|
||||
lstStpEnd = None
|
||||
for so in range(0, lenSCANDATA):
|
||||
for so in range(0, lenstpOVRS):
|
||||
cmds = list()
|
||||
PRTS = SCANDATA[so]
|
||||
PRTS = stpOVRS[so]
|
||||
lenPRTS = len(PRTS)
|
||||
first = PRTS[0][0] # first point of arc/line stepover group
|
||||
last = None
|
||||
cmds.append(Path.Command('N (Begin step {}.)'.format(so), {}))
|
||||
|
||||
if so > 0:
|
||||
if obj.CutPattern == 'CircularZigZag':
|
||||
if cutPattern == 'CircularZigZag':
|
||||
if odd is True:
|
||||
odd = False
|
||||
else:
|
||||
@@ -3037,7 +2916,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
# minTrnsHght = self._getMinSafeTravelHeight(safePDC, lstStpEnd, first) # Check safe travel height against fullSTL
|
||||
minTrnsHght = obj.SafeHeight.Value
|
||||
# cmds.append(Path.Command('N (Transition: last, first: {}, {}: minSTH: {})'.format(lstStpEnd, first, minTrnsHght), {}))
|
||||
cmds.extend(self._stepTransitionCmds(obj, lstStpEnd, first, minTrnsHght, tolrnc))
|
||||
cmds.extend(self._stepTransitionCmds(obj, cutPattern, lstStpEnd, first, minTrnsHght, tolrnc))
|
||||
|
||||
# Cycle through current step-over parts
|
||||
for i in range(0, lenPRTS):
|
||||
@@ -3048,14 +2927,14 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
# minSTH = self._getMinSafeTravelHeight(safePDC, last, nxtStart) # Check safe travel height against fullSTL
|
||||
minSTH = obj.SafeHeight.Value
|
||||
cmds.append(Path.Command('N (Break)', {}))
|
||||
cmds.extend(self._breakCmds(obj, last, nxtStart, minSTH, tolrnc))
|
||||
cmds.extend(self._breakCmds(obj, cutPattern, last, nxtStart, minSTH, tolrnc))
|
||||
else:
|
||||
cmds.append(Path.Command('N (part {}.)'.format(i + 1), {}))
|
||||
if obj.CutPattern in ['Line', 'ZigZag']:
|
||||
if cutPattern in ['Line', 'ZigZag', 'Spiral']:
|
||||
start, last = prt
|
||||
cmds.append(Path.Command('G1', {'X': start.x, 'Y': start.y, 'Z': start.z, 'F': self.horizFeed}))
|
||||
cmds.append(Path.Command('G1', {'X': last.x, 'Y': last.y, 'F': self.horizFeed}))
|
||||
elif obj.CutPattern in ['Circular', 'CircularZigZag']:
|
||||
elif cutPattern in ['Circular', 'CircularZigZag']:
|
||||
start, last, centPnt, cMode = prt
|
||||
gcode = self._makeGcodeArc(start, last, odd, gDIR, tolrnc)
|
||||
cmds.extend(gcode)
|
||||
@@ -3291,22 +3170,27 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
PathLog.debug('_clearLayer()')
|
||||
usePat = False
|
||||
useOfst = False
|
||||
clrLyr = False
|
||||
|
||||
if obj.ClearLastLayer == 'Off':
|
||||
if obj.CutPattern != 'None':
|
||||
usePat = True
|
||||
clrLyr = obj.CutPattern
|
||||
else:
|
||||
if ca == lastCA:
|
||||
obj.CutPattern = 'None'
|
||||
if ca == lastCA: # if current iteration is last layer
|
||||
PathLog.debug('... Clearing bottom layer.')
|
||||
'''
|
||||
if obj.ClearLastLayer == 'Offset':
|
||||
obj.CutPattern = 'None'
|
||||
# obj.CutPattern = 'None'
|
||||
useOfst = True
|
||||
else:
|
||||
obj.CutPattern = obj.ClearLastLayer
|
||||
# obj.CutPattern = obj.ClearLastLayer
|
||||
usePat = True
|
||||
'''
|
||||
clrLyr = obj.ClearLastLayer
|
||||
clearLastLayer = False
|
||||
|
||||
return (useOfst, usePat, clearLastLayer)
|
||||
return (clrLyr, clearLastLayer)
|
||||
|
||||
# Support methods
|
||||
def resetOpVariables(self, all=True):
|
||||
|
||||
Reference in New Issue
Block a user