diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpSurfaceEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpSurfaceEdit.ui
index 45902b44ff..e39c46b7bc 100644
--- a/src/Mod/Path/Gui/Resources/panels/PageOpSurfaceEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/PageOpSurfaceEdit.ui
@@ -156,7 +156,7 @@
-
-
+
<html><head/><body><p>Profile the edges of the selection.</p></body></html>
diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpWaterlineEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpWaterlineEdit.ui
index a9cdcac0ea..38462d7ed5 100644
--- a/src/Mod/Path/Gui/Resources/panels/PageOpWaterlineEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/PageOpWaterlineEdit.ui
@@ -165,16 +165,6 @@
None
- -
-
- Line
-
-
- -
-
- ZigZag
-
-
-
Circular
@@ -185,6 +175,26 @@
CircularZigZag
+ -
+
+ Line
+
+
+ -
+
+ Offset
+
+
+ -
+
+ Spiral
+
+
+ -
+
+ ZigZag
+
+
-
diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py
index 433e26bb2e..3c879430c3 100644
--- a/src/Mod/Path/PathScripts/PathSurface.py
+++ b/src/Mod/Path/PathScripts/PathSurface.py
@@ -38,7 +38,8 @@ from PySide import QtCore
try:
import ocl
except ImportError:
- msg = QtCore.QCoreApplication.translate("PathSurface", "This operation requires OpenCamLib to be installed.")
+ msg = QtCore.QCoreApplication.translate("PathSurface",
+ "This operation requires OpenCamLib to be installed.")
FreeCAD.Console.PrintError(msg + "\n")
raise ImportError
# import sys
@@ -206,7 +207,7 @@ class ObjectSurface(PathOp.ObjectOp):
'BoundBox': ['BaseBoundBox', 'Stock'],
'PatternCenterAt': ['CenterOfMass', 'CenterOfBoundBox', 'XminYmin', 'Custom'],
'CutMode': ['Conventional', 'Climb'],
- 'CutPattern': ['Line', 'Circular', 'CircularZigZag', 'Offset', 'Spiral', 'ZigZag'], # Additional goals ['Offset', 'ZigZagOffset', 'Grid', 'Triangle']
+ 'CutPattern': ['Circular', 'CircularZigZag', 'Line', 'Offset', 'Spiral', 'ZigZag'], # Additional goals ['Offset', 'ZigZagOffset', 'Grid', 'Triangle']
'DropCutterDir': ['X', 'Y'],
'HandleMultipleFeatures': ['Collectively', 'Individually'],
'LayerMode': ['Single-pass', 'Multi-pass'],
@@ -575,18 +576,18 @@ class ObjectSurface(PathOp.ObjectOp):
PathSurfaceSupport._prepareModelSTLs(self, JOB, obj, m, ocl)
Mdl = JOB.Model.Group[m]
- if FACES[m] is False:
- PathLog.error('No data for model base: {}'.format(JOB.Model.Group[m].Label))
- else:
+ if FACES[m]:
+ PathLog.debug('Working on Model.Group[{}]: {}'.format(m, Mdl.Label))
if m > 0:
# Raise to clearance between models
CMDS.append(Path.Command('N (Transition to base: {}.)'.format(Mdl.Label)))
CMDS.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
- PathLog.info('Working on Model.Group[{}]: {}'.format(m, Mdl.Label))
# make stock-model-voidShapes STL model for avoidance detection on transitions
PathSurfaceSupport._makeSafeSTL(self, JOB, obj, m, FACES[m], VOIDS[m], ocl)
# Process model/faces - OCL objects must be ready
CMDS.extend(self._processCutAreas(JOB, obj, m, FACES[m], VOIDS[m]))
+ else:
+ PathLog.debug('No data for model base: {}'.format(JOB.Model.Group[m].Label))
# Save gcode produced
self.commandlist.extend(CMDS)
@@ -659,7 +660,8 @@ class ObjectSurface(PathOp.ObjectOp):
exTime = str(tMins) + ' min. ' + str(round(tSecs, 5)) + ' sec.'
else:
exTime = str(round(execTime, 5)) + ' sec.'
- FreeCAD.Console.PrintMessage('3D Surface operation time is {}\n'.format(exTime))
+ msg = translate('PathSurface', 'operation time is')
+ FreeCAD.Console.PrintMessage('3D Surface ' + msg + '{}\n'.format(exTime))
if self.cancelOperation:
FreeCAD.ActiveDocument.openTransaction(translate("PathSurface", "Canceled 3D Surface operation."))
@@ -751,27 +753,21 @@ class ObjectSurface(PathOp.ObjectOp):
if obj.ProfileEdges != 'None':
prflShp = self.profileShapes[mdlIdx][fsi]
if prflShp is False:
- PathLog.error('No profile shape is False.')
+ msg = translate('PathSurface', 'No profile geometry shape returned.')
+ PathLog.error(msg)
return list()
- if self.showDebugObjects:
- P = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpNewProfileShape')
- P.Shape = prflShp
- P.purgeTouched()
- self.tempGroup.addObject(P)
+ self.showDebugObject(prflShp, 'NewProfileShape')
# get offset path geometry and perform OCL scan with that geometry
pathOffsetGeom = self._offsetFacesToPointData(obj, prflShp)
if pathOffsetGeom is False:
- PathLog.error('No profile geometry returned.')
+ msg = translate('PathSurface', 'No profile path geometry returned.')
+ PathLog.error(msg)
return list()
profScan = [self._planarPerformOclScan(obj, pdc, pathOffsetGeom, True)]
geoScan = list()
if obj.ProfileEdges != 'Only':
- if self.showDebugObjects:
- F = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpCutArea')
- F.Shape = cmpdShp
- F.purgeTouched()
- self.tempGroup.addObject(F)
+ self.showDebugObject(cmpdShp, 'CutArea')
# get internal path geometry and perform OCL scan with that geometry
PGG = PathSurfaceSupport.PathGeometryGenerator(obj, cmpdShp, obj.CutPattern)
if self.showDebugObjects:
@@ -779,12 +775,14 @@ class ObjectSurface(PathOp.ObjectOp):
self.tmpCOM = PGG.getCenterOfPattern()
pathGeom = PGG.generatePathGeometry()
if pathGeom is False:
- PathLog.error('No path geometry returned.')
+ msg = translate('PathSurface', 'No clearing shape returned.')
+ PathLog.error(msg)
return list()
if obj.CutPattern == 'Offset':
useGeom = self._offsetFacesToPointData(obj, pathGeom, profile=False)
if useGeom is False:
- PathLog.error('No profile geometry returned.')
+ msg = translate('PathSurface', 'No clearing path geometry returned.')
+ PathLog.error(msg)
return list()
geoScan = [self._planarPerformOclScan(obj, pdc, useGeom, True)]
else:
@@ -803,7 +801,8 @@ class ObjectSurface(PathOp.ObjectOp):
SCANDATA.extend(profScan)
if len(SCANDATA) == 0:
- PathLog.error('No scan data to convert to Gcode.')
+ msg = translate('PathSuface', 'No scan data to convert to Gcode.')
+ PathLog.error(msg)
return list()
# Apply depth offset
@@ -1173,7 +1172,6 @@ class ObjectSurface(PathOp.ObjectOp):
# Manage step over transition and CircularZigZag direction
if so > 0:
- # PathLog.debug(' stepover index: {}'.format(so))
# Control ZigZag direction
if obj.CutPattern == 'CircularZigZag':
if odd is True:
@@ -1195,7 +1193,6 @@ class ObjectSurface(PathOp.ObjectOp):
for i in range(0, lenAdjPrts):
prt = ADJPRTS[i]
lenPrt = len(prt)
- # PathLog.debug(' adj parts index - lenPrt: {} - {}'.format(i, lenPrt))
if prt == 'BRK' and prtsHasCmds is True:
nxtStart = ADJPRTS[i + 1][0]
minSTH = self._getMinSafeTravelHeight(safePDC, last, nxtStart, minDep=None) # Check safe travel height against fullSTL
@@ -2108,6 +2105,13 @@ class ObjectSurface(PathOp.ObjectOp):
zMax = minDep
return zMax
+ def showDebugObject(self, objShape, objName):
+ if self.showDebugObjects:
+ do = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmp_' + objName)
+ do.Shape = objShape
+ do.purgeTouched()
+ self.tempGroup.addObject(do)
+
def SetupProperties():
''' SetupProperties() ... Return list of properties required for operation.'''
diff --git a/src/Mod/Path/PathScripts/PathSurfaceGui.py b/src/Mod/Path/PathScripts/PathSurfaceGui.py
index a26b290827..d3bff733b7 100644
--- a/src/Mod/Path/PathScripts/PathSurfaceGui.py
+++ b/src/Mod/Path/PathScripts/PathSurfaceGui.py
@@ -42,6 +42,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
def initPage(self, obj):
self.setTitle("3D Surface")
# self.updateVisibility()
+ # retrieve property enumerations
+ self.propEnums = PathSurface.ObjectSurface.opPropertyEnumerations(False)
def getForm(self):
'''getForm() ... returns UI'''
@@ -52,23 +54,37 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
self.updateToolController(obj, self.form.toolController)
self.updateCoolant(obj, self.form.coolantController)
- PathGui.updateInputField(obj, 'DepthOffset', self.form.depthOffset)
- PathGui.updateInputField(obj, 'SampleInterval', self.form.sampleInterval)
-
if obj.BoundBox != str(self.form.boundBoxSelect.currentText()):
obj.BoundBox = str(self.form.boundBoxSelect.currentText())
if obj.ScanType != str(self.form.scanType.currentText()):
obj.ScanType = str(self.form.scanType.currentText())
- if obj.StepOver != self.form.stepOver.value():
- obj.StepOver = self.form.stepOver.value()
-
if obj.LayerMode != str(self.form.layerMode.currentText()):
obj.LayerMode = str(self.form.layerMode.currentText())
- if obj.CutPattern != str(self.form.cutPattern.currentText()):
- obj.CutPattern = str(self.form.cutPattern.currentText())
+ """
+ The following method of getting values from the UI form
+ allows for translations of combobox options in the UI.
+ The requirement is that the enumeration lists must
+ be in the same order in both the opPropertyEnumerations() method
+ and the UI panel QComboBox list.
+ Another step to ensure sychronization of the two lists is to
+ populate the list dynamically in this Gui module in `initPage()`
+ using the property enumerations list when loading the UI panel.
+ This type of dynamic combobox population is done for the
+ Tool Controller selection.
+ """
+ val = self.propEnums['CutPattern'][self.form.cutPattern.currentIndex()]
+ if obj.CutPattern != val:
+ obj.CutPattern = val
+
+ val = self.propEnums['ProfileEdges'][self.form.profileEdges.currentIndex()]
+ if obj.ProfileEdges != val:
+ obj.ProfileEdges = val
+
+ if obj.AvoidLastX_Faces != self.form.avoidLastX_Faces.value():
+ obj.AvoidLastX_Faces = self.form.avoidLastX_Faces.value()
obj.DropCutterExtraOffset.x = FreeCAD.Units.Quantity(self.form.boundBoxExtraOffsetX.text()).Value
obj.DropCutterExtraOffset.y = FreeCAD.Units.Quantity(self.form.boundBoxExtraOffsetY.text()).Value
@@ -77,6 +93,10 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
obj.DropCutterDir = str(self.form.dropCutterDirSelect.currentText())
PathGui.updateInputField(obj, 'DepthOffset', self.form.depthOffset)
+
+ if obj.StepOver != self.form.stepOver.value():
+ obj.StepOver = self.form.stepOver.value()
+
PathGui.updateInputField(obj, 'SampleInterval', self.form.sampleInterval)
if obj.UseStartPoint != self.form.useStartPoint.isChecked():
@@ -95,7 +115,23 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
self.selectInComboBox(obj.BoundBox, self.form.boundBoxSelect)
self.selectInComboBox(obj.ScanType, self.form.scanType)
self.selectInComboBox(obj.LayerMode, self.form.layerMode)
- self.selectInComboBox(obj.CutPattern, self.form.cutPattern)
+
+ """
+ The following method of setting values in the UI form
+ allows for translations of combobox options in the UI.
+ The requirement is that the enumeration lists must
+ be in the same order in both the opPropertyEnumerations() method
+ and the UI panel QComboBox list.
+ The original method is commented out below.
+ """
+ idx = self.propEnums['CutPattern'].index(obj.CutPattern)
+ self.form.cutPattern.setCurrentIndex(idx)
+ idx = self.propEnums['ProfileEdges'].index(obj.ProfileEdges)
+ self.form.profileEdges.setCurrentIndex(idx)
+ # self.selectInComboBox(obj.CutPattern, self.form.cutPattern)
+ # self.selectInComboBox(obj.ProfileEdges, self.form.profileEdges)
+
+ self.form.avoidLastX_Faces.setValue(obj.AvoidLastX_Faces)
self.form.boundBoxExtraOffsetX.setText(FreeCAD.Units.Quantity(obj.DropCutterExtraOffset.x, FreeCAD.Units.Length).UserString)
self.form.boundBoxExtraOffsetY.setText(FreeCAD.Units.Quantity(obj.DropCutterExtraOffset.y, FreeCAD.Units.Length).UserString)
self.selectInComboBox(obj.DropCutterDir, self.form.dropCutterDirSelect)
@@ -129,6 +165,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
signals.append(self.form.scanType.currentIndexChanged)
signals.append(self.form.layerMode.currentIndexChanged)
signals.append(self.form.cutPattern.currentIndexChanged)
+ signals.append(self.form.profileEdges.currentIndexChanged)
+ signals.append(self.form.avoidLastX_Faces.editingFinished)
signals.append(self.form.boundBoxExtraOffsetX.editingFinished)
signals.append(self.form.boundBoxExtraOffsetY.editingFinished)
signals.append(self.form.dropCutterDirSelect.currentIndexChanged)
diff --git a/src/Mod/Path/PathScripts/PathSurfaceSupport.py b/src/Mod/Path/PathScripts/PathSurfaceSupport.py
index fcaface258..3e5856a1bc 100644
--- a/src/Mod/Path/PathScripts/PathSurfaceSupport.py
+++ b/src/Mod/Path/PathScripts/PathSurfaceSupport.py
@@ -91,12 +91,14 @@ class PathGeometryGenerator:
if shape.BoundBox.ZMin != 0.0:
shape.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - shape.BoundBox.ZMin))
- if shape.BoundBox.ZLength == 0.0:
- self.shape = shape
+ if shape.BoundBox.ZLength > 1.0e-8:
+ msg = translate('PathSurfaceSupport',
+ 'Shape appears to not be horizontal planar.')
+ msg += ' ZMax == {} mm.\n'.format(shape.BoundBox.ZMax)
+ FreeCAD.Console.PrintWarning(msg)
else:
- FreeCAD.Console.PrintWarning('Shape appears to not be horizontal planar. ZMax is {}.\n'.format(shape.BoundBox.ZMax))
-
- self._prepareConstants()
+ self.shape = shape
+ self._prepareConstants()
def _prepareConstants(self):
# Apply drop cutter extra offset and set the max and min XY area of the operation
@@ -118,8 +120,11 @@ class PathGeometryGenerator:
fCnt += 1
zeroCOM = zeroCOM.add(FreeCAD.Vector(comF.x, comF.y, 0.0).multiply(areaF))
if fCnt == 0:
- msg = translate(self.module, 'Cannot calculate the Center Of Mass. Using Center of Boundbox instead.')
- FreeCAD.Console.PrintError(msg + '\n')
+ msg = translate('PathSurfaceSupport',
+ 'Cannot calculate the Center Of Mass.')
+ msg += ' ' + translate('PathSurfaceSupport',
+ 'Using Center of Boundbox instead.') + '\n'
+ FreeCAD.Console.PrintError(msg)
bbC = self.shape.BoundBox.Center
zeroCOM = FreeCAD.Vector(bbC.x, bbC.y, 0.0)
else:
@@ -156,11 +161,9 @@ class PathGeometryGenerator:
'''generatePathGeometry()...
Call this function to obtain the path geometry shape, generated by this class.'''
if self.pattern == 'None':
- # FreeCAD.Console.PrintWarning('PGG: No pattern set.\n')
return False
if self.shape is None:
- # FreeCAD.Console.PrintWarning('PGG: No shape set.\n')
return False
cmd = 'self._' + self.pattern + '()'
@@ -408,7 +411,6 @@ class PathGeometryGenerator:
while cont:
ofstArea = self._getFaceOffset(shape, ofst)
if not ofstArea:
- # FreeCAD.Console.PrintWarning('PGG: No offset clearing area returned.\n')
cont = False
True if cont else False # cont used for LGTM
break
@@ -426,9 +428,8 @@ class PathGeometryGenerator:
'''_getFaceOffset(shape, offset) ... internal function.
Original _buildPathArea() version copied from PathAreaOp.py module. This version is modified.
Adjustments made based on notes by @sliptonic at this webpage: https://github.com/sliptonic/FreeCAD/wiki/PathArea-notes.'''
- PathLog.debug('_getFaceOffset()')
-
areaParams = {}
+
areaParams['Offset'] = offset
areaParams['Fill'] = 1 # 1
areaParams['Coplanar'] = 0
@@ -439,7 +440,6 @@ class PathGeometryGenerator:
areaParams['Project'] = True
area = Path.Area() # Create instance of Area() class object
- # area.setPlane(PathUtils.makeWorkplane(shape)) # Set working plane
area.setPlane(PathUtils.makeWorkplane(self.wpc)) # Set working plane to normal at Z=1
area.add(shape)
area.setParams(**areaParams) # set parameters
@@ -475,7 +475,10 @@ class ProcessSelectedFaces:
self.module = None
self.radius = None
self.depthParams = None
- self.msgNoFaces = translate(self.module, 'Face selection is unavailable for Rotational scans. Ignoring selected faces.') + '\n'
+ self.msgNoFaces = translate('PathSurfaceSupport',
+ 'Face selection is unavailable for Rotational scans.') + '\n'
+ self.msgNoFaces += ' ' + translate('PathSurfaceSupport',
+ 'Ignoring selected faces.') + '\n'
self.JOB = JOB
self.obj = obj
self.profileEdges = 'None'
@@ -558,9 +561,8 @@ class ProcessSelectedFaces:
self.modelSTLs[m] = True
# Process each model base, as a whole, as needed
- # PathLog.debug(' -Pre-processing all models in Job.')
for m in range(0, lenGRP):
- if fShapes[m] is False:
+ if self.modelSTLs[m] and not fShapes[m]:
PathLog.debug(' -Pre-processing {} as a whole.'.format(GRP[m].Label))
if self.obj.BoundBox == 'BaseBoundBox':
base = GRP[m]
@@ -569,7 +571,9 @@ class ProcessSelectedFaces:
pPEB = self._preProcessEntireBase(base, m)
if pPEB is False:
- FreeCAD.Console.PrintError(' -Failed to pre-process base as a whole.\n')
+ msg = translate('PathSurfaceSupport',
+ 'Failed to pre-process base as a whole.') + '\n'
+ FreeCAD.Console.PrintError(msg)
else:
(fcShp, prflShp) = pPEB
if fcShp is not False:
@@ -646,11 +650,13 @@ class ProcessSelectedFaces:
if F[m] is False:
F[m] = list()
F[m].append((shape, faceIdx))
+ PathLog.debug('.. Cutting {}'.format(sub))
hasFace = True
else:
if V[m] is False:
V[m] = list()
V[m].append((shape, faceIdx))
+ PathLog.debug('.. Avoiding {}'.format(sub))
hasVoid = True
return (hasFace, hasVoid)
@@ -678,14 +684,16 @@ class ProcessSelectedFaces:
PathLog.debug('Attempting to get cross-section of collective faces.')
if len(outFCS) == 0:
- msg = translate('PathSurfaceSupport', 'Cannot process selected faces. Check horizontal surface exposure.')
+ msg = translate('PathSurfaceSupport',
+ 'Cannot process selected faces. Check horizontal surface exposure.')
FreeCAD.Console.PrintError(msg + '\n')
cont = False
else:
cfsL = Part.makeCompound(outFCS)
# Handle profile edges request
- if cont is True and self.profileEdges != 'None':
+ if cont and self.profileEdges != 'None':
+ PathLog.debug('.. include Profile Edge')
ofstVal = self._calculateOffsetValue(isHole)
psOfst = extractFaceOffset(cfsL, ofstVal, self.wpc)
if psOfst is not False:
@@ -694,7 +702,6 @@ class ProcessSelectedFaces:
mFS = True
cont = False
else:
- # FreeCAD.Console.PrintError(' -Failed to create profile geometry for selected faces.\n')
cont = False
if cont:
@@ -706,8 +713,10 @@ class ProcessSelectedFaces:
ofstVal = self._calculateOffsetValue(isHole)
faceOfstShp = extractFaceOffset(cfsL, ofstVal, self.wpc)
- if faceOfstShp is False:
- FreeCAD.Console.PrintError(' -Failed to create offset face.\n')
+ if not faceOfstShp:
+ msg = translate('PathSurfaceSupport',
+ 'Failed to create offset face.') + '\n'
+ FreeCAD.Console.PrintError(msg)
cont = False
if cont:
@@ -764,7 +773,6 @@ class ProcessSelectedFaces:
mFS.append(True)
cont = False
else:
- # PathLog.error(' -Failed to create profile geometry for Face{}.'.format(fNum))
cont = False
if cont:
@@ -825,7 +833,6 @@ class ProcessSelectedFaces:
avoid = Part.makeCompound(outFCS)
if self.showDebugObjects:
- PathLog.debug('*** tmpAvoidArea')
P = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpVoidEnvelope')
P.Shape = avoid
P.purgeTouched()
@@ -833,7 +840,6 @@ class ProcessSelectedFaces:
if cont:
if self.showDebugObjects:
- PathLog.debug('*** tmpVoidCompound')
P = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpVoidCompound')
P.Shape = avoid
P.purgeTouched()
@@ -841,13 +847,15 @@ class ProcessSelectedFaces:
ofstVal = self._calculateOffsetValue(isHole, isVoid=True)
avdOfstShp = extractFaceOffset(avoid, ofstVal, self.wpc)
if avdOfstShp is False:
- FreeCAD.Console.PrintError('Failed to create collective offset avoid face.\n')
+ msg = translate('PathSurfaceSupport',
+ 'Failed to create collective offset avoid face.')
+ FreeCAD.Console.PrintError(msg + '\n')
cont = False
if cont:
avdShp = avdOfstShp
- if self.obj.AvoidLastX_InternalFeatures is False and len(intFEAT) > 0:
+ if not self.obj.AvoidLastX_InternalFeatures and len(intFEAT) > 0:
if len(intFEAT) > 1:
ifc = Part.makeCompound(intFEAT)
else:
@@ -855,7 +863,9 @@ class ProcessSelectedFaces:
ofstVal = self._calculateOffsetValue(isHole=True)
ifOfstShp = extractFaceOffset(ifc, ofstVal, self.wpc)
if ifOfstShp is False:
- FreeCAD.Console.PrintError('Failed to create collective offset avoid internal features.\n')
+ msg = translate('PathSurfaceSupport',
+ 'Failed to create collective offset avoid internal features.') + '\n'
+ FreeCAD.Console.PrintError(msg)
else:
avdShp = avdOfstShp.cut(ifOfstShp)
@@ -891,10 +901,10 @@ class ProcessSelectedFaces:
if csFaceShape is False:
csFaceShape = getSliceFromEnvelope(baseEnv)
if csFaceShape is False:
- PathLog.error('Failed to slice baseEnv shape.')
+ PathLog.debug('Failed to slice baseEnv shape.')
cont = False
- if cont is True and self.profileEdges != 'None':
+ if cont and self.profileEdges != 'None':
PathLog.debug(' -Attempting profile geometry for model base.')
ofstVal = self._calculateOffsetValue(isHole)
psOfst = extractFaceOffset(csFaceShape, ofstVal, self.wpc)
@@ -903,14 +913,13 @@ class ProcessSelectedFaces:
return (True, psOfst)
prflShp = psOfst
else:
- # FreeCAD.Console.PrintError(' -Failed to create profile geometry.\n')
cont = False
if cont:
ofstVal = self._calculateOffsetValue(isHole)
faceOffsetShape = extractFaceOffset(csFaceShape, ofstVal, self.wpc)
if faceOffsetShape is False:
- PathLog.error('extractFaceOffset() failed for entire base.')
+ PathLog.debug('extractFaceOffset() failed for entire base.')
else:
faceOffsetShape.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - faceOffsetShape.BoundBox.ZMin))
return (faceOffsetShape, prflShp)
@@ -1024,7 +1033,6 @@ def getProjectedFace(tempGroup, wire):
else:
pWire = Part.Wire(prj.Shape.Edges)
if pWire.isClosed() is False:
- # PathLog.debug(' -pWire.isClosed() is False')
return False
slc = Part.Face(pWire)
slc.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - slc.BoundBox.ZMin))
@@ -1069,7 +1077,7 @@ def getShapeEnvelope(shape):
try:
env = PathUtils.getEnvelope(partshape=shape, depthparams=dep_par) # Produces .Shape
except Exception as ee:
- FreeCAD.Console.PrintError('try: PathUtils.getEnvelope() failed.\n' + str(ee) + '\n')
+ FreeCAD.Console.PrintError('PathUtils.getEnvelope() failed.\n' + str(ee) + '\n')
return False
else:
return env
@@ -1196,7 +1204,9 @@ def _makeSafeSTL(self, JOB, obj, mdlIdx, faceShapes, voidShapes, ocl):
adjStckWst = stckWst
fuseShapes.append(adjStckWst)
else:
- PathLog.warning('Path transitions might not avoid the model. Verify paths.')
+ msg = translate('PathSurfaceSupport',
+ 'Path transitions might not avoid the model. Verify paths.')
+ FreeCAD.Console.PrintWarning(msg + '\n')
else:
# If boundbox is Job.Stock, add hidden pad under stock as base plate
toolDiam = self.cutter.getDiameter()
@@ -1319,7 +1329,6 @@ def pathGeomToLinesPointSet(obj, compGeoShp, cutClimb, toolDiam, closedGap, gaps
closedGap = True
True if closedGap else False # used closedGap for LGTM
else:
- # PathLog.debug('---- Gap: {} mm'.format(gap))
gap = round(gap, 6)
if gap < gaps[0]:
gaps.insert(0, gap)
@@ -1348,9 +1357,9 @@ def pathGeomToLinesPointSet(obj, compGeoShp, cutClimb, toolDiam, closedGap, gaps
isEven = lnCnt % 2
if isEven == 0:
- PathLog.debug('Line count is ODD.')
+ PathLog.debug('Line count is ODD: {}.'.format(lnCnt))
else:
- PathLog.debug('Line count is even.')
+ PathLog.debug('Line count is even: {}.'.format(lnCnt))
return LINES
@@ -1432,9 +1441,9 @@ def pathGeomToZigzagPointSet(obj, compGeoShp, cutClimb, toolDiam, closedGap, gap
# Fix directional issue with LAST line when line count is even
isEven = lnCnt % 2
if isEven == 0: # Changed to != with 90 degree CutPatternAngle
- PathLog.debug('Line count is even.')
+ PathLog.debug('Line count is even: {}.'.format(lnCnt))
else:
- PathLog.debug('Line count is ODD.')
+ PathLog.debug('Line count is ODD: {}.'.format(lnCnt))
dirFlg = -1 * dirFlg
if not obj.CutPatternReversed:
if cutClimb:
@@ -1652,7 +1661,6 @@ def pathGeomToCircularPointSet(obj, compGeoShp, cutClimb, toolDiam, closedGap, g
arc = (vA, arc[1], vC)
closedGap = True
else:
- # PathLog.debug('---- Gap: {} mm'.format(gap))
gap = round(gap, 6)
if gap < gaps[0]:
gaps.insert(0, gap)
@@ -1838,7 +1846,9 @@ class FindUnifiedRegions:
tfBB_Area = tfBB.XLength * tfBB.YLength
# self._showShape(topFace, 'topFaceAlt_2_{}'.format(fNum))
if tfBB_Area < (fBB_Area * 0.9):
- FreeCAD.Console.PrintError('Faild to extract processing region for Face{}.\n'.format(fNum))
+ msg = translate('PathSurfaceSupport',
+ 'Faild to extract processing region for Face')
+ FreeCAD.Console.PrintError(msg + '{}.\n'.format(fNum))
cont = False
if cont:
@@ -2104,7 +2114,7 @@ class FindUnifiedRegions:
remList.append(s)
break
else:
- FreeCAD.Console.PrintWarning(' - No common area.\n')
+ PathLog.debug(' - No common area.\n')
remList.sort(reverse=True)
for ri in remList:
@@ -2214,29 +2224,41 @@ class FindUnifiedRegions:
of tuples (faceShape, faceIndex) received at instantiation of the class object.'''
self.INTERNALS = list()
if len(self.FACES) == 0:
- FreeCAD.Console.PrintError('No (faceShp, faceIdx) tuples received at instantiation of class.')
+ msg = translate('PathSurfaceSupport',
+ 'No FACE data tuples received at instantiation of class.')
+ FreeCAD.Console.PrintError(msg + '\n')
return []
self._extractTopFaces()
lenFaces = len(self.topFaces)
+ PathLog.debug('getUnifiedRegions() lenFaces: {}.'.format(lenFaces))
if lenFaces == 0:
return []
# if single topFace, return it
if lenFaces == 1:
topFace = self.topFaces[0][0]
- # self._showShape(topFace, 'TopFace')
+ self._showShape(topFace, 'TopFace')
# prepare inner wires as faces for internal features
lenWrs = len(topFace.Wires)
if lenWrs > 1:
for w in range(1, lenWrs):
- self.INTERNALS.append(Part.Face(topFace.Wires[w]))
- # prepare outer wire as face for return value in list
- if hasattr(topFace, 'OuterWire'):
- ow = topFace.OuterWire
- else:
- ow = topFace.Wires[0]
- face = Part.Face(ow)
+ # 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]
# process multiple top faces, unifying if possible
@@ -2254,11 +2276,8 @@ class FindUnifiedRegions:
return [topFace for (topFace, fcIdx) in self.topFaces]
else:
# Delete shared edges from edgeData list
- # FreeCAD.Console.PrintWarning('self.sharedEdgeIdxs: {}\n'.format(self.sharedEdgeIdxs))
self.sharedEdgeIdxs.sort(reverse=True)
for se in self.sharedEdgeIdxs:
- # seShp = self.edgeData[se][2]
- # self._showShape(seShp, 'SharedEdge')
self.edgeData.pop(se)
self._extractWiresFromEdges()
@@ -2274,6 +2293,8 @@ class FindUnifiedRegions:
after calling getUnifiedRegions().'''
if self.INTERNALS:
return self.INTERNALS
- FreeCAD.Console.PrintError('getUnifiedRegions() must be called before getInternalFeatures().\n')
+ msg = translate('PathSurfaceSupport',
+ 'getUnifiedRegions() must be called before getInternalFeatures().')
+ FreeCAD.Console.PrintError(msg + '\n')
return False
# Eclass
diff --git a/src/Mod/Path/PathScripts/PathWaterline.py b/src/Mod/Path/PathScripts/PathWaterline.py
index 0693a27edd..98465b0171 100644
--- a/src/Mod/Path/PathScripts/PathWaterline.py
+++ b/src/Mod/Path/PathScripts/PathWaterline.py
@@ -191,9 +191,9 @@ class ObjectWaterline(PathOp.ObjectOp):
'Algorithm': ['OCL Dropcutter', 'Experimental'],
'BoundBox': ['BaseBoundBox', 'Stock'],
'PatternCenterAt': ['CenterOfMass', 'CenterOfBoundBox', 'XminYmin', 'Custom'],
- 'ClearLastLayer': ['Off', 'Line', 'Circular', 'CircularZigZag', 'Offset', 'Spiral', 'ZigZag'],
+ 'ClearLastLayer': ['Off', 'Circular', 'CircularZigZag', 'Line', 'Offset', 'Spiral', 'ZigZag'],
'CutMode': ['Conventional', 'Climb'],
- 'CutPattern': ['None', 'Line', 'Circular', 'CircularZigZag', 'Offset', 'Spiral', 'ZigZag'], # Additional goals ['Offset', 'Spiral', 'ZigZagOffset', 'Grid', 'Triangle']
+ 'CutPattern': ['None', 'Circular', 'CircularZigZag', 'Line', 'Offset', 'Spiral', 'ZigZag'], # Additional goals ['Offset', 'Spiral', 'ZigZagOffset', 'Grid', 'Triangle']
'HandleMultipleFeatures': ['Collectively', 'Individually'],
'LayerMode': ['Single-pass', 'Multi-pass'],
}
@@ -488,7 +488,6 @@ class ObjectWaterline(PathOp.ObjectOp):
self.opApplyPropertyLimits(obj)
# Create temporary group for temporary objects, removing existing
- # if self.showDebugObjects is True:
tempGroupName = 'tempPathWaterlineGroup'
if FCAD.getObject(tempGroupName):
for to in FCAD.getObject(tempGroupName).Group:
@@ -576,7 +575,6 @@ class ObjectWaterline(PathOp.ObjectOp):
self.modelSTLs = PSF.modelSTLs
self.profileShapes = PSF.profileShapes
-
for m in range(0, len(JOB.Model.Group)):
# Create OCL.stl model objects
if obj.Algorithm == 'OCL Dropcutter':
@@ -662,7 +660,8 @@ class ObjectWaterline(PathOp.ObjectOp):
del self.midDep
execTime = time.time() - startTime
- PathLog.info('Operation time: {} sec.'.format(execTime))
+ msg = translate('PathWaterline', 'operation time is')
+ PathLog.info('Waterline ' + msg + ' {} sec.'.format(execTime))
return True
@@ -1238,11 +1237,7 @@ class ObjectWaterline(PathOp.ObjectOp):
bbFace = PathSurfaceSupport.getCrossSection(baseEnv) # returned at Z=0.0
trimFace = borderFace.cut(bbFace)
- if self.showDebugObjects is True:
- TF = FreeCAD.ActiveDocument.addObject('Part::Feature', 'trimFace')
- TF.Shape = trimFace
- TF.purgeTouched()
- self.tempGroup.addObject(TF)
+ self.showDebugObject(trimFace, 'TrimFace')
# Cycle through layer depths
CUTAREAS = self._getCutAreas(base.Shape, depthparams, bbFace, trimFace, borderFace)
@@ -1267,11 +1262,7 @@ class ObjectWaterline(PathOp.ObjectOp):
if area.Area > 0.0:
cont = True
caWireCnt = len(area.Wires) - 1 # first wire is boundFace wire
- if self.showDebugObjects:
- CA = FreeCAD.ActiveDocument.addObject('Part::Feature', 'cutArea_{}'.format(caCnt))
- CA.Shape = area
- CA.purgeTouched()
- self.tempGroup.addObject(CA)
+ self.showDebugObject(area, 'CutArea_{}'.format(caCnt))
else:
data = FreeCAD.Units.Quantity(csHght, FreeCAD.Units.Length).UserString
PathLog.debug('Cut area at {} is zero.'.format(data))
@@ -1281,11 +1272,7 @@ class ObjectWaterline(PathOp.ObjectOp):
area.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - area.BoundBox.ZMin))
activeArea = area.cut(trimFace)
activeAreaWireCnt = len(activeArea.Wires) # first wire is boundFace wire
- if self.showDebugObjects:
- CA = FreeCAD.ActiveDocument.addObject('Part::Feature', 'activeArea_{}'.format(caCnt))
- CA.Shape = activeArea
- CA.purgeTouched()
- self.tempGroup.addObject(CA)
+ self.showDebugObject(activeArea, 'ActiveArea_{}'.format(caCnt))
ofstArea = PathSurfaceSupport.extractFaceOffset(activeArea, ofst, self.wpc, makeComp=False)
if not ofstArea:
data = FreeCAD.Units.Quantity(csHght, FreeCAD.Units.Length).UserString
@@ -1294,20 +1281,21 @@ class ObjectWaterline(PathOp.ObjectOp):
if cont:
# Identify solid areas in the offset data
- ofstSolidFacesList = self._getSolidAreasFromPlanarFaces(ofstArea)
- if ofstSolidFacesList:
- clearArea = Part.makeCompound(ofstSolidFacesList)
- if self.showDebugObjects is True:
- CA = FreeCAD.ActiveDocument.addObject('Part::Feature', 'clearArea_{}'.format(caCnt))
- CA.Shape = clearArea
- CA.purgeTouched()
- self.tempGroup.addObject(CA)
+ if obj.CutPattern == 'Offset' or obj.CutPattern == 'None':
+ ofstSolidFacesList = self._getSolidAreasFromPlanarFaces(ofstArea)
+ if ofstSolidFacesList:
+ clearArea = Part.makeCompound(ofstSolidFacesList)
+ self.showDebugObject(clearArea, 'ClearArea_{}'.format(caCnt))
+ else:
+ cont = False
+ data = FreeCAD.Units.Quantity(csHght, FreeCAD.Units.Length).UserString
+ PathLog.error('Could not determine solid faces at {}.'.format(data))
else:
- cont = False
- data = FreeCAD.Units.Quantity(csHght, FreeCAD.Units.Length).UserString
- PathLog.error('Could not determine solid faces at {}.'.format(data))
+ clearArea = activeArea
if cont:
+ data = FreeCAD.Units.Quantity(csHght, FreeCAD.Units.Length).UserString
+ PathLog.debug('... Clearning area at {}.'.format(data))
# Make waterline path for current CUTAREA depth (csHght)
commands.extend(self._wiresToWaterlinePath(obj, clearArea, csHght))
clearArea.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - clearArea.BoundBox.ZMin))
@@ -1325,7 +1313,8 @@ class ObjectWaterline(PathOp.ObjectOp):
commands.extend(self._makeCutPatternLayerPaths(JOB, obj, clearArea, csHght, cutPattern))
# Efor
- if clearLastLayer:
+ if clearLastLayer and obj.ClearLastLayer != 'Off':
+ PathLog.debug('... Clearning last layer')
(clrLyr, cLL) = self._clearLayer(obj, 1, 1, False)
lastClearArea.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - lastClearArea.BoundBox.ZMin))
if clrLyr == 'Offset':
@@ -1333,7 +1322,6 @@ class ObjectWaterline(PathOp.ObjectOp):
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
def _getCutAreas(self, shape, depthparams, bbFace, trimFace, borderFace):
@@ -1363,13 +1351,7 @@ class ObjectWaterline(PathOp.ObjectOp):
if useFaces:
compAdjFaces = Part.makeCompound(useFaces)
-
- if self.showDebugObjects is True:
- CA = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpSolids_{}'.format(dp + 1))
- CA.Shape = compAdjFaces
- CA.purgeTouched()
- self.tempGroup.addObject(CA)
-
+ self.showDebugObject(compAdjFaces, 'Solids_{}'.format(dp + 1))
if isFirst:
allPrevComp = compAdjFaces
cutArea = borderFace.cut(compAdjFaces)
@@ -1395,11 +1377,7 @@ class ObjectWaterline(PathOp.ObjectOp):
# Translate path geometry to layer height
ofstPlnrShp.translate(FreeCAD.Vector(0.0, 0.0, csHght - ofstPlnrShp.BoundBox.ZMin))
- if self.showDebugObjects is True:
- OA = FreeCAD.ActiveDocument.addObject('Part::Feature', 'waterlinePathArea_{}'.format(round(csHght, 2)))
- OA.Shape = ofstPlnrShp
- OA.purgeTouched()
- self.tempGroup.addObject(OA)
+ self.showDebugObject(ofstPlnrShp, 'WaterlinePathArea_{}'.format(round(csHght, 2)))
commands.append(Path.Command('N (Cut Area {}.)'.format(round(csHght, 2))))
start = 1
@@ -1442,11 +1420,7 @@ class ObjectWaterline(PathOp.ObjectOp):
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)
+ self.showDebugObject(pathGeom, 'PathGeom_{}'.format(round(csHght, 2)))
if cutPattern == 'Line':
pntSet = PathSurfaceSupport.pathGeomToLinesPointSet(obj, pathGeom, self.CutClimb, self.toolDiam, self.closedGap, self.gaps)
@@ -1481,6 +1455,8 @@ class ObjectWaterline(PathOp.ObjectOp):
if cnt == 0:
ofst = 0.0 - self.cutOut
cnt += 1
+ PathLog.debug(' -Offset path count: {} at height: {}'.format(cnt, round(csHght, 2)))
+
return cmds
def _clearGeomToPaths(self, JOB, obj, safePDC, stpOVRS, cutPattern):
@@ -1855,6 +1831,13 @@ class ObjectWaterline(PathOp.ObjectOp):
PathLog.warning("Defaulting cutter to standard end mill.")
return ocl.CylCutter(diam_1, (CEH + lenOfst))
+ def showDebugObject(self, objShape, objName):
+ if self.showDebugObjects:
+ do = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmp_' + objName)
+ do.Shape = objShape
+ do.purgeTouched()
+ self.tempGroup.addObject(do)
+
def SetupProperties():
''' SetupProperties() ... Return list of properties required for operation.'''