def rectangularPocket(area, target_depth, stock_thickness, safe_Z, cut_per_pass, bit_diameter, debug=False): """ area argument requires tuple (length, width). target_depth is an absolute Z coordinate. Assumes that the bit is already in the origin corner. Origin is (minCornerX + bit_radius, minCornerY + bit_radius) """ file_text = G.set_ABS_mode() if debug: file_text += "; target_depth: " + str(target_depth) + "\n" file_text += "; stock_thickness: " + str(stock_thickness) + "\n" file_text += "; cut_per_pass: "******"\n" file_text += "; bit_diameter: " + str(bit_diameter) + "\n" file_text += G.G0_Z(stock_thickness) while stock_thickness > target_depth: stock_thickness -= cut_per_pass if stock_thickness < target_depth: stock_thickness = target_depth # Z-axis move by ABSOLUTE coords file_text += G.set_ABS_mode() file_text += G.G1_Z(stock_thickness) file_text += rectAreaByOutline(area, bit_diameter) file_text += G.G0_Z(safe_Z) return file_text
def _rectOutline(length, width, bit_diameter): """ Assumes it's in INCREMENTAL MODE. Since length and width arguments are not related to any position. The length and width define the outer boundaries of the cut. """ # TODO: consider whether to climb-cut or not. # Cut the whole outline of the area, returning to the origin. file_text = G.G1_X(length - bit_diameter) file_text += G.G1_Y(width - bit_diameter) file_text += G.G1_X(-(length - bit_diameter)) file_text += G.G1_Y(-(width - bit_diameter)) return file_text
def rectAreaByOutline(area, bit_diameter, debug=False): """ Required arguments: area (as (x=length, y=width), bit_diameter. Assumes that the bit is already in the origin corner at required depth of cut. This version just cuts progressively smaller rectangles. For fastest progress, orient the largest area dimension with the fastest machine axis. """ length, width = area if bit_diameter > length or bit_diameter > width: raise ValueError("Bit is too large to cut specified area") # magic number 0.5 is the pass-to-pass overlap... not sure that I'll enforce it min_overlap = 0.5 min_passes = min(math.ceil(length / bit_diameter), math.ceil(width / bit_diameter)) min_passes += min_passes % 2 x_overlap = max(min_overlap, (((min_passes * bit_diameter) - length) / (min_passes - 1))) y_overlap = max(min_overlap, (((min_passes * bit_diameter) - width) / (min_passes - 1))) x_step = bit_diameter - x_overlap y_step = bit_diameter - y_overlap file_text = G.set_INCR_mode() current_x = 0 current_y = 0 if debug: file_text += "; length: " + str(length) + "\n" file_text += "; width: " + str(width) + "\n" file_text += "; bit_diameter: " + str(bit_diameter) + "\n" file_text += "; min_passes: " + str(min_passes) + "\n" file_text += "; x_overlap: " + str(x_overlap) + "\n" file_text += "; y_overlap: " + str(y_overlap) + "\n" file_text += "; x_step: " + str(x_step) + "\n" file_text += "; y_step: " + str(y_step) + "\n" file_text += _rectOutline(length, width, bit_diameter) min_passes -= 2 while min_passes > 0: file_text += G.G1_XY((x_step, y_step)) current_x += x_step current_y += y_step length -= (2 * x_step) width -= (2 * y_step) file_text += _rectOutline(length, width, bit_diameter) min_passes -= 2 file_text += G.G0_XY((-current_x, -current_y)) file_text += G.set_ABS_mode() return file_text
def rectArea(area, bit_diameter): """ Required arguments: area (as (x=length, y=width), bit_diameter. Assumes that the bit is already in the origin corner at required depth of cut. """ length, width = area if bit_diameter > length or bit_diameter > width: raise ValueError("Bit is too large to cut specified area") file_text = G.set_INCR_mode() # magic number 0.5 is the pass-to-pass overlap pass_width = bit_diameter - 0.5 current_x = 0 # first pass file_text += G.G1_Y(width - bit_diameter) # sentinel to track the pass direction; out is opposite to back, and the # bit is now ready to come back out = False while current_x < (length - bit_diameter): # prepare for the next pass # current_x = min(current_x + pass_width, length - bit_diameter) # file_text += G.G1_X(current_x) if current_x + pass_width <= length - bit_diameter: file_text += G.G1_X(pass_width) current_x = current_x + pass_width else: file_text += G.G1_X( length - bit_diameter - current_x) current_x = length - bit_diameter if out == True: file_text += G.G1_Y(width - bit_diameter) else: file_text += G.G1_Y( - (width - bit_diameter) ) out = not out # last move is a return to relative origin if out == True: file_text += G.G1_XY(( -(length - bit_diameter), 0)) else: file_text += G.G1_XY(( -(length - bit_diameter), -(width - bit_diameter))) file_text += G.set_ABS_mode() return file_text
def addDebugFrame(frame): """Generates a debug statement formatted to add to g-code.""" file_text = '' if 'DEBUG_GCODE' in os.environ.keys(): trace = inspect.getframeinfo(frame) class_file = trace.filename.split('/')[-1].split('_class')[0] file_text = G.comment(\ '# %s' % (class_file + '.' + trace.function + ' - line:' + str(trace.lineno))) return file_text
def bore_circle_ID(Z_safe, stock_thickness, cut_per_pass, target_depth, cutter_diameter, circle_diameter): '''use G2; from specified diameter and thickness; cutter compensation in function. Note that this method mixes ABSOLUTE with INCREMENTAL modes: all moves in XY are in INCR and all moves in Z are ABS.''' assert cutter_diameter <= circle_diameter, "bit is too large for desired hole" assert Z_safe > stock_thickness, "Z_safe is too short for stock thickness" # alternate path: do a straight drill if cutter_diameter == circle_diameter: file_text = G.set_ABS_mode() file_text += G.G0_Z(stock_thickness) file_text += G.G1_Z(target_depth) file_text += G.set_dwell(0.5) file_text += G.G0_Z(Z_safe) return file_text off_set = (circle_diameter - cutter_diameter) / 2.0 file_text = G.set_ABS_mode() file_text += G.G0_Z(Z_safe) # XY-plane move to starting point file_text += G.set_INCR_mode() file_text += G.G0_XY((-off_set, 0)) # Z-axis move to starting point file_text += G.set_ABS_mode() file_text += G.G0_Z(stock_thickness) while stock_thickness > target_depth: stock_thickness -= cut_per_pass if stock_thickness < target_depth: stock_thickness = target_depth # Z-axis move file_text += G.set_ABS_mode() file_text += G.G1_Z(stock_thickness) # XY-plane arc move file_text += G.G2XY_to_INCR_FULL((0,0),(off_set, 0)) # At end of cut, ensures that the program reaches the very bottom file_text += G.set_dwell(0.5) # Z-axis move # TODO: move this to before the return to origin file_text += G.set_ABS_mode() file_text += G.G0_Z(Z_safe) # Then put the bit back to (0,0) file_text += G.set_INCR_mode() file_text += G.G0_XY((off_set, 0)) return file_text
def polar_holes(Z_safe, stock_thickness, cut_per_pass, target_depth, cutter_diameter, circle_diameter, num_holes, hole_circle_diameter): assert num_holes > 1, "too few holes to form a circle of holes; must be at least 2" hole_circle_radius = hole_circle_diameter / 2.0 file_text = G.set_ABS_mode() file_text += G.G0_Z(Z_safe) radians_increment = 2 * math.pi / num_holes # drill first hole x = math.cos(0) * hole_circle_radius y = math.sin(0) * hole_circle_radius file_text += G.set_INCR_mode() file_text += G.G0_XY((x, y)) file_text += bore_circle_ID(Z_safe, stock_thickness, cut_per_pass, target_depth, cutter_diameter, circle_diameter) # drill all other holes for i in xrange(1, int(num_holes)): x = -(math.cos((i - 1) * radians_increment) * hole_circle_radius) + ( math.cos(i * radians_increment) * hole_circle_radius) y = -(math.sin((i - 1) * radians_increment) * hole_circle_radius) + ( math.sin(i * radians_increment) * hole_circle_radius) file_text += G.set_INCR_mode() file_text += G.G0_XY((x, y)) file_text += bore_circle_ID(Z_safe, stock_thickness, cut_per_pass, target_depth, cutter_diameter, circle_diameter) # return to Z_safe and origin file_text += G.set_ABS_mode() file_text += G.G0_Z(Z_safe) file_text += G.set_INCR_mode() x = -(math.cos((num_holes - 1) * radians_increment) * hole_circle_radius) y = -(math.sin((num_holes - 1) * radians_increment) * hole_circle_radius) file_text += G.G0_XY((x, y)) return file_text
def polar_holes(Z_safe, stock_thickness, cut_per_pass, target_depth, cutter_diameter, circle_diameter, num_holes, hole_circle_diameter): assert num_holes > 1, "too few holes to form a circle of holes; must be at least 2" hole_circle_radius = hole_circle_diameter / 2.0 file_text = G.set_ABS_mode() file_text += G.G0_Z(Z_safe) radians_increment = 2 * math.pi / num_holes; # drill first hole x = math.cos(0) * hole_circle_radius y = math.sin(0) * hole_circle_radius file_text += G.set_INCR_mode() file_text += G.G0_XY( (x, y) ) file_text += bore_circle_ID(Z_safe, stock_thickness, cut_per_pass, target_depth, cutter_diameter, circle_diameter) # drill all other holes for i in xrange(1, int(num_holes)): x = - (math.cos((i - 1) * radians_increment) * hole_circle_radius) + (math.cos(i * radians_increment) * hole_circle_radius) y = - (math.sin((i - 1) * radians_increment) * hole_circle_radius) + (math.sin(i * radians_increment) * hole_circle_radius) file_text += G.set_INCR_mode() file_text += G.G0_XY( (x, y) ) file_text += bore_circle_ID(Z_safe, stock_thickness, cut_per_pass, target_depth, cutter_diameter, circle_diameter) # return to Z_safe and origin file_text += G.set_ABS_mode() file_text += G.G0_Z(Z_safe) file_text += G.set_INCR_mode() x = - (math.cos((num_holes - 1) * radians_increment) * hole_circle_radius) y = - (math.sin((num_holes - 1) * radians_increment) * hole_circle_radius) file_text += G.G0_XY( (x, y) ) return file_text
def addDebugStatement(statement): file_text = '' if 'DEBUG_GCODE' in os.environ.keys(): file_text = G.comment(statement) return file_text
def bore_tabbed_ID(Z_safe, stock_thickness, cut_per_pass, tab_thickness, cutter_diameter, circle_diameter, tab_width): """ Cut three tabs.""" assert tab_thickness <= cut_per_pass, "script not set to handle cut_per_pass when it's less than tab thickness" off_set = (circle_diameter - cutter_diameter) / 2.0 path_length = math.pi * off_set * 2 # NOTE: radius = off_set, path_length is circumference of the circle that the cutter will be tracing assert path_length > 6.0 * ( tab_width + cutter_diameter), "tabs and/or bit are too large for the circle to cut" # gap_radians is the gap (in radians) between the start and stop of each pair of cuts gap_radians = (cutter_diameter + tab_width) / off_set # file_text = "% cutting bore_tabbed_ID \n" file_text = G.set_ABS_mode() file_text += G.G0_Z(Z_safe) # XY-plane move to starting point, creating the first tab # at approximately 180 degrees file_text += G.set_INCR_mode() x = -math.cos(gap_radians) * off_set y = math.sin(gap_radians) * off_set file_text += G.G0_XY((x, y)) file_text += G.set_ABS_mode() # 1 G2 cut after the first tab file_text += G.G1_Z(0) x = (math.cos(gap_radians) + math.cos(math.pi / 3.0)) * off_set y = (-math.sin(gap_radians) + math.sin((2 * math.pi) / 3.0)) * off_set i = math.cos(gap_radians) * off_set j = -math.sin(gap_radians) * off_set file_text += G.G2XY_to_INCR_FULL((x, y), (i, j)) # 2 G2 create the second tab # at approximately 60 degrees file_text += G.G0_Z(tab_thickness) x = (math.cos((math.pi / 3.0) - gap_radians) - math.cos(math.pi / 3.0)) * off_set y = (math.sin((math.pi / 3.0) - gap_radians) - math.sin(math.pi / 3.0)) * off_set i = -math.cos(math.pi / 3.0) * off_set j = -math.sin(math.pi / 3.0) * off_set file_text += G.G2XY_to_INCR_FULL((x, y), (i, j)) # 3 G2 cut after the second tab file_text += G.set_ABS_mode() file_text += G.G1_Z(0) x = 0 y = -2 * math.sin((math.pi / 3.0) - gap_radians) * off_set i = -math.cos((math.pi / 3.0) - gap_radians) * off_set j = -math.sin((math.pi / 3.0) - gap_radians) * off_set file_text += G.G2XY_to_INCR_FULL((x, y), (i, j)) # 4 G2 create the third tab file_text += G.G0_Z(tab_thickness) x = -(math.cos( (math.pi / 3.0) - gap_radians) - math.cos(math.pi / 3.0)) * off_set y = (math.sin((math.pi / 3.0) - gap_radians) - math.sin(math.pi / 3.0)) * off_set i = -math.cos((math.pi / 3.0) - gap_radians) * off_set j = math.sin((math.pi / 3.0) - gap_radians) * off_set file_text += G.G2XY_to_INCR_FULL((x, y), (i, j)) # 5 G2 cut after the third tab file_text += G.set_ABS_mode() file_text += G.G1_Z(0) x = -(1 + math.cos(math.pi / 3.0)) * off_set y = math.sin(math.pi / 3.0) * off_set i = -math.cos(math.pi / 3.0) * off_set j = math.sin(math.pi / 3.0) * off_set file_text += G.G2XY_to_INCR_FULL((x, y), (i, j)) # return to Z_safe and origin file_text += G.set_ABS_mode() file_text += G.G0_Z(Z_safe) file_text += G.set_INCR_mode() file_text += G.G0_XY((off_set, 0)) return file_text
def endProgram(): """ Ends the program with an M2 """ return G.set_ABS_mode() + 'M2 \n'
def startProgram(feed_rate): return G.F_rate(feed_rate)
def tenon(rail, stile, offsets, tenon, mortise_bit_diameter, bit_diameter, safe_Z, cut_per_pass): """ Assumed bit location (center) is at refs origin, at safe_Z, and machine is assumed in ABS mode. It is usual that mortise_bit_diameter == bit_diameter. Requires: rail = (rail_face_width, rail_thickness) stile = (stile_face_width, stile_thickness) offsets = (offset_from_face, offset_from_end) tenon = (length, width, depth) """ # Unpacking the tuples rail_face_width, rail_thickness = rail stile_face_width, stile_thickness = stile offset_from_face, offset_from_end = offsets length, width, depth = tenon bit_radius = bit_diameter / 2.0 corner_radius = mortise_bit_diameter / 2.0 file_text = '' # if debug: # file_text += "; target_depth: " + str(target_depth) + "\n" # file_text += "; stock_thickness: " + str(stock_thickness) + "\n" # file_text += "; cut_per_pass: "******"\n" # file_text += "; bit_diameter: " + str(bit_diameter) + "\n" if (offset_from_face <= bit_diameter) and (offset_from_end <= bit_diameter) and \ (rail_face_width - offset_from_end - length <= bit_diameter) and \ (rail_thickness - offset_from_face - width <= bit_diameter): file_text += G.set_ABS_mode() file_text += G.G0_Z(safe_Z) file_text += G.set_INCR_mode() file_text += G.G0_X(offset_from_end + corner_radius) file_text += G.set_ABS_mode() file_text += G.G0_Z(stile_face_width) # Making target_depth a reference from machine's Z = 0. target_depth = stile_face_width - depth while stile_face_width > target_depth: stile_face_width -= cut_per_pass if stile_face_width < target_depth: stile_face_width = target_depth # Z-axis move by ABSOLUTE coords file_text += G.set_ABS_mode() file_text += G.G1_Z(stile_face_width) file_text += G.set_INCR_mode() file_text += roundedRectangle(length, width, mortise_bit_diameter, bit_diameter, 'inside') else: file_text = 'Not implemented yet' file_text += G.set_ABS_mode() file_text += G.G0_Z(safe_Z) return file_text
def roundedRectangle(length, width, corner_radius, bit_diameter, path_ref='outside'): """ Assumes it's in INCREMENTAL MODE. Since length and width arguments are not related to any position. With path_ref='outside', the length and width define the outer boundary of the cut. With path_ref='center', the length and width define the cut path (centerline of cut) boundary. With path_ref='inside', the length and width define the inner boundary of the cut. Assumes that the bit is already in a starting position, at least X of least Y edge; path_ref is not a consideration. """ bit_radius = bit_diameter / 2.0 corner_diameter = 2 * corner_radius if corner_radius <= 0: raise ValueError('corner_radius %d must be greater than 0.' % corner_radius) if path_ref is 'outside': # TODO: check that corner_radius > bit_radius if corner_radius <= bit_radius: raise ValueError( 'corner_radius %d must be greater than bit radius %d.' % (corner_radius, bit_radius)) center_offset = corner_radius - bit_radius file_text = G.G1_X(length - corner_diameter) file_text += G.G3XY_to_INCR_FULL((center_offset, center_offset), (0, center_offset)) file_text += G.G1_Y(width - corner_diameter) file_text += G.G3XY_to_INCR_FULL((-center_offset, center_offset), (-center_offset, 0)) file_text += G.G1_X(-length + corner_diameter) file_text += G.G3XY_to_INCR_FULL((-center_offset, -center_offset), (0, -center_offset)) file_text += G.G1_Y(-width + corner_diameter) file_text += G.G3XY_to_INCR_FULL((center_offset, -center_offset), (center_offset, 0)) elif path_ref is 'center': file_text = G.G1_X(length - corner_diameter) file_text += G.G3XY_to_INCR_FULL((corner_radius, corner_radius), (0, corner_radius)) file_text += G.G1_Y(width - corner_diameter) file_text += G.G3XY_to_INCR_FULL((-corner_radius, corner_radius), (-corner_radius, 0)) file_text += G.G1_X(-length + corner_diameter) file_text += G.G3XY_to_INCR_FULL((-corner_radius, -corner_radius), (0, -corner_radius)) file_text += G.G1_Y(-width + corner_diameter) file_text += G.G3XY_to_INCR_FULL((corner_radius, -corner_radius), (corner_radius, 0)) elif path_ref is 'inside': center_offset = corner_radius + bit_radius file_text = G.G1_X(length - corner_diameter) file_text += G.G3XY_to_INCR_FULL((center_offset, center_offset), (0, center_offset)) file_text += G.G1_Y(width - corner_diameter) file_text += G.G3XY_to_INCR_FULL((-center_offset, center_offset), (-center_offset, 0)) file_text += G.G1_X(-length + corner_diameter) file_text += G.G3XY_to_INCR_FULL((-center_offset, -center_offset), (0, -center_offset)) file_text += G.G1_Y(-width + corner_diameter) file_text += G.G3XY_to_INCR_FULL((center_offset, -center_offset), (center_offset, 0)) else: raise ValueError('Invalid path reference specified: %s.' % path_ref) return file_text
def endProgram(): ''' Ends the program with an M2 ''' return G.set_ABS_mode() + 'M2 \n'
def bore_tabbed_ID(Z_safe, stock_thickness, cut_per_pass, tab_thickness, cutter_diameter, circle_diameter, tab_width): ''' Cut three tabs.''' assert tab_thickness <= cut_per_pass, "script not set to handle cut_per_pass when it's less than tab thickness" off_set = (circle_diameter - cutter_diameter) / 2.0 path_length = math.pi * off_set * 2 # NOTE: radius = off_set, path_length is circumference of the circle that the cutter will be tracing assert path_length > 6.0 * (tab_width + cutter_diameter), "tabs and/or bit are too large for the circle to cut" gap_radians = (cutter_diameter + tab_width) / off_set # file_text = "% cutting bore_tabbed_ID \n" file_text = G.set_ABS_mode() file_text += G.G0_Z(Z_safe) # XY-plane move to starting point, creating the first tab file_text += G.set_INCR_mode() x = -math.cos(gap_radians) * off_set; y = math.sin(gap_radians) * off_set; file_text += G.G0_XY( (x, y) ) file_text += G.set_ABS_mode() # 1 G2 cut after the first tab file_text += G.G1_Z(0) x = ( math.cos(gap_radians) + math.cos(math.pi / 3.0) ) * off_set y = ( - math.sin(gap_radians) + math.sin((2 * math.pi) / 3.0) ) * off_set i = math.cos(gap_radians) * off_set j = - math.sin(gap_radians) * off_set file_text += G.G2XY_to_INCR_FULL( (x, y), (i, j) ) # 2 G2 create the second tab file_text += G.G0_Z(tab_thickness) x = (math.cos((math.pi / 3.0) - gap_radians) - math.cos(math.pi / 3.0)) * off_set y = (math.sin((math.pi / 3.0) - gap_radians) - math.sin(math.pi / 3.0)) * off_set i = - math.cos(math.pi / 3.0) * off_set j = - math.sin(math.pi / 3.0) * off_set file_text += G.G2XY_to_INCR_FULL( (x, y), (i, j) ) # 3 G2 cut after the second tab file_text += G.set_ABS_mode() file_text += G.G1_Z(0) x = 0 y = - 2 * math.sin((math.pi / 3.0) - gap_radians) * off_set i = - math.cos((math.pi / 3.0) - gap_radians) * off_set j = - math.sin((math.pi / 3.0) - gap_radians) * off_set file_text += G.G2XY_to_INCR_FULL( (x, y), (i, j) ) # 4 G2 create the third tab file_text += G.G0_Z(tab_thickness) x = - (math.cos((math.pi / 3.0) - gap_radians) - math.cos(math.pi / 3.0)) * off_set y = (math.sin((math.pi / 3.0) - gap_radians) - math.sin(math.pi / 3.0)) * off_set i = - math.cos((math.pi / 3.0) - gap_radians) * off_set j = math.sin((math.pi / 3.0) - gap_radians) * off_set file_text += G.G2XY_to_INCR_FULL( (x, y), (i, j) ) # 5 G2 cut after the third tab file_text += G.set_ABS_mode() file_text += G.G1_Z(0) x = - (1 + math.cos(math.pi / 3.0)) * off_set y = math.sin(math.pi / 3.0) * off_set i = - math.cos(math.pi / 3.0) * off_set j = math.sin(math.pi / 3.0) * off_set file_text += G.G2XY_to_INCR_FULL( (x, y), (i, j)) # return to Z_safe and origin file_text += G.set_ABS_mode() file_text += G.G0_Z(Z_safe) file_text += G.set_INCR_mode() file_text += G.G0_XY((off_set, 0)) return file_text
def rectArea(area, bit_diameter): """ Required arguments: area (as (x=length, y=width), bit_diameter. Assumes that the bit is already in the origin corner at required depth of cut. """ length, width = area if bit_diameter > length or bit_diameter > width: raise ValueError("Bit is too large to cut specified area") file_text = G.set_INCR_mode() # magic number 0.5 is the pass-to-pass overlap pass_width = bit_diameter - 0.5 current_x = 0 # first pass file_text += G.G1_Y(width - bit_diameter) # sentinel to track the pass direction; out is opposite to back, and the # bit is now ready to come back out = False while current_x < (length - bit_diameter): # prepare for the next pass # current_x = min(current_x + pass_width, length - bit_diameter) # file_text += G.G1_X(current_x) if current_x + pass_width <= length - bit_diameter: file_text += G.G1_X(pass_width) current_x = current_x + pass_width else: file_text += G.G1_X(length - bit_diameter - current_x) current_x = length - bit_diameter if out == True: file_text += G.G1_Y(width - bit_diameter) else: file_text += G.G1_Y(-(width - bit_diameter)) out = not out # last move is a return to relative origin if out == True: # Moves straight back along original side wall # file_text += G.G1_XY(( -(length - bit_diameter), 0)) # Change to: move across to other side, move straight back, then back to origin file_text += G.G1_Y((0, (width - bit_diameter))) file_text += G.G1_X((-(length - bit_diameter), 0)) file_text += G.G0_Y((0, -(width - bit_diameter))) else: # Moves diagonally across the cut area # file_text += G.G1_XY(( -(length - bit_diameter), -(width - bit_diameter))) # Change to: move straight back, then back to origin file_text += G.G1_X((-(length - bit_diameter), 0)) file_text += G.G0_Y((0, -(width - bit_diameter))) file_text += G.set_ABS_mode() return file_text
def bore_circle_ID(Z_safe, stock_thickness, cut_per_pass, target_depth, cutter_diameter, circle_diameter): """use G2; from specified diameter and thickness; cutter compensation in function. Note that this method mixes ABSOLUTE with INCREMENTAL modes: all moves in XY are in INCR and all moves in Z are ABS.""" assert cutter_diameter <= circle_diameter, "bit is too large for desired hole" assert Z_safe > stock_thickness, "Z_safe is too short for stock thickness" # alternate path: do a straight drill if cutter_diameter == circle_diameter: file_text = G.set_ABS_mode() file_text += G.G0_Z(stock_thickness) file_text += G.G1_Z(target_depth) file_text += G.set_dwell(0.5) file_text += G.G0_Z(Z_safe) return file_text off_set = (circle_diameter - cutter_diameter) / 2.0 file_text = G.set_ABS_mode() file_text += G.G0_Z(Z_safe) # XY-plane move to starting point file_text += G.set_INCR_mode() file_text += G.G0_XY((-off_set, 0)) # Z-axis move to starting point file_text += G.set_ABS_mode() file_text += G.G0_Z(stock_thickness) while stock_thickness > target_depth: stock_thickness -= cut_per_pass if stock_thickness < target_depth: stock_thickness = target_depth # Z-axis move file_text += G.set_ABS_mode() file_text += G.G1_Z(stock_thickness) # XY-plane arc move file_text += G.G2XY_to_INCR_FULL((0, 0), (off_set, 0)) # At end of cut, ensures that the program reaches the very bottom file_text += G.set_dwell(0.5) # Z-axis move # TODO: move this to before the return to origin file_text += G.set_ABS_mode() file_text += G.G0_Z(Z_safe) # Then put the bit back to (0,0) file_text += G.set_INCR_mode() file_text += G.G0_XY((off_set, 0)) return file_text