From 3419e1da3a9ce6572265c7d26fc5d14670566a77 Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Thu, 5 Mar 2020 21:12:57 +0100 Subject: [PATCH 01/12] Added explicit deburr start values; Fixed direction detection for deburr operation --- src/Mod/Path/PathScripts/PathDeburr.py | 11 +++++++++-- src/Mod/Path/PathScripts/PathDressupLeadInOut.py | 4 ++++ src/Mod/Path/PathScripts/PathEngraveBase.py | 2 +- src/Mod/Path/PathScripts/PathOpTools.py | 6 +++++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index 46ba0f58ee..952f0afe83 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -76,6 +76,8 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): obj.setEditorMode('Join', 2) # hide for now obj.addProperty('App::PropertyEnumeration', 'Direction', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'Direction of Operation')) obj.Direction = ['CW', 'CCW'] + obj.addProperty('App::PropertyEnumeration', 'Side', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'Side of Operation')) + obj.Side = ['Outside', 'Inside'] def opOnDocumentRestored(self, obj): obj.setEditorMode('Join', 2) # hide for now @@ -104,12 +106,16 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): basewires.append(Part.Wire(edgelist)) self.basewires.extend(basewires) - + + side = ["Outside"] for w in basewires: self.adjusted_basewires.append(w) - wire = PathOpTools.offsetWire(w, base.Shape, offset, True) + wire = PathOpTools.offsetWire(w, base.Shape, offset, True, side) if wire: wires.append(wire) + + # Outside or Inside + obj.Side = side[0] forward = True if obj.Direction == 'CCW': @@ -143,6 +149,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): obj.setExpression('StepDown', '0 mm') obj.StepDown = '0 mm' obj.Direction = 'CW' + obj.Side = "Outside" def SetupProperties(): diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index 4ab56d5b37..7d34061448 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -132,11 +132,13 @@ class ObjectDressup: horizFeed = tc.HorizFeed.Value vertFeed = tc.VertFeed.Value toolnummer = tc.ToolNumber + # set the correct twist command if self.getDirectionOfPath(obj) == 'left': arcdir = "G3" else: arcdir = "G2" + R = obj.Length.Value # Radius of roll or length if queue[1].Name == "G1": # line p0 = queue[0].Placement.Base @@ -148,6 +150,7 @@ class ObjectDressup: p1 = queue[1].Placement.Base # PathLog.notice(" CURRENT_IN ARC : P0 X:{} Y:{} P1 X:{} Y:{} ".format(p0.x,p0.y,p1.x,p1.y)) v = self.normalize(p1.sub(p0)) + if self.getDirectionOfPath(obj) == 'right': off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) else: @@ -157,6 +160,7 @@ class ObjectDressup: leadstart = (p0.add(off_v)).sub(offsetvector) # Rmode else: leadstart = p0.add(off_v) # Dmode + if action == 'start': extendcommand = Path.Command('G0', {"X": 0.0, "Y": 0.0, "Z": op.ClearanceHeight.Value}) results.append(extendcommand) diff --git a/src/Mod/Path/PathScripts/PathEngraveBase.py b/src/Mod/Path/PathScripts/PathEngraveBase.py index df5c2436b8..11b48a33ae 100644 --- a/src/Mod/Path/PathScripts/PathEngraveBase.py +++ b/src/Mod/Path/PathScripts/PathEngraveBase.py @@ -86,7 +86,7 @@ class ObjectOp(PathOp.ObjectOp): self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) self.commandlist.append(Path.Command('G0', {'X': last.x, 'Y': last.y, 'F': self.horizRapid})) self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) - self.appendCommand(Path.Command('G1', {'Z': last.z}), z, relZ, self.vertFeed) + self.appendCommand(Path.Command('G1', {'X': last.x, 'Y': last.y, 'Z': last.z}), z, relZ, self.vertFeed) first = False if PathGeom.pointsCoincide(last, edge.Vertexes[0].Point): diff --git a/src/Mod/Path/PathScripts/PathOpTools.py b/src/Mod/Path/PathScripts/PathOpTools.py index 7ce01d92af..af4e0cf77e 100644 --- a/src/Mod/Path/PathScripts/PathOpTools.py +++ b/src/Mod/Path/PathScripts/PathOpTools.py @@ -141,7 +141,7 @@ def orientWire(w, forward=True): PathLog.track('orientWire - ok') return wire -def offsetWire(wire, base, offset, forward): +def offsetWire(wire, base, offset, forward, Side = None): '''offsetWire(wire, base, offset, forward) ... offsets the wire away from base and orients the wire accordingly. The function tries to avoid most of the pitfalls of Part.makeOffset2D which is possible because all offsetting happens in the XY plane. @@ -195,8 +195,12 @@ def offsetWire(wire, base, offset, forward): if wire.isClosed(): if not base.isInside(owire.Edges[0].Vertexes[0].Point, offset/2, True): PathLog.track('closed - outside') + if Side: + Side[0] = "Outside" return orientWire(owire, forward) PathLog.track('closed - inside') + if Side: + Side[0] = "Inside" try: owire = wire.makeOffset2D(-offset) except Exception: # pylint: disable=broad-except From 64b1acb3aeb2987b8893e56430888ce49fd8173d Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Thu, 5 Mar 2020 22:55:24 +0100 Subject: [PATCH 02/12] Added EntryPoint option to path deburr --- src/Mod/Path/PathScripts/PathDeburr.py | 9 +++++++-- src/Mod/Path/PathScripts/PathEngraveBase.py | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index 952f0afe83..f0129fe018 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -78,6 +78,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): obj.Direction = ['CW', 'CCW'] obj.addProperty('App::PropertyEnumeration', 'Side', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'Side of Operation')) obj.Side = ['Outside', 'Inside'] + obj.addProperty('App::PropertyInteger', 'EntryPoint', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'Select the segment, there the operations starts')) def opOnDocumentRestored(self, obj): obj.setEditorMode('Join', 2) # hide for now @@ -129,9 +130,12 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): zValues.append(z) zValues.append(depth) PathLog.track(obj.Label, depth, zValues) - + + if obj.EntryPoint < 0: + obj.EntryPoint = 0; + self.wires = wires # pylint: disable=attribute-defined-outside-init - self.buildpathocc(obj, wires, zValues, True, forward) + self.buildpathocc(obj, wires, zValues, True, forward, obj.EntryPoint) # the last command is a move to clearance, which is automatically added by PathOp if self.commandlist: @@ -150,6 +154,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): obj.StepDown = '0 mm' obj.Direction = 'CW' obj.Side = "Outside" + obj.EntryPoint = 0; def SetupProperties(): diff --git a/src/Mod/Path/PathScripts/PathEngraveBase.py b/src/Mod/Path/PathScripts/PathEngraveBase.py index 11b48a33ae..459e60a839 100644 --- a/src/Mod/Path/PathScripts/PathEngraveBase.py +++ b/src/Mod/Path/PathScripts/PathEngraveBase.py @@ -59,7 +59,7 @@ class ObjectOp(PathOp.ObjectOp): zValues.append(obj.FinalDepth.Value) return zValues - def buildpathocc(self, obj, wires, zValues, relZ=False, forward=True): + def buildpathocc(self, obj, wires, zValues, relZ=False, forward=True, start_idx=0): '''buildpathocc(obj, wires, zValues, relZ=False) ... internal helper function to generate engraving commands.''' PathLog.track(obj.Label, len(wires), zValues) @@ -78,6 +78,10 @@ class ObjectOp(PathOp.ObjectOp): self.appendCommand(Path.Command('G1', {'X': last.x, 'Y': last.y, 'Z': last.z}), z, relZ, self.vertFeed) first = True + if start_idx > len(edges)-1: + start_idx = len(edges)-1 + + edges = edges[start_idx:] + edges[:start_idx] for edge in edges: if first and (not last or not wire.isClosed()): # we set the first move to our first point From 651388ad318b935cb523ba85f0ec254cd0535d7b Mon Sep 17 00:00:00 2001 From: Schildkroet Date: Sun, 8 Mar 2020 13:10:05 +0100 Subject: [PATCH 03/12] Hide side property in PathDeburr.py --- src/Mod/Path/PathScripts/PathDeburr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index f0129fe018..c831e70f28 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -78,6 +78,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): obj.Direction = ['CW', 'CCW'] obj.addProperty('App::PropertyEnumeration', 'Side', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'Side of Operation')) obj.Side = ['Outside', 'Inside'] + obj.setEditorMode('Side', 2) # Hide property, it's always outside obj.addProperty('App::PropertyInteger', 'EntryPoint', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'Select the segment, there the operations starts')) def opOnDocumentRestored(self, obj): From 08e5e9e5884ee4f721d55f8b53dd532a45dcead5 Mon Sep 17 00:00:00 2001 From: Schildkroet Date: Mon, 9 Mar 2020 14:58:12 +0100 Subject: [PATCH 04/12] Added some comments --- src/Mod/Path/PathScripts/PathDeburr.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index c831e70f28..35351759eb 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -78,7 +78,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): obj.Direction = ['CW', 'CCW'] obj.addProperty('App::PropertyEnumeration', 'Side', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'Side of Operation')) obj.Side = ['Outside', 'Inside'] - obj.setEditorMode('Side', 2) # Hide property, it's always outside + obj.setEditorMode('Side', 2) # Hide property, it's calculated by op obj.addProperty('App::PropertyInteger', 'EntryPoint', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'Select the segment, there the operations starts')) def opOnDocumentRestored(self, obj): @@ -109,16 +109,19 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): self.basewires.extend(basewires) + # Set default value side = ["Outside"] + for w in basewires: self.adjusted_basewires.append(w) wire = PathOpTools.offsetWire(w, base.Shape, offset, True, side) if wire: wires.append(wire) - # Outside or Inside + # Save Outside or Inside obj.Side = side[0] - + + # Set direction of op forward = True if obj.Direction == 'CCW': forward = False @@ -149,7 +152,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): def opSetDefaultValues(self, obj, job): PathLog.track(obj.Label, job.Label) obj.Width = '1 mm' - obj.ExtraDepth = '0.1 mm' + obj.ExtraDepth = '0.5 mm' obj.Join = 'Round' obj.setExpression('StepDown', '0 mm') obj.StepDown = '0 mm' From 7656e746e2fdde524adc57effd41a8ca6a54b6b0 Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Fri, 13 Mar 2020 00:15:10 +0100 Subject: [PATCH 05/12] Added Exten LeadIn, arcs are now correctly entered --- .../Path/PathScripts/PathDressupLeadInOut.py | 92 ++++++++++++++++++- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index 7d34061448..de24c249c0 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -3,6 +3,7 @@ # *************************************************************************** # * * # * Copyright (c) 2017 LTS under LGPL * +# * Copyright (c) 2020 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -31,6 +32,7 @@ import PathScripts.PathGeom as PathGeom import PathScripts.PathLog as PathLog import PathScripts.PathUtils as PathUtils import math +import copy from PySide import QtCore @@ -68,7 +70,9 @@ class ObjectDressup: obj.addProperty("App::PropertyEnumeration", "RadiusCenter", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "The Mode of Point Radiusoffset or Center")) obj.RadiusCenter = ["Radius", "Center"] obj.Proxy = self - + obj.addProperty("App::PropertyDistance", "ExtendLeadIn", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extends LeadIn distance")) + obj.addProperty("App::PropertyDistance", "ExtendLeadOut", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extends LeadOut distance")) + self.wire = None self.rapids = None @@ -88,6 +92,8 @@ class ObjectDressup: obj.StyleOn = 'Arc' obj.StyleOff = 'Arc' obj.RadiusCenter = 'Radius' + obj.ExtendLeadIn = 0 + obj.ExtendLeadOut = 0 def execute(self, obj): if not obj.Base: @@ -122,6 +128,26 @@ class ObjectDressup: vx = round(x / length, 0) vy = round(y / length, 0) return FreeCAD.Vector(vx, vy, 0) + + def invert(self, Vector): + x = Vector.x * -1 + y = Vector.y * -1 + z = Vector.z * -1 + return FreeCAD.Vector(x, y, z) + + def multiply(self, Vector, len): + x = Vector.x * len + y = Vector.y * len + z = Vector.z * len + return FreeCAD.Vector(x, y, z) + + def rotate(self, Vector, angle): + s = math.sin(math.radians(angle)) + c = math.cos(math.radians(angle)) + xnew = Vector.x * c - Vector.y * s; + ynew = Vector.x * s + Vector.y * c; + print("X{} Y{}; X{} Y{}".format(Vector.x, Vector.y, xnew, ynew)) + return FreeCAD.Vector(xnew, ynew, Vector.z) def getLeadStart(self, obj, queue, action): '''returns Lead In G-code.''' @@ -155,7 +181,42 @@ class ObjectDressup: off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) else: off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) - offsetvector = FreeCAD.Vector(v.x*R, v.y*R, 0) # IJ + + # Check if we enter at line or arc command + if queue[1].Name in movecommands and queue[1].Name not in arccommands: + # We have a line move + vec = p1.sub(p0) + vec_n = self.normalize(vec) + vec_inv = self.invert(vec_n) + vec_off = self.multiply(vec_inv, obj.ExtendLeadIn) + #print("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) + else: + # We have an arc move + #print("Arc X{} Y{} P {}".format(p0.x, p0.y, queue[0].Parameters)) + pij = copy.deepcopy(p0) + pij.x += queue[1].Parameters['I'] + pij.y += queue[1].Parameters['J'] + ve = pij.sub(p0) + #print("I{} J{}, vx {} vy {}".format(pij.x, pij.y, ve.x, ve.y)) + if arcdir == "G2": + vec_rot = self.rotate(ve, 90) + else: + vec_rot = self.rotate(ve, -90) + + vec_n = self.normalize(vec_rot) + v = self.invert(vec_n) + + if arcdir == "G3": + off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) + else: + off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) + + #print("vnx{} vny{}, vxi {} vyi {}".format(vec_n.x, vec_n.y, v.x, v.y)) + #print("vxo{} vyo{}".format(off_v.x, off_v.y)) + vec_off = self.multiply(vec_rot, obj.ExtendLeadIn) + #print("vxo{} vyo{}".format(vec_off.x, vec_off.y)) + + offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0) # IJ if obj.RadiusCenter == 'Radius': leadstart = (p0.add(off_v)).sub(offsetvector) # Rmode else: @@ -168,27 +229,36 @@ class ObjectDressup: results.append(extendcommand) extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y, "Z": op.SafeHeight.Value}) results.append(extendcommand) + if action == 'layer': if not obj.KeepToolDown: extendcommand = Path.Command('G0', {"Z": op.SafeHeight.Value}) results.append(extendcommand) + extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y}) results.append(extendcommand) + extendcommand = Path.Command('G1', {"X": leadstart.x, "Y": leadstart.y, "Z": p1.z, "F": vertFeed}) results.append(extendcommand) + if obj.UseMachineCRC: if self.getDirectionOfPath(obj) == 'right': results.append(Path.Command('G42', {'D': toolnummer})) else: results.append(Path.Command('G41', {'D': toolnummer})) + if obj.StyleOn == 'Arc': - arcmove = Path.Command(arcdir, {"X": p0.x, "Y": p0.y, "I": offsetvector.x, "J": offsetvector.y, "F": horizFeed}) # add G2/G3 move + arcmove = Path.Command(arcdir, {"X": p0.x+vec_off.x, "Y": p0.y+vec_off.y, "I": offsetvector.x+vec_off.x, "J": offsetvector.y+vec_off.y, "F": horizFeed}) # add G2/G3 move results.append(arcmove) + if obj.ExtendLeadIn != 0: + extendcommand = Path.Command('G1', {"X": p0.x, "Y": p0.y, "F": horizFeed}) + results.append(extendcommand) elif obj.StyleOn == 'Tangent': extendcommand = Path.Command('G1', {"X": p0.x, "Y": p0.y, "F": horizFeed}) results.append(extendcommand) else: PathLog.notice(" CURRENT_IN Perp") + return results def getLeadEnd(self, obj, queue, action): @@ -197,11 +267,13 @@ class ObjectDressup: results = [] horizFeed = PathDressup.toolController(obj.Base).HorizFeed.Value R = obj.Length.Value # Radius of roll or length + # set the correct twist command if self.getDirectionOfPath(obj) == 'right': arcdir = "G2" else: arcdir = "G3" + if queue[1].Name == "G1": # line p0 = queue[0].Placement.Base p1 = queue[1].Placement.Base @@ -210,15 +282,18 @@ class ObjectDressup: p0 = queue[0].Placement.Base p1 = queue[1].Placement.Base v = self.normalize(p1.sub(p0)) + if self.getDirectionOfPath(obj) == 'right': off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) else: off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) + offsetvector = FreeCAD.Vector(v.x*R, v.y*R, 0.0) if obj.RadiusCenter == 'Radius': leadend = (p1.add(off_v)).add(offsetvector) # Rmode else: leadend = p1.add(off_v) # Dmode + IJ = off_v # .negative() #results.append(queue[1]) if obj.StyleOff == 'Arc': @@ -229,8 +304,10 @@ class ObjectDressup: results.append(extendcommand) else: PathLog.notice(" CURRENT_IN Perp") + if obj.UseMachineCRC: # crc off results.append(Path.Command('G40', {})) + return results def generateLeadInOutCurve(self, obj): @@ -240,6 +317,7 @@ class ObjectDressup: newpath = [] queue = [] action = 'start' + for curCommand in obj.Base.Path.Commands: # replace = None # don't worry about non-move commands, just add to output @@ -263,6 +341,7 @@ class ObjectDressup: queue.append(curCommand) if action == 'start' and len(queue) < 2: continue + if action == 'layer': if len(queue) > 2: queue.pop(0) @@ -274,20 +353,25 @@ class ObjectDressup: currLocation.update(curCommand.Parameters) else: newpath.append(curCommand) + if curCommand.z != currLocation["Z"] and action != 'start': # vertical feeding to depth if obj.LeadOut: # fish cycle if len(queue) > 2: queue.pop(len(queue)-1) + temp = self.getLeadEnd(obj, queue, action) newpath.extend(temp) action = 'layer' + if len(queue) > 2: queue.pop(0) + continue else: newpath.append(curCommand) if len(queue) > 2: queue.pop(0) + if obj.LeadIn and len(queue) >= 2 and action == 'start': temp = self.getLeadStart(obj, queue, action) newpath.extend(temp) @@ -296,7 +380,9 @@ class ObjectDressup: currLocation.update(curCommand.Parameters) else: newpath.append(curCommand) + currLocation.update(curCommand.Parameters) + commands = newpath return Path.Path(commands) From 70c263b540392fd8ab669a0352786400c76ba601 Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Fri, 13 Mar 2020 16:15:28 +0100 Subject: [PATCH 06/12] Fixed rounding bug --- src/Mod/Path/PathScripts/PathDressupLeadInOut.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index de24c249c0..c8894fd531 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -124,9 +124,10 @@ class ObjectDressup: x = Vector.x y = Vector.y length = math.sqrt(x*x + y*y) + #print("Len: {}".format(length)) if((math.fabs(length)) > 0.0000000000001): - vx = round(x / length, 0) - vy = round(y / length, 0) + vx = round(x / length, 2) + vy = round(y / length, 2) return FreeCAD.Vector(vx, vy, 0) def invert(self, Vector): @@ -146,7 +147,7 @@ class ObjectDressup: c = math.cos(math.radians(angle)) xnew = Vector.x * c - Vector.y * s; ynew = Vector.x * s + Vector.y * c; - print("X{} Y{}; X{} Y{}".format(Vector.x, Vector.y, xnew, ynew)) + #print("X{} Y{}; X{} Y{}".format(Vector.x, Vector.y, xnew, ynew)) return FreeCAD.Vector(xnew, ynew, Vector.z) def getLeadStart(self, obj, queue, action): @@ -203,6 +204,7 @@ class ObjectDressup: else: vec_rot = self.rotate(ve, -90) + #print("vro{} vro{}".format(vec_rot.x, vec_rot.y)) vec_n = self.normalize(vec_rot) v = self.invert(vec_n) @@ -223,6 +225,7 @@ class ObjectDressup: leadstart = p0.add(off_v) # Dmode if action == 'start': + #print("Start") extendcommand = Path.Command('G0', {"X": 0.0, "Y": 0.0, "Z": op.ClearanceHeight.Value}) results.append(extendcommand) extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y, "Z": op.ClearanceHeight.Value}) @@ -231,6 +234,7 @@ class ObjectDressup: results.append(extendcommand) if action == 'layer': + #print("Layer") if not obj.KeepToolDown: extendcommand = Path.Command('G0', {"Z": op.SafeHeight.Value}) results.append(extendcommand) From 2088cdfeb85dd4bcb321265a54eb975e2a2ca31a Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Mon, 16 Mar 2020 21:57:19 +0100 Subject: [PATCH 07/12] Improved LeadOut --- .../Path/PathScripts/PathDressupLeadInOut.py | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index c8894fd531..168f8bdcf8 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -147,7 +147,6 @@ class ObjectDressup: c = math.cos(math.radians(angle)) xnew = Vector.x * c - Vector.y * s; ynew = Vector.x * s + Vector.y * c; - #print("X{} Y{}; X{} Y{}".format(Vector.x, Vector.y, xnew, ynew)) return FreeCAD.Vector(xnew, ynew, Vector.z) def getLeadStart(self, obj, queue, action): @@ -171,7 +170,7 @@ class ObjectDressup: p0 = queue[0].Placement.Base p1 = queue[1].Placement.Base v = self.normalize(p1.sub(p0)) - # PathLog.notice(" CURRENT_IN : P0 Z:{} p1 Z:{}".format(p0.z,p1.z)) + # PathLog.notice(" CURRENT_IN : P0 Z:{} p1 Z:{}".format(p0.z,p1.z)) else: p0 = queue[0].Placement.Base p1 = queue[1].Placement.Base @@ -213,9 +212,9 @@ class ObjectDressup: else: off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) + vec_off = self.multiply(vec_rot, obj.ExtendLeadIn) #print("vnx{} vny{}, vxi {} vyi {}".format(vec_n.x, vec_n.y, v.x, v.y)) #print("vxo{} vyo{}".format(off_v.x, off_v.y)) - vec_off = self.multiply(vec_rot, obj.ExtendLeadIn) #print("vxo{} vyo{}".format(vec_off.x, vec_off.y)) offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0) # IJ @@ -292,15 +291,57 @@ class ObjectDressup: else: off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) - offsetvector = FreeCAD.Vector(v.x*R, v.y*R, 0.0) + # Check if we leave at line or arc command + if queue[1].Name in movecommands and queue[1].Name not in arccommands: + # We have a line move + vec = p1.sub(p0) + vec_n = self.normalize(vec) + vec_inv = self.invert(vec_n) + vec_off = self.multiply(vec_inv, obj.ExtendLeadOut) + #print("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) + else: + # We have an arc move + #print("Arc0 X{} Y{} P {}".format(p0.x, p0.y, queue[0].Parameters)) + #print("Arc1 X{} Y{} P {}".format(p1.x, p1.y, queue[1].Parameters)) + pij = copy.deepcopy(p0) + pij.x += queue[1].Parameters['I'] + pij.y += queue[1].Parameters['J'] + ve = pij.sub(p1) + # print("I{} J{}, vx {} vy {}".format(pij.x, pij.y, ve.x, ve.y)) + if arcdir == "G2": + vec_rot = self.rotate(ve, -90) + else: + vec_rot = self.rotate(ve, 90) + + #print("vro{} vro{}".format(vec_rot.x, vec_rot.y)) + vec_n = self.normalize(vec_rot) + v = vec_n + + if arcdir == "G3": + off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) + else: + off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) + + vec_off = self.multiply(self.invert(vec_rot), obj.ExtendLeadOut) + #print("vnx{} vny{}, vxi {} vyi {}".format(vec_n.x, vec_n.y, v.x, v.y)) + #print("vxo{} vyo{}".format(off_v.x, off_v.y)) + #print("vxo{} vyo{}".format(vec_off.x, vec_off.y)) + + #print("Arc0 X{} Y{} P {}".format(p0.x, p0.y, queue[0].Parameters)) + offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0.0) if obj.RadiusCenter == 'Radius': leadend = (p1.add(off_v)).add(offsetvector) # Rmode + #print("End: X {}, Y{}".format(leadend.x, leadend.y)) else: leadend = p1.add(off_v) # Dmode IJ = off_v # .negative() + print("IJ: X {}, Y{}".format(IJ.x, IJ.y)) #results.append(queue[1]) if obj.StyleOff == 'Arc': + if obj.ExtendLeadOut != 0: + extendcommand = Path.Command('G1', {"X": p1.x-vec_off.x, "Y": p1.y-vec_off.y, "F": horizFeed}) + results.append(extendcommand) arcmove = Path.Command(arcdir, {"X": leadend.x, "Y": leadend.y, "I": IJ.x, "J": IJ.y, "F": horizFeed}) # add G2/G3 move results.append(arcmove) elif obj.StyleOff == 'Tangent': From d586eea42e864279fe60b338a384c8edc2209e79 Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Tue, 17 Mar 2020 19:13:56 +0100 Subject: [PATCH 08/12] Improved LeadInOut; Better layer handling --- src/Mod/Path/PathScripts/PathDeburr.py | 2 + .../Path/PathScripts/PathDressupLeadInOut.py | 285 ++++++++++++------ src/Mod/Path/PathScripts/PathProfileEdges.py | 4 +- 3 files changed, 189 insertions(+), 102 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index 35351759eb..7825ccab4d 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -58,6 +58,8 @@ def toolDepthAndOffset(width, extraDepth, tool): toolOffset = float(tool.FlatRadius) extraOffset = float(tool.Diameter) / 2 - width if 180 == angle else extraDepth / tan offset = toolOffset + extraOffset + if offset < 0.0001: + offset = 0.01 return (depth, offset) diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index 168f8bdcf8..0a9bd80d7a 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -72,6 +72,7 @@ class ObjectDressup: obj.Proxy = self obj.addProperty("App::PropertyDistance", "ExtendLeadIn", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extends LeadIn distance")) obj.addProperty("App::PropertyDistance", "ExtendLeadOut", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extends LeadOut distance")) + obj.addProperty("App::PropertyBool", "RapidPlunge", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Perform plunges with G0")) self.wire = None self.rapids = None @@ -94,6 +95,7 @@ class ObjectDressup: obj.RadiusCenter = 'Radius' obj.ExtendLeadIn = 0 obj.ExtendLeadOut = 0 + obj.RapidPlunge = False def execute(self, obj): if not obj.Base: @@ -119,6 +121,13 @@ class ObjectDressup: if hasattr(op, 'Direction') and op.Direction == 'CW': return 'right' return 'left' + + def getSideOfPath(self, obj): + op = PathDressup.baseOp(obj.Base) + if hasattr(op, 'Side'): + return op.Side + + return '' def normalize(self, Vector): x = Vector.x @@ -126,8 +135,8 @@ class ObjectDressup: length = math.sqrt(x*x + y*y) #print("Len: {}".format(length)) if((math.fabs(length)) > 0.0000000000001): - vx = round(x / length, 2) - vy = round(y / length, 2) + vx = round(x / length, 3) + vy = round(y / length, 3) return FreeCAD.Vector(vx, vy, 0) def invert(self, Vector): @@ -152,14 +161,13 @@ class ObjectDressup: def getLeadStart(self, obj, queue, action): '''returns Lead In G-code.''' results = [] - # zdepth = currLocation["Z"] op = PathDressup.baseOp(obj.Base) tc = PathDressup.toolController(obj.Base) horizFeed = tc.HorizFeed.Value vertFeed = tc.VertFeed.Value toolnummer = tc.ToolNumber - # set the correct twist command + # Set the correct twist command if self.getDirectionOfPath(obj) == 'left': arcdir = "G3" else: @@ -170,13 +178,14 @@ class ObjectDressup: p0 = queue[0].Placement.Base p1 = queue[1].Placement.Base v = self.normalize(p1.sub(p0)) - # PathLog.notice(" CURRENT_IN : P0 Z:{} p1 Z:{}".format(p0.z,p1.z)) + # PathLog.debug(" CURRENT_IN : P0 Z:{} p1 Z:{}".format(p0.z,p1.z)) else: p0 = queue[0].Placement.Base p1 = queue[1].Placement.Base - # PathLog.notice(" CURRENT_IN ARC : P0 X:{} Y:{} P1 X:{} Y:{} ".format(p0.x,p0.y,p1.x,p1.y)) v = self.normalize(p1.sub(p0)) - + # PathLog.debug(" CURRENT_IN ARC : P0 X:{} Y:{} P1 X:{} Y:{} ".format(p0.x,p0.y,p1.x,p1.y)) + + # Calculate offset vector (will be overwritten for arcs) if self.getDirectionOfPath(obj) == 'right': off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) else: @@ -189,33 +198,36 @@ class ObjectDressup: vec_n = self.normalize(vec) vec_inv = self.invert(vec_n) vec_off = self.multiply(vec_inv, obj.ExtendLeadIn) - #print("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) + #PathLog.debug("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) else: # We have an arc move - #print("Arc X{} Y{} P {}".format(p0.x, p0.y, queue[0].Parameters)) + # Calculate coordinates for middle of circle pij = copy.deepcopy(p0) pij.x += queue[1].Parameters['I'] pij.y += queue[1].Parameters['J'] - ve = pij.sub(p0) - #print("I{} J{}, vx {} vy {}".format(pij.x, pij.y, ve.x, ve.y)) - if arcdir == "G2": - vec_rot = self.rotate(ve, 90) - else: - vec_rot = self.rotate(ve, -90) - #print("vro{} vro{}".format(vec_rot.x, vec_rot.y)) + # Calculate vector circle start -> circle middle + vec_circ = pij.sub(p0) + + # Rotate vector to get direction for lead in + if arcdir == "G2": + vec_rot = self.rotate(vec_circ, 90) + else: + vec_rot = self.rotate(vec_circ, -90) + + # Normalize and invert vector vec_n = self.normalize(vec_rot) + v = self.invert(vec_n) + # Calculate offset of lead in if arcdir == "G3": off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) else: off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) + # Multiply offset by LeadIn length vec_off = self.multiply(vec_rot, obj.ExtendLeadIn) - #print("vnx{} vny{}, vxi {} vyi {}".format(vec_n.x, vec_n.y, v.x, v.y)) - #print("vxo{} vyo{}".format(off_v.x, off_v.y)) - #print("vxo{} vyo{}".format(vec_off.x, vec_off.y)) offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0) # IJ if obj.RadiusCenter == 'Radius': @@ -224,16 +236,14 @@ class ObjectDressup: leadstart = p0.add(off_v) # Dmode if action == 'start': - #print("Start") - extendcommand = Path.Command('G0', {"X": 0.0, "Y": 0.0, "Z": op.ClearanceHeight.Value}) - results.append(extendcommand) + #extendcommand = Path.Command('G0', {"X": 0.0, "Y": 0.0, "Z": op.ClearanceHeight.Value}) + #results.append(extendcommand) extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y, "Z": op.ClearanceHeight.Value}) results.append(extendcommand) - extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y, "Z": op.SafeHeight.Value}) + extendcommand = Path.Command('G0', {"Z": op.SafeHeight.Value}) results.append(extendcommand) if action == 'layer': - #print("Layer") if not obj.KeepToolDown: extendcommand = Path.Command('G0', {"Z": op.SafeHeight.Value}) results.append(extendcommand) @@ -241,7 +251,10 @@ class ObjectDressup: extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y}) results.append(extendcommand) - extendcommand = Path.Command('G1', {"X": leadstart.x, "Y": leadstart.y, "Z": p1.z, "F": vertFeed}) + if not obj.RapidPlunge: + extendcommand = Path.Command('G1', {"X": leadstart.x, "Y": leadstart.y, "Z": p1.z, "F": vertFeed}) + else: + extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y, "Z": p1.z,}) results.append(extendcommand) if obj.UseMachineCRC: @@ -260,8 +273,11 @@ class ObjectDressup: extendcommand = Path.Command('G1', {"X": p0.x, "Y": p0.y, "F": horizFeed}) results.append(extendcommand) else: - PathLog.notice(" CURRENT_IN Perp") + PathLog.debug(" CURRENT_IN Perp") + currLocation.update(results[-1].Parameters) + currLocation['Z'] = p1.z + return results def getLeadEnd(self, obj, queue, action): @@ -298,22 +314,19 @@ class ObjectDressup: vec_n = self.normalize(vec) vec_inv = self.invert(vec_n) vec_off = self.multiply(vec_inv, obj.ExtendLeadOut) - #print("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) + #PathLog.debug("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) else: # We have an arc move - #print("Arc0 X{} Y{} P {}".format(p0.x, p0.y, queue[0].Parameters)) - #print("Arc1 X{} Y{} P {}".format(p1.x, p1.y, queue[1].Parameters)) pij = copy.deepcopy(p0) pij.x += queue[1].Parameters['I'] pij.y += queue[1].Parameters['J'] ve = pij.sub(p1) - # print("I{} J{}, vx {} vy {}".format(pij.x, pij.y, ve.x, ve.y)) + if arcdir == "G2": vec_rot = self.rotate(ve, -90) else: vec_rot = self.rotate(ve, 90) - #print("vro{} vro{}".format(vec_rot.x, vec_rot.y)) vec_n = self.normalize(vec_rot) v = vec_n @@ -322,21 +335,17 @@ class ObjectDressup: else: off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) - vec_off = self.multiply(self.invert(vec_rot), obj.ExtendLeadOut) - #print("vnx{} vny{}, vxi {} vyi {}".format(vec_n.x, vec_n.y, v.x, v.y)) - #print("vxo{} vyo{}".format(off_v.x, off_v.y)) - #print("vxo{} vyo{}".format(vec_off.x, vec_off.y)) + vec_inv = self.invert(vec_rot) + + vec_off = self.multiply(vec_inv, obj.ExtendLeadOut) - #print("Arc0 X{} Y{} P {}".format(p0.x, p0.y, queue[0].Parameters)) offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0.0) if obj.RadiusCenter == 'Radius': leadend = (p1.add(off_v)).add(offsetvector) # Rmode - #print("End: X {}, Y{}".format(leadend.x, leadend.y)) else: leadend = p1.add(off_v) # Dmode IJ = off_v # .negative() - print("IJ: X {}, Y{}".format(IJ.x, IJ.y)) #results.append(queue[1]) if obj.StyleOff == 'Arc': if obj.ExtendLeadOut != 0: @@ -355,83 +364,161 @@ class ObjectDressup: return results +# def generateLeadInOutCurve(self, obj): +# global currLocation # pylint: disable=global-statement +# firstmove = Path.Command("G0", {"X": 0, "Y": 0, "Z": 0}) +# currLocation.update(firstmove.Parameters) +# newpath = [] +# queue = [] +# action = 'start' +# +# +# for curCommand in obj.Base.Path.Commands: +# if len(queue) > 2: +# queue.pop(0) +# +# # Don't worry about non-move commands, just add to output +# if curCommand.Name not in movecommands + rapidcommands: +# newpath.append(curCommand) +# continue +# +# # rapid retract triggers exit move, else just add to output +# if curCommand.Name in rapidcommands: +# +# currLocation.update(curCommand.Parameters) +# +# if curCommand.Name in movecommands: +# queue.append(curCommand) +# if action == 'start' and len(queue) < 2: +# # Not enough data +# continue +# +# if action == 'leave': +# newpath.append(curCommand) +# +# # First lead in +# if obj.LeadIn and len(queue) >= 2 and action == 'start': +# print("Calc lead in...") +# temp = self.getLeadStart(obj, queue, action) +# newpath.extend(temp) +# newpath.append(curCommand) +# print("Append: {}, P: {}".format(curCommand.Name, curCommand.Parameters)) +# action = 'leave' +# currLocation.update(curCommand.Parameters) +# continue +# +# if curCommand.z != currLocation["Z"] and action == 'leave': +# print("Calc lead out...") +# if obj.LeadOut: # fish cycle +# if len(queue) > 2: +# # Remove last cmd +# queue.pop(len(queue)-1) +# +# temp = self.getLeadEnd(obj, queue, action) +# newpath.extend(temp) +# +# action = 'layer' +# if not obj.KeepToolDown: +# newpath.append(curCommand) +# +# if action == 'layer': +# print("layer") +# while(len(queue)) > 2: +# queue.pop(0) +# +# if obj.LeadIn: +# temp = self.getLeadStart(obj, queue, action) +# newpath.extend(temp) +# #newpath.append(curCommand) +# action = 'leave' +# currLocation.update(curCommand.Parameters) +# else: +# newpath.append(curCommand) +# #print("Append: {}, P: {}".format(curCommand.Name, curCommand.Parameters)) +# +# commands = newpath +# return Path.Path(commands) + def generateLeadInOutCurve(self, obj): global currLocation # pylint: disable=global-statement firstmove = Path.Command("G0", {"X": 0, "Y": 0, "Z": 0}) + op = PathDressup.baseOp(obj.Base) currLocation.update(firstmove.Parameters) newpath = [] queue = [] action = 'start' + prevCmd = '' + layers = [] + + # Read in all commands for curCommand in obj.Base.Path.Commands: - # replace = None - # don't worry about non-move commands, just add to output if curCommand.Name not in movecommands + rapidcommands: + # Don't worry about non-move commands, just add to output newpath.append(curCommand) continue - - # rapid retract triggers exit move, else just add to output - if curCommand.Name in rapidcommands: - # detect start position - if (curCommand.x is not None) or (curCommand.y is not None): - firstmove = curCommand - currLocation.update(curCommand.Parameters) - if action != 'start': # done move out - if obj.LeadOut: - temp = self.getLeadEnd(obj, queue, 'end') - newpath.extend(temp) - newpath.append(curCommand) # Z clear DONE - - if curCommand.Name in movecommands: - queue.append(curCommand) - if action == 'start' and len(queue) < 2: - continue - - if action == 'layer': - if len(queue) > 2: - queue.pop(0) - if obj.LeadIn: - temp = self.getLeadStart(obj, queue, action) - newpath.extend(temp) - #newpath.append(curCommand) - action = 'none' - currLocation.update(curCommand.Parameters) - else: - newpath.append(curCommand) - - if curCommand.z != currLocation["Z"] and action != 'start': # vertical feeding to depth - if obj.LeadOut: # fish cycle - if len(queue) > 2: - queue.pop(len(queue)-1) - - temp = self.getLeadEnd(obj, queue, action) - newpath.extend(temp) - action = 'layer' - - if len(queue) > 2: - queue.pop(0) - - continue - else: - newpath.append(curCommand) - if len(queue) > 2: - queue.pop(0) - - if obj.LeadIn and len(queue) >= 2 and action == 'start': - temp = self.getLeadStart(obj, queue, action) - newpath.extend(temp) - newpath.append(curCommand) - action = 'none' - currLocation.update(curCommand.Parameters) - else: - newpath.append(curCommand) - - currLocation.update(curCommand.Parameters) + if curCommand.Name in rapidcommands: + # We don't care about rapid moves + prevCmd = curCommand + currLocation.update(curCommand.Parameters) + continue + + if curCommand.Name in movecommands: + if prevCmd.Name in rapidcommands and curCommand.Name in movecommands and len(queue) > 0: + # Layer changed: Save current layer cmds prepare next layer + layers.append(queue) + queue = [] + #print("New layer: {}".format(layers)) + + # Save all move commands + queue.append(curCommand) + #print("Append move: {}, P: {}".format(curCommand.Name, curCommand.Parameters)) + + currLocation.update(curCommand.Parameters) + prevCmd = curCommand + + # Add last layer + if len(queue) > 0: + layers.append(queue) + queue = [] + #print("New layer: {}".format(layers)) + + # Go through each layer and add leadIn/Out + idx = 0 + for layer in layers: + #print("Layer {}".format(idx)) + + if obj.LeadIn: + #print("Lead IN") + temp = self.getLeadStart(obj, layer, action) + newpath.extend(temp) + + for cmd in layer: + #print("CurLoc: {}, NewCmd: {}".format(currLocation, cmd)) + if currLocation['X'] == cmd.x and currLocation['Y'] == cmd.y and currLocation['Z'] == cmd.z and cmd.Name in ['G1', 'G01']: + continue + newpath.append(cmd) + + if obj.LeadOut: + #print("Lead OUT") + tmp = [] + tmp.append(layer[-2]) + tmp.append(layer[-1]) + temp = self.getLeadEnd(obj, tmp, action) + newpath.extend(temp) + + if not obj.KeepToolDown or idx == len(layers)-1: + extendcommand = Path.Command('G0', {"Z": op.ClearanceHeight.Value}) + newpath.append(extendcommand) + else: + action = 'layer' + + idx += 1 + commands = newpath return Path.Path(commands) - class ViewProviderDressup: def __init__(self, vobj): diff --git a/src/Mod/Path/PathScripts/PathProfileEdges.py b/src/Mod/Path/PathScripts/PathProfileEdges.py index 5fce9b0774..c80f190b96 100644 --- a/src/Mod/Path/PathScripts/PathProfileEdges.py +++ b/src/Mod/Path/PathScripts/PathProfileEdges.py @@ -134,7 +134,7 @@ class ObjectProfile(PathProfileBase.ObjectProfile): if FreeCAD.GuiUp: import FreeCADGui FreeCADGui.ActiveDocument.getObject(tmpGrpNm).Visibility = False - + return shapes def _flattenWire(self, obj, wire, trgtDep): @@ -297,12 +297,10 @@ class ObjectProfile(PathProfileBase.ObjectProfile): # Determine with which set of intersection tags the model intersects (cmnIntArea, cmnExtArea) = self._checkTagIntersection(iTAG, eTAG, 'QRY', comFC) if cmnExtArea > cmnIntArea: - PathLog.debug('Cutting on Ext side.') self.cutSide = 'E' self.cutSideTags = eTAG.Shape tagCOM = begExt.CenterOfMass else: - PathLog.debug('Cutting on Int side.') self.cutSide = 'I' self.cutSideTags = iTAG.Shape tagCOM = begInt.CenterOfMass From 301249bd859e37766ccf886c71bd22d8c34101e5 Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Tue, 17 Mar 2020 21:24:37 +0100 Subject: [PATCH 09/12] Fixed inner circle bug --- .../Path/PathScripts/PathDressupLeadInOut.py | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index 0a9bd80d7a..4417be654d 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -166,6 +166,7 @@ class ObjectDressup: horizFeed = tc.HorizFeed.Value vertFeed = tc.VertFeed.Value toolnummer = tc.ToolNumber + arcs_identical = False # Set the correct twist command if self.getDirectionOfPath(obj) == 'left': @@ -183,6 +184,7 @@ class ObjectDressup: p0 = queue[0].Placement.Base p1 = queue[1].Placement.Base v = self.normalize(p1.sub(p0)) + #arcdir = queue[1].Name # PathLog.debug(" CURRENT_IN ARC : P0 X:{} Y:{} P1 X:{} Y:{} ".format(p0.x,p0.y,p1.x,p1.y)) # Calculate offset vector (will be overwritten for arcs) @@ -206,32 +208,43 @@ class ObjectDressup: pij.x += queue[1].Parameters['I'] pij.y += queue[1].Parameters['J'] + if arcdir == queue[1].Name: + arcs_identical = True + # Calculate vector circle start -> circle middle vec_circ = pij.sub(p0) - + print("Vec:circ: {}".format(vec_circ)) # Rotate vector to get direction for lead in if arcdir == "G2": vec_rot = self.rotate(vec_circ, 90) else: vec_rot = self.rotate(vec_circ, -90) - + print("Vec:rot: {}".format(vec_rot)) # Normalize and invert vector vec_n = self.normalize(vec_rot) v = self.invert(vec_n) + #v = vec_n + print("Vec:inv: {}".format(v)) # Calculate offset of lead in if arcdir == "G3": off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) else: off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) - + print("Vec:off: {}".format(off_v)) # Multiply offset by LeadIn length - vec_off = self.multiply(vec_rot, obj.ExtendLeadIn) + vec_off = self.multiply(vec_n, obj.ExtendLeadIn) offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0) # IJ + print("off: {}".format(offsetvector)) if obj.RadiusCenter == 'Radius': leadstart = (p0.add(off_v)).sub(offsetvector) # Rmode + if arcs_identical: + t = p0.sub(leadstart) + t = p0.add(t) + leadstart = t + offsetvector = self.multiply(offsetvector, -1) else: leadstart = p0.add(off_v) # Dmode @@ -286,6 +299,7 @@ class ObjectDressup: results = [] horizFeed = PathDressup.toolController(obj.Base).HorizFeed.Value R = obj.Length.Value # Radius of roll or length + arcs_identical = False # set the correct twist command if self.getDirectionOfPath(obj) == 'right': @@ -322,6 +336,9 @@ class ObjectDressup: pij.y += queue[1].Parameters['J'] ve = pij.sub(p1) + if arcdir == queue[1].Name: + arcs_identical = True + if arcdir == "G2": vec_rot = self.rotate(ve, -90) else: @@ -342,6 +359,11 @@ class ObjectDressup: offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0.0) if obj.RadiusCenter == 'Radius': leadend = (p1.add(off_v)).add(offsetvector) # Rmode + if arcs_identical: + t = p1.sub(leadend) + t = p1.add(t) + leadend = t + off_v = self.multiply(off_v, -1) else: leadend = p1.add(off_v) # Dmode @@ -496,8 +518,8 @@ class ObjectDressup: for cmd in layer: #print("CurLoc: {}, NewCmd: {}".format(currLocation, cmd)) - if currLocation['X'] == cmd.x and currLocation['Y'] == cmd.y and currLocation['Z'] == cmd.z and cmd.Name in ['G1', 'G01']: - continue + #if currLocation['X'] == cmd.x and currLocation['Y'] == cmd.y and currLocation['Z'] == cmd.z and cmd.Name in ['G1', 'G01']: + #continue newpath.append(cmd) if obj.LeadOut: From c82a0498d114a33220479f5d9bdc9e7f6c9aea40 Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Tue, 17 Mar 2020 22:18:47 +0100 Subject: [PATCH 10/12] Code clean up --- src/Mod/Path/PathScripts/PathDeburr.py | 1 + .../Path/PathScripts/PathDressupLeadInOut.py | 86 +------------------ src/Mod/Path/PathScripts/PathProfileEdges.py | 2 + 3 files changed, 7 insertions(+), 82 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index 7825ccab4d..e44ab90c71 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -3,6 +3,7 @@ # *************************************************************************** # * * # * Copyright (c) 2018 sliptonic * +# * Copyright (c) 2020 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index 4417be654d..4a518f56d2 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -184,7 +184,6 @@ class ObjectDressup: p0 = queue[0].Placement.Base p1 = queue[1].Placement.Base v = self.normalize(p1.sub(p0)) - #arcdir = queue[1].Name # PathLog.debug(" CURRENT_IN ARC : P0 X:{} Y:{} P1 X:{} Y:{} ".format(p0.x,p0.y,p1.x,p1.y)) # Calculate offset vector (will be overwritten for arcs) @@ -213,31 +212,29 @@ class ObjectDressup: # Calculate vector circle start -> circle middle vec_circ = pij.sub(p0) - print("Vec:circ: {}".format(vec_circ)) + # Rotate vector to get direction for lead in if arcdir == "G2": vec_rot = self.rotate(vec_circ, 90) else: vec_rot = self.rotate(vec_circ, -90) - print("Vec:rot: {}".format(vec_rot)) + # Normalize and invert vector vec_n = self.normalize(vec_rot) v = self.invert(vec_n) - #v = vec_n - print("Vec:inv: {}".format(v)) # Calculate offset of lead in if arcdir == "G3": off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) else: off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) - print("Vec:off: {}".format(off_v)) + # Multiply offset by LeadIn length vec_off = self.multiply(vec_n, obj.ExtendLeadIn) offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0) # IJ - print("off: {}".format(offsetvector)) + if obj.RadiusCenter == 'Radius': leadstart = (p0.add(off_v)).sub(offsetvector) # Rmode if arcs_identical: @@ -386,81 +383,6 @@ class ObjectDressup: return results -# def generateLeadInOutCurve(self, obj): -# global currLocation # pylint: disable=global-statement -# firstmove = Path.Command("G0", {"X": 0, "Y": 0, "Z": 0}) -# currLocation.update(firstmove.Parameters) -# newpath = [] -# queue = [] -# action = 'start' -# -# -# for curCommand in obj.Base.Path.Commands: -# if len(queue) > 2: -# queue.pop(0) -# -# # Don't worry about non-move commands, just add to output -# if curCommand.Name not in movecommands + rapidcommands: -# newpath.append(curCommand) -# continue -# -# # rapid retract triggers exit move, else just add to output -# if curCommand.Name in rapidcommands: -# -# currLocation.update(curCommand.Parameters) -# -# if curCommand.Name in movecommands: -# queue.append(curCommand) -# if action == 'start' and len(queue) < 2: -# # Not enough data -# continue -# -# if action == 'leave': -# newpath.append(curCommand) -# -# # First lead in -# if obj.LeadIn and len(queue) >= 2 and action == 'start': -# print("Calc lead in...") -# temp = self.getLeadStart(obj, queue, action) -# newpath.extend(temp) -# newpath.append(curCommand) -# print("Append: {}, P: {}".format(curCommand.Name, curCommand.Parameters)) -# action = 'leave' -# currLocation.update(curCommand.Parameters) -# continue -# -# if curCommand.z != currLocation["Z"] and action == 'leave': -# print("Calc lead out...") -# if obj.LeadOut: # fish cycle -# if len(queue) > 2: -# # Remove last cmd -# queue.pop(len(queue)-1) -# -# temp = self.getLeadEnd(obj, queue, action) -# newpath.extend(temp) -# -# action = 'layer' -# if not obj.KeepToolDown: -# newpath.append(curCommand) -# -# if action == 'layer': -# print("layer") -# while(len(queue)) > 2: -# queue.pop(0) -# -# if obj.LeadIn: -# temp = self.getLeadStart(obj, queue, action) -# newpath.extend(temp) -# #newpath.append(curCommand) -# action = 'leave' -# currLocation.update(curCommand.Parameters) -# else: -# newpath.append(curCommand) -# #print("Append: {}, P: {}".format(curCommand.Name, curCommand.Parameters)) -# -# commands = newpath -# return Path.Path(commands) - def generateLeadInOutCurve(self, obj): global currLocation # pylint: disable=global-statement firstmove = Path.Command("G0", {"X": 0, "Y": 0, "Z": 0}) diff --git a/src/Mod/Path/PathScripts/PathProfileEdges.py b/src/Mod/Path/PathScripts/PathProfileEdges.py index c80f190b96..d44c050d93 100644 --- a/src/Mod/Path/PathScripts/PathProfileEdges.py +++ b/src/Mod/Path/PathScripts/PathProfileEdges.py @@ -297,10 +297,12 @@ class ObjectProfile(PathProfileBase.ObjectProfile): # Determine with which set of intersection tags the model intersects (cmnIntArea, cmnExtArea) = self._checkTagIntersection(iTAG, eTAG, 'QRY', comFC) if cmnExtArea > cmnIntArea: + PathLog.debug('Cutting on Ext side.') self.cutSide = 'E' self.cutSideTags = eTAG.Shape tagCOM = begExt.CenterOfMass else: + PathLog.debug('Cutting on Int side.') self.cutSide = 'I' self.cutSideTags = iTAG.Shape tagCOM = begInt.CenterOfMass From b9b9cd5c260847e5487943c9e2e94b362114b1b9 Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Wed, 18 Mar 2020 16:21:07 +0100 Subject: [PATCH 11/12] More improvements for LeadInOut --- .../Path/PathScripts/PathDressupLeadInOut.py | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index 4a518f56d2..f5ab6f8ca2 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -73,6 +73,7 @@ class ObjectDressup: obj.addProperty("App::PropertyDistance", "ExtendLeadIn", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extends LeadIn distance")) obj.addProperty("App::PropertyDistance", "ExtendLeadOut", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extends LeadOut distance")) obj.addProperty("App::PropertyBool", "RapidPlunge", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Perform plunges with G0")) + obj.addProperty("App::PropertyBool", "IncludeLayers", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Apply LeadInOut to layers within an operation")) self.wire = None self.rapids = None @@ -96,6 +97,7 @@ class ObjectDressup: obj.ExtendLeadIn = 0 obj.ExtendLeadOut = 0 obj.RapidPlunge = False + obj.IncludeLayers = True def execute(self, obj): if not obj.Base: @@ -133,7 +135,6 @@ class ObjectDressup: x = Vector.x y = Vector.y length = math.sqrt(x*x + y*y) - #print("Len: {}".format(length)) if((math.fabs(length)) > 0.0000000000001): vx = round(x / length, 3) vy = round(y / length, 3) @@ -207,6 +208,7 @@ class ObjectDressup: pij.x += queue[1].Parameters['I'] pij.y += queue[1].Parameters['J'] + # Check if lead in and operation go in same direction (usually for inner circles) if arcdir == queue[1].Name: arcs_identical = True @@ -298,7 +300,7 @@ class ObjectDressup: R = obj.Length.Value # Radius of roll or length arcs_identical = False - # set the correct twist command + # Set the correct twist command if self.getDirectionOfPath(obj) == 'right': arcdir = "G2" else: @@ -376,7 +378,7 @@ class ObjectDressup: extendcommand = Path.Command('G1', {"X": leadend.x, "Y": leadend.y, "F": horizFeed}) results.append(extendcommand) else: - PathLog.notice(" CURRENT_IN Perp") + PathLog.debug(" CURRENT_IN Perp") if obj.UseMachineCRC: # crc off results.append(Path.Command('G40', {})) @@ -392,11 +394,11 @@ class ObjectDressup: queue = [] action = 'start' prevCmd = '' - layers = [] # Read in all commands for curCommand in obj.Base.Path.Commands: + #PathLog.debug("CurCMD: {}".format(curCommand)) if curCommand.Name not in movecommands + rapidcommands: # Don't worry about non-move commands, just add to output newpath.append(curCommand) @@ -410,14 +412,17 @@ class ObjectDressup: if curCommand.Name in movecommands: if prevCmd.Name in rapidcommands and curCommand.Name in movecommands and len(queue) > 0: - # Layer changed: Save current layer cmds prepare next layer + # Layer changed: Save current layer cmds and prepare next layer + layers.append(queue) + queue = [] + if obj.IncludeLayers and curCommand.z < currLocation['Z'] and prevCmd.Name in movecommands: + # Layer change within move cmds + #PathLog.debug("Layer change in move: {}->{}".format(currLocation['Z'], curCommand.z)) layers.append(queue) queue = [] - #print("New layer: {}".format(layers)) # Save all move commands queue.append(curCommand) - #print("Append move: {}, P: {}".format(curCommand.Name, curCommand.Parameters)) currLocation.update(curCommand.Parameters) prevCmd = curCommand @@ -426,26 +431,23 @@ class ObjectDressup: if len(queue) > 0: layers.append(queue) queue = [] - #print("New layer: {}".format(layers)) # Go through each layer and add leadIn/Out idx = 0 for layer in layers: - #print("Layer {}".format(idx)) + #PathLog.debug("Layer {}".format(idx)) if obj.LeadIn: - #print("Lead IN") temp = self.getLeadStart(obj, layer, action) newpath.extend(temp) for cmd in layer: - #print("CurLoc: {}, NewCmd: {}".format(currLocation, cmd)) + #PathLog.debug("CurLoc: {}, NewCmd: {}".format(currLocation, cmd)) #if currLocation['X'] == cmd.x and currLocation['Y'] == cmd.y and currLocation['Z'] == cmd.z and cmd.Name in ['G1', 'G01']: #continue newpath.append(cmd) if obj.LeadOut: - #print("Lead OUT") tmp = [] tmp.append(layer[-2]) tmp.append(layer[-1]) From 66fbec600595c173a84c1a179530f2ae08d065c4 Mon Sep 17 00:00:00 2001 From: Patrick Felixberger Date: Tue, 31 Mar 2020 17:31:30 +0200 Subject: [PATCH 12/12] Setup length depending on tool --- src/Mod/Path/PathScripts/PathDressupLeadInOut.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index f5ab6f8ca2..db5fdc2d21 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -86,7 +86,7 @@ class ObjectDressup: return None def setup(self, obj): - obj.Length = 5.0 + obj.Length = obj.Base.ToolController.Tool.Diameter * 0.75 obj.LeadIn = True obj.LeadOut = True obj.KeepToolDown = False