Переглянути джерело

- working on a new feature: adding interdiction area for Gcode generation. They will be added in the Geometry Object

Marius Stanciu 5 роки тому
батько
коміт
2da0853b23

+ 5 - 0
CHANGELOG.md

@@ -9,6 +9,10 @@ CHANGELOG for FlatCAM beta
 
 2.05.2020
 
+- working on a new feature: adding interdiction area for Gcode generation. They will be added in the Geometry Object
+
+2.05.2020
+
 - changed the icons for the grid snap in the status bar
 - moved some of the methods from FlatCAMApp.App to flatcamGUI.FlatCAMGUI class
 - fixed bug in Gerber Editor in which the units conversion wasn't calculated correct
@@ -18,6 +22,7 @@ CHANGELOG for FlatCAM beta
 - modified the Panelize Tool to optimize the output from Cutout Tool such that there are no longer overlapping cuts
 - some string corrections
 - updated the Italian translation done by user @pcb-hobbyst
+- RELEASE 8.992
 
 01.05.2020
 

+ 2 - 2
FlatCAMApp.py

@@ -162,8 +162,8 @@ class App(QtCore.QObject):
     # ###############################################################################################################
     # ################################### Version and VERSION DATE ##################################################
     # ###############################################################################################################
-    version = 8.992
-    version_date = "2020/05/01"
+    version = 8.993
+    version_date = "2020/08/01"
     beta = True
     engine = '3D'
 

+ 4 - 0
defaults.py

@@ -341,6 +341,10 @@ class FlatCAMDefaults:
         "geometry_feedrate_probe": 75,
         "geometry_segx": 0.0,
         "geometry_segy": 0.0,
+        "geometry_area_exclusion": False,
+        "geometry_area_shape": "polygon",
+        "geometry_area_strategy": "over",
+        "geometry_area_overz": 1.0,
 
         # Geometry Editor
         "geometry_editor_sel_limit": 30,

+ 23 - 0
flatcamGUI/FlatCAMGUI.py

@@ -4146,6 +4146,29 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 # Jump to coords
                 if key == QtCore.Qt.Key_J:
                     self.app.on_jump_to()
+        elif self.app.call_source == 'geometry':
+            if modifiers == QtCore.Qt.ControlModifier:
+                pass
+            elif modifiers == QtCore.Qt.AltModifier:
+                pass
+            elif modifiers == QtCore.Qt.ShiftModifier:
+                pass
+            # NO MODIFIER
+            elif modifiers == QtCore.Qt.NoModifier:
+                if key == QtCore.Qt.Key_Escape or key == 'Escape':
+                    sel_obj = self.app.collection.get_active()
+                    assert sel_obj.kind == 'geometry', "Expected a Geometry Object, got %s" % type(sel_obj)
+
+                    sel_obj.area_disconnect()
+                    return
+
+                if key == QtCore.Qt.Key_G or key == 'G':
+                    self.app.ui.grid_snap_btn.trigger()
+                    return
+
+                # Jump to coords
+                if key == QtCore.Qt.Key_J or key == 'J':
+                    self.app.on_jump_to()
 
     def createPopupMenu(self):
         menu = super().createPopupMenu()

+ 87 - 7
flatcamGUI/ObjectUI.py

@@ -2022,14 +2022,94 @@ class GeometryObjectUI(ObjectUI):
         grid4.addWidget(pp_label, 11, 0)
         grid4.addWidget(self.pp_geometry_name_cb, 11, 1)
 
