- NCC Tool - remade and optimized the copper clearing with rest machining: now it works as expected with a reasonable performance
This commit is contained in:
@@ -12,6 +12,7 @@ CHANGELOG for FlatCAM beta
|
|||||||
- made sure that clicking the icons in the status bar works only for the left mouse click
|
- made sure that clicking the icons in the status bar works only for the left mouse click
|
||||||
- if clicking the activity icon in the status bar and there is no object selected then the effect will be a plot_all with fit_view
|
- if clicking the activity icon in the status bar and there is no object selected then the effect will be a plot_all with fit_view
|
||||||
- modified the FCLabel GUI element
|
- modified the FCLabel GUI element
|
||||||
|
- NCC Tool - remade and optimized the copper clearing with rest machining: now it works as expected with a reasonable performance
|
||||||
|
|
||||||
13.06.2020
|
13.06.2020
|
||||||
|
|
||||||
|
|||||||
@@ -443,10 +443,16 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
self.ui.ncc_method_combo.set_value(self.app.defaults["tools_nccmethod"])
|
self.ui.ncc_method_combo.set_value(self.app.defaults["tools_nccmethod"])
|
||||||
self.ui.ncc_connect_cb.set_value(self.app.defaults["tools_nccconnect"])
|
self.ui.ncc_connect_cb.set_value(self.app.defaults["tools_nccconnect"])
|
||||||
self.ui.ncc_contour_cb.set_value(self.app.defaults["tools_ncccontour"])
|
self.ui.ncc_contour_cb.set_value(self.app.defaults["tools_ncccontour"])
|
||||||
self.ui.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"])
|
|
||||||
self.ui.ncc_choice_offset_cb.set_value(self.app.defaults["tools_ncc_offset_choice"])
|
self.ui.ncc_choice_offset_cb.set_value(self.app.defaults["tools_ncc_offset_choice"])
|
||||||
self.ui.ncc_offset_spinner.set_value(self.app.defaults["tools_ncc_offset_value"])
|
self.ui.ncc_offset_spinner.set_value(self.app.defaults["tools_ncc_offset_value"])
|
||||||
|
|
||||||
|
self.ui.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"])
|
||||||
|
self.ui.rest_ncc_margin_entry.set_value(self.app.defaults["tools_nccmargin"])
|
||||||
|
self.ui.rest_ncc_connect_cb.set_value(self.app.defaults["tools_nccconnect"])
|
||||||
|
self.ui.rest_ncc_contour_cb.set_value(self.app.defaults["tools_ncccontour"])
|
||||||
|
self.ui.rest_ncc_choice_offset_cb.set_value(self.app.defaults["tools_ncc_offset_choice"])
|
||||||
|
self.ui.rest_ncc_offset_spinner.set_value(self.app.defaults["tools_ncc_offset_value"])
|
||||||
|
|
||||||
self.ui.select_combo.set_value(self.app.defaults["tools_nccref"])
|
self.ui.select_combo.set_value(self.app.defaults["tools_nccref"])
|
||||||
self.ui.area_shape_radio.set_value(self.app.defaults["tools_ncc_area_shape"])
|
self.ui.area_shape_radio.set_value(self.app.defaults["tools_ncc_area_shape"])
|
||||||
|
|
||||||
@@ -787,10 +793,39 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
self.ui.ncc_order_radio.set_value('rev')
|
self.ui.ncc_order_radio.set_value('rev')
|
||||||
self.ui.ncc_order_label.setDisabled(True)
|
self.ui.ncc_order_label.setDisabled(True)
|
||||||
self.ui.ncc_order_radio.setDisabled(True)
|
self.ui.ncc_order_radio.setDisabled(True)
|
||||||
|
|
||||||
|
self.ui.nccmarginlabel.hide()
|
||||||
|
self.ui.ncc_margin_entry.hide()
|
||||||
|
self.ui.ncc_connect_cb.hide()
|
||||||
|
self.ui.ncc_contour_cb.hide()
|
||||||
|
self.ui.ncc_choice_offset_cb.hide()
|
||||||
|
self.ui.ncc_offset_spinner.hide()
|
||||||
|
|
||||||
|
self.ui.rest_nccmarginlabel.show()
|
||||||
|
self.ui.rest_ncc_margin_entry.show()
|
||||||
|
self.ui.rest_ncc_connect_cb.show()
|
||||||
|
self.ui.rest_ncc_contour_cb.show()
|
||||||
|
self.ui.rest_ncc_choice_offset_cb.show()
|
||||||
|
self.ui.rest_ncc_offset_spinner.show()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.ui.ncc_order_label.setDisabled(False)
|
self.ui.ncc_order_label.setDisabled(False)
|
||||||
self.ui.ncc_order_radio.setDisabled(False)
|
self.ui.ncc_order_radio.setDisabled(False)
|
||||||
|
|
||||||
|
self.ui.nccmarginlabel.show()
|
||||||
|
self.ui.ncc_margin_entry.show()
|
||||||
|
self.ui.ncc_connect_cb.show()
|
||||||
|
self.ui.ncc_contour_cb.show()
|
||||||
|
self.ui.ncc_choice_offset_cb.show()
|
||||||
|
self.ui.ncc_offset_spinner.show()
|
||||||
|
|
||||||
|
self.ui.rest_nccmarginlabel.hide()
|
||||||
|
self.ui.rest_ncc_margin_entry.hide()
|
||||||
|
self.ui.rest_ncc_connect_cb.hide()
|
||||||
|
self.ui.rest_ncc_contour_cb.hide()
|
||||||
|
self.ui.rest_ncc_choice_offset_cb.hide()
|
||||||
|
self.ui.rest_ncc_offset_spinner.hide()
|
||||||
|
|
||||||
def on_tooltable_cellwidget_change(self):
|
def on_tooltable_cellwidget_change(self):
|
||||||
cw = self.sender()
|
cw = self.sender()
|
||||||
assert isinstance(cw, QtWidgets.QComboBox),\
|
assert isinstance(cw, QtWidgets.QComboBox),\
|
||||||
@@ -1474,7 +1509,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
return new_bounding_box
|
return new_bounding_box
|
||||||
|
|
||||||
def get_tool_empty_area(self, name, ncc_obj, geo_obj, isotooldia, has_offset, ncc_offset, ncc_margin,
|
def get_tool_empty_area(self, name, ncc_obj, geo_obj, isotooldia, has_offset, ncc_offset, ncc_margin,
|
||||||
bounding_box, tools_storage):
|
bounding_box, tools_storage, work_geo=None):
|
||||||
"""
|
"""
|
||||||
Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry.
|
Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry.
|
||||||
|
|
||||||
@@ -1487,6 +1522,7 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
:param ncc_margin:
|
:param ncc_margin:
|
||||||
:param bounding_box: only this area is kept
|
:param bounding_box: only this area is kept
|
||||||
:param tools_storage:
|
:param tools_storage:
|
||||||
|
:param work_geo: if provided use this geometry to generate the empty area
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -1497,6 +1533,27 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
# will store the number of tools for which the isolation is broken
|
# will store the number of tools for which the isolation is broken
|
||||||
warning_flag = 0
|
warning_flag = 0
|
||||||
|
|
||||||
|
if work_geo:
|
||||||
|
sol_geo = work_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' or empty.is_empty:
|
||||||
|
msg = '[ERROR_NOTCL] %s' % _("Could not get the extent of the area to be non copper cleared.")
|
||||||
|
self.app.inform.emit(msg)
|
||||||
|
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
|
||||||
|
|
||||||
if ncc_obj.kind == 'gerber' and not isotooldia:
|
if ncc_obj.kind == 'gerber' and not isotooldia:
|
||||||
# unfortunately for this function to work time efficient,
|
# unfortunately for this function to work time efficient,
|
||||||
# if the Gerber was loaded without buffering then it require the buffering now.
|
# if the Gerber was loaded without buffering then it require the buffering now.
|
||||||
@@ -1719,7 +1776,9 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
if cp and cp.objects:
|
if cp and cp.objects:
|
||||||
return list(cp.get_objects())
|
return list(cp.get_objects())
|
||||||
else:
|
else:
|
||||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _('Geometry could not be cleared completely'))
|
pt = pol.representative_point()
|
||||||
|
coords = (pt.x, pt.y)
|
||||||
|
self.app.inform_shell.emit('%s %s' % (_('Polygon could not be cleared. Location:'), str(coords)))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def clear_copper(self, ncc_obj, ncctooldia, isotooldia, sel_obj=None, outname=None, order=None,
|
def clear_copper(self, ncc_obj, ncctooldia, isotooldia, sel_obj=None, outname=None, order=None,
|
||||||
@@ -1808,7 +1867,6 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
|
|
||||||
app_obj.poly_not_cleared = False # flag for polygons not cleared
|
app_obj.poly_not_cleared = False # flag for polygons not cleared
|
||||||
|
|
||||||
|
|
||||||
if ncc_select == _("Reference Object"):
|
if ncc_select == _("Reference Object"):
|
||||||
bbox_geo, bbox_kind = self.calculate_bounding_box(
|
bbox_geo, bbox_kind = self.calculate_bounding_box(
|
||||||
ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select)
|
ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select)
|
||||||
@@ -1819,6 +1877,11 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
self.app.inform.emit("[ERROR_NOTCL] %s" % _("NCC Tool failed creating bounding box."))
|
self.app.inform.emit("[ERROR_NOTCL] %s" % _("NCC Tool failed creating bounding box."))
|
||||||
return "fail"
|
return "fail"
|
||||||
|
|
||||||
|
# Bounding box for current tool
|
||||||
|
ncc_margin = self.ui.ncc_margin_entry.get_value()
|
||||||
|
bbox = self.apply_margin_to_bounding_box(bbox=bbox_geo, box_kind=bbox_kind,
|
||||||
|
ncc_select=ncc_select, ncc_margin=ncc_margin)
|
||||||
|
|
||||||
# COPPER CLEARING with tools marked for CLEAR#
|
# COPPER CLEARING with tools marked for CLEAR#
|
||||||
for tool in sorted_clear_tools:
|
for tool in sorted_clear_tools:
|
||||||
log.debug("Starting geometry processing for tool: %s" % str(tool))
|
log.debug("Starting geometry processing for tool: %s" % str(tool))
|
||||||
@@ -1846,23 +1909,17 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
|
|
||||||
# parameters that are particular to the current tool
|
# parameters that are particular to the current tool
|
||||||
ncc_overlap = float(self.ncc_tools[tool_uid]["data"]["tools_nccoverlap"]) / 100.0
|
ncc_overlap = float(self.ncc_tools[tool_uid]["data"]["tools_nccoverlap"]) / 100.0
|
||||||
ncc_margin = float(self.ncc_tools[tool_uid]["data"]["tools_nccmargin"])
|
|
||||||
ncc_method = self.ncc_tools[tool_uid]["data"]["tools_nccmethod"]
|
ncc_method = self.ncc_tools[tool_uid]["data"]["tools_nccmethod"]
|
||||||
ncc_connect = self.ncc_tools[tool_uid]["data"]["tools_nccconnect"]
|
ncc_connect = self.ncc_tools[tool_uid]["data"]["tools_nccconnect"]
|
||||||
ncc_contour = self.ncc_tools[tool_uid]["data"]["tools_ncccontour"]
|
ncc_contour = self.ncc_tools[tool_uid]["data"]["tools_ncccontour"]
|
||||||
has_offset = self.ncc_tools[tool_uid]["data"]["tools_ncc_offset_choice"]
|
has_offset = self.ncc_tools[tool_uid]["data"]["tools_ncc_offset_choice"]
|
||||||
ncc_offset = float(self.ncc_tools[tool_uid]["data"]["tools_ncc_offset_value"])
|
ncc_offset = float(self.ncc_tools[tool_uid]["data"]["tools_ncc_offset_value"])
|
||||||
|
|
||||||
|
|
||||||
# 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
|
# Area to clear
|
||||||
area, warning_flag = self.get_tool_empty_area(name=name, ncc_obj=ncc_obj, geo_obj=geo_obj,
|
area, warning_flag = self.get_tool_empty_area(name=name, ncc_obj=ncc_obj, geo_obj=geo_obj,
|
||||||
isotooldia=isotooldia, ncc_margin=ncc_margin,
|
isotooldia=isotooldia, ncc_margin=ncc_margin,
|
||||||
has_offset=has_offset, ncc_offset=ncc_offset,
|
has_offset=has_offset, ncc_offset=ncc_offset,
|
||||||
tools_storage=tools_storage, bounding_box=bbox)
|
tools_storage=tools_storage, bounding_box=bbox)
|
||||||
|
|
||||||
# Transform area to MultiPolygon
|
# Transform area to MultiPolygon
|
||||||
if isinstance(area, Polygon):
|
if isinstance(area, Polygon):
|
||||||
@@ -1893,6 +1950,9 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
poly_failed = 0
|
poly_failed = 0
|
||||||
try:
|
try:
|
||||||
for pol in p:
|
for pol in p:
|
||||||
|
# provide the app with a way to process the GUI events when in a blocking loop
|
||||||
|
QtWidgets.QApplication.processEvents()
|
||||||
|
|
||||||
if pol is not None and isinstance(pol, Polygon):
|
if pol is not None and isinstance(pol, Polygon):
|
||||||
res = self.clear_polygon_worker(pol=pol, tooldia=tool,
|
res = self.clear_polygon_worker(pol=pol, tooldia=tool,
|
||||||
ncc_method=ncc_method,
|
ncc_method=ncc_method,
|
||||||
@@ -2025,30 +2085,18 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
# Initializes the new geometry object for the case of the rest-machining ####################
|
# Initializes the new geometry object for the case of the rest-machining ####################
|
||||||
# ###########################################################################################
|
# ###########################################################################################
|
||||||
def gen_clear_area_rest(geo_obj, app_obj):
|
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.")
|
log.debug("NCC Tool. Rest machining copper clearing task started.")
|
||||||
app_obj.inform.emit('_(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
|
# provide the app with a way to process the GUI events when in a blocking loop
|
||||||
if not run_threaded:
|
if not run_threaded:
|
||||||
QtWidgets.QApplication.processEvents()
|
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)
|
sorted_clear_tools.sort(reverse=True)
|
||||||
|
|
||||||
cleared_geo = []
|
|
||||||
cleared_by_last_tool = []
|
cleared_by_last_tool = []
|
||||||
rest_geo = []
|
rest_geo = []
|
||||||
current_uid = 1
|
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.
|
# repurposed flag for final object, geo_obj. True if it has any solid_geometry, False if not.
|
||||||
app_obj.poly_not_cleared = True
|
app_obj.poly_not_cleared = True
|
||||||
@@ -2063,11 +2111,30 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
self.app.inform.emit("[ERROR_NOTCL] %s" % _("NCC Tool failed creating bounding box."))
|
self.app.inform.emit("[ERROR_NOTCL] %s" % _("NCC Tool failed creating bounding box."))
|
||||||
return "fail"
|
return "fail"
|
||||||
|
|
||||||
log.debug("NCC Tool. Calculate 'empty' area.")
|
# log.debug("NCC Tool. Calculate 'empty' area.")
|
||||||
app_obj.inform.emit("NCC Tool. Calculate 'empty' area.")
|
# app_obj.inform.emit("NCC Tool. Calculate 'empty' area.")
|
||||||
|
|
||||||
|
# Bounding box for current tool
|
||||||
|
ncc_margin = self.ui.ncc_margin_entry.get_value()
|
||||||
|
bbox = self.apply_margin_to_bounding_box(bbox=env_obj, box_kind=box_obj_kind,
|
||||||
|
ncc_select=ncc_select, ncc_margin=ncc_margin)
|
||||||
|
|
||||||
|
ncc_connect = self.ui.rest_ncc_connect_cb.get_value()
|
||||||
|
ncc_contour = self.ui.rest_ncc_contour_cb.get_value()
|
||||||
|
has_offset = self.ui.rest_ncc_choice_offset_cb.get_value()
|
||||||
|
ncc_offset = self.ui.rest_ncc_offset_spinner.get_value()
|
||||||
|
|
||||||
|
# Area to clear
|
||||||
|
area, 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)
|
||||||
|
|
||||||
# Generate area for each tool
|
# Generate area for each tool
|
||||||
while sorted_clear_tools:
|
while sorted_clear_tools:
|
||||||
|
tool = sorted_clear_tools.pop(0)
|
||||||
|
|
||||||
log.debug("Starting geometry processing for tool: %s" % str(tool))
|
log.debug("Starting geometry processing for tool: %s" % str(tool))
|
||||||
if self.app.abort_flag:
|
if self.app.abort_flag:
|
||||||
# graceful abort requested by the user
|
# graceful abort requested by the user
|
||||||
@@ -2081,69 +2148,24 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
)
|
)
|
||||||
app_obj.proc_container.update_view_text(' %d%%' % 0)
|
app_obj.proc_container.update_view_text(' %d%%' % 0)
|
||||||
|
|
||||||
tool = sorted_clear_tools.pop(0)
|
tool_uid = 0 # find the current tool_uid
|
||||||
|
|
||||||
tool_uid = 0
|
|
||||||
for k, v in self.ncc_tools.items():
|
for k, v in self.ncc_tools.items():
|
||||||
if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool)):
|
if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool)):
|
||||||
tool_uid = int(k)
|
tool_uid = int(k)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# parameters that are particular to the current tool
|
||||||
ncc_overlap = float(self.ncc_tools[tool_uid]["data"]["tools_nccoverlap"]) / 100.0
|
ncc_overlap = float(self.ncc_tools[tool_uid]["data"]["tools_nccoverlap"]) / 100.0
|
||||||
ncc_margin = float(self.ncc_tools[tool_uid]["data"]["tools_nccmargin"])
|
|
||||||
ncc_method = self.ncc_tools[tool_uid]["data"]["tools_nccmethod"]
|
ncc_method = self.ncc_tools[tool_uid]["data"]["tools_nccmethod"]
|
||||||
ncc_connect = self.ncc_tools[tool_uid]["data"]["tools_nccconnect"]
|
|
||||||
ncc_contour = self.ncc_tools[tool_uid]["data"]["tools_ncccontour"]
|
|
||||||
has_offset = self.ncc_tools[tool_uid]["data"]["tools_ncc_offset_choice"]
|
|
||||||
ncc_offset = float(self.ncc_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
|
# variables to display the percentage of work done
|
||||||
geo_len = len(area.geoms)
|
geo_len = len(area.geoms)
|
||||||
old_disp_number = 0
|
old_disp_number = 0
|
||||||
log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
|
log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
|
||||||
|
|
||||||
|
# store here the geometry generated by clear operation
|
||||||
|
cleared_geo = []
|
||||||
|
|
||||||
if area.geoms:
|
if area.geoms:
|
||||||
if len(area.geoms) > 0:
|
if len(area.geoms) > 0:
|
||||||
pol_nr = 0
|
pol_nr = 0
|
||||||
@@ -2152,71 +2174,36 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
# graceful abort requested by the user
|
# graceful abort requested by the user
|
||||||
raise grace
|
raise grace
|
||||||
|
|
||||||
# clean the polygon
|
|
||||||
p = p.buffer(0)
|
|
||||||
|
|
||||||
if p is not None and p.is_valid:
|
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
|
# provide the app with a way to process the GUI events when in a blocking loop
|
||||||
QtWidgets.QApplication.processEvents()
|
QtWidgets.QApplication.processEvents()
|
||||||
|
|
||||||
if isinstance(p, Polygon):
|
poly_failed = 0
|
||||||
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:
|
# speedup the clearing by not trying to clear polygons that is clear they can't be
|
||||||
if ncc_method == _("Standard"):
|
# cleared with the current tool. this tremendously reduce the clearing time
|
||||||
cp = self.clear_polygon(poly, tool_used,
|
check_dist = -tool / 2.0
|
||||||
self.grb_circle_steps,
|
check_buff = p.buffer(check_dist)
|
||||||
overlap=ncc_overlap, contour=ncc_contour,
|
if not check_buff or check_buff.is_empty:
|
||||||
connect=ncc_connect,
|
continue
|
||||||
prog_plot=prog_plot)
|
|
||||||
elif ncc_method == _("Seed"):
|
# actual copper claring is done here
|
||||||
cp = self.clear_polygon2(poly, tool_used,
|
if isinstance(p, Polygon):
|
||||||
self.grb_circle_steps,
|
res = self.clear_polygon_worker(pol=p, tooldia=tool,
|
||||||
overlap=ncc_overlap, contour=ncc_contour,
|
ncc_method=ncc_method,
|
||||||
connect=ncc_connect,
|
ncc_overlap=ncc_overlap,
|
||||||
prog_plot=prog_plot)
|
ncc_connect=ncc_connect,
|
||||||
else:
|
ncc_contour=ncc_contour,
|
||||||
cp = self.clear_polygon3(poly, tool_used,
|
prog_plot=prog_plot)
|
||||||
self.grb_circle_steps,
|
if res is not None:
|
||||||
overlap=ncc_overlap, contour=ncc_contour,
|
cleared_geo += res
|
||||||
connect=ncc_connect,
|
else:
|
||||||
prog_plot=prog_plot)
|
poly_failed += 1
|
||||||
cleared_geo.append(list(cp.get_objects()))
|
else:
|
||||||
except Exception as e:
|
log.warning("Expected geo is a Polygon. Instead got a %s" % str(type(p)))
|
||||||
log.warning("Polygon can't be cleared. %s" % str(e))
|
|
||||||
# this polygon should be added to a list and then try clear it with
|
if poly_failed > 0:
|
||||||
# a smaller tool
|
app_obj.poly_not_cleared = True
|
||||||
rest_geo.append(poly)
|
|
||||||
|
|
||||||
pol_nr += 1
|
pol_nr += 1
|
||||||
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
|
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
|
||||||
@@ -2228,30 +2215,10 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
# log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number))
|
# log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number))
|
||||||
|
|
||||||
if self.app.abort_flag:
|
if self.app.abort_flag:
|
||||||
# graceful abort requested by the user
|
raise grace # graceful abort requested by the user
|
||||||
raise grace
|
|
||||||
|
|
||||||
# check if there is a geometry at all in the cleared geometry
|
# check if there is a geometry at all in the cleared geometry
|
||||||
if cleared_geo:
|
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
|
# find the tool uid associated with the current tool_dia so we know
|
||||||
# where to add the tool solid_geometry
|
# where to add the tool solid_geometry
|
||||||
for k, v in tools_storage.items():
|
for k, v in tools_storage.items():
|
||||||
@@ -2261,17 +2228,31 @@ class NonCopperClear(AppTool, Gerber):
|
|||||||
|
|
||||||
# add the solid_geometry to the current too in self.paint_tools dictionary
|
# add the solid_geometry to the current too in self.paint_tools dictionary
|
||||||
# and then reset the temporary list that stored that solid_geometry
|
# and then reset the temporary list that stored that solid_geometry
|
||||||
v['solid_geometry'] = deepcopy(cleared_area)
|
v['solid_geometry'] = deepcopy(cleared_geo)
|
||||||
v['data']['name'] = name
|
v['data']['name'] = name + '_' + str(tool)
|
||||||
cleared_area[:] = []
|
|
||||||
break
|
break
|
||||||
|
|
||||||
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
||||||
else:
|
else:
|
||||||
log.debug("There are no geometries in the cleared polygon.")
|
log.debug("There are no geometries in the cleared polygon.")
|
||||||
|
|
||||||
|
# Area to clear next
|
||||||
|
buffered_cleared = cascaded_union(cleared_geo).buffer(tool / 2.0)
|
||||||
|
area = area.difference(buffered_cleared)
|
||||||
|
|
||||||
|
if not area or area.is_empty:
|
||||||
|
break
|
||||||
|
|
||||||
|
# # try to clear the polygons
|
||||||
|
# buff_distance = 0.0
|
||||||
|
# try:
|
||||||
|
# new_area = [p.buffer(buff_distance) for p in area if not p.is_empty]
|
||||||
|
# except TypeError:
|
||||||
|
# new_area = [area.buffer(tool * ncc_overlap)]
|
||||||
|
# area = cascaded_union(area)
|
||||||
|
|
||||||
geo_obj.multigeo = True
|
geo_obj.multigeo = True
|
||||||
geo_obj.options["cnctooldia"] = str(tool)
|
geo_obj.options["cnctooldia"] = '0.0'
|
||||||
|
|
||||||
# clean the progressive plotted shapes if it was used
|
# clean the progressive plotted shapes if it was used
|
||||||
if self.app.defaults["tools_ncc_plotting"] == 'progressive':
|
if self.app.defaults["tools_ncc_plotting"] == 'progressive':
|
||||||
@@ -3884,19 +3865,6 @@ class NccUI:
|
|||||||
self.grid3.addWidget(nccoverlabel, 15, 0)
|
self.grid3.addWidget(nccoverlabel, 15, 0)
|
||||||
self.grid3.addWidget(self.ncc_overlap_entry, 15, 1)
|
self.grid3.addWidget(self.ncc_overlap_entry, 15, 1)
|
||||||
|
|
||||||
# Margin
|
|
||||||
nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
|
|
||||||
nccmarginlabel.setToolTip(
|
|
||||||
_("Bounding box margin.")
|
|
||||||
)
|
|
||||||
self.ncc_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
|
|
||||||
self.ncc_margin_entry.set_precision(self.decimals)
|
|
||||||
self.ncc_margin_entry.set_range(-9999.9999, 9999.9999)
|
|
||||||
self.ncc_margin_entry.setObjectName("n_margin")
|
|
||||||
|
|
||||||
self.grid3.addWidget(nccmarginlabel, 16, 0)
|
|
||||||
self.grid3.addWidget(self.ncc_margin_entry, 16, 1)
|
|
||||||
|
|
||||||
# Method
|
# Method
|
||||||
methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
|
methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
|
||||||
methodlabel.setToolTip(
|
methodlabel.setToolTip(
|
||||||
@@ -3917,8 +3885,21 @@ class NccUI:
|
|||||||
)
|
)
|
||||||
self.ncc_method_combo.setObjectName("n_method")
|
self.ncc_method_combo.setObjectName("n_method")
|
||||||
|
|
||||||
self.grid3.addWidget(methodlabel, 17, 0)
|
self.grid3.addWidget(methodlabel, 16, 0)
|
||||||
self.grid3.addWidget(self.ncc_method_combo, 17, 1)
|
self.grid3.addWidget(self.ncc_method_combo, 16, 1)
|
||||||
|
|
||||||
|
# Margin
|
||||||
|
self.nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
|
||||||
|
self.nccmarginlabel.setToolTip(
|
||||||
|
_("Bounding box margin.")
|
||||||
|
)
|
||||||
|
self.ncc_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
|
||||||
|
self.ncc_margin_entry.set_precision(self.decimals)
|
||||||
|
self.ncc_margin_entry.set_range(-9999.9999, 9999.9999)
|
||||||
|
self.ncc_margin_entry.setObjectName("n_margin")
|
||||||
|
|
||||||
|
self.grid3.addWidget(self.nccmarginlabel, 17, 0)
|
||||||
|
self.grid3.addWidget(self.ncc_margin_entry, 17, 1)
|
||||||
|
|
||||||
# Connect lines
|
# Connect lines
|
||||||
self.ncc_connect_cb = FCCheckBox('%s' % _("Connect"))
|
self.ncc_connect_cb = FCCheckBox('%s' % _("Connect"))
|
||||||
@@ -4009,6 +3990,61 @@ class NccUI:
|
|||||||
|
|
||||||
self.grid3.addWidget(self.ncc_rest_cb, 25, 0, 1, 2)
|
self.grid3.addWidget(self.ncc_rest_cb, 25, 0, 1, 2)
|
||||||
|
|
||||||
|
# Rest Margin
|
||||||
|
self.rest_nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
|
||||||
|
self.rest_nccmarginlabel.setToolTip(
|
||||||
|
_("Bounding box margin.")
|
||||||
|
)
|
||||||
|
self.rest_ncc_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
|
||||||
|
self.rest_ncc_margin_entry.set_precision(self.decimals)
|
||||||
|
self.rest_ncc_margin_entry.set_range(-9999.9999, 9999.9999)
|
||||||
|
self.rest_ncc_margin_entry.setObjectName("n_margin")
|
||||||
|
|
||||||
|
self.grid3.addWidget(self.rest_nccmarginlabel, 26, 0)
|
||||||
|
self.grid3.addWidget(self.rest_ncc_margin_entry, 26, 1)
|
||||||
|
|
||||||
|
# Rest Connect lines
|
||||||
|
self.rest_ncc_connect_cb = FCCheckBox('%s' % _("Connect"))
|
||||||
|
self.rest_ncc_connect_cb.setToolTip(
|
||||||
|
_("Draw lines between resulting\n"
|
||||||
|
"segments to minimize tool lifts.")
|
||||||
|
)
|
||||||
|
self.grid3.addWidget(self.rest_ncc_connect_cb, 27, 0)
|
||||||
|
|
||||||
|
# Rest Contour
|
||||||
|
self.rest_ncc_contour_cb = FCCheckBox('%s' % _("Contour"))
|
||||||
|
self.rest_ncc_contour_cb.setToolTip(
|
||||||
|
_("Cut around the perimeter of the polygon\n"
|
||||||
|
"to trim rough edges.")
|
||||||
|
)
|
||||||
|
self.grid3.addWidget(self.rest_ncc_contour_cb, 27, 1)
|
||||||
|
|
||||||
|
# ## Rest NCC Offset choice
|
||||||
|
self.rest_ncc_choice_offset_cb = FCCheckBox('%s' % _("Offset"))
|
||||||
|
self.rest_ncc_choice_offset_cb.setToolTip(
|
||||||
|
_("If used, it will add an offset to the copper features.\n"
|
||||||
|
"The copper clearing will finish to a distance\n"
|
||||||
|
"from the copper features.\n"
|
||||||
|
"The value can be between 0 and 10 FlatCAM units.")
|
||||||
|
)
|
||||||
|
self.grid3.addWidget(self.rest_ncc_choice_offset_cb, 28, 0)
|
||||||
|
|
||||||
|
# ## Rest NCC Offset Entry
|
||||||
|
self.rest_ncc_offset_spinner = FCDoubleSpinner(callback=self.confirmation_message)
|
||||||
|
self.rest_ncc_offset_spinner.set_range(0.00, 10.00)
|
||||||
|
self.rest_ncc_offset_spinner.set_precision(4)
|
||||||
|
self.rest_ncc_offset_spinner.setWrapping(True)
|
||||||
|
|
||||||
|
units = self.app.defaults['units'].upper()
|
||||||
|
if units == 'MM':
|
||||||
|
self.rest_ncc_offset_spinner.setSingleStep(0.1)
|
||||||
|
else:
|
||||||
|
self.rest_ncc_offset_spinner.setSingleStep(0.01)
|
||||||
|
|
||||||
|
self.grid3.addWidget(self.rest_ncc_offset_spinner, 28, 1)
|
||||||
|
|
||||||
|
self.rest_ois_ncc_offset = OptionalInputSection(self.rest_ncc_choice_offset_cb, [self.rest_ncc_offset_spinner])
|
||||||
|
|
||||||
# ## Reference
|
# ## Reference
|
||||||
# self.select_radio = RadioSet([
|
# self.select_radio = RadioSet([
|
||||||
# {'label': _('Itself'), 'value': 'itself'},
|
# {'label': _('Itself'), 'value': 'itself'},
|
||||||
@@ -4028,11 +4064,11 @@ class NccUI:
|
|||||||
"- 'Area Selection' - left mouse click to start selection of the area to be processed.\n"
|
"- 'Area Selection' - left mouse click to start selection of the area to be processed.\n"
|
||||||
"- 'Reference Object' - will process the area specified by another object.")
|
"- 'Reference Object' - will process the area specified by another object.")
|
||||||
)
|
)
|
||||||
self.grid3.addWidget(self.select_label, 26, 0, )
|
self.grid3.addWidget(self.select_label, 29, 0, )
|
||||||
self.grid3.addWidget(self.select_combo, 26, 1)
|
self.grid3.addWidget(self.select_combo, 29, 1)
|
||||||
|
|
||||||
form1 = QtWidgets.QFormLayout()
|
form1 = QtWidgets.QFormLayout()
|
||||||
self.grid3.addLayout(form1, 28, 0, 1, 2)
|
self.grid3.addLayout(form1, 30, 0, 1, 2)
|
||||||
|
|
||||||
self.reference_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type"))
|
self.reference_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type"))
|
||||||
self.reference_combo_type_label.setToolTip(
|
self.reference_combo_type_label.setToolTip(
|
||||||
@@ -4068,8 +4104,8 @@ class NccUI:
|
|||||||
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
|
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
|
||||||
{'label': _("Polygon"), 'value': 'polygon'}])
|
{'label': _("Polygon"), 'value': 'polygon'}])
|
||||||
|
|
||||||
self.grid3.addWidget(self.area_shape_label, 29, 0)
|
self.grid3.addWidget(self.area_shape_label, 31, 0)
|
||||||
self.grid3.addWidget(self.area_shape_radio, 29, 1)
|
self.grid3.addWidget(self.area_shape_radio, 31, 1)
|
||||||
|
|
||||||
self.area_shape_label.hide()
|
self.area_shape_label.hide()
|
||||||
self.area_shape_radio.hide()
|
self.area_shape_radio.hide()
|
||||||
@@ -4077,7 +4113,7 @@ class NccUI:
|
|||||||
separator_line = QtWidgets.QFrame()
|
separator_line = QtWidgets.QFrame()
|
||||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
self.grid3.addWidget(separator_line, 30, 0, 1, 2)
|
self.grid3.addWidget(separator_line, 32, 0, 1, 2)
|
||||||
|
|
||||||
self.generate_ncc_button = QtWidgets.QPushButton(_('Generate Geometry'))
|
self.generate_ncc_button = QtWidgets.QPushButton(_('Generate Geometry'))
|
||||||
self.generate_ncc_button.setToolTip(
|
self.generate_ncc_button.setToolTip(
|
||||||
|
|||||||
@@ -2588,6 +2588,10 @@ class ToolPaint(AppTool, Gerber):
|
|||||||
if cp:
|
if cp:
|
||||||
for x in cp:
|
for x in cp:
|
||||||
total_geometry += list(x.get_objects())
|
total_geometry += list(x.get_objects())
|
||||||
|
|
||||||
|
# clean the geometry
|
||||||
|
new_geo = [g for g in total_geometry if g and not g.is_empty]
|
||||||
|
total_geometry = new_geo
|
||||||
final_solid_geometry += total_geometry
|
final_solid_geometry += total_geometry
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
|||||||
Reference in New Issue
Block a user