// files include include include include /* [Settings] */ // keytop thickness, aka how many millimeters between the inside and outside of the top surface of the key keytop_thickness = 1; // wall thickness, aka the thickness of the sides of the keycap. note this is the total thickness, aka 3 = 1.5mm walls wall_thickness = 3; //whether stabilizer connectors are enabled stabilizers = false; // font used for text font="DejaVu Sans Mono:style=Book"; // font size used for text font_size = 6; // whether or not to render fake keyswitches to check clearances clearance_check = false; /* [Key profile] */ // width of the very bottom of the key bottom_key_width = 18.16; // height (from the front) of the very bottom of the ke bottom_key_height = 18.16; // how much less width there is on the top. eg top_key_width = bottom_key_width - width_difference width_difference = 6; // how much less height there is on the top height_difference = 4; // how deep the key is, before adding a dish total_depth = 11.5; // the tilt of the dish in degrees. divided by key height top_tilt = -6; // how skewed towards the back the top is (0 for center) top_skew = 1.7; // what type of dish the key has. 0 for cylindrical, 1 for spherical, 2 for something else idk TODO dish_type = "cylindrical"; // how deep the dish 'digs' into the top of the keycap. this is max depth, so you can't find the height from total_depth - dish_depth. besides the top is skewed anyways dish_depth = 1; // how skewed in the x direction the dish is dish_skew_x = 0; // how skewed in the y direction (height) the dish is dish_skew_y = 0; //length in units of key key_length = 1; //height in units of key. should remain 1 for most uses key_height = 1; //print brim for connector to help with bed adhesion has_brim = false; // invert dishing. mostly for spacebar inverted_dish = false; // array of positions of all stems. includes stabilizers as well, for now // ternary is a bad hack to keep the stabilizers flag working connectors = stabilizers ? [[0,0],[-50,0],[50,0]] : [[0,0]]; // use linear_extrude instead of hull slices to make the shape of the key // should be faster, also required for concave shapes linear_extrude_shape = false; //should the key be rounded? unnecessary for most printers, and very slow rounded_key = false; // 'cherry', 'alps' or 'cherry_rounded' stem_profile = "cherry"; // how much higher the stem is than the bottom of the keycap. // inset stem requires support but is more accurate in some profiles stem_inset = 0; // how many degrees to rotate the stems. useful for sideways keycaps, maybe stem_rotation = 0; //text to be rendered in the center of the key, if any text = ""; // is the text on the key inset? inset text is still experimental inset_text = false; // radius of corners of keycap corner_radius = 1; // keystem slop - lengthens the cross and thins out the connector slop = 0.3; // support type. default is 'flared' for easy FDM printing support_type = "flared"; // key shape type. default is 'normal'. only other supported option is 'iso_enter' key_shape_type = "normal"; // ISO enter needs to be linear extruded NOT from the center. this tells the program how far up 'not from the center' is linear_extrude_height_adjustment = 0; /* [Fancy Bowed Sides] */ // if you're doing fancy bowed keycap sides, this controls how many slices you take // default of 1 for no sampling, just top/bottom height_slices = 1; enable_side_sculpting = false; /* [Hidden] */ $fs = .1; //beginning to use unit instead of baked in 19.05 unit = 19.05; //minkowski radius. radius of sphere used in minkowski sum for minkowski_key function. 1.75 default for faux G20 $minkowski_radius = .33; // derived values. can't be variables if we want them to change when the special variables do // actual mm key width and height function total_key_width() = $bottom_key_width + unit * ($key_length - 1); function total_key_height() = $bottom_key_height + unit * ($key_height - 1); // actual mm key width and height at the top function top_total_key_width() = $bottom_key_width + (unit * ($key_length - 1)) - $width_difference; function top_total_key_height() = $bottom_key_height + (unit * ($key_height - 1)) - $height_difference; // side sculpting functions // bows the sides out on stuff like SA and DSA keycaps function side_sculpting(progress) = (1 - progress) * 2.5; // makes the rounded corners of the keycap grow larger as they move upwards function corner_sculpting(progress) = pow(progress, 2); // key shape including dish. used as the ouside and inside shape in key() module shape(thickness_difference, depth_difference){ intersection(){ dished(depth_difference, $inverted_dish) { color([.2667,.5882,1]) shape_hull(thickness_difference, depth_difference); } if ($inverted_dish) { // larger shape_hull to clip off bits of the inverted dish color([.5412, .4784, 1]) shape_hull(thickness_difference, 0, 2); } } } // shape of the key but with soft, rounded edges. much more realistic, MUCH more complex. orders of magnitude more complex module rounded_shape() { render(){ minkowski(){ // half minkowski. that means the shape is neither circumscribed nor inscribed. shape($minkowski_radius * 2, $minkowski_radius/2); difference(){ sphere(r=$minkowski_radius, $fn=24); translate([0,0,-$minkowski_radius]) cube([2*$minkowski_radius,2*$minkowski_radius,2*$minkowski_radius], center=true); } } } } // basic key shape, no dish, no inside // which is only used for dishing to cut the dish off correctly // $height_difference used for keytop thickness // extra_slices is a hack to make inverted dishes still work module shape_hull(thickness_difference, depth_difference, extra_slices = 0){ render() { if ($linear_extrude_shape) { linear_extrude_shape_hull(thickness_difference, depth_difference); } else { hull_shape_hull(thickness_difference, depth_difference, extra_slices); } } } //corollary is shape_hull module linear_extrude_shape_hull(thickness_difference, depth_difference){ height = $total_depth - depth_difference; width_scale = top_total_key_width() / total_key_width(); height_scale = top_total_key_height() / total_key_height(); translate([0,$linear_extrude_height_adjustment,0]){ linear_extrude(height = height, scale = [width_scale, height_scale]) { translate([0,-$linear_extrude_height_adjustment,0]){ key_shape(total_key_width(), total_key_height(), thickness_difference, thickness_difference, $corner_radius); } } } } module hull_shape_hull(thickness_difference, depth_difference, extra_slices = 0) { slices = 10; for (index = [0:$height_slices - 1 + extra_slices]) { hull() { shape_slice(index / $height_slices, thickness_difference, depth_difference); shape_slice((index + 1) / $height_slices, thickness_difference, depth_difference); } } } module shape_slice(progress, thickness_difference, depth_difference) { // makes the sides bow extra_side_size = $enable_side_sculpting ? side_sculpting(progress) : 0; // makes the rounded corners of the keycap grow larger as they move upwards extra_corner_size = $enable_side_sculpting ? corner_sculpting(progress) : 0; // computed values for this slice extra_width_this_slice = ($width_difference - extra_side_size) * progress; extra_height_this_slice = ($height_difference - extra_side_size) * progress; skew_this_slice = $top_skew * progress; depth_this_slice = ($total_depth - depth_difference) * progress; tilt_this_slice = -$top_tilt / $key_height * progress; translate([0, skew_this_slice, depth_this_slice]) { rotate([tilt_this_slice,0,0]){ linear_extrude(height = 0.001){ key_shape( total_key_width(), total_key_height(), thickness_difference+extra_width_this_slice, thickness_difference+extra_height_this_slice, $corner_radius + extra_corner_size ); } } } } module dished(depth_difference, inverted = false) { if (inverted) { union() { children(); translate([$dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference]){ color([.4078, .3569, .749]) dish(top_total_key_width(), top_total_key_height(), $dish_depth, $inverted_dish, $top_tilt / $key_height); } } } else { difference() { children(); translate([$dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference]){ color([.4078, .3569, .749]) dish(top_total_key_width(), top_total_key_height(), $dish_depth, $inverted_dish, $top_tilt / $key_height); } } } } // puts it's children at the center of the dishing on the key. this DOES rotate them though, it's not straight up module top_of_key(){ extra_dish_depth = ($dish_type == "no dish") ? 0 : $dish_depth; translate([$dish_skew_x, $top_skew + $dish_skew_y, $total_depth - extra_dish_depth]){ rotate([-$top_tilt,0,0]){ children(); } } } module keytext() { extra_inset_depth = ($inset_text) ? 0.3 : 0; translate([0, 0, -extra_inset_depth]){ top_of_key(){ linear_extrude(height=$dish_depth){ text(text=$text, font=font, size=font_size, halign="center", valign="center"); } } } } module connectors() { intersection() { for (connector_pos = $connectors) { translate([connector_pos[0], connector_pos[1], $stem_inset]) { rotate([0, 0, $stem_rotation]){ color([1, .6941, .2]) connector($stem_profile, $has_brim, $slop, $support_type); } } } // cut off anything that isn't underneath the keytop shape(wall_thickness, keytop_thickness); } } //approximate (fully depressed) cherry key to check clearances module clearance_check() { if(clearance_check == true && ($stem_profile == "cherry" || $stem_profile == "cherry_rounded")){ color([1,0,0, 0.5]){ translate([0,0,3.6 + $stem_inset - 5]) { %hull() { cube([15.6, 15.6, 0.01], center=true); translate([0,1,5 - 0.01]) cube([10.5,9.5, 0.01], center=true); } %hull() { cube([15.6, 15.6, 0.01], center=true); translate([0,0,-5.5]) cube([13.5,13.5,0.01], center=true); } } } } } module keytop() { difference(){ if ($rounded_key) { rounded_shape(); } else { shape(0, 0); } translate([0,0,-0.01]) shape(wall_thickness, keytop_thickness); } } // The final, penultimate key generation function. // takes all the bits and glues them together. requires configuration with special variables. module key() { difference() { union(){ keytop(); if($stem_profile != "blank") connectors(); if(!$inset_text) keytext(); clearance_check(); top_of_key() { children(); } } if ($inset_text) keytext(); } } // actual full key with space carved out and keystem/stabilizer connectors // this is an example key with all the fixins module example_key(){ $bottom_key_width = bottom_key_width; $bottom_key_height = bottom_key_height; $width_difference = width_difference; $height_difference = height_difference; $total_depth = total_depth; $top_tilt = top_tilt; $top_skew = top_skew; $dish_type = dish_type; $dish_depth = dish_depth; $dish_skew_x = dish_skew_x; $dish_skew_y = dish_skew_y; $key_length = key_length; $key_height = key_height; $has_brim = has_brim; $inverted_dish = inverted_dish; $connectors = connectors; $linear_extrude_shape = linear_extrude_shape; $rounded_key = rounded_key; $stem_profile = stem_profile; $stem_inset = stem_inset; $stem_rotation = stem_rotation; $text = text; $inset_text = inset_text; $corner_radius = corner_radius; $height_slices = height_slices; $enable_side_sculpting = enable_side_sculpting; $slop = slop; $support_type = support_type; $linear_extrude_height_adjustment = linear_extrude_height_adjustment; key(); } example_key();