import cadquery as cq from cadquery import exporters import battery_holder import cpu_holder # Base for the notebook. Basically a kbd base that extends back # as much as possible # Thickness of the outer material shell_t = 3 # Size of the kbd board kbd_height = 95.5 kbd_width = 305 kbd_angle = 5 # Size of the whole object width = kbd_width + 2 * shell_t height = 159 thickness = 27 + shell_t # 27 inside # Insert Positions ti_radius = 2.35 ti_depth = 6.25 # Positions are determined by measuring the keyboard kbd_pillar_positions = [ (18.25, -16), (142.5, -25.5), (kbd_width - 20, -16), (23.5, -79.5), (145.5, -82.5), (kbd_width - 19, -79.5), ] # 2-level mounting pillars for the kbd # Width of these need to be tweaked for # each specific keyboard kbd_pillars = ( cq.Sketch() .push(kbd_pillar_positions) .circle(2.2, mode="a") # Holes for self-tapping screws .circle(1.1, mode="s") ) kbd_lower_pillars = ( cq.Sketch() .push(kbd_pillar_positions) .circle(4, mode="a") # Holes for self-tapping screws .circle(1.1, mode="s") ) # These are placed where convenient, and are used to join the top and bottom # parts of the case. # Measured from top-left corner OUTSIDE mounting_pillar_positions = [ (6, -6), (6, -40), (120, -6), (170, -6), (width - 6, -6), (width - 6, -40), ] mounting_pillars = ( cq.Sketch() .push(mounting_pillar_positions) .trapezoid(12, 12, 90, mode="a") .circle(1.8, mode="s") ) screw_holes = cq.Sketch().push(mounting_pillar_positions).circle(3, mode="a") # Hole for the USB hub's exposed port usb_in = cq.Sketch().trapezoid(13, 5.5, 90, mode="a") # Thing to "grab" the hub so it stays in place usb_holder = ( cq.Sketch().trapezoid(10, 22, 90, mode="a").trapezoid(10, 17, 90, mode="s").clean() ) # Hole for the USB hub's exposed port audio_in = cq.Sketch().trapezoid(17, 6, 90, mode="a") # CPU holder position from back-right corner of the case cpu_offset_x = 40 cpu_offset_y = 65 # Battery holder position from front-left corner of the case battery_offset_x = 21 battery_offset_y = 56 def model(): return ( cq.Workplane("XY") .workplane(offset=thickness / 2) .tag("mid_height") # Hollow box .box(width, height, thickness) .edges("|Z") .fillet(2) .faces(">Z") .shell(-shell_t) # Power inlet # The position is arbitrary, based on the components available .faces(">Y") .workplane(centerOption="CenterOfBoundBox") # The 15 is distance from stand hole center to center of USB plug # The -1 is distance from top of the pillar to center of USB plug .center( width / 2 - battery_offset_x - battery_holder.power_in_offset_x, -thickness / 2 + battery_holder.pillar_height + shell_t + battery_holder.power_in_offset_y, ) .placeSketch(battery_holder.power_in) .cutBlind(-shell_t) # Power button # The position is arbitrary, based on the components available, .faces(">Y") .workplane(centerOption="CenterOfBoundBox") # The 67 is distance from stand hole center to center of power button # The 5 is distance from top of the pillar to center of power button .center( width / 2 - battery_offset_x - battery_holder.button_offset_x, -thickness / 2 + battery_holder.pillar_height + shell_t + battery_holder.button_offset_y, ) .placeSketch(battery_holder.power_button_cut) .cutBlind(-shell_t) # USB inlet # The position is arbitrary, based on the components # available, keyboard height, cable length, etc. .faces(">X") .workplane(centerOption="CenterOfBoundBox") .center(-height / 2 + shell_t + 45 + 13 / 2, -8) .placeSketch(usb_in) .cutBlind(-shell_t) # Audio plugs # The position is arbitrary, based on the components # available, keyboard height, cable length, etc. .faces("Z") .workplane(centerOption="CenterOfBoundBox") # Top-left kbd corner inside the box .center(-width / 2 + shell_t, kbd_height - height / 2 + shell_t) .transformed(rotate=cq.Vector(kbd_angle, 0, 0)) # These two offsets push the keyboard "down" into the case # and need to be adjusted per-keyboard .tag("sloped") .workplane(offset=-2.5) .placeSketch(kbd_pillars) .extrude(-1000) .workplaneFromTagged("sloped") .workplane(offset=-5.5) .placeSketch(kbd_lower_pillars) .extrude(-1000) # Remove the excess extrusion .workplaneFromTagged("mid_height") .transformed(offset=cq.Vector(0, 0, -thickness / 2)) .split(keepTop=True) # Slope for the beyboard .workplaneFromTagged("sloped") .split(keepBottom=True) # Pillars to join with top half .workplaneFromTagged("mid_height") .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox") .center(-width / 2, height / 2) .placeSketch(mounting_pillars) .extrude(thickness) # Holes to insert screws from the bottom .workplaneFromTagged("mid_height") .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox") .center(-width / 2, height / 2) .placeSketch(screw_holes) # 13 is 20-7 (screw thread length - threaded insert depth) .cutBlind(thickness - 13) # CPU Stands (lower) .workplaneFromTagged("mid_height") .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox") .center(width / 2 - cpu_offset_x, height / 2 - cpu_offset_y) .placeSketch(cpu_holder.lower_stands) .extrude(7) # CPU Stands (higher) .workplaneFromTagged("mid_height") .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox") .center(width / 2 - cpu_offset_x, height / 2 - cpu_offset_y) .placeSketch(cpu_holder.higher_stands) .extrude(10.5) # Battery Stands (lower) # and should be a parameter (TODO) .workplaneFromTagged("mid_height") .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox") .center(-width / 2 + battery_offset_x, height / 2 - battery_offset_y) .placeSketch(battery_holder.lower_stands) .extrude(shell_t + battery_holder.pillar_height) # Battery Stands (higher) .workplaneFromTagged("mid_height") .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox") .center(-width / 2 + battery_offset_x, height / 2 - battery_offset_y) .placeSketch(battery_holder.higher_stands) .extrude(shell_t + battery_holder.pillar_height + 3.5) # Pogo pin connector holders .workplaneFromTagged("mid_height") .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox") .center( -width / 2 + battery_offset_x, height / 2 - battery_offset_y - battery_holder.pin_holder_height / 2, ) .placeSketch(battery_holder.pin_holder) .extrude(shell_t + 3) # Channel for the usb hub .workplaneFromTagged("mid_height") .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox") .center(width / 2 - 5, -height / 2 + shell_t + 45 + 13 / 2) .placeSketch(usb_holder) .extrude(shell_t + 8) ) if __name__ == "__main__": left_cutout = cq.Sketch().polygon( [(0, 0), (width / 2, 0), (width / 2, -height), (0, -height), (0, 0)], mode="a", ) right_side = ( model() .faces("