- remade the CutOut Tool
- finished Manual Cutout Tool by adding utility geometry to the cutting geometry - added CTRL + click behavior for adding manual bridge gaps in Cutout Tool - in Tool Cutout added shortcut key 'Escape' to cancel the current adding of bridge gaps
This commit is contained in:
parent
2eccb61660
commit
d775e999fe
@ -457,7 +457,6 @@ class App(QtCore.QObject):
|
||||
"tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
|
||||
"tools_cutoutmargin": self.ui.tools_defaults_form.tools_cutout_group.cutout_margin_entry,
|
||||
"tools_cutoutgapsize": self.ui.tools_defaults_form.tools_cutout_group.cutout_gap_entry,
|
||||
"tools_gaps_rect": self.ui.tools_defaults_form.tools_cutout_group.gaps_radio,
|
||||
"tools_gaps_ff": self.ui.tools_defaults_form.tools_cutout_group.gaps_combo,
|
||||
|
||||
# Paint Area Tool
|
||||
@ -744,7 +743,6 @@ class App(QtCore.QObject):
|
||||
"tools_cutouttooldia": 0.00393701,
|
||||
"tools_cutoutmargin": 0.00393701,
|
||||
"tools_cutoutgapsize": 0.005905512,
|
||||
"tools_gaps_rect": "4",
|
||||
"tools_gaps_ff": "8",
|
||||
|
||||
"tools_painttooldia": 0.07,
|
||||
@ -920,7 +918,6 @@ class App(QtCore.QObject):
|
||||
"tools_cutouttooldia": self.ui.tools_options_form.tools_cutout_group.cutout_tooldia_entry,
|
||||
"tools_cutoutmargin": self.ui.tools_options_form.tools_cutout_group.cutout_margin_entry,
|
||||
"tools_cutoutgapsize": self.ui.tools_options_form.tools_cutout_group.cutout_gap_entry,
|
||||
"tools_gaps_rect": self.ui.tools_options_form.tools_cutout_group.gaps_radio,
|
||||
"tools_gaps_ff": self.ui.tools_options_form.tools_cutout_group.gaps_combo,
|
||||
|
||||
"tools_painttooldia": self.ui.tools_options_form.tools_paint_group.painttooldia_entry,
|
||||
@ -1035,10 +1032,10 @@ class App(QtCore.QObject):
|
||||
"tools_ncctools": "1.0, 0.5",
|
||||
"tools_nccoverlap": 0.4,
|
||||
"tools_nccmargin": 1,
|
||||
|
||||
"tools_cutouttooldia": 0.07,
|
||||
"tools_cutoutmargin": 0.1,
|
||||
"tools_cutoutgapsize": 0.15,
|
||||
"tools_gaps_rect": "4",
|
||||
"tools_gaps_ff": "8",
|
||||
|
||||
"tools_painttooldia": 0.07,
|
||||
|
@ -3849,7 +3849,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
||||
geo = self.active_tool.utility_geometry(data=(x, y))
|
||||
|
||||
if isinstance(geo, DrawToolShape) and geo.geo is not None:
|
||||
|
||||
# Remove any previous utility shape
|
||||
self.tool_shape.clear(update=True)
|
||||
self.draw_utility_geometry(geo=geo)
|
||||
|
@ -2576,7 +2576,7 @@ class GerberPreferencesUI(QtWidgets.QWidget):
|
||||
self.gerber_gen_group = GerberGenPrefGroupUI()
|
||||
self.gerber_gen_group.setFixedWidth(250)
|
||||
self.gerber_opt_group = GerberOptPrefGroupUI()
|
||||
self.gerber_opt_group.setFixedWidth(200)
|
||||
self.gerber_opt_group.setFixedWidth(230)
|
||||
self.gerber_adv_opt_group = GerberAdvOptPrefGroupUI()
|
||||
self.gerber_adv_opt_group.setFixedWidth(200)
|
||||
|
||||
@ -4853,22 +4853,9 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
|
||||
self.cutout_gap_entry = LengthEntry()
|
||||
grid0.addWidget(self.cutout_gap_entry, 2, 1)
|
||||
|
||||
gapslabel = QtWidgets.QLabel('Gaps Rect:')
|
||||
gapslabel.setToolTip(
|
||||
"Where to place the gaps when doing a Rectangular Cutout:\n"
|
||||
" - 2 (T/B) --> Top/Bottom\n"
|
||||
" - 2 (L/R) --> Left/Rigt\n"
|
||||
" - 4 --> on each of all 4 sides."
|
||||
)
|
||||
grid0.addWidget(gapslabel, 3, 0)
|
||||
self.gaps_radio = RadioSet([{'label': '2 (T/B)', 'value': 'tb'},
|
||||
{'label': '2 (L/R)', 'value': 'lr'},
|
||||
{'label': '4', 'value': '4'}])
|
||||
grid0.addWidget(self.gaps_radio, 3, 1)
|
||||
|
||||
gaps_ff_label = QtWidgets.QLabel('Gaps FF:')
|
||||
gaps_ff_label.setToolTip(
|
||||
"Number of gaps used for the FreeForm cutout.\n"
|
||||
gaps_label = QtWidgets.QLabel('Gaps:')
|
||||
gaps_label.setToolTip(
|
||||
"Number of bridge gaps used for the cutout.\n"
|
||||
"There can be maximum 8 bridges/gaps.\n"
|
||||
"The choices are:\n"
|
||||
"- lr - left + right\n"
|
||||
@ -4878,9 +4865,9 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
|
||||
"- 2tb - 2*top + 2*bottom\n"
|
||||
"- 8 - 2*left + 2*right +2*top + 2*bottom"
|
||||
)
|
||||
grid0.addWidget(gaps_ff_label, 4, 0)
|
||||
grid0.addWidget(gaps_label, 3, 0)
|
||||
self.gaps_combo = FCComboBox()
|
||||
grid0.addWidget(self.gaps_combo, 4, 1)
|
||||
grid0.addWidget(self.gaps_combo, 3, 1)
|
||||
|
||||
gaps_items = ['LR', 'TB', '4', '2LR', '2TB', '8']
|
||||
for it in gaps_items:
|
||||
|
@ -14,6 +14,10 @@ CAD program, and create G-Code for Isolation routing.
|
||||
- finished work on object hovering
|
||||
- fixed Excellon object move and all the other transformations
|
||||
- starting to work on Manual Cutout Tool
|
||||
- remade the CutOut Tool
|
||||
- finished Manual Cutout Tool by adding utility geometry to the cutting geometry
|
||||
- added CTRL + click behavior for adding manual bridge gaps in Cutout Tool
|
||||
- in Tool Cutout added shortcut key 'Escape' to cancel the current adding of bridge gaps
|
||||
|
||||
3.03.2019
|
||||
|
||||
|
@ -1,15 +1,20 @@
|
||||
from FlatCAMTool import FlatCAMTool
|
||||
from ObjectCollection import *
|
||||
from FlatCAMApp import *
|
||||
from shapely.geometry import box
|
||||
|
||||
|
||||
class CutOut(FlatCAMTool):
|
||||
|
||||
toolName = "Cutout PCB"
|
||||
gapFinished = pyqtSignal()
|
||||
|
||||
def __init__(self, app):
|
||||
FlatCAMTool.__init__(self, app)
|
||||
|
||||
self.app = app
|
||||
self.canvas = app.plotcanvas
|
||||
|
||||
## Title
|
||||
title_label = QtWidgets.QLabel("%s" % self.toolName)
|
||||
title_label.setStyleSheet("""
|
||||
@ -44,6 +49,7 @@ class CutOut(FlatCAMTool):
|
||||
"What is selected here will dictate the kind\n"
|
||||
"of objects that will populate the 'Object' combobox."
|
||||
)
|
||||
self.type_obj_combo_label.setFixedWidth(60)
|
||||
form_layout.addRow(self.type_obj_combo_label, self.type_obj_combo)
|
||||
|
||||
## Object to be cutout
|
||||
@ -58,14 +64,6 @@ class CutOut(FlatCAMTool):
|
||||
)
|
||||
form_layout.addRow(self.object_label, self.obj_combo)
|
||||
|
||||
## Title2
|
||||
title_param_label = QtWidgets.QLabel("<font size=4><b>A. Automatic Cutout</b></font>")
|
||||
self.layout.addWidget(title_param_label)
|
||||
|
||||
## Form Layout
|
||||
form_layout_2 = QtWidgets.QFormLayout()
|
||||
self.layout.addLayout(form_layout_2)
|
||||
|
||||
# Tool Diameter
|
||||
self.dia = FCEntry()
|
||||
self.dia_label = QtWidgets.QLabel("Tool Dia:")
|
||||
@ -73,7 +71,7 @@ class CutOut(FlatCAMTool):
|
||||
"Diameter of the tool used to cutout\n"
|
||||
"the PCB shape out of the surrounding material."
|
||||
)
|
||||
form_layout_2.addRow(self.dia_label, self.dia)
|
||||
form_layout.addRow(self.dia_label, self.dia)
|
||||
|
||||
# Margin
|
||||
self.margin = FCEntry()
|
||||
@ -83,26 +81,18 @@ class CutOut(FlatCAMTool):
|
||||
"will make the cutout of the PCB further from\n"
|
||||
"the actual PCB border"
|
||||
)
|
||||
form_layout_2.addRow(self.margin_label, self.margin)
|
||||
form_layout.addRow(self.margin_label, self.margin)
|
||||
|
||||
# Gapsize
|
||||
self.gapsize = FCEntry()
|
||||
self.gapsize_label = QtWidgets.QLabel("Gap size:")
|
||||
self.gapsize_label.setToolTip(
|
||||
"The size of the gaps in the cutout\n"
|
||||
"The size of the bridge gaps in the cutout\n"
|
||||
"used to keep the board connected to\n"
|
||||
"the surrounding material (the one \n"
|
||||
"from which the PCB is cutout)."
|
||||
)
|
||||
form_layout_2.addRow(self.gapsize_label, self.gapsize)
|
||||
|
||||
## Title3
|
||||
title_ff_label = QtWidgets.QLabel("<b>FreeForm Cutout</b>")
|
||||
self.layout.addWidget(title_ff_label)
|
||||
|
||||
## Form Layout
|
||||
form_layout_3 = QtWidgets.QFormLayout()
|
||||
self.layout.addLayout(form_layout_3)
|
||||
form_layout.addRow(self.gapsize_label, self.gapsize)
|
||||
|
||||
# How gaps wil be rendered:
|
||||
# lr - left + right
|
||||
@ -112,10 +102,21 @@ class CutOut(FlatCAMTool):
|
||||
# 2tb - 2*top + 2*bottom
|
||||
# 8 - 2*left + 2*right +2*top + 2*bottom
|
||||
|
||||
## Title2
|
||||
title_param_label = QtWidgets.QLabel("<font size=4><b>A. Automatic Bridge Gaps</b></font>")
|
||||
title_param_label.setToolTip(
|
||||
"This section handle creation of automatic bridge gaps."
|
||||
)
|
||||
self.layout.addWidget(title_param_label)
|
||||
|
||||
## Form Layout
|
||||
form_layout_2 = QtWidgets.QFormLayout()
|
||||
self.layout.addLayout(form_layout_2)
|
||||
|
||||
# Gaps
|
||||
gaps_ff_label = QtWidgets.QLabel('Gaps FF: ')
|
||||
gaps_ff_label.setToolTip(
|
||||
"Number of gaps used for the FreeForm cutout.\n"
|
||||
gaps_label = QtWidgets.QLabel('Gaps:')
|
||||
gaps_label.setToolTip(
|
||||
"Number of gaps used for the Automatic cutout.\n"
|
||||
"There can be maximum 8 bridges/gaps.\n"
|
||||
"The choices are:\n"
|
||||
"- lr - left + right\n"
|
||||
@ -125,84 +126,137 @@ class CutOut(FlatCAMTool):
|
||||
"- 2tb - 2*top + 2*bottom\n"
|
||||
"- 8 - 2*left + 2*right +2*top + 2*bottom"
|
||||
)
|
||||
gaps_label.setFixedWidth(60)
|
||||
|
||||
self.gaps = FCComboBox()
|
||||
gaps_items = ['LR', 'TB', '4', '2LR', '2TB', '8']
|
||||
for it in gaps_items:
|
||||
self.gaps.addItem(it)
|
||||
self.gaps.setStyleSheet('background-color: rgb(255,255,255)')
|
||||
form_layout_3.addRow(gaps_ff_label, self.gaps)
|
||||
form_layout_2.addRow(gaps_label, self.gaps)
|
||||
|
||||
## Buttons
|
||||
hlay = QtWidgets.QHBoxLayout()
|
||||
self.layout.addLayout(hlay)
|
||||
|
||||
title_ff_label = QtWidgets.QLabel("<b>FreeForm:</b>")
|
||||
title_ff_label.setToolTip(
|
||||
"The cutout shape can be of ny shape.\n"
|
||||
"Useful when the PCB has a non-rectangular shape."
|
||||
)
|
||||
hlay.addWidget(title_ff_label)
|
||||
|
||||
hlay.addStretch()
|
||||
self.ff_cutout_object_btn = QtWidgets.QPushButton(" FreeForm Cutout Object ")
|
||||
|
||||
self.ff_cutout_object_btn = QtWidgets.QPushButton("Generate Geo")
|
||||
self.ff_cutout_object_btn.setToolTip(
|
||||
"Cutout the selected object.\n"
|
||||
"The cutout shape can be any shape.\n"
|
||||
"Useful when the PCB has a non-rectangular shape.\n"
|
||||
"But if the object to be cutout is of Gerber Type,\n"
|
||||
"it needs to be an outline of the actual board shape."
|
||||
"The cutout shape can be of any shape.\n"
|
||||
"Useful when the PCB has a non-rectangular shape."
|
||||
)
|
||||
hlay.addWidget(self.ff_cutout_object_btn)
|
||||
|
||||
## Title4
|
||||
title_rct_label = QtWidgets.QLabel("<b>Rectangular Cutout</b>")
|
||||
self.layout.addWidget(title_rct_label)
|
||||
|
||||
## Form Layout
|
||||
form_layout_4 = QtWidgets.QFormLayout()
|
||||
self.layout.addLayout(form_layout_4)
|
||||
|
||||
gapslabel_rect = QtWidgets.QLabel('Type of gaps:')
|
||||
gapslabel_rect.setToolTip(
|
||||
"Where to place the gaps:\n"
|
||||
"- one gap Top / one gap Bottom\n"
|
||||
"- one gap Left / one gap Right\n"
|
||||
"- one gap on each of the 4 sides."
|
||||
)
|
||||
self.gaps_rect_radio = RadioSet([{'label': '2(T/B)', 'value': 'TB'},
|
||||
{'label': '2(L/R)', 'value': 'LR'},
|
||||
{'label': '4', 'value': '4'}])
|
||||
form_layout_4.addRow(gapslabel_rect, self.gaps_rect_radio)
|
||||
|
||||
hlay2 = QtWidgets.QHBoxLayout()
|
||||
self.layout.addLayout(hlay2)
|
||||
|
||||
title_rct_label = QtWidgets.QLabel("<b>Rectangular:</b>")
|
||||
title_rct_label.setToolTip(
|
||||
"The resulting cutout shape is\n"
|
||||
"always a rectangle shape and it will be\n"
|
||||
"the bounding box of the Object."
|
||||
)
|
||||
hlay2.addWidget(title_rct_label)
|
||||
|
||||
hlay2.addStretch()
|
||||
self.rect_cutout_object_btn = QtWidgets.QPushButton("Rectangular Cutout Object")
|
||||
self.rect_cutout_object_btn = QtWidgets.QPushButton("Generate Geo")
|
||||
self.rect_cutout_object_btn.setToolTip(
|
||||
"Cutout the selected object.\n"
|
||||
"The resulting cutout shape is\n"
|
||||
"always of a rectangle form and it will be\n"
|
||||
"always a rectangle shape and it will be\n"
|
||||
"the bounding box of the Object."
|
||||
)
|
||||
hlay2.addWidget(self.rect_cutout_object_btn)
|
||||
|
||||
## Title5
|
||||
title_manual_label = QtWidgets.QLabel("<font size=4><b>B. Manual Cutout</b></font>")
|
||||
title_manual_label = QtWidgets.QLabel("<font size=4><b>B. Manual Bridge Gaps</b></font>")
|
||||
title_manual_label.setToolTip(
|
||||
"This section handle creation of manual bridge gaps.\n"
|
||||
"This is done by mouse clicking on the perimeter of the\n"
|
||||
"Geometry object that is used as a cutout object. "
|
||||
)
|
||||
self.layout.addWidget(title_manual_label)
|
||||
|
||||
## Form Layout
|
||||
form_layout_5 = QtWidgets.QFormLayout()
|
||||
self.layout.addLayout(form_layout_4)
|
||||
form_layout_3 = QtWidgets.QFormLayout()
|
||||
self.layout.addLayout(form_layout_3)
|
||||
|
||||
## Manual Geo Object
|
||||
self.man_object_combo = QtWidgets.QComboBox()
|
||||
self.man_object_combo.setModel(self.app.collection)
|
||||
self.man_object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
|
||||
self.man_object_combo.setCurrentIndex(1)
|
||||
|
||||
self.man_object_label = QtWidgets.QLabel("Geo Obj:")
|
||||
self.man_object_label.setToolTip(
|
||||
"Geometry object used to create the manual cutout."
|
||||
)
|
||||
self.man_object_label.setFixedWidth(60)
|
||||
# e_lab_0 = QtWidgets.QLabel('')
|
||||
|
||||
form_layout_3.addRow(self.man_object_label, self.man_object_combo)
|
||||
# form_layout_3.addRow(e_lab_0)
|
||||
|
||||
hlay3 = QtWidgets.QHBoxLayout()
|
||||
self.layout.addLayout(hlay3)
|
||||
|
||||
self.man_geo_label = QtWidgets.QLabel("Manual Geo:")
|
||||
self.man_geo_label.setToolTip(
|
||||
"If the object to be cutout is a Gerber\n"
|
||||
"first create a Geometry that surrounds it,\n"
|
||||
"to be used as the cutout, if one doesn't exist yet.\n"
|
||||
"Select the source Gerber file in the top object combobox."
|
||||
)
|
||||
hlay3.addWidget(self.man_geo_label)
|
||||
|
||||
hlay3.addStretch()
|
||||
self.man_geo_creation_btn = QtWidgets.QPushButton("Generate Geo")
|
||||
self.man_geo_creation_btn.setToolTip(
|
||||
"If the object to be cutout is a Gerber\n"
|
||||
"first create a Geometry that surrounds it,\n"
|
||||
"to be used as the cutout, if one doesn't exist yet.\n"
|
||||
"Select the source Gerber file in the top object combobox."
|
||||
)
|
||||
hlay3.addWidget(self.man_geo_creation_btn)
|
||||
|
||||
hlay4 = QtWidgets.QHBoxLayout()
|
||||
self.layout.addLayout(hlay4)
|
||||
|
||||
self.man_bridge_gaps_label = QtWidgets.QLabel("Manual Add Bridge Gaps:")
|
||||
self.man_bridge_gaps_label.setToolTip(
|
||||
"Use the left mouse button (LMB) click\n"
|
||||
"to create a bridge gap to separate the PCB from\n"
|
||||
"the surrounding material."
|
||||
)
|
||||
hlay4.addWidget(self.man_bridge_gaps_label)
|
||||
|
||||
hlay4.addStretch()
|
||||
self.man_gaps_creation_btn = QtWidgets.QPushButton("Generate Gap")
|
||||
self.man_gaps_creation_btn.setToolTip(
|
||||
"Use the left mouse button (LMB) click\n"
|
||||
"to create a bridge gap to separate the PCB from\n"
|
||||
"the surrounding material.\n"
|
||||
"The LMB click has to be done on the perimeter of\n"
|
||||
"the Geometry object used as a cutout geometry."
|
||||
)
|
||||
hlay4.addWidget(self.man_gaps_creation_btn)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
## Init GUI
|
||||
# self.dia.set_value(1)
|
||||
# self.margin.set_value(0)
|
||||
# self.gapsize.set_value(1)
|
||||
# self.gaps.set_value(4)
|
||||
# self.gaps_rect_radio.set_value("4")
|
||||
self.cutting_gapsize = 0.0
|
||||
self.cutting_dia = 0.0
|
||||
|
||||
## Signals
|
||||
self.ff_cutout_object_btn.clicked.connect(self.on_freeform_cutout)
|
||||
self.rect_cutout_object_btn.clicked.connect(self.on_rectangular_cutout)
|
||||
|
||||
self.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
|
||||
# true if we want to repeat the gap without clicking again on the button
|
||||
self.repeat_gap = False
|
||||
|
||||
def on_type_obj_index_changed(self, index):
|
||||
obj_type = self.type_obj_combo.currentIndex()
|
||||
@ -236,7 +290,16 @@ class CutOut(FlatCAMTool):
|
||||
self.margin.set_value(float(self.app.defaults["tools_cutoutmargin"]))
|
||||
self.gapsize.set_value(float(self.app.defaults["tools_cutoutgapsize"]))
|
||||
self.gaps.set_value(4)
|
||||
self.gaps_rect_radio.set_value(str(self.app.defaults["tools_gaps_rect"]))
|
||||
|
||||
## Signals
|
||||
self.ff_cutout_object_btn.clicked.connect(self.on_freeform_cutout)
|
||||
self.rect_cutout_object_btn.clicked.connect(self.on_rectangular_cutout)
|
||||
|
||||
self.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
|
||||
self.man_geo_creation_btn.clicked.connect(self.on_manual_geo)
|
||||
self.man_gaps_creation_btn.clicked.connect(self.on_manual_gap_click)
|
||||
|
||||
self.gapFinished.connect(self.on_gap_finished)
|
||||
|
||||
def on_freeform_cutout(self):
|
||||
|
||||
@ -268,6 +331,11 @@ class CutOut(FlatCAMTool):
|
||||
"Add it and retry.")
|
||||
return
|
||||
|
||||
|
||||
if 0 in {dia}:
|
||||
self.app.inform.emit("[WARNING_NOTCL]Tool Diameter is zero value. Change it to a positive integer.")
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
|
||||
try:
|
||||
margin = float(self.margin.get_value())
|
||||
except ValueError:
|
||||
@ -296,10 +364,6 @@ class CutOut(FlatCAMTool):
|
||||
self.app.inform.emit("[WARNING_NOTCL] Number of gaps value is missing. Add it and retry.")
|
||||
return
|
||||
|
||||
if 0 in {dia}:
|
||||
self.app.inform.emit("[WARNING_NOTCL]Tool Diameter is zero value. Change it to a positive integer.")
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
|
||||
if gaps not in ['LR', 'TB', '2LR', '2TB', '4', '8']:
|
||||
self.app.inform.emit("[WARNING_NOTCL] Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
|
||||
"Fill in a correct value and retry. ")
|
||||
@ -377,6 +441,11 @@ class CutOut(FlatCAMTool):
|
||||
self.app.should_we_save = True
|
||||
|
||||
def on_rectangular_cutout(self):
|
||||
|
||||
def subtract_rectangle(obj_, x0, y0, x1, y1):
|
||||
pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)]
|
||||
obj_.subtract_polygon(pts)
|
||||
|
||||
name = self.obj_combo.currentText()
|
||||
|
||||
# Get source object.
|
||||
@ -400,6 +469,10 @@ class CutOut(FlatCAMTool):
|
||||
"Add it and retry.")
|
||||
return
|
||||
|
||||
if 0 in {dia}:
|
||||
self.app.inform.emit("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer.")
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
|
||||
try:
|
||||
margin = float(self.margin.get_value())
|
||||
except ValueError:
|
||||
@ -423,14 +496,15 @@ class CutOut(FlatCAMTool):
|
||||
return
|
||||
|
||||
try:
|
||||
gaps = self.gaps_rect_radio.get_value()
|
||||
gaps = self.gaps.get_value()
|
||||
except TypeError:
|
||||
self.app.inform.emit("[WARNING_NOTCL] Number of gaps value is missing. Add it and retry.")
|
||||
return
|
||||
|
||||
if 0 in {dia}:
|
||||
self.app.inform.emit("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer.")
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
if gaps not in ['LR', 'TB', '2LR', '2TB', '4', '8']:
|
||||
self.app.inform.emit("[WARNING_NOTCL] Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
|
||||
"Fill in a correct value and retry. ")
|
||||
return
|
||||
|
||||
if cutout_obj.multigeo is True:
|
||||
self.app.inform.emit("[ERROR]Cutout operation cannot be done on a multi-geo Geometry.\n"
|
||||
@ -438,45 +512,279 @@ class CutOut(FlatCAMTool):
|
||||
"and after that perform Cutout.")
|
||||
return
|
||||
|
||||
# Get min and max data for each object as we just cut rectangles across X or Y
|
||||
xmin, ymin, xmax, ymax = cutout_obj.bounds()
|
||||
geo = box(xmin, ymin, xmax, ymax)
|
||||
|
||||
px = 0.5 * (xmin + xmax) + margin
|
||||
py = 0.5 * (ymin + ymax) + margin
|
||||
lenghtx = (xmax - xmin) + (margin * 2)
|
||||
lenghty = (ymax - ymin) + (margin * 2)
|
||||
|
||||
gapsize = gapsize / 2 + (dia / 2)
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
real_margin = margin + (dia / 2)
|
||||
real_gap_size = gapsize + dia
|
||||
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
|
||||
|
||||
minx, miny, maxx, maxy = cutout_obj.bounds()
|
||||
minx -= real_margin
|
||||
maxx += real_margin
|
||||
miny -= real_margin
|
||||
maxy += real_margin
|
||||
midx = 0.5 * (minx + maxx)
|
||||
midy = 0.5 * (miny + maxy)
|
||||
hgap = 0.5 * real_gap_size
|
||||
pts = [[midx - hgap, maxy],
|
||||
[minx, maxy],
|
||||
[minx, midy + hgap],
|
||||
[minx, midy - hgap],
|
||||
[minx, miny],
|
||||
[midx - hgap, miny],
|
||||
[midx + hgap, miny],
|
||||
[maxx, miny],
|
||||
[maxx, midy - hgap],
|
||||
[maxx, midy + hgap],
|
||||
[maxx, maxy],
|
||||
[midx + hgap, maxy]]
|
||||
cases = {"TB": [[pts[0], pts[1], pts[4], pts[5]],
|
||||
[pts[6], pts[7], pts[10], pts[11]]],
|
||||
"LR": [[pts[9], pts[10], pts[1], pts[2]],
|
||||
[pts[3], pts[4], pts[7], pts[8]]],
|
||||
"4": [[pts[0], pts[1], pts[2]],
|
||||
[pts[3], pts[4], pts[5]],
|
||||
[pts[6], pts[7], pts[8]],
|
||||
[pts[9], pts[10], pts[11]]]}
|
||||
cuts = cases[gaps]
|
||||
geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts])
|
||||
outname = cutout_obj.options["name"] + "_cutout"
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
|
||||
# TODO: Check for None
|
||||
self.app.new_object("geometry", name + "_cutout", geo_init)
|
||||
self.app.inform.emit("[success] Rectangular CutOut operation finished.")
|
||||
cutout_obj = self.app.collection.get_by_name(outname)
|
||||
|
||||
if gaps == '8' or gaps == '2LR':
|
||||
subtract_rectangle(cutout_obj,
|
||||
xmin - gapsize, # botleft_x
|
||||
py - gapsize + lenghty / 4, # botleft_y
|
||||
xmax + gapsize, # topright_x
|
||||
py + gapsize + lenghty / 4) # topright_y
|
||||
subtract_rectangle(cutout_obj,
|
||||
xmin - gapsize,
|
||||
py - gapsize - lenghty / 4,
|
||||
xmax + gapsize,
|
||||
py + gapsize - lenghty / 4)
|
||||
|
||||
if gaps == '8' or gaps == '2TB':
|
||||
subtract_rectangle(cutout_obj,
|
||||
px - gapsize + lenghtx / 4,
|
||||
ymin - gapsize,
|
||||
px + gapsize + lenghtx / 4,
|
||||
ymax + gapsize)
|
||||
subtract_rectangle(cutout_obj,
|
||||
px - gapsize - lenghtx / 4,
|
||||
ymin - gapsize,
|
||||
px + gapsize - lenghtx / 4,
|
||||
ymax + gapsize)
|
||||
|
||||
if gaps == '4' or gaps == 'LR':
|
||||
subtract_rectangle(cutout_obj,
|
||||
xmin - gapsize,
|
||||
py - gapsize,
|
||||
xmax + gapsize,
|
||||
py + gapsize)
|
||||
|
||||
if gaps == '4' or gaps == 'TB':
|
||||
subtract_rectangle(cutout_obj,
|
||||
px - gapsize,
|
||||
ymin - gapsize,
|
||||
px + gapsize,
|
||||
ymax + gapsize)
|
||||
|
||||
cutout_obj.plot()
|
||||
self.app.inform.emit("[success] Any form CutOut operation finished.")
|
||||
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
|
||||
self.app.should_we_save = True
|
||||
|
||||
def on_manual_gap_click(self):
|
||||
self.app.inform.emit("Click on the selected geometry object perimeter to create a bridge gap ...")
|
||||
self.app.geo_editor.tool_shape.enabled = True
|
||||
|
||||
try:
|
||||
self.cutting_dia = float(self.dia.get_value())
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
self.cutting_dia = float(self.dia.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
|
||||
"Add it and retry.")
|
||||
return
|
||||
|
||||
if 0 in {self.cutting_dia}:
|
||||
self.app.inform.emit("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer.")
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
|
||||
try:
|
||||
self.cutting_gapsize = float(self.gapsize.get_value())
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
self.cutting_gapsize = float(self.gapsize.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit("[WARNING_NOTCL] Gap size value is missing or wrong format. "
|
||||
"Add it and retry.")
|
||||
return
|
||||
|
||||
self.app.plotcanvas.vis_disconnect('key_press', self.app.ui.keyPressEvent)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
|
||||
self.app.plotcanvas.vis_connect('key_press', self.on_key_press)
|
||||
self.app.plotcanvas.vis_connect('mouse_move', self.on_mouse_move)
|
||||
self.app.plotcanvas.vis_connect('mouse_release', self.doit)
|
||||
|
||||
# To be called after clicking on the plot.
|
||||
def doit(self, event):
|
||||
# do paint single only for left mouse clicks
|
||||
if event.button == 1:
|
||||
self.app.inform.emit("Making manual bridge gap...")
|
||||
pos = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
|
||||
self.on_manual_cutout(click_pos=pos)
|
||||
|
||||
self.app.plotcanvas.vis_disconnect('key_press', self.on_key_press)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_move', self.on_mouse_move)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_release', self.doit)
|
||||
self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent)
|
||||
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_connect('mouse_move', self.app.on_mouse_move_over_plot)
|
||||
|
||||
self.app.geo_editor.tool_shape.clear(update=True)
|
||||
self.app.geo_editor.tool_shape.enabled = False
|
||||
self.gapFinished.emit()
|
||||
|
||||
def on_manual_cutout(self, click_pos):
|
||||
name = self.man_object_combo.currentText()
|
||||
|
||||
# Get source object.
|
||||
try:
|
||||
cutout_obj = self.app.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.app.inform.emit("[ERROR_NOTCL]Could not retrieve Geoemtry object: %s" % name)
|
||||
return "Could not retrieve object: %s" % name
|
||||
|
||||
if cutout_obj is None:
|
||||
self.app.inform.emit("[ERROR_NOTCL]Geometry object for manual cutout not found: %s" % cutout_obj)
|
||||
return
|
||||
|
||||
# use the snapped position as reference
|
||||
snapped_pos = self.app.geo_editor.snap(click_pos[0], click_pos[1])
|
||||
|
||||
cut_poly = self.cutting_geo(pos=(snapped_pos[0], snapped_pos[1]))
|
||||
cutout_obj.subtract_polygon(cut_poly)
|
||||
|
||||
cutout_obj.plot()
|
||||
self.app.inform.emit("[success] Added manual Bridge Gap.")
|
||||
|
||||
self.app.should_we_save = True
|
||||
|
||||
def on_gap_finished(self):
|
||||
# if CTRL key modifier is pressed then repeat the bridge gap cut
|
||||
key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
||||
if key_modifier == Qt.ControlModifier:
|
||||
self.on_manual_gap_click()
|
||||
|
||||
def on_manual_geo(self):
|
||||
name = self.obj_combo.currentText()
|
||||
|
||||
# Get source object.
|
||||
try:
|
||||
cutout_obj = self.app.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.app.inform.emit("[ERROR_NOTCL]Could not retrieve Gerber object: %s" % name)
|
||||
return "Could not retrieve object: %s" % name
|
||||
|
||||
if cutout_obj is None:
|
||||
self.app.inform.emit("[ERROR_NOTCL]There is no Gerber object selected for Cutout.\n"
|
||||
"Select one and try again.")
|
||||
return
|
||||
|
||||
if not isinstance(cutout_obj, FlatCAMGerber):
|
||||
self.app.inform.emit("[ERROR_NOTCL]The selected object has to be of Gerber type.\n"
|
||||
"Select a Gerber file and try again.")
|
||||
return
|
||||
|
||||
try:
|
||||
dia = float(self.dia.get_value())
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
dia = float(self.dia.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
|
||||
"Add it and retry.")
|
||||
return
|
||||
|
||||
if 0 in {dia}:
|
||||
self.app.inform.emit("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer.")
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
|
||||
try:
|
||||
margin = float(self.margin.get_value())
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
margin = float(self.margin.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit("[WARNING_NOTCL] Margin value is missing or wrong format. "
|
||||
"Add it and retry.")
|
||||
return
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
geo = cutout_obj.solid_geometry.convex_hull
|
||||
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
|
||||
|
||||
outname = cutout_obj.options["name"] + "_cutout"
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
|
||||
def cutting_geo(self, pos):
|
||||
self.cutting_gapsize = self.cutting_gapsize / 2 + (self.cutting_dia / 2)
|
||||
offset = self.cutting_gapsize / 2
|
||||
|
||||
# cutting area definition
|
||||
orig_x = pos[0]
|
||||
orig_y = pos[1]
|
||||
xmin = orig_x - offset
|
||||
ymin = orig_y - offset
|
||||
xmax = orig_x + offset
|
||||
ymax = orig_y + offset
|
||||
|
||||
cut_poly = box(xmin, ymin, xmax, ymax)
|
||||
return cut_poly
|
||||
|
||||
def on_mouse_move(self, event):
|
||||
|
||||
self.app.on_mouse_move_over_plot(event=event)
|
||||
|
||||
pos = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
event.xdata, event.ydata = pos[0], pos[1]
|
||||
|
||||
try:
|
||||
x = float(event.xdata)
|
||||
y = float(event.ydata)
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
snap_x, snap_y = self.app.geo_editor.snap(x, y)
|
||||
|
||||
geo = self.cutting_geo(pos=(snap_x, snap_y))
|
||||
|
||||
# Remove any previous utility shape
|
||||
self.app.geo_editor.tool_shape.clear(update=True)
|
||||
self.draw_utility_geometry(geo=geo)
|
||||
|
||||
def draw_utility_geometry(self, geo):
|
||||
self.app.geo_editor.tool_shape.add(
|
||||
shape=geo,
|
||||
color=(self.app.defaults["global_draw_color"] + '80'),
|
||||
update=False,
|
||||
layer=0,
|
||||
tolerance=None)
|
||||
self.app.geo_editor.tool_shape.redraw()
|
||||
|
||||
def on_key_press(self, event):
|
||||
# events out of the self.app.collection view (it's about Project Tab) are of type int
|
||||
if type(event) is int:
|
||||
key = event
|
||||
# events from the GUI are of type QKeyEvent
|
||||
elif type(event) == QtGui.QKeyEvent:
|
||||
key = event.key()
|
||||
# events from Vispy are of type KeyEvent
|
||||
else:
|
||||
key = event.key
|
||||
|
||||
# Escape = Deselect All
|
||||
if key == QtCore.Qt.Key_Escape or key == 'Escape':
|
||||
self.app.plotcanvas.vis_disconnect('key_press', self.on_key_press)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_move', self.on_mouse_move)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_release', self.doit)
|
||||
self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent)
|
||||
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_connect('mouse_move', self.app.on_mouse_move_over_plot)
|
||||
|
||||
# Remove any previous utility shape
|
||||
self.app.geo_editor.tool_shape.clear(update=True)
|
||||
self.app.geo_editor.tool_shape.enabled = False
|
||||
|
||||
def reset_fields(self):
|
||||
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
||||
|
Loading…
Reference in New Issue
Block a user