From 953be31f6eee065e028478a1f2da1adbbba2725a Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Wed, 11 Nov 2020 00:37:30 +0200 Subject: [PATCH] - finished adding new feature in Cutout Tool: cut a geometry by drilling along its path --- CHANGELOG.md | 1 + appGUI/preferences/PreferencesUIManager.py | 4 + .../tools/ToolsCutoutPrefGroupUI.py | 41 ++++++++++ appTools/ToolCutOut.py | 78 ++++++++++++++++++- appTools/ToolExtract.py | 2 - defaults.py | 3 + 6 files changed, 125 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b97745b1..8f5facb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ CHANGELOG for FlatCAM beta - updated the UI for Cutout Tool - fixed Paint Tcl command; fixes issue #437 - updated the setup_ubuntu.sh script +- finished adding new feature in Cutout Tool: cut a geometry by drilling along its path 9.11.2020 diff --git a/appGUI/preferences/PreferencesUIManager.py b/appGUI/preferences/PreferencesUIManager.py index 68e6caa8..8f1d60c1 100644 --- a/appGUI/preferences/PreferencesUIManager.py +++ b/appGUI/preferences/PreferencesUIManager.py @@ -437,6 +437,10 @@ class PreferencesUIManager: "tools_cutout_mb_dia": self.ui.tools_defaults_form.tools_cutout_group.mb_dia_entry, "tools_cutout_mb_spacing": self.ui.tools_defaults_form.tools_cutout_group.mb_spacing_entry, + "tools_cutout_drill_dia": self.ui.tools_defaults_form.tools_cutout_group.drill_dia_entry, + "tools_cutout_drill_pitch": self.ui.tools_defaults_form.tools_cutout_group.drill_pitch_entry, + "tools_cutout_drill_margin": self.ui.tools_defaults_form.tools_cutout_group.drill_margin_entry, + # Paint Area Tool "tools_paint_tooldia": self.ui.tools_defaults_form.tools_paint_group.painttooldia_entry, "tools_paint_order": self.ui.tools_defaults_form.tools_paint_group.paint_order_radio, diff --git a/appGUI/preferences/tools/ToolsCutoutPrefGroupUI.py b/appGUI/preferences/tools/ToolsCutoutPrefGroupUI.py index 14195614..16f9ddff 100644 --- a/appGUI/preferences/tools/ToolsCutoutPrefGroupUI.py +++ b/appGUI/preferences/tools/ToolsCutoutPrefGroupUI.py @@ -242,4 +242,45 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI): _("Use a big cursor when adding manual gaps.")) grid0.addWidget(self.big_cursor_cb, 19, 0, 1, 2) + # Drill Cut + # Drill Tool Diameter + self.drill_dia_entry = FCDoubleSpinner() + self.drill_dia_entry.set_precision(self.decimals) + self.drill_dia_entry.set_range(0.0000, 10000.0000) + + self.drill_dia_label = FCLabel('%s:' % _("Drill Dia")) + self.drill_dia_label.setToolTip( + _("Diameter of the tool used to cutout\n" + "the PCB by drilling.") + ) + grid0.addWidget(self.drill_dia_label, 21, 0) + grid0.addWidget(self.drill_dia_entry, 21, 1) + + # Drill Tool Pitch + self.drill_pitch_entry = FCDoubleSpinner() + self.drill_pitch_entry.set_precision(self.decimals) + self.drill_pitch_entry.set_range(0.0000, 10000.0000) + + self.drill_pitch_label = FCLabel('%s:' % _("Pitch")) + self.drill_pitch_label.setToolTip( + _("Distance between the center of\n" + "two neighboring drill holes.") + ) + grid0.addWidget(self.drill_pitch_label, 23, 0) + grid0.addWidget(self.drill_pitch_entry, 23, 1) + + # Drill Tool Margin + self.drill_margin_entry = FCDoubleSpinner() + self.drill_margin_entry.set_precision(self.decimals) + self.drill_margin_entry.set_range(0.0000, 10000.0000) + + self.drill_margin_label = FCLabel('%s:' % _("Margin")) + self.drill_margin_label.setToolTip( + _("Margin over bounds. A positive value here\n" + "will make the cutout of the PCB further from\n" + "the actual PCB border") + ) + grid0.addWidget(self.drill_margin_label, 25, 0) + grid0.addWidget(self.drill_margin_entry, 25, 1) + self.layout.addStretch() diff --git a/appTools/ToolCutOut.py b/appTools/ToolCutOut.py index 3898ab0c..dd8e86e6 100644 --- a/appTools/ToolCutOut.py +++ b/appTools/ToolCutOut.py @@ -10,7 +10,7 @@ from appTool import AppTool from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox, OptionalInputSection, FCButton, \ FCLabel -from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing, MultiLineString +from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing, MultiLineString, Point from shapely.ops import unary_union, linemerge import shapely.affinity as affinity @@ -115,6 +115,7 @@ class CutOut(AppTool): self.ui.man_geo_creation_btn.clicked.connect(self.on_manual_geo) self.ui.man_gaps_creation_btn.clicked.connect(self.on_manual_gap_click) + self.ui.drillcut_btn.clicked.connect(self.on_drill_cut_click) self.ui.reset_button.clicked.connect(self.set_tool_ui) def on_type_obj_changed(self, val): @@ -250,6 +251,10 @@ class CutOut(AppTool): self.ui.cutout_type_radio.set_value('a') self.on_cutout_type(val='a') + self.ui.drill_dia_entry.set_value(float(self.app.defaults["tools_cutout_drill_dia"])) + self.ui.drill_pitch_entry.set_value(float(self.app.defaults["tools_cutout_drill_pitch"])) + self.ui.drill_margin_entry.set_value(float(self.app.defaults["tools_cutout_drill_margin"])) + def update_ui(self, tool_dict): self.ui.obj_kind_combo.set_value(self.default_data["tools_cutout_kind"]) self.ui.big_cursor_cb.set_value(self.default_data['tools_cutout_big_cursor']) @@ -1320,6 +1325,75 @@ class CutOut(AppTool): self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + def on_drill_cut_click(self): + log.debug("Cutout.on_drill_cut_click() was launched ...") + + margin = self.ui.drill_margin_entry.get_value() + pitch = self.ui.drill_pitch_entry.get_value() + drill_dia = self.ui.drill_dia_entry.get_value() + + name = self.ui.drillcut_object_combo.currentText() + + # Get source object. + try: + obj = self.app.collection.get_by_name(str(name)) + except Exception as e: + log.debug("CutOut.on_freeform_cutout() --> %s" % str(e)) + self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), name)) + return "Could not retrieve object: %s" % name + + if obj is None: + self.app.inform.emit('[ERROR_NOTCL] %s' % + _("There is no object selected for Cutout.\nSelect one and try again.")) + return + + cut_geo = unary_union(obj.solid_geometry).buffer(margin, join_style=2).exterior + geo_length = cut_geo.length + + drill_list = [] + dist = 0 + while dist <= geo_length: + drill_list.append(cut_geo.interpolate(dist)) + dist += pitch + + if dist < geo_length: + drill_list.append(Point(list(cut_geo.coords)[-1])) + + if not drill_list: + self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Failed."), _("Could not add drills."))) + return + + tools = { + 1: { + "tooldia": drill_dia, + "drills": drill_list, + "slots": [], + "solid_geometry": [] + } + } + + formatted_name = obj.options['name'].rpartition('.')[0] + if formatted_name == '': + formatted_name = obj.options['name'] + outname = '%s_drillcut' % formatted_name + + def obj_init(obj_inst, app_inst): + obj_inst.tools = deepcopy(tools) + obj_inst.create_geometry() + obj_inst.source_file = app_inst.f_handlers.export_excellon(obj_name=outname, local_use=obj_inst, + filename=None, + use_thread=False) + + with self.app.proc_container.new(_("Working ...")): + try: + ret = self.app.app_obj.new_object("excellon", outname, obj_init) + except Exception as e: + log.error("Error on Drill Cutting Excellon object creation: %s" % str(e)) + return + + if ret != 'fail': + self.app.inform.emit('[success] %s' % _("Done.")) + def on_manual_gap_click(self): name = self.ui.man_object_combo.currentText() @@ -2451,7 +2525,7 @@ class CutoutUI: separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) grid0.addWidget(separator_line, 60, 0, 1, 2) - grid0.addWidget(FCLabel(""), 62, 0, 1, 2) + # grid0.addWidget(FCLabel(""), 62, 0, 1, 2) # Cut by Drilling Title title_drillcut_label = FCLabel("%s:" % _('Cut by Drilling')) diff --git a/appTools/ToolExtract.py b/appTools/ToolExtract.py index 7cef4bc7..2331ce90 100644 --- a/appTools/ToolExtract.py +++ b/appTools/ToolExtract.py @@ -159,7 +159,6 @@ class ToolExtract(AppTool): prop_factor = self.ui.factor_entry.get_value() / 100.0 - drills = [] tools = {} selection_index = self.ui.gerber_object_combo.currentIndex() @@ -401,7 +400,6 @@ class ToolExtract(AppTool): def obj_init(obj_inst, app_inst): obj_inst.tools = tools - obj_inst.drills = drills obj_inst.create_geometry() obj_inst.source_file = app_inst.f_handlers.export_excellon(obj_name=outname, local_use=obj_inst, filename=None, diff --git a/defaults.py b/defaults.py index 8078bc26..818b1fa3 100644 --- a/defaults.py +++ b/defaults.py @@ -503,6 +503,9 @@ class FlatCAMDefaults: "tools_cutout_gap_depth": -1.0, "tools_cutout_mb_dia": 0.6, "tools_cutout_mb_spacing": 0.3, + "tools_cutout_drill_dia": 1.0, + "tools_cutout_drill_pitch": 1.0, + "tools_cutout_drill_margin": 0.0, # Paint Tool "tools_paint_tooldia": 0.3,