Compare commits

58 Commits

Author SHA1 Message Date
92a6cef8b7 only fillet in Z to match bottom case 2023-04-06 17:39:18 -03:00
0132814e64 slight height tweak in hinged lid 2023-04-06 16:59:32 -03:00
ebcbea59b0 Adjusted hinged lid dimensions based on fitting tests 2023-04-06 10:41:17 -03:00
3618e9c4f9 Hacky hinged lid WIP 2023-04-05 17:35:11 -03:00
e5308b8b24 Remove unused constant 2023-04-05 17:34:51 -03:00
f2d2f652c4 Beginnings of the notebook style lid 2023-04-04 18:04:53 -03:00
a1f3948756 Fix comments 2023-04-04 16:18:32 -03:00
05aaee6ee9 Make lid decorative cover work better 2023-04-04 15:37:19 -03:00
c9e36a5a08 Decorative flat cover that hides the seam down the middle 2023-04-04 10:43:06 -03:00
b71ed1d062 Updated output 2023-04-03 14:19:25 -03:00
3ef050bdbb Adjust dimensions of objects, add backstop for USB hub 2023-04-03 14:16:51 -03:00
8cce323574 Adjust screwhole size so heads are flat on surface 2023-04-03 14:16:20 -03:00
38929ade83 blah 2023-04-03 09:59:47 -03:00
2982f34049 blah 2023-04-03 09:53:02 -03:00
4110b9322e Fix pillar position in screen mount to match base 2023-04-03 08:58:15 -03:00
837caa4775 Dimensions in SVG (as a lark) 2023-04-01 18:46:35 -03:00
3aa568d22d No need for 3mf files 2023-04-01 16:22:37 -03:00
de04e5627c Make audio interface a real component 2023-04-01 14:26:48 -03:00
26c50d4da7 Add outline for hub (not measured) 2023-04-01 13:42:54 -03:00
d69066f465 Add outline for hub (not measured) 2023-04-01 13:42:40 -03:00
dcc6b3e552 Better fillet 2023-04-01 11:31:15 -03:00
a6992ed4a6 SVG top views 2023-03-31 15:42:28 -03:00
66c7c76528 Adjust hole position 2023-03-31 15:42:05 -03:00
a240a31fd2 Generate SVG top view, adjust some dimensions 2023-03-31 15:25:03 -03:00
86a2686f66 Generate SVG top view 2023-03-31 15:24:48 -03:00
c7bd9bc45b Updated stl files 2023-03-31 13:03:00 -03:00
445cb1caeb Slightly tweaked design 2023-03-31 12:56:51 -03:00
56c880ffe8 better cutting, accounting for shell thickness 2023-03-31 12:56:03 -03:00
1b17c1e585 New lid part 2023-03-31 12:37:19 -03:00
0335944118 Proper fillet 2023-03-31 10:10:18 -03:00
80a027b8f1 Move keyboard up 2mm 2023-03-31 09:59:20 -03:00
222240c259 Adjust hole position 2023-03-31 09:58:38 -03:00
a5274c0534 Fillet properly 2023-03-30 17:47:43 -03:00
d5f4a1f358 Added hole for HDMI out 2023-03-30 10:24:33 -03:00
f1521523e6 Keyboard mount was wrong, it worked because of looser tolerances in previous version 2023-03-30 09:36:15 -03:00
2dc9510a04 Move screen pillars a little back (interference with keyboard 2023-03-30 09:34:24 -03:00
682fb38852 Make all screen pillar holes come from the bottom 2023-03-29 09:40:43 -03:00
9bff855ad7 Vents a bit more sturdy 2023-03-29 09:25:39 -03:00
3203c4c860 account for shell when positioning the keyboard 2023-03-29 09:25:10 -03:00
3b1722f5ff Change dimensions of kbd pillars 2023-03-29 09:05:10 -03:00
6e594b0b86 Adjust screen mount pillars 2023-03-28 11:33:40 -03:00
74a17e04ec Adjust screen mount so it works with new base 2023-03-28 11:20:34 -03:00
0a2bba4067 Add 2 more pillars, adjust component position 2023-03-28 11:15:41 -03:00
68b407f5b5 oops 2023-03-28 10:46:48 -03:00
f5bf232f0e Reworked keyboard code so it's more correct and general 2023-03-28 10:36:58 -03:00
bf302deab1 Reordered components so kbd doesn't interfere with other things 2023-03-27 17:15:49 -03:00
c49f9c6348 Fixed hexagon vents, unified punch_hole / extrude_shape APIs 2023-03-27 16:57:55 -03:00
68849b5aca Make usb_offset nicer 2023-03-27 11:23:57 -03:00
2553525623 Refactored kbd into module 2023-03-21 15:48:36 -03:00
61a723c713 Missing file 2023-03-21 10:56:17 -03:00
d1e7b589c8 Refactored pillars to join screen (adds concept of screw holes) 2023-03-21 10:21:04 -03:00
cdf2e33f64 Refactored usb hub module 2023-03-20 15:04:19 -03:00
9bdabfdd98 Refactor audio plug module 2023-03-20 13:19:14 -03:00
9904a412a3 Refactor hex vents into helper function 2023-03-15 11:17:39 -03:00
d64654f7b1 Added hole support for CPU holder 2023-03-15 10:46:36 -03:00
4f1b09ab95 Refactor zero_holder to match battery_holder, added vents 2023-03-15 10:15:40 -03:00
d9749bc38e Refactor battery_holder with single add entry point 2023-03-14 14:42:01 -03:00
7aeb2cc0c1 Fancy battery holder design 2023-03-08 17:24:43 -03:00
31 changed files with 6129 additions and 218 deletions

View File

@@ -1,16 +1,86 @@
# Hole to expose a USB audio card (YMMV)
# The hole is for a USB-A plug, y is measured in the hub
# (from the bottom face to middle of the hole)
import cadquery as cq
from utils import extrude_shape, punch_hole
# The hole is for a random USB sound card.
# Consumers should set proper offsets for the hole
item_w = 49
item_h = 20.5
hole_w = 17
hole_h = 5
holes = [
# 2-jack plug
{
"x": 0,
"x": -item_h / 2,
"y": 4,
"height": 6,
"width": 17,
"fillet": 2,
"shape": cq.Sketch()
.trapezoid(hole_w, hole_h, 90, mode="a")
.vertices()
.fillet(2),
},
]
elements = [
# Outline
{
"x": item_w / 2,
"y": item_h / 2,
"shape": (
cq.Sketch()
.trapezoid(item_w, item_h, 90, mode="a")
.trapezoid(item_w - 2, item_h - 2, 90, mode="s")
),
"height": 0.2,
},
]
def add(
*,
model,
width,
height,
thickness,
offset_x,
offset_y,
bottom_face,
back_face,
shell_t
):
# Extrusions
if bottom_face:
for element in elements:
model = extrude_shape(
model=model,
face=bottom_face,
w=width,
h=height,
x_offset=offset_x,
y_offset=offset_y,
element=element,
height=-(element["height"] + shell_t),
)
# Holes
if back_face:
for hole in holes:
model = punch_hole(
model=model,
face=back_face,
# FIXME: This is weird because it's the RIGHT side,
# So it's height instead of w, offset_y instead of x
# need to work on making these coherent
w=height,
h=thickness,
x_offset=height - offset_y,
y_offset=shell_t,
hole=hole,
depth=shell_t,
)
return model

View File

@@ -1,11 +1,14 @@
import cadquery as cq
stand_positions = [(0, 0), (58, 0), (58, 49), (0, 49)]
from utils import extrude_shape, punch_hole, hex_vents
stand_positions = [(3.5, 3.5), (61.5, 3.5), (61.5, 52.5), (3.5, 52.5)]
stands = (
cq.Sketch().push(stand_positions).circle(3, mode="a").circle(2.65 / 2, mode="s")
)
pillar_height = 7
width = 85
height = 56
# This is a holder for DuPont cables so they connect to this
# things' pogo pins which are used to power the CPU
@@ -39,16 +42,38 @@ elements = [
"shape": stands,
"height": pillar_height,
},
# Pogo pin connector channels
{
"x": 0,
"y": 40,
"y": 0,
"shape": cq.Sketch().push(stand_positions).circle(5),
"height": 0,
},
# Pogo pin connector channels
{
"x": 3.5,
"y": 43.5,
"shape": pin_holder,
"height": 3,
},
# Perimeter
{
"x": width / 2,
"y": height / 2,
"shape": (
cq.Sketch()
.trapezoid(width, height, 90, mode="a")
.trapezoid(width - 2, height - 2, 90, mode="s")
.vertices()
.fillet(3)
),
"height": 0.2,
},
]
vents = hex_vents(size=3, width=width, height=height)
# Hole distances are relative to the rightmost pillar
# seen from the back of the case, that's why they are negative
# Heights are relative to base of pillars
@@ -56,18 +81,70 @@ elements = [
holes = [
# Power inlet
{
"x": -15,
"x": -17,
"y": -1 + pillar_height,
"height": 6.5,
"width": 12,
"fillet": 1,
"shape": cq.Sketch().trapezoid(12, 6.5, 90, mode="a").vertices().fillet(1),
},
# Power button
{
"x": -67,
"x": -70,
"y": 5.5 + pillar_height,
"height": 7,
"width": 7,
"fillet": 1,
"shape": cq.Sketch().trapezoid(7, 7, 90, mode="a").vertices().fillet(1),
},
]
def add(
*,
model,
width,
height,
thickness,
offset_x,
offset_y,
bottom_face,
back_face,
shell_t
):
if bottom_face:
# Vents
for vent in vents:
model = punch_hole(
model=model,
face=bottom_face,
w=width,
h=height,
x_offset=offset_x + vent["x"],
y_offset=shell_t + offset_y + vent["y"],
hole=vent,
depth=shell_t,
)
# Battery holder stands and pogo pin holder
for element in elements:
model = extrude_shape(
model=model,
face=bottom_face,
w=width,
h=height,
x_offset=offset_x,
y_offset=shell_t + offset_y,
element=element,
height=-(element["height"] + shell_t),
)
if back_face:
# Holes
for hole in holes:
model = punch_hole(
model=model,
face=back_face,
w=width,
h=thickness,
x_offset=width - offset_x,
y_offset=shell_t,
hole=hole,
depth=shell_t,
)
return model

View File

@@ -0,0 +1,46 @@
# Hole to expose a USB audio card (YMMV)
import cadquery as cq
from utils import punch_hole
# The hole is for a random USB sound card.
# Consumers should set proper offsets for the hole
holes = [
# Hole for HDMI female adapter
{
"x": 0,
"y": 7,
"shape": cq.Sketch().trapezoid(22, 12.5, 90, mode="a").vertices().fillet(2),
},
]
def add(
*,
model,
width,
height,
thickness,
offset_x,
offset_y,
bottom_face,
back_face,
shell_t
):
# Holes
if back_face:
for hole in holes:
model = punch_hole(
model=model,
face=back_face,
w=width,
h=thickness,
x_offset=width - offset_x,
y_offset=shell_t,
hole=hole,
depth=shell_t,
)
return model

View File

@@ -0,0 +1,231 @@
import cadquery as cq
from cadquery import exporters
from modelo import (
height,
mounting_pillar_positions,
ti_depth,
ti_radius,
width,
thickness as model_thickness,
shell_t,
)
import keyboard
import screen_mount
hinge_radius = 5.5
screw_radius = 1.5 # M3
ring_radius = 5 # M3
hinge_offset = max(p[1] for p in mounting_pillar_positions) + 6
hinge_width = 25
thickness = 42
# This is a constant used to control how far back the hinges go
# when open. It's arbitrary and can be adjusted experimentally
# printing small samples
hinge_slant = shell_t + 2
def model():
# Create a 2-part hinged lid
model = (
cq.Workplane("XY")
# Hollow box
.workplane(offset=-thickness / 2)
.box(width, height, thickness)
.edges("|Z")
.fillet(2)
.faces("<Z")
.shell(-shell_t)
# Outer surface of the hinge
.faces(">X")
.workplane(centerOption="CenterOfBoundBox")
.center(height / 2 - hinge_offset, thickness / 2 - hinge_radius)
.tag("rightSide")
.placeSketch(cq.Sketch().circle(hinge_radius))
.extrude(-width)
# Cut middle section between the hinges
.workplaneFromTagged("rightSide")
.workplane(offset=-hinge_width)
.placeSketch(
cq.Sketch().polygon(
[
(-hinge_radius, -hinge_radius),
(-hinge_radius, 0),
(-hinge_radius - hinge_slant, hinge_radius),
(-hinge_slant, hinge_radius),
(-hinge_slant, hinge_radius - hinge_slant),
(hinge_radius, hinge_radius - hinge_slant),
(hinge_radius, -hinge_radius),
(-hinge_radius, -hinge_radius),
]
)
)
.cutBlind(-width + 2 * hinge_width - 1)
# Hole for screws
.workplaneFromTagged("rightSide")
.placeSketch(cq.Sketch().circle(screw_radius))
.cutBlind(-width)
# Holes for rings & screw heads
.workplaneFromTagged("rightSide")
.placeSketch(cq.Sketch().circle(ring_radius))
.cutBlind(-5)
.workplaneFromTagged("rightSide")
.workplane(offset=-width + 4)
.placeSketch(cq.Sketch().circle(ring_radius))
.cutBlind(-5)
# Split hinge halves
.faces(">X")
.workplaneFromTagged("rightSide")
.workplane(offset=-hinge_width / 2)
.placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90))
.cutBlind(-1)
.workplaneFromTagged("rightSide")
.workplane(offset=-hinge_width)
.placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90))
.cutBlind(-1)
.workplaneFromTagged("rightSide")
.workplane(offset=-width + hinge_width / 2)
.placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90))
.cutBlind(-1)
.workplaneFromTagged("rightSide")
.workplane(offset=-width + hinge_width)
.placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90))
.cutBlind(-1)
# Threaded inserts
.workplaneFromTagged("rightSide")
.workplane(offset=-hinge_width / 2)
.placeSketch(cq.Sketch().circle(ti_radius))
.cutBlind(-ti_depth)
.workplaneFromTagged("rightSide")
.workplane(offset=-width + hinge_width / 2)
.placeSketch(cq.Sketch().circle(ti_radius))
.cutBlind(ti_depth)
# Split two halves
# First cut for the right hinge
.workplaneFromTagged("rightSide")
.placeSketch(
cq.Sketch()
.polygon(
[
(0, 0),
(-hinge_radius - 0.2, 0),
(-hinge_radius - hinge_slant, hinge_radius),
(0, hinge_radius),
(0, 0),
]
)
.polygon(
[
(-hinge_radius - 0.2, 0),
(-hinge_radius - 0.2, -1000),
(-hinge_radius, -1000),
(-hinge_radius, 0),
(-hinge_radius - 0.2, 0),
]
)
.circle(hinge_radius, mode="s")
)
.cutBlind(-hinge_width / 2 - 1)
# Second cut for the right hinge
.workplaneFromTagged("rightSide")
.workplane(offset=-hinge_width / 2)
.placeSketch(
cq.Sketch()
.polygon(
[
(0, 0),
(hinge_radius + 0.2, 0),
(hinge_radius + 0.2 + hinge_slant, hinge_radius),
(0, hinge_radius),
(0, 0),
]
)
.circle(hinge_radius, mode="s")
)
.cutBlind(-hinge_width / 2 - 1)
# First cut for the left hinge
.workplaneFromTagged("rightSide")
.workplane(offset=-width + hinge_width)
.placeSketch(
cq.Sketch()
.polygon(
[
(0, 0),
(hinge_radius + 0.2, 0),
(hinge_radius + 0.2 + hinge_slant, hinge_radius),
(0, hinge_radius),
(0, 0),
]
)
.circle(hinge_radius, mode="s")
)
.cutBlind(-hinge_width / 2 - 1)
# Second cut for the left hinge
.workplaneFromTagged("rightSide")
.workplane(offset=-width + hinge_width / 2)
.placeSketch(
cq.Sketch()
.polygon(
[
(0, 0),
(-hinge_radius - 0.2, 0),
(-hinge_radius - hinge_slant, hinge_radius),
(0, hinge_radius),
(0, 0),
]
)
.polygon(
[
(-hinge_radius - 0.2, 0),
(-hinge_radius - 0.2, -1000),
(-hinge_radius, -1000),
(-hinge_radius, 0),
(-hinge_radius - 0.2, 0),
]
)
.circle(hinge_radius, mode="s")
)
.cutBlind(-hinge_width / 2 - 1)
)
# Cut off shape of the base
model = (
model.workplaneFromTagged("rightSide")
.center(-height + hinge_offset, -thickness + hinge_radius)
.placeSketch(
cq.Sketch().polygon(
[
(0, 0),
(0, keyboard.front_thickness),
(shell_t, keyboard.front_thickness),
(keyboard.actual_height + shell_t, keyboard.back_thickness),
(keyboard.actual_height + shell_t, model_thickness),
(height, model_thickness),
(height, 0),
(0, 0),
]
)
)
.cutBlind(-1000)
)
return model
if __name__ == "__main__":
model = model()
exporters.export(model, "hinged_lid.stl")
exporters.export(
model,
"hinged_lid.svg",
opt={
"projectionDir": (0, 0, 1),
"strokeWidth": 0.3,
},
)

