- NCC Plugin: modified the previous change and now the simplification action is much bigger reducing the number of coordinates by a factor of 20 (20 times less)

- Paint Plugin: same as above
- Ncc Plugin: added some more tooltips
- Isolation Plugin: fixed some issues when using the Rest Machining option
This commit is contained in:
Marius Stanciu
2023-06-17 22:30:17 +03:00
parent 5fe50dbcd1
commit 968fb1f943
6 changed files with 159 additions and 113 deletions

View File

@@ -2074,13 +2074,13 @@ class ToolIsolation(AppTool, Gerber):
solid_geo = self.area_intersection(solid_geo, intersection_geo=lim_area)
# make sure that no empty geometry element is in the solid_geometry
new_solid_geo = [geo for geo in solid_geo if not geo.is_empty]
new_solid_geo = flatten_shapely_geometry(solid_geo)
tools_storage.update({
tool: {
'tooldia': float(tool_dia),
'data': tool_data,
'solid_geometry': deepcopy(new_solid_geo)
'solid_geometry': new_solid_geo
}
})
tools_storage[tool]['data']['tools_mill_tooldia'] = float(tool_dia)
@@ -2090,11 +2090,10 @@ class ToolIsolation(AppTool, Gerber):
if not work_geo:
break
total_solid_geometry = self.flatten_list(total_solid_geometry)
total_solid_geometry = [g for g in total_solid_geometry if not g.is_empty]
if simp_en:
total_solid_geometry = [
g.simplify(tolerance=simp_tol) for g in total_solid_geometry if not g.is_empty]
total_solid_geometry = flatten_shapely_geometry(total_solid_geometry, simplify_tolerance=simp_tol)
else:
total_solid_geometry = flatten_shapely_geometry(total_solid_geometry)
# clean the progressive plotted shapes if it was used
if plot and self.app.options["tools_iso_plotting"] == 'progressive':
@@ -2169,7 +2168,7 @@ class ToolIsolation(AppTool, Gerber):
pt = geo.representative_point()
coords = '(%s, %s), ' % (str(pt.x), str(pt.y))
msg += coords
self.app.inform_shell.emit(msg=msg)
self.app.inform_shell.emit(msg)
def combined_normal(self, iso_obj, iso2geo, tools_storage, lim_area, sel_tools, iso_except, extra_passes=None,
negative_dia=None, simp_en=False, simp_tol=0.001, plot=True, prog_plot=None):
@@ -3266,30 +3265,21 @@ class ToolIsolation(AppTool, Gerber):
else:
not_isolated_geo.append(geo)
work_geo_shp = work_geo.geoms if isinstance(work_geo, MultiPolygon) else work_geo
work_geo_shp = flatten_shapely_geometry(work_geo)
if invert:
try:
pl = []
for p in work_geo_shp:
if p is not None:
if isinstance(p, Polygon):
pl.append(Polygon(p.exterior.coords[::-1], p.interiors))
elif isinstance(p, LinearRing):
pl.append(Polygon(p.coords[::-1]))
if isinstance(p, Polygon):
pl.append(Polygon(p.exterior.coords[::-1], p.interiors))
elif isinstance(p, LinearRing):
pl.append(Polygon(p.coords[::-1]))
work_geo_shp = MultiPolygon(pl)
except TypeError:
if isinstance(work_geo_shp, Polygon) and work_geo_shp is not None:
work_geo_shp = [Polygon(work_geo_shp.exterior.coords[::-1], work_geo_shp.interiors)]
elif isinstance(work_geo_shp, LinearRing) and work_geo_shp is not None:
work_geo_shp = [Polygon(work_geo_shp.coords[::-1])]
else:
self.app.log.debug(
"ToolIsolation.generate_rest_geometry() Error --> Unexpected Geometry %s" % type(work_geo))
except Exception as e:
self.app.log.error("ToolIsolation.generate_rest_geometry() Error --> %s" % str(e))
return 'fail', 'fail'
actual_geo = work_geo_shp.geoms if isinstance(work_geo, MultiPolygon) else work_geo_shp
actual_geo = flatten_shapely_geometry(work_geo_shp)
if env_iso_type == 0: # exterior
for geo in actual_geo:
isolated_geo.append(geo.exterior)

View File

