Bladeren bron

- added support for G91 coordinates
- working in plotting the CNCjob generated with G91 coordinates

Marius Stanciu 6 jaren geleden
bovenliggende
commit
a2a3f1c1ed
4 gewijzigde bestanden met toevoegingen van 413 en 136 verwijderingen
  1. 2 0
      FlatCAMApp.py
  2. 2 0
      README.md
  3. 379 124
      camlib.py
  4. 30 12
      flatcamGUI/FlatCAMGUI.py

+ 2 - 0
FlatCAMApp.py

@@ -572,6 +572,7 @@ class App(QtCore.QObject):
             "cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_entry,
             "cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_entry,
 
 
             "cncjob_tooldia": self.ui.cncjob_defaults_form.cncjob_gen_group.tooldia_entry,
             "cncjob_tooldia": self.ui.cncjob_defaults_form.cncjob_gen_group.tooldia_entry,
+            "cncjob_coords_type": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_type_radio,
             "cncjob_coords_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry,
             "cncjob_coords_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry,
             "cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry,
             "cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry,
             "cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry,
             "cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry,
@@ -961,6 +962,7 @@ class App(QtCore.QObject):
             "cncjob_annotation_fontsize": 9,
             "cncjob_annotation_fontsize": 9,
             "cncjob_annotation_fontcolor": '#990000',
             "cncjob_annotation_fontcolor": '#990000',
             "cncjob_tooldia": 0.0393701,
             "cncjob_tooldia": 0.0393701,
+            "cncjob_coords_type": "G90",
             "cncjob_coords_decimals": 4,
             "cncjob_coords_decimals": 4,
             "cncjob_fr_decimals": 2,
             "cncjob_fr_decimals": 2,
             "cncjob_steps_per_circle": 128,
             "cncjob_steps_per_circle": 128,

+ 2 - 0
README.md

@@ -12,6 +12,8 @@ CAD program, and create G-Code for Isolation routing.
 4.09.2019
 4.09.2019
 
 
 - started to work on support for G91 in Gcode (relative coordinates)
 - started to work on support for G91 in Gcode (relative coordinates)
+- added support for G91 coordinates
+- working in plotting the CNCjob generated with G91 coordinates
 
 
 3.09.2019
 3.09.2019
 
 

+ 379 - 124
camlib.py

@@ -5039,8 +5039,9 @@ class CNCjob(Geometry):
         self.unitcode = {"IN": "G20", "MM": "G21"}
         self.unitcode = {"IN": "G20", "MM": "G21"}
 
 
         self.feedminutecode = "G94"
         self.feedminutecode = "G94"
-        self.absolutecode = "G90"
-        self.relativecode = "G91"
+        # self.absolutecode = "G90"
+        # self.incrementalcode = "G91"
+        self.coordinates_type = self.app.defaults["cncjob_coords_type"]
 
 
         self.gcode = ""
         self.gcode = ""
         self.gcode_parsed = None
         self.gcode_parsed = None
@@ -5442,27 +5443,51 @@ class CNCjob(Geometry):
                                 z_offset = 0
                                 z_offset = 0
                             self.z_cut += z_offset
                             self.z_cut += z_offset
 
 
-                            # Drillling!
-                            for k in node_list:
-                                locx = locations[k][0]
-                                locy = locations[k][1]
+                            self.coordinates_type = self.app.defaults["cncjob_coords_type"]
+                            if self.coordinates_type == "G90":
+                                # Drillling! for Absolute coordinates type G90
+                                for k in node_list:
+                                    locx = locations[k][0]
+                                    locy = locations[k][1]
 
 
-                                gcode += self.doformat(p.rapid_code, x=locx, y=locy)
-                                gcode += self.doformat(p.down_code, x=locx, y=locy)
+                                    gcode += self.doformat(p.rapid_code, x=locx, y=locy)
+                                    gcode += self.doformat(p.down_code, x=locx, y=locy)
 
 
-                                measured_down_distance += abs(self.z_cut) + abs(self.z_move)
+                                    measured_down_distance += abs(self.z_cut) + abs(self.z_move)
 
 
-                                if self.f_retract is False:
-                                    gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
-                                    measured_up_to_zero_distance += abs(self.z_cut)
-                                    measured_lift_distance += abs(self.z_move)
-                                else:
-                                    measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
+                                    if self.f_retract is False:
+                                        gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
+                                        measured_up_to_zero_distance += abs(self.z_cut)
+                                        measured_lift_distance += abs(self.z_move)
+                                    else:
+                                        measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
+
+                                    gcode += self.doformat(p.lift_code, x=locx, y=locy)
+                                    measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
+                                    self.oldx = locx
+                                    self.oldy = locy
+                            else:
+                                # Drillling! for Incremental coordinates type G91
+                                for k in node_list:
+                                    locx = locations[k][0] - self.oldx
+                                    locy = locations[k][1] - self.oldy
+
+                                    gcode += self.doformat(p.rapid_code, x=locx, y=locy)
+                                    gcode += self.doformat(p.down_code, x=locx, y=locy)
+
+                                    measured_down_distance += abs(self.z_cut) + abs(self.z_move)
 
 
-                                gcode += self.doformat(p.lift_code, x=locx, y=locy)
-                                measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
-                                self.oldx = locx
-                                self.oldy = locy
+                                    if self.f_retract is False:
+                                        gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
+                                        measured_up_to_zero_distance += abs(self.z_cut)
+                                        measured_lift_distance += abs(self.z_move)
+                                    else:
+                                        measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
+
+                                    gcode += self.doformat(p.lift_code, x=locx, y=locy)
+                                    measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
+                                    self.oldx = locx
+                                    self.oldy = locy
                 else:
                 else:
                     log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                     log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                               "The loaded Excellon file has no drills ...")
                               "The loaded Excellon file has no drills ...")