Binary file not shown.

View File

@@ -0,0 +1,225 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="800.0"
height="240.0"
>
<g transform="scale(1.1320754716981134, -1.1320754716981134) translate(332.1666667666666,-97.16666666666666)" stroke-width="0.3" fill="none">
<!-- hidden lines -->
<g stroke="rgb(160,160,160)" fill="none" stroke-dasharray="0.3,0.3" >
<path d="M-155.5,18.74310998702215 L-155.5,19.799999999999997 " />
<path d="M-155.50000000000003,-76.5 L-155.50000000000003,18.743109987022166 " />
<path d="M-155.5,-77.5 L-155.5,-76.5 " />
<path d="M-143.0,17.618181818181817 L-143.0,15.0 " />
<path d="M-155.5,19.8 L-152.5,19.8 " />
<path d="M-152.5,18.74310998702215 L-152.5,19.799999999999997 " />
<path d="M-152.5,18.74310998702215 L-155.5,18.74310998702215 " />
<path d="M-152.5,18.74310998702215 L-155.5,18.74310998702215 " />
<path d="M-152.50000000000003,-76.5 L-152.50000000000003,18.743109987022166 " />
<path d="M-152.5,-76.5 L-155.5,-76.5 " />
<path d="M153.5,-79.5 L153.62558103905863,-79.49605345685654 L153.7506664671286,-79.48422940262896 L153.87476262917144,-79.46457450145738 L153.99737977432972,-79.43716632225727 L154.1180339887499,-79.40211303259031 L154.23624910536935,-79.35955297177651 L154.35155858313013,-79.30965410493204 L154.46350734820342,-79.25261336008774 L154.57165358995798,-79.18865585100403 L154.67557050458495,-79.1180339887499 L154.77484797949737,-79.04102648555158 L154.86909421185737,-78.95793725484283 L154.95793725484282,-78.86909421185739 L155.04102648555158,-78.7748479794974 L155.1180339887499,-78.67557050458495 L155.18865585100403,-78.571653589958 L155.2526133600877,-78.46350734820344 L155.30965410493204,-78.35155858313016 L155.3595529717765,-78.23624910536937 L155.4021130325903,-78.11803398874991 L155.43716632225727,-77.99737977432973 L155.46457450145738,-77.87476262917147 L155.48422940262896,-77.75066646712862 L155.49605345685654,-77.62558103905864 L155.5,-77.50000000000001 " />
<path d="M155.5,-77.5 L155.5,-76.5 " />
<path d="M155.5,-76.5 L152.5,-76.5 " />
<path d="M152.5,-76.5 L-152.5,-76.5 " />
<path d="M-155.5,-77.5 L-155.49605345685654,-77.62558103905863 L-155.48422940262896,-77.75066646712861 L-155.46457450145738,-77.87476262917144 L-155.43716632225727,-77.99737977432972 L-155.4021130325903,-78.1180339887499 L-155.3595529717765,-78.23624910536935 L-155.30965410493204,-78.35155858313014 L-155.25261336008774,-78.46350734820344 L-155.18865585100403,-78.57165358995799 L-155.1180339887499,-78.67557050458494 L-155.04102648555158,-78.77484797949738 L-154.95793725484282,-78.86909421185737 L-154.86909421185737,-78.95793725484282 L-154.77484797949737,-79.04102648555158 L-154.67557050458495,-79.1180339887499 L-154.571653589958,-79.18865585100403 L-154.46350734820342,-79.25261336008772 L-154.35155858313016,-79.30965410493204 L-154.23624910536935,-79.35955297177651 L-154.1180339887499,-79.40211303259031 L-153.99737977432972,-79.43716632225727 L-153.87476262917144,-79.46457450145738 L-153.7506664671286,-79.48422940262896 L-153.62558103905863,-79.49605345685654 L-153.5,-79.5 " />
<path d="M153.5,-79.5 L-153.5,-79.5 " />
<path d="M155.5,20.0 L155.5,77.5 " />
<path d="M155.5,20.0 L155.5,19.8 " />
<path d="M155.5,18.74310998702215 L155.5,19.8 " />
<path d="M155.5,-76.5 L155.5,18.74310998702215 " />
<path d="M155.5,30.5 L155.5,30.374639560909117 L155.5,30.004844339512097 L155.5,29.40915741234015 L155.5,28.61744900929367 L155.5,27.66941869558779 L155.5,26.61260466978157 L155.5,25.5 L155.5,24.38739533021843 L155.5,23.33058130441221 L155.5,22.38255099070633 L155.5,21.59084258765985 L155.5,20.995155660487903 L155.5,20.625360439090883 L155.5,20.500000000000004 " />
<path d="M155.5,20.5 L155.5,20.625360439090883 L155.5,20.995155660487903 L155.5,21.59084258765985 L155.5,22.38255099070633 L155.5,23.33058130441221 L155.5,24.38739533021843 L155.5,25.5 L155.5,26.61260466978157 L155.5,27.66941869558779 L155.5,28.617449009293665 L155.5,29.40915741234015 L155.5,30.004844339512097 L155.5,30.374639560909117 L155.5,30.499999999999996 " />
<path d="M142.0,17.618181818181817 L142.0,15.0 " />
<path d="M142.0,17.618181818181817 L142.0,20.601020514433642 " />
<path d="M142.0,31.0 L142.0,30.86210351700003 L142.0,30.455328773463307 L142.0,29.800073153574168 L142.0,28.929193910223038 L142.0,27.88636056514657 L142.0,26.72386513675973 L142.0,25.500000000000004 L142.0,24.276134863240273 L142.0,23.113639434853432 L142.0,22.07080608977697 L142.0,21.199926846425836 L142.0,20.544671226536696 L142.0,20.13789648299997 L142.0,20.000000000000007 " />
<path d="M142.0,20.0 L142.0,20.003123670513986 L142.0,20.012491133940507 L142.0,20.028091749963444 L142.0,20.04990779815216 L142.0,20.077914498089758 L142.0,20.11208003752068 L142.0,20.15236560848561 L142.0,20.198725451402677 L142.0,20.251106907044832 L142.0,20.30945047635446 L142.0,20.373689888027144 L142.0,20.443752173787946 L142.0,20.519557751274625 L142.0,20.60102051443362 " />
<path d="M142.0,27.85 L142.0,27.791080593627285 L142.0,27.617276839570685 L142.0,27.33730398379987 L142.0,26.965201034368025 L142.0,26.519626786926263 L142.0,26.02292419479734 L142.0,25.5 L142.0,24.977075805202663 L142.0,24.48037321307374 L142.0,24.034798965631975 L142.0,23.66269601620013 L142.0,23.382723160429315 L142.0,23.208919406372715 L142.0,23.15 " />
<path d="M142.0,23.15 L142.0,23.208919406372715 L142.0,23.382723160429315 L142.0,23.66269601620013 L142.0,24.034798965631975 L142.0,24.480373213073737 L142.0,24.97707580520266 L142.0,25.5 L142.0,26.022924194797337 L142.0,26.51962678692626 L142.0,26.96520103436802 L142.0,27.33730398379987 L142.0,27.617276839570685 L142.0,27.791080593627285 L142.0,27.849999999999998 " />
<path d="M130.5,20.601020514433642 L142.0,20.601020514433642 " />
<path d="M130.5,31.0 L130.5,30.86210351700003 L130.5,30.455328773463307 L130.5,29.800073153574168 L130.5,28.929193910223038 L130.5,27.88636056514657 L130.5,26.72386513675973 L130.5,25.500000000000004 L130.5,24.276134863240273 L130.5,23.113639434853432 L130.5,22.07080608977697 L130.5,21.199926846425836 L130.5,20.544671226536696 L130.5,20.13789648299997 L130.5,20.000000000000007 " />
<path d="M130.5,20.0 L130.5,20.003123670513986 L130.5,20.012491133940507 L130.5,20.028091749963444 L130.5,20.04990779815216 L130.5,20.077914498089758 L130.5,20.11208003752068 L130.5,20.15236560848561 L130.5,20.198725451402677 L130.5,20.251106907044832 L130.5,20.30945047635446 L130.5,20.373689888027144 L130.5,20.443752173787946 L130.5,20.519557751274625 L130.5,20.60102051443362 " />
<path d="M130.5,20.601020514433642 L130.5,17.727272727272727 " />
<path d="M130.5,17.727272727272727 L130.5,15.0 " />
<path d="M130.5,27.0 L130.5,26.962391868272736 L130.5,26.851453301853628 L130.5,26.672747223702046 L130.5,26.4352347027881 L130.5,26.15082560867634 L130.5,25.833781400934473 L130.5,25.5 L130.5,25.16621859906553 L130.5,24.849174391323665 L130.5,24.5647652972119 L130.5,24.327252776297954 L130.5,24.148546698146372 L130.5,24.037608131727264 L130.5,24.0 " />
<path d="M130.5,24.0 L130.5,24.037608131727264 L130.5,24.148546698146372 L130.5,24.327252776297954 L130.5,24.5647652972119 L130.5,24.84917439132366 L130.5,25.166218599065527 L130.5,25.5 L130.5,25.83378140093447 L130.5,26.150825608676335 L130.5,26.4352347027881 L130.5,26.672747223702046 L130.5,26.851453301853628 L130.5,26.962391868272736 L130.5,27.0 " />
<path d="M-131.5,17.727272727272727 L-131.5,15.0 " />
<path d="M-131.5,17.727272727272727 L-131.5,20.601020514433642 " />
<path d="M-131.5,31.0 L-131.5,30.86210351700003 L-131.5,30.455328773463307 L-131.5,29.800073153574168 L-131.5,28.929193910223038 L-131.5,27.88636056514657 L-131.5,26.72386513675973 L-131.5,25.500000000000004 L-131.5,24.276134863240273 L-131.5,23.113639434853432 L-131.5,22.07080608977697 L-131.5,21.199926846425836 L-131.5,20.544671226536696 L-131.5,20.13789648299997 L-131.5,20.000000000000007 " />
<path d="M-131.5,20.0 L-131.5,20.003123670513986 L-131.5,20.012491133940507 L-131.5,20.028091749963444 L-131.5,20.04990779815216 L-131.5,20.077914498089758 L-131.5,20.11208003752068 L-131.5,20.15236560848561 L-131.5,20.198725451402677 L-131.5,20.251106907044832 L-131.5,20.30945047635446 L-131.5,20.373689888027144 L-131.5,20.443752173787946 L-131.5,20.519557751274625 L-131.5,20.60102051443362 " />
<path d="M-131.5,27.0 L-131.5,26.962391868272736 L-131.5,26.851453301853628 L-131.5,26.672747223702046 L-131.5,26.4352347027881 L-131.5,26.15082560867634 L-131.5,25.833781400934473 L-131.5,25.5 L-131.5,25.16621859906553 L-131.5,24.849174391323665 L-131.5,24.5647652972119 L-131.5,24.327252776297957 L-131.5,24.148546698146372 L-131.5,24.037608131727264 L-131.5,24.0 " />
<path d="M-131.5,24.0 L-131.5,24.037608131727264 L-131.5,24.148546698146372 L-131.5,24.327252776297954 L-131.5,24.5647652972119 L-131.5,24.84917439132366 L-131.5,25.166218599065527 L-131.5,25.5 L-131.5,25.83378140093447 L-131.5,26.150825608676335 L-131.5,26.4352347027881 L-131.5,26.672747223702043 L-131.5,26.851453301853628 L-131.5,26.962391868272736 L-131.5,27.0 " />
<path d="M-143.0,20.601020514433642 L-131.5,20.601020514433642 " />
<path d="M-143.0,31.0 L-143.0,30.86210351700003 L-143.0,30.455328773463307 L-143.0,29.800073153574168 L-143.0,28.929193910223038 L-143.0,27.88636056514657 L-143.0,26.72386513675973 L-143.0,25.500000000000004 L-143.0,24.276134863240273 L-143.0,23.113639434853432 L-143.0,22.07080608977697 L-143.0,21.199926846425836 L-143.0,20.544671226536696 L-143.0,20.13789648299997 L-143.0,20.000000000000007 " />
<path d="M-143.0,20.0 L-143.0,20.003123670513986 L-143.0,20.012491133940507 L-143.0,20.028091749963444 L-143.0,20.04990779815216 L-143.0,20.077914498089758 L-143.0,20.11208003752068 L-143.0,20.15236560848561 L-143.0,20.198725451402677 L-143.0,20.251106907044832 L-143.0,20.30945047635446 L-143.0,20.373689888027144 L-143.0,20.443752173787946 L-143.0,20.519557751274625 L-143.0,20.60102051443362 " />
<path d="M-143.0,20.601020514433642 L-143.0,17.618181818181817 " />
<path d="M-143.0,27.85 L-143.0,27.791080593627285 L-143.0,27.617276839570685 L-143.0,27.33730398379987 L-143.0,26.965201034368025 L-143.0,26.519626786926263 L-143.0,26.02292419479734 L-143.0,25.5 L-143.0,24.977075805202663 L-143.0,24.48037321307374 L-143.0,24.034798965631975 L-143.0,23.66269601620013 L-143.0,23.382723160429315 L-143.0,23.208919406372715 L-143.0,23.15 " />
<path d="M-143.0,23.15 L-143.0,23.208919406372715 L-143.0,23.382723160429315 L-143.0,23.66269601620013 L-143.0,24.034798965631975 L-143.0,24.480373213073737 L-143.0,24.97707580520266 L-143.0,25.5 L-143.0,26.022924194797337 L-143.0,26.51962678692626 L-143.0,26.96520103436802 L-143.0,27.33730398379987 L-143.0,27.617276839570685 L-143.0,27.791080593627285 L-143.0,27.849999999999998 " />
<path d="M-152.5,17.618181818181824 L-152.5,-76.5 " />
<path d="M-152.5,-76.5 L152.5,-76.5 " />
<path d="M152.5,17.618181818181824 L152.5,-76.5 " />
<path d="M155.5,18.74310998702215 L152.5,18.74310998702215 " />
<path d="M152.5,-76.5 L152.5,18.743109987022166 " />
<path d="M143.0,20.0 L143.0,20.13789648299997 L143.0,20.544671226536693 L143.0,21.199926846425836 L143.0,22.070806089776966 L143.0,23.11363943485343 L143.0,24.27613486324027 L143.0,25.5 L143.0,26.723865136759727 L143.0,27.886360565146568 L143.0,28.929193910223034 L143.0,29.800073153574164 L143.0,30.455328773463304 L143.0,30.86210351700003 L143.0,30.999999999999993 " />
<path d="M143.0,31.0 L143.0,30.996876329486014 L143.0,30.987508866059493 L143.0,30.971908250036556 L143.0,30.95009220184784 L143.0,30.922085501910242 L143.0,30.88791996247932 L143.0,30.84763439151439 L143.0,30.801274548597323 L143.0,30.748893092955168 L143.0,30.69054952364554 L143.0,30.626310111972856 L143.0,30.556247826212054 L143.0,30.480442248725375 L143.0,30.39897948556638 " />
<path d="M143.0,30.398979485566358 L152.5,30.398979485566358 " />
<path d="M152.5,20.0 L152.5,20.137896482999956 L152.5,20.54467122653667 L152.5,21.199926846425804 L152.5,22.07080608977693 L152.5,23.113639434853397 L152.5,24.276134863240237 L152.5,25.49999999999997 L152.5,26.723865136759706 L152.5,27.886360565146553 L152.5,28.929193910223024 L152.5,29.800073153574157 L152.5,30.455328773463304 L152.5,30.86210351700003 L152.5,30.999999999999996 " />
<path d="M152.5,31.0 L152.5,30.996876329486014 L152.5,30.987508866059496 L152.5,30.971908250036556 L152.5,30.95009220184784 L152.5,30.922085501910246 L152.5,30.887919962479323 L152.5,30.84763439151439 L152.5,30.80127454859733 L152.5,30.74889309295517 L152.5,30.690549523645547 L152.5,30.626310111972867 L152.5,30.556247826212065 L152.5,30.48044224872539 L152.5,30.39897948556638 " />
<path d="M155.5,77.5 L155.49605345685654,77.62558103905863 L155.48422940262896,77.75066646712861 L155.46457450145738,77.87476262917144 L155.43716632225727,77.99737977432972 L155.4021130325903,78.1180339887499 L155.3595529717765,78.23624910536935 L155.30965410493204,78.35155858313014 L155.25261336008774,78.46350734820344 L155.18865585100403,78.57165358995799 L155.1180339887499,78.67557050458494 L155.04102648555158,78.77484797949738 L154.95793725484282,78.86909421185737 L154.86909421185737,78.95793725484282 L154.77484797949737,79.04102648555158 L154.67557050458495,79.1180339887499 L154.571653589958,79.18865585100403 L154.46350734820342,79.25261336008772 L154.35155858313016,79.30965410493204 L154.23624910536935,79.35955297177651 L154.1180339887499,79.40211303259031 L153.99737977432972,79.43716632225727 L153.87476262917144,79.46457450145738 L153.7506664671286,79.48422940262896 L153.62558103905863,79.49605345685654 L153.5,79.5 " />
<path d="M153.5,79.5 L-153.5,79.5 " />
<path d="M-153.5,79.5 L-153.62558103905863,79.49605345685654 L-153.7506664671286,79.48422940262896 L-153.87476262917144,79.46457450145738 L-153.99737977432972,79.43716632225727 L-154.1180339887499,79.40211303259031 L-154.23624910536935,79.35955297177651 L-154.35155858313013,79.30965410493204 L-154.46350734820342,79.25261336008774 L-154.57165358995798,79.18865585100403 L-154.67557050458495,79.1180339887499 L-154.77484797949737,79.04102648555158 L-154.86909421185737,78.95793725484283 L-154.95793725484282,78.86909421185739 L-155.04102648555158,78.7748479794974 L-155.1180339887499,78.67557050458495 L-155.18865585100403,78.571653589958 L-155.2526133600877,78.46350734820344 L-155.30965410493204,78.35155858313016 L-155.3595529717765,78.23624910536937 L-155.4021130325903,78.11803398874991 L-155.43716632225727,77.99737977432973 L-155.46457450145738,77.87476262917147 L-155.48422940262896,77.75066646712862 L-155.49605345685654,77.62558103905864 L-155.5,77.50000000000001 " />
<path d="M-155.5,20.0 L-155.5,77.5 " />
<path d="M-152.5,20.0 L-155.5,20.0 " />
<path d="M-152.5,20.0 L-152.5,76.5 " />
<path d="M152.5,76.5 L-152.5,76.5 " />
<path d="M152.5,20.0 L152.5,76.5 " />
<path d="M155.5,20.0 L152.5,20.0 " />
<path d="M155.5,20.0 L152.5,20.0 " />
<path d="M155.5,19.8 L152.5,19.8 " />
<path d="M152.5,19.8 L155.5,19.8 " />
<path d="M152.5,18.74310998702215 L152.5,19.799999999999997 " />
<path d="M155.5,18.74310998702215 L152.5,18.74310998702215 " />
<path d="M155.5,30.5 L150.5,30.5 " />
<path d="M150.5,30.5 L150.5,30.374639560909117 L150.5,30.004844339512097 L150.5,29.40915741234015 L150.5,28.61744900929367 L150.5,27.66941869558779 L150.5,26.61260466978157 L150.5,25.5 L150.5,24.38739533021843 L150.5,23.33058130441221 L150.5,22.38255099070633 L150.5,21.59084258765985 L150.5,20.995155660487903 L150.5,20.625360439090883 L150.5,20.500000000000004 " />
<path d="M150.5,20.5 L150.5,20.625360439090883 L150.5,20.995155660487903 L150.5,21.59084258765985 L150.5,22.38255099070633 L150.5,23.33058130441221 L150.5,24.38739533021843 L150.5,25.5 L150.5,26.61260466978157 L150.5,27.66941869558779 L150.5,28.617449009293665 L150.5,29.40915741234015 L150.5,30.004844339512097 L150.5,30.374639560909117 L150.5,30.499999999999996 " />
<path d="M142.0,27.85 L136.75,27.85 " />
<path d="M136.75,27.85 L136.75,27.791080593627285 L136.75,27.617276839570685 L136.75,27.33730398379987 L136.75,26.965201034368025 L136.75,26.519626786926263 L136.75,26.02292419479734 L136.75,25.5 L136.75,24.977075805202663 L136.75,24.48037321307374 L136.75,24.034798965631975 L136.75,23.66269601620013 L136.75,23.382723160429315 L136.75,23.208919406372715 L136.75,23.15 " />
<path d="M136.75,23.15 L136.75,23.208919406372715 L136.75,23.382723160429315 L136.75,23.66269601620013 L136.75,24.034798965631975 L136.75,24.480373213073737 L136.75,24.97707580520266 L136.75,25.5 L136.75,26.022924194797337 L136.75,26.51962678692626 L136.75,26.96520103436802 L136.75,27.33730398379987 L136.75,27.617276839570685 L136.75,27.791080593627285 L136.75,27.849999999999998 " />
<path d="M136.75,27.0 L136.75,26.962391868272736 L136.75,26.851453301853628 L136.75,26.672747223702046 L136.75,26.4352347027881 L136.75,26.15082560867634 L136.75,25.833781400934473 L136.75,25.5 L136.75,25.16621859906553 L136.75,24.849174391323665 L136.75,24.5647652972119 L136.75,24.327252776297954 L136.75,24.148546698146372 L136.75,24.037608131727264 L136.75,24.0 " />
<path d="M136.75,24.0 L136.75,24.037608131727264 L136.75,24.148546698146372 L136.75,24.327252776297954 L136.75,24.5647652972119 L136.75,24.84917439132366 L136.75,25.166218599065527 L136.75,25.5 L136.75,25.83378140093447 L136.75,26.150825608676335 L136.75,26.4352347027881 L136.75,26.672747223702046 L136.75,26.851453301853628 L136.75,26.962391868272736 L136.75,27.0 " />
<path d="M136.75,27.0 L130.5,27.0 " />
<path d="M-131.5,27.0 L-136.75,27.0 " />
<path d="M-136.75,27.0 L-136.75,26.962391868272736 L-136.75,26.851453301853628 L-136.75,26.672747223702046 L-136.75,26.4352347027881 L-136.75,26.15082560867634 L-136.75,25.833781400934473 L-136.75,25.5 L-136.75,25.16621859906553 L-136.75,24.849174391323665 L-136.75,24.5647652972119 L-136.75,24.327252776297954 L-136.75,24.148546698146372 L-136.75,24.037608131727264 L-136.75,24.0 " />
<path d="M-136.75,24.0 L-136.75,24.037608131727264 L-136.75,24.148546698146372 L-136.75,24.327252776297954 L-136.75,24.5647652972119 L-136.75,24.84917439132366 L-136.75,25.166218599065527 L-136.75,25.5 L-136.75,25.83378140093447 L-136.75,26.150825608676335 L-136.75,26.4352347027881 L-136.75,26.672747223702046 L-136.75,26.851453301853628 L-136.75,26.962391868272736 L-136.75,27.0 " />
<path d="M-143.0,27.85 L-136.75,27.85 " />
<path d="M-136.75,27.85 L-136.75,27.791080593627285 L-136.75,27.617276839570685 L-136.75,27.33730398379987 L-136.75,26.965201034368025 L-136.75,26.519626786926263 L-136.75,26.02292419479734 L-136.75,25.5 L-136.75,24.977075805202663 L-136.75,24.48037321307374 L-136.75,24.034798965631975 L-136.75,23.66269601620013 L-136.75,23.382723160429315 L-136.75,23.208919406372715 L-136.75,23.15 " />
<path d="M-136.75,23.15 L-136.75,23.208919406372715 L-136.75,23.382723160429315 L-136.75,23.66269601620013 L-136.75,24.034798965631975 L-136.75,24.480373213073737 L-136.75,24.97707580520266 L-136.75,25.5 L-136.75,26.022924194797337 L-136.75,26.51962678692626 L-136.75,26.96520103436802 L-136.75,27.33730398379987 L-136.75,27.617276839570685 L-136.75,27.791080593627285 L-136.75,27.849999999999998 " />
<path d="M143.0,33.47272727272727 L143.0,30.398979485566358 " />
<path d="M143.0,33.47272727272727 L143.0,36.199999999999996 " />
<path d="M143.0,27.0 L143.0,26.962391868272736 L143.0,26.851453301853628 L143.0,26.672747223702046 L143.0,26.4352347027881 L143.0,26.15082560867634 L143.0,25.833781400934473 L143.0,25.5 L143.0,25.16621859906553 L143.0,24.849174391323665 L143.0,24.5647652972119 L143.0,24.327252776297954 L143.0,24.148546698146372 L143.0,24.037608131727264 L143.0,24.0 " />
<path d="M143.0,24.0 L143.0,24.037608131727264 L143.0,24.148546698146372 L143.0,24.327252776297954 L143.0,24.5647652972119 L143.0,24.84917439132366 L143.0,25.166218599065527 L143.0,25.5 L143.0,25.83378140093447 L143.0,26.150825608676335 L143.0,26.4352347027881 L143.0,26.672747223702046 L143.0,26.851453301853628 L143.0,26.962391868272736 L143.0,27.0 " />
<path d="M-152.5,76.5 L152.5,76.5 " />
<path d="M-152.5,76.5 L-152.5,30.398979485566358 " />
<path d="M-152.5,30.398979485566358 L-144.0,30.398979485566358 " />
<path d="M-144.0,30.398979485566358 L-144.0,33.47272727272727 " />
<path d="M-130.5,33.47272727272727 L-130.5,30.398979485566358 " />
<path d="M-130.5,30.398979485566358 L129.5,30.398979485566358 " />
<path d="M129.5,30.398979485566358 L129.5,33.47272727272727 " />
<path d="M152.5,76.5 L152.5,30.398979485566358 " />
<path d="M-155.5,30.5 L-155.5,30.374639560909117 L-155.5,30.004844339512097 L-155.5,29.40915741234015 L-155.5,28.61744900929367 L-155.5,27.66941869558779 L-155.5,26.61260466978157 L-155.5,25.5 L-155.5,24.38739533021843 L-155.5,23.33058130441221 L-155.5,22.38255099070633 L-155.5,21.59084258765985 L-155.5,20.995155660487903 L-155.5,20.625360439090883 L-155.5,20.500000000000004 " />
<path d="M-155.5,20.5 L-155.5,20.625360439090883 L-155.5,20.995155660487903 L-155.5,21.59084258765985 L-155.5,22.38255099070633 L-155.5,23.33058130441221 L-155.5,24.38739533021843 L-155.5,25.5 L-155.5,26.61260466978157 L-155.5,27.66941869558779 L-155.5,28.617449009293665 L-155.5,29.40915741234015 L-155.5,30.004844339512097 L-155.5,30.374639560909117 L-155.5,30.499999999999996 " />
<path d="M129.5,33.47272727272727 L129.5,36.199999999999996 " />
<path d="M129.5,20.5 L129.5,20.601020514433642 " />
<path d="M129.5,20.601020514433642 L129.5,20.59351378264821 L129.5,20.58605155087363 L129.5,20.57863388678958 L129.5,20.57126085767152 L129.5,20.563932530390094 L129.5,20.556648971410514 L129.5,20.549410246791957 L129.5,20.54221642218697 L129.5,20.53506756284088 L129.5,20.527963733591186 L129.5,20.52090499886699 L129.5,20.513891422688392 L129.5,20.506923068665934 L129.5,20.50000000000001 " />
<path d="M129.5,24.085786437626904 L129.5,20.5 " />
<path d="M129.5,26.914213562373096 L129.5,26.804930290094955 L129.5,26.655397632887727 L129.5,26.47022778921526 L129.5,26.25513215406302 L129.5,26.01674515616756 L129.5,25.762419625111594 L129.5,25.5 L129.5,25.237580374888406 L129.5,24.98325484383244 L129.5,24.74486784593698 L129.5,24.52977221078474 L129.5,24.344602367112273 L129.5,24.195069709905045 L129.5,24.085786437626908 " />
<path d="M129.5,30.977225575051662 L129.5,26.914213562373096 " />
<path d="M129.5,30.977225575051662 L129.5,30.96159998762172 L129.5,30.94193336496734 L129.5,30.918240258413242 L129.5,30.89053819847765 L129.5,30.8588476819015 L129.5,30.823192156482875 L129.5,30.783598003728024 L129.5,30.740094519331667 L129.5,30.692713891501153 L129.5,30.641491177140402 L129.5,30.586464275911325 L129.5,30.52767390219188 L129.5,30.465163554951527 L129.5,30.398979485566358 " />
<path d="M-130.5,20.5 L129.5,20.5 " />
<path d="M-130.5,30.977225575051662 L-130.5,30.96159998762172 L-130.5,30.94193336496734 L-130.5,30.918240258413242 L-130.5,30.89053819847765 L-130.5,30.8588476819015 L-130.5,30.823192156482875 L-130.5,30.783598003728024 L-130.5,30.740094519331667 L-130.5,30.692713891501153 L-130.5,30.641491177140402 L-130.5,30.586464275911325 L-130.5,30.52767390219188 L-130.5,30.465163554951527 L-130.5,30.398979485566358 " />
<path d="M-130.5,33.47272727272727 L-130.5,36.199999999999996 " />
<path d="M-130.5,20.5 L-130.5,20.601020514433642 " />
<path d="M-130.5,20.601020514433642 L-130.5,20.59351378264821 L-130.5,20.58605155087363 L-130.5,20.57863388678958 L-130.5,20.57126085767152 L-130.5,20.563932530390094 L-130.5,20.556648971410514 L-130.5,20.549410246791957 L-130.5,20.54221642218697 L-130.5,20.53506756284088 L-130.5,20.527963733591186 L-130.5,20.52090499886699 L-130.5,20.513891422688392 L-130.5,20.506923068665934 L-130.5,20.50000000000001 " />
<path d="M-130.5,24.085786437626904 L-130.5,20.5 " />
<path d="M-130.5,26.914213562373096 L-130.5,26.804930290094955 L-130.5,26.655397632887727 L-130.5,26.47022778921526 L-130.5,26.25513215406302 L-130.5,26.01674515616756 L-130.5,25.762419625111594 L-130.5,25.5 L-130.5,25.237580374888406 L-130.5,24.98325484383244 L-130.5,24.74486784593698 L-130.5,24.52977221078474 L-130.5,24.344602367112273 L-130.5,24.195069709905045 L-130.5,24.085786437626908 " />
<path d="M-130.5,30.977225575051662 L-130.5,26.914213562373096 " />
<path d="M-144.0,33.47272727272727 L-144.0,36.199999999999996 " />
<path d="M-144.0,20.0 L-144.0,20.13789648299997 L-144.0,20.544671226536693 L-144.0,21.199926846425836 L-144.0,22.070806089776966 L-144.0,23.11363943485343 L-144.0,24.27613486324027 L-144.0,25.5 L-144.0,26.723865136759727 L-144.0,27.886360565146568 L-144.0,28.929193910223034 L-144.0,29.800073153574164 L-144.0,30.455328773463304 L-144.0,30.86210351700003 L-144.0,30.999999999999993 " />
<path d="M-144.0,31.0 L-144.0,30.996876329486014 L-144.0,30.987508866059493 L-144.0,30.971908250036556 L-144.0,30.95009220184784 L-144.0,30.922085501910242 L-144.0,30.88791996247932 L-144.0,30.84763439151439 L-144.0,30.801274548597323 L-144.0,30.748893092955168 L-144.0,30.69054952364554 L-144.0,30.626310111972856 L-144.0,30.556247826212054 L-144.0,30.480442248725375 L-144.0,30.39897948556638 " />
<path d="M-144.0,27.0 L-144.0,26.962391868272736 L-144.0,26.851453301853628 L-144.0,26.672747223702046 L-144.0,26.4352347027881 L-144.0,26.15082560867634 L-144.0,25.833781400934473 L-144.0,25.5 L-144.0,25.16621859906553 L-144.0,24.849174391323665 L-144.0,24.5647652972119 L-144.0,24.327252776297954 L-144.0,24.148546698146372 L-144.0,24.037608131727264 L-144.0,24.0 " />
<path d="M-144.0,24.0 L-144.0,24.037608131727264 L-144.0,24.148546698146372 L-144.0,24.327252776297954 L-144.0,24.5647652972119 L-144.0,24.84917439132366 L-144.0,25.166218599065527 L-144.0,25.5 L-144.0,25.83378140093447 L-144.0,26.150825608676335 L-144.0,26.4352347027881 L-144.0,26.672747223702046 L-144.0,26.851453301853628 L-144.0,26.962391868272736 L-144.0,27.0 " />
<path d="M-152.5,30.398979485566358 L-152.5,30.480442248725385 L-152.5,30.55624782621206 L-152.5,30.626310111972863 L-152.5,30.690549523645544 L-152.5,30.74889309295517 L-152.5,30.801274548597327 L-152.5,30.84763439151439 L-152.5,30.887919962479323 L-152.5,30.922085501910242 L-152.5,30.95009220184784 L-152.5,30.971908250036556 L-152.5,30.987508866059496 L-152.5,30.996876329486014 L-152.5,31.0 " />
<path d="M-152.5,31.0 L-152.5,30.86210351700003 L-152.5,30.4553287734633 L-152.5,29.800073153574154 L-152.5,28.92919391022302 L-152.5,27.88636056514655 L-152.5,26.723865136759702 L-152.5,25.499999999999964 L-152.5,24.276134863240234 L-152.5,23.11363943485339 L-152.5,22.07080608977693 L-152.5,21.199926846425804 L-152.5,20.544671226536668 L-152.5,20.137896482999956 L-152.5,20.000000000000004 " />
<path d="M150.5,27.0 L150.5,26.962391868272736 L150.5,26.851453301853628 L150.5,26.672747223702046 L150.5,26.4352347027881 L150.5,26.15082560867634 L150.5,25.833781400934473 L150.5,25.5 L150.5,25.16621859906553 L150.5,24.849174391323665 L150.5,24.5647652972119 L150.5,24.327252776297954 L150.5,24.148546698146372 L150.5,24.037608131727264 L150.5,24.0 " />
<path d="M150.5,24.0 L150.5,24.037608131727264 L150.5,24.148546698146372 L150.5,24.327252776297954 L150.5,24.5647652972119 L150.5,24.84917439132366 L150.5,25.166218599065527 L150.5,25.5 L150.5,25.83378140093447 L150.5,26.150825608676335 L150.5,26.4352347027881 L150.5,26.672747223702046 L150.5,26.851453301853628 L150.5,26.962391868272736 L150.5,27.0 " />
<path d="M150.5,27.0 L143.0,27.0 " />
<path d="M-130.5,30.977225575051662 L129.5,30.977225575051662 " />
<path d="M-151.5,30.5 L-151.5,30.374639560909117 L-151.5,30.004844339512097 L-151.5,29.40915741234015 L-151.5,28.61744900929367 L-151.5,27.66941869558779 L-151.5,26.61260466978157 L-151.5,25.5 L-151.5,24.38739533021843 L-151.5,23.33058130441221 L-151.5,22.38255099070633 L-151.5,21.59084258765985 L-151.5,20.995155660487903 L-151.5,20.625360439090883 L-151.5,20.500000000000004 " />
<path d="M-151.5,20.5 L-151.5,20.625360439090883 L-151.5,20.995155660487903 L-151.5,21.59084258765985 L-151.5,22.38255099070633 L-151.5,23.33058130441221 L-151.5,24.38739533021843 L-151.5,25.5 L-151.5,26.61260466978157 L-151.5,27.66941869558779 L-151.5,28.617449009293665 L-151.5,29.40915741234015 L-151.5,30.004844339512097 L-151.5,30.374639560909117 L-151.5,30.499999999999996 " />
<path d="M-151.5,30.5 L-155.5,30.5 " />
<path d="M-130.5,20.601020514433642 L129.5,20.601020514433642 " />
<path d="M-130.5,20.5 L129.5,20.5 " />
<path d="M129.5,20.5 L-130.5,20.5 " />
<path d="M-130.5,24.085786437626904 L129.5,24.085786437626904 " />
<path d="M-130.5,26.914213562373096 L129.5,26.914213562373096 " />
<path d="M-144.0,27.0 L-151.5,27.0 " />
<path d="M-151.5,27.0 L-151.5,26.962391868272736 L-151.5,26.851453301853628 L-151.5,26.672747223702046 L-151.5,26.4352347027881 L-151.5,26.15082560867634 L-151.5,25.833781400934473 L-151.5,25.5 L-151.5,25.16621859906553 L-151.5,24.849174391323665 L-151.5,24.5647652972119 L-151.5,24.327252776297954 L-151.5,24.148546698146372 L-151.5,24.037608131727264 L-151.5,24.0 " />
<path d="M-151.5,24.0 L-151.5,24.037608131727264 L-151.5,24.148546698146372 L-151.5,24.327252776297954 L-151.5,24.5647652972119 L-151.5,24.84917439132366 L-151.5,25.166218599065527 L-151.5,25.5 L-151.5,25.83378140093447 L-151.5,26.150825608676335 L-151.5,26.4352347027881 L-151.5,26.672747223702046 L-151.5,26.851453301853628 L-151.5,26.962391868272736 L-151.5,27.0 " />
<path d="M130.5,20.0 L142.0,20.0 " />
<path d="M-143.0,20.0 L-131.5,20.0 " />
<path d="M143.0,31.0 L152.5,31.0 " />
<path d="M150.5,20.5 L155.5,20.5 " />
<path d="M136.75,23.15 L142.0,23.15 " />
<path d="M130.5,24.0 L136.75,24.0 " />
<path d="M-136.75,24.0 L-131.5,24.0 " />
<path d="M-136.75,23.15 L-143.0,23.15 " />
<path d="M-152.5,31.0 L-144.0,31.0 " />
<path d="M143.0,24.0 L150.5,24.0 " />
<path d="M-155.5,20.5 L-151.5,20.5 " />
<path d="M-151.5,24.0 L-144.0,24.0 " />
</g>
<!-- solid lines -->
<g stroke="rgb(0,0,0)" fill="none">
<path d="M-155.5,-77.5 L-155.5,15.0 " />
<path d="M-155.5,19.800000000000004 L-155.5,15.000000000000002 " />
<path d="M-155.5,-77.5 L-155.49605345685654,-77.62558103905863 L-155.48422940262896,-77.75066646712861 L-155.46457450145738,-77.87476262917144 L-155.43716632225727,-77.9973797743297 L-155.4021130325903,-78.11803398874989 L-155.3595529717765,-78.23624910536935 L-155.30965410493204,-78.35155858313014 L-155.25261336008774,-78.46350734820342 L-155.18865585100403,-78.57165358995799 L-155.1180339887499,-78.67557050458494 L-155.04102648555158,-78.77484797949737 L-154.95793725484282,-78.86909421185737 L-154.86909421185737,-78.95793725484282 L-154.7748479794974,-79.04102648555157 L-154.67557050458495,-79.11803398874989 L-154.571653589958,-79.18865585100403 L-154.46350734820345,-79.25261336008772 L-154.35155858313016,-79.30965410493204 L-154.23624910536938,-79.3595529717765 L-154.11803398874991,-79.40211303259031 L-153.99737977432972,-79.43716632225726 L-153.87476262917147,-79.46457450145738 L-153.75066646712864,-79.48422940262896 L-153.62558103905863,-79.49605345685654 L-153.50000000000003,-79.5 " />
<path d="M-153.5,-79.5 L153.5,-79.5 " />
<path d="M155.5,-77.5 L155.49605345685654,-77.62558103905863 L155.48422940262896,-77.75066646712861 L155.46457450145738,-77.87476262917144 L155.43716632225727,-77.9973797743297 L155.4021130325903,-78.11803398874989 L155.3595529717765,-78.23624910536935 L155.30965410493204,-78.35155858313014 L155.25261336008774,-78.46350734820342 L155.18865585100403,-78.57165358995799 L155.1180339887499,-78.67557050458494 L155.04102648555158,-78.77484797949737 L154.95793725484282,-78.86909421185737 L154.86909421185737,-78.95793725484282 L154.7748479794974,-79.04102648555157 L154.67557050458495,-79.11803398874989 L154.571653589958,-79.18865585100403 L154.46350734820345,-79.25261336008772 L154.35155858313016,-79.30965410493204 L154.23624910536938,-79.3595529717765 L154.11803398874991,-79.40211303259031 L153.99737977432972,-79.43716632225726 L153.87476262917147,-79.46457450145738 L153.75066646712864,-79.48422940262896 L153.62558103905863,-79.49605345685654 L153.50000000000003,-79.5 " />
<path d="M155.5,-77.5 L155.5,15.0 " />
<path d="M155.5,15.0 L142.0,15.0 " />
<path d="M142.0,15.0 L142.0,25.5 " />
<path d="M142.0,25.5 L130.5,25.5 " />
<path d="M130.5,25.5 L130.5,15.0 " />
<path d="M130.5,15.0 L-131.5,15.0 " />
<path d="M-131.5,15.0 L-131.5,25.5 " />
<path d="M-131.5,25.5 L-143.0,25.5 " />
<path d="M-143.0,25.5 L-143.0,15.0 " />
<path d="M-143.0,15.0 L-155.5,15.0 " />
<path d="M-152.5,19.800000000000004 L-152.5,17.618181818181817 " />
<path d="M-143.0,17.618181818181824 L-152.5,17.618181818181824 " />
<path d="M-152.5,19.8 L-155.5,19.8 " />
<path d="M155.5,19.8 L155.5,15.0 " />
<path d="M155.5,20.0 L155.5,19.8 " />
<path d="M155.5,25.5 L155.5,24.88419538143181 L155.5,24.276134863240273 L155.5,23.68346515924658 L155.5,23.113639434853432 L155.5,22.573823579165648 L155.5,22.070806089776966 L155.5,21.61091270347399 L155.5,21.199926846425836 L155.5,20.843016904244436 L155.5,20.544671226536696 L155.5,20.30864168330398 L155.5,20.13789648299997 L155.5,20.034582845587167 L155.5,20.0 " />
<path d="M155.5,25.5 L155.5,77.5 " />
<path d="M152.5,17.618181818181824 L142.0,17.618181818181824 " />
<path d="M152.5,19.800000000000004 L152.5,17.618181818181817 " />
<path d="M155.5,19.8 L152.5,19.8 " />
<path d="M142.0,25.5 L142.0,26.115804618568195 L142.0,26.723865136759727 L142.0,27.31653484075342 L142.0,27.886360565146568 L142.0,28.426176420834352 L142.0,28.929193910223034 L142.0,29.38908729652601 L142.0,29.800073153574164 L142.0,30.156983095755564 L142.0,30.455328773463304 L142.0,30.69135831669602 L142.0,30.86210351700003 L142.0,30.965417154412833 L142.0,31.0 " />
<path d="M130.5,25.5 L130.5,26.115804618568195 L130.5,26.723865136759727 L130.5,27.31653484075342 L130.5,27.886360565146568 L130.5,28.426176420834352 L130.5,28.929193910223034 L130.5,29.38908729652601 L130.5,29.800073153574164 L130.5,30.156983095755564 L130.5,30.455328773463304 L130.5,30.69135831669602 L130.5,30.86210351700003 L130.5,30.965417154412833 L130.5,31.0 " />
<path d="M130.5,17.727272727272737 L-131.5,17.727272727272737 " />
<path d="M-131.5,25.5 L-131.5,26.115804618568195 L-131.5,26.723865136759727 L-131.5,27.31653484075342 L-131.5,27.886360565146568 L-131.5,28.426176420834352 L-131.5,28.929193910223034 L-131.5,29.38908729652601 L-131.5,29.800073153574164 L-131.5,30.156983095755564 L-131.5,30.455328773463304 L-131.5,30.69135831669602 L-131.5,30.86210351700003 L-131.5,30.965417154412833 L-131.5,31.0 " />
<path d="M-143.0,25.5 L-143.0,26.115804618568195 L-143.0,26.723865136759727 L-143.0,27.31653484075342 L-143.0,27.886360565146568 L-143.0,28.426176420834352 L-143.0,28.929193910223034 L-143.0,29.38908729652601 L-143.0,29.800073153574164 L-143.0,30.156983095755564 L-143.0,30.455328773463304 L-143.0,30.69135831669602 L-143.0,30.86210351700003 L-143.0,30.965417154412833 L-143.0,31.0 " />
<path d="M152.5,20.0 L152.5,19.799999999999997 " />
<path d="M155.5,20.0 L152.5,20.0 " />
<path d="M155.5,25.5 L143.0,25.5 " />
<path d="M143.0,25.5 L143.0,24.884195381431805 L143.0,24.27613486324027 L143.0,23.68346515924658 L143.0,23.11363943485343 L143.0,22.573823579165648 L143.0,22.070806089776966 L143.0,21.61091270347399 L143.0,21.199926846425836 L143.0,20.843016904244436 L143.0,20.544671226536693 L143.0,20.30864168330398 L143.0,20.13789648299997 L143.0,20.034582845587167 L143.0,20.0 " />
<path d="M-155.5,25.5 L-155.5,77.5 " />
<path d="M-155.5,77.5 L-155.49605345685654,77.62558103905863 L-155.48422940262896,77.75066646712861 L-155.46457450145738,77.87476262917144 L-155.43716632225727,77.99737977432972 L-155.4021130325903,78.1180339887499 L-155.3595529717765,78.23624910536935 L-155.30965410493204,78.35155858313014 L-155.25261336008774,78.46350734820344 L-155.18865585100403,78.57165358995799 L-155.1180339887499,78.67557050458495 L-155.04102648555158,78.77484797949738 L-154.95793725484282,78.86909421185737 L-154.86909421185737,78.95793725484282 L-154.77484797949737,79.04102648555158 L-154.67557050458495,79.1180339887499 L-154.571653589958,79.18865585100403 L-154.46350734820342,79.25261336008772 L-154.35155858313016,79.30965410493204 L-154.23624910536935,79.35955297177651 L-154.1180339887499,79.40211303259031 L-153.99737977432972,79.43716632225727 L-153.87476262917144,79.46457450145738 L-153.7506664671286,79.48422940262896 L-153.62558103905863,79.49605345685654 L-153.5,79.5 " />
<path d="M-153.5,79.5 L153.5,79.5 " />
<path d="M155.5,77.5 L155.49605345685654,77.62558103905863 L155.48422940262896,77.75066646712861 L155.46457450145738,77.87476262917144 L155.43716632225727,77.99737977432972 L155.4021130325903,78.1180339887499 L155.3595529717765,78.23624910536935 L155.30965410493204,78.35155858313014 L155.25261336008774,78.46350734820344 L155.18865585100403,78.57165358995799 L155.1180339887499,78.67557050458495 L155.04102648555158,78.77484797949738 L154.95793725484282,78.86909421185737 L154.86909421185737,78.95793725484282 L154.77484797949737,79.04102648555158 L154.67557050458495,79.1180339887499 L154.571653589958,79.18865585100403 L154.46350734820342,79.25261336008772 L154.35155858313016,79.30965410493204 L154.23624910536935,79.35955297177651 L154.1180339887499,79.40211303259031 L153.99737977432972,79.43716632225727 L153.87476262917144,79.46457450145738 L153.7506664671286,79.48422940262896 L153.62558103905863,79.49605345685654 L153.5,79.5 " />
<path d="M143.0,36.2 L143.0,25.5 " />
<path d="M143.0,36.2 L129.5,36.2 " />
<path d="M129.5,20.5 L129.5,36.2 " />
<path d="M129.5,20.5 L-130.5,20.5 " />
<path d="M-130.5,36.2 L-130.5,20.5 " />
<path d="M-130.5,36.2 L-144.0,36.2 " />
<path d="M-144.0,25.5 L-144.0,36.2 " />
<path d="M-144.0,25.5 L-155.5,25.5 " />
<path d="M-130.5,33.47272727272728 L-144.0,33.47272727272728 " />
<path d="M143.0,33.47272727272728 L129.5,33.47272727272728 " />
<path d="M-155.5,25.5 L-155.5,24.88419538143181 L-155.5,24.276134863240273 L-155.5,23.68346515924658 L-155.5,23.113639434853432 L-155.5,22.573823579165648 L-155.5,22.070806089776966 L-155.5,21.61091270347399 L-155.5,21.199926846425836 L-155.5,20.843016904244436 L-155.5,20.544671226536696 L-155.5,20.30864168330398 L-155.5,20.13789648299997 L-155.5,20.034582845587167 L-155.5,20.0 " />
<path d="M-144.0,25.5 L-144.0,24.884195381431805 L-144.0,24.27613486324027 L-144.0,23.68346515924658 L-144.0,23.11363943485343 L-144.0,22.573823579165648 L-144.0,22.070806089776966 L-144.0,21.61091270347399 L-144.0,21.199926846425836 L-144.0,20.843016904244436 L-144.0,20.544671226536693 L-144.0,20.30864168330398 L-144.0,20.13789648299997 L-144.0,20.034582845587167 L-144.0,20.0 " />
<path d="M-152.5,20.0 L-155.5,20.0 " />
<path d="M130.5,31.0 L142.0,31.0 " />
<path d="M-143.0,31.0 L-131.5,31.0 " />
<path d="M143.0,20.0 L152.5,20.0 " />
<path d="M-152.5,20.0 L-144.0,20.0 " />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 38 KiB