-        grid4.addWidget(QtWidgets.QLabel(''), 12, 0, 1, 2)
+        # grid4.addWidget(QtWidgets.QLabel(''), 12, 0, 1, 2)
+
+        # Exclusion Areas
+        self.exclusion_cb = FCCheckBox('%s:' % _("Exclusion areas"))
+        self.exclusion_cb.setToolTip(
+            _(
+                "Include exclusion areas.\n"
+                "In those areas the travel of the tools\n"
+                "is forbidden."
+            )
+        )
+        grid4.addWidget(self.exclusion_cb, 12, 0, 1, 2)
+
+        # ------------------------------------------------------------------------------------------------------------
+        # ------------------------- EXCLUSION AREAS ------------------------------------------------------------------
+        # ------------------------------------------------------------------------------------------------------------
+        self.exclusion_frame = QtWidgets.QFrame()
+        self.exclusion_frame.setContentsMargins(0, 0, 0, 0)
+        grid4.addWidget(self.exclusion_frame, 14, 0, 1, 2)
+
+        self.exclusion_box = QtWidgets.QVBoxLayout()
+        self.exclusion_box.setContentsMargins(0, 0, 0, 0)
+        self.exclusion_frame.setLayout(self.exclusion_box)
+
+        h_lay = QtWidgets.QHBoxLayout()
+        self.exclusion_box.addLayout(h_lay)
+
+        # Button Add Area
+        self.add_area_button = QtWidgets.QPushButton(_('Add area'))
+        self.add_area_button.setToolTip(_("Add an Exclusion Area."))
+        h_lay.addWidget(self.add_area_button)
+
+        # Button Delete Area
+        self.delete_area_button = QtWidgets.QPushButton(_('Clear areas'))
+        self.delete_area_button.setToolTip(_("Delete all exclusion areas."))
+        h_lay.addWidget(self.delete_area_button)
+
+        grid_l = QtWidgets.QGridLayout()
+        grid_l.setColumnStretch(0, 0)
+        grid_l.setColumnStretch(1, 1)
+        self.exclusion_box.addLayout(grid_l)
+
+        # Area Selection shape
+        self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
+        self.area_shape_label.setToolTip(
+            _("The kind of selection shape used for area selection.")
+        )
+
+        self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
+                                          {'label': _("Polygon"), 'value': 'polygon'}])
+
+        grid_l.addWidget(self.area_shape_label, 0, 0)
+        grid_l.addWidget(self.area_shape_radio, 0, 1)
+
+        # Chose Strategy
+        self.strategy_label = FCLabel('%s:' % _("Strategy"))
+        self.strategy_label.setToolTip(_("The strategy followed when encountering an exclusion area.\n"
+                                         "Can be:\n"
+                                         "- Over -> when encountering the area, the tool will go to a set height\n"
+                                         "- Around -> will avoid the exclusion area by going around the area"))
+        self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
+                                        {'label': _('Around'), 'value': 'around'}])
+
+        grid_l.addWidget(self.strategy_label, 1, 0)
+        grid_l.addWidget(self.strategy_radio, 1, 1)
+
+        # Over Z
+        self.over_z_label = FCLabel('%s:' % _("Over Z"))
+        self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
+                                       "an interdiction area."))
+        self.over_z_entry = FCDoubleSpinner()
+        self.over_z_entry.set_range(0.000, 9999.9999)
+        self.over_z_entry.set_precision(self.decimals)
+
+        grid_l.addWidget(self.over_z_label, 2, 0)
+        grid_l.addWidget(self.over_z_entry, 2, 1)
+
+        # -------------------------- EXCLUSION AREAS END -------------------------------------------------------------
+        # ------------------------------------------------------------------------------------------------------------
+        self.ois_exclusion_geo = OptionalInputSection(self.exclusion_cb, [self.exclusion_frame])
+
         warning_lbl = QtWidgets.QLabel(
             _(
                 "Add / Select at least one tool in the tool-table.\n"
                 "Click the # header to select all, or Ctrl + LMB\n"
                 "for custom selection of tools."
             ))
-        grid4.addWidget(warning_lbl, 13, 0, 1, 2)
+        grid4.addWidget(warning_lbl, 15, 0, 1, 2)
 
         # Button
         self.generate_cnc_button = QtWidgets.QPushButton(_('Generate CNCJob object'))
@@ -2042,9 +2122,9 @@ class GeometryObjectUI(ObjectUI):
                             font-weight: bold;
                         }
                         """)
-        grid4.addWidget(self.generate_cnc_button, 14, 0, 1, 2)
+        grid4.addWidget(self.generate_cnc_button, 17, 0, 1, 2)
 
-        grid4.addWidget(QtWidgets.QLabel(''), 15, 0, 1, 2)
+        grid4.addWidget(QtWidgets.QLabel(''), 19, 0, 1, 2)
 
         # ##############
         # Paint area ##
@@ -2053,7 +2133,7 @@ class GeometryObjectUI(ObjectUI):
         self.tools_label.setToolTip(
             _("Launch Paint Tool in Tools Tab.")
         )
-        grid4.addWidget(self.tools_label, 16, 0, 1, 2)
+        grid4.addWidget(self.tools_label, 20, 0, 1, 2)
 
         # Paint Button
         self.paint_tool_button = QtWidgets.QPushButton(_('Paint Tool'))
@@ -2071,7 +2151,7 @@ class GeometryObjectUI(ObjectUI):
                             font-weight: bold;
                         }
                         """)