@@ -5548,27 +5573,51 @@ class CNCjob(Geometry):
                                 z_offset = 0
                                 z_offset = 0
                             self.z_cut += z_offset
                             self.z_cut += z_offset
 
 
-                            # Drillling!
-                            for k in node_list:
-                                locx = locations[k][0]
-                                locy = locations[k][1]
+                            self.coordinates_type = self.app.defaults["cncjob_coords_type"]
+                            if self.coordinates_type == "G90":
+                                # Drillling! for Absolute coordinates type G90
+                                for k in node_list:
+                                    locx = locations[k][0]
+                                    locy = locations[k][1]
 
 
-                                gcode += self.doformat(p.rapid_code, x=locx, y=locy)
-                                gcode += self.doformat(p.down_code, x=locx, y=locy)
+                                    gcode += self.doformat(p.rapid_code, x=locx, y=locy)
+                                    gcode += self.doformat(p.down_code, x=locx, y=locy)
 
 
-                                measured_down_distance += abs(self.z_cut) + abs(self.z_move)
+                                    measured_down_distance += abs(self.z_cut) + abs(self.z_move)
 
 
-                                if self.f_retract is False:
-                                    gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
-                                    measured_up_to_zero_distance += abs(self.z_cut)
-                                    measured_lift_distance += abs(self.z_move)
-                                else:
-                                    measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
+                                    if self.f_retract is False:
+                                        gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
+                                        measured_up_to_zero_distance += abs(self.z_cut)
+                                        measured_lift_distance += abs(self.z_move)
+                                    else:
+                                        measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
 
 
-                                gcode += self.doformat(p.lift_code, x=locx, y=locy)
-                                measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
-                                self.oldx = locx
-                                self.oldy = locy
+                                    gcode += self.doformat(p.lift_code, x=locx, y=locy)
+                                    measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
+                                    self.oldx = locx
+                                    self.oldy = locy
+                            else:
+                                # Drillling! for Incremental coordinates type G91
+                                for k in node_list:
+                                    locx = locations[k][0] - self.oldx
+                                    locy = locations[k][1] - self.oldy
+
+                                    gcode += self.doformat(p.rapid_code, x=locx, y=locy)
+                                    gcode += self.doformat(p.down_code, x=locx, y=locy)
+
+                                    measured_down_distance += abs(self.z_cut) + abs(self.z_move)
+
+                                    if self.f_retract is False:
+                                        gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
+                                        measured_up_to_zero_distance += abs(self.z_cut)
+                                        measured_lift_distance += abs(self.z_move)
+                                    else:
+                                        measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
+
+                                    gcode += self.doformat(p.lift_code, x=locx, y=locy)
+                                    measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
+                                    self.oldx = locx
+                                    self.oldy = locy
                 else:
                 else:
                     log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                     log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                               "The loaded Excellon file has no drills ...")
                               "The loaded Excellon file has no drills ...")
@@ -5613,28 +5662,56 @@ class CNCjob(Geometry):
                             z_offset = 0
                             z_offset = 0
                         self.z_cut += z_offset
                         self.z_cut += z_offset
 
 
-                        # Drillling!
-                        altPoints = []
-                        for point in points[tool]:
-                            altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0]))
+                        self.coordinates_type = self.app.defaults["cncjob_coords_type"]
+                        if self.coordinates_type == "G90":
+                            # Drillling! for Absolute coordinates type G90
+                            altPoints = []
+                            for point in points[tool]:
+                                altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0]))
 
 
-                        for point in self.optimized_travelling_salesman(altPoints):
-                            gcode += self.doformat(p.rapid_code, x=point[0], y=point[1])
-                            gcode += self.doformat(p.down_code, x=point[0], y=point[1])
+                            for point in self.optimized_travelling_salesman(altPoints):
+                                gcode += self.doformat(p.rapid_code, x=point[0], y=point[1])
+                                gcode += self.doformat(p.down_code, x=point[0], y=point[1])
 
 
-                            measured_down_distance += abs(self.z_cut) + abs(self.z_move)
+                                measured_down_distance += abs(self.z_cut) + abs(self.z_move)
 
 
-                            if self.f_retract is False:
-                                gcode += self.doformat(p.up_to_zero_code, x=point[0], y=point[1])
-                                measured_up_to_zero_distance += abs(self.z_cut)
-                                measured_lift_distance += abs(self.z_move)
-                            else:
-                                measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
+                                if self.f_retract is False:
+                                    gcode += self.doformat(p.up_to_zero_code, x=point[0], y=point[1])
+                                    measured_up_to_zero_distance += abs(self.z_cut)
+                                    measured_lift_distance += abs(self.z_move)
+                                else:
+                                    measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
+
+                                gcode += self.doformat(p.lift_code, x=point[0], y=point[1])
+                                measured_distance += abs(distance_euclidian(point[0], point[1], self.oldx, self.oldy))
+                                self.oldx = point[0]
+                                self.oldy = point[1]
+                        else:
+                            # Drillling! for Incremental coordinates type G91
+                            altPoints = []
+                            for point in points[tool]:
+                                altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0]))
+
+                            for point in self.optimized_travelling_salesman(altPoints):
+                                point[0] = point[0] - self.oldx
+                                point[1] = point[1] - self.oldy
+
+                                gcode += self.doformat(p.rapid_code, x=point[0], y=point[1])
+                                gcode += self.doformat(p.down_code, x=point[0], y=point[1])
+
+                                measured_down_distance += abs(self.z_cut) + abs(self.z_move)
+
+                                if self.f_retract is False:
+                                    gcode += self.doformat(p.up_to_zero_code, x=point[0], y=point[1])
+                                    measured_up_to_zero_distance += abs(self.z_cut)
+                                    measured_lift_distance += abs(self.z_move)
+                                else:
+                                    measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
 
 
-                            gcode += self.doformat(p.lift_code, x=point[0], y=point[1])
-                            measured_distance += abs(distance_euclidian(point[0], point[1], self.oldx, self.oldy))
-                            self.oldx = point[0]
-                            self.oldy = point[1]
+                                gcode += self.doformat(p.lift_code, x=point[0], y=point[1])
+                                measured_distance += abs(distance_euclidian(point[0], point[1], self.oldx, self.oldy))
+                                self.oldx = point[0]
+                                self.oldy = point[1]
                     else:
                     else:
                         log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                         log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                                   "The loaded Excellon file has no drills ...")
                                   "The loaded Excellon file has no drills ...")
