- changed the data structure for the Excellon object; modified the Excellon parser and the Excellon object class

- fixed partially the Excellon Editor to work with the new data structure
- fixed Excellon export to work with the new data structure
- fixed all transformations in the Excellon object attributes; still need to fix the App Tools that creates or use Exellon objects
This commit is contained in:
Marius Stanciu 2020-06-16 03:47:26 +03:00 committed by Marius
parent bb24a45f5a
commit 949c265378
24 changed files with 691 additions and 683 deletions

View File

@ -7,6 +7,13 @@ CHANGELOG for FlatCAM beta
=================================================
16.06.2020
- changed the data structure for the Excellon object; modified the Excellon parser and the Excellon object class
- fixed partially the Excellon Editor to work with the new data structure
- fixed Excellon export to work with the new data structure
- fixed all transformations in the Excellon object attributes; still need to fix the App Tools that creates or use Exellon objects
15.06.2020
- in Paint Tool and NCC Tool updated the way the selected tools were processed and made sure that the Tools Table rows are counted only once in the processing

View File

@ -390,8 +390,7 @@ class FCSlot(FCShapeTool):
item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
self.draw_app.tools_table_exc.setCurrentItem(item)
except KeyError:
self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
_("To add a slot first select a tool"))
self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' % _("To add a slot first select a tool"))
self.draw_app.select_tool("drill_select")
return
@ -2059,9 +2058,7 @@ class AppExcEditor(QtCore.QObject):
self.sorted_diameters = []
self.new_drills = []
self.new_tools = {}
self.new_slots = []
# dictionary to store the tool_row and diameters in Tool_table
# it will be updated everytime self.build_ui() is called
@ -2261,30 +2258,33 @@ class AppExcEditor(QtCore.QObject):
self.tool2tooldia.clear()
# build the self.points_edit dict {dimaters: [point_list]}
for drill in self.exc_obj.drills:
if drill['tool'] in self.exc_obj.tools:
tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[drill['tool']]['C']))
for tool, tool_dict in self.exc_obj.tools.items():
tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[tool]['tooldia']))
try:
self.points_edit[tool_dia].append(drill['point'])
except KeyError:
self.points_edit[tool_dia] = [drill['point']]
if 'drills' in tool_dict and tool_dict['drills']:
for drill in tool_dict['drills']:
try:
self.points_edit[tool_dia].append(drill)
except KeyError:
self.points_edit[tool_dia] = [drill]
# build the self.slot_points_edit dict {dimaters: {"start": Point, "stop": Point}}
for slot in self.exc_obj.slots:
if slot['tool'] in self.exc_obj.tools:
tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[slot['tool']]['C']))
for tool, tool_dict in self.exc_obj.tools.items():
tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[tool]['tooldia']))
if 'slots' in tool_dict and tool_dict['slots']:
for slot in tool_dict['slots']:
try:
self.slot_points_edit[tool_dia].append({
"start": slot[0],
"stop": slot[1]
})
except KeyError:
self.slot_points_edit[tool_dia] = [{
"start": slot[0],
"stop": slot[1]
}]
try:
self.slot_points_edit[tool_dia].append({
"start": slot["start"],
"stop": slot["stop"]
})
except KeyError:
self.slot_points_edit[tool_dia] = [{
"start": slot["start"],
"stop": slot["stop"]
}]
# update the olddia_newdia dict to make sure we have an updated state of the tool_table
for key in self.points_edit:
@ -2309,7 +2309,7 @@ class AppExcEditor(QtCore.QObject):
# Excellon file has no tool diameter information. In this case do not order the diameter in the table
# but use the real order found in the exc_obj.tools
for k, v in self.exc_obj.tools.items():
tool_dia = float('%.*f' % (self.decimals, v['C']))
tool_dia = float('%.*f' % (self.decimals, v['tooldia']))
self.tool2tooldia[int(k)] = tool_dia
# Init appGUI
@ -2385,21 +2385,27 @@ class AppExcEditor(QtCore.QObject):
self.tot_drill_cnt += drill_cnt
try:
# Find no of slots for the current tool
for slot in self.slot_points_edit:
if slot['tool'] == tool_no:
slot_cnt += 1
# try:
# # Find no of slots for the current tool
# for slot in self.slot_points_edit:
# if float(slot) == tool_no:
# slot_cnt += 1
#
# self.tot_slot_cnt += slot_cnt
# except AttributeError:
# # log.debug("No slots in the Excellon file")
# # Find no of slots for the current tool
# for tool_dia in self.slot_points_edit:
# if float(tool_dia) == tool_no:
# slot_cnt = len(self.slot_points_edit[tool_dia])
#
# self.tot_slot_cnt += slot_cnt
self.tot_slot_cnt += slot_cnt
except AttributeError:
# log.debug("No slots in the Excellon file")
# Find no of slots for the current tool
for tool_dia in self.slot_points_edit:
if float(tool_dia) == tool_no:
slot_cnt = len(self.slot_points_edit[tool_dia])
for tool_dia in self.slot_points_edit:
if float(tool_dia) == tool_no:
slot_cnt = len(self.slot_points_edit[tool_dia])
self.tot_slot_cnt += slot_cnt
self.tot_slot_cnt += slot_cnt
idd = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
idd.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
@ -3141,6 +3147,10 @@ class AppExcEditor(QtCore.QObject):
poly = x.geo
poly = poly.buffer(-radius)
if not poly.is_valid or poly.is_empty:
print("Polygon not valid: %s" % str(poly.wkt))
continue
xmin, ymin, xmax, ymax = poly.bounds
line_one = LineString([(xmin, ymin), (xmax, ymax)]).intersection(poly).length
line_two = LineString([(xmin, ymax), (xmax, ymin)]).intersection(poly).length
@ -3197,59 +3207,61 @@ class AppExcEditor(QtCore.QObject):
current_tool += 1
# create the self.tools for the new Excellon object (the one with edited content)
name = str(current_tool)
spec = {"C": float(tool_dia[0])}
self.new_tools[name] = spec
if current_tool not in self.new_tools:
self.new_tools[current_tool] = {}
self.new_tools[current_tool]['tooldia'] = float(tool_dia[0])
# add in self.tools the 'solid_geometry' key, the value (a list) is populated below
self.new_tools[name]['solid_geometry'] = []
self.new_tools[current_tool]['solid_geometry'] = []
# create the self.drills for the new Excellon object (the one with edited content)
for point in tool_dia[1]:
self.new_drills.append(
{
'point': Point(point),
'tool': str(current_tool)
}
)
try:
self.new_tools[current_tool]['drills'].append(Point(point))
except KeyError:
self.new_tools[current_tool]['drills'] = [Point(point)]
# repopulate the 'solid_geometry' for each tool
poly = Point(point).buffer(float(tool_dia[0]) / 2.0, int(int(exc_obj.geo_steps_per_circle) / 4))
self.new_tools[name]['solid_geometry'].append(poly)
self.new_tools[current_tool]['solid_geometry'].append(poly)
ordered_edited_slot_points = sorted(zip(edited_slot_points.keys(), edited_slot_points.values()))
for tool_dia in ordered_edited_slot_points:
tool_exist_flag = False
for tool in self.new_tools:
if tool_dia[0] == self.new_tools[tool]["C"]:
if tool_dia[0] == self.new_tools[tool]["tooldia"]:
current_tool = tool
tool_exist_flag = True
break
if tool_exist_flag is False:
current_tool += 1
# create the self.tools for the new Excellon object (the one with edited content)
name = str(current_tool)
spec = {"C": float(tool_dia[0])}
self.new_tools[name] = spec
if current_tool not in self.new_tools:
self.new_tools[current_tool] = {}
self.new_tools[current_tool]['tooldia'] = float(tool_dia[0])
# add in self.tools the 'solid_geometry' key, the value (a list) is populated below
self.new_tools[name]['solid_geometry'] = []
self.new_tools[current_tool]['solid_geometry'] = []
# create the self.slots for the new Excellon object (the one with edited content)
for coord_dict in tool_dia[1]:
self.new_slots.append(
{
'start': Point(coord_dict['start']),
'stop': Point(coord_dict['stop']),
'tool': str(current_tool)
}
slot = (
Point(coord_dict['start']),
Point(coord_dict['stop'])
)
try:
self.new_tools[current_tool]['slots'].append(slot)
except KeyError:
self.new_tools[current_tool]['slots'] = [slot]
# repopulate the 'solid_geometry' for each tool
poly = LineString([coord_dict['start'], coord_dict['stop']]).buffer(
float(tool_dia[0]) / 2.0, int(int(exc_obj.geo_steps_per_circle) / 4)
)
self.new_tools[str(current_tool)]['solid_geometry'].append(poly)
self.new_tools[current_tool]['solid_geometry'].append(poly)
if self.is_modified is True:
if "_edit" in self.edited_obj_name:

View File

@ -682,7 +682,7 @@ class NumericalEvalEntry(EvalEntry):
return evaled
class NumericalEvalTupleEntry(FCEntry):
class NumericalEvalTupleEntry(EvalEntry):
"""
Will return a text value. Accepts only float numbers and formulas using the operators: /,*,+,-,%
"""

View File

@ -138,7 +138,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
self.ser_attrs += ['options', 'kind']
@staticmethod
def merge(exc_list, exc_final, decimals=None):
def merge(exc_list, exc_final, decimals=None, fuse_tools=True):
"""
Merge Excellon objects found in exc_list parameter into exc_final object.
Options are always copied from source .
@ -153,31 +153,31 @@ class ExcellonObject(FlatCAMObj, Excellon):
:type exc_list: list
:param exc_final: Destination ExcellonObject object.
:type exc_final: class
:param decimals: The number of decimals to be used for diameters
:type decimals: int
:param fuse_tools: If True will try to fuse tools of the same diameter for the Excellon objects
:type fuse_tools: bool
:return: None
"""
if exc_final.tools is None:
exc_final.tools = {}
if decimals is None:
decimals = 4
decimals_exc = decimals
# flag to signal that we need to reorder the tools dictionary and drills and slots lists
flag_order = False
try:
flattened_list = list(itertools.chain(*exc_list))
except TypeError:
flattened_list = exc_list
# this dict will hold the unique tool diameters found in the exc_list objects as the dict keys and the dict
# values will be list of Shapely Points; for drills
custom_dict_drills = defaultdict(list)
# this dict will hold the unique tool diameters found in the exc_list objects as the dict keys and the dict
# values will be list of Shapely Points; for slots
custom_dict_slots = defaultdict(list)
new_tools = {}
total_geo = []
toolid = 0
for exc in flattened_list:
# copy options of the current excellon obj to the final excellon obj
# only the last object options will survive
for option in exc.options:
if option != 'name':
try:
@ -185,129 +185,47 @@ class ExcellonObject(FlatCAMObj, Excellon):
except Exception:
exc.app.log.warning("Failed to copy option.", option)
for drill in exc.drills:
exc_tool_dia = float('%.*f' % (decimals_exc, exc.tools[drill['tool']]['C']))
custom_dict_drills[exc_tool_dia].append(drill['point'])
for slot in exc.slots:
exc_tool_dia = float('%.*f' % (decimals_exc, exc.tools[slot['tool']]['C']))
custom_dict_slots[exc_tool_dia].append([slot['start'], slot['stop']])
for tool in exc.tools:
toolid += 1
new_tools[toolid] = exc.tools[tool]
exc_final.tools = deepcopy(new_tools)
# add the zeros and units to the exc_final object
exc_final.zeros = exc.zeros
exc_final.units = exc.units
total_geo += exc.solid_geometry
# ##########################################
# Here we add data to the exc_final object #
# ##########################################
exc_final.solid_geometry = total_geo
# variable to make tool_name for the tools
current_tool = 0
# The tools diameter are now the keys in the drill_dia dict and the values are the Shapely Points in case of
# drills
for tool_dia in custom_dict_drills:
# we create a tool name for each key in the drill_dia dict (the key is a unique drill diameter)
current_tool += 1
fused_tools_dict = {}
if exc_final.tools and fuse_tools:
toolid = 0
for tool, tool_dict in exc_final.tools.items():
current_tooldia = float('%.*f' % (decimals_exc, tool_dict['tooldia']))
toolid += 1
tool_name = str(current_tool)
spec = {"C": float(tool_dia)}
exc_final.tools[tool_name] = spec
# calculate all diameters in fused_tools_dict
all_dia = []
if fused_tools_dict:
for f_tool in fused_tools_dict:
all_dia.append(float('%.*f' % (decimals_exc, fused_tools_dict[f_tool]['tooldia'])))
# rebuild the drills list of dict's that belong to the exc_final object
for point in custom_dict_drills[tool_dia]:
exc_final.drills.append(
{
"point": point,
"tool": str(current_tool)
}
)
# The tools diameter are now the keys in the drill_dia dict and the values are a list ([start, stop])
# of two Shapely Points in case of slots
for tool_dia in custom_dict_slots:
# we create a tool name for each key in the slot_dia dict (the key is a unique slot diameter)
# but only if there are no drills
if not exc_final.tools:
current_tool += 1
tool_name = str(current_tool)
spec = {"C": float(tool_dia)}
exc_final.tools[tool_name] = spec
else:
dia_list = []
for v in exc_final.tools.values():
dia_list.append(float(v["C"]))
if tool_dia not in dia_list:
flag_order = True
current_tool = len(dia_list) + 1
tool_name = str(current_tool)
spec = {"C": float(tool_dia)}
exc_final.tools[tool_name] = spec
else:
for k, v in exc_final.tools.items():
if v["C"] == tool_dia:
current_tool = int(k)
if current_tooldia in all_dia:
# find tool for current_tooldia in fuse_tools
t = None
for f_tool in fused_tools_dict:
if fused_tools_dict[f_tool]['tooldia'] == current_tooldia:
t = f_tool
break
if t:
fused_tools_dict[t]['drills'] += tool_dict['drills']
fused_tools_dict[t]['slots'] += tool_dict['slots']
fused_tools_dict[t]['solid_geometry'] += tool_dict['solid_geometry']
else:
fused_tools_dict[toolid] = tool_dict
fused_tools_dict[toolid]['tooldia'] = current_tooldia
# rebuild the slots list of dict's that belong to the exc_final object
for point in custom_dict_slots[tool_dia]:
exc_final.slots.append(
{
"start": point[0],
"stop": point[1],
"tool": str(current_tool)
}
)
# flag_order == True means that there was an slot diameter not in the tools and we also have drills
# and the new tool was added to self.tools therefore we need to reorder the tools and drills and slots
current_tool = 0
if flag_order is True:
dia_list = []
temp_drills = []
temp_slots = []
temp_tools = {}
for v in exc_final.tools.values():
dia_list.append(float(v["C"]))
dia_list.sort()
for ordered_dia in dia_list:
current_tool += 1
tool_name_temp = str(current_tool)
spec_temp = {"C": float(ordered_dia)}
temp_tools[tool_name_temp] = spec_temp
for drill in exc_final.drills:
exc_tool_dia = float('%.*f' % (decimals_exc, exc_final.tools[drill['tool']]['C']))
if exc_tool_dia == ordered_dia:
temp_drills.append(
{
"point": drill["point"],
"tool": str(current_tool)
}
)
for slot in exc_final.slots:
slot_tool_dia = float('%.*f' % (decimals_exc, exc_final.tools[slot['tool']]['C']))
if slot_tool_dia == ordered_dia:
temp_slots.append(
{
"start": slot["start"],
"stop": slot["stop"],
"tool": str(current_tool)
}
)
# delete the exc_final tools, drills and slots
exc_final.tools = {}
exc_final.drills[:] = []
exc_final.slots[:] = []
# update the exc_final tools, drills and slots with the ordered values
exc_final.tools = temp_tools
exc_final.drills[:] = temp_drills
exc_final.slots[:] = temp_slots
exc_final.tools = fused_tools_dict
# create the geometry for the exc_final object
exc_final.create_geometry()
@ -351,7 +269,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
sort = []
for k, v in list(self.tools.items()):
sort.append((k, v.get('C')))
sort.append((k, v['tooldia']))
sorted_tools = sorted(sort, key=lambda t1: t1[1])
tools = [i[0] for i in sorted_tools]
@ -370,23 +288,23 @@ class ExcellonObject(FlatCAMObj, Excellon):
slot_cnt = 0 # variable to store the nr of slots per tool
# Find no of drills for the current tool
for drill in self.drills:
if drill['tool'] == tool_no:
drill_cnt += 1
try:
drill_cnt = len(self.tools[tool_no]['drills'])
except KeyError:
drill_cnt = 0
self.tot_drill_cnt += drill_cnt
# Find no of slots for the current tool
for slot in self.slots:
if slot['tool'] == tool_no:
slot_cnt += 1
try:
slot_cnt = len(self.tools[tool_no]['slots'])
except KeyError:
slot_cnt = 0
self.tot_slot_cnt += slot_cnt
exc_id_item = QtWidgets.QTableWidgetItem('%d' % int(tool_no))
exc_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.tools[tool_no]['C']))
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.tools[tool_no]['tooldia']))
dia_item.setFlags(QtCore.Qt.ItemIsEnabled)
drill_count_item = QtWidgets.QTableWidgetItem('%d' % drill_cnt)
@ -508,14 +426,26 @@ class ExcellonObject(FlatCAMObj, Excellon):
self.ui.tools_table.setMinimumHeight(self.ui.tools_table.getHeight())
self.ui.tools_table.setMaximumHeight(self.ui.tools_table.getHeight())
if not self.drills:
# find if we have drills:
has_drills = None
for tt in self.tools:
if 'drills' in self.tools[tt] and self.tools[tt]['drills']:
has_drills = True
break
if has_drills is None:
self.ui.tooldia_entry.hide()
self.ui.generate_milling_button.hide()
else:
self.ui.tooldia_entry.show()
self.ui.generate_milling_button.show()
if not self.slots:
# find if we have slots
has_slots = None
for tt in self.tools:
if 'slots' in self.tools[tt] and self.tools[tt]['slots']:
has_slots = True
break
if has_slots is None:
self.ui.slot_tooldia_entry.hide()
self.ui.generate_milling_slots_button.hide()
else:
@ -962,7 +892,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
:rtype: list
"""
return [str(x.text()) for x in self.ui.tools_table.selectedItems()]
return [x.text() for x in self.ui.tools_table.selectedItems()]
def get_selected_tools_table_items(self):
"""
@ -1017,23 +947,37 @@ class ExcellonObject(FlatCAMObj, Excellon):
excellon_code = ''
# store here if the file has slots, return 1 if any slots, 0 if only drills
has_slots = 0
slots_in_file = 0
# find if we have drills:
has_drills = None
for tt in self.tools:
if 'drills' in self.tools[tt] and self.tools[tt]['drills']:
has_drills = True
break
# find if we have slots:
has_slots = None
for tt in self.tools:
if 'slots' in self.tools[tt] and self.tools[tt]['slots']:
has_slots = True
slots_in_file = 1
break
# drills processing
try:
if self.drills:
if has_drills:
length = whole + fract
for tool in self.tools:
excellon_code += 'T0%s\n' % str(tool) if int(tool) < 10 else 'T%s\n' % str(tool)
for drill in self.drills:
if form == 'dec' and tool == drill['tool']:
drill_x = drill['point'].x * factor
drill_y = drill['point'].y * factor
for drill in self.tools[tool]['drills']:
if form == 'dec':
drill_x = drill.x * factor
drill_y = drill.y * factor
excellon_code += "X{:.{dec}f}Y{:.{dec}f}\n".format(drill_x, drill_y, dec=fract)
elif e_zeros == 'LZ' and tool == drill['tool']:
drill_x = drill['point'].x * factor
drill_y = drill['point'].y * factor
elif e_zeros == 'LZ':
drill_x = drill.x * factor
drill_y = drill.y * factor
exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract)
exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract)
@ -1053,9 +997,9 @@ class ExcellonObject(FlatCAMObj, Excellon):
excellon_code += "X{xform}Y{yform}\n".format(xform=exc_x_formatted,
yform=exc_y_formatted)
elif tool == drill['tool']:
drill_x = drill['point'].x * factor
drill_y = drill['point'].y * factor
else:
drill_x = drill.x * factor
drill_y = drill.y * factor
exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract).replace('.', '')
exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract).replace('.', '')
@ -1071,8 +1015,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
# slots processing
try:
if self.slots:
has_slots = 1
if has_slots:
for tool in self.tools:
excellon_code += 'G05\n'
@ -1081,12 +1024,12 @@ class ExcellonObject(FlatCAMObj, Excellon):
else:
excellon_code += 'T' + str(tool) + '\n'
for slot in self.slots:
if form == 'dec' and tool == slot['tool']:
start_slot_x = slot['start'].x * factor
start_slot_y = slot['start'].y * factor
stop_slot_x = slot['stop'].x * factor
stop_slot_y = slot['stop'].y * factor
for slot in self.tools[tool]['slots']:
if form == 'dec':
start_slot_x = slot.x * factor
start_slot_y = slot.y * factor
stop_slot_x = slot.x * factor
stop_slot_y = slot.y * factor
if slot_type == 'routing':
excellon_code += "G00X{:.{dec}f}Y{:.{dec}f}\nM15\n".format(start_slot_x,
start_slot_y,
@ -1099,11 +1042,11 @@ class ExcellonObject(FlatCAMObj, Excellon):
start_slot_x, start_slot_y, stop_slot_x, stop_slot_y, dec=fract
)
elif e_zeros == 'LZ' and tool == slot['tool']:
start_slot_x = slot['start'].x * factor
start_slot_y = slot['start'].y * factor
stop_slot_x = slot['stop'].x * factor
stop_slot_y = slot['stop'].y * factor
elif e_zeros == 'LZ':
start_slot_x = slot.x * factor
start_slot_y = slot.y * factor
stop_slot_x = slot.x * factor
stop_slot_y = slot.y * factor
start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '')
start_slot_y_formatted = "{:.{dec}f}".format(start_slot_y, dec=fract).replace('.', '')
@ -1139,11 +1082,11 @@ class ExcellonObject(FlatCAMObj, Excellon):
xstart=start_slot_x_formatted, ystart=start_slot_y_formatted,
xstop=stop_slot_x_formatted, ystop=stop_slot_y_formatted
)
elif tool == slot['tool']:
start_slot_x = slot['start'].x * factor
start_slot_y = slot['start'].y * factor
stop_slot_x = slot['stop'].x * factor
stop_slot_y = slot['stop'].y * factor
else:
start_slot_x = slot.x * factor
start_slot_y = slot.y * factor
stop_slot_x = slot.x * factor
stop_slot_y = slot.y * factor
length = whole + fract
start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '')
@ -1170,11 +1113,11 @@ class ExcellonObject(FlatCAMObj, Excellon):
except Exception as e:
log.debug(str(e))
if not self.drills and not self.slots:
if not has_drills and not has_slots:
log.debug("FlatCAMObj.ExcellonObject.export_excellon() --> Excellon Object is empty: no drills, no slots.")
return 'fail'
return has_slots, excellon_code
return slots_in_file, excellon_code
def generate_milling_drills(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False):
"""
@ -1215,7 +1158,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
sort = []
for k, v in self.tools.items():
sort.append((k, v.get('C')))
sort.append((k, v['tooldia']))
sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all":
@ -1223,19 +1166,15 @@ class ExcellonObject(FlatCAMObj, Excellon):
log.debug("Tools 'all' and sorted are: %s" % str(tools))
if len(tools) == 0:
self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Please select one or more tools from the list and try again."))
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Please select one or more tools from the list and try again."))
return False, "Error: No tools."
for tool in tools:
if tooldia > self.tools[tool]["C"]:
self.app.inform.emit(
'[ERROR_NOTCL] %s %s: %s' % (
_("Milling tool for DRILLS is larger than hole size. Cancelled."),
_("Tool"),
str(tool)
)
)
if tooldia > self.tools[tool]["tooldia"]:
mseg = '[ERROR_NOTCL] %s %s: %s' % (_("Milling tool for DRILLS is larger than hole size. Cancelled."),
_("Tool"),
str(tool))
self.app.inform.emit(mseg)
return False, "Error: Milling tool is larger than hole."
def geo_init(geo_obj, app_obj):
@ -1266,15 +1205,13 @@ class ExcellonObject(FlatCAMObj, Excellon):
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
# for FlatCAM is 6 decimals,
# we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero"
for hole in self.drills:
if hole['tool'] in tools:
buffer_value = self.tools[hole['tool']]["C"] / 2 - tooldia / 2
for tool in tools:
for drill in self.tools[tool]['drills']:
buffer_value = self.tools[tool]['tooldia'] / 2 - tooldia / 2
if buffer_value == 0:
geo_obj.solid_geometry.append(
Point(hole['point']).buffer(0.0000001).exterior)
geo_obj.solid_geometry.append(drill.buffer(0.0000001).exterior)
else:
geo_obj.solid_geometry.append(
Point(hole['point']).buffer(buffer_value).exterior)
geo_obj.solid_geometry.append(drill.buffer(buffer_value).exterior)
if use_thread:
def geo_thread(a_obj):
@ -1329,7 +1266,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
sort = []
for k, v in self.tools.items():
sort.append((k, v.get('C')))
sort.append((k, v['tooldia']))
sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all":
@ -1337,14 +1274,13 @@ class ExcellonObject(FlatCAMObj, Excellon):
log.debug("Tools 'all' and sorted are: %s" % str(tools))
if len(tools) == 0:
self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Please select one or more tools from the list and try again."))
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Please select one or more tools from the list and try again."))
return False, "Error: No tools."
for tool in tools:
# I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse
adj_toolstable_tooldia = float('%.*f' % (self.decimals, float(tooldia)))
adj_file_tooldia = float('%.*f' % (self.decimals, float(self.tools[tool]["C"])))
adj_file_tooldia = float('%.*f' % (self.decimals, float(self.tools[tool]["tooldia"])))
if adj_toolstable_tooldia > adj_file_tooldia + 0.0001:
self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Milling tool for SLOTS is larger than hole size. Cancelled."))
@ -1369,24 +1305,24 @@ class ExcellonObject(FlatCAMObj, Excellon):
# in case that the tool used has the same diameter with the hole, and since the maximum resolution
# for FlatCAM is 6 decimals,
# we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero"
for slot in self.slots:
if slot['tool'] in tools:
for tool in tools:
for slot in self.tools[tool]['slots']:
toolstable_tool = float('%.*f' % (self.decimals, float(tooldia)))
file_tool = float('%.*f' % (self.decimals, float(self.tools[tool]["C"])))
file_tool = float('%.*f' % (self.decimals, float(self.tools[tool]["tooldia"])))
# I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse
# for the file_tool (tooldia actually)
buffer_value = float(file_tool / 2) - float(toolstable_tool / 2) + 0.0001
if buffer_value == 0:
start = slot['start']
stop = slot['stop']
start = slot[0]
stop = slot[1]
lines_string = LineString([start, stop])
poly = lines_string.buffer(0.0000001, int(self.geo_steps_per_circle)).exterior
geo_obj.solid_geometry.append(poly)
else:
start = slot['start']
stop = slot['stop']
start = slot[0]
stop = slot[1]
lines_string = LineString([start, stop])
poly = lines_string.buffer(buffer_value, int(self.geo_steps_per_circle)).exterior
@ -1522,7 +1458,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
# tool number) it means that there are 3 rows (1 tool and 2 totals).
# in this case regardless of the selection status of that tool, use it.
if self.ui.tools_table.rowCount() == 3:
tools.append(self.ui.tools_table.item(0, 0).text())
tools.append(int(self.ui.tools_table.item(0, 0).text()))
else:
self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Please select one or more tools from the list and try again."))

View File

@ -40,30 +40,13 @@ class Excellon(Geometry):
================ ====================================
Key Value
================ ====================================
C Diameter of the tool
solid_geometry Geometry list for each tool
tooldia Diameter of the tool
drills List that store the Shapely Points for drill points
slots List that store the Shapely Points for slots. Each is a tuple: (start_point, stop_point
data dictionary which holds the options for each tool
Others Not supported (Ignored).
solid_geometry Geometry list for each tool
================ ====================================
* ``drills`` (list): Each is a dictionary:
================ ====================================
Key Value
================ ====================================
point (Shapely.Point) Where to drill
tool (str) A key in ``tools``
================ ====================================
* ``slots`` (list): Each is a dictionary
================ ====================================
Key Value
================ ====================================
start (Shapely.Point) Start point of the slot
stop (Shapely.Point) Stop point of the slot
tool (str) A key in ``tools``
================ ====================================
"""
defaults = {
@ -96,10 +79,6 @@ class Excellon(Geometry):
# dictionary to store tools, see above for description
self.tools = {}
# list to store the drills, see above for description
self.drills = []
# self.slots (list) to store the slots; each is a dictionary
self.slots = []
self.source_file = ''
@ -110,9 +89,6 @@ class Excellon(Geometry):
self.match_routing_start = None
self.match_routing_stop = None
self.num_tools = [] # List for keeping the tools sorted
self.index_per_tool = {} # Dictionary to store the indexed points for each tool
# ## IN|MM -> Units are inherited from Geometry
self.units = self.app.defaults['units']
self.units_found = self.app.defaults['units']
@ -142,9 +118,8 @@ class Excellon(Geometry):
# Attributes to be included in serialization
# Always append to it because it carries contents
# from Geometry.
self.ser_attrs += ['tools', 'drills', 'zeros', 'excellon_format_upper_mm', 'excellon_format_lower_mm',
'excellon_format_upper_in', 'excellon_format_lower_in', 'excellon_units', 'slots',
'source_file']
self.ser_attrs += ['tools', 'zeros', 'excellon_format_upper_mm', 'excellon_format_lower_mm',
'excellon_format_upper_in', 'excellon_format_lower_in', 'excellon_units', 'source_file']
# ### Patterns ####
# Regex basics:
@ -346,14 +321,22 @@ class Excellon(Geometry):
if match.group(2):
name_tool += 1
# ---------- add a TOOL ------------ #
if name_tool not in self.tools:
self.tools[name_tool] = {}
if line_units == 'MILS':
spec = {"C": (float(match.group(2)) / 1000)}
self.tools[str(name_tool)] = spec
log.debug("Tool definition: %s %s" % (name_tool, spec))
spec = {
'tooldia': (float(match.group(2)) / 1000)
}
self.tools[name_tool]['tooldia'] = (float(match.group(2)) / 1000)
log.debug("Tool definition: %d %s" % (name_tool, spec))
else:
spec = {"C": float(match.group(2))}
self.tools[str(name_tool)] = spec
log.debug("Tool definition: %s %s" % (name_tool, spec))
spec = {
'tooldia': float(match.group(2))
}
self.tools[name_tool]['tooldia'] = float(match.group(2))
log.debug("Tool definition: %d %s" % (name_tool, spec))
spec['solid_geometry'] = []
continue
# search for Altium Excellon Format / Sprint Layout who is included as a comment
@ -400,7 +383,7 @@ class Excellon(Geometry):
lower_tools = set()
if not self.excellon_units_found and self.tools:
for tool in self.tools:
tool_dia = float(self.tools[tool]['C'])
tool_dia = float(self.tools[tool]['tooldia'])
lower_tools.add(tool_dia) if tool_dia <= 0.1 else greater_tools.add(tool_dia)
assumed_units = "IN" if len(lower_tools) > len(greater_tools) else "MM"
@ -434,12 +417,12 @@ class Excellon(Geometry):
# ## Tool change ###
match = self.toolsel_re.search(eline)
if match:
current_tool = str(int(match.group(1)))
current_tool = int(match.group(1))
log.debug("Tool change: %s" % current_tool)
if bool(headerless):
match = self.toolset_hl_re.search(eline)
if match:
name = str(int(match.group(1)))
name = int(match.group(1))
try:
diam = float(match.group(2))
except Exception:
@ -467,8 +450,13 @@ class Excellon(Geometry):
else:
diam = (self.toolless_diam + (int(current_tool) - 1) / 100) / 25.4
spec = {"C": diam, 'solid_geometry': []}
self.tools[name] = spec
# ---------- add a TOOL ------------ #
spec = {"tooldia": diam, 'solid_geometry': []}
if name not in self.tools:
self.tools[name] = {}
self.tools[name]['tooldia'] = diam
self.tools[name]['solid_geometry'] = []
log.debug("Tool definition out of header: %s %s" % (name, spec))
continue
@ -479,8 +467,8 @@ class Excellon(Geometry):
match1 = self.stop_re.search(eline)
if match or match1:
name_tool += 1
current_tool = str(name_tool)
log.debug("Tool change for Allegro type of Excellon: %s" % current_tool)
current_tool = name_tool
log.debug("Tool change for Allegro type of Excellon: %d" % current_tool)
continue
# ## Slots parsing for drilled slots (contain G85)
@ -546,7 +534,7 @@ class Excellon(Geometry):
# store current tool diameter as slot diameter
slot_dia = 0.05
try:
slot_dia = float(self.tools[current_tool]['C'])
slot_dia = float(self.tools[current_tool]['tooldia'])
except Exception:
pass
log.debug(
@ -556,13 +544,17 @@ class Excellon(Geometry):
)
)
self.slots.append(
{
'start': Point(slot_start_x, slot_start_y),
'stop': Point(slot_stop_x, slot_stop_y),
'tool': current_tool
}
# ---------- add a slot ------------ #
slot = (
Point(slot_start_x, slot_start_y),
Point(slot_stop_x, slot_stop_y)
)
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'slots' in self.tools[current_tool]:
self.tools[current_tool]['slots'].apend(slot)
else:
self.tools[current_tool]['slots'] = [slot]
continue
# Slot coordinates with period: Use literally. ###
@ -616,7 +608,7 @@ class Excellon(Geometry):
# store current tool diameter as slot diameter
slot_dia = 0.05
try:
slot_dia = float(self.tools[current_tool]['C'])
slot_dia = float(self.tools[current_tool]['tooldia'])
except Exception:
pass
log.debug(
@ -626,13 +618,17 @@ class Excellon(Geometry):
)
)
self.slots.append(
{
'start': Point(slot_start_x, slot_start_y),
'stop': Point(slot_stop_x, slot_stop_y),
'tool': current_tool
}
# ---------- add a Slot ------------ #
slot = (
Point(slot_start_x, slot_start_y),
Point(slot_stop_x, slot_stop_y)
)
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'slots' in self.tools[current_tool]:
self.tools[current_tool]['slots'].apend(slot)
else:
self.tools[current_tool]['slots'] = [slot]
continue
# ## Coordinates without period # ##
@ -660,7 +656,14 @@ class Excellon(Geometry):
coordx += repeating_x
if repeating_y:
coordy += repeating_y
self.drills.append({'point': Point((coordx, coordy)), 'tool': current_tool})
# ---------- add a Drill ------------ #
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'drills' in self.tools[current_tool]:
self.tools[current_tool]['drills'].append(Point((coordx, coordy)))
else:
self.tools[current_tool]['drills'] = [Point((coordx, coordy))]
repeat -= 1
current_x = coordx
@ -710,19 +713,31 @@ class Excellon(Geometry):
self.routing_flag = 1
slot_stop_x = x
slot_stop_y = y
self.slots.append(
{
'start': Point(slot_start_x, slot_start_y),
'stop': Point(slot_stop_x, slot_stop_y),
'tool': current_tool
}
# ---------- add a Slot ------------ #
slot = (
Point(slot_start_x, slot_start_y),
Point(slot_stop_x, slot_stop_y)
)
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'slots' in self.tools[current_tool]:
self.tools[current_tool]['slots'].apend(slot)
else:
self.tools[current_tool]['slots'] = [slot]
continue
if self.match_routing_start is None and self.match_routing_stop is None:
# signal that there are drill operations
self.defaults['excellon_drills'] = True
self.drills.append({'point': Point((x, y)), 'tool': current_tool})
# ---------- add a Drill ------------ #
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'drills' in self.tools[current_tool]:
self.tools[current_tool]['drills'].append(Point((x, y)))
else:
self.tools[current_tool]['drills'] = [Point((x, y))]
# log.debug("{:15} {:8} {:8}".format(eline, x, y))
continue
@ -778,13 +793,18 @@ class Excellon(Geometry):
self.routing_flag = 1
slot_stop_x = x
slot_stop_y = y
self.slots.append(
{
'start': Point(slot_start_x, slot_start_y),
'stop': Point(slot_stop_x, slot_stop_y),
'tool': current_tool
}
# ---------- add a Slot ------------ #
slot = (
Point(slot_start_x, slot_start_y),
Point(slot_stop_x, slot_stop_y)
)
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'slots' in self.tools[current_tool]:
self.tools[current_tool]['slots'].apend(slot)
else:
self.tools[current_tool]['slots'] = [slot]
continue
if self.match_routing_start is None and self.match_routing_stop is None:
@ -792,7 +812,14 @@ class Excellon(Geometry):
if repeat == 0:
# signal that there are drill operations
self.defaults['excellon_drills'] = True
self.drills.append({'point': Point((x, y)), 'tool': current_tool})
# ---------- add a Drill ------------ #
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'drills' in self.tools[current_tool]:
self.tools[current_tool]['drills'].append(Point((x, y)))
else:
self.tools[current_tool]['drills'] = [Point((x, y))]
else:
coordx = x
coordy = y
@ -801,7 +828,15 @@ class Excellon(Geometry):
coordx = (repeat * x) + repeating_x
if repeating_y:
coordy = (repeat * y) + repeating_y
self.drills.append({'point': Point((coordx, coordy)), 'tool': current_tool})
# ---------- add a Drill ------------ #
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'drills' in self.tools[current_tool]:
self.tools[current_tool]['drills'].append(Point((coordx, coordy)))
else:
self.tools[current_tool]['drills'] = [Point((coordx, coordy))]
repeat -= 1
repeating_x = repeating_y = 0
# log.debug("{:15} {:8} {:8}".format(eline, x, y))
@ -813,9 +848,14 @@ class Excellon(Geometry):
# ## Tool definitions # ##
match = self.toolset_re.search(eline)
if match:
name = str(int(match.group(1)))
# ---------- add a TOOL ------------ #
name = int(match.group(1))
spec = {"C": float(match.group(2)), 'solid_geometry': []}
self.tools[name] = spec
if name not in self.tools:
self.tools[name] = {}
self.tools[name]['tooldia'] = float(match.group(2))
self.tools[name]['solid_geometry'] = []
log.debug("Tool definition: %s %s" % (name, spec))
continue
@ -977,8 +1017,9 @@ class Excellon(Geometry):
def create_geometry(self):
"""
Creates circles of the tool diameter at every point
specified in ``self.drills``. Also creates geometries (polygons)
for the slots as specified in ``self.slots``
specified in self.tools[tool]['drills'].
Also creates geometries (polygons)
for the slots as specified in self.tools[tool]['slots']
All the resulting geometry is stored into self.solid_geometry list.
The list self.solid_geometry has 2 elements: first is a dict with the drills geometry,
and second element is another similar dict that contain the slots geometry.
@ -1001,36 +1042,35 @@ class Excellon(Geometry):
self.tools[tool]['solid_geometry'] = []
self.tools[tool]['data'] = {}
for drill in self.drills:
# poly = drill['point'].buffer(self.tools[drill['tool']]["C"]/2.0)
if drill['tool'] == '':
self.app.inform.emit('[WARNING] %s' %
_("Excellon.create_geometry() -> a drill location was skipped "
"due of not having a tool associated.\n"
"Check the resulting GCode."))
log.debug("appParsers.ParseExcellon.Excellon.create_geometry() -> a drill location was skipped "
"due of not having a tool associated")
continue
tooldia = self.tools[drill['tool']]['C']
poly = drill['point'].buffer(tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
self.solid_geometry.append(poly)
for tool in self.tools:
tooldia = self.tools[tool]['tooldia']
tool_in_drills = drill['tool']
self.tools[tool_in_drills]['solid_geometry'].append(poly)
self.tools[tool_in_drills]['data'] = deepcopy(self.default_data)
if 'drills' in self.tools[tool]:
for drill in self.tools[tool]['drills']:
poly = drill.buffer(tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
for slot in self.slots:
slot_tooldia = self.tools[slot['tool']]['C']
start = slot['start']
stop = slot['stop']
# add poly in the tools geometry
self.tools[tool]['solid_geometry'].append(poly)
self.tools[tool]['data'] = deepcopy(self.default_data)
lines_string = LineString([start, stop])
poly = lines_string.buffer(slot_tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
self.solid_geometry.append(poly)
# add poly to the total solid geometry
self.solid_geometry.append(poly)
if 'slots' in self.tools[tool]:
for slot in self.tools[tool]['slots']:
start = slot[0]
stop = slot[1]
lines_string = LineString([start, stop])
poly = lines_string.buffer(tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
# add poly in the tools geometry
self.tools[tool]['solid_geometry'].append(poly)
self.tools[tool]['data'] = deepcopy(self.default_data)
# add poly to the total solid geometry
self.solid_geometry.append(poly)
tool_in_slots = slot['tool']
self.tools[tool_in_slots]['solid_geometry'].append(poly)
self.tools[tool_in_slots]['data'] = deepcopy(self.default_data)
except Exception as e:
log.debug("appParsers.ParseExcellon.Excellon.create_geometry() -> "
"Excellon geometry creation failed due of ERROR: %s" % str(e))
@ -1126,7 +1166,7 @@ class Excellon(Geometry):
# Tools
for tname in self.tools:
self.tools[tname]["C"] *= factor
self.tools[tname]["tooldia"] *= factor
self.create_geometry()
return factor
@ -1173,31 +1213,39 @@ class Excellon(Geometry):
# variables to display the percentage of work done
self.geo_len = 0
try:
self.geo_len = len(self.drills)
self.geo_len = len(self.tools)
except TypeError:
self.geo_len = 1
self.old_disp_number = 0
self.el_count = 0
# Drills
for drill in self.drills:
drill['point'] = affinity.scale(drill['point'], xfactor, yfactor, origin=(px, py))
for tool in self.tools:
# Scale Drills
if 'drills' in self.tools[tool]:
new_drills = []
for drill in self.tools[tool]['drills']:
new_drills.append(affinity.scale(drill, xfactor, yfactor, origin=(px, py)))
self.tools[tool]['drills'] = new_drills
# Scale Slots
if 'slots' in self.tools[tool]:
new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.scale(slot[0], xfactor, yfactor, origin=(px, py))
new_stop = affinity.scale(slot[1], xfactor, yfactor, origin=(px, py))
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Scale solid_geometry
self.tools[tool]['solid_geometry'] = scale_geom(self.tools[tool]['solid_geometry'])
# update status display
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
# scale solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = scale_geom(self.tools[tool]['solid_geometry'])
# Slots
for slot in self.slots:
slot['stop'] = affinity.scale(slot['stop'], xfactor, yfactor, origin=(px, py))
slot['start'] = affinity.scale(slot['start'], xfactor, yfactor, origin=(px, py))
self.create_geometry()
self.app.proc_container.new_text = ''
@ -1231,31 +1279,39 @@ class Excellon(Geometry):
# variables to display the percentage of work done
self.geo_len = 0
try:
self.geo_len = len(self.drills)
self.geo_len = len(self.tools)
except TypeError:
self.geo_len = 1
self.old_disp_number = 0
self.el_count = 0
# Drills
for drill in self.drills:
drill['point'] = affinity.translate(drill['point'], xoff=dx, yoff=dy)
for tool in self.tools:
# Offset Drills
if 'drills' in self.tools[tool]:
new_drills = []
for drill in self.tools[tool]['drills']:
new_drills.append(affinity.translate(drill, xoff=dx, yoff=dy))
self.tools[tool]['drills'] = new_drills
# Offset Slots
if 'slots' in self.tools[tool]:
new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.translate(slot[0], xoff=dx, yoff=dy)
new_stop = affinity.translate(slot[1], xoff=dx, yoff=dy)
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Offset solid_geometry
self.tools[tool]['solid_geometry'] = offset_geom(self.tools[tool]['solid_geometry'])
# update status display
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
# offset solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = offset_geom(self.tools[tool]['solid_geometry'])
# Slots
for slot in self.slots:
slot['stop'] = affinity.translate(slot['stop'], xoff=dx, yoff=dy)
slot['start'] = affinity.translate(slot['start'], xoff=dx, yoff=dy)
# Recreate geometry
self.create_geometry()
self.app.proc_container.new_text = ''
@ -1291,31 +1347,39 @@ class Excellon(Geometry):
# variables to display the percentage of work done
self.geo_len = 0
try:
self.geo_len = len(self.drills)
self.geo_len = len(self.tools)
except TypeError:
self.geo_len = 1
self.old_disp_number = 0
self.el_count = 0
# Drills
for drill in self.drills:
drill['point'] = affinity.scale(drill['point'], xscale, yscale, origin=(px, py))
for tool in self.tools:
# Offset Drills
if 'drills' in self.tools[tool]:
new_drills = []
for drill in self.tools[tool]['drills']:
new_drills.append(affinity.scale(drill, xscale, yscale, origin=(px, py)))
self.tools[tool]['drills'] = new_drills
# Offset Slots
if 'slots' in self.tools[tool]:
new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.scale(slot[0], xscale, yscale, origin=(px, py))
new_stop = affinity.scale(slot[1], xscale, yscale, origin=(px, py))
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Offset solid_geometry
self.tools[tool]['solid_geometry'] = mirror_geom(self.tools[tool]['solid_geometry'])
# update status display
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
# mirror solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = mirror_geom(self.tools[tool]['solid_geometry'])
# Slots
for slot in self.slots:
slot['stop'] = affinity.scale(slot['stop'], xscale, yscale, origin=(px, py))
slot['start'] = affinity.scale(slot['start'], xscale, yscale, origin=(px, py))
# Recreate geometry
self.create_geometry()
self.app.proc_container.new_text = ''
@ -1361,7 +1425,7 @@ class Excellon(Geometry):
# variables to display the percentage of work done
self.geo_len = 0
try:
self.geo_len = len(self.drills)
self.geo_len = len(self.tools)
except TypeError:
self.geo_len = 1
self.old_disp_number = 0
@ -1369,47 +1433,35 @@ class Excellon(Geometry):
if point is None:
px, py = 0, 0
# Drills
for drill in self.drills:
drill['point'] = affinity.skew(drill['point'], angle_x, angle_y,
origin=(px, py))
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
# skew solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry'])
# Slots
for slot in self.slots:
slot['stop'] = affinity.skew(slot['stop'], angle_x, angle_y, origin=(px, py))
slot['start'] = affinity.skew(slot['start'], angle_x, angle_y, origin=(px, py))
else:
px, py = point
# Drills
for drill in self.drills:
drill['point'] = affinity.skew(drill['point'], angle_x, angle_y,
origin=(px, py))
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
for tool in self.tools:
# Offset Drills
if 'drills' in self.tools[tool]:
new_drills = []
for drill in self.tools[tool]['drills']:
new_drills.append(affinity.skew(drill, angle_x, angle_y, origin=(px, py)))
self.tools[tool]['drills'] = new_drills
# skew solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry'])
# Offset Slots
if 'slots' in self.tools[tool]:
new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.skew(slot[0], angle_x, angle_y, origin=(px, py))
new_stop = affinity.skew(slot[1], angle_x, angle_y, origin=(px, py))
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Slots
for slot in self.slots:
slot['stop'] = affinity.skew(slot['stop'], angle_x, angle_y, origin=(px, py))
slot['start'] = affinity.skew(slot['start'], angle_x, angle_y, origin=(px, py))
# Offset solid_geometry
self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry'])
# update status display
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
self.create_geometry()
self.app.proc_container.new_text = ''
@ -1441,58 +1493,50 @@ class Excellon(Geometry):
return obj
else:
try:
return affinity.rotate(obj, angle, origin=(px, py))
return affinity.rotate(obj, angle, origin=orig)
except AttributeError:
return obj
# variables to display the percentage of work done
self.geo_len = 0
try:
self.geo_len = len(self.drills)
self.geo_len = len(self.tools)
except TypeError:
self.geo_len = 1
self.old_disp_number = 0
self.el_count = 0
if point is None:
# Drills
for drill in self.drills:
drill['point'] = affinity.rotate(drill['point'], angle, origin='center')
# rotate solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry'], origin='center')
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
# Slots
for slot in self.slots:
slot['stop'] = affinity.rotate(slot['stop'], angle, origin='center')
slot['start'] = affinity.rotate(slot['start'], angle, origin='center')
orig = 'center'
else:
px, py = point
# Drills
for drill in self.drills:
drill['point'] = affinity.rotate(drill['point'], angle, origin=(px, py))
orig = point
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
for tool in self.tools:
# Offset Drills
if 'drills' in self.tools[tool]:
new_drills = []
for drill in self.tools[tool]['drills']:
new_drills.append(affinity.rotate(drill, angle, origin=orig))
self.tools[tool]['drills'] = new_drills
# rotate solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry'])
# Offset Slots
if 'slots' in self.tools[tool]:
new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.rotate(slot[0], angle, origin=orig)
new_stop = affinity.rotate(slot[1], angle, origin=orig)
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Slots
for slot in self.slots:
slot['stop'] = affinity.rotate(slot['stop'], angle, origin=(px, py))
slot['start'] = affinity.rotate(slot['start'], angle, origin=(px, py))
# Offset solid_geometry
self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry'], origin=orig)
# update status display
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
self.create_geometry()
self.app.proc_container.new_text = ''
@ -1534,8 +1578,8 @@ class Excellon(Geometry):
except TypeError:
self.tools[tool]['solid_geometry'] = [res]
if factor is None:
self.tools[tool]['C'] += distance
self.tools[tool]['tooldia'] += distance
else:
self.tools[tool]['C'] *= distance
self.tools[tool]['tooldia'] *= distance
self.create_geometry()

View File

@ -613,9 +613,9 @@ class DblSidedTool(AppTool):
return
tools = {}
tools["1"] = {}
tools["1"]["C"] = dia
tools["1"]['solid_geometry'] = []
tools[1] = {}
tools[1]["tooldia"] = dia
tools[1]['solid_geometry'] = []
# holes = self.alignment_holes.get_value()
holes = eval('[{}]'.format(self.alignment_holes.text()))
@ -624,21 +624,16 @@ class DblSidedTool(AppTool):
"Add them and retry."))
return
drills = []
for hole in holes:
point = Point(hole)
point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
drills.append({"point": point, "tool": "1"})
drills.append({"point": point_mirror, "tool": "1"})
tools["1"]['solid_geometry'].append(point)
tools["1"]['solid_geometry'].append(point_mirror)
tools[1]['drills'] = [point, point_mirror]
tools[1]['solid_geometry'].append(point)
tools[1]['solid_geometry'].append(point_mirror)
def obj_init(obj_inst, app_inst):
obj_inst.tools = tools
obj_inst.drills = drills
obj_inst.create_geometry()
obj_inst.source_file = app_inst.export_excellon(obj_name=obj_inst.options['name'], local_use=obj_inst,
filename=None, use_thread=False)

View File

@ -172,7 +172,6 @@ class ToolDrilling(AppTool, Excellon):
# ############################ SIGNALS ########################################
# #############################################################################
self.ui.object_combo.currentIndexChanged.connect(self.on_object_changed)
self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
self.ui.generate_cnc_button.clicked.connect(self.on_cnc_button_click)
self.ui.tools_table.drag_drop_sig.connect(self.rebuild_ui)
@ -365,8 +364,6 @@ class ToolDrilling(AppTool, Excellon):
if opt_key.find('geometry_') == 0:
self.default_data[opt_key] = deepcopy(opt_val)
self.on_object_changed()
self.obj_name = ""
self.excellon_obj = None
@ -382,6 +379,16 @@ class ToolDrilling(AppTool, Excellon):
self.ui.operation_radio.set_value("drill")
self.ui.operation_radio.setEnabled(False)
self.on_object_changed()
if self.excellon_obj:
self.build_ui()
try:
self.ui.object_combo.currentIndexChanged.disconnect()
except (AttributeError, TypeError):
pass
self.ui.object_combo.currentIndexChanged.connect(self.on_object_changed)
def rebuild_ui(self):
# read the table tools uid
current_uid_list = []
@ -419,17 +426,8 @@ class ToolDrilling(AppTool, Excellon):
if self.excellon_obj:
self.ui.exc_param_frame.setDisabled(False)
sort = []
for k, v in list(self.excellon_obj.tools.items()):
sort.append((k, float('%.*f' % (self.decimals, float(v.get('C'))))))
order = self.ui.order_radio.get_value()
if order == 'fwd':
sorted_tools = sorted(sort, key=lambda t1: t1[1])
elif order == 'rev':
sorted_tools = sorted(sort, key=lambda t1: t1[1], reverse=True)
else:
sorted_tools = sort
tools = [i[0] for i in sorted_tools]
tools = [k for k in self.excellon_tools]
else:
tools = []
@ -438,30 +436,23 @@ class ToolDrilling(AppTool, Excellon):
self.ui.tools_table.setRowCount(n + 2)
self.tool_row = 0
new_options = {}
if self.excellon_obj:
for opt in self.excellon_obj.options:
new_options[opt] = self.excellon_obj.options[opt]
for tool_no in tools:
# add the data dictionary for each tool with the default values
self.tools[tool_no]['data'] = deepcopy(new_options)
drill_cnt = 0 # variable to store the nr of drills per tool
slot_cnt = 0 # variable to store the nr of slots per tool
# Find no of drills for the current tool
for drill in self.drills:
if drill['tool'] == tool_no:
drill_cnt += 1
try:
drill_cnt = len(self.excellon_tools[tool_no]["drills"])
except KeyError:
drill_cnt = 0
self.tot_drill_cnt += drill_cnt
# Find no of slots for the current tool
for slot in self.slots:
if slot['tool'] == tool_no:
slot_cnt += 1
try:
slot_cnt = len(self.excellon_tools[tool_no]["slots"])
except KeyError:
slot_cnt = 0
self.tot_slot_cnt += slot_cnt
# Tool name/id
@ -470,7 +461,7 @@ class ToolDrilling(AppTool, Excellon):
self.ui.tools_table.setItem(self.tool_row, 0, exc_id_item)
# Tool Diameter
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.tools[tool_no]['C']))
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.excellon_tools[tool_no]['tooldia']))
dia_item.setFlags(QtCore.Qt.ItemIsEnabled)
self.ui.tools_table.setItem(self.tool_row, 1, dia_item)
@ -496,6 +487,8 @@ class ToolDrilling(AppTool, Excellon):
# add a last row with the Total number of drills
empty_1 = QtWidgets.QTableWidgetItem('')
empty_1.setFlags(~QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
empty_1_1 = QtWidgets.QTableWidgetItem('')
empty_1_1.setFlags(~QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
label_tot_drill_count = QtWidgets.QTableWidgetItem(_('Total Drills'))
label_tot_drill_count.setFlags(QtCore.Qt.ItemIsEnabled)
@ -506,6 +499,7 @@ class ToolDrilling(AppTool, Excellon):
self.ui.tools_table.setItem(self.tool_row, 0, empty_1)
self.ui.tools_table.setItem(self.tool_row, 1, label_tot_drill_count)
self.ui.tools_table.setItem(self.tool_row, 2, tot_drill_count) # Total number of drills
self.ui.tools_table.setItem(self.tool_row, 4, empty_1_1)
font = QtGui.QFont()
font.setBold(True)
@ -642,35 +636,15 @@ class ToolDrilling(AppTool, Excellon):
self.excellon_obj = self.app.collection.get_by_name(self.obj_name)
except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name)))
return "Could not retrieve object: %s with error: %s" % (self.obj_name, str(e))
return
if self.excellon_obj is None:
self.ui.exc_param_frame.setDisabled(True)
else:
self.ui.exc_param_frame.setDisabled(False)
sort = []
for k, v in list(self.excellon_obj.tools.items()):
sort.append((k, float('%.*f' % (self.decimals, float(v.get('C'))))))
dias = [i[0] for i in sort]
self.excellon_tools = self.excellon_obj.tools
if not dias:
log.error("At least one tool diameter needed. Excellon object might be empty.")
return
tooluid = 0
self.excellon_tools.clear()
for tool_dia in dias:
tooluid += 1
self.excellon_tools.update({
int(tooluid): {
'tooldia': float('%.*f' % (self.decimals, tool_dia)),
'data': deepcopy(self.default_data),
'solid_geometry': []
}
})
self.build_ui()
self.build_ui()
def ui_connect(self):
@ -809,7 +783,7 @@ class ToolDrilling(AppTool, Excellon):
item = self.ui.tools_table.item(c_row, 3)
if type(item) is not None:
tooluid = item.text()
self.storage_to_form(self.drilling_tools[str(tooluid)]['data'])
self.storage_to_form(self.excellon_tools[str(tooluid)]['data'])
else:
self.blockSignals(False)
return
@ -845,8 +819,9 @@ class ToolDrilling(AppTool, Excellon):
:return: None
:rtype:
"""
if self.ui.tools_table.rowCount() == 0:
if self.ui.tools_table.rowCount() == 2:
# there is no tool in tool table so we can't save the GUI elements values to storage
# Excellon Tool Table has 2 rows by default
return
self.blockSignals(True)
@ -862,7 +837,7 @@ class ToolDrilling(AppTool, Excellon):
row = 0
tooluid_item = int(self.ui.tools_table.item(row, 3).text())
for tooluid_key, tooluid_val in self.iso_tools.items():
for tooluid_key, tooluid_val in self.excellon_tools.items():
if int(tooluid_key) == tooluid_item:
new_option_value = self.form_fields[option_changed].get_value()
if option_changed in tooluid_val:
@ -1034,7 +1009,7 @@ class ToolDrilling(AppTool, Excellon):
sort = []
for k, v in self.tools.items():
sort.append((k, v.get('C')))
sort.append((k, v.get('tooldia')))
sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all":
@ -1150,7 +1125,7 @@ class ToolDrilling(AppTool, Excellon):
sort = []
for k, v in self.tools.items():
sort.append((k, v.get('C')))
sort.append((k, v.get('tooldia')))
sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all":

View File

@ -530,7 +530,7 @@ class ToolExtractDrills(AppTool):
tool_in_drills = False
for tool, tool_val in tools.items():
if abs(float('%.*f' % (self.decimals, tool_val["C"])) - float('%.*f' % (self.decimals, dia))) < \
if abs(float('%.*f' % (self.decimals, tool_val["tooldia"])) - float('%.*f' % (self.decimals, dia))) < \
(10 ** -self.decimals):
tool_in_drills = tool
@ -615,7 +615,7 @@ class ToolExtractDrills(AppTool):
tool_in_drills = False
for tool, tool_val in tools.items():
if abs(float('%.*f' % (self.decimals, tool_val["C"])) - float('%.*f' % (self.decimals, dia))) < \
if abs(float('%.*f' % (self.decimals, tool_val["tooldia"])) - float('%.*f' % (self.decimals, dia))) < \
(10 ** -self.decimals):
tool_in_drills = tool

View File

@ -397,16 +397,14 @@ class Properties(AppTool):
slot_cnt = 0 # variable to store the nr of slots per tool
# Find no of drills for the current tool
for drill in obj.drills:
if drill['tool'] == tool:
drill_cnt += 1
if 'drills' in value and value['drills']:
drill_cnt = len(value['drills'])
tot_drill_cnt += drill_cnt
# Find no of slots for the current tool
for slot in obj.slots:
if slot['tool'] == tool:
slot_cnt += 1
if 'slots' in value and value['slots']:
slot_cnt = len(value['slots'])
tot_slot_cnt += slot_cnt
@ -414,7 +412,7 @@ class Properties(AppTool):
toolid,
[
_('Diameter'),
'%.*f %s' % (self.decimals, value['C'], self.app.defaults['units'].lower())
'%.*f %s' % (self.decimals, value['tooldia'], self.app.defaults['units'].lower())
],
True
)

View File

@ -558,7 +558,7 @@ class ToolPunchGerber(AppTool):
# make it work only for Gerber Flashes who are Points in 'follow'
if 'solid' in elem and isinstance(elem['follow'], Point):
for drill in exc_obj.drills:
clear_apid_size = exc_obj.tools[drill['tool']]['C']
clear_apid_size = exc_obj.tools[drill['tool']]['tooldia']
# since there may be drills that do not drill into a pad we test only for drills in a pad
if drill['point'].within(elem['solid']):

View File

@ -2314,7 +2314,20 @@ class App(QtCore.QObject):
self.ui.tool_scroll_area.takeWidget()
# delete the old object (the source object) if it was an empty one
if len(edited_obj.drills) == 0 and len(edited_obj.slots) == 0:
# find if we have drills:
has_drills = None
for tt in edited_obj.tools:
if 'drills' in edited_obj.tools[tt] and edited_obj.tools[tt]['drills']:
has_drills = True
break
# find if we have slots:
has_slots = None
for tt in edited_obj.tools:
if 'slots' in edited_obj.tools[tt] and edited_obj.tools[tt]['slots']:
has_slots = True
slots_in_file = 1
break
if has_drills is None and has_slots is None:
old_name = edited_obj.options['name']
self.collection.delete_by_name(name=old_name)
self.inform.emit('[success] %s' % _("Editor exited. Editor content saved."))
@ -8230,11 +8243,11 @@ class App(QtCore.QObject):
for tool in obj.tools:
if eunits == 'METRIC':
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor,
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool),
dec=2)
else:
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor,
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool),
dec=4)
else:
@ -8247,13 +8260,15 @@ class App(QtCore.QObject):
for tool in obj.tools:
if eunits == 'METRIC':
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor,
tool=str(tool),
dec=2)
header += "T{tool}F00S00C{:.{dec}f}\n".format(
float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool),
dec=2)
else:
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor,
tool=str(tool),
dec=4)
header += "T{tool}F00S00C{:.{dec}f}\n".format(
float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool),
dec=4)
else:
has_slots, excellon_code = obj.export_excellon(ewhole, efract,
form='ndec', e_zeros='TZ', factor=factor,
@ -8263,13 +8278,15 @@ class App(QtCore.QObject):
for tool in obj.tools:
if eunits == 'METRIC':
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor,
tool=str(tool),
dec=2)
header += "T{tool}F00S00C{:.{dec}f}\n".format(
float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool),
dec=2)
else:
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor,
tool=str(tool),
dec=4)
header += "T{tool}F00S00C{:.{dec}f}\n".format(
float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool),
dec=4)
header += '%\n'
footer = 'M30\n'

204
camlib.py
View File

@ -2555,8 +2555,6 @@ class CNCjob(Geometry):
# here store the routing time
self.routing_time = 0.0
# used for creating drill CCode geometry; will be updated in the generate_from_excellon_by_tool()
self.exc_drills = None
# store here the Excellon source object tools to be accessible locally
self.exc_tools = None
@ -2710,8 +2708,7 @@ class CNCjob(Geometry):
:rtype: None
"""
# create a local copy of the exobj.drills so it can be used for creating drill CCode geometry
self.exc_drills = deepcopy(exobj.drills)
# create a local copy of the exobj.tools so it can be used for creating drill CCode geometry
self.exc_tools = deepcopy(exobj.tools)
# the Excellon GCode preprocessor will use this info in the start_code() method
@ -2774,8 +2771,8 @@ class CNCjob(Geometry):
# sorted_tools = sorted(exobj.tools.items(), key=lambda t1: t1['C'])
sort = []
for k, v in list(exobj.tools.items()):
sort.append((k, v.get('C')))
for k, v in list(self.exc_tools.items()):
sort.append((int(k), v['tooldia']))
sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all":
@ -2783,7 +2780,7 @@ class CNCjob(Geometry):
log.debug("Tools 'all' and sorted are: %s" % str(tools))
else:
selected_tools = [x.strip() for x in tools.split(",")] # we strip spaces and also separate the tools by ','
selected_tools = [t1 for t1 in selected_tools if t1 in selected_tools]
selected_tools = [int(t1) for t1 in selected_tools if t1 in selected_tools]
# Create a sorted list of selected tools from the sorted_tools list
tools = [i for i, j in sorted_tools for k in selected_tools if i == k]
@ -2803,18 +2800,20 @@ class CNCjob(Geometry):
for it in sorted_tools:
for to_ol in tools:
if to_ol == it[0]:
drill_no = 0
sol_geo = []
for dr in exobj.drills:
if dr['tool'] == it[0]:
drill_no += 1
sol_geo.append(dr['point'])
drill_no = 0
if 'drills' in exobj.tools[to_ol]:
drill_no = len(exobj.tools[to_ol]['drills'])
for drill in exobj.tools[to_ol]['drills']:
sol_geo.append(drill.buffer((it[1] / 2.0), resolution=self.geo_steps_per_circle))
slot_no = 0
for dr in exobj.slots:
if dr['tool'] == it[0]:
slot_no += 1
start = (dr['start'].x, dr['start'].y)
stop = (dr['stop'].x, dr['stop'].y)
if 'slots' in exobj.tools[to_ol]:
slot_no = len(exobj.tools[to_ol]['slots'])
for slot in exobj.tools[to_ol]['slots']:
start = (slot[0].x, slot[0].y)
stop = (slot[1].x, slot[1].y)
sol_geo.append(
LineString([start, stop]).buffer((it[1] / 2.0), resolution=self.geo_steps_per_circle)
)
@ -2850,25 +2849,26 @@ class CNCjob(Geometry):
# Points (Group by tool): a dictionary of shapely Point geo elements grouped by tool number
points = {}
for drill in exobj.drills:
for tool, tool_dict in self.exc_tools.items():
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
if drill['tool'] in tools:
try:
points[drill['tool']].append(drill['point'])
except KeyError:
points[drill['tool']] = [drill['point']]
if 'drills' in tool_dict and tool_dict['drills']:
for drill_pt in tool_dict['drills']:
try:
points[tool].append(drill_pt)
except KeyError:
points[tool] = [drill_pt]
# log.debug("Found %d drills." % len(points))
log.debug("Found %d TOOLS." % len(points))
# check if there are drill points in the exclusion areas.
# If we find any within the exclusion areas return 'fail'
for tool in points:
for pt in points[tool]:
for area in self.app.exc_areas.exclusion_areas_storage:
pt_buf = pt.buffer(exobj.tools[tool]['C'] / 2.0)
pt_buf = pt.buffer(self.exc_tools[tool]['tooldia'] / 2.0)
if pt_buf.within(area['shape']) or pt_buf.intersects(area['shape']):
self.app.inform.emit("[ERROR_NOTCL] %s" % _("Failed. Drill points inside the exclusion zones."))
return 'fail'
@ -2954,22 +2954,28 @@ class CNCjob(Geometry):
used_excellon_optimization_type = self.excellon_optimization_type
if used_excellon_optimization_type == 'M':
log.debug("Using OR-Tools Metaheuristic Guided Local Search drill path optimization.")
if exobj.drills:
has_drills = None
for tool, tool_dict in self.exc_tools.items():
if 'drills' in tool_dict and tool_dict['drills']:
has_drills = True
break
if has_drills:
for tool in tools:
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
self.tool = tool
self.postdata['toolC'] = exobj.tools[tool]["C"]
self.tooldia = exobj.tools[tool]["C"]
self.tooldia = self.exc_tools[tool]["tooldia"]
self.postdata['toolC'] = self.tooldia
if self.use_ui:
self.z_feedrate = exobj.tools[tool]['data']['feedrate_z']
self.feedrate = exobj.tools[tool]['data']['feedrate']
self.z_feedrate = self.exc_tools[tool]['data']['feedrate_z']
self.feedrate = self.exc_tools[tool]['data']['feedrate']
gcode += self.doformat(p.z_feedrate_code)
self.z_cut = exobj.tools[tool]['data']['cutz']
self.z_cut = self.exc_tools[tool]['data']['cutz']
if self.machinist_setting == 0:
if self.z_cut > 0:
@ -2991,12 +2997,12 @@ class CNCjob(Geometry):
old_zcut = deepcopy(self.z_cut)
self.z_move = exobj.tools[tool]['data']['travelz']
self.spindlespeed = exobj.tools[tool]['data']['spindlespeed']
self.dwell = exobj.tools[tool]['data']['dwell']
self.dwelltime = exobj.tools[tool]['data']['dwelltime']
self.multidepth = exobj.tools[tool]['data']['multidepth']
self.z_depthpercut = exobj.tools[tool]['data']['depthperpass']
self.z_move = self.exc_tools[tool]['data']['travelz']
self.spindlespeed = self.exc_tools[tool]['data']['spindlespeed']
self.dwell = self.exc_tools[tool]['data']['dwell']
self.dwelltime = self.exc_tools[tool]['data']['dwelltime']
self.multidepth = self.exc_tools[tool]['data']['multidepth']
self.z_depthpercut = self.exc_tools[tool]['data']['depthperpass']
else:
old_zcut = deepcopy(self.z_cut)
@ -3085,7 +3091,7 @@ class CNCjob(Geometry):
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
current_tooldia = float('%.*f' % (self.decimals, float(exobj.tools[tool]["C"])))
current_tooldia = float('%.*f' % (self.decimals, float(self.exc_tools[tool]["tooldia"])))
self.app.inform.emit(
'%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
@ -3098,7 +3104,7 @@ class CNCjob(Geometry):
# because the values for Z offset are created in build_ui()
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
try:
z_offset = float(exobj.tools[tool]['data']['offset']) * (-1)
z_offset = float(self.exc_tools[tool]['data']['offset']) * (-1)
except KeyError:
z_offset = 0
self.z_cut = z_offset + old_zcut
@ -3138,7 +3144,7 @@ class CNCjob(Geometry):
gcode += self.doformat(p.lift_code, x=locx, y=locy)
# restore z_move
self.z_move = exobj.tools[tool]['data']['travelz']
self.z_move = self.exc_tools[tool]['data']['travelz']
else:
if prev_z is not None:
# move to next point
@ -3146,7 +3152,7 @@ class CNCjob(Geometry):
# we assume that previously the z_move was altered therefore raise to
# the travel_z (z_move)
self.z_move = exobj.tools[tool]['data']['travelz']
self.z_move = self.exc_tools[tool]['data']['travelz']
gcode += self.doformat(p.lift_code, x=locx, y=locy)
else:
# move to next point
@ -3218,21 +3224,28 @@ class CNCjob(Geometry):
if used_excellon_optimization_type == 'B':
log.debug("Using OR-Tools Basic drill path optimization.")
if exobj.drills:
has_drills = None
for tool, tool_dict in self.exc_tools.items():
if 'drills' in tool_dict and tool_dict['drills']:
has_drills = True
break
if has_drills:
for tool in tools:
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
self.tool = tool
self.postdata['toolC'] = exobj.tools[tool]["C"]
self.tooldia = exobj.tools[tool]["C"]
self.tooldia = self.exc_tools[tool]["tooldia"]
self.postdata['toolC'] = self.tooldia
if self.use_ui:
self.z_feedrate = exobj.tools[tool]['data']['feedrate_z']
self.feedrate = exobj.tools[tool]['data']['feedrate']
self.z_feedrate = self.exc_tools[tool]['data']['feedrate_z']
self.feedrate = self.exc_tools[tool]['data']['feedrate']
gcode += self.doformat(p.z_feedrate_code)
self.z_cut = exobj.tools[tool]['data']['cutz']
self.z_cut = self.exc_tools[tool]['data']['cutz']
if self.machinist_setting == 0:
if self.z_cut > 0:
@ -3254,13 +3267,13 @@ class CNCjob(Geometry):
old_zcut = deepcopy(self.z_cut)
self.z_move = exobj.tools[tool]['data']['travelz']
self.z_move = self.exc_tools[tool]['data']['travelz']
self.spindlespeed = exobj.tools[tool]['data']['spindlespeed']
self.dwell = exobj.tools[tool]['data']['dwell']
self.dwelltime = exobj.tools[tool]['data']['dwelltime']
self.multidepth = exobj.tools[tool]['data']['multidepth']
self.z_depthpercut = exobj.tools[tool]['data']['depthperpass']
self.spindlespeed = self.exc_tools[tool]['data']['spindlespeed']
self.dwell = self.exc_tools[tool]['data']['dwell']
self.dwelltime = self.exc_tools[tool]['data']['dwelltime']
self.multidepth = self.exc_tools[tool]['data']['multidepth']
self.z_depthpercut = self.exc_tools[tool]['data']['depthperpass']
else:
old_zcut = deepcopy(self.z_cut)
@ -3338,7 +3351,7 @@ class CNCjob(Geometry):
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
current_tooldia = float('%.*f' % (self.decimals, float(exobj.tools[tool]["C"])))
current_tooldia = float('%.*f' % (self.decimals, float(self.exc_tools[tool]["tooldia"])))
self.app.inform.emit(
'%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
@ -3351,7 +3364,7 @@ class CNCjob(Geometry):
# because the values for Z offset are created in build_ui()
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
try:
z_offset = float(exobj.tools[tool]['data']['offset']) * (-1)
z_offset = float(self.exc_tools[tool]['data']['offset']) * (-1)
except KeyError:
z_offset = 0
self.z_cut = z_offset + old_zcut
@ -3390,7 +3403,7 @@ class CNCjob(Geometry):
gcode += self.doformat(p.lift_code, x=locx, y=locy)
# restore z_move
self.z_move = exobj.tools[tool]['data']['travelz']
self.z_move = self.exc_tools[tool]['data']['travelz']
else:
if prev_z is not None:
# move to next point
@ -3398,7 +3411,7 @@ class CNCjob(Geometry):
# we assume that previously the z_move was altered therefore raise to
# the travel_z (z_move)
self.z_move = exobj.tools[tool]['data']['travelz']
self.z_move = self.exc_tools[tool]['data']['travelz']
gcode += self.doformat(p.lift_code, x=locx, y=locy)
else:
# move to next point
@ -3473,22 +3486,29 @@ class CNCjob(Geometry):
if used_excellon_optimization_type == 'T':
log.debug("Using Travelling Salesman drill path optimization.")
has_drills = None
for tool, tool_dict in self.exc_tools.items():
if 'drills' in tool_dict and tool_dict['drills']:
has_drills = True
break
for tool in tools:
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
if exobj.drills:
if has_drills:
self.tool = tool
self.postdata['toolC'] = exobj.tools[tool]["C"]
self.tooldia = exobj.tools[tool]["C"]
self.tooldia = self.exc_tools[tool]["tooldia"]
self.postdata['toolC'] = self.tooldia
if self.use_ui:
self.z_feedrate = exobj.tools[tool]['data']['feedrate_z']
self.feedrate = exobj.tools[tool]['data']['feedrate']
self.z_feedrate = self.exc_tools[tool]['data']['feedrate_z']
self.feedrate = self.exc_tools[tool]['data']['feedrate']
gcode += self.doformat(p.z_feedrate_code)
self.z_cut = exobj.tools[tool]['data']['cutz']
self.z_cut = self.exc_tools[tool]['data']['cutz']
if self.machinist_setting == 0:
if self.z_cut > 0:
@ -3510,12 +3530,12 @@ class CNCjob(Geometry):
old_zcut = deepcopy(self.z_cut)
self.z_move = exobj.tools[tool]['data']['travelz']
self.spindlespeed = exobj.tools[tool]['data']['spindlespeed']
self.dwell = exobj.tools[tool]['data']['dwell']
self.dwelltime = exobj.tools[tool]['data']['dwelltime']
self.multidepth = exobj.tools[tool]['data']['multidepth']
self.z_depthpercut = exobj.tools[tool]['data']['depthperpass']
self.z_move = self.exc_tools[tool]['data']['travelz']
self.spindlespeed = self.exc_tools[tool]['data']['spindlespeed']
self.dwell = self.exc_tools[tool]['data']['dwell']
self.dwelltime = self.exc_tools[tool]['data']['dwelltime']
self.multidepth = self.exc_tools[tool]['data']['multidepth']
self.z_depthpercut = self.exc_tools[tool]['data']['depthperpass']
else:
old_zcut = deepcopy(self.z_cut)
@ -3536,7 +3556,7 @@ class CNCjob(Geometry):
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
current_tooldia = float('%.*f' % (self.decimals, float(exobj.tools[tool]["C"])))
current_tooldia = float('%.*f' % (self.decimals, float(self.exc_tools[tool]["tooldia"])))
self.app.inform.emit(
'%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
@ -3549,7 +3569,7 @@ class CNCjob(Geometry):
# because the values for Z offset are created in build_ui()
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
try:
z_offset = float(exobj.tools[tool]['data']['offset']) * (-1)
z_offset = float(self.exc_tools[tool]['data']['offset']) * (-1)
except KeyError:
z_offset = 0
self.z_cut = z_offset + old_zcut
@ -3593,7 +3613,7 @@ class CNCjob(Geometry):
gcode += self.doformat(p.lift_code, x=locx, y=locy)
# restore z_move
self.z_move = exobj.tools[tool]['data']['travelz']
self.z_move = self.exc_tools[tool]['data']['travelz']
else:
if prev_z is not None:
# move to next point
@ -3601,7 +3621,7 @@ class CNCjob(Geometry):
# we assume that previously the z_move was altered therefore raise to
# the travel_z (z_move)
self.z_move = exobj.tools[tool]['data']['travelz']
self.z_move = self.exc_tools[tool]['data']['travelz']
gcode += self.doformat(p.lift_code, x=locx, y=locy)
else:
# move to next point
@ -3687,6 +3707,7 @@ class CNCjob(Geometry):
self.routing_time += lift_time + traveled_time
self.gcode = gcode
self.app.inform.emit(_("Finished G-Code generation..."))
return 'OK'
@ -4914,22 +4935,27 @@ class CNCjob(Geometry):
)
# find the drill diameter knowing the drill coordinates
for pt_dict in self.exc_drills:
point_in_dict_coords = (
float('%.*f' % (self.decimals, pt_dict['point'].x)),
float('%.*f' % (self.decimals, pt_dict['point'].y))
)
if point_in_dict_coords == current_drill_point_coords:
tool = pt_dict['tool']
dia = self.exc_tools[tool]['C']
kind = ['C', 'F']
geometry.append(
{
"geom": Point(current_drill_point_coords).buffer(dia / 2.0).exterior,
"kind": kind
}
)
break
break_loop = False
for tool, tool_dict in self.exc_tools.items():
if 'drills' in tool_dict:
for drill_pt in tool_dict['drills']:
point_in_dict_coords = (
float('%.*f' % (self.decimals, drill_pt.x)),
float('%.*f' % (self.decimals, drill_pt.y))
)
if point_in_dict_coords == current_drill_point_coords:
dia = self.exc_tools[tool]['tooldia']
kind = ['C', 'F']
geometry.append(
{
"geom": Point(current_drill_point_coords).buffer(dia / 2.0).exterior,
"kind": kind
}
)
break_loop = True
break
if break_loop:
break
if 'G' in gobj:
current['G'] = int(gobj['G'])

View File

@ -48,7 +48,7 @@ class Berta_CNC(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():

View File

@ -38,7 +38,7 @@ class ISEL_CNC(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():

View File

@ -38,7 +38,7 @@ class ISEL_ICP_CNC(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n;TOOLS DIAMETER: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + '\n'
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + '\n'
gcode += '\n;FEEDRATE Z: \n'
for tool, val in p['exc_tools'].items():

View File

@ -40,7 +40,7 @@ class Marlin(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n;TOOLS DIAMETER: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + '\n'
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + '\n'
gcode += '\n;FEEDRATE Z: \n'
for tool, val in p['exc_tools'].items():

View File

@ -40,7 +40,7 @@ class Repetier(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n;TOOLS DIAMETER: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + '\n'
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + '\n'
gcode += '\n;FEEDRATE Z: \n'
for tool, val in p['exc_tools'].items():

View File

@ -39,7 +39,7 @@ class Toolchange_Custom(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():

View File

@ -39,7 +39,7 @@ class Toolchange_Manual(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():

View File

@ -39,7 +39,7 @@ class Toolchange_Probe_MACH3(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():

View File

@ -40,7 +40,7 @@ class default(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():

View File

@ -40,7 +40,7 @@ class grbl_11(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():

View File

@ -39,7 +39,7 @@ class line_xyz(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():

View File

@ -198,25 +198,23 @@ class TclCommandDrillcncjob(TclCommandSignaled):
if tools == 'all':
sort = []
for k, v in list(obj.tools.items()):
sort.append((k, v.get('C')))
sort.append((k, v.get('tooldia')))
sorted_tools = sorted(sort, key=lambda t1: t1[1])
use_tools = [i[0] for i in sorted_tools]
for tool_no in use_tools:
tool_dia_used = obj.tools[tool_no]['C']
tool_dia_used = obj.tools[tool_no]['tooldia']
drill_cnt = 0 # variable to store the nr of drills per tool
slot_cnt = 0 # variable to store the nr of slots per tool
# Find no of drills for the current tool
for drill in obj.drills:
if drill['tool'] == tool_no:
drill_cnt += 1
if 'drills' in obj.tools[tool_no] and obj.tools[tool_no]['drills']:
drill_cnt = len(obj.tools[tool_no]['drills'])
# Find no of slots for the current tool
for slot in obj.slots:
if slot['tool'] == tool_no:
slot_cnt += 1
if 'slots' in obj.tools[tool_no] and obj.tools[tool_no]['slots']:
slot_cnt = len(obj.tools[tool_no]['slots'])
used_tools_info.append([str(tool_no), str(tool_dia_used), str(drill_cnt), str(slot_cnt)])