- Isolation Tool - added a check for having a complete isolation
This commit is contained in:
@@ -11,6 +11,7 @@ CHANGELOG for FlatCAM beta
|
|||||||
|
|
||||||
- small change in the NCC Tool UI
|
- small change in the NCC Tool UI
|
||||||
- some strings are changed and therefore the translation strings source are updated
|
- some strings are changed and therefore the translation strings source are updated
|
||||||
|
- Isolation Tool - added a check for having a complete isolation
|
||||||
|
|
||||||
7.10.2020
|
7.10.2020
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
self.grb_circle_steps = int(self.app.defaults["gerber_circle_steps"])
|
self.grb_circle_steps = int(self.app.defaults["gerber_circle_steps"])
|
||||||
|
|
||||||
self.tooldia = None
|
self.tooldia = None
|
||||||
|
# store here the tool diameter that is guaranteed to isolate the object
|
||||||
|
self.safe_tooldia = None
|
||||||
|
|
||||||
# multiprocessing
|
# multiprocessing
|
||||||
self.pool = self.app.pool
|
self.pool = self.app.pool
|
||||||
@@ -225,6 +227,9 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
def set_tool_ui(self):
|
def set_tool_ui(self):
|
||||||
self.units = self.app.defaults['units'].upper()
|
self.units = self.app.defaults['units'].upper()
|
||||||
|
|
||||||
|
# reset the value to prepare for another isolation
|
||||||
|
self.safe_tooldia = None
|
||||||
|
|
||||||
# try to select in the Gerber combobox the active object
|
# try to select in the Gerber combobox the active object
|
||||||
try:
|
try:
|
||||||
selected_obj = self.app.collection.get_active()
|
selected_obj = self.app.collection.get_active()
|
||||||
@@ -888,6 +893,9 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def on_find_optimal_tooldia(self):
|
def on_find_optimal_tooldia(self):
|
||||||
|
self.find_safe_tooldia_worker(is_displayed=True)
|
||||||
|
|
||||||
|
def find_safe_tooldia_worker(self, is_displayed):
|
||||||
self.units = self.app.defaults['units'].upper()
|
self.units = self.app.defaults['units'].upper()
|
||||||
|
|
||||||
obj_name = self.ui.object_combo.currentText()
|
obj_name = self.ui.object_combo.currentText()
|
||||||
@@ -903,85 +911,109 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
|
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
|
||||||
return
|
return
|
||||||
|
|
||||||
proc = self.app.proc_container.new(_("Working..."))
|
def job_thread(app_obj, is_display):
|
||||||
|
with self.app.proc_container.new(_("Working...")) as proc:
|
||||||
|
try:
|
||||||
|
old_disp_number = 0
|
||||||
|
pol_nr = 0
|
||||||
|
app_obj.proc_container.update_view_text(' %d%%' % 0)
|
||||||
|
total_geo = []
|
||||||
|
|
||||||
def job_thread(app_obj):
|
for ap in list(fcobj.apertures.keys()):
|
||||||
try:
|
if 'geometry' in fcobj.apertures[ap]:
|
||||||
old_disp_number = 0
|
for geo_el in fcobj.apertures[ap]['geometry']:
|
||||||
pol_nr = 0
|
if self.app.abort_flag:
|
||||||
app_obj.proc_container.update_view_text(' %d%%' % 0)
|
# graceful abort requested by the user
|
||||||
total_geo = []
|
raise grace
|
||||||
|
|
||||||
for ap in list(fcobj.apertures.keys()):
|
if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
|
||||||
if 'geometry' in fcobj.apertures[ap]:
|
total_geo.append(geo_el['solid'])
|
||||||
for geo_el in fcobj.apertures[ap]['geometry']:
|
|
||||||
|
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:
|
if self.app.abort_flag:
|
||||||
# graceful abort requested by the user
|
# graceful abort requested by the user
|
||||||
raise grace
|
raise grace
|
||||||
|
|
||||||
if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
|
# minimize the number of distances by not taking into considerations those that are too small
|
||||||
total_geo.append(geo_el['solid'])
|
dist = geo.distance(s_geo)
|
||||||
|
dist = float('%.*f' % (self.decimals, dist))
|
||||||
|
loc_1, loc_2 = nearest_points(geo, s_geo)
|
||||||
|
|
||||||
total_geo = MultiPolygon(total_geo)
|
proc_loc = (
|
||||||
total_geo = total_geo.buffer(0)
|
(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)))
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
if dist in min_dict:
|
||||||
__ = iter(total_geo)
|
min_dict[dist].append(proc_loc)
|
||||||
geo_len = len(total_geo)
|
else:
|
||||||
geo_len = (geo_len * (geo_len - 1)) / 2
|
min_dict[dist] = [proc_loc]
|
||||||
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 = {}
|
pol_nr += 1
|
||||||
idx = 1
|
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
|
||||||
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
|
if old_disp_number < disp_number <= 100:
|
||||||
dist = geo.distance(s_geo)
|
app_obj.proc_container.update_view_text(' %d%%' % disp_number)
|
||||||
dist = float('%.*f' % (self.decimals, dist))
|
old_disp_number = disp_number
|
||||||
loc_1, loc_2 = nearest_points(geo, s_geo)
|
idx += 1
|
||||||
|
|
||||||
proc_loc = (
|
min_list = list(min_dict.keys())
|
||||||
(float('%.*f' % (self.decimals, loc_1.x)), float('%.*f' % (self.decimals, loc_1.y))),
|
min_dist = min(min_list)
|
||||||
(float('%.*f' % (self.decimals, loc_2.x)), float('%.*f' % (self.decimals, loc_2.y)))
|
|
||||||
)
|
|
||||||
|
|
||||||
if dist in min_dict:
|
min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
|
||||||
min_dict[dist].append(proc_loc)
|
self.safe_tooldia = min_dist_truncated
|
||||||
else:
|
|
||||||
min_dict[dist] = [proc_loc]
|
|
||||||
|
|
||||||
pol_nr += 1
|
if is_display:
|
||||||
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
|
self.optimal_found_sig.emit(min_dist_truncated)
|
||||||
|
|
||||||
if old_disp_number < disp_number <= 100:
|
app_obj.inform.emit('[success] %s: %s %s' %
|
||||||
app_obj.proc_container.update_view_text(' %d%%' % disp_number)
|
(_("Optimal tool diameter found"), str(min_dist_truncated),
|
||||||
old_disp_number = disp_number
|
self.units.lower()))
|
||||||
idx += 1
|
else:
|
||||||
|
if self.safe_tooldia:
|
||||||
|
# find the selected tool ID's
|
||||||
|
sorted_tools = []
|
||||||
|
table_items = self.ui.tools_table.selectedItems()
|
||||||
|
sel_rows = {t.row() for t in table_items}
|
||||||
|
for row in sel_rows:
|
||||||
|
tid = int(self.ui.tools_table.item(row, 3).text())
|
||||||
|
sorted_tools.append(tid)
|
||||||
|
if not sorted_tools:
|
||||||
|
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table."))
|
||||||
|
return 'fail'
|
||||||
|
|
||||||
min_list = list(min_dict.keys())
|
# check if the tools diameters are less then the safe tool diameter
|
||||||
min_dist = min(min_list)
|
for tool in sorted_tools:
|
||||||
|
tool_dia = float(self.iso_tools[tool]['tooldia'])
|
||||||
|
if tool_dia > self.safe_tooldia:
|
||||||
|
msg = _("Incomplete isolation. "
|
||||||
|
"At least one tool could not do a complete isolation.")
|
||||||
|
self.app.inform.emit('[WARNING] %s' % msg)
|
||||||
|
break
|
||||||
|
|
||||||
min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
|
# reset the value to prepare for another isolation
|
||||||
|
self.safe_tooldia = None
|
||||||
|
except Exception as ee:
|
||||||
|
log.debug(str(ee))
|
||||||
|
return
|
||||||
|
|
||||||
self.optimal_found_sig.emit(min_dist_truncated)
|
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app, is_displayed]})
|
||||||
|
|
||||||
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, custom_dia=None):
|
def on_tool_add(self, custom_dia=None):
|
||||||
self.blockSignals(True)
|
self.blockSignals(True)
|
||||||
@@ -1335,12 +1367,14 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
self.grb_obj = self.app.collection.get_by_name(self.obj_name)
|
self.grb_obj = self.app.collection.get_by_name(self.obj_name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name)))
|
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name)))
|
||||||
return "Could not retrieve object: %s with error: %s" % (self.obj_name, str(e))
|
return
|
||||||
|
|
||||||
if self.grb_obj is None:
|
if self.grb_obj is None:
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(self.obj_name)))
|
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(self.obj_name)))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.find_safe_tooldia_worker(is_displayed=False)
|
||||||
|
|
||||||
def worker_task(iso_obj):
|
def worker_task(iso_obj):
|
||||||
with self.app.proc_container.new(_("Isolating...")):
|
with self.app.proc_container.new(_("Isolating...")):
|
||||||
self.isolate_handler(iso_obj)
|
self.isolate_handler(iso_obj)
|
||||||
|
|||||||
Reference in New Issue
Block a user