103
notebook_nueva/keyboard.py Normal file
View File

@@ -0,0 +1,103 @@
import cadquery as cq
import math
# Size of the kbd board
kbd_height = 95.5
kbd_width = 305
back_thickness = 19
front_thickness = 12
# Pythagoras
actual_height = (kbd_height**2 - (back_thickness - front_thickness) ** 2) ** 0.5
kbd_angle = math.acos(actual_height / kbd_height) * 180 / math.pi
kbd_pillar_positions = [
(19, 16),
(142.5, 25.5),
(kbd_width - 20, 16),
(23.5, 79.5),
(145.5, 82.5),
(kbd_width - 19, 79.5),
]
elements = [
# Shorter pillars
{
"x": 0,
"y": 0,
"z": 5.5,
"shape": cq.Sketch().push(kbd_pillar_positions).circle(5, mode="a"),
},
# Taller pillars with holes for self-tapping screws
{
"x": 0,
"y": 0,
"z": 2.5,
"shape": (
cq.Sketch()
.push(kbd_pillar_positions)
.circle(2.4, mode="a")
.circle(1.1, mode="s")
),
},
]
def add(
*,
model,
width,
height,
thickness,
offset_x,
offset_y,
bottom_face,
back_face,
shell_t
):
# This one is special, it creates angled things and cuts off the
# case, so ... it's going to do weird stuff
if bottom_face:
model = (
model.faces(bottom_face)
.workplane(centerOption="CenterOfBoundBox", offset=-front_thickness)
.center(
-width / 2,
height / 2,
)
.transformed(rotate=cq.Vector(kbd_angle, 0, 0))
.tag("kbd_sloped")
)
for element in elements:
model = (
model.workplaneFromTagged("kbd_sloped")
.center(offset_x + element["x"], -offset_y - element["y"])
.workplane(offset=element["z"])
.placeSketch(element["shape"])
.extrude(100)
)
model = (
model.workplaneFromTagged("mid_height")
.transformed(offset=cq.Vector(0, 0, -thickness / 2))
.split(keepTop=True)
.faces(">X")
.workplane(centerOption="CenterOfBoundBox")
.center(-height / 2, -thickness / 2)
.placeSketch(
cq.Sketch().polygon(
[
[0, front_thickness],
[shell_t, front_thickness],
[actual_height + shell_t, back_thickness],
[actual_height + shell_t, 1000],
[0, 1000],
[0, front_thickness],
]
)
)
.cutBlind(-1000)
)
return model

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

109
notebook_nueva/lid.py Normal file
View File

@@ -0,0 +1,109 @@
import cadquery as cq
from cadquery import exporters
from modelo import mounting_pillar_positions, shell_t, width
from utils import hex_vents, punch_hole, extrude_shape2
# Dimensions for countersunk M4 screws
m4_top = 9
m4_bottom = 4
lip_thickness = 1.5
# Position of pillar + shell_t + pillar "radius" + lip
height = max([y for _, y in mounting_pillar_positions]) + 6 + shell_t + lip_thickness
thickness = shell_t
front_lip = 8
def model():
# Create the basic shape of the case lid
model = (
cq.Workplane("XY")
# Hollow box
.box(width, height, thickness)
.edges("|Z and >Y")
.fillet(2)
)
# Make many holes
vent = hex_vents(size=6, width=width * 0.9, height=height * 0.9)[0]
model = punch_hole(
model=model,
face=">Z",
w=width,
h=height,
x_offset=0.05 * width,
y_offset=0.05 * height,
hole=vent,
depth=thickness,
)
# Add screw holes
for position in mounting_pillar_positions:
model = (
model.faces(">Z")
.workplane(centerOption="CenterOfBoundBox")
.center(-width / 2 + position[0], height / 2 - position[1] - shell_t)
.placeSketch(cq.Sketch().circle(m4_top / 2 + 1.5))
.extrude(-thickness)
.faces(">Z")
.workplane(centerOption="CenterOfBoundBox")
.center(-width / 2 + position[0], height / 2 - position[1] - shell_t)
.cskHole(m4_bottom, m4_top, 82, depth=None)
)
# Add front lip
model = (
model.faces(">Z")
.workplane(centerOption="CenterOfBoundBox")
.center(0, -height / 2 + lip_thickness / 2)
.placeSketch(cq.Sketch().trapezoid(width - 2 * shell_t, lip_thickness, 90))
.extrude(-front_lip - thickness)
)
return model
def decorative_cover():
# A decorative thingie to cover the ugly seam in the middle
model = cq.Workplane("XY").box(10, height, 1).edges("|Z").fillet(1)
vent = hex_vents(size=6, width=width * 0.9, height=height * 0.9, density=0.775)[0]
model = extrude_shape2(
model=model,
face=">Z",
w=width,
h=height,
x_offset=0.05 * width,
y_offset=0.05 * height,
hole=vent,
depth=3,
)
return model
if __name__ == "__main__":
model = model()
exporters.export(model, "lid.stl")
cover = decorative_cover()
exporters.export(cover, "lid_cover.stl")
exporters.export(
model,
"lid.svg",
opt={
"projectionDir": (0, 0, 1),
},
)
exporters.export(
model.faces(">X").workplane(offset=-width / 2).split(keepTop=True),
"right_side_lid.stl",
)
exporters.export(
model.faces(">X").workplane(offset=-width / 2).split(keepBottom=True),
"left_side_lid.stl",
)

BIN
notebook_nueva/lid.stl Normal file

Binary file not shown.

1269
notebook_nueva/lid.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Binary file not shown.

3529
notebook_nueva/model.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 952 KiB

View File

@@ -1,11 +1,14 @@
import cadquery as cq
from cadquery import exporters
from cq_warehouse.drafting import Draft
import audio_plug
import battery_holder
import hdmi_out
import keyboard
import screen_pillars
import usb_hub
import zero_holder as cpu_holder
from utils import extrude_shape, punch_hole
# Base for the notebook. Basically a kbd base that extends back
# as much as possible
@@ -16,7 +19,6 @@ 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
@@ -27,81 +29,37 @@ thickness = 27 + shell_t # 27 inside
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, -30),
(6, 6),
(6, 48),
(120, 6),
(170, 6),
(width - 6, 6),
(width - 6, 30),
(120, 48),
(170, 48),
]
screen_pillars.init(mounting_pillar_positions, thickness - shell_t)
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")
# Thing to "grab" the hub so it stays in place
# Distance from edge to center of USB plug
usb_offset = 48
# Offset for the USB port from back-left corner
# of the case to left side of the hub
usb_offset_x = width - audio_plug.item_w - usb_hub.item_w
# CPU holder position from back-left corner of the case
cpu_offset_x = 160
cpu_offset_y = 25
cpu_offset_x = 177
cpu_offset_y = 2
# Battery holder position from back-left corner of the case
battery_offset_x = 22
battery_offset_y = 8
# Offset for the USB port from back-right corner of the case
usb_offset = 48
battery_offset_x = 15
battery_offset_y = 3
def model():
# Create the basic shape of the case bottom.
# Currently also adds keyboard stuff and things to connect
# to the screen case, but that should be refactored
# out (FIXME)
model = (
cq.Workplane("XY")
.workplane(offset=thickness / 2)
@@ -112,129 +70,103 @@ def model():
.fillet(2)
.faces(">Z")
.shell(-shell_t)
# Slanted mounting pillars on the kbd top
.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)
)
# Now the basic box shape is in place, start adding things
# and cutting holes.
# Holes in the back of the case for battery holder
for hole in battery_holder.holes:
model = punch_hole(
model = usb_hub.add(
model=model,
face=">Y",
w=width,
h=thickness,
x_offset=width - battery_offset_x,
y_offset=shell_t,
hole=hole,
depth=shell_t,
)
# Hole for USB hub in the back
for hole in usb_hub.holes:
model = punch_hole(
model=model,
face=">Y",
w=width,
h=thickness,
x_offset=usb_offset + shell_t + usb_hub.holes[0]["width"] / 2,
y_offset=shell_t,
hole=hole,
depth=shell_t,
)
# USB hub holder
for element in usb_hub.elements:
model = extrude_shape(
model=model,
face="<Z",
w=width,
h=height,
x_offset=width - usb_offset - shell_t - +usb_hub.holes[0]["width"] / 2,
y_offset=shell_t + element["y"],
shape=element["shape"],
height=-(element["height"] + shell_t),
width=width,
height=height,
thickness=thickness,
bottom_face="<Z",
back_face=">Y",
offset_x=usb_offset_x,
offset_y=0,
shell_t=shell_t,
)
# Hole for audio in right side
for hole in audio_plug.holes:
model = punch_hole(
model = audio_plug.add(
model=model,
face=">X",
w=height,
h=thickness,
x_offset=height - shell_t - 34.5 - audio_plug.holes[0]["width"] / 2,
y_offset=shell_t,
hole=hole,
depth=shell_t,
width=width,
height=height,
thickness=thickness,
offset_x=width - audio_plug.item_w,
offset_y=40,
bottom_face="<Z",
back_face=">X",
shell_t=shell_t,
)
# Battery holder stands and pogo pin holder
for element in battery_holder.elements:
model = extrude_shape(
# Hole for HDMI out in the back
model = hdmi_out.add(
model=model,
face="<Z",
w=width,
h=height,
x_offset=battery_offset_x + element["x"],
y_offset=shell_t + battery_offset_y + element["y"],
shape=element["shape"],
height=-(element["height"] + shell_t),
width=width,
height=height,
thickness=thickness,
offset_x=138,
offset_y=0,
bottom_face=None,
back_face=">Y",
shell_t=shell_t,
)
# CPU holder stands
for element in cpu_holder.elements:
model = extrude_shape(
model = cpu_holder.add(
model=model,
face="<Z",
w=width,
h=height,
x_offset=cpu_offset_x + element["x"],
y_offset=shell_t + cpu_offset_y + element["y"],
shape=element["shape"],
height=-(element["height"] + shell_t),
width=width,
height=height,
thickness=thickness,
offset_x=cpu_offset_x,
offset_y=cpu_offset_y,
bottom_face="<Z",
back_face=None, # Not exposing the holes
shell_t=shell_t,
)
# This adds all the holes and extrusions for the battery system
model = battery_holder.add(
model=model,
width=width,
height=height,
thickness=thickness,
offset_x=battery_offset_x,
offset_y=battery_offset_y,
bottom_face="<Z",
back_face=">Y",
shell_t=shell_t,
)
model = screen_pillars.add(
model=model,
width=width,
height=height,
thickness=thickness,
offset_x=0,
offset_y=0,
bottom_face="<Z",
back_face=None,
shell_t=shell_t,
)
model = keyboard.add(
model=model,
width=width,
height=height,
thickness=thickness,
bottom_face="<Z",
back_face=None,
offset_x=shell_t,
offset_y=kbd_height + shell_t,
shell_t=shell_t,
)
return model
if __name__ == "__main__":
model = model()
left_cutout = cq.Sketch().polygon(
[(0, 0), (width / 2, 0), (width / 2, -height), (0, -height), (0, 0)],
@@ -242,8 +174,7 @@ if __name__ == "__main__":
)
right_side = (
model()
.faces("<Z")
model.faces("<Z")
.workplaneFromTagged("mid_height")
.transformed(offset=cq.Vector(0, 0, -thickness / 2))
.center(-width / 2, height / 2)
@@ -265,8 +196,7 @@ if __name__ == "__main__":
)
left_side = (
model()
.faces("<Z")
model.faces("<Z")
.workplaneFromTagged("mid_height")
.transformed(offset=cq.Vector(0, 0, -thickness / 2))
.center(-width / 2, height / 2)
@@ -275,4 +205,38 @@ if __name__ == "__main__":
)
exporters.export(left_side, "left_side.stl")
exporters.export(model(), "model.stl")
draft = Draft(decimal_precision=1)
dimensions = []
dimensions.append(
draft.extension_line(
object_edge=[
cq.Vertex.makeVertex(-width / 2, -height / 2, 0),
cq.Vertex.makeVertex(width / 2, -height / 2, 0),
],
offset=10.0,
)
)
dimensions.append(
draft.extension_line(
object_edge=[
cq.Vertex.makeVertex(width / 2, -height / 2, 0),
cq.Vertex.makeVertex(width / 2, height / 2, 0),
],
offset=10.0,
)
)
exporters.export(model, "model.stl")
for d in dimensions[1:]:
dimensions[0].add(d.toCompound())
dimensions[0].add(model)
exporters.export(
dimensions[0].toCompound(),
"model.svg",
opt={
"projectionDir": (0, 0, 1),
"strokeWidth": 0.3,
},
)

Binary file not shown.

View File

@@ -0,0 +1,2 @@
cadquery
git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -42,6 +42,9 @@ board_cutout = cq.Sketch().trapezoid(
kbd_cable_hole = cq.Sketch().trapezoid(20, 9, 90, mode="a").vertices().fillet(1)
# y needs to be inverted because this is the top side, adn there's 2 pillars we don't use
mounting_pillar_positions = [(x, -y) for x, y in mounting_pillar_positions[:-2]]
mounting_pillars = (
cq.Sketch()
.push(mounting_pillar_positions)
@@ -119,7 +122,7 @@ def model():
.cutBlind(-6)
.workplaneFromTagged("mid_height")
.workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox")
.center(-width / 2, height_bottom - height / 2)
.center(-width / 2, height_bottom - height / 2 - shell_t)
.placeSketch(mounting_pillars)
.extrude(10)
# Fillet the front edge of the screen case so it looks softer
@@ -129,7 +132,6 @@ def model():
if __name__ == "__main__":
print("Exporting")
exporters.export(model(), "screen_mount.stl")

Binary file not shown.

View File

@@ -0,0 +1,75 @@
from utils import extrude_shape, punch_hole
import cadquery as cq
elements = None
bottom_holes = None # Not really vents FIXME
def init(positions, thickness):
"""Because these need to match in multiple models, we create the
elemments dynamically"""
global elements, bottom_holes
elements = [
{
"x": 0,
"y": 0,
"shape": cq.Sketch().push(positions).trapezoid(12, 12, 90, mode="a"),
"height": thickness,
}
]
bottom_holes = [
{
"x": 0,
"y": 0,
"shape": cq.Sketch().push(positions).circle(3, mode="a"),
"depth": thickness - 13, # (screw thread length - threaded insert depth)
},
{
"x": 0,
"y": 0,
"shape": cq.Sketch().push(positions).circle(1.8, mode="a"),
"depth": 100,
},
]
def add(
*,
model,
width,
height,
thickness,
offset_x,
offset_y,
bottom_face,
back_face,
shell_t
):
if bottom_face:
# Mounting pillars
for element in elements:
model = extrude_shape(
model=model,
face=bottom_face,
w=width,
h=height,
x_offset=offset_x,
y_offset=shell_t + offset_y,
element=element,
height=-(element["height"] + shell_t),
)
# Screw holes
for hole in bottom_holes:
model = punch_hole(
model=model,
face=bottom_face,
w=width,
h=height,
x_offset=offset_x,
y_offset=shell_t + offset_y,
hole=hole,
depth=hole["depth"],
)
return model

View File

@@ -1,33 +1,101 @@
import cadquery as cq
from utils import punch_hole, extrude_shape
# Measurements for my USB hub, YMMV
# The hole is for a USB-A plug, y is measured in the hub
# (from the bottom face to middle of the hole)
# Consumers should set proper offsets for the hole
item_w = 17
item_h = 93
holes = [
# USB-A port
{
"x": 0,
"x": -item_w / 2,
"y": 4,
"height": 5.5,
"width": 13,
"fillet": 2,
"shape": cq.Sketch().trapezoid(13, 5, 90, mode="a").vertices().fillet(1),
},
]
elements = [
# Thing to grab the hub
{
"x": 0,
"x": item_w / 2,
"y": 5,
"shape": (
cq.Sketch()
.trapezoid(22, 10, 90, mode="a")
.trapezoid(17, 10, 90, mode="s")
.clean()
cq.Sketch().trapezoid(22, 10, 90, mode="a").trapezoid(17, 10, 90, mode="s")
),
"height": 8,
}
},
{
"x": item_w / 2 + 5,
"y": item_h - 3,
"shape": (cq.Sketch().circle(2.5, mode="a")),
"height": 8,
},
{
"x": item_w / 2 - 5,
"y": item_h - 3,
"shape": (cq.Sketch().circle(2.5, mode="a")),
"height": 8,
},
# Outline
{
"x": item_w / 2,
"y": item_h / 2,
"shape": (
cq.Sketch()
.trapezoid(item_w, item_h, 90, mode="a")
.trapezoid(item_w - 2, item_h - 2, 90, mode="s")
.vertices()
.fillet(3)
),
"height": 0.2,
},
]
def add(
*,
model,
width,
height,
thickness,
offset_x,
offset_y,
bottom_face,
back_face,
shell_t
):
# USB Hub extrusions
if bottom_face:
for element in elements:
model = extrude_shape(
model=model,
face=bottom_face,
w=width,
h=height,
x_offset=offset_x,
y_offset=shell_t + offset_y,
element=element,
height=-(element["height"] + shell_t),
)
# Holes
if back_face:
for hole in holes:
model = punch_hole(
model=model,
face=back_face,
w=width,
h=thickness,
x_offset=width - offset_x,
y_offset=shell_t,
hole=hole,
depth=shell_t,
)
return model

View File

@@ -1,12 +1,13 @@
import cadquery as cq
from math import floor
# TODO make API of extrude_shape and punch_hole more consistent
def extrude_shape(*, model, face, w, h, x_offset, y_offset, shape, height):
def extrude_shape(*, model, face, w, h, x_offset, y_offset, element, height):
return (
model.faces(face)
.workplane(centerOption="CenterOfBoundBox")
.center(-w / 2 + x_offset, -h / 2 + y_offset)
.placeSketch(shape)
.center(-w / 2 + x_offset + element["x"], -h / 2 + y_offset + element["y"])
.placeSketch(element["shape"])
.extrude(height)
)
@@ -16,11 +17,57 @@ def punch_hole(*, model, face, w, h, x_offset, y_offset, hole, depth):
model.faces(face)
.workplane(centerOption="CenterOfBoundBox")
.center(-w / 2 + x_offset + hole["x"], -h / 2 + y_offset + hole["y"])
.placeSketch(
cq.Sketch()
.trapezoid(hole["width"], hole["height"], 90, mode="a")
.vertices()
.fillet(hole["fillet"])
)
.placeSketch(hole["shape"])
.cutBlind(-depth)
)
def extrude_shape2(*, model, face, w, h, x_offset, y_offset, hole, depth):
return (
model.faces(face)
.workplane(centerOption="CenterOfBoundBox")
.center(-w / 2 + x_offset + hole["x"], -h / 2 + y_offset + hole["y"])
.placeSketch(hole["shape"])
.extrude(-depth)
)
def hex_vents(*, size, width, height, density=0.85):
# size is radius of the hexagon
# Information about how this works:
# https://www.redblobgames.com/grids/hexagons/
x_step = size * (3**0.5)
y_step = size * 3 / 2
x_count = floor(width / x_step) - 1
if height > 4 * size:
y_count = floor((height - 2 * size) / (1.5 * size))
else:
y_count = 1
x_size = (x_count + 0.5) * x_step # Assumes at least 2 rows
y_size = 2 * size + 1.5 * size * (y_count - 1)
x_offset = (width - x_size) / 2 + 0.5 * x_step
y_offset = (height - y_size) / 2 + size
vent_positions = []
for x in range(0, x_count):
for y in range(0, y_count):
vent_positions.append(
(
(x + (y % 2) / 2) * x_step + x_offset,
y * y_step + y_offset,
)
)
vents = [
{
"x": 0,
"y": 0,
"shape": cq.Sketch().push(vent_positions).regularPolygon(size * density, 6),
}
]
return vents

View File

@@ -1,10 +1,18 @@
import cadquery as cq
positions = [(0, 0), (0, 23), (58, 23), (58, 0)]
from utils import extrude_shape, punch_hole, hex_vents
stands = cq.Sketch().push(positions).circle(3, mode="a").circle(2.65 / 2, mode="s")
width = 65
height = 30
pillar_height = 7
stand_positions = [(3.5, 3.5), (3.5, 26.5), (61.5, 26.5), (61.5, 3.5)]
stands = (
cq.Sketch().push(stand_positions).circle(3, mode="a").circle(2.65 / 2, mode="s")
)
elements = [
# CPU holder stands
{
@@ -12,5 +20,91 @@ elements = [
"y": 0,
"shape": stands,
"height": pillar_height,
},
{
"x": 0,
"y": 0,
"shape": cq.Sketch().push(stand_positions).circle(5),
"height": 0,
},
# Perimeter
{
"x": width / 2,
"y": height / 2,
"shape": (
cq.Sketch()
.trapezoid(width, height, 90, mode="a")
.trapezoid(width - 2, height - 2, 90, mode="s")
.vertices()
.fillet(3)
),
"height": 0.2,
},
]
vents = hex_vents(size=3, width=width, height=height)
holes = [
# One hole for everything TODO: improve
{
"x": -width / 2,
"y": 1 + pillar_height,
"shape": cq.Sketch().trapezoid(50, 6, 90, mode="a").vertices().fillet(1),
}
]
def add(
*,
model,
width,
height,
thickness,
offset_x,
offset_y,
bottom_face,
back_face,
shell_t
):
if bottom_face:
# Vents
for vent in vents:
model = punch_hole(
model=model,
face=bottom_face,
w=width,
h=height,
x_offset=offset_x + vent["x"],
y_offset=shell_t + offset_y + vent["y"],
hole=vent,
depth=shell_t,
)
# CPU holder extrusions
for element in elements:
model = extrude_shape(
model=model,
face=bottom_face,
w=width,
h=height,
x_offset=offset_x,
y_offset=shell_t + offset_y,
element=element,
height=-(element["height"] + shell_t),
)
# Holes
if back_face:
for hole in holes:
model = punch_hole(
model=model,
face=back_face,
w=width,
h=thickness,
x_offset=width - offset_x,
y_offset=shell_t,
hole=hole,
depth=shell_t,
)
return model