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
19 default gives a 1mm = (20mm - 19mm) gap between keycaps and cuts in
23 /* the angle between the halves is twice this number */
26 /* The radius of screw holes. Holes will be slightly bigger due
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;
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;
39 /* Distance between halves. */
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;
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;
51 /* Number of rows and columns in the matrix. You need to update
52 staggering_offsets if you change n_cols. */
56 /* Number of thumb keys (per hand), try 1 or 2. */
59 /* The width of the USB cable hole in the spacer. */
60 cable_hole_width = 12;
62 /* Vertical column staggering offsets. The first element should
64 staggering_offsets = [0, 5, 11, 6, 3];
66 /* Width and height between screw holes of OLED */
70 /* Width and height between screw holes of pointer */
74 ///////////////////////////////////////////////////////////////////////////////
76 ///////////////////////////////////////////////////////////////////////////////
78 module rz(angle, center=undef) {
79 /* Rotate children `angle` degrees around `center`. */
83 for (i=[0:$children-1])
90 /* Compute coordinates of a point obtained by rotating p angle degrees
91 around center. Used to compute locations of screw holes near the
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]];
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. */
100 notch_width = 3.5001;
101 notch_offset = 4.2545;
102 notch_depth = 0.8128;
103 translate(position) {
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);
110 translate([0, -notch_offset]) {
111 square([hole_size+2*notch_depth, notch_width], center=true);
118 module regular_key(position, size) {
119 /* Create a hole for a regular key. */
120 translate(position) {
121 square([size, size], center=true);
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]);
132 regular_key([0, i*column_spacing], key_size);
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]) {
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 */
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.
164 direction is the 2-element vector specifying to which side of
165 position to move to, [-1, -1] for bottom left, etc.
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);
171 /* key_hole_offset if the difference between key spacing and key
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];
181 module oled_holes(hole_radius) {
183 translate([-oled_width/2,-oled_height/2]) {
184 translate([0, oled_height]) {
185 screw_hole(hole_radius, washer_radius);
187 translate([oled_width, oled_height]) {
188 screw_hole(hole_radius, washer_radius);
190 translate([oled_width, 0]) {
191 screw_hole(hole_radius, washer_radius);
193 screw_hole(hole_radius, washer_radius);
196 square(oled_width-5, center=true);
201 module pointer_holes(hole_radius) {
203 translate([-pointer_width/2,-pointer_height/2]) {
204 translate([0, pointer_height]) {
205 screw_hole(hole_radius, washer_radius);
207 translate([pointer_width, pointer_height]) {
208 screw_hole(hole_radius, washer_radius);
210 translate([pointer_width, 0]) {
211 screw_hole(hole_radius, washer_radius);
213 screw_hole(hole_radius, washer_radius);
215 translate([0, -12]) {
216 square(pointer_width-5, center=true);
221 module passthrough() {
223 square(15, center=true);
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];
232 tmp = rz_fun(back_right, angle, [0, 2.25*column_spacing]);
237 add_hand_separation() {
238 screw_hole(hole_radius, washer_radius,
241 screw_hole(hole_radius, washer_radius,
242 [(n_cols+n_thumb_keys)*row_spacing, staggering_offsets[n_cols-1]],
244 screw_hole(hole_radius, washer_radius,
250 /* add the screw hole near the cable hole */
251 translate([washer_radius - tmp[0] - 0.5*hand_separation,
252 back_screw_hole_offset]) {
254 add_hand_separation() {
255 screw_hole(hole_radius,
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); }
270 ///////////////////////////////////////////////////////////////////////////////
272 ///////////////////////////////////////////////////////////////////////////////
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
278 x_offset = 0.5 * row_spacing;
279 y_offset = 0.5 * column_spacing;
280 thumb_key_offset = y_offset + 0.25 * column_spacing;
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]);
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);
294 module left_half(switch_holes=true, key_size=key_hole_size) {
296 right_half(switch_holes, key_size);
300 module bottom_plate() {
301 /* bottom layer of the case */
304 screw_holes(washer_radius);
306 screw_holes(screw_hole_radius);
311 /* top layer of the case */
316 oled_holes(screw_hole_radius);
317 pointer_holes(screw_hole_radius);
321 module switch_plate() {
322 /* the switch plate */
332 /* Create a spacer. */
338 right_half(switch_holes=false, key_size=switch_hole_size + 3);
339 left_half(switch_holes=false, key_size=switch_hole_size + 3);
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]);
346 screw_holes(washer_radius);
348 screw_holes(screw_hole_radius);
352 /* Create all four layers. */
354 translate([300, 0]) {
357 translate([0, 150]) {
360 translate([300, 150]) {
364 ///////////////////////////////////////////////////////////////////////////////