Compare commits
27 Commits
539583ac8f
...
main
Author | SHA1 | Date | |
---|---|---|---|
337b4a93bc | |||
993c82f5d8 | |||
fbf1a79dfb | |||
bae753ba0a | |||
bd215573eb | |||
ea06783e66 | |||
e015585a8b | |||
7217c7bc3b | |||
158d7bfd87 | |||
812b9c1285 | |||
1a60b2327e | |||
e6b08d2fa4 | |||
07b9cc47ba | |||
cc1ff47e53 | |||
bd4b8d4cc5 | |||
6d527cbbb8 | |||
99e003d8f7 | |||
c53263626c | |||
8e0d3ad1a2 | |||
cf751ce523 | |||
67de0b13b2 | |||
517bb93d8d | |||
59f67b5077 | |||
e7c2d1d69d | |||
3a4e6a66ae | |||
4f53615c0e | |||
1a8e93775f |
2
notebook_nueva/.flake8
Normal file
2
notebook_nueva/.flake8
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[flake8]
|
||||||
|
extend-ignore = E501, E266
|
176
notebook_nueva/.gitignore
vendored
Normal file
176
notebook_nueva/.gitignore
vendored
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
|
||||||
|
|
||||||
|
### Created by https://www.gitignore.io
|
||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
### Python Patch ###
|
||||||
|
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||||
|
poetry.toml
|
||||||
|
|
||||||
|
# ruff
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# LSP config files
|
||||||
|
pyrightconfig.json
|
||||||
|
|
||||||
|
.lint
|
11
notebook_nueva/.vscode/settings.json
vendored
Normal file
11
notebook_nueva/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"python.formatting.provider": "black",
|
||||||
|
"emeraldwalk.runonsave": {
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"match": ".py",
|
||||||
|
"isAsync": true,
|
||||||
|
"cmd": "make"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
notebook_nueva/Makefile
Normal file
13
notebook_nueva/Makefile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
STL_FILES = base.stl hinged_lid.stl simple_lid.stl tandy_lid.stl
|
||||||
|
|
||||||
|
all: $(STL_FILES) lint
|
||||||
|
|
||||||
|
%.stl: %.py dimensions.py utils.py components/*py
|
||||||
|
python $<
|
||||||
|
|
||||||
|
lint: .lint
|
||||||
|
|
||||||
|
.lint: **.py
|
||||||
|
flake8
|
||||||
|
touch .lint
|
||||||
|
|
49
notebook_nueva/README.md
Normal file
49
notebook_nueva/README.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Homemade Computer Project To Be Named Later
|
||||||
|
|
||||||
|
Here are the files used to build the case for my homemade
|
||||||
|
computer. You can see a lot about it in [this article](http://ralsina.me/weblog/posts/so-i-built-a-laptop.html) (or at least about it as it was in early march 2023)
|
||||||
|
|
||||||
|
The main gist is to use [CadQuery](https://cadquery.readthedocs.io/en/latest/)
|
||||||
|
and Python to build flexible, deeply parametric cases for computers based on
|
||||||
|
Single Board Computers (think Raspberry Pi and similar things).
|
||||||
|
|
||||||
|
## How flexible?
|
||||||
|
|
||||||
|
Suppose you buy a mechanic keyboard and use it as the base to build something like a classic C64-style wedge using a cheap 3d printer:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Yes, you can do this using pretty much any mechanical keyboard you are willing to butcher.
|
||||||
|
|
||||||
|
And then you remove a few screws, replace a few components and turn it into a
|
||||||
|
Tandy Model-100 style laptop:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Or into a "normal" notebook:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
And what's inside?
|
||||||
|
|
||||||
|
* A SBC
|
||||||
|
* Maybe batteries
|
||||||
|
* Maybe a USB hub
|
||||||
|
* Perhaps a soundcard?
|
||||||
|
* Storage?
|
||||||
|
|
||||||
|
How would I know, you are going to be the one that builds it!
|
||||||
|
|
||||||
|
## How is it going
|
||||||
|
|
||||||
|
The basic concepts work, as proven by me building the damned things. But still:
|
||||||
|
|
||||||
|
* The software needs a lot of work
|
||||||
|
* It has to be made much more user friendly
|
||||||
|
* All the "lids" are pretty custom one-offs (they need to be made more parametric)
|
||||||
|
* The component library is very limited (just the things I am using in my build)
|
||||||
|
|
||||||
|
None of those things is an insurmountable problem, and I am working on them,
|
||||||
|
and I have plans to fix it all. Eventually. Some day.
|
||||||
|
|
||||||
|
In the meantime, if you want to use any of this and need a hand, just contact me at roberto.alsina@gmail.com and I'll try to help.
|
209
notebook_nueva/base.py
Normal file
209
notebook_nueva/base.py
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
import cadquery as cq
|
||||||
|
# from cq_warehouse.drafting import Draft
|
||||||
|
|
||||||
|
import components.audio_plug as audio_plug
|
||||||
|
import components.battery_holder as battery_holder
|
||||||
|
import components.hdmi_out as hdmi_out
|
||||||
|
import components.keyboard as keyboard
|
||||||
|
import components.screen_pillars as screen_pillars
|
||||||
|
import components.usb_hub as usb_hub
|
||||||
|
import components.zero_holder as cpu_holder
|
||||||
|
import dimensions as dim
|
||||||
|
|
||||||
|
from utils import export
|
||||||
|
|
||||||
|
# Base for the notebook. Basically a kbd base that extends back
|
||||||
|
# as much as possible
|
||||||
|
|
||||||
|
screen_pillars.init(dim.mounting_pillar_positions, dim.base_thickness - dim.shell_t)
|
||||||
|
|
||||||
|
|
||||||
|
def model():
|
||||||
|
# Create the basic shape of the case bottom.
|
||||||
|
model = (
|
||||||
|
cq.Workplane("XY")
|
||||||
|
.workplane(offset=dim.base_thickness / 2)
|
||||||
|
.tag("mid_height")
|
||||||
|
# Hollow box
|
||||||
|
.box(dim.width, dim.height, dim.base_thickness)
|
||||||
|
.edges("|Z")
|
||||||
|
.fillet(2)
|
||||||
|
.faces(">Z")
|
||||||
|
.shell(-dim.shell_t)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now the basic box shape is in place, start adding things
|
||||||
|
# and cutting holes.
|
||||||
|
|
||||||
|
model = usb_hub.add(
|
||||||
|
model=model,
|
||||||
|
width=dim.width,
|
||||||
|
height=dim.height,
|
||||||
|
thickness=dim.base_thickness,
|
||||||
|
bottom_face="<Z",
|
||||||
|
back_face=">Y",
|
||||||
|
offset_x=dim.usb_offset_x,
|
||||||
|
offset_y=0,
|
||||||
|
shell_t=dim.shell_t,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Hole for audio in right side
|
||||||
|
model = audio_plug.add(
|
||||||
|
model=model,
|
||||||
|
width=dim.width,
|
||||||
|
height=dim.height,
|
||||||
|
thickness=dim.base_thickness,
|
||||||
|
offset_x=dim.width - audio_plug.item_w,
|
||||||
|
offset_y=19,
|
||||||
|
bottom_face="<Z",
|
||||||
|
back_face=">X",
|
||||||
|
shell_t=dim.shell_t,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Hole for HDMI out in the back
|
||||||
|
model = hdmi_out.add(
|
||||||
|
model=model,
|
||||||
|
width=dim.width,
|
||||||
|
height=dim.height,
|
||||||
|
thickness=dim.base_thickness,
|
||||||
|
offset_x=dim.hdmi_out_offset_x,
|
||||||
|
offset_y=0,
|
||||||
|
bottom_face=None,
|
||||||
|
back_face=">Y",
|
||||||
|
shell_t=dim.shell_t,
|
||||||
|
)
|
||||||
|
|
||||||
|
model = cpu_holder.add(
|
||||||
|
model=model,
|
||||||
|
width=dim.width,
|
||||||
|
height=dim.height,
|
||||||
|
thickness=dim.base_thickness,
|
||||||
|
offset_x=dim.cpu_offset_x,
|
||||||
|
offset_y=dim.cpu_offset_y,
|
||||||
|
bottom_face="<Z",
|
||||||
|
back_face=None, # Not exposing the holes
|
||||||
|
shell_t=dim.shell_t,
|
||||||
|
)
|
||||||
|
|
||||||
|
# This adds all the holes and extrusions for the battery system
|
||||||
|
model = battery_holder.add(
|
||||||
|
model=model,
|
||||||
|
width=dim.width,
|
||||||
|
height=dim.height,
|
||||||
|
thickness=dim.base_thickness,
|
||||||
|
offset_x=dim.battery_offset_x,
|
||||||
|
offset_y=dim.battery_offset_y,
|
||||||
|
bottom_face="<Z",
|
||||||
|
back_face=">Y",
|
||||||
|
shell_t=dim.shell_t,
|
||||||
|
)
|
||||||
|
|
||||||
|
model = screen_pillars.add(
|
||||||
|
model=model,
|
||||||
|
width=dim.width,
|
||||||
|
height=dim.height,
|
||||||
|
thickness=dim.base_thickness,
|
||||||
|
offset_x=0,
|
||||||
|
offset_y=0,
|
||||||
|
bottom_face="<Z",
|
||||||
|
back_face=None,
|
||||||
|
shell_t=dim.shell_t,
|
||||||
|
)
|
||||||
|
|
||||||
|
model = keyboard.add(
|
||||||
|
model=model,
|
||||||
|
width=dim.width,
|
||||||
|
height=dim.height,
|
||||||
|
thickness=dim.base_thickness,
|
||||||
|
bottom_face="<Z",
|
||||||
|
back_face=None,
|
||||||
|
offset_x=dim.shell_t,
|
||||||
|
offset_y=keyboard.kbd_height + dim.shell_t,
|
||||||
|
shell_t=dim.shell_t,
|
||||||
|
)
|
||||||
|
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
model = model()
|
||||||
|
|
||||||
|
left_cutout = cq.Sketch().polygon(
|
||||||
|
[
|
||||||
|
(0, 0),
|
||||||
|
(dim.width / 2, 0),
|
||||||
|
(dim.width / 2, -dim.height),
|
||||||
|
(0, -dim.height),
|
||||||
|
(0, 0),
|
||||||
|
],
|
||||||
|
mode="a",
|
||||||
|
)
|
||||||
|
|
||||||
|
right_side = (
|
||||||
|
model.faces("<Z")
|
||||||
|
.workplaneFromTagged("mid_height")
|
||||||
|
.transformed(offset=cq.Vector(0, 0, -dim.base_thickness / 2))
|
||||||
|
.center(-dim.width / 2, dim.height / 2)
|
||||||
|
.placeSketch(left_cutout)
|
||||||
|
.cutBlind(100)
|
||||||
|
)
|
||||||
|
|
||||||
|
export(right_side, "base_right.stl")
|
||||||
|
|
||||||
|
right_cutout = cq.Sketch().polygon(
|
||||||
|
[
|
||||||
|
(dim.width / 2, 0),
|
||||||
|
(dim.width, 0),
|
||||||
|
(dim.width, -dim.height),
|
||||||
|
(dim.width / 2, -dim.height),
|
||||||
|
(dim.width / 2, 0),
|
||||||
|
],
|
||||||
|
mode="a",
|
||||||
|
)
|
||||||
|
|
||||||
|
left_side = (
|
||||||
|
model.faces("<Z")
|
||||||
|
.workplaneFromTagged("mid_height")
|
||||||
|
.transformed(offset=cq.Vector(0, 0, -dim.base_thickness / 2))
|
||||||
|
.center(-dim.width / 2, dim.height / 2)
|
||||||
|
.placeSketch(right_cutout)
|
||||||
|
.cutBlind(100)
|
||||||
|
)
|
||||||
|
export(left_side, "base_left.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,
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
|
||||||
|
export(model, "base.stl")
|
||||||
|
|
||||||
|
# for d in dimensions[1:]:
|
||||||
|
# dimensions[0].add(d.toCompound())
|
||||||
|
# dimensions[0].add(model)
|
||||||
|
|
||||||
|
export(
|
||||||
|
# model[0].toCompound(),
|
||||||
|
model,
|
||||||
|
"base.svg",
|
||||||
|
opt={
|
||||||
|
"projectionDir": (0, 0, 1),
|
||||||
|
"strokeWidth": 0.3,
|
||||||
|
},
|
||||||
|
)
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 952 KiB After Width: | Height: | Size: 786 KiB |
Binary file not shown.
Binary file not shown.
0
notebook_nueva/components/__init__.py
Normal file
0
notebook_nueva/components/__init__.py
Normal file
@ -81,7 +81,7 @@ vents = hex_vents(size=3, width=width, height=height)
|
|||||||
holes = [
|
holes = [
|
||||||
# Power inlet
|
# Power inlet
|
||||||
{
|
{
|
||||||
"x": -17,
|
"x": -18.5,
|
||||||
"y": -1 + pillar_height,
|
"y": -1 + pillar_height,
|
||||||
"shape": cq.Sketch().trapezoid(12, 6.5, 90, mode="a").vertices().fillet(1),
|
"shape": cq.Sketch().trapezoid(12, 6.5, 90, mode="a").vertices().fillet(1),
|
||||||
},
|
},
|
@ -1,43 +1,42 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
import math
|
|
||||||
|
|
||||||
# Size of the kbd board
|
# These should be set from dimensions.py
|
||||||
kbd_height = 95.5
|
elements = None
|
||||||
kbd_width = 305
|
kbd_pillar_positions = []
|
||||||
back_thickness = 19
|
kbd_height = 0
|
||||||
front_thickness = 12
|
kbd_width = 0
|
||||||
|
kbd_back_thickness = 0
|
||||||
|
kbd_front_thickness = 0
|
||||||
|
kbd_actual_height = 0
|
||||||
|
kbd_angle = 0
|
||||||
|
kbd_pillar_offset_1 = 0
|
||||||
|
kbd_pillar_radius_1 = 0
|
||||||
|
kbd_pillar_offset_2 = 0
|
||||||
|
kbd_pillar_radius_2 = 0
|
||||||
|
kbd_screw_radius = 0
|
||||||
|
|
||||||
# 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 = [
|
def init():
|
||||||
(19, 16),
|
global elements
|
||||||
(142.5, 25.5),
|
|
||||||
(kbd_width - 20, 16),
|
|
||||||
(23.5, 79.5),
|
|
||||||
(145.5, 82.5),
|
|
||||||
(kbd_width - 19, 79.5),
|
|
||||||
]
|
|
||||||
|
|
||||||
elements = [
|
elements = [
|
||||||
# Shorter pillars
|
# Shorter pillars
|
||||||
{
|
{
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"z": 5.5,
|
"z": kbd_pillar_offset_1,
|
||||||
"shape": cq.Sketch().push(kbd_pillar_positions).circle(5, mode="a"),
|
"shape": cq.Sketch().push(kbd_pillar_positions).circle(kbd_pillar_radius_1, mode="a"),
|
||||||
},
|
},
|
||||||
# Taller pillars with holes for self-tapping screws
|
# Taller pillars with holes for self-tapping screws
|
||||||
{
|
{
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"z": 2.5,
|
"z": kbd_pillar_offset_2,
|
||||||
"shape": (
|
"shape": (
|
||||||
cq.Sketch()
|
cq.Sketch()
|
||||||
.push(kbd_pillar_positions)
|
.push(kbd_pillar_positions)
|
||||||
.circle(2.4, mode="a")
|
.circle(kbd_pillar_radius_2, mode="a")
|
||||||
.circle(1.1, mode="s")
|
.circle(kbd_screw_radius, mode="s")
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -61,7 +60,7 @@ def add(
|
|||||||
if bottom_face:
|
if bottom_face:
|
||||||
model = (
|
model = (
|
||||||
model.faces(bottom_face)
|
model.faces(bottom_face)
|
||||||
.workplane(centerOption="CenterOfBoundBox", offset=-front_thickness)
|
.workplane(centerOption="CenterOfBoundBox", offset=-kbd_front_thickness)
|
||||||
.center(
|
.center(
|
||||||
-width / 2,
|
-width / 2,
|
||||||
height / 2,
|
height / 2,
|
||||||
@ -88,16 +87,15 @@ def add(
|
|||||||
.placeSketch(
|
.placeSketch(
|
||||||
cq.Sketch().polygon(
|
cq.Sketch().polygon(
|
||||||
[
|
[
|
||||||
[0, front_thickness],
|
[0, kbd_front_thickness],
|
||||||
[shell_t, front_thickness],
|
[shell_t, kbd_front_thickness],
|
||||||
[actual_height + shell_t, back_thickness],
|
[kbd_actual_height + shell_t, kbd_back_thickness],
|
||||||
[actual_height + shell_t, 1000],
|
[kbd_actual_height + shell_t, 1000],
|
||||||
[0, 1000],
|
[0, 1000],
|
||||||
[0, front_thickness],
|
[0, kbd_front_thickness],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.cutBlind(-1000)
|
.cutBlind(-1000)
|
||||||
)
|
)
|
||||||
|
|
||||||
return model
|
return model
|
@ -2,7 +2,14 @@ from utils import extrude_shape, punch_hole
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
elements = None
|
elements = None
|
||||||
bottom_holes = None # Not really vents FIXME
|
bottom_holes = None
|
||||||
|
|
||||||
|
# These are set from dimensions.py
|
||||||
|
pillar_width = 0
|
||||||
|
pillar_height = 0
|
||||||
|
screw_head_radius = 0
|
||||||
|
screw_head_depth = 0
|
||||||
|
screw_radius = 0
|
||||||
|
|
||||||
|
|
||||||
def init(positions, thickness):
|
def init(positions, thickness):
|
||||||
@ -13,7 +20,9 @@ def init(positions, thickness):
|
|||||||
{
|
{
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"shape": cq.Sketch().push(positions).trapezoid(12, 12, 90, mode="a"),
|
"shape": cq.Sketch()
|
||||||
|
.push(positions)
|
||||||
|
.trapezoid(pillar_width, pillar_height, 90, mode="a"),
|
||||||
"height": thickness,
|
"height": thickness,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -22,13 +31,13 @@ def init(positions, thickness):
|
|||||||
{
|
{
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"shape": cq.Sketch().push(positions).circle(3, mode="a"),
|
"shape": cq.Sketch().push(positions).circle(screw_head_radius, mode="a"),
|
||||||
"depth": thickness - 13, # (screw thread length - threaded insert depth)
|
"depth": screw_head_depth,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"shape": cq.Sketch().push(positions).circle(1.8, mode="a"),
|
"shape": cq.Sketch().push(positions).circle(screw_radius, mode="a"),
|
||||||
"depth": 100,
|
"depth": 100,
|
||||||
},
|
},
|
||||||
]
|
]
|
107
notebook_nueva/components/simple_lid.py
Normal file
107
notebook_nueva/components/simple_lid.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import cadquery as cq
|
||||||
|
|
||||||
|
import dimensions as dim
|
||||||
|
from utils import extrude_shape2, hex_vents, punch_hole, export
|
||||||
|
|
||||||
|
|
||||||
|
def model():
|
||||||
|
# Create the basic shape of the case lid
|
||||||
|
model = (
|
||||||
|
cq.Workplane("XY")
|
||||||
|
# Hollow box
|
||||||
|
.box(dim.width, dim.sl_height, dim.sl_thickness)
|
||||||
|
.edges("|Z and >Y")
|
||||||
|
.fillet(2)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make many holes
|
||||||
|
vent = hex_vents(size=6, width=dim.width * 0.9, height=dim.sl_height * 0.9)[0]
|
||||||
|
model = punch_hole(
|
||||||
|
model=model,
|
||||||
|
face=">Z",
|
||||||
|
w=dim.width,
|
||||||
|
h=dim.sl_height,
|
||||||
|
x_offset=0.05 * dim.width,
|
||||||
|
y_offset=0.05 * dim.sl_height,
|
||||||
|
hole=vent,
|
||||||
|
depth=dim.sl_thickness,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add screw holes
|
||||||
|
for position in dim.mounting_pillar_positions:
|
||||||
|
model = (
|
||||||
|
model.faces(">Z")
|
||||||
|
.workplane(centerOption="CenterOfBoundBox")
|
||||||
|
.center(
|
||||||
|
-dim.width / 2 + position[0],
|
||||||
|
dim.sl_height / 2 - position[1] - dim.shell_t,
|
||||||
|
)
|
||||||
|
.placeSketch(cq.Sketch().circle(dim.m4_top / 2 + 1.5))
|
||||||
|
.extrude(-dim.sl_thickness)
|
||||||
|
.faces(">Z")
|
||||||
|
.workplane(centerOption="CenterOfBoundBox")
|
||||||
|
.center(
|
||||||
|
-dim.width / 2 + position[0],
|
||||||
|
dim.sl_height / 2 - position[1] - dim.shell_t,
|
||||||
|
)
|
||||||
|
.cskHole(dim.m4_bottom, dim.m4_top, 82, depth=None)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add front lip
|
||||||
|
|
||||||
|
model = (
|
||||||
|
model.faces(">Z")
|
||||||
|
.workplane(centerOption="CenterOfBoundBox")
|
||||||
|
.center(0, -dim.sl_height / 2 + dim.sl_lip_thickness / 2)
|
||||||
|
.placeSketch(
|
||||||
|
cq.Sketch().trapezoid(dim.width - 2 * dim.shell_t, dim.sl_lip_thickness, 90)
|
||||||
|
)
|
||||||
|
.extrude(-dim.sl_front_lip - dim.sl_thickness)
|
||||||
|
)
|
||||||
|
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
def decorative_cover():
|
||||||
|
# A decorative thingie to cover the ugly seam in the middle
|
||||||
|
model = cq.Workplane("XY").box(10, dim.sl_height, 1).edges("|Z").fillet(1)
|
||||||
|
vent = hex_vents(
|
||||||
|
size=6, width=dim.width * 0.9, height=dim.sl_height * 0.9, density=0.775
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
model = extrude_shape2(
|
||||||
|
model=model,
|
||||||
|
face=">Z",
|
||||||
|
w=dim.width,
|
||||||
|
h=dim.sl_height,
|
||||||
|
x_offset=0.05 * dim.width,
|
||||||
|
y_offset=0.05 * dim.sl_height,
|
||||||
|
hole=vent,
|
||||||
|
depth=3,
|
||||||
|
)
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
model = model()
|
||||||
|
export(model, "simple_lid.stl")
|
||||||
|
|
||||||
|
cover = decorative_cover()
|
||||||
|
export(cover, "simple_lid_cover.stl")
|
||||||
|
|
||||||
|
export(
|
||||||
|
model,
|
||||||
|
"simple_lid.svg",
|
||||||
|
opt={
|
||||||
|
"projectionDir": (0, 0, 1),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export(
|
||||||
|
model.faces(">X").workplane(offset=-dim.width / 2).split(keepTop=True),
|
||||||
|
"simple_lid_right.stl",
|
||||||
|
)
|
||||||
|
export(
|
||||||
|
model.faces(">X").workplane(offset=-dim.width / 2).split(keepBottom=True),
|
||||||
|
"simple_lid_left.stl",
|
||||||
|
)
|
@ -31,13 +31,13 @@ elements = [
|
|||||||
"height": 8,
|
"height": 8,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": item_w / 2 + 5,
|
"x": item_w / 2 + 5.5,
|
||||||
"y": item_h - 3,
|
"y": item_h - 3,
|
||||||
"shape": (cq.Sketch().circle(2.5, mode="a")),
|
"shape": (cq.Sketch().circle(2.5, mode="a")),
|
||||||
"height": 8,
|
"height": 8,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": item_w / 2 - 5,
|
"x": item_w / 2 - 5.5,
|
||||||
"y": item_h - 3,
|
"y": item_h - 3,
|
||||||
"shape": (cq.Sketch().circle(2.5, mode="a")),
|
"shape": (cq.Sketch().circle(2.5, mode="a")),
|
||||||
"height": 8,
|
"height": 8,
|
144
notebook_nueva/dimensions.py
Normal file
144
notebook_nueva/dimensions.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
import components.audio_plug as audio_plug
|
||||||
|
import components.usb_hub as usb_hub
|
||||||
|
import components.keyboard as keyboard
|
||||||
|
import components.screen_pillars as screen_pillars
|
||||||
|
|
||||||
|
## Standard things (TODO move to separate file)
|
||||||
|
|
||||||
|
# M3 threaded insert sizes
|
||||||
|
ti_radius = 2.35
|
||||||
|
ti_depth = 6.25
|
||||||
|
|
||||||
|
# M3 hex nut dimensions
|
||||||
|
m3_hn_diam = 5.5
|
||||||
|
m3_hn_hole = 3
|
||||||
|
m3_hn_thickness = 2.5
|
||||||
|
|
||||||
|
# Dimensions for countersunk M4 screws
|
||||||
|
m4_top = 9
|
||||||
|
m4_bottom = 4
|
||||||
|
|
||||||
|
## Keyboard dimensions
|
||||||
|
keyboard.kbd_height = 95.5
|
||||||
|
keyboard.kbd_width = 305
|
||||||
|
keyboard.kbd_back_thickness = 19
|
||||||
|
keyboard.kbd_front_thickness = 12
|
||||||
|
# Pythagoras
|
||||||
|
keyboard.kbd_actual_height = (
|
||||||
|
keyboard.kbd_height**2
|
||||||
|
- (keyboard.kbd_back_thickness - keyboard.kbd_front_thickness) ** 2
|
||||||
|
) ** 0.5
|
||||||
|
keyboard.kbd_angle = (
|
||||||
|
math.acos(keyboard.kbd_actual_height / keyboard.kbd_height) * 180 / math.pi
|
||||||
|
)
|
||||||
|
keyboard.kbd_pillar_positions = [
|
||||||
|
(19, 16),
|
||||||
|
(142.5, 25.5),
|
||||||
|
(keyboard.kbd_width - 20, 16),
|
||||||
|
(23.5, 79.5),
|
||||||
|
(145.5, 82.5),
|
||||||
|
(keyboard.kbd_width - 19, 79.5),
|
||||||
|
]
|
||||||
|
keyboard.kbd_pillar_offset_1 = 5.5
|
||||||
|
keyboard.kbd_pillar_radius_1 = 5
|
||||||
|
keyboard.kbd_pillar_offset_2 = 2.5
|
||||||
|
keyboard.kbd_pillar_radius_2 = 2.4
|
||||||
|
keyboard.kbd_screw_radius = 1.1
|
||||||
|
keyboard.init()
|
||||||
|
|
||||||
|
## Screen dimensions
|
||||||
|
# Whole screen size
|
||||||
|
scr_w = 231
|
||||||
|
scr_h = 65
|
||||||
|
scr_thickness = 5.5
|
||||||
|
# Visible screen size
|
||||||
|
vis_w = 219
|
||||||
|
vis_h = 55
|
||||||
|
|
||||||
|
|
||||||
|
## Dimensions for the base of the computer
|
||||||
|
|
||||||
|
# Thickness of the outer material
|
||||||
|
shell_t = 3
|
||||||
|
|
||||||
|
# Size of the base
|
||||||
|
width = keyboard.kbd_width + 2 * shell_t
|
||||||
|
height = 159
|
||||||
|
base_thickness = 30 + shell_t # 30 inside
|
||||||
|
|
||||||
|
|
||||||
|
# These are placed where convenient, and are used to join the top and bottom
|
||||||
|
# parts of the case.
|
||||||
|
# Measured from back-left corner OUTSIDE
|
||||||
|
mounting_pillar_positions = [
|
||||||
|
(6, 6),
|
||||||
|
(6, 43),
|
||||||
|
(120, 6),
|
||||||
|
(170, 6),
|
||||||
|
(width - 6, 6),
|
||||||
|
(width - 6, 43),
|
||||||
|
(120, 48),
|
||||||
|
(170, 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 = 177
|
||||||
|
cpu_offset_y = 2
|
||||||
|
|
||||||
|
# Battery holder position from back-left corner of the case
|
||||||
|
battery_offset_x = 15
|
||||||
|
battery_offset_y = 3
|
||||||
|
|
||||||
|
# HDMI out hole from back-left corner of the case
|
||||||
|
hdmi_out_offset_x = 138
|
||||||
|
|
||||||
|
## Dimensions for the Tandy lid
|
||||||
|
|
||||||
|
# Size of the whole object
|
||||||
|
tl_height = 66
|
||||||
|
tl_height_bottom = 59
|
||||||
|
tl_full_thickness = 48 # Will be shorter after construction
|
||||||
|
# Screen angle
|
||||||
|
tl_scr_angle = 20
|
||||||
|
|
||||||
|
|
||||||
|
## Dimensions for the hinged lid
|
||||||
|
|
||||||
|
# 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
|
||||||
|
hl_hinge_slant = shell_t + 2
|
||||||
|
|
||||||
|
hl_bezel_width = m3_hn_diam + 2
|
||||||
|
hl_bezel_height = 1
|
||||||
|
hl_bezel_thickness = 2
|
||||||
|
|
||||||
|
hl_hinge_radius = 5.5
|
||||||
|
hl_screw_radius = 1.5 # M3
|
||||||
|
hl_ring_radius = 5 # M3
|
||||||
|
hl_hinge_offset = max(p[1] for p in mounting_pillar_positions) + 6
|
||||||
|
hl_hinge_width = 25
|
||||||
|
# Base + this lid
|
||||||
|
hl_full_thickness = 43
|
||||||
|
|
||||||
|
|
||||||
|
## Dimensions for the simple lid
|
||||||
|
sl_lip_thickness = 1.5
|
||||||
|
sl_height = (
|
||||||
|
max([y for _, y in mounting_pillar_positions]) + 6 + shell_t + sl_lip_thickness
|
||||||
|
)
|
||||||
|
sl_thickness = shell_t
|
||||||
|
sl_front_lip = 8
|
||||||
|
|
||||||
|
## Dimensions for pillars that connect base and lids
|
||||||
|
screen_pillars.pillar_width = 12
|
||||||
|
screen_pillars.pillar_height = 12
|
||||||
|
screen_pillars.screw_head_radius = 3
|
||||||
|
screen_pillars.screw_radius = 1.8
|
||||||
|
screen_pillars.screw_head_depth = base_thickness - 13 # (screw thread length - threaded insert depth)
|
@ -1,44 +1,19 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
from cadquery import exporters
|
|
||||||
|
|
||||||
import screen_pillars
|
import dimensions as dim
|
||||||
|
import components.keyboard as keyboard
|
||||||
|
import components.screen_pillars as screen_pillars
|
||||||
|
from utils import export
|
||||||
|
|
||||||
from modelo import (
|
mounting_pillar_positions = [(x, -y) for x, y in dim.mounting_pillar_positions]
|
||||||
height,
|
|
||||||
mounting_pillar_positions,
|
|
||||||
ti_depth,
|
|
||||||
ti_radius,
|
|
||||||
width,
|
|
||||||
thickness as model_thickness,
|
|
||||||
shell_t,
|
|
||||||
)
|
|
||||||
|
|
||||||
import screen_mount
|
|
||||||
|
|
||||||
import keyboard
|
|
||||||
|
|
||||||
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 = 43
|
|
||||||
|
|
||||||
|
|
||||||
mounting_pillar_positions = [(x, -y) for x, y in mounting_pillar_positions]
|
|
||||||
mounting_pillars = (
|
mounting_pillars = (
|
||||||
cq.Sketch()
|
cq.Sketch()
|
||||||
.push(mounting_pillar_positions)
|
.push(mounting_pillar_positions)
|
||||||
.trapezoid(-12, 12, 90, mode="a")
|
.trapezoid(screen_pillars.pillar_height, screen_pillars.pillar_width, 90, mode="a")
|
||||||
.circle(ti_radius, mode="s")
|
.circle(dim.ti_radius, mode="s")
|
||||||
.clean()
|
.clean()
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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():
|
def model():
|
||||||
# Create a 2-part hinged lid
|
# Create a 2-part hinged lid
|
||||||
@ -46,8 +21,8 @@ def model():
|
|||||||
model = (
|
model = (
|
||||||
cq.Workplane("XY")
|
cq.Workplane("XY")
|
||||||
# Hollow box
|
# Hollow box
|
||||||
.workplane(offset=-thickness / 2)
|
.workplane(offset=-dim.hl_full_thickness / 2)
|
||||||
.box(width, height, thickness)
|
.box(dim.width, dim.height, dim.hl_full_thickness)
|
||||||
.tag("base")
|
.tag("base")
|
||||||
.edges("|X and >Z and <Y")
|
.edges("|X and >Z and <Y")
|
||||||
.fillet(10)
|
.fillet(10)
|
||||||
@ -56,90 +31,110 @@ def model():
|
|||||||
.edges("|Z")
|
.edges("|Z")
|
||||||
.fillet(2)
|
.fillet(2)
|
||||||
.faces("<Z")
|
.faces("<Z")
|
||||||
.shell(-shell_t)
|
.shell(-dim.shell_t)
|
||||||
.faces(">X")
|
.faces(">X")
|
||||||
.workplane()
|
.workplane()
|
||||||
.center(height / 2 - hinge_offset, thickness / 2 - hinge_radius)
|
.center(
|
||||||
|
dim.height / 2 - dim.hl_hinge_offset,
|
||||||
|
dim.hl_full_thickness / 2 - dim.hl_hinge_radius,
|
||||||
|
)
|
||||||
.tag("rightSide")
|
.tag("rightSide")
|
||||||
# Outer surface of the hinge
|
# Outer surface of the hinge
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.placeSketch(cq.Sketch().circle(hinge_radius))
|
.placeSketch(cq.Sketch().circle(dim.hl_hinge_radius))
|
||||||
.extrude(-hinge_width)
|
.extrude(-dim.hl_hinge_width)
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-width + hinge_width)
|
.workplane(offset=-dim.width + dim.hl_hinge_width)
|
||||||
.placeSketch(cq.Sketch().circle(hinge_radius))
|
.placeSketch(cq.Sketch().circle(dim.hl_hinge_radius))
|
||||||
.extrude(-hinge_width)
|
.extrude(-dim.hl_hinge_width)
|
||||||
# Cut middle section between the hinges
|
# Cut middle section between the hinges
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-hinge_width)
|
.workplane(offset=-dim.hl_hinge_width)
|
||||||
.placeSketch(
|
.placeSketch(
|
||||||
cq.Sketch().polygon(
|
cq.Sketch().polygon(
|
||||||
[
|
[
|
||||||
(-hinge_radius, -hinge_radius),
|
(-dim.hl_hinge_radius, -dim.hl_hinge_radius),
|
||||||
(-hinge_radius, 0),
|
(-dim.hl_hinge_radius, 0),
|
||||||
(-hinge_radius - hinge_slant, hinge_radius),
|
(-dim.hl_hinge_radius - dim.hl_hinge_slant, dim.hl_hinge_radius),
|
||||||
(-hinge_slant, hinge_radius),
|
(-dim.hl_hinge_slant, dim.hl_hinge_radius),
|
||||||
(-hinge_slant, hinge_radius - hinge_slant),
|
(-dim.hl_hinge_slant, dim.hl_hinge_radius - dim.hl_hinge_slant),
|
||||||
(hinge_radius, hinge_radius - hinge_slant),
|
(dim.hl_hinge_radius, dim.hl_hinge_radius - dim.hl_hinge_slant),
|
||||||
(hinge_radius, -hinge_radius),
|
(dim.hl_hinge_radius, -dim.hl_hinge_radius),
|
||||||
(-hinge_radius, -hinge_radius),
|
(-dim.hl_hinge_radius, -dim.hl_hinge_radius),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.cutBlind(-width + 2 * hinge_width - 1)
|
.cutBlind(-dim.width + 2 * dim.hl_hinge_width - 1)
|
||||||
# Pillars to attach to base
|
# Pillars to attach to base
|
||||||
.workplaneFromTagged("base")
|
.workplaneFromTagged("base")
|
||||||
.workplane(
|
.workplane(
|
||||||
centerOption="CenterOfBoundBox", offset=model_thickness - thickness / 2
|
centerOption="CenterOfBoundBox",
|
||||||
|
offset=dim.base_thickness - dim.hl_full_thickness / 2,
|
||||||
)
|
)
|
||||||
.workplaneFromTagged("base")
|
.workplaneFromTagged("base")
|
||||||
.workplane(offset=thickness / 2 - shell_t)
|
.workplane(offset=dim.hl_full_thickness / 2 - dim.shell_t)
|
||||||
.center(-width / 2, height / 2 - shell_t)
|
.center(-dim.width / 2, dim.height / 2 - dim.shell_t)
|
||||||
.placeSketch(mounting_pillars)
|
.placeSketch(mounting_pillars)
|
||||||
.extrude(-10)
|
.extrude(-10)
|
||||||
# Hole for screws
|
# Hole for screws
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.placeSketch(cq.Sketch().circle(screw_radius))
|
.placeSketch(cq.Sketch().circle(dim.hl_screw_radius))
|
||||||
.cutBlind(-hinge_width)
|
.cutBlind(-dim.hl_hinge_width)
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-width + hinge_width)
|
.workplane(offset=-dim.width + dim.hl_hinge_width)
|
||||||
.placeSketch(cq.Sketch().circle(screw_radius))
|
.placeSketch(cq.Sketch().circle(dim.hl_screw_radius))
|
||||||
.cutBlind(-hinge_width)
|
.cutBlind(-dim.hl_hinge_width)
|
||||||
# Holes for rings & screw heads
|
# Holes for rings & screw heads
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.placeSketch(cq.Sketch().circle(ring_radius))
|
.placeSketch(cq.Sketch().circle(dim.hl_ring_radius))
|
||||||
.cutBlind(-5)
|
.cutBlind(-5)
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-width + 4)
|
.workplane(offset=-dim.width + 4)
|
||||||
.placeSketch(cq.Sketch().circle(ring_radius))
|
.placeSketch(cq.Sketch().circle(dim.hl_ring_radius))
|
||||||
.cutBlind(-5)
|
.cutBlind(-5)
|
||||||
# Split hinge halves
|
# Split hinge halves
|
||||||
.faces(">X")
|
.faces(">X")
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-hinge_width / 2)
|
.workplane(offset=-dim.hl_hinge_width / 2)
|
||||||
.placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90))
|
.placeSketch(
|
||||||
|
cq.Sketch().trapezoid(
|
||||||
|
dim.hl_hinge_radius * 2 + 1, dim.hl_hinge_radius * 2, 90
|
||||||
|
)
|
||||||
|
)
|
||||||
.cutBlind(-1)
|
.cutBlind(-1)
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-hinge_width)
|
.workplane(offset=-dim.hl_hinge_width)
|
||||||
.placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90))
|
.placeSketch(
|
||||||
|
cq.Sketch().trapezoid(
|
||||||
|
dim.hl_hinge_radius * 2 + 1, dim.hl_hinge_radius * 2, 90
|
||||||
|
)
|
||||||
|
)
|
||||||
.cutBlind(-1)
|
.cutBlind(-1)
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-width + hinge_width / 2)
|
.workplane(offset=-dim.width + dim.hl_hinge_width / 2)
|
||||||
.placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90))
|
.placeSketch(
|
||||||
|
cq.Sketch().trapezoid(
|
||||||
|
dim.hl_hinge_radius * 2 + 1, dim.hl_hinge_radius * 2, 90
|
||||||
|
)
|
||||||
|
)
|
||||||
.cutBlind(-1)
|
.cutBlind(-1)
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-width + hinge_width)
|
.workplane(offset=-dim.width + dim.hl_hinge_width)
|
||||||
.placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90))
|
.placeSketch(
|
||||||
|
cq.Sketch().trapezoid(
|
||||||
|
dim.hl_hinge_radius * 2 + 1, dim.hl_hinge_radius * 2, 90
|
||||||
|
)
|
||||||
|
)
|
||||||
.cutBlind(-1)
|
.cutBlind(-1)
|
||||||
# Threaded inserts
|
# Threaded inserts
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-hinge_width / 2)
|
.workplane(offset=-dim.hl_hinge_width / 2)
|
||||||
.placeSketch(cq.Sketch().circle(ti_radius))
|
.placeSketch(cq.Sketch().circle(dim.ti_radius))
|
||||||
.cutBlind(-ti_depth)
|
.cutBlind(-dim.ti_depth)
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-width + hinge_width / 2)
|
.workplane(offset=-dim.width + dim.hl_hinge_width / 2)
|
||||||
.placeSketch(cq.Sketch().circle(ti_radius))
|
.placeSketch(cq.Sketch().circle(dim.ti_radius))
|
||||||
.cutBlind(ti_depth)
|
.cutBlind(dim.ti_depth)
|
||||||
# Split two halves
|
# Split two halves
|
||||||
# First cut for the right hinge
|
# First cut for the right hinge
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
@ -148,140 +143,164 @@ def model():
|
|||||||
.polygon(
|
.polygon(
|
||||||
[
|
[
|
||||||
(0, 0),
|
(0, 0),
|
||||||
(-hinge_radius - 0.2, 0),
|
(-dim.hl_hinge_radius - 0.2, 0),
|
||||||
(-hinge_radius - hinge_slant, hinge_radius),
|
(-dim.hl_hinge_radius - dim.hl_hinge_slant, dim.hl_hinge_radius),
|
||||||
(0, hinge_radius),
|
(0, dim.hl_hinge_radius),
|
||||||
(0, 0),
|
(0, 0),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
.polygon(
|
.polygon(
|
||||||
[
|
[
|
||||||
(-hinge_radius - 0.2, 0),
|
(-dim.hl_hinge_radius - 0.2, 0),
|
||||||
(-hinge_radius - 0.2, -1000),
|
(-dim.hl_hinge_radius - 0.2, -1000),
|
||||||
(-hinge_radius, -1000),
|
(-dim.hl_hinge_radius, -1000),
|
||||||
(-hinge_radius, 0),
|
(-dim.hl_hinge_radius, 0),
|
||||||
(-hinge_radius - 0.2, 0),
|
(-dim.hl_hinge_radius - 0.2, 0),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
.circle(hinge_radius, mode="s")
|
.circle(dim.hl_hinge_radius, mode="s")
|
||||||
)
|
)
|
||||||
.cutBlind(-hinge_width / 2 - 1)
|
.cutBlind(-dim.hl_hinge_width / 2 - 1)
|
||||||
# Second cut for the right hinge
|
# Second cut for the right hinge
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-hinge_width / 2)
|
.workplane(offset=-dim.hl_hinge_width / 2)
|
||||||
.placeSketch(
|
.placeSketch(
|
||||||
cq.Sketch()
|
cq.Sketch()
|
||||||
.polygon(
|
.polygon(
|
||||||
[
|
[
|
||||||
(0, 0),
|
(0, 0),
|
||||||
(hinge_radius + 0.2, 0),
|
(dim.hl_hinge_radius + 0.2, 0),
|
||||||
(hinge_radius + 0.2 + hinge_slant, hinge_radius),
|
(
|
||||||
(0, hinge_radius),
|
dim.hl_hinge_radius + 0.2 + dim.hl_hinge_slant,
|
||||||
|
dim.hl_hinge_radius,
|
||||||
|
),
|
||||||
|
(0, dim.hl_hinge_radius),
|
||||||
(0, 0),
|
(0, 0),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
.circle(hinge_radius, mode="s")
|
.circle(dim.hl_hinge_radius, mode="s")
|
||||||
)
|
)
|
||||||
.cutBlind(-hinge_width / 2 - 1)
|
.cutBlind(-dim.hl_hinge_width / 2 - 1)
|
||||||
# First cut for the left hinge
|
# First cut for the left hinge
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-width + hinge_width)
|
.workplane(offset=-dim.width + dim.hl_hinge_width)
|
||||||
.placeSketch(
|
.placeSketch(
|
||||||
cq.Sketch()
|
cq.Sketch()
|
||||||
.polygon(
|
.polygon(
|
||||||
[
|
[
|
||||||
(0, 0),
|
(0, 0),
|
||||||
(hinge_radius + 0.2, 0),
|
(dim.hl_hinge_radius + 0.2, 0),
|
||||||
(hinge_radius + 0.2 + hinge_slant, hinge_radius),
|
(
|
||||||
(0, hinge_radius),
|
dim.hl_hinge_radius + 0.2 + dim.hl_hinge_slant,
|
||||||
|
dim.hl_hinge_radius,
|
||||||
|
),
|
||||||
|
(0, dim.hl_hinge_radius),
|
||||||
(0, 0),
|
(0, 0),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
.circle(hinge_radius, mode="s")
|
.circle(dim.hl_hinge_radius, mode="s")
|
||||||
)
|
)
|
||||||
.cutBlind(-hinge_width / 2 - 1)
|
.cutBlind(-dim.hl_hinge_width / 2 - 1)
|
||||||
# Second cut for the left hinge
|
# Second cut for the left hinge
|
||||||
.workplaneFromTagged("rightSide")
|
.workplaneFromTagged("rightSide")
|
||||||
.workplane(offset=-width + hinge_width / 2)
|
.workplane(offset=-dim.width + dim.hl_hinge_width / 2)
|
||||||
.placeSketch(
|
.placeSketch(
|
||||||
cq.Sketch()
|
cq.Sketch()
|
||||||
.polygon(
|
.polygon(
|
||||||
[
|
[
|
||||||
(0, 0),
|
(0, 0),
|
||||||
(-hinge_radius - 0.2, 0),
|
(-dim.hl_hinge_radius - 0.2, 0),
|
||||||
(-hinge_radius - hinge_slant, hinge_radius),
|
(-dim.hl_hinge_radius - dim.hl_hinge_slant, dim.hl_hinge_radius),
|
||||||
(0, hinge_radius),
|
(0, dim.hl_hinge_radius),
|
||||||
(0, 0),
|
(0, 0),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
.polygon(
|
.polygon(
|
||||||
[
|
[
|
||||||
(-hinge_radius - 0.2, 0),
|
(-dim.hl_hinge_radius - 0.2, 0),
|
||||||
(-hinge_radius - 0.2, -1000),
|
(-dim.hl_hinge_radius - 0.2, -1000),
|
||||||
(-hinge_radius, -1000),
|
(-dim.hl_hinge_radius, -1000),
|
||||||
(-hinge_radius, 0),
|
(-dim.hl_hinge_radius, 0),
|
||||||
(-hinge_radius - 0.2, 0),
|
(-dim.hl_hinge_radius - 0.2, 0),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
.circle(hinge_radius, mode="s")
|
.circle(dim.hl_hinge_radius, mode="s")
|
||||||
)
|
)
|
||||||
.cutBlind(-hinge_width / 2 - 1)
|
.cutBlind(-dim.hl_hinge_width / 2 - 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Screen mount
|
# Screen mount
|
||||||
model = (
|
model = (
|
||||||
|
# 1st layer
|
||||||
model.workplaneFromTagged("base")
|
model.workplaneFromTagged("base")
|
||||||
.center(0, -32)
|
.center(0, -32)
|
||||||
|
.workplane(offset=dim.hl_full_thickness / 2 - dim.shell_t)
|
||||||
.tag("screen_plane")
|
.tag("screen_plane")
|
||||||
.workplane(offset=thickness / 2 - shell_t)
|
|
||||||
.placeSketch(
|
.placeSketch(
|
||||||
cq.Sketch()
|
cq.Sketch()
|
||||||
.trapezoid(
|
.trapezoid(
|
||||||
screen_mount.scr_w + 2 * shell_t, screen_mount.scr_h + 2 * shell_t, 90
|
dim.scr_w + 2 * dim.hl_bezel_width,
|
||||||
|
dim.scr_h + 2 * dim.hl_bezel_height,
|
||||||
|
90,
|
||||||
)
|
)
|
||||||
.vertices()
|
.vertices()
|
||||||
.fillet(2)
|
.fillet(2)
|
||||||
)
|
)
|
||||||
.extrude(-9)
|
.extrude(-2 - dim.scr_thickness)
|
||||||
|
# Hole for screws
|
||||||
|
.workplaneFromTagged("screen_plane")
|
||||||
|
.workplane(offset=1)
|
||||||
|
.rect(
|
||||||
|
dim.scr_w + 2 * dim.hl_bezel_width - dim.m3_hn_diam - 1,
|
||||||
|
dim.scr_h + 2 * dim.hl_bezel_height - dim.m3_hn_diam - 1,
|
||||||
|
forConstruction=True,
|
||||||
|
)
|
||||||
|
.vertices()
|
||||||
|
.hole(dim.m3_hn_hole, depth=10)
|
||||||
|
# Holes for captured nuts
|
||||||
|
.workplaneFromTagged("screen_plane")
|
||||||
|
.workplane(offset=1)
|
||||||
|
.rect(
|
||||||
|
dim.scr_w + 2 * dim.hl_bezel_width - dim.m3_hn_diam - 1,
|
||||||
|
dim.scr_h + 2 * dim.hl_bezel_height - dim.m3_hn_diam - 1,
|
||||||
|
forConstruction=True,
|
||||||
|
)
|
||||||
|
.vertices()
|
||||||
|
.hole(dim.m3_hn_diam, depth=dim.m3_hn_thickness + 0.5)
|
||||||
|
# Remove middle of the screen holder
|
||||||
.workplaneFromTagged("screen_plane")
|
.workplaneFromTagged("screen_plane")
|
||||||
.workplane(offset=thickness / 2 - shell_t)
|
|
||||||
.placeSketch(
|
.placeSketch(
|
||||||
cq.Sketch()
|
cq.Sketch().trapezoid(
|
||||||
.trapezoid(screen_mount.vis_w, screen_mount.vis_h, 90)
|
dim.scr_w - 40,
|
||||||
.vertices()
|
dim.scr_h + 2 * dim.hl_bezel_height,
|
||||||
.fillet(2)
|
90,
|
||||||
)
|
)
|
||||||
.cutBlind(-9)
|
|
||||||
.workplaneFromTagged("screen_plane")
|
|
||||||
.workplane(offset=thickness / 2 - shell_t)
|
|
||||||
.placeSketch(
|
|
||||||
cq.Sketch()
|
|
||||||
.trapezoid(screen_mount.scr_w - 40, screen_mount.scr_h + 2 * shell_t, 90)
|
|
||||||
.vertices()
|
|
||||||
.fillet(2)
|
|
||||||
)
|
)
|
||||||
.cutBlind(-9)
|
.cutBlind(-100)
|
||||||
|
# Hole to place screen
|
||||||
.workplaneFromTagged("screen_plane")
|
.workplaneFromTagged("screen_plane")
|
||||||
.workplane(offset=thickness / 2 - shell_t - screen_mount.scr_thickness - 2)
|
.workplane(offset=-dim.scr_thickness - 2)
|
||||||
.placeSketch(cq.Sketch().trapezoid(screen_mount.scr_w, screen_mount.scr_h, 90))
|
.placeSketch(cq.Sketch().trapezoid(dim.scr_w, dim.scr_h, 90))
|
||||||
.cutBlind(screen_mount.scr_thickness)
|
.cutBlind(dim.scr_thickness)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cut off shape of the base
|
# Cut off shape of the base
|
||||||
|
|
||||||
model = (
|
model = (
|
||||||
model.workplaneFromTagged("rightSide")
|
model.workplaneFromTagged("rightSide")
|
||||||
.center(-height + hinge_offset, -thickness + hinge_radius)
|
.center(
|
||||||
|
-dim.height + dim.hl_hinge_offset,
|
||||||
|
-dim.hl_full_thickness + dim.hl_hinge_radius,
|
||||||
|
)
|
||||||
.placeSketch(
|
.placeSketch(
|
||||||
cq.Sketch().polygon(
|
cq.Sketch().polygon(
|
||||||
[
|
[
|
||||||
(0, 0),
|
(0, 0),
|
||||||
(0, keyboard.front_thickness),
|
(0, keyboard.kbd_front_thickness),
|
||||||
(shell_t, keyboard.front_thickness),
|
(dim.shell_t, keyboard.kbd_front_thickness),
|
||||||
(keyboard.actual_height + shell_t, keyboard.back_thickness),
|
(keyboard.kbd_actual_height + dim.shell_t, keyboard.kbd_back_thickness),
|
||||||
(keyboard.actual_height + shell_t, model_thickness),
|
(keyboard.kbd_actual_height + dim.shell_t, dim.base_thickness),
|
||||||
(height, model_thickness),
|
(dim.height, dim.base_thickness),
|
||||||
(height, 0),
|
(dim.height, 0),
|
||||||
(0, 0),
|
(0, 0),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -292,16 +311,106 @@ def model():
|
|||||||
return model
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
def front_bezel():
|
||||||
|
model = (
|
||||||
|
cq.Workplane("XY")
|
||||||
|
# Hollow box
|
||||||
|
.tag("base")
|
||||||
|
.placeSketch(
|
||||||
|
cq.Sketch()
|
||||||
|
.trapezoid(
|
||||||
|
dim.scr_w + 2 * dim.hl_bezel_width + 2 * dim.hl_bezel_thickness,
|
||||||
|
dim.scr_h + 2 * dim.hl_bezel_height + 2 * dim.hl_bezel_thickness,
|
||||||
|
90,
|
||||||
|
)
|
||||||
|
.vertices()
|
||||||
|
.fillet(2)
|
||||||
|
)
|
||||||
|
.extrude(-2 - dim.scr_thickness - dim.hl_bezel_thickness)
|
||||||
|
.workplaneFromTagged("base")
|
||||||
|
.workplane(offset=-dim.hl_bezel_thickness)
|
||||||
|
.placeSketch(
|
||||||
|
cq.Sketch()
|
||||||
|
.trapezoid(
|
||||||
|
dim.scr_w + 2 * dim.hl_bezel_width,
|
||||||
|
dim.scr_h + 2 * dim.hl_bezel_height,
|
||||||
|
90,
|
||||||
|
)
|
||||||
|
.vertices()
|
||||||
|
.fillet(2)
|
||||||
|
)
|
||||||
|
.cutBlind(-100)
|
||||||
|
# Holes for screws
|
||||||
|
.workplaneFromTagged("base")
|
||||||
|
.rect(
|
||||||
|
dim.scr_w + 2 * dim.hl_bezel_width - dim.m3_hn_diam - 1,
|
||||||
|
dim.scr_h + 2 * dim.hl_bezel_height - dim.m3_hn_diam - 1,
|
||||||
|
forConstruction=True,
|
||||||
|
)
|
||||||
|
.vertices()
|
||||||
|
.hole(dim.m3_hn_hole, depth=10)
|
||||||
|
# Viewport hole
|
||||||
|
.workplaneFromTagged("base")
|
||||||
|
.placeSketch(
|
||||||
|
cq.Sketch()
|
||||||
|
.trapezoid(
|
||||||
|
dim.vis_w,
|
||||||
|
dim.vis_h,
|
||||||
|
90,
|
||||||
|
)
|
||||||
|
.vertices()
|
||||||
|
.fillet(2)
|
||||||
|
)
|
||||||
|
.cutBlind("last")
|
||||||
|
# Cable gap
|
||||||
|
.workplaneFromTagged("base")
|
||||||
|
.workplane(offset=-dim.scr_thickness - dim.hl_bezel_thickness)
|
||||||
|
.center(0, 10)
|
||||||
|
.placeSketch(
|
||||||
|
cq.Sketch()
|
||||||
|
.trapezoid(
|
||||||
|
dim.vis_w,
|
||||||
|
dim.vis_h,
|
||||||
|
90,
|
||||||
|
)
|
||||||
|
.vertices()
|
||||||
|
.fillet(2)
|
||||||
|
)
|
||||||
|
.cutBlind(-10)
|
||||||
|
)
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
model = model()
|
model = model()
|
||||||
|
|
||||||
exporters.export(model, "hinged_lid.stl")
|
export(model, "hinged_lid.stl")
|
||||||
|
|
||||||
exporters.export(
|
export(front_bezel(), "hinged_lid_bezel.stl")
|
||||||
|
|
||||||
|
export(
|
||||||
model,
|
model,
|
||||||
"hinged_lid.svg",
|
"hinged_lid.svg",
|
||||||
opt={
|
opt={
|
||||||
"projectionDir": (0, 0, 1),
|
"projectionDir": (0, 0, -1),
|
||||||
"strokeWidth": 0.3,
|
"strokeWidth": 0.3,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
offset_width = -dim.width / 2
|
||||||
|
|
||||||
|
right_side = (
|
||||||
|
model.faces(">X")
|
||||||
|
.workplane(centerOption="CenterOfBoundBox", offset=offset_width)
|
||||||
|
.split(keepTop=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
export(right_side, "hinged_lid_right.stl")
|
||||||
|
|
||||||
|
left_side = (
|
||||||
|
model.faces(">X")
|
||||||
|
.workplane(centerOption="CenterOfBoundBox", offset=offset_width)
|
||||||
|
.split(keepBottom=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
export(left_side, "hinged_lid_left.stl")
|
||||||
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 192 KiB |
BIN
notebook_nueva/hinged_lid_bezel.stl
Normal file
BIN
notebook_nueva/hinged_lid_bezel.stl
Normal file
Binary file not shown.
BIN
notebook_nueva/hinged_lid_left.stl
Normal file
BIN
notebook_nueva/hinged_lid_left.stl
Normal file
Binary file not shown.
BIN
notebook_nueva/hinged_lid_right.stl
Normal file
BIN
notebook_nueva/hinged_lid_right.stl
Normal file
Binary file not shown.
Binary file not shown.
@ -1,109 +0,0 @@
|
|||||||
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",
|
|
||||||
)
|
|
@ -1,242 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
# Base for the notebook. Basically a kbd base that extends back
|
|
||||||
# as much as possible
|
|
||||||
|
|
||||||
# Thickness of the outer material
|
|
||||||
shell_t = 3
|
|
||||||
|
|
||||||
# Size of the kbd board
|
|
||||||
kbd_height = 95.5
|
|
||||||
kbd_width = 305
|
|
||||||
|
|
||||||
# Size of the whole object
|
|
||||||
width = kbd_width + 2 * shell_t
|
|
||||||
height = 159
|
|
||||||
thickness = 27 + shell_t # 27 inside
|
|
||||||
|
|
||||||
# Insert Positions
|
|
||||||
ti_radius = 2.35
|
|
||||||
ti_depth = 6.25
|
|
||||||
|
|
||||||
# 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, 48),
|
|
||||||
(120, 6),
|
|
||||||
(170, 6),
|
|
||||||
(width - 6, 6),
|
|
||||||
(width - 6, 30),
|
|
||||||
(120, 48),
|
|
||||||
(170, 48),
|
|
||||||
]
|
|
||||||
screen_pillars.init(mounting_pillar_positions, thickness - shell_t)
|
|
||||||
|
|
||||||
# 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 = 177
|
|
||||||
cpu_offset_y = 2
|
|
||||||
|
|
||||||
# Battery holder position from back-left corner of the case
|
|
||||||
battery_offset_x = 15
|
|
||||||
battery_offset_y = 3
|
|
||||||
|
|
||||||
|
|
||||||
def model():
|
|
||||||
# Create the basic shape of the case bottom.
|
|
||||||
model = (
|
|
||||||
cq.Workplane("XY")
|
|
||||||
.workplane(offset=thickness / 2)
|
|
||||||
.tag("mid_height")
|
|
||||||
# Hollow box
|
|
||||||
.box(width, height, thickness)
|
|
||||||
.edges("|Z")
|
|
||||||
.fillet(2)
|
|
||||||
.faces(">Z")
|
|
||||||
.shell(-shell_t)
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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=usb_offset_x,
|
|
||||||
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=width - audio_plug.item_w,
|
|
||||||
offset_y=40,
|
|
||||||
bottom_face="<Z",
|
|
||||||
back_face=">X",
|
|
||||||
shell_t=shell_t,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Hole for HDMI out in the back
|
|
||||||
model = hdmi_out.add(
|
|
||||||
model=model,
|
|
||||||
width=width,
|
|
||||||
height=height,
|
|
||||||
thickness=thickness,
|
|
||||||
offset_x=138,
|
|
||||||
offset_y=0,
|
|
||||||
bottom_face=None,
|
|
||||||
back_face=">Y",
|
|
||||||
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), (width / 2, 0), (width / 2, -height), (0, -height), (0, 0)],
|
|
||||||
mode="a",
|
|
||||||
)
|
|
||||||
|
|
||||||
right_side = (
|
|
||||||
model.faces("<Z")
|
|
||||||
.workplaneFromTagged("mid_height")
|
|
||||||
.transformed(offset=cq.Vector(0, 0, -thickness / 2))
|
|
||||||
.center(-width / 2, height / 2)
|
|
||||||
.placeSketch(left_cutout)
|
|
||||||
.cutBlind(100)
|
|
||||||
)
|
|
||||||
|
|
||||||
exporters.export(right_side, "right_side.stl")
|
|
||||||
|
|
||||||
right_cutout = cq.Sketch().polygon(
|
|
||||||
[
|
|
||||||
(width / 2, 0),
|
|
||||||
(width, 0),
|
|
||||||
(width, -height),
|
|
||||||
(width / 2, -height),
|
|
||||||
(width / 2, 0),
|
|
||||||
],
|
|
||||||
mode="a",
|
|
||||||
)
|
|
||||||
|
|
||||||
left_side = (
|
|
||||||
model.faces("<Z")
|
|
||||||
.workplaneFromTagged("mid_height")
|
|
||||||
.transformed(offset=cq.Vector(0, 0, -thickness / 2))
|
|
||||||
.center(-width / 2, height / 2)
|
|
||||||
.placeSketch(right_cutout)
|
|
||||||
.cutBlind(100)
|
|
||||||
)
|
|
||||||
exporters.export(left_side, "left_side.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,
|
|
||||||
},
|
|
||||||
)
|
|
@ -1,2 +1,3 @@
|
|||||||
cadquery
|
cadquery
|
||||||
git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
|
git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
|
||||||
|
flake8
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 227 KiB |
Binary file not shown.
Binary file not shown.
@ -1,55 +1,34 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
from cadquery import exporters
|
|
||||||
|
|
||||||
from modelo import (
|
import dimensions as dim
|
||||||
kbd_height,
|
from utils import export
|
||||||
kbd_width,
|
from components import screen_pillars
|
||||||
mounting_pillar_positions,
|
|
||||||
shell_t,
|
viewport_cutout = (
|
||||||
ti_depth,
|
cq.Sketch().trapezoid(dim.vis_w, dim.vis_h, 90, mode="a").vertices().fillet(2)
|
||||||
ti_radius,
|
|
||||||
)
|
)
|
||||||
|
screen_cutout = cq.Sketch().trapezoid(dim.scr_w, dim.scr_h, 90, mode="a")
|
||||||
# Size of the whole object
|
|
||||||
width = kbd_width + 2 * shell_t
|
|
||||||
height = 66
|
|
||||||
height_bottom = 59
|
|
||||||
thickness = 48 # Will be shorter after construction
|
|
||||||
|
|
||||||
# Visible screen size
|
|
||||||
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
|
|
||||||
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.
|
# Circuit board and cable hole.
|
||||||
# This is in the back of the screen, and is a bit shorter in height than the
|
# 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.
|
# screen. It's wider so it removes enough material to make the shape simpler.
|
||||||
board_cutout = cq.Sketch().trapezoid(
|
board_cutout = cq.Sketch().trapezoid(
|
||||||
scr_w + 5,
|
dim.scr_w + 5,
|
||||||
scr_h - 10,
|
dim.scr_h - 10,
|
||||||
90,
|
90,
|
||||||
mode="a",
|
mode="a",
|
||||||
)
|
)
|
||||||
|
|
||||||
kbd_cable_hole = cq.Sketch().trapezoid(20, 9, 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
|
# y needs to be inverted because this is the top side, and there's 2 pillars we don't use
|
||||||
mounting_pillar_positions = [(x, -y) for x, y in mounting_pillar_positions[:-2]]
|
mounting_pillar_positions = [(x, -y) for x, y in dim.mounting_pillar_positions[:-2]]
|
||||||
|
|
||||||
mounting_pillars = (
|
mounting_pillars = (
|
||||||
cq.Sketch()
|
cq.Sketch()
|
||||||
.push(mounting_pillar_positions)
|
.push(dim.mounting_pillar_positions)
|
||||||
.trapezoid(-12, 12, 90, mode="a")
|
.trapezoid(screen_pillars.pillar_width, screen_pillars.pillar_height, 90, mode="a")
|
||||||
.circle(ti_radius, mode="s")
|
.circle(dim.ti_radius, mode="s")
|
||||||
.clean()
|
.clean()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -59,10 +38,10 @@ def model():
|
|||||||
cq.Workplane("XY")
|
cq.Workplane("XY")
|
||||||
.workplane()
|
.workplane()
|
||||||
.tag("mid_height")
|
.tag("mid_height")
|
||||||
.box(width, height, thickness)
|
.box(dim.width, dim.tl_height, dim.tl_full_thickness)
|
||||||
# The screen goes rotated
|
# The screen goes rotated
|
||||||
.faces(">Z")
|
.faces(">Z")
|
||||||
.transformed(rotate=(scr_angle, 0, 0))
|
.transformed(rotate=(dim.tl_scr_angle, 0, 0))
|
||||||
# Move the screen "lower" so it doesn't interfere
|
# Move the screen "lower" so it doesn't interfere
|
||||||
# so much with the back
|
# so much with the back
|
||||||
.center(0, -2)
|
.center(0, -2)
|
||||||
@ -79,18 +58,18 @@ def model():
|
|||||||
# Make bottom smaller to fit with base
|
# Make bottom smaller to fit with base
|
||||||
.faces(">X")
|
.faces(">X")
|
||||||
.workplane(centerOption="CenterOfBoundBox")
|
.workplane(centerOption="CenterOfBoundBox")
|
||||||
.center(-height / 2, -thickness / 2)
|
.center(-dim.tl_height / 2, -dim.tl_full_thickness / 2)
|
||||||
.placeSketch(
|
.placeSketch(
|
||||||
cq.Sketch()
|
cq.Sketch()
|
||||||
.polygon(
|
.polygon(
|
||||||
[
|
[
|
||||||
(height_bottom, 0),
|
(dim.tl_height_bottom, 0),
|
||||||
(height_bottom, thickness / 3),
|
(dim.tl_height_bottom, dim.tl_full_thickness / 3),
|
||||||
(height, thickness - 21),
|
(dim.tl_height, dim.tl_full_thickness - 21),
|
||||||
(height, thickness),
|
(dim.tl_height, dim.tl_full_thickness),
|
||||||
(height + 5, thickness + 5),
|
(dim.tl_height + 5, dim.tl_full_thickness + 5),
|
||||||
(height + 5, 0),
|
(dim.tl_height + 5, 0),
|
||||||
(height_bottom, 0),
|
(dim.tl_height_bottom, 0),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
.vertices()
|
.vertices()
|
||||||
@ -103,26 +82,26 @@ def model():
|
|||||||
# Cut off viewport hole so we can see the screen
|
# Cut off viewport hole so we can see the screen
|
||||||
.workplaneFromTagged("slanted")
|
.workplaneFromTagged("slanted")
|
||||||
.placeSketch(viewport_cutout)
|
.placeSketch(viewport_cutout)
|
||||||
.cutBlind(-shell_t)
|
.cutBlind(-dim.shell_t)
|
||||||
# Make hole for screen assembly so the whole screen fits
|
# Make hole for screen assembly so the whole screen fits
|
||||||
.workplaneFromTagged("slanted")
|
.workplaneFromTagged("slanted")
|
||||||
.workplane(offset=-shell_t, centerOption="CenterOfBoundBox")
|
.workplane(offset=-dim.shell_t, centerOption="CenterOfBoundBox")
|
||||||
# Left bezel is wider than right one, so this hole is displaced to the left
|
# Left bezel is wider than right one, so this hole is displaced to the left
|
||||||
.center(-3, 0)
|
.center(-3, 0)
|
||||||
.placeSketch(screen_cutout)
|
.placeSketch(screen_cutout)
|
||||||
.cutBlind(-scr_thickness)
|
.cutBlind(-dim.scr_thickness)
|
||||||
# Make it hollow
|
# Make it hollow
|
||||||
.faces("<Z")
|
.faces("<Z")
|
||||||
# Can't be exactly shell_t because cq fails
|
# Can't be exactly shell_t because cq fails
|
||||||
.shell(-shell_t + 0.01)
|
.shell(-dim.shell_t + 0.01)
|
||||||
# Cut hole for the screen board and cables
|
# Cut hole for the screen board and cables
|
||||||
.workplaneFromTagged("slanted")
|
.workplaneFromTagged("slanted")
|
||||||
.workplane(offset=-scr_thickness, centerOption="CenterOfBoundBox")
|
.workplane(offset=-dim.scr_thickness, centerOption="CenterOfBoundBox")
|
||||||
.placeSketch(board_cutout)
|
.placeSketch(board_cutout)
|
||||||
.cutBlind(-6)
|
.cutBlind(-6)
|
||||||
.workplaneFromTagged("mid_height")
|
.workplaneFromTagged("mid_height")
|
||||||
.workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox")
|
.workplane(offset=-dim.tl_full_thickness / 2, centerOption="CenterOfBoundBox")
|
||||||
.center(-width / 2, height_bottom - height / 2 - shell_t)
|
.center(-dim.width / 2, dim.tl_height_bottom - dim.tl_height / 2 - dim.shell_t)
|
||||||
.placeSketch(mounting_pillars)
|
.placeSketch(mounting_pillars)
|
||||||
.extrude(10)
|
.extrude(10)
|
||||||
# Fillet the front edge of the screen case so it looks softer
|
# Fillet the front edge of the screen case so it looks softer
|
||||||
@ -132,25 +111,23 @@ def model():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("Exporting")
|
model = model()
|
||||||
exporters.export(model(), "screen_mount.stl")
|
export(model, "tandy_lid.stl")
|
||||||
|
|
||||||
offset_width = -width / 2
|
offset_width = -dim.width / 2
|
||||||
|
|
||||||
right_side = (
|
right_side = (
|
||||||
model()
|
model.faces(">X")
|
||||||
.faces(">X")
|
|
||||||
.workplane(centerOption="CenterOfBoundBox", offset=offset_width)
|
.workplane(centerOption="CenterOfBoundBox", offset=offset_width)
|
||||||
.split(keepTop=True)
|
.split(keepTop=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
exporters.export(right_side, "right_screen_mount.stl")
|
export(right_side, "tandy_lid_right.stl")
|
||||||
|
|
||||||
left_side = (
|
left_side = (
|
||||||
model()
|
model.faces(">X")
|
||||||
.faces(">X")
|
|
||||||
.workplane(centerOption="CenterOfBoundBox", offset=offset_width)
|
.workplane(centerOption="CenterOfBoundBox", offset=offset_width)
|
||||||
.split(keepBottom=True)
|
.split(keepBottom=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
exporters.export(left_side, "left_screen_mount.stl")
|
export(left_side, "tandy_lid_left.stl")
|
BIN
notebook_nueva/tandy_lid.stl
Normal file
BIN
notebook_nueva/tandy_lid.stl
Normal file
Binary file not shown.
BIN
notebook_nueva/tandy_lid_left.stl
Normal file
BIN
notebook_nueva/tandy_lid_left.stl
Normal file
Binary file not shown.
BIN
notebook_nueva/tandy_lid_right.stl
Normal file
BIN
notebook_nueva/tandy_lid_right.stl
Normal file
Binary file not shown.
@ -1,6 +1,10 @@
|
|||||||
import cadquery as cq
|
import shutil
|
||||||
|
import tempfile
|
||||||
from math import floor
|
from math import floor
|
||||||
|
|
||||||
|
import cadquery as cq
|
||||||
|
from cadquery import exporters
|
||||||
|
|
||||||
|
|
||||||
def extrude_shape(*, model, face, w, h, x_offset, y_offset, element, height):
|
def extrude_shape(*, model, face, w, h, x_offset, y_offset, element, height):
|
||||||
return (
|
return (
|
||||||
@ -71,3 +75,9 @@ def hex_vents(*, size, width, height, density=0.85):
|
|||||||
]
|
]
|
||||||
|
|
||||||
return vents
|
return vents
|
||||||
|
|
||||||
|
|
||||||
|
def export(model, fname, **kwarg):
|
||||||
|
tmpfile = tempfile.mktemp(suffix="." + fname.split(".")[-1])
|
||||||
|
exporters.export(model, tmpfile, **kwarg)
|
||||||
|
shutil.move(tmpfile, fname)
|
||||||
|
Reference in New Issue
Block a user