-        grid4.addWidget(self.paint_tool_button, 17, 0, 1, 2)
+        grid4.addWidget(self.paint_tool_button, 22, 0, 1, 2)
 
         # NCC Tool
         self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool'))
@@ -2085,7 +2165,7 @@ class GeometryObjectUI(ObjectUI):
                             font-weight: bold;
                         }
                         """)
-        grid4.addWidget(self.generate_ncc_button, 18, 0, 1, 2)
+        grid4.addWidget(self.generate_ncc_button, 24, 0, 1, 2)
 
 
 class CNCObjectUI(ObjectUI):

+ 4 - 0
flatcamGUI/preferences/PreferencesUIManager.py

@@ -303,6 +303,10 @@ class PreferencesUIManager:
             "geometry_f_plunge": self.ui.geometry_defaults_form.geometry_adv_opt_group.fplunge_cb,
             "geometry_segx": self.ui.geometry_defaults_form.geometry_adv_opt_group.segx_entry,
             "geometry_segy": self.ui.geometry_defaults_form.geometry_adv_opt_group.segy_entry,
+            "geometry_area_exclusion": self.ui.geometry_defaults_form.geometry_adv_opt_group.exclusion_cb,
+            "geometry_area_shape": self.ui.geometry_defaults_form.geometry_adv_opt_group.area_shape_radio,
+            "geometry_area_strategy": self.ui.geometry_defaults_form.geometry_adv_opt_group.strategy_radio,
+            "geometry_area_overz": self.ui.geometry_defaults_form.geometry_adv_opt_group.over_z_entry,
 
             # Geometry Editor
             "geometry_editor_sel_limit": self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry,

+ 58 - 1
flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py

@@ -1,7 +1,7 @@
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 
-from flatcamGUI.GUIElements import FCEntry, FloatEntry, FCDoubleSpinner, FCCheckBox, RadioSet
+from flatcamGUI.GUIElements import FCEntry, FloatEntry, FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel
 from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 import gettext
@@ -186,4 +186,61 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(segy_label, 11, 0)
         grid1.addWidget(self.segy_entry, 11, 1)
 
+        # -----------------------------
+        # --- Area Exclusion ----------
+        # -----------------------------
+        self.adv_label = QtWidgets.QLabel('<b>%s:</b>' % _('Area Exclusion'))
+        self.adv_label.setToolTip(
+            _("Area exclusion parameters.\n"
+              "Those parameters are available only for\n"
+              "Advanced App. Level.")
+        )
+        grid1.addWidget(self.adv_label, 12, 0, 1, 2)
+
+        # Exclusion Area CB
+        self.exclusion_cb = FCCheckBox('%s:' % _("Exclusion areas"))
+        self.exclusion_cb.setToolTip(
+            _(
+                "Include exclusion areas.\n"
+                "In those areas the travel of the tools\n"
+                "is forbidden."
+            )
+        )
+        grid1.addWidget(self.exclusion_cb, 13, 0, 1, 2)
+
+        # Area Selection shape
+        self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
+        self.area_shape_label.setToolTip(
+            _("The kind of selection shape used for area selection.")
+        )
+
+        self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
+                                          {'label': _("Polygon"), 'value': 'polygon'}])
+
+        grid1.addWidget(self.area_shape_label, 14, 0)
+        grid1.addWidget(self.area_shape_radio, 14, 1)
+
+        # Chose Strategy
+        self.strategy_label = FCLabel('%s:' % _("Strategy"))
+        self.strategy_label.setToolTip(_("The strategy followed when encountering an exclusion area.\n"
+                                         "Can be:\n"
+                                         "- Over -> when encountering the area, the tool will go to a set height\n"
+                                         "- Around -> will avoid the exclusion area by going around the area"))
+        self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
+                                        {'label': _('Around'), 'value': 'around'}])
+
+        grid1.addWidget(self.strategy_label, 15, 0)
+        grid1.addWidget(self.strategy_radio, 15, 1)
+
+        # Over Z
+        self.over_z_label = FCLabel('%s:' % _("Over Z"))
+        self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
+                                       "an interdiction area."))
+        self.over_z_entry = FCDoubleSpinner()
+        self.over_z_entry.set_range(0.000, 9999.9999)
+        self.over_z_entry.set_precision(self.decimals)
+
+        grid1.addWidget(self.over_z_label, 18, 0)
+        grid1.addWidget(self.over_z_entry, 18, 1)
+
         self.layout.addStretch()

+ 242 - 1
flatcamObjects/FlatCAMGeometry.py

@@ -16,6 +16,7 @@ import shapely.affinity as affinity
 from camlib import Geometry
 
 from flatcamObjects.FlatCAMObj import *
+import FlatCAMTool
 
 import ezdxf
 import math
@@ -68,6 +69,10 @@ class GeometryObject(FlatCAMObj, Geometry):
             "extracut_length": 0.1,
             "endz": 2.0,
             "endxy": '',
+            "area_exclusion": False,
+            "area_shape": "polygon",
+            "area_strategy": "over",
+            "area_overz": 1.0,
 
             "startz": None,
             "toolchange": False,
@@ -146,6 +151,18 @@ class GeometryObject(FlatCAMObj, Geometry):
 
         self.param_fields = {}
 
+        # Event signals disconnect id holders
+        self.mr = None
+        self.mm = None
+        self.kp = None
+
+        # variables to be used in area exclusion
+        self.cursor_pos = (0, 0)
+        self.exclusion_areas_list = []
+        self.first_click = False
+        self.points = []
+        self.poly_drawn = False
+
         # Attributes to be included in serialization
         # Always append to it because it carries contents
         # from predecessors.
@@ -344,7 +361,11 @@ class GeometryObject(FlatCAMObj, Geometry):
             "toolchangez": self.ui.toolchangez_entry,
             "endz": self.ui.endz_entry,
             "endxy": self.ui.endxy_entry,
-            "cnctooldia": self.ui.addtool_entry
+            "cnctooldia": self.ui.addtool_entry,
+            "area_exclusion": self.ui.exclusion_cb,
+            "area_shape":self.ui.area_shape_radio,
+            "area_strategy": self.ui.strategy_radio,
+            "area_overz": self.ui.over_z_entry,
         })
 
         self.param_fields.update({
@@ -402,6 +423,10 @@ class GeometryObject(FlatCAMObj, Geometry):
             "toolchangez": None,
             "endz": None,
             "endxy": '',
+            "area_exclusion": None,
+            "area_shape": None,
+            "area_strategy": None,
+            "area_overz": None,
             "spindlespeed": 0,
             "toolchangexy": None,
             "startz": None
@@ -522,6 +547,9 @@ class GeometryObject(FlatCAMObj, Geometry):
         self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
         self.ui.cutz_entry.returnPressed.connect(self.on_cut_z_changed)
 
+        self.ui.add_area_button.clicked.connect(self.on_add_area_click)
+        self.ui.delete_area_button.clicked.connect(self.on_clear_area_click)
+
     def on_cut_z_changed(self):
         self.old_cutz = self.ui.cutz_entry.get_value()
 
@@ -2545,6 +2573,219 @@ class GeometryObject(FlatCAMObj, Geometry):
             self.ui.plot_cb.setChecked(True)
         self.ui_connect()
 
+    def on_add_area_click(self):
+        self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
+        self.app.call_source = 'geometry'
+
+        if self.app.is_legacy is False:
+            self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        else:
+            self.app.plotcanvas.graph_event_disconnect(self.app.mp)
+            self.app.plotcanvas.graph_event_disconnect(self.app.mm)
+            self.app.plotcanvas.graph_event_disconnect(self.app.mr)
+
+        self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
+        self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
+        # self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press)
+
+    # To be called after clicking on the plot.
+    def on_mouse_release(self, event):
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+            # event_is_dragging = event.is_dragging
+            right_button = 2
+        else:
+            event_pos = (event.xdata, event.ydata)
+            # event_is_dragging = self.app.plotcanvas.is_dragging
+            right_button = 3
+
+        event_pos = self.app.plotcanvas.translate_coords(event_pos)
+        if self.app.grid_status():
+            curr_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
+        else:
+            curr_pos = (event_pos[0], event_pos[1])
+
+        x1, y1 = curr_pos[0], curr_pos[1]
+
+        shape_type = self.ui.area_shape_radio.get_value()
+
+        # do clear area only for left mouse clicks
+        if event.button == 1:
+            if shape_type == "square":
+                if self.first_click is False:
+                    self.first_click = True
+                    self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the area."))
+
+                    self.cursor_pos = self.app.plotcanvas.translate_coords(event_pos)
+                    if self.app.grid_status():
+                        self.cursor_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
+                else:
+                    self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish."))
+                    self.app.delete_selection_shape()
+
+                    x0, y0 = self.cursor_pos[0], self.cursor_pos[1]
+
+                    pt1 = (x0, y0)
+                    pt2 = (x1, y0)
+                    pt3 = (x1, y1)
+                    pt4 = (x0, y1)
+
+                    new_rectangle = Polygon([pt1, pt2, pt3, pt4])
+                    self.exclusion_areas_list.append(new_rectangle)
+
+                    # add a temporary shape on canvas
+                    FlatCAMTool.FlatCAMTool.draw_tool_selection_shape(self, old_coords=(x0, y0), coords=(x1, y1))
+
+                    self.first_click = False
+                    return
+            else:
+                self.points.append((x1, y1))
+
+                if len(self.points) > 1:
+                    self.poly_drawn = True
+                    self.app.inform.emit(_("Click on next Point or click right mouse button to complete ..."))
+
+                return ""
+        elif event.button == right_button and self.mouse_is_dragging is False:
+
+            shape_type = self.ui.area_shape_radio.get_value()
+
+            if shape_type == "square":
+                self.first_click = False
+            else:
+                # if we finish to add a polygon
+                if self.poly_drawn is True:
+                    try:
+                        # try to add the point where we last clicked if it is not already in the self.points
+                        last_pt = (x1, y1)
+                        if last_pt != self.points[-1]:
+                            self.points.append(last_pt)
+                    except IndexError:
+                        pass
+
+                    # we need to add a Polygon and a Polygon can be made only from at least 3 points
+                    if len(self.points) > 2:
+                        FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
+                        pol = Polygon(self.points)
+                        # do not add invalid polygons even if they are drawn by utility geometry
+                        if pol.is_valid:
+                            self.exclusion_areas_list.append(pol)
+                            FlatCAMTool.FlatCAMTool.draw_selection_shape_polygon(self, points=self.points)
+                            self.app.inform.emit(
+                                _("Zone added. Click to start adding next zone or right click to finish."))
+
+                    self.points = []
+                    self.poly_drawn = False
+                    return
+
+            FlatCAMTool.FlatCAMTool.delete_tool_selection_shape(self)
+
+            if self.app.is_legacy is False:
+                self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
+                self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
+                # self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
+            else:
+                self.app.plotcanvas.graph_event_disconnect(self.mr)
+                self.app.plotcanvas.graph_event_disconnect(self.mm)
+                # self.app.plotcanvas.graph_event_disconnect(self.kp)
+
+            self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
+                                                                  self.app.on_mouse_click_over_plot)
+            self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
+                                                                  self.app.on_mouse_move_over_plot)
+            self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
+                                                                  self.app.on_mouse_click_release_over_plot)
+
+            self.app.call_source = 'app'
+
+            if len(self.exclusion_areas_list) == 0:
+                return
+
+    def area_disconnect(self):
+        if self.app.is_legacy is False:
+            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
+            self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
+        else:
+            self.app.plotcanvas.graph_event_disconnect(self.mr)
+            self.app.plotcanvas.graph_event_disconnect(self.mm)
+            self.app.plotcanvas.graph_event_disconnect(self.kp)
+
+        self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
+                                                              self.app.on_mouse_click_over_plot)
+        self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
+                                                              self.app.on_mouse_move_over_plot)
+        self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
+                                                              self.app.on_mouse_click_release_over_plot)
+        self.points = []
+        self.poly_drawn = False
+        self.exclusion_areas_list = []
+
+        FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
+        FlatCAMTool.FlatCAMTool.delete_tool_selection_shape(self)
+
+        self.app.call_source = "app"
+        self.app.inform.emit("[WARNING_NOTCL] %s" % _("Cancelled. Area exclusion drawing was interrupted."))
+
+    # called on mouse move
+    def on_mouse_move(self, event):
+        shape_type = self.ui.area_shape_radio.get_value()
+
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+            event_is_dragging = event.is_dragging
+            # right_button = 2
+        else:
+            event_pos = (event.xdata, event.ydata)
+            event_is_dragging = self.app.plotcanvas.is_dragging
+            # right_button = 3
+
+        curr_pos = self.app.plotcanvas.translate_coords(event_pos)
+
+        # detect mouse dragging motion
+        if event_is_dragging is True:
+            self.mouse_is_dragging = True
+        else:
+            self.mouse_is_dragging = False
+
+        # update the cursor position
+        if self.app.grid_status():
+            # Update cursor
+            curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
+
+            self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]),
+                                         symbol='++', edge_color=self.app.cursor_color_3D,
+                                         edge_width=self.app.defaults["global_cursor_width"],
+                                         size=self.app.defaults["global_cursor_size"])
+
+        # update the positions on status bar
+        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+                                           "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
+        if self.cursor_pos is None:
+            self.cursor_pos = (0, 0)
+
+        self.app.dx = curr_pos[0] - float(self.cursor_pos[0])
+        self.app.dy = curr_pos[1] - float(self.cursor_pos[1])
+        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
+
+        # draw the utility geometry
+        if shape_type == "square":
+            if self.first_click:
+                self.app.delete_selection_shape()
+                self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]),
+                                                     coords=(curr_pos[0], curr_pos[1]))
+        else:
+            FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
+            FlatCAMTool.FlatCAMTool.draw_moving_selection_shape_poly(
+                self, points=self.points, data=(curr_pos[0], curr_pos[1]))
+
+    def on_clear_area_click(self):
+        self.exclusion_areas_list = []
+        FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
+        self.app.delete_selection_shape()
+
     @staticmethod
     def merge(geo_list, geo_final, multigeo=None):
         """

+ 4 - 0
flatcamParsers/ParseHPGL2.py

@@ -73,6 +73,10 @@ class HPGL2:
             "toolchangez": self.app.defaults["geometry_toolchangez"],
             "endz": self.app.defaults["geometry_endz"],
             "endxy": self.app.defaults["geometry_endxy"],
+            "area_exclusion": self.app.defaults["geometry_area_exclusion"],
+            "area_shape": self.app.defaults["geometry_area_shape"],
+            "area_strategy": self.app.defaults["geometry_area_strategy"],
+            "area_overz": self.app.defaults["geometry_area_overz"],
 
             "spindlespeed": self.app.defaults["geometry_spindlespeed"],
             "toolchangexy": self.app.defaults["geometry_toolchangexy"],

+ 4 - 0
flatcamTools/ToolCutOut.py

@@ -479,6 +479,10 @@ class CutOut(FlatCAMTool):
             "toolchangez":      float(self.app.defaults["geometry_toolchangez"]),
             "startz":           self.app.defaults["geometry_startz"],
             "endz":             float(self.app.defaults["geometry_endz"]),
+            "area_exclusion":   self.app.defaults["geometry_area_exclusion"],
+            "area_shape":       self.app.defaults["geometry_area_shape"],
+            "area_strategy":    self.app.defaults["geometry_area_strategy"],
+            "area_overz":       float(self.app.defaults["geometry_area_overz"]),
 
             # NCC
             "tools_nccoperation":       self.app.defaults["tools_nccoperation"],

