/////////////////////////////////////////////////////////////////////////////// // Atreides case file, OpenSCAD laser cut format // // Copyright 2014-2020 Phil Hagelberg, 2021 Armaan Bhojwani, GPLv3 // // All distances are in mm // /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // VARIABLES // /////////////////////////////////////////////////////////////////////////////// // Set output quality $fn = 150; // Distance between key centers. column_spacing = 19; row_spacing = column_spacing; /* This number should exceed row_spacing and column_spacing. The default gives a 1mm = (20mm - 19mm) gap between keycaps and cuts in the top plate. */ key_hole_size = 20; // The angle between the halves is twice this number angle = 10; /* This constant allows tweaking the location of the screw holes near the USB cable. Only useful with small `angle` values. Try the value of 10 with angle=0. */ back_screw_hole_offset = 0; /* The radius of screw holes. Holes will be slightly bigger due to the cut width. */ screw_hole_radius = 1.5; /* Each screw hole is a hole in a "washer". How big these "washers" depends on the material used: this parameter and the `switch_hole_size` determine the spacer wall thickness. */ washer_radius = 4 * screw_hole_radius; /* The approximate size of switch holes. Used to determine how thick walls can be, i.e. how much room around each switch hole to leave. See spacer(). */ switch_hole_size = 14; // Distance between halves. hand_separation = key_hole_size - switch_hole_size; /* Number of rows and columns in the matrix. You need to update staggering_offsets if you change n_cols. */ n_rows = 4; n_cols = 5; // The width of the USB cable hole in the spacer. cable_hole_width = 12; // Vertical column staggering offsets. The first element should be zero. staggering_offsets = [0, 5, 11, 6, 3]; // Width and height between screw holes of OLED oled_width = 20; oled_height = 18; /////////////////////////////////////////////////////////////////////////////// // META MODULES // /////////////////////////////////////////////////////////////////////////////// module rz(angle, center=undef) { // Rotate children `angle` degrees around `center`. translate(center) { rotate(angle) { translate(-center) { for (i=[0:$children-1]) children(i); } } } } /* Compute coordinates of a point obtained by rotating p angle degrees around center. Used to compute locations of screw holes near the USB cable hole. */ function rz_fun(p, angle, center) = [ cos(angle) * (p[0] - center[0]) - sin(angle) * (p[1] - center[1]) + center[0], sin(angle) * (p[0] - center[0]) + cos(angle) * (p[1] - center[1])+ center[1]]; /* Cherry MX switch hole with the center at `position`. Sizes come from the ErgoDox design. */ module switch_hole(position) { hole_size = 13.97; notch_width = 3.5001; notch_offset = 4.2545; notch_depth = 0.8128; translate(position) { square([hole_size, hole_size], center=true); } }; // Create a hole for a regular key module regular_key(position, size) { translate(position) { square([size, size], center=true); } } // Create a column of keys module column (bottom_position, switch_holes, key_size=key_hole_size, rows=n_rows) { translate(bottom_position) { for (i = [0:(rows-1)]) { if (switch_holes == true) { switch_hole([0, i*column_spacing]); } else { regular_key([0, i*column_spacing], key_size); } } } } /* Rotate the right half of the keys around the top left corner of the thumb key. Assumes that the thumb key is a 1x1.5 key and that it is shifted 0.5*column_spacing up relative to the nearest column. */ module rotate_half() { rotation_y_offset = 1.75 * column_spacing; for (i=[0:$children-1]) { rz(angle, [hand_separation, rotation_y_offset]) { children(i); } } } // Shift everything right to get desired hand separation module add_hand_separation() { for (i=[0:$children-1]) { // Half the value because when it is mirrored, it gets cancelled out translate([0.5*hand_separation, 0]) children(i); } } module screw_hole(radius, offset_radius, position=[0,0], direction=[0,0]) { /* Create a screw hole of radius `radius` at a location `offset_radius` from `position`, (diagonally), in the direction `direction`. Oh, what a mess this is. direction is the 2-element vector specifying to which side of position to move to, [-1, -1] for bottom left, etc. radius_offset is the offset in the x (or y) direction so that we're offset_radius from position */ radius_offset = offset_radius / sqrt(2); // key_hole_offset if the difference between key spacing and key hole edge key_hole_offset = 0.5*(row_spacing - key_hole_size); x = position[0] + (radius_offset - key_hole_offset) * direction[0]; y = position[1] + (radius_offset - key_hole_offset) * direction[1]; translate([x,y]) { circle(radius); } } // Create screw holes for the oled screen module oled_holes(hole_radius) { translate([0,85]) { translate([-oled_width/2,-oled_height/2]) { translate([0, oled_height]) { screw_hole(hole_radius, washer_radius); } translate([oled_width, oled_height]) { screw_hole(hole_radius, washer_radius); } translate([oled_width, 0]) { screw_hole(hole_radius, washer_radius); } screw_hole(hole_radius, washer_radius); } } oled_passthrough(); } // Passthrough in switch plate for the OLED cables module oled_passthrough() { translate([0, 97]) { square([14, 8], center=true); } } module right_screw_holes(hole_radius) { // Coordinates of the back right screw hole before rotation... back_right = [(n_cols+2)*row_spacing, staggering_offsets[n_cols-1] + n_rows * column_spacing]; // ...and after tmp = rz_fun(back_right, angle, [0, 2.25*column_spacing]); nudge = 0.75; rotate_half() { add_hand_separation() { screw_hole(hole_radius, washer_radius, [row_spacing, 0], [-nudge, -nudge]); screw_hole(hole_radius, washer_radius, [(n_cols+2)*row_spacing, staggering_offsets[n_cols-1]], [nudge, -nudge]); screw_hole(hole_radius, washer_radius, back_right, [nudge, nudge]); } } // Add the screw hole near the cable hole translate([washer_radius - tmp[0] - 0.5*hand_separation, back_screw_hole_offset]) { rotate_half() { add_hand_separation() { screw_hole(hole_radius, washer_radius, back_right, [nudge, nudge]); } } } } // Create all the screw holes module screw_holes(hole_radius) { right_screw_holes(hole_radius); mirror ([1,0,0]) { right_screw_holes(hole_radius); } } /////////////////////////////////////////////////////////////////////////////// // PLATE MODULES // /////////////////////////////////////////////////////////////////////////////// /* Create switch holes or key holes for the right half of the keyboard. Different key_sizes are used in top_plate() and spacer(). */ module right_half(switch_holes=true, key_size=key_hole_size) { x_offset = 0.5 * row_spacing; y_offset = 0.5 * column_spacing; thumb_key_offset = y_offset + 0.25 * column_spacing; rotate_half() { add_hand_separation() { // Add thumb keys for (j=[0:(2-1)]) { column([x_offset + (j)*row_spacing, y_offset], switch_holes, key_size, 2); } // Add thumb keys for (j=[0:(n_cols-1)]) { column([x_offset + (j+2)*row_spacing, y_offset + staggering_offsets[j]], switch_holes, key_size); } } } } // Mirror the right half to create the left half module left_half(switch_holes=true, key_size=key_hole_size) { mirror ([1,0,0]) { right_half(switch_holes, key_size); } } // Create the bottom layer of the case module bottom_plate() { difference() { hull() { screw_holes(washer_radius); } screw_holes(screw_hole_radius); } } // Create the top layer of the case module top_plate() { difference() { bottom_plate(); right_half(false); left_half(false); oled_holes(screw_hole_radius); } } // Create the switch plate module switch_plate() { difference() { bottom_plate(); right_half(); left_half(); oled_passthrough(); } } // Create a spacer layer module spacer() { difference() { union() { difference() { bottom_plate(); hull() { right_half(switch_holes=false, key_size=switch_hole_size + 3); left_half(switch_holes=false, key_size=switch_hole_size + 3); } // Add the USB cable hole translate([-0.5*cable_hole_width+100, 2*column_spacing]) { square([cable_hole_width, (2*n_rows) * column_spacing]); } } screw_holes(washer_radius); } screw_holes(screw_hole_radius); } } module cover() { square([10,10]); } // Display all five layers top_plate(); translate([350, 0]) { switch_plate(); } translate([0, 175]) { bottom_plate(); } translate([350, 175]) { spacer(); } /* translate([550, 0]) { */ /* cover(); */ /* } */ ///////////////////////////////////////////////////////////////////////////////