- fixed open handlers
- fixed issue in NCC Tool where the tool table context menu could be installed multiple times - added new ability to create simple isolation's in the NCC Tool
This commit is contained in:
parent
bb9c35a527
commit
1295a94af1
|
@ -6764,7 +6764,7 @@ class App(QtCore.QObject):
|
||||||
self.report_usage("obj_move()")
|
self.report_usage("obj_move()")
|
||||||
self.move_tool.run(toggle=False)
|
self.move_tool.run(toggle=False)
|
||||||
|
|
||||||
def on_fileopengerber(self, name=None):
|
def on_fileopengerber(self, checked=None, name=None):
|
||||||
"""
|
"""
|
||||||
File menu callback for opening a Gerber.
|
File menu callback for opening a Gerber.
|
||||||
|
|
||||||
|
@ -6802,10 +6802,9 @@ class App(QtCore.QObject):
|
||||||
else:
|
else:
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if filename != '':
|
if filename != '':
|
||||||
self.worker_task.emit({'fcn': self.open_gerber,
|
self.worker_task.emit({'fcn': self.open_gerber, 'params': [filename]})
|
||||||
'params': [filename]})
|
|
||||||
|
|
||||||
def on_fileopenexcellon(self, name=None):
|
def on_fileopenexcellon(self, checked=None, name=None):
|
||||||
"""
|
"""
|
||||||
File menu callback for opening an Excellon file.
|
File menu callback for opening an Excellon file.
|
||||||
|
|
||||||
|
@ -6833,10 +6832,9 @@ class App(QtCore.QObject):
|
||||||
else:
|
else:
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if filename != '':
|
if filename != '':
|
||||||
self.worker_task.emit({'fcn': self.open_excellon,
|
self.worker_task.emit({'fcn': self.open_excellon, 'params': [filename]})
|
||||||
'params': [filename]})
|
|
||||||
|
|
||||||
def on_fileopengcode(self, name=None):
|
def on_fileopengcode(self, checked=None, name=None):
|
||||||
"""
|
"""
|
||||||
File menu call back for opening gcode.
|
File menu call back for opening gcode.
|
||||||
|
|
||||||
|
@ -6868,10 +6866,9 @@ class App(QtCore.QObject):
|
||||||
else:
|
else:
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if filename != '':
|
if filename != '':
|
||||||
self.worker_task.emit({'fcn': self.open_gcode,
|
self.worker_task.emit({'fcn': self.open_gcode, 'params': [filename]})
|
||||||
'params': [filename]})
|
|
||||||
|
|
||||||
def on_file_openproject(self):
|
def on_file_openproject(self, checked=None):
|
||||||
"""
|
"""
|
||||||
File menu callback for opening a project.
|
File menu callback for opening a project.
|
||||||
|
|
||||||
|
@ -6901,7 +6898,7 @@ class App(QtCore.QObject):
|
||||||
# thread safe. The new_project()
|
# thread safe. The new_project()
|
||||||
self.open_project(filename)
|
self.open_project(filename)
|
||||||
|
|
||||||
def on_file_openconfig(self):
|
def on_file_openconfig(self, checked=None):
|
||||||
"""
|
"""
|
||||||
File menu callback for opening a config file.
|
File menu callback for opening a config file.
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,12 @@ CAD program, and create G-Code for Isolation routing.
|
||||||
|
|
||||||
=================================================
|
=================================================
|
||||||
|
|
||||||
|
1.09.2019
|
||||||
|
|
||||||
|
- fixed open handlers
|
||||||
|
- fixed issue in NCC Tool where the tool table context menu could be installed multiple times
|
||||||
|
- added new ability to create simple isolation's in the NCC Tool
|
||||||
|
|
||||||
27.08.2019
|
27.08.2019
|
||||||
|
|
||||||
- made FlatCAM so that whenever an associated file is double clicked, if there is an opened instance of FlatCAM, the file will be opened in the first instance without launching a new instance of FlatCAM. If FlatCAM is launched again it will spawn a new process (hopefully it will work when freezed).
|
- made FlatCAM so that whenever an associated file is double clicked, if there is an opened instance of FlatCAM, the file will be opened in the first instance without launching a new instance of FlatCAM. If FlatCAM is launched again it will spawn a new process (hopefully it will work when freezed).
|
||||||
|
|
|
@ -912,7 +912,7 @@ class GeometryObjectUI(ObjectUI):
|
||||||
self.geo_tools_table.horizontalHeaderItem(3).setToolTip(
|
self.geo_tools_table.horizontalHeaderItem(3).setToolTip(
|
||||||
_(
|
_(
|
||||||
"The (Operation) Type has only informative value. Usually the UI form values \n"
|
"The (Operation) Type has only informative value. Usually the UI form values \n"
|
||||||
"are choosed based on the operation type and this will serve as a reminder.\n"
|
"are choose based on the operation type and this will serve as a reminder.\n"
|
||||||
"Can be 'Roughing', 'Finishing' or 'Isolation'.\n"
|
"Can be 'Roughing', 'Finishing' or 'Isolation'.\n"
|
||||||
"For Roughing we may choose a lower Feedrate and multiDepth cut.\n"
|
"For Roughing we may choose a lower Feedrate and multiDepth cut.\n"
|
||||||
"For Finishing we may choose a higher Feedrate, without multiDepth.\n"
|
"For Finishing we may choose a higher Feedrate, without multiDepth.\n"
|
||||||
|
|
|
@ -103,8 +103,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
self.tools_table = FCTable()
|
self.tools_table = FCTable()
|
||||||
self.tools_box.addWidget(self.tools_table)
|
self.tools_box.addWidget(self.tools_table)
|
||||||
|
|
||||||
self.tools_table.setColumnCount(4)
|
self.tools_table.setColumnCount(5)
|
||||||
self.tools_table.setHorizontalHeaderLabels(['#', _('Diameter'), _('TT'), ''])
|
self.tools_table.setHorizontalHeaderLabels(['#', _('Diameter'), _('TT'), '', _("Operation")])
|
||||||
self.tools_table.setColumnHidden(3, True)
|
self.tools_table.setColumnHidden(3, True)
|
||||||
self.tools_table.setSortingEnabled(False)
|
self.tools_table.setSortingEnabled(False)
|
||||||
# self.tools_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
# self.tools_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||||
|
@ -118,22 +118,28 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
"this function will not be able to create painting geometry.")
|
"this function will not be able to create painting geometry.")
|
||||||
)
|
)
|
||||||
self.tools_table.horizontalHeaderItem(1).setToolTip(
|
self.tools_table.horizontalHeaderItem(1).setToolTip(
|
||||||
_("Tool Diameter. It's value (in current FlatCAM units) \n"
|
_("Tool Diameter. It's value (in current FlatCAM units)\n"
|
||||||
"is the cut width into the material."))
|
"is the cut width into the material."))
|
||||||
|
|
||||||
self.tools_table.horizontalHeaderItem(2).setToolTip(
|
self.tools_table.horizontalHeaderItem(2).setToolTip(
|
||||||
_("The Tool Type (TT) can be:<BR>"
|
_("The Tool Type (TT) can be:\n"
|
||||||
"- <B>Circular</B> with 1 ... 4 teeth -> it is informative only. Being circular, <BR>"
|
"- Circular with 1 ... 4 teeth -> it is informative only. Being circular,\n"
|
||||||
"the cut width in material is exactly the tool diameter.<BR>"
|
"the cut width in material is exactly the tool diameter.\n"
|
||||||
"- <B>Ball</B> -> informative only and make reference to the Ball type endmill.<BR>"
|
"- Ball -> informative only and make reference to the Ball type endmill.\n"
|
||||||
"- <B>V-Shape</B> -> it will disable de Z-Cut parameter in the resulting geometry UI form "
|
"- V-Shape -> it will disable de Z-Cut parameter in the resulting geometry UI form\n"
|
||||||
"and enable two additional UI form fields in the resulting geometry: V-Tip Dia and "
|
"and enable two additional UI form fields in the resulting geometry: V-Tip Dia and\n"
|
||||||
"V-Tip Angle. Adjusting those two values will adjust the Z-Cut parameter such "
|
"V-Tip Angle. Adjusting those two values will adjust the Z-Cut parameter such\n"
|
||||||
"as the cut width into material will be equal with the value in the Tool Diameter "
|
"as the cut width into material will be equal with the value in the Tool Diameter\n"
|
||||||
"column of this table.<BR>"
|
"column of this table.\n"
|
||||||
"Choosing the <B>V-Shape</B> Tool Type automatically will select the Operation Type "
|
"Choosing the 'V-Shape' Tool Type automatically will select the Operation Type\n"
|
||||||
"in the resulting geometry as Isolation."))
|
"in the resulting geometry as Isolation."))
|
||||||
|
|
||||||
|
self.tools_table.horizontalHeaderItem(4).setToolTip(
|
||||||
|
_("The 'Operation' can be:\n"
|
||||||
|
"- Isolation -> will ensure that the non-copper clearing is always complete.\n"
|
||||||
|
"If it's not successful then the non-copper clearing will fail, too.\n"
|
||||||
|
"- Clear -> the regular non-copper clearing."))
|
||||||
|
|
||||||
self.ncc_order_label = QtWidgets.QLabel('<b>%s:</b>' % _('Tool order'))
|
self.ncc_order_label = QtWidgets.QLabel('<b>%s:</b>' % _('Tool order'))
|
||||||
self.ncc_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
|
self.ncc_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
|
||||||
"'No' --> means that the used order is the one in the tool table\n"
|
"'No' --> means that the used order is the one in the tool table\n"
|
||||||
|
@ -363,6 +369,13 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
self.tools_box.addWidget(self.generate_ncc_button)
|
self.tools_box.addWidget(self.generate_ncc_button)
|
||||||
self.tools_box.addStretch()
|
self.tools_box.addStretch()
|
||||||
|
|
||||||
|
self.tools_table.setupContextMenu()
|
||||||
|
self.tools_table.addContextMenu(
|
||||||
|
"Add", lambda: self.on_tool_add(dia=None, muted=None), icon=QtGui.QIcon("share/plus16.png"))
|
||||||
|
self.tools_table.addContextMenu(
|
||||||
|
"Delete", lambda:
|
||||||
|
self.on_tool_delete(rows_to_delete=None, all=None), icon=QtGui.QIcon("share/delete32.png"))
|
||||||
|
|
||||||
self.units = ''
|
self.units = ''
|
||||||
self.ncc_tools = {}
|
self.ncc_tools = {}
|
||||||
self.tooluid = 0
|
self.tooluid = 0
|
||||||
|
@ -381,6 +394,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
self.cursor_pos = None
|
self.cursor_pos = None
|
||||||
self.mouse_is_dragging = False
|
self.mouse_is_dragging = False
|
||||||
|
|
||||||
|
# store here solid_geometry when there are tool with isolation job
|
||||||
|
self.solid_geometry = []
|
||||||
|
|
||||||
self.addtool_btn.clicked.connect(self.on_tool_add)
|
self.addtool_btn.clicked.connect(self.on_tool_add)
|
||||||
self.addtool_entry.returnPressed.connect(self.on_tool_add)
|
self.addtool_entry.returnPressed.connect(self.on_tool_add)
|
||||||
self.deltool_btn.clicked.connect(self.on_tool_delete)
|
self.deltool_btn.clicked.connect(self.on_tool_delete)
|
||||||
|
@ -448,13 +464,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
self.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"])
|
self.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"])
|
||||||
self.reference_radio.set_value(self.app.defaults["tools_nccref"])
|
self.reference_radio.set_value(self.app.defaults["tools_nccref"])
|
||||||
|
|
||||||
self.tools_table.setupContextMenu()
|
|
||||||
self.tools_table.addContextMenu(
|
|
||||||
"Add", lambda: self.on_tool_add(dia=None, muted=None), icon=QtGui.QIcon("share/plus16.png"))
|
|
||||||
self.tools_table.addContextMenu(
|
|
||||||
"Delete", lambda:
|
|
||||||
self.on_tool_delete(rows_to_delete=None, all=None), icon=QtGui.QIcon("share/delete32.png"))
|
|
||||||
|
|
||||||
# init the working variables
|
# init the working variables
|
||||||
self.default_data.clear()
|
self.default_data.clear()
|
||||||
self.default_data.update({
|
self.default_data.update({
|
||||||
|
@ -515,6 +524,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
'offset_value': 0.0,
|
'offset_value': 0.0,
|
||||||
'type': 'Iso',
|
'type': 'Iso',
|
||||||
'tool_type': 'V',
|
'tool_type': 'V',
|
||||||
|
'operation': 'clear',
|
||||||
'data': dict(self.default_data),
|
'data': dict(self.default_data),
|
||||||
'solid_geometry': []
|
'solid_geometry': []
|
||||||
}
|
}
|
||||||
|
@ -583,12 +593,22 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
|
|
||||||
tool_uid_item = QtWidgets.QTableWidgetItem(str(int(tooluid_key)))
|
tool_uid_item = QtWidgets.QTableWidgetItem(str(int(tooluid_key)))
|
||||||
|
|
||||||
|
operation_type = QtWidgets.QComboBox()
|
||||||
|
operation_type.addItem('iso')
|
||||||
|
operation_type.setStyleSheet('background-color: rgb(255,255,255)')
|
||||||
|
operation_type.addItem('clear')
|
||||||
|
operation_type.setStyleSheet('background-color: rgb(255,255,255)')
|
||||||
|
op_idx = operation_type.findText(tooluid_value['operation'])
|
||||||
|
operation_type.setCurrentIndex(op_idx)
|
||||||
|
|
||||||
self.tools_table.setItem(row_no, 1, dia) # Diameter
|
self.tools_table.setItem(row_no, 1, dia) # Diameter
|
||||||
self.tools_table.setCellWidget(row_no, 2, tool_type_item)
|
self.tools_table.setCellWidget(row_no, 2, tool_type_item)
|
||||||
|
|
||||||
# ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
|
# ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
|
||||||
self.tools_table.setItem(row_no, 3, tool_uid_item) # Tool unique ID
|
self.tools_table.setItem(row_no, 3, tool_uid_item) # Tool unique ID
|
||||||
|
|
||||||
|
self.tools_table.setCellWidget(row_no, 4, operation_type)
|
||||||
|
|
||||||
# make the diameter column editable
|
# make the diameter column editable
|
||||||
for row in range(tool_id):
|
for row in range(tool_id):
|
||||||
self.tools_table.item(row, 1).setFlags(
|
self.tools_table.item(row, 1).setFlags(
|
||||||
|
@ -728,6 +748,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
'offset_value': 0.0,
|
'offset_value': 0.0,
|
||||||
'type': 'Iso',
|
'type': 'Iso',
|
||||||
'tool_type': 'V',
|
'tool_type': 'V',
|
||||||
|
'operation': 'clear',
|
||||||
'data': dict(self.default_data),
|
'data': dict(self.default_data),
|
||||||
'solid_geometry': []
|
'solid_geometry': []
|
||||||
}
|
}
|
||||||
|
@ -863,8 +884,10 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
self.app.inform.emit(_("[ERROR_NOTCL] Object not found: %s") % self.ncc_obj)
|
self.app.inform.emit(_("[ERROR_NOTCL] Object not found: %s") % self.ncc_obj)
|
||||||
return
|
return
|
||||||
|
|
||||||
# use the selected tools in the tool table; get diameters
|
# use the selected tools in the tool table; get diameters for non-copper clear
|
||||||
tooldia_list = list()
|
iso_dia_list = list()
|
||||||
|
# use the selected tools in the tool table; get diameters for non-copper clear
|
||||||
|
ncc_dia_list = list()
|
||||||
if self.tools_table.selectedItems():
|
if self.tools_table.selectedItems():
|
||||||
for x in self.tools_table.selectedItems():
|
for x in self.tools_table.selectedItems():
|
||||||
try:
|
try:
|
||||||
|
@ -877,7 +900,11 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong Tool Dia value format entered, "
|
self.app.inform.emit(_("[ERROR_NOTCL] Wrong Tool Dia value format entered, "
|
||||||
"use a number."))
|
"use a number."))
|
||||||
continue
|
continue
|
||||||
tooldia_list.append(tooldia)
|
|
||||||
|
if self.tools_table.cellWidget(x.row(), 4).currentText() == 'iso':
|
||||||
|
iso_dia_list.append(tooldia)
|
||||||
|
else:
|
||||||
|
ncc_dia_list.append(tooldia)
|
||||||
else:
|
else:
|
||||||
self.app.inform.emit(_("[ERROR_NOTCL] No selected tools in Tool Table."))
|
self.app.inform.emit(_("[ERROR_NOTCL] No selected tools in Tool Table."))
|
||||||
return
|
return
|
||||||
|
@ -895,7 +922,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
return "Could not retrieve object: %s" % self.obj_name
|
return "Could not retrieve object: %s" % self.obj_name
|
||||||
|
|
||||||
self.clear_copper(ncc_obj=self.ncc_obj,
|
self.clear_copper(ncc_obj=self.ncc_obj,
|
||||||
tooldia=tooldia_list,
|
ncctooldia=ncc_dia_list,
|
||||||
|
isotooldia=iso_dia_list,
|
||||||
has_offset=has_offset,
|
has_offset=has_offset,
|
||||||
outname=o_name,
|
outname=o_name,
|
||||||
overlap=overlap,
|
overlap=overlap,
|
||||||
|
@ -951,7 +979,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
self.sel_rect = cascaded_union(self.sel_rect)
|
self.sel_rect = cascaded_union(self.sel_rect)
|
||||||
self.clear_copper(ncc_obj=self.ncc_obj,
|
self.clear_copper(ncc_obj=self.ncc_obj,
|
||||||
sel_obj=self.bound_obj,
|
sel_obj=self.bound_obj,
|
||||||
tooldia=tooldia_list,
|
ncctooldia=ncc_dia_list,
|
||||||
|
isotooldia=iso_dia_list,
|
||||||
has_offset=has_offset,
|
has_offset=has_offset,
|
||||||
outname=o_name,
|
outname=o_name,
|
||||||
overlap=overlap,
|
overlap=overlap,
|
||||||
|
@ -977,7 +1006,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
self.sel_rect = cascaded_union(self.sel_rect)
|
self.sel_rect = cascaded_union(self.sel_rect)
|
||||||
self.clear_copper(ncc_obj=self.ncc_obj,
|
self.clear_copper(ncc_obj=self.ncc_obj,
|
||||||
sel_obj=self.bound_obj,
|
sel_obj=self.bound_obj,
|
||||||
tooldia=tooldia_list,
|
ncctooldia=ncc_dia_list,
|
||||||
|
isotooldia=iso_dia_list,
|
||||||
has_offset=has_offset,
|
has_offset=has_offset,
|
||||||
outname=o_name,
|
outname=o_name,
|
||||||
overlap=overlap,
|
overlap=overlap,
|
||||||
|
@ -1026,7 +1056,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
|
|
||||||
self.clear_copper(ncc_obj=self.ncc_obj,
|
self.clear_copper(ncc_obj=self.ncc_obj,
|
||||||
sel_obj=self.bound_obj,
|
sel_obj=self.bound_obj,
|
||||||
tooldia=tooldia_list,
|
ncctooldia=ncc_dia_list,
|
||||||
|
isotooldia=iso_dia_list,
|
||||||
has_offset=has_offset,
|
has_offset=has_offset,
|
||||||
outname=o_name,
|
outname=o_name,
|
||||||
overlap=overlap,
|
overlap=overlap,
|
||||||
|
@ -1036,7 +1067,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
|
|
||||||
def clear_copper(self, ncc_obj,
|
def clear_copper(self, ncc_obj,
|
||||||
sel_obj=None,
|
sel_obj=None,
|
||||||
tooldia=None,
|
ncctooldia=None,
|
||||||
|
isotooldia=None,
|
||||||
margin=None,
|
margin=None,
|
||||||
has_offset=None,
|
has_offset=None,
|
||||||
offset=None,
|
offset=None,
|
||||||
|
@ -1053,7 +1085,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
Clear the excess copper from the entire object.
|
Clear the excess copper from the entire object.
|
||||||
|
|
||||||
:param ncc_obj: ncc cleared object
|
:param ncc_obj: ncc cleared object
|
||||||
:param tooldia: a tuple or single element made out of diameters of the tools to be used
|
:param ncctooldia: a tuple or single element made out of diameters of the tools to be used to ncc clear
|
||||||
|
:param isotooldia: a tuple or single element made out of diameters of the tools to be used for isolation
|
||||||
:param overlap: value by which the paths will overlap
|
:param overlap: value by which the paths will overlap
|
||||||
:param order: if the tools are ordered and how
|
:param order: if the tools are ordered and how
|
||||||
:param select_method: if to do ncc on the whole object, on an defined area or on an area defined by
|
:param select_method: if to do ncc on the whole object, on an defined area or on an area defined by
|
||||||
|
@ -1124,17 +1157,18 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
# # Read the tooldia parameter and create a sorted list out them - they may be more than one diameter ##
|
# # Read the tooldia parameter and create a sorted list out them - they may be more than one diameter ##
|
||||||
# ######################################################################################################
|
# ######################################################################################################
|
||||||
sorted_tools = []
|
sorted_tools = []
|
||||||
if tooldia is not None:
|
if ncctooldia is not None:
|
||||||
try:
|
try:
|
||||||
sorted_tools = [float(eval(dia)) for dia in tooldia.split(",") if dia != '']
|
sorted_tools = [float(eval(dia)) for dia in ncctooldia.split(",") if dia != '']
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
if not isinstance(tooldia, list):
|
if not isinstance(ncctooldia, list):
|
||||||
sorted_tools = [float(tooldia)]
|
sorted_tools = [float(ncctooldia)]
|
||||||
else:
|
else:
|
||||||
sorted_tools = tooldia
|
sorted_tools = ncctooldia
|
||||||
else:
|
else:
|
||||||
for row in range(self.tools_table.rowCount()):
|
for row in range(self.tools_table.rowCount()):
|
||||||
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
|
if self.tools_table.cellWidget(row, 1).currentText() == 'clear':
|
||||||
|
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
|
||||||
|
|
||||||
# ##############################################################################################################
|
# ##############################################################################################################
|
||||||
# Prepare non-copper polygons. Create the bounding box area from which the copper features will be subtracted ##
|
# Prepare non-copper polygons. Create the bounding box area from which the copper features will be subtracted ##
|
||||||
|
@ -1200,37 +1234,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
log.debug("NonCopperClear.clear_copper() --> %s" % str(e))
|
log.debug("NonCopperClear.clear_copper() --> %s" % str(e))
|
||||||
return 'fail'
|
return 'fail'
|
||||||
|
|
||||||
# ###################################################################################################
|
|
||||||
# Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry ##
|
|
||||||
# ###################################################################################################
|
|
||||||
if isinstance(ncc_obj, FlatCAMGerber):
|
|
||||||
if has_offset is True:
|
|
||||||
self.app.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
|
|
||||||
offseted_geo = ncc_obj.solid_geometry.buffer(distance=ncc_offset)
|
|
||||||
self.app.inform.emit(_("[success] Buffering finished ..."))
|
|
||||||
empty = self.get_ncc_empty_area(target=offseted_geo, boundary=bounding_box)
|
|
||||||
else:
|
|
||||||
empty = self.get_ncc_empty_area(target=ncc_obj.solid_geometry, boundary=bounding_box)
|
|
||||||
elif isinstance(ncc_obj, FlatCAMGeometry):
|
|
||||||
sol_geo = cascaded_union(ncc_obj.solid_geometry)
|
|
||||||
if has_offset is True:
|
|
||||||
self.app.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
|
|
||||||
offseted_geo = sol_geo.buffer(distance=ncc_offset)
|
|
||||||
self.app.inform.emit(_("[success] Buffering finished ..."))
|
|
||||||
empty = self.get_ncc_empty_area(target=offseted_geo, boundary=bounding_box)
|
|
||||||
else:
|
|
||||||
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
|
|
||||||
else:
|
|
||||||
self.inform.emit(_('[ERROR_NOTCL] The selected object is not suitable for copper clearing.'))
|
|
||||||
return
|
|
||||||
|
|
||||||
if empty.is_empty:
|
|
||||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not get the extent of the area to be non copper cleared."))
|
|
||||||
return
|
|
||||||
|
|
||||||
if type(empty) is Polygon:
|
|
||||||
empty = MultiPolygon([empty])
|
|
||||||
|
|
||||||
# ########################################################################################################
|
# ########################################################################################################
|
||||||
# set the name for the future Geometry object
|
# set the name for the future Geometry object
|
||||||
# I do it here because it is also stored inside the gen_clear_area() and gen_clear_area_rest() methods
|
# I do it here because it is also stored inside the gen_clear_area() and gen_clear_area_rest() methods
|
||||||
|
@ -1267,8 +1270,110 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
current_uid = int(1)
|
current_uid = int(1)
|
||||||
tool = eval(self.app.defaults["tools_ncctools"])[0]
|
tool = eval(self.app.defaults["tools_ncctools"])[0]
|
||||||
|
|
||||||
|
# ###################################################################################################
|
||||||
|
# Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry ##
|
||||||
|
# ###################################################################################################
|
||||||
|
if isinstance(ncc_obj, FlatCAMGerber) and not isotooldia:
|
||||||
|
sol_geo = ncc_obj.solid_geometry
|
||||||
|
if has_offset is True:
|
||||||
|
app_obj.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
|
||||||
|
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||||
|
app_obj.inform.emit(_("[success] Buffering finished ..."))
|
||||||
|
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
|
||||||
|
elif isinstance(ncc_obj, FlatCAMGerber) and isotooldia:
|
||||||
|
isolated_geo = []
|
||||||
|
self.solid_geometry = ncc_obj.solid_geometry
|
||||||
|
|
||||||
|
# if milling type is climb then the move is counter-clockwise around features
|
||||||
|
milling_type = 'cl'
|
||||||
|
|
||||||
|
for tool_iso in isotooldia:
|
||||||
|
new_geometry = []
|
||||||
|
|
||||||
|
if milling_type == 'cl':
|
||||||
|
isolated_geo = self.generate_envelope(tool_iso, 1)
|
||||||
|
else:
|
||||||
|
isolated_geo = self.generate_envelope(tool_iso, 0)
|
||||||
|
|
||||||
|
if isolated_geo == 'fail':
|
||||||
|
app_obj.inform.emit(_("[ERROR_NOTCL] Isolation geometry could not be generated."))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
for geo_elem in isolated_geo:
|
||||||
|
if isinstance(geo_elem, Polygon):
|
||||||
|
for ring in self.poly2rings(geo_elem):
|
||||||
|
new_geo = ring.intersection(bounding_box)
|
||||||
|
if new_geo and not new_geo.is_empty:
|
||||||
|
new_geometry.append(new_geo)
|
||||||
|
elif isinstance(geo_elem, MultiPolygon):
|
||||||
|
for poly in geo_elem:
|
||||||
|
for ring in self.poly2rings(poly):
|
||||||
|
new_geo = ring.intersection(bounding_box)
|
||||||
|
if new_geo and not new_geo.is_empty:
|
||||||
|
new_geometry.append(new_geo)
|
||||||
|
elif isinstance(geo_elem, LineString):
|
||||||
|
new_geo = geo_elem.intersection(bounding_box)
|
||||||
|
if new_geo:
|
||||||
|
if not new_geo.is_empty:
|
||||||
|
new_geometry.append(new_geo)
|
||||||
|
elif isinstance(geo_elem, MultiLineString):
|
||||||
|
for line_elem in geo_elem:
|
||||||
|
new_geo = line_elem.intersection(bounding_box)
|
||||||
|
if new_geo and not new_geo.is_empty:
|
||||||
|
new_geometry.append(new_geo)
|
||||||
|
except TypeError:
|
||||||
|
if isinstance(isolated_geo, Polygon):
|
||||||
|
for ring in self.poly2rings(isolated_geo):
|
||||||
|
new_geo = ring.intersection(bounding_box)
|
||||||
|
if new_geo:
|
||||||
|
if not new_geo.is_empty:
|
||||||
|
new_geometry.append(new_geo)
|
||||||
|
elif isinstance(isolated_geo, LineString):
|
||||||
|
new_geo = isolated_geo.intersection(bounding_box)
|
||||||
|
if new_geo and not new_geo.is_empty:
|
||||||
|
new_geometry.append(new_geo)
|
||||||
|
elif isinstance(isolated_geo, MultiLineString):
|
||||||
|
for line_elem in isolated_geo:
|
||||||
|
new_geo = line_elem.intersection(bounding_box)
|
||||||
|
if new_geo and not new_geo.is_empty:
|
||||||
|
new_geometry.append(new_geo)
|
||||||
|
|
||||||
|
for k, v in tools_storage.items():
|
||||||
|
if float('%.4f' % v['tooldia']) == float('%.4f' % tool_iso):
|
||||||
|
current_uid = int(k)
|
||||||
|
# add the solid_geometry to the current too in self.paint_tools dictionary
|
||||||
|
# and then reset the temporary list that stored that solid_geometry
|
||||||
|
v['solid_geometry'] = deepcopy(new_geometry)
|
||||||
|
v['data']['name'] = name
|
||||||
|
break
|
||||||
|
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
||||||
|
|
||||||
|
sol_geo = cascaded_union(isolated_geo)
|
||||||
|
if has_offset is True:
|
||||||
|
app_obj.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
|
||||||
|
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||||
|
app_obj.inform.emit(_("[success] Buffering finished ..."))
|
||||||
|
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
|
||||||
|
elif isinstance(ncc_obj, FlatCAMGeometry):
|
||||||
|
sol_geo = cascaded_union(ncc_obj.solid_geometry)
|
||||||
|
if has_offset is True:
|
||||||
|
app_obj.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
|
||||||
|
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||||
|
app_obj.inform.emit(_("[success] Buffering finished ..."))
|
||||||
|
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
|
||||||
|
else:
|
||||||
|
app_obj.inform.emit(_('[ERROR_NOTCL] The selected object is not suitable for copper clearing.'))
|
||||||
|
return
|
||||||
|
|
||||||
|
if empty.is_empty:
|
||||||
|
app_obj.inform.emit(_("[ERROR_NOTCL] Could not get the extent of the area to be non copper cleared."))
|
||||||
|
return 'fail'
|
||||||
|
|
||||||
|
if type(empty) is Polygon:
|
||||||
|
empty = MultiPolygon([empty])
|
||||||
|
|
||||||
for tool in sorted_tools:
|
for tool in sorted_tools:
|
||||||
self.app.inform.emit(_('[success] Non-Copper Clearing with ToolDia = %s started.') % str(tool))
|
app_obj.inform.emit(_('[success] Non-Copper Clearing with ToolDia = %s started.') % str(tool))
|
||||||
cleared_geo[:] = []
|
cleared_geo[:] = []
|
||||||
|
|
||||||
# Get remaining tools offset
|
# Get remaining tools offset
|
||||||
|
@ -1352,9 +1457,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
if geo_obj.tools[tooluid]['solid_geometry']:
|
if geo_obj.tools[tooluid]['solid_geometry']:
|
||||||
has_solid_geo += 1
|
has_solid_geo += 1
|
||||||
if has_solid_geo == 0:
|
if has_solid_geo == 0:
|
||||||
self.app.inform.emit(_("[ERROR] There is no Painting Geometry in the file.\n"
|
app_obj.inform.emit(_("[ERROR] There is no Painting Geometry in the file.\n"
|
||||||
"Usually it means that the tool diameter is too big for the painted geometry.\n"
|
"Usually it means that the tool diameter is too big for the painted geometry.\n"
|
||||||
"Change the painting parameters and try again."))
|
"Change the painting parameters and try again."))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Experimental...
|
# Experimental...
|
||||||
|
@ -1381,11 +1486,71 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
# repurposed flag for final object, geo_obj. True if it has any solid_geometry, False if not.
|
# repurposed flag for final object, geo_obj. True if it has any solid_geometry, False if not.
|
||||||
app_obj.poly_not_cleared = True
|
app_obj.poly_not_cleared = True
|
||||||
|
|
||||||
|
# ###################################################################################################
|
||||||
|
# Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry ##
|
||||||
|
# ###################################################################################################
|
||||||
|
if isinstance(ncc_obj, FlatCAMGerber) and not isotooldia:
|
||||||
|
sol_geo = ncc_obj.solid_geometry
|
||||||
|
if has_offset is True:
|
||||||
|
app_obj.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
|
||||||
|
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||||
|
app_obj.inform.emit(_("[success] Buffering finished ..."))
|
||||||
|
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
|
||||||
|
elif isinstance(ncc_obj, FlatCAMGerber) and isotooldia:
|
||||||
|
isolated_geo = []
|
||||||
|
self.solid_geometry = ncc_obj.solid_geometry
|
||||||
|
|
||||||
|
# if milling type is climb then the move is counter-clockwise around features
|
||||||
|
milling_type = 'cl'
|
||||||
|
|
||||||
|
for tool_iso in isotooldia:
|
||||||
|
if milling_type == 'cl':
|
||||||
|
isolated_geo = self.generate_envelope(tool_iso, 1)
|
||||||
|
else:
|
||||||
|
isolated_geo = self.generate_envelope(tool_iso, 0)
|
||||||
|
|
||||||
|
if isolated_geo == 'fail':
|
||||||
|
app_obj.inform.emit(_("[ERROR_NOTCL] Isolation geometry could not be generated."))
|
||||||
|
else:
|
||||||
|
for k, v in tools_storage.items():
|
||||||
|
if float('%.4f' % v['tooldia']) == float('%.4f' % tool_iso):
|
||||||
|
current_uid = int(k)
|
||||||
|
# add the solid_geometry to the current too in self.paint_tools dictionary
|
||||||
|
# and then reset the temporary list that stored that solid_geometry
|
||||||
|
v['solid_geometry'] = deepcopy(isolated_geo)
|
||||||
|
v['data']['name'] = name
|
||||||
|
break
|
||||||
|
geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
|
||||||
|
|
||||||
|
sol_geo = cascaded_union(isolated_geo)
|
||||||
|
if has_offset is True:
|
||||||
|
app_obj.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
|
||||||
|
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||||
|
app_obj.inform.emit(_("[success] Buffering finished ..."))
|
||||||
|
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
|
||||||
|
elif isinstance(ncc_obj, FlatCAMGeometry):
|
||||||
|
sol_geo = cascaded_union(ncc_obj.solid_geometry)
|
||||||
|
if has_offset is True:
|
||||||
|
app_obj.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
|
||||||
|
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||||
|
app_obj.inform.emit(_("[success] Buffering finished ..."))
|
||||||
|
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
|
||||||
|
else:
|
||||||
|
app_obj.inform.emit(_('[ERROR_NOTCL] The selected object is not suitable for copper clearing.'))
|
||||||
|
return
|
||||||
|
|
||||||
|
if empty.is_empty:
|
||||||
|
app_obj.inform.emit(_("[ERROR_NOTCL] Could not get the extent of the area to be non copper cleared."))
|
||||||
|
return 'fail'
|
||||||
|
|
||||||
|
if type(empty) is Polygon:
|
||||||
|
empty = MultiPolygon([empty])
|
||||||
|
|
||||||
area = empty.buffer(0)
|
area = empty.buffer(0)
|
||||||
# Generate area for each tool
|
# Generate area for each tool
|
||||||
while sorted_tools:
|
while sorted_tools:
|
||||||
tool = sorted_tools.pop(0)
|
tool = sorted_tools.pop(0)
|
||||||
self.app.inform.emit(_('[success] Non-Copper Rest Clearing with ToolDia = %s started.') % str(tool))
|
app_obj.inform.emit(_('[success] Non-Copper Rest Clearing with ToolDia = %s started.') % str(tool))
|
||||||
|
|
||||||
tool_used = tool - 1e-12
|
tool_used = tool - 1e-12
|
||||||
cleared_geo[:] = []
|
cleared_geo[:] = []
|
||||||
|
@ -1879,3 +2044,44 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
||||||
self.mouse_is_dragging = False
|
self.mouse_is_dragging = False
|
||||||
|
|
||||||
self.sel_rect = []
|
self.sel_rect = []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def poly2rings(poly):
|
||||||
|
return [poly.exterior] + [interior for interior in poly.interiors]
|
||||||
|
|
||||||
|
def generate_envelope(self, offset, invert, envelope_iso_type=2, follow=None):
|
||||||
|
# isolation_geometry produces an envelope that is going on the left of the geometry
|
||||||
|
# (the copper features). To leave the least amount of burrs on the features
|
||||||
|
# the tool needs to travel on the right side of the features (this is called conventional milling)
|
||||||
|
# the first pass is the one cutting all of the features, so it needs to be reversed
|
||||||
|
# the other passes overlap preceding ones and cut the left over copper. It is better for them
|
||||||
|
# to cut on the right side of the left over copper i.e on the left side of the features.
|
||||||
|
try:
|
||||||
|
geom = self.isolation_geometry(offset, iso_type=envelope_iso_type, follow=follow)
|
||||||
|
except Exception as e:
|
||||||
|
log.debug('NonCopperClear.generate_envelope() --> %s' % str(e))
|
||||||
|
return 'fail'
|
||||||
|
|
||||||
|
if invert:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
pl = []
|
||||||
|
for p in geom:
|
||||||
|
if p is not None:
|
||||||
|
if isinstance(p, Polygon):
|
||||||
|
pl.append(Polygon(p.exterior.coords[::-1], p.interiors))
|
||||||
|
elif isinstance(p, LinearRing):
|
||||||
|
pl.append(Polygon(p.coords[::-1]))
|
||||||
|
geom = MultiPolygon(pl)
|
||||||
|
except TypeError:
|
||||||
|
if isinstance(geom, Polygon) and geom is not None:
|
||||||
|
geom = Polygon(geom.exterior.coords[::-1], geom.interiors)
|
||||||
|
elif isinstance(geom, LinearRing) and geom is not None:
|
||||||
|
geom = Polygon(geom.coords[::-1])
|
||||||
|
else:
|
||||||
|
log.debug("NonCopperClear.generate_envelope() Error --> Unexpected Geometry %s" %
|
||||||
|
type(geom))
|
||||||
|
except Exception as e:
|
||||||
|
log.debug("NonCopperClear.generate_envelope() Error --> %s" % str(e))
|
||||||
|
return 'fail'
|
||||||
|
return geom
|
||||||
|
|
Loading…
Reference in New Issue