Procházet zdrojové kódy

- updated the camlib.CNCJob.scale() function so now the GCode is scaled also (quite a HACK :( it will need to be replaced at some point)). Units change work now on the GCODE also.

Marius Stanciu před 7 roky
rodič
revize
d5853722c3
4 změnil soubory, kde provedl 248 přidání a 46 odebrání
  1. 2 2
      FlatCAMApp.py
  2. 112 37
      FlatCAMObj.py
  3. 1 0
      README.md
  4. 133 7
      camlib.py

+ 2 - 2
FlatCAMApp.py

@@ -2652,9 +2652,9 @@ class App(QtCore.QObject):
                       'excellon_travelz', 'excellon_feedrate', 'excellon_feedrate_rapid', 'excellon_toolchangez',
                       'excellon_travelz', 'excellon_feedrate', 'excellon_feedrate_rapid', 'excellon_toolchangez',
                       'excellon_tooldia', 'excellon_endz', 'cncjob_tooldia',
                       'excellon_tooldia', 'excellon_endz', 'cncjob_tooldia',
                       'geometry_cutz', 'geometry_travelz', 'geometry_feedrate', 'geometry_feedrate_rapid',
                       'geometry_cutz', 'geometry_travelz', 'geometry_feedrate', 'geometry_feedrate_rapid',
-                      'geometry_cnctooldia', 'geometry_painttooldia', 'geometry_paintoverlap', 'geometry_toolchangexy',
+                      'geometry_cnctooldia', 'tools_painttooldia', 'tools_paintoverlap', 'geometry_toolchangexy',
                       'geometry_toolchangez',
                       'geometry_toolchangez',
-                      'geometry_paintmargin', 'geometry_endz', 'geometry_depthperpass', 'global_gridx', 'global_gridy']
+                      'tools_paintmargin', 'geometry_endz', 'geometry_depthperpass', 'global_gridx', 'global_gridy']
 
 
         def scale_options(sfactor):
         def scale_options(sfactor):
             for dim in dimensions:
             for dim in dimensions:

+ 112 - 37
FlatCAMObj.py

@@ -9,7 +9,7 @@
 from io import StringIO
 from io import StringIO
 from PyQt5 import QtCore, QtGui
 from PyQt5 import QtCore, QtGui
 from PyQt5.QtCore import Qt
 from PyQt5.QtCore import Qt
-from copy import copy, deepcopy
+import copy
 import inspect  # TODO: For debugging only.
 import inspect  # TODO: For debugging only.
 from shapely.geometry.base import JOIN_STYLE
 from shapely.geometry.base import JOIN_STYLE
 from datetime import datetime
 from datetime import datetime
@@ -165,7 +165,7 @@ class FlatCAMObj(QtCore.QObject):
         self.muted_ui = False
         self.muted_ui = False
 
 
     def on_name_activate(self):
     def on_name_activate(self):
-        old_name = copy(self.options["name"])
+        old_name = copy.copy(self.options["name"])
         new_name = self.ui.name_entry.get_value()
         new_name = self.ui.name_entry.get_value()
 
 
         # update the SHELL auto-completer model data
         # update the SHELL auto-completer model data
@@ -1870,7 +1870,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 if not isinstance(geo, FlatCAMGerber) and not isinstance(geo, FlatCAMExcellon):
                 if not isinstance(geo, FlatCAMGerber) and not isinstance(geo, FlatCAMExcellon):
                     for tool_uid in geo.tools:
                     for tool_uid in geo.tools:
                         max_uid += 1
                         max_uid += 1
-                        geo_final.tools[max_uid] = dict(geo.tools[tool_uid])
+                        geo_final.tools[max_uid] = copy.deepcopy(geo.tools[tool_uid])
 
 
     @staticmethod
     @staticmethod
     def get_pts(o):
     def get_pts(o):
@@ -2208,7 +2208,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                     'type': 'Rough',
                     'type': 'Rough',
                     'tool_type': 'C1',
                     'tool_type': 'C1',
                     'data': self.default_data,
                     'data': self.default_data,
-                    'solid_geometry': []
+                    'solid_geometry': self.solid_geometry
                 }
                 }
             })
             })
         else:
         else:
@@ -2220,12 +2220,12 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             temp_tools = {}
             temp_tools = {}
             new_key = 0.0
             new_key = 0.0
             for tooluid_key in self.tools:
             for tooluid_key in self.tools:
-                val =  dict(self.tools[tooluid_key])
-                new_key = deepcopy(int(tooluid_key))
+                val =  copy.deepcopy(self.tools[tooluid_key])
+                new_key = copy.deepcopy(int(tooluid_key))
                 temp_tools[new_key] = val
                 temp_tools[new_key] = val
 
 
             self.tools.clear()
             self.tools.clear()
-            self.tools = dict(temp_tools)
+            self.tools = copy.deepcopy(temp_tools)
 
 
         self.ui.tool_offset_entry.hide()
         self.ui.tool_offset_entry.hide()
         self.ui.tool_offset_lbl.hide()
         self.ui.tool_offset_lbl.hide()
@@ -2435,8 +2435,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                     'offset_value': 0.0,
                     'offset_value': 0.0,
                     'type': 'Rough',
                     'type': 'Rough',
                     'tool_type': 'C1',
                     'tool_type': 'C1',
-                    'data': dict(self.default_data),
-                    'solid_geometry': []
+                    'data': copy.deepcopy(self.default_data),
+                    'solid_geometry': self.solid_geometry
                 }
                 }
             })
             })
         else:
         else:
@@ -2448,6 +2448,11 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             last_tool_type = self.tools[max_uid]['tool_type']
             last_tool_type = self.tools[max_uid]['tool_type']
             last_solid_geometry = self.tools[max_uid]['solid_geometry']
             last_solid_geometry = self.tools[max_uid]['solid_geometry']
 
 
+            # if previous geometry was empty (it may happen for the first tool added)
+            # then copy the object.solid_geometry
+            if not last_solid_geometry:
+                last_solid_geometry = self.solid_geometry
+
             self.tools.update({
             self.tools.update({
                 self.tooluid: {
                 self.tooluid: {
                     'tooldia': tooldia,
                     'tooldia': tooldia,
@@ -2455,8 +2460,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                     'offset_value': last_offset_value,
                     'offset_value': last_offset_value,
                     'type': last_type,
                     'type': last_type,
                     'tool_type': last_tool_type,
                     'tool_type': last_tool_type,
-                    'data': dict(last_data),
-                    'solid_geometry': deepcopy(last_solid_geometry)
+                    'data': copy.deepcopy(last_data),
+                    'solid_geometry': copy.deepcopy(last_solid_geometry)
                 }
                 }
             })
             })
             # print("CURRENT", self.tools[-1])
             # print("CURRENT", self.tools[-1])
@@ -2497,7 +2502,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                         tooluid_copy = int(self.ui.geo_tools_table.item(current_row.row(), 5).text())
                         tooluid_copy = int(self.ui.geo_tools_table.item(current_row.row(), 5).text())
                         self.set_tool_offset_visibility(current_row.row())
                         self.set_tool_offset_visibility(current_row.row())
                         max_uid += 1
                         max_uid += 1
-                        self.tools[int(max_uid)] = dict(self.tools[tooluid_copy])
+                        self.tools[int(max_uid)] = copy.deepcopy(self.tools[tooluid_copy])
                     except AttributeError:
                     except AttributeError:
                         self.app.inform.emit("[warning_notcl]Failed. Select a tool to copy.")
                         self.app.inform.emit("[warning_notcl]Failed. Select a tool to copy.")
                         self.build_ui()
                         self.build_ui()
@@ -2513,10 +2518,10 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         else:
         else:
             # we copy all tools in geo_tools_table
             # we copy all tools in geo_tools_table
             try:
             try:
-                temp_tools = dict(self.tools)
+                temp_tools = copy.deepcopy(self.tools)
                 max_uid += 1
                 max_uid += 1
                 for tooluid in temp_tools:
                 for tooluid in temp_tools:
-                    self.tools[int(max_uid)] = dict(temp_tools[tooluid])
+                    self.tools[int(max_uid)] = copy.deepcopy(temp_tools[tooluid])
                 temp_tools.clear()
                 temp_tools.clear()
             except Exception as e:
             except Exception as e:
                 log.debug("on_tool_copy() --> " + str(e))
                 log.debug("on_tool_copy() --> " + str(e))
@@ -2570,11 +2575,11 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                         tooluid_del = int(self.ui.geo_tools_table.item(current_row.row(), 5).text())
                         tooluid_del = int(self.ui.geo_tools_table.item(current_row.row(), 5).text())
                         self.set_tool_offset_visibility(current_row.row())
                         self.set_tool_offset_visibility(current_row.row())
 
 
-                        temp_tools = dict(self.tools)
+                        temp_tools = copy.deepcopy(self.tools)
                         for tooluid_key in self.tools:
                         for tooluid_key in self.tools:
                             if int(tooluid_key) == tooluid_del:
                             if int(tooluid_key) == tooluid_del:
                                 temp_tools.pop(tooluid_del, None)
                                 temp_tools.pop(tooluid_del, None)
-                        self.tools = dict(temp_tools)
+                        self.tools = copy.deepcopy(temp_tools)
                         temp_tools.clear()
                         temp_tools.clear()
                     except AttributeError:
                     except AttributeError:
                         self.app.inform.emit("[warning_notcl]Failed. Select a tool to delete.")
                         self.app.inform.emit("[warning_notcl]Failed. Select a tool to delete.")
@@ -2840,19 +2845,19 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                             # updated from self.app.defaults
                             # updated from self.app.defaults
                             if data_key not in self.form_fields:
                             if data_key not in self.form_fields:
                                 temp_data[data_key] = value[data_key]
                                 temp_data[data_key] = value[data_key]
-                        temp_dia[key] = dict(temp_data)
+                        temp_dia[key] = copy.deepcopy(temp_data)
                         temp_data.clear()
                         temp_data.clear()
 
 
                     if key == 'solid_geometry':
                     if key == 'solid_geometry':
-                        temp_dia[key] = deepcopy(self.tools[tooluid_key]['solid_geometry'])
+                        temp_dia[key] = copy.deepcopy(self.tools[tooluid_key]['solid_geometry'])
 
 
-                    temp_tools[tooluid_key] = dict(temp_dia)
+                    temp_tools[tooluid_key] = copy.deepcopy(temp_dia)
 
 
             else:
             else:
-                temp_tools[tooluid_key] = dict(tooluid_value)
+                temp_tools[tooluid_key] = copy.deepcopy(tooluid_value)
 
 
         self.tools.clear()
         self.tools.clear()
-        self.tools = dict(temp_tools)
+        self.tools = copy.deepcopy(temp_tools)
         temp_tools.clear()
         temp_tools.clear()
         self.ui_connect()
         self.ui_connect()
 
 
@@ -2957,7 +2962,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 for tooluid_key, tooluid_value in self.tools.items():
                 for tooluid_key, tooluid_value in self.tools.items():
                     if int(tooluid_key) == tooluid:
                     if int(tooluid_key) == tooluid:
                         self.sel_tools.update({
                         self.sel_tools.update({
-                            tooluid: dict(tooluid_value)
+                            tooluid: copy.deepcopy(tooluid_value)
                         })
                         })
             self.mtool_gen_cncjob()
             self.mtool_gen_cncjob()
 
 
@@ -3084,7 +3089,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                             if data_key == "dwelltime":
                             if data_key == "dwelltime":
                                 dwelltime = data_value
                                 dwelltime = data_value
 
 
-                        datadict = dict(diadict_value)
+                        datadict = copy.deepcopy(diadict_value)
                         dia_cnc_dict.update({
                         dia_cnc_dict.update({
                             diadict_key: datadict
                             diadict_key: datadict
                         })
                         })
@@ -3153,7 +3158,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 app_obj.progress.emit(80)
                 app_obj.progress.emit(80)
 
 
                 job_obj.cnc_tools.update({
                 job_obj.cnc_tools.update({
-                    tooluid_key: dict(dia_cnc_dict)
+                    tooluid_key: copy.deepcopy(dia_cnc_dict)
                 })
                 })
                 dia_cnc_dict.clear()
                 dia_cnc_dict.clear()
 
 
