- Tool Drilling - fixed incorrect annotations in CNCJob objects generated; one drawback is that now each tool (when Toolchange is ON) has it's own annotation order which lead to overlapping in the start point of one tool and the end of previous tool

- Tool Drilling - refactoring methods and optimizations
This commit is contained in:
Marius Stanciu 2020-07-10 17:05:32 +03:00 committed by Marius
parent 24192540d7
commit f53ffe54d9
5 changed files with 239 additions and 203 deletions

View File

@ -13,6 +13,8 @@ CHANGELOG for FlatCAM beta
- optimized the CNCJob UI to look like other parts of the app
- in Gerber and Excellon UI added buttons to start the Editor
- in all Editors Selected Tab added a button to Exit the Editor
- Tool Drilling - fixed incorrect annotations in CNCJob objects generated; one drawback is that now each tool (when Toolchange is ON) has it's own annotation order which lead to overlapping in the start point of one tool and the end of previous tool
- Tool Drilling - refactoring methods and optimizations
9.07.2020

View File

@ -1137,13 +1137,6 @@ class CNCJobObject(FlatCAMObj, CNCjob):
dia_plot = [float(el) for el in self.options["tooldia"].split(',') if el != ''][0]
self.plot2(tooldia=dia_plot, obj=self, visible=visible, kind=kind)
else:
# multiple tools usage
if self.cnc_tools:
for tooluid_key in self.cnc_tools:
tooldia = float('%.*f' % (self.decimals, float(self.cnc_tools[tooluid_key]['tooldia'])))
gcode_parsed = self.cnc_tools[tooluid_key]['gcode_parsed']
self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind)
# I do this so the travel lines thickness will reflect the tool diameter
# may work only for objects created within the app and not Gcode imported from elsewhere for which we
# don't know the origin
@ -1156,6 +1149,13 @@ class CNCJobObject(FlatCAMObj, CNCjob):
continue
# gcode_parsed = self.gcode_parsed
self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind)
else:
# multiple tools usage
if self.cnc_tools:
for tooluid_key in self.cnc_tools:
tooldia = float('%.*f' % (self.decimals, float(self.cnc_tools[tooluid_key]['tooldia'])))
gcode_parsed = self.cnc_tools[tooluid_key]['gcode_parsed']
self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind)
self.shapes.redraw()
except (ObjectDeleted, AttributeError):

View File

