|
|
@@ -18,7 +18,7 @@ import numpy as np
|
|
|
import math
|
|
|
|
|
|
from shapely.ops import cascaded_union
|
|
|
-from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing
|
|
|
+from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing, Point
|
|
|
|
|
|
from matplotlib.backend_bases import KeyEvent as mpl_key_event
|
|
|
|
|
|
@@ -536,6 +536,20 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
self.reference_combo_type.hide()
|
|
|
self.reference_combo_type_label.hide()
|
|
|
|
|
|
+ # Polygon interiors selection
|
|
|
+ self.poly_int_label = QtWidgets.QLabel('%s:' % _("Interiors"))
|
|
|
+ self.poly_int_label.setToolTip(
|
|
|
+ _("When checked the user can select interiors of a polygon.\n"
|
|
|
+ "(holes in the polygon).")
|
|
|
+ )
|
|
|
+ self.poly_int_cb = FCCheckBox()
|
|
|
+
|
|
|
+ self.grid3.addWidget(self.poly_int_label, 33, 0)
|
|
|
+ self.grid3.addWidget(self.poly_int_cb, 33, 1)
|
|
|
+
|
|
|
+ self.poly_int_label.hide()
|
|
|
+ self.poly_int_cb.hide()
|
|
|
+
|
|
|
# Area Selection shape
|
|
|
self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
|
|
|
self.area_shape_label.setToolTip(
|
|
|
@@ -545,8 +559,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
|
|
|
{'label': _("Polygon"), 'value': 'polygon'}])
|
|
|
|
|
|
- self.grid3.addWidget(self.area_shape_label, 33, 0)
|
|
|
- self.grid3.addWidget(self.area_shape_radio, 33, 1)
|
|
|
+ self.grid3.addWidget(self.area_shape_label, 35, 0)
|
|
|
+ self.grid3.addWidget(self.area_shape_radio, 35, 1)
|
|
|
|
|
|
self.area_shape_label.hide()
|
|
|
self.area_shape_radio.hide()
|
|
|
@@ -554,7 +568,7 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
separator_line = QtWidgets.QFrame()
|
|
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
|
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
|
|
- self.grid3.addWidget(separator_line, 34, 0, 1, 2)
|
|
|
+ self.grid3.addWidget(separator_line, 36, 0, 1, 2)
|
|
|
|
|
|
self.generate_iso_button = QtWidgets.QPushButton("%s" % _("Generate Isolation Geometry"))
|
|
|
self.generate_iso_button.setStyleSheet("""
|
|
|
@@ -865,6 +879,7 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
self.milling_type_radio.set_value(self.app.defaults["tools_iso_milling_type"])
|
|
|
self.combine_passes_cb.set_value(self.app.defaults["tools_iso_combine_passes"])
|
|
|
self.area_shape_radio.set_value(self.app.defaults["tools_iso_area_shape"])
|
|
|
+ self.poly_int_cb.set_value(self.app.defaults["tools_iso_poly_ints"])
|
|
|
|
|
|
self.cutz_entry.set_value(self.app.defaults["tools_iso_tool_cutz"])
|
|
|
self.tool_type_radio.set_value(self.app.defaults["tools_iso_tool_type"])
|
|
|
@@ -1291,6 +1306,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
self.reference_combo_type_label.hide()
|
|
|
self.area_shape_label.hide()
|
|
|
self.area_shape_radio.hide()
|
|
|
+ self.poly_int_label.hide()
|
|
|
+ self.poly_int_cb.hide()
|
|
|
|
|
|
# disable rest-machining for area painting
|
|
|
self.rest_cb.setDisabled(False)
|
|
|
@@ -1301,6 +1318,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
self.reference_combo_type_label.hide()
|
|
|
self.area_shape_label.show()
|
|
|
self.area_shape_radio.show()
|
|
|
+ self.poly_int_label.hide()
|
|
|
+ self.poly_int_cb.hide()
|
|
|
|
|
|
# disable rest-machining for area isolation
|
|
|
self.rest_cb.set_value(False)
|
|
|
@@ -1312,6 +1331,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
self.reference_combo_type_label.hide()
|
|
|
self.area_shape_label.hide()
|
|
|
self.area_shape_radio.hide()
|
|
|
+ self.poly_int_label.show()
|
|
|
+ self.poly_int_cb.show()
|
|
|
else:
|
|
|
self.reference_combo.show()
|
|
|
self.reference_combo_label.show()
|
|
|
@@ -1319,6 +1340,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
self.reference_combo_type_label.show()
|
|
|
self.area_shape_label.hide()
|
|
|
self.area_shape_radio.hide()
|
|
|
+ self.poly_int_label.hide()
|
|
|
+ self.poly_int_cb.hide()
|
|
|
|
|
|
# disable rest-machining for area painting
|
|
|
self.rest_cb.setDisabled(False)
|
|
|
@@ -1714,7 +1737,7 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
use_geo = cascaded_union(isolated_obj.solid_geometry).difference(ref_geo)
|
|
|
self.isolate(isolated_obj=isolated_obj, geometry=use_geo)
|
|
|
|
|
|
- def isolate(self, isolated_obj, geometry=None, limited_area=None, plot=True):
|
|
|
+ def isolate(self, isolated_obj, geometry=None, limited_area=None, negative_dia=None, plot=True):
|
|
|
"""
|
|
|
Creates an isolation routing geometry object in the project.
|
|
|
|
|
|
@@ -1724,6 +1747,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
:type geometry: List of Shapely polygon
|
|
|
:param limited_area: if not None isolate only this area
|
|
|
:type limited_area: Shapely Polygon or a list of them
|
|
|
+ :param negative_dia: isolate the geometry with a negative value for the tool diameter
|
|
|
+ :type negative_dia: bool
|
|
|
:param plot: if to plot the resulting geometry object
|
|
|
:type plot: bool
|
|
|
:return: None
|
|
|
@@ -1745,10 +1770,10 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
if combine:
|
|
|
if self.rest_cb.get_value():
|
|
|
self.combined_rest(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage,
|
|
|
- lim_area=limited_area, plot=plot)
|
|
|
+ lim_area=limited_area, negative_dia=negative_dia, plot=plot)
|
|
|
else:
|
|
|
self.combined_normal(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage,
|
|
|
- lim_area=limited_area, plot=plot)
|
|
|
+ lim_area=limited_area, negative_dia=negative_dia, plot=plot)
|
|
|
|
|
|
else:
|
|
|
prog_plot = self.app.defaults["tools_iso_plotting"]
|
|
|
@@ -1780,6 +1805,9 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
tool_type = tools_storage[tool]['tool_type']
|
|
|
|
|
|
iso_offset = tool_dia * ((2 * i + 1) / 2.0000001) - (i * overlap * tool_dia)
|
|
|
+ if negative_dia:
|
|
|
+ iso_offset = -iso_offset
|
|
|
+
|
|
|
outname = "%s_%.*f" % (isolated_obj.options["name"], self.decimals, float(tool_dia))
|
|
|
|
|
|
if passes > 1:
|
|
|
@@ -1879,7 +1907,7 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
# Switch notebook to Selected page
|
|
|
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
|
|
|
|
|
|
- def combined_rest(self, iso_obj, iso2geo, tools_storage, lim_area, plot=True):
|
|
|
+ def combined_rest(self, iso_obj, iso2geo, tools_storage, lim_area, negative_dia=None, plot=True):
|
|
|
"""
|
|
|
Isolate the provided Gerber object using "rest machining" strategy
|
|
|
|
|
|
@@ -1891,6 +1919,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
:type tools_storage: dict
|
|
|
:param lim_area: if not None restrict isolation to this area
|
|
|
:type lim_area: Shapely Polygon or a list of them
|
|
|
+ :param negative_dia: isolate the geometry with a negative value for the tool diameter
|
|
|
+ :type negative_dia: bool
|
|
|
:param plot: if to plot the resulting geometry object
|
|
|
:type plot: bool
|
|
|
:return: Isolated solid geometry
|
|
|
@@ -1957,7 +1987,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
|
|
|
solid_geo, work_geo = self.generate_rest_geometry(geometry=work_geo, tooldia=tool_dia,
|
|
|
passes=passes, overlap=overlap, invert=mill_dir,
|
|
|
- env_iso_type=iso_t, prog_plot=prog_plot,
|
|
|
+ env_iso_type=iso_t, negative_dia=negative_dia,
|
|
|
+ prog_plot=prog_plot,
|
|
|
prog_plot_handler=self.plot_temp_shapes)
|
|
|
|
|
|
# ############################################################
|
|
|
@@ -2050,7 +2081,7 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
msg += coords
|
|
|
self.app.shell_message(msg=msg)
|
|
|
|
|
|
- def combined_normal(self, iso_obj, iso2geo, tools_storage, lim_area, plot=True):
|
|
|
+ def combined_normal(self, iso_obj, iso2geo, tools_storage, lim_area, negative_dia=None, plot=True):
|
|
|
"""
|
|
|
|
|
|
:param iso_obj: the isolated Gerber object
|
|
|
@@ -2061,6 +2092,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
:type tools_storage: dict
|
|
|
:param lim_area: if not None restrict isolation to this area
|
|
|
:type lim_area: Shapely Polygon or a list of them
|
|
|
+ :param negative_dia: isolate the geometry with a negative value for the tool diameter
|
|
|
+ :type negative_dia: bool
|
|
|
:param plot: if to plot the resulting geometry object
|
|
|
:type plot: bool
|
|
|
:return: Isolated solid geometry
|
|
|
@@ -2116,6 +2149,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
solid_geo = []
|
|
|
for nr_pass in range(passes):
|
|
|
iso_offset = tool_dia * ((2 * nr_pass + 1) / 2.0000001) - (nr_pass * overlap * tool_dia)
|
|
|
+ if negative_dia:
|
|
|
+ iso_offset = -iso_offset
|
|
|
|
|
|
# if milling type is climb then the move is counter-clockwise around features
|
|
|
mill_dir = 1 if milling_type == 'cl' else 0
|
|
|
@@ -2340,7 +2375,14 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
curr_pos = (curr_pos[0], curr_pos[1])
|
|
|
|
|
|
if event.button == 1:
|
|
|
- clicked_poly = self.find_polygon(point=(curr_pos[0], curr_pos[1]), geoset=self.grb_obj.solid_geometry)
|
|
|
+ if self.poly_int_cb.get_value() is True:
|
|
|
+ clicked_poly = self.find_polygon_ignore_interiors(point=(curr_pos[0], curr_pos[1]),
|
|
|
+ geoset=self.grb_obj.solid_geometry)
|
|
|
+
|
|
|
+ clicked_poly = self.get_selected_interior(clicked_poly, point=(curr_pos[0], curr_pos[1]))
|
|
|
+
|
|
|
+ else:
|
|
|
+ clicked_poly = self.find_polygon(point=(curr_pos[0], curr_pos[1]), geoset=self.grb_obj.solid_geometry)
|
|
|
|
|
|
if self.app.selection_type is not None:
|
|
|
self.selection_area_handler(self.app.pos, curr_pos, self.app.selection_type)
|
|
|
@@ -2380,7 +2422,7 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
|
|
|
if self.app.is_legacy is False:
|
|
|
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_poly_mouse_click_release)
|
|
|
- self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_pres)
|
|
|
+ self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
|
|
|
else:
|
|
|
self.app.plotcanvas.graph_event_disconnect(self.mr)
|
|
|
self.app.plotcanvas.graph_event_disconnect(self.kp)
|
|
|
@@ -2395,7 +2437,11 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
|
|
|
if self.poly_dict:
|
|
|
poly_list = deepcopy(list(self.poly_dict.values()))
|
|
|
- self.isolate(isolated_obj=self.grb_obj, geometry=poly_list)
|
|
|
+ if self.poly_int_cb.get_value() is True:
|
|
|
+ # isolate the interior polygons with a negative tool
|
|
|
+ self.isolate(isolated_obj=self.grb_obj, geometry=poly_list, negative_dia=True)
|
|
|
+ else:
|
|
|
+ self.isolate(isolated_obj=self.grb_obj, geometry=poly_list)
|
|
|
self.poly_dict.clear()
|
|
|
else:
|
|
|
self.app.inform.emit('[ERROR_NOTCL] %s' % _("List of single polygons is empty. Aborting."))
|
|
|
@@ -2709,7 +2755,7 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
|
|
|
if self.app.is_legacy is False:
|
|
|
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_poly_mouse_click_release)
|
|
|
- self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_pres)
|
|
|
+ self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
|
|
|
else:
|
|
|
self.app.plotcanvas.graph_event_disconnect(self.mr)
|
|
|
self.app.plotcanvas.graph_event_disconnect(self.kp)
|
|
|
@@ -2722,6 +2768,127 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
self.delete_moving_selection_shape()
|
|
|
self.delete_tool_selection_shape()
|
|
|
|
|
|
+ def on_iso_tool_add_from_db_executed(self, tool):
|
|
|
+ """
|
|
|
+ Here add the tool from DB in the selected geometry object
|
|
|
+ :return:
|
|
|
+ """
|
|
|
+ tool_from_db = deepcopy(tool)
|
|
|
+
|
|
|
+ res = self.on_tool_from_db_inserted(tool=tool_from_db)
|
|
|
+
|
|
|
+ for idx in range(self.app.ui.plot_tab_area.count()):
|
|
|
+ if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
|
|
|
+ wdg = self.app.ui.plot_tab_area.widget(idx)
|
|
|
+ wdg.deleteLater()
|
|
|
+ self.app.ui.plot_tab_area.removeTab(idx)
|
|
|
+
|
|
|
+ if res == 'fail':
|
|
|
+ return
|
|
|
+ self.app.inform.emit('[success] %s' % _("Tool from DB added in Tool Table."))
|
|
|
+
|
|
|
+ # select last tool added
|
|
|
+ toolid = res
|
|
|
+ for row in range(self.tools_table.rowCount()):
|
|
|
+ if int(self.tools_table.item(row, 3).text()) == toolid:
|
|
|
+ self.tools_table.selectRow(row)
|
|
|
+ self.on_row_selection_change()
|
|
|
+
|
|
|
+ def on_tool_from_db_inserted(self, tool):
|
|
|
+ """
|
|
|
+ Called from the Tools DB object through a App method when adding a tool from Tools Database
|
|
|
+ :param tool: a dict with the tool data
|
|
|
+ :return: None
|
|
|
+ """
|
|
|
+
|
|
|
+ self.ui_disconnect()
|
|
|
+ self.units = self.app.defaults['units'].upper()
|
|
|
+
|
|
|
+ tooldia = float(tool['tooldia'])
|
|
|
+
|
|
|
+ # construct a list of all 'tooluid' in the self.tools
|
|
|
+ tool_uid_list = []
|
|
|
+ for tooluid_key in self.iso_tools:
|
|
|
+ tool_uid_item = int(tooluid_key)
|
|
|
+ tool_uid_list.append(tool_uid_item)
|
|
|
+
|
|
|
+ # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
|
|
|
+ if not tool_uid_list:
|
|
|
+ max_uid = 0
|
|
|
+ else:
|
|
|
+ max_uid = max(tool_uid_list)
|
|
|
+ tooluid = max_uid + 1
|
|
|
+
|
|
|
+ tooldia = float('%.*f' % (self.decimals, tooldia))
|
|
|
+
|
|
|
+ tool_dias = []
|
|
|
+ for k, v in self.iso_tools.items():
|
|
|
+ for tool_v in v.keys():
|
|
|
+ if tool_v == 'tooldia':
|
|
|
+ tool_dias.append(float('%.*f' % (self.decimals, (v[tool_v]))))
|
|
|
+
|
|
|
+ if float('%.*f' % (self.decimals, tooldia)) in tool_dias:
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Tool already in Tool Table."))
|
|
|
+ self.ui_connect()
|
|
|
+ return 'fail'
|
|
|
+
|
|
|
+ self.iso_tools.update({
|
|
|
+ tooluid: {
|
|
|
+ 'tooldia': float('%.*f' % (self.decimals, tooldia)),
|
|
|
+ 'offset': tool['offset'],
|
|
|
+ 'offset_value': tool['offset_value'],
|
|
|
+ 'type': tool['type'],
|
|
|
+ 'tool_type': tool['tool_type'],
|
|
|
+ 'data': deepcopy(tool['data']),
|
|
|
+ 'solid_geometry': []
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ self.iso_tools[tooluid]['data']['name'] = '_iso'
|
|
|
+
|
|
|
+ self.app.inform.emit('[success] %s' % _("New tool added to Tool Table."))
|
|
|
+
|
|
|
+ self.ui_connect()
|
|
|
+ self.build_ui()
|
|
|
+
|
|
|
+ # if self.tools_table.rowCount() != 0:
|
|
|
+ # self.param_frame.setDisabled(False)
|
|
|
+
|
|
|
+ def on_tool_add_from_db_clicked(self):
|
|
|
+ """
|
|
|
+ Called when the user wants to add a new tool from Tools Database. It will create the Tools Database object
|
|
|
+ and display the Tools Database tab in the form needed for the Tool adding
|
|
|
+ :return: None
|
|
|
+ """
|
|
|
+
|
|
|
+ # if the Tools Database is already opened focus on it
|
|
|
+ for idx in range(self.app.ui.plot_tab_area.count()):
|
|
|
+ if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
|
|
|
+ self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
|
|
|
+ break
|
|
|
+ self.app.on_tools_database(source='iso')
|
|
|
+ self.app.tools_db_tab.ok_to_add = True
|
|
|
+ self.app.tools_db_tab.buttons_frame.hide()
|
|
|
+ self.app.tools_db_tab.add_tool_from_db.show()
|
|
|
+ self.app.tools_db_tab.cancel_tool_from_db.show()
|
|
|
+
|
|
|
+ def reset_fields(self):
|
|
|
+ self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
|
|
+
|
|
|
+ def reset_usage(self):
|
|
|
+ self.obj_name = ""
|
|
|
+ self.grb_obj = None
|
|
|
+
|
|
|
+ self.first_click = False
|
|
|
+ self.cursor_pos = None
|
|
|
+ self.mouse_is_dragging = False
|
|
|
+
|
|
|
+ prog_plot = True if self.app.defaults["tools_iso_plotting"] == 'progressive' else False
|
|
|
+ if prog_plot:
|
|
|
+ self.temp_shapes.clear(update=True)
|
|
|
+
|
|
|
+ self.sel_rect = []
|
|
|
+
|
|
|
@staticmethod
|
|
|
def poly2rings(poly):
|
|
|
return [poly.exterior] + [interior for interior in poly.interiors]
|
|
|
@@ -2797,7 +2964,7 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
return geom
|
|
|
|
|
|
@staticmethod
|
|
|
- def generate_rest_geometry(geometry, tooldia, passes, overlap, invert, env_iso_type=2,
|
|
|
+ def generate_rest_geometry(geometry, tooldia, passes, overlap, invert, env_iso_type=2, negative_dia=None,
|
|
|
prog_plot="normal", prog_plot_handler=None):
|
|
|
"""
|
|
|
Will try to isolate the geometry and return a tuple made of list of paths made through isolation
|
|
|
@@ -2815,6 +2982,8 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
:type invert: bool
|
|
|
:param env_iso_type: can be either 0 = keep exteriors or 1 = keep interiors or 2 = keep all paths
|
|
|
:type env_iso_type: int
|
|
|
+ :param negative_dia: isolate the geometry with a negative value for the tool diameter
|
|
|
+ :type negative_dia: bool
|
|
|
:param prog_plot: kind of plotting: "progressive" or "normal"
|
|
|
:type prog_plot: str
|
|
|
:param prog_plot_handler: method used to plot shapes if plot_prog is "proggressive"
|
|
|
@@ -2834,6 +3003,9 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
|
|
|
for nr_pass in range(passes):
|
|
|
iso_offset = tooldia * ((2 * nr_pass + 1) / 2.0) - (nr_pass * overlap * tooldia)
|
|
|
+ if negative_dia:
|
|
|
+ iso_offset = -iso_offset
|
|
|
+
|
|
|
buf_chek = iso_offset * 2
|
|
|
check_geo = geo.buffer(buf_chek)
|
|
|
|
|
|
@@ -2925,123 +3097,47 @@ class ToolIsolation(AppTool, Gerber):
|
|
|
|
|
|
return isolated_geo, not_isolated_geo
|
|
|
|
|
|
- def on_iso_tool_add_from_db_executed(self, tool):
|
|
|
- """
|
|
|
- Here add the tool from DB in the selected geometry object
|
|
|
- :return:
|
|
|
- """
|
|
|
- tool_from_db = deepcopy(tool)
|
|
|
-
|
|
|
- res = self.on_tool_from_db_inserted(tool=tool_from_db)
|
|
|
-
|
|
|
- for idx in range(self.app.ui.plot_tab_area.count()):
|
|
|
- if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
|
|
|
- wdg = self.app.ui.plot_tab_area.widget(idx)
|
|
|
- wdg.deleteLater()
|
|
|
- self.app.ui.plot_tab_area.removeTab(idx)
|
|
|
+ @staticmethod
|
|
|
+ def get_selected_interior(poly: Polygon, point: tuple) -> [Polygon, None]:
|
|
|
+ try:
|
|
|
+ ints = [Polygon(x) for x in poly.interiors]
|
|
|
+ except AttributeError:
|
|
|
+ return None
|
|
|
|
|
|
- if res == 'fail':
|
|
|
- return
|
|
|
- self.app.inform.emit('[success] %s' % _("Tool from DB added in Tool Table."))
|
|
|
+ for poly in ints:
|
|
|
+ if poly.contains(Point(point)):
|
|
|
+ return poly
|
|
|
|
|
|
- # select last tool added
|
|
|
- toolid = res
|
|
|
- for row in range(self.tools_table.rowCount()):
|
|
|
- if int(self.tools_table.item(row, 3).text()) == toolid:
|
|
|
- self.tools_table.selectRow(row)
|
|
|
- self.on_row_selection_change()
|
|
|
+ return None
|
|
|
|
|
|
- def on_tool_from_db_inserted(self, tool):
|
|
|
- """
|
|
|
- Called from the Tools DB object through a App method when adding a tool from Tools Database
|
|
|
- :param tool: a dict with the tool data
|
|
|
- :return: None
|
|
|
+ def find_polygon_ignore_interiors(self, point, geoset=None):
|
|
|
"""
|
|
|
+ Find an object that object.contains(Point(point)) in
|
|
|
+ poly, which can can be iterable, contain iterable of, or
|
|
|
+ be itself an implementer of .contains(). Will test the Polygon as it is full with no interiors.
|
|
|
|
|
|
- self.ui_disconnect()
|
|
|
- self.units = self.app.defaults['units'].upper()
|
|
|
-
|
|
|
- tooldia = float(tool['tooldia'])
|
|
|
-
|
|
|
- # construct a list of all 'tooluid' in the self.tools
|
|
|
- tool_uid_list = []
|
|
|
- for tooluid_key in self.iso_tools:
|
|
|
- tool_uid_item = int(tooluid_key)
|
|
|
- tool_uid_list.append(tool_uid_item)
|
|
|
-
|
|
|
- # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
|
|
|
- if not tool_uid_list:
|
|
|
- max_uid = 0
|
|
|
- else:
|
|
|
- max_uid = max(tool_uid_list)
|
|
|
- tooluid = max_uid + 1
|
|
|
-
|
|
|
- tooldia = float('%.*f' % (self.decimals, tooldia))
|
|
|
-
|
|
|
- tool_dias = []
|
|
|
- for k, v in self.iso_tools.items():
|
|
|
- for tool_v in v.keys():
|
|
|
- if tool_v == 'tooldia':
|
|
|
- tool_dias.append(float('%.*f' % (self.decimals, (v[tool_v]))))
|
|
|
-
|
|
|
- if float('%.*f' % (self.decimals, tooldia)) in tool_dias:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Tool already in Tool Table."))
|
|
|
- self.ui_connect()
|
|
|
- return 'fail'
|
|
|
-
|
|
|
- self.iso_tools.update({
|
|
|
- tooluid: {
|
|
|
- 'tooldia': float('%.*f' % (self.decimals, tooldia)),
|
|
|
- 'offset': tool['offset'],
|
|
|
- 'offset_value': tool['offset_value'],
|
|
|
- 'type': tool['type'],
|
|
|
- 'tool_type': tool['tool_type'],
|
|
|
- 'data': deepcopy(tool['data']),
|
|
|
- 'solid_geometry': []
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- self.iso_tools[tooluid]['data']['name'] = '_iso'
|
|
|
-
|
|
|
- self.app.inform.emit('[success] %s' % _("New tool added to Tool Table."))
|
|
|
-
|
|
|
- self.ui_connect()
|
|
|
- self.build_ui()
|
|
|
-
|
|
|
- # if self.tools_table.rowCount() != 0:
|
|
|
- # self.param_frame.setDisabled(False)
|
|
|
-
|
|
|
- def on_tool_add_from_db_clicked(self):
|
|
|
+ :param point: See description
|
|
|
+ :param geoset: a polygon or list of polygons where to find if the param point is contained
|
|
|
+ :return: Polygon containing point or None.
|
|
|
"""
|
|
|
- Called when the user wants to add a new tool from Tools Database. It will create the Tools Database object
|
|
|
- and display the Tools Database tab in the form needed for the Tool adding
|
|
|
- :return: None
|
|
|
- """
|
|
|
-
|
|
|
- # if the Tools Database is already opened focus on it
|
|
|
- for idx in range(self.app.ui.plot_tab_area.count()):
|
|
|
- if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
|
|
|
- self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
|
|
|
- break
|
|
|
- self.app.on_tools_database(source='iso')
|
|
|
- self.app.tools_db_tab.ok_to_add = True
|
|
|
- self.app.tools_db_tab.buttons_frame.hide()
|
|
|
- self.app.tools_db_tab.add_tool_from_db.show()
|
|
|
- self.app.tools_db_tab.cancel_tool_from_db.show()
|
|
|
|
|
|
- def reset_fields(self):
|
|
|
- self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
|
|
-
|
|
|
- def reset_usage(self):
|
|
|
- self.obj_name = ""
|
|
|
- self.grb_obj = None
|
|
|
-
|
|
|
- self.first_click = False
|
|
|
- self.cursor_pos = None
|
|
|
- self.mouse_is_dragging = False
|
|
|
-
|
|
|
- prog_plot = True if self.app.defaults["tools_iso_plotting"] == 'progressive' else False
|
|
|
- if prog_plot:
|
|
|
- self.temp_shapes.clear(update=True)
|
|
|
-
|
|
|
- self.sel_rect = []
|
|
|
+ if geoset is None:
|
|
|
+ geoset = self.solid_geometry
|
|
|
+
|
|
|
+ try: # Iterable
|
|
|
+ for sub_geo in geoset:
|
|
|
+ p = self.find_polygon_ignore_interiors(point, geoset=sub_geo)
|
|
|
+ if p is not None:
|
|
|
+ return p
|
|
|
+ except TypeError: # Non-iterable
|
|
|
+ try: # Implements .contains()
|
|
|
+ if isinstance(geoset, LinearRing):
|
|
|
+ geoset = Polygon(geoset)
|
|
|
+
|
|
|
+ poly_ext = Polygon(geoset.exterior)
|
|
|
+ if poly_ext.contains(Point(point)):
|
|
|
+ return geoset
|
|
|
+ except AttributeError: # Does not implement .contains()
|
|
|
+ return None
|
|
|
+
|
|
|
+ return None
|