- 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:
Marius Stanciu 2019-03-04 16:09:41 +02:00
parent 2eccb61660
commit d775e999fe
5 changed files with 428 additions and 133 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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()))