+ 5 - 0
flatcamTools/ToolNCC.py

@@ -1039,6 +1039,11 @@ class NonCopperClear(FlatCAMTool, Gerber):
             "toolchangexy": self.app.defaults["geometry_toolchangexy"],
             "startz": self.app.defaults["geometry_startz"],
 
+            "area_exclusion": self.app.defaults["geometry_area_exclusion"],
+            "area_shape": self.app.defaults["geometry_area_shape"],
+            "area_strategy": self.app.defaults["geometry_area_strategy"],
+            "area_overz": float(self.app.defaults["geometry_area_overz"]),
+
             "tools_nccoperation": self.app.defaults["tools_nccoperation"],
             "tools_nccmargin": self.app.defaults["tools_nccmargin"],
             "tools_nccmethod": self.app.defaults["tools_nccmethod"],

+ 5 - 0
flatcamTools/ToolPaint.py

@@ -1013,6 +1013,11 @@ class ToolPaint(FlatCAMTool, Gerber):
             "toolchangexy": self.app.defaults["geometry_toolchangexy"],
             "startz": self.app.defaults["geometry_startz"],
 
+            "area_exclusion": self.app.defaults["geometry_area_exclusion"],
+            "area_shape": self.app.defaults["geometry_area_shape"],
+            "area_strategy": self.app.defaults["geometry_area_strategy"],
+            "area_overz": float(self.app.defaults["geometry_area_overz"]),
+
             "tooldia": self.app.defaults["tools_painttooldia"],
             "tools_paintmargin": self.app.defaults["tools_paintmargin"],
             "tools_paintmethod": self.app.defaults["tools_paintmethod"],

