From 2368961370227ecde1b98a7ab57c7eebad264d3e Mon Sep 17 00:00:00 2001 From: Bob - Home - Windows Date: Sun, 4 Feb 2018 14:33:12 -0500 Subject: [PATCH] a whole bunch of work --- README.md | 31 ++++-- key_sizes.scad | 1 - key_transformations.scad | 22 ++-- key_types.scad | 8 +- keys.scad | 25 +++-- keysets.scad | 2 +- src/dishes.scad | 86 +++++++-------- src/key.scad | 230 +++++++++++++++++++++++---------------- src/settings.scad | 53 +++++++-- src/shapes.scad | 23 ++-- src/stems.scad | 163 ++++++++------------------- src/supports.scad | 42 ++++++- 12 files changed, 365 insertions(+), 321 deletions(-) diff --git a/README.md b/README.md index 1a303ad..7af66ea 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ cherry() key(); which is a bog-standard DCS row 5 keycap. To change key profile or make varying width keys, you can use the row and unit length functions, like so: ``` -sa_row(2) u(2) cherry() key(); +sa_row(2) 2u() cherry() key(); ``` ![a 2 unit SA row 2 cherry key](assets/example2.JPG) @@ -36,7 +36,7 @@ If you wanted to generate some 2u stabilized keycaps for an Ergodox for instance ``` legends = ["Enter", "Escape", "Tab", "Shift"]; for(y=[0:3]) { - translate_u(0,y) 2u() dsa_row() stabilized() legend(legends[y], inset=true) cherry() key(); + translate_u(0,y) 2u() dsa_row() stabilized() cherry() key(legends[y], inset=true); } ``` @@ -52,19 +52,27 @@ cherry() key() { ![an artisan key with no-face on it](assets/example4.JPG) +(no face courtesy of [this thing](https://www.thingiverse.com/thing:519727/)) + +Artisan support also supports _subtracting_ children by doing `key(inset=true) { ... }`, which is super helpful if you want to make keycaps with legends that are not text. The children will be placed just above the middle of the dish as per usual; you will need to translate them downwards (`ex translate([0,0,-1])`) to get them to 'dig in' to the top of the key. + ## What if I want to get _really_ technical? -If you're not afraid to write some code yourself, at the base level this library _should_ function well as a key profile design library. by loading up `src/key.scad` (notice no s) you can tweak variables in `src/settings.scad` to prototype your own profiles. You can design your own keyset with custom width height and depth, dish tilt, top skew, fonts, wall thickness, etc. +At the base level this library should function well as a key profile design library. by loading up `src/key.scad` (notice no s) you can tweak variables in `src/settings.scad` to prototype your own profiles. There are currently 44 different settings to tweak in `src/settings.scad` including width height and depth of the keycap, dish tilt, top skew, fonts, wall thickness, etc. -In addition, the library should be abstract enough to handle new dish types, keystems, and key shapes, in case you want to design your own DataMancer keycaps, support buckling spring keyboards (maybe) or design some kind of triangular dished profile. `src/shapes.scad` `src/stems.scad` and `src/dishes.scad` all have a 'selector' module at the top that should allow you to implement your own creations alongside what already exists. +### What if I want to get even more technical than that? + +Now we're talkin! + +This library should be abstract enough to handle new dish types, keystems, and key shapes, in case you want to design your own Typewriter-style keycaps, support buckling spring keyboards or design some kind of triangular dished profile. `src/shapes.scad` `src/stems.scad` and `src/dishes.scad` all have a 'selector' module at the top that should allow you to implement your own creations alongside what already exists. Here's an example of tweaking the settings and code to make a 'stop sign' key profile: In `key_shape()` in `shapes.scad`: ``` - else if ($key_shape_type == "circle") { - rotate([0,0,22.5]) circle(d=width - width_difference, $fn=8); + else if ($key_shape_type == "stop_sign") { + rotate([0,0,22.5]) circle(d=size[0] - delta[0], $fn=8); } ``` @@ -72,17 +80,20 @@ In `keys.scad`: ``` union() { + // make the font smaller $font_size = 3; + // top of keycap is the same size as the bottom $width_difference = 0; $height_difference = 0; - $key_shape_type="circle"; + $key_shape_type="stop_sign"; $dish_type = "cylindrical"; + // some keycap tops are slid backwards a little, and we don't want that $top_skew = 0; legends = ["Stop..", "Hammer", "time!"]; for(x=[0:len(legends)-1]) { - translate_u(x) legend(legends[x]) cherry() key(); + translate_u(x) cherry() key(legends[x]); } } ``` @@ -93,9 +104,9 @@ union() { Prints from this library are still challenging, despite all efforts to the contrary. Resin printers can create great looking keycaps; FDM printers can create usable keys that look alright, but may require tweaking to get prints acceptable. There are a few quick things that you can do: -1. If your switch isn't fitting in the keycap, try upping the slop factor, accessed by giving your keystem function a numeric value (eg `cherry(0.5) key()`). This will lengthen the cross and decrease the overall size of the keystem. The default value is 0.3, and represents millimeters. Note that even if you have a resin printer, you should probably keep the default value; keys printed with 0 slop will barely fit on the stem. +1. If your stem isn't fitting in the switch, try upping the slop factor, accessed by giving your keystem function a numeric value (eg `cherry(0.5) key()`). This will lengthen the cross and decrease the overall size of the keystem. The default value is 0.3, and represents millimeters. Note that even if you have a resin printer, you should probably keep the default value; keys printed with 0 slop will barely fit on the stem. -2. If your keystem breaks off mid-print, you can enable a brim by adding the `brimmed()` modifier. This will give a solid base for the keystem to anchor into. +2. If your keystem breaks off the bed mid-print, you can enable a brim by adding the `brimmed()` modifier. This will give a solid base for the keystem to anchor into. 3. If you are unsatisfied with the quality of the top surface, you can try printing the keycap on a different surface than the bottom, though it may impact the quality of the stem. diff --git a/key_sizes.scad b/key_sizes.scad index 7b72683..6e06fb5 100644 --- a/key_sizes.scad +++ b/key_sizes.scad @@ -9,7 +9,6 @@ module 1u() { u(1) children(); } - module 1_25u() { u(1.25) children(); } diff --git a/key_transformations.scad b/key_transformations.scad index be8dab2..3fd663b 100644 --- a/key_transformations.scad +++ b/key_transformations.scad @@ -51,39 +51,33 @@ module inset(val=1) { } module filled() { - $stem_profile = "filled"; + $stem_type = "filled"; children(); } module blank() { - $stem_profile = "blank"; + $stem_type = "blank"; children(); } module cherry(slop = 0.3) { - $slop = slop; - $stem_profile = "cherry"; + $stem_slop = slop; + $stem_type = "cherry"; children(); } module alps(slop = 0.3) { - $slop = slop; - $stem_profile = "alps"; + $stem_slop = slop; + $stem_type = "alps"; children(); } module rounded_cherry(slop = 0.3) { - $slop = slop; - $stem_profile = "cherry_rounded"; + $stem_slop = slop; + $stem_type = "cherry_rounded"; children(); } -module legend(text, inset=false) { - $text=text; - $inset_text = inset; - children(); -} - module flared_support() { $support_type = "flared"; children(); diff --git a/key_types.scad b/key_types.scad index 1e9c987..6ec0f30 100644 --- a/key_types.scad +++ b/key_types.scad @@ -1,11 +1,7 @@ module spacebar() { $inverted_dish = true; - if ($dish_type == "cylindrical") { - $dish_type = "sideways cylindrical"; - 6_25u() stabilized(mm=50) children(); - } else { - 6_25u() stabilized(mm=50) children(); - } + $dish_type = "sideways cylindrical"; + 6_25u() stabilized(mm=50) children(); } module lshift() { diff --git a/keys.scad b/keys.scad index 8b3f7a2..b99700f 100644 --- a/keys.scad +++ b/keys.scad @@ -20,13 +20,22 @@ module translate_u(x=0, y=0, z=0){ translate([x * unit, y*unit, z*unit]) children(); } -dcs_row(2) u(2) dishless() cherry() { - /* $inverted_dish = true; */ - $key_shape_type = "obloid"; +// basic +cherry() key(); + +translate_u(1) sa_row(2) cherry() key("q"); +translate_u(2) oem_row(2) alps() key("q", inset=true); +translate_u(3) dsa_row() rounded_cherry() key(); + +translate_u(1, 1) sa_row(3) lshift() cherry() key(inset=true) { + sphere(1); +}; + +translate_u(3, 2) sa_row(3) bar_support() spacebar() cherry() key("space bar"); + +translate_u(3,1) sa_row(3) 2u() cherry() { + $key_shape_type = "spherical"; + $support_type = false; + $inverted_dish = true; key(); } - - translate_u(3) u(2) dcs_row(3) dishless() cherry() { - $key_shape_type = "obloid"; - key(); - } diff --git a/keysets.scad b/keysets.scad index 6a5505e..0c500b4 100644 --- a/keysets.scad +++ b/keysets.scad @@ -22,7 +22,7 @@ for (row = [0:len(60_percent)-1]){ translate_u(columnDist - (a/2), -row) dishless() dcs_row((row+4) % 5 + 1) u(a) cherry() { $width_difference = 0; $height_difference = 0; - $key_shape_type = "obloid"; + $key_shape_type = "spherical"; if (a != 6.25) { key(); } else { diff --git a/src/dishes.scad b/src/dishes.scad index 2d90fd0..e6ebe3d 100644 --- a/src/dishes.scad +++ b/src/dishes.scad @@ -4,23 +4,26 @@ include geodesic=false; //dish selector -module dish(width, height, depth, inverted, tilt) { +module dish(width, height, depth, inverted) { if($dish_type == "cylindrical"){ - cylindrical_dish(width, height, depth, inverted, tilt); + cylindrical_dish(width, height, depth, inverted); } else if ($dish_type == "spherical") { - spherical_dish(width, height, depth, inverted, tilt); + spherical_dish(width, height, depth, inverted); } else if ($dish_type == "sideways cylindrical"){ - sideways_cylindrical_dish(width, height, depth, inverted, tilt); + sideways_cylindrical_dish(width, height, depth, inverted); } else if ($dish_type == "old spherical") { - old_spherical_dish(width, height, depth, inverted, tilt); + old_spherical_dish(width, height, depth, inverted); + } else { + // else no dish, "no dish" is the value + // switchted to actually diffing a cube here due to (hopeful) changes to stems being differenced from the dish instead of the inside + translate([0,0,500]) cube([width, height, 1000], center=true); } - // else no dish, "no dish" is the value } -module cylindrical_dish(width, height, depth, inverted, tilt){ +module cylindrical_dish(width, height, depth, inverted){ // .5 has problems starting around 3u $fa=.25; /* we do some funky math here @@ -35,28 +38,25 @@ module cylindrical_dish(width, height, depth, inverted, tilt){ rad = (pow(width, 2) + 4 * pow(depth, 2)) / (8 * depth); direction = inverted ? -1 : 1; - rotate([90-tilt,0,0]){ - translate([0,chord_length * direction,0]){ - cylinder(h=height + 20, r=rad, center=true); - } + translate([0,0, chord_length * direction]){ + rotate([90, 0, 0]) cylinder(h=height + 20, r=rad, center=true); } } -module sideways_cylindrical_dish(width, height, depth, inverted, tilt){ +module sideways_cylindrical_dish(width, height, depth, inverted){ $fa=1; chord_length = (pow(height, 2) - 4 * pow(depth, 2)) / (8 * depth); rad = (pow(height, 2) + 4 * pow(depth, 2)) / (8 * depth); direction = inverted ? -1 : 1; - rotate([90,tilt,90]){ - translate([0,chord_length * direction,0]){ - cylinder(h = width + 20,r=rad, center=true); // +20 for fudge factor - } + translate([0,0, chord_length * direction]){ + // cylinder is rendered facing up, so we rotate it on the y axis first + rotate([0,90,0]) cylinder(h = width + 20,r=rad, center=true); // +20 for fudge factor } } -module spherical_dish(width, height, depth, inverted, tilt, txt=""){ +module spherical_dish(width, height, depth, inverted){ //same thing as the cylindrical dish here, but we need the corners to just touch - so we have to find the hypotenuse of the top chord = pow((pow(width,2) + pow(height, 2)),0.5); //getting diagonal of the top @@ -67,29 +67,26 @@ module spherical_dish(width, height, depth, inverted, tilt, txt=""){ rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth); direction = inverted ? -1 : 1; - /*intersection(){*/ - rotate([-tilt,0,0]){ - translate([0,0,0 * direction]){ - if (geodesic){ - $fa=20; - scale([chord/2/depth, chord/2/depth]) { - geodesic_sphere(r=depth); - } - } else { - $fa=7; - // rotate 1 because the bottom of the sphere looks like trash. - scale([chord/2/depth, chord/2/depth]) { - geodesic_sphere(r=depth); - } - } - } + translate([0,0,0 * direction]){ + if (geodesic){ + $fa=20; + scale([chord/2/depth, chord/2/depth]) { + geodesic_sphere(r=depth); + } + } else { + $fa=7; + // rotate 1 because the bottom of the sphere looks like trash. + scale([chord/2/depth, chord/2/depth]) { + geodesic_sphere(r=depth); + } } + } } //the older, 'more accurate', and MUCH slower spherical dish. // generates the largest sphere possible that still contains the chord we are looking for // much more graduated curvature at an immense cost -module old_spherical_dish(width, height, depth, inverted, tilt, txt=""){ +module old_spherical_dish(width, height, depth, inverted){ //same thing as the cylindrical dish here, but we need the corners to just touch - so we have to find the hypotenuse of the top chord = pow((pow(width,2) + pow(height, 2)),0.5); //getting diagonal of the top @@ -100,17 +97,14 @@ module old_spherical_dish(width, height, depth, inverted, tilt, txt=""){ rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth); direction = inverted ? -1 : 1; - /*intersection(){*/ - rotate([-tilt,0,0]){ - translate([0,0,chord_length * direction]){ - if (geodesic){ - $fa=7; - geodesic_sphere(r=rad); - } else { - $fa=1; - // rotate 1 because the bottom of the sphere looks like trash - sphere(r=rad); - } - } + translate([0,0,chord_length * direction]){ + if (geodesic){ + $fa=7; + geodesic_sphere(r=rad); + } else { + $fa=1; + // rotate 1 because the bottom of the sphere looks like trash + sphere(r=rad); } + } } diff --git a/src/key.scad b/src/key.scad index b193b36..058b868 100644 --- a/src/key.scad +++ b/src/key.scad @@ -2,28 +2,18 @@ include include include +include + include -/* [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; - - - +color1 = [.2667,.5882,1]; +color2 = [.5412, .4784, 1]; +color3 = [.4078, .3569, .749]; +color4 = [1, .6941, .2]; +transparent_red = [1,0,0, 0.5]; // derived values. can't be variables if we want them to change when the special variables do @@ -36,34 +26,30 @@ function top_total_key_width() = $bottom_key_width + (unit * ($key_length - 1)) function top_total_key_height() = $bottom_key_height + (unit * ($key_height - 1)) - $height_difference; -// key shape including dish. used as the ouside and inside shape in key() +// key shape including dish. used as the ouside and inside shape in keytop(). allows for itself to be shrunk in depth and width / height 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); - } + dished(depth_difference, $inverted_dish) { + color(color1) shape_hull(thickness_difference, depth_difference, 1); } } // 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(){ + color(color1) 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); + translate([0,0,-$minkowski_radius]){ + cube($minkowski_radius * 2, 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 @@ -71,17 +57,16 @@ module rounded_shape() { module shape_hull(thickness_difference, depth_difference, extra_slices = 0){ render() { if ($linear_extrude_shape) { - linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices); + linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices); } else { hull_shape_hull(thickness_difference, depth_difference, extra_slices); } } } -//corollary is hull_shape_hull +// corollary is hull_shape_hull // extra_slices unused, only to match argument signatures module linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices = 0){ - height = $total_depth - depth_difference; width_scale = top_total_key_width() / total_key_width(); height_scale = top_total_key_height() / total_key_height(); @@ -96,7 +81,6 @@ module linear_extrude_shape_hull(thickness_difference, depth_difference, extra_s } 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); @@ -118,10 +102,7 @@ module shape_slice(progress, thickness_difference, depth_difference) { total_key_width(thickness_difference), total_key_height(thickness_difference) ], - [ - $width_difference, - $height_difference - ], + [$width_difference, $height_difference], progress ); } @@ -129,78 +110,125 @@ module shape_slice(progress, thickness_difference, depth_difference) { } } -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() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_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() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, $inverted_dish, $top_tilt / $key_height); - } - } +// for when you want something to only exist inside the keycap. +// used for the support structure +module inside() { + intersection() { + shape($wall_thickness, $keytop_thickness); + children(); } } -// 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]){ +// put something at the top of the key, with no adjustments for dishing +module top_placement(depth_difference) { + translate([$dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference]){ rotate([-$top_tilt,0,0]){ children(); } } } -module keytext() { - extra_inset_depth = ($inset_text) ? 0.3 : 0; +// just to DRY up the code +module _dish() { + color(color3) dish(top_total_key_width() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, $inverted_dish); +} - 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"); +// for when you want to take the dish out of things +// used for adding the dish to the key shape and making sure stems don't stick out the top +module dished(depth_difference, inverted = false) { + difference() { + children(); + top_placement(depth_difference){ + difference(){ + union() { + translate([-500, -500]) cube(1000); + if (!inverted) _dish(); + } + if (inverted) _dish(); } } } } -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, $total_depth, $has_brim, $slop, $stem_inset, $support_type); - } +// puts it's children at the center of the dishing on the key, including dish height +// more user-friendly than top_placement +module top_of_key(){ + // if there is a dish, we need to account for how much it digs into the top + dish_depth = ($dish_type == "no dish") ? 0 : $dish_depth; + // if the dish is inverted, we need to account for that too. in this case we do half, otherwise the children would be floating on top of the dish + corrected_dish_depth = ($inverted_dish) ? -dish_depth / 2 : dish_depth; + + top_placement(corrected_dish_depth) { + children(); + } +} + +module keytext(text, depth = 0) { + translate([0, 0, -depth]){ + linear_extrude(height=$dish_depth){ + text(text=text, font=$font, size=$font_size, halign="center", valign="center"); + } + } +} + +module keystem_positions() { + for (connector_pos = $connectors) { + translate(connector_pos) { + rotate([0, 0, $stem_rotation]){ + children(); } } - // cut off anything that isn't underneath the keytop - shape($wall_thickness, $keytop_thickness); + } +} + +module keystems() { + keystem_positions() { + color(color4) stem($stem_type, $total_depth, $has_brim); + } +} + +module keystem_supports() { + keystem_positions() { + color(color4) supports($support_type, $stem_type, $stem_throw, $total_depth - $stem_throw); + } +} + +// a fake cherry keyswitch, abstracted out to maybe replace with a better one later +module cherry_keyswitch() { + union() { + 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); + } } } //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]){ + if($stem_type == "cherry" || $stem_type == "cherry_rounded"){ + color(transparent_red){ 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); - } + cherry_keyswitch(); } } } } +// legends / artisan support +module artisan(legend, depth) { + top_of_key() { + // outset legend + if (legend != "") keytext(legend, depth); + // artisan objects / outset shape legends + children(); + } +} + +// key with hollowed inside but no stem module keytop() { difference(){ if ($rounded_key) { @@ -208,30 +236,50 @@ module keytop() { } else { shape(0, 0); } - translate([0,0,-0.01]) shape($wall_thickness, $keytop_thickness); + // translation purely for aesthetic purposes, to get rid of that awful lattice + translate([0,0,-0.005]) { + 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() { +module key(legend = "", inset = false) { difference() { union(){ + // the shape of the key, inside and out keytop(); - if($stem_profile != "blank") connectors(); - if(!$inset_text) keytext(); - clearance_check(); - top_of_key() { - children(); - } + // additive objects at the top of the key + if(!inset) artisan(legend) children(); + // render the clearance check if it's enabled, but don't have it intersect with anything + if ($clearance_check) %clearance_check(); + } + + // subtractive objects at the top of the key + if (inset) artisan(legend, 0.3) children(); + // subtract the clearance check if it's enabled, letting the user see the + // parts of the keycap that will hit the cherry switch + if ($clearance_check) clearance_check(); + } + + // both stem and support are optional + if ($stem_type){ + dished($keytop_thickness, $inverted_dish) { + translate([0, 0, $stem_inset]) keystems(); + } + } + + if ($support_type){ + inside() { + translate([0, 0, $stem_inset]) keystem_supports(); } - if ($inset_text) keytext(); } } // actual full key with space carved out and keystem/stabilizer connectors -// this is an example key with all the fixins +// this is an example key with all the fixins from settings.scad module example_key(){ include key(); diff --git a/src/settings.scad b/src/settings.scad index 0447b30..94053a2 100644 --- a/src/settings.scad +++ b/src/settings.scad @@ -27,7 +27,7 @@ $total_depth = 11.5; $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 +// what type of dish the key has. note that unlike stems and supports a dish ALWAYS gets rendered. $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; @@ -51,25 +51,21 @@ $connectors = $stabilizers ? [[0,0],[-50,0],[50,0]] : [[0,0]]; $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"; +// what type of stem you want. To turn off stems pass false. "cherry", "alps", and "cherry_rounded" supported +$stem_type = "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 +$stem_slop = 0.3; +// support type. default is "flared" for easy FDM printing. to disable pass false $support_type = "flared"; -// key shape type. default is 'normal'. only other supported option is 'iso_enter' -$key_shape_type = "normal"; +// key shape type, determines the shape of the key. default is 'rounded square' +$key_shape_type = "rounded_square"; // 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; // if you need the dish to extend further, you can 'overdraw' the rectangle it will hit @@ -77,4 +73,39 @@ $dish_overdraw_width = 0; // same as width but for height $dish_overdraw_height = 0; // how many slices will be made, to approximate curves on corners. Leave at 1 if you are not curving corners +// if you're doing fancy bowed keycap sides, this controls how many slices you take $height_slices = 1; +// this enables some fancy and currently hardcoded logic to bow the sides and corners of SA keycaps +$enable_side_sculpting = false; + +//minkowski radius. radius of sphere used in minkowski sum for minkowski_key function. 1.75 for G20 +$minkowski_radius = .33; + + +// [ Stem Variables ] + + +// the stem is the hardest part to print, so this variable controls how much 'slop' there is in the stem +$stem_slop = 0.3; + +// how tall in mm the brim is, if there is one. brim sits around the keystem and helps to secure it while printing. +$brim_height = 0.4; +// how far the throw distance of the switch is. determines how far the 'cross' in the cherry switch digs into the stem, and how long the keystem needs to be before supports can start. luckily, alps and cherries have a pretty similar throw. can modify to have stouter keycaps for low profile switches, etc +$stem_throw = 4; + +// cherry stem dimensions +$cherry_stem = [7.2 - $stem_slop * 2, 5.5 - $stem_slop * 2]; + +// .005 purely for aesthetics, to get rid of that ugly crosshatch +$cherry_cross = [ + // horizontal tine + [4.03 + $stem_slop, 1.15], + // vertical tine + [1.25, $cherry_stem[1] + .005], +]; + +// diameter of the outside of the rounded cherry stem +$rounded_cherry_stem_d = 5.5; + +// dimensions of alps stem +$alps_stem = [4.45, 2.25]; diff --git a/src/shapes.scad b/src/shapes.scad index 5bd3924..c463b76 100644 --- a/src/shapes.scad +++ b/src/shapes.scad @@ -10,26 +10,25 @@ function corner_sculpting(progress) = pow(progress, 2); module key_shape(size, delta, progress = 0) { if ($key_shape_type == "iso_enter") { ISO_enter(size, delta, progress); - } else if ($key_shape_type == "normal") { + } else if ($key_shape_type == "rounded_square") { roundedSquare(size, delta, progress); - } else if ($key_shape_type == "circle") { - circle(d=width); } else if ($key_shape_type == "square") { square(size - delta, center = true); - } else if ($key_shape_type == "obloid") { - obloid(size, delta, progress); + } else if ($key_shape_type == "spherical") { + spherical(size, delta, progress); + } else { + echo("Warning: unsupported $key_shape_type"); } } -module obloid(size, delta, progress) { - width = size[0]; - height = size[1] - delta[1] * progress; // TODO if we don't account for the delta somehow 1u keys will not render as the offset margin is greater than the size of the key. this does not work however +module spherical(size, delta, progress) { + // .05 is because of offset. if we set offset to be half the height of the shape, and then subtract height from the shape, the height of the shape will be zero (because the shape would be [width - height, height - height]). that doesn't play well with openSCAD (understandably), so we add this tiny fudge factor to make sure the shape we offset has a positive width + height = size[1] - delta[1] * progress - .05; - if (progress < .5 && false) { - circle(d=5.5); + if (progress < 0.5) { } else { - offset(r=height / 2.1) { - square(size - [height / 1.05, height / 1.05] - delta * progress, center=true); + offset(r=height / 2) { + square(size - [height, height] - delta * progress, center=true); } } } diff --git a/src/stems.scad b/src/stems.scad index d1a9ad3..bcb7f5e 100644 --- a/src/stems.scad +++ b/src/stems.scad @@ -1,147 +1,80 @@ -include - -brim_height = 0.4; - -//whole connector, alps or cherry, trimmed to fit -module connector(stem_profile, depth, has_brim, slop, stem_inset, support_type){ - echo(slop); - if (stem_profile == "alps") { - alps_stem(depth, has_brim, slop, stem_inset, support_type); - } else if (stem_profile == "cherry_rounded") { - cherry_stem_rounded(depth, has_brim, slop, stem_inset, support_type); - } else if (stem_profile == "cherry") { - cherry_stem(depth, has_brim, slop, stem_inset, support_type); - } else if (stem_profile == "filled") { +//whole stem, alps or cherry, trimmed to fit +module stem(stem_type, depth, has_brim){ + if (stem_type == "alps") { + alps_stem(depth, has_brim); + } else if (stem_type == "cherry_rounded") { + cherry_stem_rounded(depth, has_brim); + } else if (stem_type == "cherry") { + cherry_stem(depth, has_brim); + } else if (stem_type == "filled") { // just a cube, so no args filled_stem(); + } else { + echo("Warning: unsupported $stem_type"); } } -module cherry_stem(depth, has_brim, slop, stem_inset, support_type) { - stem_width = 7.2 - slop * 2; - stem_height = 5.5 - slop * 2; +module cherry_stem(depth, has_brim) { + difference(){ + union() { + // outside shape + linear_extrude(height = depth) { + offset(r=1){ + square($cherry_stem - [2,2], center=true); + } + } - vertical_cross_width = 1.25; - // currently unused, as we want a split stem - vertical_cross_length = 3.93; - - horizontal_cross_width = 1.15; - horizontal_cross_length = 4.03; - - cross_depth = 4; - - stem = [stem_width, stem_height]; - vertical_cross = [vertical_cross_width, stem_height]; - horizontal_cross = [horizontal_cross_length + slop, horizontal_cross_width]; - - translate([0,0,stem_inset]) { - difference(){ - union() { - linear_extrude(height = depth) { + // brim, if applicable + if(has_brim) { + linear_extrude(height = brim_height){ offset(r=1){ - square(stem - [2,2], center=true); + square($cherry_stem - [2,2], center=true); } } - if(has_brim) { - linear_extrude(height = brim_height){ - offset(r=1){ - square(stem - [2,2], center=true); - } - } - } - } - linear_extrude(height = cross_depth) { - square(vertical_cross, center=true); - square(horizontal_cross, center=true); } } - // supports - if (support_type == "flared") { - flared(cross_depth, (depth - cross_depth), [stem_width, stem_height]) { - roundedSquare(stem, 1, center=true); + // inside cross + // translation purely for aesthetic purposes, to get rid of that awful lattice + translate([0,0,-0.005]) { + linear_extrude(height = $stem_throw) { + square($cherry_cross[0], center=true); + square($cherry_cross[1], center=true); } - } else if (support_type == "flat") { - flat(cross_depth, (depth - cross_depth), [stem_width, stem_height]); - } else if (support_type == "bars") { - bars(cross_depth, (depth - cross_depth), [stem_width, stem_height]); } } } -module cherry_stem_rounded(depth, has_brim, slop, stem_inset, support_type) { - // cross length - cross_length = 4.4; - //dimensions of connector - // outer cross extra length in y - extra_outer_cross_height = 1.1; - // dimensions of cross - // horizontal cross bar width - horizontal_cross_width = 1.4; - // vertical cross bar width - vertical_cross_width = 1.3; - // cross depth, stem height is 3.4mm - cross_depth = 4; +module cherry_stem_rounded(depth, has_brim) { - total_diameter = cross_length+extra_outer_cross_height; - translate([0,0,stem_inset]){ - difference(){ - union(){ - cylinder(d=total_diameter, h=depth); - if(has_brim) { - cylinder(d=total_diameter * 2, h=brim_height); - } - } - //the cross part of the steam - translate([0,0,(cross_depth)/2]){ - cube([vertical_cross_width,cross_length,cross_depth], center=true ); - cube([cross_length,horizontal_cross_width,cross_depth], center=true ); + difference(){ + union(){ + cylinder(d=$rounded_cherry_stem_d, h=depth); + if(has_brim) { + cylinder(d=$rounded_cherry_stem_d * 2, h=brim_height); } } - // supports - if (support_type == "flared") { - flared(cross_depth, (depth - cross_depth)) { - circle(d = cross_length+extra_outer_cross_height); + // inside cross + // translation purely for aesthetic purposes, to get rid of that awful lattice + translate([0,0,-0.005]) { + linear_extrude(height = $stem_throw) { + square($cherry_cross[0], center=true); + square($cherry_cross[1], center=true); } - } else if (support_type == "flat") { - flat(cross_depth, (depth - cross_depth)); - } else if (support_type == "bars") { - bars(cross_depth, (depth - cross_depth)); } } } -module alps_stem(depth, has_brim, slop, stem_inset, support_type){ - // not really cross depth, basically just the max length of stem we need for the key to function properly - cross_depth = 4; - - width = 4.45; - height = 2.25; - - base_width = 12; - base_height = 15; - - translate([0,0,stem_inset]){ - if(has_brim) { - translate([0,0,brim_height / 2]) cube([width*2,height*2,brim_height], center = true); - } - translate([0,0,depth/2]){ - cube([width,height,depth], center = true); +module alps_stem(depth, has_brim){ + if(has_brim) { + linear_extrude(h=brim_height) { + square($alps_stem * [2,2], center = true); } } - - translate([0, 0, stem_inset]){ - if (support_type == "flared") { - flared(cross_depth, (depth - cross_depth)) { - square([width,height]); - } - } else if (support_type == "flat") { - flat(cross_depth, (depth - cross_depth)); - } else if (support_type == "bars") { - bars(cross_depth, (depth - cross_depth)); - } + linear_extrude(h=depth) { + square($alps_stem, center = true); } } diff --git a/src/supports.scad b/src/supports.scad index f36d2d8..0579fbd 100644 --- a/src/supports.scad +++ b/src/supports.scad @@ -1,19 +1,49 @@ -// flared support designed for FDM printing, for the normal cherry stem -module flared(loft, height) { +// figures out the scale factor needed to make a 45 degree wall +function scale_for_45(height, starting_size) = (height * 2 + starting_size) / starting_size; + +module supports(type, stem_type, loft, height) { + if (type == "flared") { + flared(stem_type, loft, height); + } else if (type == "flat") { + flat(stem_type, loft, height); + } else if (type == "bars") { + bars(stem_type, loft, height); + } else { + echo("Warning: unsupported $support_type"); + } +} + +// complicated since we want the different stems to work well +// also kind of messy... oh well +module flared(stem_type, loft, height) { translate([0,0,loft]){ - linear_extrude(height=height, scale = [height/2,height/2]){ - children(); + if(stem_type == "cherry") { + cherry_scale = [scale_for_45(height, $cherry_stem[0]), scale_for_45(height, $cherry_stem[1])]; + linear_extrude(height=height, scale = cherry_scale){ + offset(r=1){ + square($cherry_stem - [2,2], center=true); + } + } + } else if (stem_type == "cherry_rounded") { + linear_extrude(height=height, scale = scale_for_45(height, $rounded_cherry_stem_d)){ + circle(d=$rounded_cherry_stem_d); + } + } else if (stem_type == "alps") { + alps_scale = [scale_for_45(height, $alps_stem[0]), scale_for_45(height, $alps_stem[1])]; + linear_extrude(height=height, scale = alps_scale){ + square($alps_stem, center=true); + } } } } -module flat(loft, height) { +module flat(stem_type, loft, height) { translate([0,0,loft + 500]){ cube(1000, center=true); } } -module bars(loft, height) { +module bars(stem_type, loft, height) { translate([0,0,loft + height / 2]){ cube([2, 100, height], center = true); cube([100, 2, height], center = true);