diff --git a/CHANGELOG.md b/CHANGELOG.md index 400d0200..c8ecd922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/appObjects/FlatCAMCNCJob.py b/appObjects/FlatCAMCNCJob.py index 5e986027..9e049da3 100644 --- a/appObjects/FlatCAMCNCJob.py +++ b/appObjects/FlatCAMCNCJob.py @@ -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): diff --git a/appTools/ToolDrilling.py b/appTools/ToolDrilling.py index 69093a09..bfbe58a3 100644 --- a/appTools/ToolDrilling.py +++ b/appTools/ToolDrilling.py @@ -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 diff --git a/app_Main.py b/app_Main.py index 4f6ee7e3..af74905e 100644 --- a/app_Main.py +++ b/app_Main.py @@ -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) diff --git a/camlib.py b/camlib.py index 48ce306c..3cb03503 100644 --- a/camlib.py +++ b/camlib.py @@ -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)