2017-08-14 22:48:01 -04:00
// files
2017-11-12 13:01:17 -05:00
include < shapes.scad >
2017-08-12 01:10:48 -04:00
include < stems.scad >
include < dishes.scad >
2017-08-15 01:59:12 -04:00
include < libraries/geodesic_sphere.scad >
2017-08-12 01:10:48 -04:00
/* [Settings] */
2015-04-14 00:50:30 -04:00
2016-09-04 02:35:44 -04:00
// 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 ;
2015-04-14 00:50:30 -04:00
//whether stabilizer connectors are enabled
2017-08-12 01:10:48 -04:00
stabilizers = false ;
2017-08-13 03:04:53 -04:00
// font used for text
2017-11-05 17:56:05 -05:00
font = "DejaVu Sans Mono:style=Book" ;
2017-08-13 03:04:53 -04:00
// font size used for text
2017-11-05 17:56:05 -05:00
font_size = 6 ;
2017-08-13 03:04:53 -04:00
// whether or not to render fake keyswitches to check clearances
2017-08-16 01:59:19 -04:00
clearance_check = false ;
2017-08-12 01:10:48 -04:00
/* [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
2017-09-25 00:04:34 -04:00
dish_type = "cylindrical" ;
2017-08-12 01:10:48 -04:00
// 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 ] ] ;
2017-11-12 13:01:17 -05:00
// 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 ;
2017-08-12 01:10:48 -04:00
//should the key be rounded? unnecessary for most printers, and very slow
rounded_key = false ;
2017-08-13 03:04:53 -04:00
// '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 ;
2017-08-14 22:48:01 -04:00
// radius of corners of keycap
corner_radius = 1 ;
2017-11-05 17:56:05 -05:00
// keystem slop - lengthens the cross and thins out the connector
slop = 0.3 ;
2017-11-12 13:01:17 -05:00
// 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 ;
2017-08-16 01:59:19 -04:00
/* [Fancy Bowed Sides] */
2017-08-15 01:59:12 -04:00
// 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 ;
2017-08-16 01:59:19 -04:00
enable_side_sculpting = false ;
2017-08-13 03:04:53 -04:00
2015-04-14 00:50:30 -04:00
2016-09-04 02:35:44 -04:00
/* [Hidden] */
2017-08-13 03:04:53 -04:00
$fs = . 1 ;
2016-09-04 02:35:44 -04:00
//beginning to use unit instead of baked in 19.05
unit = 19.05 ;
2016-09-04 03:22:48 -04:00
//minkowski radius. radius of sphere used in minkowski sum for minkowski_key function. 1.75 default for faux G20
2017-11-05 17:56:05 -05:00
$ minkowski_radius = . 33 ;
2017-08-14 22:48:01 -04:00
2017-08-13 03:04:53 -04:00
2016-09-04 02:35:44 -04:00
2015-04-14 00:50:30 -04:00
2017-09-25 00:04:34 -04:00
// derived values. can't be variables if we want them to change when the special variables do
2016-09-04 02:35:44 -04:00
// actual mm key width and height
2017-11-12 13:01:17 -05:00
function total_key_width ( ) = $ bottom_key_width + unit * ( $ key_length - 1 ) ;
function total_key_height ( ) = $ bottom_key_height + unit * ( $ key_height - 1 ) ;
2016-09-04 02:35:44 -04:00
// actual mm key width and height at the top
2017-08-12 01:10:48 -04:00
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 ;
2015-04-14 00:50:30 -04:00
2017-11-12 13:01:17 -05:00
// 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 ) ;
2017-08-13 03:04:53 -04:00
// key shape including dish. used as the ouside and inside shape in key()
2017-08-12 01:10:48 -04:00
module shape ( thickness_difference , depth_difference ) {
2017-09-25 00:04:34 -04:00
intersection ( ) {
dished ( depth_difference , $ inverted_dish ) {
2017-11-12 13:01:17 -05:00
color ( [ . 2667 , . 5882 , 1 ] ) shape_hull ( thickness_difference , depth_difference ) ;
2016-04-23 21:26:29 -04:00
}
2017-09-25 00:04:34 -04:00
if ( $ inverted_dish ) {
// larger shape_hull to clip off bits of the inverted dish
2017-11-12 13:01:17 -05:00
color ( [ . 5412 , . 4784 , 1 ] ) shape_hull ( thickness_difference , 0 , 2 ) ;
2017-08-13 03:04:53 -04:00
}
2015-07-28 01:42:05 -04:00
}
}
2017-08-13 03:04:53 -04:00
// shape of the key but with soft, rounded edges. much more realistic, MUCH more complex. orders of magnitude more complex
2017-08-12 01:10:48 -04:00
module rounded_shape ( ) {
2017-11-05 17:56:05 -05:00
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 ] )
2017-08-14 22:48:01 -04:00
cube ( [ 2 * $ minkowski_radius , 2 * $ minkowski_radius , 2 * $ minkowski_radius ] , center = true ) ;
2017-11-05 17:56:05 -05:00
}
2015-04-14 00:50:30 -04:00
}
}
}
2017-11-12 13:01:17 -05:00
// 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 ) ;
}
}
}
2017-11-11 17:32:27 -05:00
//corollary is shape_hull
2017-11-12 13:01:17 -05:00
module linear_extrude_shape_hull ( thickness_difference , depth_difference ) {
2017-11-11 17:32:27 -05:00
height = $t otal_depth - depth_difference ;
width_scale = top_total_key_width ( ) / total_key_width ( ) ;
height_scale = top_total_key_height ( ) / total_key_height ( ) ;
2017-11-12 13:01:17 -05:00
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 ) ;
}
}
2017-11-11 17:32:27 -05:00
}
}
2017-11-12 13:01:17 -05:00
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 ) ;
2017-08-14 22:48:01 -04:00
}
}
}
2017-11-12 13:01:17 -05:00
module shape_slice ( progress , thickness_difference , depth_difference ) {
2017-11-11 17:32:27 -05:00
// makes the sides bow
2017-11-12 13:01:17 -05:00
extra_side_size = $ enable_side_sculpting ? side_sculpting ( progress ) : 0 ;
2017-11-11 17:32:27 -05:00
// makes the rounded corners of the keycap grow larger as they move upwards
2017-11-12 13:01:17 -05:00
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 = $t op_skew * progress ;
depth_this_slice = ( $t otal_depth - depth_difference ) * progress ;
tilt_this_slice = - $t op_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
) ;
}
}
2017-08-14 22:48:01 -04:00
}
}
2017-09-25 00:04:34 -04:00
module dished ( depth_difference , inverted = false ) {
if ( inverted ) {
union ( ) {
children ( ) ;
translate ( [ $ dish_skew_x , $t op_skew + $ dish_skew_y , $t otal_depth - depth_difference ] ) {
2017-10-24 22:56:35 -04:00
color ( [ . 4078 , . 3569 , . 749 ] ) dish ( top_total_key_width ( ) , top_total_key_height ( ) , $ dish_depth , $ inverted_dish , $t op_tilt / $ key_height ) ;
2017-08-12 01:10:48 -04:00
}
}
2017-09-25 00:04:34 -04:00
} else {
difference ( ) {
children ( ) ;
translate ( [ $ dish_skew_x , $t op_skew + $ dish_skew_y , $t otal_depth - depth_difference ] ) {
2017-10-24 22:56:35 -04:00
color ( [ . 4078 , . 3569 , . 749 ] ) dish ( top_total_key_width ( ) , top_total_key_height ( ) , $ dish_depth , $ inverted_dish , $t op_tilt / $ key_height ) ;
2017-09-25 00:04:34 -04:00
}
2017-08-12 01:10:48 -04:00
}
2016-04-23 21:26:29 -04:00
}
}
2017-10-17 00:50:48 -04:00
// 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 ( ) {
2017-09-25 00:04:34 -04:00
extra_dish_depth = ( $ dish_type = = "no dish" ) ? 0 : $ dish_depth ;
2017-10-17 00:50:48 -04:00
translate ( [ $ dish_skew_x , $t op_skew + $ dish_skew_y , $t otal_depth - extra_dish_depth ] ) {
rotate ( [ - $t op_tilt , 0 , 0 ] ) {
children ( ) ;
}
}
}
module keytext ( ) {
2017-11-05 17:56:05 -05:00
extra_inset_depth = ( $ inset_text ) ? 0.3 : 0 ;
2017-08-13 03:04:53 -04:00
2017-10-17 00:50:48 -04:00
translate ( [ 0 , 0 , - extra_inset_depth ] ) {
top_of_key ( ) {
2017-08-13 03:04:53 -04:00
linear_extrude ( height = $ dish_depth ) {
text ( text = $t ext , font = font , size = font_size , halign = "center" , valign = "center" ) ;
}
}
}
}
2017-11-05 17:56:05 -05:00
module connectors ( ) {
2017-09-25 00:04:34 -04:00
intersection ( ) {
2017-08-13 03:04:53 -04:00
for ( connector_pos = $ connectors ) {
translate ( [ connector_pos [ 0 ] , connector_pos [ 1 ] , $ stem_inset ] ) {
rotate ( [ 0 , 0 , $ stem_rotation ] ) {
2017-11-12 13:01:17 -05:00
color ( [ 1 , . 6941 , . 2 ] ) connector ( $ stem_profile , $ has_brim , $ slop , $ support_type ) ;
2017-08-13 03:04:53 -04:00
}
}
2017-08-12 01:10:48 -04:00
}
2017-10-24 22:56:35 -04:00
// cut off anything that isn't underneath the keytop
2017-09-25 00:04:34 -04:00
shape ( wall_thickness , keytop_thickness ) ;
2015-04-14 00:50:30 -04:00
}
2016-04-23 21:26:29 -04:00
}
2015-04-14 00:50:30 -04:00
2017-08-13 03:04:53 -04:00
//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 ) ;
}
}
}
}
2016-04-23 21:26:29 -04:00
}
2017-08-12 01:10:48 -04:00
module keytop ( ) {
difference ( ) {
if ( $ rounded_key ) {
rounded_shape ( ) ;
} else {
shape ( 0 , 0 ) ;
}
2017-11-12 13:01:17 -05:00
translate ( [ 0 , 0 , - 0.01 ] ) shape ( wall_thickness , keytop_thickness ) ;
2015-04-14 00:50:30 -04:00
}
}
2017-08-13 03:04:53 -04:00
// The final, penultimate key generation function.
// takes all the bits and glues them together. requires configuration with special variables.
module key ( ) {
difference ( ) {
union ( ) {
keytop ( ) ;
2017-11-05 17:56:05 -05:00
if ( $ stem_profile ! = "blank" ) connectors ( ) ;
2017-08-13 03:04:53 -04:00
if ( ! $ inset_text ) keytext ( ) ;
clearance_check ( ) ;
2017-10-17 00:50:48 -04:00
top_of_key ( ) {
children ( ) ;
}
2017-08-13 03:04:53 -04:00
}
if ( $ inset_text ) keytext ( ) ;
}
}
// actual full key with space carved out and keystem/stabilizer connectors
2017-08-12 01:10:48 -04:00
// this is an example key with all the fixins
2017-08-13 03:04:53 -04:00
module example_key ( ) {
2017-08-12 01:10:48 -04:00
$ bottom_key_width = bottom_key_width ;
$ bottom_key_height = bottom_key_height ;
$ width_difference = width_difference ;
$ height_difference = height_difference ;
$t otal_depth = total_depth ;
$t op_tilt = top_tilt ;
$t op_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 ;
2017-11-12 13:01:17 -05:00
$ linear_extrude_shape = linear_extrude_shape ;
2017-08-12 01:10:48 -04:00
$ rounded_key = rounded_key ;
$ stem_profile = stem_profile ;
2017-08-13 03:04:53 -04:00
$ stem_inset = stem_inset ;
$ stem_rotation = stem_rotation ;
$t ext = text ;
$ inset_text = inset_text ;
2017-08-14 22:48:01 -04:00
$ corner_radius = corner_radius ;
2017-08-15 01:59:12 -04:00
$ height_slices = height_slices ;
2017-08-16 01:59:19 -04:00
$ enable_side_sculpting = enable_side_sculpting ;
2017-11-05 17:56:05 -05:00
$ slop = slop ;
2017-11-12 13:01:17 -05:00
$ support_type = support_type ;
$ linear_extrude_height_adjustment = linear_extrude_height_adjustment ;
2017-08-13 03:04:53 -04:00
key ( ) ;
}
example_key ( ) ;