@@ -5864,7 +5941,7 @@ class CNCjob(Geometry):
                     # calculate the cut distance
                     # calculate the cut distance
                     total_cut = total_cut + geo.length
                     total_cut = total_cut + geo.length
 
 
-                    self.gcode += self.create_gcode_single_pass(geo, extracut, tolerance)
+                    self.gcode += self.create_gcode_single_pass(geo, extracut, tolerance, old_point=current_pt)
 
 
                 # --------- Multi-pass ---------
                 # --------- Multi-pass ---------
                 else:
                 else:
@@ -5879,7 +5956,7 @@ class CNCjob(Geometry):
                     total_cut += (geo.length * nr_cuts)
                     total_cut += (geo.length * nr_cuts)
 
 
                     self.gcode += self.create_gcode_multi_pass(geo, extracut, tolerance,
                     self.gcode += self.create_gcode_multi_pass(geo, extracut, tolerance,
-                                                               postproc=p, current_point=current_pt)
+                                                               postproc=p, old_point=current_pt)
 
 
                 # calculate the total distance
                 # calculate the total distance
                 total_travel = total_travel + abs(distance(pt1=current_pt, pt2=pt))
                 total_travel = total_travel + abs(distance(pt1=current_pt, pt2=pt))
@@ -6157,7 +6234,7 @@ class CNCjob(Geometry):
                 if not multidepth:
                 if not multidepth:
                     # calculate the cut distance
                     # calculate the cut distance
                     total_cut += geo.length
                     total_cut += geo.length
-                    self.gcode += self.create_gcode_single_pass(geo, extracut, tolerance)
+                    self.gcode += self.create_gcode_single_pass(geo, extracut, tolerance, old_point=current_pt)
 
 
                 # --------- Multi-pass ---------
                 # --------- Multi-pass ---------
                 else:
                 else:
@@ -6172,7 +6249,7 @@ class CNCjob(Geometry):
                     total_cut += (geo.length * nr_cuts)
                     total_cut += (geo.length * nr_cuts)
 
 
                     self.gcode += self.create_gcode_multi_pass(geo, extracut, tolerance,
                     self.gcode += self.create_gcode_multi_pass(geo, extracut, tolerance,
-                                                               postproc=p, current_point=current_pt)
+                                                               postproc=p, old_point=current_pt)
 
 
                 # calculate the travel distance
                 # calculate the travel distance
                 total_travel += abs(distance(pt1=current_pt, pt2=pt))
                 total_travel += abs(distance(pt1=current_pt, pt2=pt))
@@ -6283,7 +6360,7 @@ class CNCjob(Geometry):
                 if pt != geo.coords[0] and pt == geo.coords[-1]:
                 if pt != geo.coords[0] and pt == geo.coords[-1]:
                     geo.coords = list(geo.coords)[::-1]
                     geo.coords = list(geo.coords)[::-1]
 
 
-                self.gcode += self.create_soldepaste_gcode(geo, p=p)
+                self.gcode += self.create_soldepaste_gcode(geo, p=p, old_point=current_pt)
                 current_pt = geo.coords[-1]
                 current_pt = geo.coords[-1]
                 pt, geo = storage.nearest(current_pt)  # Next
                 pt, geo = storage.nearest(current_pt)  # Next
 
 
@@ -6298,13 +6375,23 @@ class CNCjob(Geometry):
 
 
         return self.gcode
         return self.gcode
 
 
-    def create_soldepaste_gcode(self, geometry, p):
+    def create_soldepaste_gcode(self, geometry, p, old_point=(0, 0)):
         gcode = ''
         gcode = ''
         path = geometry.coords
         path = geometry.coords
 
 
+        self.coordinates_type = self.app.defaults["cncjob_coords_type"]
+        if self.coordinates_type == "G90":
+            # For Absolute coordinates type G90
+            first_x = path[0][0]
+            first_y = path[0][1]
+        else:
+            # For Incremental coordinates type G91
+            first_x = path[0][0] - old_point[0]
+            first_y = path[0][1] - old_point[1]
+
         if type(geometry) == LineString or type(geometry) == LinearRing:
         if type(geometry) == LineString or type(geometry) == LinearRing:
             # Move fast to 1st point
             # Move fast to 1st point
-            gcode += self.doformat(p.rapid_code, x=path[0][0], y=path[0][1])  # Move to first point
+            gcode += self.doformat(p.rapid_code, x=first_x, y=first_y)  # Move to first point
 
 
             # Move down to cutting depth
             # Move down to cutting depth
             gcode += self.doformat(p.z_feedrate_code)
             gcode += self.doformat(p.z_feedrate_code)