@@ -3256,7 +3261,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                             if data_key == "dwelltime":
                             if data_key == "dwelltime":
                                 dwelltime = data_value
                                 dwelltime = data_value
 
 
-                        datadict = dict(diadict_value)
+                        datadict = copy.deepcopy(diadict_value)
                         dia_cnc_dict.update({
                         dia_cnc_dict.update({
                             diadict_key: datadict
                             diadict_key: datadict
                         })
                         })
@@ -3325,7 +3330,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 app_obj.progress.emit(80)
                 app_obj.progress.emit(80)
 
 
                 job_obj.cnc_tools.update({
                 job_obj.cnc_tools.update({
-                    tooluid_key: dict(dia_cnc_dict)
+                    tooluid_key: copy.deepcopy(dia_cnc_dict)
                 })
                 })
                 dia_cnc_dict.clear()
                 dia_cnc_dict.clear()
 
 
@@ -3584,9 +3589,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         self.options['feedrate_rapid'] *= factor
         self.options['feedrate_rapid'] *= factor
         self.options['endz'] *= factor
         self.options['endz'] *= factor
         # self.options['cnctooldia'] *= factor
         # self.options['cnctooldia'] *= factor
-        self.options['painttooldia'] *= factor
-        self.options['paintmargin'] *= factor
-        self.options['paintoverlap'] *= factor
+        # self.options['painttooldia'] *= factor
+        # self.options['paintmargin'] *= factor
+        # self.options['paintoverlap'] *= factor
 
 
         self.options["toolchangez"] *= factor
         self.options["toolchangez"] *= factor
 
 
@@ -3642,17 +3647,17 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                         # copy the other dict entries that are not convertible
                         # copy the other dict entries that are not convertible
                         if data_key not in param_list:
                         if data_key not in param_list:
                             data_copy[data_key] = data_value
                             data_copy[data_key] = data_value
-                    tool_dia_copy[dia_key] = dict(data_copy)
+                    tool_dia_copy[dia_key] = copy.deepcopy(data_copy)
                     data_copy.clear()
                     data_copy.clear()
 
 
             temp_tools_dict.update({
             temp_tools_dict.update({
-                tooluid_key: dict(tool_dia_copy)
+                tooluid_key: copy.deepcopy(tool_dia_copy)
             })
             })
             tool_dia_copy.clear()
             tool_dia_copy.clear()
 
 
 
 
         self.tools.clear()
         self.tools.clear()
-        self.tools = dict(temp_tools_dict)
+        self.tools = copy.deepcopy(temp_tools_dict)
 
 
         # if there is a value in the new tool field then convert that one too
         # if there is a value in the new tool field then convert that one too
         tooldia = self.ui.addtool_entry.get_value()
         tooldia = self.ui.addtool_entry.get_value()
@@ -3817,11 +3822,22 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         # (like the one in the TCL Command), False
         # (like the one in the TCL Command), False
         self.multitool = False
         self.multitool = False
 
 
-        # used for parsing the GCode lines to adjust the offset when the GCode was offseted
-        offsetx_re_string = r'(?=.*(X[-\+]?\d*\.\d*))'
-        self.g_offsetx_re = re.compile(offsetx_re_string)
-        offsety_re_string = r'(?=.*(Y[-\+]?\d*\.\d*))'
-        self.g_offsety_re = re.compile(offsety_re_string)
+        # used for parsing the GCode lines to adjust the GCode when the GCode is offseted or scaled
+        gcodex_re_string = r'(?=.*(X[-\+]?\d*\.\d*))'
+        self.g_x_re = re.compile(gcodex_re_string)
+        gcodey_re_string = r'(?=.*(Y[-\+]?\d*\.\d*))'
+        self.g_y_re = re.compile(gcodey_re_string)
+        gcodez_re_string = r'(?=.*(Z[-\+]?\d*\.\d*))'
+        self.g_z_re = re.compile(gcodez_re_string)
+
+        gcodef_re_string = r'(?=.*(F[-\+]?\d*\.\d*))'
+        self.g_f_re = re.compile(gcodef_re_string)
+        gcodet_re_string = r'(?=.*(\=\s*[-\+]?\d*\.\d*))'
+        self.g_t_re = re.compile(gcodet_re_string)
+
+        gcodenr_re_string = r'([+-]?\d*\.\d*)'
+        self.g_nr_re = re.compile(gcodenr_re_string)
+
         # Attributes to be included in serialization
         # Attributes to be included in serialization
         # Always append to it because it carries contents
         # Always append to it because it carries contents
         # from predecessors.
         # from predecessors.