+ 2 - 2
locale/en/LC_MESSAGES/strings.po

@@ -17916,7 +17916,7 @@ msgstr "No Geometry name in args. Provide a name and try again."
 #~ msgid ""
 #~ "- 'Itself' -  the non copper clearing extent\n"
 #~ "is based on the object that is copper cleared.\n"
-#~ " - 'Area Selection' - left mouse click to start selection of the area to "
+#~ "- 'Area Selection' - left mouse click to start selection of the area to "
 #~ "be painted.\n"
 #~ "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple "
 #~ "areas.\n"
@@ -17925,7 +17925,7 @@ msgstr "No Geometry name in args. Provide a name and try again."
 #~ msgstr ""
 #~ "- 'Itself' -  the non copper clearing extent\n"
 #~ "is based on the object that is copper cleared.\n"
-#~ " - 'Area Selection' - left mouse click to start selection of the area to "
+#~ "- 'Area Selection' - left mouse click to start selection of the area to "
 #~ "be painted.\n"
 #~ "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple "
 #~ "areas.\n"

+ 1 - 1
locale/es/LC_MESSAGES/strings.po

@@ -18196,7 +18196,7 @@ msgstr ""
 #~ msgid ""
 #~ "- 'Itself' -  the non copper clearing extent\n"
 #~ "is based on the object that is copper cleared.\n"
