def generate_toolpath(self, cutter, models, motion_grid, minz=None, maxz=None, draw_callback=None): path = [] quit_requested = False model = pycam.Geometry.Model.get_combined_model(models) # Transfer the grid (a generator) into a list of lists and count the # items. lines = [] # usually there is only one layer - but an xy-grid consists of two for layer in motion_grid: for line in layer: lines.append(line) num_of_lines = len(lines) progress_counter = ProgressCounter(len(lines), draw_callback) current_line = 0 args = [] for one_grid_line in lines: # simplify the data (useful for remote processing) xy_coords = [(pos[0], pos[1]) for pos in one_grid_line] args.append((xy_coords, minz, maxz, model, cutter)) for points in run_in_parallel(_process_one_grid_line, args, callback=progress_counter.update): if draw_callback and draw_callback( text="DropCutter: processing line %d/%d" % (current_line + 1, num_of_lines)): # cancel requested quit_requested = True break for point in points: if point is None: # exceeded maxz - the cutter has to skip this point path.append(MoveSafety()) else: path.append(MoveStraight(point)) # The progress counter may return True, if cancel was requested. if draw_callback and draw_callback(tool_position=point, toolpath=path): quit_requested = True break # add a move to safety height after each line of moves path.append(MoveSafety()) progress_counter.increment() # update progress current_line += 1 if quit_requested: break return path
def GenerateToolPath(self, cutter, models, motion_grid, minz=None, maxz=None, draw_callback=None): quit_requested = False model = pycam.Geometry.Model.get_combined_model(models) # Transfer the grid (a generator) into a list of lists and count the # items. lines = [] # usually there is only one layer - but an xy-grid consists of two for layer in motion_grid: for line in layer: lines.append(line) num_of_lines = len(lines) progress_counter = ProgressCounter(len(lines), draw_callback) current_line = 0 self.pa.new_direction(0) args = [] for one_grid_line in lines: # simplify the data (useful for remote processing) xy_coords = [(pos.x, pos.y) for pos in one_grid_line] args.append((xy_coords, minz, maxz, model, cutter, self.physics)) for points in run_in_parallel(_process_one_grid_line, args, callback=progress_counter.update): self.pa.new_scanline() if draw_callback and draw_callback(text="DropCutter: processing " \ + "line %d/%d" % (current_line + 1, num_of_lines)): # cancel requested quit_requested = True break for point in points: if point is None: # exceeded maxz - the cutter has to skip this point self.pa.end_scanline() self.pa.new_scanline() continue self.pa.append(point) # "draw_callback" returns true, if the user requested to quit # via the GUI. # The progress counter may return True, if cancel was requested. if draw_callback and draw_callback(tool_position=point, toolpath=self.pa.paths): quit_requested = True break progress_counter.increment() self.pa.end_scanline() # update progress current_line += 1 if quit_requested: break self.pa.end_direction() self.pa.finish() return self.pa.paths
def GenerateToolPath(self, cutter, models, motion_grid, minz=None, maxz=None, draw_callback=None): path = [] quit_requested = False model = pycam.Geometry.Model.get_combined_model(models) # Transfer the grid (a generator) into a list of lists and count the # items. lines = [] # usually there is only one layer - but an xy-grid consists of two for layer in motion_grid: for line in layer: lines.append(line) num_of_lines = len(lines) progress_counter = ProgressCounter(len(lines), draw_callback) current_line = 0 args = [] for one_grid_line in lines: # simplify the data (useful for remote processing) xy_coords = [(pos[0], pos[1]) for pos in one_grid_line] args.append((xy_coords, minz, maxz, model, cutter, self.physics)) for points in run_in_parallel(_process_one_grid_line, args, callback=progress_counter.update): if draw_callback and draw_callback(text="DropCutter: processing " \ + "line %d/%d" % (current_line + 1, num_of_lines)): # cancel requested quit_requested = True break for point in points: if point is None: # exceeded maxz - the cutter has to skip this point path.append((MOVE_SAFETY, None)) else: path.append((MOVE_STRAIGHT, point)) # The progress counter may return True, if cancel was requested. if draw_callback and draw_callback(tool_position=point, toolpath=path): quit_requested = True break # add a move to safety height after each line of moves path.append((MOVE_SAFETY, None)) progress_counter.increment() # update progress current_line += 1 if quit_requested: break return path
def GenerateToolPath(self, minz, maxz, horiz_step, dz, draw_callback=None): quit_requested = False # calculate the number of steps num_of_layers = 1 + ceil(abs(maxz - minz) / dz) if num_of_layers > 1: z_step = abs(maxz - minz) / (num_of_layers - 1) z_steps = [(maxz - i * z_step) for i in range(num_of_layers)] # The top layer is treated as the current surface - thus it does not # require engraving. z_steps = z_steps[1:] else: z_steps = [minz] num_of_layers = len(z_steps) current_layer = 0 num_of_lines = self.contour_model.get_num_of_lines() progress_counter = ProgressCounter(len(z_steps) * num_of_lines, draw_callback) if draw_callback: draw_callback(text="Engrave: optimizing polygon order") # Sort the polygons according to their directions (first inside, then # outside. This reduces the problem of break-away pieces. inner_polys = [] outer_polys = [] for poly in self.contour_model.get_polygons(): if poly.get_area() <= 0: inner_polys.append(poly) else: outer_polys.append(poly) inner_sorter = PolygonSorter(inner_polys, callback=draw_callback) outer_sorter = PolygonSorter(outer_polys, callback=draw_callback) line_groups = inner_sorter.get_polygons() + outer_sorter.get_polygons() if self.clockwise: for line_group in line_groups: line_group.reverse_direction() # push slices for all layers above ground if maxz == minz: # only one layer - use PushCutter instead of DropCutter # put "last_z" clearly above the model plane last_z = maxz + 1 push_steps = z_steps drop_steps = [] else: # multiple layers last_z = maxz push_steps = z_steps[:-1] drop_steps = [z_steps[-1]] for z in push_steps: # update the progress bar and check, if we should cancel the process if draw_callback and draw_callback( text="Engrave: processing" + " layer %d/%d" % (current_layer + 1, num_of_layers) ): # cancel immediately break for line_group in line_groups: for line in line_group.get_lines(): self.GenerateToolPathLinePush(self.pa_push, line, z, last_z, draw_callback=draw_callback) if progress_counter.increment(): # cancel requested quit_requested = True # finish the current path self.pa_push.finish() break self.pa_push.finish() # break the outer loop if requested if quit_requested: break current_layer += 1 last_z = z if quit_requested: return self.pa_push.paths for z in drop_steps: if draw_callback: draw_callback(text="Engrave: processing layer %d/%d" % (current_layer + 1, num_of_layers)) # process the final layer with a drop cutter for line_group in line_groups: self.pa_drop.new_direction(0) self.pa_drop.new_scanline() for line in line_group.get_lines(): self.GenerateToolPathLineDrop( self.pa_drop, line, z, maxz, horiz_step, last_z, draw_callback=draw_callback ) if progress_counter.increment(): # quit requested quit_requested = True break self.pa_drop.end_scanline() self.pa_drop.end_direction() # break the outer loop if requested if quit_requested: break current_layer += 1 last_z = z self.pa_drop.finish() return self.pa_push.paths + self.pa_drop.paths
def GenerateToolPath(self, minz, maxz, horiz_step, dz, draw_callback=None): quit_requested = False # calculate the number of steps num_of_layers = 1 + ceil(abs(maxz - minz) / dz) if num_of_layers > 1: z_step = abs(maxz - minz) / (num_of_layers - 1) z_steps = [(maxz - i * z_step) for i in range(num_of_layers)] # The top layer is treated as the current surface - thus it does not # require engraving. z_steps = z_steps[1:] else: z_steps = [minz] num_of_layers = len(z_steps) current_layer = 0 num_of_lines = self.contour_model.get_num_of_lines() progress_counter = ProgressCounter(len(z_steps) * num_of_lines, draw_callback) if draw_callback: draw_callback(text="Engrave: optimizing polygon order") # Sort the polygons according to their directions (first inside, then # outside. This reduces the problem of break-away pieces. inner_polys = [] outer_polys = [] for poly in self.contour_model.get_polygons(): if poly.get_area() <= 0: inner_polys.append(poly) else: outer_polys.append(poly) inner_sorter = PolygonSorter(inner_polys, callback=draw_callback) outer_sorter = PolygonSorter(outer_polys, callback=draw_callback) line_groups = inner_sorter.get_polygons() + outer_sorter.get_polygons() if self.clockwise: for line_group in line_groups: line_group.reverse_direction() # push slices for all layers above ground if maxz == minz: # only one layer - use PushCutter instead of DropCutter # put "last_z" clearly above the model plane last_z = maxz + 1 push_steps = z_steps drop_steps = [] else: # multiple layers last_z = maxz push_steps = z_steps[:-1] drop_steps = [z_steps[-1]] for z in push_steps: # update the progress bar and check, if we should cancel the process if draw_callback and draw_callback(text="Engrave: processing" \ + " layer %d/%d" % (current_layer + 1, num_of_layers)): # cancel immediately break for line_group in line_groups: for line in line_group.get_lines(): self.GenerateToolPathLinePush(self.pa_push, line, z, last_z, draw_callback=draw_callback) if progress_counter.increment(): # cancel requested quit_requested = True # finish the current path self.pa_push.finish() break self.pa_push.finish() # break the outer loop if requested if quit_requested: break current_layer += 1 last_z = z if quit_requested: return self.pa_push.paths for z in drop_steps: if draw_callback: draw_callback(text="Engrave: processing layer %d/%d" \ % (current_layer + 1, num_of_layers)) # process the final layer with a drop cutter for line_group in line_groups: self.pa_drop.new_direction(0) self.pa_drop.new_scanline() for line in line_group.get_lines(): self.GenerateToolPathLineDrop(self.pa_drop, line, z, maxz, horiz_step, last_z, draw_callback=draw_callback) if progress_counter.increment(): # quit requested quit_requested = True break self.pa_drop.end_scanline() self.pa_drop.end_direction() # break the outer loop if requested if quit_requested: break current_layer += 1 last_z = z self.pa_drop.finish() return self.pa_push.paths + self.pa_drop.paths