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 ///////////////////////////////////////////////////////////////////////////////
7 ///////////////////////////////////////////////////////////////////////////////
9 ///////////////////////////////////////////////////////////////////////////////
11 /* Set output quality */
14 /* Distance between key centers. */
16 row_spacing = column_spacing;
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. */
22 /* The angle between the halves is twice this number */
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
28 back_screw_hole_offset = 0;
30 /* The radius of screw holes. Holes will be slightly bigger due to the cut
32 screw_hole_radius = 1.5;
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;
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;
43 /* Distance between halves. */
44 hand_separation = key_hole_size - switch_hole_size;
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;
50 /* Number of rows and columns in the matrix. You need to update
51 staggering_offsets if you change n_cols. */
55 /* The width of the USB cable hole in the spacer. */
56 cable_hole_width = 12;
58 /* Vertical column staggering offsets. The first element should be zero. */
59 staggering_offsets = [0, 5, 11, 6, 3];
61 /* Width and height between screw holes of OLED */
65 /* Width and height between screw holes of pointer */
67 pointer_height = 10.5;
69 ///////////////////////////////////////////////////////////////////////////////
71 ///////////////////////////////////////////////////////////////////////////////
73 module rz(angle, center=undef) {
74 /* Rotate children `angle` degrees around `center`. */
78 for (i=[0:$children-1])
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]];
91 /* Cherry MX switch hole with the center at `position`. Sizes come from the
93 module switch_hole(position, notches=use_notched_holes) {
96 notch_offset = 4.2545;
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);
105 translate([0, -notch_offset]) {
106 square([hole_size+2*notch_depth, notch_width], center=true);
113 /* Create a hole for a regular key */
114 module regular_key(position, size) {
115 translate(position) {
116 square([size, size], center=true);
120 /* Create a column of keys */
121 module column (bottom_position, switch_holes, key_size=key_hole_size,
123 translate(bottom_position) {
124 for (i = [0:(rows-1)]) {
125 if (switch_holes == true) {
126 switch_hole([0, i*column_spacing]);
128 regular_key([0, i*column_spacing], key_size);
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]) {
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);
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
159 direction is the 2-element vector specifying to which side of position to
160 move to, [-1, -1] for bottom left, etc.
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);
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];
175 /* Create screw holes for the oled screen */
176 module oled_holes(hole_radius) {
178 translate([-oled_width/2,-oled_height/2]) {
179 translate([0, oled_height]) {
180 screw_hole(hole_radius, washer_radius);
182 translate([oled_width, oled_height]) {
183 screw_hole(hole_radius, washer_radius);
185 translate([oled_width, 0]) {
186 screw_hole(hole_radius, washer_radius);
188 screw_hole(hole_radius, washer_radius);
194 /* Passthrough in switch plate for the OLED cables */
195 module oled_passthrough() {
197 square([14, 8], center=true);
201 /* Create screw holes for the pointer module */
202 module pointer_holes(hole_radius) {
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);
209 translate([pointer_width, pointer_height]) {
210 screw_hole(hole_radius, washer_radius);
212 translate([pointer_width, 0]) {
213 screw_hole(hole_radius, washer_radius);
215 screw_hole(hole_radius, washer_radius);
219 pointer_passthrough();
222 module pointer_passthrough() {
224 translate([column_spacing*1.95-4, 16+row_spacing*2.5]) {
225 square([pointer_width+5, 15], center=true);
232 square([20, 30], center=true);
236 translate([-18, 60]) {
241 translate([30, 95]) {
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];
254 tmp = rz_fun(back_right, angle, [0, 2.25*column_spacing]);
259 add_hand_separation() {
260 screw_hole(hole_radius, washer_radius,
263 screw_hole(hole_radius, washer_radius,
264 [(n_cols+2)*row_spacing, staggering_offsets[n_cols-1]],
266 screw_hole(hole_radius, washer_radius,
272 /* Add the screw hole near the cable hole */
273 translate([washer_radius - tmp[0] - 0.5*hand_separation,
274 back_screw_hole_offset]) {
276 add_hand_separation() {
277 screw_hole(hole_radius, washer_radius, back_right, [nudge, nudge]);
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); }
289 ///////////////////////////////////////////////////////////////////////////////
291 ///////////////////////////////////////////////////////////////////////////////
293 /* Create switch holes or key holes for the right half of the
294 keyboard. Different key_sizes are used in top_plate() and
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;
301 add_hand_separation() {
304 column([x_offset + (j)*row_spacing, y_offset], switch_holes,
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);
316 /* Mirror the right half to create the left half */
317 module left_half(switch_holes=true, key_size=key_hole_size) {
319 right_half(switch_holes, key_size);
323 /* Create the bottom layer of the case */
324 module bottom_plate() {
327 screw_holes(washer_radius);
329 screw_holes(screw_hole_radius);
333 /* Create the top layer of the case */
339 oled_holes(screw_hole_radius);
340 pointer_holes(screw_hole_radius);
344 /* Create the switch plate */
345 module switch_plate() {
351 pointer_passthrough();
356 /* Create a spacer layer */
363 right_half(switch_holes=false, key_size=switch_hole_size + 3);
364 left_half(switch_holes=false, key_size=switch_hole_size + 3);
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]);
371 screw_holes(washer_radius);
373 screw_holes(screw_hole_radius);
377 /* Display all four layers */
380 translate([350, 0]) {
384 translate([0, 175]) {
388 translate([350, 175]) {
392 ///////////////////////////////////////////////////////////////////////////////