- modified the Paint Tool. Now the Single Polygon and Area/Reference Object painting works with multiple tools too. The tools have to be selected in the Tool Table.

- remade the TclCommand Paint to work in the new configuration of the the app (the painting functions are now in their own tool, Paint Tool)
- fixed a bug in the Properties Tool
This commit is contained in:
Marius Stanciu
2019-08-24 19:55:03 +03:00
committed by Marius
parent 82b39d64d2
commit 87d1558977
5 changed files with 621 additions and 207 deletions

View File

@@ -15,6 +15,9 @@ CAD program, and create G-Code for Isolation routing.
- added ability to turn on/of the grid snapping and to jump to a location while in CutOut Tool manual gap adding action - added ability to turn on/of the grid snapping and to jump to a location while in CutOut Tool manual gap adding action
- made PlotCanvas class inherit from VisPy Canvas instead of creating an instance of it (work of JP) - made PlotCanvas class inherit from VisPy Canvas instead of creating an instance of it (work of JP)
- fixed selection by dragging a selection shape in Geometry Editor - fixed selection by dragging a selection shape in Geometry Editor
- modified the Paint Tool. Now the Single Polygon and Area/Reference Object painting works with multiple tools too. The tools have to be selected in the Tool Table.
- remade the TclCommand Paint to work in the new configuration of the the app (the painting functions are now in their own tool, Paint Tool)
- fixed a bug in the Properties Tool
23.08.2019 23.08.2019

View File

@@ -232,10 +232,10 @@ class ToolPaint(FlatCAMTool, Gerber):
# Method # Method
methodlabel = QtWidgets.QLabel('%s:' % _('Method')) methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
methodlabel.setToolTip( methodlabel.setToolTip(
_("Algorithm for non-copper clearing:<BR>" _("Algorithm for painting:\n"
"<B>Standard</B>: Fixed step inwards.<BR>" "- Standard: Fixed step inwards.\n"
"<B>Seed-based</B>: Outwards from seed.<BR>" "- Seed-based: Outwards from seed.\n"
"<B>Line-based</B>: Parallel lines.") "- Line-based: Parallel lines.")
) )
grid3.addWidget(methodlabel, 3, 0) grid3.addWidget(methodlabel, 3, 0)
self.paintmethod_combo = RadioSet([ self.paintmethod_combo = RadioSet([
@@ -473,14 +473,14 @@ class ToolPaint(FlatCAMTool, Gerber):
self.rest_cb.set_value(False) self.rest_cb.set_value(False)
self.rest_cb.setDisabled(True) self.rest_cb.setDisabled(True)
# delete all tools except first row / tool for single polygon painting # delete all tools except first row / tool for single polygon painting
list_to_del = list(range(1, self.tools_table.rowCount())) # list_to_del = list(range(1, self.tools_table.rowCount()))
if list_to_del: # if list_to_del:
self.on_tool_delete(rows_to_delete=list_to_del) # self.on_tool_delete(rows_to_delete=list_to_del)
# disable addTool and delTool # # disable addTool and delTool
self.addtool_entry.setDisabled(True) # self.addtool_entry.setDisabled(True)
self.addtool_btn.setDisabled(True) # self.addtool_btn.setDisabled(True)
self.deltool_btn.setDisabled(True) # self.deltool_btn.setDisabled(True)
self.tools_table.setContextMenuPolicy(Qt.NoContextMenu) # self.tools_table.setContextMenuPolicy(Qt.NoContextMenu)
if self.selectmethod_combo.get_value() == 'area': if self.selectmethod_combo.get_value() == 'area':
# disable rest-machining for single polygon painting # disable rest-machining for single polygon painting
self.rest_cb.set_value(False) self.rest_cb.set_value(False)
@@ -941,8 +941,28 @@ class ToolPaint(FlatCAMTool, Gerber):
o_name = '%s_multitool_paint' % self.obj_name o_name = '%s_multitool_paint' % self.obj_name
# use the selected tools in the tool table; get diameters
tooldia_list = list()
if self.tools_table.selectedItems():
for x in self.tools_table.selectedItems():
try:
tooldia = float(self.tools_table.item(x.row(), 1).text())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
tooldia = float(self.tools_table.item(x.row(), 1).text().replace(',', '.'))
except ValueError:
self.app.inform.emit(_("[ERROR_NOTCL] Wrong Tool Dia value format entered, "
"use a number."))
continue
tooldia_list.append(tooldia)
else:
self.app.inform.emit(_("[ERROR_NOTCL] No selected tools in Tool Table."))
return
if select_method == "all": if select_method == "all":
self.paint_poly_all(self.paint_obj, self.paint_poly_all(self.paint_obj,
tooldia=tooldia_list,
outname=o_name, outname=o_name,
overlap=overlap, overlap=overlap,
connect=connect, connect=connect,
@@ -952,7 +972,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.app.inform.emit(_("[WARNING_NOTCL] Click inside the desired polygon.")) self.app.inform.emit(_("[WARNING_NOTCL] Click inside the desired polygon."))
# use the first tool in the tool table; get the diameter # use the first tool in the tool table; get the diameter
tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text())) # tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text()))
# To be called after clicking on the plot. # To be called after clicking on the plot.
def doit(event): def doit(event):
@@ -967,12 +987,14 @@ class ToolPaint(FlatCAMTool, Gerber):
self.paint_poly(self.paint_obj, self.paint_poly(self.paint_obj,
inside_pt=[pos[0], pos[1]], inside_pt=[pos[0], pos[1]],
tooldia=tooldia, tooldia=tooldia_list,
overlap=overlap, overlap=overlap,
connect=connect, connect=connect,
contour=contour) contour=contour)
self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot) self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot) self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_connect('mouse_press', doit) self.app.plotcanvas.vis_connect('mouse_press', doit)
@@ -980,7 +1002,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.app.inform.emit(_("[WARNING_NOTCL] Click the start point of the paint area.")) self.app.inform.emit(_("[WARNING_NOTCL] Click the start point of the paint area."))
# use the first tool in the tool table; get the diameter # use the first tool in the tool table; get the diameter
tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text())) # tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text()))
# To be called after clicking on the plot. # To be called after clicking on the plot.
def on_mouse_release(event): def on_mouse_release(event):
@@ -1024,6 +1046,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.sel_rect = cascaded_union(self.sel_rect) self.sel_rect = cascaded_union(self.sel_rect)
self.paint_poly_area(obj=self.paint_obj, self.paint_poly_area(obj=self.paint_obj,
tooldia=tooldia_list,
sel_obj= self.sel_rect, sel_obj= self.sel_rect,
outname=o_name, outname=o_name,
overlap=overlap, overlap=overlap,
@@ -1047,6 +1070,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.sel_rect = cascaded_union(self.sel_rect) self.sel_rect = cascaded_union(self.sel_rect)
self.paint_poly_area(obj=self.paint_obj, self.paint_poly_area(obj=self.paint_obj,
tooldia=tooldia_list,
sel_obj=self.sel_rect, sel_obj=self.sel_rect,
outname=o_name, outname=o_name,
overlap=overlap, overlap=overlap,
@@ -1093,32 +1117,27 @@ class ToolPaint(FlatCAMTool, Gerber):
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name) self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
return "Could not retrieve object: %s" % self.obj_name return "Could not retrieve object: %s" % self.obj_name
geo = self.bound_obj.solid_geometry self.paint_poly_ref(obj=self.paint_obj,
try: sel_obj=self.bound_obj,
if isinstance(geo, MultiPolygon): tooldia=tooldia_list,
env_obj = geo.convex_hull overlap=overlap,
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \ outname=o_name,
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon): connect=connect,
env_obj = cascaded_union(self.bound_obj.solid_geometry) contour=contour)
else:
env_obj = cascaded_union(self.bound_obj.solid_geometry)
env_obj = env_obj.convex_hull
sel_rect = env_obj.buffer(distance=0.0000001, join_style=base.JOIN_STYLE.mitre)
except Exception as e:
log.debug("ToolPaint.on_paint_button_click() --> %s" % str(e))
self.app.inform.emit(_("[ERROR_NOTCL] No object available."))
return
self.paint_poly_area(obj=self.paint_obj, def paint_poly(self, obj,
sel_obj=sel_rect, inside_pt=None,
outname=o_name, tooldia=None,
overlap=overlap, overlap=None,
connect=connect, order=None,
contour=contour) margin=None,
method=None,
def paint_poly(self, obj, inside_pt, tooldia, overlap, outname=None, connect=True, contour=True): outname=None,
connect=None,
contour=None,
tools_storage=None):
""" """
Paints a polygon selected by clicking on its interior. Paints a polygon selected by clicking on its interior or by having a point coordinates given
Note: Note:
* The margin is taken directly from the form. * The margin is taken directly from the form.
@@ -1126,27 +1145,35 @@ class ToolPaint(FlatCAMTool, Gerber):
:param inside_pt: [x, y] :param inside_pt: [x, y]
:param tooldia: Diameter of the painting tool :param tooldia: Diameter of the painting tool
:param overlap: Overlap of the tool between passes. :param overlap: Overlap of the tool between passes.
:param order: if the tools are ordered and how
:param margin: a border around painting area
:param outname: Name of the resulting Geometry Object. :param outname: Name of the resulting Geometry Object.
:param connect: Connect lines to avoid tool lifts. :param connect: Connect lines to avoid tool lifts.
:param contour: Paint around the edges. :param contour: Paint around the edges.
: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 :return: None
""" """
# Which polygon. # Which polygon.
# poly = find_polygon(self.solid_geometry, inside_pt) # poly = find_polygon(self.solid_geometry, inside_pt)
poly = self.find_polygon(point=inside_pt, geoset=obj.solid_geometry) poly = self.find_polygon(point=inside_pt, geoset=obj.solid_geometry)
paint_method = self.paintmethod_combo.get_value() paint_method = method if method is None else self.paintmethod_combo.get_value()
try: if margin is not None:
paint_margin = float(self.paintmargin_entry.get_value()) paint_margin = margin
except ValueError: else:
# try to convert comma to decimal point. if it's still not working error message and return
try: try:
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.')) paint_margin = float(self.paintmargin_entry.get_value())
except ValueError: except ValueError:
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, " # try to convert comma to decimal point. if it's still not working error message and return
"use a number.")) try:
return paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
# No polygon? # No polygon?
if poly is None: if poly is None:
@@ -1156,41 +1183,72 @@ class ToolPaint(FlatCAMTool, Gerber):
proc = self.app.proc_container.new(_("Painting polygon.")) proc = self.app.proc_container.new(_("Painting polygon."))
name = outname if outname else self.obj_name + "_paint" name = outname if outname is not None else self.obj_name + "_paint"
over = overlap if overlap is not None else float(self.app.defaults["tools_paintoverlap"])
conn = connect if connect is not None else self.app.defaults["tools_pathconnect"]
cont = contour if contour is not None else self.app.defaults["tools_paintcontour"]
order = order if order is not None else self.order_radio.get_value()
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.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
if tools_storage is not None:
tools_storage = tools_storage
else:
tools_storage = self.paint_tools
# Initializes the new geometry object # Initializes the new geometry object
def gen_paintarea(geo_obj, app_obj): def gen_paintarea(geo_obj, app_obj):
assert isinstance(geo_obj, FlatCAMGeometry), \ # assert isinstance(geo_obj, FlatCAMGeometry), \
"Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj) # "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
# assert isinstance(app_obj, App) # assert isinstance(app_obj, App)
def paint_p(polyg): tool_dia = None
if order == 'fwd':
sorted_tools.sort(reverse=False)
elif order == 'rev':
sorted_tools.sort(reverse=True)
else:
pass
def paint_p(polyg, tooldia):
if paint_method == "seed": if paint_method == "seed":
# Type(cp) == FlatCAMRTreeStorage | None # Type(cp) == FlatCAMRTreeStorage | None
cpoly = self.clear_polygon2(polyg, cpoly = self.clear_polygon2(polyg,
tooldia=tooldia, tooldia=tooldia,
steps_per_circle=self.app.defaults["geometry_circle_steps"], steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=overlap, overlap=over,
contour=contour, contour=cont,
connect=connect) connect=conn)
elif paint_method == "lines": elif paint_method == "lines":
# Type(cp) == FlatCAMRTreeStorage | None # Type(cp) == FlatCAMRTreeStorage | None
cpoly = self.clear_polygon3(polyg, cpoly = self.clear_polygon3(polyg,
tooldia=tooldia, tooldia=tooldia,
steps_per_circle=self.app.defaults["geometry_circle_steps"], steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=overlap, overlap=over,
contour=contour, contour=cont,
connect=connect) connect=conn)
else: else:
# Type(cp) == FlatCAMRTreeStorage | None # Type(cp) == FlatCAMRTreeStorage | None
cpoly = self.clear_polygon(polyg, cpoly = self.clear_polygon(polyg,
tooldia=tooldia, tooldia=tooldia,
steps_per_circle=self.app.defaults["geometry_circle_steps"], steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=overlap, overlap=over,
contour=contour, contour=cont,
connect=connect) connect=conn)
if cpoly is not None: if cpoly is not None:
geo_obj.solid_geometry += list(cpoly.get_objects()) geo_obj.solid_geometry += list(cpoly.get_objects())
@@ -1199,8 +1257,6 @@ class ToolPaint(FlatCAMTool, Gerber):
self.app.inform.emit(_('[ERROR_NOTCL] Geometry could not be painted completely')) self.app.inform.emit(_('[ERROR_NOTCL] Geometry could not be painted completely'))
return None return None
geo_obj.solid_geometry = []
try: try:
a, b, c, d = poly.bounds a, b, c, d = poly.bounds
geo_obj.options['xmin'] = a geo_obj.options['xmin'] = a
@@ -1211,39 +1267,78 @@ class ToolPaint(FlatCAMTool, Gerber):
log.debug("ToolPaint.paint_poly.gen_paintarea() bounds error --> %s" % str(e)) log.debug("ToolPaint.paint_poly.gen_paintarea() bounds error --> %s" % str(e))
return return
try: total_geometry = []
poly_buf = poly.buffer(-paint_margin) current_uid = int(1)
if isinstance(poly_buf, MultiPolygon):
cp = [] geo_obj.solid_geometry = []
for pp in poly_buf:
cp.append(paint_p(pp)) for tool_dia in sorted_tools:
else: # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry
cp = paint_p(poly_buf) for k, v in tools_storage.items():
except Exception as e: if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia):
log.debug("Could not Paint the polygons. %s" % str(e)) current_uid = int(k)
self.app.inform.emit( break
_("[ERROR] Could not do Paint. Try a different combination of parameters. "
"Or a different strategy of paint\n%s") % str(e)) try:
poly_buf = poly.buffer(-paint_margin)
if isinstance(poly_buf, MultiPolygon):
cp = []
for pp in poly_buf:
cp.append(paint_p(pp, tooldia=tool_dia))
else:
cp = paint_p(poly_buf, tooldia=tool_dia)
if cp is not None:
if isinstance(cp, list):
for x in cp:
total_geometry += list(x.get_objects())
else:
total_geometry = list(cp.get_objects())
except Exception as e:
log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit(
_("[ERROR] Could not do Paint. Try a different combination of parameters. "
"Or a different strategy of paint\n%s") % str(e))
return
# 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[:] = []
# delete tools with empty geometry
keys_to_delete = []
# look for keys in the tools_storage dict that have 'solid_geometry' values empty
for uid in tools_storage:
# if the solid_geometry (type=list) is empty
if not tools_storage[uid]['solid_geometry']:
keys_to_delete.append(uid)
# actual delete of keys from the tools_storage dict
for k in keys_to_delete:
tools_storage.pop(k, None)
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)
# 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] 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 return
if cp is not None: self.app.inform.emit(_("[success] Paint Single Done."))
if isinstance(cp, list):
for x in cp:
geo_obj.solid_geometry += list(x.get_objects())
else:
geo_obj.solid_geometry = list(cp.get_objects())
geo_obj.options["cnctooldia"] = str(tooldia)
# this turn on the FlatCAMCNCJob plot for multiple tools
geo_obj.multigeo = False
geo_obj.multitool = True
current_uid = int(self.tools_table.item(0, 3).text())
for k, v in self.paint_tools.items():
if k == current_uid:
v['data']['name'] = name
geo_obj.tools = dict(self.paint_tools)
# Experimental... # Experimental...
# print("Indexing...", end=' ') # print("Indexing...", end=' ')
@@ -1278,36 +1373,73 @@ class ToolPaint(FlatCAMTool, Gerber):
# Background # Background
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def paint_poly_all(self, obj, overlap, outname=None, connect=True, contour=True): def paint_poly_all(self, obj,
tooldia=None,
overlap=None,
order=None,
margin=None,
method=None,
outname=None,
connect=None,
contour=None,
tools_storage=None):
""" """
Paints all polygons in this object. Paints all polygons in this object.
:param obj: painted object :param obj: painted object
:param overlap: :param tooldia: a tuple or single element made out of diameters of the tools to be used
:param outname: :param overlap: value by which the paths will overlap
:param order: if the tools are ordered and how
:param margin: a border around painting area
:param outname: name of the resulting object
:param connect: Connect lines to avoid tool lifts. :param connect: Connect lines to avoid tool lifts.
:param contour: Paint around the edges. :param contour: Paint around the edges.
: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: :return:
""" """
paint_method = self.paintmethod_combo.get_value() paint_method = method if method is None else self.paintmethod_combo.get_value()
try: if margin is not None:
paint_margin = float(self.paintmargin_entry.get_value()) paint_margin = margin
except ValueError: else:
# try to convert comma to decimal point. if it's still not working error message and return
try: try:
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.')) paint_margin = float(self.paintmargin_entry.get_value())
except ValueError: except ValueError:
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, " # try to convert comma to decimal point. if it's still not working error message and return
"use a number.")) try:
return paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
proc = self.app.proc_container.new(_("Painting polygon...")) proc = self.app.proc_container.new(_("Painting polygon..."))
name = outname if outname else self.obj_name + "_paint" name = outname if outname is not None else self.obj_name + "_paint"
over = overlap
conn = connect
cont = contour
over = overlap if overlap is not None else float(self.app.defaults["tools_paintoverlap"])
conn = connect if connect is not None else self.app.defaults["tools_pathconnect"]
cont = contour if contour is not None else self.app.defaults["tools_paintcontour"]
order = order if order is not None else self.order_radio.get_value()
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.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
if tools_storage is not None:
tools_storage = tools_storage
else:
tools_storage = self.paint_tools
# This is a recursive generator of individual Polygons. # This is a recursive generator of individual Polygons.
# Note: Double check correct implementation. Might exit # Note: Double check correct implementation. Might exit
# early if it finds something that is not a Polygon? # early if it finds something that is not a Polygon?
@@ -1355,15 +1487,10 @@ class ToolPaint(FlatCAMTool, Gerber):
# Initializes the new geometry object # Initializes the new geometry object
def gen_paintarea(geo_obj, app_obj): def gen_paintarea(geo_obj, app_obj):
assert isinstance(geo_obj, FlatCAMGeometry), \ # assert isinstance(geo_obj, FlatCAMGeometry), \
"Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj) # "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
tool_dia = None tool_dia = None
sorted_tools = []
for row in range(self.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
order = self.order_radio.get_value()
if order == 'fwd': if order == 'fwd':
sorted_tools.sort(reverse=False) sorted_tools.sort(reverse=False)
elif order == 'rev': elif order == 'rev':
@@ -1383,10 +1510,12 @@ class ToolPaint(FlatCAMTool, Gerber):
total_geometry = [] total_geometry = []
current_uid = int(1) current_uid = int(1)
geo_obj.solid_geometry = [] geo_obj.solid_geometry = []
for tool_dia in sorted_tools: for tool_dia in sorted_tools:
# find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry
for k, v in self.paint_tools.items(): for k, v in tools_storage.items():
if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia): if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia):
current_uid = int(k) current_uid = int(k)
break break
@@ -1434,19 +1563,31 @@ class ToolPaint(FlatCAMTool, Gerber):
"Or a different Method of paint\n%s") % str(e)) "Or a different Method of paint\n%s") % str(e))
return return
# add the solid_geometry to the current too in self.paint_tools dictionary and then reset the # add the solid_geometry to the current too in self.paint_tools (tools_storage)
# temporary list that stored that solid_geometry # dictionary and then reset the temporary list that stored that solid_geometry
self.paint_tools[current_uid]['solid_geometry'] = deepcopy(total_geometry) tools_storage[current_uid]['solid_geometry'] = deepcopy(total_geometry)
self.paint_tools[current_uid]['data']['name'] = name tools_storage[current_uid]['data']['name'] = name
total_geometry[:] = [] total_geometry[:] = []
# delete tools with empty geometry
keys_to_delete = []
# look for keys in the tools_storage dict that have 'solid_geometry' values empty
for uid in tools_storage:
# if the solid_geometry (type=list) is empty
if not tools_storage[uid]['solid_geometry']:
keys_to_delete.append(uid)
# actual delete of keys from the tools_storage dict
for k in keys_to_delete:
tools_storage.pop(k, None)
geo_obj.options["cnctooldia"] = str(tool_dia) geo_obj.options["cnctooldia"] = str(tool_dia)
# this turn on the FlatCAMCNCJob plot for multiple tools # this turn on the FlatCAMCNCJob plot for multiple tools
geo_obj.multigeo = True geo_obj.multigeo = True
geo_obj.multitool = True geo_obj.multitool = True
geo_obj.tools.clear() geo_obj.tools.clear()
geo_obj.tools = dict(self.paint_tools) 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 # test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
has_solid_geo = 0 has_solid_geo = 0
@@ -1467,13 +1608,10 @@ class ToolPaint(FlatCAMTool, Gerber):
# Initializes the new geometry object # Initializes the new geometry object
def gen_paintarea_rest_machining(geo_obj, app_obj): def gen_paintarea_rest_machining(geo_obj, app_obj):
assert isinstance(geo_obj, FlatCAMGeometry), \ # assert isinstance(geo_obj, FlatCAMGeometry), \
"Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj) # "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
tool_dia = None tool_dia = None
sorted_tools = []
for row in range(self.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
sorted_tools.sort(reverse=True) sorted_tools.sort(reverse=True)
cleared_geo = [] cleared_geo = []
@@ -1526,16 +1664,16 @@ class ToolPaint(FlatCAMTool, Gerber):
return return
# find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry
for k, v in self.paint_tools.items(): for k, v in tools_storage.items():
if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia): if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia):
current_uid = int(k) current_uid = int(k)
break break
# add the solid_geometry to the current too in self.paint_tools dictionary and then reset the # add the solid_geometry to the current too in self.paint_tools (or tools_storage) dictionary and
# temporary list that stored that solid_geometry # then reset the temporary list that stored that solid_geometry
self.paint_tools[current_uid]['solid_geometry'] = deepcopy(cleared_geo) tools_storage[current_uid]['solid_geometry'] = deepcopy(cleared_geo)
self.paint_tools[current_uid]['data']['name'] = name tools_storage[current_uid]['data']['name'] = name
cleared_geo[:] = [] cleared_geo[:] = []
geo_obj.options["cnctooldia"] = str(tool_dia) geo_obj.options["cnctooldia"] = str(tool_dia)
@@ -1543,7 +1681,7 @@ class ToolPaint(FlatCAMTool, Gerber):
geo_obj.multigeo = True geo_obj.multigeo = True
geo_obj.multitool = True geo_obj.multitool = True
geo_obj.tools.clear() geo_obj.tools.clear()
geo_obj.tools = dict(self.paint_tools) 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 # test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
has_solid_geo = 0 has_solid_geo = 0
@@ -1584,36 +1722,74 @@ class ToolPaint(FlatCAMTool, Gerber):
# Background # Background
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def paint_poly_area(self, obj, sel_obj, overlap, outname=None, connect=True, contour=True): def paint_poly_area(self, obj, sel_obj,
tooldia=None,
overlap=None,
order=None,
margin=None,
method=None,
outname=None,
connect=None,
contour=None,
tools_storage=None):
""" """
Paints all polygons in this object that are within the sel_obj object Paints all polygons in this object that are within the sel_obj object
:param obj: painted object :param obj: painted object
:param sel_obj: paint only what is inside this object bounds :param sel_obj: paint only what is inside this object bounds
:param overlap: :param tooldia: a tuple or single element made out of diameters of the tools to be used
:param outname: :param overlap: value by which the paths will overlap
:param order: if the tools are ordered and how
:param margin: a border around painting area
:param outname: name of the resulting object
:param connect: Connect lines to avoid tool lifts. :param connect: Connect lines to avoid tool lifts.
:param contour: Paint around the edges. :param contour: Paint around the edges.
: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: :return:
""" """
paint_method = self.paintmethod_combo.get_value() paint_method = method if method is None else self.paintmethod_combo.get_value()
try: if margin is not None:
paint_margin = float(self.paintmargin_entry.get_value()) paint_margin = margin
except ValueError: else:
# try to convert comma to decimal point. if it's still not working error message and return
try: try:
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.')) paint_margin = float(self.paintmargin_entry.get_value())
except ValueError: except ValueError:
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, " # try to convert comma to decimal point. if it's still not working error message and return
"use a number.")) try:
return paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
proc = self.app.proc_container.new(_("Painting polygon...")) proc = self.app.proc_container.new(_("Painting polygon..."))
name = outname if outname else self.obj_name + "_paint" name = outname if outname is not None else self.obj_name + "_paint"
over = overlap
conn = connect over = overlap if overlap is not None else float(self.app.defaults["tools_paintoverlap"])
cont = contour conn = connect if connect is not None else self.app.defaults["tools_pathconnect"]
cont = contour if contour is not None else self.app.defaults["tools_paintcontour"]
order = order if order is not None else self.order_radio.get_value()
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.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
if tools_storage is not None:
tools_storage = tools_storage
else:
tools_storage = self.paint_tools
def recurse(geometry, reset=True): def recurse(geometry, reset=True):
""" """
@@ -1648,15 +1824,9 @@ class ToolPaint(FlatCAMTool, Gerber):
# Initializes the new geometry object # Initializes the new geometry object
def gen_paintarea(geo_obj, app_obj): def gen_paintarea(geo_obj, app_obj):
assert isinstance(geo_obj, FlatCAMGeometry), \ # assert isinstance(geo_obj, FlatCAMGeometry), \
"Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj) # "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
tool_dia = None tool_dia = None
sorted_tools = []
for row in range(self.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
order = self.order_radio.get_value()
if order == 'fwd': if order == 'fwd':
sorted_tools.sort(reverse=False) sorted_tools.sort(reverse=False)
elif order == 'rev': elif order == 'rev':
@@ -1664,6 +1834,7 @@ class ToolPaint(FlatCAMTool, Gerber):
else: else:
pass pass
# this is were heavy lifting is done and creating the geometry to be painted
geo_to_paint = [] geo_to_paint = []
if not isinstance(obj.solid_geometry, list): if not isinstance(obj.solid_geometry, list):
target_geo = [obj.solid_geometry] target_geo = [obj.solid_geometry]
@@ -1686,10 +1857,12 @@ class ToolPaint(FlatCAMTool, Gerber):
total_geometry = [] total_geometry = []
current_uid = int(1) current_uid = int(1)
geo_obj.solid_geometry = [] geo_obj.solid_geometry = []
for tool_dia in sorted_tools: for tool_dia in sorted_tools:
# find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry
for k, v in self.paint_tools.items(): for k, v in tools_storage.items():
if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia): if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia):
current_uid = int(k) current_uid = int(k)
break break
@@ -1737,19 +1910,31 @@ class ToolPaint(FlatCAMTool, Gerber):
"Or a different Method of paint\n%s") % str(e)) "Or a different Method of paint\n%s") % str(e))
return return
# add the solid_geometry to the current too in self.paint_tools dictionary and then reset the # add the solid_geometry to the current too in self.paint_tools (tools_storage)
# temporary list that stored that solid_geometry # dictionary and then reset the temporary list that stored that solid_geometry
self.paint_tools[current_uid]['solid_geometry'] = deepcopy(total_geometry) tools_storage[current_uid]['solid_geometry'] = deepcopy(total_geometry)
self.paint_tools[current_uid]['data']['name'] = name tools_storage[current_uid]['data']['name'] = name
total_geometry[:] = [] total_geometry[:] = []
# delete tools with empty geometry
keys_to_delete = []
# look for keys in the tools_storage dict that have 'solid_geometry' values empty
for uid in tools_storage:
# if the solid_geometry (type=list) is empty
if not tools_storage[uid]['solid_geometry']:
keys_to_delete.append(uid)
# actual delete of keys from the tools_storage dict
for k in keys_to_delete:
tools_storage.pop(k, None)
geo_obj.options["cnctooldia"] = str(tool_dia) geo_obj.options["cnctooldia"] = str(tool_dia)
# this turn on the FlatCAMCNCJob plot for multiple tools # this turn on the FlatCAMCNCJob plot for multiple tools
geo_obj.multigeo = True geo_obj.multigeo = True
geo_obj.multitool = True geo_obj.multitool = True
geo_obj.tools.clear() geo_obj.tools.clear()
geo_obj.tools = dict(self.paint_tools) 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 # test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
has_solid_geo = 0 has_solid_geo = 0
@@ -1766,7 +1951,7 @@ class ToolPaint(FlatCAMTool, Gerber):
# print("Indexing...", end=' ') # print("Indexing...", end=' ')
# geo_obj.make_index() # geo_obj.make_index()
self.app.inform.emit(_("[success] Paint All Done.")) self.app.inform.emit(_("[success] Paint Area Done."))
# Initializes the new geometry object # Initializes the new geometry object
def gen_paintarea_rest_machining(geo_obj, app_obj): def gen_paintarea_rest_machining(geo_obj, app_obj):
@@ -1774,9 +1959,6 @@ class ToolPaint(FlatCAMTool, Gerber):
"Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj) "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
tool_dia = None tool_dia = None
sorted_tools = []
for row in range(self.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
sorted_tools.sort(reverse=True) sorted_tools.sort(reverse=True)
cleared_geo = [] cleared_geo = []
@@ -1829,16 +2011,16 @@ class ToolPaint(FlatCAMTool, Gerber):
return return
# find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry
for k, v in self.paint_tools.items(): for k, v in tools_storage.items():
if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia): if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia):
current_uid = int(k) current_uid = int(k)
break break
# add the solid_geometry to the current too in self.paint_tools dictionary and then reset the # add the solid_geometry to the current too in self.paint_tools (or tools_storage) dictionary and
# temporary list that stored that solid_geometry # then reset the temporary list that stored that solid_geometry
self.paint_tools[current_uid]['solid_geometry'] = deepcopy(cleared_geo) tools_storage[current_uid]['solid_geometry'] = deepcopy(cleared_geo)
self.paint_tools[current_uid]['data']['name'] = name tools_storage[current_uid]['data']['name'] = name
cleared_geo[:] = [] cleared_geo[:] = []
geo_obj.options["cnctooldia"] = str(tool_dia) geo_obj.options["cnctooldia"] = str(tool_dia)
@@ -1887,6 +2069,61 @@ class ToolPaint(FlatCAMTool, Gerber):
# Background # Background
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def paint_poly_ref(self, obj, sel_obj,
tooldia=None,
overlap=None,
order=None,
margin=None,
method=None,
outname=None,
connect=None,
contour=None,
tools_storage=None):
"""
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 overlap: value by which the paths will overlap
:param order: if the tools are ordered and how
:param margin: a border around painting area
:param outname: name of the resulting object
:param connect: Connect lines to avoid tool lifts.
:param contour: Paint around the edges.
: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:
"""
geo = sel_obj.solid_geometry
try:
if isinstance(geo, MultiPolygon):
env_obj = geo.convex_hull
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
env_obj = cascaded_union(self.bound_obj.solid_geometry)
else:
env_obj = cascaded_union(self.bound_obj.solid_geometry)
env_obj = env_obj.convex_hull
sel_rect = env_obj.buffer(distance=0.0000001, join_style=base.JOIN_STYLE.mitre)
except Exception as e:
log.debug("ToolPaint.on_paint_button_click() --> %s" % str(e))
self.app.inform.emit(_("[ERROR_NOTCL] No object available."))
return
self.paint_poly_area(obj=obj,
sel_obj=sel_rect,
tooldia=tooldia,
overlap=overlap,
order=order,
margin=margin,
method=method,
outname=outname,
connect=connect,
contour=contour,
tools_storage=tools_storage)
@staticmethod @staticmethod
def paint_bounds(geometry): def paint_bounds(geometry):
def bounds_rec(o): def bounds_rec(o):

View File

@@ -175,10 +175,10 @@ class Properties(FlatCAMTool):
env_obj = geo.convex_hull env_obj = geo.convex_hull
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \ elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon): (isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
env_obj = cascaded_union(self.bound_obj.solid_geometry) env_obj = cascaded_union(obj.solid_geometry)
env_obj = env_obj.convex_hull env_obj = env_obj.convex_hull
else: else:
env_obj = cascaded_union(self.bound_obj.solid_geometry) env_obj = cascaded_union(obj.solid_geometry)
env_obj = env_obj.convex_hull env_obj = env_obj.convex_hull
area_chull = env_obj.area area_chull = env_obj.area

View File

@@ -202,7 +202,6 @@ class TclCommand(object):
""" """
arguments, options = self.parse_arguments(args) arguments, options = self.parse_arguments(args)
named_args = {} named_args = {}
unnamed_args = [] unnamed_args = []
@@ -274,7 +273,7 @@ class TclCommand(object):
:return: None, output text or exception :return: None, output text or exception
""" """
#self.worker_task.emit({'fcn': self.exec_command_test, 'params': [text, False]}) # self.worker_task.emit({'fcn': self.exec_command_test, 'params': [text, False]})
try: try:
self.log.debug("TCL command '%s' executed." % str(self.__class__)) self.log.debug("TCL command '%s' executed." % str(self.__class__))
@@ -283,7 +282,7 @@ class TclCommand(object):
return self.execute(args, unnamed_args) return self.execute(args, unnamed_args)
except Exception as unknown: except Exception as unknown:
error_info = sys.exc_info() error_info = sys.exc_info()
self.log.error("TCL command '%s' failed." % str(self)) self.log.error("TCL command '%s' failed. Error text: %s" % (str(self), str(unknown)))
self.app.display_tcl_error(unknown, error_info) self.app.display_tcl_error(unknown, error_info)
self.raise_tcl_unknown_error(unknown) self.raise_tcl_unknown_error(unknown)

View File

@@ -1,8 +1,16 @@
from ObjectCollection import * from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled from tclCommands.TclCommand import TclCommand
import gettext
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class TclCommandPaint(TclCommandSignaled): class TclCommandPaint(TclCommand):
""" """
Paint the interior of polygons Paint the interior of polygons
""" """
@@ -13,32 +21,53 @@ class TclCommandPaint(TclCommandSignaled):
# dictionary of types from Tcl command, needs to be ordered # dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([ arg_names = collections.OrderedDict([
('name', str), ('name', str),
('tooldia', float),
('overlap', float)
]) ])
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value # dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([ option_types = collections.OrderedDict([
('outname', str), ('tooldia', str),
('overlap', float),
('order', str),
('margin', float),
('method', str),
('connect', bool),
('contour', bool),
('all', bool), ('all', bool),
('single', bool),
('ref', bool),
('box', str),
('x', float), ('x', float),
('y', float) ('y', float),
('outname', str),
]) ])
# array of mandatory options for current Tcl command: required = {'name','outname'} # array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name', 'tooldia', 'overlap'] required = ['name']
# structured help for current command, args needs to be ordered # structured help for current command, args needs to be ordered
help = { help = {
'main': "Paint polygons", 'main': "Paint polygons",
'args': collections.OrderedDict([ 'args': collections.OrderedDict([
('name', 'Name of the source Geometry object.'), ('name', 'Name of the source Geometry object. String.'),
('tooldia', 'Diameter of the tool to be used.'), ('tooldia', 'Diameter of the tool to be used. Can be a comma separated list of diameters. No space is '
('overlap', 'Fraction of the tool diameter to overlap cuts.'), 'allowed between tool diameters. E.g: correct: 0.5,1 / incorrect: 0.5, 1'),
('outname', 'Name of the resulting Geometry object.'), ('overlap', 'Fraction of the tool diameter to overlap cuts. Float number.'),
('all', 'Paint all polygons in the object.'), ('order', 'Can have the values: "no", "fwd" and "rev". String.'
('x', 'X value of coordinate for the selection of a single polygon.'), 'It is useful when there are multiple tools in tooldia parameter.'
('y', 'Y value of coordinate for the selection of a single polygon.') '"no" -> the order used is the one provided.'
'"fwd" -> tools are ordered from smallest to biggest.'
'"rev" -> tools are ordered from biggest to smallest.'),
('method', 'Algorithm for painting. Can be: "standard", "seed" or "lines".'),
('connect', 'Draw lines to minimize tool lifts. True or False'),
('contour', 'Cut around the perimeter of the painting. True or False'),
('all', 'Paint all polygons in the object. True or False'),
('single', 'Paint a single polygon specified by "x" and "y" parameters. True or False'),
('ref', 'Paint all polygons within a specified object with the name in "box" parameter. True or False'),
('box', 'name of the object to be used as paint reference when selecting "ref"" True. String.'),
('x', 'X value of coordinate for the selection of a single polygon. Float number.'),
('y', 'Y value of coordinate for the selection of a single polygon. Float number.'),
('outname', 'Name of the resulting Geometry object. String.'),
]), ]),
'examples': [] 'examples': []
} }
@@ -54,31 +83,177 @@ class TclCommandPaint(TclCommandSignaled):
""" """
name = args['name'] name = args['name']
tooldia = args['tooldia']
overlap = args['overlap'] if 'tooldia' in args:
tooldia = str(args['tooldia'])
else:
tooldia = float(self.app.defaults["tools_paintoverlap"])
if 'overlap' in args:
overlap = float(args['overlap'])
else:
overlap = float(self.app.defaults["tools_paintoverlap"])
if 'order' in args:
order = args['order']
else:
order = str(self.app.defaults["tools_paintorder"])
if 'margin' in args:
margin = float(args['margin'])
else:
margin = float(self.app.defaults["tools_paintmargin"])
if 'method' in args:
method = args['method']
else:
method = str(self.app.defaults["tools_paintmethod"])
if 'connect' in args:
connect = eval(str(args['connect']).capitalize())
else:
connect = eval(str(self.app.defaults["tools_pathconnect"]))
if 'contour' in args:
contour = eval(str(args['contour']).capitalize())
else:
contour = eval(str(self.app.defaults["tools_paintcontour"]))
if 'outname' in args: if 'outname' in args:
outname = args['outname'] outname = args['outname']
else: else:
outname = name + "_paint" outname = name + "_paint"
obj = self.app.collection.get_by_name(name) # Get source object.
try:
obj = self.app.collection.get_by_name(str(name))
except Exception as e:
log.debug("TclCommandPaint.execute() --> %s" % str(e))
self.raise_tcl_error("%s: %s" % (_("Could not retrieve object"), name))
return "Could not retrieve object: %s" % name
try:
tools = [float(eval(dia)) for dia in tooldia.split(",") if dia != '']
except AttributeError:
tools = [float(tooldia)]
# store here the default data for Geometry Data
default_data = {}
default_data.update({
"name": '_paint',
"plot": self.app.defaults["geometry_plot"],
"cutz": self.app.defaults["geometry_cutz"],
"vtipdia": 0.1,
"vtipangle": 30,
"travelz": self.app.defaults["geometry_travelz"],
"feedrate": self.app.defaults["geometry_feedrate"],
"feedrate_z": self.app.defaults["geometry_feedrate_z"],
"feedrate_rapid": self.app.defaults["geometry_feedrate_rapid"],
"dwell": self.app.defaults["geometry_dwell"],
"dwelltime": self.app.defaults["geometry_dwelltime"],
"multidepth": self.app.defaults["geometry_multidepth"],
"ppname_g": self.app.defaults["geometry_ppname_g"],
"depthperpass": self.app.defaults["geometry_depthperpass"],
"extracut": self.app.defaults["geometry_extracut"],
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangez": self.app.defaults["geometry_toolchangez"],
"endz": self.app.defaults["geometry_endz"],
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"startz": self.app.defaults["geometry_startz"],
"tooldia": self.app.defaults["tools_painttooldia"],
"paintmargin": self.app.defaults["tools_paintmargin"],
"paintmethod": self.app.defaults["tools_paintmethod"],
"selectmethod": self.app.defaults["tools_selectmethod"],
"pathconnect": self.app.defaults["tools_pathconnect"],
"paintcontour": self.app.defaults["tools_paintcontour"],
"paintoverlap": self.app.defaults["tools_paintoverlap"]
})
paint_tools = dict()
tooluid = 0
for tool in tools:
tooluid += 1
paint_tools.update({
int(tooluid): {
'tooldia': float('%.4f' % tool),
'offset': 'Path',
'offset_value': 0.0,
'type': 'Iso',
'tool_type': 'C1',
'data': dict(default_data),
'solid_geometry': []
}
})
if obj is None: if obj is None:
self.raise_tcl_error("Object not found: %s" % name) return "Object not found: %s" % name
if not isinstance(obj, Geometry): # Paint all polygons in the painted object
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj))) if 'all' in args and args['all'] is True:
self.app.paint_tool.paint_poly_all(obj=obj,
if 'all' in args and args['all']: tooldia=tooldia,
obj.paint_poly_all(tooldia, overlap, outname) overlap=overlap,
order=order,
margin=margin,
method=method,
outname=outname,
connect=connect,
contour=contour,
tools_storage=paint_tools)
return return
if 'x' not in args or 'y' not in args: # Paint single polygon in the painted object
self.raise_tcl_error('Expected -all 1 or -x <value> and -y <value>.') elif 'single' in args and args['single'] is True:
if 'x' not in args or 'y' not in args:
self.raise_tcl_error('%s' % _("Expected -x <value> and -y <value>."))
else:
x = args['x']
y = args['y']
x = args['x'] self.app.paint_tool.paint_poly(obj=obj,
y = args['y'] inside_pt=[x, y],
tooldia=tooldia,
overlap=overlap,
order=order,
margin=margin,
method=method,
outname=outname,
connect=connect,
contour=contour,
tools_storage=paint_tools)
return
obj.paint_poly_single_click([x, y], tooldia, overlap, outname) # Paint all polygons found within the box object from the the painted object
elif 'ref' in args and args['ref'] is True:
if 'box' not in args:
self.raise_tcl_error('%s' % _("Expected -box <value>."))
else:
box_name = args['box']
# Get box source object.
try:
box_obj = self.app.collection.get_by_name(str(box_name))
except Exception as e:
log.debug("TclCommandPaint.execute() --> %s" % str(e))
self.raise_tcl_error("%s: %s" % (_("Could not retrieve box object"), name))
return "Could not retrieve object: %s" % name
self.app.paint_tool.paint_poly_ref(obj=obj,
sel_obj=box_obj,
tooldia=tooldia,
overlap=overlap,
order=order,
margin=margin,
method=method,
outname=outname,
connect=connect,
contour=contour,
tools_storage=paint_tools)
return
else:
self.raise_tcl_error("%s:" % _("There was none of the following args: 'ref', 'single', 'all'.\n"
"Paint failed."))
return "There was none of the following args: 'ref', 'single', 'all'.\n" \
"Paint failed."