- code cleanup in Isolation Tool
This commit is contained in:
@@ -12,20 +12,17 @@ from AppGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, F
|
|||||||
FCComboBox, OptionalHideInputSection, FCSpinner
|
FCComboBox, OptionalHideInputSection, FCSpinner
|
||||||
from AppParsers.ParseGerber import Gerber
|
from AppParsers.ParseGerber import Gerber
|
||||||
|
|
||||||
from camlib import grace
|
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import math
|
import math
|
||||||
from shapely.geometry import base
|
|
||||||
from shapely.ops import cascaded_union
|
from shapely.ops import cascaded_union
|
||||||
from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing
|
from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing
|
||||||
|
|
||||||
from matplotlib.backend_bases import KeyEvent as mpl_key_event
|
from matplotlib.backend_bases import KeyEvent as mpl_key_event
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
|
||||||
import gettext
|
import gettext
|
||||||
import AppTranslation as fcTranslate
|
import AppTranslation as fcTranslate
|
||||||
import builtins
|
import builtins
|
||||||
@@ -1603,110 +1600,6 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
|
|
||||||
self.app.worker_task.emit({'fcn': buffer_task, 'params': []})
|
self.app.worker_task.emit({'fcn': buffer_task, 'params': []})
|
||||||
|
|
||||||
def on_isolate_click(self):
|
|
||||||
"""
|
|
||||||
Slot for clicking signal of the self.generate_iso_button
|
|
||||||
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
|
|
||||||
# init values for the next usage
|
|
||||||
self.reset_usage()
|
|
||||||
|
|
||||||
self.app.defaults.report_usage("on_paint_button_click")
|
|
||||||
|
|
||||||
self.grb_circle_steps = int(self.app.defaults["gerber_circle_steps"])
|
|
||||||
self.obj_name = self.object_combo.currentText()
|
|
||||||
|
|
||||||
# Get source object.
|
|
||||||
try:
|
|
||||||
self.grb_obj = self.app.collection.get_by_name(self.obj_name)
|
|
||||||
except Exception as e:
|
|
||||||
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))
|
|
||||||
|
|
||||||
if self.grb_obj is None:
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(self.obj_name)))
|
|
||||||
return
|
|
||||||
|
|
||||||
# use the selected tools in the tool table; get diameters for isolation
|
|
||||||
self.iso_dia_list = []
|
|
||||||
# use the selected tools in the tool table; get diameters for non-copper clear
|
|
||||||
self.ncc_dia_list = []
|
|
||||||
|
|
||||||
if self.tools_table.selectedItems():
|
|
||||||
for x in self.tools_table.selectedItems():
|
|
||||||
try:
|
|
||||||
self.tooldia = float(self.tools_table.item(x.row(), 1).text())
|
|
||||||
except ValueError:
|
|
||||||
# try to convert comma to decimal point. if it's still not working error message and return
|
|
||||||
try:
|
|
||||||
self.tooldia = float(self.tools_table.item(x.row(), 1).text().replace(',', '.'))
|
|
||||||
except ValueError:
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong Tool Dia value format entered, "
|
|
||||||
"use a number."))
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.iso_dia_list.append(self.tooldia)
|
|
||||||
else:
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table."))
|
|
||||||
return
|
|
||||||
|
|
||||||
self.o_name = '%s_ncc' % self.obj_name
|
|
||||||
|
|
||||||
self.select_method = self.select_combo.get_value()
|
|
||||||
if self.select_method == _('Itself'):
|
|
||||||
self.bound_obj_name = self.object_combo.currentText()
|
|
||||||
# Get source object.
|
|
||||||
try:
|
|
||||||
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
|
|
||||||
except Exception as e:
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.bound_obj_name))
|
|
||||||
return "Could not retrieve object: %s with error: %s" % (self.bound_obj_name, str(e))
|
|
||||||
|
|
||||||
self.clear_copper(ncc_obj=self.grb_obj,
|
|
||||||
ncctooldia=self.ncc_dia_list,
|
|
||||||
isotooldia=self.iso_dia_list,
|
|
||||||
outname=self.o_name)
|
|
||||||
elif self.select_method == _("Area Selection"):
|
|
||||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
|
|
||||||
|
|
||||||
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)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
|
||||||
else:
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.app.mp)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.app.mm)
|
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.app.mr)
|
|
||||||
|
|
||||||
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
|
|
||||||
self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
|
|
||||||
self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press)
|
|
||||||
|
|
||||||
elif self.select_method == _("Reference Object"):
|
|
||||||
self.bound_obj_name = self.reference_combo.currentText()
|
|
||||||
# Get source object.
|
|
||||||
try:
|
|
||||||
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
|
|
||||||
except Exception as e:
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.bound_obj_name))
|
|
||||||
return "Could not retrieve object: %s. Error: %s" % (self.bound_obj_name, str(e))
|
|
||||||
|
|
||||||
self.clear_copper(ncc_obj=self.grb_obj,
|
|
||||||
sel_obj=self.bound_obj,
|
|
||||||
ncctooldia=self.ncc_dia_list,
|
|
||||||
isotooldia=self.iso_dia_list,
|
|
||||||
outname=self.o_name)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ###########################################
|
|
||||||
# ###########################################
|
|
||||||
# ###########################################
|
|
||||||
# ###########################################
|
|
||||||
|
|
||||||
def on_iso_button_click(self, *args):
|
def on_iso_button_click(self, *args):
|
||||||
|
|
||||||
self.obj_name = self.object_combo.currentText()
|
self.obj_name = self.object_combo.currentText()
|
||||||
@@ -1799,7 +1692,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
else:
|
else:
|
||||||
self.grid_status_memory = False
|
self.grid_status_memory = False
|
||||||
|
|
||||||
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_click_release)
|
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_poly_mouse_click_release)
|
||||||
self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press)
|
self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press)
|
||||||
|
|
||||||
if self.app.is_legacy is False:
|
if self.app.is_legacy is False:
|
||||||
@@ -1941,8 +1834,8 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
if len(self.iso_tools) > 1:
|
if len(self.iso_tools) > 1:
|
||||||
geo_obj.multigeo = True
|
geo_obj.multigeo = True
|
||||||
else:
|
else:
|
||||||
passes = float(self.iso_tools[0]['data']['tools_iso_passes'])
|
passes_no = float(self.iso_tools[0]['data']['tools_iso_passes'])
|
||||||
geo_obj.multigeo = True if passes > 1 else False
|
geo_obj.multigeo = True if passes_no > 1 else False
|
||||||
|
|
||||||
# detect if solid_geometry is empty and this require list flattening which is "heavy"
|
# detect if solid_geometry is empty and this require list flattening which is "heavy"
|
||||||
# or just looking in the lists (they are one level depth) and if any is not empty
|
# or just looking in the lists (they are one level depth) and if any is not empty
|
||||||
@@ -2096,7 +1989,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
return 'fail'
|
return 'fail'
|
||||||
else:
|
else:
|
||||||
fc_obj.inform.emit('[success] %s: %s' %
|
fc_obj.inform.emit('[success] %s: %s' %
|
||||||
(_("Isolation geometry created"), geo_obj.options["name"]))
|
(_("Isolation geometry created"), geo_obj.options["name"]))
|
||||||
geo_obj.multigeo = False
|
geo_obj.multigeo = False
|
||||||
|
|
||||||
# TODO: Do something if this is None. Offer changing name?
|
# TODO: Do something if this is None. Offer changing name?
|
||||||
@@ -2215,7 +2108,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
new_geometry.append(new_geo)
|
new_geometry.append(new_geo)
|
||||||
return new_geometry
|
return new_geometry
|
||||||
|
|
||||||
def on_mouse_click_release(self, event):
|
def on_poly_mouse_click_release(self, event):
|
||||||
if self.app.is_legacy is False:
|
if self.app.is_legacy is False:
|
||||||
event_pos = event.pos
|
event_pos = event.pos
|
||||||
right_button = 2
|
right_button = 2
|
||||||
@@ -2278,7 +2171,7 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
self.app.ui.grid_snap_btn.trigger()
|
self.app.ui.grid_snap_btn.trigger()
|
||||||
|
|
||||||
if self.app.is_legacy is False:
|
if self.app.is_legacy is False:
|
||||||
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release)
|
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_poly_mouse_click_release)
|
||||||
self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_pres)
|
self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_pres)
|
||||||
else:
|
else:
|
||||||
self.app.plotcanvas.graph_event_disconnect(self.mr)
|
self.app.plotcanvas.graph_event_disconnect(self.mr)
|
||||||
@@ -2365,14 +2258,6 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
else:
|
else:
|
||||||
self.app.inform.emit(_("No polygon in selection."))
|
self.app.inform.emit(_("No polygon in selection."))
|
||||||
|
|
||||||
# ###########################################
|
|
||||||
# ###########################################
|
|
||||||
# ###########################################
|
|
||||||
# ###########################################
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# To be called after clicking on the plot.
|
# To be called after clicking on the plot.
|
||||||
def on_mouse_release(self, event):
|
def on_mouse_release(self, event):
|
||||||
if self.app.is_legacy is False:
|
if self.app.is_legacy is False:
|
||||||
@@ -2605,785 +2490,6 @@ class ToolIsolation(AppTool, Gerber):
|
|||||||
self.delete_moving_selection_shape()
|
self.delete_moving_selection_shape()
|
||||||
self.delete_tool_selection_shape()
|
self.delete_tool_selection_shape()
|
||||||
|
|
||||||
def get_tool_empty_area(self, name, ncc_obj, geo_obj, isotooldia, has_offset, ncc_offset, ncc_margin,
|
|
||||||
bounding_box, tools_storage):
|
|
||||||
"""
|
|
||||||
Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry.
|
|
||||||
|
|
||||||
:param name:
|
|
||||||
:param ncc_obj:
|
|
||||||
:param geo_obj:
|
|
||||||
:param isotooldia:
|
|
||||||
:param has_offset:
|
|
||||||
:param ncc_offset:
|
|
||||||
:param ncc_margin:
|
|
||||||
:param bounding_box:
|
|
||||||
:param tools_storage:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
log.debug("NCC Tool. Calculate 'empty' area.")
|
|
||||||
self.app.inform.emit(_("NCC Tool. Calculate 'empty' area."))
|
|
||||||
|
|
||||||
# a flag to signal that the isolation is broken by the bounding box in 'area' and 'box' cases
|
|
||||||
# will store the number of tools for which the isolation is broken
|
|
||||||
warning_flag = 0
|
|
||||||
|
|
||||||
if ncc_obj.kind == 'gerber' and isotooldia:
|
|
||||||
isolated_geo = []
|
|
||||||
|
|
||||||
# unfortunately for this function to work time efficient,
|
|
||||||
# if the Gerber was loaded without buffering then it require the buffering now.
|
|
||||||
# TODO 'buffering status' should be a property of the object not the project property
|
|
||||||
if self.app.defaults['gerber_buffering'] == 'no':
|
|
||||||
self.solid_geometry = ncc_obj.solid_geometry.buffer(0)
|
|
||||||
else:
|
|
||||||
self.solid_geometry = ncc_obj.solid_geometry
|
|
||||||
|
|
||||||
# if milling type is climb then the move is counter-clockwise around features
|
|
||||||
milling_type = self.milling_type_radio.get_value()
|
|
||||||
|
|
||||||
for tool_iso in isotooldia:
|
|
||||||
new_geometry = []
|
|
||||||
|
|
||||||
if milling_type == 'cl':
|
|
||||||
isolated_geo = self.generate_envelope(tool_iso / 2, 1)
|
|
||||||
else:
|
|
||||||
isolated_geo = self.generate_envelope(tool_iso / 2, 0)
|
|
||||||
|
|
||||||
if isolated_geo == 'fail':
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s %s' %
|
|
||||||
(_("Isolation geometry could not be generated."), str(tool_iso)))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if ncc_margin < tool_iso:
|
|
||||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Isolation geometry is broken. Margin is less "
|
|
||||||
"than isolation tool diameter."))
|
|
||||||
try:
|
|
||||||
for geo_elem in isolated_geo:
|
|
||||||
# provide the app with a way to process the GUI events when in a blocking loop
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
if isinstance(geo_elem, Polygon):
|
|
||||||
for ring in self.poly2rings(geo_elem):
|
|
||||||
new_geo = ring.intersection(bounding_box)
|
|
||||||
if new_geo and 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.intersection(bounding_box)
|
|
||||||
if new_geo and not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
elif isinstance(geo_elem, LineString):
|
|
||||||
new_geo = geo_elem.intersection(bounding_box)
|
|
||||||
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.intersection(bounding_box)
|
|
||||||
if new_geo and not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
except TypeError:
|
|
||||||
if isinstance(isolated_geo, Polygon):
|
|
||||||
for ring in self.poly2rings(isolated_geo):
|
|
||||||
new_geo = ring.intersection(bounding_box)
|
|
||||||
if new_geo:
|
|
||||||
if not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
elif isinstance(isolated_geo, LineString):
|
|
||||||
new_geo = isolated_geo.intersection(bounding_box)
|
|
||||||
if new_geo and not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
elif isinstance(isolated_geo, MultiLineString):
|
|
||||||
for line_elem in isolated_geo:
|
|
||||||
new_geo = line_elem.intersection(bounding_box)
|
|
||||||
if new_geo and not new_geo.is_empty:
|
|
||||||
new_geometry.append(new_geo)
|
|
||||||
|
|
||||||
# a MultiLineString geometry element will show that the isolation is broken for this tool
|
|
||||||
for geo_e in new_geometry:
|
|
||||||
if type(geo_e) == MultiLineString:
|
|
||||||
warning_flag += 1
|
|
||||||
break
|
|
||||||
|
|
||||||
current_uid = 0
|
|
||||||
for k, v in tools_storage.items():
|
|
||||||
if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
|
|
||||||
tool_iso)):
|
|
||||||
current_uid = int(k)
|
|
||||||
# add the solid_geometry to the current too in self.paint_tools dictionary
|
|
||||||
# and then reset the temporary list that stored that solid_geometry
|
|
||||||
v['solid_geometry'] = deepcopy(new_geometry)
|
|
||||||
v['data']['name'] = name
|
|
||||||
break
|
|
||||||
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
|
||||||
|
|
||||||
sol_geo = cascaded_union(isolated_geo)
|
|
||||||
if has_offset is True:
|
|
||||||
self.app.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
|
|
||||||
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
|
||||||
self.app.inform.emit('[success] %s ...' % _("Buffering finished"))
|
|
||||||
|
|
||||||
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
|
|
||||||
if empty == 'fail':
|
|
||||||
return 'fail'
|
|
||||||
|
|
||||||
if empty.is_empty:
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
||||||
_("Isolation geometry is broken. Margin is less than isolation tool diameter."))
|
|
||||||
return 'fail'
|
|
||||||
else:
|
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _('The selected object is not suitable for copper clearing.'))
|
|
||||||
return 'fail'
|
|
||||||
|
|
||||||
if type(empty) is Polygon:
|
|
||||||
empty = MultiPolygon([empty])
|
|
||||||
|
|
||||||
log.debug("NCC Tool. Finished calculation of 'empty' area.")
|
|
||||||
self.app.inform.emit(_("NCC Tool. Finished calculation of 'empty' area."))
|
|
||||||
|
|
||||||
return empty, warning_flag
|
|
||||||
|
|
||||||
def clear_copper(self, ncc_obj, sel_obj=None, ncctooldia=None, isotooldia=None, outname=None, order=None,
|
|
||||||
tools_storage=None, run_threaded=True):
|
|
||||||
"""
|
|
||||||
Clear the excess copper from the entire object.
|
|
||||||
|
|
||||||
:param ncc_obj: ncc cleared object
|
|
||||||
:param sel_obj:
|
|
||||||
:param ncctooldia: a tuple or single element made out of diameters of the tools to be used to ncc clear
|
|
||||||
:param isotooldia: a tuple or single element made out of diameters of the tools to be used for isolation
|
|
||||||
:param outname: name of the resulting object
|
|
||||||
:param order: Tools order
|
|
||||||
:param tools_storage: whether to use the current tools_storage self.iso_tools or a different one.
|
|
||||||
Usage of the different one is related to when this function is called
|
|
||||||
from a TcL command.
|
|
||||||
|
|
||||||
:param run_threaded: If True the method will be run in a threaded way suitable for GUI usage; if False
|
|
||||||
it will run non-threaded for TclShell usage
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
log.debug("Executing the handler ...")
|
|
||||||
|
|
||||||
if run_threaded:
|
|
||||||
proc = self.app.proc_container.new(_("Non-Copper clearing ..."))
|
|
||||||
else:
|
|
||||||
self.app.proc_container.view.set_busy(_("Non-Copper clearing ..."))
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
# ######################################################################################################
|
|
||||||
# ######################### Read the parameters ########################################################
|
|
||||||
# ######################################################################################################
|
|
||||||
|
|
||||||
units = self.app.defaults['units']
|
|
||||||
order = order if order else self.order_radio.get_value()
|
|
||||||
ncc_select = self.select_combo.get_value()
|
|
||||||
rest_machining_choice = self.rest_cb.get_value()
|
|
||||||
|
|
||||||
# determine if to use the progressive plotting
|
|
||||||
prog_plot = True if self.app.defaults["tools_iso_plotting"] == 'progressive' else False
|
|
||||||
tools_storage = tools_storage if tools_storage is not None else self.iso_tools
|
|
||||||
|
|
||||||
# ######################################################################################################
|
|
||||||
# # Read the tooldia parameter and create a sorted list out them - they may be more than one diameter ##
|
|
||||||
# ######################################################################################################
|
|
||||||
sorted_clear_tools = []
|
|
||||||
if ncctooldia is not None:
|
|
||||||
try:
|
|
||||||
sorted_clear_tools = [float(eval(dia)) for dia in ncctooldia.split(",") if dia != '']
|
|
||||||
except AttributeError:
|
|
||||||
if not isinstance(ncctooldia, list):
|
|
||||||
sorted_clear_tools = [float(ncctooldia)]
|
|
||||||
else:
|
|
||||||
sorted_clear_tools = ncctooldia
|
|
||||||
else:
|
|
||||||
# for row in range(self.tools_table.rowCount()):
|
|
||||||
# if self.tools_table.cellWidget(row, 1).currentText() == 'clear_op':
|
|
||||||
# sorted_clear_tools.append(float(self.tools_table.item(row, 1).text()))
|
|
||||||
for tooluid in self.iso_tools:
|
|
||||||
if self.iso_tools[tooluid]['data']['tools_nccoperation'] == 'clear':
|
|
||||||
sorted_clear_tools.append(self.iso_tools[tooluid]['tooldia'])
|
|
||||||
|
|
||||||
# ########################################################################################################
|
|
||||||
# set the name for the future Geometry object
|
|
||||||
# I do it here because it is also stored inside the gen_clear_area() and gen_clear_area_rest() methods
|
|
||||||
# ########################################################################################################
|
|
||||||
name = outname if outname is not None else self.obj_name + "_ncc"
|
|
||||||
|
|
||||||
# ########################################################################################################
|
|
||||||
# ######### #####Initializes the new geometry object #####################################################
|
|
||||||
# ########################################################################################################
|
|
||||||
def gen_clear_area(geo_obj, app_obj):
|
|
||||||
log.debug("NCC Tool. Normal copper clearing task started.")
|
|
||||||
self.app.inform.emit(_("NCC Tool. Finished non-copper polygons. Normal copper clearing task started."))
|
|
||||||
|
|
||||||
# provide the app with a way to process the GUI events when in a blocking loop
|
|
||||||
if not run_threaded:
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
# a flag to signal that the isolation is broken by the bounding box in 'area' and 'box' cases
|
|
||||||
# will store the number of tools for which the isolation is broken
|
|
||||||
warning_flag = 0
|
|
||||||
|
|
||||||
if order == 'fwd':
|
|
||||||
sorted_clear_tools.sort(reverse=False)
|
|
||||||
elif order == 'rev':
|
|
||||||
sorted_clear_tools.sort(reverse=True)
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
|
|
||||||
cleared_geo = []
|
|
||||||
cleared = MultiPolygon() # Already cleared area
|
|
||||||
app_obj.poly_not_cleared = False # flag for polygons not cleared
|
|
||||||
|
|
||||||
# Generate area for each tool
|
|
||||||
offset = sum(sorted_clear_tools)
|
|
||||||
current_uid = int(1)
|
|
||||||
# try:
|
|
||||||
# tool = eval(self.app.defaults["tools_ncctools"])[0]
|
|
||||||
# except TypeError:
|
|
||||||
# tool = eval(self.app.defaults["tools_ncctools"])
|
|
||||||
|
|
||||||
if ncc_select == _("Reference Object"):
|
|
||||||
bbox_geo, bbox_kind = self.calculate_bounding_box(
|
|
||||||
ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select)
|
|
||||||
else:
|
|
||||||
bbox_geo, bbox_kind = self.calculate_bounding_box(ncc_obj=ncc_obj, ncc_select=ncc_select)
|
|
||||||
|
|
||||||
if bbox_geo is None and bbox_kind is None:
|
|
||||||
self.app.inform.emit("[ERROR_NOTCL] %s" % _("NCC Tool failed creating bounding box."))
|
|
||||||
return "fail"
|
|
||||||
|
|
||||||
# COPPER CLEARING with tools marked for CLEAR#
|
|
||||||
for tool in sorted_clear_tools:
|
|
||||||
log.debug("Starting geometry processing for tool: %s" % str(tool))
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
# provide the app with a way to process the GUI events when in a blocking loop
|
|
||||||
if not run_threaded:
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
app_obj.inform.emit('[success] %s = %s%s %s' % (
|
|
||||||
_('NCC Tool clearing with tool diameter'), str(tool), units.lower(), _('started.'))
|
|
||||||
)
|
|
||||||
app_obj.proc_container.update_view_text(' %d%%' % 0)
|
|
||||||
|
|
||||||
tool_uid = 0 # find the current tool_uid
|
|
||||||
for k, v in self.iso_tools.items():
|
|
||||||
if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool)):
|
|
||||||
tool_uid = int(k)
|
|
||||||
break
|
|
||||||
|
|
||||||
# parameters that are particular to the current tool
|
|
||||||
ncc_overlap = float(self.iso_tools[tool_uid]["data"]["tools_nccoverlap"]) / 100.0
|
|
||||||
ncc_margin = float(self.iso_tools[tool_uid]["data"]["tools_nccmargin"])
|
|
||||||
ncc_method = self.iso_tools[tool_uid]["data"]["tools_nccmethod"]
|
|
||||||
ncc_connect = self.iso_tools[tool_uid]["data"]["tools_nccconnect"]
|
|
||||||
ncc_contour = self.iso_tools[tool_uid]["data"]["tools_ncccontour"]
|
|
||||||
has_offset = self.iso_tools[tool_uid]["data"]["tools_ncc_offset_choice"]
|
|
||||||
ncc_offset = float(self.iso_tools[tool_uid]["data"]["tools_ncc_offset_value"])
|
|
||||||
|
|
||||||
# Get remaining tools offset
|
|
||||||
offset -= (tool - 1e-12)
|
|
||||||
|
|
||||||
# Bounding box for current tool
|
|
||||||
bbox = self.apply_margin_to_bounding_box(bbox=bbox_geo, box_kind=bbox_kind,
|
|
||||||
ncc_select=ncc_select, ncc_margin=ncc_margin)
|
|
||||||
|
|
||||||
# Area to clear
|
|
||||||
empty, warning_flag = self.get_tool_empty_area(name=name, ncc_obj=ncc_obj, geo_obj=geo_obj,
|
|
||||||
isotooldia=isotooldia, ncc_margin=ncc_margin,
|
|
||||||
has_offset=has_offset, ncc_offset=ncc_offset,
|
|
||||||
tools_storage=tools_storage, bounding_box=bbox)
|
|
||||||
|
|
||||||
area = empty.buffer(-offset)
|
|
||||||
try:
|
|
||||||
area = area.difference(cleared)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Transform area to MultiPolygon
|
|
||||||
if isinstance(area, Polygon):
|
|
||||||
area = MultiPolygon([area])
|
|
||||||
|
|
||||||
# variables to display the percentage of work done
|
|
||||||
geo_len = len(area.geoms)
|
|
||||||
|
|
||||||
old_disp_number = 0
|
|
||||||
log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
|
|
||||||
|
|
||||||
cleared_geo[:] = []
|
|
||||||
if area.geoms:
|
|
||||||
if len(area.geoms) > 0:
|
|
||||||
pol_nr = 0
|
|
||||||
for p in area.geoms:
|
|
||||||
# provide the app with a way to process the GUI events when in a blocking loop
|
|
||||||
if not run_threaded:
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
# clean the polygon
|
|
||||||
p = p.buffer(0)
|
|
||||||
|
|
||||||
if p is not None and p.is_valid:
|
|
||||||
poly_failed = 0
|
|
||||||
try:
|
|
||||||
for pol in p:
|
|
||||||
if pol is not None and isinstance(pol, Polygon):
|
|
||||||
res = self.clear_polygon_worker(pol=pol, tooldia=tool,
|
|
||||||
ncc_method=ncc_method,
|
|
||||||
ncc_overlap=ncc_overlap,
|
|
||||||
ncc_connect=ncc_connect,
|
|
||||||
ncc_contour=ncc_contour,
|
|
||||||
prog_plot=prog_plot)
|
|
||||||
if res is not None:
|
|
||||||
cleared_geo += res
|
|
||||||
else:
|
|
||||||
poly_failed += 1
|
|
||||||
else:
|
|
||||||
log.warning("Expected geo is a Polygon. Instead got a %s" % str(type(pol)))
|
|
||||||
except TypeError:
|
|
||||||
if isinstance(p, Polygon):
|
|
||||||
res = self.clear_polygon_worker(pol=p, tooldia=tool,
|
|
||||||
ncc_method=ncc_method,
|
|
||||||
ncc_overlap=ncc_overlap,
|
|
||||||
ncc_connect=ncc_connect,
|
|
||||||
ncc_contour=ncc_contour,
|
|
||||||
prog_plot=prog_plot)
|
|
||||||
if res is not None:
|
|
||||||
cleared_geo += res
|
|
||||||
else:
|
|
||||||
poly_failed += 1
|
|
||||||
else:
|
|
||||||
log.warning("Expected geo is a Polygon. Instead got a %s" % str(type(p)))
|
|
||||||
|
|
||||||
if poly_failed > 0:
|
|
||||||
app_obj.poly_not_cleared = True
|
|
||||||
|
|
||||||
pol_nr += 1
|
|
||||||
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
|
|
||||||
# log.debug("Polygons cleared: %d" % pol_nr)
|
|
||||||
|
|
||||||
if old_disp_number < disp_number <= 100:
|
|
||||||
self.app.proc_container.update_view_text(' %d%%' % disp_number)
|
|
||||||
old_disp_number = disp_number
|
|
||||||
# log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number))
|
|
||||||
|
|
||||||
# check if there is a geometry at all in the cleared geometry
|
|
||||||
if cleared_geo:
|
|
||||||
cleared = empty.buffer(-offset * (1 + ncc_overlap)) # Overall cleared area
|
|
||||||
cleared = cleared.buffer(-tool / 1.999999).buffer(tool / 1.999999)
|
|
||||||
|
|
||||||
# clean-up cleared geo
|
|
||||||
cleared = cleared.buffer(0)
|
|
||||||
|
|
||||||
# find the tooluid associated with the current tool_dia so we know where to add the tool
|
|
||||||
# solid_geometry
|
|
||||||
for k, v in tools_storage.items():
|
|
||||||
if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
|
|
||||||
tool)):
|
|
||||||
current_uid = int(k)
|
|
||||||
|
|
||||||
# add the solid_geometry to the current too in self.paint_tools dictionary
|
|
||||||
# and then reset the temporary list that stored that solid_geometry
|
|
||||||
v['solid_geometry'] = deepcopy(cleared_geo)
|
|
||||||
v['data']['name'] = name
|
|
||||||
break
|
|
||||||
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
|
||||||
else:
|
|
||||||
log.debug("There are no geometries in the cleared polygon.")
|
|
||||||
# clean the progressive plotted shapes if it was used
|
|
||||||
if self.app.defaults["tools_iso_plotting"] == 'progressive':
|
|
||||||
self.temp_shapes.clear(update=True)
|
|
||||||
|
|
||||||
# delete tools with empty geometry
|
|
||||||
# look for keys in the tools_storage dict that have 'solid_geometry' values empty
|
|
||||||
for uid, uid_val in list(tools_storage.items()):
|
|
||||||
try:
|
|
||||||
# if the solid_geometry (type=list) is empty
|
|
||||||
if not uid_val['solid_geometry']:
|
|
||||||
tools_storage.pop(uid, None)
|
|
||||||
except KeyError:
|
|
||||||
tools_storage.pop(uid, None)
|
|
||||||
|
|
||||||
geo_obj.options["cnctooldia"] = str(tool)
|
|
||||||
|
|
||||||
geo_obj.multigeo = True
|
|
||||||
geo_obj.tools.clear()
|
|
||||||
geo_obj.tools = dict(tools_storage)
|
|
||||||
|
|
||||||
# test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
|
|
||||||
has_solid_geo = 0
|
|
||||||
for tid in geo_obj.tools:
|
|
||||||
if geo_obj.tools[tid]['solid_geometry']:
|
|
||||||
has_solid_geo += 1
|
|
||||||
if has_solid_geo == 0:
|
|
||||||
app_obj.inform.emit('[ERROR] %s' %
|
|
||||||
_("There is no NCC Geometry in the file.\n"
|
|
||||||
"Usually it means that the tool diameter is too big for the painted geometry.\n"
|
|
||||||
"Change the painting parameters and try again."))
|
|
||||||
return 'fail'
|
|
||||||
|
|
||||||
# check to see if geo_obj.tools is empty
|
|
||||||
# it will be updated only if there is a solid_geometry for tools
|
|
||||||
if geo_obj.tools:
|
|
||||||
if warning_flag == 0:
|
|
||||||
self.app.inform.emit('[success] %s' % _("NCC Tool clear all done."))
|
|
||||||
else:
|
|
||||||
self.app.inform.emit('[WARNING] %s: %s %s.' % (
|
|
||||||
_("NCC Tool clear all done but the copper features isolation is broken for"),
|
|
||||||
str(warning_flag),
|
|
||||||
_("tools")))
|
|
||||||
return
|
|
||||||
|
|
||||||
# create the solid_geometry
|
|
||||||
geo_obj.solid_geometry = []
|
|
||||||
for tool_id in geo_obj.tools:
|
|
||||||
if geo_obj.tools[tool_id]['solid_geometry']:
|
|
||||||
try:
|
|
||||||
for geo in geo_obj.tools[tool_id]['solid_geometry']:
|
|
||||||
geo_obj.solid_geometry.append(geo)
|
|
||||||
except TypeError:
|
|
||||||
geo_obj.solid_geometry.append(geo_obj.tools[tool_id]['solid_geometry'])
|
|
||||||
else:
|
|
||||||
# I will use this variable for this purpose although it was meant for something else
|
|
||||||
# signal that we have no geo in the object therefore don't create it
|
|
||||||
app_obj.poly_not_cleared = False
|
|
||||||
return "fail"
|
|
||||||
|
|
||||||
# # Experimental...
|
|
||||||
# # print("Indexing...", end=' ')
|
|
||||||
# # geo_obj.make_index()
|
|
||||||
|
|
||||||
# ###########################################################################################
|
|
||||||
# Initializes the new geometry object for the case of the rest-machining ####################
|
|
||||||
# ###########################################################################################
|
|
||||||
def gen_clear_area_rest(geo_obj, app_obj):
|
|
||||||
assert geo_obj.kind == 'geometry', \
|
|
||||||
"Initializer expected a GeometryObject, got %s" % type(geo_obj)
|
|
||||||
|
|
||||||
log.debug("NCC Tool. Rest machining copper clearing task started.")
|
|
||||||
app_obj.inform.emit('_(NCC Tool. Rest machining copper clearing task started.')
|
|
||||||
|
|
||||||
# provide the app with a way to process the GUI events when in a blocking loop
|
|
||||||
if not run_threaded:
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
# a flag to signal that the isolation is broken by the bounding box in 'area' and 'box' cases
|
|
||||||
# will store the number of tools for which the isolation is broken
|
|
||||||
warning_flag = 0
|
|
||||||
|
|
||||||
sorted_clear_tools.sort(reverse=True)
|
|
||||||
|
|
||||||
cleared_geo = []
|
|
||||||
cleared_by_last_tool = []
|
|
||||||
rest_geo = []
|
|
||||||
current_uid = 1
|
|
||||||
try:
|
|
||||||
tool = eval(self.app.defaults["tools_ncctools"])[0]
|
|
||||||
except TypeError:
|
|
||||||
tool = eval(self.app.defaults["tools_ncctools"])
|
|
||||||
|
|
||||||
# repurposed flag for final object, geo_obj. True if it has any solid_geometry, False if not.
|
|
||||||
app_obj.poly_not_cleared = True
|
|
||||||
|
|
||||||
if ncc_select == _("Reference Object"):
|
|
||||||
env_obj, box_obj_kind = self.calculate_bounding_box(
|
|
||||||
ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select)
|
|
||||||
else:
|
|
||||||
env_obj, box_obj_kind = self.calculate_bounding_box(ncc_obj=ncc_obj, ncc_select=ncc_select)
|
|
||||||
|
|
||||||
if env_obj is None and box_obj_kind is None:
|
|
||||||
self.app.inform.emit("[ERROR_NOTCL] %s" % _("NCC Tool failed creating bounding box."))
|
|
||||||
return "fail"
|
|
||||||
|
|
||||||
log.debug("NCC Tool. Calculate 'empty' area.")
|
|
||||||
app_obj.inform.emit("NCC Tool. Calculate 'empty' area.")
|
|
||||||
|
|
||||||
# Generate area for each tool
|
|
||||||
while sorted_clear_tools:
|
|
||||||
log.debug("Starting geometry processing for tool: %s" % str(tool))
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
# provide the app with a way to process the GUI events when in a blocking loop
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
app_obj.inform.emit('[success] %s = %s%s %s' % (
|
|
||||||
_('NCC Tool clearing with tool diameter'), str(tool), units.lower(), _('started.'))
|
|
||||||
)
|
|
||||||
app_obj.proc_container.update_view_text(' %d%%' % 0)
|
|
||||||
|
|
||||||
tool = sorted_clear_tools.pop(0)
|
|
||||||
|
|
||||||
tool_uid = 0
|
|
||||||
for k, v in self.iso_tools.items():
|
|
||||||
if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool)):
|
|
||||||
tool_uid = int(k)
|
|
||||||
break
|
|
||||||
|
|
||||||
ncc_overlap = float(self.iso_tools[tool_uid]["data"]["tools_nccoverlap"]) / 100.0
|
|
||||||
ncc_margin = float(self.iso_tools[tool_uid]["data"]["tools_nccmargin"])
|
|
||||||
ncc_method = self.iso_tools[tool_uid]["data"]["tools_nccmethod"]
|
|
||||||
ncc_connect = self.iso_tools[tool_uid]["data"]["tools_nccconnect"]
|
|
||||||
ncc_contour = self.iso_tools[tool_uid]["data"]["tools_ncccontour"]
|
|
||||||
has_offset = self.iso_tools[tool_uid]["data"]["tools_ncc_offset_choice"]
|
|
||||||
ncc_offset = float(self.iso_tools[tool_uid]["data"]["tools_ncc_offset_value"])
|
|
||||||
|
|
||||||
tool_used = tool - 1e-12
|
|
||||||
cleared_geo[:] = []
|
|
||||||
|
|
||||||
# Bounding box for current tool
|
|
||||||
bbox = self.apply_margin_to_bounding_box(bbox=env_obj, box_kind=box_obj_kind,
|
|
||||||
ncc_select=ncc_select, ncc_margin=ncc_margin)
|
|
||||||
|
|
||||||
# Area to clear
|
|
||||||
empty, warning_flag = self.get_tool_empty_area(name=name, ncc_obj=ncc_obj, geo_obj=geo_obj,
|
|
||||||
isotooldia=isotooldia,
|
|
||||||
has_offset=has_offset, ncc_offset=ncc_offset,
|
|
||||||
ncc_margin=ncc_margin, tools_storage=tools_storage,
|
|
||||||
bounding_box=bbox)
|
|
||||||
|
|
||||||
area = empty.buffer(0)
|
|
||||||
|
|
||||||
# Area to clear
|
|
||||||
for poly in cleared_by_last_tool:
|
|
||||||
# provide the app with a way to process the GUI events when in a blocking loop
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
try:
|
|
||||||
area = area.difference(poly)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
cleared_by_last_tool[:] = []
|
|
||||||
|
|
||||||
# Transform area to MultiPolygon
|
|
||||||
if type(area) is Polygon:
|
|
||||||
area = MultiPolygon([area])
|
|
||||||
|
|
||||||
# add the rest that was not able to be cleared previously; area is a MultyPolygon
|
|
||||||
# and rest_geo it's a list
|
|
||||||
allparts = [p.buffer(0) for p in area.geoms]
|
|
||||||
allparts += deepcopy(rest_geo)
|
|
||||||
rest_geo[:] = []
|
|
||||||
area = MultiPolygon(deepcopy(allparts))
|
|
||||||
allparts[:] = []
|
|
||||||
|
|
||||||
# variables to display the percentage of work done
|
|
||||||
geo_len = len(area.geoms)
|
|
||||||
old_disp_number = 0
|
|
||||||
log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
|
|
||||||
|
|
||||||
if area.geoms:
|
|
||||||
if len(area.geoms) > 0:
|
|
||||||
pol_nr = 0
|
|
||||||
for p in area.geoms:
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
# clean the polygon
|
|
||||||
p = p.buffer(0)
|
|
||||||
|
|
||||||
if p is not None and p.is_valid:
|
|
||||||
# provide the app with a way to process the GUI events when in a blocking loop
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
if isinstance(p, Polygon):
|
|
||||||
try:
|
|
||||||
if ncc_method == _("Standard"):
|
|
||||||
cp = self.clear_polygon(p, tool_used,
|
|
||||||
self.grb_circle_steps,
|
|
||||||
overlap=ncc_overlap, contour=ncc_contour,
|
|
||||||
connect=ncc_connect,
|
|
||||||
prog_plot=prog_plot)
|
|
||||||
elif ncc_method == _("Seed"):
|
|
||||||
cp = self.clear_polygon2(p, tool_used,
|
|
||||||
self.grb_circle_steps,
|
|
||||||
overlap=ncc_overlap, contour=ncc_contour,
|
|
||||||
connect=ncc_connect,
|
|
||||||
prog_plot=prog_plot)
|
|
||||||
else:
|
|
||||||
cp = self.clear_polygon3(p, tool_used,
|
|
||||||
self.grb_circle_steps,
|
|
||||||
overlap=ncc_overlap, contour=ncc_contour,
|
|
||||||
connect=ncc_connect,
|
|
||||||
prog_plot=prog_plot)
|
|
||||||
cleared_geo.append(list(cp.get_objects()))
|
|
||||||
except Exception as e:
|
|
||||||
log.warning("Polygon can't be cleared. %s" % str(e))
|
|
||||||
# this polygon should be added to a list and then try clear it with
|
|
||||||
# a smaller tool
|
|
||||||
rest_geo.append(p)
|
|
||||||
elif isinstance(p, MultiPolygon):
|
|
||||||
for poly in p:
|
|
||||||
if poly is not None:
|
|
||||||
# provide the app with a way to process the GUI events when
|
|
||||||
# in a blocking loop
|
|
||||||
QtWidgets.QApplication.processEvents()
|
|
||||||
|
|
||||||
try:
|
|
||||||
if ncc_method == _("Standard"):
|
|
||||||
cp = self.clear_polygon(poly, tool_used,
|
|
||||||
self.grb_circle_steps,
|
|
||||||
overlap=ncc_overlap, contour=ncc_contour,
|
|
||||||
connect=ncc_connect,
|
|
||||||
prog_plot=prog_plot)
|
|
||||||
elif ncc_method == _("Seed"):
|
|
||||||
cp = self.clear_polygon2(poly, tool_used,
|
|
||||||
self.grb_circle_steps,
|
|
||||||
overlap=ncc_overlap, contour=ncc_contour,
|
|
||||||
connect=ncc_connect,
|
|
||||||
prog_plot=prog_plot)
|
|
||||||
else:
|
|
||||||
cp = self.clear_polygon3(poly, tool_used,
|
|
||||||
self.grb_circle_steps,
|
|
||||||
overlap=ncc_overlap, contour=ncc_contour,
|
|
||||||
connect=ncc_connect,
|
|
||||||
prog_plot=prog_plot)
|
|
||||||
cleared_geo.append(list(cp.get_objects()))
|
|
||||||
except Exception as e:
|
|
||||||
log.warning("Polygon can't be cleared. %s" % str(e))
|
|
||||||
# this polygon should be added to a list and then try clear it with
|
|
||||||
# a smaller tool
|
|
||||||
rest_geo.append(poly)
|
|
||||||
|
|
||||||
pol_nr += 1
|
|
||||||
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
|
|
||||||
# log.debug("Polygons cleared: %d" % pol_nr)
|
|
||||||
|
|
||||||
if old_disp_number < disp_number <= 100:
|
|
||||||
self.app.proc_container.update_view_text(' %d%%' % disp_number)
|
|
||||||
old_disp_number = disp_number
|
|
||||||
# log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number))
|
|
||||||
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
# check if there is a geometry at all in the cleared geometry
|
|
||||||
if cleared_geo:
|
|
||||||
# Overall cleared area
|
|
||||||
cleared_area = list(self.flatten_list(cleared_geo))
|
|
||||||
|
|
||||||
# cleared = MultiPolygon([p.buffer(tool_used / 2).buffer(-tool_used / 2)
|
|
||||||
# for p in cleared_area])
|
|
||||||
|
|
||||||
# here we store the poly's already processed in the original geometry by the current tool
|
|
||||||
# into cleared_by_last_tool list
|
|
||||||
# this will be sutracted from the original geometry_to_be_cleared and make data for
|
|
||||||
# the next tool
|
|
||||||
buffer_value = tool_used / 2
|
|
||||||
for p in cleared_area:
|
|
||||||
if self.app.abort_flag:
|
|
||||||
# graceful abort requested by the user
|
|
||||||
raise grace
|
|
||||||
|
|
||||||
poly = p.buffer(buffer_value)
|
|
||||||
cleared_by_last_tool.append(poly)
|
|
||||||
|
|
||||||
# find the tool uid associated with the current tool_dia so we know
|
|
||||||
# where to add the tool solid_geometry
|
|
||||||
for k, v in tools_storage.items():
|
|
||||||
if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
|
|
||||||
tool)):
|
|
||||||
current_uid = int(k)
|
|
||||||
|
|
||||||
# add the solid_geometry to the current too in self.paint_tools dictionary
|
|
||||||
# and then reset the temporary list that stored that solid_geometry
|
|
||||||
v['solid_geometry'] = deepcopy(cleared_area)
|
|
||||||
v['data']['name'] = name
|
|
||||||
cleared_area[:] = []
|
|
||||||
break
|
|
||||||
|
|
||||||
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
|
||||||
else:
|
|
||||||
log.debug("There are no geometries in the cleared polygon.")
|
|
||||||
|
|
||||||
geo_obj.multigeo = True
|
|
||||||
geo_obj.options["cnctooldia"] = str(tool)
|
|
||||||
|
|
||||||
# clean the progressive plotted shapes if it was used
|
|
||||||
if self.app.defaults["tools_iso_plotting"] == 'progressive':
|
|
||||||
self.temp_shapes.clear(update=True)
|
|
||||||
|
|
||||||
# check to see if geo_obj.tools is empty
|
|
||||||
# it will be updated only if there is a solid_geometry for tools
|
|
||||||
if geo_obj.tools:
|
|
||||||
if warning_flag == 0:
|
|
||||||
self.app.inform.emit('[success] %s' % _("NCC Tool Rest Machining clear all done."))
|
|
||||||
else:
|
|
||||||
self.app.inform.emit(
|
|
||||||
'[WARNING] %s: %s %s.' % (_("NCC Tool Rest Machining clear all done but the copper features "
|
|
||||||
"isolation is broken for"), str(warning_flag), _("tools")))
|
|
||||||
return
|
|
||||||
|
|
||||||
# create the solid_geometry
|
|
||||||
geo_obj.solid_geometry = []
|
|
||||||
for tool_uid in geo_obj.tools:
|
|
||||||
if geo_obj.tools[tool_uid]['solid_geometry']:
|
|
||||||
try:
|
|
||||||
for geo in geo_obj.tools[tool_uid]['solid_geometry']:
|
|
||||||
geo_obj.solid_geometry.append(geo)
|
|
||||||
except TypeError:
|
|
||||||
geo_obj.solid_geometry.append(geo_obj.tools[tool_uid]['solid_geometry'])
|
|
||||||
else:
|
|
||||||
# I will use this variable for this purpose although it was meant for something else
|
|
||||||
# signal that we have no geo in the object therefore don't create it
|
|
||||||
app_obj.poly_not_cleared = False
|
|
||||||
return "fail"
|
|
||||||
|
|
||||||
# ###########################################################################################
|
|
||||||
# Create the Job function and send it to the worker to be processed in another thread #######
|
|
||||||
# ###########################################################################################
|
|
||||||
def job_thread(a_obj):
|
|
||||||
try:
|
|
||||||
if rest_machining_choice is True:
|
|
||||||
a_obj.app_obj.new_object("geometry", name, gen_clear_area_rest)
|
|
||||||
else:
|
|
||||||
a_obj.app_obj.new_object("geometry", name, gen_clear_area)
|
|
||||||
except grace:
|
|
||||||
if run_threaded:
|
|
||||||
proc.done()
|
|
||||||
return
|
|
||||||
except Exception:
|
|
||||||
if run_threaded:
|
|
||||||
proc.done()
|
|
||||||
traceback.print_stack()
|
|
||||||
return
|
|
||||||
|
|
||||||
if run_threaded:
|
|
||||||
proc.done()
|
|
||||||
else:
|
|
||||||
a_obj.proc_container.view.set_idle()
|
|
||||||
|
|
||||||
# focus on Selected Tab
|
|
||||||
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
|
||||||
|
|
||||||
if run_threaded:
|
|
||||||
# Promise object with the new name
|
|
||||||
self.app.collection.promise(name)
|
|
||||||
|
|
||||||
# Background
|
|
||||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
|
||||||
else:
|
|
||||||
job_thread(app_obj=self.app)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def poly2rings(poly):
|
def poly2rings(poly):
|
||||||
return [poly.exterior] + [interior for interior in poly.interiors]
|
return [poly.exterior] + [interior for interior in poly.interiors]
|
||||||
|
|||||||
Reference in New Issue
Block a user