ソースを参照

- Isolation Tool - added ability to find the tool diameter that will guarantee total isolation of the currently selected Gerber object

Marius Stanciu 5 年 前
コミット
be01e00898
2 ファイル変更103 行追加3 行削除
  1. 1 0
      CHANGELOG.md
  2. 102 3
      appTools/ToolIsolation.py

+ 1 - 0
CHANGELOG.md

@@ -17,6 +17,7 @@ CHANGELOG for FlatCAM beta
 - Drilling Tool - when replacing Tools if more than one tool for one diameter is found, the application exit the process and display an error in status bar; some minor fixes
 - Isolation Tool - remade the UI
 - Isolation Tool - modified the add new tool method to search first in Tools Database  for a suitable tool
+- Isolation Tool - added ability to find the tool diameter that will guarantee total isolation of the currently selected Gerber object
 
 25.08.2020
 

+ 102 - 3
appTools/ToolIsolation.py

@@ -11,15 +11,15 @@ from appTool import AppTool
 from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCInputDialog, FCButton, \
     FCComboBox, OptionalInputSection, FCSpinner, FCLabel
 from appParsers.ParseGerber import Gerber
+from camlib import grace
 
 from copy import deepcopy
 
 import numpy as np
-import math
 import simplejson as json
 import sys
 
-from shapely.ops import cascaded_union
+from shapely.ops import cascaded_union, nearest_points
 from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing, Point
 
 from matplotlib.backend_bases import KeyEvent as mpl_key_event
@@ -38,6 +38,8 @@ log = logging.getLogger('base')
 
 class ToolIsolation(AppTool, Gerber):
 
+    optimal_found_sig = QtCore.pyqtSignal(float)
+
     def __init__(self, app):
         self.app = app
         self.decimals = self.app.decimals
@@ -193,6 +195,9 @@ class ToolIsolation(AppTool, Gerber):
         self.t_ui.add_newtool_button.clicked.connect(self.on_tool_add)
         self.t_ui.deltool_btn.clicked.connect(self.on_tool_delete)
 
+        self.t_ui.find_optimal_button.clicked.connect(self.on_find_optimal_tooldia)
+        self.optimal_found_sig.connect(lambda val: self.t_ui.new_tooldia_entry.set_value(float(val)))
+
         self.t_ui.reference_combo_type.currentIndexChanged.connect(self.on_reference_combo_changed)
         self.t_ui.select_combo.currentIndexChanged.connect(self.on_toggle_reference)
 
@@ -889,7 +894,100 @@ class ToolIsolation(AppTool, Gerber):
             })
 
     def on_find_optimal_tooldia(self):
-        pass
+        self.units = self.app.defaults['units'].upper()
+
+        obj_name = self.t_ui.object_combo.currentText()
+
+        # Get source object.
+        try:
+            fcobj = self.app.collection.get_by_name(obj_name)
+        except Exception:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name)))
+            return
+
+        if fcobj is None:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
+            return
+
+        proc = self.app.proc_container.new(_("Working..."))
+
+        def job_thread(app_obj):
+            try:
+                old_disp_number = 0
+                pol_nr = 0
+                app_obj.proc_container.update_view_text(' %d%%' % 0)
+                total_geo = []
+
+                for ap in list(fcobj.apertures.keys()):
+                    if 'geometry' in fcobj.apertures[ap]:
+                        for geo_el in fcobj.apertures[ap]['geometry']:
+                            if self.app.abort_flag:
+                                # graceful abort requested by the user
+                                raise grace
+
+                            if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
+                                total_geo.append(geo_el['solid'])
+
+                total_geo = MultiPolygon(total_geo)
+                total_geo = total_geo.buffer(0)
+
+                try:
+                    __ = iter(total_geo)
+                    geo_len = len(total_geo)
+                    geo_len = (geo_len * (geo_len - 1)) / 2
+                except TypeError:
+                    app_obj.inform.emit('[ERROR_NOTCL] %s' %
+                                        _("The Gerber object has one Polygon as geometry.\n"
+                                          "There are no distances between geometry elements to be found."))
+                    return 'fail'
+
+                min_dict = {}
+                idx = 1
+                for geo in total_geo:
+                    for s_geo in total_geo[idx:]:
+                        if self.app.abort_flag:
+                            # graceful abort requested by the user
+                            raise grace
+
+                        # minimize the number of distances by not taking into considerations those that are too small
+                        dist = geo.distance(s_geo)
+                        dist = float('%.*f' % (self.decimals, dist))
+                        loc_1, loc_2 = nearest_points(geo, s_geo)
+
+                        proc_loc = (
+                            (float('%.*f' % (self.decimals, loc_1.x)), float('%.*f' % (self.decimals, loc_1.y))),
+                            (float('%.*f' % (self.decimals, loc_2.x)), float('%.*f' % (self.decimals, loc_2.y)))
+                        )
+
+                        if dist in min_dict:
+                            min_dict[dist].append(proc_loc)
+                        else:
+                            min_dict[dist] = [proc_loc]
+
+                        pol_nr += 1
+                        disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
+
+                        if old_disp_number < disp_number <= 100:
+                            app_obj.proc_container.update_view_text(' %d%%' % disp_number)
+                            old_disp_number = disp_number
+                    idx += 1
+
+                min_list = list(min_dict.keys())
+                min_dist = min(min_list)
+
+                min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
+
+                self.optimal_found_sig.emit(min_dist_truncated)
+
+                app_obj.inform.emit('[success] %s: %s %s' %
+                                    (_("Optimal tool diameter found"), str(min_dist_truncated), self.units.lower()))
+            except Exception as ee:
+                proc.done()
+                log.debug(str(ee))
+                return
+            proc.done()
+
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
 
     def on_tool_add(self):
         self.blockSignals(True)
@@ -2972,6 +3070,7 @@ class IsoUI:
 
         # Find Optimal Tooldia
         self.find_optimal_button = FCButton(_('Find Optimal'))
+        self.find_optimal_button.setIcon(QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'))
         self.find_optimal_button.setToolTip(
             _("Find a tool diameter that is guaranteed\n"
               "to do a complete isolation.")