@ -45,7 +45,7 @@ class ToolDrilling(AppTool, Excellon):
def __init__(self, app):
self.app = app
self.decimals = self.app.decimals
self.dec_format = self.app.dec_format
AppTool.__init__(self, app)
Excellon.__init__(self, geo_steps_per_circle=self.app.defaults["geometry_circle_steps"])
@ -465,7 +465,7 @@ class ToolDrilling(AppTool, Excellon):
# order the tools by tool diameter if it's the case
sorted_tools = []
for k, v in self.excellon_tools.items():
sorted_tools.append(float('%.*f' % (self.decimals, float(v['tooldia']))))
sorted_tools.append(self.dec_format(float(v['tooldia'])))
order = self.t_ui.order_radio.get_value()
if order == 'fwd':
@ -480,7 +480,7 @@ class ToolDrilling(AppTool, Excellon):
new_tools = {}
for tooldia in sorted_tools:
for old_tool in self.excellon_tools:
if float('%.*f' % (self.decimals, float(self.excellon_tools[old_tool]['tooldia']))) == tooldia:
if self.dec_format(float(self.excellon_tools[old_tool]['tooldia'])) == tooldia:
new_tools[new_id] = deepcopy(self.excellon_tools[old_tool])
new_id += 1
@ -524,7 +524,7 @@ class ToolDrilling(AppTool, Excellon):
self.t_ui.tools_table.setItem(self.tool_row, 0, exc_id_item)
# Tool Diameter
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.excellon_tools[tool_no]['tooldia']))
dia_item = QtWidgets.QTableWidgetItem(str(self.dec_format(self.excellon_tools[tool_no]['tooldia'])))
dia_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDragEnabled)
self.t_ui.tools_table.setItem(self.tool_row, 1, dia_item)
@ -1305,6 +1305,86 @@ class ToolDrilling(AppTool, Excellon):
drills_list.append(stop_pt)
return drills_list
def is_valid_excellon(self):
slots_as_drills = self.t_ui.drill_slots_cb.get_value()
has_drills = None
for tool_key, tool_dict in self.excellon_tools.items():
if 'drills' in tool_dict and tool_dict['drills']:
has_drills = True
break
has_slots = None
for tool_key, tool_dict in self.excellon_tools.items():
if 'slots' in tool_dict and tool_dict['slots']:
has_slots = True
break
if not has_drills:
if slots_as_drills and has_slots:
return True
else:
return False
def get_selected_tools_uid(self):
"""
Return a list of the selected tools UID from the Tool Table
"""
selected_uid = set()
for sel_it in self.t_ui.tools_table.selectedItems():
uid = int(self.t_ui.tools_table.item(sel_it.row(), 3).text())
selected_uid.add(uid)
return list(selected_uid)
def create_drill_points(self, selected_tools, selected_sorted_tools, convert_slots=False):
points = {}
# create drill points out of the drills locations
for tool_key, tl_dict in self.excellon_tools.items():
if tool_key in selected_tools:
if 'drills' in tl_dict and tl_dict['drills']:
for drill_pt in tl_dict['drills']:
try:
points[tool_key].append(drill_pt)
except KeyError:
points[tool_key] = [drill_pt]
log.debug("Found %d TOOLS with drills." % len(points))
# convert slots to a sequence of drills and add them to drill points
should_add_last_pt = self.t_ui.last_drill_cb.get_value()
if convert_slots:
for tool_key, tl_dict in self.excellon_tools.items():
if tool_key in selected_tools:
overlap = 1 - (self.t_ui.drill_overlap_entry.get_value() / 100.0)
drill_overlap = 0.0
for i in selected_sorted_tools:
if i[0] == tool_key:
slot_tool_dia = i[1]
drill_overlap = overlap * slot_tool_dia
break
new_drills = []
if 'slots' in tl_dict and tl_dict['slots']:
for slot in tl_dict['slots']:
new_drills += self.process_slot_as_drills(slot=slot, overlap=drill_overlap,
add_last_pt=should_add_last_pt)
if new_drills:
try:
points[tool_key] += new_drills
except Exception:
points[tool_key] = new_drills
log.debug("Found %d TOOLS with drills after converting slots to drills." % len(points))
return points
def check_intersection(self, points):
for tool_key in points:
for pt in points[tool_key]:
for area in self.app.exc_areas.exclusion_areas_storage:
pt_buf = pt.buffer(self.excellon_tools[tool_key]['tooldia'] / 2.0)
if pt_buf.within(area['shape']) or pt_buf.intersects(area['shape']):
return True
return False
def on_cnc_button_click(self):
obj_name = self.t_ui.object_combo.currentText()
toolchange = self.t_ui.toolchange_cb.get_value()
@ -1320,45 +1400,6 @@ class ToolDrilling(AppTool, Excellon):
self.app.inform.emit('[ERROR_NOTCL] %s.' % _("Object not found"))
return
slots_as_drills = self.t_ui.drill_slots_cb.get_value()
has_drills = None
for tool_key, tool_dict in self.excellon_tools.items():
if 'drills' in tool_dict and tool_dict['drills']:
has_drills = True
break
has_slots = None
for tool_key, tool_dict in self.excellon_tools.items():
if 'slots' in tool_dict and tool_dict['slots']:
has_slots = True
break
if not has_drills:
if slots_as_drills and has_slots:
pass
else:
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
"The loaded Excellon file has no drills ...")
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('The loaded Excellon file has no drills'))
return
# Get the tools from the Tool Table
selected_uid = set()
for sel_it in self.t_ui.tools_table.selectedItems():
uid = int(self.t_ui.tools_table.item(sel_it.row(), 3).text())
selected_uid.add(uid)
selected_tools_id = list(selected_uid)
if len(selected_tools_id) == 0:
# if there is a single tool in the table (remember that the last 2 rows are for totals and do not count in
# 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.t_ui.tools_table.rowCount() >= 3:
selected_tools_id.append(int(self.t_ui.tools_table.item(0, 3).text()))
else:
self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Please select one or more tools from the list and try again."))
return
xmin = obj.options['xmin']
ymin = obj.options['ymin']
xmax = obj.options['xmax']
@ -1367,6 +1408,27 @@ class ToolDrilling(AppTool, Excellon):
job_name = obj.options["name"] + "_cnc"
obj.pp_excellon_name = self.t_ui.pp_excellon_name_cb.get_value()
convert_slots = self.t_ui.drill_slots_cb.get_value()
if self.is_valid_excellon() is False:
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
"The loaded Excellon file has no drills ...")
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('The loaded Excellon file has no drills'))
return
# Get the tools from the Tool Table
selected_tools_id = self.get_selected_tools_uid()
if not selected_tools_id:
# if there is a single tool in the table (remember that the last 2 rows are for totals and do not count in
# 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.t_ui.tools_table.rowCount() >= 3:
selected_tools_id.append(int(self.t_ui.tools_table.item(0, 3).text()))
else:
msg = '[ERROR_NOTCL] %s' % _("Please select one or more tools from the list and try again.")
self.app.inform.emit(msg)
return
# #############################################################################################################
# #############################################################################################################
# TOOLS
@ -1400,59 +1462,14 @@ class ToolDrilling(AppTool, Excellon):
# #############################################################################################################
self.app.inform.emit(_("Creating a list of points to drill..."))
points = {}
for tool_key, tl_dict in self.excellon_tools.items():
if tool_key in sel_tools:
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
# points is a dictionary: keys are tools ad values are lists of Shapely Points
points = self.create_drill_points(selected_tools=sel_tools, selected_sorted_tools=sorted_tools,
convert_slots=convert_slots)
if 'drills' in tl_dict and tl_dict['drills']:
for drill_pt in tl_dict['drills']:
try:
points[tool_key].append(drill_pt)
except KeyError:
points[tool_key] = [drill_pt]
log.debug("Found %d TOOLS with drills." % len(points))
# add slots as drills
should_add_last_pt = self.t_ui.last_drill_cb.get_value()
if slots_as_drills:
for tool_key, tl_dict in self.excellon_tools.items():
if tool_key in sel_tools:
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
overlap = 1 - (self.t_ui.drill_overlap_entry.get_value() / 100.0)
drill_overlap = 0.0
for i in sorted_tools:
if i[0] == tool_key:
slot_tool_dia = i[1]
drill_overlap = overlap * slot_tool_dia
break
new_drills = []
if 'slots' in tl_dict and tl_dict['slots']:
for slot in tl_dict['slots']:
new_drills += self.process_slot_as_drills(slot=slot, overlap=drill_overlap,
add_last_pt=should_add_last_pt)
if new_drills:
try:
points[tool_key] += new_drills
except Exception:
points[tool_key] = new_drills
log.debug("Found %d TOOLS with drills after converting slots to drills." % len(points))
# check if there are drill points in the exclusion areas.
# If we find any within the exclusion areas return 'fail'
for tool_key in points:
for pt in points[tool_key]:
for area in self.app.exc_areas.exclusion_areas_storage:
pt_buf = pt.buffer(self.exc_tools[tool_key]['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'
# check if there are drill points in the exclusion areas (if any areas)
if self.app.exc_areas.exclusion_areas_storage and self.check_intersection(points) is True:
self.app.inform.emit("[ERROR_NOTCL] %s" % _("Failed. Drill points inside the exclusion zones."))
return 'fail'
# #############################################################################################################
# General Parameters
@ -1549,15 +1566,6 @@ class ToolDrilling(AppTool, Excellon):
# Prepprocessor
job_obj.pp_excellon_name = self.default_data["tools_drill_ppname_e"]
job_obj.pp_excellon = self.app.preprocessors[job_obj.pp_excellon_name]
p = job_obj.pp_excellon
job_obj.xy_toolchange = self.app.defaults["excellon_toolchangexy"]
if job_obj.xy_toolchange is not None:
job_obj.oldx = job_obj.xy_toolchange[0]
job_obj.oldy = job_obj.xy_toolchange[1]
else:
job_obj.oldx = 0.0
job_obj.oldy = 0.0
# get the tool_table items in a list of row items
tool_table_items = self.get_selected_tools_table_items()
@ -1565,58 +1573,70 @@ class ToolDrilling(AppTool, Excellon):
tool_table_items.insert(0, [_("Tool_nr"), _("Diameter"), _("Drills_Nr"), _("Slots_Nr")])
# ## Add properties to the object
job_obj.origin_kind = 'excellon'
job_obj.options['Tools_in_use'] = tool_table_items
job_obj.options['type'] = 'Excellon'
job_obj.options['ppname_e'] = obj.pp_excellon_name
job_obj.toolchange_xy_type = "excellon"
job_obj.coords_decimals = int(self.app.defaults["cncjob_coords_decimals"])
job_obj.fr_decimals = int(self.app.defaults["cncjob_fr_decimals"])
job_obj.options['xmin'] = xmin
job_obj.options['ymin'] = ymin
job_obj.options['xmax'] = xmax
job_obj.options['ymax'] = ymax
start_gcode = job_obj.doformat(p.start_code)
job_obj.origin_kind = 'excellon'
job_obj.toolchange_xy_type = "excellon"
job_obj.coords_decimals = int(self.app.defaults["cncjob_coords_decimals"])
job_obj.fr_decimals = int(self.app.defaults["cncjob_fr_decimals"])
job_obj.multitool = True
# first drill point
job_obj.xy_toolchange = self.app.defaults["excellon_toolchangexy"]
if job_obj.xy_toolchange is not None:
job_obj.oldx = job_obj.xy_toolchange[0]
job_obj.oldy = job_obj.xy_toolchange[1]
else:
job_obj.oldx = 0.0
job_obj.oldy = 0.0
first_drill_point = (job_obj.oldx, job_obj.oldy)
# ####################### TOOLCHANGE ######################################################
if toolchange is True:
add_start_gcode = True
for tool in sel_tools:
tool_points = points[tool]
used_tooldia = self.excellon_tools[tool]['tooldia']
if slots_as_drills is True:
if convert_slots is True:
nr_drills = len(points[tool])
nr_slots = 0
job_obj.exc_cnc_tools[used_tooldia]['nr_drills'] = nr_drills
job_obj.exc_cnc_tools[used_tooldia]['nr_slots'] = nr_slots
for line in range(1, len(job_obj.options['Tools_in_use'])):
if float('%.*f' % (self.decimals, float(job_obj.options['Tools_in_use'][line][1]))) == \
float('%.*f' % (self.decimals, used_tooldia)):
if self.dec_format(float(job_obj.options['Tools_in_use'][line][1])) == \
self.dec_format(used_tooldia):
job_obj.options['Tools_in_use'][line][2] = str(nr_drills)
job_obj.options['Tools_in_use'][line][3] = str(nr_slots)
tool_gcode = job_obj.gcode_from_excellon_by_tool(tool, tool_points, self.excellon_tools,
opt_type=used_excellon_optimization_type,
toolchange=True)
if add_start_gcode is True:
tool_gcode = start_gcode + tool_gcode
add_start_gcode = False
job_obj.exc_cnc_tools[used_tooldia]['gcode'] = tool_gcode
is_last_tool = True if tool == sel_tools[-1] else False
is_first_tool = True if tool == sel_tools[0] else False
tool_gcode, last_pt = job_obj.excellon_tool_gcode_gen(tool, tool_points, self.excellon_tools,
first_pt=first_drill_point,
is_first=is_first_tool,
is_last=is_last_tool,
opt_type=used_excellon_optimization_type,
toolchange=True)
tool_gcode_parsed = job_obj.excellon_tool_gcode_parse(used_tooldia,
start_pt=(job_obj.oldx, job_obj.oldy))
tool_gcode_parsed = job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode,
start_pt=first_drill_point)
first_drill_point = last_pt
# store the results
job_obj.exc_cnc_tools[used_tooldia]['gcode'] = tool_gcode
job_obj.exc_cnc_tools[used_tooldia]['gcode_parsed'] = tool_gcode_parsed
self.total_gcode += tool_gcode
self.total_gcode_parsed += tool_gcode_parsed
# ####################### NO TOOLCHANGE ######################################################
else:
tool_points = []
@ -1634,7 +1654,7 @@ class ToolDrilling(AppTool, Excellon):
# process all in one go with no toolchange and with only one tool
nr_drills = 0
nr_slots = 0
if slots_as_drills is False:
if convert_slots is False:
for line in range(1, len(tool_table_items)):
# we may have exception ValueError if there are no drills/slots for the current tool/line
try:
@ -1659,17 +1679,21 @@ class ToolDrilling(AppTool, Excellon):
tool_table_items.insert(0, [_("Tool_nr"), _("Diameter"), _("Drills_Nr"), _("Slots_Nr")])
job_obj.options['Tools_in_use'] = tool_table_items
tool_gcode = start_gcode
# tool_gcode = start_gcode
# TODO set the oldx and oldy to start values
# add a Toolchange event here to load the first tool
tool_gcode += job_obj.doformat(p.toolchange_code, toolchangexy=(job_obj.oldx, job_obj.oldy))
tool_gcode += job_obj.gcode_from_excellon_by_tool(used_tool, tool_points, self.excellon_tools,
opt_type=used_excellon_optimization_type,
toolchange=False)
job_obj.exc_cnc_tools[used_tooldia]['gcode'] = tool_gcode
# tool_gcode += job_obj.doformat(p.toolchange_code, toolchangexy=(job_obj.oldx, job_obj.oldy))
tool_gcode, __ = job_obj.excellon_tool_gcode_gen(used_tool, tool_points, self.excellon_tools,
first_pt=first_drill_point,
is_first=True,
is_last=True,
opt_type=used_excellon_optimization_type,
toolchange=True)
tool_gcode_parsed = job_obj.excellon_tool_gcode_parse(used_tooldia,
start_pt=(job_obj.oldx, job_obj.oldy))
tool_gcode_parsed = job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode,
start_pt=first_drill_point)
# store the results
job_obj.exc_cnc_tools[used_tooldia]['gcode'] = tool_gcode
job_obj.exc_cnc_tools[used_tooldia]['gcode_parsed'] = tool_gcode_parsed
self.total_gcode = tool_gcode

