190 lines
5.7 KiB
Python
190 lines
5.7 KiB
Python
import cadquery2 as cq
|
|
from cadquery2 import exporters
|
|
from math import floor, atan, pi
|
|
|
|
# Naming convention
|
|
# _l means Y
|
|
# _w means X
|
|
# _h means Z
|
|
|
|
|
|
shell_t = 2 # Shell thickness
|
|
front_h = 25 # height at the front (near user)
|
|
back_h = 40 # height at the back (away from user)
|
|
width = 290 # width of the whole thing
|
|
fillet_s = 4 # Fillet of case sides
|
|
|
|
kbd_base_l = 150 # length of the front half of the case
|
|
screen_base_l = 100 # length of the back half of the case
|
|
kbd_angle = atan((back_h - front_h) / kbd_base_l) * 180 / pi
|
|
print("kbd_angle=", kbd_angle)
|
|
|
|
# Added an extra 1mm on each direction for tolerance to make sure it slots in
|
|
kbd_w = 202 # width of the keyboard itself
|
|
kbd_l = 127 # "length" of the keyboard
|
|
kbd_h = 4 # depth of the keyboard holder cutoff
|
|
kbd_fillet = 10 # width of the kbd corner fillet
|
|
|
|
# battery dimensions (mx7)
|
|
bat_w = 60
|
|
bat_l = 140
|
|
bat_h = 22
|
|
|
|
# width, length VISIBLE WITHIN THE BEZEL
|
|
screen_w = 223
|
|
screen_l = 57
|
|
screen_left_margin = 10 # Distance from the left of the case to screen cutout
|
|
# width, length, height INCLUDING_BEZEL
|
|
display_w = 233
|
|
display_l = 65
|
|
display_h = 5
|
|
# Distance from the left of the case to display end (7 is bezel width on that side)
|
|
display_left_margin = screen_left_margin - 7
|
|
# Distance from the right of the case to display end
|
|
display_right_margin = width - display_left_margin - display_w
|
|
# Distance from the top of the case to display top
|
|
display_top_margin = (screen_base_l - display_l) / 2
|
|
display_bottom_margin = display_top_margin # Symmetrical
|
|
|
|
kbd_cutout = (
|
|
cq.Sketch().trapezoid(kbd_w, kbd_l, 90).reset().vertices().fillet(kbd_fillet)
|
|
)
|
|
|
|
bat_holder_top = (
|
|
cq.Sketch()
|
|
.trapezoid(bat_w + 2 * shell_t, bat_h + 2 * shell_t, 90)
|
|
.trapezoid(bat_w, bat_h, 90, mode="s")
|
|
.trapezoid(bat_w + 10, bat_h - 10, 90, mode="s")
|
|
)
|
|
|
|
# Base to hold (magnetically?) the keyboard (half the case)
|
|
kbd_base = (
|
|
cq.Workplane("left")
|
|
.lineTo(0, kbd_base_l)
|
|
.lineTo(front_h, kbd_base_l)
|
|
.lineTo(back_h, 0)
|
|
.close()
|
|
.extrude(width)
|
|
.edges("+Z")
|
|
.fillet(fillet_s)
|
|
.edges("+Y")
|
|
.fillet(fillet_s)
|
|
# Cut kbd holder area from top face
|
|
.faces(">Z")
|
|
.workplane(centerOption="CenterOfBoundBox")
|
|
.center((screen_w - width) / 2 + screen_left_margin, 0)
|
|
.placeSketch(kbd_cutout)
|
|
.cutBlind(-kbd_h)
|
|
# Make it hollow
|
|
.faces("<Y")
|
|
.shell(-shell_t)
|
|
# Add battery holder
|
|
.faces("<Y")
|
|
.workplane(centerOption="CenterOfBoundBox", offset=-1)
|
|
# The final constants are fudging because it impinges on the surface
|
|
.center(
|
|
-(width - bat_w - 2 * shell_t) / 2 + 1,
|
|
(back_h - bat_h - 2 * shell_t) / 2 - 0.25,
|
|
)
|
|
.transformed(rotate=cq.Vector(-kbd_angle, 0, 0))
|
|
.placeSketch(bat_holder_top)
|
|
# The 3 is fudging so the bottom of the holder doesn't
|
|
# impinge on the bottom face
|
|
.extrude(-(bat_l - 3 * fillet_s))
|
|
)
|
|
|
|
# Slots for ventilation, applied at the bottom of the screen base
|
|
ventilacion = (
|
|
cq.Sketch() # slots are 3mm separated 5
|
|
.rarray(8, 6, floor(width / 9), 1)
|
|
.trapezoid(3, 0.8 * screen_base_l, 90, mode="a")
|
|
.reset()
|
|
.vertices()
|
|
.fillet(1)
|
|
)
|
|
|
|
screen_cutout = (
|
|
cq.Sketch().trapezoid(screen_w, screen_l, 90, mode="a").reset().vertices().fillet(1)
|
|
)
|
|
|
|
shelf_cutout = (
|
|
cq.Sketch()
|
|
.trapezoid(width - 2 * shell_t, screen_l, 90, mode="a")
|
|
.reset()
|
|
.vertices()
|
|
.fillet(1)
|
|
)
|
|
|
|
screen_lateral_stop = (
|
|
cq.Sketch()
|
|
.rarray(display_l - 2 * shell_t, 1, 2, 1)
|
|
.trapezoid(2 * shell_t, display_h + 2 * shell_t, 90, mode="a")
|
|
)
|
|
|
|
screen_vertical_stop = cq.Sketch().trapezoid(width, 2 * shell_t, 90, mode="a")
|
|
|
|
# Holder for the screen (other half of the case)
|
|
screen_base = (
|
|
# Basic filleted box shape
|
|
cq.Workplane("bottom")
|
|
.lineTo(-width, 0)
|
|
.lineTo(-width, back_h)
|
|
.lineTo(0, back_h)
|
|
.close()
|
|
.extrude(screen_base_l)
|
|
.edges("|Y and <Z")
|
|
.fillet(fillet_s)
|
|
.faces(">Y")
|
|
.shell(-shell_t)
|
|
# Account for outer shell thickness in offset
|
|
.faces(">Z")
|
|
.workplane(offset=-display_h - shell_t)
|
|
# Add friction shelve for screen assembly
|
|
.lineTo(-width, 0)
|
|
.lineTo(-width, -screen_base_l)
|
|
.lineTo(0, -screen_base_l)
|
|
.close()
|
|
.extrude(-shell_t)
|
|
# Cutout visible screen area from top face
|
|
.faces(">Z")
|
|
.workplane(centerOption="CenterOfBoundBox")
|
|
.center((width - screen_w) / 2 - screen_left_margin, 0)
|
|
.placeSketch(screen_cutout)
|
|
.cutBlind(-shell_t)
|
|
# Cut screen "shelf" to allow for cable routing
|
|
.faces(">Z")
|
|
.workplane(centerOption="CenterOfBoundBox", offset=-display_h - shell_t)
|
|
.placeSketch(shelf_cutout)
|
|
.cutBlind(-shell_t)
|
|
# Stop the screen from sliding back and forward
|
|
.faces(">Y")
|
|
.workplane(centerOption="CenterOfBoundBox")
|
|
.center(0, back_h / 2 - display_h - shell_t)
|
|
.placeSketch(screen_vertical_stop)
|
|
.extrude(-display_bottom_margin)
|
|
.faces("<Y")
|
|
.workplane(centerOption="CenterOfBoundBox")
|
|
.center(0, back_h / 2 - display_h - shell_t)
|
|
.placeSketch(screen_vertical_stop)
|
|
.extrude(-display_top_margin)
|
|
# Stop the screen from sliding left and right
|
|
.faces(">X")
|
|
.workplane(centerOption="CenterOfBoundBox")
|
|
.center(0, back_h / 2 - display_h - shell_t)
|
|
.placeSketch(screen_lateral_stop)
|
|
.extrude(-display_left_margin)
|
|
.faces("<X")
|
|
.workplane(centerOption="CenterOfBoundBox")
|
|
.center(0, back_h / 2 - display_h - shell_t)
|
|
.placeSketch(screen_lateral_stop)
|
|
.extrude(-display_right_margin)
|
|
# Place ventilation grid in bottom face
|
|
.faces("<Z")
|
|
.workplane(centerOption="CenterOfBoundBox")
|
|
.placeSketch(ventilacion)
|
|
.cutBlind(-5)
|
|
)
|
|
|
|
exporters.export(screen_base, "screen_base.stl")
|
|
exporters.export(kbd_base, "kbd_base.stl")
|