@@ -2187,7 +2187,6 @@ class NonCopperClear(AppTool, Gerber):
steps_per_circle=self.circle_steps,
overlap=ncc_overlap, contour=ncc_contour,
connect=ncc_connect,
simplify_tol=simplify_tol,
prog_plot=prog_plot)
except grace:
return "fail"
@@ -2248,8 +2247,10 @@ class NonCopperClear(AppTool, Gerber):
self.app.log.error("NonCopperClear.clear_polygon_worker() Combo --> %s" % str(ee))
if cp and cp.objects:
ret_val = list(cp.get_objects())
return ret_val
if simplify_tol > 0.0:
return [x.simplify(simplify_tol) for x in cp.get_objects()]
else:
return [x for x in cp.get_objects()]
else:
pt = pol.representative_point()
coords = (pt.x, pt.y)
@@ -2285,9 +2286,9 @@ class NonCopperClear(AppTool, Gerber):
self.app.log.debug("Executing the clear_copper handler ...")
if run_threaded:
proc = self.app.proc_container.new('%s...' % _("Non-Copper Clearing"))
proc = self.app.proc_container.new('%s...' % _("Working"))
else:
self.app.proc_container.view.set_busy('%s...' % _("Non-Copper Clearing"))
self.app.proc_container.view.set_busy('%s...' % _("Working"))
QtWidgets.QApplication.processEvents()
# ######################################################################################################
@@ -2300,7 +2301,7 @@ class NonCopperClear(AppTool, Gerber):
rest_machining_choice = self.ui.ncc_rest_cb.get_value()
# TODO this should be in preferences and in the UI
simplification_value = 0.02
simplification_value = 0.01
# determine if to use the progressive plotting
prog_plot = True if self.app.options["tools_ncc_plotting"] == 'progressive' else False
@@ -2360,7 +2361,9 @@ class NonCopperClear(AppTool, Gerber):
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#
# ----------------------------------------------------
for tool in sorted_clear_tools:
self.app.log.debug("Starting geometry processing for tool: %s" % str(tool))
if self.app.abort_flag:
@@ -2376,16 +2379,23 @@ class NonCopperClear(AppTool, Gerber):
)
app_obj.proc_container.update_view_text(' %d%%' % 0)
# ----------------------------------------------------
# store here the geometry generated by clear operation
# ----------------------------------------------------
cleared_geo = []
tool_uid = 0 # find the current tool_uid
# ----------------------------------------------------
# find the current tool_uid
# ----------------------------------------------------
tool_uid = 0
for k, v in self.ncc_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.ncc_tools[tool_uid]["data"]["tools_ncc_overlap"]) / 100.0
ncc_method = self.ncc_tools[tool_uid]["data"]["tools_ncc_method"]
ncc_connect = self.ncc_tools[tool_uid]["data"]["tools_ncc_connect"]
@@ -2393,107 +2403,119 @@ class NonCopperClear(AppTool, Gerber):
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"])
# ----------------------------------------------------
# Area to clear
# ----------------------------------------------------
result = self.get_tool_empty_area(name=name, ncc_obj=ncc_obj, geo_obj=geo_obj, isotooldia=isotd_list,
ncc_margin=ncc_margin, has_offset=has_offset, ncc_offset=ncc_offset,
tools_storage=tools_storage, bounding_box=bbox)
area, warning_flag = result
if area == "fail":
self.app.log.debug("Failed to create empty area for this tool.")
continue
# Transform area to MultiPolygon
if isinstance(area, Polygon):
area = MultiPolygon([area])
tool_empty_area = flatten_shapely_geometry(area)
if not tool_empty_area:
continue
# variables to display the percentage of work done
geo_len = len(area.geoms)
old_disp_number = 0
geo_len = len(tool_empty_area)
self.app.log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
tool_empty_area = flatten_shapely_geometry(area.geoms)
# ----------------------------------------------------
# Copper-clear the Polygons in the non-copper-area
# Iterate over them
# ----------------------------------------------------
pol_nr = 0
for p in tool_empty_area:
# provide the app with a way to process the GUI events when in a blocking loop
if not run_threaded:
QtWidgets.QApplication.processEvents()
if tool_empty_area:
pol_nr = 0
for p in tool_empty_area:
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
# ----------------------------------------------------
# attempt to fix possible problems with the polygon
# ----------------------------------------------------
p = p.buffer(0.0000001)
p = flatten_shapely_geometry(p, simplify_tolerance=simplification_value)
poly_failed = 0
for pol in p:
# provide the app with a way to process the GUI events when in a blocking loop
if not run_threaded:
QtWidgets.QApplication.processEvents()
QtWidgets.QApplication.processEvents()
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
# clean the polygon
p = p.buffer(0.0000001)
p = flatten_shapely_geometry(p, simplify_tolerance=simplification_value)
poly_failed = 0
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 pol.is_valid 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,
simplify_tol=simplification_value,
prog_plot=prog_plot)
if res is not None:
cleared_geo += res
else:
poly_failed += 1
if pol is not None and pol.is_valid and isinstance(pol, Polygon):
# ----------------------------------------------------
# This is where copper clearing is happening
# ----------------------------------------------------
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,
simplify_tol=simplification_value,
prog_plot=prog_plot)
if res is not None:
cleared_geo += res
else:
self.app.log.warning(
"Expected geo is a Polygon. Instead got a %s" % str(type(pol)))
poly_failed += 1
else:
self.app.log.warning(
"Expected geo is a Polygon. Instead got a %s" % str(type(pol)))
pol_nr += 1
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
# log.debug("Polygons cleared: %d" % pol_nr)
pol_nr += 1
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
if old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
old_disp_number = disp_number
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 poly_failed > 0:
app_obj.poly_not_cleared = True
if poly_failed > 0:
app_obj.poly_not_cleared = True
# ---------------------------------------------------------
# Debug message regarding how many points are in the result
# ---------------------------------------------------------
l_coords = 0
for i in range(len(cleared_geo)):
l_coords += len(cleared_geo[i].coords)
self.app.log.debug(
"NCC Tool.clear_copper.gen_clear_area() -> Number of cleared geo coords: %s" % str(l_coords))
l_coords = 0
for i in range(len(cleared_geo)):
l_coords += len(cleared_geo[i].coords)
self.app.log.debug(
"NCC Tool.clear_copper.gen_clear_area() -> Number of cleared geo coords: %s" % str(l_coords))
# -----------------------------------------------------------
# check if there is a geometry at all in the cleared geometry
# -----------------------------------------------------------
if cleared_geo:
formatted_tool = self.app.dec_format(tool, self.decimals)
# 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 self.app.dec_format(v['tooldia'], self.decimals) == formatted_tool:
current_uid = int(k)
# check if there is a geometry at all in the cleared geometry
if cleared_geo:
formatted_tool = self.app.dec_format(tool, self.decimals)
# 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 self.app.dec_format(v['tooldia'], self.decimals) == formatted_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
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
break
else:
self.app.log.debug("There are no geometries in the cleared polygon.")
# 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
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
break
else:
self.app.log.debug("There are no geometries in the cleared polygon.")
# ----------------------------------------------------
# clean the progressive plotted shapes if it was used
# ----------------------------------------------------
if self.app.options["tools_ncc_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
@@ -2516,7 +2538,9 @@ class NonCopperClear(AppTool, Gerber):
geo_obj.multigeo = True
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']:
@@ -2528,8 +2552,10 @@ class NonCopperClear(AppTool, Gerber):
app_obj.inform.emit(msg)
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."))
@@ -2889,9 +2915,9 @@ class NonCopperClear(AppTool, Gerber):
:return:
"""
if run_threaded:
proc = self.app.proc_container.new('%s...' % _("Non-Copper Clearing"))
proc = self.app.proc_container.new('%s...' % _("Working"))
else:
self.app.proc_container.view.set_busy('%s...' % _("Non-Copper Clearing"))
self.app.proc_container.view.set_busy('%s...' % _("Working"))
QtWidgets.QApplication.processEvents()
# #####################################################################

View File

@@ -1871,6 +1871,9 @@ class ToolPaint(AppTool, Gerber):
tools_storage = self.paint_tools if tools_storage is None else tools_storage
use_rest_strategy = rest if rest is not None else self.ui.rest_cb.get_value()
# TODO this should be in preferences and in the UI
simplification_value = 0.01
sorted_tools = []
if tooldia is not None:
try:
@@ -1959,7 +1962,7 @@ class ToolPaint(AppTool, Gerber):
# effective polygon clearing job
# -----------------------------
try:
cp = []
cp_list = []
for pp in poly_buf:
# provide the app with a way to process the GUI events when in a blocking loop
QtWidgets.QApplication.processEvents()
@@ -1970,7 +1973,7 @@ class ToolPaint(AppTool, Gerber):
cont=cont, paint_method=paint_method, obj=obj,
prog_plot=prog_plot)
if geo_res:
cp.append(geo_res)
cp_list.append(geo_res)
pol_nr += 1
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
# log.debug("Polygons cleared: %d" % pol_nr)
@@ -1980,9 +1983,12 @@ class ToolPaint(AppTool, Gerber):
old_disp_number = disp_number
total_geometry = []
if cp:
for x in cp:
total_geometry += list(x.get_objects())
if cp_list:
for cp in cp_list:
if simplification_value > 0.0:
total_geometry += [x.simplify(simplification_value) for x in cp.get_objects()]
else:
total_geometry += [x for x in cp.get_objects()]
# clean the geometry
total_geometry = [g for g in total_geometry if g and not g.is_empty]
@@ -2141,7 +2147,12 @@ class ToolPaint(AppTool, Gerber):
geo_res = self.paint_polygon_worker(pp, tooldiameter=tool_dia, over=over, conn=conn,
cont=cont, paint_method=paint_method, obj=obj,
prog_plot=prog_plot)
geo_elems = list(geo_res.get_objects())
if simplification_value > 0.0:
geo_elems = [x.simplify(simplification_value) for x in geo_res.get_objects()]
else:
geo_elems = [x for x in geo_res.get_objects()]
# See if the polygon was completely cleared
pp_cleared = unary_union(geo_elems).buffer(tool_dia / 2.0)
rest_geo = pp.difference(pp_cleared)
@@ -2153,6 +2164,7 @@ class ToolPaint(AppTool, Gerber):
if geo_res:
cleared_geo += geo_elems
pol_nr += 1
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
# log.debug("Polygons cleared: %d" % pol_nr)