From 0d5a7286988fe337a3481234be2e788ca571a1b4 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Sun, 22 Nov 2020 15:37:31 -0600 Subject: [PATCH 1/5] Path: Standardize debugging mode control code --- src/Mod/Path/PathScripts/PathSlot.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index 2fdfcd5079..2d30067a8a 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -44,19 +44,16 @@ Part = LazyLoader('Part', globals(), 'Part') if FreeCAD.GuiUp: import FreeCADGui -DEBUG = False -if DEBUG: - PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) - PathLog.trackModule(PathLog.thisModule()) -else: - PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) - # Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) +PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) + + class ObjectSlot(PathOp.ObjectOp): '''Proxy object for Slot operation.''' From 1ee1c9a5df29b55c83d0466199ddbc415fe4ce04 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Sun, 22 Nov 2020 15:38:50 -0600 Subject: [PATCH 2/5] Path: Simplify and fix debugging code --- src/Mod/Path/PathScripts/PathSlot.py | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index 2d30067a8a..f66abed2d4 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -352,10 +352,9 @@ class ObjectSlot(PathOp.ObjectOp): self.arcRadius = 0.0 self.newRadius = 0.0 self.isDebug = False if PathLog.getLevel(PathLog.thisModule()) != 4 else True - self.showDebugObjects = obj.ShowTempObjects + self.showDebugObjects = False self.stockZMin = self.job.Stock.Shape.BoundBox.ZMin CMDS = list() - FCAD = FreeCAD.ActiveDocument try: dotIdx = __name__.index('.') + 1 @@ -363,18 +362,17 @@ class ObjectSlot(PathOp.ObjectOp): dotIdx = 0 self.module = __name__[dotIdx:] - if not self.isDebug: - self.showDebugObjects = False + # Setup debugging group for temp objects, when in DEBUG mode + if self.isDebug: + self.showDebugObjects = obj.ShowTempObjects if self.showDebugObjects: + FCAD = FreeCAD.ActiveDocument for grpNm in ['tmpDebugGrp', 'tmpDebugGrp001']: - if hasattr(FreeCAD.ActiveDocument, grpNm): - for go in FreeCAD.ActiveDocument.getObject(grpNm).Group: - FreeCAD.ActiveDocument.removeObject(go.Name) - FreeCAD.ActiveDocument.removeObject(grpNm) - self.tmpGrp = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroup', 'tmpDebugGrp') - tmpGrpNm = self.tmpGrp.Name - - # self.updateEnumerations(obj) + if hasattr(FCAD, grpNm): + for go in FCAD.getObject(grpNm).Group: + FCAD.removeObject(go.Name) + FCAD.removeObject(grpNm) + self.tmpGrp = FCAD.addObject('App::DocumentObjectGroup', 'tmpDebugGrp') # Begin GCode for operation with basic information # ... and move cutter to clearance height and startpoint @@ -412,8 +410,7 @@ class ObjectSlot(PathOp.ObjectOp): # Hide the temporary objects if self.showDebugObjects: if FreeCAD.GuiUp: - import FreeCADGui - FreeCADGui.ActiveDocument.getObject(tmpGrpNm).Visibility = False + FreeCADGui.ActiveDocument.getObject(self.tmpGrp.Name).Visibility = False self.tmpGrp.purgeTouched() return True @@ -592,6 +589,9 @@ class ObjectSlot(PathOp.ObjectOp): # Raise to SafeHeight when finished CMDS.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) + if self.isDebug: + PathLog.debug('G-code arc command is: {}'.format(PATHS[path_index][2])) + return CMDS def _finishLine(self, obj, pnts, featureCnt): @@ -1679,7 +1679,7 @@ class ObjectSlot(PathOp.ObjectOp): do = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmp_' + objName) do.Shape = objShape do.purgeTouched() - self.tempGroup.addObject(do) + self.tmpGrp.addObject(do) # Eclass From da3f0522704a03cbe36edbcce6dd6223183840a1 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Sun, 22 Nov 2020 18:26:06 -0600 Subject: [PATCH 3/5] Path: Fix g-code arc direction Fixed incorrect point orders assigned to g2 and g3 commands. Add application of `ReverseDirection` property to `ZigZag` cut pattern. Make default arc direction clockwise(g2) for all cut patterns and layer modes. Make tool diameter access backward compatible. --- src/Mod/Path/PathScripts/PathSlot.py | 32 +++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index f66abed2d4..a0ce57afdb 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -549,7 +549,8 @@ class ObjectSlot(PathOp.ObjectOp): It accepts the operation object and two end points for the path. It returns the slot gcode for the operation.""" CMDS = list() - PATHS = [(p1, p2, 'G2'), (p2, p1, 'G3')] + PATHS = [(p2, p1, 'G2'), (p1, p2, 'G3')] + path_index = 0 def arcPass(PNTS, depth): cmds = list() @@ -566,25 +567,29 @@ class ObjectSlot(PathOp.ObjectOp): return cmds if obj.LayerMode == 'Single-pass': - PNTS = PATHS[0] if obj.ReverseDirection: - PNTS = PATHS[1] - CMDS.extend(arcPass(PNTS, obj.FinalDepth.Value)) + path_index = 1 + CMDS.extend(arcPass(PATHS[path_index], obj.FinalDepth.Value)) else: if obj.CutPattern == 'Line': - PNTS = PATHS[0] if obj.ReverseDirection: - PNTS = PATHS[1] + path_index = 1 for dep in self.depthParams: - CMDS.extend(arcPass(PNTS, dep)) + CMDS.extend(arcPass(PATHS[path_index], dep)) CMDS.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) elif obj.CutPattern == 'ZigZag': i = 0 for dep in self.depthParams: - if i % 2.0 == 0: # even - CMDS.extend(arcPass(PATHS[0], dep)) - else: # odd - CMDS.extend(arcPass(PATHS[1], dep)) + if obj.ReverseDirection: + if i % 2.0 == 0: # even + CMDS.extend(arcPass(PATHS[0], dep)) + else: # odd + CMDS.extend(arcPass(PATHS[1], dep)) + else: + if i % 2.0 == 0: # even + CMDS.extend(arcPass(PATHS[1], dep)) + else: # odd + CMDS.extend(arcPass(PATHS[0], dep)) i += 1 # Raise to SafeHeight when finished CMDS.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) @@ -1568,7 +1573,10 @@ class ObjectSlot(PathOp.ObjectOp): Make arch face between circles. Fuse and extrude it vertically. Check for collision with model.""" # Make path travel of tool as 3D solid. - rad = self.tool.Diameter / 2.0 + if hasattr(self.tool.Diameter, 'Value'): + rad = self.tool.Diameter.Value / 2.0 + else: + rad = self.tool.Diameter / 2.0 extFwd = obj.StartDepth.Value - obj.FinalDepth.Value extVect = FreeCAD.Vector(0.0, 0.0, extFwd) From 1468c7e8428e9bf81328e85d19118d51c0b16a09 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Sun, 22 Nov 2020 18:20:48 -0600 Subject: [PATCH 4/5] Path: Implement LazyLoader for Arcs module --- src/Mod/Path/PathScripts/PathSlot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index a0ce57afdb..d651522855 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -40,6 +40,7 @@ import math # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader Part = LazyLoader('Part', globals(), 'Part') +Arcs = LazyLoader('draftgeoutils.arcs', globals(), 'draftgeoutils.arcs') if FreeCAD.GuiUp: import FreeCADGui @@ -1619,8 +1620,7 @@ class ObjectSlot(PathOp.ObjectOp): # Make wire with inside and outside arcs, and lines on ends. # Convert wire to face, then extrude - import draftgeoutils.arcs as Arcs - # Arc 1 - inside + # verify offset does not force radius < 0 newRadius = arcRadius - rad # PathLog.debug('arcRadius, newRadius: {}, {}'.format(arcRadius, newRadius)) From 37cd09e7ae0990a522c6995539b942097db512e4 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Sun, 22 Nov 2020 18:21:43 -0600 Subject: [PATCH 5/5] Path: LGTM cleanup --- src/Mod/Path/PathScripts/PathSlot.py | 94 ++++++++++++---------------- 1 file changed, 41 insertions(+), 53 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index d651522855..4795b9b1d1 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -182,7 +182,8 @@ class ObjectSlot(PathOp.ObjectOp): ENUMS = self.opPropertyEnumerations() if hasattr(obj, 'Base'): if obj.Base: - (base, subsList) = obj.Base[0] + # (base, subsList) = obj.Base[0] + subsList = obj.Base[0][1] subCnt = len(subsList) if subCnt == 1: # Adjust available enumerations @@ -228,7 +229,8 @@ class ObjectSlot(PathOp.ObjectOp): C = 0 if hasattr(obj, 'Base'): if obj.Base: - (base, subsList) = obj.Base[0] + # (base, subsList) = obj.Base[0] + subsList = obj.Base[0][1] subCnt = len(subsList) if subCnt == 1: A = 0 @@ -282,7 +284,7 @@ class ObjectSlot(PathOp.ObjectOp): if isinstance(val, int) or isinstance(val, float): setVal = True if setVal: - propVal = getattr(prop, 'Value') + # propVal = getattr(prop, 'Value') setattr(prop, 'Value', val) else: setattr(obj, n, val) @@ -422,9 +424,6 @@ class ObjectSlot(PathOp.ObjectOp): pnts = False featureCnt = 0 - def eLen(E): - return E.Length - if not hasattr(obj, 'Base'): msg = translate('PathSlot', 'No Base Geometry object in the operation.') @@ -443,30 +442,27 @@ class ObjectSlot(PathOp.ObjectOp): FreeCAD.Console.PrintError(msg + '\n') return False - if pnts: - (p1, p2) = pnts + baseGeom = obj.Base[0] + base, subsList = baseGeom + self.base = base + lenSL = len(subsList) + featureCnt = lenSL + if lenSL == 1: + PathLog.debug('Reference 1: {}'.format(obj.Reference1)) + sub1 = subsList[0] + shape_1 = getattr(base.Shape, sub1) + self.shape1 = shape_1 + pnts = self._processSingle(obj, shape_1, sub1) else: - baseGeom = obj.Base[0] - base, subsList = baseGeom - self.base = base - lenSL = len(subsList) - featureCnt = lenSL - if lenSL == 1: - PathLog.debug('Reference 1: {}'.format(obj.Reference1)) - sub1 = subsList[0] - shape_1 = getattr(base.Shape, sub1) - self.shape1 = shape_1 - pnts = self._processSingle(obj, shape_1, sub1) - else: - PathLog.debug('Reference 1: {}'.format(obj.Reference1)) - PathLog.debug('Reference 2: {}'.format(obj.Reference2)) - sub1 = subsList[0] - sub2 = subsList[1] - shape_1 = getattr(base.Shape, sub1) - shape_2 = getattr(base.Shape, sub2) - self.shape1 = shape_1 - self.shape2 = shape_2 - pnts = self._processDouble(obj, shape_1, sub1, shape_2, sub2) + PathLog.debug('Reference 1: {}'.format(obj.Reference1)) + PathLog.debug('Reference 2: {}'.format(obj.Reference2)) + sub1 = subsList[0] + sub2 = subsList[1] + shape_1 = getattr(base.Shape, sub1) + shape_2 = getattr(base.Shape, sub2) + self.shape1 = shape_1 + self.shape2 = shape_2 + pnts = self._processDouble(obj, shape_1, sub1, shape_2, sub2) if not pnts: return False @@ -697,7 +693,6 @@ class ObjectSlot(PathOp.ObjectOp): def _processSingle(self, obj, shape_1, sub1): """This is the control method for slots based on a single Base Geometry feature.""" - cmds = False make = False cat1 = sub1[:4] @@ -895,7 +890,7 @@ class ObjectSlot(PathOp.ObjectOp): return True def circleCentFrom3Points(P1, P2, P3): - # Source code for this function copied from: + # Source code for this function copied from (with modifications): # https://wiki.freecadweb.org/Macro_Draft_Circle_3_Points_3D P1P2 = (P2 - P1).Length P2P3 = (P3 - P2).Length @@ -903,21 +898,20 @@ class ObjectSlot(PathOp.ObjectOp): # Circle radius. l = ((P1 - P2).cross(P2 - P3)).Length - try: - r = P1P2 * P2P3 * P3P1 / 2 / l - except: + # r = P1P2 * P2P3 * P3P1 / 2 / l + if round(l, 8) == 0.0: PathLog.error("The three points are aligned.") return False - else: - # Sphere center. - a = P2P3**2 * (P1 - P2).dot(P1 - P3) / 2 / l**2 - b = P3P1**2 * (P2 - P1).dot(P2 - P3) / 2 / l**2 - c = P1P2**2 * (P3 - P1).dot(P3 - P2) / 2 / l**2 - P1.multiply(a) - P2.multiply(b) - P3.multiply(c) - PC = P1 + P2 + P3 - return PC + + # Sphere center. + a = P2P3**2 * (P1 - P2).dot(P1 - P3) / 2 / l**2 + b = P3P1**2 * (P2 - P1).dot(P2 - P3) / 2 / l**2 + c = P1P2**2 * (P3 - P1).dot(P3 - P2) / 2 / l**2 + P1.multiply(a) + P2.multiply(b) + P3.multiply(c) + PC = P1 + P2 + P3 + return PC # Process edge based on curve type if edge.Curve.TypeId in lineTypes: @@ -981,9 +975,7 @@ class ObjectSlot(PathOp.ObjectOp): PathLog.debug('_processDouble()') """This is the control method for slots based on a two Base Geometry features.""" - cmds = False - make = False - cat2 = sub2[:4] + p1 = None p2 = None dYdX1 = None @@ -1075,7 +1067,7 @@ class ObjectSlot(PathOp.ObjectOp): for V in shape_1.Vertexes: if V.Z < zmin: zmin = V.Z - vMin = V + # vMin = V elif V.Z == zmin: same.append(V) if len(same) > 1: @@ -1095,7 +1087,7 @@ class ObjectSlot(PathOp.ObjectOp): for V in shape_1.Vertexes: if V.Z > zmax: zmax = V.Z - vMax = V + # vMax = V elif V.Z == zmax: same.append(V) if len(same) > 1: @@ -1180,7 +1172,6 @@ class ObjectSlot(PathOp.ObjectOp): y = self.newRadius * math.sin(rads) a = FreeCAD.Vector(self.newRadius, 0.0, 0.0) b = FreeCAD.Vector(x, y, 0.0) - c = FreeCAD.Vector(0.0, 0.0, 0.0) return Part.makeLine(a, b) if begExt or endExt: @@ -1289,7 +1280,6 @@ class ObjectSlot(PathOp.ObjectOp): return (n1, n2) else: toEnd = p2.sub(p1) - factor = halfDist / toEnd.Length perp = FreeCAD.Vector(-1 * toEnd.y, toEnd.x, 0.0) perp.normalize() perp.multiply(halfDist) @@ -1465,8 +1455,6 @@ class ObjectSlot(PathOp.ObjectOp): for i in slcs: wires.append(i) if len(wires) > 0: - isFace = False - csWire = wires[0] if wires[0].isClosed(): face = Part.Face(wires[0]) if face.Area > 0: