From ee3b38327e515514ddc9eee280580d4cc8d36d4e Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 13 Aug 2019 00:42:58 +0300 Subject: [PATCH] - added new option in ToolSub: the ability to close (or not) the resulting paths when using tool on Geometry objects. Added also a new category in the Edit -> Preferences -> Tools, the Substractor Tool Options --- FlatCAMApp.py | 7 ++-- README.md | 4 +++ flatcamGUI/FlatCAMGUI.py | 26 +++++++++++++++ flatcamTools/ToolSub.py | 72 +++++++++++++++++++++++++++++++++++----- 4 files changed, 99 insertions(+), 10 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 9f3419b4..d568ce25 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -590,7 +590,8 @@ class App(QtCore.QObject): "tools_solderpaste_dwellfwd": self.ui.tools_defaults_form.tools_solderpaste_group.dwellfwd_entry, "tools_solderpaste_speedrev": self.ui.tools_defaults_form.tools_solderpaste_group.speedrev_entry, "tools_solderpaste_dwellrev": self.ui.tools_defaults_form.tools_solderpaste_group.dwellrev_entry, - "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo + "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo, + "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb } @@ -922,7 +923,9 @@ class App(QtCore.QObject): "tools_solderpaste_dwellfwd": 1, "tools_solderpaste_speedrev": 10, "tools_solderpaste_dwellrev": 1, - "tools_solderpaste_pp": 'Paste_1' + "tools_solderpaste_pp": 'Paste_1', + + "tools_sub_close_paths": True }) # ############################## diff --git a/README.md b/README.md index 5b6b3fbe..da1bc082 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ CAD program, and create G-Code for Isolation routing. ================================================= +13.08.2019 + +- added new option in ToolSub: the ability to close (or not) the resulting paths when using tool on Geometry objects. Added also a new category in the Edit -> Preferences -> Tools, the Substractor Tool Options + 12.08.2019 - done regression to solve the bug with multiple passes cutting from the copper features (I should remember not to make mods here) diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index d9714897..665ee5be 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -3280,6 +3280,9 @@ class ToolsPreferencesUI(QtWidgets.QWidget): self.tools_solderpaste_group = ToolsSolderpastePrefGroupUI() self.tools_solderpaste_group.setMinimumWidth(200) + self.tools_sub_group = ToolsSubPrefGroupUI() + self.tools_sub_group.setMinimumWidth(200) + self.vlay = QtWidgets.QVBoxLayout() self.vlay.addWidget(self.tools_ncc_group) self.vlay.addWidget(self.tools_paint_group) @@ -3296,6 +3299,7 @@ class ToolsPreferencesUI(QtWidgets.QWidget): self.vlay3 = QtWidgets.QVBoxLayout() self.vlay3.addWidget(self.tools_solderpaste_group) + self.vlay3.addWidget(self.tools_sub_group) self.layout.addLayout(self.vlay) self.layout.addLayout(self.vlay1) @@ -6708,6 +6712,28 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): self.layout.addStretch() +class ToolsSubPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + + super(ToolsSubPrefGroupUI, self).__init__(self) + + self.setTitle(str(_("Substractor Tool Options"))) + + # ## Solder Paste Dispensing + self.sublabel = QtWidgets.QLabel(_("Parameters:")) + self.sublabel.setToolTip( + _("A tool to substract one Gerber or Geometry object\n" + "from another of the same type.") + ) + self.layout.addWidget(self.sublabel) + + self.close_paths_cb = FCCheckBox(_("Close paths")) + self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the Geometry substractor object.")) + self.layout.addWidget(self.close_paths_cb) + + self.layout.addStretch() + + class FlatCAMActivityView(QtWidgets.QWidget): def __init__(self, parent=None): diff --git a/flatcamTools/ToolSub.py b/flatcamTools/ToolSub.py index 7f6cab59..5715742d 100644 --- a/flatcamTools/ToolSub.py +++ b/flatcamTools/ToolSub.py @@ -130,6 +130,10 @@ class ToolSub(FlatCAMTool): form_geo_layout.addRow(self.sub_geo_label, self.sub_geo_combo) + self.close_paths_cb = FCCheckBox(_("Close paths")) + self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the Geometry substractor object.")) + self.tools_box.addWidget(self.close_paths_cb) + self.intersect_geo_btn = FCButton(_('Substract Geometry')) self.intersect_geo_btn.setToolTip( _("Will remove the area occupied by the substractor\n" @@ -217,6 +221,7 @@ class ToolSub(FlatCAMTool): def set_tool_ui(self): self.tools_frame.show() + self.close_paths_cb.setChecked(self.app.defaults["tools_sub_close_paths"]) def on_grb_intersection_click(self): # reset previous values @@ -231,7 +236,7 @@ class ToolSub(FlatCAMTool): self.app.inform.emit(_("[ERROR_NOTCL] No Target object loaded.")) return - # Get source object. + # Get target object. try: self.target_grb_obj = self.app.collection.get_by_name(self.target_grb_obj_name) except Exception as e: @@ -244,7 +249,7 @@ class ToolSub(FlatCAMTool): self.app.inform.emit(_("[ERROR_NOTCL] No Substractor object loaded.")) return - # Get source object. + # Get substractor object. try: self.sub_grb_obj = self.app.collection.get_by_name(self.sub_grb_obj_name) except Exception as e: @@ -424,7 +429,7 @@ class ToolSub(FlatCAMTool): self.app.inform.emit(_("[ERROR_NOTCL] No Target object loaded.")) return - # Get source object. + # Get target object. try: self.target_geo_obj = self.app.collection.get_by_name(self.target_geo_obj_name) except Exception as e: @@ -437,7 +442,7 @@ class ToolSub(FlatCAMTool): self.app.inform.emit(_("[ERROR_NOTCL] No Substractor object loaded.")) return - # Get source object. + # Get substractor object. try: self.sub_geo_obj = self.app.collection.get_by_name(self.sub_geo_obj_name) except Exception as e: @@ -496,10 +501,58 @@ class ToolSub(FlatCAMTool): text = _("Parsing tool %s geometry ...") % str(tool) with self.app.proc_container.new(text): - new_geo = (cascaded_union(geo)).difference(self.sub_union) - if new_geo: - if not new_geo.is_empty: - new_geometry.append(new_geo) + # resulting paths are closed resulting into Polygons + if self.close_paths_cb.isChecked(): + new_geo = (cascaded_union(geo)).difference(self.sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + # resulting paths are unclosed resulting in a multitude of rings + else: + try: + for geo_elem in geo: + if isinstance(geo_elem, Polygon): + for ring in self.poly2rings(geo_elem): + new_geo = ring.difference(self.sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, MultiPolygon): + for poly in geo_elem: + for ring in self.poly2rings(poly): + new_geo = ring.difference(self.sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, LineString): + new_geo = geo_elem.difference(self.sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, MultiLineString): + for line_elem in geo_elem: + new_geo = line_elem.difference(self.sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + except TypeError: + if isinstance(geo, Polygon): + for ring in self.poly2rings(geo): + new_geo = ring.difference(self.sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo, LineString): + new_geo = geo.difference(self.sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo, MultiLineString): + for line_elem in geo: + new_geo = line_elem.difference(self.sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) if new_geometry: if tool == "single": @@ -620,4 +673,7 @@ class ToolSub(FlatCAMTool): self.target_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex())) self.sub_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex())) + @staticmethod + def poly2rings(poly): + return [poly.exterior] + [interior for interior in poly.interiors] # end of file