- Tool Fiducials - added choice of shapes: circular or non-standard cross

- Tool Fiducials - finished the work on adding soldermask openings
- Tool Fiducials - finished the tool
This commit is contained in:
Marius Stanciu
2019-11-21 23:50:13 +02:00
committed by Marius
parent c9f8cf8abe
commit 3a635117df
2 changed files with 218 additions and 65 deletions

View File

@@ -12,6 +12,9 @@ CAD program, and create G-Code for Isolation routing.
21.11.2019 21.11.2019
- Tool Fiducials - finished the part with adding copper fiducials: manual and auto - Tool Fiducials - finished the part with adding copper fiducials: manual and auto
- Tool Fiducials - added choice of shapes: circular or non-standard cross
- Tool Fiducials - finished the work on adding soldermask openings
- Tool Fiducials - finished the tool
20.11.2019 20.11.2019

View File

@@ -10,9 +10,7 @@ from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet, EvalEntry, FCTable from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet, EvalEntry, FCTable
import shapely.geometry.base as base from shapely.geometry import Point, MultiPolygon, LineString
from shapely.ops import unary_union
from shapely.geometry import Point
from shapely.geometry import box as box from shapely.geometry import box as box
@@ -216,6 +214,38 @@ class ToolFiducials(FlatCAMTool):
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid_lay.addWidget(separator_line, 5, 0, 1, 2) grid_lay.addWidget(separator_line, 5, 0, 1, 2)
# Fiducial type #
self.fid_type_radio = RadioSet([
{'label': _('Circular'), 'value': 'circular'},
{"label": _("Cross"), "value": "cross"}
], stretch=False)
self.fid_type_label = QtWidgets.QLabel('%s:' % _("Fiducial Type"))
self.fid_type_label.setToolTip(
_("The type of fiducial.\n"
"- 'Circular' - this is the regular fiducial.\n "
"- 'Cross' - non-standard fiducial.")
)
grid_lay.addWidget(self.fid_type_label, 6, 0)
grid_lay.addWidget(self.fid_type_radio, 6, 1)
# Line Thickness #
self.line_thickness_label = QtWidgets.QLabel('%s:' % _("Line thickness"))
self.line_thickness_label.setToolTip(
_("Bounding box margin.")
)
self.line_thickness_entry = FCDoubleSpinner()
self.line_thickness_entry.set_range(0.00001, 9999.9999)
self.line_thickness_entry.set_precision(self.decimals)
self.line_thickness_entry.setSingleStep(0.1)
grid_lay.addWidget(self.line_thickness_label, 7, 0)
grid_lay.addWidget(self.line_thickness_entry, 7, 1)
separator_line_1 = QtWidgets.QFrame()
separator_line_1.setFrameShape(QtWidgets.QFrame.HLine)
separator_line_1.setFrameShadow(QtWidgets.QFrame.Sunken)
grid_lay.addWidget(separator_line_1, 8, 0, 1, 2)
# Copper Gerber object # Copper Gerber object
self.grb_object_combo = QtWidgets.QComboBox() self.grb_object_combo = QtWidgets.QComboBox()
self.grb_object_combo.setModel(self.app.collection) self.grb_object_combo.setModel(self.app.collection)
@@ -227,20 +257,20 @@ class ToolFiducials(FlatCAMTool):
_("Gerber Object to which will be added a copper thieving.") _("Gerber Object to which will be added a copper thieving.")
) )
grid_lay.addWidget(self.grbobj_label, 6, 0, 1, 2) grid_lay.addWidget(self.grbobj_label, 9, 0, 1, 2)
grid_lay.addWidget(self.grb_object_combo, 7, 0, 1, 2) grid_lay.addWidget(self.grb_object_combo, 10, 0, 1, 2)
# ## Insert Copper Fiducial # ## Insert Copper Fiducial
self.add_cfid_button = QtWidgets.QPushButton(_("Add Fiducial")) self.add_cfid_button = QtWidgets.QPushButton(_("Add Fiducial"))
self.add_cfid_button.setToolTip( self.add_cfid_button.setToolTip(
_("Will add a polygon on the copper layer to serve as fiducial.") _("Will add a polygon on the copper layer to serve as fiducial.")
) )
grid_lay.addWidget(self.add_cfid_button, 8, 0, 1, 2) grid_lay.addWidget(self.add_cfid_button, 11, 0, 1, 2)
separator_line_1 = QtWidgets.QFrame() separator_line_2 = QtWidgets.QFrame()
separator_line_1.setFrameShape(QtWidgets.QFrame.HLine) separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
separator_line_1.setFrameShadow(QtWidgets.QFrame.Sunken) separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
grid_lay.addWidget(separator_line_1, 9, 0, 1, 2) grid_lay.addWidget(separator_line_2, 12, 0, 1, 2)
# Soldermask Gerber object # # Soldermask Gerber object #
self.sm_object_label = QtWidgets.QLabel('<b>%s:</b>' % _("Soldermask Gerber")) self.sm_object_label = QtWidgets.QLabel('<b>%s:</b>' % _("Soldermask Gerber"))
@@ -252,8 +282,8 @@ class ToolFiducials(FlatCAMTool):
self.sm_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) self.sm_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.sm_object_combo.setCurrentIndex(1) self.sm_object_combo.setCurrentIndex(1)
grid_lay.addWidget(self.sm_object_label, 10, 0, 1, 2) grid_lay.addWidget(self.sm_object_label, 13, 0, 1, 2)
grid_lay.addWidget(self.sm_object_combo, 11, 0, 1, 2) grid_lay.addWidget(self.sm_object_combo, 14, 0, 1, 2)
# ## Insert Soldermask opening for Fiducial # ## Insert Soldermask opening for Fiducial
self.add_sm_opening_button = QtWidgets.QPushButton(_("Add Soldermask Opening")) self.add_sm_opening_button = QtWidgets.QPushButton(_("Add Soldermask Opening"))
@@ -263,7 +293,7 @@ class ToolFiducials(FlatCAMTool):
"The diameter is always double of the diameter\n" "The diameter is always double of the diameter\n"
"for the copper fiducial.") "for the copper fiducial.")
) )
grid_lay.addWidget(self.add_sm_opening_button, 12, 0, 1, 2) grid_lay.addWidget(self.add_sm_opening_button, 15, 0, 1, 2)
self.layout.addStretch() self.layout.addStretch()
@@ -301,7 +331,7 @@ class ToolFiducials(FlatCAMTool):
self.add_cfid_button.clicked.connect(self.add_fiducials) self.add_cfid_button.clicked.connect(self.add_fiducials)
self.add_sm_opening_button.clicked.connect(self.add_soldermask_opening) self.add_sm_opening_button.clicked.connect(self.add_soldermask_opening)
# self.reference_radio.group_toggle_fn = self.on_toggle_reference self.fid_type_radio.activated_custom.connect(self.on_fiducial_type)
self.pos_radio.activated_custom.connect(self.on_second_point) self.pos_radio.activated_custom.connect(self.on_second_point)
self.mode_radio.activated_custom.connect(self.on_method_change) self.mode_radio.activated_custom.connect(self.on_method_change)
@@ -373,11 +403,20 @@ class ToolFiducials(FlatCAMTool):
except TypeError: except TypeError:
pass pass
def on_fiducial_type(self, val):
if val == 'cross':
self.line_thickness_label.setDisabled(False)
self.line_thickness_entry.setDisabled(False)
else:
self.line_thickness_label.setDisabled(True)
self.line_thickness_entry.setDisabled(True)
def add_fiducials(self): def add_fiducials(self):
self.app.call_source = "fiducials_tool" self.app.call_source = "fiducials_tool"
self.mode_method = self.mode_radio.get_value() self.mode_method = self.mode_radio.get_value()
self.margin_val = self.margin_entry.get_value() self.margin_val = self.margin_entry.get_value()
self.sec_position = self.pos_radio.get_value() self.sec_position = self.pos_radio.get_value()
fid_type = self.fid_type_radio.get_value()
# get the Gerber object on which the Fiducial will be inserted # get the Gerber object on which the Fiducial will be inserted
selection_index = self.grb_object_combo.currentIndex() selection_index = self.grb_object_combo.currentIndex()
@@ -431,7 +470,7 @@ class ToolFiducials(FlatCAMTool):
) )
self.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y0)) self.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y0))
self.add_fiducials_geo(self.click_points) self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
self.on_exit() self.on_exit()
else: else:
self.app.inform.emit(_("Click to add first Fiducial. Bottom Left...")) self.app.inform.emit(_("Click to add first Fiducial. Bottom Left..."))
@@ -439,66 +478,151 @@ class ToolFiducials(FlatCAMTool):
self.top_right_coords_entry.set_value('') self.top_right_coords_entry.set_value('')
self.sec_points_coords_entry.set_value('') self.sec_points_coords_entry.set_value('')
if self.app.is_legacy is False: self.connect_event_handlers()
self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
# self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
else:
self.app.plotcanvas.graph_event_disconnect(self.app.mp)
self.app.plotcanvas.graph_event_disconnect(self.app.mm)
self.app.plotcanvas.graph_event_disconnect(self.app.mr)
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
# self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
# To be called after clicking on the plot. # To be called after clicking on the plot.
def add_fiducials_geo(self, points_list): def add_fiducials_geo(self, points_list, g_obj, fid_size=None, fid_type=None, line_size=None):
""" """
Add geometry to the solid_geometry of the copper Gerber object Add geometry to the solid_geometry of the copper Gerber object
:param points_list: list of coordinates for the fiducials :param points_list: list of coordinates for the fiducials
:param g_obj: the Gerber object where to add the geometry
:param fid_size: the overall size of the fiducial or fiducial opening depending on the g_obj type
:param fid_type: the type of fiducial: circular or cross
:param line_size: the line thickenss when the fiducial type is cross
:return: :return:
""" """
self.fid_dia = self.dia_entry.get_value() fid_dia = self.dia_entry.get_value() if fid_size is None else fid_size
radius = self.fid_dia / 2.0 fid_type = 'crcular' if fid_type is None else fid_type
line_thickness = self.line_thickness_entry.get_value() if line_size is None else line_size
geo_list = [Point(pt).buffer(radius) for pt in points_list] radius = fid_dia / 2.0
aperture_found = None if fid_type == 'circular':
for ap_id, ap_val in self.grb_object.apertures.items(): geo_list = [Point(pt).buffer(radius) for pt in points_list]
if ap_val['type'] == 'C' and ap_val['size'] == self.fid_dia:
aperture_found = ap_id
break
if aperture_found: aperture_found = None
for geo in geo_list: for ap_id, ap_val in g_obj.apertures.items():
dict_el = dict() if ap_val['type'] == 'C' and ap_val['size'] == fid_dia:
dict_el['follow'] = geo.centroid aperture_found = ap_id
dict_el['solid'] = geo break
self.grb_object.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
if aperture_found:
for geo in geo_list:
dict_el = dict()
dict_el['follow'] = geo.centroid
dict_el['solid'] = geo
g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
else:
ap_keys = list(g_obj.apertures.keys())
if ap_keys:
new_apid = str(int(max(ap_keys)) + 1)
else:
new_apid = '10'
g_obj.apertures[new_apid] = dict()
g_obj.apertures[new_apid]['type'] = 'C'
g_obj.apertures[new_apid]['size'] = fid_dia
g_obj.apertures[new_apid]['geometry'] = list()
for geo in geo_list:
dict_el = dict()
dict_el['follow'] = geo.centroid
dict_el['solid'] = geo
g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
s_list = list()
if g_obj.solid_geometry:
try:
for poly in g_obj.solid_geometry:
s_list.append(poly)
except TypeError:
s_list.append(g_obj.solid_geometry)
s_list += geo_list
g_obj.solid_geometry = MultiPolygon(s_list)
else: else:
new_apid = int(max(list(self.grb_object.apertures.keys()))) + 1 geo_list = list()
self.grb_object.apertures[new_apid] = dict()
self.grb_object.apertures[new_apid]['type'] = 'C'
self.grb_object.apertures[new_apid]['size'] = self.fid_dia
self.grb_object.apertures[new_apid]['geometry'] = list()
for geo in geo_list: for pt in points_list:
dict_el = dict() x = pt[0]
dict_el['follow'] = geo.centroid y = pt[1]
dict_el['solid'] = geo line_geo_hor = LineString([
self.grb_object.apertures[new_apid]['geometry'].append(deepcopy(dict_el)) (x - radius + (line_thickness / 2.0), y), (x + radius - (line_thickness / 2.0), y)
])
line_geo_vert = LineString([
(x, y - radius + (line_thickness / 2.0)), (x, y + radius - (line_thickness / 2.0))
])
geo_list.append([line_geo_hor, line_geo_vert])
if self.grb_object.solid_geometry: aperture_found = None
self.grb_object.solid_geometry = [self.grb_object.solid_geometry, geo_list] for ap_id, ap_val in g_obj.apertures.items():
if ap_val['type'] == 'C' and ap_val['size'] == line_thickness:
aperture_found = ap_id
break
geo_buff_list = list()
if aperture_found:
for geo in geo_list:
geo_buff_h = geo[0].buffer(line_thickness / 2.0)
geo_buff_v = geo[1].buffer(line_thickness / 2.0)
geo_buff_list.append(geo_buff_h)
geo_buff_list.append(geo_buff_v)
dict_el = dict()
dict_el['follow'] = geo_buff_h.centroid
dict_el['solid'] = geo_buff_h
g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
dict_el['follow'] = geo_buff_v.centroid
dict_el['solid'] = geo_buff_v
g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
else:
ap_keys = list(g_obj.apertures.keys())
if ap_keys:
new_apid = str(int(max(ap_keys)) + 1)
else:
new_apid = '10'
g_obj.apertures[new_apid] = dict()
g_obj.apertures[new_apid]['type'] = 'C'
g_obj.apertures[new_apid]['size'] = line_thickness
g_obj.apertures[new_apid]['geometry'] = list()
for geo in geo_list:
geo_buff_h = geo[0].buffer(line_thickness / 2.0)
geo_buff_v = geo[1].buffer(line_thickness / 2.0)
geo_buff_list.append(geo_buff_h)
geo_buff_list.append(geo_buff_v)
dict_el = dict()
dict_el['follow'] = geo_buff_h.centroid
dict_el['solid'] = geo_buff_h
g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
dict_el['follow'] = geo_buff_v.centroid
dict_el['solid'] = geo_buff_v
g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
s_list = list()
if g_obj.solid_geometry:
try:
for poly in g_obj.solid_geometry:
s_list.append(poly)
except TypeError:
s_list.append(g_obj.solid_geometry)
geo_buff_list = MultiPolygon(geo_buff_list)
geo_buff_list = geo_buff_list.buffer(0)
for poly in geo_buff_list:
s_list.append(poly)
g_obj.solid_geometry = MultiPolygon(s_list)
def add_soldermask_opening(self): def add_soldermask_opening(self):
self.sm_opening_dia = self.dia_entry.get_value() * 2.0 sm_opening_dia = self.dia_entry.get_value() * 2.0
# get the Gerber object on which the Fiducial will be inserted # get the Gerber object on which the Fiducial will be inserted
selection_index = self.grb_object_combo.currentIndex() selection_index = self.sm_object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.grb_object_combo.rootModelIndex()) model_index = self.app.collection.index(selection_index, 0, self.sm_object_combo.rootModelIndex())
try: try:
self.sm_object = model_index.internalPointer().obj self.sm_object = model_index.internalPointer().obj
@@ -509,6 +633,9 @@ class ToolFiducials(FlatCAMTool):
self.sm_obj_set.add(self.sm_object.options['name']) self.sm_obj_set.add(self.sm_object.options['name'])
self.add_fiducials_geo(self.click_points, g_obj=self.sm_object, fid_size=sm_opening_dia, fid_type='circular')
self.on_exit()
def on_mouse_release(self, event): def on_mouse_release(self, event):
if event.button == 1: if event.button == 1:
if self.app.is_legacy is False: if self.app.is_legacy is False:
@@ -532,6 +659,8 @@ class ToolFiducials(FlatCAMTool):
self.check_points() self.check_points()
def check_points(self): def check_points(self):
fid_type = self.fid_type_radio.get_value()
if len(self.click_points) == 1: if len(self.click_points) == 1:
self.bottom_left_coords_entry.set_value(self.click_points[0]) self.bottom_left_coords_entry.set_value(self.click_points[0])
self.app.inform.emit(_("Click to add the last fiducial. Top Right...")) self.app.inform.emit(_("Click to add the last fiducial. Top Right..."))
@@ -549,25 +678,31 @@ class ToolFiducials(FlatCAMTool):
if len(self.click_points) == 2: if len(self.click_points) == 2:
self.sec_points_coords_entry.set_value(self.click_points[2]) self.sec_points_coords_entry.set_value(self.click_points[2])
self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added.")) self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added."))
self.add_fiducials_geo(self.click_points) self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
self.on_exit() self.on_exit()
def on_mouse_move(self, event): def on_mouse_move(self, event):
pass pass
def replot(self, obj): def replot(self, obj, run_thread=True):
def worker_task(): def worker_task():
with self.app.proc_container.new('%s...' % _("Plotting")): with self.app.proc_container.new('%s...' % _("Plotting")):
obj.plot() obj.plot()
self.app.worker_task.emit({'fcn': worker_task, 'params': []}) if run_thread:
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
else:
worker_task()
def on_exit(self): def on_exit(self):
# plot the object # plot the object
for ob_name in self.copper_obj_set: for ob_name in self.copper_obj_set:
try: try:
copper_obj = self.app.collection.get_by_name(name=ob_name) copper_obj = self.app.collection.get_by_name(name=ob_name)
self.replot(obj=copper_obj) if len(self.copper_obj_set) > 1:
self.replot(obj=copper_obj, run_thread=False)
else:
self.replot(obj=copper_obj)
except (AttributeError, TypeError): except (AttributeError, TypeError):
continue continue
@@ -584,7 +719,10 @@ class ToolFiducials(FlatCAMTool):
for ob_name in self.sm_obj_set: for ob_name in self.sm_obj_set:
try: try:
sm_obj = self.app.collection.get_by_name(name=ob_name) sm_obj = self.app.collection.get_by_name(name=ob_name)
self.replot(obj=sm_obj) if len(self.sm_obj_set) > 1:
self.replot(obj=sm_obj, run_thread=False)
else:
self.replot(obj=sm_obj)
except (AttributeError, TypeError): except (AttributeError, TypeError):
continue continue
@@ -614,19 +752,31 @@ class ToolFiducials(FlatCAMTool):
if len(self.click_points) not in [0, 3]: if len(self.click_points) not in [0, 3]:
self.click_points = list() self.click_points = list()
if self.mode_method == 'manual' and len(self.click_points) not in [0, 3]: self.disconnect_event_handlers()
self.disconnect_event_handlers()
self.app.call_source = "app" self.app.call_source = "app"
self.app.inform.emit('[success] %s' % _("Fiducials Tool exit.")) self.app.inform.emit('[success] %s' % _("Fiducials Tool exit."))
def connect_event_handlers(self):
if self.app.is_legacy is False:
self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
# self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
else:
self.app.plotcanvas.graph_event_disconnect(self.app.mp)
# self.app.plotcanvas.graph_event_disconnect(self.app.mm)
self.app.plotcanvas.graph_event_disconnect(self.app.mr)
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
# self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
def disconnect_event_handlers(self): def disconnect_event_handlers(self):
if self.app.is_legacy is False: if self.app.is_legacy is False:
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release) self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move) # self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
else: else:
self.app.plotcanvas.graph_event_disconnect(self.mr) self.app.plotcanvas.graph_event_disconnect(self.mr)
self.app.plotcanvas.graph_event_disconnect(self.mm) # self.app.plotcanvas.graph_event_disconnect(self.mm)
self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press', self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
self.app.on_mouse_click_over_plot) self.app.on_mouse_click_over_plot)