]> git.armaanb.net Git - atreides.git/blob - case.scad
Major updates to case design
[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 /* Sets whether the case should use notched holes. As far as I can tell these
47    notches are not all that useful... */
48 use_notched_holes = true;
49
50 /* Number of rows and columns in the matrix. You need to update
51    staggering_offsets if you change n_cols. */
52 n_rows = 4;
53 n_cols = 5;
54
55 /* The width of the USB cable hole in the spacer. */
56 cable_hole_width = 12;
57
58 /* Vertical column staggering offsets. The first element should be zero. */
59 staggering_offsets = [0, 5, 11, 6, 3];
60
61 /* Width and height between screw holes of OLED */
62 oled_width = 20;
63 oled_height = 20;
64
65 /* Width and height between screw holes of pointer */
66 pointer_width = 10.5;
67 pointer_height = 10.5;
68
69 ///////////////////////////////////////////////////////////////////////////////
70 // META MODULES                                                              //
71 ///////////////////////////////////////////////////////////////////////////////
72
73 module rz(angle, center=undef) {
74   /* Rotate children `angle` degrees around `center`. */
75   translate(center) {
76     rotate(angle) {
77       translate(-center) {
78         for (i=[0:$children-1])
79           children(i);
80       }
81     }
82   }
83 }
84
85 /* Compute coordinates of a point obtained by rotating p angle degrees around
86    center. Used to compute locations of screw holes near the USB cable hole. */
87 function rz_fun(p, angle, center) = [
88   cos(angle) * (p[0] - center[0]) - sin(angle) * (p[1] - center[1]) + center[0],
89   sin(angle) * (p[0] - center[0]) + cos(angle) * (p[1] - center[1])+ center[1]];
90
91 /* Cherry MX switch hole with the center at `position`. Sizes come from the
92    ErgoDox design. */
93 module switch_hole(position, notches=use_notched_holes) {
94   hole_size = 13.97;
95   notch_width = 3.5001;
96   notch_offset = 4.2545;
97   notch_depth = 0.8128;
98   translate(position) {
99     union() {
100       square([hole_size, hole_size], center=true);
101       if (notches == true) {
102         translate([0, notch_offset]) {
103           square([hole_size+2*notch_depth, notch_width], center=true);
104         }
105         translate([0, -notch_offset]) {
106           square([hole_size+2*notch_depth, notch_width], center=true);
107         }
108       }
109     }
110   }
111 };
112
113 /* Create a hole for a regular key */
114 module regular_key(position, size) {
115   translate(position) {
116     square([size, size], center=true);
117   }
118 }
119
120 /* Create a column of keys */
121 module column (bottom_position, switch_holes, key_size=key_hole_size,
122     rows=n_rows) {
123   translate(bottom_position) {
124     for (i = [0:(rows-1)]) {
125       if (switch_holes == true) {
126         switch_hole([0, i*column_spacing]);
127       } else {
128         regular_key([0, i*column_spacing], key_size);
129       }
130     }
131   }
132 }
133
134 /* Rotate the right half of the keys around the top left corner of the thumb
135    key. Assumes that the thumb key is a 1x1.5 key and that it is shifted
136    0.5*column_spacing up relative to the nearest column. */
137 module rotate_half() {
138   rotation_y_offset = 1.75 * column_spacing;
139   for (i=[0:$children-1]) {
140     rz(angle, [hand_separation, rotation_y_offset]) {
141       children(i);
142     }
143   }
144 }
145
146 /* Shift everything right to get desired hand separation */
147 module add_hand_separation() {
148   for (i=[0:$children-1]) {
149     /* Half the value because when it is mirrored, it gets cancelled out */
150     translate([0.5*hand_separation, 0]) children(i);
151   }
152 }
153
154 module screw_hole(radius, offset_radius, position, direction) {
155   /* Create a screw hole of radius `radius` at a location `offset_radius` from
156      `position`, (diagonally), in the direction `direction`. Oh, what a mess
157      this is. 
158
159      direction is the 2-element vector specifying to which side of position to
160      move to, [-1, -1] for bottom left, etc.
161
162      radius_offset is the offset in the x (or y) direction so that we're
163      offset_radius from position */
164   radius_offset = offset_radius / sqrt(2);
165
166   /* key_hole_offset if the difference between key spacing and key hole edge */
167   key_hole_offset = 0.5*(row_spacing - key_hole_size);
168   x = position[0] + (radius_offset - key_hole_offset) * direction[0];
169   y = position[1] + (radius_offset - key_hole_offset) * direction[1];
170   translate([x,y]) {
171     circle(radius);
172   }
173 }
174
175 /* Create screw holes for the oled screen */
176 module oled_holes(hole_radius) {
177   translate([0,85]) {
178     translate([-oled_width/2,-oled_height/2]) {
179       translate([0, oled_height]) {
180         screw_hole(hole_radius, washer_radius);
181       }
182       translate([oled_width, oled_height]) {
183         screw_hole(hole_radius, washer_radius);
184       }
185       translate([oled_width, 0]) {
186         screw_hole(hole_radius, washer_radius);
187       }
188       screw_hole(hole_radius, washer_radius);
189     }
190   }
191   oled_passthrough();
192 }
193
194 /* Passthrough in switch plate for the OLED cables */
195 module oled_passthrough() {
196   translate([0, 97]) {
197     square([14, 8], center=true);
198   }
199 }
200
201 /* Create screw holes for the pointer module */
202 module pointer_holes(hole_radius) {
203   rotate(angle) {
204     translate([column_spacing*1.95, row_spacing*2.5]) {
205       translate([-pointer_width/2,-pointer_height/2]) {
206         translate([0, pointer_height]) {
207           screw_hole(hole_radius, washer_radius);
208         }
209         translate([pointer_width, pointer_height]) {
210           screw_hole(hole_radius, washer_radius);
211         }
212         translate([pointer_width, 0]) {
213           screw_hole(hole_radius, washer_radius);
214         }
215         screw_hole(hole_radius, washer_radius);
216       }
217     }
218   }
219   pointer_passthrough();
220 }
221
222 module pointer_passthrough() {
223   rotate(angle) {
224     translate([column_spacing*1.95-4, 16+row_spacing*2.5]) {
225       square([pointer_width+5, 15], center=true);
226     }
227   }
228 }
229
230 // Arduino Pro mini
231 module promini(){
232   square([20, 30], center=true);
233 }
234
235 module prominis(){
236     translate([-18, 60]) {
237       rotate(-angle-90) {
238         promini();
239       }
240     }
241     translate([30, 95]) {
242       rotate(90) {
243         promini();
244       }
245     }
246 }
247
248 module right_screw_holes(hole_radius) {
249   /* Coordinates of the back right screw hole before rotation... */
250   back_right = [(n_cols+2)*row_spacing,
251              staggering_offsets[n_cols-1] + n_rows * column_spacing];
252
253   /* ...and after */
254   tmp = rz_fun(back_right, angle, [0, 2.25*column_spacing]);
255
256   nudge = 0.75;
257
258   rotate_half() {
259     add_hand_separation() {
260       screw_hole(hole_radius, washer_radius,
261           [row_spacing, 0],
262           [-nudge, -nudge]);
263       screw_hole(hole_radius, washer_radius,
264           [(n_cols+2)*row_spacing, staggering_offsets[n_cols-1]],
265           [nudge, -nudge]);
266       screw_hole(hole_radius, washer_radius,
267           back_right,
268           [nudge, nudge]);
269     }
270   }
271
272   /* Add the screw hole near the cable hole */
273   translate([washer_radius - tmp[0] - 0.5*hand_separation,
274       back_screw_hole_offset]) {
275     rotate_half() {
276       add_hand_separation() {
277         screw_hole(hole_radius, washer_radius, back_right, [nudge, nudge]);
278       }
279     }
280   }
281 }
282
283 /* Create all the screw holes */
284 module screw_holes(hole_radius) {
285   right_screw_holes(hole_radius);
286   mirror ([1,0,0]) { right_screw_holes(hole_radius); }
287 }
288
289 ///////////////////////////////////////////////////////////////////////////////
290 // PLATE MODULES                                                             //
291 ///////////////////////////////////////////////////////////////////////////////
292
293 /* Create switch holes or key holes for the right half of the
294    keyboard. Different key_sizes are used in top_plate() and
295    spacer(). */
296 module right_half(switch_holes=true, key_size=key_hole_size) {
297   x_offset = 0.5 * row_spacing;
298   y_offset = 0.5 * column_spacing;
299   thumb_key_offset = y_offset + 0.25 * column_spacing;
300   rotate_half() {
301     add_hand_separation() {
302       // Add thumb keys
303       for (j=[0:(2-1)]) {
304         column([x_offset + (j)*row_spacing, y_offset], switch_holes,
305             key_size, 2);
306       }
307       // Add thumb keys
308       for (j=[0:(n_cols-1)]) {
309         column([x_offset + (j+2)*row_spacing,
310             y_offset + staggering_offsets[j]], switch_holes, key_size);
311       }
312     }
313   }
314 }
315
316 /* Mirror the right half to create the left half */
317 module left_half(switch_holes=true, key_size=key_hole_size) {
318   mirror ([1,0,0]) {
319     right_half(switch_holes, key_size);
320   }
321 }
322
323 /* Create the bottom layer of the case */
324 module bottom_plate() {
325   difference() {
326     hull() {
327       screw_holes(washer_radius);
328     }
329     screw_holes(screw_hole_radius);
330   }
331 }
332
333 /* Create the top layer of the case */
334 module top_plate() {
335   difference() {
336     bottom_plate();
337     right_half(false);
338     left_half(false);
339     oled_holes(screw_hole_radius);
340     pointer_holes(screw_hole_radius);
341   }
342 }
343
344 /* Create the switch plate */
345 module switch_plate() {
346   difference() {
347     bottom_plate();
348     right_half();
349     left_half();
350     oled_passthrough();
351     pointer_passthrough();
352     /* prominis(); */
353   }
354 }
355
356 /* Create a spacer layer */
357 module spacer() {
358   difference() {
359     union() {
360       difference() {
361         bottom_plate();
362         hull() {
363           right_half(switch_holes=false, key_size=switch_hole_size + 3);
364           left_half(switch_holes=false, key_size=switch_hole_size + 3);
365         }
366         /* Add the USB cable hole */
367         translate([-0.5*cable_hole_width+100, 2*column_spacing]) {
368           square([cable_hole_width, (2*n_rows) * column_spacing]);
369         }
370       }
371       screw_holes(washer_radius);
372     }
373     screw_holes(screw_hole_radius);
374   }
375 }
376
377 /* Display all four layers */
378 top_plate();
379
380 translate([350, 0]) {
381   switch_plate();
382 }
383
384 translate([0, 175]) {
385   bottom_plate();
386 }
387
388 translate([350, 175]) {
389   spacer();
390 }
391
392 ///////////////////////////////////////////////////////////////////////////////