@@ -4272,4 +4288,63 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         FlatCAMApp.App.log.debug("FlatCAMCNCjob.convert_units()")
         FlatCAMApp.App.log.debug("FlatCAMCNCjob.convert_units()")
         self.options["tooldia"] *= factor
         self.options["tooldia"] *= factor
 
 
+        param_list = ['cutz', 'depthperpass', 'travelz', 'feedrate', 'feedrate_z', 'feedrate_rapid',
+                      'endz', 'toolchangez']
+
+        temp_tools_dict = {}
+        tool_dia_copy = {}
+        data_copy = {}
+
+        for tooluid_key, tooluid_value in self.cnc_tools.items():
+            for dia_key, dia_value in tooluid_value.items():
+                if dia_key == 'tooldia':
+                    dia_value *= factor
+                    dia_value = float('%.4f' % dia_value)
+                    tool_dia_copy[dia_key] = dia_value
+                if dia_key == 'offset':
+                    tool_dia_copy[dia_key] = dia_value
+                if dia_key == 'offset_value':
+                    dia_value *= factor
+                    tool_dia_copy[dia_key] = dia_value
+
+                if dia_key == 'type':
+                    tool_dia_copy[dia_key] = dia_value
+                if dia_key == 'tool_type':
+                    tool_dia_copy[dia_key] = dia_value
+                if dia_key == 'data':
+                    for data_key, data_value in dia_value.items():
+                        # convert the form fields that are convertible
+                        for param in param_list:
+                            if data_key == param and data_value is not None:
+                                data_copy[data_key] = data_value * factor
+                        # copy the other dict entries that are not convertible
+                        if data_key not in param_list:
+                            data_copy[data_key] = data_value
+                    tool_dia_copy[dia_key] = copy.deepcopy(data_copy)
+                    data_copy.clear()
+
+                if dia_key == 'gcode':
+                    tool_dia_copy[dia_key] = dia_value
+                if dia_key == 'gcode_parsed':
+                    tool_dia_copy[dia_key] = dia_value
+                if dia_key == 'solid_geometry':
+                    tool_dia_copy[dia_key] = dia_value
+
+                # if dia_key == 'solid_geometry':
+                #     tool_dia_copy[dia_key] = affinity.scale(dia_value, xfact=factor, origin=(0, 0))
+                # if dia_key == 'gcode_parsed':
+                #     for g in dia_value:
+                #         g['geom'] = affinity.scale(g['geom'], factor, factor, origin=(0, 0))
+                #
+                #     tool_dia_copy['gcode_parsed'] = copy.deepcopy(dia_value)
+                #     tool_dia_copy['solid_geometry'] = cascaded_union([geo['geom'] for geo in dia_value])
+
+            temp_tools_dict.update({
+                tooluid_key: copy.deepcopy(tool_dia_copy)
+            })
+            tool_dia_copy.clear()
+
+        self.cnc_tools.clear()
+        self.cnc_tools = copy.deepcopy(temp_tools_dict)
+
 # end of file
 # end of file

+ 1 - 0
README.md

@@ -15,6 +15,7 @@ CAD program, and create G-Code for Isolation routing.
 - added new function to toggle fullscreen status in Menu -> View -> Toggle Full Screen. Shortcut key: Alt+F10
 - added new function to toggle fullscreen status in Menu -> View -> Toggle Full Screen. Shortcut key: Alt+F10
 - added key shortcuts for Enable Plots, Disable Plots and Disable other plots functions (Alt+1, Alt+2, Alt+3)
 - added key shortcuts for Enable Plots, Disable Plots and Disable other plots functions (Alt+1, Alt+2, Alt+3)
 - hidden the snap magnet entry and snap magnet toggle from the main view; they are now active only in Editor Mode
 - hidden the snap magnet entry and snap magnet toggle from the main view; they are now active only in Editor Mode
