/* [Knob dimensions] */ outer_diameter = 30; // [20:0.5:60] total_height = 14; // [8:0.5:30] top_fillet_radius = 2; // [0.5:0.25:4] /* [Nut pocket and bore] */ nut_across_flats = 10; // [8:0.1:16] nut_pocket_depth = 5; // [2:0.1:8] through_hole_diameter = 6.5; // [3:0.1:10] // Optional additional clearance; zero preserves the requested dimensions. nut_fit_clearance = 0; // [0:0.05:0.5] hole_fit_clearance = 0; // [0:0.05:0.5] /* [Diamond knurl] */ grooves_per_direction = 36; // [18:1:60] knurl_depth = 1; // [0.3:0.1:1.5] helix_angle = 45; // [25:1:60] groove_included_angle = 90; // [60:5:110] /* [Appearance and resolution] */ knob_color = "Silver"; // [Silver, SlateGray, SteelBlue, DarkOrange] circumference_segments = 180; // [96:12:288] helix_slices = 96; // [48:12:180] fillet_segments = 32; // [12:4:64] /* [Hidden] */ $fn = circumference_segments; outer_radius = outer_diameter / 2; knurled_height = total_height - top_fillet_radius; helix_twist = knurled_height * tan(helix_angle) / outer_radius * 180 / PI; boolean_overlap = 0.02; // Revolved quarter-circle gives a true 2 mm outer top fillet, // while keeping the bottom flat at Z=0 and the top at Z=14. module knob_blank() { rotate_extrude(convexity=10, $fn=circumference_segments) polygon(points=concat( [[0,0], [outer_radius,0], [outer_radius,knurled_height]], [for (arc_step=[1:fillet_segments]) let(arc_angle=90*arc_step/fillet_segments) [outer_radius-top_fillet_radius+top_fillet_radius*cos(arc_angle), knurled_height+top_fillet_radius*sin(arc_angle)]], [[0,total_height]] )); } // The transverse V is widened by 1/cos(helix_angle) so its // included angle normal to the helical groove is 90 degrees. module groove_profiles() { cutter_extension = knurl_depth + 2; cutter_half_width = cutter_extension * tan(groove_included_angle/2) / cos(helix_angle); for (groove_index=[0:grooves_per_direction-1]) rotate(groove_index*360/grooves_per_direction) polygon(points=[ [outer_radius-knurl_depth,0], [outer_radius-knurl_depth+cutter_extension,-cutter_half_width], [outer_radius-knurl_depth+cutter_extension,cutter_half_width] ]); } module diamond_knurl_cutters() { // Cuts stop at the fillet tangent, leaving the entire rounded edge smooth. for (handedness=[-1,1]) linear_extrude(height=knurled_height, twist=handedness*helix_twist, slices=helix_slices, convexity=20) groove_profiles(); } module nut_pocket() { translate([0,0,-boolean_overlap]) cylinder(h=nut_pocket_depth+boolean_overlap, d=(nut_across_flats+nut_fit_clearance)/cos(30), $fn=6); } module clearance_bore() { translate([0,0,-boolean_overlap]) cylinder(h=total_height+2*boolean_overlap, d=through_hole_diameter+hole_fit_clearance, $fn=96); } color(knob_color) difference() { knob_blank(); diamond_knurl_cutters(); nut_pocket(); clearance_bore(); }