]> git.armaanb.net Git - atreides.git/blob - case/openscad/atreus_case.scad
Merge pull request #5 from ckhroulev/new-thumb-diagram
[atreides.git] / case / openscad / atreus_case.scad
1 // -*- mode: c -*-
2 /* All distances are in mm. */
3
4 /* set output quality */
5 $fn = 50;
6
7 /* Distance between key centers. */
8 column_spacing   = 19;
9 row_spacing      = column_spacing;
10
11 /* This number should exceed row_spacing and column_spacing. The
12  default gives a 1mm = (20mm - 19mm) gap between keycaps and cuts in
13  the top plate.*/
14 key_hole_size = 20;
15
16 /* rotation angle; the angle between the halves is twice this
17    number */
18 angle = 10;
19
20 /* The radius of screw holes. Holes will be slightly bigger due
21    to the cut width. */
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;
27
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;
32
33 /* Distance between halves. */
34 hand_separation        = 0;
35
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;
40
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;
44
45 /* Number of rows and columns in the matrix. You need to update
46    staggering_offsets if you change n_cols. */
47 n_rows = 4;
48 n_cols = 5;
49
50 /* The width of the USB cable hole in the spacer. */
51 cable_hole_width = 4;
52
53 /* Vertical column staggering offsets. The first element should
54    be zero. */
55 staggering_offsets = [0, 5, 11, 6, 3];
56
57 module rz(angle, center=undef) {
58   /* Rotate children `angle` degrees around `center`. */
59   translate(center) {
60     rotate(angle) {
61       translate(-center) {
62         for (i=[0:$children-1])
63           child(i);
64       }
65     }
66   }
67 }
68
69 /* Compute coordinates of a point obtained by rotating p angle degrees
70    around center. Used to compute locations of screw holes near the
71    USB cable hole. */
72 function rz_fun(p, angle, center) = [cos(angle) * (p[0] - center[0]) - sin(angle) * (p[1] - center[1]) + center[0],
73                                      sin(angle) * (p[0] - center[0]) + cos(angle) * (p[1] - center[1])+ center[1]];
74
75 module switch_hole(position, notches=use_notched_holes) {
76   /* Cherry MX switch hole with the center at `position`. Sizes come
77      from the ErgoDox design. */
78   hole_size    = 13.97;
79   notch_width  = 3.5001;
80   notch_offset = 4.2545;
81   notch_depth  = 0.8128;
82   translate(position) {
83     union() {
84       square([hole_size, hole_size], center=true);
85       if (notches == true) {
86         translate([0, notch_offset]) {
87           square([hole_size+2*notch_depth, notch_width], center=true);
88         }
89         translate([0, -notch_offset]) {
90           square([hole_size+2*notch_depth, notch_width], center=true);
91         }
92       }
93     }
94   }
95 };
96
97 module regular_key(position, size) {
98   /* Create a hole for a regular key. */
99   translate(position) {
100     square([size, size], center=true);
101   }
102 }
103
104 module thumb_key(position, size) {
105   /* Create a hole for a 1x1.5 unit thumb key. */
106   translate(position) {
107     scale([1, 1.5]) {
108       translate(-position) {
109         regular_key(position, size);
110       }
111     }
112   }
113 }
114
115 module column (bottom_position, switch_holes, key_size=key_hole_size) {
116   /* Create a column of keys. */
117   translate(bottom_position) {
118     for (i = [0:(n_rows-1)]) {
119       if (switch_holes == true) {
120         switch_hole([0, i*column_spacing]);
121       } else {
122         regular_key([0, i*column_spacing], key_size);
123       }
124     }
125   }
126 }
127
128 module rotate_half() {
129   /* Rotate the right half of the keys around the top left corner of
130      the thumb key. Assumes that the thumb key is a 1x1.5 key and that
131      it is shifted 0.5*column_spacing up relative to the nearest column. */
132   rotation_y_offset = 1.75 * column_spacing;
133   for (i=[0:$children-1]) {
134     rz(angle, [hand_separation, rotation_y_offset]) {
135       child(i);
136     }
137   }
138 }
139
140 module add_hand_separation() {
141   /* Shift everything right to get desired hand separation. */
142   for (i=[0:$children-1]) {
143     translate([0.5*hand_separation, /* we get back the full separation
144                                        because of mirroring */
145                0]) child(i);
146   }
147 }
148
149 module right_half (switch_holes=true, key_size=key_hole_size) {
150   /* Create switch holes or key holes for the right half of the
151      keyboard. Different key_sizes are used in top_plate() and
152      spacer(). */
153   x_offset = 0.5 * row_spacing;
154   y_offset = 0.5 * column_spacing;
155   thumb_key_offset = y_offset + 0.5 * column_spacing;
156   rotate_half() {
157     add_hand_separation() {
158       if (switch_holes == true) {
159         switch_hole([x_offset, thumb_key_offset]);
160       } else {
161         thumb_key([x_offset, thumb_key_offset], key_size);
162       }
163       for (j=[0:(n_cols-1)]) {
164         column([x_offset + (j+1)*row_spacing, y_offset + staggering_offsets[j]], switch_holes, key_size);
165       }
166     }
167   }
168 }
169
170 module screw_hole(radius, offset_radius, position, direction) {
171   /* Create a screw hole of radius `radius` at a location
172      `offset_radius` from `position`, (diagonally), in the direction
173      `direction`. Oh, what a mess this is. */
174   /* direction is the 2-element vector specifying to which side of
175      position to move to, [-1, -1] for bottom left, etc. */
176
177   /* radius_offset is the offset in the x (or y) direction so that
178      we're offset_radius from position */
179   radius_offset = offset_radius / sqrt(2);
180   /* key_hole_offset if the difference between key spacing and key
181      hole edge */
182   key_hole_offset = 0.5*(row_spacing - key_hole_size);
183   x = position[0] + (radius_offset - key_hole_offset) * direction[0];
184   y = position[1] + (radius_offset - key_hole_offset) * direction[1];
185   translate([x,y]) {
186     circle(radius);
187   }
188 }
189
190 module right_screw_holes(hole_radius) {
191   /* coordinates of the back right screw hole before rotation... */
192   back_right = [(n_cols+1)*row_spacing,
193                staggering_offsets[n_cols-1] + n_rows * column_spacing];
194   /* and after */
195   tmp = rz_fun(back_right, angle, [0, 2.25*column_spacing]);
196
197   nudge = 0.75;
198
199   rotate_half() {
200     add_hand_separation() {
201       screw_hole(hole_radius, washer_radius,
202                  [row_spacing, 0],
203                  [-nudge, -nudge]);
204       screw_hole(hole_radius, washer_radius,
205                  [(n_cols+1)*row_spacing, staggering_offsets[n_cols-1]],
206                  [nudge, -nudge]);
207       screw_hole(hole_radius, washer_radius,
208                  back_right,
209                  [nudge, nudge]);
210     }
211   }
212
213   /* add the screw hole near the cable hole */
214   translate([cable_hole_width + washer_radius - tmp[0],
215              back_screw_hole_offset]) {
216     rotate_half() {
217       add_hand_separation() {
218         screw_hole(hole_radius,
219                    washer_radius,
220                    back_right,
221                    [nudge, nudge]);
222       }
223     }
224   }
225 }
226
227 module screw_holes(hole_radius) {
228   /* Create all the screw holes. */
229   right_screw_holes(hole_radius);
230   mirror ([1,0,0]) { right_screw_holes(hole_radius); }
231 }
232
233 module left_half(switch_holes=true, key_size=key_hole_size) {
234   mirror ([1,0,0]) { right_half(switch_holes, key_size); }
235 }
236
237 module bottom_plate() {
238   /* bottom layer of the case */
239   difference() {
240     hull() { screw_holes(washer_radius); }
241     screw_holes(screw_hole_radius);
242   }
243 }
244
245 module top_plate() {
246   /* top layer of the case */
247   difference() {
248     bottom_plate();
249     right_half(false);
250     left_half(false);
251   }
252 }
253
254 module switch_plate() {
255   /* the switch plate */
256   difference() {
257     bottom_plate();
258     right_half();
259     left_half();
260   }
261 }
262
263 module spacer() {
264   /* Create a spacer. */
265   difference() {
266     union() {
267       difference() {
268         bottom_plate();
269         hull() {
270           right_half(switch_holes=false, key_size=switch_hole_size + 3);
271           left_half(switch_holes=false, key_size=switch_hole_size + 3);
272         }
273       }
274       screw_holes(washer_radius);
275     }
276     screw_holes(screw_hole_radius);
277     /* add the USB cable hole: */
278     translate([-0.5*cable_hole_width, 2*column_spacing]) {
279       square([cable_hole_width, (2*n_rows) * column_spacing]);
280     }
281   }
282 }
283
284 /* Create all four layers. */
285 top_plate();
286 translate([300, 150]) { spacer(); }
287 translate([300, 0]) { switch_plate(); }
288 translate([0, 150]) { bottom_plate(); }