+- updated the camlib.CNCJob.scale() function so now the GCode is scaled also (quite a HACK :( it will need to be replaced at some point)). Units change work now on the GCODE also.
 
 
 30.01.2019
 30.01.2019
 
 

+ 133 - 7
camlib.py

@@ -5869,6 +5869,7 @@ class CNCjob(Geometry):
             else:
             else:
                 # it's a Shapely object, return it's bounds
                 # it's a Shapely object, return it's bounds
                 return obj.bounds
                 return obj.bounds
+
         if self.multitool is False:
         if self.multitool is False:
             log.debug("CNCJob->bounds()")
             log.debug("CNCJob->bounds()")
             if self.solid_geometry is None:
             if self.solid_geometry is None:
@@ -5877,21 +5878,30 @@ class CNCjob(Geometry):
 
 
             bounds_coords = bounds_rec(self.solid_geometry)
             bounds_coords = bounds_rec(self.solid_geometry)
         else:
         else:
+
             for k, v in self.cnc_tools.items():
             for k, v in self.cnc_tools.items():
                 minx = Inf
                 minx = Inf
                 miny = Inf
                 miny = Inf
                 maxx = -Inf
                 maxx = -Inf
                 maxy = -Inf
                 maxy = -Inf
-
-                for k in v['solid_geometry']:
-                    minx_, miny_, maxx_, maxy_ = bounds_rec(k)
+                try:
+                    for k in v['solid_geometry']:
+                        minx_, miny_, maxx_, maxy_ = bounds_rec(k)
+                        minx = min(minx, minx_)
+                        miny = min(miny, miny_)
+                        maxx = max(maxx, maxx_)
+                        maxy = max(maxy, maxy_)
+                except TypeError:
+                    minx_, miny_, maxx_, maxy_ = bounds_rec(v['solid_geometry'])
                     minx = min(minx, minx_)
                     minx = min(minx, minx_)
                     miny = min(miny, miny_)
                     miny = min(miny, miny_)
                     maxx = max(maxx, maxx_)
                     maxx = max(maxx, maxx_)
                     maxy = max(maxy, maxy_)
                     maxy = max(maxy, maxy_)
+
             bounds_coords = minx, miny, maxx, maxy
             bounds_coords = minx, miny, maxx, maxy
         return bounds_coords
         return bounds_coords
 
 
+    # TODO This function should be replaced at some point with a "real" function. Until then it's an ugly hack ...
     def scale(self, xfactor, yfactor=None, point=None):
     def scale(self, xfactor, yfactor=None, point=None):
         """
         """
         Scales all the geometry on the XY plane in the object by the
         Scales all the geometry on the XY plane in the object by the
@@ -5915,8 +5925,124 @@ class CNCjob(Geometry):
         else:
         else:
             px, py = point
             px, py = point
 
 
