|
|
@@ -1675,19 +1675,12 @@ class ToolPaint(AppTool, Gerber):
|
|
|
self.app.inform.emit('[ERROR_NOTCL] %s' % _('Geometry could not be painted completely'))
|
|
|
return None
|
|
|
|
|
|
- def paint_poly(self, obj, inside_pt=None, poly_list=None, tooldia=None, order=None, method=None, outname=None,
|
|
|
+ def paint_geo(self, obj, geometry, tooldia=None, order=None, method=None, outname=None,
|
|
|
tools_storage=None, plot=True, run_threaded=True):
|
|
|
"""
|
|
|
- Paints a polygon selected by clicking on its interior or by having a point coordinates given
|
|
|
-
|
|
|
- Note:
|
|
|
- * The margin is taken directly from the form.
|
|
|
+ Paints a given geometry.
|
|
|
|
|
|
- :param run_threaded:
|
|
|
- :param plot:
|
|
|
- :param poly_list:
|
|
|
:param obj: painted object
|
|
|
- :param inside_pt: [x, y]
|
|
|
:param tooldia: Diameter of the painting tool
|
|
|
:param order: if the tools are ordered and how
|
|
|
:param outname: Name of the resulting Geometry Object.
|
|
|
@@ -1695,39 +1688,11 @@ class ToolPaint(AppTool, Gerber):
|
|
|
:param tools_storage: whether to use the current tools_storage self.paints_tools or a different one.
|
|
|
Usage of the different one is related to when this function is called
|
|
|
from a TcL command.
|
|
|
+ :param plot:
|
|
|
+ :param run_threaded:
|
|
|
:return: None
|
|
|
"""
|
|
|
|
|
|
- if obj.kind == 'gerber':
|
|
|
- # I don't do anything here, like buffering when the Gerber is loaded without buffering????!!!!
|
|
|
- if self.app.defaults["gerber_buffering"] == 'no':
|
|
|
- msg = '%s %s %s' % (_("Paint Tool."),
|
|
|
- _("Normal painting polygon task started."),
|
|
|
- _("Buffering geometry..."))
|
|
|
- self.app.inform.emit(msg)
|
|
|
- else:
|
|
|
- self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Normal painting polygon task started.")))
|
|
|
-
|
|
|
- if self.app.defaults["tools_paint_plotting"] == 'progressive':
|
|
|
- if isinstance(obj.solid_geometry, list):
|
|
|
- obj.solid_geometry = MultiPolygon(obj.solid_geometry).buffer(0)
|
|
|
- else:
|
|
|
- obj.solid_geometry = obj.solid_geometry.buffer(0)
|
|
|
- else:
|
|
|
- self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Normal painting polygon task started.")))
|
|
|
-
|
|
|
- if inside_pt and poly_list is None:
|
|
|
- polygon_list = [self.find_polygon(point=inside_pt, geoset=obj.solid_geometry)]
|
|
|
- elif (inside_pt is None and poly_list) or (inside_pt and poly_list):
|
|
|
- polygon_list = poly_list
|
|
|
- else:
|
|
|
- return
|
|
|
-
|
|
|
- # No polygon?
|
|
|
- if polygon_list is None:
|
|
|
- self.app.log.warning('No polygon found.')
|
|
|
- self.app.inform.emit('[WARNING] %s' % _('No polygon found.'))
|
|
|
- return
|
|
|
|
|
|
paint_method = method if method is not None else self.ui.paintmethod_combo.get_value()
|
|
|
# determine if to use the progressive plotting
|
|
|
@@ -1764,6 +1729,7 @@ class ToolPaint(AppTool, Gerber):
|
|
|
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table."))
|
|
|
return 'fail'
|
|
|
|
|
|
+ # Initializes the new geometry object
|
|
|
def job_normal_clear(geo_obj, app_obj):
|
|
|
tool_dia = None
|
|
|
current_uid = None
|
|
|
@@ -1773,8 +1739,10 @@ class ToolPaint(AppTool, Gerber):
|
|
|
# sort the tools if we have an order selected in the UI
|
|
|
if order == 'fwd':
|
|
|
sorted_tools.sort(reverse=False)
|
|
|
- else:
|
|
|
+ elif order == 'rev':
|
|
|
sorted_tools.sort(reverse=True)
|
|
|
+ else:
|
|
|
+ pass
|
|
|
|
|
|
for tool_dia in sorted_tools:
|
|
|
log.debug("Starting geometry processing for tool: %s" % str(tool_dia))
|
|
|
@@ -1801,7 +1769,7 @@ class ToolPaint(AppTool, Gerber):
|
|
|
paint_offset = float(tools_storage[current_uid]['data']['tools_paintoffset'])
|
|
|
|
|
|
poly_buf = []
|
|
|
- for pol in polygon_list:
|
|
|
+ for pol in geometry:
|
|
|
buffered_pol = pol.buffer(-paint_offset)
|
|
|
if buffered_pol and not buffered_pol.is_empty:
|
|
|
poly_buf.append(buffered_pol)
|
|
|
@@ -1858,17 +1826,18 @@ class ToolPaint(AppTool, Gerber):
|
|
|
if cp:
|
|
|
for x in cp:
|
|
|
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
|
|
|
except grace:
|
|
|
return "fail"
|
|
|
except Exception as e:
|
|
|
log.debug("Could not Paint the polygons. %s" % str(e))
|
|
|
- self.app.inform.emit(
|
|
|
- '[ERROR] %s\n%s' %
|
|
|
- (_("Could not do Paint. Try a different combination of parameters. "
|
|
|
- "Or a different strategy of paint"), str(e)
|
|
|
- )
|
|
|
- )
|
|
|
+ mssg = '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
|
|
|
+ "Or a different strategy of paint"), str(e))
|
|
|
+ self.app.inform.emit(mssg)
|
|
|
continue
|
|
|
|
|
|
# add the solid_geometry to the current too in self.paint_tools (tools_storage)
|
|
|
@@ -1930,6 +1899,7 @@ class ToolPaint(AppTool, Gerber):
|
|
|
# print("Indexing...", end=' ')
|
|
|
# geo_obj.make_index()
|
|
|
|
|
|
+ # Initializes the new geometry object
|
|
|
def job_rest_clear(geo_obj, app_obj):
|
|
|
current_uid = None
|
|
|
final_solid_geometry = []
|
|
|
@@ -1941,7 +1911,7 @@ class ToolPaint(AppTool, Gerber):
|
|
|
paint_offset = self.ui.rest_offset_entry.get_value()
|
|
|
|
|
|
poly_buf = []
|
|
|
- for pol in polygon_list:
|
|
|
+ for pol in geometry:
|
|
|
buffered_pol = pol.buffer(-paint_offset)
|
|
|
if buffered_pol and not buffered_pol.is_empty:
|
|
|
try:
|
|
|
@@ -2021,9 +1991,11 @@ class ToolPaint(AppTool, Gerber):
|
|
|
if rest and not rest.is_empty:
|
|
|
try:
|
|
|
for r in rest:
|
|
|
- rest_list.append(r)
|
|
|
+ if r.is_valid:
|
|
|
+ rest_list.append(r)
|
|
|
except TypeError:
|
|
|
- rest_list.append(rest)
|
|
|
+ if rest.is_valid:
|
|
|
+ rest_list.append(rest)
|
|
|
|
|
|
if geo_res:
|
|
|
cleared_geo += geo_elems
|
|
|
@@ -2059,9 +2031,11 @@ class ToolPaint(AppTool, Gerber):
|
|
|
if rest and not rest.is_empty:
|
|
|
try:
|
|
|
for r in rest:
|
|
|
- rest_list.append(r)
|
|
|
+ if r.is_valid:
|
|
|
+ rest_list.append(r)
|
|
|
except TypeError:
|
|
|
- rest_list.append(rest)
|
|
|
+ if rest.is_valid:
|
|
|
+ rest_list.append(rest)
|
|
|
|
|
|
if geo_res:
|
|
|
cleared_geo += geo_elems
|
|
|
@@ -2088,19 +2062,27 @@ class ToolPaint(AppTool, Gerber):
|
|
|
|
|
|
# Area to clear next
|
|
|
log.debug("Generating rest geometry for the next tool.")
|
|
|
- buffered_cleared = cascaded_union(cleared_geo).buffer(tool_dia / 2.0)
|
|
|
+
|
|
|
+ buffered_cleared = unary_union(cleared_geo)
|
|
|
+ buffered_cleared = buffered_cleared.buffer(tool_dia / 2.0)
|
|
|
poly_buf = poly_buf.difference(buffered_cleared)
|
|
|
|
|
|
tmp = []
|
|
|
try:
|
|
|
for p in poly_buf:
|
|
|
- tmp.append(p)
|
|
|
+ if p.is_valid:
|
|
|
+ tmp.append(p)
|
|
|
except TypeError:
|
|
|
- tmp.append(poly_buf)
|
|
|
+ if poly_buf.is_valid:
|
|
|
+ tmp.append(poly_buf)
|
|
|
+
|
|
|
tmp += rest_list
|
|
|
+
|
|
|
poly_buf = MultiPolygon(tmp)
|
|
|
+ if not poly_buf.is_valid:
|
|
|
+ poly_buf = cascaded_union(tmp)
|
|
|
|
|
|
- if not poly_buf or poly_buf.is_empty:
|
|
|
+ if not poly_buf or poly_buf.is_empty or not poly_buf.is_valid:
|
|
|
log.debug("Rest geometry empty. Breaking.")
|
|
|
break
|
|
|
|
|
|
@@ -2168,26 +2150,24 @@ class ToolPaint(AppTool, Gerber):
|
|
|
return
|
|
|
except Exception as er:
|
|
|
proc.done()
|
|
|
- app_obj.inform.emit('[ERROR] %s --> %s' % ('PaintTool.paint_poly()', str(er)))
|
|
|
+ app_obj.inform.emit('[ERROR] %s --> %s' % ('PaintTool.paint_geo()', str(er)))
|
|
|
traceback.print_stack()
|
|
|
return
|
|
|
proc.done()
|
|
|
|
|
|
if ret == 'fail':
|
|
|
- self.app.inform.emit('[ERROR] %s' % _("Paint Single failed."))
|
|
|
+ self.app.inform.emit('[ERROR] %s' % _("Paint failed."))
|
|
|
return
|
|
|
|
|
|
# focus on Selected Tab
|
|
|
# self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
|
|
|
|
|
- self.app.inform.emit('[success] %s' % _("Paint Single Done."))
|
|
|
-
|
|
|
- self.app.inform.emit(_("Polygon Paint started ..."))
|
|
|
+ self.app.inform.emit('[success] %s' % _("Paint Done."))
|
|
|
|
|
|
# Promise object with the new name
|
|
|
self.app.collection.promise(name)
|
|
|
|
|
|
- proc = self.app.proc_container.new(_("Painting polygon..."))
|
|
|
+ proc = self.app.proc_container.new(_("Painting..."))
|
|
|
|
|
|
if run_threaded:
|
|
|
# Background
|
|
|
@@ -2195,6 +2175,63 @@ class ToolPaint(AppTool, Gerber):
|
|
|
else:
|
|
|
job_thread(app_obj=self.app)
|
|
|
|
|
|
+ def paint_poly(self, obj, inside_pt=None, poly_list=None, tooldia=None, order=None, method=None, outname=None,
|
|
|
+ tools_storage=None, plot=True, run_threaded=True):
|
|
|
+ """
|
|
|
+ Paints a polygon selected by clicking on its interior or by having a point coordinates given
|
|
|
+
|
|
|
+ Note:
|
|
|
+ * The margin is taken directly from the form.
|
|
|
+
|
|
|
+ :param run_threaded:
|
|
|
+ :param plot:
|
|
|
+ :param poly_list:
|
|
|
+ :param obj: painted object
|
|
|
+ :param inside_pt: [x, y]
|
|
|
+ :param tooldia: Diameter of the painting tool
|
|
|
+ :param order: if the tools are ordered and how
|
|
|
+ :param outname: Name of the resulting Geometry Object.
|
|
|
+ :param method: choice out of _("Seed"), 'normal', 'lines'
|
|
|
+ :param tools_storage: whether to use the current tools_storage self.paints_tools or a different one.
|
|
|
+ Usage of the different one is related to when this function is called
|
|
|
+ from a TcL command.
|
|
|
+ :return: None
|
|
|
+ """
|
|
|
+
|
|
|
+ if obj.kind == 'gerber':
|
|
|
+ # I don't do anything here, like buffering when the Gerber is loaded without buffering????!!!!
|
|
|
+ if self.app.defaults["gerber_buffering"] == 'no':
|
|
|
+ msg = '%s %s %s' % (_("Paint Tool."),
|
|
|
+ _("Normal painting polygon task started."),
|
|
|
+ _("Buffering geometry..."))
|
|
|
+ self.app.inform.emit(msg)
|
|
|
+ else:
|
|
|
+ self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Normal painting polygon task started.")))
|
|
|
+
|
|
|
+ if self.app.defaults["tools_paint_plotting"] == 'progressive':
|
|
|
+ if isinstance(obj.solid_geometry, list):
|
|
|
+ obj.solid_geometry = MultiPolygon(obj.solid_geometry).buffer(0)
|
|
|
+ else:
|
|
|
+ obj.solid_geometry = obj.solid_geometry.buffer(0)
|
|
|
+ else:
|
|
|
+ self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Normal painting polygon task started.")))
|
|
|
+
|
|
|
+ if inside_pt and poly_list is None:
|
|
|
+ polygon_list = [self.find_polygon(point=inside_pt, geoset=obj.solid_geometry)]
|
|
|
+ elif (inside_pt is None and poly_list) or (inside_pt and poly_list):
|
|
|
+ polygon_list = poly_list
|
|
|
+ else:
|
|
|
+ return
|
|
|
+
|
|
|
+ # No polygon?
|
|
|
+ if polygon_list is None:
|
|
|
+ self.app.log.warning('No polygon found.')
|
|
|
+ self.app.inform.emit('[WARNING] %s' % _('No polygon found.'))
|
|
|
+ return
|
|
|
+
|
|
|
+ self.paint_geo(obj, polygon_list, tooldia=tooldia, order=order, method=method, outname=outname,
|
|
|
+ tools_storage=tools_storage, plot=plot, run_threaded=run_threaded)
|
|
|
+
|
|
|
def paint_poly_all(self, obj, tooldia=None, order=None, method=None, outname=None, tools_storage=None, plot=True,
|
|
|
run_threaded=True):
|
|
|
"""
|
|
|
@@ -2248,7 +2285,7 @@ class ToolPaint(AppTool, Gerber):
|
|
|
# ## If iterable, expand recursively.
|
|
|
try:
|
|
|
for geo in geometry:
|
|
|
- if geo is not None:
|
|
|
+ if geo and not geo.is_empty and geo.is_valid:
|
|
|
recurse(geometry=geo, reset=False)
|
|
|
|
|
|
# ## Not iterable, do the actual indexing and add.
|
|
|
@@ -2285,930 +2322,90 @@ class ToolPaint(AppTool, Gerber):
|
|
|
self.app.inform.emit('[WARNING] %s' % _('No polygon found.'))
|
|
|
return
|
|
|
|
|
|
- paint_method = method if method is not None else self.ui.paintmethod_combo.get_value()
|
|
|
- # determine if to use the progressive plotting
|
|
|
- prog_plot = True if self.app.defaults["tools_paint_plotting"] == 'progressive' else False
|
|
|
-
|
|
|
- name = outname if outname is not None else self.obj_name + "_paint"
|
|
|
- order = order if order is not None else self.ui.order_radio.get_value()
|
|
|
- tools_storage = self.paint_tools if tools_storage is None else tools_storage
|
|
|
-
|
|
|
- sorted_tools = []
|
|
|
- if tooldia is not None:
|
|
|
- try:
|
|
|
- sorted_tools = [float(eval(dia)) for dia in tooldia.split(",") if dia != '']
|
|
|
- except AttributeError:
|
|
|
- if not isinstance(tooldia, list):
|
|
|
- sorted_tools = [float(tooldia)]
|
|
|
- else:
|
|
|
- sorted_tools = tooldia
|
|
|
- else:
|
|
|
- for row in range(self.ui.tools_table.rowCount()):
|
|
|
- sorted_tools.append(float(self.ui.tools_table.item(row, 1).text()))
|
|
|
-
|
|
|
- proc = self.app.proc_container.new(_("Painting polygons..."))
|
|
|
-
|
|
|
- # Initializes the new geometry object
|
|
|
- def gen_paintarea(geo_obj, app_obj):
|
|
|
- log.debug("Paint Tool. Normal painting all task started.")
|
|
|
+ self.paint_geo(obj, painted_area, tooldia=tooldia, order=order, method=method, outname=outname,
|
|
|
+ tools_storage=tools_storage, plot=plot, run_threaded=run_threaded)
|
|
|
|
|
|
- if order == 'fwd':
|
|
|
- sorted_tools.sort(reverse=False)
|
|
|
- elif order == 'rev':
|
|
|
- sorted_tools.sort(reverse=True)
|
|
|
- else:
|
|
|
- pass
|
|
|
+ def paint_poly_area(self, obj, sel_obj, tooldia=None, order=None, method=None, outname=None,
|
|
|
+ tools_storage=None, plot=True, run_threaded=True):
|
|
|
+ """
|
|
|
+ Paints all polygons in this object that are within the sel_obj object
|
|
|
|
|
|
- tool_dia = None
|
|
|
- current_uid = int(1)
|
|
|
- old_disp_number = 0
|
|
|
+ :param obj: painted object
|
|
|
+ :param sel_obj: paint only what is inside this object bounds
|
|
|
+ :param tooldia: a tuple or single element made out of diameters of the tools to be used
|
|
|
+ :param order: if the tools are ordered and how
|
|
|
+ :param outname: name of the resulting object
|
|
|
+ :param method: choice out of _("Seed"), 'normal', 'lines'
|
|
|
+ :param tools_storage: whether to use the current tools_storage self.paints_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:
|
|
|
+ :param plot:
|
|
|
+ :return:
|
|
|
+ """
|
|
|
|
|
|
- final_solid_geometry = []
|
|
|
+ def recurse(geometry, reset=True):
|
|
|
+ """
|
|
|
+ Creates a list of non-iterable linear geometry objects.
|
|
|
+ Results are placed in self.flat_geometry
|
|
|
|
|
|
- for tool_dia in sorted_tools:
|
|
|
- log.debug("Starting geometry processing for tool: %s" % str(tool_dia))
|
|
|
- mssg = '[success] %s %s%s %s' % (_('Painting with tool diameter = '),
|
|
|
- str(tool_dia), self.units.lower(),
|
|
|
- _('started'))
|
|
|
- app_obj.inform.emit(mssg)
|
|
|
- app_obj.proc_container.update_view_text(' %d%%' % 0)
|
|
|
+ :param geometry: Shapely type or list or list of list of such.
|
|
|
+ :param reset: Clears the contents of self.flat_geometry.
|
|
|
+ """
|
|
|
+ if self.app.abort_flag:
|
|
|
+ # graceful abort requested by the user
|
|
|
+ raise grace
|
|
|
|
|
|
- # 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_dia)):
|
|
|
- current_uid = int(k)
|
|
|
- break
|
|
|
- if not current_uid:
|
|
|
- return "fail"
|
|
|
+ if geometry is None:
|
|
|
+ return
|
|
|
|
|
|
- # determine the tool parameters to use
|
|
|
- over = float(tools_storage[current_uid]['data']['tools_paintoverlap']) / 100.0
|
|
|
- conn = tools_storage[current_uid]['data']['tools_pathconnect']
|
|
|
- cont = tools_storage[current_uid]['data']['tools_paintcontour']
|
|
|
+ if reset:
|
|
|
+ self.flat_geometry = []
|
|
|
|
|
|
- paint_offset = float(tools_storage[current_uid]['data']['tools_paintoffset'])
|
|
|
- poly_buf = []
|
|
|
- for pol in painted_area:
|
|
|
- pol = Polygon(pol) if not isinstance(pol, Polygon) else pol
|
|
|
- buffered_pol = pol.buffer(-paint_offset)
|
|
|
- if buffered_pol and not buffered_pol.is_empty:
|
|
|
- poly_buf.append(buffered_pol)
|
|
|
+ # ## If iterable, expand recursively.
|
|
|
+ try:
|
|
|
+ for geo in geometry:
|
|
|
+ if geo is not None:
|
|
|
+ recurse(geometry=geo, reset=False)
|
|
|
|
|
|
- if not poly_buf:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("Margin parameter too big. Tool is not used"))
|
|
|
- continue
|
|
|
+ # ## Not iterable, do the actual indexing and add.
|
|
|
+ except TypeError:
|
|
|
+ if isinstance(geometry, LinearRing):
|
|
|
+ g = Polygon(geometry)
|
|
|
+ self.flat_geometry.append(g)
|
|
|
+ else:
|
|
|
+ self.flat_geometry.append(geometry)
|
|
|
|
|
|
- # variables to display the percentage of work done
|
|
|
- geo_len = len(poly_buf)
|
|
|
+ return self.flat_geometry
|
|
|
|
|
|
- log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
|
|
|
+ # this is were heavy lifting is done and creating the geometry to be painted
|
|
|
+ target_geo = MultiPolygon(obj.solid_geometry)
|
|
|
+ if obj.kind == 'gerber':
|
|
|
+ # I don't do anything here, like buffering when the Gerber is loaded without buffering????!!!!
|
|
|
+ if self.app.defaults["gerber_buffering"] == 'no':
|
|
|
+ msg = '%s %s %s' % (_("Paint Tool."),
|
|
|
+ _("Painting area task started."),
|
|
|
+ _("Buffering geometry..."))
|
|
|
+ self.app.inform.emit(msg)
|
|
|
+ else:
|
|
|
+ self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Painting area task started.")))
|
|
|
|
|
|
- pol_nr = 0
|
|
|
+ if obj.kind == 'gerber':
|
|
|
+ if self.app.defaults["tools_paint_plotting"] == 'progressive':
|
|
|
+ target_geo = target_geo.buffer(0)
|
|
|
+ else:
|
|
|
+ self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Painting area task started.")))
|
|
|
|
|
|
- # -----------------------------
|
|
|
- # effective polygon clearing job
|
|
|
- # -----------------------------
|
|
|
- poly_processed = []
|
|
|
+ geo_to_paint = target_geo.intersection(sel_obj)
|
|
|
+ painted_area = recurse(geo_to_paint)
|
|
|
|
|
|
- try:
|
|
|
- cp = []
|
|
|
- try:
|
|
|
- for pp in poly_buf:
|
|
|
- # 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
|
|
|
+ # No polygon?
|
|
|
+ if not painted_area:
|
|
|
+ self.app.log.warning('No polygon found.')
|
|
|
+ self.app.inform.emit('[WARNING] %s' % _('No polygon found.'))
|
|
|
+ return
|
|
|
|
|
|
- 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)
|
|
|
- if geo_res:
|
|
|
- cp.append(geo_res)
|
|
|
- poly_processed.append(True)
|
|
|
- else:
|
|
|
- poly_processed.append(False)
|
|
|
-
|
|
|
- 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:
|
|
|
- app_obj.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))
|
|
|
-
|
|
|
- except TypeError:
|
|
|
- # 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
|
|
|
-
|
|
|
- geo_res = self.paint_polygon_worker(poly_buf, tooldiameter=tool_dia, over=over, conn=conn,
|
|
|
- cont=cont, paint_method=paint_method, obj=obj,
|
|
|
- prog_plot=prog_plot)
|
|
|
- if geo_res:
|
|
|
- cp.append(geo_res)
|
|
|
- poly_processed.append(True)
|
|
|
- else:
|
|
|
- poly_processed.append(False)
|
|
|
-
|
|
|
- total_geometry = []
|
|
|
- if cp:
|
|
|
- for x in cp:
|
|
|
- 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
|
|
|
-
|
|
|
- except Exception as err:
|
|
|
- log.debug("Could not Paint the polygons. %s" % str(err))
|
|
|
- self.app.inform.emit(
|
|
|
- '[ERROR] %s\n%s' %
|
|
|
- (_("Could not do Paint. Try a different combination of parameters. "
|
|
|
- "Or a different strategy of paint"), str(err)
|
|
|
- )
|
|
|
- )
|
|
|
- continue
|
|
|
-
|
|
|
- p_cleared = poly_processed.count(True)
|
|
|
- p_not_cleared = poly_processed.count(False)
|
|
|
-
|
|
|
- if p_not_cleared:
|
|
|
- app_obj.poly_not_cleared = True
|
|
|
-
|
|
|
- if p_cleared == 0:
|
|
|
- continue
|
|
|
-
|
|
|
- # add the solid_geometry to the current too in self.paint_tools (tools_storage)
|
|
|
- # dictionary and then reset the temporary list that stored that solid_geometry
|
|
|
- tools_storage[current_uid]['solid_geometry'] = deepcopy(total_geometry)
|
|
|
- tools_storage[current_uid]['data']['name'] = name
|
|
|
-
|
|
|
- # clean the progressive plotted shapes if it was used
|
|
|
- if self.app.defaults["tools_paint_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 in list(tools_storage.keys()):
|
|
|
- # if the solid_geometry (type=list) is empty
|
|
|
- if not tools_storage[uid]['solid_geometry']:
|
|
|
- tools_storage.pop(uid, None)
|
|
|
-
|
|
|
- if not tools_storage:
|
|
|
- return 'fail'
|
|
|
-
|
|
|
- geo_obj.options["cnctooldia"] = str(tool_dia)
|
|
|
- # this turn on the FlatCAMCNCJob plot for multiple tools
|
|
|
- geo_obj.multigeo = True
|
|
|
- geo_obj.multitool = True
|
|
|
- geo_obj.tools.clear()
|
|
|
- geo_obj.tools = dict(tools_storage)
|
|
|
-
|
|
|
- geo_obj.solid_geometry = cascaded_union(final_solid_geometry)
|
|
|
-
|
|
|
- try:
|
|
|
- # a, b, c, d = obj.bounds()
|
|
|
- if isinstance(geo_obj.solid_geometry, list):
|
|
|
- a, b, c, d = MultiPolygon(geo_obj.solid_geometry).bounds
|
|
|
- else:
|
|
|
- a, b, c, d = geo_obj.solid_geometry.bounds
|
|
|
-
|
|
|
- geo_obj.options['xmin'] = a
|
|
|
- geo_obj.options['ymin'] = b
|
|
|
- geo_obj.options['xmax'] = c
|
|
|
- geo_obj.options['ymax'] = d
|
|
|
- except Exception as e:
|
|
|
- log.debug("ToolPaint.paint_poly.gen_paintarea() bounds error --> %s" % str(e))
|
|
|
- return
|
|
|
-
|
|
|
- # test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
|
|
|
- has_solid_geo = 0
|
|
|
- for tooluid in geo_obj.tools:
|
|
|
- if geo_obj.tools[tooluid]['solid_geometry']:
|
|
|
- has_solid_geo += 1
|
|
|
-
|
|
|
- if has_solid_geo == 0:
|
|
|
- self.app.inform.emit('[ERROR] %s' %
|
|
|
- _("There is no Painting 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"
|
|
|
-
|
|
|
- # Experimental...
|
|
|
- # print("Indexing...", end=' ')
|
|
|
- # geo_obj.make_index()
|
|
|
-
|
|
|
- self.app.inform.emit('[success] %s' % _("Paint All Done."))
|
|
|
-
|
|
|
- # Initializes the new geometry object
|
|
|
- def gen_paintarea_rest_machining(geo_obj, app_obj):
|
|
|
- log.debug("Paint Tool. Rest machining painting all task started.")
|
|
|
-
|
|
|
- # when using rest machining use always the reverse order; from bigger tool to smaller one
|
|
|
- sorted_tools.sort(reverse=True)
|
|
|
-
|
|
|
- tool_dia = None
|
|
|
- cleared_geo = []
|
|
|
- current_uid = int(1)
|
|
|
- old_disp_number = 0
|
|
|
-
|
|
|
- final_solid_geometry = []
|
|
|
-
|
|
|
- for tool_dia in sorted_tools:
|
|
|
- log.debug("Starting geometry processing for tool: %s" % str(tool_dia))
|
|
|
- mssg = '[success] %s %s%s %s' % (_('Painting with tool diameter = '),
|
|
|
- str(tool_dia),
|
|
|
- self.units.lower(),
|
|
|
- _('started'))
|
|
|
- app_obj.inform.emit(mssg)
|
|
|
- app_obj.proc_container.update_view_text(' %d%%' % 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_dia)):
|
|
|
- current_uid = int(k)
|
|
|
- break
|
|
|
- if not current_uid:
|
|
|
- return "fail"
|
|
|
-
|
|
|
- # determine the tool parameters to use
|
|
|
- over = float(tools_storage[current_uid]['data']['tools_paintoverlap']) / 100.0
|
|
|
- conn = tools_storage[current_uid]['data']['tools_pathconnect']
|
|
|
- cont = tools_storage[current_uid]['data']['tools_paintcontour']
|
|
|
-
|
|
|
- paint_offset = float(tools_storage[current_uid]['data']['tools_paintoffset'])
|
|
|
- poly_buf = []
|
|
|
- for pol in painted_area:
|
|
|
- pol = Polygon(pol) if not isinstance(pol, Polygon) else pol
|
|
|
- buffered_pol = pol.buffer(-paint_offset)
|
|
|
- if buffered_pol and not buffered_pol.is_empty:
|
|
|
- poly_buf.append(buffered_pol)
|
|
|
-
|
|
|
- if not poly_buf:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("Margin parameter too big. Tool is not used"))
|
|
|
- continue
|
|
|
-
|
|
|
- # variables to display the percentage of work done
|
|
|
- geo_len = len(poly_buf)
|
|
|
-
|
|
|
- log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
|
|
|
-
|
|
|
- pol_nr = 0
|
|
|
-
|
|
|
- # -----------------------------
|
|
|
- # effective polygon clearing job
|
|
|
- # -----------------------------
|
|
|
- try:
|
|
|
- cp = []
|
|
|
- try:
|
|
|
- for pp in poly_buf:
|
|
|
- # 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
|
|
|
- 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)
|
|
|
- if geo_res:
|
|
|
- cp.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)
|
|
|
-
|
|
|
- if old_disp_number < disp_number <= 100:
|
|
|
- self.app.proc_container.update_view_text(' %d%%' % disp_number)
|
|
|
- old_disp_number = disp_number
|
|
|
- except TypeError:
|
|
|
- # 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
|
|
|
-
|
|
|
- geo_res = self.paint_polygon_worker(poly_buf, tooldiameter=tool_dia, over=over, conn=conn,
|
|
|
- cont=cont, paint_method=paint_method, obj=obj,
|
|
|
- prog_plot=prog_plot)
|
|
|
- if geo_res:
|
|
|
- cp.append(geo_res)
|
|
|
-
|
|
|
- if cp:
|
|
|
- for x in cp:
|
|
|
- cleared_geo += list(x.get_objects())
|
|
|
- final_solid_geometry += cleared_geo
|
|
|
- except grace:
|
|
|
- return "fail"
|
|
|
- except Exception as e:
|
|
|
- log.debug("Could not Paint the polygons. %s" % str(e))
|
|
|
- mssg = '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
|
|
|
- "Or a different strategy of paint"),
|
|
|
- str(e))
|
|
|
- self.app.inform.emit(mssg)
|
|
|
- continue
|
|
|
-
|
|
|
- # add the solid_geometry to the current too in self.paint_tools (or tools_storage) dictionary and
|
|
|
- # then reset the temporary list that stored that solid_geometry
|
|
|
- tools_storage[current_uid]['solid_geometry'] = deepcopy(cleared_geo)
|
|
|
- tools_storage[current_uid]['data']['name'] = name
|
|
|
- cleared_geo[:] = []
|
|
|
-
|
|
|
- # clean the progressive plotted shapes if it was used
|
|
|
- if self.app.defaults["tools_paint_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 in list(tools_storage.keys()):
|
|
|
- # if the solid_geometry (type=list) is empty
|
|
|
- if not tools_storage[uid]['solid_geometry']:
|
|
|
- tools_storage.pop(uid, None)
|
|
|
-
|
|
|
- if not tools_storage:
|
|
|
- return 'fail'
|
|
|
-
|
|
|
- geo_obj.options["cnctooldia"] = str(tool_dia)
|
|
|
- # this turn on the FlatCAMCNCJob plot for multiple tools
|
|
|
- geo_obj.multigeo = True
|
|
|
- geo_obj.multitool = True
|
|
|
- geo_obj.tools.clear()
|
|
|
- geo_obj.tools = dict(tools_storage)
|
|
|
-
|
|
|
- geo_obj.solid_geometry = cascaded_union(final_solid_geometry)
|
|
|
-
|
|
|
- try:
|
|
|
- # a, b, c, d = obj.bounds()
|
|
|
- if isinstance(geo_obj.solid_geometry, list):
|
|
|
- a, b, c, d = MultiPolygon(geo_obj.solid_geometry).bounds
|
|
|
- else:
|
|
|
- a, b, c, d = geo_obj.solid_geometry.bounds
|
|
|
-
|
|
|
- geo_obj.options['xmin'] = a
|
|
|
- geo_obj.options['ymin'] = b
|
|
|
- geo_obj.options['xmax'] = c
|
|
|
- geo_obj.options['ymax'] = d
|
|
|
- except Exception as e:
|
|
|
- log.debug("ToolPaint.paint_poly.gen_paintarea_rest_machining() bounds error --> %s" % str(e))
|
|
|
- return
|
|
|
-
|
|
|
- # test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
|
|
|
- has_solid_geo = 0
|
|
|
- for tooluid in geo_obj.tools:
|
|
|
- if geo_obj.tools[tooluid]['solid_geometry']:
|
|
|
- has_solid_geo += 1
|
|
|
- if has_solid_geo == 0:
|
|
|
- self.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
|
- _("There is no Painting 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'
|
|
|
-
|
|
|
- # Experimental...
|
|
|
- # print("Indexing...", end=' ')
|
|
|
- # geo_obj.make_index()
|
|
|
-
|
|
|
- self.app.inform.emit('[success] %s' % _("Paint All with Rest-Machining done."))
|
|
|
-
|
|
|
- def job_thread(app_obj):
|
|
|
- try:
|
|
|
- if self.ui.rest_cb.isChecked():
|
|
|
- ret = app_obj.app_obj.new_object("geometry", name, gen_paintarea_rest_machining, plot=plot)
|
|
|
- else:
|
|
|
- ret = app_obj.app_obj.new_object("geometry", name, gen_paintarea, plot=plot)
|
|
|
- except grace:
|
|
|
- proc.done()
|
|
|
- return
|
|
|
- except Exception as err:
|
|
|
- proc.done()
|
|
|
- app_obj.inform.emit('[ERROR] %s --> %s' % ('PaintTool.paint_poly_all()', str(err)))
|
|
|
- traceback.print_stack()
|
|
|
- return
|
|
|
- proc.done()
|
|
|
-
|
|
|
- if ret == 'fail':
|
|
|
- self.app.inform.emit('[ERROR] %s' % _("Paint All failed."))
|
|
|
- return
|
|
|
-
|
|
|
- # focus on Selected Tab
|
|
|
- # self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
|
|
-
|
|
|
- self.app.inform.emit('[success] %s' % _("Paint Poly All Done."))
|
|
|
-
|
|
|
- self.app.inform.emit(_("Polygon Paint started ..."))
|
|
|
-
|
|
|
- # Promise object with the new name
|
|
|
- self.app.collection.promise(name)
|
|
|
-
|
|
|
- if run_threaded:
|
|
|
- # Background
|
|
|
- self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
|
|
- else:
|
|
|
- job_thread(app_obj=self.app)
|
|
|
-
|
|
|
- def paint_poly_area(self, obj, sel_obj, tooldia=None, order=None, method=None, outname=None,
|
|
|
- tools_storage=None, plot=True, run_threaded=True):
|
|
|
- """
|
|
|
- Paints all polygons in this object that are within the sel_obj object
|
|
|
-
|
|
|
- :param obj: painted object
|
|
|
- :param sel_obj: paint only what is inside this object bounds
|
|
|
- :param tooldia: a tuple or single element made out of diameters of the tools to be used
|
|
|
- :param order: if the tools are ordered and how
|
|
|
- :param outname: name of the resulting object
|
|
|
- :param method: choice out of _("Seed"), 'normal', 'lines'
|
|
|
- :param tools_storage: whether to use the current tools_storage self.paints_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:
|
|
|
- :param plot:
|
|
|
- :return:
|
|
|
- """
|
|
|
-
|
|
|
- def recurse(geometry, reset=True):
|
|
|
- """
|
|
|
- Creates a list of non-iterable linear geometry objects.
|
|
|
- Results are placed in self.flat_geometry
|
|
|
-
|
|
|
- :param geometry: Shapely type or list or list of list of such.
|
|
|
- :param reset: Clears the contents of self.flat_geometry.
|
|
|
- """
|
|
|
- if self.app.abort_flag:
|
|
|
- # graceful abort requested by the user
|
|
|
- raise grace
|
|
|
-
|
|
|
- if geometry is None:
|
|
|
- return
|
|
|
-
|
|
|
- if reset:
|
|
|
- self.flat_geometry = []
|
|
|
-
|
|
|
- # ## If iterable, expand recursively.
|
|
|
- try:
|
|
|
- for geo in geometry:
|
|
|
- if geo is not None:
|
|
|
- recurse(geometry=geo, reset=False)
|
|
|
-
|
|
|
- # ## Not iterable, do the actual indexing and add.
|
|
|
- except TypeError:
|
|
|
- if isinstance(geometry, LinearRing):
|
|
|
- g = Polygon(geometry)
|
|
|
- self.flat_geometry.append(g)
|
|
|
- else:
|
|
|
- self.flat_geometry.append(geometry)
|
|
|
-
|
|
|
- return self.flat_geometry
|
|
|
-
|
|
|
- # this is were heavy lifting is done and creating the geometry to be painted
|
|
|
- target_geo = MultiPolygon(obj.solid_geometry)
|
|
|
- if obj.kind == 'gerber':
|
|
|
- # I don't do anything here, like buffering when the Gerber is loaded without buffering????!!!!
|
|
|
- if self.app.defaults["gerber_buffering"] == 'no':
|
|
|
- msg = '%s %s %s' % (_("Paint Tool."),
|
|
|
- _("Painting area task started."),
|
|
|
- _("Buffering geometry..."))
|
|
|
- self.app.inform.emit(msg)
|
|
|
- else:
|
|
|
- self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Painting area task started.")))
|
|
|
-
|
|
|
- if obj.kind == 'gerber':
|
|
|
- if self.app.defaults["tools_paint_plotting"] == 'progressive':
|
|
|
- target_geo = target_geo.buffer(0)
|
|
|
- else:
|
|
|
- self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Painting area task started.")))
|
|
|
-
|
|
|
- geo_to_paint = target_geo.intersection(sel_obj)
|
|
|
- painted_area = recurse(geo_to_paint)
|
|
|
-
|
|
|
- # No polygon?
|
|
|
- if not painted_area:
|
|
|
- self.app.log.warning('No polygon found.')
|
|
|
- self.app.inform.emit('[WARNING] %s' % _('No polygon found.'))
|
|
|
- return
|
|
|
-
|
|
|
- paint_method = method if method is not None else self.ui.paintmethod_combo.get_value()
|
|
|
- # determine if to use the progressive plotting
|
|
|
- prog_plot = True if self.app.defaults["tools_paint_plotting"] == 'progressive' else False
|
|
|
-
|
|
|
- name = outname if outname is not None else self.obj_name + "_paint"
|
|
|
- order = order if order is not None else self.ui.order_radio.get_value()
|
|
|
- tools_storage = self.paint_tools if tools_storage is None else tools_storage
|
|
|
-
|
|
|
- sorted_tools = []
|
|
|
- if tooldia is not None:
|
|
|
- try:
|
|
|
- sorted_tools = [float(eval(dia)) for dia in tooldia.split(",") if dia != '']
|
|
|
- except AttributeError:
|
|
|
- if not isinstance(tooldia, list):
|
|
|
- sorted_tools = [float(tooldia)]
|
|
|
- else:
|
|
|
- sorted_tools = tooldia
|
|
|
- else:
|
|
|
- for row in range(self.ui.tools_table.rowCount()):
|
|
|
- sorted_tools.append(float(self.ui.tools_table.item(row, 1).text()))
|
|
|
-
|
|
|
- proc = self.app.proc_container.new(_("Painting polygons..."))
|
|
|
-
|
|
|
- # Initializes the new geometry object
|
|
|
- def gen_paintarea(geo_obj, app_obj):
|
|
|
- log.debug("Paint Tool. Normal painting area task started.")
|
|
|
-
|
|
|
- if order == 'fwd':
|
|
|
- sorted_tools.sort(reverse=False)
|
|
|
- elif order == 'rev':
|
|
|
- sorted_tools.sort(reverse=True)
|
|
|
- else:
|
|
|
- pass
|
|
|
-
|
|
|
- tool_dia = None
|
|
|
- current_uid = int(1)
|
|
|
- old_disp_number = 0
|
|
|
-
|
|
|
- final_solid_geometry = []
|
|
|
-
|
|
|
- for tool_dia in sorted_tools:
|
|
|
- log.debug("Starting geometry processing for tool: %s" % str(tool_dia))
|
|
|
- mssg = '[success] %s %s%s %s' % (_('Painting with tool diameter = '),
|
|
|
- str(tool_dia),
|
|
|
- self.units.lower(),
|
|
|
- _('started'))
|
|
|
- app_obj.inform.emit(mssg)
|
|
|
- app_obj.proc_container.update_view_text(' %d%%' % 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_dia)):
|
|
|
- current_uid = int(k)
|
|
|
- break
|
|
|
- if not current_uid:
|
|
|
- return "fail"
|
|
|
-
|
|
|
- # determine the tool parameters to use
|
|
|
- over = float(tools_storage[current_uid]['data']['tools_paintoverlap']) / 100.0
|
|
|
- conn = tools_storage[current_uid]['data']['tools_pathconnect']
|
|
|
- cont = tools_storage[current_uid]['data']['tools_paintcontour']
|
|
|
-
|
|
|
- paint_offset = float(tools_storage[current_uid]['data']['tools_paintoffset'])
|
|
|
-
|
|
|
- poly_buf = []
|
|
|
- for pol in painted_area:
|
|
|
- pol = Polygon(pol) if not isinstance(pol, Polygon) else pol
|
|
|
- buffered_pol = pol.buffer(-paint_offset)
|
|
|
- if buffered_pol and not buffered_pol.is_empty:
|
|
|
- poly_buf.append(buffered_pol)
|
|
|
-
|
|
|
- if not poly_buf:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("Margin parameter too big. Tool is not used"))
|
|
|
- continue
|
|
|
-
|
|
|
- # variables to display the percentage of work done
|
|
|
- geo_len = len(poly_buf)
|
|
|
-
|
|
|
- log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
|
|
|
-
|
|
|
- pol_nr = 0
|
|
|
-
|
|
|
- # -----------------------------
|
|
|
- # effective polygon clearing job
|
|
|
- # -----------------------------
|
|
|
- poly_processed = []
|
|
|
- total_geometry = []
|
|
|
-
|
|
|
- try:
|
|
|
- try:
|
|
|
- for pp in poly_buf:
|
|
|
- # 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
|
|
|
-
|
|
|
- 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)
|
|
|
- if geo_res and geo_res.objects:
|
|
|
- total_geometry += list(geo_res.get_objects())
|
|
|
- poly_processed.append(True)
|
|
|
- else:
|
|
|
- poly_processed.append(False)
|
|
|
-
|
|
|
- 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:
|
|
|
- app_obj.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))
|
|
|
-
|
|
|
- except TypeError:
|
|
|
- # 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
|
|
|
-
|
|
|
- geo_res = self.paint_polygon_worker(poly_buf, tooldiameter=tool_dia, over=over, conn=conn,
|
|
|
- cont=cont, paint_method=paint_method, obj=obj,
|
|
|
- prog_plot=prog_plot)
|
|
|
- if geo_res and geo_res.objects:
|
|
|
- total_geometry += list(geo_res.get_objects())
|
|
|
- poly_processed.append(True)
|
|
|
- else:
|
|
|
- poly_processed.append(False)
|
|
|
-
|
|
|
- except Exception as err:
|
|
|
- log.debug("Could not Paint the polygons. %s" % str(err))
|
|
|
- self.app.inform.emit(
|
|
|
- '[ERROR] %s\n%s' %
|
|
|
- (_("Could not do Paint. Try a different combination of parameters. "
|
|
|
- "Or a different strategy of paint"), str(err)
|
|
|
- )
|
|
|
- )
|
|
|
- continue
|
|
|
-
|
|
|
- p_cleared = poly_processed.count(True)
|
|
|
- p_not_cleared = poly_processed.count(False)
|
|
|
-
|
|
|
- if p_not_cleared:
|
|
|
- app_obj.poly_not_cleared = True
|
|
|
-
|
|
|
- if p_cleared == 0:
|
|
|
- continue
|
|
|
-
|
|
|
- # add the solid_geometry to the current too in self.paint_tools (tools_storage)
|
|
|
- # dictionary and then reset the temporary list that stored that solid_geometry
|
|
|
- tools_storage[current_uid]['solid_geometry'] = deepcopy(total_geometry)
|
|
|
- tools_storage[current_uid]['data']['name'] = name
|
|
|
- total_geometry[:] = []
|
|
|
-
|
|
|
- # clean the progressive plotted shapes if it was used
|
|
|
- if self.app.defaults["tools_paint_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 in list(tools_storage.keys()):
|
|
|
- # if the solid_geometry (type=list) is empty
|
|
|
- if not tools_storage[uid]['solid_geometry']:
|
|
|
- tools_storage.pop(uid, None)
|
|
|
-
|
|
|
- if not tools_storage:
|
|
|
- return 'fail'
|
|
|
-
|
|
|
- geo_obj.options["cnctooldia"] = str(tool_dia)
|
|
|
- # this turn on the FlatCAMCNCJob plot for multiple tools
|
|
|
- geo_obj.multigeo = True
|
|
|
- geo_obj.multitool = True
|
|
|
- geo_obj.tools.clear()
|
|
|
- geo_obj.tools = dict(tools_storage)
|
|
|
-
|
|
|
- geo_obj.solid_geometry = cascaded_union(final_solid_geometry)
|
|
|
-
|
|
|
- try:
|
|
|
- a, b, c, d = self.paint_bounds(geo_to_paint)
|
|
|
- geo_obj.options['xmin'] = a
|
|
|
- geo_obj.options['ymin'] = b
|
|
|
- geo_obj.options['xmax'] = c
|
|
|
- geo_obj.options['ymax'] = d
|
|
|
- except Exception as e:
|
|
|
- log.debug("ToolPaint.paint_poly.gen_paintarea() bounds error --> %s" % str(e))
|
|
|
- return
|
|
|
-
|
|
|
- # test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
|
|
|
- has_solid_geo = 0
|
|
|
- for tooluid in geo_obj.tools:
|
|
|
- if geo_obj.tools[tooluid]['solid_geometry']:
|
|
|
- has_solid_geo += 1
|
|
|
- if has_solid_geo == 0:
|
|
|
- self.app.inform.emit('[ERROR] %s' %
|
|
|
- _("There is no Painting 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"
|
|
|
-
|
|
|
- # Experimental...
|
|
|
- # print("Indexing...", end=' ')
|
|
|
- # geo_obj.make_index()
|
|
|
-
|
|
|
- self.app.inform.emit('[success] %s' % _("Paint Area Done."))
|
|
|
-
|
|
|
- # Initializes the new geometry object
|
|
|
- def gen_paintarea_rest_machining(geo_obj, app_obj):
|
|
|
- log.debug("Paint Tool. Rest machining painting area task started.")
|
|
|
-
|
|
|
- sorted_tools.sort(reverse=True)
|
|
|
-
|
|
|
- cleared_geo = []
|
|
|
-
|
|
|
- tool_dia = None
|
|
|
- current_uid = int(1)
|
|
|
- old_disp_number = 0
|
|
|
-
|
|
|
- final_solid_geometry = []
|
|
|
-
|
|
|
- for tool_dia in sorted_tools:
|
|
|
- log.debug("Starting geometry processing for tool: %s" % str(tool_dia))
|
|
|
- mssg = '[success] %s %s%s %s' % (_('Painting with tool diameter = '),
|
|
|
- str(tool_dia),
|
|
|
- self.units.lower(),
|
|
|
- _('started'))
|
|
|
- app_obj.inform.emit(mssg)
|
|
|
- app_obj.proc_container.update_view_text(' %d%%' % 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_dia)):
|
|
|
- current_uid = int(k)
|
|
|
- break
|
|
|
- if not current_uid:
|
|
|
- return "fail"
|
|
|
-
|
|
|
- # determine the tool parameters to use
|
|
|
- over = float(tools_storage[current_uid]['data']['tools_paintoverlap']) / 100.0
|
|
|
- conn = tools_storage[current_uid]['data']['tools_pathconnect']
|
|
|
- cont = tools_storage[current_uid]['data']['tools_paintcontour']
|
|
|
-
|
|
|
- paint_offset = float(tools_storage[current_uid]['data']['tools_paintoffset'])
|
|
|
-
|
|
|
- poly_buf = []
|
|
|
- for pol in painted_area:
|
|
|
- pol = Polygon(pol) if not isinstance(pol, Polygon) else pol
|
|
|
- buffered_pol = pol.buffer(-paint_offset)
|
|
|
- if buffered_pol and not buffered_pol.is_empty:
|
|
|
- poly_buf.append(buffered_pol)
|
|
|
-
|
|
|
- if not poly_buf:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("Margin parameter too big. Tool is not used"))
|
|
|
- continue
|
|
|
-
|
|
|
- # variables to display the percentage of work done
|
|
|
- geo_len = len(poly_buf)
|
|
|
-
|
|
|
- log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
|
|
|
-
|
|
|
- pol_nr = 0
|
|
|
-
|
|
|
- # -----------------------------
|
|
|
- # effective polygon clearing job
|
|
|
- # -----------------------------
|
|
|
- poly_processed = []
|
|
|
-
|
|
|
- try:
|
|
|
- try:
|
|
|
- for pp in poly_buf:
|
|
|
- # 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
|
|
|
-
|
|
|
- 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)
|
|
|
- if geo_res and geo_res.objects:
|
|
|
- cleared_geo += list(geo_res.get_objects())
|
|
|
- poly_processed.append(True)
|
|
|
- else:
|
|
|
- poly_processed.append(False)
|
|
|
-
|
|
|
- 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:
|
|
|
- app_obj.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))
|
|
|
-
|
|
|
- except TypeError:
|
|
|
- # 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
|
|
|
-
|
|
|
- geo_res = self.paint_polygon_worker(poly_buf, tooldiameter=tool_dia, over=over, conn=conn,
|
|
|
- cont=cont, paint_method=paint_method, obj=obj,
|
|
|
- prog_plot=prog_plot)
|
|
|
- if geo_res and geo_res.objects:
|
|
|
- cleared_geo += list(geo_res.get_objects())
|
|
|
- poly_processed.append(True)
|
|
|
- else:
|
|
|
- poly_processed.append(False)
|
|
|
-
|
|
|
- except Exception as err:
|
|
|
- log.debug("Could not Paint the polygons. %s" % str(err))
|
|
|
- mssg = '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
|
|
|
- "Or a different strategy of paint"),
|
|
|
- str(err))
|
|
|
- self.app.inform.emit(mssg)
|
|
|
- continue
|
|
|
-
|
|
|
- p_cleared = poly_processed.count(True)
|
|
|
- p_not_cleared = poly_processed.count(False)
|
|
|
-
|
|
|
- if p_not_cleared:
|
|
|
- app_obj.poly_not_cleared = True
|
|
|
-
|
|
|
- if p_cleared == 0:
|
|
|
- continue
|
|
|
-
|
|
|
- final_solid_geometry += cleared_geo
|
|
|
- # add the solid_geometry to the current too in self.paint_tools (or tools_storage) dictionary and
|
|
|
- # then reset the temporary list that stored that solid_geometry
|
|
|
- tools_storage[current_uid]['solid_geometry'] = deepcopy(cleared_geo)
|
|
|
- tools_storage[current_uid]['data']['name'] = name
|
|
|
- cleared_geo[:] = []
|
|
|
-
|
|
|
- # clean the progressive plotted shapes if it was used
|
|
|
- if self.app.defaults["tools_paint_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 in list(tools_storage.keys()):
|
|
|
- # if the solid_geometry (type=list) is empty
|
|
|
- if not tools_storage[uid]['solid_geometry']:
|
|
|
- tools_storage.pop(uid, None)
|
|
|
-
|
|
|
- if not tools_storage:
|
|
|
- return 'fail'
|
|
|
-
|
|
|
- geo_obj.options["cnctooldia"] = str(tool_dia)
|
|
|
- # this turn on the FlatCAMCNCJob plot for multiple tools
|
|
|
- geo_obj.multigeo = True
|
|
|
- geo_obj.multitool = True
|
|
|
- geo_obj.tools.clear()
|
|
|
- geo_obj.tools = dict(tools_storage)
|
|
|
-
|
|
|
- geo_obj.solid_geometry = cascaded_union(final_solid_geometry)
|
|
|
-
|
|
|
- try:
|
|
|
- a, b, c, d = self.paint_bounds(geo_to_paint)
|
|
|
- geo_obj.options['xmin'] = a
|
|
|
- geo_obj.options['ymin'] = b
|
|
|
- geo_obj.options['xmax'] = c
|
|
|
- geo_obj.options['ymax'] = d
|
|
|
- except Exception as e:
|
|
|
- log.debug("ToolPaint.paint_poly.gen_paintarea_rest_machining() bounds error --> %s" % str(e))
|
|
|
- return
|
|
|
-
|
|
|
- # test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
|
|
|
- has_solid_geo = 0
|
|
|
- for tooluid in geo_obj.tools:
|
|
|
- if geo_obj.tools[tooluid]['solid_geometry']:
|
|
|
- has_solid_geo += 1
|
|
|
- if has_solid_geo == 0:
|
|
|
- self.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
|
- _("There is no Painting 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'
|
|
|
-
|
|
|
- # Experimental...
|
|
|
- # print("Indexing...", end=' ')
|
|
|
- # geo_obj.make_index()
|
|
|
-
|
|
|
- self.app.inform.emit('[success] %s' % _("Paint All with Rest-Machining done."))
|
|
|
-
|
|
|
- def job_thread(app_obj):
|
|
|
- try:
|
|
|
- if self.ui.rest_cb.isChecked():
|
|
|
- ret = app_obj.app_obj.new_object("geometry", name, gen_paintarea_rest_machining, plot=plot)
|
|
|
- else:
|
|
|
- ret = app_obj.app_obj.new_object("geometry", name, gen_paintarea, plot=plot)
|
|
|
- except grace:
|
|
|
- proc.done()
|
|
|
- return
|
|
|
- except Exception as err:
|
|
|
- proc.done()
|
|
|
- app_obj.inform.emit('[ERROR] %s --> %s' % ('PaintTool.paint_poly_area()', str(err)))
|
|
|
- traceback.print_stack()
|
|
|
- return
|
|
|
- proc.done()
|
|
|
-
|
|
|
- if ret == 'fail':
|
|
|
- self.app.inform.emit('[ERROR] %s' % _("Paint Area failed."))
|
|
|
- return
|
|
|
-
|
|
|
- # focus on Selected Tab
|
|
|
- # self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
|
|
-
|
|
|
- self.app.inform.emit('[success] %s' % _("Paint Poly Area Done."))
|
|
|
-
|
|
|
- self.app.inform.emit(_("Polygon Paint started ..."))
|
|
|
-
|
|
|
- # Promise object with the new name
|
|
|
- self.app.collection.promise(name)
|
|
|
-
|
|
|
- if run_threaded:
|
|
|
- # Background
|
|
|
- self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
|
|
- else:
|
|
|
- job_thread(app_obj=self.app)
|
|
|
+ self.paint_geo(obj, painted_area, tooldia=tooldia, order=order, method=method, outname=outname,
|
|
|
+ tools_storage=tools_storage, plot=plot, run_threaded=run_threaded)
|
|
|
|
|
|
def paint_poly_ref(self, obj, sel_obj, tooldia=None, order=None, method=None, outname=None,
|
|
|
tools_storage=None, plot=True, run_threaded=True):
|