-#~ " - 'Area Selection' - left mouse click to start selection of the area to "
+#~ "- 'Area Selection' - left mouse click to start selection of the area to "
 #~ "be painted.\n"
 #~ "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple "
 #~ "areas.\n"

+ 2 - 2
locale/fr/LC_MESSAGES/strings.po

@@ -11051,7 +11051,7 @@ msgid ""
 "Selection of area to be processed.\n"
 "- 'Itself' - the processing extent is based on the object that is "
 "processed.\n"
-" - 'Area Selection' - left mouse click to start selection of the area to be "
+"- 'Area Selection' - left mouse click to start selection of the area to be "
 "processed.\n"
 "- 'Reference Object' - will process the area specified by another object."
 msgstr ""
@@ -18201,7 +18201,7 @@ msgstr ""
 #~ msgid ""
 #~ "- 'Itself' -  the non copper clearing extent\n"
 #~ "is based on the object that is copper cleared.\n"
-#~ " - 'Area Selection' - left mouse click to start selection of the area to "
+#~ "- 'Area Selection' - left mouse click to start selection of the area to "
 #~ "be painted.\n"
 #~ "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple "
 #~ "areas.\n"

+ 3 - 3
locale/hu/LC_MESSAGES/strings.po

@@ -10875,7 +10875,7 @@ msgid ""
 "Selection of area to be processed.\n"
 "- 'Itself' - the processing extent is based on the object that is "
 "processed.\n"
-" - 'Area Selection' - left mouse click to start selection of the area to be "
+"- 'Area Selection' - left mouse click to start selection of the area to be "
 "processed.\n"
 "- 'Reference Object' - will process the area specified by another object."
 msgstr ""
@@ -17881,7 +17881,7 @@ msgstr "No Geometry name in args. Provide a name and try again."
 #~ msgid ""
 #~ "- 'Itself' -  the non copper clearing extent\n"
 #~ "is based on the object that is copper cleared.\n"
-#~ " - 'Area Selection' - left mouse click to start selection of the area to "
+#~ "- 'Area Selection' - left mouse click to start selection of the area to "
 #~ "be painted.\n"
 #~ "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple "
 #~ "areas.\n"
@@ -17890,7 +17890,7 @@ msgstr "No Geometry name in args. Provide a name and try again."
 #~ msgstr ""
 #~ "- 'Itself' -  the non copper clearing extent\n"
 #~ "is based on the object that is copper cleared.\n"
-#~ " - 'Area Selection' - left mouse click to start selection of the area to "
+#~ "- 'Area Selection' - left mouse click to start selection of the area to "
 #~ "be painted.\n"
 #~ "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple "
 #~ "areas.\n"

+ 3 - 3
locale/it/LC_MESSAGES/strings.po

@@ -13154,7 +13154,7 @@ msgstr ""
 #| msgid ""
 #| "- 'Itself' - the non copper clearing extent is based on the object that "
 #| "is copper cleared.\n"
-#| " - 'Area Selection' - left mouse click to start selection of the area to "
+#| "- 'Area Selection' - left mouse click to start selection of the area to "
 #| "be painted.\n"
 #| "- 'Reference Object' - will do non copper clearing within the area "
 #| "specified by another object."
