def check_frame_size(contour): # We're limited by our stock size and clamp clearances # We can be about 160mm wide max, and about 65mm high max. if abs(poly.top(contour)) > 86: return "Frame is too wide: %f mm" % (poly.top(contour) * 2) if poly.right(contour) - poly.left(contour) > 60: return "Frame is too tall: %f mm" % (poly.right(contour) - poly.left(contour)) return None
def index_holes(contours, thickness): # We put the index holes 1/2 between top and bottom, 160mm apart # log(str(poly.right(face_c))) # log(str(poly.left(face_c))) # log(str(poly.right(face_c) - poly.left(face_c))) # log(str((poly.right(face_c) - poly.left(face_c))/2)) # log(str(poly.right(face_c) - (poly.right(face_c) - poly.left(face_c))/2)) rightmost = -1000000 leftmost = 1000000 for contour in contours: right = poly.right(contour) left = poly.left(contour) rightmost = max(right, rightmost) leftmost = min(left, leftmost) # x_offset = poly.right(face_c) - (poly.right(face_c) - poly.left(face_c))/2 x_offset = rightmost - (rightmost - leftmost) / 2 hole_radius = 4.85 / 2 # Measured from dowel pin tool_radius = 3.175 / 2 helix_radius = hole_radius - tool_radius r_hole = [x_offset, 90] l_hole = [x_offset, -90] r = [ cam.comment("Index Holes for secondary operations"), cam.change_tool("1/8in endmill"), cam.start_spindle(15000), cam.feedrate(1000), cam.dwell(3), cam.rmh(r_hole + [-thickness - 1.0], helix_radius, 0.5, 1), cam.rmh(l_hole + [-thickness - 1.0], helix_radius, 0.5, 1), ] return r
def index_holes(contours, thickness): # We put the index holes 1/2 between top and bottom, 160mm apart # log(str(poly.right(face_c))) # log(str(poly.left(face_c))) # log(str(poly.right(face_c) - poly.left(face_c))) # log(str((poly.right(face_c) - poly.left(face_c))/2)) # log(str(poly.right(face_c) - (poly.right(face_c) - poly.left(face_c))/2)) rightmost = -1000000 leftmost = 1000000 for contour in contours: right = poly.right(contour) left = poly.left(contour) rightmost = max(right, rightmost) leftmost = min(left, leftmost) # x_offset = poly.right(face_c) - (poly.right(face_c) - poly.left(face_c))/2 x_offset = rightmost - (rightmost - leftmost)/2 hole_radius = 4.85/2 # Measured from dowel pin tool_radius = 3.175/2 helix_radius = hole_radius - tool_radius r_hole = [x_offset, 90] l_hole = [x_offset, -90] r = [ cam.comment("Index Holes for secondary operations"), cam.change_tool("1/8in endmill"), cam.start_spindle(15000), cam.feedrate(1000), cam.dwell(3), cam.rmh(r_hole + [-thickness - 1.0], helix_radius, 0.5, 1), cam.rmh(l_hole + [-thickness - 1.0], helix_radius, 0.5, 1), ] return r
def frame_offset(contour): """ The origin of the glasses is in the middle of the pair (i.e. centered at Y) with the X origin at the line between the pupils. The fixture on the milling machine has its origin centered on the Y axis but with the X axis at the edge of the clamp. We need to shift the machine origin toward the middle of the stock. """ xoffset = poly.right(contour) + 10 # Offset by the furthest point, plus some extra for the tool return [xoffset, 0]
def frame_offset(contour): """ The origin of the glasses is in the middle of the pair (i.e. centered at Y) with the X origin at the line between the pupils. The fixture on the milling machine has its origin centered on the Y axis but with the X axis at the edge of the clamp. We need to shift the machine origin toward the middle of the stock. """ xoffset = poly.right(contour) + 12.5 # Offset by the furthest point, plus some extra for the tool #Note: we don't actually need room for the tool since we're laser cutting the outside. return [xoffset, 0]
def mill_fronts(outdir, order): """Creates the g-code for the first milling operation. The first milling operation is done on an unregistered plastic blank, so includes creating registration holes for subsequent operations.""" # Initial thickness of the forward-facing lamination. 0 if no lamination. top_thickness = 6 bottom_thickness = 0 top_raw = 6 bottom_raw = 0 if order.get("face_material"): top_thickness = order["face_material"].get("top_thickness") bottom_thickness = order["face_material"].get("bottom_thickness") or 0 top_raw = order["face_material"].get("top_raw_thickness") or top_thickness bottom_raw = order["face_material"].get("bottom_raw_thickness") or bottom_thickness frame_thickness = top_thickness + bottom_thickness machining_z_offset = bottom_raw - bottom_thickness # top_thickness = 6 # bottom_thickness = 0 # total_thickness = 6 # top_raw = 0 # Automatically determine some surfacing. The bottom thickness # is a lamination and should be thin. It should be thinned to 1mm. # The top should be thinned to bring total finished thickness to 6mm or less. thin_back = 0 if bottom_raw > bottom_thickness: thin_back = bottom_raw - bottom_thickness thin_front = 0 if top_raw > top_thickness: thin_front = top_raw - top_thickness # The machine has the stock clamp oriented 90 degrees to the way the # software creates the contours. face_c = poly.rotate_90(order["face_con"]) left_lens_c = poly.rotate_90(order["lhole_con"]) right_lens_c = poly.mirror_y(left_lens_c, True) face_c = face_c + poly.reverse(poly.mirror_y(face_c, False))[1:] temple_height = abs(order["ltemple_con"][0][1] - order["ltemple_con"][-1][1]) msg = check_frame_size(left_lens_c) if msg: print msg sys.exit(1) # Instead of offset, we'll make sure this thing is centered on the stock offset = frame_offset(face_c) top = poly.top(face_c) bottom = poly.bottom(face_c) left = poly.left(face_c) right = poly.right(face_c) print 'frame bounding box: ', top, bottom, left, right y_shift = (top + bottom)/2 x_shift = (left + right)/2 print 'x and y shift', x_shift, y_shift face_c = poly.translate(face_c, -x_shift, -y_shift) left_lens_c = poly.translate(left_lens_c, -x_shift, -y_shift) right_lens_c = poly.translate(right_lens_c, -x_shift, -y_shift) # Groove for lenses is 2/3 of the distance from back to front. # Here we're calculating the actual cutting depth so we need to add # back the material that we surfaced away from the back. groove_depth = -(float(machining_z_offset) + (2.0/3)*float(frame_thickness)) hinge_loc = order["ltemple_con"][0][1] - (order["ltemple_con"][0][1] - order["ltemple_con"][-1][1])/2 size_info = order.get('size_info') if not size_info and order.get('usersizes'): # Legacy order size_info = order.get('usersizes') size_info["noseradius"] = size_info["nose_radius"] size_info["noseheight"] = size_info["nose_height"] size_info["splayangle"] = size_info["nose_splayangle"] size_info["ridgeangle"] = size_info["nose_ridgeangle"] generic = not size_info or len(size_info) == 0 generic = True # TESTING program = [ cam.setup(), cam.select_fixture("blank_clamp"), cam.retract_spindle(), cam.activate_pin("stock_clamp"), cam.rapid([0,0]), surface_front(thin_front), surface_back(thin_back), cam.change_tool("1/8in endmill"), cam.rapid([0, 0]), # Note that X and Y parameters in the order are switched from our system #TODO: replace the thickness offset with the thickness of the TEMPLE, not the fronts. index_holes(frame_thickness+machining_z_offset), lens_holes(left_lens_c, right_lens_c, frame_thickness + machining_z_offset), lens_groove(left_lens_c, right_lens_c, groove_depth), # rough_nose_contour( # float(size_info["noseradius"]), # float(size_info["noseheight"]), # float(size_info["splayangle"]), # float(size_info["ridgeangle"]), # face_c, frame_thickness, machining_z_offset, -x_shift ), face_hinge_pockets(order.get("lhinge") or 1, hinge_loc, order["ltemple_x"], (-x_shift, -y_shift), machining_z_offset), #nose_pads(order, thickness), nose_contour( float(size_info["noseradius"]), float(size_info["noseheight"]), float(size_info["splayangle"]), float(size_info["ridgeangle"]), face_c, frame_thickness, machining_z_offset, -x_shift ), # nose_contour( # 7.0, 8.0, 30.0, 32.0, face_c, frame_thickness, machining_z_offset, -x_shift), #generic_nose_contour(face_c, frame_thickness, machining_z_offset, -x_shift), cam.retract_spindle(), cam.deactivate_pin("stock_clamp"), cam.change_tool("1/8in endmill"), # cam.rapid([face_c[0][0], face_c[0][1], -thin_back] ), # cam.contour(face_c, True), cam.end_program(), ] print 'Writing face milling program to ', outdir + "/face_stange1.ngc" open(outdir + "/face_stage1.ngc", "w").write(to_string(program))
def arrange_temple_curves(left_temple_contour, hinge): # Normalize temple to get origin at 0,0 left_temple_contour = poly.translate(left_temple_contour, -left_temple_contour[0][0], -left_temple_contour[0][1]) right_temple_contour = poly.mirror_x(left_temple_contour, False) left_hinge = hinges.get_hinge(hinge) right_hinge = hinges.get_hinge(hinge, False) hinge_y = (left_temple_contour[0][1]+left_temple_contour[-1][1])/2 left_holes = left_hinge['temple_holes'] right_holes = right_hinge['temple_holes'] # opposite direction. FIX someday right_hinge_contour = right_hinge['temple_contour'] left_hinge_contour = left_hinge['temple_contour'] # y offset is mucked up because it's calculated from the center of the hinge boxing # square, not the center of the straight edge highpoint = poly.top(left_hinge_contour); lowpoint = poly.bottom(left_hinge_contour); y_offset = hinge_y-(lowpoint + (highpoint-lowpoint)/2.0) # Distance to move hinge to center it on temple # strategy for hinge placement. Align corner of print left_temple_contour[0][0], left_temple_contour[-1][0] temple_x = left_temple_contour[0][0] - (left_temple_contour[0][0] - left_temple_contour[-1][0]) / 2 x_offset = temple_x - left_hinge_contour[0][0] - 2.5 left_hinge_contour = poly.translate(left_hinge_contour, x_offset, y_offset) left_holes = poly.translate(left_holes, x_offset, y_offset) right_hinge_contour = poly.translate(right_hinge_contour, -x_offset, y_offset) right_holes = poly.translate(right_holes, -x_offset, y_offset) left_temple_contour = poly.rotate_90(left_temple_contour) left_hinge_contour = poly.rotate_90(left_hinge_contour) left_holes = poly.rotate_90(left_holes) right_temple_contour = poly.rotate(right_temple_contour, 90) right_hinge_contour = poly.rotate(right_hinge_contour, 90) right_holes = poly.rotate(right_holes, 90) # Move them apart so they're not touching height = poly.right(left_temple_contour) - poly.left(left_temple_contour)+1 left_temple_contour = poly.translate(left_temple_contour, height, 0) left_holes = poly.translate(left_holes, height, 0) left_hinge_contour = poly.translate(left_hinge_contour, height, 0) right_temple_contour = poly.translate(right_temple_contour, -height, 0) right_holes = poly.translate(right_holes, -height, 0) right_hinge_contour = poly.translate(right_hinge_contour, -height, 0) # # Move left one left, right one right to offset them temple_width = poly.top(left_temple_contour) - poly.bottom(left_temple_contour) optimization_offset = (160-temple_width)/2.0 print 'optimization offset', optimization_offset, temple_width left_temple_contour = poly.translate(left_temple_contour, 0, optimization_offset) left_holes = poly.translate(left_holes, 0, optimization_offset) left_hinge_contour = poly.translate(left_hinge_contour, 0, optimization_offset) right_temple_contour = poly.translate(right_temple_contour, 0, -optimization_offset) right_hinge_contour = poly.translate(right_hinge_contour, 0, -optimization_offset) right_holes = poly.translate(right_holes, 0, -optimization_offset) # Make sure they're not intersecting # translation_step = 2.5 # intersection = poly.intersection(left_temple_contour, right_temple_contour) # while(len(intersection) > 0): # left_temple_contour = poly.translate(left_temple_contour, translation_step, 0) # left_holes = poly.translate(left_holes, translation_step, 0) # left_hinge_contour = poly.translate(left_hinge_contour, translation_step, 0) # right_temple_contour = poly.translate(right_temple_contour, -translation_step, 0) # right_holes = poly.translate(right_holes, -translation_step, 0) # right_hinge_contour = poly.translate(right_hinge_contour, -translation_step, 0) # intersection = poly.intersection(left_temple_contour, right_temple_contour) # Move them together until they touch (may will not have moved at all above because they are far) # then back off. #smaller translation step (was 1.0) translation_step = 0.1 intersection = [] while(len(intersection) == 0): print "still intersecting", len(intersection) left_temple_contour = poly.translate(left_temple_contour, -translation_step, 0) left_holes = poly.translate(left_holes, -translation_step, 0) left_hinge_contour = poly.translate(left_hinge_contour, -translation_step, 0) right_temple_contour = poly.translate(right_temple_contour, translation_step, 0) right_holes = poly.translate(right_holes, translation_step, 0) right_hinge_contour = poly.translate(right_hinge_contour, translation_step, 0) intersection = poly.intersection(left_temple_contour, right_temple_contour) # # Sarah commented the code below out as it seems to be speading the temples out farther than necessary # And I don't think we need it? # We're just overlapping, so now back off # translation_step = 0.5 # left_temple_contour = poly.translate(left_temple_contour, translation_step, 0) # left_holes = poly.translate(left_holes, translation_step, 0) # left_hinge_contour = poly.translate(left_hinge_contour, translation_step, 0) # right_temple_contour = poly.translate(right_temple_contour, -translation_step, 0) # right_holes = poly.translate(right_holes, -translation_step, 0) # right_hinge_contour = poly.translate(right_hinge_contour, -translation_step, 0) # # sanity check that we fit on stock total_width = poly.right(left_temple_contour) - poly.left(right_temple_contour) print "before spreading, total width is:", total_width if total_width > 55: print 'Error! temples did not pack tight enough.', total_width raise 'Sizing error' # ## Spread them out while total_width < 55: print "spreading out temples" left_temple_contour = poly.translate(left_temple_contour, translation_step, 0) left_holes = poly.translate(left_holes, translation_step, 0) left_hinge_contour = poly.translate(left_hinge_contour, translation_step, 0) right_temple_contour = poly.translate(right_temple_contour, -translation_step, 0) right_holes = poly.translate(right_holes, -translation_step, 0) right_hinge_contour = poly.translate(right_hinge_contour, -translation_step, 0) total_width = poly.right(left_temple_contour) - poly.left(right_temple_contour) intersection = poly.intersection(left_temple_contour, right_temple_contour) if len(intersection) > 0: print 'Error! Temples are too big to fit on stock.', total_width raise 'Sizing Error' print 'Total width of temples', total_width # Now they're packed togegther OK but not centred on the stock #Midpoint of curves should be 0, so that's how much we'll shift it y_centering_shift = poly.bottom(right_temple_contour) + (poly.top(left_temple_contour) - poly.bottom(right_temple_contour))/2.0 x_centering_shift = (poly.right(left_temple_contour) + poly.left(right_temple_contour))/2; left_temple_contour = poly.translate(left_temple_contour, -x_centering_shift, -y_centering_shift) right_temple_contour = poly.translate(right_temple_contour, -x_centering_shift, -y_centering_shift) left_hinge_contour = poly.translate(left_hinge_contour, -x_centering_shift, -y_centering_shift) right_hinge_contour = poly.translate(right_hinge_contour, -x_centering_shift, -y_centering_shift) left_holes = poly.translate(left_holes, -x_centering_shift, -y_centering_shift) right_holes = poly.translate(right_holes, -x_centering_shift, -y_centering_shift) # The temple contours aren't closed, close them here # NOTE: Taken out because we close the curves with a little arc to account for front curvature #left_temple_contour.append(left_temple_contour[0]) #right_temple_contour.append(right_temple_contour[0]) return { "pocket_depth": 1, "left_hinge_contour": left_hinge_contour, "right_hinge_contour": right_hinge_contour, "left_hinge_holes": left_holes, "right_hinge_holes": right_holes, "left_temple_contour": left_temple_contour, "right_temple_contour": right_temple_contour }
def arrange_temple_curves(left_temple_contour, right_temple_contour, hinge, lhinge_y, rhinge_y): left_hinge = hinges.get_hinge(hinge) right_hinge = hinges.get_hinge(hinge, False) print 'first and last point of left_temple_contour' print left_temple_contour[0], left_temple_contour[-1] left_holes = poly.rotate_90(left_hinge['temple_holes']) right_holes = poly.rotate(right_hinge['temple_holes'], 90) # opposite direction. FIX someday left_hinge_contour = poly.rotate_90(left_hinge['temple_contour']) right_hinge_contour = poly.rotate(right_hinge['temple_contour'], 90) #right_hinge_contour = right_hinge['temple_contour'] #left_holes = left_hinge['temple_holes'] #right_holes = right_hinge['temple_holes'] # Get the thing as horizontal as possible # When top-bottom distance is minimized, it's horizontal height = poly.top(left_temple_contour) - poly.bottom(left_temple_contour) opt_angle = 0 for angle in range(2, 41): candidate_contour = poly.rotate(left_temple_contour, -angle) candidate_height = poly.top(candidate_contour) - poly.bottom( candidate_contour) if candidate_height < height: height = candidate_height opt_angle = angle else: break # The temple is raised or lowered as compared to the Y axis. We need # to bring it to the Y origin for rotation to avoid offsetting it, then # send it back to its original spot. original_y_offset = left_temple_contour[0][1] - ( left_temple_contour[0][1] - left_temple_contour[-1][1]) / 2 left_temple_contour = poly.translate(left_temple_contour, 0, -original_y_offset) left_temple_contour = poly.rotate(left_temple_contour, -opt_angle) left_temple_contour = poly.translate(left_temple_contour, 0, original_y_offset) right_temple_contour = poly.translate(right_temple_contour, 0, -original_y_offset) right_temple_contour = poly.rotate(right_temple_contour, opt_angle) right_temple_contour = poly.translate(right_temple_contour, 0, original_y_offset) #left_holes = poly.rotate(left_hinge['temple_holes'], -opt_angle) #right_holes = poly.rotate(right_hinge['temple_holes'], opt_angle) #left_hinge_contour = poly.rotate(left_hinge['temple_contour'], -opt_angle) #right_hinge_contour = poly.rotate(right_hinge['temple_contour'], opt_angle) left_holes = poly.rotate(left_holes, -opt_angle) right_holes = poly.rotate(right_holes, opt_angle) left_hinge_contour = poly.rotate(left_hinge_contour, -opt_angle) right_hinge_contour = poly.rotate(right_hinge_contour, opt_angle) left_hinge_contour = poly.translate(left_hinge_contour, lhinge_y, -left_hinge['pocket_depth']) right_hinge_contour = poly.translate(right_hinge_contour, rhinge_y, right_hinge['pocket_depth']) # Left and right translate are reversed because we've rotated everything left_holes = poly.translate(left_holes, lhinge_y, -left_hinge['pocket_depth']) right_holes = poly.translate(right_holes, rhinge_y, right_hinge['pocket_depth']) temple_width = poly.right(left_temple_contour) - poly.left( left_temple_contour) print "temple width", temple_width lt_trans = [temple_width / 2, 0] rt_trans = [-lt_trans[0], 0] #lt_trans = [180-poly.right(left_temple_contour), -poly.top(left_temple_contour) - 30] #lt_trans = [-poly.right(left_temple_contour)-30, 180 -poly.top(left_temple_contour)] #rt_trans = [-poly.left(right_temple_contour)+25, lt_trans[1]-height] # Translate them left_temple_contour = poly.translate(left_temple_contour, lt_trans[0], lt_trans[1]) right_temple_contour = poly.translate(right_temple_contour, rt_trans[0], rt_trans[1]) left_hinge_contour = poly.translate(left_hinge_contour, lt_trans[1], -lt_trans[0]) right_hinge_contour = poly.translate(right_hinge_contour, rt_trans[1], -rt_trans[0]) left_holes = poly.translate(left_holes, lt_trans[1], -lt_trans[0]) right_holes = poly.translate(right_holes, rt_trans[1], -rt_trans[0]) # Translate bottom one upward as much as we can trans_amount = poly.bottom(left_temple_contour) - poly.top( right_temple_contour) - 8 for i in range(1, 100): candidate = poly.translate(right_temple_contour, 0, trans_amount + i) intersection = poly.intersection(candidate, left_temple_contour) if len(intersection) > 0: trans_amount = trans_amount + i - 8 right_temple_contour = poly.translate(right_temple_contour, 0, trans_amount) break print 'trans amount:', trans_amount right_hinge_contour = poly.translate(right_hinge_contour, trans_amount, 0) right_holes = poly.translate(right_holes, trans_amount, 0) return { "pocket_depth": left_hinge['pocket_depth'], "left_hinge_contour": left_hinge_contour, "right_hinge_contour": right_hinge_contour, "left_hinge_holes": left_holes, "right_hinge_holes": right_holes, "left_temple_contour": left_temple_contour, "right_temple_contour": right_temple_contour }
def contour_face(body_removal, hinge_removal, nosepad_removal, temple_height, face_c, lens_c, x_pos): ''' Create the heightmap of the frame, surfacing the back and adding thickness for the hinge location and the nosepads. ''' if body_removal == hinge_removal == nosepad_removal == 0: return [] # Nothing to do cutter_radius = 6.35 / 2 # 3/4 inch cutter entry_point = [x_pos, 110, 0] facing_contour = poly.dilate(0.05, lens_c) # Reshape the facing contour so the first point is near the hinge center_y = poly.bottom(facing_contour) + (poly.top(facing_contour) - poly.bottom(facing_contour)) / 2 center_x = poly.right(facing_contour) + (poly.left(facing_contour) - poly.right(facing_contour)) / 2 split_idx = -1 for idx, pt in enumerate(facing_contour): if pt[1] > center_y and (idx + 1) < len(facing_contour): if (pt[0] < x_pos and facing_contour[idx + 1][0] > x_pos) or ( pt[0] > x_pos and facing_contour[idx + 1][0] < x_pos): split_idx = idx break if split_idx < 0: print 'Error contouring back of frame: could not locate entry point for surfacing cut' return [] facing_contour = poly.new_start(facing_contour, split_idx) # Ensure we're going clockwise, i.e. starting at the hinge and moving up over the frame if poly.is_ccw(facing_contour): facing_contour = poly.reverse(facing_contour) # Calculate the Z values # We'll need a few helper values. nosepad_start is the inflection point of the nose bridge. nosepad_start = max([pt[0] for pt in face_c if pt[1] == 0]) + cutter_radius hinge_rampdown_start_x = x_pos + temple_height / 2 + cutter_radius hinge_rampdown_start_y = facing_contour[0][1] - cutter_radius hinge_rampup_start_x = x_pos - temple_height / 2 - cutter_radius hinge_rampup_start_y = facing_contour[0][1] - cutter_radius print nosepad_start, hinge_rampdown_start_x, hinge_rampdown_start_y, hinge_rampup_start_x, hinge_rampup_start_y ''' Arbitrary heuristic, adjusted for aesthetics. 1. If we're past the center point of the lens hole, we're either on the body of the frame or over the raised hinge point. 2. If we're before the center point we're either on the body or over the nosepiece. 1a. If we're above the cutter-radius-adjusted top of the temple, we're ramping down 1b. If we're below the cutter-radius-adjusted bottom of the temple, we're ramping up 1c. Otherwise we're at body thickness 2a. If we're above the top of the nose cutout, we're at body thickness 2b. When we reach nose cutout, we do a s-curve over 3 mm to nosepad height 2c. Continue for length of cutter diameter to get rear of cutter over highest point 2d. Continue for 10mm 2e. S-curve down over 10mm ''' print hinge_removal, body_removal def add_hinge_heights(contour): heightmap = [] over_hinge = True # Start over hinge items_to_skip = 0 # for fast-forwarding enumeration for idx, pt in enumerate(contour): if items_to_skip > 0: items_to_skip = items_to_skip - 1 if items_to_skip == 0: print 'first post ramp point', contour[idx + 1] continue if pt[1] < center_y: heightmap = heightmap + [pt] # Going up and around: start ramping down when we're clear of X or Y elif pt[0] > x_pos: if pt[0] > hinge_rampdown_start_x or pt[ 1] < hinge_rampdown_start_y: if (over_hinge): # starting transition transition_length = poly.polyline_length( contour[:(idx + 1)], False) ramp_segment = poly.segment(contour, transition_length, transition_length + 5, False) ramp_segment = poly.ramp(ramp_segment, hinge_removal, body_removal, False) heightmap = heightmap + ramp_segment[:-1] items_to_skip = len(ramp_segment) print 'last ramp segment', ramp_segment[-1] over_hinge = False else: # past transition but still on hinge side of lens hole heightmap = heightmap + [pt + [body_removal]] else: # We're on the top part but haven't reached the transition yet heightmap = heightmap + [pt + [hinge_removal]] # Coming back up to the hinge: start ramping up if we encroach on both x and y elif pt[0] < x_pos and (pt[0] > hinge_rampup_start_x and pt[1] > hinge_rampdown_start_y): if (not over_hinge): # starting transition print pt, x_pos, hinge_rampup_start_x, hinge_rampdown_start_y, idx transition_length = poly.polyline_length( contour[:(idx + 1)], False) ramp_segment = poly.segment(contour, transition_length, transition_length + 5, False) ramp_segment = poly.ramp(ramp_segment, body_removal, hinge_removal, False) heightmap = heightmap + ramp_segment items_to_skip = len(ramp_segment) over_hinge = True else: # Over flat hinge area heightmap = heightmap + [pt + [hinge_removal]] else: # We're over the body area but back on the hinge side heightmap = heightmap + [pt + [body_removal]] return heightmap def add_nosepad_heights(contour): heightmap = [] over_nosepad = False past_nosepad = False nosepad_flat_idx = -1 items_to_skip = 0 # for fast-forwarding the enumeration for idx, pt in enumerate(contour): if items_to_skip > 0: items_to_skip = items_to_skip - 1 continue if pt[1] >= center_y: heightmap = heightmap + [pt] elif not over_nosepad and not past_nosepad: if pt[0] < nosepad_start: # Transition transition_length = poly.polyline_length( contour[:(idx + 1)], False) ramp_segment = poly.segment(contour, transition_length, transition_length + 5, False) ramp_segment = poly.ramp(ramp_segment, body_removal, nosepad_removal, False) heightmap = heightmap + ramp_segment[:-1] items_to_skip = len(ramp_segment) nosepad_flat_idx = idx + items_to_skip # we'll need this to go down over_nosepad = True else: # we're past the nosepad heightmap = heightmap + [pt + [body_removal]] elif over_nosepad and not past_nosepad: if nosepad_flat_idx < 0: print "ERROR! I think I'm on the nosepad but have not transitioned yet" return [] # We'll be cutting the far side with the back of the cutter, so need to move at # least the diameter to get any flat at all flat_length = poly.polyline_length( contour[nosepad_flat_idx:(idx + 1)], False) - (cutter_radius * 2) if flat_length < 5: heightmap = heightmap + [pt + [nosepad_removal]] else: # ramp down transition_length = poly.polyline_length( contour[:(idx + 1)], False) ramp_segment = poly.segment(contour, transition_length, transition_length + 5, False) ramp_segment = poly.ramp(ramp_segment, nosepad_removal, body_removal, False) heightmap = heightmap + ramp_segment[:-1] items_to_skip = len(ramp_segment) nosepad_flat_idx = idx + items_to_skip # we'll need this to go down over_nosepad = False past_nosepad = True else: heightmap = heightmap + [pt + [body_removal]] return heightmap facing_contour = add_hinge_heights(facing_contour) facing_contour = add_nosepad_heights(facing_contour) facing_contour = poly.reverse(facing_contour) right_facing = poly.mirror_y(facing_contour, True) passes = [1] heights = [p[2] for p in facing_contour] r = [ cam.change_tool("1/4in ballmill"), cam.spindle_speed(22000), cam.feedrate(1000), cam.start_spindle(), cam.rmp(entry_point), cam.contour(facing_contour, True), ] for dilate in passes: dilated = poly.reverse(poly.dilate(dilate, facing_contour)) # dilated = add_hinge_heights(dilated) dilated = add_nosepad_heights(dilated) r = r + [ cam.contour(dilated, True), ] return r
def arrange_temple_curves(left_temple_contour, right_temple_contour, hinge, lhinge_y, rhinge_y): left_hinge = hinges.get_hinge(hinge) right_hinge = hinges.get_hinge(hinge, False) print 'first and last point of left_temple_contour' print left_temple_contour[0], left_temple_contour[-1] left_holes = poly.rotate_90(left_hinge['temple_holes']) right_holes = poly.rotate(right_hinge['temple_holes'], 90) # opposite direction. FIX someday left_hinge_contour = poly.rotate_90(left_hinge['temple_contour']) right_hinge_contour = poly.rotate(right_hinge['temple_contour'], 90) #right_hinge_contour = right_hinge['temple_contour'] #left_holes = left_hinge['temple_holes'] #right_holes = right_hinge['temple_holes'] # Get the thing as horizontal as possible # When top-bottom distance is minimized, it's horizontal height = poly.top(left_temple_contour) - poly.bottom(left_temple_contour) opt_angle = 0 for angle in range(2, 41): candidate_contour = poly.rotate(left_temple_contour, -angle) candidate_height = poly.top(candidate_contour)-poly.bottom(candidate_contour) if candidate_height < height: height = candidate_height opt_angle = angle else: break # The temple is raised or lowered as compared to the Y axis. We need # to bring it to the Y origin for rotation to avoid offsetting it, then # send it back to its original spot. original_y_offset = left_temple_contour[0][1] - (left_temple_contour[0][1] - left_temple_contour[-1][1])/2 left_temple_contour = poly.translate(left_temple_contour, 0, -original_y_offset) left_temple_contour = poly.rotate(left_temple_contour, -opt_angle); left_temple_contour = poly.translate(left_temple_contour, 0, original_y_offset) right_temple_contour = poly.translate(right_temple_contour, 0, -original_y_offset) right_temple_contour = poly.rotate(right_temple_contour, opt_angle); right_temple_contour = poly.translate(right_temple_contour, 0, original_y_offset) #left_holes = poly.rotate(left_hinge['temple_holes'], -opt_angle) #right_holes = poly.rotate(right_hinge['temple_holes'], opt_angle) #left_hinge_contour = poly.rotate(left_hinge['temple_contour'], -opt_angle) #right_hinge_contour = poly.rotate(right_hinge['temple_contour'], opt_angle) left_holes = poly.rotate(left_holes, -opt_angle) right_holes = poly.rotate(right_holes, opt_angle) left_hinge_contour = poly.rotate(left_hinge_contour, -opt_angle) right_hinge_contour = poly.rotate(right_hinge_contour, opt_angle) left_hinge_contour = poly.translate(left_hinge_contour, lhinge_y, -left_hinge['pocket_depth']) right_hinge_contour = poly.translate(right_hinge_contour, rhinge_y, right_hinge['pocket_depth']) # Left and right translate are reversed because we've rotated everything left_holes = poly.translate(left_holes, lhinge_y, -left_hinge['pocket_depth']) right_holes = poly.translate(right_holes, rhinge_y, right_hinge['pocket_depth']) temple_width = poly.right(left_temple_contour) - poly.left(left_temple_contour) print "temple width", temple_width lt_trans = [temple_width/2, 0] rt_trans = [-lt_trans[0], 0] #lt_trans = [180-poly.right(left_temple_contour), -poly.top(left_temple_contour) - 30] #lt_trans = [-poly.right(left_temple_contour)-30, 180 -poly.top(left_temple_contour)] #rt_trans = [-poly.left(right_temple_contour)+25, lt_trans[1]-height] # Translate them left_temple_contour = poly.translate(left_temple_contour, lt_trans[0], lt_trans[1]) right_temple_contour = poly.translate(right_temple_contour, rt_trans[0], rt_trans[1]) left_hinge_contour = poly.translate(left_hinge_contour, lt_trans[1], -lt_trans[0]) right_hinge_contour = poly.translate(right_hinge_contour, rt_trans[1], -rt_trans[0]) left_holes = poly.translate(left_holes, lt_trans[1], -lt_trans[0]) right_holes = poly.translate(right_holes, rt_trans[1], -rt_trans[0]) # Translate bottom one upward as much as we can trans_amount = poly.bottom(left_temple_contour) - poly.top(right_temple_contour) - 8 for i in range(1, 100): candidate = poly.translate(right_temple_contour, 0, trans_amount + i) intersection = poly.intersection(candidate, left_temple_contour) if len(intersection) > 0: trans_amount = trans_amount + i -8 right_temple_contour = poly.translate(right_temple_contour, 0, trans_amount) break print 'trans amount:', trans_amount right_hinge_contour = poly.translate(right_hinge_contour, trans_amount, 0) right_holes = poly.translate(right_holes, trans_amount, 0) return { "pocket_depth": left_hinge['pocket_depth'], "left_hinge_contour": left_hinge_contour, "right_hinge_contour": right_hinge_contour, "left_hinge_holes": left_holes, "right_hinge_holes": right_holes, "left_temple_contour": left_temple_contour, "right_temple_contour": right_temple_contour }
def contour_face(body_removal, hinge_removal, nosepad_removal, temple_height, face_c, lens_c, x_pos): ''' Create the heightmap of the frame, surfacing the back and adding thickness for the hinge location and the nosepads. ''' if body_removal == hinge_removal == nosepad_removal == 0: return [] # Nothing to do cutter_radius = 6.35/2 # 3/4 inch cutter entry_point = [x_pos, 110, 0] facing_contour = poly.dilate(0.05, lens_c) # Reshape the facing contour so the first point is near the hinge center_y = poly.bottom(facing_contour) + (poly.top(facing_contour) - poly.bottom(facing_contour))/2 center_x = poly.right(facing_contour) + (poly.left(facing_contour) - poly.right(facing_contour))/2 split_idx = -1 for idx, pt in enumerate(facing_contour): if pt[1] > center_y and (idx+1) < len(facing_contour): if (pt[0] < x_pos and facing_contour[idx+1][0] > x_pos) or (pt[0] > x_pos and facing_contour[idx+1][0] < x_pos): split_idx = idx break if split_idx < 0: print 'Error contouring back of frame: could not locate entry point for surfacing cut' return [] facing_contour = poly.new_start(facing_contour, split_idx) # Ensure we're going clockwise, i.e. starting at the hinge and moving up over the frame if poly.is_ccw(facing_contour): facing_contour = poly.reverse(facing_contour) # Calculate the Z values # We'll need a few helper values. nosepad_start is the inflection point of the nose bridge. nosepad_start = max([pt[0] for pt in face_c if pt[1] == 0]) + cutter_radius hinge_rampdown_start_x = x_pos + temple_height/2 + cutter_radius hinge_rampdown_start_y = facing_contour[0][1] - cutter_radius hinge_rampup_start_x = x_pos - temple_height/2 - cutter_radius hinge_rampup_start_y = facing_contour[0][1] - cutter_radius print nosepad_start, hinge_rampdown_start_x, hinge_rampdown_start_y, hinge_rampup_start_x, hinge_rampup_start_y ''' Arbitrary heuristic, adjusted for aesthetics. 1. If we're past the center point of the lens hole, we're either on the body of the frame or over the raised hinge point. 2. If we're before the center point we're either on the body or over the nosepiece. 1a. If we're above the cutter-radius-adjusted top of the temple, we're ramping down 1b. If we're below the cutter-radius-adjusted bottom of the temple, we're ramping up 1c. Otherwise we're at body thickness 2a. If we're above the top of the nose cutout, we're at body thickness 2b. When we reach nose cutout, we do a s-curve over 3 mm to nosepad height 2c. Continue for length of cutter diameter to get rear of cutter over highest point 2d. Continue for 10mm 2e. S-curve down over 10mm ''' print hinge_removal, body_removal def add_hinge_heights(contour): heightmap = [] over_hinge = True # Start over hinge items_to_skip = 0 # for fast-forwarding enumeration for idx, pt in enumerate(contour): if items_to_skip > 0: items_to_skip = items_to_skip - 1 if items_to_skip == 0: print 'first post ramp point', contour[idx+1] continue if pt[1] < center_y: heightmap = heightmap + [pt] # Going up and around: start ramping down when we're clear of X or Y elif pt[0] > x_pos: if pt[0] > hinge_rampdown_start_x or pt[1] < hinge_rampdown_start_y: if(over_hinge): # starting transition transition_length = poly.polyline_length(contour[:(idx+1)], False) ramp_segment = poly.segment(contour, transition_length, transition_length+5, False) ramp_segment = poly.ramp(ramp_segment, hinge_removal, body_removal, False) heightmap = heightmap + ramp_segment[:-1] items_to_skip = len(ramp_segment) print 'last ramp segment', ramp_segment[-1] over_hinge = False else: # past transition but still on hinge side of lens hole heightmap = heightmap + [pt + [body_removal]] else: # We're on the top part but haven't reached the transition yet heightmap = heightmap + [pt + [hinge_removal]] # Coming back up to the hinge: start ramping up if we encroach on both x and y elif pt[0] < x_pos and (pt[0] > hinge_rampup_start_x and pt[1] > hinge_rampdown_start_y): if(not over_hinge): # starting transition print pt, x_pos, hinge_rampup_start_x, hinge_rampdown_start_y, idx transition_length = poly.polyline_length(contour[:(idx+1)], False) ramp_segment = poly.segment(contour, transition_length, transition_length+5, False) ramp_segment = poly.ramp(ramp_segment, body_removal, hinge_removal, False) heightmap = heightmap + ramp_segment items_to_skip = len(ramp_segment) over_hinge = True else: # Over flat hinge area heightmap = heightmap + [pt + [hinge_removal]] else: # We're over the body area but back on the hinge side heightmap = heightmap + [pt + [body_removal]] return heightmap def add_nosepad_heights(contour): heightmap = [] over_nosepad = False past_nosepad = False nosepad_flat_idx = -1 items_to_skip = 0 # for fast-forwarding the enumeration for idx, pt in enumerate(contour): if items_to_skip > 0: items_to_skip = items_to_skip-1 continue if pt[1] >= center_y: heightmap = heightmap + [pt] elif not over_nosepad and not past_nosepad: if pt[0] < nosepad_start: # Transition transition_length = poly.polyline_length(contour[:(idx+1)], False) ramp_segment = poly.segment(contour, transition_length, transition_length+5, False) ramp_segment = poly.ramp(ramp_segment, body_removal, nosepad_removal, False) heightmap = heightmap + ramp_segment[:-1] items_to_skip = len(ramp_segment) nosepad_flat_idx = idx + items_to_skip # we'll need this to go down over_nosepad = True else: # we're past the nosepad heightmap = heightmap + [pt + [body_removal]] elif over_nosepad and not past_nosepad: if nosepad_flat_idx < 0: print "ERROR! I think I'm on the nosepad but have not transitioned yet" return [] # We'll be cutting the far side with the back of the cutter, so need to move at # least the diameter to get any flat at all flat_length = poly.polyline_length(contour[nosepad_flat_idx:(idx+1)], False) - (cutter_radius*2) if flat_length < 5: heightmap = heightmap + [pt + [nosepad_removal]] else: # ramp down transition_length = poly.polyline_length(contour[:(idx+1)], False) ramp_segment = poly.segment(contour, transition_length, transition_length+5, False) ramp_segment = poly.ramp(ramp_segment, nosepad_removal, body_removal, False) heightmap = heightmap + ramp_segment[:-1] items_to_skip = len(ramp_segment) nosepad_flat_idx = idx + items_to_skip # we'll need this to go down over_nosepad = False past_nosepad = True else: heightmap = heightmap + [pt + [body_removal]] return heightmap facing_contour = add_hinge_heights(facing_contour) facing_contour = add_nosepad_heights(facing_contour) facing_contour = poly.reverse(facing_contour) right_facing = poly.mirror_y(facing_contour, True) passes = [1] heights = [p[2] for p in facing_contour] r = [ cam.change_tool("1/4in ballmill"), cam.spindle_speed(22000), cam.feedrate(1000), cam.start_spindle(), cam.rmp(entry_point), cam.contour(facing_contour, True), ] for dilate in passes: dilated = poly.reverse(poly.dilate(dilate, facing_contour)) # dilated = add_hinge_heights(dilated) dilated = add_nosepad_heights(dilated) r = r + [ cam.contour(dilated, True),] return r