From 55f9e4d1dda22ff8867eb5c18310a969929a3d4e Mon Sep 17 00:00:00 2001 From: Christian Mesh Date: Sun, 12 Feb 2023 11:50:02 -0500 Subject: [PATCH] Apply Placement to simulated/exported GCode This changed is inspired by (and slightly refactors) the Array Path modifier. It currently takes the Placement XYZ and Z Angle into consideration. Future work could also implement rotating the path around the X and Y axes. --- src/Mod/Path/Path/Main/Gui/Inspect.py | 2 +- src/Mod/Path/Path/Main/Gui/Simulator.py | 7 +- src/Mod/Path/Path/Op/Gui/Array.py | 66 +------------- src/Mod/Path/Path/Post/UtilsParse.py | 3 +- .../Post/scripts/KineticNCBeamicon2_post.py | 3 +- .../Path/Path/Post/scripts/centroid_post.py | 3 +- .../Path/Path/Post/scripts/comparams_post.py | 3 +- src/Mod/Path/Path/Post/scripts/dumper_post.py | 3 +- src/Mod/Path/Path/Post/scripts/dxf_post.py | 3 +- .../Path/Post/scripts/dynapath_4060_post.py | 3 +- .../Path/Path/Post/scripts/dynapath_post.py | 3 +- src/Mod/Path/Path/Post/scripts/fablin_post.py | 3 +- src/Mod/Path/Path/Post/scripts/fanuc_post.py | 8 +- src/Mod/Path/Path/Post/scripts/grbl_post.py | 3 +- .../Path/Path/Post/scripts/heidenhain_post.py | 13 ++- src/Mod/Path/Path/Post/scripts/jtech_post.py | 3 +- .../Path/Path/Post/scripts/linuxcnc_post.py | 3 +- .../Path/Post/scripts/mach3_mach4_post.py | 3 +- src/Mod/Path/Path/Post/scripts/marlin_post.py | 3 +- src/Mod/Path/Path/Post/scripts/nccad_post.py | 3 +- .../Path/Path/Post/scripts/opensbp_post.py | 3 +- .../Path/Path/Post/scripts/philips_post.py | 3 +- .../Post/scripts/refactored_centroid_post.py | 1 + src/Mod/Path/Path/Post/scripts/rrf_post.py | 3 +- .../Path/Path/Post/scripts/smoothie_post.py | 3 +- src/Mod/Path/Path/Post/scripts/uccnc_post.py | 3 +- src/Mod/Path/PathScripts/PathUtils.py | 89 +++++++++++++++++++ 27 files changed, 151 insertions(+), 95 deletions(-) diff --git a/src/Mod/Path/Path/Main/Gui/Inspect.py b/src/Mod/Path/Path/Main/Gui/Inspect.py index e24e537817..fe4967ba03 100644 --- a/src/Mod/Path/Path/Main/Gui/Inspect.py +++ b/src/Mod/Path/Path/Main/Gui/Inspect.py @@ -182,7 +182,7 @@ class GCodeEditorDialog(QtGui.QDialog): cursor.setPosition(ep) endrow = cursor.blockNumber() - commands = self.PathObj.Commands + commands = PathUtils.getPathWithPlacement(self.PathObj).Commands # Derive the starting position for the first selected command prevX = prevY = prevZ = None diff --git a/src/Mod/Path/Path/Main/Gui/Simulator.py b/src/Mod/Path/Path/Main/Gui/Simulator.py index 430f12b559..888fd0205e 100644 --- a/src/Mod/Path/Path/Main/Gui/Simulator.py +++ b/src/Mod/Path/Path/Main/Gui/Simulator.py @@ -24,6 +24,7 @@ import FreeCAD import Path import Path.Base.Util as PathUtil import Path.Dressup.Utils as PathDressup +import PathScripts.PathUtils as PathUtils import Path.Main.Job as PathJob import PathGui import PathSimulator @@ -202,7 +203,7 @@ class PathSimulation: self.icmd = 0 self.curpos = FreeCAD.Placement(self.initialPos, self.stdrot) self.cutTool.Placement = self.curpos - self.opCommands = self.operation.Path.Commands + self.opCommands = PathUtils.getPathWithPlacement(self.operation).Commands def SimulateMill(self): self.job = self.jobs[self.taskForm.form.comboJobs.currentIndex()] @@ -258,7 +259,7 @@ class PathSimulation: return self.busy = True - cmd = self.operation.Path.Commands[self.icmd] + cmd = self.opCommands[self.icmd] pathSolid = None if cmd.Name in ["G0"]: @@ -302,7 +303,7 @@ class PathSimulation: self.icmd += 1 self.iprogress += 1 self.UpdateProgress() - if self.icmd >= len(self.operation.Path.Commands): + if self.icmd >= len(self.opCommands): self.ioperation += 1 if self.ioperation >= len(self.activeOps): self.EndSimulation() diff --git a/src/Mod/Path/Path/Op/Gui/Array.py b/src/Mod/Path/Path/Op/Gui/Array.py index b7daea3400..4388ed0135 100644 --- a/src/Mod/Path/Path/Op/Gui/Array.py +++ b/src/Mod/Path/Path/Op/Gui/Array.py @@ -24,6 +24,7 @@ import FreeCAD import FreeCADGui import Path import PathScripts +from PathScripts.PathUtils import rotatePath from Path.Dressup.Utils import toolController from PySide import QtCore import math @@ -285,69 +286,6 @@ class PathArray: else: self.baseList = [baseList] - def rotatePath(self, path, angle, centre): - """ - Rotates Path around given centre vector - Only X and Y is considered - """ - CmdMoveRapid = ["G0", "G00"] - CmdMoveStraight = ["G1", "G01"] - CmdMoveCW = ["G2", "G02"] - CmdMoveCCW = ["G3", "G03"] - CmdDrill = ["G73", "G81", "G82", "G83"] - CmdMoveArc = CmdMoveCW + CmdMoveCCW - CmdMove = CmdMoveStraight + CmdMoveArc - - commands = [] - ang = angle / 180 * math.pi - currX = 0 - currY = 0 - for cmd in path.Commands: - if ( - (cmd.Name in CmdMoveRapid) - or (cmd.Name in CmdMove) - or (cmd.Name in CmdDrill) - ): - params = cmd.Parameters - x = params.get("X") - if x is None: - x = currX - currX = x - y = params.get("Y") - if y is None: - y = currY - currY = y - - # "move" the centre to origin - x = x - centre.x - y = y - centre.y - - # rotation around origin: - nx = x * math.cos(ang) - y * math.sin(ang) - ny = y * math.cos(ang) + x * math.sin(ang) - - # "move" the centre back and update - params.update({"X": nx + centre.x, "Y": ny + centre.y}) - - # Arcs need to have the I and J params rotated as well - if cmd.Name in CmdMoveArc: - i = params.get("I") - if i is None: - i = 0 - j = params.get("J") - if j is None: - j = 0 - - ni = i * math.cos(ang) - j * math.sin(ang) - nj = j * math.cos(ang) + i * math.sin(ang) - params.update({"I": ni, "J": nj}) - - cmd.Parameters = params - commands.append(cmd) - newPath = Path.Path(commands) - - return newPath - # Private method def _calculateJitter(self, pos): """_calculateJitter(pos) ... @@ -474,7 +412,7 @@ class PathArray: ang = 360 if self.copies > 0: ang = self.angle / self.copies * (1 + i) - np = self.rotatePath(b.Path, ang, self.centre) + np = rotatePath(b.Path, ang, self.centre) output += np.toGCode() # return output diff --git a/src/Mod/Path/Path/Post/UtilsParse.py b/src/Mod/Path/Path/Post/UtilsParse.py index 906118e2ab..7158cb483e 100644 --- a/src/Mod/Path/Path/Post/UtilsParse.py +++ b/src/Mod/Path/Path/Post/UtilsParse.py @@ -34,6 +34,7 @@ from FreeCAD import Units import Path import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils def create_comment(values, comment_string): """Create a comment from a string using the correct comment symbol.""" @@ -354,7 +355,7 @@ def parse_a_path(values, pathobj): f"Tool Controller Vertical Rapid Values are unset{nl}" ) - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: # List of elements in the command, code, and params. outstring = [] diff --git a/src/Mod/Path/Path/Post/scripts/KineticNCBeamicon2_post.py b/src/Mod/Path/Path/Post/scripts/KineticNCBeamicon2_post.py index 1248eaf37b..1ca3f74e8c 100644 --- a/src/Mod/Path/Path/Post/scripts/KineticNCBeamicon2_post.py +++ b/src/Mod/Path/Path/Post/scripts/KineticNCBeamicon2_post.py @@ -40,6 +40,7 @@ import argparse import datetime import shlex from PathScripts import PathUtils +import PathScripts.PathUtils as PathUtils TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to @@ -345,7 +346,7 @@ def parse(pathobj): # if OUTPUT_COMMENTS: # out += linenumber() + "(" + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outstring = [] command = c.Name diff --git a/src/Mod/Path/Path/Post/scripts/centroid_post.py b/src/Mod/Path/Path/Post/scripts/centroid_post.py index b61899da5f..f14ac7ba43 100644 --- a/src/Mod/Path/Path/Post/scripts/centroid_post.py +++ b/src/Mod/Path/Path/Post/scripts/centroid_post.py @@ -28,6 +28,7 @@ import os import FreeCAD from FreeCAD import Units import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils import datetime import Path @@ -277,7 +278,7 @@ def parse(pathobj): # if OUTPUT_COMMENTS: # out += linenumber() + "(" + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: commandlist = [] # list of elements in the command, code and params. command = c.Name # command M or G code or comment string diff --git a/src/Mod/Path/Path/Post/scripts/comparams_post.py b/src/Mod/Path/Path/Post/scripts/comparams_post.py index d4b1af4018..1b4dda0412 100644 --- a/src/Mod/Path/Path/Post/scripts/comparams_post.py +++ b/src/Mod/Path/Path/Post/scripts/comparams_post.py @@ -23,6 +23,7 @@ import FreeCAD import Path import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils TOOLTIP = """Example Post, using Path.Commands instead of Path.toGCode strings for Path gcode output.""" @@ -95,7 +96,7 @@ def export(obj, filename, argstring): gcode += firstcommand.Name if hasattr(fp, "Path"): - for c in fp.Path.Commands: + for c in PathUtils.getPathWithPlacement(fp).Commands: gcode += lineout(c, oldvals, modal) + "\n" oldvals = saveVals(c) gcode += "M2\n" diff --git a/src/Mod/Path/Path/Post/scripts/dumper_post.py b/src/Mod/Path/Path/Post/scripts/dumper_post.py index 753c367f42..b3e6df4451 100644 --- a/src/Mod/Path/Path/Post/scripts/dumper_post.py +++ b/src/Mod/Path/Path/Post/scripts/dumper_post.py @@ -31,6 +31,7 @@ shows the dialog so you can see it. Useful for debugging, but not much else. """ import datetime import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils now = datetime.datetime.now() SHOW_EDITOR = True @@ -95,7 +96,7 @@ def parse(pathobj): out += "(Path: " + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: out += str(c) + "\n" return out diff --git a/src/Mod/Path/Path/Post/scripts/dxf_post.py b/src/Mod/Path/Path/Post/scripts/dxf_post.py index 3b1052ad7f..e169099e27 100644 --- a/src/Mod/Path/Path/Post/scripts/dxf_post.py +++ b/src/Mod/Path/Path/Post/scripts/dxf_post.py @@ -24,6 +24,7 @@ from __future__ import print_function import FreeCAD import Part import Path +import PathScripts.PathUtils as PathUtils import datetime import importDXF @@ -100,7 +101,7 @@ def parse(pathobj): # Gotta start somewhere. Assume 0,0,0 curPoint = FreeCAD.Vector(0, 0, 0) - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: Path.Log.debug("{} -> {}".format(curPoint, c)) if "Z" in c.Parameters: newparams = c.Parameters diff --git a/src/Mod/Path/Path/Post/scripts/dynapath_4060_post.py b/src/Mod/Path/Path/Post/scripts/dynapath_4060_post.py index a0de64f569..64732c9c8a 100644 --- a/src/Mod/Path/Path/Post/scripts/dynapath_4060_post.py +++ b/src/Mod/Path/Path/Post/scripts/dynapath_4060_post.py @@ -33,6 +33,7 @@ import argparse import datetime import shlex import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils TOOLTIP = """ This is a post processor file for the FreeCAD Path workbench. It is used to @@ -421,7 +422,7 @@ def parse(pathobj): if not hasattr(pathobj, "Path"): return out - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outstring = [] command = c.Name # Convert G54-G59 Fixture offsets to E01-E06 for Dynapath Delta Control diff --git a/src/Mod/Path/Path/Post/scripts/dynapath_post.py b/src/Mod/Path/Path/Post/scripts/dynapath_post.py index 5f1798d50a..a556844821 100644 --- a/src/Mod/Path/Path/Post/scripts/dynapath_post.py +++ b/src/Mod/Path/Path/Post/scripts/dynapath_post.py @@ -34,6 +34,7 @@ from __future__ import print_function import FreeCAD from FreeCAD import Units import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils import argparse import datetime import shlex @@ -325,7 +326,7 @@ def parse(pathobj): if OUTPUT_COMMENTS: out += linenumber() + "(Path: " + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outstring = [] command = c.Name outstring.append(command) diff --git a/src/Mod/Path/Path/Post/scripts/fablin_post.py b/src/Mod/Path/Path/Post/scripts/fablin_post.py index 0c106f17f1..d2e08fabb6 100644 --- a/src/Mod/Path/Path/Post/scripts/fablin_post.py +++ b/src/Mod/Path/Path/Post/scripts/fablin_post.py @@ -26,6 +26,7 @@ import datetime import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils now = datetime.datetime.now() @@ -253,7 +254,7 @@ def parse(pathobj): if OUTPUT_COMMENTS: out += linenumber() + "(Path: " + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outstring = [] command = c.Name diff --git a/src/Mod/Path/Path/Post/scripts/fanuc_post.py b/src/Mod/Path/Path/Post/scripts/fanuc_post.py index afb7b8244f..7660ed7676 100644 --- a/src/Mod/Path/Path/Post/scripts/fanuc_post.py +++ b/src/Mod/Path/Path/Post/scripts/fanuc_post.py @@ -30,6 +30,7 @@ import datetime import shlex import os.path import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to @@ -410,14 +411,15 @@ def parse(pathobj): "Tool Controller Vertical Rapid Values are unset" + "\n" ) - for index, c in enumerate(pathobj.Path.Commands): + commands = PathUtils.getPathWithPlacement(pathobj).Commands + for index, c in enumerate(commands): outstring = [] command = c.Name - if index + 1 == len(pathobj.Path.Commands): + if index + 1 == len(commands): nextcommand = "" else: - nextcommand = pathobj.Path.Commands[index + 1].Name + nextcommand = commands[index + 1].Name if adaptiveOp and c.Name in ["G0", "G00"]: if opHorizRapid and opVertRapid: diff --git a/src/Mod/Path/Path/Post/scripts/grbl_post.py b/src/Mod/Path/Path/Post/scripts/grbl_post.py index 5c27cd5fcf..c7f71c91ee 100755 --- a/src/Mod/Path/Path/Post/scripts/grbl_post.py +++ b/src/Mod/Path/Path/Post/scripts/grbl_post.py @@ -29,6 +29,7 @@ from FreeCAD import Units import Path import Path.Base.Util as PathUtil import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils import argparse import datetime import shlex @@ -481,7 +482,7 @@ def parse(pathobj): if OUTPUT_COMMENTS: out += linenumber() + "(Path: " + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outstring = [] command = c.Name diff --git a/src/Mod/Path/Path/Post/scripts/heidenhain_post.py b/src/Mod/Path/Path/Post/scripts/heidenhain_post.py index 418db4ea64..66e3f55ffb 100644 --- a/src/Mod/Path/Path/Post/scripts/heidenhain_post.py +++ b/src/Mod/Path/Path/Post/scripts/heidenhain_post.py @@ -22,6 +22,7 @@ import argparse import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils import Path import PathScripts import shlex @@ -361,12 +362,14 @@ def export(objectslist, filename, argstring): elif isinstance(obj.Proxy, Path.Op.Helix.ObjectHelix): Object_Kind = "HELIX" + commands = PathUtils.getPathWithPlacement(obj).Commands + # If used compensated path, store, recompute and diff when asked if hasattr(obj, "UseComp") and SOLVE_COMPENSATION_ACTIVE: if obj.UseComp: if hasattr(obj.Path, "Commands") and Object_Kind == "PROFILE": # Take a copy of compensated path - STORED_COMPENSATED_OBJ = obj.Path.Commands + STORED_COMPENSATED_OBJ = commands # Find mill compensation if hasattr(obj, "Side") and hasattr(obj, "Direction"): if obj.Side == "Outside" and obj.Direction == "CW": @@ -380,14 +383,16 @@ def export(objectslist, filename, argstring): # set obj.UseComp to false and recompute() to get uncompensated path obj.UseComp = False obj.recompute() + commands = PathUtils.getPathWithPlacement(obj).Commands # small edges could be skipped and movements joints can add edges NameStr = "" if hasattr(obj, "Label"): NameStr = str(obj.Label) - if len(obj.Path.Commands) != len(STORED_COMPENSATED_OBJ): + if len(commands) != len(STORED_COMPENSATED_OBJ): # not same number of edges obj.UseComp = True obj.recompute() + commands = PathUtils.getPathWithPlacement(obj).Commands POSTGCODE.append("; MISSING EDGES UNABLE TO GET COMPENSATION") if not SKIP_WARNS: ( @@ -422,7 +427,7 @@ def export(objectslist, filename, argstring): POSTGCODE.append("; COMPENSATION ACTIVE") COMPENSATION_DIFF_STATUS[0] = True - for c in obj.Path.Commands: + for c in commands: Cmd_Count += 1 command = c.Name if command != "G0": @@ -458,7 +463,7 @@ def export(objectslist, filename, argstring): Spindle_Status += str(MACHINE_SPINDLE_DIRECTION) # Activate spindle Spindle_Active = True else: # At last rapid movement we turn off spindle - if Cmd_Count == len(obj.Path.Commands): + if Cmd_Count == len(commands): Spindle_Status += "5" # Deactivate spindle Spindle_Active = False else: diff --git a/src/Mod/Path/Path/Post/scripts/jtech_post.py b/src/Mod/Path/Path/Post/scripts/jtech_post.py index 36e5b1d498..57521dba4b 100644 --- a/src/Mod/Path/Path/Post/scripts/jtech_post.py +++ b/src/Mod/Path/Path/Post/scripts/jtech_post.py @@ -29,6 +29,7 @@ import argparse import datetime import shlex import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to @@ -308,7 +309,7 @@ def parse(pathobj): if not hasattr(pathobj, "Path"): return out - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outstring = [] command = c.Name diff --git a/src/Mod/Path/Path/Post/scripts/linuxcnc_post.py b/src/Mod/Path/Path/Post/scripts/linuxcnc_post.py index d21eba1bad..17e79f9b19 100644 --- a/src/Mod/Path/Path/Post/scripts/linuxcnc_post.py +++ b/src/Mod/Path/Path/Post/scripts/linuxcnc_post.py @@ -29,6 +29,7 @@ import argparse import datetime import shlex import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to @@ -356,7 +357,7 @@ def parse(pathobj): # if OUTPUT_COMMENTS: # out += linenumber() + "(" + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outstring = [] command = c.Name diff --git a/src/Mod/Path/Path/Post/scripts/mach3_mach4_post.py b/src/Mod/Path/Path/Post/scripts/mach3_mach4_post.py index d4cb8ecb66..8b06ca1aeb 100644 --- a/src/Mod/Path/Path/Post/scripts/mach3_mach4_post.py +++ b/src/Mod/Path/Path/Post/scripts/mach3_mach4_post.py @@ -28,6 +28,7 @@ import argparse import datetime import shlex import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils TOOLTIP = """ This is a postprocessor file for the Path workbench. It is used to @@ -387,7 +388,7 @@ def parse(pathobj): "Tool Controller Vertical Rapid Values are unset" + "\n" ) - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outstring = [] command = c.Name diff --git a/src/Mod/Path/Path/Post/scripts/marlin_post.py b/src/Mod/Path/Path/Post/scripts/marlin_post.py index a948ac9df5..abebe51ae5 100644 --- a/src/Mod/Path/Path/Post/scripts/marlin_post.py +++ b/src/Mod/Path/Path/Post/scripts/marlin_post.py @@ -35,6 +35,7 @@ from FreeCAD import Units import Path import Path.Base.Util as PathUtil import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils Revised = "2020-11-03" # Revision date for this file. @@ -542,7 +543,7 @@ def parse(pathobj): if OUTPUT_COMMENTS and OUTPUT_PATH: out += linenumber() + "(Path: " + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outlist = [] command = c.Name outlist.append(command) diff --git a/src/Mod/Path/Path/Post/scripts/nccad_post.py b/src/Mod/Path/Path/Post/scripts/nccad_post.py index 7661c888e1..2b3791f88a 100644 --- a/src/Mod/Path/Path/Post/scripts/nccad_post.py +++ b/src/Mod/Path/Path/Post/scripts/nccad_post.py @@ -23,6 +23,7 @@ """Postprocessor to output real GCode for Max Computer GmbH nccad9.""" import FreeCAD import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils import datetime @@ -89,7 +90,7 @@ def export(objectslist, filename, argstring): gcode = HEADER for obj in objectslist: - for command in obj.Path.Commands: + for command in PathUtils.getPathWithPlacement(obj).Commands: # Manipulate tool change commands if "M6" == command.Name: gcode += TOOL_CHANGE.replace("TOOL", str(int(command.Parameters["T"]))) diff --git a/src/Mod/Path/Path/Post/scripts/opensbp_post.py b/src/Mod/Path/Path/Post/scripts/opensbp_post.py index 3d2e2af199..b1bbc23b8d 100644 --- a/src/Mod/Path/Path/Post/scripts/opensbp_post.py +++ b/src/Mod/Path/Path/Post/scripts/opensbp_post.py @@ -24,6 +24,7 @@ from __future__ import print_function import datetime import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils TOOLTIP = """ @@ -352,7 +353,7 @@ def parse(pathobj): return output if OUTPUT_COMMENTS: output += linenumber() + "'(Path: " + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: command = c.Name if command in scommands: output += scommands[command](c) diff --git a/src/Mod/Path/Path/Post/scripts/philips_post.py b/src/Mod/Path/Path/Post/scripts/philips_post.py index d6e57cee6d..d52cb6eb59 100644 --- a/src/Mod/Path/Path/Post/scripts/philips_post.py +++ b/src/Mod/Path/Path/Post/scripts/philips_post.py @@ -28,6 +28,7 @@ import FreeCAD import argparse import time import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils import math TOOLTIP = """Post processor for Maho M 600E mill @@ -416,7 +417,7 @@ def export(objectslist, filename, argstring): for obj in objectslist: if hasattr(obj, "Comment"): gcode += linenumberify("(" + obj.Comment + ")") - for c in obj.Path.Commands: + for c in PathUtils.getPathWithPlacement(obj).Commands: outstring = [] command = c.Name if command != "G0": diff --git a/src/Mod/Path/Path/Post/scripts/refactored_centroid_post.py b/src/Mod/Path/Path/Post/scripts/refactored_centroid_post.py index b326bdf13c..793e628fa0 100644 --- a/src/Mod/Path/Path/Post/scripts/refactored_centroid_post.py +++ b/src/Mod/Path/Path/Post/scripts/refactored_centroid_post.py @@ -29,6 +29,7 @@ from __future__ import print_function import Path.Post.UtilsArguments as PostUtilsArguments import Path.Post.UtilsExport as PostUtilsExport + # # The following variables need to be global variables # to keep the PathPostProcessor.load method happy: diff --git a/src/Mod/Path/Path/Post/scripts/rrf_post.py b/src/Mod/Path/Path/Post/scripts/rrf_post.py index 4d09d7b9ba..5dd9c9f158 100644 --- a/src/Mod/Path/Path/Post/scripts/rrf_post.py +++ b/src/Mod/Path/Path/Post/scripts/rrf_post.py @@ -34,6 +34,7 @@ from FreeCAD import Units import Path import Path.Base.Util as PathUtil import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils Revised = "2021-10-21" # Revision date for this file. @@ -533,7 +534,7 @@ def parse(pathobj): if OUTPUT_COMMENTS and OUTPUT_PATH: out += linenumber() + "(Path: " + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outlist = [] command = c.Name outlist.append(command) diff --git a/src/Mod/Path/Path/Post/scripts/smoothie_post.py b/src/Mod/Path/Path/Post/scripts/smoothie_post.py index 0429997475..0ac62b3f5e 100644 --- a/src/Mod/Path/Path/Post/scripts/smoothie_post.py +++ b/src/Mod/Path/Path/Post/scripts/smoothie_post.py @@ -26,6 +26,7 @@ from __future__ import print_function import argparse import datetime import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils import FreeCAD from FreeCAD import Units import shlex @@ -399,7 +400,7 @@ def parse(pathobj): # if OUTPUT_COMMENTS: # out += linenumber() + "(" + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: outstring = [] command = c.Name outstring.append(command) diff --git a/src/Mod/Path/Path/Post/scripts/uccnc_post.py b/src/Mod/Path/Path/Post/scripts/uccnc_post.py index 3a70425294..8a65e0babe 100644 --- a/src/Mod/Path/Path/Post/scripts/uccnc_post.py +++ b/src/Mod/Path/Path/Post/scripts/uccnc_post.py @@ -33,6 +33,7 @@ from __future__ import print_function import FreeCAD from FreeCAD import Units import Path +import PathScripts.PathUtils as PathUtils import argparse import datetime @@ -602,7 +603,7 @@ def parse(pathobj): # if OUTPUT_COMMENTS: # out += linenumber() + "(" + pathobj.Label + ")\n" - for c in pathobj.Path.Commands: + for c in PathUtils.getPathWithPlacement(pathobj).Commands: commandlist = [] # list of elements in the command, code and params. command = c.Name.strip() # command M or G code or comment string commandlist.append(command) diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index fb16c65ae6..03c4950148 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -838,3 +838,92 @@ def RtoIJ(startpoint, command): newcommand.Parameters = params return newcommand + +def rotatePath(path, angle, centre): + """ + Rotates Path around given centre vector + Only X and Y is considered + """ + CmdMoveRapid = ["G0", "G00"] + CmdMoveStraight = ["G1", "G01"] + CmdMoveCW = ["G2", "G02"] + CmdMoveCCW = ["G3", "G03"] + CmdDrill = ["G73", "G81", "G82", "G83"] + CmdMoveArc = CmdMoveCW + CmdMoveCCW + CmdMove = CmdMoveStraight + CmdMoveArc + + commands = [] + ang = angle / 180 * math.pi + currX = 0 + currY = 0 + for cmd in path.Commands: + if ( + (cmd.Name in CmdMoveRapid) + or (cmd.Name in CmdMove) + or (cmd.Name in CmdDrill) + ): + params = cmd.Parameters + x = params.get("X") + if x is None: + x = currX + currX = x + y = params.get("Y") + if y is None: + y = currY + currY = y + + # "move" the centre to origin + x = x - centre.x + y = y - centre.y + + # rotation around origin: + nx = x * math.cos(ang) - y * math.sin(ang) + ny = y * math.cos(ang) + x * math.sin(ang) + + # "move" the centre back and update + nx += centre.x + ny += centre.y + + if nx != currX: + params.update({"X": nx}) + if ny != currY: + params.update({"Y": ny}) + + # Arcs need to have the I and J params rotated as well + if cmd.Name in CmdMoveArc: + i = params.get("I") + if i is None: + i = 0 + j = params.get("J") + if j is None: + j = 0 + + ni = i * math.cos(ang) - j * math.sin(ang) + nj = j * math.cos(ang) + i * math.sin(ang) + + if ni != i: + params.update({"I": ni}) + if nj != j: + params.update({"J": nj}) + + cmd.Parameters = params + commands.append(cmd) + newPath = Path.Path(commands) + + return newPath + +def getPathWithPlacement(pathobj): + """ + This function takes the pathobj's Placement Position, along with + the Z rotation and computes a new path. + + A more "complete" version of this function would take the X and Y axis into + account for the rotation. Currently it is limited by the rotatePath + function that was refactored out of the Array Operation. + """ + placement = pathobj.Placement + xAngle, yAngle, zAngle = placement.Rotation.toEulerAngles('XYZ') + if xAngle != 0 or yAngle != 0: + Path.Log.warning("Invalid Path Placement: Rotations about X and Y axes are not currently supported and will be ignored") + commands = rotatePath(pathobj.Path, zAngle, FreeCAD.Vector(0, 0, 0)).Commands + return Path.Path([cm.transform(placement) for cm in commands])