From 4a3a0a5669bdce2268f81d53e5b070111ddc6aca Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Fri, 25 Oct 2019 23:08:44 +0300 Subject: [PATCH] - Copper Fill Tool: added possibility to select between a bounding box rectangular or convex hull when the reference is the geometry of the source Gerber object - Copper Fill Tool: cleanup on not regular tool exit --- README.md | 2 + flatcamGUI/FlatCAMGUI.py | 24 ++++++++ flatcamTools/ToolCopperFill.py | 96 ++++++++++++++++++++++++++---- flatcamTools/ToolDblSided.py | 6 +- flatcamTools/ToolNonCopperClear.py | 6 +- flatcamTools/ToolPaint.py | 6 +- 6 files changed, 118 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 07404fb2..6549ab7a 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ CAD program, and create G-Code for Isolation routing. - QRCode Tool: added GUI category in Preferences window - QRCode Tool: shortcut key for this tool is now ALT+Q while PDF import Tool was relegated to CTRL+Q combo key shortcut - added a new FlatCAM Tool: Copper Fill Tool. It will pour copper into a Gerber filling all empty space with copper, at a clearance distance of the Gerber features +- Copper Fill Tool: added possibility to select between a bounding box rectangular or convex hull when the reference is the geometry of the source Gerber object +- Copper Fill Tool: cleanup on not regular tool exit 24.10.2019 diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 43c1f3be..5d23a6ed 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -3512,6 +3512,30 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # Jump to coords if key == QtCore.Qt.Key_J: self.app.on_jump_to() + elif self.app.call_source == 'copperfill_tool': + if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier: + if key == QtCore.Qt.Key_X: + self.app.abort_all_tasks() + return + + elif modifiers == QtCore.Qt.ControlModifier: + pass + elif modifiers == QtCore.Qt.ShiftModifier: + pass + elif modifiers == QtCore.Qt.AltModifier: + pass + elif modifiers == QtCore.Qt.NoModifier: + # Escape = Deselect All + if key == QtCore.Qt.Key_Escape or key == 'Escape': + self.app.copperfill_tool.on_exit() + + # Grid toggle + if key == QtCore.Qt.Key_G: + self.app.ui.grid_snap_btn.trigger() + + # Jump to coords + if key == QtCore.Qt.Key_J: + self.app.on_jump_to() def createPopupMenu(self): menu = super().createPopupMenu() diff --git a/flatcamTools/ToolCopperFill.py b/flatcamTools/ToolCopperFill.py index 2756f16d..6410a20b 100644 --- a/flatcamTools/ToolCopperFill.py +++ b/flatcamTools/ToolCopperFill.py @@ -15,6 +15,7 @@ from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMExcellon import shapely.geometry.base as base from shapely.ops import cascaded_union, unary_union from shapely.geometry import Polygon, MultiPolygon +from shapely.geometry import box as box import logging from copy import deepcopy @@ -138,9 +139,9 @@ class ToolCopperFill(FlatCAMTool): "It can be Gerber, Excellon or Geometry.") ) self.box_combo_type = QtWidgets.QComboBox() - self.box_combo_type.addItem(_("Gerber Reference Box Object")) - self.box_combo_type.addItem(_("Excellon Reference Box Object")) - self.box_combo_type.addItem(_("Geometry Reference Box Object")) + self.box_combo_type.addItem(_("Reference Gerber")) + self.box_combo_type.addItem(_("Reference Excellon")) + self.box_combo_type.addItem(_("Reference Geometry")) grid_lay.addWidget(self.box_combo_type_label, 4, 0) grid_lay.addWidget(self.box_combo_type, 4, 1) @@ -162,6 +163,21 @@ class ToolCopperFill(FlatCAMTool): self.box_combo_type.hide() self.box_combo_type_label.hide() + # Bounding Box Type # + self.bbox_type_radio = RadioSet([ + {'label': _('Rectangular'), 'value': 'rect'}, + {"label": _("Minimal"), "value": "min"} + ], stretch=False) + self.bbox_type_label = QtWidgets.QLabel(_("Box Type:")) + self.bbox_type_label.setToolTip( + _("- 'Rectangular' - the bounding box will be of rectangular shape.\n " + "- 'Minimal' - the bounding box will be the convex hull shape.") + ) + grid_lay.addWidget(self.bbox_type_label, 6, 0) + grid_lay.addWidget(self.bbox_type_radio, 6, 1) + self.bbox_type_label.hide() + self.bbox_type_radio.hide() + # ## Insert Copper Fill self.fill_button = QtWidgets.QPushButton(_("Insert Copper Fill")) self.fill_button.setToolTip( @@ -186,6 +202,8 @@ class ToolCopperFill(FlatCAMTool): self.cursor_pos = (0, 0) self.first_click = False + self.area_method = False + # Tool properties self.clearance_val = None self.margin_val = None @@ -233,10 +251,14 @@ class ToolCopperFill(FlatCAMTool): # self.margin_entry.set_value(float(self.app.defaults["tools_copperfill_margin"])) # self.reference_radio.set_value(self.app.defaults["tools_copperfill_reference"]) # self.geo_steps_per_circle = int(self.app.defaults["tools_copperfill_circle_steps"]) + # self.bbox_type_radio.set_value(self.app.defaults["tools_copperfill_box_type"]) self.clearance_entry.set_value(0.5) self.margin_entry.set_value(1.0) self.reference_radio.set_value('itself') + self.bbox_type_radio.set_value('rect') + + self.area_method = False def on_combo_box_type(self): obj_type = self.box_combo_type.currentIndex() @@ -255,7 +277,16 @@ class ToolCopperFill(FlatCAMTool): self.box_combo_type.show() self.box_combo_type_label.show() + if self.reference_radio.get_value() == "itself": + self.bbox_type_label.show() + self.bbox_type_radio.show() + else: + self.bbox_type_label.hide() + self.bbox_type_radio.hide() + def execute(self): + self.app.call_source = "copperfill_tool" + self.clearance_val = self.clearance_entry.get_value() self.margin_val = self.margin_entry.get_value() reference_method = self.reference_radio.get_value() @@ -290,6 +321,8 @@ class ToolCopperFill(FlatCAMTool): elif reference_method == 'area': self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area.")) + self.area_method = True + 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) @@ -363,7 +396,7 @@ class ToolCopperFill(FlatCAMTool): elif event.button == right_button and self.mouse_is_dragging == False: self.app.delete_selection_shape() - + self.area_method = False self.first_click = False if self.app.is_legacy is False: @@ -464,7 +497,6 @@ class ToolCopperFill(FlatCAMTool): log.debug("Copper Filling Tool started. Reading parameters.") self.app.inform.emit(_("Copper Filling Tool started. Reading parameters.")) - units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value() ref_selected = self.reference_radio.get_value() if c_val is None: c_val = float(self.app.defaults["tools_copperfill_clearance"]) @@ -540,14 +572,29 @@ class ToolCopperFill(FlatCAMTool): geo_n = working_obj.solid_geometry try: - if isinstance(geo_n, MultiPolygon): - env_obj = geo_n.convex_hull - elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \ - (isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon): - env_obj = cascaded_union(geo_n) + if self.bbox_type_radio.get_value() == 'min': + if isinstance(geo_n, MultiPolygon): + env_obj = geo_n.convex_hull + elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \ + (isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon): + env_obj = cascaded_union(geo_n) + else: + env_obj = cascaded_union(geo_n) + env_obj = env_obj.convex_hull else: - env_obj = cascaded_union(geo_n) - env_obj = env_obj.convex_hull + if isinstance(geo_n, Polygon) or \ + (isinstance(geo_n, list) and len(geo_n) == 1) or \ + (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1): + env_obj = geo_n.buffer(0, join_style=base.JOIN_STYLE.mitre).exterior + elif isinstance(geo_n, MultiPolygon): + x0, y0, x1, y1 = geo_n.bounds + geo = box(x0, y0, x1, y1) + env_obj = geo.buffer(0, join_style=base.JOIN_STYLE.mitre) + else: + self.app.inform.emit( + '[ERROR_NOTCL] %s: %s' % (_("Geometry not supported for bounding box"), type(geo_n)) + ) + return 'fail' bounding_box = env_obj.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre) except Exception as e: @@ -568,7 +615,7 @@ class ToolCopperFill(FlatCAMTool): if self.app.abort_flag: # graceful abort requested by the user raise FlatCAMApp.GracefulException - geo_buff_list.append(poly.buffer(distance=0.0, join_style=base.JOIN_STYLE.mitre)) + geo_buff_list.append(poly.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)) bounding_box = cascaded_union(geo_buff_list) @@ -676,3 +723,26 @@ class ToolCopperFill(FlatCAMTool): self.mouse_is_dragging = False self.cursor_pos = (0, 0) self.first_click = False + + # if True it means we exited from tool in the middle of area adding therefore disconnect the events + if self.area_method is True: + self.app.delete_selection_shape() + self.area_method = False + + 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.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" + self.app.inform.emit('[success] %s' % _("Copper Fill Tool exit.")) + diff --git a/flatcamTools/ToolDblSided.py b/flatcamTools/ToolDblSided.py index 9747bb4a..96ca7c2b 100644 --- a/flatcamTools/ToolDblSided.py +++ b/flatcamTools/ToolDblSided.py @@ -187,9 +187,9 @@ class DblSidedTool(FlatCAMTool): self.box_combo.setCurrentIndex(1) self.box_combo_type = QtWidgets.QComboBox() - self.box_combo_type.addItem(_("Gerber Reference Box Object")) - self.box_combo_type.addItem(_("Excellon Reference Box Object")) - self.box_combo_type.addItem(_("Geometry Reference Box Object")) + self.box_combo_type.addItem(_("Reference Gerber")) + self.box_combo_type.addItem(_("Reference Excellon")) + self.box_combo_type.addItem(_("Reference Geometry")) self.point_box_container.addWidget(self.box_combo_type) self.point_box_container.addWidget(self.box_combo) diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index deb35b20..8b773cd1 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -445,9 +445,9 @@ class NonCopperClear(FlatCAMTool, Gerber): "It can be Gerber, Excellon or Geometry.") ) self.box_combo_type = QtWidgets.QComboBox() - self.box_combo_type.addItem(_("Gerber Reference Box Object")) - self.box_combo_type.addItem(_("Excellon Reference Box Object")) - self.box_combo_type.addItem(_("Geometry Reference Box Object")) + self.box_combo_type.addItem(_("Reference Gerber")) + self.box_combo_type.addItem(_("Reference Excellon")) + self.box_combo_type.addItem(_("Reference Geometry")) form1.addRow(self.box_combo_type_label, self.box_combo_type) self.box_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object")) diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index 95b8c9e3..46ea4d4e 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -335,9 +335,9 @@ class ToolPaint(FlatCAMTool, Gerber): "It can be Gerber, Excellon or Geometry.") ) self.box_combo_type = QtWidgets.QComboBox() - self.box_combo_type.addItem(_("Gerber Reference Box Object")) - self.box_combo_type.addItem(_("Excellon Reference Box Object")) - self.box_combo_type.addItem(_("Geometry Reference Box Object")) + self.box_combo_type.addItem(_("Reference Gerber")) + self.box_combo_type.addItem(_("Reference Excellon")) + self.box_combo_type.addItem(_("Reference Geometry")) form1.addRow(self.box_combo_type_label, self.box_combo_type) self.box_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object"))