@@ -13162,7 +13162,7 @@ msgid ""
 "Selection of area to be processed.\n"
 "- 'Itself' - the processing extent is based on the object that is "
 "processed.\n"
-" - 'Area Selection' - left mouse click to start selection of the area to be "
+"- 'Area Selection' - left mouse click to start selection of the area to be "
 "processed.\n"
 "- 'Reference Object' - will process the area specified by another object."
 msgstr ""
@@ -19163,7 +19163,7 @@ msgstr "Nessun nome di geometria negli argomenti. Fornisci un nome e riprova."
 #~ msgid ""
 #~ "- 'Itself' -  the non copper clearing extent\n"
 #~ "is based on the object that is copper cleared.\n"
-#~ " - 'Area Selection' - left mouse click to start selection of the area to "
+#~ "- 'Area Selection' - left mouse click to start selection of the area to "
 #~ "be painted.\n"
 #~ "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple "
 #~ "areas.\n"

+ 1 - 1
locale/pt_BR/LC_MESSAGES/strings.po

@@ -17959,7 +17959,7 @@ msgstr "Nenhum nome de geometria nos argumentos. Altere e tente novamente."
 #~ msgid ""
 #~ "- 'Itself' -  the non copper clearing extent\n"
 #~ "is based on the object that is copper cleared.\n"
-#~ " - 'Area Selection' - left mouse click to start selection of the area to "
+#~ "- 'Area Selection' - left mouse click to start selection of the area to "
 #~ "be painted.\n"
 #~ "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple "
 #~ "areas.\n"

+ 2 - 2
locale/ro/LC_MESSAGES/strings.po

@@ -11068,7 +11068,7 @@ msgid ""
 "Selection of area to be processed.\n"
 "- 'Itself' - the processing extent is based on the object that is "
 "processed.\n"
-" - 'Area Selection' - left mouse click to start selection of the area to be "
+"- 'Area Selection' - left mouse click to start selection of the area to be "
 "processed.\n"
 "- 'Reference Object' - will process the area specified by another object."
 msgstr ""
@@ -18233,7 +18233,7 @@ msgstr ""
 #~ msgid ""
 #~ "- 'Itself' -  the non copper clearing extent\n"
 #~ "is based on the object that is copper cleared.\n"
-#~ " - 'Area Selection' - left mouse click to start selection of the area to "
+#~ "- 'Area Selection' - left mouse click to start selection of the area to "
 #~ "be painted.\n"
 #~ "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple "
 #~ "areas.\n"

+ 1 - 1
locale_template/strings.pot

@@ -9707,7 +9707,7 @@ msgstr ""
 msgid ""
 "Selection of area to be processed.\n"
 "- 'Itself' - the processing extent is based on the object that is processed.\n"
-" - 'Area Selection' - left mouse click to start selection of the area to be processed.\n"
+"- 'Area Selection' - left mouse click to start selection of the area to be processed.\n"
 "- 'Reference Object' - will process the area specified by another object."
 msgstr ""
 

+ 5 - 0
tclCommands/TclCommandCopperClear.py

@@ -218,6 +218,11 @@ class TclCommandCopperClear(TclCommand):
             "toolchangexy": self.app.defaults["geometry_toolchangexy"],
             "startz": self.app.defaults["geometry_startz"],
 
+            "area_exclusion": self.app.defaults["geometry_area_exclusion"],
+            "area_shape": self.app.defaults["geometry_area_shape"],
+            "area_strategy": self.app.defaults["geometry_area_strategy"],
+            "area_overz": float(self.app.defaults["geometry_area_overz"]),
+
             "tooldia": self.app.defaults["tools_painttooldia"],
             "tools_nccmargin": margin,
             "tools_nccmethod": method_data,

+ 5 - 0
tclCommands/TclCommandPaint.py

@@ -197,6 +197,11 @@ class TclCommandPaint(TclCommand):
             "toolchangexy": self.app.defaults["geometry_toolchangexy"],
             "startz": self.app.defaults["geometry_startz"],
 
+            "area_exclusion": self.app.defaults["geometry_area_exclusion"],
+            "area_shape": self.app.defaults["geometry_area_shape"],
+            "area_strategy": self.app.defaults["geometry_area_strategy"],
+            "area_overz": float(self.app.defaults["geometry_area_overz"]),
+
             "tooldia": self.app.defaults["tools_painttooldia"],
             "paintmargin": margin,
             "paintmethod": method,