@@ -6316,8 +6403,20 @@ class CNCjob(Geometry):
             gcode += self.doformat(p.feedrate_xy_code)
             gcode += self.doformat(p.feedrate_xy_code)
 
 
             # Cutting...
             # Cutting...
+            prev_x = first_x
+            prev_y = first_y
             for pt in path[1:]:
             for pt in path[1:]:
-                gcode += self.doformat(p.linear_code, x=pt[0], y=pt[1])  # Linear motion to point
+                if self.coordinates_type == "G90":
+                    # For Absolute coordinates type G90
+                    next_x = pt[0]
+                    next_y = pt[1]
+                else:
+                    # For Incremental coordinates type G91
+                    next_x = pt[0] - prev_x
+                    next_y = pt[1] - prev_y
+                gcode += self.doformat(p.linear_code, x=next_x, y=next_y)  # Linear motion to point
+                prev_x = next_x
+                prev_y = next_y
 
 
             # Up to travelling height.
             # Up to travelling height.
             gcode += self.doformat(p.spindle_off_code) # Stop dispensing
             gcode += self.doformat(p.spindle_off_code) # Stop dispensing
@@ -6328,7 +6427,7 @@ class CNCjob(Geometry):
             gcode += self.doformat(p.z_feedrate_code)
             gcode += self.doformat(p.z_feedrate_code)
             gcode += self.doformat(p.lift_code)
             gcode += self.doformat(p.lift_code)
         elif type(geometry) == Point:
         elif type(geometry) == Point:
-            gcode += self.doformat(p.linear_code, x=path[0][0], y=path[0][1])  # Move to first point
+            gcode += self.doformat(p.linear_code, x=first_x, y=first_y)  # Move to first point
 
 
             gcode += self.doformat(p.feedrate_z_dispense_code)
             gcode += self.doformat(p.feedrate_z_dispense_code)
             gcode += self.doformat(p.down_z_start_code)
             gcode += self.doformat(p.down_z_start_code)
@@ -6345,18 +6444,18 @@ class CNCjob(Geometry):
             gcode += self.doformat(p.lift_code)
             gcode += self.doformat(p.lift_code)
         return gcode
         return gcode
 
 
-    def create_gcode_single_pass(self, geometry, extracut, tolerance):
+    def create_gcode_single_pass(self, geometry, extracut, tolerance, old_point=(0, 0)):
         # G-code. Note: self.linear2gcode() and self.point2gcode() will lower and raise the tool every time.
         # G-code. Note: self.linear2gcode() and self.point2gcode() will lower and raise the tool every time.
         gcode_single_pass = ''
         gcode_single_pass = ''
 
 
         if type(geometry) == LineString or type(geometry) == LinearRing:
         if type(geometry) == LineString or type(geometry) == LinearRing:
             if extracut is False:
             if extracut is False:
-                gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance)
+                gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, old_point=old_point)
             else:
             else:
                 if geometry.is_ring:
                 if geometry.is_ring:
-                    gcode_single_pass = self.linear2gcode_extra(geometry, tolerance=tolerance)
+                    gcode_single_pass = self.linear2gcode_extra(geometry, tolerance=tolerance, old_point=old_point)
                 else:
                 else:
-                    gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance)
+                    gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, old_point=old_point)
         elif type(geometry) == Point:
         elif type(geometry) == Point:
             gcode_single_pass = self.point2gcode(geometry)
             gcode_single_pass = self.point2gcode(geometry)
         else:
         else:
@@ -6365,7 +6464,7 @@ class CNCjob(Geometry):
 
 
         return gcode_single_pass
         return gcode_single_pass
 
 
-    def create_gcode_multi_pass(self, geometry, extracut, tolerance, postproc, current_point):
+    def create_gcode_multi_pass(self, geometry, extracut, tolerance, postproc, old_point=(0, 0)):
 
 
         gcode_multi_pass = ''
         gcode_multi_pass = ''
 
 
@@ -6394,17 +6493,19 @@ class CNCjob(Geometry):
             # is inconsequential.
             # is inconsequential.
             if type(geometry) == LineString or type(geometry) == LinearRing:
             if type(geometry) == LineString or type(geometry) == LinearRing:
                 if extracut is False:
                 if extracut is False:
-                    gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False)
+                    gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False,
+                                                          old_point=old_point)
                 else:
                 else:
                     if geometry.is_ring:
                     if geometry.is_ring:
                         gcode_multi_pass += self.linear2gcode_extra(geometry, tolerance=tolerance, z_cut=depth,
                         gcode_multi_pass += self.linear2gcode_extra(geometry, tolerance=tolerance, z_cut=depth,
-                                                                    up=False)
+                                                                    up=False, old_point=old_point)
                     else:
                     else:
-                        gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False)
+                        gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False,
+                                                              old_point=old_point)
 
 
             # Ignore multi-pass for points.
             # Ignore multi-pass for points.
             elif type(geometry) == Point:
             elif type(geometry) == Point:
-                gcode_multi_pass += self.point2gcode(geometry)
+                gcode_multi_pass += self.point2gcode(geometry, old_point=old_point)
                 break  # Ignoring ...
                 break  # Ignoring ...
             else:
             else:
                 log.warning("G-code generation not implemented for %s" % (str(type(geometry))))
                 log.warning("G-code generation not implemented for %s" % (str(type(geometry))))
@@ -6420,7 +6521,7 @@ class CNCjob(Geometry):
                 geometry.coords = list(geometry.coords)[::-1]
                 geometry.coords = list(geometry.coords)[::-1]
 
 
         # Lift the tool
         # Lift the tool
