2 /* All distances are in mm. */
4 /* set output quality */
7 /* Distance between key centers. */
9 row_spacing = column_spacing;
11 /* This number should exceed row_spacing and column_spacing. The
12 default gives a 1mm = (20mm - 19mm) gap between keycaps and cuts in
16 /* rotation angle; the angle between the halves is twice this
20 /* The radius of screw holes. Holes will be slightly bigger due
22 screw_hole_radius = 1.5;
23 /* Each screw hole is a hole in a "washer". How big these "washers"
24 should be depends on the material used: this parameter and the
25 `switch_hole_size` determine the spacer wall thickness. */
26 washer_radius = 4 * screw_hole_radius;
28 /* This constant allows tweaking the location of the screw holes near
29 the USB cable. Only useful with small `angle` values. Try the value
30 of 10 with angle=0. */
31 back_screw_hole_offset = 0;
33 /* Distance between halves. */
36 /* The approximate size of switch holes. Used to determine how
37 thick walls can be, i.e. how much room around each switch hole to
38 leave. See spacer(). */
39 switch_hole_size = 14;
41 /* Sets whether the case should use notched holes. As far as I can
42 tell these notches are not all that useful... */
43 use_notched_holes = true;
45 /* Number of rows and columns in the matrix. You need to update
46 staggering_offsets if you change n_cols. */
50 /* Number of thumb keys (per hand), try 1 or 2. */
53 /* The width of the USB cable hole in the spacer. */
54 cable_hole_width = 12;
56 /* Vertical column staggering offsets. The first element should
58 staggering_offsets = [0, 5, 11, 6, 3];
60 module rz(angle, center=undef) {
61 /* Rotate children `angle` degrees around `center`. */
65 for (i=[0:$children-1])
72 /* Compute coordinates of a point obtained by rotating p angle degrees
73 around center. Used to compute locations of screw holes near the
75 function rz_fun(p, angle, center) = [cos(angle) * (p[0] - center[0]) - sin(angle) * (p[1] - center[1]) + center[0],
76 sin(angle) * (p[0] - center[0]) + cos(angle) * (p[1] - center[1])+ center[1]];
78 module switch_hole(position, notches=use_notched_holes) {
79 /* Cherry MX switch hole with the center at `position`. Sizes come
80 from the ErgoDox design. */
83 notch_offset = 4.2545;
87 square([hole_size, hole_size], center=true);
88 if (notches == true) {
89 translate([0, notch_offset]) {
90 square([hole_size+2*notch_depth, notch_width], center=true);
92 translate([0, -notch_offset]) {
93 square([hole_size+2*notch_depth, notch_width], center=true);
100 module regular_key(position, size) {
101 /* Create a hole for a regular key. */
102 translate(position) {
103 square([size, size], center=true);
107 module thumb_key(position, size) {
108 /* Create a hole for a 1x1.5 unit thumb key. */
109 translate(position) {
111 translate(-position) {
112 regular_key(position, size);
118 module column (bottom_position, switch_holes, key_size=key_hole_size) {
119 /* Create a column of keys. */
120 translate(bottom_position) {
121 for (i = [0:(n_rows-1)]) {
122 if (switch_holes == true) {
123 switch_hole([0, i*column_spacing]);
125 regular_key([0, i*column_spacing], key_size);
131 module rotate_half() {
132 /* Rotate the right half of the keys around the top left corner of
133 the thumb key. Assumes that the thumb key is a 1x1.5 key and that
134 it is shifted 0.5*column_spacing up relative to the nearest column. */
135 rotation_y_offset = 1.75 * column_spacing;
136 for (i=[0:$children-1]) {
137 rz(angle, [hand_separation, rotation_y_offset]) {
143 module add_hand_separation() {
144 /* Shift everything right to get desired hand separation. */
145 for (i=[0:$children-1]) {
146 translate([0.5*hand_separation, /* we get back the full separation
147 because of mirroring */
152 module right_half (switch_holes=true, key_size=key_hole_size) {
153 /* Create switch holes or key holes for the right half of the
154 keyboard. Different key_sizes are used in top_plate() and
156 x_offset = 0.5 * row_spacing;
157 y_offset = 0.5 * column_spacing;
158 thumb_key_offset = y_offset + 0.5 * column_spacing;
160 add_hand_separation() {
161 for (j=[0:(n_thumb_keys-1)]) {
162 if (switch_holes == true) {
163 switch_hole([x_offset + j*row_spacing, thumb_key_offset]);
165 thumb_key([x_offset + j*row_spacing, thumb_key_offset], key_size);
168 for (j=[0:(n_cols-1)]) {
169 column([x_offset + (j+n_thumb_keys)*row_spacing, y_offset + staggering_offsets[j]], switch_holes, key_size);
175 module screw_hole(radius, offset_radius, position, direction) {
176 /* Create a screw hole of radius `radius` at a location
177 `offset_radius` from `position`, (diagonally), in the direction
178 `direction`. Oh, what a mess this is. */
179 /* direction is the 2-element vector specifying to which side of
180 position to move to, [-1, -1] for bottom left, etc. */
182 /* radius_offset is the offset in the x (or y) direction so that
183 we're offset_radius from position */
184 radius_offset = offset_radius / sqrt(2);
185 /* key_hole_offset if the difference between key spacing and key
187 key_hole_offset = 0.5*(row_spacing - key_hole_size);
188 x = position[0] + (radius_offset - key_hole_offset) * direction[0];
189 y = position[1] + (radius_offset - key_hole_offset) * direction[1];
195 module right_screw_holes(hole_radius) {
196 /* coordinates of the back right screw hole before rotation... */
197 back_right = [(n_cols+n_thumb_keys)*row_spacing,
198 staggering_offsets[n_cols-1] + n_rows * column_spacing];
200 tmp = rz_fun(back_right, angle, [0, 2.25*column_spacing]);
205 add_hand_separation() {
206 screw_hole(hole_radius, washer_radius,
209 screw_hole(hole_radius, washer_radius,
210 [(n_cols+n_thumb_keys)*row_spacing, staggering_offsets[n_cols-1]],
212 screw_hole(hole_radius, washer_radius,
218 /* add the screw hole near the cable hole */
219 translate([washer_radius - tmp[0],
220 back_screw_hole_offset]) {
222 add_hand_separation() {
223 screw_hole(hole_radius,
232 module screw_holes(hole_radius) {
233 /* Create all the screw holes. */
234 right_screw_holes(hole_radius);
235 mirror ([1,0,0]) { right_screw_holes(hole_radius); }
238 module left_half(switch_holes=true, key_size=key_hole_size) {
239 mirror ([1,0,0]) { right_half(switch_holes, key_size); }
242 module bottom_plate() {
243 /* bottom layer of the case */
245 hull() { screw_holes(washer_radius); }
246 screw_holes(screw_hole_radius);
251 /* top layer of the case */
259 module switch_plate() {
260 /* the switch plate */
269 /* Create a spacer. */
275 right_half(switch_holes=false, key_size=switch_hole_size + 3);
276 left_half(switch_holes=false, key_size=switch_hole_size + 3);
278 /* add the USB cable hole: */
279 translate([-0.5*cable_hole_width, 2*column_spacing]) {
280 square([cable_hole_width, (2*n_rows) * column_spacing]);
283 screw_holes(washer_radius);
285 screw_holes(screw_hole_radius);
289 /* Create all four layers. */
291 translate([300, 150]) { spacer(); }
292 translate([300, 0]) { switch_plate(); }
293 translate([0, 150]) { bottom_plate(); }