From 2e8d5b3b962f36da802f06c3d98a857fabd76351 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sat, 23 May 2020 04:32:57 +0300 Subject: [PATCH] - added the Exclusion zones processing to Geometry GCode generation --- AppObjects/FlatCAMCNCJob.py | 19 +- App_Main.py | 2 +- CHANGELOG.md | 1 + camlib.py | 383 ++++++++++++++++++++---- preprocessors/Toolchange_Probe_MACH3.py | 4 +- 5 files changed, 346 insertions(+), 63 deletions(-) diff --git a/AppObjects/FlatCAMCNCJob.py b/AppObjects/FlatCAMCNCJob.py index c8655168..97ccf248 100644 --- a/AppObjects/FlatCAMCNCJob.py +++ b/AppObjects/FlatCAMCNCJob.py @@ -989,7 +989,6 @@ class CNCJobObject(FlatCAMObj, CNCjob): for key in self.cnc_tools: ppg = self.cnc_tools[key]['data']['ppname_g'] if 'toolchange_custom' not in str(ppg).lower(): - print(ppg) if self.ui.toolchange_cb.get_value(): self.ui.toolchange_cb.set_value(False) self.app.inform.emit('[WARNING_NOTCL] %s' % @@ -1107,7 +1106,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): except ValueError: # we may have a tuple with only one element and a comma dia_plot = [float(el) for el in self.options["tooldia"].split(',') if el != ''][0] - self.plot2(dia_plot, obj=self, visible=visible, kind=kind) + self.plot2(tooldia=dia_plot, obj=self, visible=visible, kind=kind) else: # multiple tools usage if self.cnc_tools: @@ -1117,12 +1116,16 @@ class CNCJobObject(FlatCAMObj, CNCjob): self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind) # TODO: until the gcode parsed will be stored on each Excellon tool this will not get executed - if self.exc_cnc_tools: - for tooldia_key in self.exc_cnc_tools: - tooldia = float('%.*f' % (self.decimals, float(tooldia_key))) - # gcode_parsed = self.cnc_tools[tooldia_key]['gcode_parsed'] - gcode_parsed = self.gcode_parsed - self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind) + # I do this so the travel lines thickness will reflect the tool diameter + # may work only for objects created within the app and not Gcode imported from elsewhere for which we + # don't know the origin + if self.origin_kind == "excellon": + if self.exc_cnc_tools: + for tooldia_key in self.exc_cnc_tools: + tooldia = float('%.*f' % (self.decimals, float(tooldia_key))) + # gcode_parsed = self.exc_cnc_tools[tooldia_key]['gcode_parsed'] + gcode_parsed = self.gcode_parsed + self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind) self.shapes.redraw() except (ObjectDeleted, AttributeError): diff --git a/App_Main.py b/App_Main.py index b17a5f06..dc72594a 100644 --- a/App_Main.py +++ b/App_Main.py @@ -14,8 +14,8 @@ import urllib.error import getopt import random import simplejson as json -import lzma import shutil +import lzma from datetime import datetime import time import ctypes diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ee25bf2..988af6e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG for FlatCAM beta 22.05.2020 - fixed the algorithm for calculating closest points in the Exclusion areas +- added the Exclusion zones processing to Geometry GCode generation 21.05.2020 diff --git a/camlib.py b/camlib.py index 492b86af..d8701b87 100644 --- a/camlib.py +++ b/camlib.py @@ -1103,15 +1103,17 @@ class Geometry(object): """ Imports shapes from an IMAGE file into the object's geometry. - :param filename: Path to the IMAGE file. - :type filename: str - :param flip: Flip the object vertically. - :type flip: bool - :param units: FlatCAM units - :param dpi: dots per inch on the imported image - :param mode: how to import the image: as 'black' or 'color' - :param mask: level of detail for the import - :return: None + :param filename: Path to the IMAGE file. + :type filename: str + :param flip: Flip the object vertically. + :type flip: bool + :param units: FlatCAM units + :type units: str + :param dpi: dots per inch on the imported image + :param mode: how to import the image: as 'black' or 'color' + :type mode: str + :param mask: level of detail for the import + :return: None """ if mask is None: mask = [128, 128, 128, 128] @@ -1985,7 +1987,7 @@ class Geometry(object): it again in descendents. :param obj_units: "IN" or "MM" - :type units: str + :type obj_units: str :return: Scaling factor resulting from unit change. :rtype: float """ @@ -2550,9 +2552,23 @@ class CNCjob(Geometry): @property def postdata(self): + """ + This will return all the attributes of the class in the form of a dictionary + + :return: Class attributes + :rtype: dict + """ return self.__dict__ def convert_units(self, units): + """ + Will convert the parameters in the class that are relevant, from metric to imperial and reverse + + :param units: FlatCAM units + :type units: str + :return: conversion factor + :rtype: float + """ log.debug("camlib.CNCJob.convert_units()") factor = Geometry.convert_units(self, units) @@ -2573,6 +2589,17 @@ class CNCjob(Geometry): return self.doformat2(fun, **kwargs) + "\n" def doformat2(self, fun, **kwargs): + """ + This method will call one of the current preprocessor methods having as parameters all the attributes of + current class to which will add the kwargs parameters + + :param fun: One of the methods inside the preprocessor classes which get loaded here in the 'p' object + :type fun: class 'function' + :param kwargs: keyword args which will update attributes of the current class + :type kwargs: dict + :return: Gcode line + :rtype: str + """ attributes = AttrDict() attributes.update(self.postdata) attributes.update(kwargs) @@ -2584,6 +2611,16 @@ class CNCjob(Geometry): return '' def parse_custom_toolchange_code(self, data): + """ + Will parse a text and get a toolchange sequence in text format suitable to be included in a Gcode file. + The '%' symbol is used to surround class variables name and must be removed in the returned string. + After that, the class variables (attributes) are replaced with the current values. The result is returned. + + :param data: Toolchange sequence + :type data: str + :return: Processed toolchange sequence + :rtype: str + """ text = data match_list = self.re_toolchange_custom.findall(text) @@ -2615,6 +2652,13 @@ class CNCjob(Geometry): [2, 3], [2, 4], [3, 4], [3, 3], [3, 2], [3, 1], [3, 0], [4, 0], [4, 1], [4, 2], [4, 3], [4, 4]] >>> optimized_travelling_salesman([[0,0],[10,0],[6,0]]) [[0, 0], [6, 0], [10, 0]] + + :param points: List of tuples with x, y coordinates + :type points: list + :param start: a tuple with a x,y coordinates of the start point + :type start: tuple + :return: List of points ordered in a optimized way + :rtype: list """ if start is None: @@ -3899,7 +3943,9 @@ class CNCjob(Geometry): # calculate the cut distance total_cut = total_cut + geo.length - self.gcode += self.create_gcode_single_pass(geo, extracut, extracut_length, tolerance, + self.gcode += self.create_gcode_single_pass(geo, current_tooldia, extracut, extracut_length, + tolerance, + z_move=z_move, postproc=p, old_point=current_pt) # --------- Multi-pass --------- @@ -3914,8 +3960,10 @@ class CNCjob(Geometry): total_cut += (geo.length * nr_cuts) - self.gcode += self.create_gcode_multi_pass(geo, extracut, extracut_length, tolerance, - postproc=p, old_point=current_pt) + self.gcode += self.create_gcode_multi_pass(geo, current_tooldia, extracut, extracut_length, + tolerance, + z_move=z_move, postproc=p, + old_point=current_pt) # calculate the total distance total_travel = total_travel + abs(distance(pt1=current_pt, pt2=pt)) @@ -4216,20 +4264,24 @@ class CNCjob(Geometry): # this is the tool diameter, it is used as such to accommodate the preprocessor who need the tool diameter # given under the name 'toolC' + # this is a fancy way of adding a class attribute (which should be added in the __init__ method) without doing + # it there :) self.postdata['toolC'] = self.tooldia # Initial G-Code self.pp_geometry = self.app.preprocessors[self.pp_geometry_name] + + # the 'p' local attribute is a reference to the current preprocessor class p = self.pp_geometry self.oldx = 0.0 self.oldy = 0.0 self.gcode = self.doformat(p.start_code) - self.gcode += self.doformat(p.feedrate_code) # sets the feed rate if toolchange is False: + # all the x and y parameters in self.doformat() are used only by some preprocessors not by all self.gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height self.gcode += self.doformat(p.startz_code, x=self.oldx, y=self.oldy) @@ -4277,6 +4329,9 @@ class CNCjob(Geometry): path_count = 0 current_pt = (0, 0) pt, geo = storage.nearest(current_pt) + + # when nothing is left in the storage a StopIteration exception will be raised therefore stopping + # the whole process including the infinite loop while True below. try: while True: if self.app.abort_flag: @@ -4297,7 +4352,9 @@ class CNCjob(Geometry): if not multidepth: # calculate the cut distance total_cut += geo.length - self.gcode += self.create_gcode_single_pass(geo, extracut, self.extracut_length, tolerance, + self.gcode += self.create_gcode_single_pass(geo, current_tooldia, extracut, self.extracut_length, + tolerance, + z_move=z_move, postproc=p, old_point=current_pt) # --------- Multi-pass --------- @@ -4312,8 +4369,10 @@ class CNCjob(Geometry): total_cut += (geo.length * nr_cuts) - self.gcode += self.create_gcode_multi_pass(geo, extracut, self.extracut_length, tolerance, - postproc=p, old_point=current_pt) + self.gcode += self.create_gcode_multi_pass(geo, current_tooldia, extracut, self.extracut_length, + tolerance, + z_move=z_move,_postproc=p, + old_point=current_pt) # calculate the travel distance total_travel += abs(distance(pt1=current_pt, pt2=pt)) @@ -4321,6 +4380,7 @@ class CNCjob(Geometry): pt, geo = storage.nearest(current_pt) # Next + # update the activity counter (lower left side of the app, status bar) disp_number = int(np.interp(path_count, [0, geo_len], [0, 100])) if old_disp_number < disp_number <= 100: self.app.proc_container.update_view_text(' %d%%' % disp_number) @@ -4535,27 +4595,76 @@ class CNCjob(Geometry): gcode += self.doformat(p.lift_code) return gcode - def create_gcode_single_pass(self, geometry, extracut, extracut_length, tolerance, old_point=(0, 0)): + def create_gcode_single_pass(self, geometry, cdia, extracut, extracut_length, tolerance, z_move, postproc, + old_point=(0, 0)): + """ # G-code. Note: self.linear2gcode() and self.point2gcode() will lower and raise the tool every time. + :param geometry: A Shapely Geometry (LineString or LinearRing) which is the path to be cut + :type geometry: LineString, LinearRing + :param cdia: Tool diameter + :type cdia: float + :param extracut: Will add an extra cut over the point where start of the cut is met with the end cut + :type extracut: bool + :param extracut_length: The length of the extra cut: half before the meeting point, half after + :type extracut_length: float + :param tolerance: Tolerance used to simplify the paths (making them mre rough) + :type tolerance: float + :param z_move: Travel Z + :type z_move: float + :param postproc: Preprocessor class + :type postproc: class + :param old_point: Previous point + :type old_point: tuple + :return: Gcode + :rtype: str + """ + # p = postproc + if type(geometry) == LineString or type(geometry) == LinearRing: if extracut is False: - gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, old_point=old_point) + gcode_single_pass = self.linear2gcode(geometry, z_move=z_move, dia=cdia, tolerance=tolerance, + old_point=old_point) else: if geometry.is_ring: gcode_single_pass = self.linear2gcode_extra(geometry, extracut_length, tolerance=tolerance, + z_move=z_move, dia=cdia, old_point=old_point) else: - gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, old_point=old_point) + gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, z_move=z_move, dia=cdia, + old_point=old_point) elif type(geometry) == Point: - gcode_single_pass = self.point2gcode(geometry) + gcode_single_pass = self.point2gcode(geometry, dia=cdia, z_move=z_move, old_point=old_point) else: log.warning("G-code generation not implemented for %s" % (str(type(geometry)))) return return gcode_single_pass - def create_gcode_multi_pass(self, geometry, extracut, extracut_length, tolerance, postproc, old_point=(0, 0)): + def create_gcode_multi_pass(self, geometry, cdia, extracut, extracut_length, tolerance, postproc, z_move, + old_point=(0, 0)): + """ + + :param geometry: A Shapely Geometry (LineString or LinearRing) which is the path to be cut + :type geometry: LineString, LinearRing + :param cdia: Tool diameter + :type cdia: float + :param extracut: Will add an extra cut over the point where start of the cut is met with the end cut + :type extracut: bool + :param extracut_length: The length of the extra cut: half before the meeting point, half after + :type extracut_length: float + :param tolerance: Tolerance used to simplify the paths (making them mre rough) + :type tolerance: float + :param postproc: Preprocessor class + :type postproc: class + :param z_move: Travel Z + :type z_move: float + :param old_point: Previous point + :type old_point: tuple + :return: Gcode + :rtype: str + """ + p = postproc gcode_multi_pass = '' @@ -4585,18 +4694,20 @@ class CNCjob(Geometry): if type(geometry) == LineString or type(geometry) == LinearRing: if extracut is False: gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False, - old_point=old_point) + z_move=z_move, dia=cdia, old_point=old_point) else: if geometry.is_ring: gcode_multi_pass += self.linear2gcode_extra(geometry, extracut_length, tolerance=tolerance, - z_cut=depth, up=False, old_point=old_point) + dia=cdia, z_move=z_move, z_cut=depth, up=False, + old_point=old_point) else: gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False, + dia=cdia, z_move=z_move, old_point=old_point) # Ignore multi-pass for points. elif type(geometry) == Point: - gcode_multi_pass += self.point2gcode(geometry, old_point=old_point) + gcode_multi_pass += self.point2gcode(geometry, dia=cdia, z_move=z_move, old_point=old_point) break # Ignoring ... else: log.warning("G-code generation not implemented for %s" % (str(type(geometry)))) @@ -4612,7 +4723,7 @@ class CNCjob(Geometry): geometry.coords = list(geometry.coords)[::-1] # Lift the tool - gcode_multi_pass += self.doformat(postproc.lift_code, x=old_point[0], y=old_point[1]) + gcode_multi_pass += self.doformat(p.lift_code, x=old_point[0], y=old_point[1]) return gcode_multi_pass def codes_split(self, gline): @@ -4620,8 +4731,10 @@ class CNCjob(Geometry): Parses a line of G-Code such as "G01 X1234 Y987" into a dictionary: {'G': 1.0, 'X': 1234.0, 'Y': 987.0} - :param gline: G-Code line string - :return: Dictionary with parsed line. + :param gline: G-Code line string + :type gline: str + :return: Dictionary with parsed line. + :rtype: dict """ command = {} @@ -4690,6 +4803,18 @@ class CNCjob(Geometry): G-Code parser (from self.gcode). Generates dictionary with single-segment LineString's and "kind" indicating cut or travel, fast or feedrate speed. + + Will return a dict in the format: + { + "geom": LineString(path), + "kind": kind + } + where kind can be either ["C", "F"] # T=travel, C=cut, F=fast, S=slow + + :param force_parsing: + :type force_parsing: + :return: + :rtype: dict """ kind = ["C", "F"] # T=travel, C=cut, F=fast, S=slow @@ -4879,16 +5004,28 @@ class CNCjob(Geometry): """ Plots the G-code job onto the given axes. - :param tooldia: Tool diameter. - :param dpi: Not used! - :param margin: Not used! - :param color: Color specification. - :param alpha: Transparency specification. - :param tool_tolerance: Tolerance when drawing the toolshape. - :param obj - :param visible - :param kind - :return: None + :param tooldia: Tool diameter. + :type tooldia: float + :param dpi: Not used! + :type dpi: float + :param margin: Not used! + :type margin: float + :param gcode_parsed: Parsed Gcode + :type gcode_parsed: str + :param color: Color specification. + :type color: str + :param alpha: Transparency specification. + :type alpha: dict + :param tool_tolerance: Tolerance when drawing the toolshape. + :type tool_tolerance: float + :param obj: The object for whih to plot + :type obj: class + :param visible: Visibility status + :type visible: bool + :param kind: Can be: "travel", "cut", "all" + :type kind: str + :return: None + :rtype: """ # units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() @@ -5045,9 +5182,17 @@ class CNCjob(Geometry): log.debug("CNCJob.plot2() --> annotations --> %s" % str(e)) def create_geometry(self): - self.app.inform.emit('%s: %s' % (_("Unifying Geometry from parsed Geometry segments"), - str(len(self.gcode_parsed)))) + """ + It is used by the Excellon objects. Will create the solid_geometry which will be an attribute of the + Excellon object class. + + :return: List of Shapely geometry elements + :rtype: list + """ + # TODO: This takes forever. Too much data? + # self.app.inform.emit('%s: %s' % (_("Unifying Geometry from parsed Geometry segments"), + # str(len(self.gcode_parsed)))) # self.solid_geometry = cascaded_union([geo['geom'] for geo in self.gcode_parsed]) # This is much faster but not so nice to look at as you can see different segments of the geometry @@ -5055,10 +5200,15 @@ class CNCjob(Geometry): return self.solid_geometry - # code snippet added by Lei Zheng in a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ def segment(self, coords): """ - break long linear lines to make it more auto level friendly + Break long linear lines to make it more auto level friendly. + Code snippet added by Lei Zheng in a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ + + :param coords: List of coordinates tuples + :type coords: list + :return: A path; list with the multiple coordinates breaking a line. + :rtype: list """ if len(coords) < 2 or self.segx <= 0 and self.segy <= 0: @@ -5110,7 +5260,7 @@ class CNCjob(Geometry): return path - def linear2gcode(self, linear, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None, + def linear2gcode(self, linear, dia, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None, feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False, old_point=(0, 0)): """ @@ -5118,6 +5268,8 @@ class CNCjob(Geometry): :param linear: The path to cut along. :type: Shapely.LinearRing or Shapely.Linear String + :param dia: The tool diameter that is going on the path + :type dia: float :param tolerance: All points in the simplified object will be within the tolerance distance of the original geometry. :type tolerance: float @@ -5177,7 +5329,42 @@ class CNCjob(Geometry): # Move fast to 1st point if not cont: - gcode += self.doformat(p.rapid_code, x=first_x, y=first_y) # Move to first point + current_tooldia = dia + travels = self.app.exc_areas.travel_coordinates(start_point=(old_point[0], old_point[1]), + end_point=(first_x, first_y), + tooldia=current_tooldia) + prev_z = None + for travel in travels: + locx = travel[1][0] + locy = travel[1][1] + + if travel[0] is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # raise to safe Z (travel[0]) each time because safe Z may be different + self.z_move = travel[0] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + + # restore z_move + self.z_move = z_move + else: + if prev_z is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # we assume that previously the z_move was altered therefore raise to + # the travel_z (z_move) + self.z_move = z_move + gcode += self.doformat(p.lift_code, x=locx, y=locy) + else: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # store prev_z + prev_z = travel[0] + + # gcode += self.doformat(p.rapid_code, x=first_x, y=first_y) # Move to first point # Move down to cutting depth if down: @@ -5216,7 +5403,7 @@ class CNCjob(Geometry): gcode += self.doformat(p.lift_code, x=prev_x, y=prev_y, z_move=z_move) # Stop cutting return gcode - def linear2gcode_extra(self, linear, extracut_length, tolerance=0, down=True, up=True, + def linear2gcode_extra(self, linear, dia, extracut_length, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None, feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False, old_point=(0, 0)): """ @@ -5225,6 +5412,8 @@ class CNCjob(Geometry): :param linear: The path to cut along. :type: Shapely.LinearRing or Shapely.Linear String + :param dia: The tool diameter that is going on the path + :type dia: float :param extracut_length: how much to cut extra over the first point at the end of the path :param tolerance: All points in the simplified object will be within the tolerance distance of the original geometry. @@ -5284,7 +5473,42 @@ class CNCjob(Geometry): # Move fast to 1st point if not cont: - gcode += self.doformat(p.rapid_code, x=first_x, y=first_y) # Move to first point + current_tooldia = dia + travels = self.app.exc_areas.travel_coordinates(start_point=(old_point[0], old_point[1]), + end_point=(first_x, first_y), + tooldia=current_tooldia) + prev_z = None + for travel in travels: + locx = travel[1][0] + locy = travel[1][1] + + if travel[0] is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # raise to safe Z (travel[0]) each time because safe Z may be different + self.z_move = travel[0] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + + # restore z_move + self.z_move = z_move + else: + if prev_z is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # we assume that previously the z_move was altered therefore raise to + # the travel_z (z_move) + self.z_move = z_move + gcode += self.doformat(p.lift_code, x=locx, y=locy) + else: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # store prev_z + prev_z = travel[0] + + # gcode += self.doformat(p.rapid_code, x=first_x, y=first_y) # Move to first point # Move down to cutting depth if down: @@ -5450,7 +5674,20 @@ class CNCjob(Geometry): return gcode - def point2gcode(self, point, old_point=(0, 0)): + def point2gcode(self, point, dia, z_move=None, old_point=(0, 0)): + """ + + :param point: A Shapely Point + :type point: Point + :param dia: The tool diameter that is going on the path + :type dia: float + :param z_move: Travel Z + :type z_move: float + :param old_point: Old point coordinates from which we moved to the 'point' + :type old_point: tuple + :return: G-code to cut on the Point feature. + :rtype: str + """ gcode = "" if self.app.abort_flag: @@ -5474,7 +5711,42 @@ class CNCjob(Geometry): first_x = path[0][0] first_y = path[0][1] - gcode += self.doformat(p.linear_code, x=first_x, y=first_y) # Move to first point + current_tooldia = dia + travels = self.app.exc_areas.travel_coordinates(start_point=(old_point[0], old_point[1]), + end_point=(first_x, first_y), + tooldia=current_tooldia) + prev_z = None + for travel in travels: + locx = travel[1][0] + locy = travel[1][1] + + if travel[0] is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # raise to safe Z (travel[0]) each time because safe Z may be different + self.z_move = travel[0] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + + # restore z_move + self.z_move = z_move + else: + if prev_z is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # we assume that previously the z_move was altered therefore raise to + # the travel_z (z_move) + self.z_move = z_move + gcode += self.doformat(p.lift_code, x=locx, y=locy) + else: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # store prev_z + prev_z = travel[0] + + # gcode += self.doformat(p.linear_code, x=first_x, y=first_y) # Move to first point if self.z_feedrate is not None: gcode += self.doformat(p.z_feedrate_code) @@ -5490,8 +5762,10 @@ class CNCjob(Geometry): """ Exports the CNC Job as a SVG Element - :scale_factor: float - :return: SVG Element string + :param scale_stroke_factor: A factor to scale the SVG geometry + :type scale_stroke_factor: float + :return: SVG Element string + :rtype: str """ # scale_factor is a multiplication factor for the SVG stroke-width used within shapely's svg export # If not specified then try and use the tool diameter @@ -5511,6 +5785,9 @@ class CNCjob(Geometry): # This way we can add different formatting / colors to both cuts = [] travels = [] + cutsgeom = '' + travelsgeom = '' + for g in self.gcode_parsed: if self.app.abort_flag: # graceful abort requested by the user @@ -5548,10 +5825,12 @@ class CNCjob(Geometry): def bounds(self, flatten=None): """ - Returns coordinates of rectangular bounds - of geometry: (xmin, ymin, xmax, ymax). + Returns coordinates of rectangular bounds of geometry: (xmin, ymin, xmax, ymax). :param flatten: Not used, it is here for compatibility with base class method + :type flatten: bool + :return: Bounding values in format (xmin, ymin, xmax, ymax) + :rtype: tuple """ log.debug("camlib.CNCJob.bounds()") diff --git a/preprocessors/Toolchange_Probe_MACH3.py b/preprocessors/Toolchange_Probe_MACH3.py index 1234226d..4872b12b 100644 --- a/preprocessors/Toolchange_Probe_MACH3.py +++ b/preprocessors/Toolchange_Probe_MACH3.py @@ -106,10 +106,10 @@ class Toolchange_Probe_MACH3(PreProc): return g def lift_code(self, p): - return 'G00 Z' + self.coordinate_format%(p.coords_decimals, p.z_move) + return 'G00 Z' + self.coordinate_format % (p.coords_decimals, p.z_move) def down_code(self, p): - return 'G01 Z' + self.coordinate_format%(p.coords_decimals, p.z_cut) + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, p.z_cut) def toolchange_code(self, p): z_toolchange = p.z_toolchange