-        gcode_multi_pass += self.doformat(postproc.lift_code, x=current_point[0], y=current_point[1])
+        gcode_multi_pass += self.doformat(postproc.lift_code, x=old_point[0], y=old_point[1])
         return gcode_multi_pass
         return gcode_multi_pass
 
 
     def codes_split(self, gline):
     def codes_split(self, gline):
@@ -6692,43 +6793,134 @@ class CNCjob(Geometry):
         else:
         else:
             text = []
             text = []
             pos = []
             pos = []
-            for geo in gcode_parsed:
-                if geo['kind'][0] == 'T':
-                    current_position = geo['geom'].coords[0]
-                    if current_position not in pos:
-                        pos.append(current_position)
-                        path_num += 1
-                        text.append(str(path_num))
-                    current_position = geo['geom'].coords[-1]
-                    if current_position not in pos:
-                        pos.append(current_position)
-                        path_num += 1
-                        text.append(str(path_num))
-
-                # plot the geometry of Excellon objects
-                if self.origin_kind == 'excellon':
-                    try:
-                        poly = Polygon(geo['geom'])
-                    except ValueError:
-                        # if the geos are travel lines it will enter into Exception
+            self.coordinates_type = self.app.defaults["cncjob_coords_type"]
+            if self.coordinates_type == "G90":
+                # For Absolute coordinates type G90
+                for geo in gcode_parsed:
+                    if geo['kind'][0] == 'T':
+                        current_position = geo['geom'].coords[0]
+                        if current_position not in pos:
+                            pos.append(current_position)
+                            path_num += 1
+                            text.append(str(path_num))
+
+                        current_position = geo['geom'].coords[-1]
+                        if current_position not in pos:
+                            pos.append(current_position)
+                            path_num += 1
+                            text.append(str(path_num))
+
+                    # plot the geometry of Excellon objects
+                    if self.origin_kind == 'excellon':
+                        try:
+                            poly = Polygon(geo['geom'])
+                        except ValueError:
+                            # if the geos are travel lines it will enter into Exception
+                            poly = geo['geom'].buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle)
+                            poly = poly.simplify(tool_tolerance)
+                    else:
+                        # plot the geometry of any objects other than Excellon
                         poly = geo['geom'].buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle)
                         poly = geo['geom'].buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle)
                         poly = poly.simplify(tool_tolerance)
                         poly = poly.simplify(tool_tolerance)
-                else:
-                    # plot the geometry of any objects other than Excellon
-                    poly = geo['geom'].buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle)
-                    poly = poly.simplify(tool_tolerance)
 
 
-                if kind == 'all':
-                    obj.add_shape(shape=poly, color=color[geo['kind'][0]][1], face_color=color[geo['kind'][0]][0],
-                                  visible=visible, layer=1 if geo['kind'][0] == 'C' else 2)
-                elif kind == 'travel':
+                    if kind == 'all':
+                        obj.add_shape(shape=poly, color=color[geo['kind'][0]][1], face_color=color[geo['kind'][0]][0],
+                                      visible=visible, layer=1 if geo['kind'][0] == 'C' else 2)
+                    elif kind == 'travel':
+                        if geo['kind'][0] == 'T':
+                            obj.add_shape(shape=poly, color=color['T'][1], face_color=color['T'][0],
+                                          visible=visible, layer=2)
+                    elif kind == 'cut':
+                        if geo['kind'][0] == 'C':
+                            obj.add_shape(shape=poly, color=color['C'][1], face_color=color['C'][0],
+                                          visible=visible, layer=1)
+            else:
+                # For Incremental coordinates type G91
+                current_x = gcode_parsed[0]['geom'].coords[0][0]
+                current_y = gcode_parsed[0]['geom'].coords[0][1]
+                old_pos = (
+                    current_x,
+                    current_y
+                )
+
+                for geo in gcode_parsed:
+                    print(list(geo['geom'].coordsner))
                     if geo['kind'][0] == 'T':
                     if geo['kind'][0] == 'T':
