- updated the Extract Tool - new functionality added: Extract Cutout Gerber from a given Gerber object; added parameters in Preferences

This commit is contained in:
Marius Stanciu
2020-11-10 15:55:32 +02:00
committed by Marius
parent 5d2d810898
commit 4262eef3e5
5 changed files with 183 additions and 8 deletions

View File

@@ -15,6 +15,7 @@ CHANGELOG for FlatCAM beta
- in Cutout Tool added the UI for a new feature: Cut by Drilling - in Cutout Tool added the UI for a new feature: Cut by Drilling
- fixed a bug in Extract Tool, when extracting drills some of the drills were lost; added a new UI control to select/deselect all apertures - fixed a bug in Extract Tool, when extracting drills some of the drills were lost; added a new UI control to select/deselect all apertures
- updated the Extract Tool - Extract Soldermask functionality, such that the selection of apertures will control the final SolderMask Gerber content - updated the Extract Tool - Extract Soldermask functionality, such that the selection of apertures will control the final SolderMask Gerber content
- updated the Extract Tool - new functionality added: Extract Cutout Gerber from a given Gerber object; added parameters in Preferences
9.11.2020 9.11.2020

View File

@@ -646,6 +646,8 @@ class PreferencesUIManager:
"tools_extract_rectangular": self.ui.tools2_defaults_form.tools2_edrills_group.rectangular_cb, "tools_extract_rectangular": self.ui.tools2_defaults_form.tools2_edrills_group.rectangular_cb,
"tools_extract_others": self.ui.tools2_defaults_form.tools2_edrills_group.other_cb, "tools_extract_others": self.ui.tools2_defaults_form.tools2_edrills_group.other_cb,
"tools_extract_sm_clearance": self.ui.tools2_defaults_form.tools2_edrills_group.clearance_entry, "tools_extract_sm_clearance": self.ui.tools2_defaults_form.tools2_edrills_group.clearance_entry,
"tools_extract_cut_margin": self.ui.tools2_defaults_form.tools2_edrills_group.margin_cut_entry,
"tools_extract_cut_thickness": self.ui.tools2_defaults_form.tools2_edrills_group.thick_cut_entry,
# Punch Gerber Tool # Punch Gerber Tool
"tools_punch_hole_type": self.ui.tools2_defaults_form.tools2_punch_group.hole_size_radio, "tools_punch_hole_type": self.ui.tools2_defaults_form.tools2_punch_group.hole_size_radio,

View File

@@ -247,5 +247,39 @@ class Tools2EDrillsPrefGroupUI(OptionsGroupUI):
grid_lay.addWidget(self.clearance_label, 24, 0) grid_lay.addWidget(self.clearance_label, 24, 0)
grid_lay.addWidget(self.clearance_entry, 24, 1) grid_lay.addWidget(self.clearance_entry, 24, 1)
# EXTRACT CUTOUT
self.extract_cut_label = FCLabel('<b>%s</b>' % _("Extract Cutout"))
self.extract_cut_label.setToolTip(
_("Extract a cutout from a given Gerber file."))
grid_lay.addWidget(self.extract_cut_label, 26, 0, 1, 2)
# Margin Cutout
self.margin_cut_label = FCLabel('%s:' % _("Margin"))
self.margin_cut_label.setToolTip(
_("Margin over bounds. A positive value here\n"
"will make the cutout of the PCB further from\n"
"the actual PCB border")
)
self.margin_cut_entry = FCDoubleSpinner()
self.margin_cut_entry.set_range(-10000.0000, 10000.0000)
self.margin_cut_entry.set_precision(self.decimals)
self.margin_cut_entry.setSingleStep(0.1)
grid_lay.addWidget(self.margin_cut_label, 28, 0)
grid_lay.addWidget(self.margin_cut_entry, 28, 1)
# Thickness Cutout
self.thick_cut_label = FCLabel('%s:' % _("Thickness"))
self.thick_cut_label.setToolTip(
_("The thickness of the line that makes the cutout geometry.")
)
self.thick_cut_entry = FCDoubleSpinner()
self.thick_cut_entry.set_range(0.0000, 10000.0000)
self.thick_cut_entry.set_precision(self.decimals)
self.thick_cut_entry.setSingleStep(0.1)
grid_lay.addWidget(self.thick_cut_label, 30, 0)
grid_lay.addWidget(self.thick_cut_entry, 30, 1)
self.layout.addStretch() self.layout.addStretch()

View File

@@ -10,7 +10,8 @@ from PyQt5 import QtWidgets, QtCore, QtGui
from appTool import AppTool from appTool import AppTool
from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox, FCLabel from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox, FCLabel
from shapely.geometry import Point from shapely.geometry import Point, MultiPolygon, Polygon, box
from shapely.ops import unary_union
from copy import deepcopy from copy import deepcopy
@@ -40,9 +41,6 @@ class ToolExtract(AppTool):
# ## Signals # ## Signals
self.ui.hole_size_radio.activated_custom.connect(self.on_hole_size_toggle) self.ui.hole_size_radio.activated_custom.connect(self.on_hole_size_toggle)
self.ui.e_drills_button.clicked.connect(self.on_extract_drills_click)
self.ui.e_sm_button.clicked.connect(self.on_extract_soldermask_click)
self.ui.reset_button.clicked.connect(self.set_tool_ui)
self.ui.circular_cb.stateChanged.connect( self.ui.circular_cb.stateChanged.connect(
lambda state: lambda state:
@@ -72,6 +70,11 @@ class ToolExtract(AppTool):
self.ui.all_cb.stateChanged.connect(self.on_select_all) self.ui.all_cb.stateChanged.connect(self.on_select_all)
self.ui.e_drills_button.clicked.connect(self.on_extract_drills_click)
self.ui.e_sm_button.clicked.connect(self.on_extract_soldermask_click)
self.ui.e_cut_button.clicked.connect(self.on_extract_cutout_click)
self.ui.reset_button.clicked.connect(self.set_tool_ui)
def install(self, icon=None, separator=None, **kwargs): def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='Alt+I', **kwargs) AppTool.install(self, icon, separator, shortcut='Alt+I', **kwargs)
@@ -123,8 +126,13 @@ class ToolExtract(AppTool):
self.ui.factor_entry.set_value(float(self.app.defaults["tools_extract_hole_prop_factor"])) self.ui.factor_entry.set_value(float(self.app.defaults["tools_extract_hole_prop_factor"]))
# Extract Soldermask
self.ui.clearance_entry.set_value(float(self.app.defaults["tools_extract_sm_clearance"])) self.ui.clearance_entry.set_value(float(self.app.defaults["tools_extract_sm_clearance"]))
# Extract Cutout
self.ui.margin_cut_entry.set_value(float(self.app.defaults["tools_extract_cut_margin"]))
self.ui.thick_cut_entry.set_value(float(self.app.defaults["tools_extract_cut_thickness"]))
def on_select_all(self, state): def on_select_all(self, state):
if state: if state:
@@ -435,7 +443,7 @@ class ToolExtract(AppTool):
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ...")) self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
return return
outname = '%s_sm' % obj.options['name'].rpartition('.')[0] outname = '%s_esm' % obj.options['name'].rpartition('.')[0]
new_apertures = deepcopy(obj.apertures) new_apertures = deepcopy(obj.apertures)
new_solid_geometry = [] new_solid_geometry = []
@@ -511,6 +519,81 @@ class ToolExtract(AppTool):
log.error("Error on Extracted Soldermask Gerber object creation: %s" % str(e)) log.error("Error on Extracted Soldermask Gerber object creation: %s" % str(e))
return return
def on_extract_cutout_click(self):
margin = self.ui.margin_cut_entry.get_value()
thickness = self.ui.thick_cut_entry.get_value()
buff_radius = thickness / 2.0
selection_index = self.ui.gerber_object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.ui.gerber_object_combo.rootModelIndex())
try:
obj = model_index.internalPointer().obj
except Exception:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
return
outname = '%s_ecut' % obj.options['name'].rpartition('.')[0]
cut_solid_geometry = obj.solid_geometry
if isinstance(obj.solid_geometry, list):
cut_solid_geometry = MultiPolygon(obj.solid_geometry)
if isinstance(cut_solid_geometry, (MultiPolygon, Polygon)):
x0, y0, x1, y1 = cut_solid_geometry.bounds
object_geo = box(x0, y0, x1, y1)
else:
self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Failed."), _("No cutout extracted.")))
return
try:
geo_buf = object_geo.buffer(margin)
new_geo_follow = geo_buf.exterior
new_geo_solid = new_geo_follow.buffer(buff_radius)
except Exception as e:
log.debug("ToolExtrct.on_extrct_cutout_click() -> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Failed."), _("No cutout extracted.")))
return
if not new_geo_solid.is_valid or new_geo_solid.is_empty:
self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Failed."), _("No cutout extracted.")))
return
new_apertures = {
'10': {
'type': 'C',
'size': thickness,
'geometry': [
{
'solid': deepcopy(new_geo_solid),
'follow': deepcopy(new_geo_follow)
}
]
}
}
def obj_init(new_obj, app_obj):
new_obj.multitool = False
new_obj.multigeo = False
new_obj.follow = False
new_obj.apertures = deepcopy(new_apertures)
new_obj.solid_geometry = [deepcopy(new_geo_solid)]
new_obj.follow_geometry = [deepcopy(new_geo_follow)]
try:
new_obj.source_file = app_obj.f_handlers.export_gerber(obj_name=outname, filename=None,
local_use=new_obj, use_thread=False)
except (AttributeError, TypeError):
pass
with self.app.proc_container.new(_("Working ...")):
try:
self.app.app_obj.new_object("gerber", outname, obj_init)
except Exception as e:
log.error("Error on Extracted Cutout Gerber object creation: %s" % str(e))
return
def on_hole_size_toggle(self, val): def on_hole_size_toggle(self, val):
if val == "fixed": if val == "fixed":
self.ui.fixed_label.setVisible(True) self.ui.fixed_label.setVisible(True)
@@ -663,14 +746,14 @@ class ExtractUI:
grid1.setColumnStretch(0, 0) grid1.setColumnStretch(0, 0)
grid1.setColumnStretch(1, 1) grid1.setColumnStretch(1, 1)
grid1.addWidget(FCLabel(""), 0, 0, 1, 2) # grid1.addWidget(FCLabel(""), 0, 0, 1, 2)
self.extract_drills_label = FCLabel('<b>%s</b>' % _("Extract Drills").upper()) self.extract_drills_label = FCLabel('<b>%s</b>' % _("Extract Drills").upper())
self.extract_drills_label.setToolTip( self.extract_drills_label.setToolTip(
_("Extract an Excellon object from the Gerber pads.")) _("Extract an Excellon object from the Gerber pads."))
grid1.addWidget(self.extract_drills_label, 1, 0, 1, 2) grid1.addWidget(self.extract_drills_label, 1, 0, 1, 2)
self.method_label = FCLabel('<b>%s</b>' % _("Method")) self.method_label = FCLabel('<b>%s:</b>' % _("Method"))
self.method_label.setToolTip( self.method_label.setToolTip(
_("The method for processing pads. Can be:\n" _("The method for processing pads. Can be:\n"
"- Fixed Diameter -> all holes will have a set size\n" "- Fixed Diameter -> all holes will have a set size\n"
@@ -850,7 +933,7 @@ class ExtractUI:
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid3.addWidget(separator_line, 14, 0, 1, 2) grid3.addWidget(separator_line, 14, 0, 1, 2)
grid3.addWidget(FCLabel(""), 16, 0, 1, 2) # grid3.addWidget(FCLabel(""), 16, 0, 1, 2)
# EXTRACT SOLDERMASK # EXTRACT SOLDERMASK
self.extract_sm_label = FCLabel('<b>%s</b>' % _("Extract Soldermask").upper()) self.extract_sm_label = FCLabel('<b>%s</b>' % _("Extract Soldermask").upper())
@@ -891,6 +974,59 @@ class ExtractUI:
""") """)
grid3.addWidget(self.e_sm_button, 24, 0, 1, 2) grid3.addWidget(self.e_sm_button, 24, 0, 1, 2)
# EXTRACT CUTOUT
self.extract_sm_label = FCLabel('<b>%s</b>' % _("Extract Cutout").upper())
self.extract_sm_label.setToolTip(
_("Extract a cutout from a given Gerber file."))
grid3.addWidget(self.extract_sm_label, 26, 0, 1, 2)
# Margin
self.margin_cut_label = FCLabel('%s:' % _("Margin"))
self.margin_cut_label.setToolTip(
_("Margin over bounds. A positive value here\n"
"will make the cutout of the PCB further from\n"
"the actual PCB border")
)
self.margin_cut_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.margin_cut_entry.set_range(-10000.0000, 10000.0000)
self.margin_cut_entry.set_precision(self.decimals)
self.margin_cut_entry.setSingleStep(0.1)
grid3.addWidget(self.margin_cut_label, 28, 0)
grid3.addWidget(self.margin_cut_entry, 28, 1)
# Thickness
self.thick_cut_label = FCLabel('%s:' % _("Thickness"))
self.thick_cut_label.setToolTip(
_("The thickness of the line that makes the cutout geometry.")
)
self.thick_cut_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.thick_cut_entry.set_range(0.0000, 10000.0000)
self.thick_cut_entry.set_precision(self.decimals)
self.thick_cut_entry.setSingleStep(0.1)
grid3.addWidget(self.thick_cut_label, 30, 0)
grid3.addWidget(self.thick_cut_entry, 30, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid3.addWidget(separator_line, 32, 0, 1, 2)
# Extract cutout from Gerber apertures flashes (pads)
self.e_cut_button = QtWidgets.QPushButton(_("Extract Cutout"))
self.e_cut_button.setIcon(QtGui.QIcon(self.app.resource_location + '/extract32.png'))
self.e_cut_button.setToolTip(
_("Extract soldermask from a given Gerber file.")
)
self.e_cut_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
grid3.addWidget(self.e_cut_button, 34, 0, 1, 2)
self.layout.addStretch() self.layout.addStretch()
# ## Reset Tool # ## Reset Tool

View File

@@ -713,6 +713,8 @@ class FlatCAMDefaults:
"tools_extract_rectangular": False, "tools_extract_rectangular": False,
"tools_extract_others": False, "tools_extract_others": False,
"tools_extract_sm_clearance": 0.1, "tools_extract_sm_clearance": 0.1,
"tools_extract_cut_margin": 0.1,
"tools_extract_cut_thickness": 0.1,
# Punch Gerber Tool # Punch Gerber Tool
"tools_punch_hole_type": 'exc', "tools_punch_hole_type": 'exc',