Compare commits
66 Commits
resin
...
682fb38852
Author | SHA1 | Date | |
---|---|---|---|
682fb38852 | |||
9bff855ad7 | |||
3203c4c860 | |||
3b1722f5ff | |||
6e594b0b86 | |||
74a17e04ec | |||
0a2bba4067 | |||
68b407f5b5 | |||
f5bf232f0e | |||
bf302deab1 | |||
c49f9c6348 | |||
68849b5aca | |||
2553525623 | |||
61a723c713 | |||
d1e7b589c8 | |||
cdf2e33f64 | |||
9bdabfdd98 | |||
9904a412a3 | |||
d64654f7b1 | |||
4f1b09ab95 | |||
d9749bc38e | |||
7aeb2cc0c1 | |||
4037ac4a1a | |||
b3f813eaf1 | |||
576edc839b | |||
120638f50f | |||
d9f7d7f7a9 | |||
0dd1c858e1 | |||
6ea773109b | |||
b5deb70cbd | |||
7fbf323e45 | |||
84f5978454 | |||
a894e8319c | |||
af3e1824f4 | |||
67db8765d8 | |||
e307750c8d | |||
804f012b19 | |||
2baebe8895 | |||
8d962eba14 | |||
d4e309f081 | |||
424396a266 | |||
9840e6ad70 | |||
ccb3220b1b | |||
31630ab1b0 | |||
64db220a46 | |||
4624a2531f | |||
be6245aa3b | |||
4b0cf1cfa0 | |||
6eb3eba4f2 | |||
dbaa9832f8 | |||
76af86ac70 | |||
6cbd7e2b15 | |||
459021ff83 | |||
4f2c1e35f6 | |||
1e89b41995 | |||
37d96b0e4f | |||
b85ba1f3e1 | |||
a6d3f32797 | |||
92291013c6 | |||
d048e19cec | |||
bebc4d5729 | |||
26dc83baf9 | |||
29e2464bef | |||
4386b1c017 | |||
8058118491 | |||
4279547773 |
50
notebook_nueva/audio_plug.py
Normal file
50
notebook_nueva/audio_plug.py
Normal file
@ -0,0 +1,50 @@
|
||||
# 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 = [
|
||||
# 2-jack plug
|
||||
{
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"shape": cq.Sketch().trapezoid(17, 5, 90, mode="a").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,
|
||||
# FIXME: This is weird because it's the RIGHT side,
|
||||
# So it's height instead of w
|
||||
# need to work on making these coherent
|
||||
w=height,
|
||||
h=thickness,
|
||||
x_offset=offset_x - 17 / 2,
|
||||
y_offset=shell_t,
|
||||
hole=hole,
|
||||
depth=shell_t,
|
||||
)
|
||||
|
||||
return model
|
151
notebook_nueva/battery_holder.py
Normal file
151
notebook_nueva/battery_holder.py
Normal file
@ -0,0 +1,151 @@
|
||||
import cadquery as cq
|
||||
|
||||
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
|
||||
pin_positions = [(3.5, 0), (4 * 2.54 + 3.5, 0)]
|
||||
pin_holder_width = 25
|
||||
pin_holder_height = 15
|
||||
pin_holder = (
|
||||
cq.Sketch()
|
||||
.polygon(
|
||||
[
|
||||
(0.5, 0),
|
||||
(pin_holder_width, 0),
|
||||
(pin_holder_width, pin_holder_height),
|
||||
(0, pin_holder_height),
|
||||
(0.5, 0),
|
||||
],
|
||||
mode="a",
|
||||
)
|
||||
.push(pin_positions)
|
||||
.polygon(
|
||||
[(0, 0), (2.6, 0), (2.6, pin_holder_height), (0, pin_holder_height), (0, 0)],
|
||||
mode="s",
|
||||
)
|
||||
)
|
||||
|
||||
elements = [
|
||||
# Battery holder stands
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"shape": stands,
|
||||
"height": pillar_height,
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"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
|
||||
# All distances are measured to the CENTER of the hole
|
||||
holes = [
|
||||
# Power inlet
|
||||
{
|
||||
"x": -15,
|
||||
"y": -1 + pillar_height,
|
||||
"shape": cq.Sketch().trapezoid(12, 6.5, 90, mode="a").vertices().fillet(1),
|
||||
},
|
||||
# Power button
|
||||
{
|
||||
"x": -67,
|
||||
"y": 5.5 + pillar_height,
|
||||
"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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1,28 +0,0 @@
|
||||
import cadquery2 as cq
|
||||
from cadquery2 import exporters
|
||||
|
||||
lower_stands = (
|
||||
cq.Sketch().push([(0, 0), (58, 0), (58, 23), (0, 23)]).circle(3, mode="a")
|
||||
)
|
||||
|
||||
higher_stands = (
|
||||
cq.Sketch().push([(0, 0), (58, 0), (58, 23), (0, 23)]).circle(2.65 / 2, mode="a")
|
||||
)
|
||||
|
||||
model = (
|
||||
cq.Workplane("XY")
|
||||
.workplane()
|
||||
.box(75, 40, 2)
|
||||
.edges("+Z")
|
||||
.fillet(3)
|
||||
.faces(">Z")
|
||||
.workplane(centerOption="CenterOfBoundBox")
|
||||
.center(-29, -11.5)
|
||||
.placeSketch(lower_stands)
|
||||
.extrude(4)
|
||||
.workplane()
|
||||
.placeSketch(higher_stands)
|
||||
.extrude(9)
|
||||
)
|
||||
|
||||
exporters.export(model, "cpu_holder.stl")
|
File diff suppressed because it is too large
Load Diff
103
notebook_nueva/keyboard.py
Normal file
103
notebook_nueva/keyboard.py
Normal 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 = 17
|
||||
front_thickness = 10
|
||||
|
||||
# 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 = [
|
||||
(18.25, 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],
|
||||
[actual_height, back_thickness],
|
||||
[actual_height, 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.
@ -1,5 +1,12 @@
|
||||
import cadquery2 as cq
|
||||
from cadquery2 import exporters
|
||||
import cadquery as cq
|
||||
from cadquery import exporters
|
||||
|
||||
import audio_plug
|
||||
import battery_holder
|
||||
import usb_hub
|
||||
import zero_holder as cpu_holder
|
||||
import screen_pillars
|
||||
import keyboard
|
||||
|
||||
# Base for the notebook. Basically a kbd base that extends back
|
||||
# as much as possible
|
||||
@ -8,88 +15,54 @@ from cadquery2 import exporters
|
||||
shell_t = 3
|
||||
|
||||
# Size of the kbd board
|
||||
kbd_height = 98
|
||||
kbd_width = 286
|
||||
kbd_height = 95.5
|
||||
kbd_width = 305
|
||||
kbd_angle = 5
|
||||
|
||||
# Size of the whole object
|
||||
width = kbd_width + 2 * shell_t
|
||||
height = 164 # Max bed size
|
||||
thickness = 20 + shell_t # 20 inside
|
||||
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
|
||||
# mounting holes
|
||||
kbd_pillars = (
|
||||
cq.Sketch()
|
||||
.push(
|
||||
[
|
||||
(19, -16.5),
|
||||
(133, -16.5),
|
||||
(247.5, -16.5),
|
||||
(24, -86),
|
||||
(142.5, -91),
|
||||
(261.5, -86),
|
||||
]
|
||||
)
|
||||
.circle(6, mode="a")
|
||||
# Holes for M3 threaded inserts
|
||||
.circle(ti_radius, 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),
|
||||
(width - 6, -6),
|
||||
(width - 6, -40),
|
||||
(120, -6),
|
||||
(170, -6),
|
||||
(6, 6),
|
||||
(6, 52),
|
||||
(120, 6),
|
||||
(170, 6),
|
||||
(width - 6, 6),
|
||||
(width - 6, 30),
|
||||
(120, 52),
|
||||
(170, 52),
|
||||
]
|
||||
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")
|
||||
|
||||
battery_holder = (
|
||||
cq.Sketch()
|
||||
.polygon(
|
||||
[(-67, 5), (0, 5), (0, -12), (-67, -12), (-67, 5)],
|
||||
mode="a",
|
||||
)
|
||||
.trapezoid(83, 83, 90, mode="a")
|
||||
.trapezoid(80, 80, 90, mode="s")
|
||||
.polygon(
|
||||
[(-67, 3), (0, 3), (0, -10), (-67, -10), (-67, 3)],
|
||||
mode="s",
|
||||
)
|
||||
# Cutout for the
|
||||
.polygon(
|
||||
[(-67, 30), (0, 30), (0, 12), (-67, 12), (-67, 30)],
|
||||
mode="s",
|
||||
)
|
||||
)
|
||||
# Thing to "grab" the hub so it stays in place
|
||||
# Distance from left edge to center of USB plug
|
||||
usb_offset = width - 48
|
||||
|
||||
|
||||
power_in = cq.Sketch().circle(5, mode="a")
|
||||
usb_in = cq.Sketch().trapezoid(13, 5.5, 90, mode="a")
|
||||
switch_in = cq.Sketch().trapezoid(13.5, 8, 90, mode="a")
|
||||
# CPU holder position from back-left corner of the case
|
||||
cpu_offset_x = 180
|
||||
cpu_offset_y = 3
|
||||
|
||||
# Battery holder position from back-left corner of the case
|
||||
battery_offset_x = 15
|
||||
battery_offset_y = 3
|
||||
|
||||
# Motherboard mount
|
||||
# Offset for the USB port from back-right corner of the case
|
||||
usb_offset = 48
|
||||
|
||||
|
||||
def model():
|
||||
return (
|
||||
# Create the basic shape of the case bottom.
|
||||
model = (
|
||||
cq.Workplane("XY")
|
||||
.workplane(offset=thickness / 2)
|
||||
.tag("mid_height")
|
||||
@ -99,71 +72,99 @@ def model():
|
||||
.fillet(2)
|
||||
.faces(">Z")
|
||||
.shell(-shell_t)
|
||||
# Battery holder
|
||||
.workplaneFromTagged("mid_height")
|
||||
.center(-width / 2 + shell_t + 65, height / 2 - shell_t - 45)
|
||||
.placeSketch(battery_holder)
|
||||
.extrude(-height / 2)
|
||||
# Power cable inlet
|
||||
.faces("<X")
|
||||
.workplane(centerOption="CenterOfBoundBox")
|
||||
.center(-height / 2 + shell_t + 48.5, -3)
|
||||
.placeSketch(power_in)
|
||||
.cutBlind(-shell_t)
|
||||
# USB inlet
|
||||
.faces(">X")
|
||||
.workplane(centerOption="CenterOfBoundBox")
|
||||
.center(-height / 2 + shell_t + 50, -5)
|
||||
.placeSketch(usb_in)
|
||||
.cutBlind(-shell_t)
|
||||
# Hole for power switch
|
||||
.faces(">Y")
|
||||
.workplane(centerOption="CenterOfBoundBox")
|
||||
.center(0, 0)
|
||||
.placeSketch(switch_in)
|
||||
.cutBlind(-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))
|
||||
.tag("sloped")
|
||||
.placeSketch(kbd_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.
|
||||
|
||||
model = usb_hub.add(
|
||||
model=model,
|
||||
width=width,
|
||||
height=height,
|
||||
thickness=thickness,
|
||||
bottom_face="<Z",
|
||||
back_face=">Y",
|
||||
offset_x=width - usb_offset,
|
||||
offset_y=0,
|
||||
shell_t=shell_t,
|
||||
)
|
||||
|
||||
# Hole for audio in right side
|
||||
model = audio_plug.add(
|
||||
model=model,
|
||||
width=width,
|
||||
height=height,
|
||||
thickness=thickness,
|
||||
offset_x=111, # Offset from the front-left corner
|
||||
offset_y=0, # Offset from the bottom
|
||||
bottom_face=None,
|
||||
back_face=">X",
|
||||
shell_t=shell_t,
|
||||
)
|
||||
|
||||
model = cpu_holder.add(
|
||||
model=model,
|
||||
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), (160, 0), (160, -100), (135, -100), (135, -200), (0, -200), (0, 0)],
|
||||
[(0, 0), (width / 2, 0), (width / 2, -height), (0, -height), (0, 0)],
|
||||
mode="a",
|
||||
)
|
||||
|
||||
right_side = (
|
||||
model()
|
||||
.faces("<Z")
|
||||
model.faces("<Z")
|
||||
.workplaneFromTagged("mid_height")
|
||||
.transformed(offset=cq.Vector(0, 0, -thickness / 2))
|
||||
.center(-width / 2, height / 2)
|
||||
@ -175,20 +176,17 @@ if __name__ == "__main__":
|
||||
|
||||
right_cutout = cq.Sketch().polygon(
|
||||
[
|
||||
(160, 0),
|
||||
(width / 2, 0),
|
||||
(width, 0),
|
||||
(width, -height),
|
||||
(135, -height),
|
||||
(135, -100),
|
||||
(160, -100),
|
||||
(160, 0),
|
||||
(width / 2, -height),
|
||||
(width / 2, 0),
|
||||
],
|
||||
mode="a",
|
||||
)
|
||||
|
||||
left_side = (
|
||||
model()
|
||||
.faces("<Z")
|
||||
model.faces("<Z")
|
||||
.workplaneFromTagged("mid_height")
|
||||
.transformed(offset=cq.Vector(0, 0, -thickness / 2))
|
||||
.center(-width / 2, height / 2)
|
||||
@ -197,4 +195,4 @@ if __name__ == "__main__":
|
||||
)
|
||||
exporters.export(left_side, "left_side.stl")
|
||||
|
||||
exporters.export(model(), "model.stl")
|
||||
exporters.export(model, "model.stl")
|
||||
|
BIN
notebook_nueva/pogo_plug.stl
Normal file
BIN
notebook_nueva/pogo_plug.stl
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
import cadquery2 as cq
|
||||
from cadquery2 import exporters
|
||||
import cadquery as cq
|
||||
from cadquery import exporters
|
||||
|
||||
from modelo import (
|
||||
kbd_height,
|
||||
@ -10,17 +10,16 @@ from modelo import (
|
||||
ti_radius,
|
||||
)
|
||||
|
||||
ti_radius = 2.5
|
||||
|
||||
# Size of the whole object
|
||||
width = kbd_width + 2 * shell_t
|
||||
height = 59
|
||||
thickness = 62 # Will be shorter after construction
|
||||
height = 66
|
||||
height_bottom = 59
|
||||
thickness = 48 # Will be shorter after construction
|
||||
|
||||
# Visible screen size
|
||||
vis_w = 220
|
||||
vis_h = 58
|
||||
viewport_cutout = cq.Sketch().trapezoid(vis_w, vis_h, 90, mode="a")
|
||||
vis_w = 219
|
||||
vis_h = 55
|
||||
viewport_cutout = cq.Sketch().trapezoid(vis_w, vis_h, 90, mode="a").vertices().fillet(2)
|
||||
|
||||
# Whole screen size
|
||||
scr_w = 231
|
||||
@ -28,6 +27,9 @@ scr_h = 65
|
||||
scr_thickness = 5.5
|
||||
screen_cutout = cq.Sketch().trapezoid(scr_w, scr_h, 90, mode="a")
|
||||
|
||||
# Screen angle
|
||||
scr_angle = 20
|
||||
|
||||
# Circuit board and cable hole.
|
||||
# This is in the back of the screen, and is a bit shorter in height than the
|
||||
# screen. It's wider so it removes enough material to make the shape simpler.
|
||||
@ -38,13 +40,17 @@ board_cutout = cq.Sketch().trapezoid(
|
||||
mode="a",
|
||||
)
|
||||
|
||||
kbd_cable_hole = cq.Sketch().trapezoid(15, 5, 90, mode="a").vertices().fillet(1)
|
||||
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()
|
||||
.polygon([(0, 0), (width, 0), (width, -12), (0, -12), (0, 0)], mode="a")
|
||||
.push(mounting_pillar_positions)
|
||||
.trapezoid(-12, 12, 90, mode="a")
|
||||
.circle(ti_radius, mode="s")
|
||||
.clean()
|
||||
)
|
||||
|
||||
|
||||
@ -54,9 +60,9 @@ def model():
|
||||
.workplane()
|
||||
.tag("mid_height")
|
||||
.box(width, height, thickness)
|
||||
# The screen goes at a 45 degree angle
|
||||
# The screen goes rotated
|
||||
.faces(">Z")
|
||||
.transformed(rotate=(45, 0, 0))
|
||||
.transformed(rotate=(scr_angle, 0, 0))
|
||||
# Move the screen "lower" so it doesn't interfere
|
||||
# so much with the back
|
||||
.center(0, -2)
|
||||
@ -65,6 +71,35 @@ def model():
|
||||
# of the inclined screen
|
||||
.placeSketch(cq.Sketch().trapezoid(1000, 1000, 90, mode="a"))
|
||||
.cutBlind(1000)
|
||||
# Trim the top
|
||||
.workplaneFromTagged("mid_height")
|
||||
.workplane(offset=21)
|
||||
.placeSketch(cq.Sketch().trapezoid(1000, 1000, 90, mode="a"))
|
||||
.cutBlind(100)
|
||||
# Make bottom smaller to fit with base
|
||||
.faces(">X")
|
||||
.workplane(centerOption="CenterOfBoundBox")
|
||||
.center(-height / 2, -thickness / 2)
|
||||
.placeSketch(
|
||||
cq.Sketch()
|
||||
.polygon(
|
||||
[
|
||||
(height_bottom, 0),
|
||||
(height_bottom, thickness / 3),
|
||||
(height, thickness - 21),
|
||||
(height, thickness),
|
||||
(height + 5, thickness + 5),
|
||||
(height + 5, 0),
|
||||
(height_bottom, 0),
|
||||
]
|
||||
)
|
||||
.vertices()
|
||||
.fillet(3)
|
||||
)
|
||||
.cutBlind(-1000)
|
||||
# Fillet top of the object
|
||||
.edges("|X and >Z")
|
||||
.fillet(3)
|
||||
# Cut off viewport hole so we can see the screen
|
||||
.workplaneFromTagged("slanted")
|
||||
.placeSketch(viewport_cutout)
|
||||
@ -76,11 +111,6 @@ def model():
|
||||
.center(-3, 0)
|
||||
.placeSketch(screen_cutout)
|
||||
.cutBlind(-scr_thickness)
|
||||
# Trim the top
|
||||
.workplaneFromTagged("mid_height")
|
||||
.workplane(offset=21)
|
||||
.placeSketch(cq.Sketch().trapezoid(1000, 1000, 90, mode="a"))
|
||||
.cutBlind(100)
|
||||
# Make it hollow
|
||||
.faces("<Z")
|
||||
# Can't be exactly shell_t because cq fails
|
||||
@ -90,44 +120,38 @@ def model():
|
||||
.workplane(offset=-scr_thickness, centerOption="CenterOfBoundBox")
|
||||
.placeSketch(board_cutout)
|
||||
.cutBlind(-6)
|
||||
# Fillet top of the object
|
||||
.edges(">Z and |X")
|
||||
.fillet(5)
|
||||
# Make small hole for the keyboard cable
|
||||
.faces(">Y")
|
||||
.workplane(offset=-5, centerOption="CenterOfBoundBox")
|
||||
.center(-width / 2 + 128, -23)
|
||||
.placeSketch(kbd_cable_hole)
|
||||
.cutBlind(-1000)
|
||||
# Pillars to join with bottom half
|
||||
.workplaneFromTagged("mid_height")
|
||||
.workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox")
|
||||
.center(-width / 2, height / 2)
|
||||
.center(-width / 2, height_bottom - height / 2)
|
||||
.placeSketch(mounting_pillars)
|
||||
.extrude(10)
|
||||
# Fillet the front edge of the screen case so it looks softer
|
||||
.edges(">(0, -10, 5)")
|
||||
.fillet(2)
|
||||
)
|
||||
|
||||
|
||||
exporters.export(model(), "screen_mount.stl")
|
||||
if __name__ == "__main__":
|
||||
|
||||
split_offset = -133
|
||||
print("Exporting")
|
||||
exporters.export(model(), "screen_mount.stl")
|
||||
|
||||
right_side = (
|
||||
model()
|
||||
.faces(">X")
|
||||
.workplane(centerOption="CenterOfBoundBox", offset=split_offset)
|
||||
.center(0, height / 2)
|
||||
.split(keepTop=True)
|
||||
)
|
||||
offset_width = -width / 2
|
||||
|
||||
exporters.export(right_side, "right_screen_mount.stl")
|
||||
right_side = (
|
||||
model()
|
||||
.faces(">X")
|
||||
.workplane(centerOption="CenterOfBoundBox", offset=offset_width)
|
||||
.split(keepTop=True)
|
||||
)
|
||||
|
||||
left_side = (
|
||||
model()
|
||||
.faces(">X")
|
||||
.workplane(centerOption="CenterOfBoundBox", offset=split_offset)
|
||||
.center(0, height / 2)
|
||||
.split(keepBottom=True)
|
||||
)
|
||||
exporters.export(right_side, "right_screen_mount.stl")
|
||||
|
||||
exporters.export(left_side, "left_screen_mount.stl")
|
||||
left_side = (
|
||||
model()
|
||||
.faces(">X")
|
||||
.workplane(centerOption="CenterOfBoundBox", offset=offset_width)
|
||||
.split(keepBottom=True)
|
||||
)
|
||||
|
||||
exporters.export(left_side, "left_screen_mount.stl")
|
||||
|
Binary file not shown.
78
notebook_nueva/screen_pillars.py
Normal file
78
notebook_nueva/screen_pillars.py
Normal file
@ -0,0 +1,78 @@
|
||||
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
|
74
notebook_nueva/usb_hub.py
Normal file
74
notebook_nueva/usb_hub.py
Normal file
@ -0,0 +1,74 @@
|
||||
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
|
||||
|
||||
holes = [
|
||||
# USB-A port
|
||||
{
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"shape": cq.Sketch().trapezoid(13, 5, 90, mode="a").vertices().fillet(2),
|
||||
},
|
||||
]
|
||||
|
||||
elements = [
|
||||
# Thing to grab the hub
|
||||
{
|
||||
"x": 0,
|
||||
"y": 5,
|
||||
"shape": (
|
||||
cq.Sketch().trapezoid(22, 10, 90, mode="a").trapezoid(17, 10, 90, mode="s")
|
||||
),
|
||||
"height": 8,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
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=263, # offset_x,
|
||||
y_offset=0, # 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
|
73
notebook_nueva/utils.py
Normal file
73
notebook_nueva/utils.py
Normal file
@ -0,0 +1,73 @@
|
||||
import cadquery as cq
|
||||
from math import floor
|
||||
|
||||
|
||||
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 + element["x"], -h / 2 + y_offset + element["y"])
|
||||
.placeSketch(element["shape"])
|
||||
.extrude(height)
|
||||
)
|
||||
|
||||
|
||||
def punch_hole(*, 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"])
|
||||
.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):
|
||||
# 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 * 0.85, 6),
|
||||
}
|
||||
]
|
||||
|
||||
return vents
|
111
notebook_nueva/zero_holder.py
Normal file
111
notebook_nueva/zero_holder.py
Normal file
@ -0,0 +1,111 @@
|
||||
import cadquery as cq
|
||||
|
||||
from utils import extrude_shape, punch_hole, hex_vents
|
||||
|
||||
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
|
||||
{
|
||||
"x": 0,
|
||||
"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
|
@ -1 +1 @@
|
||||
cadquery
|
||||
cadquery2
|
||||
|
Reference in New Issue
Block a user