-                        obj.add_shape(shape=poly, color=color['T'][1], face_color=color['T'][0],
-                                      visible=visible, layer=2)
-                elif kind == 'cut':
-                    if geo['kind'][0] == 'C':
-                        obj.add_shape(shape=poly, color=color['C'][1], face_color=color['C'][0],
-                                      visible=visible, layer=1)
+                        current_position = (
+                            geo['geom'].coords[0][0] + old_pos[0],
+                            geo['geom'].coords[0][1] + old_pos[1]
+                        )
+                        if current_position not in pos:
+                            pos.append(current_position)
+                            path_num += 1
+                            text.append(str(path_num))
+
+                        delta = (
+                            geo['geom'].coords[-1][0] - geo['geom'].coords[0][0],
+                            geo['geom'].coords[-1][1] - geo['geom'].coords[0][1]
+                        )
+                        current_position = (
+                            current_position[0] + geo['geom'].coords[-1][0],
+                            current_position[1] + geo['geom'].coords[-1][1]
+                        )
+                        if current_position not in pos:
+                            pos.append(current_position)
+                            path_num += 1
+                            text.append(str(path_num))
+
+                    # plot the geometry of Excellon objects
+                    if self.origin_kind == 'excellon':
+                        if isinstance(geo['geom'], Point):
+                            # if geo is Point
+                            current_position = (
+                                current_position[0] + geo['geom'].x,
+                                current_position[1] + geo['geom'].y
+                            )
+                            poly = Polygon(Point(current_position))
+                        elif isinstance(geo['geom'], LineString):
+                            # if the geos are travel lines (LineStrings)
+                            new_line_pts = []
+                            old_line_pos = deepcopy(current_position)
+                            for p in list(geo['geom'].coords):
+                                current_position = (
+                                    current_position[0] + p[0],
+                                    current_position[1] + p[1]
+                                )
+                                new_line_pts.append(current_position)
+                                old_line_pos = p
+                            new_line = LineString(new_line_pts)
+
+                            poly = new_line.buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle)
+                            poly = poly.simplify(tool_tolerance)
+                    else:
+                        # plot the geometry of any objects other than Excellon
+                        new_line_pts = []
+                        old_line_pos = deepcopy(current_position)
+                        for p in list(geo['geom'].coords):
+                            current_position = (
+                                current_position[0] + p[0],
+                                current_position[1] + p[1]
+                            )
+                            new_line_pts.append(current_position)
+                            old_line_pos = p
+                        new_line = LineString(new_line_pts)
+
+                        poly = new_line.buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle)
+                        poly = poly.simplify(tool_tolerance)
+
+                    old_pos = deepcopy(current_position)
+
+                    if kind == 'all':
+                        obj.add_shape(shape=poly, color=color[geo['kind'][0]][1], face_color=color[geo['kind'][0]][0],
+                                      visible=visible, layer=1 if geo['kind'][0] == 'C' else 2)
+                    elif kind == 'travel':
+                        if geo['kind'][0] == 'T':
+                            obj.add_shape(shape=poly, color=color['T'][1], face_color=color['T'][0],
+                                          visible=visible, layer=2)
+                    elif kind == 'cut':
+                        if geo['kind'][0] == 'C':
+                            obj.add_shape(shape=poly, color=color['C'][1], face_color=color['C'][0],
+                                          visible=visible, layer=1)
             try:
             try:
                 obj.annotation.set(text=text, pos=pos, visible=obj.options['plot'],
                 obj.annotation.set(text=text, pos=pos, visible=obj.options['plot'],
                                    font_size=self.app.defaults["cncjob_annotation_fontsize"],
                                    font_size=self.app.defaults["cncjob_annotation_fontsize"],
@@ -6798,7 +6990,7 @@ class CNCjob(Geometry):
 
 
     def linear2gcode(self, linear, tolerance=0, down=True, up=True,
     def linear2gcode(self, linear, tolerance=0, down=True, up=True,
                      z_cut=None, z_move=None, zdownrate=None,
                      z_cut=None, z_move=None, zdownrate=None,
-                     feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False):
+                     feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False, old_point=(0, 0)):
         """
         """
         Generates G-code to cut along the linear feature.
         Generates G-code to cut along the linear feature.
 
 
@@ -6845,30 +7037,51 @@ class CNCjob(Geometry):
 
 
         p = self.pp_geometry
         p = self.pp_geometry
 
 
+        self.coordinates_type = self.app.defaults["cncjob_coords_type"]
+        if self.coordinates_type == "G90":
+            # For Absolute coordinates type G90
+            first_x = path[0][0]
+            first_y = path[0][1]
+        else:
+            # For Incremental coordinates type G91
+            first_x = path[0][0] - old_point[0]
+            first_y = path[0][1] - old_point[1]
+
         # Move fast to 1st point
         # Move fast to 1st point
         if not cont:
         if not cont:
-            gcode += self.doformat(p.rapid_code, x=path[0][0], y=path[0][1])  # Move to first point
+            gcode += self.doformat(p.rapid_code, x=first_x, y=first_y)  # Move to first point
 
 
         # Move down to cutting depth
         # Move down to cutting depth
         if down:
         if down:
             # Different feedrate for vertical cut?
             # Different feedrate for vertical cut?
             gcode += self.doformat(p.z_feedrate_code)
             gcode += self.doformat(p.z_feedrate_code)
             # gcode += self.doformat(p.feedrate_code)
             # gcode += self.doformat(p.feedrate_code)
-            gcode += self.doformat(p.down_code, x=path[0][0], y=path[0][1], z_cut=z_cut)
+            gcode += self.doformat(p.down_code, x=first_x, y=first_y, z_cut=z_cut)
             gcode += self.doformat(p.feedrate_code, feedrate=feedrate)
             gcode += self.doformat(p.feedrate_code, feedrate=feedrate)
 
 
         # Cutting...
         # Cutting...
+        prev_x = first_x
+        prev_y = first_y
         for pt in path[1:]:
         for pt in path[1:]:
-            gcode += self.doformat(p.linear_code, x=pt[0], y=pt[1], z=z_cut)  # Linear motion to point
-
+            if self.coordinates_type == "G90":
+                # For Absolute coordinates type G90
+                next_x = pt[0]
+                next_y = pt[1]
+            else:
+                # For Incremental coordinates type G91
+                next_x = pt[0] - prev_x
+                next_y = pt[1] - prev_y
+            gcode += self.doformat(p.linear_code, x=next_x, y=next_y, z=z_cut)  # Linear motion to point
+            prev_x = pt[0]
+            prev_y = pt[1]
         # Up to travelling height.
         # Up to travelling height.
         if up:
         if up:
-            gcode += self.doformat(p.lift_code, x=pt[0], y=pt[1], z_move=z_move)  # Stop cutting
+            gcode += self.doformat(p.lift_code, x=prev_x, y=prev_y, z_move=z_move)  # Stop cutting
         return gcode
         return gcode
 
 
     def linear2gcode_extra(self, linear, tolerance=0, down=True, up=True,
     def linear2gcode_extra(self, linear, tolerance=0, down=True, up=True,
                      z_cut=None, z_move=None, zdownrate=None,
                      z_cut=None, z_move=None, zdownrate=None,
-                     feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False):
+                     feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False, old_point=(0, 0)):
         """
         """
         Generates G-code to cut along the linear feature.
         Generates G-code to cut along the linear feature.
 
 
@@ -6913,9 +7126,19 @@ class CNCjob(Geometry):
         path = list(target_linear.coords)
         path = list(target_linear.coords)
         p = self.pp_geometry
         p = self.pp_geometry
 
 
+        self.coordinates_type = self.app.defaults["cncjob_coords_type"]
+        if self.coordinates_type == "G90":
+            # For Absolute coordinates type G90
+            first_x = path[0][0]
+            first_y = path[0][1]
+        else:
+            # For Incremental coordinates type G91
+            first_x = path[0][0] - old_point[0]
+            first_y = path[0][1] - old_point[1]
+
         # Move fast to 1st point
         # Move fast to 1st point
         if not cont:
         if not cont:
-            gcode += self.doformat(p.rapid_code, x=path[0][0], y=path[0][1])  # Move to first point
+            gcode += self.doformat(p.rapid_code, x=first_x, y=first_y)  # Move to first point
 
 
         # Move down to cutting depth
         # Move down to cutting depth
         if down:
         if down:
@@ -6923,40 +7146,72 @@ class CNCjob(Geometry):
             if self.z_feedrate is not None:
             if self.z_feedrate is not None:
                 gcode += self.doformat(p.z_feedrate_code)
                 gcode += self.doformat(p.z_feedrate_code)
                 # gcode += self.doformat(p.feedrate_code)
                 # gcode += self.doformat(p.feedrate_code)
-                gcode += self.doformat(p.down_code, x=path[0][0], y=path[0][1], z_cut=z_cut)
+                gcode += self.doformat(p.down_code, x=first_x, y=first_y, z_cut=z_cut)
                 gcode += self.doformat(p.feedrate_code, feedrate=feedrate)
                 gcode += self.doformat(p.feedrate_code, feedrate=feedrate)
             else:
             else:
-                gcode += self.doformat(p.down_code, x=path[0][0], y=path[0][1], z_cut=z_cut)  # Start cutting
+                gcode += self.doformat(p.down_code, x=first_x, y=first_y, z_cut=z_cut)  # Start cutting
 
 
         # Cutting...
         # Cutting...
+        prev_x = first_x
+        prev_y = first_y
         for pt in path[1:]:
         for pt in path[1:]:
-            gcode += self.doformat(p.linear_code, x=pt[0], y=pt[1], z=z_cut)  # Linear motion to point
+            if self.coordinates_type == "G90":
+                # For Absolute coordinates type G90
+                next_x = pt[0]
+                next_y = pt[1]
+            else:
+                # For Incremental coordinates type G91
+                next_x = pt[0] - prev_x
+                next_y = pt[1] - prev_y
+            gcode += self.doformat(p.linear_code, x=next_x, y=next_y, z=z_cut)  # Linear motion to point
+            prev_x = pt[0]
+            prev_y = pt[1]
 
 
         # this line is added to create an extra cut over the first point in patch
         # this line is added to create an extra cut over the first point in patch
         # to make sure that we remove the copper leftovers
         # to make sure that we remove the copper leftovers
-        gcode += self.doformat(p.linear_code, x=path[1][0], y=path[1][1])    # Linear motion to the 1st point in the cut path
+        # Linear motion to the 1st point in the cut path
+        if self.coordinates_type == "G90":
+            # For Absolute coordinates type G90
+            last_x = path[1][0]
+            last_y = path[1][1]
+        else:
+            # For Incremental coordinates type G91
+            last_x = path[1][0] - first_x
+            last_y = path[1][1] - first_y
+        gcode += self.doformat(p.linear_code, x=last_x, y=last_y)
 
 
         # Up to travelling height.
         # Up to travelling height.
         if up:
         if up:
-            gcode += self.doformat(p.lift_code, x=path[1][0], y=path[1][1], z_move=z_move)  # Stop cutting
+            gcode += self.doformat(p.lift_code, x=last_x, y=last_y, z_move=z_move)  # Stop cutting
 
 
         return gcode
         return gcode
 
 
-    def point2gcode(self, point):
+    def point2gcode(self, point, old_point=(0, 0)):
         gcode = ""
         gcode = ""
 
 
         path = list(point.coords)
         path = list(point.coords)
         p = self.pp_geometry
         p = self.pp_geometry
-        gcode += self.doformat(p.linear_code, x=path[0][0], y=path[0][1])  # Move to first point
+
+        self.coordinates_type = self.app.defaults["cncjob_coords_type"]
+        if self.coordinates_type == "G90":
+            # For Absolute coordinates type G90
+            first_x = path[0][0]
+            first_y = path[0][1]
+        else:
+            # For Incremental coordinates type G91
+            first_x = path[0][0] - old_point[0]
+            first_y = path[0][1] - old_point[1]
+
+        gcode += self.doformat(p.linear_code, x=first_x, y=first_y)  # Move to first point
 
 
         if self.z_feedrate is not None:
         if self.z_feedrate is not None:
             gcode += self.doformat(p.z_feedrate_code)
             gcode += self.doformat(p.z_feedrate_code)
-            gcode += self.doformat(p.down_code, x=path[0][0], y=path[0][1], z_cut = self.z_cut)
+            gcode += self.doformat(p.down_code, x=first_x, y=first_y, z_cut = self.z_cut)
             gcode += self.doformat(p.feedrate_code)
             gcode += self.doformat(p.feedrate_code)
         else:
         else:
-            gcode += self.doformat(p.down_code, x=path[0][0], y=path[0][1], z_cut = self.z_cut)  # Start cutting
+            gcode += self.doformat(p.down_code, x=first_x, y=first_y, z_cut = self.z_cut)  # Start cutting
 
 
-        gcode += self.doformat(p.lift_code, x=path[0][0], y=path[0][1])  # Stop cutting
+        gcode += self.doformat(p.lift_code, x=first_x, y=first_y)  # Stop cutting
         return gcode
         return gcode
 
 
     def export_svg(self, scale_factor=0.00):
     def export_svg(self, scale_factor=0.00):

+ 30 - 12
flatcamGUI/FlatCAMGUI.py

@@ -6119,8 +6119,8 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
 
 
         grid0 = QtWidgets.QGridLayout()
         grid0 = QtWidgets.QGridLayout()
         self.layout.addLayout(grid0)
         self.layout.addLayout(grid0)
-        grid0.setColumnStretch(1, 1)
-        grid0.setColumnStretch(2, 1)
+        # grid0.setColumnStretch(1, 1)
+        # grid0.setColumnStretch(2, 1)
 
 
         # Plot CB
         # Plot CB
         # self.plot_cb = QtWidgets.QCheckBox('Plot')
         # self.plot_cb = QtWidgets.QCheckBox('Plot')
@@ -6129,7 +6129,7 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.plot_cb, 0, 0)
         grid0.addWidget(self.plot_cb, 0, 0)
 
 
         # Plot Kind
         # Plot Kind
-        self.cncplot_method_label = QtWidgets.QLabel('%s:' % _("Plot kind:"))
+        self.cncplot_method_label = QtWidgets.QLabel('%s:' % _("Plot kind"))
         self.cncplot_method_label.setToolTip(
         self.cncplot_method_label.setToolTip(
             _("This selects the kind of geometries on the canvas to plot.\n"
             _("This selects the kind of geometries on the canvas to plot.\n"
               "Those can be either of type 'Travel' which means the moves\n"
               "Those can be either of type 'Travel' which means the moves\n"
@@ -6205,34 +6205,52 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.steps_per_circle_entry, 5, 1)
         grid0.addWidget(self.steps_per_circle_entry, 5, 1)
 
 
         # Tool dia for plot
         # Tool dia for plot
-        tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
+        tdlabel = QtWidgets.QLabel('%s:' % _('Travel dia'))
         tdlabel.setToolTip(
         tdlabel.setToolTip(
-            _("Diameter of the tool to be\n"
+            _("The width of the travel lines to be\n"
               "rendered in the plot.")
               "rendered in the plot.")
         )
         )
-        grid0.addWidget(tdlabel, 6, 0)
         self.tooldia_entry = LengthEntry()
         self.tooldia_entry = LengthEntry()
+        grid0.addWidget(tdlabel, 6, 0)
         grid0.addWidget(self.tooldia_entry, 6, 1)
         grid0.addWidget(self.tooldia_entry, 6, 1)
 
 
+        # add a space
+        grid0.addWidget(QtWidgets.QLabel(''), 7, 0)
+
         # Number of decimals to use in GCODE coordinates
         # Number of decimals to use in GCODE coordinates
-        cdeclabel = QtWidgets.QLabel('%s:' % _('Coords dec.'))
+        cdeclabel = QtWidgets.QLabel('%s:' % _('Coordinates decimals'))
         cdeclabel.setToolTip(
         cdeclabel.setToolTip(
             _("The number of decimals to be used for \n"
             _("The number of decimals to be used for \n"
               "the X, Y, Z coordinates in CNC code (GCODE, etc.)")
               "the X, Y, Z coordinates in CNC code (GCODE, etc.)")
         )
         )
-        grid0.addWidget(cdeclabel, 7, 0)
         self.coords_dec_entry = IntEntry()
         self.coords_dec_entry = IntEntry()
-        grid0.addWidget(self.coords_dec_entry, 7, 1)
+        grid0.addWidget(cdeclabel, 8, 0)
+        grid0.addWidget(self.coords_dec_entry, 8, 1)
 
 
         # Number of decimals to use in GCODE feedrate
         # Number of decimals to use in GCODE feedrate
-        frdeclabel = QtWidgets.QLabel('%s:' % _('Feedrate dec.'))
+        frdeclabel = QtWidgets.QLabel('%s:' % _('Feedrate decimals'))
         frdeclabel.setToolTip(
         frdeclabel.setToolTip(
             _("The number of decimals to be used for \n"
             _("The number of decimals to be used for \n"
               "the Feedrate parameter in CNC code (GCODE, etc.)")
               "the Feedrate parameter in CNC code (GCODE, etc.)")
         )
         )
-        grid0.addWidget(frdeclabel, 8, 0)
         self.fr_dec_entry = IntEntry()
         self.fr_dec_entry = IntEntry()
-        grid0.addWidget(self.fr_dec_entry, 8, 1)
+        grid0.addWidget(frdeclabel, 9, 0)
+        grid0.addWidget(self.fr_dec_entry, 9, 1)
+
+        # The type of coordinates used in the Gcode: Absolute or Incremental
+        coords_type_label = QtWidgets.QLabel('%s:' % _('Coordinates type'))
+        coords_type_label.setToolTip(
+            _("The type of coordinates to be used in Gcode.\n"
+              "Can be:\n"
+              "- Absolute G90 -> the reference is the origin x=0, y=0\n"
+              "- Incremental G91 -> the reference is the previous position")
+        )
+        self.coords_type_radio = RadioSet([
+            {"label": _("Absolute G90"), "value": "G90"},
+            {"label": _("Incremental G91"), "value": "G91"}
+        ], orientation='vertical', stretch=False)
+        grid0.addWidget(coords_type_label, 10, 0)
+        grid0.addWidget(self.coords_type_radio, 10, 1)
 
 
         self.layout.addStretch()
         self.layout.addStretch()