Download src/Mod/CAM/Path/Post/scripts/rml_post.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 8.06 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Post/scripts/rml_post.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/Path/Post/scripts/rml_post.py
-
curl -L -o rml_post.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Post/scripts/rml_post.py
8.06 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2015 Jon Nordby <jononor@gmail.com> * | |
| # * * | |
| # * This file is part of the FreeCAD CAx development system. * | |
| # * * | |
| # * This program is free software; you can redistribute it and/or modify * | |
| # * it under the terms of the GNU Lesser General Public License (LGPL) * | |
| # * as published by the Free Software Foundation; either version 2 of * | |
| # * the License, or (at your option) any later version. * | |
| # * for detail see the LICENCE text file. * | |
| # * * | |
| # * FreeCAD is distributed in the hope that it will be useful, * | |
| # * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| # * GNU Lesser General Public License for more details. * | |
| # * * | |
| # * You should have received a copy of the GNU Library General Public * | |
| # * License along with FreeCAD; if not, write to the Free Software * | |
| # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * | |
| # * USA * | |
| # * * | |
| # *************************************************************************** | |
| TOOLTIP = """ | |
| FreeCAD Path post-processor to output code for the Roland Modela MDX-## machines. | |
| The machine speaks RML-1, specified in 'Roland RML-1 Programming Guidelines' | |
| https://itp.nyu.edu/classes/cdp-spring2014/files/2014/09/RML1_Manual.pdf | |
| http://altlab.org/d/content/m/pangelo/ideas/rml_command_guide_en_v100.pdf | |
| The format has some overlap with HPGL: | |
| https://en.wikipedia.org/wiki/HPGL | |
| http://paulbourke.net/dataformats/hpgl/ | |
| """ | |
| import FreeCAD | |
| import Part | |
| import Path.Post.Utils as PostUtils | |
| from builtins import open as pyopen | |
| # Entrypoint used by FreeCAD | |
| def export(objectslist, filename, argstring): | |
| "Export objects as Roland Modela code." | |
| code = "" | |
| for obj in objectslist: | |
| code += convertobject(obj) | |
| gfile = pyopen(filename, "w") | |
| gfile.write(code) | |
| gfile.close() | |
| def convertobject(obj): | |
| gcode = obj.Path.toGCode() | |
| gcode = parse(gcode) | |
| return gcode | |
| def motoron(): | |
| return ["!MC1;"] | |
| def motoroff(): | |
| return ["!MC0;"] | |
| def home(): | |
| return ["H;"] | |
| def setjog(): | |
| # "!PZ%d,%d;",iz_down,iz_up); // set z down, jog | |
| return "" | |
| def addheader(): | |
| return ["PA;PA;"] # absolute positioning | |
| def addfooter(): | |
| return [] | |
| def mm2cord(mm): | |
| mm = float(mm) | |
| return int(40.0 * mm) | |
| def feed(x=None, y=None, z=None, state=None): | |
| c = [] | |
| if state is None: | |
| state = {} | |
| if x is not None: | |
| x = float(x) | |
| state["X"] = x | |
| if y is not None: | |
| y = float(y) | |
| state["Y"] = y | |
| if z is not None: | |
| z = float(z) | |
| state["Z"] = z | |
| if x is not None and y is not None and z is not None: | |
| # 3d motion | |
| c.append("Z%d,%d,%d;" % (mm2cord(x), mm2cord(y), mm2cord(z))) | |
| elif x is not None and y is not None: | |
| # 2d in XY plane | |
| c.append("PD%d,%d;" % (mm2cord(x), mm2cord(y))) | |
| elif z is not None: | |
| pass | |
| return c | |
| def jog(x=None, y=None, z=None, state=None): | |
| c = [] | |
| if state is None: | |
| state = {} | |
| if x is not None and y is not None: | |
| x, y = float(x), float(y) | |
| c.append("PU%d,%d;" % (mm2cord(x), mm2cord(y))) | |
| state["X"] = x | |
| state["Y"] = y | |
| if z is not None: | |
| z = float(z) | |
| c.append("PU;") | |
| state["Z"] = z | |
| return c | |
| def xyarc(args, state): | |
| # no native support in RML/Modela, convert to linear line segments | |
| c = [] | |
| lastPoint = FreeCAD.Vector(state["X"], state["Y"]) | |
| newPoint = FreeCAD.Vector(float(args["X"]), float(args["Y"])) | |
| centerOffset = FreeCAD.Vector(float(args["I"]), float(args["J"])) | |
| center = lastPoint + centerOffset | |
| radius = (center - lastPoint).Length | |
| xyNormal = FreeCAD.Vector(0, 0, 1) | |
| circle = Part.Circle(center, xyNormal, radius) | |
| p0 = circle.parameter(lastPoint) | |
| p1 = circle.parameter(newPoint) | |
| arc = Part.ArcOfCircle(circle, p0, p1) | |
| steps = 64 # specify max error instead? | |
| points = arc.discretize(steps) | |
| # consider direction? | |
| # print('p = Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(%f, %f), FreeCAD.Vector(0, 0, 1), %f), %f, %f)' % (center.x, center.y, radius, p0, p1)) | |
| for p in points: | |
| c += feed(p.x, p.y, state["Z"], state) | |
| return c | |
| def speed(xy=None, z=None, state=None): | |
| c = [] | |
| if state is None: | |
| state = {} | |
| print(xy, z, state) | |
| if xy is not None: | |
| xy = float(xy) | |
| if xy > 0.0 and xy != state["XYspeed"]: | |
| c.append("VS%.1f;" % xy) | |
| state["XYspeed"] = xy | |
| if z is not None: | |
| z = float(z) | |
| if z > 0.0 and z != state["Zspeed"]: | |
| c.append("!VZ%.1f;" % z) | |
| state["Zspeed"] = z | |
| return c | |
| def convertgcode(cmd, args, state): | |
| """Convert a single gcode command to equivalent Roland code""" | |
| if cmd == "G0": | |
| # jog | |
| return jog(args["X"], args["Y"], args["Z"], state) | |
| elif cmd == "G1": | |
| # linear feed | |
| c = [] | |
| # feedrate | |
| c += speed(xy=args["F"], z=args["F"], state=state) | |
| # motion | |
| c += feed(args["X"], args["Y"], args["Z"], state) | |
| return c | |
| elif cmd == "G2" or cmd == "G3": | |
| # arc feed | |
| c = [] | |
| # feedrate | |
| c += speed(xy=args["F"], state=state) | |
| # motion | |
| if args["X"] and args["Y"] and args["Z"]: | |
| # helical motion | |
| pass | |
| elif args["X"] and args["Y"]: | |
| # arc in plane | |
| c += xyarc(args, state) | |
| return c | |
| elif cmd == "G20": | |
| # inches mode | |
| raise ValueError("rml_post: Inches mode not supported") | |
| elif cmd == "G21": | |
| # millimeter mode | |
| return "" | |
| elif cmd == "G40": | |
| # tool compensation off | |
| return "" | |
| elif cmd == "G80": | |
| # cancel all cycles (drill normally) | |
| return "PU;" | |
| elif cmd == "G81": | |
| c = [] | |
| # feedrate | |
| c += speed(z=args["F"], state=state) | |
| # motion | |
| c += jog(args["X"], args["Y"], state=state) | |
| c += feed(args["X"], args["Y"], args["Z"], state) | |
| return c | |
| elif cmd == "G90": | |
| # absolute mode? | |
| return "" | |
| elif cmd == "G98": | |
| # feedrate | |
| return "" | |
| else: | |
| raise NotImplementedError("rml_post: GCode command %s not understood" % (cmd,)) | |
| def parse(inputstring): | |
| "parse(inputstring): returns a parsed output string" | |
| state = {"X": 0.0, "Y": 0.0, "Z": 0.0, "XYspeed": -1.0, "Zspeed": -1.0} | |
| output = [] | |
| # header | |
| output += addheader() | |
| output += motoron() | |
| output += speed(2.0, 1.0, state) # defaults | |
| # respect clearance height? | |
| # treat the input line by line | |
| lines = inputstring.split("\n") | |
| for line in lines: | |
| if not line: | |
| continue | |
| parsed = PostUtils.stringsplit(line) | |
| command = parsed["command"] | |
| print("cmd", line) | |
| try: | |
| if command: | |
| code = convertgcode(command, parsed, state) | |
| if not isinstance(code, list): | |
| code = [code] | |
| if len(code) and code[0]: | |
| output += code | |
| except NotImplementedError as e: | |
| print(e) | |
| # footer | |
| output += motoroff() | |
| output += home() | |
| output += addfooter() | |
| return "\n".join(output) | |
| # print (__name__ + " gcode postprocessor loaded.") | |