From 3e83b1d8e54aad3476f4b4f843d8cd05a888758c Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Tue, 2 Jun 2020 21:21:16 -0500 Subject: [PATCH 1/9] Path: Add missing cut patterns and organize the enumeration lists --- .../Resources/panels/PageOpWaterlineEdit.ui | 30 ++++++++++++------- src/Mod/Path/PathScripts/PathSurface.py | 2 +- src/Mod/Path/PathScripts/PathWaterline.py | 4 +-- 3 files changed, 23 insertions(+), 13 deletions(-) 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..824f8b0254 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -206,7 +206,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'], diff --git a/src/Mod/Path/PathScripts/PathWaterline.py b/src/Mod/Path/PathScripts/PathWaterline.py index 0693a27edd..804125de3f 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'], } From 7fd047a9beebe8400ff2bebecc5b314254b45487 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 3 Jun 2020 01:29:08 -0500 Subject: [PATCH 2/9] Path: Make missing connections between UI panel and operation --- .../Gui/Resources/panels/PageOpSurfaceEdit.ui | 2 +- src/Mod/Path/PathScripts/PathSurfaceGui.py | 20 +++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) 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/PathScripts/PathSurfaceGui.py b/src/Mod/Path/PathScripts/PathSurfaceGui.py index a26b290827..ab09c33d6f 100644 --- a/src/Mod/Path/PathScripts/PathSurfaceGui.py +++ b/src/Mod/Path/PathScripts/PathSurfaceGui.py @@ -52,24 +52,24 @@ 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()) + if obj.ProfileEdges != str(self.form.profileEdges.currentText()): + obj.ProfileEdges = str(self.form.profileEdges.currentText()) + + 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 +77,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(): @@ -96,6 +100,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.selectInComboBox(obj.ScanType, self.form.scanType) self.selectInComboBox(obj.LayerMode, self.form.layerMode) 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 +135,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) From d94ae6e813a94f27580ed4a4c90c416ddc2f9630 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 3 Jun 2020 02:12:24 -0500 Subject: [PATCH 3/9] Path: Simplify creation of debug objects Path: Improve debug messages --- src/Mod/Path/PathScripts/PathSurface.py | 19 ++++--- .../Path/PathScripts/PathSurfaceSupport.py | 8 +-- src/Mod/Path/PathScripts/PathWaterline.py | 52 +++++-------------- 3 files changed, 27 insertions(+), 52 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 824f8b0254..315119788b 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -753,11 +753,7 @@ class ObjectSurface(PathOp.ObjectOp): if prflShp is False: PathLog.error('No profile shape is False.') 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: @@ -767,11 +763,7 @@ class ObjectSurface(PathOp.ObjectOp): 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: @@ -2108,6 +2100,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/PathSurfaceSupport.py b/src/Mod/Path/PathScripts/PathSurfaceSupport.py index fcaface258..3ee6e70cec 100644 --- a/src/Mod/Path/PathScripts/PathSurfaceSupport.py +++ b/src/Mod/Path/PathScripts/PathSurfaceSupport.py @@ -1348,9 +1348,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 +1432,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: diff --git a/src/Mod/Path/PathScripts/PathWaterline.py b/src/Mod/Path/PathScripts/PathWaterline.py index 804125de3f..8492aa8491 100644 --- a/src/Mod/Path/PathScripts/PathWaterline.py +++ b/src/Mod/Path/PathScripts/PathWaterline.py @@ -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: @@ -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 @@ -1297,11 +1284,7 @@ class ObjectWaterline(PathOp.ObjectOp): 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) + self.showDebugObject(clearArea, 'ClearArea_{}'.format(caCnt)) else: cont = False data = FreeCAD.Units.Quantity(csHght, FreeCAD.Units.Length).UserString @@ -1363,13 +1346,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 +1372,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 +1415,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) @@ -1855,6 +1824,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.''' From 7ba218f417443f9ea8df00e4329a98ad4934bc76 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 3 Jun 2020 01:14:26 -0500 Subject: [PATCH 4/9] Path: Fix paths through model All cut patterns except Offset were cutting through model. --- src/Mod/Path/PathScripts/PathSurfaceSupport.py | 9 ++++----- src/Mod/Path/PathScripts/PathWaterline.py | 17 ++++++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSurfaceSupport.py b/src/Mod/Path/PathScripts/PathSurfaceSupport.py index 3ee6e70cec..5599eb4ac3 100644 --- a/src/Mod/Path/PathScripts/PathSurfaceSupport.py +++ b/src/Mod/Path/PathScripts/PathSurfaceSupport.py @@ -91,12 +91,11 @@ 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 - else: + if shape.BoundBox.ZLength > 1.0e-8: FreeCAD.Console.PrintWarning('Shape appears to not be horizontal planar. ZMax is {}.\n'.format(shape.BoundBox.ZMax)) - - self._prepareConstants() + else: + self.shape = shape + self._prepareConstants() def _prepareConstants(self): # Apply drop cutter extra offset and set the max and min XY area of the operation diff --git a/src/Mod/Path/PathScripts/PathWaterline.py b/src/Mod/Path/PathScripts/PathWaterline.py index 8492aa8491..0344350b59 100644 --- a/src/Mod/Path/PathScripts/PathWaterline.py +++ b/src/Mod/Path/PathScripts/PathWaterline.py @@ -1281,14 +1281,17 @@ class ObjectWaterline(PathOp.ObjectOp): if cont: # Identify solid areas in the offset data - ofstSolidFacesList = self._getSolidAreasFromPlanarFaces(ofstArea) - if ofstSolidFacesList: - clearArea = Part.makeCompound(ofstSolidFacesList) - self.showDebugObject(clearArea, 'ClearArea_{}'.format(caCnt)) + 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: # Make waterline path for current CUTAREA depth (csHght) From 2fe53c58bffb052fbcc8126000c14775d16e2602 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 3 Jun 2020 12:47:10 -0500 Subject: [PATCH 5/9] Path: Fix failure to create model STL when selecting faces --- src/Mod/Path/PathScripts/PathSurfaceSupport.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSurfaceSupport.py b/src/Mod/Path/PathScripts/PathSurfaceSupport.py index 5599eb4ac3..ae891d3503 100644 --- a/src/Mod/Path/PathScripts/PathSurfaceSupport.py +++ b/src/Mod/Path/PathScripts/PathSurfaceSupport.py @@ -425,7 +425,7 @@ 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()') + # PathLog.debug('_getFaceOffset()') areaParams = {} areaParams['Offset'] = offset @@ -559,7 +559,7 @@ class ProcessSelectedFaces: # 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] From 4b9b1e3c761d787acc61f26350c94a85783609f6 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 3 Jun 2020 18:42:19 -0500 Subject: [PATCH 6/9] Path: Clean up messaging and add `translation()` usage --- src/Mod/Path/PathScripts/PathSurface.py | 31 ++++--- .../Path/PathScripts/PathSurfaceSupport.py | 85 ++++++++++--------- 2 files changed, 65 insertions(+), 51 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 315119788b..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 @@ -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,13 +753,15 @@ 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() 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)] @@ -771,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: @@ -795,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 @@ -1165,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: @@ -1187,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 diff --git a/src/Mod/Path/PathScripts/PathSurfaceSupport.py b/src/Mod/Path/PathScripts/PathSurfaceSupport.py index ae891d3503..16865b2e38 100644 --- a/src/Mod/Path/PathScripts/PathSurfaceSupport.py +++ b/src/Mod/Path/PathScripts/PathSurfaceSupport.py @@ -92,7 +92,10 @@ 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 > 1.0e-8: - FreeCAD.Console.PrintWarning('Shape appears to not be horizontal planar. ZMax is {}.\n'.format(shape.BoundBox.ZMax)) + msg = translate('PathSurfaceSupport', + 'Shape appears to not be horizontal planar.') + msg += ' ZMax == {} mm.\n'.format(shape.BoundBox.ZMax) + FreeCAD.Console.PrintWarning(msg) else: self.shape = shape self._prepareConstants() @@ -117,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: @@ -155,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 + '()' @@ -407,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 @@ -425,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 @@ -438,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 @@ -474,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' @@ -557,7 +561,6 @@ 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 self.modelSTLs[m] and not fShapes[m]: PathLog.debug(' -Pre-processing {} as a whole.'.format(GRP[m].Label)) @@ -568,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: @@ -677,7 +682,8 @@ 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: @@ -693,7 +699,6 @@ class ProcessSelectedFaces: mFS = True cont = False else: - # FreeCAD.Console.PrintError(' -Failed to create profile geometry for selected faces.\n') cont = False if cont: @@ -705,8 +710,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: @@ -763,7 +770,6 @@ class ProcessSelectedFaces: mFS.append(True) cont = False else: - # PathLog.error(' -Failed to create profile geometry for Face{}.'.format(fNum)) cont = False if cont: @@ -824,7 +830,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() @@ -832,7 +837,6 @@ class ProcessSelectedFaces: if cont: if self.showDebugObjects: - PathLog.debug('*** tmpVoidCompound') P = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpVoidCompound') P.Shape = avoid P.purgeTouched() @@ -840,13 +844,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: @@ -854,7 +860,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) @@ -890,10 +898,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) @@ -902,14 +910,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) @@ -1023,7 +1030,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)) @@ -1068,7 +1074,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 @@ -1195,7 +1201,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() @@ -1318,7 +1326,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) @@ -1651,7 +1658,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) @@ -1837,7 +1843,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: @@ -2103,7 +2111,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: @@ -2213,7 +2221,9 @@ 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() @@ -2253,11 +2263,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() @@ -2273,6 +2280,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 From c7222a51a79a4c5450032ee17bf39b5aa090a956 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 3 Jun 2020 20:13:39 -0500 Subject: [PATCH 7/9] Path: Fix duplication of gcode for Single-pass layer mode.. --- src/Mod/Path/PathScripts/PathWaterline.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathWaterline.py b/src/Mod/Path/PathScripts/PathWaterline.py index 0344350b59..98465b0171 100644 --- a/src/Mod/Path/PathScripts/PathWaterline.py +++ b/src/Mod/Path/PathScripts/PathWaterline.py @@ -575,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': @@ -661,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 @@ -1294,6 +1294,8 @@ class ObjectWaterline(PathOp.ObjectOp): 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)) @@ -1311,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': @@ -1319,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): @@ -1453,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): From c8f654e7a191a503fd0a5bb8ee90788d15aae3d7 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 3 Jun 2020 21:24:18 -0500 Subject: [PATCH 8/9] Path: Fix error with multi-language usage of QComboBox inputs The methods here might need to be applied throughout PathWB to allow language translations of GUI combobox inputs. --- src/Mod/Path/PathScripts/PathSurfaceGui.py | 42 ++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSurfaceGui.py b/src/Mod/Path/PathScripts/PathSurfaceGui.py index ab09c33d6f..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''' @@ -61,11 +63,25 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): 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 - if obj.ProfileEdges != str(self.form.profileEdges.currentText()): - obj.ProfileEdges = str(self.form.profileEdges.currentText()) + 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() @@ -99,8 +115,22 @@ 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) - self.selectInComboBox(obj.ProfileEdges, self.form.profileEdges) + + """ + 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) From 1d18ad5e0a5f99645b39ccb482561bbce52389fd Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Thu, 4 Jun 2020 00:03:31 -0500 Subject: [PATCH 9/9] Path: Fix for single selected non-planar face --- .../Path/PathScripts/PathSurfaceSupport.py | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSurfaceSupport.py b/src/Mod/Path/PathScripts/PathSurfaceSupport.py index 16865b2e38..3e5856a1bc 100644 --- a/src/Mod/Path/PathScripts/PathSurfaceSupport.py +++ b/src/Mod/Path/PathScripts/PathSurfaceSupport.py @@ -650,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) @@ -690,7 +692,8 @@ class ProcessSelectedFaces: 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: @@ -2228,24 +2231,34 @@ class FindUnifiedRegions: 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