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