-        for g in self.gcode_parsed:
-            g['geom'] = affinity.scale(g['geom'], xfactor, yfactor, origin=(px, py))
+        def scale_g(g):
+            """
+
+            :param g: 'g' parameter it's a gcode string
+            :return:  scaled gcode string
+            """
+
+            temp_gcode = ''
+            header_start = False
+            header_stop = False
+            units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
+
+            lines = StringIO(g)
+            for line in lines:
+
+                # this changes the GCODE header ---- UGLY HACK
+                if "TOOL DIAMETER" in line or "Feedrate:" in line:
+                    header_start = True
+
+                if "G20" in line or "G21" in line:
+                    header_start = False
+                    header_stop = True
+
+                if header_start is True:
+                    header_stop = False
+                    if "in" in line:
+                        if units == 'MM':
+                            line = line.replace("in", "mm")
+                    if "mm" in line:
+                        if units == 'IN':
+                            line = line.replace("mm", "in")
+
+                    # find any number in header and convert it
+                    match_nr = self.g_nr_re.search(line)
+                    if match_nr:
+                        new_nr = float(match_nr.group()) * xfactor
+                        # replace the updated string
+                        line = line.replace(match_nr.group(),
+                                            ('%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_nr))
+                        )
+
+                # this scales all the X and Y and Z and F values and also the Tool Dia in the toolchange message
+                if header_stop is True:
+                    if "G20" in line:
+                        if units == 'MM':
+                            line = line.replace("G20", "G21")
+                    if "G21" in line:
+                        if units == 'IN':
+                            line = line.replace("G21", "G20")
+
+                    # find the X group
+                    match_x = self.g_x_re.search(line)
+                    if match_x:
+                        if match_x.group(1) is not None:
+                            new_x = float(match_x.group(1)[1:]) * xfactor
+                            # replace the updated string
+                            line = line.replace(
+                                match_x.group(1),
+                                'X%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_x)
+                            )
+                    # find the Y group
+                    match_y = self.g_y_re.search(line)
+                    if match_y:
+                        if match_y.group(1) is not None:
+                            new_y = float(match_y.group(1)[1:]) * yfactor
+                            line = line.replace(
+                                match_y.group(1),
+                                'Y%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_y)
+                            )
+                    # find the Z group
+                    match_z = self.g_z_re.search(line)
+                    if match_z:
+                        if match_z.group(1) is not None:
+                            new_z = float(match_z.group(1)[1:]) * xfactor
+                            line = line.replace(
+                                match_z.group(1),
+                                'Z%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_z)
+                            )
+
+                    # find the F group
+                    match_f = self.g_f_re.search(line)
+                    if match_f:
+                        if match_f.group(1) is not None:
+                            new_f = float(match_f.group(1)[1:]) * xfactor
+                            line = line.replace(
+                                match_f.group(1),
+                                'F%.*f' % (self.app.defaults["cncjob_fr_decimals"], new_f)
+                            )
+                    # find the T group (tool dia on toolchange)
+                    match_t = self.g_t_re.search(line)
+                    if match_t:
+                        if match_t.group(1) is not None:
+                            new_t = float(match_t.group(1)[1:]) * xfactor
+                            line = line.replace(
+                                match_t.group(1),
+                                '= %.*f' % (self.app.defaults["cncjob_coords_decimals"], new_t)
+                            )
+
+                temp_gcode += line
+            lines.close()
+            header_stop = False
+            return temp_gcode
+
+        if self.multitool is False:
+            # offset Gcode
+            self.gcode = scale_g(self.gcode)
+            # offset geometry
+            for g in self.gcode_parsed:
+                g['geom'] = affinity.scale(g['geom'], xfactor, yfactor, origin=(px, py))
+            self.create_geometry()
+        else:
+            for k, v in self.cnc_tools.items():
+                # scale Gcode
+                v['gcode'] = scale_g(v['gcode'])
+                # scale gcode_parsed
+                for g in v['gcode_parsed']:
+                    g['geom'] = affinity.scale(g['geom'], xfactor, yfactor, origin=(px, py))
+                v['solid_geometry'] = cascaded_union([geo['geom'] for geo in v['gcode_parsed']])
 
 
         self.create_geometry()
         self.create_geometry()
 
 
@@ -5946,7 +6072,7 @@ class CNCjob(Geometry):
             lines = StringIO(g)
             lines = StringIO(g)
             for line in lines:
             for line in lines:
                 # find the X group
                 # find the X group
-                match_x = self.g_offsetx_re.search(line)
+                match_x = self.g_x_re.search(line)
                 if match_x:
                 if match_x:
                     if match_x.group(1) is not None:
                     if match_x.group(1) is not None:
                         # get the coordinate and add X offset
                         # get the coordinate and add X offset
@@ -5956,7 +6082,7 @@ class CNCjob(Geometry):
                             match_x.group(1),
                             match_x.group(1),
                             'X%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_x)
                             'X%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_x)
                         )
                         )
-                match_y = self.g_offsety_re.search(line)
+                match_y = self.g_y_re.search(line)
                 if match_y:
                 if match_y:
                     if match_y.group(1) is not None:
                     if match_y.group(1) is not None:
                         new_y = float(match_y.group(1)[1:]) + dy
                         new_y = float(match_y.group(1)[1:]) + dy