]> git.armaanb.net Git - atreides.git/blob - case.scad
Update
[atreides.git] / case.scad
1 ///////////////////////////////////////////////////////////////////////////////
2 // Atreides case file, OpenSCAD laser cut format                             //
3 // Copyright 2014-2020 Phil Hagelberg, 2021 Armaan Bhojwani, GPLv3           //
4 // All distances are in mm                                                   //
5 ///////////////////////////////////////////////////////////////////////////////
6
7 ///////////////////////////////////////////////////////////////////////////////
8 // VARIABLES                                                                 //
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // Set output quality
12 $fn = 150;
13
14 // Distance between key centers.
15 column_spacing = 19;
16 row_spacing = column_spacing;
17
18 /* This number should exceed row_spacing and column_spacing. The default gives
19          a 1mm = (20mm - 19mm) gap between keycaps and cuts in the top plate. */
20 key_hole_size = 20;
21
22 // The angle between the halves is twice this number
23 angle = 10;
24
25 /* This constant allows tweaking the location of the screw holes near the USB
26          cable. Only useful with small `angle` values. Try the value of 10 with
27          angle=0. */
28 back_screw_hole_offset = 0;
29
30 /* The radius of screw holes. Holes will be slightly bigger due to the cut
31          width. */
32 screw_hole_radius = 1.5;
33
34 /* Each screw hole is a hole in a "washer". How big these "washers" depends on
35          the material used: this parameter and the `switch_hole_size` determine the
36          spacer wall thickness. */
37 washer_radius = 4 * screw_hole_radius;
38
39 /* The approximate size of switch holes. Used to determine how thick walls can
40          be, i.e. how much room around each switch hole to leave. See spacer(). */
41 switch_hole_size = 14;
42
43 // Distance between halves.
44 hand_separation = key_hole_size - switch_hole_size;
45
46 /* Number of rows and columns in the matrix. You need to update
47          staggering_offsets if you change n_cols. */
48 n_rows = 4;
49 n_cols = 5;
50
51 // The width of the USB cable hole in the spacer.
52 cable_hole_width = 12;
53
54 // Vertical column staggering offsets. The first element should be zero.
55 staggering_offsets = [0, 5, 11, 6, 3];
56
57 // Width and height between screw holes of OLED
58 oled_width = 20;
59 oled_height = 18;
60
61 ///////////////////////////////////////////////////////////////////////////////
62 // META MODULES                                                              //
63 ///////////////////////////////////////////////////////////////////////////////
64
65 module rz(angle, center=undef) {
66         // Rotate children `angle` degrees around `center`.
67         translate(center) {
68                 rotate(angle) {
69                         translate(-center) {
70                                 for (i=[0:$children-1])
71                                         children(i);
72                         }
73                 }
74         }
75 }
76
77 /* Compute coordinates of a point obtained by rotating p angle degrees around
78          center. Used to compute locations of screw holes near the USB cable hole. */
79 function rz_fun(p, angle, center) = [
80         cos(angle) * (p[0] - center[0]) - sin(angle) * (p[1] - center[1]) + center[0],
81         sin(angle) * (p[0] - center[0]) + cos(angle) * (p[1] - center[1])+ center[1]];
82
83 /* Cherry MX switch hole with the center at `position`. Sizes come from the
84          ErgoDox design. */
85 module switch_hole(position) {
86         hole_size = 13.97;
87         notch_width = 3.5001;
88         notch_offset = 4.2545;
89         notch_depth = 0.8128;
90         translate(position) {
91                 square([hole_size, hole_size], center=true);
92         }
93 };
94
95 // Create a hole for a regular key
96 module regular_key(position, size) {
97         translate(position) {
98                 square([size, size], center=true);
99         }
100 }
101
102 // Create a column of keys
103 module column (bottom_position, switch_holes, key_size=key_hole_size,
104                 rows=n_rows) {
105         translate(bottom_position) {
106                 for (i = [0:(rows-1)]) {
107                         if (switch_holes == true) {
108                                 switch_hole([0, i*column_spacing]);
109                         } else {
110                                 regular_key([0, i*column_spacing], key_size);
111                         }
112                 }
113         }
114 }
115
116 /* Rotate the right half of the keys around the top left corner of the thumb
117          key. Assumes that the thumb key is a 1x1.5 key and that it is shifted
118          0.5*column_spacing up relative to the nearest column. */
119 module rotate_half() {
120         rotation_y_offset = 1.75 * column_spacing;
121         for (i=[0:$children-1]) {
122                 rz(angle, [hand_separation, rotation_y_offset]) {
123                         children(i);
124                 }
125         }
126 }
127
128 // Shift everything right to get desired hand separation
129 module add_hand_separation() {
130         for (i=[0:$children-1]) {
131                 // Half the value because when it is mirrored, it gets cancelled out
132                 translate([0.5*hand_separation, 0]) children(i);
133         }
134 }
135
136 module screw_hole(radius, offset_radius, position=[0,0], direction=[0,0]) {
137         /* Create a screw hole of radius `radius` at a location `offset_radius` from
138                  `position`, (diagonally), in the direction `direction`. Oh, what a mess
139                  this is.
140
141                  direction is the 2-element vector specifying to which side of position to
142                  move to, [-1, -1] for bottom left, etc.
143
144                  radius_offset is the offset in the x (or y) direction so that we're
145                  offset_radius from position */
146         radius_offset = offset_radius / sqrt(2);
147
148         // key_hole_offset if the difference between key spacing and key hole edge
149         key_hole_offset = 0.5*(row_spacing - key_hole_size);
150         x = position[0] + (radius_offset - key_hole_offset) * direction[0];
151         y = position[1] + (radius_offset - key_hole_offset) * direction[1];
152         translate([x,y]) {
153                 circle(radius);
154         }
155 }
156
157 // Create screw holes for the oled screen
158 module oled_holes(hole_radius) {
159         translate([0,85]) {
160                 translate([-oled_width/2,-oled_height/2]) {
161                         translate([0, oled_height]) {
162                                 screw_hole(hole_radius, washer_radius);
163                         }
164                         translate([oled_width, oled_height]) {
165                                 screw_hole(hole_radius, washer_radius);
166                         }
167                         translate([oled_width, 0]) {
168                                 screw_hole(hole_radius, washer_radius);
169                         }
170                         screw_hole(hole_radius, washer_radius);
171                 }
172         }
173         oled_passthrough();
174 }
175
176 // Passthrough in switch plate for the OLED cables
177 module oled_passthrough() {
178         translate([0, 97]) {
179                 square([14, 8], center=true);
180         }
181 }
182
183 module right_screw_holes(hole_radius) {
184         // Coordinates of the back right screw hole before rotation...
185         back_right = [(n_cols+2)*row_spacing,
186                                                  staggering_offsets[n_cols-1] + n_rows * column_spacing];
187
188         // ...and after
189         tmp = rz_fun(back_right, angle, [0, 2.25*column_spacing]);
190
191         nudge = 0.75;
192
193         rotate_half() {
194                 add_hand_separation() {
195                         screw_hole(hole_radius, washer_radius,
196                                         [row_spacing, 0],
197                                         [-nudge, -nudge]);
198                         screw_hole(hole_radius, washer_radius,
199                                         [(n_cols+2)*row_spacing, staggering_offsets[n_cols-1]],
200                                         [nudge, -nudge]);
201                         screw_hole(hole_radius, washer_radius,
202                                         back_right,
203                                         [nudge, nudge]);
204                 }
205         }
206
207         // Add the screw hole near the cable hole
208         translate([washer_radius - tmp[0] - 0.5*hand_separation,
209                         back_screw_hole_offset]) {
210                 rotate_half() {
211                         add_hand_separation() {
212                                 screw_hole(hole_radius, washer_radius, back_right, [nudge, nudge]);
213                         }
214                 }
215         }
216 }
217
218 // Create all the screw holes
219 module screw_holes(hole_radius) {
220         right_screw_holes(hole_radius);
221         mirror ([1,0,0]) { right_screw_holes(hole_radius); }
222 }
223
224 ///////////////////////////////////////////////////////////////////////////////
225 // PLATE MODULES                                                             //
226 ///////////////////////////////////////////////////////////////////////////////
227
228 /* Create switch holes or key holes for the right half of the
229          keyboard. Different key_sizes are used in top_plate() and
230          spacer(). */
231 module right_half(switch_holes=true, key_size=key_hole_size) {
232         x_offset = 0.5 * row_spacing;
233         y_offset = 0.5 * column_spacing;
234         thumb_key_offset = y_offset + 0.25 * column_spacing;
235         rotate_half() {
236                 add_hand_separation() {
237                         // Add thumb keys
238                         for (j=[0:(2-1)]) {
239                                 column([x_offset + (j)*row_spacing, y_offset], switch_holes,
240                                                 key_size, 2);
241                         }
242                         // Add thumb keys
243                         for (j=[0:(n_cols-1)]) {
244                                 column([x_offset + (j+2)*row_spacing,
245                                                 y_offset + staggering_offsets[j]], switch_holes, key_size);
246                         }
247                 }
248         }
249 }
250
251 // Mirror the right half to create the left half
252 module left_half(switch_holes=true, key_size=key_hole_size) {
253         mirror ([1,0,0]) {
254                 right_half(switch_holes, key_size);
255         }
256 }
257
258 // Create the bottom layer of the case
259 module bottom_plate() {
260         difference() {
261                 hull() {
262                         screw_holes(washer_radius);
263                 }
264                 screw_holes(screw_hole_radius);
265         }
266 }
267
268 // Create the top layer of the case
269 module top_plate() {
270         difference() {
271                 bottom_plate();
272                 right_half(false);
273                 left_half(false);
274                 oled_holes(screw_hole_radius);
275         }
276 }
277
278 // Create the switch plate
279 module switch_plate() {
280         difference() {
281                 bottom_plate();
282                 right_half();
283                 left_half();
284                 oled_passthrough();
285         }
286 }
287
288 // Create a spacer layer
289 module spacer() {
290         difference() {
291                 union() {
292                         difference() {
293                                 bottom_plate();
294                                 hull() {
295                                         right_half(switch_holes=false, key_size=switch_hole_size + 3);
296                                         left_half(switch_holes=false, key_size=switch_hole_size + 3);
297                                 }
298                                 // Add the USB cable hole
299                                 translate([-0.5*cable_hole_width+100, 2*column_spacing]) {
300                                         square([cable_hole_width, (2*n_rows) * column_spacing]);
301                                 }
302                         }
303                         screw_holes(washer_radius);
304                 }
305                 screw_holes(screw_hole_radius);
306         }
307 }
308
309 module cover() {
310         square([10,10]);
311 }
312
313 // Display all five layers
314 top_plate();
315
316 translate([350, 0]) {
317         switch_plate();
318 }
319
320 translate([0, 175]) {
321         bottom_plate();
322 }
323
324 translate([350, 175]) {
325         spacer();
326 }
327
328 /* translate([550, 0]) { */
329 /*   cover(); */
330 /* } */
331
332 ///////////////////////////////////////////////////////////////////////////////