View File

@ -10351,6 +10351,14 @@ class App(QtCore.QObject):
except AttributeError:
log.debug("shell_message() is called before Shell Class is instantiated. The message is: %s", str(msg))
def dec_format(self, val, dec=None):
"""
Returns a formatted float value with a certain number of decimals
"""
dec_nr = dec if dec is not None else self.decimals
return float('%.*f' % (dec_nr, val))
class ArgsThread(QtCore.QObject):
open_signal = pyqtSignal(list)

122
camlib.py
View File

@ -2721,11 +2721,7 @@ class CNCjob(Geometry):
@staticmethod
def create_tool_data_array(points):
# Create the data.
loc_list = []
for pt in points:
loc_list.append((pt.coords.xy[0][0], pt.coords.xy[1][0]))
return loc_list
return [(pt.coords.xy[0][0], pt.coords.xy[1][0]) for pt in points]
def optimized_ortools_meta(self, locations, start=None):
optimized_path = []
@ -2890,13 +2886,16 @@ class CNCjob(Geometry):
self.app.inform.emit('[WARNING] %s.' % _("The Cut Z parameter is zero. There will be no cut, aborting"))
return 'fail'
def gcode_from_excellon_by_tool(self, tool, points, tools, opt_type='T', toolchange=False):
def excellon_tool_gcode_gen(self, tool, points, tools, first_pt, is_first=False, is_last=False, opt_type='T',
toolchange=False):
"""
Creates Gcode for this object from an Excellon object
for the specified tools.
:return: Tool GCode
:rtype: str
:return: A tuple made from tool_gcode and another tuple holding the coordinates of the last point
:rtype: tuple
"""
log.debug("Creating CNC Job from Excellon for tool: %s" % str(tool))
@ -2905,6 +2904,10 @@ class CNCjob(Geometry):
p = self.pp_excellon
self.toolchange = toolchange
# holds the temporary coordinates of the processed drill point
locx, locy = first_pt
temp_locx, temp_locy = first_pt
# #############################################################################################################
# #############################################################################################################
# ################################## DRILLING !!! #########################################################
@ -2936,13 +2939,11 @@ class CNCjob(Geometry):
# #########################################################################################################
self.tool = str(tool)
self.tooldia = tools[tool]["tooldia"]
self.postdata['toolC'] = self.tooldia
self.postdata['toolC'] = tools[tool]["tooldia"]
self.z_feedrate = tool_dict['feedrate_z']
self.feedrate = tool_dict['feedrate']
t_gcode += self.doformat(p.z_feedrate_code)
# Z_cut parameter
if self.machinist_setting == 0:
self.z_cut = self.check_zcut(zcut=tool_dict["excellon_cutz"])
@ -2996,10 +2997,9 @@ class CNCjob(Geometry):
# #########################################################################################################
# #########################################################################################################
# ############ Create the data. #################
# ############ Create the data. ###########################################################################
# #########################################################################################################
locations = []
altPoints = []
optimized_path = []
if opt_type == 'M':
@ -3015,12 +3015,11 @@ class CNCjob(Geometry):
return 'fail'
optimized_path = self.optimized_ortools_basic(locations=locations)
elif opt_type == 'T':
for point in points:
altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0]))
locations = self.create_tool_data_array(points=points)
# if there are no locations then go to the next tool
if not altPoints:
if not locations:
return 'fail'
optimized_path = self.optimized_travelling_salesman(altPoints)
optimized_path = self.optimized_travelling_salesman(locations)
else:
# it's actually not optimized path but here we build a list of (x,y) coordinates
# out of the tool's drills
@ -3041,16 +3040,25 @@ class CNCjob(Geometry):
# graceful abort requested by the user
raise grace
start_gcode = self.doformat(p.start_code)
if is_first:
t_gcode += start_gcode
t_gcode += self.doformat(p.z_feedrate_code)
# Tool change sequence (optional)
if toolchange:
t_gcode += self.doformat(p.toolchange_code, toolchangexy=(self.oldx, self.oldy))
else:
if self.xy_toolchange is not None and isinstance(self.xy_toolchange, (tuple, list)):
t_gcode += self.doformat(p.lift_code, x=self.xy_toolchange[0], y=self.xy_toolchange[1])
t_gcode += self.doformat(p.startz_code, x=self.xy_toolchange[0], y=self.xy_toolchange[1])
else:
t_gcode += self.doformat(p.lift_code, x=0.0, y=0.0)
t_gcode += self.doformat(p.startz_code, x=0.0, y=0.0)
# if toolchange:
# t_gcode += self.doformat(p.toolchange_code, toolchangexy=(temp_locx, temp_locy))
# else:
# if self.xy_toolchange is not None and isinstance(self.xy_toolchange, (tuple, list)):
# t_gcode += self.doformat(p.lift_code, x=self.xy_toolchange[0], y=self.xy_toolchange[1])
# t_gcode += self.doformat(p.startz_code, x=self.xy_toolchange[0], y=self.xy_toolchange[1])
# else:
# t_gcode += self.doformat(p.lift_code, x=0.0, y=0.0)
# t_gcode += self.doformat(p.startz_code, x=0.0, y=0.0)
# do the ToolChange event
t_gcode += self.doformat(p.toolchange_code, toolchangexy=(temp_locx, temp_locy))
# Spindle start
t_gcode += self.doformat(p.spindle_code)
@ -3098,7 +3106,7 @@ class CNCjob(Geometry):
locx = locations[point][0]
locy = locations[point][1]
travels = self.app.exc_areas.travel_coordinates(start_point=(self.oldx, self.oldy),
travels = self.app.exc_areas.travel_coordinates(start_point=(temp_locx, temp_locy),
end_point=(locx, locy),
tooldia=current_tooldia)
prev_z = None
@ -3170,7 +3178,9 @@ class CNCjob(Geometry):
t_gcode += self.doformat(p.lift_code, x=locx, y=locy)
self.measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
self.measured_distance += abs(distance_euclidian(locx, locy, temp_locx, temp_locy))
temp_locx = locx
temp_locy = locy
self.oldx = locx
self.oldy = locy
@ -3186,12 +3196,13 @@ class CNCjob(Geometry):
return 'fail'
self.z_cut = deepcopy(old_zcut)
t_gcode += self.doformat(p.spindle_stop_code)
# Move to End position
t_gcode += self.doformat(p.end_code, x=0, y=0)
if is_last:
t_gcode += self.doformat(p.spindle_stop_code)
# Move to End position
t_gcode += self.doformat(p.end_code, x=0, y=0)
self.app.inform.emit(_("Finished G-Code generation for tool: %s" % str(tool)))
return t_gcode
return t_gcode, (locx, locy)
def generate_from_excellon_by_tool(self, exobj, tools="all", order='fwd', use_ui=False):
"""
@ -5879,13 +5890,13 @@ class CNCjob(Geometry):
self.gcode_parsed = geometry
return geometry
def excellon_tool_gcode_parse(self, dia, start_pt=(0, 0), force_parsing=None):
def excellon_tool_gcode_parse(self, dia, gcode, start_pt=(0, 0), force_parsing=None):
"""
G-Code parser (from self.exc_cnc_tools['tooldia']['gcode']). Generates dictionary with
single-segment LineString's and "kind" indicating cut or travel,
fast or feedrate speed.
Will return a list of dict in the format:
Will return the Geometry as a list of dict in the format:
{
"geom": LineString(path),
"kind": kind
@ -5894,11 +5905,13 @@ class CNCjob(Geometry):
:param dia: the dia is a tool diameter which is the key in self.exc_cnc_tools dict
:type dia: float
:param gcode: Gcode to parse
:type gcode: str
:param start_pt: the point coordinates from where to start the parsing
:type start_pt: tuple
:param force_parsing:
:type force_parsing: bool
:return: list of dictionaries
:return: Geometry as a list of dictionaries
:rtype: list
"""
@ -5917,7 +5930,7 @@ class CNCjob(Geometry):
path = [pos_xy]
# path = [(0, 0)]
gcode_lines_list = self.exc_cnc_tools[dia]['gcode'].splitlines()
gcode_lines_list = gcode.splitlines()
self.app.inform.emit(
'%s: %s. %s: %d' % (_("Parsing GCode file for tool diameter"),
str(dia), _("Number of lines"),
@ -5964,20 +5977,19 @@ class CNCjob(Geometry):
path = [path[-1]] # Start with the last point of last path.
# create the geometry for the holes created when drilling Excellon drills
if self.origin_kind == 'excellon':
if current['Z'] < 0:
current_drill_point_coords = (
float('%.*f' % (self.decimals, current['X'])),
float('%.*f' % (self.decimals, current['Y']))
)
if current['Z'] < 0:
current_drill_point_coords = (
float('%.*f' % (self.decimals, current['X'])),
float('%.*f' % (self.decimals, current['Y']))
)
kind = ['C', 'F']
geometry.append(
{
"geom": Point(current_drill_point_coords).buffer(dia/2.0).exterior,
"kind": kind
}
)
kind = ['C', 'F']
geometry.append(
{
"geom": Point(current_drill_point_coords).buffer(dia/2.0).exterior,
"kind": kind
}
)
if 'G' in gobj:
current['G'] = int(gobj['G'])
@ -6147,8 +6159,8 @@ class CNCjob(Geometry):
# plot the geometry of Excellon objects
if self.origin_kind == 'excellon':
try:
# if the geos are travel lines
if geo['kind'][0] == 'T':
# if the geos are travel lines it will enter into Exception
poly = geo['geom'].buffer(distance=(tooldia / 1.99999999),
resolution=self.steps_per_circle)
else:
@ -6158,16 +6170,6 @@ class CNCjob(Geometry):
except Exception:
# deal here with unexpected plot errors due of LineStrings not valid
continue
# try:
# poly = Polygon(geo['geom'])
# except ValueError:
# # if the geos are travel lines it will enter into Exception
# poly = geo['geom'].buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle)
# poly = poly.simplify(tool_tolerance)
# except Exception:
# # deal here with unexpected plot errors due of LineStrings not valid
# continue
else:
# plot the geometry of any objects other than Excellon
poly = geo['geom'].buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle)