include include semitone_strings = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "X", "XI"]; octave_strings = ["", "˙", "¨", "¯"]; octave_scales = [1.1, 1, 0.9, 0.8]; z_resolution = $preview ? 0.5 : 0.1; $fs = $preview ? 1 : 0.01; $fa = $preview ? 5 : 0.1; profile_arc_steps_backside = $preview ? 8 : 360; profile_arc_steps_topside = $preview ? 8 : 60; fret_width = 2.4; //2.8; // Jumbo = 0.11", 0.055" tall zero_fret_width = fret_width*1.5; zero_fret_extra_height = 1.2; fret_angle = 60; //Fret angle of 45° is normal, anything over 60 will not work correctly tube_radius = Cap_Spine + T_circumcenter[0]; standard_profile_center_x = T_circumcenter[0]; x_min = standard_profile_center_x - tube_radius; minimum_y_width = 26.4517; standard_y_width = 28; nut_spacing = 10; bridge_spacing = 20; y_width_extra_mm = standard_y_width - (2 * nut_spacing); y_width_extra = y_width_extra_mm / (2 * nut_spacing); module tube(from_fret, to_fret, scale_offset = 0, fingerboard_min_thick = 2.5, fingerboard_max_thick = 5) { function fsl(fret_number) = fret_scale_length(fret_number + scale_offset); z0 = fsl(0); function get_desired_width_scale(z) = let (spacing = 2.0-(z/z0)) (spacing + y_width_extra)/(1 + y_width_extra); function get_desired_profile_radius(ws) = let ( x = fingerboard_max_thick, y = standard_y_width*0.5*ws, cc = tri_circumcenter([[x, -y], [x, y], [x_min, 0]]) ) cc[0]-x_min; z_min = fsl(to_fret) - fret_width/2; z_max = fsl(from_fret) + zero_fret_width/2; z_offset = z_min; Cap_Length = z_max - z_min; echo(str("Cap length from fret ", from_fret, " to ", to_fret, " is ", Cap_Length, "mm")); module solid_tube(cap_taper = 1.5) { function get_x_max(z) = let ( fret_num = mm_to_fret_number(z)-scale_offset, nearest_fret = round(fret_num), nearest_fret_height = fingerboard_max_thick + ((nearest_fret == 0) ? zero_fret_extra_height : 0), nearest_fret_mm = fsl(nearest_fret) ) z; module outline_xy(width_scale = 1.0, x_max = fingerboard_max_thick, rounding_radius = 1.5, backside = true, topside = true) { profile_radius = get_desired_profile_radius(width_scale); profile_radius_scale = profile_radius / tube_radius; profile_center_x = (profile_radius_scale-1.0)*tube_radius + standard_profile_center_x; // Backside should join up relatively straight across the whole mesh a_backside_start = acos((-2-profile_center_x)/profile_radius); a_backside_end = 360 - a_backside_start; a_backside_step = (a_backside_end - a_backside_start)/profile_arc_steps_backside; // Frontside should have very detailed topology a_topside_start = acos((x_max-profile_center_x)/profile_radius); a_topside_end = 360 - a_topside_start; a_topside_step = (a_backside_start - a_topside_start)/profile_arc_steps_topside; function pt(a) = [profile_center_x + profile_radius*cos(a), profile_radius*sin(a)]; backside_points = backside ? [for (i = [0:profile_arc_steps_backside]) pt(a_backside_start + i*a_backside_step)] : []; topside_points_1 = topside ? [for (i = [0:profile_arc_steps_topside]) pt(a_topside_start + i*a_topside_step)] : []; topside_points_2 = topside ? [for (i = [0:profile_arc_steps_topside]) pt(a_backside_end + i*a_topside_step)] : []; polygon(points = concat(topside_points_1, backside_points, topside_points_2)); top_width = pt(a_topside_start)[1] * 2; echo(WScale=width_scale, PScale=profile_radius_scale, r=profile_radius, TopWidth=top_width, StringSpacing=(top_width-y_width_extra_mm)/2, dv=top_width/width_scale, goal=standard_y_width); } module fret_bumps(w=100) { translate([fingerboard_max_thick - fret_width/2, w/2, -z_offset + fsl(from_fret)]) rotate([90,0,0]) cylinder(h = w, r = zero_fret_width/2, $fn=32); translate([fingerboard_max_thick - fret_width/2, w/2, -z_offset]) for (fret = [from_fret+1:to_fret]) translate([0, 0, fsl(fret)]) rotate([90,0,0]) cylinder(h = w, r = fret_width/2, $fn=32); } module scallops(w=100) { // for (fret = [from_fret:to_fret+1]) for (fret = [from_fret:to_fret]) let( f_prev = fsl(fret-1) - z_offset, f_next = fsl(fret) - z_offset, o = fret_width/2 * sin(fret_angle), x0 = f_prev - o, x1 = f_next + o, y0 = fingerboard_min_thick, y1 = fingerboard_max_thick - (fret_width/2 * cos(fret_angle)), pts = [[x0, y1], [x1, y1], [(x0+x1)/2, y0]], cc = tri_circumcenter(pts), r = norm([x0, y1] - cc) ) { translate([0,-w/2,0]) rotate([-90,-90,0]) translate(cc) cylinder(h = w, r = r, $fn=max(8*ceil(r), 32)); } } module basic_fretboard() { intersection() { hull() { translate([0,0,Cap_Length]) linear_extrude(0.001) outline_xy(get_desired_width_scale(z_max), topside=true); translate([0,0,0]) linear_extrude(0.001) outline_xy(get_desired_width_scale(z_min), topside=true); } union() { color("black") fret_bumps(); color("white", alpha=0.01) translate([-50,-50,0]) cube([50 + fingerboard_max_thick - (fret_width/2 * cos(fret_angle)), 100, Cap_Length]); } } } // Make a solid tube and then subtract the notches difference() { basic_fretboard(); difference() { color("red") scallops(); color("blue") fret_bumps(); } } // color("red") scallops(); } module fret_number_markers(engrave_depth=1) { color("purple") // for (fret = [from_fret:to_fret]) for (fret = [from_fret+1:to_fret]) { zn = fsl(fret); pr = get_desired_profile_radius(get_desired_width_scale(zn)); octave = floor(fret/12); semitone = fret%12; octave_scale = octave_scales[octave]; // translate([-3,-16,zn-z_offset]) rotate([90,25,16]) linear_extrude(100) { // translate([-3,-16,zn-z_offset]) rotate([90,25,-16+16*(zn/z0)]) linear_extrude(100) { p_x0 = pr+x_min; translate([p_x0, 0, zn-z_offset]) rotate([90,25,-asin((p_x0+2)/pr)]) translate([0, 0, pr-engrave_depth]) linear_extrude(engrave_depth+1) { text(semitone_strings[semitone], size=4*octave_scale, halign="center", valign="center", font="Noto Sans"); text(octave_strings[octave], size=6*octave_scale, halign="center", valign="center", font="Noto Sans"); } translate([p_x0, 0, zn-z_offset]) rotate([90,-25,180+asin((p_x0+2)/pr)]) translate([0, 0, pr-engrave_depth]) linear_extrude(engrave_depth+1) { text(semitone_strings[semitone], size=4*octave_scale, halign="center", valign="center", font="Noto Sans"); text(octave_strings[octave], size=6*octave_scale, halign="center", valign="center", font="Noto Sans"); } } } difference() { solid_tube(1.5); linear_extrude(Cap_Length) T_hole(); fret_number_markers(); tapered_T_hole(z0 = 0, z1 = 3); tapered_T_hole(z0 = Cap_Length-3, z1 = Cap_Length+0.0001, o0 = 0, o1 = 1); // Version number translate([-10,10,Cap_Length-0.5]) linear_extrude(1) text("06", size=5, halign="center", valign="center", font="Noto Sans"); } } tube(from_fret = 0, to_fret = 36, scale_offset = -5); //translate([50, 0]) tube(from_fret = -4, to_fret = -3); //translate([100, 0]) tube(from_fret = -2, to_fret = 0); //translate([150, 0]) tube(from_fret = 1, to_fret = 3); //translate([200, 0]) tube(from_fret = 4, to_fret = 7); //translate([250, 0]) tube(from_fret = 8, to_fret = 12); //translate([300, 0]) tube(from_fret = 13, to_fret = 19); //translate([350, 0]) tube(from_fret = 20, to_fret = 30);