def square(g, mat, dx, dy, fill: bool): with GContext(g): g.relative() if fill: num_lines = int(math.ceil(abs(dy) / (mat['tool_size'] * OVERLAP))) + 1 line_step = 0 if num_lines > 1: line_step = dy / (num_lines - 1) g.comment("Square fill={0}".format(fill)) is_out = False for i in range(0, num_lines): if is_out: g.move(x=-dx) else: g.move(x=dx) is_out = not is_out if i < num_lines - 1: g.move(y=line_step) if is_out: g.move(x=-dx) g.move(y=-dy) else: g.move(x=dx) g.move(y=dy) g.move(x=-dx) g.move(y=-dy)
def hole_abs(g, mat, cut_depth, radius, x, y): with GContext(g): g.absolute() g.feed(mat['travel_feed']) g.move(z=CNC_TRAVEL_Z) g.move(x=x, y=y) hole(g, mat, cut_depth, r=radius) g.absolute()
def board(g, mat, stock_height): with GContext(g): g.absolute() bolt(g, mat, stock_height, 0.5, 0.5) bolt(g, mat, stock_height, 0.5, 7.5) bolt(g, mat, stock_height, 7.5, 0.5) bolt(g, mat, stock_height, 7.5, 7.5) bolt(g, mat, stock_height, 4.75, 4.0) # get back to the origin, assuming the next step is a plane travel(g, mat, x=0, y=0) g.move(z=0)
def drill_points(g, mat, cut_depth, points): with GContext(g): g.absolute() sort_shortest_path(points) for p in points: g.feed(mat['travel_feed']) g.move(z=CNC_TRAVEL_Z) g.move(x=p['x'], y=p['y']) g.move(z=0) drill(g, mat, cut_depth) # Leaves the head at CNC_TRAVEL_Z) g.move(z=CNC_TRAVEL_Z) g.move(x=0, y=0)
def overCut(g, mat, cut_depth, _dx, _dy): g.feed(mat['travel_feed']) g.spindle('CW', mat['spindle_speed']) with GContext(g): g.relative() tool = mat['tool_size'] half = tool / 2 dx = _dx - tool dy = _dy - tool length = dx * 2 + dy * 2 g.move(z=1) g.move(x=half, y=half) g.move(z=-1) def path(g, plunge, total_plunge): g.move(x=dx, z=plunge * dx / length) g.move(x=half) g.move(x=-half, y=-half) g.move(y=half) g.move(y=dy, z=plunge * dy / length) g.move(y=half) g.move(x=half, y=-half) g.move(x=-half) g.move(x=-dx, z=plunge * dx / length) g.move(x=-half) g.move(x=half, y=half) g.move(y=-half) g.move(y=-dy, z=plunge * dy / length) g.move(y=-half) g.move(x=-half, y=half) g.move(x=half) steps = calc_steps(cut_depth, -mat['pass_depth']) run_3_stages(path, g, steps) g.move(z=-cut_depth) g.move(z=1) g.move(x=-half, y=-half) g.move(z=-1)
def plane(g, mat, depth, x0, y0, x1, y1): dx = x1 - x0 dy = y1 - y0 # print("plane", dx, dy) with GContext(g): g.comment("Plane depth = {} size = {}, {}".format(depth, dx, dy)) g.relative() g.spindle('CW', mat['spindle_speed']) g.feed(mat['feed_rate']) g.move(x=x0, y=y0) z = 0 while (z > depth): dz = -mat['pass_depth'] if z + dz < depth: dz = depth - z z = depth else: z = z + dz # first line to distribute depth cut. g.move(x=dx, z=dz / 2) g.move(x=-dx, z=dz / 2) # nice edges g.move(x=dx) g.move(y=dy) g.move(x=-dx) g.move(y=-dy) # now the business of cutting. square(g, mat, dx, dy, True) g.move(z=-depth) g.move(x=-x0, y=-y0)
def drill(g, mat, cut_depth): if cut_depth >= 0: raise RuntimeError('Cut depth must be less than zero.') with GContext(g): g.relative() num_plunge = 1 + int(-cut_depth / (0.05 * mat['plunge_rate'])) dz = cut_depth / num_plunge g.comment("Drill depth={} num_taps={}".format(cut_depth, num_plunge)) g.spindle('CW', mat['spindle_speed']) g.feed(mat['plunge_rate']) if num_plunge > 1: # move up and down in stages. for i in range(0, num_plunge): g.move(z=dz) g.move(z=-dz) g.move(z=dz) else: g.move(z=cut_depth) g.move(z=-cut_depth)
def hill(g, mat, diameter, dx, dy): r_hill = diameter / 2 ht = mat['tool_size'] * 0.5 hy = dy / 2 doc = mat['pass_depth'] step = 0.5 def rough_arc(bias): y = 0 end = hy + ht last_plane = 0 last_y = 0 step = ht / 4 g.move(x=dx) g.move(x=-dx / 2) while y < end: do_plane = False y += step if y >= end: do_plane = True y = end if last_plane - height_func(y + step, ht, r_hill) >= doc: do_plane = True if do_plane: # move to the beginning of the plane g.move(y=bias * (y - last_y)) # cut the plane z = height_func(y, ht, r_hill) dz = z - last_plane g.comment("Cutting plane. y={} z={} dx={} dy={} dz={}".format( y, z, dx, end - y, dz)) rectangleTool(g, mat, dz, dx, end - y, 0.0, "bottom" if bias > 0 else "top", "center", True) # move to the bottom of the plane we just cut g.move(z=dz) g.comment("Cutting plane done.") last_y = y last_plane += dz g.move(-dx / 2) g.abs_move(z=origin_z) g.move(y=-end * bias) def arc(bias, step): y = 0 low_x = True end = hy + ht while y < end: if y + step > end: g.move(y=(end - y) * bias) y = end else: y += step g.move(y=step * bias) dz = height_func(y, ht, r_hill) g.feed(mat['plunge_rate']) g.abs_move(z=origin_z + dz) g.feed(mat['feed_rate']) if low_x is True: g.move(x=dx) low_x = False else: g.move(x=-dx) low_x = True if low_x is False: g.move(x=-dx) g.abs_move(z=origin_z) g.move(y=-end * bias) with GContext(g): g.comment('hill') g.relative() mult = 0.2 # rough pass origin_z = g.current_position['z'] g.spindle() g.dwell(0.5) g.spindle('CW', mat['spindle_speed']) rough_arc(1) g.spindle() g.dwell(0.5) g.spindle('CCW', mat['spindle_speed']) rough_arc(-1) # smooth pass g.spindle() g.spindle() g.dwell(0.5) g.spindle('CW', mat['spindle_speed']) arc(1, mult * ht) g.spindle() g.dwell(0.5) g.spindle('CCW', mat['spindle_speed']) arc(-1, mult * ht) g.spindle()
def hole(g, mat, cut_depth, **kwargs): radius = 0 offset = "inside" fill = True di = None if 'r' in kwargs: radius = kwargs['r'] if 'd' in kwargs: radius = kwargs['d'] / 2 if 'offset' in kwargs: offset = kwargs['offset'] if 'fill' in kwargs: fill = kwargs['fill'] if 'di' in kwargs: di = kwargs['di'] tool_size = mat['tool_size'] half_tool = tool_size / 2 if offset == 'inside': radius_inner = radius - half_tool elif offset == 'outside': radius_inner = radius + half_tool elif offset == 'middle': radius_inner = radius else: raise RuntimeError("offset not correctly specified") if radius_inner < 0.2: raise RuntimeError("Radius too small. Consider a drill.") if cut_depth >= 0: raise RuntimeError('Cut depth must be less than zero.') if mat["tool_size"] < 0: raise RuntimeError('Tool size must be zero or greater.') was_relative = g.is_relative with GContext(g): g.relative() g.comment("hole") g.comment("depth=" + str(cut_depth)) g.comment("tool size=" + str(mat['tool_size'])) g.comment("radius=" + str(radius)) g.comment("pass depth=" + str(mat['pass_depth'])) g.comment("feed rate=" + str(mat['feed_rate'])) g.comment("plunge rate=" + str(mat['plunge_rate'])) # The trick is to neither exceed the plunge or the depth-of-cut/pass_depth. # Approaches below. feed_rate = mat['feed_rate'] path_len_mm = 2.0 * math.pi * radius_inner path_time_min = path_len_mm / feed_rate plunge_from_path = mat['pass_depth'] / path_time_min depth_of_cut = mat['pass_depth'] # Both 1) fast little holes and 2) too fast plunge are bad. # Therefore, apply corrections to both. (If for some reason # alternate approaches need to be reviewed, they are in # source control.) if plunge_from_path > mat['plunge_rate']: factor = mat['plunge_rate'] / plunge_from_path if factor < 0.3: factor = 0.3 # slowing down to less than 10% (factor * factor) seems excessive depth_of_cut = mat['pass_depth'] * factor feed_rate = mat['feed_rate'] * factor g.comment('adjusted pass depth=' + str(depth_of_cut)) g.comment('adjusted feed rate =' + str(feed_rate)) g.spindle('CW', mat['spindle_speed']) g.feed(mat['travel_plunge']) g.move(x=radius_inner) if 'z' in kwargs: if was_relative: g.move(z=kwargs['z']) else: g.abs_move(z=kwargs['z']) g.feed(feed_rate) def path(g, plunge, total_plunge): g.arc2(x=-radius_inner, y=radius_inner, i=-radius_inner, j=0, direction='CCW', helix_dim='z', helix_len=plunge / 4) g.arc2(x=-radius_inner, y=-radius_inner, i=0, j=-radius_inner, direction='CCW', helix_dim='z', helix_len=plunge / 4) g.arc2(x=radius_inner, y=-radius_inner, i=radius_inner, j=0, direction='CCW', helix_dim='z', helix_len=plunge / 4) g.arc2(x=radius_inner, y=radius_inner, i=0, j=radius_inner, direction='CCW', helix_dim='z', helix_len=plunge / 4) if fill and radius_inner > half_tool: r = radius_inner dr = 0 step = tool_size * 0.8 min_rad = half_tool * 0.8 if di: min_rad = di / 2 while r > min_rad: if r - step < min_rad: step = r - min_rad r -= step #print("r={} step={}".format(r, step)) dr += step g.move(x=-step) g.arc2(x=-r, y=r, i=-r, j=0, direction='CCW') g.arc2(x=-r, y=-r, i=0, j=-r, direction='CCW') g.arc2(x=r, y=-r, i=r, j=0, direction='CCW') g.arc2(x=r, y=r, i=0, j=r, direction='CCW') g.move(x=dr) steps = calc_steps(cut_depth, -depth_of_cut) run_3_stages(path, g, steps) g.move(z=-cut_depth) # up to the starting point g.feed(mat['travel_plunge']) # go fast again...else. wow. boring. g.move(z=1.0) return_center = True if 'return_center' in kwargs: return_center = kwargs['return_center'] if return_center: g.move(x=-radius_inner) # back to center of the circle
def rectangleTool(g, mat, cut_depth, dx, dy, fillet, origin, align, fill=False, adjust_trim=False): if cut_depth >= 0: raise RuntimeError('Cut depth must be less than zero.') with GContext(g): g.relative() g.feed(mat['travel_feed']) g.spindle('CW', mat['spindle_speed']) tool_size = mat['tool_size'] half_tool = tool_size / 2 x = 0 y = 0 x_sign = 0 y_sign = 0 rect_origin = origin if origin == "left": x_sign = 1 elif origin == "bottom": y_sign = 1 elif origin == "right": x_sign = -1 elif origin == "top": y_sign = -1 elif origin == "center": x_sign = 1 rect_origin = "left" else: raise RuntimeError("unrecognized origin") if origin == "center": g.move(x=-dx / 2) if align == 'inner': x = half_tool * x_sign y = half_tool * y_sign dx -= tool_size dy -= tool_size if adjust_trim: fillet -= half_tool if fillet < 0: fillet = 0 elif align == 'outer': x = -half_tool * x_sign y = -half_tool * y_sign dx += tool_size dy += tool_size if adjust_trim: if fillet > 0: fillet += half_tool elif align == "center": pass else: raise RuntimeError("unrecognized align") if dx == 0 and dy == 0: raise RuntimeError('dx and dy may not both be zero') if dx < 0 or dy < 0: raise RuntimeError('dx and dy must be positive') if abs(x) or abs(y): g.move(z=CNC_TRAVEL_Z) g.move(x=x, y=y) g.move(z=-CNC_TRAVEL_Z) if fill == False or dx == 0 or dy == 0: rectangle(g, mat, cut_depth, dx, dy, fillet, rect_origin) else: z_depth = 0 z_step = mat['pass_depth'] single_pass = True # the outer loop walks downward. while z_depth > cut_depth: this_cut = 0 if z_depth - z_step <= cut_depth: this_cut = cut_depth - z_depth single_pass = False z_depth = cut_depth else: this_cut = -z_step z_depth -= z_step dx0 = dx dy0 = dy fillet0 = fillet step = tool_size * 0.7 first = True total_step = 0 #print("dx={} dy={}".format(dx, dy)) # the inner loop walks inward. # note that the cut hasn't happened at the top of the loop; # so only abort when they cross while dx0 > 0 and dy0 > 0: #print(" dx={} dy={} step={}".format(dx0, dy0, step)); if first: first = False rectangle(g, mat, this_cut, dx0, dy0, fillet0, rect_origin, single_pass=single_pass, restore_z=False) else: g.move(x=step * x_sign, y=step * y_sign) total_step += step rectangle(g, mat, 0.0, dx0, dy0, fillet0, rect_origin, single_pass=True) # subtle the last cut doesn't overlap itself. # probably a better algorithm for this if dx0 - step * 2 < 0 or dy0 - step * 2 < 0: dx0 -= step dy0 -= step else: dx0 -= step * 2 dy0 -= step * 2 fillet0 -= step if fillet0 < 0: fillet0 = 0 g.move(x=-total_step * x_sign, y=-total_step * y_sign) # don't need to move down; z is not restored g.move(z=this_cut) g.move(z=-cut_depth) if abs(x) or abs(y): g.move(z=CNC_TRAVEL_Z) g.move(x=-x, y=-y) if origin == "center": g.move(x=dx / 2) g.move(z=-CNC_TRAVEL_Z)