- added a new tool named "Optimal Tool" which will determine the minimum distance between the copper features for a Gerber object, in fact determining the maximum diameter for a isolation tool that can be used for a complete isolation

This commit is contained in:
Marius Stanciu 2019-09-28 03:51:39 +03:00 committed by Marius
parent c5ecc7ad88
commit a2bef40097
5 changed files with 246 additions and 2 deletions

View File

@ -2377,6 +2377,7 @@ class App(QtCore.QObject):
self.move_tool = None self.move_tool = None
self.cutout_tool = None self.cutout_tool = None
self.ncclear_tool = None self.ncclear_tool = None
self.optimal_tool=None
self.paint_tool = None self.paint_tool = None
self.transform_tool = None self.transform_tool = None
self.properties_tool = None self.properties_tool = None
@ -2911,7 +2912,10 @@ class App(QtCore.QObject):
self.sub_tool.install(icon=QtGui.QIcon('share/sub32.png'), pos=self.ui.menutool, separator=True) self.sub_tool.install(icon=QtGui.QIcon('share/sub32.png'), pos=self.ui.menutool, separator=True)
self.rules_tool = RulesCheck(self) self.rules_tool = RulesCheck(self)
self.rules_tool.install(icon=QtGui.QIcon('share/rules32.png'), pos=self.ui.menutool, separator=True) self.rules_tool.install(icon=QtGui.QIcon('share/rules32.png'), pos=self.ui.menutool, separator=False)
self.optimal_tool = ToolOptimal(self)
self.optimal_tool.install(icon=QtGui.QIcon('share/open_excellon32.png'), pos=self.ui.menutool, separator=True)
self.move_tool = ToolMove(self) self.move_tool = ToolMove(self)
self.move_tool.install(icon=QtGui.QIcon('share/move16.png'), pos=self.ui.menuedit, self.move_tool.install(icon=QtGui.QIcon('share/move16.png'), pos=self.ui.menuedit,
@ -3041,6 +3045,7 @@ class App(QtCore.QObject):
self.ui.solder_btn.triggered.connect(lambda: self.paste_tool.run(toggle=True)) self.ui.solder_btn.triggered.connect(lambda: self.paste_tool.run(toggle=True))
self.ui.sub_btn.triggered.connect(lambda: self.sub_tool.run(toggle=True)) self.ui.sub_btn.triggered.connect(lambda: self.sub_tool.run(toggle=True))
self.ui.rules_btn.triggered.connect(lambda: self.rules_tool.run(toggle=True)) self.ui.rules_btn.triggered.connect(lambda: self.rules_tool.run(toggle=True))
self.ui.optimal_btn.triggered.connect(lambda: self.optimal_tool.run(toggle=True))
self.ui.calculators_btn.triggered.connect(lambda: self.calculator_tool.run(toggle=True)) self.ui.calculators_btn.triggered.connect(lambda: self.calculator_tool.run(toggle=True))
self.ui.transform_btn.triggered.connect(lambda: self.transform_tool.run(toggle=True)) self.ui.transform_btn.triggered.connect(lambda: self.transform_tool.run(toggle=True))

View File

@ -12,6 +12,7 @@ CAD program, and create G-Code for Isolation routing.
28.09.2019 28.09.2019
- changed the icon for Open Script and reused it for the Check Rules Tool - changed the icon for Open Script and reused it for the Check Rules Tool
- added a new tool named "Optimal Tool" which will determine the minimum distance between the copper features for a Gerber object, in fact determining the maximum diameter for a isolation tool that can be used for a complete isolation
27.09.2019 27.09.2019

View File

@ -679,6 +679,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.solder_btn = self.toolbartools.addAction(QtGui.QIcon('share/solderpastebis32.png'), _("SolderPaste Tool")) self.solder_btn = self.toolbartools.addAction(QtGui.QIcon('share/solderpastebis32.png'), _("SolderPaste Tool"))
self.sub_btn = self.toolbartools.addAction(QtGui.QIcon('share/sub32.png'), _("Substract Tool")) self.sub_btn = self.toolbartools.addAction(QtGui.QIcon('share/sub32.png'), _("Substract Tool"))
self.rules_btn = self.toolbartools.addAction(QtGui.QIcon('share/rules32.png'), _("Rules Tool")) self.rules_btn = self.toolbartools.addAction(QtGui.QIcon('share/rules32.png'), _("Rules Tool"))
self.optimal_btn = self.toolbartools.addAction(QtGui.QIcon('share/open_excellon32.png'), _("Optimal Tool"))
self.toolbartools.addSeparator() self.toolbartools.addSeparator()
@ -1236,6 +1237,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
<td height="20"><strong>ALT+N</strong></td> <td height="20"><strong>ALT+N</strong></td>
<td>&nbsp;%s</td> <td>&nbsp;%s</td>
</tr> </tr>
<tr height="20">
<td height="20"><strong>ALT+O</strong></td>
<td>&nbsp;%s</td>
</tr>
<tr height="20"> <tr height="20">
<td height="20"><strong>ALT+P</strong></td> <td height="20"><strong>ALT+P</strong></td>
<td>&nbsp;%s</td> <td>&nbsp;%s</td>
@ -1333,7 +1338,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
_("Rotate by 90 degree CCW"), _("Run a Script"), _("Toggle the workspace"), _("Skew on X axis"), _("Rotate by 90 degree CCW"), _("Run a Script"), _("Toggle the workspace"), _("Skew on X axis"),
_("Skew on Y axis"), _("Calculators Tool"), _("2-Sided PCB Tool"), _("Transformations Tool"), _("Skew on Y axis"), _("Calculators Tool"), _("2-Sided PCB Tool"), _("Transformations Tool"),
_("Solder Paste Dispensing Tool"), _("Solder Paste Dispensing Tool"),
_("Film PCB Tool"), _("Non-Copper Clearing Tool"), _("Film PCB Tool"), _("Non-Copper Clearing Tool"), _("Optimal Tool"),
_("Paint Area Tool"), _("PDF Import Tool"), _("Rules Check Tool"), _("Paint Area Tool"), _("PDF Import Tool"), _("Rules Check Tool"),
_("View File Source"), _("View File Source"),
_("Cutout PCB Tool"), _("Enable all Plots"), _("Disable all Plots"), _("Disable Non-selected Plots"), _("Cutout PCB Tool"), _("Enable all Plots"), _("Disable all Plots"), _("Disable Non-selected Plots"),
@ -2433,6 +2438,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.app.ncclear_tool.run(toggle=True) self.app.ncclear_tool.run(toggle=True)
return return
# Optimal Tool
if key == QtCore.Qt.Key_O:
self.app.optimal_tool.run(toggle=True)
return
# Paint Tool # Paint Tool
if key == QtCore.Qt.Key_P: if key == QtCore.Qt.Key_P:
self.app.paint_tool.run(toggle=True) self.app.paint_tool.run(toggle=True)

226
flatcamTools/ToolOptimal.py Normal file
View File

@ -0,0 +1,226 @@
from FlatCAMTool import FlatCAMTool
from FlatCAMObj import *
from shapely.geometry import Point
from shapely import affinity
from PyQt5 import QtCore
import gettext
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class ToolOptimal(FlatCAMTool):
toolName = _("Optimal Tool")
def __init__(self, app):
FlatCAMTool.__init__(self, app)
# ## Title
title_label = QtWidgets.QLabel("%s" % self.toolName)
title_label.setStyleSheet("""
QLabel
{
font-size: 16px;
font-weight: bold;
}
""")
self.layout.addWidget(title_label)
# ## Form Layout
form_lay = QtWidgets.QFormLayout()
self.layout.addLayout(form_lay)
# ## Gerber Object to mirror
self.gerber_object_combo = QtWidgets.QComboBox()
self.gerber_object_combo.setModel(self.app.collection)
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.gerber_object_combo.setCurrentIndex(1)
self.gerber_object_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
self.gerber_object_label.setToolTip(
"Gerber to be mirrored."
)
self.title_res_label = QtWidgets.QLabel('<b>%s</b>' % _("Minimum distance between copper features"))
self.result_label = QtWidgets.QLabel('%s:' % _("Found"))
self.show_res = QtWidgets.QLabel('%s' % '')
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
self.units_lbl = QtWidgets.QLabel(self.units)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(self.show_res)
hlay.addStretch()
hlay.addWidget(self.units_lbl)
form_lay.addRow(QtWidgets.QLabel(""))
form_lay.addRow(self.gerber_object_label, self.gerber_object_combo)
form_lay.addRow(QtWidgets.QLabel(""))
form_lay.addRow(self.title_res_label)
form_lay.addRow(self.result_label, hlay)
self.calculate_button = QtWidgets.QPushButton(_("Find Distance"))
self.calculate_button.setToolTip(
_("Calculate the minimum distance between copper features,\n"
"this will allow the determination of the right tool to\n"
"use for isolation or copper clearing.")
)
self.calculate_button.setMinimumWidth(60)
self.layout.addWidget(self.calculate_button)
# self.dt_label = QtWidgets.QLabel("<b>%s:</b>" % _('Alignment Drill Diameter'))
# self.dt_label.setToolTip(
# _("Diameter of the drill for the "
# "alignment holes.")
# )
# self.layout.addWidget(self.dt_label)
#
# hlay = QtWidgets.QHBoxLayout()
# self.layout.addLayout(hlay)
#
# self.drill_dia = FCEntry()
# self.dd_label = QtWidgets.QLabel('%s:' % _("Drill dia"))
# self.dd_label.setToolTip(
# _("Diameter of the drill for the "
# "alignment holes.")
# )
# hlay.addWidget(self.dd_label)
# hlay.addWidget(self.drill_dia)
self.calculate_button.clicked.connect(self.find_minimum_distance)
self.layout.addStretch()
# ## Signals
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+O', **kwargs)
def run(self, toggle=True):
self.app.report_usage("ToolOptimal()")
self.show_res.setText('')
if toggle:
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Optimal Tool"))
def set_tool_ui(self):
self.reset_fields()
def find_minimum_distance(self):
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
selection_index = self.gerber_object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.gerber_object_combo.rootModelIndex())
try:
fcobj = model_index.internalPointer().obj
except Exception as e:
log.debug("ToolOptimal.find_minimum_distance() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
return
if not isinstance(fcobj, FlatCAMGerber):
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Only Gerber objects can be evaluated."))
return
proc = self.app.proc_container.new(_("Working..."))
def job_thread(app_obj):
app_obj.inform.emit(_("Optimal Tool. Started to search for the minimum distance between copper features."))
try:
old_disp_number = 0
pol_nr = 0
app_obj.proc_container.update_view_text(' %d%%' % 0)
total_geo = list()
for ap in list(fcobj.apertures.keys()):
if 'geometry' in fcobj.apertures[ap]:
app_obj.inform.emit(
'%s: %s' % (_("Optimal Tool. Parsing geometry for aperture"), str(ap)))
for geo_el in fcobj.apertures[ap]['geometry']:
if self.app.abort_flag:
# graceful abort requested by the user
raise FlatCAMApp.GracefulException
if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
total_geo.append(geo_el['solid'])
app_obj.inform.emit(
_("Optimal Tool. Creating a buffer for the object geometry."))
total_geo = MultiPolygon(total_geo)
total_geo = total_geo.buffer(0)
geo_len = len(total_geo)
geo_len = (geo_len * (geo_len - 1)) / 2
app_obj.inform.emit(
'%s: %s' % (_("Optimal Tool. Finding the distances between each two elements. Iterations"),
str(geo_len)))
min_set = set()
idx = 1
for geo in total_geo:
for s_geo in total_geo[idx:]:
if self.app.abort_flag:
# graceful abort requested by the user
raise FlatCAMApp.GracefulException
# minimize the number of distances by not taking into considerations those that are too small
dist = geo.distance(s_geo)
min_set.add(dist)
pol_nr += 1
disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
if old_disp_number < disp_number <= 100:
app_obj.proc_container.update_view_text(' %d%%' % disp_number)
old_disp_number = disp_number
idx += 1
app_obj.inform.emit(
_("Optimal Tool. Finding the minimum distance."))
min_dist = min(min_set)
min_dist = '%.4f' % min_dist
self.show_res.setText(min_dist)
app_obj.inform.emit('[success] %s' % _("Optimal Tool. Finished successfully."))
except Exception as ee:
proc.done()
log.debug(str(ee))
return
proc.done()
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def reset_fields(self):
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.gerber_object_combo.setCurrentIndex(0)

View File

@ -15,6 +15,8 @@ from flatcamTools.ToolMove import ToolMove
from flatcamTools.ToolNonCopperClear import NonCopperClear from flatcamTools.ToolNonCopperClear import NonCopperClear
from flatcamTools.ToolOptimal import ToolOptimal
from flatcamTools.ToolPaint import ToolPaint from flatcamTools.ToolPaint import ToolPaint
from flatcamTools.ToolPanelize import Panelize from flatcamTools.ToolPanelize import Panelize
from flatcamTools.ToolPcbWizard import PcbWizard from flatcamTools.ToolPcbWizard import PcbWizard