Compare commits
587 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
31bfa3b268 | ||
|
ec44fe96b4 | ||
|
9e50d1217d | ||
|
87c8bbb9a5 | ||
|
ed1cc8b488 | ||
|
4cac382581 | ||
|
04b98a3786 | ||
|
6f8ff606fa | ||
|
acd5de0fbd | ||
|
cc1e3baaf6 | ||
|
ab50b6f9a6 | ||
|
d9cb604f92 | ||
|
52d2a49e1c | ||
|
85cb54ef31 | ||
|
cd4447a2e6 | ||
|
9699a70cf5 | ||
|
6d79e587aa | ||
|
15b3b3a8c2 | ||
|
ec891c8013 | ||
|
c7811f21e0 | ||
|
782deccf6b | ||
|
69da4c1c23 | ||
|
dad2bccbaa | ||
|
0a00f244e4 | ||
|
76ea28e88e | ||
|
44cf9e910b | ||
|
0261362794 | ||
|
2e6b55bcbc | ||
|
20901f0356 | ||
|
77ffdcb1ac | ||
|
6ab4bad97a | ||
|
14c6219733 | ||
|
b21b7b9de0 | ||
|
73d814d2fe | ||
|
fa658d9eaa | ||
|
6d3f54b7a5 | ||
|
157ff60e19 | ||
|
cbd3cf29af | ||
|
bf618bb482 | ||
|
1e6f0a5c4d | ||
|
53c3cdb598 | ||
|
f4857f6862 | ||
|
1acc8d01c0 | ||
|
344e8d1583 | ||
|
dd757a1461 | ||
|
b4f8892b1a | ||
|
4b033d9945 | ||
|
d5a711f4cc | ||
|
42b76ab8d3 | ||
|
f18044915d | ||
|
17b12c7f31 | ||
|
18ff4c6d46 | ||
|
2eecce819c | ||
|
f7d81738bb | ||
|
6d44124bab | ||
|
9bb9f09dca | ||
|
773a53829f | ||
|
07766d8cf0 | ||
|
ceac5cdb27 | ||
|
c5b35daeac | ||
|
ffb4512523 | ||
|
35ffbad74c | ||
|
fb685a0f42 | ||
|
5d42b2e1ab | ||
|
2fe815d1bd | ||
|
5c577cccd0 | ||
|
1dbfafd366 | ||
|
68b3dfb098 | ||
|
25dceee20a | ||
|
d70ddf5359 | ||
|
70b60522ce | ||
|
ecba7eaea4 | ||
|
f751dd9a73 | ||
|
3f359f6839 | ||
|
3e5947c161 | ||
|
66dc430541 | ||
|
4dc83d62cb | ||
|
ebbec3c903 | ||
|
9944aab73e | ||
|
eb9bcf0ada | ||
|
ff5e8c0372 | ||
|
17ebf36e27 | ||
|
e38d9abfa0 | ||
|
fc7fd5482e | ||
|
cee1202fd9 | ||
|
6e342441c6 | ||
|
072c38f955 | ||
|
b342549d74 | ||
|
2b83a15e5d | ||
|
ab81c6538c | ||
|
27b0a442e4 | ||
|
38acef9e27 | ||
|
5415beb80d | ||
|
040985c0db | ||
|
0216093a68 | ||
|
30302431c0 | ||
|
1fb429e9a5 | ||
|
9571e68629 | ||
|
b01e6a673c | ||
|
9239c6da3c | ||
|
ba5e5fa390 | ||
|
c7dfdd0fb9 | ||
|
814ce4f15d | ||
|
f661cf6934 | ||
|
305d2146f2 | ||
|
e39ee1797d | ||
|
520569cb30 | ||
|
f73a7b46a2 | ||
|
fb9eca85c6 | ||
|
166ed05d4a | ||
|
ce6aec428d | ||
|
4e9d169c31 | ||
|
1810160103 | ||
|
0c9ae8d60c | ||
|
9a0bad4e61 | ||
|
90047815b0 | ||
|
b583202fb7 | ||
|
eac0086199 | ||
|
03beaec470 | ||
|
51c649cc53 | ||
|
5fa33d7c4d | ||
|
78ce51d045 | ||
|
23cbadf6df | ||
|
c9c2ffafba | ||
|
2e0e833d40 | ||
|
6c51f8726c | ||
|
0b035dbd15 | ||
|
34b58e3b64 | ||
|
df43fe7dc6 | ||
|
b5fe03fcb2 | ||
|
1658f6f0b4 | ||
|
7b126f9792 | ||
|
479207fd4f | ||
|
3ee55981f9 | ||
|
8c2b4a20fe | ||
|
1529759406 | ||
|
c4a986aa21 | ||
|
ebee729d08 | ||
|
90e7f1a315 | ||
|
e39af154bb | ||
|
933fea687c | ||
|
a7803b1efb | ||
|
1255e71271 | ||
|
b11c5914b3 | ||
|
ac60057801 | ||
|
332933a4fd | ||
|
6b0132c32e | ||
|
afac5f9737 | ||
|
8d8df3cb8a | ||
|
81eb183db9 | ||
|
c99ed98a64 | ||
|
7f65e5d539 | ||
|
ffb7f87cc5 | ||
|
d0513c7299 | ||
|
d1429a3b7d | ||
|
70513993bd | ||
|
9eb35accfd | ||
|
7276f18566 | ||
|
d944198dc4 | ||
|
04f2499a9e | ||
|
1eb8b378e9 | ||
|
362dbdb4fc | ||
|
57d223d84b | ||
|
699385342f | ||
|
547a418cea | ||
|
b6d25048bc | ||
|
4cdab218d9 | ||
|
b6147e5684 | ||
|
966ba536ed | ||
|
2419d50641 | ||
|
02211c2034 | ||
|
77d73b075d | ||
|
cb54a3131b | ||
|
3cf275579c | ||
|
fb41f218fe | ||
|
e6a26bc7b1 | ||
|
cb4fa40643 | ||
|
6a26903514 | ||
|
d08d949887 | ||
|
574a73e527 | ||
|
87a35126de | ||
|
1ca485b66b | ||
|
bc919529d3 | ||
|
9f4ed2b915 | ||
|
7ce055373a | ||
|
71ac571346 | ||
|
e4d93366fa | ||
|
f047ac27f7 | ||
|
a9e479d971 | ||
|
47b01af1ea | ||
|
fe19eba237 | ||
|
235f7b86e3 | ||
|
92d7e18b16 | ||
|
6a7226120f | ||
|
8aa00cd041 | ||
|
f6b512da1f | ||
|
c7ea0939b9 | ||
|
265b5ab555 | ||
|
186dbbfd08 | ||
|
60659a43f8 | ||
|
f412cb1736 | ||
|
2b878556fc | ||
|
1f1a360b7c | ||
|
a547c98995 | ||
|
d9fa8c8668 | ||
|
bf5b6d7c30 | ||
|
9cfde7f524 | ||
|
184f19ef04 | ||
|
c88472121e | ||
|
a74bf094aa | ||
|
5a06f79466 | ||
|
98e17080d8 | ||
|
d3f308a45e | ||
|
fe454884e0 | ||
|
c019448dd3 | ||
|
eadc541e8f | ||
|
02791c40ac | ||
|
85b8ffbbc3 | ||
|
dd5d3869ad | ||
|
b84eb3cf31 | ||
|
0ec7aabcfb | ||
|
baa737c4d8 | ||
|
70b13d2f27 | ||
|
c9aad0178e | ||
|
df96551b11 | ||
|
a5a360e0d1 | ||
|
828e5ad36e | ||
|
cedaafed3d | ||
|
0d38d82416 | ||
|
041341b946 | ||
|
70622ba8de | ||
|
4ab0a981ef | ||
|
bb7dd51270 | ||
|
48293b9abd | ||
|
c9d10eeb8b | ||
|
192460c0fa | ||
|
d4402c6713 | ||
|
b9890ca589 | ||
|
026b9daf59 | ||
|
2afc00cfa9 | ||
|
84b5686af6 | ||
|
71af8f98ed | ||
|
6cecb4d466 | ||
|
4ef926a18f | ||
|
5afc4f816c | ||
|
5be14f5e89 | ||
|
efff17dfc7 | ||
|
ac4a5500a9 | ||
|
6b90a7aac8 | ||
|
3c3d72f366 | ||
|
dbcab8377f | ||
|
11d96d37e1 | ||
|
40843b421f | ||
|
cc8f4d3bd3 | ||
|
69d6fc8bf0 | ||
|
01b1e2e84d | ||
|
cb64cadb92 | ||
|
b57b50d2b0 | ||
|
8f282775a4 | ||
|
bf833b0452 | ||
|
52011fd6f7 | ||
|
9da8634769 | ||
|
147ff9b24f | ||
|
b4379907a2 | ||
|
3be88f6517 | ||
|
d42f99e437 | ||
|
65455930f8 | ||
|
7e0c5fdb6e | ||
|
bc4e18d788 | ||
|
f5980b4703 | ||
|
7b3d7fab55 | ||
|
654571a70e | ||
|
e8abcde52f | ||
|
390957fdd0 | ||
|
bde8cbe7a6 | ||
|
fbe8533a42 | ||
|
a9c2f854c6 | ||
|
6187d90c57 | ||
|
65f320141d | ||
|
d367e743da | ||
|
3a4305f5ca | ||
|
a7dde2d4e2 | ||
|
56390bf8dd | ||
|
53f0bbcd6e | ||
|
f2ec3e71f4 | ||
|
c1b5bd1b87 | ||
|
268c066965 | ||
|
fd8712d6bf | ||
|
b6a32b6b41 | ||
|
0738893510 | ||
|
849bc479cc | ||
|
86d7e0f124 | ||
|
c897060726 | ||
|
b2c2fc668b | ||
|
4914f90994 | ||
|
2210396234 | ||
|
2eef050f60 | ||
|
23a64f238d | ||
|
a8422a6aa6 | ||
|
b56ddea1e3 | ||
|
c16a1c3804 | ||
|
245b158f1d | ||
|
60628dfec0 | ||
|
f7db793c74 | ||
|
53292c9f89 | ||
|
bd60b50b09 | ||
|
4d51cb73f3 | ||
|
3b77c97532 | ||
|
4442042197 | ||
|
4e233e5e9d | ||
|
675953d334 | ||
|
9527fa1c1f | ||
|
3f209cbdd3 | ||
|
7b76aceaad | ||
|
4edd08977f | ||
|
a1d385f9ec | ||
|
fc17b52518 | ||
|
9fbdb82991 | ||
|
7706716fa2 | ||
|
c815c1592d | ||
|
e32ef88c90 | ||
|
702c450a27 | ||
|
343351155c | ||
|
c41b1fa115 | ||
|
1a197ac823 | ||
|
ab592e049c | ||
|
9b104e4fe7 | ||
|
f0b4d767e9 | ||
|
82baed4c8f | ||
|
acb2cb2a48 | ||
|
cf94972b18 | ||
|
7c0a48d0c1 | ||
|
a5a640e273 | ||
|
95c4359421 | ||
|
bcff26a27b | ||
|
07b00fabe0 | ||
|
8ed0f3c1af | ||
|
0e8e387d35 | ||
|
0c17620adb | ||
|
6470f1b004 | ||
|
29ba402530 | ||
|
aed4a699f2 | ||
|
10b846c0d5 | ||
|
2de479d691 | ||
|
054dab5025 | ||
|
b559319b03 | ||
|
a487ca0f20 | ||
|
a2ce3f1278 | ||
|
01eb6931d9 | ||
|
c09a74b4c9 | ||
|
aab7f3d683 | ||
|
ad719dad5a | ||
|
00690b61d2 | ||
|
1a24e0d87e | ||
|
171c9a6a34 | ||
|
8bb4b3881e | ||
|
79f8bc66e1 | ||
|
33c9d158ef | ||
|
d2874a0531 | ||
|
b6fd795a25 | ||
|
ad0882db40 | ||
|
8afb08aaa4 | ||
|
e87dffd92c | ||
|
c364bf06b2 | ||
|
1668f9c54c | ||
|
e0dd174010 | ||
|
f104bd42e5 | ||
|
8342ba87e9 | ||
|
0e58e92fbc | ||
|
1af2e18594 | ||
|
598527edbe | ||
|
9c666f8f47 | ||
|
bc1f135e40 | ||
|
ce36729e50 | ||
|
1dca024e64 | ||
|
c1833f0820 | ||
|
6a7f7dcbbf | ||
|
5d3f1115bb | ||
|
9f9adeb6ca | ||
|
f9b06c855c | ||
|
05bfe9b159 | ||
|
f961874ce3 | ||
|
7a6210f442 | ||
|
5bae0aaa51 | ||
|
8649d59e8a | ||
|
e9bc300b8d | ||
|
dcb1e74894 | ||
|
42b9479094 | ||
|
f46f35e909 | ||
|
0513b151b2 | ||
|
dcf258f11a | ||
|
4ff6c7d0a8 | ||
|
2edb6d4df2 | ||
|
26e8497018 | ||
|
bfde879ce2 | ||
|
4641e3a642 | ||
|
42cfed8846 | ||
|
8c339a18c4 | ||
|
065ec0a430 | ||
|
e7eea0520c | ||
|
5cb994b58b | ||
|
573425055a | ||
|
66a7a9bfe0 | ||
|
0335545334 | ||
|
fa91acbaad | ||
|
8bad05e721 | ||
|
9d6727d371 | ||
|
8902d83f0a | ||
|
e40b10f5a0 | ||
|
66a8d1d583 | ||
|
d066648a76 | ||
|
b8546414c0 | ||
|
581c2f8a96 | ||
|
e9ee88dfe9 | ||
|
b07a8ad245 | ||
|
ab17de3b0b | ||
|
0b8141844c | ||
|
7d0548b033 | ||
|
ad3e8d85af | ||
|
8435a350d0 | ||
|
3e5d4b1c8e | ||
|
20f830a008 | ||
|
10bd8ff354 | ||
|
4459cca3ea | ||
|
95bbebd42d | ||
|
cc84a5a536 | ||
|
28c795e9f4 | ||
|
9a0477d16b | ||
|
0583da2eb7 | ||
|
9a08fe9b5c | ||
|
8ab7993148 | ||
|
16dd8d6d17 | ||
|
08c268145e | ||
|
ba7e64233a | ||
|
7090c714ce | ||
|
c5038db6d9 | ||
|
654f094304 | ||
|
1e5e5860e1 | ||
|
a08216d0b8 | ||
|
3d8a9ec8aa | ||
|
7ec059142f | ||
|
7f0d96b824 | ||
|
9e826c1a09 | ||
|
2c1dbe04a9 | ||
|
d644d6b698 | ||
|
8db4cc2cb5 | ||
|
509a87939c | ||
|
40607e6cfc | ||
|
599fbba6c2 | ||
|
52729d012c | ||
|
6097e07094 | ||
|
9d71438a3c | ||
|
ffa1ab940b | ||
|
3174013e1a | ||
|
c4eea38a2b | ||
|
0cd89279a5 | ||
|
92051e0b28 | ||
|
f4b22e35c7 | ||
|
17ecfc07f3 | ||
|
9658205efd | ||
|
53140a4cc1 | ||
|
2798d39538 | ||
|
8be0cc98ea | ||
|
6a5f31edd8 | ||
|
e068918e21 | ||
|
1614f50b73 | ||
|
32522b28d7 | ||
|
1936c95d06 | ||
|
16060629c0 | ||
|
b7b5c837bd | ||
|
a793cb6d43 | ||
|
dc4e24b63a | ||
|
8f85ac73dc | ||
|
3ab934d83e | ||
|
d1324a670e | ||
|
f5fdec613c | ||
|
c55b8b6d1c | ||
|
56ec8e03ad | ||
|
728b7adf38 | ||
|
75747687d9 | ||
|
03c97e8b6a | ||
|
8ac06b53e7 | ||
|
de76eb46e7 | ||
|
2c77f184a2 | ||
|
4d3d9dfdfe | ||
|
f7a972f946 | ||
|
4e81fcbd4f | ||
|
1f038decd4 | ||
|
da958fe112 | ||
|
61493eaa34 | ||
|
804c00bdcb | ||
|
11ea68681f | ||
|
d703ae4997 | ||
|
ed97d226f8 | ||
|
4107a2c848 | ||
|
f7ef075434 | ||
|
6f93b6af9a | ||
|
53f416eef1 | ||
|
6354219627 | ||
|
56e2b71bda | ||
|
8f5503586d | ||
|
1cd9edfe87 | ||
|
a85fdaf176 | ||
|
18294b4b81 | ||
|
ad62ce362c | ||
|
9f27f26894 | ||
|
2eaa4bfc21 | ||
|
1944039f22 | ||
|
1c221ad612 | ||
|
85adf7b4f4 | ||
|
64bde2cb3a | ||
|
c34469e852 | ||
|
dbc3c36f44 | ||
|
8c51183ba6 | ||
|
a9c4e60cac | ||
|
d041b18025 | ||
|
e85887fec4 | ||
|
ededb514b8 | ||
|
d0d525b97a | ||
|
371d274906 | ||
|
7118e6eb03 | ||
|
46004381b7 | ||
|
cfdf759a49 | ||
|
aeded1b807 | ||
|
79f1c95136 | ||
|
9bb84593be | ||
|
ab91defcd2 | ||
|
7c1ff5ecd5 | ||
|
6f4859a4b5 | ||
|
e35fb695a2 | ||
|
61bec656d7 | ||
|
3a087be0e9 | ||
|
854adab665 | ||
|
77aa8fe44d | ||
|
6fe4548213 | ||
|
b7654f0384 | ||
|
312f12dfd0 | ||
|
be3999ed3e | ||
|
566cbce98f | ||
|
b8dba626d2 | ||
|
2adb936f41 | ||
|
017ec480c0 | ||
|
e3a500e9c6 | ||
|
4ac48c9603 | ||
|
78ce316b86 | ||
|
ec274fdca1 | ||
|
3640963da1 | ||
|
466a7a667d | ||
|
be324c31da | ||
|
49c3b6be2c | ||
|
8583f5472d | ||
|
645ff21d3d | ||
|
4ff12b1d8b | ||
|
847dba544f | ||
|
13c654a10e | ||
|
49ee92db8e | ||
|
be14a52c21 | ||
|
af4c5e2b3e | ||
|
f85a7e85be | ||
|
c68f879d13 | ||
|
3d4653fc0f | ||
|
b40c076d23 | ||
|
5ae040079e | ||
|
881a032aa3 | ||
|
4b1d6298e4 | ||
|
b4cc6f6c37 | ||
|
6238f82bf0 | ||
|
fbc688c81f | ||
|
7b2b239a8b | ||
|
de8eb594a7 | ||
|
f061b0a2f8 | ||
|
43b17c6f0b | ||
|
886319a6e4 | ||
|
723be7ac64 | ||
|
3a5e027bbb | ||
|
d068b972ff | ||
|
0fb0432b39 | ||
|
107951aad0 | ||
|
55fd552c8f | ||
|
8014aadd1d | ||
|
a3e51d2607 | ||
|
8ea7d92997 | ||
|
e386ce7927 | ||
|
fbc0b1d27d | ||
|
5f287ef8ad | ||
|
0ccda3378e | ||
|
c155e95599 |
16
.gitattributes
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto eol=lf
|
||||
|
||||
# Declare text files that are normalized and converted on checkout.
|
||||
*.scad text
|
||||
*.py text
|
||||
*.md text
|
||||
|
||||
# Handle Windows batch and command files
|
||||
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||
*.{cmd,[cC][mM][dD]} text eol=crlf
|
||||
|
||||
# Denote files that are binary and should not be modified.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
|
1
.gitignore
vendored
@@ -7,6 +7,7 @@ tests/bom/
|
||||
*.log
|
||||
*.html
|
||||
bounds.json
|
||||
options.json
|
||||
times.txt
|
||||
*_diff.png
|
||||
*.echo
|
||||
|
@@ -18,10 +18,10 @@
|
||||
//
|
||||
|
||||
//
|
||||
// Include this file to use the miniumum library
|
||||
// Include this file to use the minimum library plus screws, nuts and washers
|
||||
//
|
||||
include <global_defs.scad>
|
||||
include <utils/core/core.scad>
|
||||
//
|
||||
// Global functions and modules
|
||||
// Fasteners used by a lot of other vitamins
|
||||
//
|
||||
use <utils/core/global.scad>
|
||||
include <vitamins/screws.scad>
|
||||
|
BIN
docs/metric_threads.png
Normal file
After Width: | Height: | Size: 17 KiB |
128
docs/usage.md
@@ -22,37 +22,37 @@ The 7 SEGMENT.TTF font from the docs directory is needed for simulating 7 segmen
|
||||
|
||||
## Installation
|
||||
|
||||
OpenSCAD has to be setup to find libraries by setting the ```OPENSCADPATH``` environment variable to where you want to file your libraries and NopSCADlib needs to be installed
|
||||
in the directory it points to. This can be done with ```git clone https://github.com/nophead/NopSCADlib.git``` while in that directory or, if you don't want to use GIT,
|
||||
OpenSCAD has to be setup to find libraries by setting the `OPENSCADPATH` environment variable to where you want to file your libraries and NopSCADlib needs to be installed
|
||||
in the directory it points to. This can be done with `git clone https://github.com/nophead/NopSCADlib.git` while in that directory or, if you don't want to use GIT,
|
||||
by downloading https://github.com/nophead/NopSCADlib/archive/master.zip and unzipping it to a directory called NopSCADlib.
|
||||
|
||||
The ```NopSCADlib/scripts``` directory needs to be added to the executable search path.
|
||||
The `NopSCADlib/scripts` directory needs to be added to the executable search path, `PATH` on Windows and `path` on Linux and Mac.
|
||||
|
||||
The installation can be tested by opening ```NopSCADlib/libtest.scad``` in the OpenSCAD GUI. It should render all the objects in the library in about 1 minute.
|
||||
The installation can be tested by opening `NopSCADlib/libtest.scad` in the OpenSCAD GUI. It should render all the objects in the library in about 1 minute.
|
||||
|
||||
Running ```tests``` from the command line will run all the tests in the ```tests``` directory and build the ```readme.md``` catalog for GitHub and render it to ```readme.html```
|
||||
Running `tests` from the command line will run all the tests in the `tests` directory and build the `readme.md` catalog for GitHub and render it to `readme.html`
|
||||
for local preview.
|
||||
|
||||
## Directory structure
|
||||
|
||||
| Path | Contents |
|
||||
|:-----|:------|
|
||||
| ```NopSCADlib``` | Top level scad files, e.g. ```lib.scad``` |
|
||||
| ```NopSCADlib/doc``` | Documentation like this that is not automatically generated |
|
||||
| ```NopSCADlib/examples``` | Example projects |
|
||||
| ```NopSCADlib/gallery``` | Pictures of items that have been made with the library |
|
||||
| ```NopSCADlib/printed``` | Scad files for making reusable printed parts |
|
||||
| ```NopSCADlib/scripts``` | Python scripts |
|
||||
| ```NopSCADlib/tests``` | A stand alone test for each type of vitamin and most of the utilities |
|
||||
| ```NopSCADlib/utils``` | Utility scad files |
|
||||
| ```NopSCADlib/utils/core``` | Core utilities used by nearly everything |
|
||||
| ```NopSCADlib/vitamins``` | Generally a pair of .scad files for each type of vitamin. The plural version contains object definitions to be included and it uses the singular version. |
|
||||
| `NopSCADlib` | Top level scad files, e.g. `lib.scad` |
|
||||
| `NopSCADlib/doc` | Documentation like this that is not automatically generated |
|
||||
| `NopSCADlib/examples` | Example projects |
|
||||
| `NopSCADlib/gallery` | Pictures of items that have been made with the library |
|
||||
| `NopSCADlib/printed` | Scad files for making reusable printed parts |
|
||||
| `NopSCADlib/scripts` | Python scripts |
|
||||
| `NopSCADlib/tests` | A stand alone test for each type of vitamin and most of the utilities |
|
||||
| `NopSCADlib/utils` | Utility scad files |
|
||||
| `NopSCADlib/utils/core` | Core utilities used by nearly everything |
|
||||
| `NopSCADlib/vitamins` | Generally a pair of .scad files for each type of vitamin. The plural version contains object definitions to be included and it uses the singular version. |
|
||||
|
||||
|
||||
## Making a project
|
||||
|
||||
Each project has its own directory and that is used to derive the project's name.
|
||||
There should also be a subdirectory called ```scad``` with a scad file in it that contains the main assembly.
|
||||
There should also be a subdirectory called `scad` with a scad file in it that contains the main assembly.
|
||||
A skeleton project looks like this: -
|
||||
|
||||
//! Project description in Markdown format before the first include.
|
||||
@@ -72,30 +72,30 @@ A skeleton project looks like this: -
|
||||
|
||||
Other scad files can be added to the scad directory and included or used as required.
|
||||
|
||||
* Subassemblies can be added in the same format as ```main_assembly()```, i.e. a module called ```something_assembly()```, taking no parameters and calling ```assembly("something")```
|
||||
* Subassemblies can be added in the same format as `main_assembly()`, i.e. a module called `something_assembly()`, taking no parameters and calling `assembly("something")`
|
||||
with the rest of its contents passed as children.
|
||||
It needs to be called directly or indirectly from ```main_assembly()``` to appear in the view and on the BOM.
|
||||
Assembly instructions should be added directly before the module definition in comments marked with ```//!```.
|
||||
It needs to be called directly or indirectly from `main_assembly()` to appear in the view and on the BOM.
|
||||
Assembly instructions should be added directly before the module definition in comments marked with `//!`.
|
||||
|
||||
* Any printed parts should be made by a module called ```something_stl()```, taking no parameters and calling ```stl("something")``` so they appear on the BOM when called from
|
||||
an assembly. Printed parts are usually ```color```ed and ```render```ed at the point they are used in the assembly.
|
||||
* Any printed parts should be made by a module called `something_stl()`, taking no parameters and calling `stl("something")` so they appear on the BOM when called from
|
||||
an assembly. Printed parts are usually `color`ed and `render`ed at the point they are used in the assembly.
|
||||
|
||||
* Any 2D routed parts should be made by a module called ```something_dxf()```, taking no parameters and calling ```dxf("something")``` so they appear on the BOM when called from an
|
||||
assembly. They are generally made from a ```sheet_2D()``` with holes subtracted from it. That will also put the sheet material on the BOM.
|
||||
They are then expanded to 3D using ```render_2D_sheet()``` when they are placed in an assembly.
|
||||
* Any 2D routed parts should be made by a module called `something_dxf()`, taking no parameters and calling `dxf("something")` so they appear on the BOM when called from an
|
||||
assembly. They are generally made from a `sheet_2D()` with holes subtracted from it. That will also put the sheet material on the BOM.
|
||||
They are then expanded to 3D using `render_2D_sheet()` when they are placed in an assembly.
|
||||
|
||||
When ```make_all``` is run from the top level directory of the project it will create the following sub-directories and populate them:-
|
||||
When `make_all` is run from the top level directory of the project it will create the following sub-directories and populate them:-
|
||||
|
||||
| Directory | Contents |
|
||||
|:----------|:---------|
|
||||
| assemblies | For each assembly: an assembled view and an exploded assembly view, in large and small formats |
|
||||
| bom | A flat BOM in ```bom.txt``` for the whole project, flat BOMs in text format for each assembly and a hierarchical BOM in JSON format: ```bom.json```.|
|
||||
| bom | A flat BOM in `bom.txt` for the whole project, flat BOMs in text format for each assembly and a hierarchical BOM in JSON format: `bom.json`.|
|
||||
| deps | Dependency files for each scad file in the project, so that subsequent builds can be incremental |
|
||||
| dxfs | DXF files for all the CNC routed parts in the project and small PNG images of them |
|
||||
| stls | STL files for all the printed parts in the project and small PNG images of them |
|
||||
|
||||
It will also make a Markdown assembly manual called ```readme.md``` suitable for GitHub, a version rendered to HTML for viewing locally called ```readme.html``` and a second
|
||||
HTML version called ```printme.html```. This has page breaks instead of horizontal rules between sections and can be converted to PDF using Chrome to make a stand alone manual.
|
||||
It will also make a Markdown assembly manual called `readme.md` suitable for GitHub, a version rendered to HTML for viewing locally called `readme.html` and a second
|
||||
HTML version called `printme.html`. This has page breaks instead of horizontal rules between sections and can be converted to PDF using Chrome to make a stand alone manual.
|
||||
|
||||
Each time OpenSCAD is run to produce STL files, DXF files or assembly views, the time it takes is recorded and compared with the previous time.
|
||||
At the end the times are printed with the delta from the last run and coloured red or green if they have got significantly faster or slower.
|
||||
@@ -112,28 +112,28 @@ All the vitamins and utilities are included if you include [NopSCADlib/lib.scad]
|
||||
Printed parts are not included and need to be used or included explicitly, their documentation states which files need to be included rather than used.
|
||||
|
||||
This is the easiest way to use the library and is reasonably efficient because the only files included are the object list definitions, all the functions and modules are used, so
|
||||
get shared if other files in the project include ```lib.scad``` as well, or if you have multiple projects open in the same instance of OpenSCAD.
|
||||
get shared if other files in the project include `lib.scad` as well, or if you have multiple projects open in the same instance of OpenSCAD.
|
||||
|
||||
One downside is that any change to the library will mean all the project files need regenerating.
|
||||
A more optimised approach for large projects is to include [NopSCADlib/core.scad](../core.scad) instead.
|
||||
That only has the a small set of utilities and the global settings in [global_defs.scad](../global_defs.scad). Any vitamins used need to be included explicitly.
|
||||
One can copy the include or use line from [NopSCADlib/lib.scad](../lib.scad).
|
||||
That only has the a small set of utilities and the global settings in [global_defs.scad](../global_defs.scad) plus screws, nuts and washers that are required by a lot of other vitamins.
|
||||
Any other vitamins used need to be included explicitly. One can copy the includes or use a line from [NopSCADlib/lib.scad](../lib.scad).
|
||||
|
||||
### Parametric parts.
|
||||
|
||||
Modules that generate parts and assemblies need to take no parameters so that they can be called from the framework to make the STL files and assembly views, etc.
|
||||
Sometimes parts or asemblies need to be parametric, for example fan guards take the fan as a parameter.
|
||||
To work around this the ```fan_guard()``` module calls the ```stl()``` module with a variable name which has a suffix of the fan width, e.g. "fan_guard_60".
|
||||
To work around this the `fan_guard()` module calls the `stl()` module with a variable name which has a suffix of the fan width, e.g. "fan_guard_60".
|
||||
This ensures that if there are different sized fans in the same project the STL files have unique names.
|
||||
It is then up to the user to add a wrapper to their project called ```fan_guard_60_stl()``` that calls ```fan_guard()``` with a 60mm fan: -
|
||||
It is then up to the user to add a wrapper to their project called `fan_guard_60_stl()` that calls `fan_guard()` with a 60mm fan: -
|
||||
|
||||
module fan_guard_60_stl() fan_guard(fan60x15);
|
||||
|
||||
Calling ```fan_guard(fan60x15)``` draws a fan guard for a 60mm fan and puts ```fan_guard_60.stl``` on the BOM. The framework then looks for a module ```fan_guard_60_stl()``` to
|
||||
Calling `fan_guard(fan60x15)` draws a fan guard for a 60mm fan and puts `fan_guard_60.stl` on the BOM. The framework then looks for a module `fan_guard_60_stl()` to
|
||||
generate it.
|
||||
|
||||
This is OK if the fan will always be 60mm but what if the project is parametric and the fan size can vary?
|
||||
To cater for that ```fan_guard()``` can be passed a ```name``` parameter to name the STL.
|
||||
To cater for that `fan_guard()` can be passed a `name` parameter to name the STL.
|
||||
For example a 3D printer might have a bed cooling fan and different sized machines might have different size fans.
|
||||
|
||||
bed_fan = fan80x38;
|
||||
@@ -144,7 +144,7 @@ In this case the STL file has a constant name related to its use, regardless of
|
||||
|
||||
### Assembly boundaries
|
||||
|
||||
The ```assembly()``` module is used to mark assemblies that correspond to a step of construction.
|
||||
The `assembly()` module is used to mark assemblies that correspond to a step of construction.
|
||||
Each assembly named in this way gets a page in the build manual with a list of the parts and sub-assemblies that it uses, an exploded view,
|
||||
some build instructions and then the assembled view.
|
||||
This doesn't always correspond with how one would want to structure the code.
|
||||
@@ -164,7 +164,7 @@ This is achieved by having a pair of modules: -
|
||||
module handle_assembly() pose([225, 0, 150], [0, 0, 14]) //! Printed part with inserts in place
|
||||
assembly("handle") {
|
||||
translate_z(handle_height())
|
||||
color(pp1_colour) vflip() handle_stl();
|
||||
stl_colour(pp1_colour) vflip() handle_stl();
|
||||
|
||||
handle_screw_positions()
|
||||
vflip()
|
||||
@@ -182,26 +182,26 @@ This is achieved by having a pair of modules: -
|
||||
screw_and_washer(screw, screw_length, true);
|
||||
}
|
||||
|
||||
Notice how the first module ```handle_assembly()``` uses ```assembly()``` and has a build instruction. It isn't used directly in a project though, ```handle_fastened_assembly()``` is
|
||||
Notice how the first module `handle_assembly()` uses `assembly()` and has a build instruction. It isn't used directly in a project though, `handle_fastened_assembly()` is
|
||||
what is actually called from the parent assembly.
|
||||
Because it doesn't have a call to ```assembly()```, the fasteners are added to the parent but the STL and the inserts become a sub-assembly.
|
||||
Because it doesn't have a call to `assembly()`, the fasteners are added to the parent but the STL and the inserts become a sub-assembly.
|
||||
|
||||
When the parent assembly is shown exploded the handle's screws will be exploded but the inserts won't. They only explode when the sub-assembly is shown exloded.
|
||||
|
||||
Note also the ```pose([225, 0, 150], [0, 0, 14])``` call before the ```assembly()``` call. This allows the sub-assembly to be posed differently in its build step but doesn't
|
||||
Note also the `pose([225, 0, 150], [0, 0, 14])` call before the `assembly()` call. This allows the sub-assembly to be posed differently in its build step but doesn't
|
||||
affect its orientation in the parent assembly. The pose parameters are the rotation and the translation taken from the GUI.
|
||||
|
||||
### Exploded diagrams
|
||||
|
||||
A lot of vitamins explode themselves when ```$explode=1```. This is done with module ```explode()``` that can be passed a Z offset, or a 3D vector that gives the displacement
|
||||
and it draws a line from the origial position to the exploded position. The line can be offset from the origin of the component by specifying an offset vector.
|
||||
A lot of vitamins explode themselves when `$explode=1`. This is done with module `explode()` that can be passed a Z offset, or a 3D vector that gives the displacement
|
||||
and it draws a line from the original position to the exploded position. The line can be offset from the origin of the component by specifying an offset vector.
|
||||
|
||||
Often user assemblies need to add ```explode()``` in various places to explode printed parts, for example.
|
||||
Often user assemblies need to add `explode()` in various places to explode printed parts, for example.
|
||||
|
||||
### Creating vitamins
|
||||
|
||||
Most vitamins are parametric and use a named list of properties to describe them is a pseudo OO style.
|
||||
These lists are passed to every function or module related to the vitamin as the first parameter called ```type```.
|
||||
These lists are passed to every function or module related to the vitamin as the first parameter called `type`.
|
||||
They need to be included in the user code, so that the list names are visible. The functions and modules however only need to be used, not included.
|
||||
|
||||
This leads to a pair of files for each vitamin. One with a plural name that defines the objects and then uses the file with a singular name
|
||||
@@ -216,20 +216,50 @@ These functions take a particular form, so they can be scraped out and added to
|
||||
|
||||
function spring_od(type) = type[1]; //! Outside diameter
|
||||
|
||||
Other functions and modules with ```//!``` comments will be added to the documentation as functions and modules.
|
||||
Other functions and modules with `//!` comments will be added to the documentation as functions and modules.
|
||||
Functions and modules without these special comments are considered private and do not appear in the documentation.
|
||||
|
||||
A vitamin announces itself to the BOM by calling the ```vitamin()``` module with a string description composed of two parts separated by a colon.
|
||||
A vitamin announces itself to the BOM by calling the `vitamin()` module with a string description composed of two parts separated by a colon.
|
||||
The first part is a string representation of the module instantiation.
|
||||
This is used in the documentation to show how to instantiate every part available.
|
||||
To facilitate this the first element in the type list is the name of the list as a string and is simply accessed as ```type[0]```.
|
||||
To facilitate this the first element in the type list is the name of the list as a string and is simply accessed as `type[0]`.
|
||||
|
||||
The part of the description after the colon is free format text that appears on the BOM. Since vitamins are listed alphabetically starting the description with the broad
|
||||
category of the part and leaving the more refined description to the end generates tidier parts lists.
|
||||
For example ```Screw M3 pan x 30mm``` ensures all the screws appear together and are ordered by their diameter before length, although ```M3 x 30mm pan screw``` would be
|
||||
For example `Screw M3 pan x 30mm` ensures all the screws appear together and are ordered by their diameter before length, although `M3 x 30mm pan screw` would be
|
||||
more natural.
|
||||
|
||||
Vitamins are only ever previewed, so they are optimised to draw quickly in F5 and don't need to worry about being manifold.
|
||||
In OpenCSG 3D difference and intersection are relatively slow and the negative volumes interfere with nearby objects when they are composed into assemblies. For this reason as much
|
||||
as possible is done by unioning primitives and extruded 2D shapes. Any 3D differences or intersections are wrapped in ```render()``` so that CGAL will compute a polyhedron
|
||||
as possible is done by unioning primitives and extruded 2D shapes. Any 3D differences or intersections are wrapped in `render()` so that CGAL will compute a polyhedron
|
||||
that is cached and reused. This will be very slow the first time it renders but very fast afterwards.
|
||||
|
||||
### Panels and Platters
|
||||
|
||||
The `stls` and `dxfs` scripts produce a file for each part but often it is desirable to print or route collections of parts laid out together.
|
||||
This can be done by adding scad files to folders called `platters` for STL files and `panels` for DXF files.
|
||||
These can aggregate and lay out parts by including `NopSCADlib/core.scad` and using modules `use_stl(name)` and `use_dxf(name)`.
|
||||
These modules import the already generated singular STL and DXF files, so they are relatively fast. The name does not include the suffix.
|
||||
The scad files typically also need to include other files from the project to get the dimensions of the parts to calculate their positions.
|
||||
|
||||
The composite part files have the same name as the scad file that generates them, with the suffix changed to `.stl` or `.dxf`.
|
||||
|
||||
The generated files are placed in `stls/printed` and `dxfs/routed`.
|
||||
Any parts that are not covered by the platters / panels are copied into the `printed` / `routed` directories, so that they contain everything to be made.
|
||||
|
||||
### Multiple configurations
|
||||
|
||||
Some parametric designs might have several configurations, for example a 3D printer with different size options. If several configurations need to be supported at the
|
||||
same time multiple sets of BOMS, STLS and DXFs need to be generated in separate diectories. NopSCADlib supports this by having multiple configuration files named
|
||||
`config_<target_name>.scad`. All the scripts take an optional first parameter that selects one of these config files by specifying `target_name`.
|
||||
|
||||
The target config file is selected by generating `target.scad` that includes `config_<target_name>.scad`.
|
||||
The rest of the project includes `target.scad` to use the configuration.
|
||||
Additionally all the generated file directories (assemblies, bom, stls, dxfs, etc.) are placed in a sub-directory called `<target_name>`.
|
||||
|
||||
### Other libraries
|
||||
|
||||
The build scripts need to be able to locate the source files where the modules to generate the STL files and assemblies reside. They will search all the scad files
|
||||
in the project plus any `printed` directories outside the project. This covers the printed parts in NopSCADlib but also allows other libraries of printed parts.
|
||||
|
||||
Other libraries of vitamins and utilities can be used provided they follow the same convensions of NopSCADlib. The build scripts don't need to search those.
|
||||
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 137 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
@@ -1,67 +1,119 @@
|
||||
[
|
||||
{
|
||||
"name": "base_assembly",
|
||||
"big": null,
|
||||
"count": 1,
|
||||
"assemblies": {},
|
||||
"vitamins": {
|
||||
"insert(F1BM3): Heatfit insert M3": 2
|
||||
"insert(F1BM3): Heatfit insert M3": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"printed": {
|
||||
"socket_box.stl": 1
|
||||
"socket_box.stl": {
|
||||
"count": 1,
|
||||
"colour": "dimgrey"
|
||||
}
|
||||
},
|
||||
"routed": {}
|
||||
},
|
||||
{
|
||||
"name": "feet_assembly",
|
||||
"big": null,
|
||||
"count": 1,
|
||||
"assemblies": {
|
||||
"base_assembly": 1
|
||||
},
|
||||
"vitamins": {
|
||||
"washer(M3_washer): Washer M3 x 7mm x 0.5mm": 8,
|
||||
"screw(M3_dome_screw, 10): Screw M3 dome x 10mm": 4,
|
||||
"nut(M3_nut, nyloc = true): Nut M3 x 2.4mm nyloc": 4
|
||||
"washer(M3_washer): Washer M3 x 7mm x 0.5mm": {
|
||||
"count": 8
|
||||
},
|
||||
"screw(M3_dome_screw, 10): Screw M3 dome x 10mm": {
|
||||
"count": 4
|
||||
},
|
||||
"nut(M3_nut, nyloc = true): Nut M3 x 2.4mm nyloc": {
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"printed": {
|
||||
"foot.stl": 4
|
||||
"foot.stl": {
|
||||
"count": 4,
|
||||
"colour": "darkorange"
|
||||
}
|
||||
},
|
||||
"routed": {}
|
||||
},
|
||||
{
|
||||
"name": "mains_in_assembly",
|
||||
"big": null,
|
||||
"count": 1,
|
||||
"assemblies": {
|
||||
"feet_assembly": 1
|
||||
},
|
||||
"vitamins": {
|
||||
": Wire green & yellow 30/0.25mm strands, length 150mm - not shown": 1,
|
||||
": Wire blue 30/0.25mm strands, length 150mm - not shown": 1,
|
||||
": Wire brown 30/0.25mm strands, length 150mm - not shown": 2,
|
||||
"tubing(HSHRNK32): Heatshrink sleeving ID 3.2mm x 15mm - not shown": 3,
|
||||
"iec(IEC_inlet_atx): IEC inlet for ATX": 1,
|
||||
"screw(M3_cs_cap_screw, 12): Screw M3 cs cap x 12mm": 2,
|
||||
"washer(M3_washer): Washer M3 x 7mm x 0.5mm": 2,
|
||||
"nut(M3_nut, nyloc = true): Nut M3 x 2.4mm nyloc": 2
|
||||
": Wire green & yellow 30/0.25mm strands, length 150mm - not shown": {
|
||||
"count": 1
|
||||
},
|
||||
": Wire blue 30/0.25mm strands, length 150mm - not shown": {
|
||||
"count": 1
|
||||
},
|
||||
": Wire brown 30/0.25mm strands, length 150mm - not shown": {
|
||||
"count": 2
|
||||
},
|
||||
"tubing(HSHRNK32): Heatshrink sleeving ID 3.2mm x 15mm - not shown": {
|
||||
"count": 3
|
||||
},
|
||||
"iec(IEC_inlet_atx): IEC inlet for ATX": {
|
||||
"count": 1
|
||||
},
|
||||
"screw(M3_cs_cap_screw, 12): Screw M3 cs cap x 12mm": {
|
||||
"count": 2
|
||||
},
|
||||
"washer(M3_washer): Washer M3 x 7mm x 0.5mm": {
|
||||
"count": 2
|
||||
},
|
||||
"nut(M3_nut, nyloc = true): Nut M3 x 2.4mm nyloc": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"printed": {},
|
||||
"routed": {}
|
||||
},
|
||||
{
|
||||
"name": "main_assembly",
|
||||
"big": null,
|
||||
"count": 1,
|
||||
"assemblies": {
|
||||
"mains_in_assembly": 1
|
||||
},
|
||||
"vitamins": {
|
||||
": Wire green & yellow 30/0.25mm strands, length 150mm - not shown": 1,
|
||||
": Wire blue 30/0.25mm strands, length 150mm - not shown": 1,
|
||||
"tubing(HSHRNK32): Heatshrink sleeving ID 3.2mm x 15mm - not shown": 5,
|
||||
": Ferrule for 1.5mm^2 wire - not shown": 3,
|
||||
"mains_socket(Contactum): Mains socket 13A": 1,
|
||||
"screw(M3_cs_cap_screw, 20): Screw M3 cs cap x 20mm": 2,
|
||||
"jack_4mm_shielded(\"blue\", 3, \"royalblue\"): 4mm shielded jack socket blue": 2,
|
||||
"jack_4mm_shielded(\"brown\", 3, \"sienna\"): 4mm shielded jack socket brown": 1,
|
||||
"jack_4mm_shielded(\"green\", 3): 4mm shielded jack socket green": 2
|
||||
": Wire green & yellow 30/0.25mm strands, length 150mm - not shown": {
|
||||
"count": 1
|
||||
},
|
||||
": Wire blue 30/0.25mm strands, length 150mm - not shown": {
|
||||
"count": 1
|
||||
},
|
||||
"tubing(HSHRNK32): Heatshrink sleeving ID 3.2mm x 15mm - not shown": {
|
||||
"count": 5
|
||||
},
|
||||
": Ferrule for 1.5mm^2 wire - not shown": {
|
||||
"count": 3
|
||||
},
|
||||
"mains_socket(Contactum): Mains socket 13A": {
|
||||
"count": 1
|
||||
},
|
||||
"screw(M3_cs_cap_screw, 20): Screw M3 cs cap x 20mm": {
|
||||
"count": 2
|
||||
},
|
||||
"jack_4mm_shielded(\"blue\", 3, \"royalblue\"): 4mm shielded jack socket blue": {
|
||||
"count": 2
|
||||
},
|
||||
"jack_4mm_shielded(\"brown\", 3, \"sienna\"): 4mm shielded jack socket brown": {
|
||||
"count": 1
|
||||
},
|
||||
"jack_4mm_shielded(\"green\", 3): 4mm shielded jack socket green": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"printed": {},
|
||||
"routed": {}
|
||||
|
@@ -30,7 +30,8 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
<a name="Parts_list"></a>
|
||||
## Parts list
|
||||
| <span style="writing-mode: vertical-rl; text-orientation: mixed;">Base</span> | <span style="writing-mode: vertical-rl; text-orientation: mixed;">Feet</span> | <span style="writing-mode: vertical-rl; text-orientation: mixed;">Mains In</span> | <span style="writing-mode: vertical-rl; text-orientation: mixed;">Main</span> | <span style="writing-mode: vertical-rl; text-orientation: mixed;">TOTALS</span> | |
|
||||
|--:|--:|--:|--:|--:|:--|
|
||||
|---:|---:|---:|---:|---:|:---|
|
||||
| | | | | | **Vitamins** |
|
||||
| . | . | . | 2 | 2 | 4mm shielded jack socket blue |
|
||||
| . | . | . | 1 | 1 | 4mm shielded jack socket brown |
|
||||
| . | . | . | 2 | 2 | 4mm shielded jack socket green |
|
||||
@@ -47,9 +48,11 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
| . | . | 1 | 1 | 2 | Wire blue 30/0.25mm strands, length 150mm - not shown |
|
||||
| . | . | 2 | . | 2 | Wire brown 30/0.25mm strands, length 150mm - not shown |
|
||||
| . | . | 1 | 1 | 2 | Wire green & yellow 30/0.25mm strands, length 150mm - not shown |
|
||||
| | | | | | **3D Printed parts** |
|
||||
| . | 4 | . | . | 4 | foot.stl |
|
||||
| 1 | . | . | . | 1 | socket_box.stl |
|
||||
| 2 | 16 | 14 | 18 | 50 | Total vitamins count |
|
||||
| | | | | | **3D printed parts** |
|
||||
| . | 4 | . | . | 4 | foot.stl |
|
||||
| 1 | . | . | . | 1 | socket_box.stl |
|
||||
| 1 | 4 | . | . | 5 | Total 3D printed parts count |
|
||||
|
||||
[Top](#TOP)
|
||||
|
||||
@@ -58,14 +61,14 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
## Base Assembly
|
||||
### Vitamins
|
||||
|Qty|Description|
|
||||
|--:|:----------|
|
||||
|---:|:----------|
|
||||
|2| Heatfit insert M3|
|
||||
|
||||
|
||||
### 3D Printed parts
|
||||
|
||||
| 1 x socket_box.stl |
|
||||
|--|
|
||||
|---|
|
||||
| 
|
||||
|
||||
|
||||
@@ -85,16 +88,16 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
## Feet Assembly
|
||||
### Vitamins
|
||||
|Qty|Description|
|
||||
|--:|:----------|
|
||||
|8| Washer M3 x 7mm x 0.5mm|
|
||||
|4| Screw M3 dome x 10mm|
|
||||
|---:|:----------|
|
||||
|4| Nut M3 x 2.4mm nyloc|
|
||||
|4| Screw M3 dome x 10mm|
|
||||
|8| Washer M3 x 7mm x 0.5mm|
|
||||
|
||||
|
||||
### 3D Printed parts
|
||||
|
||||
| 4 x foot.stl |
|
||||
|--|
|
||||
|---|
|
||||
| 
|
||||
|
||||
|
||||
@@ -102,7 +105,7 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
### Sub-assemblies
|
||||
|
||||
| 1 x base_assembly |
|
||||
|--|
|
||||
|---|
|
||||
| 
|
||||
|
||||
|
||||
@@ -121,21 +124,21 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
## Mains In Assembly
|
||||
### Vitamins
|
||||
|Qty|Description|
|
||||
|--:|:----------|
|
||||
|1| Wire green & yellow 30/0.25mm strands, length 150mm - not shown|
|
||||
|1| Wire blue 30/0.25mm strands, length 150mm - not shown|
|
||||
|2| Wire brown 30/0.25mm strands, length 150mm - not shown|
|
||||
|---:|:----------|
|
||||
|3| Heatshrink sleeving ID 3.2mm x 15mm - not shown|
|
||||
|1| IEC inlet for ATX|
|
||||
|2| Nut M3 x 2.4mm nyloc|
|
||||
|2| Screw M3 cs cap x 12mm|
|
||||
|2| Washer M3 x 7mm x 0.5mm|
|
||||
|2| Nut M3 x 2.4mm nyloc|
|
||||
|1| Wire blue 30/0.25mm strands, length 150mm - not shown|
|
||||
|2| Wire brown 30/0.25mm strands, length 150mm - not shown|
|
||||
|1| Wire green & yellow 30/0.25mm strands, length 150mm - not shown|
|
||||
|
||||
|
||||
### Sub-assemblies
|
||||
|
||||
| 1 x feet_assembly |
|
||||
|--|
|
||||
|---|
|
||||
| 
|
||||
|
||||
|
||||
@@ -160,22 +163,22 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
## Main Assembly
|
||||
### Vitamins
|
||||
|Qty|Description|
|
||||
|--:|:----------|
|
||||
|1| Wire green & yellow 30/0.25mm strands, length 150mm - not shown|
|
||||
|1| Wire blue 30/0.25mm strands, length 150mm - not shown|
|
||||
|5| Heatshrink sleeving ID 3.2mm x 15mm - not shown|
|
||||
|3| Ferrule for 1.5mm^2 wire - not shown|
|
||||
|1| Mains socket 13A|
|
||||
|2| Screw M3 cs cap x 20mm|
|
||||
|---:|:----------|
|
||||
|2| 4mm shielded jack socket blue|
|
||||
|1| 4mm shielded jack socket brown|
|
||||
|2| 4mm shielded jack socket green|
|
||||
|3| Ferrule for 1.5mm^2 wire - not shown|
|
||||
|5| Heatshrink sleeving ID 3.2mm x 15mm - not shown|
|
||||
|1| Mains socket 13A|
|
||||
|2| Screw M3 cs cap x 20mm|
|
||||
|1| Wire blue 30/0.25mm strands, length 150mm - not shown|
|
||||
|1| Wire green & yellow 30/0.25mm strands, length 150mm - not shown|
|
||||
|
||||
|
||||
### Sub-assemblies
|
||||
|
||||
| 1 x mains_in_assembly |
|
||||
|--|
|
||||
|---|
|
||||
| 
|
||||
|
||||
|
||||
|
@@ -35,7 +35,14 @@ $extrusion_width = 0.5;
|
||||
$pp1_colour = "dimgrey";
|
||||
$pp2_colour = [0.9, 0.9, 0.9];
|
||||
|
||||
include <NopSCADlib/lib.scad>
|
||||
include <NopSCADlib/core.scad>
|
||||
include <NopSCADlib/vitamins/iecs.scad>
|
||||
include <NopSCADlib/vitamins/mains_sockets.scad>
|
||||
include <NopSCADlib/vitamins/tubings.scad>
|
||||
|
||||
use <NopSCADlib/vitamins/insert.scad>
|
||||
use <NopSCADlib/vitamins/wire.scad>
|
||||
use <NopSCADlib/vitamins/jack.scad>
|
||||
use <NopSCADlib/printed/foot.scad>
|
||||
|
||||
echo(extrusion_width = extrusion_width, layer_height = layer_height);
|
||||
@@ -45,7 +52,7 @@ iec = IEC_inlet_atx;
|
||||
socket = Contactum;
|
||||
|
||||
|
||||
foot = [20, 8, 3, 1, M3_dome_screw, 10];
|
||||
foot = Foot(d = 20, h = 8, t = 3, r = 1, screw = M3_dome_screw);
|
||||
|
||||
module foot_stl() foot(foot);
|
||||
|
||||
@@ -166,7 +173,7 @@ module socket_box_stl() {
|
||||
//
|
||||
module base_assembly()
|
||||
assembly("base") {
|
||||
color(pp1_colour) render() /*clip(ymax = 0)*/ socket_box_stl();
|
||||
stl_colour(pp1_colour) render() /*clip(ymax = 0)*/ socket_box_stl();
|
||||
|
||||
mains_socket_hole_positions(socket)
|
||||
translate_z(height)
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 107 KiB |
BIN
gallery/EnviroPlus.png
Normal file
After Width: | Height: | Size: 121 KiB |
BIN
gallery/FilamentDryBox.png
Normal file
After Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 168 KiB |
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 290 KiB |
Before Width: | Height: | Size: 291 KiB After Width: | Height: | Size: 293 KiB |
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 286 KiB |
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 236 KiB |
BIN
gallery/SunBot.png
Normal file
After Width: | Height: | Size: 178 KiB |
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 215 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 202 KiB |
@@ -6,6 +6,20 @@ Arduino thermostat to control a beer fridge to use it as an environmental chambe
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
## EnviroPlus
|
||||
Environmental monitor using Enviro+ sensor board and a Raspberry Pi Zero.
|
||||
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
## FilamentDryBox
|
||||
A small fan oven with a spool holder to keep the filament warm and dry.
|
||||
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
## HydraBot
|
||||
Current state of HydraRaptor after being modified for laser engraving.
|
||||
@@ -68,6 +82,13 @@ Mains isolated and variable supply with metering.
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
## SunBot
|
||||
A solar tracker to keep a solar panel pointing at the sun.
|
||||
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
## Turntable
|
||||
WiFi enabled remote control turntable for photography
|
||||
|
@@ -33,12 +33,13 @@ $exploded = is_undef($explode) ? 0 : $explode; // 1 f
|
||||
layer_height = is_undef($layer_height) ? 0.25 : $layer_height; // layer heigth when printing
|
||||
extrusion_width = is_undef($extrusion_width) ? 0.5 : $extrusion_width; // filament width when printing
|
||||
nozzle = is_undef($nozzle) ? 0.45 : $nozzle; // 3D printer nozzle
|
||||
cnc_bit_r = is_undef($cnc_bit_r) ? 1.2 : $cnc_bit_r; // miniumum tool radius when milling 2D objects
|
||||
pp1_colour = is_undef($pp1_colour) ? "lime" : $pp1_colour; // printed part colour 1
|
||||
pp2_colour = is_undef($pp2_colour) ? "red" : $pp2_colour; // printed part colour 2
|
||||
pp3_colour = is_undef($pp3_colour) ? "blue" : $pp3_colour; // printed part colour 3
|
||||
cnc_bit_r = is_undef($cnc_bit_r) ? 1.2 : $cnc_bit_r; // minimum tool radius when milling 2D objects
|
||||
pp1_colour = is_undef($pp1_colour) ? [0, 146/255, 0] : $pp1_colour; // printed part colour 1, RepRap logo colour
|
||||
pp2_colour = is_undef($pp2_colour) ? "Crimson" : $pp2_colour; // printed part colour 2
|
||||
pp3_colour = is_undef($pp3_colour) ? "SteelBlue" : $pp3_colour; // printed part colour 3
|
||||
pp4_colour = is_undef($pp4_colour) ? "darkorange" : $pp4_colour;// printed part colour 4
|
||||
show_rays = is_undef($show_rays) ? false : $show_rays; // show camera sight lines and light direction
|
||||
show_threads = is_undef($show_threads) ? false : $show_threads; // show screw threads
|
||||
|
||||
// Minimum wall is about two filaments wide but we extrude it closer to get better bonding
|
||||
squeezed_wall = $preview ? 2 * extrusion_width - layer_height * (1 - PI / 4)
|
||||
@@ -50,16 +51,11 @@ $fa = 6;
|
||||
$fs = extrusion_width / 2;
|
||||
|
||||
function round_to_layer(z) = ceil(z / layer_height) * layer_height;
|
||||
// Some additional named colors
|
||||
grey20 = [0.2, 0.2, 0.2];
|
||||
grey30 = [0.3, 0.3, 0.3];
|
||||
grey40 = [0.4, 0.4, 0.4];
|
||||
grey50 = [0.5, 0.5, 0.5];
|
||||
grey60 = [0.6, 0.6, 0.6];
|
||||
grey70 = [0.7, 0.7, 0.7];
|
||||
grey80 = [0.8, 0.8, 0.8];
|
||||
grey90 = [0.9, 0.9, 0.9];
|
||||
brass = "gold";
|
||||
// Some additional named colours
|
||||
function grey(n) = [0.01, 0.01, 0.01] * n; //! Generate a shade of grey to pass to color().
|
||||
gold = [255/255, 215/255, 0/255];
|
||||
brass = [255/255, 220/255, 100/255];
|
||||
silver = [0.75, 0.75, 0.75];
|
||||
|
||||
/*
|
||||
* Enums
|
||||
|
71
lib.scad
@@ -22,48 +22,54 @@
|
||||
//
|
||||
include <core.scad>
|
||||
|
||||
include <vitamins/fans.scad>
|
||||
include <vitamins/psus.scad>
|
||||
include <vitamins/pcbs.scad>
|
||||
|
||||
include <vitamins/batteries.scad>
|
||||
include <vitamins/blowers.scad>
|
||||
include <vitamins/bulldogs.scad>
|
||||
include <vitamins/buttons.scad>
|
||||
include <vitamins/cameras.scad>
|
||||
include <vitamins/components.scad>
|
||||
include <vitamins/displays.scad>
|
||||
include <vitamins/extrusions.scad>
|
||||
include <vitamins/extrusion_brackets.scad>
|
||||
include <vitamins/geared_steppers.scad>
|
||||
include <vitamins/green_terminals.scad>
|
||||
include <vitamins/hot_ends.scad>
|
||||
include <vitamins/inserts.scad>
|
||||
include <vitamins/kp_pillow_blocks.scad>
|
||||
include <vitamins/ldrs.scad>
|
||||
include <vitamins/leadnuts.scad>
|
||||
include <vitamins/led_meter.scad>
|
||||
include <vitamins/light_strips.scad>
|
||||
include <vitamins/magnets.scad>
|
||||
include <vitamins/mains_sockets.scad>
|
||||
include <vitamins/modules.scad>
|
||||
include <vitamins/panel_meters.scad>
|
||||
include <vitamins/pillars.scad>
|
||||
include <vitamins/pin_headers.scad>
|
||||
include <vitamins/pulleys.scad>
|
||||
include <vitamins/ring_terminals.scad>
|
||||
include <vitamins/rails.scad>
|
||||
include <vitamins/belts.scad>
|
||||
include <vitamins/pulleys.scad>
|
||||
include <vitamins/sheets.scad>
|
||||
include <vitamins/stepper_motors.scad>
|
||||
include <vitamins/components.scad>
|
||||
include <vitamins/hot_ends.scad>
|
||||
include <vitamins/tubings.scad>
|
||||
include <vitamins/zipties.scad>
|
||||
include <vitamins/linear_bearings.scad>
|
||||
include <vitamins/rod.scad>
|
||||
include <vitamins/leadnuts.scad>
|
||||
include <vitamins/bulldogs.scad>
|
||||
include <vitamins/pillars.scad>
|
||||
include <vitamins/psus.scad>
|
||||
include <vitamins/iecs.scad>
|
||||
include <vitamins/rockers.scad>
|
||||
include <vitamins/scs_bearing_blocks.scad>
|
||||
include <vitamins/shaft_couplings.scad>
|
||||
include <vitamins/sheets.scad>
|
||||
include <vitamins/sk_brackets.scad>
|
||||
include <vitamins/spools.scad>
|
||||
include <vitamins/ssrs.scad>
|
||||
include <vitamins/d_connectors.scad>
|
||||
include <vitamins/buttons.scad>
|
||||
include <vitamins/pcbs.scad>
|
||||
include <vitamins/modules.scad>
|
||||
include <vitamins/displays.scad>
|
||||
include <vitamins/blowers.scad>
|
||||
include <vitamins/leds.scad>
|
||||
include <vitamins/stepper_motors.scad>
|
||||
include <vitamins/swiss_clips.scad>
|
||||
include <vitamins/toggles.scad>
|
||||
include <vitamins/transformers.scad>
|
||||
include <vitamins/tubings.scad>
|
||||
include <vitamins/variacs.scad>
|
||||
include <vitamins/springs.scad>
|
||||
include <vitamins/batteries.scad>
|
||||
include <vitamins/microswitches.scad>
|
||||
include <vitamins/ball_bearings.scad>
|
||||
include <vitamins/light_strips.scad>
|
||||
include <vitamins/spools.scad>
|
||||
include <vitamins/mains_sockets.scad>
|
||||
include <vitamins/zipties.scad>
|
||||
|
||||
use <vitamins/jack.scad>
|
||||
use <vitamins/meter.scad>
|
||||
use <vitamins/fuseholder.scad>
|
||||
use <vitamins/hygrometer.scad>
|
||||
|
||||
use <vitamins/opengrab.scad>
|
||||
use <vitamins/wire.scad>
|
||||
@@ -80,10 +86,13 @@ use <utils/rounded_cylinder.scad>
|
||||
use <utils/dogbones.scad>
|
||||
use <utils/tube.scad>
|
||||
use <utils/quadrant.scad>
|
||||
use <utils/gears.scad>
|
||||
use <utils/hanging_hole.scad>
|
||||
use <utils/fillet.scad>
|
||||
use <utils/rounded_polygon.scad>
|
||||
use <utils/rounded_right_triangle.scad>
|
||||
use <utils/layout.scad>
|
||||
use <utils/round.scad>
|
||||
use <utils/offset.scad>
|
||||
use <utils/sector.scad>
|
||||
use <utils/thread.scad>
|
||||
|
BIN
libtest.png
Before Width: | Height: | Size: 660 KiB After Width: | Height: | Size: 859 KiB |
207
libtest.scad
@@ -17,6 +17,23 @@
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//!# NopSCADlib
|
||||
//! An ever expanding library of parts modelled in OpenSCAD useful for 3D printers and enclosures for electronics, etc.
|
||||
//!
|
||||
//! It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and some utilities.
|
||||
//! There are also Python scripts to generate Bills of Materials (BOMs),
|
||||
//! STL files for all the printed parts, DXF files for CNC routed parts in a project and a manual containing assembly
|
||||
//! instructions and exploded views by scraping markdown embedded in OpenSCAD comments, [see scripts](scripts/readme.md).
|
||||
//!
|
||||
//! A simple example project can be found [here](examples/MainsBreakOutBox/readme.md).
|
||||
//!
|
||||
//! For more examples of what it can make see the [gallery](gallery/readme.md).
|
||||
//!
|
||||
//! The license is GNU General Public License v3.0, see [COPYING](COPYING).
|
||||
//!
|
||||
//! See [usage](docs/usage.md) for requirements, installation instructions and a usage guide.
|
||||
//!
|
||||
//! <img src="libtest.png" width="100%"/>
|
||||
//
|
||||
// This file shows all the parts in the library.
|
||||
//
|
||||
@@ -29,40 +46,56 @@ use <tests/blowers.scad>
|
||||
use <tests/bulldogs.scad>
|
||||
use <tests/buttons.scad>
|
||||
use <tests/cable_strips.scad>
|
||||
use <tests/cameras.scad>
|
||||
use <tests/camera_housing.scad>
|
||||
use <tests/circlips.scad>
|
||||
use <tests/components.scad>
|
||||
use <tests/d_connectors.scad>
|
||||
use <tests/displays.scad>
|
||||
use <tests/drag_chain.scad>
|
||||
use <tests/extrusions.scad>
|
||||
use <tests/extrusion_brackets.scad>
|
||||
use <tests/fans.scad>
|
||||
use <tests/fuseholder.scad>
|
||||
use <tests/geared_steppers.scad>
|
||||
use <tests/hot_ends.scad>
|
||||
use <tests/iecs.scad>
|
||||
use <tests/IECs.scad>
|
||||
use <tests/inserts.scad>
|
||||
use <tests/jack.scad>
|
||||
use <tests/KP_pillow_blocks.scad>
|
||||
use <tests/leadnuts.scad>
|
||||
use <tests/leds.scad>
|
||||
use <tests/LDRs.scad>
|
||||
use <tests/LEDs.scad>
|
||||
use <tests/light_strips.scad>
|
||||
use <tests/linear_bearings.scad>
|
||||
use <tests/meter.scad>
|
||||
use <tests/LED_meters.scad>
|
||||
use <tests/magnets.scad>
|
||||
use <tests/microswitches.scad>
|
||||
use <tests/modules.scad>
|
||||
use <tests/nuts.scad>
|
||||
use <tests/o_ring.scad>
|
||||
use <tests/opengrab.scad>
|
||||
use <tests/pcbs.scad>
|
||||
use <tests/panel_meters.scad>
|
||||
use <tests/PCBs.scad>
|
||||
use <tests/pillars.scad>
|
||||
use <tests/psus.scad>
|
||||
use <tests/press_fit.scad>
|
||||
use <tests/PSUs.scad>
|
||||
use <tests/pulleys.scad>
|
||||
use <tests/rails.scad>
|
||||
use <tests/ring_terminals.scad>
|
||||
use <tests/rockers.scad>
|
||||
use <tests/rod.scad>
|
||||
use <tests/screws.scad>
|
||||
use <tests/SCS_bearing_blocks.scad>
|
||||
use <tests/sealing_strip.scad>
|
||||
use <tests/shaft_couplings.scad>
|
||||
use <tests/sheets.scad>
|
||||
use <tests/SK_brackets.scad>
|
||||
use <tests/spades.scad>
|
||||
use <tests/springs.scad>
|
||||
use <tests/ssrs.scad>
|
||||
use <tests/SSRs.scad>
|
||||
use <tests/stepper_motors.scad>
|
||||
use <tests/Swiss_clips.scad>
|
||||
use <tests/toggles.scad>
|
||||
use <tests/transformers.scad>
|
||||
use <tests/tubings.scad>
|
||||
@@ -80,32 +113,45 @@ use <tests/door_hinge.scad>
|
||||
use <tests/door_latch.scad>
|
||||
use <tests/fan_guard.scad>
|
||||
use <tests/fixing_block.scad>
|
||||
use <tests/flat_hinge.scad>
|
||||
use <tests/foot.scad>
|
||||
use <tests/handle.scad>
|
||||
use <tests/PCB_mount.scad>
|
||||
use <tests/printed_box.scad>
|
||||
use <tests/ribbon_clamp.scad>
|
||||
use <tests/screw_knob.scad>
|
||||
use <tests/socket_box.scad>
|
||||
use <tests/strap_handle.scad>
|
||||
use <tests/ssr_shroud.scad>
|
||||
use <tests/SSR_shroud.scad>
|
||||
use <tests/PSU_shroud.scad>
|
||||
|
||||
x5 = 800;
|
||||
x0 = 0;
|
||||
x1 = x0 + 110;
|
||||
x2 = x1 + 90;
|
||||
x3 = x2 + 130;
|
||||
x4 = x3 + 200;
|
||||
x5 = 850;
|
||||
x6 = x5 + 150;
|
||||
|
||||
cable_grommets_y = 0;
|
||||
|
||||
translate([x5, cable_grommets_y])
|
||||
cable_grommets();
|
||||
|
||||
translate([x5, cable_grommets_y + 50])
|
||||
feet();
|
||||
translate([x5 + 50, cable_grommets_y])
|
||||
ribbon_clamps();
|
||||
|
||||
translate([x5, cable_grommets_y + 75])
|
||||
translate([x5 + 95, cable_grommets_y])
|
||||
press_fits();
|
||||
|
||||
translate([x5, cable_grommets_y + 60])
|
||||
fixing_blocks();
|
||||
|
||||
translate([x5, cable_grommets_y + 100])
|
||||
translate([x5, cable_grommets_y + 90])
|
||||
corner_blocks();
|
||||
|
||||
translate([x5, cable_grommets_y + 150])
|
||||
ribbon_clamps();
|
||||
feet();
|
||||
|
||||
translate([x5 + 70, cable_grommets_y + 150])
|
||||
screw_knobs();
|
||||
@@ -113,6 +159,9 @@ translate([x5 + 70, cable_grommets_y + 150])
|
||||
translate([x5, cable_grommets_y + 470]) {
|
||||
door_hinges()
|
||||
door_latches();
|
||||
|
||||
translate([120, 0])
|
||||
flat_hinges();
|
||||
}
|
||||
|
||||
translate([x5, cable_grommets_y + 370])
|
||||
@@ -124,34 +173,38 @@ translate([x5 + 60, cable_grommets_y + 200])
|
||||
translate([x5, cable_grommets_y + 250])
|
||||
handle();
|
||||
|
||||
translate([900, 600])
|
||||
translate([950, 600])
|
||||
box_test();
|
||||
|
||||
translate([850, 1170])
|
||||
translate([890, 750])
|
||||
printed_boxes();
|
||||
|
||||
translate([850, 1330])
|
||||
bbox_test();
|
||||
|
||||
x0 = 0;
|
||||
|
||||
inserts_y = 0;
|
||||
nuts_y = inserts_y + 20;
|
||||
washers_y = nuts_y + 60;
|
||||
washers_y = nuts_y + 120;
|
||||
screws_y = washers_y + 120;
|
||||
o_rings_y = screws_y + 130;
|
||||
springs_y = o_rings_y + 20;
|
||||
circlips_y = screws_y + 160;
|
||||
springs_y = circlips_y + 20;
|
||||
o_rings_y = springs_y;
|
||||
sealing_strip_y = springs_y + 20;
|
||||
tubings_y = sealing_strip_y + 20;
|
||||
pillars_y = tubings_y + 20;
|
||||
leadnuts_y = pillars_y + 40;
|
||||
pulleys_y = leadnuts_y +40;
|
||||
ball_bearings_y = pillars_y + 40;
|
||||
pulleys_y = ball_bearings_y +40;
|
||||
hot_ends_y = pulleys_y + 60;
|
||||
linear_bearings_y = hot_ends_y + 50;
|
||||
sheets_y = linear_bearings_y + 50;
|
||||
sheets_y = linear_bearings_y + 100;
|
||||
pcbs_y = sheets_y + 40;
|
||||
displays_y = pcbs_y + 150;
|
||||
fans_y = displays_y + 100;
|
||||
displays_y = pcbs_y + 170;
|
||||
fans_y = displays_y + 80;
|
||||
transformers_y = fans_y + 120;
|
||||
psus_y = transformers_y + 190;
|
||||
|
||||
translate([x0 + 20, inserts_y])
|
||||
translate([x0 + 35, inserts_y])
|
||||
inserts();
|
||||
|
||||
translate([x0, inserts_y])
|
||||
@@ -166,10 +219,13 @@ translate([x0, washers_y])
|
||||
translate([x0, screws_y])
|
||||
screws();
|
||||
|
||||
translate([x0, circlips_y])
|
||||
circlips();
|
||||
|
||||
translate([x0, o_rings_y])
|
||||
o_rings();
|
||||
|
||||
translate([x0, springs_y])
|
||||
translate([x0 + 20, springs_y])
|
||||
springs();
|
||||
|
||||
translate([x0 + 50, sealing_strip_y])
|
||||
@@ -181,10 +237,7 @@ translate([x0, tubings_y])
|
||||
translate([x0, pillars_y])
|
||||
pillars();
|
||||
|
||||
translate([x0, leadnuts_y ])
|
||||
leadnuts();
|
||||
|
||||
translate([x0 + 80, leadnuts_y])
|
||||
translate([x0, ball_bearings_y])
|
||||
ball_bearings();
|
||||
|
||||
translate([x0, pulleys_y])
|
||||
@@ -217,13 +270,16 @@ translate([x0, fans_y]) {
|
||||
translate([x0, transformers_y])
|
||||
variacs();
|
||||
|
||||
translate([x0, psus_y])
|
||||
translate([x0, psus_y]) {
|
||||
psus();
|
||||
|
||||
psu_shrouds();
|
||||
}
|
||||
|
||||
x1 = x0 + 100;
|
||||
zipties_y = 0;
|
||||
bulldogs_y = zipties_y + 40;
|
||||
bulldogs_y = zipties_y + 30;
|
||||
swiss_clips_y = bulldogs_y + 35;
|
||||
leadnuts_y = swiss_clips_y + 50;
|
||||
|
||||
translate([x1, zipties_y])
|
||||
zipties();
|
||||
@@ -231,27 +287,40 @@ translate([x1, zipties_y])
|
||||
translate([x1, bulldogs_y])
|
||||
bulldogs();
|
||||
|
||||
x2 = x1 + 90;
|
||||
translate([x1, swiss_clips_y])
|
||||
swiss_clips();
|
||||
|
||||
translate([x1, leadnuts_y])
|
||||
leadnuts();
|
||||
|
||||
|
||||
leds_y = 0;
|
||||
carriers_y = leds_y + 40;
|
||||
spades_y = carriers_y + 40;
|
||||
buttons_y = spades_y + 40;
|
||||
magnets_y = carriers_y + 40;
|
||||
spades_y = magnets_y + 20;
|
||||
buttons_y = spades_y + 20;
|
||||
jacks_y = buttons_y + 40;
|
||||
microswitches_y = jacks_y + 40;
|
||||
rockers_y = microswitches_y + 40;
|
||||
toggles_y = rockers_y + 40;
|
||||
toggles_y = rockers_y + 60;
|
||||
components_y = toggles_y + 40;
|
||||
|
||||
translate([x2, leds_y])
|
||||
leds();
|
||||
|
||||
translate([x2 + 35, leds_y])
|
||||
ldrs();
|
||||
|
||||
translate([x2 + 8, carriers_y])
|
||||
carriers();
|
||||
|
||||
translate([x2+ 38, carriers_y])
|
||||
meters();
|
||||
translate([x2, magnets_y])
|
||||
magnets();
|
||||
|
||||
translate([x2 + 68, carriers_y])
|
||||
translate([x2 + 20, carriers_y])
|
||||
led_meters();
|
||||
|
||||
translate([x2 + 70, leds_y])
|
||||
fuseholders();
|
||||
|
||||
translate([x2, spades_y])
|
||||
@@ -275,8 +344,6 @@ translate([x2, toggles_y])
|
||||
translate([x2, components_y])
|
||||
components();
|
||||
|
||||
|
||||
x3 = x2 + 150;
|
||||
veroboard_y = 0;
|
||||
d_connectors_y = veroboard_y + 110;
|
||||
iecs_y = d_connectors_y + 80;
|
||||
@@ -285,20 +352,37 @@ ssrs_y = modules_y + 80;
|
||||
blowers_y = ssrs_y + 60;
|
||||
batteries_y = blowers_y + 100;
|
||||
steppers_y = batteries_y + 70;
|
||||
panel_meters_y = steppers_y + 70;
|
||||
extrusions_y = panel_meters_y + 80;
|
||||
|
||||
translate([x3, veroboard_y])
|
||||
veroboard_test();
|
||||
|
||||
translate([x3 + 70, veroboard_y + 30])
|
||||
geared_steppers();
|
||||
|
||||
translate([x3 + 140, veroboard_y + 20])
|
||||
pcb_mounts();
|
||||
|
||||
translate([x3 + 170, veroboard_y + 16])
|
||||
cameras();
|
||||
|
||||
translate([x3, d_connectors_y])
|
||||
d_connectors();
|
||||
|
||||
translate([x3 + 170, d_connectors_y - 10])
|
||||
camera_housings();
|
||||
|
||||
translate([x3, iecs_y])
|
||||
iecs();
|
||||
|
||||
translate([x3 + 15, modules_y])
|
||||
microview();
|
||||
|
||||
translate([x3 + 40, modules_y])
|
||||
translate([x3 + 60, modules_y])
|
||||
hygrometer();
|
||||
|
||||
translate([x3 + 90, modules_y])
|
||||
modules();
|
||||
|
||||
translate([x3, ssrs_y]) {
|
||||
@@ -316,28 +400,55 @@ translate([x3, batteries_y])
|
||||
translate([x2, steppers_y]) // interloper
|
||||
stepper_motors();
|
||||
|
||||
translate([x2, panel_meters_y])
|
||||
panel_meters();
|
||||
|
||||
translate([x2, extrusions_y])
|
||||
extrusions();
|
||||
|
||||
translate([x3, transformers_y])
|
||||
transformers();
|
||||
|
||||
|
||||
x4 = x3 + 220;
|
||||
belts_y = 0;
|
||||
rails_y = belts_y + 200;
|
||||
cable_strips_y = rails_y + 300;
|
||||
extrusion_brackets_y = rails_y + 250;
|
||||
sk_brackets_y = extrusion_brackets_y + 80;
|
||||
kp_pillow_blocks_y = sk_brackets_y + 50;
|
||||
scs_bearing_blocks_y = kp_pillow_blocks_y + 60;
|
||||
|
||||
translate([x4 + 112, belts_y + 58]) {
|
||||
translate([x4 + 200, belts_y + 58]) {
|
||||
belt_test();
|
||||
|
||||
translate([0, 60])
|
||||
opengrab_test();
|
||||
|
||||
}
|
||||
|
||||
translate([x4 + 175, belts_y, -20])
|
||||
drag_chains();
|
||||
|
||||
translate([x4, rails_y + 130])
|
||||
rails();
|
||||
|
||||
translate([x4, cable_strips_y])
|
||||
translate([770, fans_y + 50])
|
||||
cable_strips();
|
||||
|
||||
x6 = x5 + 150;
|
||||
translate([x4, kp_pillow_blocks_y])
|
||||
kp_pillow_blocks();
|
||||
|
||||
translate([x4, sk_brackets_y])
|
||||
sk_brackets();
|
||||
|
||||
translate([x4, extrusion_brackets_y])
|
||||
extrusion_brackets();
|
||||
|
||||
translate([x4 + 120, extrusion_brackets_y])
|
||||
shaft_couplings();
|
||||
|
||||
translate([x4, scs_bearing_blocks_y])
|
||||
scs_bearing_blocks();
|
||||
|
||||
|
||||
translate([x6, 125])
|
||||
light_strips();
|
||||
|
279
printed/box.scad
@@ -23,7 +23,7 @@
|
||||
//! together. The box panels can be customised to have holes and parts mounted on them by overriding the
|
||||
//! definitions of `box_base()`, `box_front()`, etc.
|
||||
//!
|
||||
//! `box.scad` should be ```use```d and `box_assembly.scad` ```include```d.
|
||||
//! `box.scad` should be `use`d and `box_assembly.scad` `include`d.
|
||||
//!
|
||||
//! A box is defined with a list that specifies the inside dimensions, top, bottom and side sheet materials, the
|
||||
//! screw type and printed part wall thickness. This diagram shows how the various dimensions are labelled:
|
||||
@@ -33,25 +33,27 @@
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../vitamins/sheet.scad>
|
||||
use <../vitamins/screw.scad>
|
||||
use <../vitamins/washer.scad>
|
||||
|
||||
include <../vitamins/inserts.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
use <../utils/quadrant.scad>
|
||||
use <../utils/round.scad>
|
||||
|
||||
bezel_clearance = 0.2;
|
||||
sheet_end_clearance = 1;
|
||||
sheet_slot_clearance = 0.2;
|
||||
|
||||
function box_screw(type) = type[0]; //! Screw type to be used at the corners
|
||||
function box_wall(type) = type[1]; //! Wall thickness of 3D parts
|
||||
function box_sheets(type) = type[2]; //! Sheet type used for the sides
|
||||
function box_top_sheet(type) = type[3]; //! Sheet type for the top
|
||||
function box_base_sheet(type)= type[4]; //! Sheet type for the bottom
|
||||
function box_feet(type) = type[5]; //! True to enable feet on the bottom bezel
|
||||
function box_width(type) = type[6]; //! Internal width
|
||||
function box_depth(type) = type[7]; //! Internal depth
|
||||
function box_height(type) = type[8]; //! Internal height
|
||||
function box_screw(type) = type[0]; //! Screw type to be used at the corners
|
||||
function box_shelf_screw(type) = type[1]; //! Screw type to hold a shelf
|
||||
function box_wall(type) = type[2]; //! Wall thickness of 3D parts
|
||||
function box_sheets(type) = type[3]; //! Sheet type used for the sides
|
||||
function box_top_sheet(type) = type[4]; //! Sheet type for the top
|
||||
function box_base_sheet(type) = type[5]; //! Sheet type for the bottom
|
||||
function box_feet(type) = type[6]; //! True to enable feet on the bottom bezel
|
||||
function box_width(type) = type[7]; //! Internal width
|
||||
function box_depth(type) = type[8]; //! Internal depth
|
||||
function box_height(type) = type[9]; //! Internal height
|
||||
|
||||
function box(screw, wall, sheets, top_sheet, base_sheet, size, feet = false, shelf_screw = M3_dome_screw) = //! Construct a property list for a box.
|
||||
concat([screw, shelf_screw, wall, sheets, top_sheet, base_sheet, feet], size);
|
||||
|
||||
function box_bezel_clearance(type) = bezel_clearance;
|
||||
|
||||
@@ -60,6 +62,7 @@ function box_profile_overlap(type) = 3 + sheet_end_clearance / 2;
|
||||
|
||||
function box_washer(type) = screw_washer(box_screw(type));
|
||||
function box_insert(type) = screw_insert(box_screw(type));
|
||||
function box_shelf_insert(type) = screw_insert(box_shelf_screw(type));
|
||||
|
||||
function box_hole_inset(type) = washer_radius(box_washer(type)) + 1;
|
||||
function box_insert_r(type) = insert_hole_radius(box_insert(type));
|
||||
@@ -88,32 +91,42 @@ function box_bezel_height(type, bottom) = //! Bezel height for top or bottom
|
||||
|
||||
grill_hole = 5;
|
||||
grill_gap = 1.9;
|
||||
module grill(width, height, r = 1000, poly = false, h = 0) { //! A staggered array of 5mm holes to make grills in sheets. Can be constrained to be circular. Set ```poly``` ```true``` for printing, ```false``` for milling.
|
||||
|
||||
function box_grill_hole_r() = grill_hole / 2;
|
||||
|
||||
module grill_hole_positions(width, height, r = 1000) {
|
||||
nx = floor(width / (grill_hole + grill_gap));
|
||||
xpitch = width / nx;
|
||||
ny = floor(height / ((grill_hole + grill_gap) * cos(30)));
|
||||
ypitch = height / ny;
|
||||
|
||||
for(y = [0 : ny - 1], x = [0 : nx - 1 - (y % 2)]) {
|
||||
$x = -width / 2 + (x + 0.5 + (y % 2) / 2) * xpitch;
|
||||
$y = -height / 2 + (y + 0.5) * ypitch;
|
||||
if(sqrt(sqr($x) + sqr($y)) + grill_hole / 2 <= r)
|
||||
translate([$x, $y])
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
module grill(width, height, r = 1000, poly = false, h = 0) { //! A staggered array of 5mm holes to make grills in sheets. Can be constrained to be circular. Set `poly` `true` for printing, `false` for milling.
|
||||
extrude_if(h)
|
||||
for(y = [0 : ny - 1], x = [0 : nx - 1 - (y % 2)]) {
|
||||
x = -width / 2 + (x + 0.5 + (y % 2) / 2) * xpitch;
|
||||
y = -height / 2 + (y + 0.5) * ypitch;
|
||||
if(sqrt(sqr(x) + sqr(y)) + grill_hole / 2 <= r)
|
||||
translate([x, y])
|
||||
if(poly)
|
||||
poly_circle(r = grill_hole / 2);
|
||||
else
|
||||
circle(d = grill_hole);
|
||||
}
|
||||
if(poly)
|
||||
grill_hole_positions(width, height, r)
|
||||
poly_circle(r = grill_hole / 2);
|
||||
else
|
||||
grill_hole_positions(width, height, r)
|
||||
circle(d = grill_hole);
|
||||
}
|
||||
|
||||
module box_corner_profile_2D(type) { //! The 2D shape of the corner profile.
|
||||
t = box_sheet_slot(type);
|
||||
inset = box_corner_gap(type) + box_profile_overlap(type);
|
||||
difference() {
|
||||
union() {
|
||||
quadrant(box_hole_inset(type) + box_boss_r(type), box_boss_r(type)); // inside corner
|
||||
|
||||
translate([box_corner_gap(type) + box_profile_overlap(type), box_corner_gap(type) + box_profile_overlap(type)])
|
||||
translate([inset, inset])
|
||||
rotate(180)
|
||||
quadrant(box_profile_overlap(type) + box_corner_rad(type), box_corner_rad(type)); // outside corner
|
||||
}
|
||||
@@ -130,7 +143,7 @@ module box_corner_profile(type) { //! Generates the corner profile STL for 3D pr
|
||||
|
||||
length = box_height(type) - 2 * box_margin(type);
|
||||
difference() {
|
||||
linear_extrude(height = length, center = true, convexity = 5)
|
||||
linear_extrude(length, center = true, convexity = 5)
|
||||
box_corner_profile_2D(type);
|
||||
|
||||
for(z = [-1, 1])
|
||||
@@ -149,24 +162,24 @@ module box_corner_profile_section(type, section, sections) { //! Generates inter
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(height = h, convexity = 5)
|
||||
linear_extrude(h, convexity = 5)
|
||||
box_corner_profile_2D(type);
|
||||
|
||||
if(!last_section) // male end always at the top
|
||||
translate_z(section_length - 1)
|
||||
for(i = [0 : 1], offset = i * layer_height)
|
||||
linear_extrude(height = overlap + 1 - offset)
|
||||
linear_extrude(overlap + 1 - offset)
|
||||
offset(1 + offset - layer_height)
|
||||
offset(-overlap_wall - 1)
|
||||
box_corner_profile_2D(type);
|
||||
}
|
||||
if(section > 0)
|
||||
translate_z(last_section ? h : 0) { // female at bottom unless last section
|
||||
linear_extrude(height = 2 * (overlap + layer_height), center = true, convexity = 5)
|
||||
linear_extrude(2 * (overlap + layer_height), center = true, convexity = 5)
|
||||
offset(-overlap_wall)
|
||||
box_corner_profile_2D(type);
|
||||
|
||||
linear_extrude(height = 2 * layer_height, center = true, convexity = 5)
|
||||
linear_extrude(2 * layer_height, center = true, convexity = 5)
|
||||
offset(-overlap_wall + layer_height)
|
||||
box_corner_profile_2D(type);
|
||||
}
|
||||
@@ -176,6 +189,15 @@ module box_corner_profile_section(type, section, sections) { //! Generates inter
|
||||
}
|
||||
}
|
||||
|
||||
module box_corner_profile_sections(type, section, sections) { //! Generate four copies of a corner profile section
|
||||
stl("box_corner_profile");
|
||||
offset = box_boss_r(type) + 1;
|
||||
for(i = [0 : 3])
|
||||
rotate(i * 90)
|
||||
translate([offset, offset])
|
||||
box_corner_profile_section(type, section, sections);
|
||||
}
|
||||
|
||||
module box_corner_quadrants(type, width, depth)
|
||||
for(corner = [0:3]) {
|
||||
x = [-1,1,1,-1][corner];
|
||||
@@ -191,63 +213,69 @@ module box_bezel(type, bottom) { //! Generates top and bottom bezel STLs
|
||||
feet = bottom && box_feet(type);
|
||||
t = box_sheet_slot(type);
|
||||
outset = box_outset(type);
|
||||
inset = box_inset(type);
|
||||
inner_r = box_sheet_r(type);
|
||||
foot_height = box_corner_gap(type) + sheet_thickness(box_base_sheet(type)) + washer_thickness(box_washer(type)) + screw_head_height(box_screw(type)) + box_profile_overlap(type) + 2;
|
||||
cgap = box_corner_gap(type);
|
||||
foot_height = cgap + sheet_thickness(box_base_sheet(type)) + washer_thickness(box_washer(type)) + screw_head_height(box_screw(type)) + box_profile_overlap(type) + 2;
|
||||
foot_length = box_corner_rad(type) * 2;
|
||||
height = box_bezel_height(type, bottom);
|
||||
foot_extension = foot_height - height;
|
||||
|
||||
difference() {
|
||||
w = box_width(type);
|
||||
d = box_depth(type);
|
||||
translate_z(-box_profile_overlap(type)) difference() {
|
||||
rounded_rectangle([box_width(type) + 2 * outset, box_depth(type) + 2 * outset, feet ? foot_height : height], box_corner_rad(type), false);
|
||||
tw = w + 2 * outset;
|
||||
td = d + 2 * outset;
|
||||
rounded_rectangle([tw, td, feet ? foot_height : height], box_corner_rad(type), false);
|
||||
//
|
||||
// Remove edges between the feet
|
||||
//
|
||||
if(feet)
|
||||
hull() {
|
||||
translate_z(height + 0.5)
|
||||
cube([box_width(type) - 2 * foot_length, box_depth(type) + 2 * outset + 1, 1], center = true);
|
||||
cube([w - 2 * foot_length, td + 1, 1], center = true);
|
||||
|
||||
translate_z(foot_height + 1)
|
||||
cube([box_width(type) - 2 * (foot_length - foot_extension), box_depth(type) + 2 * outset + 1, 1], center = true);
|
||||
cube([w - 2 * (foot_length - foot_extension), td + 1, 1], center = true);
|
||||
}
|
||||
if(feet)
|
||||
hull() {
|
||||
translate_z(height + 0.5)
|
||||
cube([box_width(type) + 2 * outset + 1, box_depth(type) - 2 * foot_length, 1], center = true);
|
||||
cube([tw + 1, d - 2 * foot_length, 1], center = true);
|
||||
|
||||
translate_z(foot_height + 1)
|
||||
cube([box_width(type) + 2 * outset + 1, box_depth(type) - 2 * (foot_length - foot_extension), 1], center = true);
|
||||
cube([tw + 1, d - 2 * (foot_length - foot_extension), 1], center = true);
|
||||
}
|
||||
}
|
||||
//
|
||||
// slots for side panels
|
||||
//
|
||||
translate_z(-box_profile_overlap(type))
|
||||
linear_extrude(height = 2 * box_profile_overlap(type), center = true)
|
||||
linear_extrude(2 * box_profile_overlap(type), center = true)
|
||||
for(i = [-1, 1]) {
|
||||
translate([i * (box_width(type) / 2 + t / 2 - sheet_slot_clearance / 2), 0])
|
||||
square([t, box_depth(type) - 2 * box_corner_gap(type)], center = true);
|
||||
translate([i * (w + t - sheet_slot_clearance) / 2, 0])
|
||||
square([t, d - 2 * cgap], center = true);
|
||||
|
||||
translate([0, i * (box_depth(type) / 2 + t / 2 - sheet_slot_clearance / 2)])
|
||||
square([box_width(type) - 2 * box_corner_gap(type), t], center = true);
|
||||
translate([0, i * (d + t - sheet_slot_clearance) / 2])
|
||||
square([w - 2 * cgap, t], center = true);
|
||||
}
|
||||
//
|
||||
// recess for top / bottom panel
|
||||
//
|
||||
translate_z(box_corner_gap(type))
|
||||
rounded_rectangle([box_width(type) + bezel_clearance, box_depth(type) + bezel_clearance, height], inner_r + bezel_clearance / 2, false);
|
||||
translate_z(cgap)
|
||||
rounded_rectangle([w + bezel_clearance, d + bezel_clearance, height], inner_r + bezel_clearance / 2, false);
|
||||
//
|
||||
// leave plastic over the corner profiles
|
||||
//
|
||||
translate_z(-box_profile_overlap(type) - 1)
|
||||
linear_extrude(height = box_profile_overlap(type) + box_corner_gap(type) + 2)
|
||||
linear_extrude(box_profile_overlap(type) + cgap + 2)
|
||||
union() {
|
||||
difference() {
|
||||
square([box_width(type) - 2 * box_inset(type),
|
||||
box_depth(type) - 2 * box_inset(type)], center = true);
|
||||
square([w - 2 * inset,
|
||||
d - 2 * inset], center = true);
|
||||
|
||||
box_corner_quadrants(type, box_width(type), box_depth(type));
|
||||
box_corner_quadrants(type, w, d);
|
||||
}
|
||||
box_screw_hole_positions(type)
|
||||
poly_circle(screw_clearance_radius(box_screw(type)));
|
||||
@@ -259,32 +287,35 @@ dowel_length = 20;
|
||||
dowel_wall = extrusion_width * 3;
|
||||
dowel_h_wall = layer_height * 6;
|
||||
|
||||
|
||||
module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlocking sections of the bezel to allow it to be bigger than the printer
|
||||
w = (box_width(type) + 2 * box_outset(type)) / cols;
|
||||
h = (box_depth(type) + 2 * box_outset(type)) / rows;
|
||||
tw = box_width(type) + 2 * box_outset(type);
|
||||
w = tw / cols;
|
||||
th = box_depth(type) + 2 * box_outset(type);
|
||||
h = th / rows;
|
||||
bw = box_outset(type) - bezel_clearance / 2;
|
||||
bw2 = box_outset(type) + box_inset(type);
|
||||
|
||||
dw = bw - 2 * dowel_wall;
|
||||
dh = box_bezel_height(type, bottom) - dowel_h_wall;
|
||||
|
||||
dh2 = box_profile_overlap(type) + box_corner_gap(type) - dowel_h_wall;
|
||||
profile_overlap = box_profile_overlap(type);
|
||||
|
||||
dh2 = profile_overlap + box_corner_gap(type) - dowel_h_wall;
|
||||
|
||||
end_clearance = 0.5;
|
||||
module male() {
|
||||
rotate([90, 0, 90])
|
||||
linear_extrude(height = dowel_length - 2 * end_clearance, center = true)
|
||||
linear_extrude(dowel_length - 2 * end_clearance, center = true)
|
||||
difference() {
|
||||
union() {
|
||||
h = dh - layer_height;
|
||||
h1 = dh - layer_height;
|
||||
h2 = dh2 - layer_height;
|
||||
hull() {
|
||||
translate([bw / 2, h / 2])
|
||||
square([dw - 1, h], center = true);
|
||||
translate([bw / 2, h1 / 2])
|
||||
square([dw - 1, h1], center = true);
|
||||
|
||||
translate([bw / 2, (h - 1) / 2])
|
||||
square([dw, h - 1], center = true);
|
||||
translate([bw / 2, (h1 - 1) / 2])
|
||||
square([dw, h1 - 1], center = true);
|
||||
}
|
||||
|
||||
hull() {
|
||||
@@ -296,7 +327,7 @@ module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlo
|
||||
}
|
||||
}
|
||||
translate([bw2 / 2, 0])
|
||||
square([box_sheet_slot(type), 2 * box_profile_overlap(type)], center = true);
|
||||
square([box_sheet_slot(type), 2 * profile_overlap], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +368,7 @@ module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlo
|
||||
render() difference() {
|
||||
union() {
|
||||
clip(xmin = 0, xmax = w, ymin = 0, ymax = h)
|
||||
translate([box_width(type) / 2 + box_outset(type) - x * w, box_depth(type) / 2 + box_outset(type) - y * h, box_profile_overlap(type)])
|
||||
translate([tw / 2 - x * w, th / 2 - y * h, profile_overlap])
|
||||
box_bezel(type, bottom);
|
||||
|
||||
if(x < cols - 1 && y == 0)
|
||||
@@ -397,21 +428,14 @@ module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlo
|
||||
}
|
||||
}
|
||||
|
||||
module box_shelf_blank(type) { //! Generates a 2D template for a shelf sheet
|
||||
dxf("box_shelf");
|
||||
|
||||
difference() {
|
||||
sheet_2D(box_sheets(type), box_width(type) - bezel_clearance, box_depth(type) - bezel_clearance, 1);
|
||||
|
||||
offset(bezel_clearance / 2)
|
||||
box_corner_quadrants(type, box_width(type), box_depth(type));
|
||||
}
|
||||
}
|
||||
|
||||
module box_screw_hole_positions(type)
|
||||
module box_screw_hole_positions(type) {
|
||||
inset = box_hole_inset(type);
|
||||
w = box_width(type) / 2 - inset;
|
||||
d = box_depth(type) / 2 - inset;
|
||||
for(x = [-1, 1], y = [-1, 1])
|
||||
translate([x * (box_width(type) / 2 - box_hole_inset(type)), y * (box_depth(type) / 2 - box_hole_inset(type))])
|
||||
translate([x * w, y * d])
|
||||
children();
|
||||
}
|
||||
|
||||
module box_base_blank(type) { //! Generates a 2D template for the base sheet
|
||||
dxf("box_base");
|
||||
@@ -439,25 +463,126 @@ function subst_sheet(type, sheet) =
|
||||
let(s = box_sheets(type))
|
||||
sheet ? assert(sheet_thickness(sheet) == sheet_thickness(s)) sheet : s;
|
||||
|
||||
module box_left_blank(type, sheet = false) { //! Generates a 2D template for the left sheet, ```sheet``` can be set to override the type
|
||||
module box_shelf_blank(type, sheet = false) { //! Generates a 2D template for a shelf sheet
|
||||
dxf("box_shelf");
|
||||
|
||||
difference() {
|
||||
sheet_2D(subst_sheet(type, sheet), box_width(type) - bezel_clearance, box_depth(type) - bezel_clearance, 1);
|
||||
|
||||
offset(bezel_clearance / 2)
|
||||
box_corner_quadrants(type, box_width(type), box_depth(type));
|
||||
}
|
||||
}
|
||||
|
||||
module box_shelf_screw_positions(type, screw_positions, thickness = 0, wall = undef) { //! Place children at the shelf screw positions
|
||||
w = is_undef(wall) ? box_wall(type) : wall;
|
||||
insert = box_shelf_insert(type);
|
||||
translate_z(-insert_boss_radius(insert, w))
|
||||
for(p = screw_positions)
|
||||
multmatrix(p)
|
||||
translate_z(thickness)
|
||||
children();
|
||||
}
|
||||
|
||||
module box_shelf_bracket(type, screw_positions, wall = undef) { //! Generates a shelf bracket, the first optional child is a 2D cutout and the second 3D cutouts
|
||||
stl("shelf_bracket");
|
||||
w = is_undef(wall) ? box_wall(type) : wall;
|
||||
insert = box_shelf_insert(type);
|
||||
lip = 2 * insert_boss_radius(insert, w);
|
||||
width = insert_length(insert) + w;
|
||||
|
||||
module shape()
|
||||
difference() {
|
||||
square([box_width(type), box_depth(type)], center = true);
|
||||
|
||||
offset(bezel_clearance / 2)
|
||||
box_corner_quadrants(type, box_width(type), box_depth(type));
|
||||
|
||||
if($children)
|
||||
hflip()
|
||||
children();
|
||||
}
|
||||
|
||||
module boss()
|
||||
translate_z(-width + eps)
|
||||
linear_extrude(width - 2 * eps)
|
||||
hull() {
|
||||
circle4n(r = lip / 2 - eps);
|
||||
|
||||
translate([-lip / 2, -lip / 2 + eps])
|
||||
square([lip, eps]);
|
||||
}
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(w)
|
||||
difference() {
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
|
||||
round(2) offset(-width)
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
}
|
||||
|
||||
linear_extrude(lip)
|
||||
difference() {
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
|
||||
offset(-w)
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
}
|
||||
|
||||
hflip()
|
||||
box_shelf_screw_positions(type, screw_positions, 0, w)
|
||||
boss();
|
||||
}
|
||||
if($children > 1)
|
||||
hflip()
|
||||
children(1);
|
||||
|
||||
hflip()
|
||||
box_shelf_screw_positions(type, screw_positions, 0, w)
|
||||
insert_hole(insert, counterbore = 1, horizontal = true);
|
||||
}
|
||||
}
|
||||
|
||||
module box_shelf_bracket_section(type, rows, cols, x, y) { //! Generates sections of the shelf bracket to allow it to be bigger than the printer
|
||||
tw = box_width(type);
|
||||
w = tw / cols;
|
||||
th = box_depth(type);
|
||||
h = th / rows;
|
||||
|
||||
clip(xmin = 0, xmax = w, ymin = 0, ymax = h)
|
||||
translate([tw / 2 - x * w, th / 2 - y * h])
|
||||
children();
|
||||
}
|
||||
|
||||
module box_left_blank(type, sheet = false) { //! Generates a 2D template for the left sheet, `sheet` can be set to override the type
|
||||
dxf("box_left");
|
||||
|
||||
sheet_2D(subst_sheet(type, sheet), box_depth(type) - sheet_reduction(type), box_height(type) - sheet_reduction(type), 1);
|
||||
}
|
||||
|
||||
module box_right_blank(type, sheet = false) { //! Generates a 2D template for the right sheet, ```sheet``` can be set to override the type
|
||||
module box_right_blank(type, sheet = false) { //! Generates a 2D template for the right sheet, `sheet` can be set to override the type
|
||||
dxf("box_right");
|
||||
|
||||
sheet_2D(subst_sheet(type, sheet), box_depth(type) - sheet_reduction(type), box_height(type) - sheet_reduction(type), 1);
|
||||
}
|
||||
|
||||
module box_front_blank(type, sheet = false) { //! Generates a 2D template for the front sheet, ```sheet``` can be set to override the type
|
||||
module box_front_blank(type, sheet = false) { //! Generates a 2D template for the front sheet, `sheet` can be set to override the type
|
||||
dxf("box_front");
|
||||
|
||||
sheet_2D(subst_sheet(type, sheet), box_width(type) - sheet_reduction(type), box_height(type) - sheet_reduction(type), 1);
|
||||
}
|
||||
|
||||
module box_back_blank(type, sheet = false) { //! Generates a 2D template for the back sheet, ```sheet``` can be set to override the type
|
||||
module box_back_blank(type, sheet = false) { //! Generates a 2D template for the back sheet, `sheet` can be set to override the type
|
||||
dxf("box_back");
|
||||
|
||||
sheet_2D(subst_sheet(type, sheet), box_width(type) - sheet_reduction(type), box_height(type) - sheet_reduction(type), 1);
|
||||
|
@@ -18,7 +18,7 @@
|
||||
//
|
||||
|
||||
//
|
||||
// The assembly is ```include```d so the panel definitions can be overridden to add holes and components.
|
||||
// The assembly is `include`d so the panel definitions can be overridden to add holes and components.
|
||||
// The _box_module also needs to be wrapped in the file that uses it so it can be called without
|
||||
// parameters to make the assembly views. E.g. module box_assembly() _box_assembly(box);
|
||||
//
|
||||
@@ -33,7 +33,7 @@ assembly("box") {
|
||||
y = [-1,-1,1,1][corner];
|
||||
translate([x * (box_width(type) / 2 + 25 * exploded()), y * (box_depth(type) / 2 + 25 * exploded())])
|
||||
rotate(corner * 90) {
|
||||
color(pp2_colour) render()
|
||||
stl_colour(pp2_colour) render()
|
||||
box_corner_profile(type);
|
||||
|
||||
translate([box_hole_inset(type), box_hole_inset(type)])
|
||||
@@ -50,7 +50,7 @@ assembly("box") {
|
||||
translate_z(z * (box_height(type) / 2 - box_corner_gap(type) + 50 * exploded()))
|
||||
rotate([z * 90 - 90, 0, 0])
|
||||
if(bezels && (z > 0 ? top : base))
|
||||
color(pp1_colour) render() box_bezel(type, z < 0);
|
||||
stl_colour(pp1_colour) render() box_bezel(type, z < 0);
|
||||
|
||||
translate_z(z * (box_height(type) / 2 + sheet_thickness + 50 * exploded()))
|
||||
box_screw_hole_positions(type)
|
||||
|
@@ -21,12 +21,16 @@
|
||||
//! A box made from CNC cut panels butted together using printed fixing blocks. Useful for making large
|
||||
//! boxes with minimal 3D printing. More blocks are added as the box gets bigger.
|
||||
//!
|
||||
//! Needs to be ```include```d rather than ```use```d to allow the panel definitions to be overridden to add holes
|
||||
//! Needs to be `include`d rather than `use`d to allow the panel definitions to be overridden to add holes
|
||||
//! and mounted components.
|
||||
//!
|
||||
//! A list specifies the internal dimensions, screw type, top, bottom and side sheet types and the block
|
||||
//! maximum spacing.
|
||||
//!
|
||||
//! * An optional name can be specified to allow more then one box in a project.
|
||||
//! * An optional list of fixing blocks to be omitted can be given.
|
||||
//! * Star washers can be omitted by setting the 11th parameter to false.
|
||||
//!
|
||||
//! Uses [fixing blocks](#fixing_block) and [corner blocks](#corner_block).
|
||||
//
|
||||
|
||||
@@ -34,17 +38,27 @@ use <fixing_block.scad>
|
||||
use <corner_block.scad>
|
||||
use <../utils/maths.scad>
|
||||
|
||||
function bbox_screw(type) = type[0]; //! Screw type for corner blocks
|
||||
function bbox_sheets(type) = type[1]; //! Sheet type for the sides
|
||||
function bbox_base_sheet(type)= type[2]; //! Sheet type for the base
|
||||
function bbox_top_sheet(type) = type[3]; //! Sheet type for the top
|
||||
function bbox_span(type) = type[4]; //! Maximum span between fixing blocks
|
||||
function bbox_width(type) = type[5]; //! Internal width
|
||||
function bbox_depth(type) = type[6]; //! Internal depth
|
||||
function bbox_height(type) = type[7]; //! Internal height
|
||||
function bbox_screw(type) = type[0]; //! Screw type for corner blocks
|
||||
function bbox_sheets(type) = type[1]; //! Sheet type for the sides
|
||||
function bbox_base_sheet(type) = type[2]; //! Sheet type for the base
|
||||
function bbox_top_sheet(type) = type[3]; //! Sheet type for the top
|
||||
function bbox_span(type) = type[4]; //! Maximum span between fixing blocks
|
||||
function bbox_width(type) = type[5]; //! Internal width
|
||||
function bbox_depth(type) = type[6]; //! Internal depth
|
||||
function bbox_height(type) = type[7]; //! Internal height
|
||||
function bbox_name(type) = type[8] ? type[8] : "bbox"; //! Optional name if there is more than one box in a project
|
||||
function bbox_skip_blocks(type)= type[9] ? type[9] : []; //! List of fixing blocks to skip, used to allow a hinged panel for example
|
||||
function star_washers(type) = type[10] ? type[10] : is_undef(type[10]); //! Set to false to remove star washers.
|
||||
|
||||
function bbox(screw, sheets, base_sheet, top_sheet, span, size, name = "bbox", skip_blocks = [], star_washers = true) = //! Construct the property list for a butt_box
|
||||
[ screw, sheets, base_sheet, top_sheet, span, size.x, size.y, size.z, name, skip_blocks, star_washers ];
|
||||
|
||||
function bbox_volume(type) = bbox_width(type) * bbox_depth(type) * bbox_height(type) / 1000000; //! Internal volume in litres
|
||||
function bbox_area(type) = let(w = bbox_width(type), d = bbox_depth(type), h = bbox_height(type)) //! Internal surdface area in m^2
|
||||
2 * (w * d + w * h + d * h) / 1000000;
|
||||
|
||||
module bbox_shelf_blank(type) { //! 2D template for a shelf
|
||||
dxf("bbox_shelf");
|
||||
dxf(str(bbox_name(type), "_shelf"));
|
||||
|
||||
sheet_2D(bbox_sheets(type), bbox_width(type), bbox_depth(type), 1);
|
||||
}
|
||||
@@ -60,18 +74,8 @@ function corner_block_positions(type) = let(
|
||||
y = [-1,-1,1,1][corner]
|
||||
) translate([x * (width / 2), y * (depth / 2), z * height / 2]) *
|
||||
rotate([z > 0 ? 180 : 0, 0, corner * 90 + (z > 0 ? 90 : 0)])
|
||||
|
||||
];
|
||||
|
||||
module corner_block_positions(type) {
|
||||
bt = sheet_thickness(bbox_base_sheet(type));
|
||||
tt = sheet_thickness(bbox_top_sheet(type));
|
||||
for(p = corner_block_positions(type))
|
||||
let($thickness = transform([0, 0, 0], p).z > 0 ? tt : bt)
|
||||
multmatrix(p)
|
||||
children();
|
||||
}
|
||||
|
||||
function corner_holes(type) = [for(p = corner_block_positions(type), q = corner_block_holes(bbox_screw(type))) p * q];
|
||||
|
||||
function fixing_block_positions(type) = let(
|
||||
@@ -84,55 +88,44 @@ function fixing_block_positions(type) = let(
|
||||
dspans = floor(depth / span),
|
||||
dspan = depth / (dspans + 1),
|
||||
hspans = floor(height / span),
|
||||
hspan = height / (hspans + 1)
|
||||
hspan = height / (hspans + 1),
|
||||
skips = bbox_skip_blocks(type)
|
||||
)
|
||||
[
|
||||
for(i = [0 : 1 : wspans - 1], y = [-1, 1], z = [-1, 1])
|
||||
translate([(i - (wspans - 1) / 2) * wspan, y * depth / 2, z * height / 2]) *
|
||||
rotate([0, z * 90 + 90, y * 90 + 90]),
|
||||
if(!in(skips, [0, y, z]))
|
||||
translate([(i - (wspans - 1) / 2) * wspan, y * depth / 2, z * height / 2]) *
|
||||
rotate([0, z * 90 + 90, y * 90 + 90]),
|
||||
|
||||
for(i = [0 : 1 : dspans - 1], x = [-1, 1], z = [-1, 1])
|
||||
translate([x * width / 2, (i - (dspans - 1) / 2) * dspan, z * height / 2]) *
|
||||
rotate([0, z * 90 + 90, x * 90]),
|
||||
if(!in(skips, [x, 0, z]))
|
||||
translate([x * width / 2, (i - (dspans - 1) / 2) * dspan, z * height / 2]) *
|
||||
rotate([0, z * 90 + 90, x * 90]),
|
||||
|
||||
for(i = [0 : 1 : hspans - 1], x = [-1, 1], y = [-1, 1])
|
||||
translate([x * width / 2, y * depth / 2, (i - (hspans - 1) / 2) * hspan]) *
|
||||
rotate([y > 0 ? 180 : 0, x * y * 90]),
|
||||
|
||||
if(!in(skips, [x, y, 0]))
|
||||
translate([x * width / 2, y * depth / 2, (i - (hspans - 1) / 2) * hspan]) *
|
||||
rotate([y > 0 ? 180 : 0, x * y * 90, 0]),
|
||||
];
|
||||
|
||||
function side_holes(type) = [for(p = fixing_block_positions(type), q = fixing_block_holes(bbox_screw(type))) p * q];
|
||||
|
||||
module fixing_block_positions(type) {
|
||||
t = sheet_thickness(bbox_sheets(type));
|
||||
bt = sheet_thickness(bbox_base_sheet(type));
|
||||
tt = sheet_thickness(bbox_top_sheet(type));
|
||||
h = bbox_height(type) / 2 - 1;
|
||||
for(p = fixing_block_positions(type))
|
||||
let(z = transform([0, 0, 0], p).z, $thickness = z > h ? tt : z < -h ? bt : t)
|
||||
multmatrix(p)
|
||||
children();
|
||||
}
|
||||
|
||||
module drill_holes(type, t)
|
||||
for(list = [corner_holes(type), side_holes(type)], p = list)
|
||||
let(q = t * p)
|
||||
if(abs(transform([0, 0, 0], q).z) < eps)
|
||||
multmatrix(q)
|
||||
drill(screw_clearance_radius(bbox_screw(type)), 0);
|
||||
module bbox_drill_holes(type, t)
|
||||
position_children(concat(corner_holes(type), side_holes(type)), t)
|
||||
drill(screw_clearance_radius(bbox_screw(type)), 0);
|
||||
|
||||
module bbox_base_blank(type) { //! 2D template for the base
|
||||
dxf("bbox_base");
|
||||
dxf(str(bbox_name(type), "_base"));
|
||||
|
||||
difference() {
|
||||
sheet_2D(bbox_base_sheet(type), bbox_width(type), bbox_depth(type), 1);
|
||||
|
||||
drill_holes(type, translate(bbox_height(type) / 2));
|
||||
bbox_drill_holes(type, translate(bbox_height(type) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
module bbox_top_blank(type) { //! 2D template for the top
|
||||
dxf("bbox_top");
|
||||
dxf(str(bbox_name(type), "_top"));
|
||||
|
||||
t = sheet_thickness(bbox_sheets(type));
|
||||
|
||||
@@ -140,40 +133,44 @@ module bbox_top_blank(type) { //! 2D template for the top
|
||||
translate([0, t / 2])
|
||||
sheet_2D(bbox_top_sheet(type), bbox_width(type) + 2 * t, bbox_depth(type) + t);
|
||||
|
||||
drill_holes(type, translate(-bbox_height(type) / 2));
|
||||
bbox_drill_holes(type, translate(-bbox_height(type) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
module bbox_left_blank(type) { //! 2D template for the left side
|
||||
dxf("bbox_left");
|
||||
function subst_sheet(type, sheet) =
|
||||
let(s = bbox_sheets(type))
|
||||
sheet ? assert(sheet_thickness(sheet) == sheet_thickness(s)) sheet : s;
|
||||
|
||||
module bbox_left_blank(type, sheet = false) { //! 2D template for the left side
|
||||
dxf(str(bbox_name(type), "_left"));
|
||||
|
||||
t = sheet_thickness(bbox_sheets(type));
|
||||
bb = sheet_thickness(bbox_base_sheet(type));
|
||||
|
||||
difference() {
|
||||
translate([-t / 2, -bb / 2])
|
||||
sheet_2D(bbox_sheets(type), bbox_depth(type) + t, bbox_height(type) + bb);
|
||||
sheet_2D(subst_sheet(type, sheet), bbox_depth(type) + t, bbox_height(type) + bb);
|
||||
|
||||
drill_holes(type, rotate([0, 90, 90]) * translate([bbox_width(type) / 2, 0]));
|
||||
bbox_drill_holes(type, rotate([0, 90, 90]) * translate([bbox_width(type) / 2, 0]));
|
||||
}
|
||||
}
|
||||
|
||||
module bbox_right_blank(type) { //! 2D template for the right side
|
||||
dxf("bbox_right");
|
||||
module bbox_right_blank(type, sheet = false) { //! 2D template for the right side
|
||||
dxf(str(bbox_name(type), "_right"));
|
||||
|
||||
t = sheet_thickness(bbox_sheets(type));
|
||||
bb = sheet_thickness(bbox_base_sheet(type));
|
||||
|
||||
difference() {
|
||||
translate([t / 2, -bb / 2])
|
||||
sheet_2D(bbox_sheets(type), bbox_depth(type) + t, bbox_height(type) + bb);
|
||||
sheet_2D(subst_sheet(type, sheet), bbox_depth(type) + t, bbox_height(type) + bb);
|
||||
|
||||
drill_holes(type, rotate([0, -90, 90]) * translate([-bbox_width(type) / 2, 0]));
|
||||
bbox_drill_holes(type, rotate([0, 90, 90]) * translate([-bbox_width(type) / 2, 0]));
|
||||
}
|
||||
}
|
||||
|
||||
module bbox_front_blank(type) { //! 2D template for the front
|
||||
dxf("bbox_front");
|
||||
module bbox_front_blank(type, sheet = false, width = 0) { //! 2D template for the front
|
||||
dxf(str(bbox_name(type), "_front"));
|
||||
|
||||
t = sheet_thickness(bbox_sheets(type));
|
||||
bb = sheet_thickness(bbox_base_sheet(type));
|
||||
@@ -181,23 +178,23 @@ module bbox_front_blank(type) { //! 2D template for the front
|
||||
|
||||
difference() {
|
||||
translate([0, (bt - bb) / 2])
|
||||
sheet_2D(bbox_sheets(type), bbox_width(type) + 2 * t, bbox_height(type) + bb + bt);
|
||||
sheet_2D(subst_sheet(type, sheet), max(bbox_width(type) + 2 * t, width), bbox_height(type) + bb + bt);
|
||||
|
||||
drill_holes(type, rotate([-90, 0, 0]) * translate([0, bbox_depth(type) / 2]));
|
||||
bbox_drill_holes(type, rotate([-90, 0, 0]) * translate([0, bbox_depth(type) / 2]));
|
||||
}
|
||||
}
|
||||
|
||||
module bbox_back_blank(type) { //! 2D template for the back
|
||||
dxf("bbox_back");
|
||||
module bbox_back_blank(type, sheet = false) { //! 2D template for the back
|
||||
dxf(str(bbox_name(type), "_back"));
|
||||
|
||||
bb = sheet_thickness(bbox_base_sheet(type));
|
||||
t = sheet_thickness(bbox_sheets(type));
|
||||
|
||||
difference() {
|
||||
translate([0, -bb / 2])
|
||||
sheet_2D(bbox_sheets(type), bbox_width(type), bbox_height(type) + bb);
|
||||
sheet_2D(subst_sheet(type, sheet), bbox_width(type), bbox_height(type) + bb);
|
||||
|
||||
drill_holes(type, rotate([90, 0, 0]) * translate([0, -bbox_depth(type) / 2]));
|
||||
bbox_drill_holes(type, rotate([-90, 0, 0]) * translate([0, -bbox_depth(type) / 2]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,54 +205,64 @@ module bbox_front(type) render_2D_sheet(bbox_sheets(type)) bbox_front_blank(type
|
||||
module bbox_left(type) render_2D_sheet(bbox_sheets(type)) bbox_left_blank(type); //! Default left side, can be overridden to customise
|
||||
module bbox_right(type) render_2D_sheet(bbox_sheets(type)) bbox_right_blank(type); //! Default right side, can be overridden to customise
|
||||
|
||||
module _bbox_assembly(type, top = true, base = true, left = true, right = true, back = true, front = true) //! The box assembly, wrap with a local copy without parameters
|
||||
assembly("bbox") {
|
||||
module _bbox_assembly(type, top = true, base = true, left = true, right = true, back = true, front = true) { //! The box assembly, wrap with a local copy without parameters
|
||||
width = bbox_width(type);
|
||||
depth = bbox_depth(type);
|
||||
height = bbox_height(type);
|
||||
echo("Box:", width, depth, height);
|
||||
echo("Box:", width, depth, height, volume = bbox_volume(type), area = bbox_area(type));
|
||||
|
||||
t = sheet_thickness(bbox_sheets(type));
|
||||
bt = sheet_thickness(bbox_base_sheet(type));
|
||||
tt = sheet_thickness(bbox_top_sheet(type));
|
||||
|
||||
corner_block_positions(type)
|
||||
fastened_corner_block_assembly(t, bbox_screw(type), $thickness);
|
||||
function is_missing_screw(p) = p.y > depth / 2 - 1 ? !back : false;
|
||||
|
||||
fixing_block_positions(type)
|
||||
fastened_fixing_block_assembly(t, bbox_screw(type), thickness2 = $thickness);
|
||||
assembly(bbox_name(type)) {
|
||||
|
||||
for(x = [-1, 1])
|
||||
translate([x * (width / 2 + t / 2 + eps + 25 * exploded()), 0])
|
||||
rotate([90, 0, x * 90])
|
||||
if(x > 0) {
|
||||
if(right)
|
||||
bbox_right(type);
|
||||
for(p = corner_block_positions(type))
|
||||
let(q = transform([0, 0, 0], p), thickness = q.z > 0 ? tt : bt)
|
||||
multmatrix(p)
|
||||
fastened_corner_block_assembly(is_missing_screw(q) && ((q.z > 0) != (q.x > 0)) ? 0 : t, bbox_screw(type), thickness,
|
||||
is_missing_screw(q) && ((q.z > 0) == (q.x > 0)) ? 0 : t, star_washers = star_washers(type));
|
||||
|
||||
h = height / 2 - 1;
|
||||
for(p = fixing_block_positions(type))
|
||||
let(q = transform([0, 0, 0], p), thickness = q.z > h ? tt : q.z < -h ? bt : t)
|
||||
multmatrix(p)
|
||||
fastened_fixing_block_assembly(is_missing_screw(q) ? 0 : t, bbox_screw(type), thickness2 = thickness, star_washers = star_washers(type));
|
||||
|
||||
for(x = [-1, 1])
|
||||
translate([x * (width / 2 + t / 2 + eps + 25 * exploded()), 0])
|
||||
rotate([90, 0, x * 90])
|
||||
if(x > 0) {
|
||||
if(right)
|
||||
bbox_right(type);
|
||||
}
|
||||
else
|
||||
if(left)
|
||||
bbox_left(type);
|
||||
|
||||
for(y = [1, -1])
|
||||
translate([0, y * (depth / 2 + t / 2 + eps + 25 * exploded())])
|
||||
rotate([90, 0, y * 90 + 90])
|
||||
if(y < 0) {
|
||||
if(front)
|
||||
bbox_front(type);
|
||||
}
|
||||
else
|
||||
if(back)
|
||||
bbox_back(type);
|
||||
|
||||
for(z = [-1, 1]) {
|
||||
sheet_thickness = z > 0 ? tt : bt;
|
||||
translate_z(z * (height / 2 + sheet_thickness / 2 + eps + 100 * exploded()))
|
||||
if(z > 0) {
|
||||
if(top)
|
||||
bbox_top(type);
|
||||
}
|
||||
else
|
||||
if(left)
|
||||
bbox_left(type);
|
||||
|
||||
for(y = [-1, 1])
|
||||
translate([0, y * (depth / 2 + t / 2 + eps + 25 * exploded())])
|
||||
rotate([90, 0, y * 90 + 90])
|
||||
if(y < 0) {
|
||||
if(front)
|
||||
bbox_front(type);
|
||||
}
|
||||
else
|
||||
if(back)
|
||||
bbox_back(type);
|
||||
|
||||
for(z = [-1, 1]) {
|
||||
sheet_thickness = z > 0 ? tt : bt;
|
||||
translate_z(z * (height / 2 + sheet_thickness / 2 + eps + 100 * exploded()))
|
||||
if(z > 0) {
|
||||
if(top)
|
||||
bbox_top(type);
|
||||
}
|
||||
else
|
||||
if(base)
|
||||
bbox_base(type);
|
||||
if(base)
|
||||
bbox_base(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@
|
||||
//! Printed cable grommets for passing cables through panels avoiding sharp edges and in the case
|
||||
//! of conductive panels, an extra layer of insulation.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../utils/core/core.scad>
|
||||
use <../vitamins/cable_strip.scad>
|
||||
|
||||
base = 1.25;
|
||||
@@ -59,7 +59,7 @@ module ribbon_grommet(ways, thickness) { //! Generate the STL for a printed ribb
|
||||
union() {
|
||||
for(side = [-1, 1])
|
||||
translate_z(side * (width - wall) / 2)
|
||||
linear_extrude(height = wall, center = true, convexity = 5)
|
||||
linear_extrude(wall, center = true, convexity = 5)
|
||||
difference() {
|
||||
hull() {
|
||||
translate([-length / 2, 0])
|
||||
@@ -73,7 +73,7 @@ module ribbon_grommet(ways, thickness) { //! Generate the STL for a printed ribb
|
||||
square([slot_length, slot_height]);
|
||||
}
|
||||
|
||||
linear_extrude(height = width -1, center = true)
|
||||
linear_extrude(width -1, center = true)
|
||||
difference() {
|
||||
ribbon_grommet_hole(ways, expand = false, h = 0);
|
||||
|
||||
@@ -130,15 +130,15 @@ module round_grommet_bottom(diameter, od = undef) { //! Generate the STL for a r
|
||||
}
|
||||
|
||||
module round_grommet_hole(diameter, h = 100) //! Make a hole for a round grommet
|
||||
drill(diameter / 2 + wall + clearance, h);
|
||||
drill(corrected_radius(diameter / 2) + wall + clearance, h);
|
||||
|
||||
module round_grommet_assembly(diameter, thickness, od = undef) {
|
||||
color(pp1_colour)
|
||||
stl_colour(pp1_colour)
|
||||
translate_z(wall)
|
||||
vflip()
|
||||
round_grommet_top(diameter, thickness, od);
|
||||
|
||||
color(pp2_colour)
|
||||
stl_colour(pp2_colour)
|
||||
translate_z(-thickness)
|
||||
vflip()
|
||||
round_grommet_bottom(diameter, od);
|
||||
@@ -149,12 +149,17 @@ module mouse_grommet_hole(r, h = 50, z = undef, expand = wall + clearance) //! M
|
||||
hull(){
|
||||
R = r + expand;
|
||||
translate([0, z == undef ? R : z])
|
||||
semi_circle(R);
|
||||
if(expand)
|
||||
semi_circle(R);
|
||||
else
|
||||
semi_teardrop(r = R, h = 0);
|
||||
|
||||
translate([-R, 0])
|
||||
square([2 * R, eps]);
|
||||
}
|
||||
|
||||
function mouse_grommet_offset(r) = r + wall;
|
||||
|
||||
module mouse_grommet(r, thickness) { //! Make the STL for a mouse grommet
|
||||
stl(str("mouse_grommet_", r * 10, "_", thickness));
|
||||
|
||||
@@ -165,14 +170,14 @@ module mouse_grommet(r, thickness) { //! Make the STL for a mouse grommet
|
||||
union() {
|
||||
for(side = [-1, 1])
|
||||
translate_z(side * (width - wall) / 2)
|
||||
linear_extrude(height = wall, center = true)
|
||||
linear_extrude(wall, center = true)
|
||||
difference() {
|
||||
mouse_grommet_hole(r + wall + overlap, z = r + wall, h = 0, expand = 0);
|
||||
mouse_grommet_hole(r, z = r + wall, h = 0, expand = wall + overlap);
|
||||
|
||||
translate([0, wall])
|
||||
mouse_grommet_hole(r, h = 0, expand = 0);
|
||||
}
|
||||
linear_extrude(height = width - 1, center = true)
|
||||
linear_extrude(width - 1, center = true)
|
||||
difference() {
|
||||
mouse_grommet_hole(r, h = 0, z = r + wall, expand = wall);
|
||||
|
||||
@@ -182,6 +187,23 @@ module mouse_grommet(r, thickness) { //! Make the STL for a mouse grommet
|
||||
}
|
||||
}
|
||||
|
||||
module mouse_grommet_assembly(r, thickness)
|
||||
stl_colour(pp1_colour)
|
||||
rotate([-90, 0, 0])
|
||||
mouse_grommet(r, thickness);
|
||||
|
||||
module ribbon_grommet_20_3_stl() ribbon_grommet(20, 3);
|
||||
module mouse_grommet_20_3_stl() mouse_grommet(2,3);
|
||||
module mouse_grommet_30_3_stl() mouse_grommet(3,3);
|
||||
module mouse_grommet_15_3_stl() mouse_grommet(1.5, 3);
|
||||
module mouse_grommet_20_3_stl() mouse_grommet(2, 3);
|
||||
module mouse_grommet_25_3_stl() mouse_grommet(2.5, 3);
|
||||
module mouse_grommet_30_3_stl() mouse_grommet(3, 3);
|
||||
|
||||
module round_grommet_bottom_30_stl() round_grommet_bottom(3);
|
||||
module round_grommet_bottom_40_stl() round_grommet_bottom(4);
|
||||
module round_grommet_bottom_50_stl() round_grommet_bottom(5);
|
||||
module round_grommet_bottom_60_stl() round_grommet_bottom(6);
|
||||
|
||||
module round_grommet_top_30_3_stl() round_grommet_top(3, 3);
|
||||
module round_grommet_top_40_3_stl() round_grommet_top(4, 3);
|
||||
module round_grommet_top_50_3_stl() round_grommet_top(5, 3);
|
||||
module round_grommet_top_60_3_stl() round_grommet_top(6, 3);
|
||||
|
392
printed/camera_housing.scad
Normal file
@@ -0,0 +1,392 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! Housings for PCB cameras.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/cameras.scad>
|
||||
use <../vitamins/pcb.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
|
||||
wall = 1.75;
|
||||
min_wall = 2 * extrusion_width;
|
||||
clearance = 0.2;
|
||||
|
||||
connector_size = [23, 6, 2.65]; // Worst case size of flat flex connector
|
||||
|
||||
cam_back_clearance = round_to_layer(1.5); // Clearance for components on the back of the pcb
|
||||
cam_back_overlap = 1; // How much the back overlaps the edge of the pcb
|
||||
cam_back_wall = min_wall;
|
||||
|
||||
function cam_front_clearance(cam) = round_to_layer(camera_connector_size(cam).z + clearance);
|
||||
|
||||
function cam_back_size(cam) = let(
|
||||
pcb = camera_pcb(cam),
|
||||
pcb_size = pcb_size(pcb),
|
||||
nut = screw_nut(pcb_screw(pcb)),
|
||||
holes = [for(h = pcb_holes(pcb)) pcb_coord(pcb, h).x],
|
||||
pitch = max(holes) - min(holes),
|
||||
length = pitch + 2 * (nut_radius(nut) + min_wall),
|
||||
width = pcb_size.y + (length - pcb_size.x) * cos(30)
|
||||
) [length, width, wall + max(connector_size.z, cam_back_clearance + nut_trap_depth(nut))];
|
||||
|
||||
|
||||
function cam_front_size(cam) = cam_back_size(cam) + [ //! Outside dimensions of the case
|
||||
2 * (wall + clearance),
|
||||
2 * (wall + clearance),
|
||||
pcb_thickness(camera_pcb(cam)) + cam_front_clearance(cam) + wall
|
||||
];
|
||||
|
||||
hinge_screw = M2_cap_screw;
|
||||
hinge_nut = screw_nut(hinge_screw);
|
||||
hinge_screw_length = 12;
|
||||
|
||||
hinge_r = nut_trap_radius(hinge_nut) + 3 * extrusion_width;
|
||||
hinge_h = wall + nut_trap_depth(hinge_nut);
|
||||
hinge_offset = hinge_r + 1;
|
||||
|
||||
bracket_screw = M3_dome_screw;
|
||||
|
||||
function cam_screw_length(cam) = let(
|
||||
front = cam_front_size(cam),
|
||||
screw = pcb_screw(camera_pcb(cam)),
|
||||
nut = screw_nut(screw)
|
||||
) screw_longer_than(front.z + washer_thickness(screw_washer(screw)) - nut_trap_depth(nut) + nut_thickness(nut, true));
|
||||
|
||||
function hinge_z(cam) = cam_screw_length(cam) - hinge_r;
|
||||
|
||||
module cam_holes(cam) {
|
||||
pcb = camera_pcb(cam);
|
||||
lens_y = camera_lens_offset(cam).y;
|
||||
two_holes = !!len([for (h = pcb_holes(pcb)) if(abs(pcb_coord(pcb, h).y - lens_y) < 1) true]);
|
||||
pcb_screw_positions(pcb) // screw holes
|
||||
if($i > 1 || !two_holes)
|
||||
children();
|
||||
}
|
||||
|
||||
module rpi_camera_focus_ring_stl() { //! Focus ring the glue onto RPI lens
|
||||
stl("rpi_camera_focus_ring");
|
||||
|
||||
rad = 15 / 2;
|
||||
hole_r1 = 2.5 / 2;
|
||||
hole_r2 = 5 / 2;
|
||||
thickness = 3;
|
||||
flutes = 8;
|
||||
angle = 180 / flutes;
|
||||
x = rad / (sin(angle / 2) + cos(angle / 2));
|
||||
r = x * sin(angle / 2);
|
||||
|
||||
difference() {
|
||||
linear_extrude(height = thickness, convexity = 5)
|
||||
difference() {
|
||||
union() {
|
||||
circle(x);
|
||||
for(i = [0 : flutes - 1])
|
||||
rotate([0, 0, 2 * angle * i])
|
||||
translate([x, 0])
|
||||
circle(r);
|
||||
}
|
||||
for(i = [0 : flutes - 1])
|
||||
rotate([0, 0, 2 * angle * i + angle])
|
||||
translate([x, 0])
|
||||
circle(r);
|
||||
}
|
||||
hull() {
|
||||
poly_cylinder(r = hole_r1, h = 0.1, center = true);
|
||||
|
||||
translate([0, 0, thickness])
|
||||
poly_cylinder(r = hole_r2, h = 0.1, center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module camera_back(cam) { //! Make the STL for a camera case back
|
||||
stl(str("camera_back_", cam[0]));
|
||||
pcb = camera_pcb(cam);
|
||||
back = cam_back_size(cam);
|
||||
screw = pcb_screw(pcb);
|
||||
nut = screw_nut(screw);
|
||||
|
||||
translate_z(back.z)
|
||||
hflip()
|
||||
difference() {
|
||||
translate_z(back.z / 2)
|
||||
cube(back, center = true);
|
||||
|
||||
translate([0, -cam_back_overlap])
|
||||
cube([pcb_length(pcb) - 2 * cam_back_overlap, pcb_width(pcb), 2 * cam_back_clearance], center = true);
|
||||
|
||||
translate([0, -pcb_width(pcb) / 2])
|
||||
cube([connector_size.x + 2 * clearance, 2 * connector_size.y + 1, 2 * round_to_layer(connector_size.z + clearance)], center = true);
|
||||
|
||||
translate_z(back.z)
|
||||
cam_holes(cam)
|
||||
hflip()
|
||||
nut_trap(screw, nut, supported = true);
|
||||
}
|
||||
}
|
||||
|
||||
module camera_front(cam, hinge = 0) { //! Make the STL for a camera case front
|
||||
stl(str("camera_front_", cam[0]));
|
||||
front = cam_front_size(cam);
|
||||
back = cam_back_size(cam);
|
||||
pcb = camera_pcb(cam);
|
||||
pcb_size = pcb_size(pcb);
|
||||
lens_offset = camera_lens_offset(cam);
|
||||
screw = pcb_screw(pcb);
|
||||
|
||||
shelf = front.z - back.z;
|
||||
|
||||
connector_slot = connector_size + 2 * [clearance, 0, layer_height];
|
||||
rad = wall;
|
||||
led_hole_r = 1;
|
||||
led_clearance = [5, 2, 1 * 2];
|
||||
res_clearance = [3.5, 2, 1 * 2];
|
||||
|
||||
conn_pos = camera_connector_pos(cam);
|
||||
conn = camera_connector_size(cam);
|
||||
sensor_length = conn_pos.y + conn.y / 2 - lens_offset.y + clearance;
|
||||
|
||||
module hinge_pos()
|
||||
if(!is_undef(hinge))
|
||||
rotate(hinge * 90)
|
||||
translate([0, (hinge ? front.x * hinge : front.y) / 2 + hinge_offset, hinge_r])
|
||||
children();
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
hull()
|
||||
for(x = [-1, 1], y = [-1, 1])
|
||||
translate([x * (front.x / 2 - rad), y * (front.y / 2 - rad)])
|
||||
hull() { // 3D truncated teardrop gives radiused edges without exceeding 45 degree overhang
|
||||
translate_z(front.z - 1)
|
||||
cylinder(r = rad, h = 1);
|
||||
|
||||
translate_z(rad)
|
||||
sphere(rad);
|
||||
|
||||
cylinder(r = rad * (sqrt(2) - 1), h = eps);
|
||||
}
|
||||
|
||||
hinge_pos()
|
||||
hull() {
|
||||
rotate([-90, 0, -90])
|
||||
teardrop(r = hinge_r, h = hinge_h, center = false);
|
||||
|
||||
translate([0, -10, -hinge_r])
|
||||
cube([hinge_h, eps, 2 * hinge_r]);
|
||||
}
|
||||
}
|
||||
|
||||
hinge_pos()
|
||||
rotate([90, 0, 90])
|
||||
teardrop_plus(r = screw_clearance_radius(hinge_screw), h = 100, center = true);
|
||||
|
||||
translate_z(front.z / 2 + shelf - layer_height) // recess for the back
|
||||
cube([back.x + 2 * clearance, back.y + 2 * clearance, front.z], center = true);
|
||||
|
||||
translate_z(front.z / 2 + shelf - pcb_size.z) // recess for PCB
|
||||
cube([pcb_size.x + 2 * clearance, pcb_size.y + 2 * clearance, front.z], center = true);
|
||||
|
||||
translate_z(shelf)
|
||||
hflip() {
|
||||
pcb_component_position(pcb, "smd_led") // clearance for LED
|
||||
cube(led_clearance, center = true);
|
||||
|
||||
pcb_component_position(pcb, "smd_res") // clearance for resistor
|
||||
cube(res_clearance, center = true);
|
||||
}
|
||||
|
||||
translate([conn_pos.x, lens_offset.y + sensor_length / 2, shelf - pcb_size.z]) // clearance for sensor connector
|
||||
cube([conn.x + 2 * clearance, sensor_length, 2 * cam_front_clearance(cam)], center = true);
|
||||
|
||||
translate([0, -front.y / 2, shelf + front.z / 2]) // slot for connector
|
||||
cube([connector_slot.x, connector_slot.y, front.z], center = true);
|
||||
|
||||
translate_z(cam_back_clearance + layer_height)
|
||||
cam_holes(cam)
|
||||
rotate(90)
|
||||
poly_cylinder(r = screw_clearance_radius(screw), h = 100, center = true);
|
||||
|
||||
translate_z(shelf - pcb_size.z)
|
||||
hflip()
|
||||
camera_lens(cam, clearance);
|
||||
|
||||
hflip()
|
||||
pcb_component_position(pcb, "smd_led")
|
||||
rotate(45)
|
||||
poly_cylinder(r = led_hole_r, h = 100, center = true); // hole for led
|
||||
}
|
||||
}
|
||||
|
||||
function bracket_thickness(cam) = max(wall, min(3.5, hinge_z(cam) - hinge_r - 1));
|
||||
|
||||
module camera_bracket_screw_positions(cam) { //! Position children at the bracket screw positions
|
||||
r = washer_radius(screw_washer(bracket_screw)) + 0.5;
|
||||
wide = bracket_thickness(cam) == wall;
|
||||
pitch = wide ? cam_front_size(cam).x / 2 - r : hinge_h + 1 + r;
|
||||
|
||||
for(side = [-1, 1])
|
||||
translate([side * pitch, 0])
|
||||
children();
|
||||
}
|
||||
|
||||
module camera_bracket_position(cam) //! Position children at the bracket position
|
||||
translate([0, cam_front_size(cam).y / 2 + hinge_offset])
|
||||
children();
|
||||
|
||||
module camera_bracket(cam) { //! Make the STL for the camera bracket
|
||||
stl(str("camera_bracket_", cam[0]));
|
||||
|
||||
t = bracket_thickness(cam);
|
||||
z = hinge_z(cam);
|
||||
translate([hinge_h / 2, 0])
|
||||
difference() {
|
||||
hull() {
|
||||
translate_z(eps / 2)
|
||||
cube([hinge_h, 2 * hinge_r, eps], center = true);
|
||||
|
||||
translate_z(z)
|
||||
rotate([0, 90, 0])
|
||||
cylinder(r = hinge_r, h = hinge_h, center = true);
|
||||
}
|
||||
translate([hinge_h / 2, 0, z])
|
||||
rotate([90, 0, 90])
|
||||
nut_trap(hinge_screw, screw_nut(hinge_screw), horizontal = true);
|
||||
}
|
||||
|
||||
linear_extrude(t)
|
||||
difference() {
|
||||
hull()
|
||||
camera_bracket_screw_positions(cam)
|
||||
circle(washer_radius(screw_washer(bracket_screw)) + 0.5);
|
||||
|
||||
camera_bracket_screw_positions(cam)
|
||||
poly_circle(screw_clearance_radius(bracket_screw));
|
||||
}
|
||||
}
|
||||
|
||||
module camera_assembly(cam, angle = 0) //! Camera case assembly
|
||||
assembly(str("camera_", cam[0])) {
|
||||
front = cam_front_size(cam);
|
||||
screw = pcb_screw(camera_pcb(cam));
|
||||
nut = screw_nut(screw);
|
||||
screw_length = cam_screw_length(cam);
|
||||
hinge_z = hinge_z(cam);
|
||||
hinge_pos = [0, front.y / 2 + hinge_offset, -hinge_r];
|
||||
|
||||
camera_bracket_position(cam) {
|
||||
nut = screw_nut(hinge_screw);
|
||||
|
||||
stl_colour(pp1_colour) render()
|
||||
camera_bracket(cam);
|
||||
|
||||
translate([-hinge_h, 0, hinge_z(cam)])
|
||||
rotate([-90, 0, 90]) {
|
||||
vflip()
|
||||
translate_z(2 * hinge_h - nut_trap_depth(nut))
|
||||
nut(nut, true);
|
||||
|
||||
screw_and_washer(hinge_screw, screw_longer_than(2 * hinge_h));
|
||||
}
|
||||
}
|
||||
|
||||
translate_z(hinge_z(cam) + hinge_r)
|
||||
translate(hinge_pos)
|
||||
rotate([-angle, 0, 0])
|
||||
translate(-hinge_pos) {
|
||||
translate_z(cam_back_size(cam).z - front.z)
|
||||
camera(cam);
|
||||
|
||||
stl_colour(pp1_colour) render()
|
||||
translate_z(-front.z)
|
||||
camera_back(cam);
|
||||
|
||||
cam_holes(cam) {
|
||||
screw_and_washer(screw, screw_length);
|
||||
|
||||
translate_z(-front.z + nut_trap_depth(nut))
|
||||
vflip()
|
||||
nut(nut, true);
|
||||
}
|
||||
|
||||
*translate(camera_lens_offset(cam))
|
||||
translate_z(1.5)
|
||||
stl_colour(pp1_colour) render()
|
||||
rpi_camera_focus_ring_stl();
|
||||
|
||||
stl_colour(pp2_colour) render()
|
||||
hflip()
|
||||
camera_front(cam, 0);
|
||||
}
|
||||
}
|
||||
|
||||
module camera_fastened_assembly(cam, thickness, angle = 0) {
|
||||
camera_assembly(cam, angle);
|
||||
|
||||
camera_bracket_position(cam)
|
||||
camera_bracket_screw_positions(cam) {
|
||||
nut = screw_nut(bracket_screw);
|
||||
washer = screw_washer(bracket_screw);
|
||||
t = bracket_thickness(cam);
|
||||
screw_length = screw_longer_than(thickness + t + nut_thickness(nut, true) + 2 * washer_thickness(washer));
|
||||
vflip()
|
||||
translate_z(thickness)
|
||||
screw_and_washer(bracket_screw, screw_length);
|
||||
|
||||
translate_z(t)
|
||||
nut_and_washer(nut, true);
|
||||
}
|
||||
}
|
||||
|
||||
module camera_back_rpi_camera_stl() camera_back(rpi_camera);
|
||||
module camera_back_rpi_camera_v1_stl() camera_back(rpi_camera_v1);
|
||||
module camera_back_rpi_camera_v2_stl() camera_back(rpi_camera_v2);
|
||||
|
||||
module camera_front_rpi_camera_stl() camera_front(rpi_camera);
|
||||
module camera_front_rpi_camera_v1_stl() camera_front(rpi_camera_v1);
|
||||
module camera_front_rpi_camera_v2_stl() camera_front(rpi_camera_v2);
|
||||
|
||||
module camera_bracket_rpi_camera_stl() camera_bracket(rpi_camera);
|
||||
module camera_bracket_rpi_camera_v1_stl() camera_bracket(rpi_camera_v1);
|
||||
module camera_bracket_rpi_camera_v2_stl() camera_bracket(rpi_camera_v2);
|
||||
|
||||
module camera_rpi_camera_assembly() camera_assembly(rpi_camera);
|
||||
module camera_rpi_camera_v1_assembly() camera_assembly(rpi_camera_v1);
|
||||
module camera_rpi_camera_v2_assembly() camera_assembly(rpi_camera_v2);
|
||||
|
||||
module camera_housing(cam) {
|
||||
front = cam_front_size(cam);
|
||||
|
||||
camera_front(cam, 0);
|
||||
|
||||
translate([front.x, 0])
|
||||
camera_back(cam);
|
||||
|
||||
translate([-front.x / 2 - 2 - hinge_r, 0])
|
||||
rotate(90)
|
||||
camera_bracket(cam);
|
||||
}
|
||||
|
||||
cam = rpi_camera_v2;
|
||||
if($preview)
|
||||
camera_fastened_assembly(cam, 3);
|
||||
else
|
||||
camera_housing(cam);
|
@@ -18,11 +18,13 @@
|
||||
//
|
||||
|
||||
//
|
||||
//! Adapts ESP12 module to 0.1" grid. See <https://hydraraptor.blogspot.com/2018/04/esp-12-module-breakout-adaptor.html>.
|
||||
//! Adapts ESP12 modules and various small PCBs to 0.1" grid. See <https://hydraraptor.blogspot.com/2018/04/esp-12-module-breakout-adaptor.html>.
|
||||
//
|
||||
$extrusion_width = 0.5;
|
||||
|
||||
include <../core.scad>
|
||||
include <../utils/core/core.scad>
|
||||
|
||||
function carrier_height() = 3; //! Height of PCB carrier
|
||||
|
||||
module ESP12F_carrier_stl() { //! Generate the STL for an ESP12 carrier
|
||||
stl("ESP12F_carrier");
|
||||
@@ -33,7 +35,7 @@ module ESP12F_carrier_stl() { //! Generate the STL for an ESP12 carrier
|
||||
hole2 = pitch2 - 3 * extrusion_width;
|
||||
length1 = (pins - 1) * pitch1 + hole + squeezed_wall * 2;
|
||||
length2 = (pins - 1) * pitch2 + hole + squeezed_wall * 2;
|
||||
height = 3;
|
||||
height = carrier_height();
|
||||
|
||||
wpitch1 = (pins - 1) * pitch1;
|
||||
wpitch2 = ceil(wpitch1 / 2.54) * 2.54;
|
||||
@@ -61,3 +63,71 @@ module ESP12F_carrier_stl() { //! Generate the STL for an ESP12 carrier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module TP4056_carrier_stl() { //! Generate the STL for an TP4056 carrier, two required
|
||||
stl("TP4056_carrier");
|
||||
pitch = 2.54;
|
||||
outer_pitch = 13.9;
|
||||
inner_pitch = 7.54;
|
||||
hole = pitch - 3 * extrusion_width;
|
||||
pins = 6;
|
||||
length1 = outer_pitch + hole + squeezed_wall * 2;
|
||||
length2 = (pins - 1) * pitch + hole + squeezed_wall * 2;
|
||||
height = carrier_height();
|
||||
|
||||
width = hole + squeezed_wall * 2;
|
||||
spacing = inch(0.9);
|
||||
|
||||
difference() {
|
||||
hull() {
|
||||
translate_z(height - eps / 2)
|
||||
cube([width, length1, eps], center = true);
|
||||
|
||||
translate_z(eps / 2)
|
||||
cube([width, length2, eps], center = true);
|
||||
}
|
||||
|
||||
for(i = [0 : pins - 1])
|
||||
let(x = [-outer_pitch / 2, - inner_pitch / 2, 0, 0, inner_pitch / 2, outer_pitch / 2][i])
|
||||
if(x)
|
||||
hull() {
|
||||
translate([0, x, height])
|
||||
cube([hole, hole, eps], center = true);
|
||||
|
||||
translate([0, i * pitch - (pins - 1) * pitch / 2])
|
||||
cube([hole, hole, eps], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module MT3608_carrier_stl() { //! Generate the STL for an MT3608 carrier, two required
|
||||
stl("MT3608_carrier");
|
||||
pcb_width = 17;
|
||||
w_pitch_top = 6.81;
|
||||
w_pitch_bot = inch(0.3);
|
||||
l_pitch_top = 30.855;
|
||||
l_pitch_bot = inch(1.2);
|
||||
hole = 1;
|
||||
height = carrier_height();
|
||||
wall = 2 * extrusion_width;
|
||||
width = hole + 2 * wall;
|
||||
offset = (l_pitch_top - l_pitch_bot) / 2;
|
||||
|
||||
difference() {
|
||||
hull() {
|
||||
translate([offset, 0, height - eps / 2])
|
||||
rounded_rectangle([width, pcb_width - 2, eps], 1);
|
||||
|
||||
translate_z(eps / 2)
|
||||
rounded_rectangle([width, pcb_width - 2, eps], 1);
|
||||
}
|
||||
for(side = [-1, 1])
|
||||
hull() {
|
||||
translate([offset, side * w_pitch_top / 2, height])
|
||||
cube([hole, hole, eps], center = true);
|
||||
|
||||
translate([0, side * w_pitch_bot / 2])
|
||||
cube([hole, hole, eps], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,10 +24,14 @@
|
||||
//! See [butt_box](#Butt_box) for an example of usage.
|
||||
//!
|
||||
//! Note that the block with its inserts is defined as a sub assembly, but its fasteners get added to the parent assembly.
|
||||
//!
|
||||
//! Specific fasteners can be omitted by setting a side's thickness to 0 and the block omitted by setting `show_block` to false.
|
||||
//! This allows the block and one set of fasteners to be on one assembly and the other fasteners on the mating assemblies.
|
||||
//!
|
||||
//! Star washers can be omitted by setting `star_washers` to false.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/inserts.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
use <../utils/rounded_cylinder.scad>
|
||||
use <../utils/maths.scad>
|
||||
|
||||
@@ -57,8 +61,8 @@ module corner_block_v_hole(screw = def_screw) //! Place children at the bottom s
|
||||
multmatrix(corner_block_v_hole(screw))
|
||||
children();
|
||||
|
||||
module corner_block_h_holes(screw = def_screw) //! Place children at the side screw holes
|
||||
for(p = corner_block_h_holes(screw))
|
||||
module corner_block_h_holes(screw = def_screw, index = undef) //! Place children at the side screw holes
|
||||
for(p = !is_undef(index) ? [corner_block_h_holes(screw)[index]] : corner_block_h_holes(screw))
|
||||
multmatrix(p)
|
||||
children();
|
||||
|
||||
@@ -116,7 +120,7 @@ module corner_block_assembly(screw = def_screw, name = false) //! The printed bl
|
||||
assembly(str("corner_block_M", 20 * screw_radius(screw))) {
|
||||
insert = screw_insert(screw);
|
||||
|
||||
color(name ? pp2_colour : pp1_colour)
|
||||
stl_colour(name ? pp2_colour : pp1_colour)
|
||||
render() corner_block(screw, name) children();
|
||||
|
||||
corner_block_h_holes(screw)
|
||||
@@ -126,22 +130,33 @@ assembly(str("corner_block_M", 20 * screw_radius(screw))) {
|
||||
insert(insert);
|
||||
}
|
||||
|
||||
module fastened_corner_block_assembly(thickness, screw = def_screw, thickness_below = undef, name = false) { //! Printed block with all fasteners
|
||||
module fastened_corner_block_assembly(thickness, screw = def_screw, thickness_below = undef, thickness_side2 = undef, name = false, show_block = true, star_washers = true) { //! Printed block with all fasteners
|
||||
thickness2 = !is_undef(thickness_below) ? thickness_below : thickness;
|
||||
thickness3 = !is_undef(thickness_side2) ? thickness_side2 : thickness;
|
||||
washer = screw_washer(screw);
|
||||
insert = screw_insert(screw);
|
||||
screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + insert_length(insert) + overshoot);
|
||||
function screw_length(t) = screw_shorter_than((star_washers ? 2 : 1) * washer_thickness(washer) + t + insert_length(insert) + overshoot);
|
||||
screw_length = screw_length(thickness);
|
||||
screw_length2 = screw_length(thickness2);
|
||||
screw_length3 = screw_length(thickness3);
|
||||
|
||||
corner_block_assembly(screw, name) children();
|
||||
if(show_block)
|
||||
corner_block_assembly(screw, name) children();
|
||||
|
||||
corner_block_h_holes(screw)
|
||||
translate_z(thickness)
|
||||
screw_and_washer(screw, screw_length, true);
|
||||
if(thickness)
|
||||
corner_block_h_holes(screw, 0)
|
||||
translate_z(thickness)
|
||||
screw_and_washer(screw, screw_length, star_washers);
|
||||
|
||||
thickness2 = thickness_below ? thickness_below : thickness;
|
||||
screw_length2 = screw_shorter_than(2 * washer_thickness(washer) + thickness2 + insert_length(insert) + overshoot);
|
||||
corner_block_v_hole(screw)
|
||||
translate_z(thickness2)
|
||||
screw_and_washer(screw, screw_length2, true);
|
||||
if(thickness3)
|
||||
corner_block_h_holes(screw, 1)
|
||||
translate_z(thickness3)
|
||||
screw_and_washer(screw, screw_length3, star_washers);
|
||||
|
||||
if(thickness2)
|
||||
corner_block_v_hole(screw)
|
||||
translate_z(thickness2)
|
||||
screw_and_washer(screw, screw_length2, star_washers);
|
||||
}
|
||||
|
||||
module corner_block_M20_stl() corner_block(M2_cap_screw);
|
||||
|
@@ -24,7 +24,6 @@
|
||||
//! Rubber door [sealing strip](#sealing_strip) is used to make it airtight and a [door_latch](#door_latch) holds it closed.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
|
||||
width = 18;
|
||||
thickness = 4;
|
||||
@@ -61,7 +60,7 @@ module door_hinge(door_thickness) { //! Generates STL fo
|
||||
|
||||
union() {
|
||||
rotate([90, 0, 0])
|
||||
linear_extrude(height = width, center = true)
|
||||
linear_extrude(width, center = true)
|
||||
difference() {
|
||||
hull() {
|
||||
translate([dia / 2, thickness + door_thickness / 2])
|
||||
@@ -75,9 +74,9 @@ module door_hinge(door_thickness) { //! Generates STL fo
|
||||
square([1, thickness + door_thickness]);
|
||||
}
|
||||
translate([dia / 2, thickness + door_thickness / 2])
|
||||
teardrop(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
teardrop_plus(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
}
|
||||
linear_extrude(height = thickness)
|
||||
linear_extrude(thickness)
|
||||
difference() {
|
||||
hull() {
|
||||
translate([0, -width / 2])
|
||||
@@ -109,7 +108,7 @@ module door_hinge_stat_stl() { //! Generates the STL for the stationary part
|
||||
stl("door_hinge_stat");
|
||||
|
||||
union() {
|
||||
linear_extrude(height = thickness)
|
||||
linear_extrude(thickness)
|
||||
difference() {
|
||||
rounded_square([stat_length, stat_width], rad);
|
||||
|
||||
@@ -118,7 +117,7 @@ module door_hinge_stat_stl() { //! Generates the STL for the stationary part
|
||||
}
|
||||
|
||||
rotate([90, 0, 0])
|
||||
linear_extrude(height = stat_width, center = true)
|
||||
linear_extrude(stat_width, center = true)
|
||||
difference() {
|
||||
hull() {
|
||||
translate([0, dia / 2 + stat_clearance])
|
||||
@@ -128,7 +127,7 @@ module door_hinge_stat_stl() { //! Generates the STL for the stationary part
|
||||
square([dia, 1], center = true);
|
||||
}
|
||||
translate([0, dia / 2 + stat_clearance])
|
||||
teardrop(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
teardrop_plus(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,7 +141,7 @@ module door_hinge_assembly(top, door_thickness = 6) { //! The moving assembly th
|
||||
|
||||
translate([0, pin_y - (thickness + door_thickness / 2), dir * width / 2]) {
|
||||
rotate([90, 0, 180])
|
||||
color("red") door_hinge(door_thickness);
|
||||
stl_colour(pp2_colour) door_hinge(door_thickness);
|
||||
|
||||
rotate([90, 0, 0])
|
||||
door_hinge_hole_positions()
|
||||
@@ -166,7 +165,7 @@ module door_hinge_static_assembly(top, sheet_thickness = 3) { //! The stationary
|
||||
|
||||
translate([pin_x, 0, -dir * (stat_width / 2 + washer_thickness(screw_washer(pin_screw)))])
|
||||
rotate([90, 0, 0]) {
|
||||
color("lime") door_hinge_stat_stl();
|
||||
stl_colour(pp1_colour) door_hinge_stat_stl();
|
||||
|
||||
door_hinge_stat_hole_positions() {
|
||||
screw_and_washer(stat_screw, stat_screw_length);
|
||||
|
@@ -22,7 +22,6 @@
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../utils/hanging_hole.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
|
||||
length = 35;
|
||||
width = 12;
|
||||
@@ -66,7 +65,7 @@ module door_latch_assembly(sheet_thickness = 3) { //! The assembly for a specifi
|
||||
|
||||
translate([0, -height - washer_thickness(washer)])
|
||||
rotate([-90, 0, 0]) {
|
||||
color("lime") render() door_latch_stl();
|
||||
stl_colour(pp1_colour) render() door_latch_stl();
|
||||
|
||||
translate_z(nut_trap_depth)
|
||||
vflip()
|
||||
|
321
printed/drag_chain.scad
Normal file
@@ -0,0 +1,321 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! Parametric cable drag chain to limit the bend radius of a cable run.
|
||||
//!
|
||||
//! Each link has a maximum bend angle of 45°, so the mininium radius is proportional to the link length.
|
||||
//!
|
||||
//! The travel property is how far it can move in each direction, i.e. half the maximum travel if the chain is mounted in the middle of the travel.
|
||||
//!
|
||||
//! The ends can have screw lugs with four screw positions to choose from, specified by a list of two arrays of four bools.
|
||||
//! If none are enabled then a child object is expected to customise the end and this gets unioned with the blank end.
|
||||
//! If both ends are customised then two children are expected.
|
||||
//! Each child is called twice, once with `$fasteners` set to 0 to augment the STL and again with `$fasteners` set to 1 to add
|
||||
//! to the assembly, for example to add inserts.
|
||||
//
|
||||
|
||||
include <../core.scad>
|
||||
use <../utils/horiholes.scad>
|
||||
use <../utils/maths.scad>
|
||||
|
||||
clearance = 0.1;
|
||||
|
||||
function drag_chain_name(type) = type[0]; //! The name to allow more than one in a project
|
||||
function drag_chain_size(type) = type[1]; //! The internal size and link length
|
||||
function drag_chain_travel(type) = type[2]; //! X travel
|
||||
function drag_chain_wall(type) = type[3]; //! Side wall thickness
|
||||
function drag_chain_bwall(type) = type[4]; //! Bottom wall
|
||||
function drag_chain_twall(type) = type[5]; //! Top wall
|
||||
function drag_chain_screw(type) = type[6]; //! Mounting screw for the ends
|
||||
function drag_chain_screw_lists(type) = type[7]; //! Two lists of four bools to say which screws positions are used
|
||||
|
||||
function drag_chain_clearance() = clearance; //! Clearance around joints.
|
||||
|
||||
function drag_chain_radius(type) = //! The bend radius at the pivot centres
|
||||
let(s = drag_chain_size(type))
|
||||
s.x / 2 / sin(360 / 16);
|
||||
|
||||
function drag_chain_z(type) = //! Outside dimension of a 180 bend
|
||||
let(os = drag_chain_outer_size(type), s = drag_chain_size(type))
|
||||
2 * drag_chain_radius(type) + os.z;
|
||||
|
||||
function drag_chain(name, size, travel, wall = 1.6, bwall = 1.5, twall = 1.5, screw = M2_cap_screw, screw_lists = [[1,0,0,1],[1,0,0,1]]) = //! Constructor
|
||||
[name, size, travel, wall, bwall, twall, screw, screw_lists];
|
||||
|
||||
function drag_chain_outer_size(type) = //! Link outer dimensions
|
||||
let(s = drag_chain_size(type), z = s.z + drag_chain_bwall(type) + drag_chain_twall(type))
|
||||
[s.x + z, s.y + 4 * drag_chain_wall(type) + 2 * clearance, z];
|
||||
|
||||
function screw_lug_radius(screw) = //! Radius of a screw lug
|
||||
corrected_radius(screw_clearance_radius(screw)) + 3.1 * extrusion_width;
|
||||
|
||||
module screw_lug(screw, h = 0) //! Create a D shaped lug for a screw
|
||||
extrude_if(h, center = false)
|
||||
difference() {
|
||||
r = screw_lug_radius(screw);
|
||||
hull() {
|
||||
circle4n(r);
|
||||
|
||||
translate([-r, -r])
|
||||
square([2 * r, eps]);
|
||||
}
|
||||
poly_circle(screw_clearance_radius(screw));
|
||||
}
|
||||
|
||||
function bool2int(b) = b ? 1 : 0;
|
||||
|
||||
module drag_chain_screw_positions(type, end) { //! Place children at the screw positions, end = 0 for the start, 1 for the end
|
||||
r = screw_lug_radius(drag_chain_screw(type));
|
||||
s = drag_chain_size(type);
|
||||
os = drag_chain_outer_size(type);
|
||||
R = os.z / 2;
|
||||
x0 = end ? R + norm([drag_chain_cam_x(type), R - drag_chain_twall(type)]) + clearance + r : r;
|
||||
x1 = end ? os.x - r : os.x - 2 * R - clearance - r;
|
||||
for(i = [0 : 3], x = [x0, x1, x0, x1][i], y = [-1, -1, 1, 1][i])
|
||||
if(drag_chain_screw_lists(type)[bool2int(end)][i])
|
||||
translate([x, y * (s.y / 2 + r)])
|
||||
let($a = [180, 0, 180, 0][i])
|
||||
children();
|
||||
}
|
||||
|
||||
function drag_chain_cam_x(type) = // how far the cam sticks out
|
||||
let(s = drag_chain_size(type),
|
||||
r = drag_chain_outer_size(type).z / 2,
|
||||
wall = drag_chain_wall(type),
|
||||
cam_r = s.x - 2 * clearance - wall - r, // inner_x_normal - clearance - r
|
||||
twall = drag_chain_twall(type)
|
||||
) min(sqrt(max(sqr(cam_r) - sqr(r - twall), 0)), r);
|
||||
|
||||
module drag_chain_link(type, start = false, end = false, check_kids = true) { //! One link of the chain, special case for start and end
|
||||
stl(str(drag_chain_name(type), "_drag_chain_link", start ? "_start" : end ? "_end" : ""));
|
||||
|
||||
s = drag_chain_size(type);
|
||||
wall = drag_chain_wall(type);
|
||||
bwall = drag_chain_bwall(type);
|
||||
twall = drag_chain_twall(type);
|
||||
os = drag_chain_outer_size(type);
|
||||
r = os.z / 2;
|
||||
pin_r = r / 2;
|
||||
|
||||
socket_x = r;
|
||||
pin_x = socket_x + s.x;
|
||||
|
||||
outer_normal_x = pin_x - r - clearance; // s.x - clearance
|
||||
outer_end_x = end ? os.x : outer_normal_x;
|
||||
|
||||
inner_x = start ? 0 : outer_normal_x - wall; // s.x - clearance - wall
|
||||
|
||||
roof_x_normal = 2 * r - twall;
|
||||
roof_x = start ? 0 : roof_x_normal;
|
||||
|
||||
floor_x = start ? 0 : 2 * r;
|
||||
cam_x = drag_chain_cam_x(type);
|
||||
assert(r + norm([drag_chain_cam_x(type), r - drag_chain_twall(type)]) + clearance <= inner_x || start, "Link must be longer");
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
for(side = [-1, 1])
|
||||
rotate([90, 0, 0]) {
|
||||
// Outer cheeks
|
||||
translate_z(side * (os.y / 2 - wall / 2))
|
||||
linear_extrude(wall, center = true)
|
||||
difference() {
|
||||
hull() {
|
||||
if(start)
|
||||
square([eps, os.z]);
|
||||
else
|
||||
translate([socket_x, r])
|
||||
rotate(180)
|
||||
teardrop(r = r, h = 0);
|
||||
|
||||
translate([outer_end_x - eps, 0])
|
||||
square([eps, os.z]);
|
||||
}
|
||||
if(!start)
|
||||
translate([socket_x, r])
|
||||
horihole(pin_r, r);
|
||||
}
|
||||
// Inner cheeks
|
||||
translate_z(side * (s.y / 2 + wall / 2))
|
||||
linear_extrude(wall, center = true)
|
||||
difference() {
|
||||
union() {
|
||||
hull() {
|
||||
if(!end) {
|
||||
translate([pin_x, r])
|
||||
rotate(180)
|
||||
teardrop(r = r, h = 0);
|
||||
|
||||
translate([pin_x, twall])
|
||||
square([cam_x, eps]);
|
||||
}
|
||||
else
|
||||
translate([os.x - eps, 0])
|
||||
square([eps, os.z]);
|
||||
|
||||
translate([inner_x, 0])
|
||||
square([eps, os.z]);
|
||||
}
|
||||
}
|
||||
// Cutout for top wall
|
||||
if(!end)
|
||||
intersection() {
|
||||
translate([pin_x - r, 0])
|
||||
square([3 * r, twall]); // When straight
|
||||
|
||||
translate([pin_x, r])
|
||||
rotate(-45)
|
||||
translate([-r + roof_x_normal, -r - twall]) // When bent fully
|
||||
square(os.z);
|
||||
}
|
||||
}
|
||||
// Pin
|
||||
if(!end)
|
||||
translate([pin_x, r, side * (s.y / 2 + wall + clearance)])
|
||||
horicylinder(r = pin_r, z = r, h = 2 * wall);
|
||||
|
||||
// Cheek joint
|
||||
translate([inner_x, 0, side * (s.y / 2 + wall) - 0.5])
|
||||
cube([outer_end_x - inner_x, os.z, 1]);
|
||||
}
|
||||
|
||||
// Roof, actually the floor when printed
|
||||
roof_end = end ? s.x + 2 * r : s.x + r - twall - clearance;
|
||||
translate([roof_x, -s.y / 2 - 0.5])
|
||||
cube([roof_end - roof_x , s.y + 1, twall]);
|
||||
|
||||
translate([roof_x, -os.y / 2 + 0.5])
|
||||
cube([s.x - clearance - roof_x, os.y - 1, twall]);
|
||||
|
||||
// Floor, actually the roof when printed
|
||||
floor_end = end ? s.x + 2 * r : s.x + r;
|
||||
translate([floor_x, -s.y / 2 - wall, os.z - bwall])
|
||||
cube([floor_end - floor_x, s.y + 2 * wall, bwall]);
|
||||
|
||||
translate([floor_x, -os.y / 2 + 0.5, os.z - bwall])
|
||||
cube([s.x - floor_x - clearance, os.y -1, bwall]);
|
||||
|
||||
if(start || end) {
|
||||
drag_chain_screw_positions(type, end)
|
||||
rotate($a)
|
||||
screw_lug(drag_chain_screw(type), os.z);
|
||||
|
||||
if(check_kids) {
|
||||
custom = drag_chain_screw_lists(type)[bool2int(end)] == [0, 0, 0, 0];
|
||||
assert($children == bool2int(custom), str("wrong number of children for ", end ? "end" : "start", " STL customisation: ", $children));
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
if(start || end)
|
||||
translate_z(-eps)
|
||||
drag_chain_screw_positions(type, end)
|
||||
rotate($a)
|
||||
poly_cylinder(r = screw_clearance_radius(drag_chain_screw(type)), h = os.z + 2 * eps, center = false);
|
||||
|
||||
}
|
||||
|
||||
if(show_supports() && !end) {
|
||||
for(side = [-1, 1]) {
|
||||
w = 2.1 * extrusion_width;
|
||||
translate([s.x + r + cam_x - w / 2, side * (s.y / 2 + wall / 2), twall / 2])
|
||||
cube([w, wall, twall], center = true);
|
||||
|
||||
h = round_to_layer(r - pin_r / sqrt(2));
|
||||
y = s.y / 2 + max(wall + w / 2 + clearance, 2 * wall + clearance - w / 2);
|
||||
translate([s.x + r, side * y, h / 2])
|
||||
cube([pin_r * sqrt(2), w, h], center = true);
|
||||
|
||||
gap = cam_x - pin_r / sqrt(2) + extrusion_width;
|
||||
translate([s.x + r + cam_x - gap / 2, side * (s.y / 2 + wall + clearance / 2), layer_height / 2])
|
||||
cube([gap, 2 * wall + clearance, layer_height], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to use a wrapper because can't define nested modules in an assembly
|
||||
module _drag_chain_assembly(type, pos = 0) {
|
||||
s = drag_chain_size(type);
|
||||
x = (1 + exploded()) * s.x;
|
||||
r = drag_chain_radius(type) * x / s.x;
|
||||
travel = drag_chain_travel(type);
|
||||
links = ceil(travel / s.x);
|
||||
actual_travel = links * s.x;
|
||||
z = drag_chain_outer_size(type).z;
|
||||
|
||||
zb = z / 2; // z of bottom track
|
||||
c = [actual_travel / 2 + pos / 2, 0, r + zb]; // centre of bend
|
||||
|
||||
points = [ // Calculate list of hinge points
|
||||
for(i = 0, p = [0, 0, z / 2 + 2 * r]; i < links + 5;
|
||||
i = i + 1,
|
||||
dx = p.z > c.z ? x : -x,
|
||||
p = max(p.x + dx, p.x) <= c.x ? p + [dx, 0, 0] // Straight sections
|
||||
: let(q = circle_intersect(p, x, c, r))
|
||||
q.x <= c.x ? [p.x - sqrt(sqr(x) - sqr(p.z - zb)), 0, zb] // Transition back to straight
|
||||
: q) // Circular section
|
||||
p
|
||||
];
|
||||
npoints = len(points);
|
||||
|
||||
module link(n) // Position and colour link with origin at the hinge hole
|
||||
translate([-z / 2, 0, -z / 2]) {
|
||||
stl_colour(n < 0 || n == npoints - 1 ? pp3_colour : n % 2 ? pp1_colour : pp2_colour)
|
||||
drag_chain_link(type, start = n == -1, end = n == npoints - 1, check_kids = false)
|
||||
let($fasteners = 0) children();
|
||||
let($fasteners = 1) children();
|
||||
}
|
||||
|
||||
screws = drag_chain_screw_lists(type);
|
||||
custom_start = screws[0] == [0, 0, 0, 0];
|
||||
custom_end = screws[1] == [0, 0, 0, 0];
|
||||
assert($children == bool2int(custom_start) + bool2int(custom_end), str("wrong number of children for end customisation: ", $children));
|
||||
|
||||
for(i = [0 : npoints - 2]) let(v = points[i + 1] - points[i])
|
||||
translate(points[i])
|
||||
rotate([0, -atan2(v.z, v.x), 0])
|
||||
link(i);
|
||||
|
||||
translate(points[0] - [x, 0, 0])
|
||||
link(-1)
|
||||
if(custom_start)
|
||||
children(0);
|
||||
|
||||
translate(points[npoints - 1])
|
||||
hflip()
|
||||
link(npoints - 1)
|
||||
if(custom_end)
|
||||
children(custom_start ? 1 : 0);
|
||||
}
|
||||
|
||||
//! 1. Remove the support material from the links with side cutters.
|
||||
//! 1. Clip the links together with the special ones at the ends.
|
||||
module drag_chain_assembly(type, pos = 0) //! Drag chain assembly
|
||||
assembly(str(drag_chain_name(type), "_drag_chain"), big = true)
|
||||
if($children == 2)
|
||||
_drag_chain_assembly(type, pos) {
|
||||
children(0);
|
||||
children(1);
|
||||
}
|
||||
else if($children == 1)
|
||||
_drag_chain_assembly(type, pos)
|
||||
children(0);
|
||||
else
|
||||
_drag_chain_assembly(type, pos);
|
@@ -17,7 +17,7 @@
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
//! Pintable fan finger guard to match the specified fan. To be ```include```d, not ```use```d.
|
||||
//! Pintable fan finger guard to match the specified fan. To be `include`d, not `use`d.
|
||||
//!
|
||||
//! The ring spacing as well as the number of spokes can be specified, if zero a gasket is generated instead of a guard.
|
||||
//
|
||||
|
@@ -24,10 +24,14 @@
|
||||
//! See [butt_box](#Butt_box) for an example of usage.
|
||||
//!
|
||||
//! Note that the block with its inserts is defined as a sub assembly, but its fasteners get added to the parent assembly.
|
||||
//!
|
||||
//! Specific fasteners can be omitted by setting a side's thickness to 0 and the block omitted by setting `show_block` to false.
|
||||
//! This allows the block and one set of fasteners to be on one assembly and the other fasteners on the mating assemblies.
|
||||
//!
|
||||
//! Star washers can be omitted by setting `star_washers` to false.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/inserts.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
use <../utils/maths.scad>
|
||||
|
||||
def_screw = M3_cap_screw;
|
||||
@@ -77,7 +81,7 @@ module fixing_block(screw = def_screw) { //! Generate the STL
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(height = fb_height, convexity = 5)
|
||||
linear_extrude(fb_height, convexity = 5)
|
||||
difference() {
|
||||
hull() {
|
||||
for(side = [-1, 1]) {
|
||||
@@ -105,7 +109,7 @@ module fixing_block_assembly(screw = def_screw) pose([55, 180, 25], [0, 4.8, 4.8
|
||||
assembly(str("fixing_block_M", 20 * screw_radius(screw))) {
|
||||
translate_z(fixing_block_height(screw))
|
||||
rotate([0, 180, 0])
|
||||
color(pp1_colour) render() fixing_block(screw);
|
||||
stl_colour(pp1_colour) render() fixing_block(screw);
|
||||
|
||||
insert = screw_insert(screw);
|
||||
|
||||
@@ -116,20 +120,24 @@ assembly(str("fixing_block_M", 20 * screw_radius(screw))) {
|
||||
insert(insert);
|
||||
}
|
||||
|
||||
module fastened_fixing_block_assembly(thickness, screw = def_screw, screw2 = undef, thickness2 = undef) { //! Assembly with fasteners in place
|
||||
module fastened_fixing_block_assembly(thickness, screw = def_screw, screw2 = undef, thickness2 = undef, show_block = true, star_washers = true) { //! Assembly with fasteners in place
|
||||
module fb_screw(screw, thickness) {
|
||||
washer = screw_washer(screw);
|
||||
insert = screw_insert(screw);
|
||||
screw_length = screw_longer_than(2 * washer_thickness(washer) + thickness + insert_length(insert));
|
||||
screw_length = screw_longer_than((star_washers ? 2 : 1) * washer_thickness(washer) + thickness + insert_length(insert));
|
||||
|
||||
translate_z(thickness)
|
||||
screw_and_washer(screw, screw_length, true);
|
||||
if(thickness)
|
||||
translate_z(thickness)
|
||||
screw_and_washer(screw, screw_length, star_washers);
|
||||
}
|
||||
|
||||
no_pose() fixing_block_assembly(screw);
|
||||
if(show_block)
|
||||
no_pose()
|
||||
fixing_block_assembly(screw);
|
||||
|
||||
t2 = !is_undef(thickness2) ? thickness2 : thickness;
|
||||
fixing_block_v_holes(screw)
|
||||
fb_screw(screw, thickness2 ? thickness2 : thickness);
|
||||
fb_screw(screw, t2);
|
||||
|
||||
fixing_block_h_hole(screw)
|
||||
fb_screw(screw2 ? screw2 : screw, thickness);
|
||||
|
182
printed/flat_hinge.scad
Normal file
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2018
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! A parametric flat hinge. A piece of filament can be used for the hinge pin.
|
||||
//!
|
||||
//! The width, depth, thickness, number and type of screws, number of knuckles, knuckle diameter, pin diameter and clearance can all be varied.
|
||||
//! A margin between the screws and the knuckle edge can be enforced to allow the hinge to bend all the way back to 270° from closed.
|
||||
//!
|
||||
//! Opening the test in OpenSCAD with its customiser enabled allows these parameters to be played with.
|
||||
//!
|
||||
//! Note setting `thickness1` or `thickness2` to zero in the `hinge_fastened_assembly()` removes the screws from one side or the other and
|
||||
//! setting `show_hinge` to false removes the hinge.
|
||||
//! This allows the hinges and one set of screws to belong to one assembly and the other set of screws to another assembly.
|
||||
//
|
||||
include <../core.scad>
|
||||
|
||||
function hinge_width(type) = type[1]; //! Width
|
||||
function hinge_depth(type) = type[2]; //! Depth of each leaf
|
||||
function hinge_thickness(type) = type[3]; //! Thickness of the leaves
|
||||
function hinge_pin_dia(type) = type[4]; //! The pin diameter
|
||||
function hinge_knuckle_dia(type) = type[5]; //! The knuckle diameter
|
||||
function hinge_knuckles(type) = type[6]; //! How many knuckles
|
||||
function hinge_screw(type) = type[7]; //! Screw type to mount it
|
||||
function hinge_screws(type) = type[8]; //! How many screws
|
||||
function hinge_clearance(type) = type[9]; //! Clearance between knuckles
|
||||
function hinge_margin(type) = type[10]; //! How far to keep the screws from the knuckes
|
||||
|
||||
function flat_hinge(name, size, pin_d, knuckle_d, knuckles, screw, screws, clearance, margin) = //! Construct the property list for a flat hinge.
|
||||
[name, size.x, size.y, size.z, pin_d, knuckle_d, knuckles, screw, screws, clearance, margin];
|
||||
|
||||
function hinge_radius(type) = washer_radius(screw_washer(hinge_screw(type))) + 1;
|
||||
|
||||
module hinge_screw_positions(type) { //! Place children at the screw positions
|
||||
screws = hinge_screws(type);
|
||||
w = hinge_width(type);
|
||||
d = hinge_depth(type);
|
||||
r = hinge_radius(type);
|
||||
m = hinge_margin(type);
|
||||
assert(screws > 1, "must be at least two screws");
|
||||
w_pitch = (w - 2 * r) / (screws - 1);
|
||||
d_pitch = d - 2 * r - m;
|
||||
wr = washer_radius(screw_washer(hinge_screw(type)));
|
||||
assert(w_pitch >= wr && norm([w_pitch, d_pitch]) >= 2 * wr && d_pitch >= 0, "not enough room for screws");
|
||||
|
||||
for(i = [0 : screws - 1])
|
||||
translate([-w / 2 + r + i * w_pitch, r + m + (i % 2) * d_pitch])
|
||||
children();
|
||||
}
|
||||
|
||||
module hinge_male(type, female = false) { //! The half with the stationary pin
|
||||
stl(str("hinge_", female ? "fe": "", "male_", type[0]));
|
||||
|
||||
r = hinge_radius(type);
|
||||
w = hinge_width(type);
|
||||
t = hinge_thickness(type);
|
||||
kr = hinge_knuckle_dia(type) / 2;
|
||||
pr = hinge_pin_dia(type) / 2;
|
||||
assert(kr > pr, "knuckle diameter must be bigger than the pin diameter");
|
||||
|
||||
n = hinge_knuckles(type);
|
||||
assert(n >= 3, "must be at least three knuckes");
|
||||
mn = ceil(n / 2); // Male knuckles
|
||||
fn = floor(n / 2); // Female knuckles
|
||||
gap = hinge_clearance(type);
|
||||
mw = (w - (n - 1) * gap) / 2 / mn; // Male knuckle width
|
||||
fw = (w - (n - 1) * gap) / 2 / fn; // Female knuckle width
|
||||
|
||||
teardrop_r = kr / cos(22.5); // The corner on the teardrop
|
||||
inset = sqrt(sqr(teardrop_r + gap) - sqr(kr - t)) - kr;
|
||||
|
||||
linear_extrude(t)
|
||||
difference() {
|
||||
hull() {
|
||||
for(side = [-1, 1])
|
||||
translate([side * (w / 2 - r), hinge_depth(type) - r])
|
||||
circle4n(r);
|
||||
|
||||
translate([-w / 2, inset])
|
||||
square([w, eps]);
|
||||
}
|
||||
hinge_screw_positions(type)
|
||||
poly_circle(screw_clearance_radius(hinge_screw(type)));
|
||||
}
|
||||
|
||||
pitch = mw + gap + fw + gap;
|
||||
dir = female ? -1 : 1;
|
||||
translate([0, -kr, kr])
|
||||
rotate([90, 0, -90])
|
||||
for(z = [0 : (female ? fn : mn) - 1])
|
||||
translate_z(-dir * w / 2 + z * dir * pitch + (female ? -fw - mw - gap : 0))
|
||||
linear_extrude(female ? fw : mw)
|
||||
difference() {
|
||||
hull() {
|
||||
rotate(180)
|
||||
teardrop(r = kr, h = 0);
|
||||
|
||||
translate([-kr - 1, -kr])
|
||||
square(1);
|
||||
}
|
||||
teardrop_plus(r = pr + (female ? gap : 0), h = 0);
|
||||
}
|
||||
}
|
||||
|
||||
module hinge_female(type) hinge_male(type, true);
|
||||
|
||||
module hinge_both(type) { //! Both parts together for printing
|
||||
hinge_male(type);
|
||||
|
||||
translate([0, -hinge_knuckle_dia(type)])
|
||||
rotate(180)
|
||||
hinge_female(type);
|
||||
}
|
||||
|
||||
module hinge_assembly(type, angle = 0)
|
||||
assembly(str("hinge_", type[0])) { //! Assembled hinge
|
||||
kr = hinge_knuckle_dia(type) / 2;
|
||||
hr = hinge_pin_dia(type) / 2;
|
||||
w = hinge_width(type);
|
||||
|
||||
vitamin(str(": Hinge pin ", w, " x ", 2 * hr, "mm"));
|
||||
|
||||
stl_colour(pp1_colour) hinge_male(type);
|
||||
|
||||
translate([0, -kr, kr]) {
|
||||
rotate([0, 90, 0])
|
||||
explode(w + 10)
|
||||
stl_colour("silver") cylinder(r = hr , h = w, center = true);
|
||||
|
||||
rotate([-angle, 0, 0])
|
||||
translate([0, -kr, -kr])
|
||||
rotate(180)
|
||||
stl_colour(pp2_colour) hinge_female(type);
|
||||
}
|
||||
}
|
||||
|
||||
module hinge_fastened_assembly(type, thickness1, thickness2, angle, show_hinge = true) { //! Assembled hinge with its fasteners
|
||||
if(show_hinge)
|
||||
hinge_assembly(type, angle);
|
||||
|
||||
screw = hinge_screw(type);
|
||||
washer_t = 2 * washer_thickness(screw_washer(screw));
|
||||
nut = screw_nut(screw);
|
||||
nut_t = nut_thickness(nut, true);
|
||||
t = hinge_thickness(type);
|
||||
kr = hinge_knuckle_dia(type) / 2;
|
||||
|
||||
module fasteners(thickness)
|
||||
if(thickness)
|
||||
hinge_screw_positions(type) {
|
||||
translate_z(t)
|
||||
screw_and_washer(screw, screw_longer_than(t + thickness + washer_t + nut_t));
|
||||
|
||||
translate_z(-thickness)
|
||||
vflip()
|
||||
nut_and_washer(nut, true);
|
||||
}
|
||||
|
||||
fasteners(thickness1);
|
||||
|
||||
translate([0, -kr, kr])
|
||||
rotate([-angle, 0, 0])
|
||||
translate([0, -kr, - kr])
|
||||
rotate(180)
|
||||
fasteners(thickness2);
|
||||
}
|
@@ -22,13 +22,13 @@
|
||||
//! inserts don't grip well in rubber.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/inserts.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
|
||||
foot = [25, 12, 3, 2, M4_cap_screw, 10];
|
||||
insert_foot = [20, 10, 0, 2, M3_cap_screw, 10];
|
||||
function Foot(d, h, t, r, screw, slant = 10) = [d, h, t, r, screw, slant]; //! Construct a foot property list
|
||||
|
||||
foot = Foot(25, 12, 3, 2, M4_cap_screw);
|
||||
insert_foot = Foot(20, 10, 0, 2, M3_cap_screw);
|
||||
|
||||
function foot() = foot; //! Default foot used unless a list of parameters is passed
|
||||
function insert_foot() = insert_foot; //! Default foot with insert
|
||||
|
||||
function foot_diameter(type = foot) = type[0]; //! Outside maximum diameter
|
||||
@@ -58,7 +58,7 @@ module foot(type = foot) { //! Generate STL
|
||||
circle4n(r);
|
||||
}
|
||||
}
|
||||
linear_extrude(height = t)
|
||||
linear_extrude(t)
|
||||
difference() {
|
||||
circle(r1 + eps);
|
||||
|
||||
@@ -67,7 +67,7 @@ module foot(type = foot) { //! Generate STL
|
||||
}
|
||||
}
|
||||
|
||||
module foot_assembly(t = 0, type = foot) { //! Assembly with fasteners in place for specified sheet thickness
|
||||
module foot_assembly(t = 0, type = foot, flip = false) { //! Assembly with fasteners in place for specified sheet thickness
|
||||
screw = foot_screw(type);
|
||||
washer = screw_washer(screw);
|
||||
nut = screw_nut(screw);
|
||||
@@ -75,16 +75,22 @@ module foot_assembly(t = 0, type = foot) { //! Assembly with fasteners in place
|
||||
screw_length = screw_longer_than(foot_thickness(type) + t + 2 * washer_thickness(washer) + nut_thickness(nut, true) - squeeze);
|
||||
|
||||
vflip() explode(15, true) {
|
||||
color(pp4_colour) foot(type);
|
||||
stl_colour(pp4_colour) foot(type);
|
||||
|
||||
if(t)
|
||||
explode(15, true)
|
||||
translate_z(foot_thickness(type))
|
||||
screw_and_washer(screw, screw_length);
|
||||
if(flip)
|
||||
nut_and_washer(nut, true);
|
||||
else
|
||||
screw_and_washer(screw, screw_length);
|
||||
}
|
||||
if(t)
|
||||
translate_z(t)
|
||||
nut_and_washer(nut, true);
|
||||
if(flip)
|
||||
screw_and_washer(screw, screw_length);
|
||||
else
|
||||
nut_and_washer(nut, true);
|
||||
}
|
||||
|
||||
module insert_foot(type = insert_foot) { //! Generate STL for foot with insert
|
||||
@@ -112,7 +118,7 @@ module insert_foot(type = insert_foot) { //! Generate STL for foot with insert
|
||||
}
|
||||
}
|
||||
}
|
||||
linear_extrude(height = h2 + eps)
|
||||
linear_extrude(h2 + eps)
|
||||
difference() {
|
||||
circle(r5 + eps);
|
||||
|
||||
@@ -132,7 +138,7 @@ assembly("insert_foot") {
|
||||
insert = screw_insert(screw);
|
||||
|
||||
vflip()
|
||||
color(pp1_colour) insert_foot(type);
|
||||
stl_colour(pp4_colour) insert_foot(type);
|
||||
|
||||
translate_z(-foot_thickness(type))
|
||||
insert(insert);
|
||||
|
@@ -21,8 +21,7 @@
|
||||
//! Printed handle that can be printed without needing support material due to its truncated teardrop profile.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/inserts.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
|
||||
dia = 18;
|
||||
length = 90; // inside length
|
||||
@@ -84,7 +83,7 @@ module handle_stl() { //! generate the STL
|
||||
module handle_assembly() pose([225, 0, 150], [0, 0, 14]) //! Printed part with inserts in place
|
||||
assembly("handle") {
|
||||
translate_z(handle_height())
|
||||
color(pp1_colour) vflip() handle_stl();
|
||||
stl_colour(pp1_colour) vflip() handle_stl();
|
||||
|
||||
handle_screw_positions()
|
||||
vflip()
|
||||
|
123
printed/pcb_mount.scad
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2019
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! A frame to mount a PCB by its corners when it has no mounting holes.
|
||||
//! The stl must be given a parameterless wrapper in the project that uses it.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../vitamins/pcb.scad>
|
||||
|
||||
clearance = 0.2;
|
||||
min_wall = extrusion_width * 2;
|
||||
wall = 2;
|
||||
|
||||
overlap = 2;
|
||||
|
||||
screw = M3_cap_screw;
|
||||
screw_clearance_r = 3.1 / 2;
|
||||
pillar_r = corrected_radius(screw_clearance_r) + wall;
|
||||
|
||||
function pillar_x_pitch(pcb) = pcb_length(pcb) + 2 * clearance + 2 * (pillar_r - overlap) / sqrt(2); //! x pitch of screw pillars
|
||||
function pillar_y_pitch(pcb) = pcb_width(pcb) + 2 * clearance + 2 * (pillar_r - overlap) / sqrt(2); //! y pitch of screw pillars
|
||||
|
||||
function pcb_mount_length(pcb) = pillar_x_pitch(pcb) + 2 * pillar_r; //! Outside length of the mount
|
||||
function pcb_mount_width(pcb) = pillar_y_pitch(pcb) + 2 * pillar_r; //! Outside width of the mount
|
||||
|
||||
frame_w = 3;
|
||||
frame_t = 2;
|
||||
|
||||
washer_thickness = 1.25;
|
||||
|
||||
module pcb_mount_screw_positions(pcb) //! Positions of the screws and pillars
|
||||
for(x = [-1, 1], y = [-1, 1])
|
||||
translate([x * pillar_x_pitch(pcb) / 2, y * pillar_y_pitch(pcb) / 2])
|
||||
children();
|
||||
|
||||
module pcb_mount_holes(pcb, h = 0) //! Drill holes for PCB mount
|
||||
pcb_mount_screw_positions(pcb)
|
||||
drill(screw_clearance_radius(screw), h);
|
||||
|
||||
module pcb_mount_ring()
|
||||
difference() {
|
||||
circle(pillar_r);
|
||||
|
||||
poly_circle(screw_clearance_r);
|
||||
}
|
||||
|
||||
module pcb_mount_washer_stl() //! A plastic washer to clamp a PCB
|
||||
linear_extrude(washer_thickness)
|
||||
pcb_mount_ring();
|
||||
|
||||
module pcb_mount(pcb, height = 5, washers = true) { //! Make the STL of a pcb mount for the specified PCB.
|
||||
stl(str("pcb_mount_", pcb[0], "_", height));
|
||||
|
||||
y_pitch = pcb_width(pcb) > 4 * pillar_r + 4 ? pillar_r + 1
|
||||
: pcb_width(pcb) / 2 + frame_w + 1 + pillar_r;
|
||||
|
||||
if(washers)
|
||||
for(x = [-1, 1], y = [-1, 1])
|
||||
translate([x * (pillar_r + 1), y * y_pitch, 0])
|
||||
pcb_mount_washer_stl();
|
||||
|
||||
for(x = [-1, 1])
|
||||
translate([x * pillar_x_pitch(pcb) / 2, 0, frame_t / 2])
|
||||
cube([frame_w, pillar_y_pitch(pcb) - 2 * wall, frame_t], center = true);
|
||||
|
||||
for(y = [-1, 1])
|
||||
translate([0, y * pillar_y_pitch(pcb) / 2, frame_t / 2])
|
||||
cube([pillar_x_pitch(pcb) - 2 * wall, frame_w, frame_t], center = true);
|
||||
|
||||
pcb_mount_screw_positions(pcb)
|
||||
linear_extrude(height)
|
||||
pcb_mount_ring();
|
||||
|
||||
linear_extrude(height + pcb_thickness(pcb) - layer_height)
|
||||
difference() {
|
||||
pcb_mount_screw_positions(pcb)
|
||||
pcb_mount_ring();
|
||||
|
||||
square([pcb_length(pcb) + 2 * clearance, pcb_width(pcb) + 2 * clearance], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
module pcb_mount_assembly(pcb, thickness, height = 5) { //! A PCB mount assembly with fasteners
|
||||
translate_z(height)
|
||||
pcb(pcb);
|
||||
|
||||
stl_colour(pp1_colour) pcb_mount(pcb, washers = false);
|
||||
|
||||
washer = screw_washer(screw);
|
||||
nut = screw_nut(screw);
|
||||
t = pcb_thickness(pcb);
|
||||
screw_length = screw_longer_than(height + t + washer_thickness + thickness + washer_thickness(washer) + nut_thickness(nut, true));
|
||||
|
||||
pcb_mount_screw_positions(pcb) {
|
||||
translate_z(height + t) {
|
||||
stl_colour(pp2_colour) pcb_mount_washer_stl();
|
||||
|
||||
translate_z(washer_thickness)
|
||||
screw(screw, screw_length);
|
||||
}
|
||||
|
||||
translate_z(-thickness)
|
||||
vflip()
|
||||
nut_and_washer(nut, true);
|
||||
}
|
||||
}
|
50
printed/press_fit.scad
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! Utility for making printed press fit connectors to join printed parts.
|
||||
//!
|
||||
//! Add solvent or glue to make a permanent fixture.
|
||||
//
|
||||
include <../core.scad>
|
||||
|
||||
interference = 0.0;
|
||||
|
||||
bridge_droop = layer_height; //sqrt(4 * layer_height * filament_width / PI) - layer_height;
|
||||
|
||||
module press_fit_socket(w = 5, h = 50, horizontal = false) { //! Make a square hole to accept a peg
|
||||
h = horizontal ? h : h + bridge_droop;
|
||||
|
||||
cube([w, w, 2 * h], center = true);
|
||||
}
|
||||
|
||||
module press_fit_peg(h, w = 5, horizontal = false) { //! Make a rounded chamfered peg for easy insertion
|
||||
module chamfered_square(w, horizontal) {
|
||||
h = horizontal ? w - bridge_droop : w;
|
||||
rounded_square([w, h], 1);
|
||||
}
|
||||
|
||||
translate_z(-eps)
|
||||
linear_extrude(height = h + eps - layer_height)
|
||||
chamfered_square(w + interference, horizontal);
|
||||
|
||||
translate_z(h - layer_height - eps)
|
||||
linear_extrude(height = layer_height + eps)
|
||||
chamfered_square(w - layer_height, horizontal);
|
||||
}
|
235
printed/printed_box.scad
Normal file
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! A fully parametric 3D printed case that can be customised with cutouts and additions specified by children.
|
||||
//!
|
||||
//! The walls can be made wavy, which possibly reduces warping when printing and looks nice, however if holes need to be made
|
||||
//! in the sides you can't print a wavy bridge. Any holes need to be surrounded by a 45° chamfer to make the bridges straight.
|
||||
//! See the mounting points for the feet in the first example.
|
||||
//!
|
||||
//! It can also have printed feet on the base with the screws doubling up to hold the base on.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
use <foot.scad>
|
||||
|
||||
function pbox(name, wall, top_t, base_t, radius, size, foot = false, screw = false, ridges = [0, 0]) //! Construct a printed box property list
|
||||
= concat([name, wall, top_t, base_t, foot, screw, radius, ridges], size);
|
||||
|
||||
function pbox_name(type) = type[0]; //! Name to allow more than one box in a project
|
||||
function pbox_wall(type) = type[1]; //! Wall thickness
|
||||
function pbox_top(type) = type[2]; //! Top thickness
|
||||
function pbox_base(type) = type[3]; //! Base thickness, can be zero for no base
|
||||
function pbox_foot(type) = type[4]; //! Printed foot, can be false to suppress feet
|
||||
function pbox_base_screw(type) = type[5]; //! Screw type if no feet
|
||||
function pbox_radius(type) = type[6]; //! Internal corner radius
|
||||
function pbox_ridges(type) = type[7]; //! Ridge wavelength and amplitude
|
||||
function pbox_width(type) = type[8]; //! Internal width
|
||||
function pbox_depth(type) = type[9]; //! Internal depth
|
||||
function pbox_height(type) = type[10]; //! Internal height
|
||||
|
||||
base_outset = 1; // How much the base overlaps the inner dimensions
|
||||
base_overlap = 2; // The width of ledge the base sits on
|
||||
height_overlap = 1; // How far the edges sit below the base
|
||||
|
||||
function pbox_inclusion(type) = pbox_base(type) ? base_overlap - base_outset : 0; //! How far the ledge for the base extends inwards
|
||||
|
||||
function pbox_total_height(type) = //! Total height including base overlap
|
||||
let(base = pbox_base(type),
|
||||
foot = pbox_foot(type),
|
||||
washer = pbox_washer(type),
|
||||
screw = pbox_screw(type))
|
||||
pbox_height(type) + pbox_top(type) + base + (base ? height_overlap : 0) + (foot || !base ? 0 : washer_thickness(washer) + screw_head_height(screw));
|
||||
|
||||
function pbox_screw(type) = //! Foot screw if got feet else base_screw
|
||||
let(foot = pbox_foot(type)) foot ? foot_screw(foot) : pbox_base_screw(type);
|
||||
|
||||
function pbox_insert(type) = screw_insert(pbox_screw(type)); //! The insert for the base screws
|
||||
function pbox_washer(type) = screw_washer(pbox_screw(type)); //! The washer for the base screws
|
||||
|
||||
function pbox_screw_length(type, panel_thickness = 0) = //! Length of the base screw
|
||||
let(foot = pbox_foot(type))
|
||||
screw_shorter_than(pbox_base(type) + washer_thickness(pbox_washer(type))
|
||||
+ insert_length(pbox_insert(type))
|
||||
+ (foot ? foot_thickness(foot) : panel_thickness));
|
||||
|
||||
function pbox_mid_offset(type) = pbox_ridges(type).y + pbox_wall(type) / 2; // Offset to wall midpoint
|
||||
|
||||
function pbox_screw_inset(type) = //! How far the base screws are inset
|
||||
let(foot = pbox_foot(type),
|
||||
r = foot ? foot_diameter(foot) / 2 : pbox_base(type) ? washer_radius(pbox_washer(type)) : insert_hole_radius(pbox_insert(type)),
|
||||
R = pbox_radius(type)
|
||||
) max(r, R - (R - r) / sqrt(2));
|
||||
|
||||
module pbox_screw_positions(type) { //! Place children at base screw positions
|
||||
foot = pbox_foot(type);
|
||||
inset = pbox_screw_inset(type);
|
||||
for(x = [-1, 1], y = [-1, 1])
|
||||
translate([x * (pbox_width(type) / 2 - inset), y * (pbox_depth(type) / 2 - inset)])
|
||||
rotate((y > 0 ? -x * 45 : -x * 135) + 90)
|
||||
children();
|
||||
}
|
||||
|
||||
module pbox_mid_shape(type) {
|
||||
ridges = pbox_ridges(type);
|
||||
offset = ridges.y + pbox_wall(type) / 2;
|
||||
rad = pbox_radius(type) + offset;
|
||||
w = pbox_width(type) + 2 * offset;
|
||||
d = pbox_depth(type) + 2 * offset;
|
||||
|
||||
module waves(length) {
|
||||
l = length - 2 * rad;
|
||||
|
||||
waves = round(l / ridges.x);
|
||||
points = 16;
|
||||
translate([-l / 2, ridges.y / 2])
|
||||
polygon(concat([[0, -10]], [for(i = [0 : waves * points], a = 360 * i / points) [i * l / waves / points, -cos(a) * ridges.y / 2] ], [[l, -10]]));
|
||||
}
|
||||
|
||||
difference() {
|
||||
rounded_square([w, d], rad, center = true);
|
||||
|
||||
if(ridges.y)
|
||||
for(side = [-1, 1]) {
|
||||
translate([0, side * d / 2])
|
||||
rotate(90 + side * 90)
|
||||
waves(w);
|
||||
|
||||
translate([side * w / 2, 0])
|
||||
rotate(side * 90)
|
||||
waves(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module pbox_inner_shape(type) {
|
||||
rad = pbox_radius(type);
|
||||
w = pbox_width(type);
|
||||
d = pbox_depth(type);
|
||||
|
||||
rounded_square([w, d], rad, center = true);
|
||||
}
|
||||
|
||||
module pbox_outer_shape(type) //! 2D outer shape of the box
|
||||
offset(pbox_wall(type) / 2) pbox_mid_shape(type);
|
||||
|
||||
module pbox_base(type) { //! Generate the STL for the base
|
||||
stl(str(pbox_name(type),"_base"));
|
||||
t = pbox_base(type);
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(t)
|
||||
offset(base_outset - 0.2)
|
||||
pbox_inner_shape(type);
|
||||
|
||||
if($children > 0)
|
||||
children(0);
|
||||
}
|
||||
pbox_screw_positions(type)
|
||||
poly_cylinder(r = screw_clearance_radius(pbox_screw(type)), h = 2 * t + eps, center = true);
|
||||
|
||||
if($children > 1)
|
||||
children(1);
|
||||
}
|
||||
}
|
||||
|
||||
module pbox(type) { //! Generate the STL for the main case
|
||||
stl(pbox_name(type));
|
||||
|
||||
height = pbox_height(type);
|
||||
total_height = pbox_total_height(type);
|
||||
top_thickness = pbox_top(type);
|
||||
wall = pbox_wall(type);
|
||||
ledge_outset = pbox_ridges(type).y;
|
||||
ledge_inset = base_outset - base_overlap;
|
||||
ledge_h = pbox_base(type) ? (ledge_outset - ledge_inset) * 2 : 0;
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(total_height)
|
||||
pbox_outer_shape(type);
|
||||
|
||||
if($children > 2)
|
||||
children(2);
|
||||
}
|
||||
difference() {
|
||||
translate_z(top_thickness)
|
||||
union() {
|
||||
linear_extrude(height + eps)
|
||||
offset(-wall / 2) pbox_mid_shape(type);
|
||||
|
||||
translate_z(height) // Recess for the base
|
||||
linear_extrude(total_height - height)
|
||||
offset(base_outset)
|
||||
pbox_inner_shape(type);
|
||||
}
|
||||
// Ledge to support the lid
|
||||
if(ledge_h)
|
||||
translate_z(top_thickness + height - ledge_h)
|
||||
difference() {
|
||||
rounded_rectangle([pbox_width(type) + 2 * outset, pbox_depth(type) + 2 * outset, ledge_h], 1, center = false);
|
||||
|
||||
hull() {
|
||||
linear_extrude(ledge_h + eps)
|
||||
offset(ledge_inset)
|
||||
pbox_inner_shape(type);
|
||||
|
||||
linear_extrude(eps)
|
||||
offset(ledge_outset)
|
||||
pbox_inner_shape(type);
|
||||
}
|
||||
pbox_screw_positions(type)
|
||||
insert_hole(pbox_insert(type));
|
||||
}
|
||||
|
||||
// Corner lugs for inserts
|
||||
outset = wall + pbox_ridges(type).y;
|
||||
or = pbox_radius(type) + outset;
|
||||
inset = pbox_screw_inset(type) + outset;
|
||||
br = insert_boss_radius(pbox_insert(type), wall);
|
||||
ext = sqrt(2) * inset - or * (sqrt(2) - 1) - br;
|
||||
translate_z(height + top_thickness)
|
||||
pbox_screw_positions(type)
|
||||
insert_lug(pbox_insert(type), wall, counter_bore = 0, extension = ext, corner_r = or);
|
||||
|
||||
if($children > 0)
|
||||
children(0);
|
||||
}
|
||||
if($children > 1)
|
||||
children(1);
|
||||
}
|
||||
}
|
||||
|
||||
module pbox_inserts(type) //! Place the inserts for the base screws
|
||||
translate_z(pbox_height(type) + pbox_top(type))
|
||||
pbox_screw_positions(type)
|
||||
insert(pbox_insert(type));
|
||||
|
||||
module pbox_base_screws(type, thickness = 0) //! Place the screws and feet
|
||||
translate_z(pbox_height(type) + pbox_top(type) + pbox_base(type))
|
||||
pbox_screw_positions(type) {
|
||||
foot = pbox_foot(type);
|
||||
if(foot)
|
||||
stl_colour(pp4_colour)
|
||||
foot(foot);
|
||||
|
||||
translate_z(foot ? foot_thickness(foot) : thickness)
|
||||
screw_and_washer(pbox_screw(type), pbox_screw_length(type, thickness));
|
||||
}
|
171
printed/psu_shroud.scad
Normal file
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2018
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! A cover to go over the mains end of a PSU terminal strip to make it safe.
|
||||
//! The stl and assembly must be given a name and parameterless wrappers for the stl and assembly added to the project.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
|
||||
use <../vitamins/wire.scad>
|
||||
use <../vitamins/psu.scad>
|
||||
use <../utils/round.scad>
|
||||
|
||||
wall = 1.8;
|
||||
top = 1.5;
|
||||
screw = M3_cap_screw;
|
||||
insert = screw_insert(screw);
|
||||
boss_r = insert_boss_radius(insert, wall);
|
||||
counter_bore = 2;
|
||||
rad = 2;
|
||||
clearance = layer_height;
|
||||
overlap = 6;
|
||||
|
||||
cable_tie_inset = wall + 4;
|
||||
|
||||
function psu_shroud_extent(type) = 15 + wall; //! How far it extends beyond the PSU to clear the connections
|
||||
function psu_shroud_depth(type) = //! Outside depth of the shroud
|
||||
psu_left_bay(type) + overlap + psu_shroud_extent(type);
|
||||
|
||||
function psu_shroud_width(type) = //! Outside width of the shroud
|
||||
let(terminals = psu_terminals(type))
|
||||
terminals ?
|
||||
let(y = terminals.y, tb = terminals.z)
|
||||
wall + clearance / 2 + y + 3 * terminal_block_pitch(tb) + terminal_block_divider(tb) / 2 + wall / 2
|
||||
: psu_width(type) + 2 * wall + clearance;
|
||||
|
||||
function psu_shroud_height(type) = psu_height(type) + top + clearance; //! Outside height
|
||||
function psu_shroud_centre_y(type) = //! Shroud centre relative to PSU centre
|
||||
psu_width(type) / 2 + clearance / 2 + wall - psu_shroud_width(type) / 2;
|
||||
|
||||
function psu_shroud_pitch(type) = psu_shroud_width(type) - 2 * boss_r - eps;
|
||||
function psu_shroud_screw(type) = screw; //! Screw used to fasten
|
||||
function psu_shroud_cable_pitch(cable_d) = cable_d + 5; //! Pitch between cable entries
|
||||
|
||||
module psu_shroud_hole_positions(type) //! Place children at the screw hole positions
|
||||
for($side = [-1, 1])
|
||||
translate([-psu_length(type) / 2 - boss_r - 1, psu_shroud_centre_y(type) + $side * psu_shroud_pitch(type) / 2])
|
||||
children();
|
||||
|
||||
module psu_shroud_cable_positions(type, cable_d, cables = 1) //! Place children at the cable tie positions
|
||||
for(i = [0 : 1 : cables - 1])
|
||||
translate([-psu_length(type) / 2 - psu_shroud_extent(type) + cable_tie_inset,
|
||||
psu_shroud_centre_y(type) + (i - cables / 2 + 0.5) * psu_shroud_cable_pitch(cable_d)])
|
||||
children();
|
||||
|
||||
module psu_shroud_holes(type, cable_d, cables = 1) { //! Drill the screw and ziptie holes
|
||||
psu_shroud_hole_positions(type)
|
||||
drill(screw_clearance_radius(screw), 0);
|
||||
|
||||
psu_shroud_cable_positions(type, cable_d, cables)
|
||||
cable_tie_holes(cable_d / 2, h = 0);
|
||||
}
|
||||
|
||||
module psu_shroud(type, cable_d, name, cables = 1) { //! Generate the STL file for a specified ssr and cable
|
||||
stl(str("psu_shroud_", name));
|
||||
|
||||
extent = psu_shroud_extent(type);
|
||||
depth = psu_shroud_depth(type);
|
||||
width = psu_shroud_width(type);
|
||||
height = psu_shroud_height(type);
|
||||
centre_x = -psu_length(type) / 2 - psu_shroud_extent(type) + psu_shroud_depth(type) / 2;
|
||||
centre_y = psu_shroud_centre_y(type);
|
||||
terminal_clearance = 0.5;
|
||||
tb = psu_terminals(type).z;
|
||||
|
||||
module shape() {
|
||||
difference() {
|
||||
round(or = wall / 2 - eps, ir = 0) difference() {
|
||||
rounded_square([depth, width], rad);
|
||||
|
||||
rounded_square([depth - 2 * wall, width - 2 * wall], rad - wall);
|
||||
|
||||
translate([depth / 2, 0])
|
||||
square([2 * rad, width], center = true);
|
||||
|
||||
translate([depth / 2, width / 2 - 5])
|
||||
square([2 * (overlap + terminal_clearance), 10], center = true);
|
||||
}
|
||||
for(i = [0 : 1 : cables - 1])
|
||||
translate([0, (i - cables / 2 + 0.5) * psu_shroud_cable_pitch(cable_d)])
|
||||
square([depth + 1, cable_d], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
// base and sides
|
||||
translate([centre_x, -centre_y]) {
|
||||
rounded_rectangle([depth - eps, width - eps, top], rad, center = false);
|
||||
|
||||
linear_extrude(height)
|
||||
difference() {
|
||||
shape();
|
||||
|
||||
translate([depth / 2, width / 2 - 5])
|
||||
square([2 * (depth - extent + terminal_clearance), 10], center = true);
|
||||
}
|
||||
linear_extrude(height - terminal_block_height(tb) - psu_terminal_block_z(type) - terminal_clearance)
|
||||
shape();
|
||||
}
|
||||
// cable slots
|
||||
for(i = [0 : 1 : cables - 1])
|
||||
translate([centre_x - depth / 2 + wall / 2, -centre_y + (i - cables / 2 + 0.5) * psu_shroud_cable_pitch(cable_d), height / 2])
|
||||
rotate([90, 0, 90])
|
||||
linear_extrude(wall, center = true)
|
||||
difference() {
|
||||
square([cable_d + eps, height], center = true);
|
||||
|
||||
translate([0, height / 2])
|
||||
vertical_tearslot(h = 0, r = cable_d / 2, l = cable_d);
|
||||
}
|
||||
// insert lugs
|
||||
mirror([0, 1, 0])
|
||||
psu_shroud_hole_positions(type)
|
||||
translate_z(height)
|
||||
rotate($side * 90)
|
||||
insert_lug(insert, wall, counter_bore);
|
||||
}
|
||||
|
||||
module psu_shroud_assembly(type, cable_d, name, cables = 1) //! The printed parts with inserts fitted
|
||||
assembly(str("PSU_shroud_", name)) {
|
||||
|
||||
translate_z(psu_shroud_height(type))
|
||||
vflip()
|
||||
stl_colour(pp1_colour) psu_shroud(type, cable_d, name, cables);
|
||||
|
||||
psu_shroud_hole_positions(type)
|
||||
vflip()
|
||||
insert(insert);
|
||||
}
|
||||
|
||||
module psu_shroud_fastened_assembly(type, cable_d, thickness, name, cables = 1) //! Assembly with screws in place
|
||||
{
|
||||
washer = screw_washer(screw);
|
||||
screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + insert_length(insert) + counter_bore);
|
||||
|
||||
psu_shroud_assembly(type, cable_d, name, cables);
|
||||
|
||||
translate_z(-thickness)
|
||||
psu_shroud_hole_positions(type)
|
||||
vflip()
|
||||
screw_and_washer(screw, screw_length, true);
|
||||
|
||||
psu_shroud_cable_positions(type, cable_d, cables)
|
||||
cable_tie(cable_d / 2, thickness);
|
||||
}
|
@@ -21,73 +21,75 @@
|
||||
//! Clamp for ribbon cable and polypropylene strip.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/inserts.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
use <../vitamins/cable_strip.scad>
|
||||
|
||||
wall = 2;
|
||||
wall = 1.6;
|
||||
min_wall = 2 * extrusion_width;
|
||||
screw = M3_cap_screw;
|
||||
insert = screw_insert(screw);
|
||||
screw_depth = insert_length(insert) + 1;
|
||||
|
||||
function ribbon_clamp_hole_pitch(ways) = ribbon_clamp_slot(ways) + 2 * min_wall + 2 * corrected_radius(insert_hole_radius(insert)); //! Hole pitch
|
||||
function ribbon_clamp_width() = 2 * (insert_hole_radius(insert) + 2); //! Width
|
||||
function ribbon_clamp_length(ways) = ribbon_clamp_hole_pitch(ways) + ribbon_clamp_width(); //! Length given ways
|
||||
function ribbon_clamp_height() = screw_depth + 1; //! Height
|
||||
function ribbon_clamp_screw_depth(screw = screw) = insert_length(screw_insert(screw)) + 1;
|
||||
function ribbon_clamp_hole_pitch(ways, screw = screw) =
|
||||
ribbon_clamp_slot(ways) + 2 * min_wall + 2 * corrected_radius(insert_hole_radius(screw_insert(screw))); //! Hole pitch
|
||||
|
||||
module ribbon_clamp_hole_positions(ways, side = undef) //! Place children at hole positions
|
||||
function ribbon_clamp_width(screw = screw) = 2 * (insert_hole_radius(screw_insert(screw)) + wall); //! Width
|
||||
function ribbon_clamp_length(ways, screw = screw) = ribbon_clamp_hole_pitch(ways, screw) + ribbon_clamp_width(screw); //! Length given ways
|
||||
function ribbon_clamp_height(screw = screw) = ribbon_clamp_screw_depth(screw) + 1; //! Height
|
||||
|
||||
module ribbon_clamp_hole_positions(ways, screw = screw, side = undef) //! Place children at hole positions
|
||||
for(x = is_undef(side) ? [-1, 1] : side)
|
||||
translate([x * ribbon_clamp_hole_pitch(ways) / 2, 0])
|
||||
translate([x * ribbon_clamp_hole_pitch(ways, screw) / 2, 0])
|
||||
children();
|
||||
|
||||
module ribbon_clamp_holes(ways, h = 20) //! Drill screw holes
|
||||
ribbon_clamp_hole_positions(ways)
|
||||
module ribbon_clamp_holes(ways, h = 20, screw = screw) //! Drill screw holes
|
||||
ribbon_clamp_hole_positions(ways, screw)
|
||||
drill(screw_clearance_radius(screw), h);
|
||||
|
||||
module ribbon_clamp(ways) { //! Generate STL for given number of ways
|
||||
stl(str("ribbon_clamp_", ways));
|
||||
module ribbon_clamp(ways, screw = screw) { //! Generate STL for given number of ways
|
||||
screw_d = screw_radius(screw) * 2;
|
||||
stl(str("ribbon_clamp_", ways, screw_d != 3 ? str("_", screw_d) : ""));
|
||||
|
||||
pitch = ribbon_clamp_hole_pitch(ways);
|
||||
d = ribbon_clamp_width();
|
||||
h = ribbon_clamp_height();
|
||||
t = h - ribbon_clamp_slot_depth() - wall;
|
||||
pitch = ribbon_clamp_hole_pitch(ways, screw);
|
||||
d = ribbon_clamp_width(screw);
|
||||
h = ribbon_clamp_height(screw);
|
||||
t = round_to_layer(ribbon_clamp_slot_depth() + wall);
|
||||
insert = screw_insert(screw);
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
hull() {
|
||||
translate_z(h - t / 2)
|
||||
cube([ribbon_clamp_hole_pitch(ways), d, t], center = true);
|
||||
cube([ribbon_clamp_hole_pitch(ways, screw), d, t], center = true);
|
||||
|
||||
translate_z(1)
|
||||
cube([pitch, max(wall, d - 2 * (h - t)), 2], center = true);
|
||||
}
|
||||
ribbon_clamp_hole_positions(ways, -1)
|
||||
ribbon_clamp_hole_positions(ways, screw, -1)
|
||||
cylinder(d = d, h = h);
|
||||
|
||||
ribbon_clamp_hole_positions(ways, 1)
|
||||
ribbon_clamp_hole_positions(ways, screw, 1)
|
||||
cylinder(d = d, h = h);
|
||||
|
||||
}
|
||||
|
||||
translate_z(h)
|
||||
cube([ribbon_clamp_slot(ways), d + 1, ribbon_clamp_slot_depth() * 2], center = true);
|
||||
|
||||
ribbon_clamp_hole_positions(ways)
|
||||
ribbon_clamp_hole_positions(ways, screw)
|
||||
translate_z(h)
|
||||
rotate(22.5)
|
||||
insert_hole(insert, screw_depth - insert_length(insert));
|
||||
insert_hole(insert, ribbon_clamp_screw_depth(screw) - insert_length(insert));
|
||||
}
|
||||
}
|
||||
|
||||
module ribbon_clamp_assembly(ways) pose([55, 180, 25]) //! Printed part with inserts in place
|
||||
assembly(str("ribbon_clamp_", ways)) {
|
||||
h = ribbon_clamp_height();
|
||||
module ribbon_clamp_assembly(ways, screw = screw) pose([55, 180, 25]) //! Printed part with inserts in place
|
||||
assembly(let(screw_d = screw_radius(screw) * 2)str("ribbon_clamp_", ways, screw_d != 3 ? str("_", screw_d) : "")) {
|
||||
h = ribbon_clamp_height(screw);
|
||||
insert = screw_insert(screw);
|
||||
|
||||
color(pp1_colour) render()
|
||||
translate_z(h) vflip() ribbon_clamp(ways);
|
||||
stl_colour(pp1_colour) render()
|
||||
translate_z(h) vflip() ribbon_clamp(ways, screw);
|
||||
|
||||
ribbon_clamp_hole_positions(ways)
|
||||
ribbon_clamp_hole_positions(ways, screw)
|
||||
vflip()
|
||||
insert(insert);
|
||||
}
|
||||
@@ -100,20 +102,28 @@ module ribbon_clamp_fastened_assembly(ways, thickness, screw = screw) { //! Clam
|
||||
vitamin(str(": Tape self amalgamating silicone ",tape_l," x 25mm"));
|
||||
|
||||
washer = screw_washer(screw);
|
||||
screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + screw_depth);
|
||||
screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + ribbon_clamp_screw_depth(screw));
|
||||
|
||||
ribbon_clamp_assembly(ways);
|
||||
ribbon_clamp_assembly(ways, screw);
|
||||
|
||||
color("red") translate_z(tape_thickness / 2)
|
||||
cube([tape_l, tape_width, tape_thickness], center = true);
|
||||
|
||||
ribbon_clamp_hole_positions(ways)
|
||||
ribbon_clamp_hole_positions(ways, screw)
|
||||
vflip()
|
||||
translate_z(thickness)
|
||||
screw_and_washer(screw, screw_length, true);
|
||||
}
|
||||
|
||||
module ribbon_clamp_20_stl() ribbon_clamp(20);
|
||||
module ribbon_clamp_8_2_stl() ribbon_clamp(8, M2_dome_screw);
|
||||
module ribbon_clamp_7_2_stl() ribbon_clamp(8, M2_dome_screw);
|
||||
|
||||
//! * Place inserts into the holes and press home with a soldering iron with a conical bit heated to 200°C.
|
||||
module ribbon_clamp_20_assembly() ribbon_clamp_assembly(20);
|
||||
|
||||
//! * Place inserts into the holes and press home with a soldering iron with a conical bit heated to 200°C.
|
||||
module ribbon_clamp_8_2_assembly() ribbon_clamp_assembly(8, M2_dome_screw);
|
||||
|
||||
//! * Place inserts into the holes and press home with a soldering iron with a conical bit heated to 200°C.
|
||||
module ribbon_clamp_7_2_assembly() ribbon_clamp_assembly(8, M2_dome_screw);
|
||||
|
@@ -21,7 +21,6 @@
|
||||
//! Knob with embedded hex head screw.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
use <../utils/hanging_hole.scad>
|
||||
|
||||
knob_wall = 2;
|
||||
@@ -30,11 +29,11 @@ knob_stem_h = 6;
|
||||
knob_thickness = 4;
|
||||
knob_r = 8;
|
||||
knob_wave = 1;
|
||||
knob_waves = 9;
|
||||
knob_waves = 5;
|
||||
knob_height = knob_stem_h + knob_thickness;
|
||||
function knob_height() = knob_height;
|
||||
|
||||
module screw_knob(screw) { //! Generate the STL foe a knob to fit the specified hex screw
|
||||
module screw_knob(screw) { //! Generate the STL for a knob to fit the specified hex screw
|
||||
stl(str("screw_knob_M", screw_radius(screw) * 20));
|
||||
|
||||
knob_stem_r = nut_trap_radius(screw_nut(screw)) + knob_wall;
|
||||
@@ -49,7 +48,7 @@ module screw_knob(screw) { //! Generate the STL foe a knob to fit the specified
|
||||
rotate(45)
|
||||
circle(r = nut_trap_radius(screw_nut(screw)), $fn = 6);
|
||||
}
|
||||
linear_extrude(height = knob_thickness, convexity = 3)
|
||||
linear_extrude(knob_thickness, convexity = 3)
|
||||
difference() {
|
||||
polygon(points = [for(a = [0 : 359]) [wave(a) * sin(a), wave(a) * cos(a)]]);
|
||||
|
||||
@@ -63,7 +62,7 @@ module screw_knob_assembly(screw, length) //! Assembly with the screw in place
|
||||
assembly(str("screw_knob_M", 20 * screw_radius(screw), "_", length)) {
|
||||
translate_z(knob_height)
|
||||
vflip()
|
||||
color(pp1_colour) screw_knob(screw);
|
||||
stl_colour(pp1_colour) screw_knob(screw);
|
||||
|
||||
translate_z(knob_height - knob_nut_trap_depth(screw))
|
||||
rotate(-45)
|
||||
@@ -73,5 +72,8 @@ assembly(str("screw_knob_M", 20 * screw_radius(screw), "_", length)) {
|
||||
module screw_knob_M30_stl() screw_knob(M3_hex_screw);
|
||||
module screw_knob_M40_stl() screw_knob(M4_hex_screw);
|
||||
|
||||
//! * Press the M3 x 16 hex screw into the knob
|
||||
module screw_knob_M30_16_assembly() screw_knob_assembly(M3_hex_screw, 16);
|
||||
|
||||
//! * Press the M4 x 16 hex screw into the knob
|
||||
module screw_knob_M40_16_assembly() screw_knob_assembly(M4_hex_screw, 16);
|
||||
|
@@ -22,9 +22,8 @@
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/mains_sockets.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/inserts.scad>
|
||||
include <../vitamins/ring_terminals.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
|
||||
box_height = 19;
|
||||
base_thickness = 2;
|
||||
@@ -53,12 +52,12 @@ module socket_box(type) { //! Generate STL of the backbox for the specified sock
|
||||
insert_hole_radius = insert_hole_radius(insert);
|
||||
|
||||
difference() {
|
||||
linear_extrude(height = height, convexity = 5)
|
||||
linear_extrude(height, convexity = 5)
|
||||
face_plate(type);
|
||||
|
||||
difference() {
|
||||
translate_z(base_thickness)
|
||||
linear_extrude(height = height, convexity = 5)
|
||||
linear_extrude(height, convexity = 5)
|
||||
offset(-wall) offset(1) face_plate(type);
|
||||
|
||||
for(side = [-1, 1])
|
||||
@@ -93,7 +92,7 @@ assembly(str("socket_box_", type[0])) {
|
||||
screw = mains_socket_screw(type);
|
||||
insert = screw_insert(screw);
|
||||
|
||||
color("lime") render() socket_box(type);
|
||||
stl_colour(pp1_colour) render() socket_box(type);
|
||||
|
||||
mains_socket_hole_positions(type)
|
||||
translate_z(height)
|
||||
|
@@ -19,23 +19,21 @@
|
||||
|
||||
//
|
||||
//! A cover to go over the mains end of an SSR to make it safe to be touched.
|
||||
//! The stl and assembly must be given a name and parameterless wrappers for the stl and assembly added to the project.
|
||||
//! The STL and assembly must be given a name and parameterless wrappers for the stl and assembly added to the project.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/inserts.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
|
||||
use <../vitamins/wire.scad>
|
||||
use <../vitamins/ssr.scad>
|
||||
use <../utils/round.scad>
|
||||
|
||||
wall = 1.8;
|
||||
top = 1.5;
|
||||
screw = M3_cap_screw;
|
||||
screw_length = 10;
|
||||
insert = screw_insert(screw);
|
||||
boss_r = wall + corrected_radius(insert_hole_radius(insert));
|
||||
boss_h = insert_hole_length(insert);
|
||||
boss_r = insert_boss_radius(insert, wall);
|
||||
counter_bore = 2;
|
||||
boss_h2 = boss_h + counter_bore;
|
||||
rad = 3;
|
||||
clearance = layer_height;
|
||||
|
||||
@@ -44,6 +42,7 @@ function ssr_shroud_screw(type) = screw; //!
|
||||
function ssr_shroud_extent(type, cable_d) = 2 * boss_r + 1 + cable_d + rad; //! How far it extends beyond the SSR
|
||||
function ssr_shroud_width(type) = ssr_width(type) + 2 * wall + clearance; //! Outside width of shroud
|
||||
function ssr_shroud_height(type) = ssr_height(type) + top + clearance; //! Outside height
|
||||
function ssr_shroud_cable_x(type, cable_d) = -ssr_length(type) / 2 - 2 * boss_r - 1 - cable_d / 2; //! Position of cable entry holes
|
||||
|
||||
module ssr_shroud_hole_positions(type) //! Place children at the screw hole positions
|
||||
for($side = [-1, 1])
|
||||
@@ -51,23 +50,30 @@ module ssr_shroud_hole_positions(type) //!
|
||||
vflip()
|
||||
children();
|
||||
|
||||
module ssr_shroud_holes(type) //: Drill the screw holes
|
||||
module ssr_shroud_holes(type, cable_d) { //! Drill the screw and ziptie holes
|
||||
ssr_shroud_hole_positions(type)
|
||||
drill(screw_clearance_radius(screw), 0);
|
||||
|
||||
for(side = [-1, 1])
|
||||
translate([ssr_shroud_cable_x(type, cable_d), side * (ssr_width(type) / 2 - 2 * boss_r)])
|
||||
rotate(-90)
|
||||
cable_tie_holes(cable_d / 2, h = 0);
|
||||
}
|
||||
|
||||
module ssr_shroud(type, cable_d, name) { //! Generate the STL file for a specified ssr and cable
|
||||
stl(str("ssr_shroud_", name));
|
||||
|
||||
width = ssr_shroud_width(type);
|
||||
depth = ssr_length(type) / 3 + ssr_shroud_extent(type, cable_d);
|
||||
height = ssr_shroud_height(type);
|
||||
cable_x = -ssr_length(type) / 2 - 2 * boss_r - 1 - cable_d / 2;
|
||||
cable_x = ssr_shroud_cable_x(type, cable_d);
|
||||
center_x = -ssr_length(type) / 6 - depth / 2;
|
||||
|
||||
// base and sides
|
||||
translate([center_x, 0]) {
|
||||
rounded_rectangle([depth - eps, width - eps, top], rad, center = false);
|
||||
|
||||
linear_extrude(height = height) difference() {
|
||||
linear_extrude(height) difference() {
|
||||
round(or = wall / 2 - eps, ir = 0) difference() {
|
||||
rounded_square([depth, width], rad);
|
||||
|
||||
@@ -81,74 +87,55 @@ module ssr_shroud(type, cable_d, name) { //! Generate the STL file for a spec
|
||||
square([cable_d, width + 1], center = true);
|
||||
}
|
||||
}
|
||||
// cable slots
|
||||
for(side = [-1, 1])
|
||||
translate([cable_x, side * (width / 2 - wall / 2), height / 2])
|
||||
rotate([90, 0, 0])
|
||||
linear_extrude(height = wall, center = true)
|
||||
linear_extrude(wall, center = true)
|
||||
difference() {
|
||||
square([cable_d + eps, height], center = true);
|
||||
|
||||
translate([0, height / 2])
|
||||
vertical_tearslot(h = 0, r = cable_d / 2, l = cable_d);
|
||||
}
|
||||
|
||||
translate_z(height - boss_h)
|
||||
linear_extrude(height = boss_h)
|
||||
ssr_shroud_hole_positions(type)
|
||||
difference() {
|
||||
hull() {
|
||||
circle(boss_r);
|
||||
|
||||
translate([0, -$side * (boss_r - 1)])
|
||||
square([2 * boss_r, eps], center = true);
|
||||
}
|
||||
poly_circle(insert_hole_radius(insert));
|
||||
}
|
||||
|
||||
translate_z(height - boss_h2)
|
||||
linear_extrude(height = counter_bore + eps)
|
||||
ssr_shroud_hole_positions(type)
|
||||
difference() {
|
||||
hull() {
|
||||
circle(boss_r);
|
||||
|
||||
translate([0, -$side * (boss_r - 1)])
|
||||
square([2 * boss_r, eps], center = true);
|
||||
}
|
||||
poly_circle(insert_screw_diameter(insert) / 2 + 0.1);
|
||||
}
|
||||
|
||||
// insert boss
|
||||
ssr_shroud_hole_positions(type)
|
||||
hull() {
|
||||
translate_z(-height + boss_h2) {
|
||||
cylinder(h = eps, r = boss_r - eps);
|
||||
|
||||
translate([0, -$side * (boss_r - 1)])
|
||||
cube([2 * boss_r, eps, eps], center = true);
|
||||
}
|
||||
translate([0, -$side * (boss_r - wall), -height + boss_h2 + (2 * boss_r - wall)])
|
||||
cube(eps);
|
||||
}
|
||||
|
||||
vflip()
|
||||
translate_z(height)
|
||||
rotate($side * 90)
|
||||
insert_lug(insert, wall, counter_bore);
|
||||
}
|
||||
|
||||
module ssr_shroud_assembly(type, cable_d, name) //! The printed parts with inserts fitted
|
||||
assembly(str("ssr_shroud_", name)) {
|
||||
assembly(str("SSR_shroud_", name)) {
|
||||
|
||||
translate_z(ssr_height(type) + top)
|
||||
translate_z(ssr_shroud_height(type))
|
||||
vflip()
|
||||
color(pp1_colour) ssr_shroud(type, cable_d, name);
|
||||
stl_colour(pp1_colour) ssr_shroud(type, cable_d, name);
|
||||
|
||||
ssr_shroud_hole_positions(type)
|
||||
insert(insert);
|
||||
|
||||
}
|
||||
|
||||
module ssr_shroud_fastened_assembly(type, cable_d, thickness, name) //! Assembly with screws in place
|
||||
{
|
||||
washer = screw_washer(screw);
|
||||
screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + insert_length(insert) + counter_bore);
|
||||
|
||||
ssr_shroud_assembly(type, cable_d, name);
|
||||
|
||||
translate_z(-thickness)
|
||||
ssr_shroud_hole_positions(type)
|
||||
screw_and_washer(screw, screw_length, true);
|
||||
|
||||
for(side = [-1, 1])
|
||||
translate([ssr_shroud_cable_x(type, cable_d), side * (ssr_width(type) / 2 - 2 * boss_r)]) {
|
||||
rotate(-90)
|
||||
cable_tie(cable_d / 2, thickness);
|
||||
|
||||
*translate_z(cable_d / 2)
|
||||
rotate([90, 0, 0])
|
||||
stl_colour(grey(20))
|
||||
cylinder(d = cable_d, h = 20, center = true);
|
||||
}
|
||||
}
|
||||
|
@@ -22,11 +22,9 @@
|
||||
//! be fully customised by passing a list of properties.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/inserts.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
|
||||
strap = [18, 2, M3_pan_screw, 3, 25];
|
||||
function strap() = strap;
|
||||
strap = strap();
|
||||
|
||||
wall = 2;
|
||||
clearance = 0.5;
|
||||
@@ -41,6 +39,10 @@ function strap_screw(type = strap) = type[2]; //! Screw type
|
||||
function strap_panel(type = strap) = type[3]; //! Panel thickness
|
||||
function strap_extension(type = strap) = type[4]; //! How much length of the strap that can pull out
|
||||
|
||||
|
||||
function strap(width = 18, thickness = 2, screw = M3_pan_screw, panel_thickness = 3, extension = 25) = //! Construct a property list for a strap
|
||||
[ width, thickness, screw, panel_thickness, extension ];
|
||||
|
||||
function strap_insert(type) = screw_insert(strap_screw(type)); //! The insert type
|
||||
function strap_key(type) = strap_panel(type) - panel_clearance;
|
||||
function strap_height(type) = wall + max(insert_length(strap_insert(type)) - strap_key(type), strap_thickness(type) + clearance); //! Height of the ends
|
||||
@@ -78,7 +80,7 @@ module strap(length, type = strap) { //! Generate the STL for the rubber strap
|
||||
len = length - 2 * (wall + clearance);
|
||||
w = strap_width(type);
|
||||
|
||||
linear_extrude(height = strap_thickness(type), convexity = 3)
|
||||
linear_extrude(strap_thickness(type), convexity = 3)
|
||||
difference() {
|
||||
rounded_square([len, w], w / 2 - eps);
|
||||
|
||||
@@ -120,12 +122,12 @@ module strap_end(type = strap) { //! Generate the STL for end piece
|
||||
}
|
||||
|
||||
union() {
|
||||
linear_extrude(height = z1)
|
||||
linear_extrude(z1)
|
||||
with_hole()
|
||||
outer();
|
||||
|
||||
translate_z(z1)
|
||||
linear_extrude(height = strap_height(type) - z1)
|
||||
linear_extrude(strap_height(type) - z1)
|
||||
difference() {
|
||||
outer();
|
||||
|
||||
@@ -138,11 +140,11 @@ module strap_end(type = strap) { //! Generate the STL for end piece
|
||||
}
|
||||
}
|
||||
|
||||
linear_extrude(height = strap_height(type) - layer_height)
|
||||
linear_extrude(strap_height(type) - layer_height)
|
||||
with_hole()
|
||||
strap_boss_shape(type);
|
||||
|
||||
linear_extrude(height = z2)
|
||||
linear_extrude(z2)
|
||||
with_hole()
|
||||
offset(cnc_bit_r)
|
||||
offset(-step - cnc_bit_r)
|
||||
@@ -161,7 +163,7 @@ module strap_end(type = strap) { //! Generate the STL for end piece
|
||||
//
|
||||
module strap_end_assembly(type = strap)
|
||||
assembly("strap_end") {
|
||||
color(pp1_colour)
|
||||
stl_colour(pp1_colour)
|
||||
strap_end(type);
|
||||
|
||||
translate_z(strap_height(type) + strap_key(type))
|
||||
@@ -176,7 +178,7 @@ module strap_assembly(length, type = strap) { //! Assembly with screws in place
|
||||
|
||||
screw_length = screw_shorter_than(washer_thickness(washer) + washer_thickness(penny) + insert_length(insert) + panel_clearance + counterbore);
|
||||
|
||||
color(pp4_colour) strap(length, type);
|
||||
stl_colour(pp4_colour) strap(length, type);
|
||||
|
||||
strap_screw_positions(length, type)
|
||||
translate_z(strap_height(type))
|
||||
|
173
scripts/bom.py
Normal file → Executable file
@@ -29,6 +29,7 @@ import openscad
|
||||
from time import *
|
||||
from set_config import *
|
||||
import json
|
||||
import re
|
||||
|
||||
def find_scad_file(mname):
|
||||
for filename in os.listdir(source_dir):
|
||||
@@ -45,9 +46,20 @@ def find_scad_file(mname):
|
||||
return filename
|
||||
return None
|
||||
|
||||
class Part:
|
||||
def __init__(self, args):
|
||||
self.count = 1
|
||||
for arg in args:
|
||||
arg = arg.replace('true', 'True').replace('false', 'False').replace('undef', 'None')
|
||||
exec('self.' + arg)
|
||||
|
||||
def data(self):
|
||||
return self.__dict__
|
||||
|
||||
class BOM:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.big = None
|
||||
self.count = 1
|
||||
self.vitamins = {}
|
||||
self.printed = {}
|
||||
@@ -60,14 +72,20 @@ class BOM:
|
||||
assemblies[ass] = self.assemblies[ass].count
|
||||
return {
|
||||
"name" : self.name,
|
||||
"big" : self.big,
|
||||
"count" : self.count,
|
||||
"assemblies" : assemblies,
|
||||
"vitamins" : self.vitamins,
|
||||
"printed" : self.printed,
|
||||
"routed" : self.routed
|
||||
"vitamins" : {v : self.vitamins[v].data() for v in self.vitamins},
|
||||
"printed" : {p : self.printed[p].data() for p in self.printed},
|
||||
"routed" : {r : self.routed[r].data() for r in self.routed}
|
||||
}
|
||||
|
||||
def add_part(self, s):
|
||||
args = []
|
||||
match = re.match(r'^(.*?\.stl|.*?\.dxf)\((.*)\)$', s) #look for name.stl(...) or name.dxf(...)
|
||||
if match:
|
||||
s = match.group(1)
|
||||
args = [match.group(2)]
|
||||
if s[-4:] == ".stl":
|
||||
parts = self.printed
|
||||
else:
|
||||
@@ -76,15 +94,19 @@ class BOM:
|
||||
else:
|
||||
parts = self.vitamins
|
||||
if s in parts:
|
||||
parts[s] += 1
|
||||
parts[s].count += 1
|
||||
else:
|
||||
parts[s] = 1
|
||||
parts[s] = Part(args)
|
||||
|
||||
def add_assembly(self, ass):
|
||||
def add_assembly(self, ass, args = []):
|
||||
if ass in self.assemblies:
|
||||
self.assemblies[ass].count += 1
|
||||
else:
|
||||
self.assemblies[ass] = BOM(ass)
|
||||
bom = BOM(ass)
|
||||
for arg in args:
|
||||
arg = arg.replace('true', 'True').replace('false', 'False').replace('undef', 'None')
|
||||
exec('bom.' + arg, locals())
|
||||
self.assemblies[ass] = bom
|
||||
|
||||
def make_name(self, ass):
|
||||
if self.count == 1:
|
||||
@@ -119,10 +141,10 @@ class BOM:
|
||||
for ass in sorted(self.assemblies):
|
||||
bom = self.assemblies[ass]
|
||||
if part in bom.vitamins:
|
||||
file.write("%2d|" % bom.vitamins[part])
|
||||
file.write("%2d|" % bom.vitamins[part].count)
|
||||
else:
|
||||
file.write(" |")
|
||||
print("%3d" % self.vitamins[part], description, file=file)
|
||||
print("%3d" % self.vitamins[part].count, description, file=file)
|
||||
|
||||
if self.printed:
|
||||
if self.vitamins:
|
||||
@@ -133,10 +155,10 @@ class BOM:
|
||||
for ass in sorted(self.assemblies):
|
||||
bom = self.assemblies[ass]
|
||||
if part in bom.printed:
|
||||
file.write("%2d|" % bom.printed[part])
|
||||
file.write("%2d|" % bom.printed[part].count)
|
||||
else:
|
||||
file.write(" |")
|
||||
print("%3d" % self.printed[part], part, file=file)
|
||||
print("%3d" % self.printed[part].count, part, file=file)
|
||||
|
||||
if self.routed:
|
||||
print(file=file)
|
||||
@@ -146,10 +168,10 @@ class BOM:
|
||||
for ass in sorted(self.assemblies):
|
||||
bom = self.assemblies[ass]
|
||||
if part in bom.routed:
|
||||
file.write("%2d|" % bom.routed[part])
|
||||
file.write("%2d|" % bom.routed[part].count)
|
||||
else:
|
||||
file.write(" |")
|
||||
print("%3d" % self.routed[part], part, file=file)
|
||||
print("%3d" % self.routed[part].count, part, file=file)
|
||||
|
||||
if self.assemblies:
|
||||
print(file=file)
|
||||
@@ -161,17 +183,22 @@ def parse_bom(file = "openscad.log", name = None):
|
||||
main = BOM(name)
|
||||
main.ordered_assemblies = []
|
||||
stack = []
|
||||
|
||||
prog = re.compile(r'^(.*)\((.*)\)$')
|
||||
for line in open(file):
|
||||
pos = line.find('ECHO: "~')
|
||||
if pos > -1:
|
||||
s = line[pos + 8 : line.rfind('"')]
|
||||
if s[-1] == '{':
|
||||
ass = s[:-1]
|
||||
args = []
|
||||
match = prog.match(ass) #look for (...)
|
||||
if match:
|
||||
ass = match.group(1)
|
||||
args = match.group(2).split(',')
|
||||
if stack:
|
||||
main.assemblies[stack[-1]].add_assembly(ass) #add to nested BOM
|
||||
stack.append(ass)
|
||||
main.add_assembly(ass) #add to flat BOM
|
||||
main.add_assembly(ass, args) #add to flat BOM
|
||||
if ass in main.ordered_assemblies:
|
||||
main.ordered_assemblies.remove(ass)
|
||||
main.ordered_assemblies.insert(0, ass)
|
||||
@@ -184,63 +211,83 @@ def parse_bom(file = "openscad.log", name = None):
|
||||
main.add_part(s)
|
||||
if stack:
|
||||
main.assemblies[stack[-1]].add_part(s)
|
||||
else:
|
||||
if 'ERROR:' in line or 'WARNING:' in line:
|
||||
raise Exception(line[:-1])
|
||||
return main
|
||||
|
||||
def usage():
|
||||
print("\nusage:\n\tbom [target_config] [<accessory_name>_assembly] - Generate BOMs for a project or an accessory to a project.")
|
||||
sys.exit(1)
|
||||
|
||||
def boms(target = None, assembly = None):
|
||||
bom_dir = set_config(target) + "bom"
|
||||
if assembly:
|
||||
bom_dir += "/accessories"
|
||||
if not os.path.isdir(bom_dir):
|
||||
try:
|
||||
bom_dir = set_config(target, usage) + "bom"
|
||||
if assembly:
|
||||
bom_dir += "/accessories"
|
||||
if not os.path.isdir(bom_dir):
|
||||
os.makedirs(bom_dir)
|
||||
else:
|
||||
assembly = "main_assembly"
|
||||
if os.path.isdir(bom_dir):
|
||||
shutil.rmtree(bom_dir)
|
||||
sleep(0.1)
|
||||
os.makedirs(bom_dir)
|
||||
else:
|
||||
assembly = "main_assembly"
|
||||
if os.path.isdir(bom_dir):
|
||||
shutil.rmtree(bom_dir)
|
||||
sleep(0.1)
|
||||
os.makedirs(bom_dir)
|
||||
#
|
||||
# Find the scad file that makes the module
|
||||
#
|
||||
scad_file = find_scad_file(assembly)
|
||||
if not scad_file:
|
||||
raise Exception("can't find source for " + assembly)
|
||||
#
|
||||
# make a file to use the module
|
||||
#
|
||||
bom_maker_name = source_dir + "/bom.scad"
|
||||
with open(bom_maker_name, "w") as f:
|
||||
f.write("use <%s>\n" % scad_file)
|
||||
f.write("%s();\n" % assembly);
|
||||
#
|
||||
# Run openscad
|
||||
#
|
||||
openscad.run("-D","$bom=2","-D","$preview=true","-o", "openscad.echo", bom_maker_name)
|
||||
os.remove(bom_maker_name)
|
||||
print("Generating bom ...", end=" ")
|
||||
#
|
||||
# Find the scad file that makes the module
|
||||
#
|
||||
scad_file = find_scad_file(assembly)
|
||||
if not scad_file:
|
||||
raise Exception("can't find source for " + assembly)
|
||||
#
|
||||
# make a file to use the module
|
||||
#
|
||||
bom_maker_name = source_dir + "/bom.scad"
|
||||
with open(bom_maker_name, "w") as f:
|
||||
f.write("use <%s>\n" % scad_file)
|
||||
f.write("%s();\n" % assembly);
|
||||
#
|
||||
# Run openscad
|
||||
#
|
||||
openscad.run("-D", "$bom=2", "-D", "$preview=true", "--hardwarnings", "-o", "openscad.echo", "-d", bom_dir + "/bom.deps", bom_maker_name)
|
||||
os.remove(bom_maker_name)
|
||||
print("Generating bom ...", end=" ")
|
||||
|
||||
main = parse_bom("openscad.echo", assembly)
|
||||
main = parse_bom("openscad.echo", assembly)
|
||||
|
||||
if assembly == "main_assembly":
|
||||
main.print_bom(True, open(bom_dir + "/bom.txt","wt"))
|
||||
if assembly == "main_assembly":
|
||||
main.print_bom(True, open(bom_dir + "/bom.txt","wt"))
|
||||
|
||||
for ass in main.assemblies:
|
||||
with open(bom_dir + "/" + ass + ".txt", "wt") as f:
|
||||
bom = main.assemblies[ass]
|
||||
print(bom.make_name(ass) + ":", file=f)
|
||||
bom.print_bom(False, f)
|
||||
for ass in main.assemblies:
|
||||
with open(bom_dir + "/" + ass + ".txt", "wt") as f:
|
||||
bom = main.assemblies[ass]
|
||||
print(bom.make_name(ass) + ":", file=f)
|
||||
bom.print_bom(False, f)
|
||||
|
||||
data = [main.assemblies[ass].flat_data() for ass in main.ordered_assemblies]
|
||||
with open(bom_dir + "/bom.json", 'w') as outfile:
|
||||
json.dump(data, outfile, indent = 4)
|
||||
data = [main.assemblies[ass].flat_data() for ass in main.ordered_assemblies]
|
||||
with open(bom_dir + "/bom.json", 'w') as outfile:
|
||||
json.dump(data, outfile, indent = 4)
|
||||
|
||||
print("done")
|
||||
print("done")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = len(sys.argv)
|
||||
if args > 1:
|
||||
if args > 2:
|
||||
boms(sys.argv[1], sys.argv[2])
|
||||
else:
|
||||
boms(sys.argv[1])
|
||||
if len(sys.argv) > 3: usage()
|
||||
|
||||
if len(sys.argv) == 3:
|
||||
target, assembly = sys.argv[1], sys.argv[2]
|
||||
else:
|
||||
boms();
|
||||
if len(sys.argv) == 2:
|
||||
if sys.argv[1][-9:] == "_assembly":
|
||||
target, assembly = None, sys.argv[1]
|
||||
else:
|
||||
target, assembly = sys.argv[1], None
|
||||
else:
|
||||
target, assembly = None, None
|
||||
|
||||
if assembly:
|
||||
if assembly[-9:] != "_assembly": usage()
|
||||
|
||||
boms(target, assembly)
|
||||
|
@@ -113,5 +113,5 @@ if __name__ == '__main__':
|
||||
if len(sys.argv) == 2:
|
||||
canonicalise(sys.argv[1])
|
||||
else:
|
||||
print("usage: c14n_stl file")
|
||||
print("\nusage:\n\t c14n_stl file - Canonicalise an STL file created by OpenSCAD.")
|
||||
sys.exit(1)
|
||||
|
@@ -17,6 +17,7 @@
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import os
|
||||
from set_config import source_dir
|
||||
|
||||
def mtime(file):
|
||||
if os.path.isfile(file):
|
||||
@@ -32,14 +33,15 @@ def read_deps(dname):
|
||||
deps = []
|
||||
for line in lines:
|
||||
if line.startswith('\t'):
|
||||
dep = line[1 : -1].rstrip(' \\')
|
||||
dep = line[1 : -1].rstrip(' \\').replace('\\ ', ' ')
|
||||
if not os.path.basename(dep) in ['stl.scad', 'dxf.scad', 'svf.scad', 'png.scad', 'target.scad']:
|
||||
deps.append(dep)
|
||||
return deps
|
||||
|
||||
def check_deps(target_mtime, dname):
|
||||
def check_deps(target, dname):
|
||||
target_mtime = mtime(target)
|
||||
if not target_mtime:
|
||||
return "target missing"
|
||||
return target + " missing"
|
||||
if not os.path.isfile(dname):
|
||||
return "no deps"
|
||||
deps = read_deps(dname)
|
||||
@@ -47,3 +49,18 @@ def check_deps(target_mtime, dname):
|
||||
if mtime(dep) > target_mtime:
|
||||
return dep + ' changed'
|
||||
return None
|
||||
|
||||
def source_dirs(bom_dir):
|
||||
dirs = set()
|
||||
lib_dirs = set()
|
||||
deps = read_deps(bom_dir + '/bom.deps')
|
||||
cwd = os.getcwd().replace('\\', '/')
|
||||
for dep in deps:
|
||||
dir = os.path.dirname(dep)
|
||||
if dir.startswith(cwd):
|
||||
dirs.add(dir[len(cwd) + 1:])
|
||||
else:
|
||||
if dir.endswith('/printed'):
|
||||
lib_dirs.add(dir)
|
||||
dirs.remove(source_dir)
|
||||
return [source_dir] + sorted(dirs) + sorted(lib_dirs)
|
||||
|
8
scripts/doc_scripts.py
Normal file → Executable file
@@ -26,6 +26,7 @@ from __future__ import print_function
|
||||
|
||||
import os
|
||||
from tests import do_cmd
|
||||
import argparse
|
||||
|
||||
dir = 'scripts'
|
||||
|
||||
@@ -35,12 +36,12 @@ def doc_scripts():
|
||||
print(
|
||||
'''
|
||||
# Python scripts
|
||||
These are located in the ```scripts``` subdirectory, which needs to be added to the program search path.
|
||||
These are located in the `scripts` subdirectory, which needs to be added to the program search path.
|
||||
|
||||
They should work with both Python 2 and Python 3.
|
||||
|
||||
| Script | Function |
|
||||
|:--|:--|''', file = doc_file)
|
||||
|:---|:---|''', file = doc_file)
|
||||
for file in os.listdir('scripts'):
|
||||
if file.endswith('.py'):
|
||||
blurb = ''
|
||||
@@ -59,7 +60,7 @@ They should work with both Python 2 and Python 3.
|
||||
break
|
||||
if not blurb:
|
||||
print("Missing description for", file)
|
||||
print("| ```%s``` | %s |" % (file, blurb), file = doc_file)
|
||||
print("| `%s` | %s |" % (file, blurb), file = doc_file)
|
||||
|
||||
with open(dir + "/readme.html", "wt") as html_file:
|
||||
do_cmd(("python -m markdown -x tables " + doc_name).split(), html_file)
|
||||
@@ -74,4 +75,5 @@ They should work with both Python 2 and Python 3.
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
argparse.ArgumentParser(description='Generate scripts/readme.md and make html versions of that and doc/usage.md').parse_args()
|
||||
doc_scripts()
|
||||
|
0
scripts/dxfs.py
Normal file → Executable file
@@ -30,14 +30,14 @@ import times
|
||||
from deps import *
|
||||
import json
|
||||
|
||||
def bom_to_parts(target_dir, part_type, assembly = None):
|
||||
def bom_to_parts(bom_dir, part_type, assembly = None):
|
||||
#
|
||||
# Make a list of all the parts in the BOM
|
||||
#
|
||||
part_files = []
|
||||
bom = assembly + '.txt' if assembly else "bom.txt"
|
||||
suffix = ".dxf" if part_type == 'svg' else '.' + part_type
|
||||
with open(target_dir + "/../bom/" + bom, "rt") as f:
|
||||
with open(bom_dir + '/' + bom, "rt") as f:
|
||||
for line in f.readlines():
|
||||
words = line.split()
|
||||
if words:
|
||||
@@ -46,13 +46,24 @@ def bom_to_parts(target_dir, part_type, assembly = None):
|
||||
part_files.append(last_word[:-4] + '.' + part_type)
|
||||
return part_files
|
||||
|
||||
def usage(t):
|
||||
print("\nusage:\n\t%ss [target_config] [<name1>.%s] ... [<nameN>.%s] - Generate specified %s files or all if none specified." % ( t, t, t, t.upper()))
|
||||
sys.exit(1)
|
||||
|
||||
def make_parts(target, part_type, parts = None):
|
||||
#
|
||||
# Check list of parts is the correct type
|
||||
#
|
||||
if parts:
|
||||
for p in parts:
|
||||
if not p.endswith('.' + part_type): usage(part_type)
|
||||
#
|
||||
# Make the target directory
|
||||
#
|
||||
top_dir = set_config(target)
|
||||
top_dir = set_config(target, lambda: usage(part_type))
|
||||
target_dir = top_dir + part_type + 's'
|
||||
deps_dir = top_dir + "deps"
|
||||
bom_dir = top_dir + "bom"
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
if not os.path.isdir(deps_dir):
|
||||
@@ -64,7 +75,7 @@ def make_parts(target, part_type, parts = None):
|
||||
if parts:
|
||||
targets = list(parts) #copy the list so we dont modify the list passed in
|
||||
else:
|
||||
targets = bom_to_parts(target_dir, part_type)
|
||||
targets = bom_to_parts(bom_dir, part_type)
|
||||
for file in os.listdir(target_dir):
|
||||
if file.endswith('.' + part_type):
|
||||
if not file in targets:
|
||||
@@ -83,56 +94,49 @@ def make_parts(target, part_type, parts = None):
|
||||
#
|
||||
# Find all the scad files
|
||||
#
|
||||
lib_dir = os.environ['OPENSCADPATH'] + '/NopSCADlib/printed'
|
||||
used = []
|
||||
module_suffix = '_dxf' if part_type == 'svg' else '_' + part_type
|
||||
for dir in [source_dir, lib_dir]:
|
||||
for filename in os.listdir(dir):
|
||||
if filename[-5:] == ".scad":
|
||||
#
|
||||
# find any modules ending in _<part_type>
|
||||
#
|
||||
with open(dir + "/" + filename, "r") as f:
|
||||
for line in f.readlines():
|
||||
words = line.split()
|
||||
if(len(words) and words[0] == "module"):
|
||||
module = words[1].split('(')[0]
|
||||
if module.endswith(module_suffix):
|
||||
base_name = module[:-4]
|
||||
part = base_name + '.' + part_type
|
||||
if part in targets:
|
||||
#
|
||||
# make a file to use the module
|
||||
#
|
||||
part_maker_name = part_type + ".scad"
|
||||
with open(part_maker_name, "w") as f:
|
||||
f.write("use <%s/%s>\n" % (dir, filename))
|
||||
f.write("%s();\n" % module);
|
||||
#
|
||||
# Run openscad on the created file
|
||||
#
|
||||
part_file = target_dir + "/" + part
|
||||
dname = deps_name(deps_dir, filename)
|
||||
changed = check_deps(mtime(part_file), dname)
|
||||
changed = times.check_have_time(changed, part)
|
||||
if part_type == 'stl' and not changed and not part in bounds_map:
|
||||
changed = "No bounds"
|
||||
if changed:
|
||||
print(changed)
|
||||
t = time.time()
|
||||
openscad.run("-D$bom=1", "-d", dname, "-o", part_file, part_maker_name)
|
||||
times.add_time(part, t)
|
||||
if part_type == 'stl':
|
||||
bounds = c14n_stl.canonicalise(part_file)
|
||||
bounds_map[part] = bounds
|
||||
targets.remove(part)
|
||||
os.remove(part_maker_name)
|
||||
#
|
||||
# Add the files on the BOM to the used list for plates.py
|
||||
#
|
||||
for line in open("openscad.log"):
|
||||
if line[:7] == 'ECHO: "' and line[-6:] == '.' + part_type + '"\n':
|
||||
used.append(line[7:-2])
|
||||
for dir in source_dirs(bom_dir):
|
||||
if targets and os.path.isdir(dir):
|
||||
for filename in os.listdir(dir):
|
||||
if targets and filename[-5:] == ".scad":
|
||||
#
|
||||
# find any modules ending in _<part_type>
|
||||
#
|
||||
with open(dir + "/" + filename, "r") as f:
|
||||
for line in f.readlines():
|
||||
words = line.split()
|
||||
if(len(words) and words[0] == "module"):
|
||||
module = words[1].split('(')[0]
|
||||
if module.endswith(module_suffix):
|
||||
base_name = module[:-4]
|
||||
part = base_name + '.' + part_type
|
||||
if part in targets:
|
||||
#
|
||||
# Run openscad on the created file
|
||||
#
|
||||
part_file = target_dir + "/" + part
|
||||
dname = deps_name(deps_dir, filename)
|
||||
changed = check_deps(part_file, dname)
|
||||
changed = times.check_have_time(changed, part)
|
||||
if part_type == 'stl' and not changed and not part in bounds_map:
|
||||
changed = "No bounds"
|
||||
if changed:
|
||||
print(changed)
|
||||
#
|
||||
# make a file to use the module
|
||||
#
|
||||
part_maker_name = part_type + ".scad"
|
||||
with open(part_maker_name, "w") as f:
|
||||
f.write("use <%s/%s>\n" % (dir, filename))
|
||||
f.write("%s();\n" % module);
|
||||
t = time.time()
|
||||
openscad.run("-D$bom=1", "-d", dname, "-o", part_file, part_maker_name)
|
||||
times.add_time(part, t)
|
||||
if part_type == 'stl':
|
||||
bounds = c14n_stl.canonicalise(part_file)
|
||||
bounds_map[part] = bounds
|
||||
os.remove(part_maker_name)
|
||||
targets.remove(part)
|
||||
#
|
||||
# Write new bounds file
|
||||
#
|
||||
@@ -144,10 +148,6 @@ def make_parts(target, part_type, parts = None):
|
||||
#
|
||||
if targets:
|
||||
for part in targets:
|
||||
if part[-4:] != '.' + part_type:
|
||||
print(part, "is not a", part_type, "file")
|
||||
else:
|
||||
print("Could not find a module called", part[:-4] + module_suffix, "to make", part)
|
||||
sys.exit(1)
|
||||
print("Could not find a module called", part[:-4] + module_suffix, "to make", part)
|
||||
usage(part_type)
|
||||
times.print_times()
|
||||
return used
|
||||
|
8
scripts/gallery.py
Normal file → Executable file
@@ -30,6 +30,7 @@ import re
|
||||
from shutil import copyfile
|
||||
from tests import update_image
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
project_dirs = ['../..', 'examples']
|
||||
target_dir = 'gallery'
|
||||
@@ -39,7 +40,6 @@ def gallery(force):
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
|
||||
|
||||
paths = sorted([pdir + '/' + i for pdir in project_dirs for i in os.listdir(pdir) if os.path.isdir(pdir + '/' + i + '/assemblies')], key = lambda s: os.path.basename(s))
|
||||
with open(output_name, 'wt') as output_file:
|
||||
print("# A gallery of projects made with NopSCADlib", file = output_file)
|
||||
@@ -78,4 +78,8 @@ def gallery(force):
|
||||
|
||||
if __name__ == '__main__':
|
||||
init()
|
||||
gallery(force = len(sys.argv) > 1 and sys.argv[1] == '-f')
|
||||
parser = argparse.ArgumentParser(description='Creates a galley of projects by copying the top level image and description to gallery/readme.md.')
|
||||
parser.add_argument("-f", help = "run make_all in each project to force update", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
gallery(force = args.f)
|
||||
|
13
scripts/make_all.py
Normal file → Executable file
@@ -18,7 +18,7 @@
|
||||
# You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
#! Generates all the files for a project by running ```bom.py```, ```stls.py```, ```dxfs.py```, ```render.py``` and ```views.py```.
|
||||
#! Generates all the files for a project by running `bom.py`, `stls.py`, `dxfs.py`, `render.py` and `views.py`.
|
||||
|
||||
import sys
|
||||
|
||||
@@ -26,11 +26,22 @@ from exports import make_parts
|
||||
from bom import boms
|
||||
from render import render
|
||||
from views import views
|
||||
from plateup import plateup
|
||||
from set_config import set_config
|
||||
|
||||
|
||||
def usage():
|
||||
print("\nusage:\n\tmake_all [target_config] - Make all the manufacturing files and readme for a project.")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 2: usage()
|
||||
target = None if len(sys.argv) == 1 else sys.argv[1]
|
||||
set_config(target, usage)
|
||||
boms(target)
|
||||
for part in ['stl', 'dxf']:
|
||||
make_parts(target, part)
|
||||
render(target, part)
|
||||
plateup(target, part)
|
||||
|
||||
views(target)
|
||||
|
@@ -24,15 +24,25 @@ from __future__ import print_function
|
||||
|
||||
import subprocess, sys
|
||||
|
||||
def run(*args):
|
||||
cmd = ["openscad"] + list(args)
|
||||
for arg in cmd:
|
||||
print(arg, end=" ")
|
||||
print()
|
||||
def run_list(args, silent = False, verbose = False):
|
||||
cmd = ["openscad.exe"] + args
|
||||
if not silent:
|
||||
for arg in cmd:
|
||||
print(arg, end=" ")
|
||||
print()
|
||||
with open("openscad.log", "w") as log:
|
||||
rc = subprocess.call(cmd, stdout = log, stderr = log)
|
||||
for line in open("openscad.log", "rt"):
|
||||
if 'ERROR:' in line or 'WARNING:' in line:
|
||||
if verbose or 'ERROR:' in line or 'WARNING:' in line:
|
||||
print(line[:-1])
|
||||
if rc:
|
||||
sys.exit(rc)
|
||||
|
||||
def run(*args):
|
||||
run_list(list(args), False)
|
||||
|
||||
def run_silent(*args):
|
||||
run_list(list(args), True);
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_list(sys.argv[1:], True, True)
|
||||
|
49
scripts/options.py
Normal file
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# NopSCADlib Copyright Chris Palmer 2020
|
||||
# nop.head@gmail.com
|
||||
# hydraraptor.blogspot.com
|
||||
#
|
||||
# This file is part of NopSCADlib.
|
||||
#
|
||||
# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Set command line options from enviroment variables and check if they have changed
|
||||
|
||||
import json, os, deps
|
||||
|
||||
def check_options(dir = '.'):
|
||||
global options, options_mtime
|
||||
options = { "show_threads": str(os.getenv("NOPSCADLIB_SHOW_THREADS")) }
|
||||
options_fname = dir + '/options.json'
|
||||
try:
|
||||
with open(options_fname) as json_file:
|
||||
last_options = json.load(json_file)
|
||||
except:
|
||||
last_options = {}
|
||||
if last_options != options:
|
||||
with open(options_fname, 'w') as outfile:
|
||||
json.dump(options, outfile, indent = 4)
|
||||
options_mtime = deps.mtime(options_fname)
|
||||
|
||||
def have_changed(changed, target):
|
||||
if not changed and deps.mtime(target) < options_mtime:
|
||||
return "command line options changed"
|
||||
return changed
|
||||
|
||||
def list():
|
||||
result = []
|
||||
for name in options.keys():
|
||||
value = options[name]
|
||||
if value != 'None':
|
||||
result.append('-D$' + name + '=' + value)
|
||||
return result
|
39
scripts/panels.py
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# NopSCADlib Copyright Chris Palmer 2018
|
||||
# nop.head@gmail.com
|
||||
# hydraraptor.blogspot.com
|
||||
#
|
||||
# This file is part of NopSCADlib.
|
||||
#
|
||||
# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
#! Panelises DXF files so they can be routed together by running scad files found in the `panels` directory.
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
|
||||
from plateup import plateup
|
||||
|
||||
def usage():
|
||||
print("\nusage:\n\tpanels [target_config] - Aggregate DXF files for routing together.")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 2: usage()
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
target = sys.argv[1]
|
||||
else:
|
||||
target = None
|
||||
plateup(target, 'dxf', usage)
|
111
scripts/plateup.py
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# NopSCADlib Copyright Chris Palmer 2018
|
||||
# nop.head@gmail.com
|
||||
# hydraraptor.blogspot.com
|
||||
#
|
||||
# This file is part of NopSCADlib.
|
||||
#
|
||||
# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import openscad
|
||||
import sys
|
||||
import c14n_stl
|
||||
from set_config import *
|
||||
from deps import *
|
||||
from shutil import copyfile
|
||||
import re
|
||||
|
||||
source_dirs = { "stl" : "platters", "dxf" : "panels" }
|
||||
target_dirs = { "stl" : "printed", "dxf" : "routed" }
|
||||
|
||||
def plateup(target, part_type, usage = None):
|
||||
#
|
||||
# Make the target directory
|
||||
#
|
||||
top_dir = set_config(target, usage)
|
||||
parts_dir = top_dir + part_type + 's'
|
||||
target_dir = parts_dir + '/' + target_dirs[part_type]
|
||||
source_dir1 = source_dirs[part_type]
|
||||
source_dir2 = top_dir + source_dirs[part_type]
|
||||
#
|
||||
# Loop through source directories
|
||||
#
|
||||
used = []
|
||||
all_sources = []
|
||||
for dir in [source_dir1, source_dir2]:
|
||||
if not os.path.isdir(dir):
|
||||
continue
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
#
|
||||
# Make the deps dir
|
||||
#
|
||||
deps_dir = dir + "/deps"
|
||||
if not os.path.isdir(deps_dir):
|
||||
os.makedirs(deps_dir)
|
||||
#
|
||||
# Decide which files to make
|
||||
#
|
||||
sources = [file for file in os.listdir(dir) if file.endswith('.scad')]
|
||||
all_sources += sources
|
||||
#
|
||||
# Run OpenSCAD on the source files to make the targets
|
||||
#
|
||||
for src in sources:
|
||||
src_file = dir + '/' + src
|
||||
part_file = target_dir + '/' + src[:-4] + part_type
|
||||
dname = deps_name(deps_dir, src)
|
||||
changed = check_deps(part_file, dname)
|
||||
if changed:
|
||||
print(changed)
|
||||
openscad.run("-D$bom=1", "-d", dname, "-o", part_file, src_file)
|
||||
if part_type == 'stl':
|
||||
c14n_stl.canonicalise(part_file)
|
||||
log_name = 'openscad.log'
|
||||
else:
|
||||
log_name = 'openscad.echo'
|
||||
openscad.run_silent("-D$bom=1", "-o", log_name, src_file)
|
||||
#
|
||||
# Add the files on the BOM to the used list
|
||||
#
|
||||
with open(log_name) as file:
|
||||
for line in file.readlines():
|
||||
match = re.match(r'^ECHO: "~(.*?\.' + part_type + r').*"$', line)
|
||||
if match:
|
||||
used.append(match.group(1))
|
||||
copied = []
|
||||
if all_sources:
|
||||
#
|
||||
# Copy files that are not included
|
||||
#
|
||||
for file in os.listdir(parts_dir):
|
||||
if file.endswith('.' + part_type) and not file in used:
|
||||
src = parts_dir + '/' + file
|
||||
dst = target_dir + '/' + file
|
||||
if mtime(src) > mtime(dst):
|
||||
print("Copying %s to %s" % (src, dst))
|
||||
copyfile(src, dst)
|
||||
copied.append(file)
|
||||
#
|
||||
# Remove any cruft
|
||||
#
|
||||
targets = [file[:-4] + part_type for file in all_sources]
|
||||
for file in os.listdir(target_dir):
|
||||
if file.endswith('.' + part_type):
|
||||
if not file in targets and not file in copied:
|
||||
print("Removing %s" % file)
|
||||
os.remove(target_dir + '/' + file)
|
39
scripts/platters.py
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# NopSCADlib Copyright Chris Palmer 2018
|
||||
# nop.head@gmail.com
|
||||
# hydraraptor.blogspot.com
|
||||
#
|
||||
# This file is part of NopSCADlib.
|
||||
#
|
||||
# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
#! Generates build plates of STL files for efficient printing by running scad files found in the `platters` directory.
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
|
||||
from plateup import plateup
|
||||
|
||||
def usage():
|
||||
print("\nusage:\n\tplatters [target_config] - Aggregate STL files for printing together.")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 2: usage()
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
target = sys.argv[1]
|
||||
else:
|
||||
target = None
|
||||
plateup(target, 'stl', usage)
|
@@ -1,20 +1,24 @@
|
||||
|
||||
# Python scripts
|
||||
These are located in the ```scripts``` subdirectory, which needs to be added to the program search path.
|
||||
These are located in the `scripts` subdirectory, which needs to be added to the program search path.
|
||||
|
||||
They should work with both Python 2 and Python 3.
|
||||
|
||||
| Script | Function |
|
||||
|:--|:--|
|
||||
| ```bom.py``` | Generates BOM files for the project. |
|
||||
| ```c14n_stl.py``` | OpenSCAD produces randomly ordered STL files. This script re-orders them consistently so that GIT can tell if they have changed or not. |
|
||||
| ```doc_scripts.py``` | Makes this document and doc/usage.md. |
|
||||
| ```dxfs.py``` | Generates DXF files for all the routed parts listed on the BOM or a specified list. |
|
||||
| ```gallery.py``` | Finds projects and adds them to the gallery. |
|
||||
| ```make_all.py``` | Generates all the files for a project by running ```bom.py```, ```stls.py```, ```dxfs.py```, ```render.py``` and ```views.py```. |
|
||||
| ```render.py``` | Renders STL and DXF files to PNG for inclusion in the build instructions. |
|
||||
| ```set_config.py``` | Sets the target configuration for multi-target projects that have variable configurations. |
|
||||
| ```stls.py``` | Generates STL files for all the printed parts listed on the BOM or a specified list. |
|
||||
| ```svgs.py``` | Generates SVG files for all the routed parts listed on the BOM or a specified list. |
|
||||
| ```tests.py``` | Runs all the tests in the tests directory and makes the readme file with a catalog of the results. |
|
||||
| ```views.py``` | Generates exploded and unexploded assembly views and scrapes build instructions to make readme.md, readme.html and printme.html files for the project. |
|
||||
|:---|:---|
|
||||
| `bom.py` | Generates BOM files for the project. |
|
||||
| `c14n_stl.py` | OpenSCAD produces randomly ordered STL files. This script re-orders them consistently so that GIT can tell if they have changed or not. |
|
||||
| `doc_scripts.py` | Makes this document and doc/usage.md. |
|
||||
| `dxf.py` | |
|
||||
| `dxf2pdf.py` | |
|
||||
| `dxfs.py` | Generates DXF files for all the routed parts listed on the BOM or a specified list. |
|
||||
| `gallery.py` | Finds projects and adds them to the gallery. |
|
||||
| `make_all.py` | Generates all the files for a project by running `bom.py`, `stls.py`, `dxfs.py`, `render.py` and `views.py`. |
|
||||
| `panels.py` | Panelises DXF files so they can be routed together by running scad files found in the `panels` directory. |
|
||||
| `platters.py` | Generates build plates of STL files for efficient printing by running scad files found in the `platters` directory. |
|
||||
| `render.py` | Renders STL and DXF files to PNG for inclusion in the build instructions. |
|
||||
| `set_config.py` | Sets the target configuration for multi-target projects that have variable configurations. |
|
||||
| `stls.py` | Generates STL files for all the printed parts listed on the BOM or a specified list. |
|
||||
| `svgs.py` | Generates SVG files for all the routed parts listed on the BOM or a specified list. |
|
||||
| `tests.py` | Runs all the tests in the tests directory and makes the readme file with a catalog of the results. |
|
||||
| `views.py` | Generates exploded and unexploded assembly views and scrapes build instructions to make readme.md, readme.html and printme.html files for the project. |
|
||||
|
40
scripts/render.py
Normal file → Executable file
@@ -29,18 +29,39 @@ import openscad
|
||||
from tests import do_cmd, update_image, colour_scheme, background
|
||||
from deps import mtime
|
||||
from colorama import init
|
||||
import json
|
||||
|
||||
def usage():
|
||||
print("\nusage:\n\trender [target_config] - Render images of the stl and dxf files.");
|
||||
sys.exit(1)
|
||||
|
||||
def render(target, type):
|
||||
#
|
||||
# Make the target directory
|
||||
#
|
||||
target_dir = set_config(target) + type + 's'
|
||||
top_dir = set_config(target, usage)
|
||||
target_dir = top_dir + type + 's'
|
||||
bom_dir = top_dir + 'bom'
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
#
|
||||
# Find all the parts
|
||||
#
|
||||
parts = bom_to_parts(target_dir, type)
|
||||
parts = bom_to_parts(bom_dir, type)
|
||||
#
|
||||
# Read the json bom to get the colours
|
||||
#
|
||||
bom_file = bom_dir + "/bom.json"
|
||||
with open(bom_file) as json_file:
|
||||
flat_bom = json.load(json_file)
|
||||
|
||||
things = { 'stl' : 'printed', 'dxf' : 'routed' }[type]
|
||||
colours = {}
|
||||
for ass in flat_bom:
|
||||
for part in ass[things]:
|
||||
obj = ass[things][part]
|
||||
if "colour" in obj:
|
||||
colours[part] = obj["colour"]
|
||||
#
|
||||
# Remove unused png files
|
||||
#
|
||||
@@ -49,7 +70,9 @@ def render(target, type):
|
||||
if not file[:-4] + '.' + type in parts:
|
||||
print("Removing %s" % file)
|
||||
os.remove(target_dir + '/' + file)
|
||||
|
||||
#
|
||||
# Render the parts
|
||||
#
|
||||
for part in parts:
|
||||
part_file = target_dir + '/' + part
|
||||
png_name = target_dir + '/' + part[:-4] + '.png'
|
||||
@@ -58,10 +81,16 @@ def render(target, type):
|
||||
#
|
||||
if mtime(part_file) > mtime(png_name):
|
||||
png_maker_name = "png.scad"
|
||||
pp1 = [0, 146/255, 0]
|
||||
colour = pp1
|
||||
if part in colours:
|
||||
colour = colours[part]
|
||||
if not '[' in colour:
|
||||
colour = '"' + colour + '"'
|
||||
with open(png_maker_name, "w") as f:
|
||||
f.write('color("lime") import("%s");\n' % part_file)
|
||||
f.write('color(%s) import("%s");\n' % (colour, part_file))
|
||||
cam = "--camera=0,0,0,70,0,315,500" if type == 'stl' else "--camera=0,0,0,0,0,0,500"
|
||||
render = "--preview" if type == 'stl' else "--render"
|
||||
render = "--preview" if type == 'stl' or colour != pp1 else "--render"
|
||||
tmp_name = 'tmp.png'
|
||||
openscad.run(colour_scheme, "--projection=p", "--imgsize=4096,4096", cam, render, "--autocenter", "--viewall", "-o", tmp_name, png_maker_name);
|
||||
do_cmd(("magick "+ tmp_name + " -trim -resize 280x280 -background %s -gravity Center -extent 280x280 -bordercolor %s -border 10 %s"
|
||||
@@ -71,6 +100,7 @@ def render(target, type):
|
||||
|
||||
if __name__ == '__main__':
|
||||
init()
|
||||
if len(sys.argv) > 2: usage()
|
||||
target = sys.argv[1] if len(sys.argv) > 1 else None
|
||||
render(target, 'stl')
|
||||
render(target, 'dxf')
|
||||
|
18
scripts/set_config.py
Normal file → Executable file
@@ -45,20 +45,27 @@ def valid_targets_string():
|
||||
return result
|
||||
|
||||
|
||||
def set_config(target):
|
||||
def set_config(target, usage = None):
|
||||
if target and target[:1] == '-' and usage: usage()
|
||||
targets = valid_targets()
|
||||
if not target:
|
||||
if not targets:
|
||||
return ""
|
||||
print("Must specify a configuration: " + valid_targets_string())
|
||||
if usage:
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
if not targets:
|
||||
print("Not a muli-configuration project (no config_<target>.scad files found)")
|
||||
if usage:
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
if not target in targets:
|
||||
print(target + " is not a configuration, avaliable configurations are: " + valid_targets_string())
|
||||
if usage:
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
fname = source_dir + "/target.scad"
|
||||
@@ -75,10 +82,13 @@ def set_config(target):
|
||||
f. write(text);
|
||||
return target + "/"
|
||||
|
||||
def usage():
|
||||
print("\nusage:\n\tset_config config_name")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = len(sys.argv)
|
||||
if args == 2:
|
||||
set_config(sys.argv[1])
|
||||
set_config(sys.argv[1], usage)
|
||||
else:
|
||||
print("usage: set_config config_name")
|
||||
sys.exit(1)
|
||||
usage()
|
||||
|
0
scripts/stls.py
Normal file → Executable file
129
scripts/tests.py
Normal file → Executable file
@@ -27,6 +27,7 @@ import openscad
|
||||
import subprocess
|
||||
import bom
|
||||
import times
|
||||
import options
|
||||
import time
|
||||
import json
|
||||
import shutil
|
||||
@@ -84,6 +85,10 @@ def depluralise(name):
|
||||
def is_plural(name):
|
||||
return name != depluralise(name)
|
||||
|
||||
def usage():
|
||||
print("\nusage:\n\ttests [test_name1] ... [test_nameN] - Run specified tests or all tests in none specified.");
|
||||
sys.exit(1)
|
||||
|
||||
def tests(tests):
|
||||
scad_dir = "tests"
|
||||
deps_dir = scad_dir + "/deps"
|
||||
@@ -92,28 +97,45 @@ def tests(tests):
|
||||
for dir in [deps_dir, png_dir, bom_dir]:
|
||||
if not os.path.isdir(dir):
|
||||
os.makedirs(dir)
|
||||
doc_name = "readme.md"
|
||||
index = {}
|
||||
bodies = {}
|
||||
done = []
|
||||
times.read_times()
|
||||
options.check_options(deps_dir)
|
||||
#
|
||||
# Make cover pic if does not exist as very slow. Delete it to force an update.
|
||||
#
|
||||
png_name = "libtest.png"
|
||||
scad_name = "libtest.scad"
|
||||
if not os.path.isfile(png_name):
|
||||
openscad.run(colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall", "-o", png_name, scad_name);
|
||||
do_cmd(["magick", png_name, "-trim", "-resize", "1280", "-bordercolor", background, "-border", "10", png_name])
|
||||
if os.path.isfile(scad_name):
|
||||
libtest = True
|
||||
lib_blurb = scrape_blurb(scad_name)
|
||||
if not os.path.isfile(png_name):
|
||||
openscad.run(colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall", "-o", png_name, scad_name);
|
||||
do_cmd(["magick", png_name, "-trim", "-resize", "1280", "-bordercolor", background, "-border", "10", png_name])
|
||||
else:
|
||||
#
|
||||
# Project tests so just a title
|
||||
#
|
||||
libtest = False
|
||||
project = ' '.join(word[0].upper() + word[1:] for word in os.path.basename(os.getcwd()).split('_'))
|
||||
lib_blurb = '#' + project + ' Tests\n'
|
||||
|
||||
doc_base_name = "readme" if libtest else "tests"
|
||||
doc_name = doc_base_name + ".md"
|
||||
#
|
||||
# List of individual part files
|
||||
#
|
||||
scads = [i for i in os.listdir(scad_dir) if i[-5:] == ".scad"]
|
||||
|
||||
scads = [i for i in sorted(os.listdir(scad_dir), key = lambda s: s.lower()) if i[-5:] == ".scad"]
|
||||
types = []
|
||||
for scad in scads:
|
||||
base_name = scad[:-5]
|
||||
if not tests or base_name in tests:
|
||||
print(base_name)
|
||||
done.append(base_name)
|
||||
print('\n'+base_name)
|
||||
cap_name = base_name[0].capitalize() + base_name[1:]
|
||||
base_name = base_name.lower()
|
||||
scad_name = scad_dir + '/' + scad
|
||||
png_name = png_dir + '/' + base_name + '.png'
|
||||
bom_name = bom_dir + '/' + base_name + '.json'
|
||||
@@ -123,26 +145,42 @@ def tests(tests):
|
||||
if is_plural(base_name) and os.path.isfile(vits_name):
|
||||
objects_name = vits_name
|
||||
|
||||
locations = [
|
||||
('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins'),
|
||||
('printed/' + base_name + '.scad', 'Printed'),
|
||||
('utils/' + base_name + '.scad', 'Utilities'),
|
||||
('utils/core/' + base_name + '.scad', 'Core Utilities'),
|
||||
]
|
||||
locations = []
|
||||
if os.path.isdir('vitamins'):
|
||||
locations.append(('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins'))
|
||||
if os.path.isdir('printed'):
|
||||
locations.append(('printed/' + base_name + '.scad', 'Printed'))
|
||||
if os.path.isdir('utils'):
|
||||
locations.append(('utils/' + base_name + '.scad', 'Utilities'))
|
||||
if libtest and os.path.isdir('utils/core'):
|
||||
locations.append(('utils/core/' + base_name + '.scad', 'Core Utilities'))
|
||||
|
||||
for name, type in locations:
|
||||
if os.path.isfile(name):
|
||||
impl_name = name
|
||||
break
|
||||
else:
|
||||
print("Can't find implementation!")
|
||||
continue
|
||||
if libtest:
|
||||
print("Can't find implementation!")
|
||||
continue
|
||||
else:
|
||||
type = 'Tests' # OK when testing part of a project
|
||||
impl_name = None
|
||||
|
||||
vsplit = "N"
|
||||
vtype = locations[0][1]
|
||||
types = [vtype + ' A-' + vsplit[0], vtype + ' ' + chr(ord(vsplit) + 1) + '-Z'] + [loc[1] for loc in locations[1 :]]
|
||||
if type == vtype:
|
||||
type = types[0] if cap_name[0] <= vsplit else types[1]
|
||||
if libtest:
|
||||
vsplit = "AJR" + chr(ord('Z') + 1)
|
||||
vtype = locations[0][1]
|
||||
types = [vtype + ' ' + vsplit[i] + '-' + chr(ord(vsplit[i + 1]) - 1) for i in range(len(vsplit) - 1)] + [loc[1] for loc in locations[1 :]]
|
||||
if type == vtype:
|
||||
for i in range(1, len(vsplit)):
|
||||
if cap_name[0] < vsplit[i]:
|
||||
type = types[i - 1]
|
||||
break
|
||||
else:
|
||||
if not types:
|
||||
types = [loc[1] for loc in locations] # No need to split up the vitamin list
|
||||
if not type in types: # Will happen when implementation is not found and type is set to Tests
|
||||
types.append(type)
|
||||
|
||||
for t in types:
|
||||
if not t in bodies:
|
||||
@@ -181,20 +219,21 @@ def tests(tests):
|
||||
if things:
|
||||
body += ['### %s\n| %s | Description |\n|:--- |:--- |' % (thing.title(), heading)]
|
||||
for item in sorted(things):
|
||||
body += ['| ```%s``` | %s |' % (item, things[item])]
|
||||
body += ['| `%s` | %s |' % (item, things[item])]
|
||||
body += ['']
|
||||
|
||||
body += ["\n" %(base_name, png_name)]
|
||||
|
||||
dname = deps_name(deps_dir, scad)
|
||||
oldest = min(mtime(png_name), mtime(bom_name))
|
||||
dname = deps_name(deps_dir, scad.lower())
|
||||
oldest = png_name if mtime(png_name) < mtime(bom_name) else bom_name
|
||||
changed = check_deps(oldest, dname)
|
||||
changed = times.check_have_time(changed, scad_name)
|
||||
changed = options.have_changed(changed, oldest)
|
||||
if changed:
|
||||
print(changed)
|
||||
t = time.time()
|
||||
tmp_name = 'tmp.png'
|
||||
openscad.run("-D", "$bom=2", colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,70,0,315,500", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, scad_name);
|
||||
openscad.run_list(options.list() + ["-D$bom=2", colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,70,0,315,500", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, scad_name]);
|
||||
times.add_time(scad_name, t)
|
||||
do_cmd(["magick", tmp_name, "-trim", "-resize", "1000x600", "-bordercolor", background, "-border", "10", tmp_name])
|
||||
update_image(tmp_name, png_name)
|
||||
@@ -213,39 +252,31 @@ def tests(tests):
|
||||
desc = ''
|
||||
if thing == "vitamins":
|
||||
vit = item.split(':')
|
||||
name = '```' + vit[0] + '```' if vit[0] else ''
|
||||
name = '`' + vit[0] + '`' if vit[0] else ''
|
||||
while '[[' in name and ']]' in name:
|
||||
i = name.find('[[')
|
||||
j = name.find(']]') + 2
|
||||
name = name.replace(name[i : j], '[ ... ]')
|
||||
desc = vit[1]
|
||||
body += ['| %3d | %s | %s |' % (things[item], name, desc)]
|
||||
body += ['| %3d | %s | %s |' % (things[item]["count"], name, desc)]
|
||||
else:
|
||||
body += ['| %3d | %s |' % (things[item], name)]
|
||||
count = things[item] if thing == 'assemblies' else things[item]["count"]
|
||||
body += ['| %3d | %s |' % (count, name)]
|
||||
body += ['']
|
||||
|
||||
body += ['\n<a href="#top">Top</a>']
|
||||
body += ["\n---"]
|
||||
|
||||
for test in done:
|
||||
if test in tests:
|
||||
tests.remove(test)
|
||||
if tests:
|
||||
for test in tests:
|
||||
print(Fore.MAGENTA + "Could not find a test called", test, Fore.WHITE)
|
||||
usage()
|
||||
|
||||
with open(doc_name, "wt") as doc_file:
|
||||
print('# NopSCADlib', file = doc_file)
|
||||
print('''\
|
||||
An ever expanding library of parts modelled in OpenSCAD useful for 3D printers and enclosures for electronics, etc.
|
||||
|
||||
It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and
|
||||
some utilities. There are also Python scripts to generate Bills of Materials (BOMs),
|
||||
STL files for all the printed parts, DXF files for CNC routed parts in a project and a manual containing assembly
|
||||
instructions and exploded views by scraping markdown embedded in OpenSCAD comments, [see scripts](scripts/readme.md). A simple example project can be found [here](examples/MainsBreakOutBox/readme.md).
|
||||
|
||||
For more examples of what it can make see the [gallery](gallery/readme.md).
|
||||
|
||||
The license is GNU General Public License v3.0, see [COPYING](COPYING).
|
||||
|
||||
See [usage](docs/usage.md) for requirements, installation instructions and a usage guide.
|
||||
|
||||
<img src="libtest.png" width="100%"/>\n
|
||||
''', file = doc_file)
|
||||
|
||||
print(lib_blurb, file = doc_file)
|
||||
print('## Table of Contents<a name="top"/>', file = doc_file)
|
||||
print('<table><tr>', file = doc_file)
|
||||
n = 0
|
||||
@@ -257,7 +288,7 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa
|
||||
print('<tr>', file = doc_file, end = '')
|
||||
for type in types:
|
||||
if i < len(index[type]):
|
||||
name = index[type][i]
|
||||
name = sorted(index[type])[i]
|
||||
print('<td> <a href = "#' + name + '">' + name + '</a> </td>', file = doc_file, end = '')
|
||||
else:
|
||||
print('<td></td>', file = doc_file, end = '')
|
||||
@@ -266,10 +297,12 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa
|
||||
for type in types:
|
||||
for line in bodies[type]:
|
||||
print(line, file = doc_file)
|
||||
with open("readme.html", "wt") as html_file:
|
||||
do_cmd("python -m markdown -x tables readme.md".split(), html_file)
|
||||
with open(doc_base_name + ".html", "wt") as html_file:
|
||||
do_cmd(("python -m markdown -x tables " + doc_name).split(), html_file)
|
||||
times.print_times()
|
||||
do_cmd('codespell -L od readme.md'.split())
|
||||
do_cmd(('codespell -L od ' + doc_name).split())
|
||||
|
||||
if __name__ == '__main__':
|
||||
for arg in sys.argv[1:]:
|
||||
if arg[:1] == '-': usage()
|
||||
tests(sys.argv[1:])
|
||||
|
@@ -48,6 +48,8 @@ def check_have_time(changed, name):
|
||||
return changed
|
||||
|
||||
def add_time(name, start):
|
||||
if name.lower() in times:
|
||||
del times[name.lower()]
|
||||
times[name] = round(time.time() - start, 3)
|
||||
|
||||
def print_times():
|
||||
|
273
scripts/views.py
Normal file → Executable file
@@ -28,6 +28,7 @@ import openscad
|
||||
from tests import do_cmd, update_image, colour_scheme, background
|
||||
import time
|
||||
import times
|
||||
import options
|
||||
from deps import *
|
||||
import os
|
||||
import json
|
||||
@@ -51,28 +52,30 @@ def bom_to_assemblies(bom_dir, bounds_map):
|
||||
# Decide if we need big or small assembly pictures
|
||||
#
|
||||
for bom in flat_bom:
|
||||
big = False
|
||||
for ass in bom["assemblies"]:
|
||||
for b in flat_bom:
|
||||
if b["name"] == ass:
|
||||
if b["big"]:
|
||||
if bom["big"] == None:
|
||||
big = False
|
||||
for ass in bom["assemblies"]:
|
||||
for b in flat_bom:
|
||||
if b["name"] == ass:
|
||||
if b["big"]:
|
||||
big = True
|
||||
break
|
||||
if not big:
|
||||
for stl in bom["printed"]:
|
||||
bounds = bounds_map[stl]
|
||||
width = bounds[1][0] - bounds[0][0]
|
||||
depth = bounds[1][1] - bounds[0][1]
|
||||
if max(width, depth) > 80:
|
||||
big = True
|
||||
break
|
||||
if not big:
|
||||
for stl in bom["printed"]:
|
||||
bounds = bounds_map[stl]
|
||||
width = bounds[1][0] - bounds[0][0]
|
||||
depth = bounds[1][1] - bounds[0][1]
|
||||
if max(width, depth) > 80:
|
||||
big = True
|
||||
break
|
||||
bom["big"] = big or bom["routed"]
|
||||
break
|
||||
bom["big"] = big or bom["routed"]
|
||||
#
|
||||
# Remove the main assembly if it is a shell
|
||||
#
|
||||
ass = flat_bom[-1]
|
||||
if len(ass["assemblies"]) < 2 and not ass["vitamins"] and not ass["printed"] and not ass["routed"]:
|
||||
flat_bom = flat_bom[:-1]
|
||||
if flat_bom:
|
||||
ass = flat_bom[-1]
|
||||
if len(ass["assemblies"]) < 2 and not ass["vitamins"] and not ass["printed"] and not ass["routed"]:
|
||||
flat_bom = flat_bom[:-1]
|
||||
return [assembly["name"] for assembly in flat_bom]
|
||||
|
||||
def eop(print_mode, doc_file, last = False, first = False):
|
||||
@@ -87,12 +90,24 @@ def eop(print_mode, doc_file, last = False, first = False):
|
||||
def pad(s, before, after = 0):
|
||||
return ' ' * before + str(s) + ' ' * after
|
||||
|
||||
def titalise(name):
|
||||
cap_next = True
|
||||
result = ''
|
||||
for c in name.replace('_', ' '):
|
||||
result = result + (c.upper() if cap_next else c);
|
||||
cap_next = c == ' '
|
||||
return result
|
||||
|
||||
def usage():
|
||||
print("\nusage:\n\t views [target_config] [<name1>_assembly] ... [<nameN>_assembly] - Create assembly images and readme.")
|
||||
sys.exit(1)
|
||||
|
||||
def views(target, do_assemblies = None):
|
||||
done_assemblies = []
|
||||
#
|
||||
# Make the target directory
|
||||
#
|
||||
top_dir = set_config(target)
|
||||
top_dir = set_config(target, usage)
|
||||
target_dir = top_dir + 'assemblies'
|
||||
deps_dir = top_dir + "deps"
|
||||
bom_dir = top_dir + "bom"
|
||||
@@ -102,13 +117,15 @@ def views(target, do_assemblies = None):
|
||||
os.makedirs(deps_dir)
|
||||
|
||||
times.read_times(target_dir)
|
||||
options.check_options(deps_dir)
|
||||
bounds_fname = top_dir + 'stls/bounds.json'
|
||||
with open(bounds_fname) as json_file:
|
||||
bounds_map = json.load(json_file)
|
||||
#
|
||||
# Find all the assemblies
|
||||
# Find all the assemblies and remove any old views
|
||||
#
|
||||
assemblies = bom_to_assemblies(bom_dir, bounds_map)
|
||||
lc_assemblies = [ass.lower() for ass in assemblies]
|
||||
for file in os.listdir(target_dir):
|
||||
if file.endswith('.png'):
|
||||
assembly = file[:-4].replace('_assembled', '_assembly')
|
||||
@@ -121,66 +138,69 @@ def views(target, do_assemblies = None):
|
||||
# Find all the scad files
|
||||
#
|
||||
main_blurb = None
|
||||
lib_dir = os.environ['OPENSCADPATH'] + '/NopSCADlib/printed'
|
||||
for dir in [source_dir, lib_dir]:
|
||||
for filename in os.listdir(dir):
|
||||
if filename.endswith('.scad'):
|
||||
#
|
||||
# find any modules with names ending in _assembly
|
||||
#
|
||||
with open(dir + "/" + filename, "r") as f:
|
||||
lines = f.readlines()
|
||||
line_no = 0
|
||||
for line in lines:
|
||||
words = line.split()
|
||||
if len(words) and words[0] == "module":
|
||||
module = words[1].split('(')[0]
|
||||
if is_assembly(module):
|
||||
if module in assemblies:
|
||||
#
|
||||
# Scrape the assembly instructions
|
||||
#
|
||||
for ass in flat_bom:
|
||||
if ass["name"] == module:
|
||||
if not "blurb" in ass:
|
||||
ass["blurb"] = blurb.scrape_module_blurb(lines[:line_no])
|
||||
break
|
||||
if not do_assemblies or module in do_assemblies:
|
||||
for dir in source_dirs(bom_dir):
|
||||
if os.path.isdir(dir):
|
||||
for filename in os.listdir(dir):
|
||||
if filename.endswith('.scad'):
|
||||
#
|
||||
# find any modules with names ending in _assembly
|
||||
#
|
||||
with open(dir + "/" + filename, "r") as f:
|
||||
lines = f.readlines()
|
||||
line_no = 0
|
||||
for line in lines:
|
||||
words = line.split()
|
||||
if len(words) and words[0] == "module":
|
||||
module = words[1].split('(')[0]
|
||||
if is_assembly(module):
|
||||
lc_module = module.lower()
|
||||
if lc_module in lc_assemblies:
|
||||
real_name = assemblies[lc_assemblies.index(lc_module)]
|
||||
#
|
||||
# make a file to use the module
|
||||
# Scrape the assembly instructions
|
||||
#
|
||||
png_maker_name = 'png.scad'
|
||||
with open(png_maker_name, "w") as f:
|
||||
f.write("use <%s/%s>\n" % (dir, filename))
|
||||
f.write("%s();\n" % module);
|
||||
#
|
||||
# Run openscad on the created file
|
||||
#
|
||||
dname = deps_name(deps_dir, filename)
|
||||
for explode in [0, 1]:
|
||||
png_name = target_dir + '/' + module + '.png'
|
||||
if not explode:
|
||||
png_name = png_name.replace('_assembly', '_assembled')
|
||||
changed = check_deps(mtime(png_name), dname)
|
||||
changed = times.check_have_time(changed, png_name)
|
||||
tmp_name = 'tmp.png'
|
||||
if changed:
|
||||
print(changed)
|
||||
t = time.time()
|
||||
openscad.run("-D$pose=1", "-D$explode=%d" % explode, colour_scheme, "--projection=p", "--imgsize=4096,4096", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, png_maker_name);
|
||||
times.add_time(png_name, t)
|
||||
do_cmd(["magick", tmp_name, "-trim", "-resize", "1004x1004", "-bordercolor", background, "-border", "10", tmp_name])
|
||||
update_image(tmp_name, png_name)
|
||||
tn_name = png_name.replace('.png', '_tn.png')
|
||||
if mtime(png_name) > mtime(tn_name):
|
||||
do_cmd(("magick "+ png_name + " -trim -resize 280x280 -background " + background + " -gravity Center -extent 280x280 -bordercolor " + background + " -border 10 " + tmp_name).split())
|
||||
update_image(tmp_name, tn_name)
|
||||
os.remove(png_maker_name)
|
||||
done_assemblies.append(module)
|
||||
else:
|
||||
if module == 'main_assembly':
|
||||
main_blurb = blurb.scrape_module_blurb(lines[:line_no])
|
||||
line_no += 1
|
||||
for ass in flat_bom:
|
||||
if ass["name"] == real_name:
|
||||
if not "blurb" in ass:
|
||||
ass["blurb"] = blurb.scrape_module_blurb(lines[:line_no])
|
||||
break
|
||||
if not do_assemblies or real_name in do_assemblies:
|
||||
#
|
||||
# Run openscad on the created file
|
||||
#
|
||||
dname = deps_name(deps_dir, filename)
|
||||
for explode in [0, 1]:
|
||||
png_name = target_dir + '/' + real_name + '.png'
|
||||
if not explode:
|
||||
png_name = png_name.replace('_assembly', '_assembled')
|
||||
changed = check_deps(png_name, dname)
|
||||
changed = times.check_have_time(changed, png_name)
|
||||
changed = options.have_changed(changed, png_name)
|
||||
tmp_name = 'tmp.png'
|
||||
if changed:
|
||||
print(changed)
|
||||
#
|
||||
# make a file to use the module
|
||||
#
|
||||
png_maker_name = 'png.scad'
|
||||
with open(png_maker_name, "w") as f:
|
||||
f.write("use <%s/%s>\n" % (dir, filename))
|
||||
f.write("%s();\n" % module);
|
||||
t = time.time()
|
||||
openscad.run_list(options.list() + ["-D$pose=1", "-D$explode=%d" % explode, colour_scheme, "--projection=p", "--imgsize=4096,4096", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, png_maker_name]);
|
||||
times.add_time(png_name, t)
|
||||
do_cmd(["magick", tmp_name, "-trim", "-resize", "1004x1004", "-bordercolor", background, "-border", "10", tmp_name])
|
||||
update_image(tmp_name, png_name)
|
||||
os.remove(png_maker_name)
|
||||
tn_name = png_name.replace('.png', '_tn.png')
|
||||
if mtime(png_name) > mtime(tn_name):
|
||||
do_cmd(("magick "+ png_name + " -trim -resize 280x280 -background " + background + " -gravity Center -extent 280x280 -bordercolor " + background + " -border 10 " + tmp_name).split())
|
||||
update_image(tmp_name, tn_name)
|
||||
done_assemblies.append(real_name)
|
||||
else:
|
||||
if module == 'main_assembly':
|
||||
main_blurb = blurb.scrape_module_blurb(lines[:line_no])
|
||||
line_no += 1
|
||||
times.print_times()
|
||||
#
|
||||
# Build the document
|
||||
@@ -211,7 +231,7 @@ def views(target, do_assemblies = None):
|
||||
print('1. [Parts list](#Parts_list)', file = doc_file)
|
||||
for ass in flat_bom:
|
||||
name = ass["name"]
|
||||
cap_name = name.replace('_', ' ').title()
|
||||
cap_name = titalise(name)
|
||||
print('1. [%s](#%s)' % (cap_name, name), file = doc_file)
|
||||
print(file = doc_file)
|
||||
eop(print_mode, doc_file)
|
||||
@@ -219,36 +239,49 @@ def views(target, do_assemblies = None):
|
||||
# Global BOM
|
||||
#
|
||||
print('<a name="Parts_list"></a>\n## Parts list', file = doc_file)
|
||||
vitamins = {}
|
||||
printed = {}
|
||||
routed = {}
|
||||
types = ["vitamins", "printed", "routed"]
|
||||
headings = {"vitamins" : "vitamins", "printed" : "3D printed parts", "routed" : "CNC routed parts"}
|
||||
things = {}
|
||||
for t in types:
|
||||
things[t] = {}
|
||||
for ass in flat_bom:
|
||||
for v in ass["vitamins"]:
|
||||
if v in vitamins:
|
||||
vitamins[v] += ass["vitamins"][v]
|
||||
else:
|
||||
vitamins[v] = ass["vitamins"][v]
|
||||
for p in ass["printed"]:
|
||||
if p in printed:
|
||||
printed[p] += ass["printed"][p]
|
||||
else:
|
||||
printed[p] = ass["printed"][p]
|
||||
for t in types:
|
||||
for thing in ass[t]:
|
||||
if thing in things[t]:
|
||||
things[t][thing] += ass[t][thing]["count"]
|
||||
else:
|
||||
things[t][thing] = ass[t][thing]["count"]
|
||||
for ass in flat_bom:
|
||||
name = ass["name"][:-9].replace('_', ' ').title().replace(' ',' ')
|
||||
name = titalise(ass["name"][:-9]).replace(' ',' ')
|
||||
print('| <span style="writing-mode: vertical-rl; text-orientation: mixed;">%s</span> ' % name, file = doc_file, end = '')
|
||||
print('| <span style="writing-mode: vertical-rl; text-orientation: mixed;">TOTALS</span> | |', file = doc_file)
|
||||
print(('|--:' * len(flat_bom) + '|--:|:--|'), file = doc_file)
|
||||
for v in sorted(vitamins, key = lambda s: s.split(":")[-1]):
|
||||
for ass in flat_bom:
|
||||
count = ass["vitamins"][v] if v in ass["vitamins"] else '.'
|
||||
print('| %s ' % pad(count, 2, 1), file = doc_file, end = '')
|
||||
print('| %s | %s |' % (pad(vitamins[v], 2, 1), pad(v.split(":")[1], 2)), file = doc_file)
|
||||
print(('| ' * len(flat_bom) + '| | **3D Printed parts** |'), file = doc_file)
|
||||
for p in sorted(printed):
|
||||
for ass in flat_bom:
|
||||
count = ass["printed"][p] if p in ass["printed"] else '.'
|
||||
print('| %s ' % pad(count, 2, 1), file = doc_file, end = '')
|
||||
print('| %s | %s |' % (pad(printed[p], 2, 1), pad(p, 3)), file = doc_file)
|
||||
|
||||
print(('|---:' * len(flat_bom) + '|---:|:---|'), file = doc_file)
|
||||
|
||||
for t in types:
|
||||
if things[t]:
|
||||
totals = {}
|
||||
heading = headings[t][0:1].upper() + headings[t][1:]
|
||||
print(('| ' * len(flat_bom) + '| | **%s** |') % heading, file = doc_file)
|
||||
for thing in sorted(things[t], key = lambda s: s.split(":")[-1]):
|
||||
for ass in flat_bom:
|
||||
count = ass[t][thing]["count"] if thing in ass[t] else 0
|
||||
print('| %s ' % pad(count if count else '.', 2, 1), file = doc_file, end = '')
|
||||
name = ass["name"]
|
||||
if name in totals:
|
||||
totals[name] += count
|
||||
else:
|
||||
totals[name] = count
|
||||
print('| %s | %s |' % (pad(things[t][thing], 2, 1), pad(thing.split(":")[-1], 2)), file = doc_file)
|
||||
|
||||
grand_total = 0
|
||||
for ass in flat_bom:
|
||||
name = ass["name"]
|
||||
total = totals[name] if name in totals else 0
|
||||
print('| %s ' % pad(total if total else '.', 2, 1), file = doc_file, end = '')
|
||||
grand_total += total
|
||||
print("| %s | %s |" % (pad(grand_total, 2, 1), pad('Total %s count' % headings[t], 2)), file = doc_file)
|
||||
|
||||
print(file = doc_file)
|
||||
eop(print_mode, doc_file)
|
||||
#
|
||||
@@ -256,7 +289,7 @@ def views(target, do_assemblies = None):
|
||||
#
|
||||
for ass in flat_bom:
|
||||
name = ass["name"]
|
||||
cap_name = name.replace('_', ' ').title()
|
||||
cap_name = titalise(name)
|
||||
|
||||
if ass["count"] > 1:
|
||||
print('<a name="%s"></a>\n## %d x %s' % (name, ass["count"], cap_name), file = doc_file)
|
||||
@@ -266,21 +299,20 @@ def views(target, do_assemblies = None):
|
||||
if vitamins:
|
||||
print("### Vitamins", file = doc_file)
|
||||
print("|Qty|Description|", file = doc_file)
|
||||
print("|--:|:----------|", file = doc_file)
|
||||
for v in vitamins:
|
||||
print("|%d|%s|" % (vitamins[v], v.split(":")[1]), file = doc_file)
|
||||
print("|---:|:----------|", file = doc_file)
|
||||
for v in sorted(vitamins, key = lambda s: s.split(":")[-1]):
|
||||
print("|%d|%s|" % (vitamins[v]["count"], v.split(":")[1]), file = doc_file)
|
||||
print("\n", file = doc_file)
|
||||
|
||||
printed = ass["printed"]
|
||||
if printed:
|
||||
print('### 3D Printed parts', file = doc_file)
|
||||
keys = sorted(list(printed.keys()))
|
||||
for i in range(len(keys)):
|
||||
p = keys[i]
|
||||
print('%s %d x %s |' % ('\n|' if not (i % 3) else '', printed[p], p), file = doc_file, end = '')
|
||||
for i, p in enumerate(keys):
|
||||
print('%s %d x %s |' % ('\n|' if not (i % 3) else '', printed[p]["count"], p), file = doc_file, end = '')
|
||||
if (i % 3) == 2 or i == len(printed) - 1:
|
||||
n = (i % 3) + 1
|
||||
print('\n|%s' % ('--|' * n), file = doc_file)
|
||||
print('\n|%s' % ('---|' * n), file = doc_file)
|
||||
for j in range(n):
|
||||
part = keys[i - n + j + 1]
|
||||
print('|  %s' % (part, part.replace('.stl','.png'), '|\n' if j == j - 1 else ''), end = '', file = doc_file)
|
||||
@@ -291,12 +323,11 @@ def views(target, do_assemblies = None):
|
||||
if routed:
|
||||
print("### CNC Routed parts", file = doc_file)
|
||||
keys = sorted(list(routed.keys()))
|
||||
for i in range(len(keys)):
|
||||
r = keys[i]
|
||||
print('%s %d x %s |' % ('\n|' if not (i % 3) else '', routed[r], r), file = doc_file, end = '')
|
||||
for i, r in enumerate(keys):
|
||||
print('%s %d x %s |' % ('\n|' if not (i % 3) else '', routed[r]["count"], r), file = doc_file, end = '')
|
||||
if (i % 3) == 2 or i == len(routed) - 1:
|
||||
n = (i % 3) + 1
|
||||
print('\n|%s' % ('--|' * n), file = doc_file)
|
||||
print('\n|%s' % ('---|' * n), file = doc_file)
|
||||
for j in range(n):
|
||||
part = keys[i - n + j + 1]
|
||||
print('|  %s' % (part, part.replace('.dxf','.png'), '|\n' if j == j - 1 else ''), end = '', file = doc_file)
|
||||
@@ -307,12 +338,11 @@ def views(target, do_assemblies = None):
|
||||
if sub_assemblies:
|
||||
print("### Sub-assemblies", file = doc_file)
|
||||
keys = sorted(list(sub_assemblies.keys()))
|
||||
for i in range(len(keys)):
|
||||
a = keys[i]
|
||||
for i, a in enumerate(keys):
|
||||
print('%s %d x %s |' % ('\n|' if not (i % 3) else '', sub_assemblies[a], a), file = doc_file, end = '')
|
||||
if (i % 3) == 2 or i == len(keys) - 1:
|
||||
n = (i % 3) + 1
|
||||
print('\n|%s' % ('--|' * n), file = doc_file)
|
||||
print('\n|%s' % ('---|' * n), file = doc_file)
|
||||
for j in range(n):
|
||||
a = keys[i - n + j + 1].replace('_assembly', '_assembled')
|
||||
print('|  %s' % (a, a + '_tn.png', '|\n' if j == j - 1 else ''), end = '', file = doc_file)
|
||||
@@ -367,4 +397,7 @@ if __name__ == '__main__':
|
||||
else:
|
||||
target, assemblies = None, sys.argv[1:]
|
||||
|
||||
for a in assemblies:
|
||||
if a[-9:] != "_assembly": usage()
|
||||
|
||||
views(target, assemblies)
|
||||
|
@@ -21,9 +21,8 @@
|
||||
//! BOM and assembly demonstration
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/inserts.scad>
|
||||
include <../vitamins/sheets.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
$explode = 1; // Normally set on the command line when generating assembly views with views.py
|
||||
|
||||
screw = M3_cap_screw;
|
||||
@@ -68,7 +67,7 @@ module widgit_dxf() {
|
||||
//! * Push the insert into the base with a soldering iron heated to 200°C
|
||||
module widgit_base_assembly()
|
||||
assembly("widgit_base") {
|
||||
color(pp1_colour)
|
||||
stl_colour(pp1_colour)
|
||||
widgit_stl();
|
||||
|
||||
translate_z(height)
|
30
tests/DIP.scad
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../vitamins/dip.scad>
|
||||
|
||||
dips = [[6, "OPTO"], [8, "NE555"], [14, "74HC00"], [16, "ULN2003"], [18, "ULN2803"], [20, "74HC245"], [28, "ATMEGA328"]];
|
||||
|
||||
module dips()
|
||||
for(i = [0 : len(dips) - 1]) let(dip = dips[i])
|
||||
translate([i * inch(0.5), 0])
|
||||
pdip(dip[0], dip[1], dip[0] > 20);
|
||||
|
||||
if($preview)
|
||||
dips();
|
@@ -17,10 +17,10 @@
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/iecs.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module iecs()
|
||||
layout([for(i = iecs) iec_flange_h(i)], 10)
|
||||
rotate(90)
|
33
tests/KP_pillow_blocks.scad
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2018
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/kp_pillow_blocks.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module kp_pillow_blocks() {
|
||||
screws = [M4_cap_screw, M4_cap_screw, M5_cap_screw, M5_cap_screw];
|
||||
nuts = [M4_sliding_t_nut, M4_hammer_nut, M5_sliding_t_nut, M5_nut];
|
||||
assert(len(screws) == len(kp_pillow_blocks) && len(nuts) == len(kp_pillow_blocks));
|
||||
layout([for(k = kp_pillow_blocks) 2 * kp_size(k)[1]])
|
||||
kp_pillow_block_assembly(kp_pillow_blocks[$i], screw_type = screws[$i], nut_type = nuts[$i]);
|
||||
}
|
||||
|
||||
if($preview)
|
||||
kp_pillow_blocks();
|
27
tests/LDRs.scad
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2018
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../vitamins/ldrs.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module ldrs()
|
||||
layout([for(l = ldrs) ldr_diameter(l)], 5)
|
||||
LDR(ldrs[$i]);
|
||||
|
||||
ldrs();
|
37
tests/LED_meters.scad
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2018
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../utils/core/core.scad>
|
||||
include <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/led_meters.scad>
|
||||
|
||||
module led_meters()
|
||||
layout([for(m = led_meters) meter_bezel_length(m)], 5) let(m = led_meters[$i])
|
||||
if($preview) {
|
||||
hflip()
|
||||
meter(m, colour = "blue", value = "123");
|
||||
|
||||
if(!$i)
|
||||
translate([0, meter_bezel_width(m)])
|
||||
meter_assembly(m, value = "123");
|
||||
}
|
||||
else
|
||||
meter_bezel(m);
|
||||
|
||||
led_meters();
|
@@ -16,7 +16,7 @@
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../utils/core/core.scad>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/leds.scad>
|
143
tests/PCB.scad
Normal file
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/microswitches.scad>
|
||||
include <../vitamins/d_connectors.scad>
|
||||
include <../vitamins/leds.scad>
|
||||
include <../vitamins/axials.scad>
|
||||
include <../vitamins/smds.scad>
|
||||
|
||||
use <../vitamins/pcb.scad>
|
||||
|
||||
gt_5x17 = ["gt_5x17", 5, 10, 17, 5, 11, 0.4, 9, 2,1.5, 1, 3, 6, 0.5, 0, 0, 0];
|
||||
gt_5x11 = ["gt_5x11", 5, 8, 11, 5, 7, 0.4, 7, 1.5,1.5, 1,2.5, 6, 0.5, 0, 0, 0];
|
||||
|
||||
TMC2130HeatSinkColor = "DeepSkyBlue";
|
||||
TMC2130 = ["TMC2130", "TMC2130",
|
||||
20, 14, 1.6, 0, 3, 0, "white", false, [],
|
||||
[
|
||||
[ 10, 1, 0, "-2p54header", 8, 1 ,undef, "blue" ],
|
||||
[ 10, 13, 0, "-2p54header", 8, 1],
|
||||
[ 12, 7, 0, "-chip", 6, 4, 1, grey(20) ],
|
||||
// mock up a heat sink
|
||||
[ 10, 7, 0, "block", 9, 9, 2, TMC2130HeatSinkColor ],
|
||||
[ 10, 11, 0, "block", 9, 1, 11, TMC2130HeatSinkColor ],
|
||||
[ 10, 9, 0, "block", 9, 1, 11, TMC2130HeatSinkColor ],
|
||||
[ 10, 7, 0, "block", 9, 1, 11, TMC2130HeatSinkColor ],
|
||||
[ 10, 5, 0, "block", 9, 1, 11, TMC2130HeatSinkColor ],
|
||||
[ 10, 3, 0, "block", 9, 1, 11, TMC2130HeatSinkColor ],
|
||||
],
|
||||
[]
|
||||
];
|
||||
|
||||
test_pcb = ["TestPCB", "Test PCB",
|
||||
100, 250, 1.6, // length, width, thickness
|
||||
3, // Corner radius
|
||||
2.75, // Mounting hole diameter
|
||||
5.5, // Pad around mounting hole
|
||||
"green",// Color
|
||||
true, // True if the parts should be separate BOM items
|
||||
// hole offsets
|
||||
[ [3, 3], [3, -3], [-3, 3], [-3, -3] ],
|
||||
// components
|
||||
[
|
||||
[ 20, -5, 180, "trimpot10"],
|
||||
[ 20, -15, 90, "trimpot10", true],
|
||||
[ 10, 2, 90, "smd_led", LED0805, "red"],
|
||||
[ 13, 2, 90, "smd_led", LED0603, "orange"],
|
||||
[ 16, 2, 90, "smd_res", RES1206, "1K"],
|
||||
[ 19, 2, 90, "smd_res", RES0805, "1K"],
|
||||
[ 22, 2, 90, "smd_res", RES0603, "1K"],
|
||||
[ 10, 10, 0, "2p54header", 4, 1],
|
||||
[ 25, 10, 0, "2p54header", 5, 1, false, "blue" ],
|
||||
[ 10, 20, 0, "2p54boxhdr", 4, 2],
|
||||
[ 10, 30, 0, "2p54socket", 6, 1],
|
||||
[ 25, 30, 0, "2p54socket", 4, 1, false, 0, false, "red" ],
|
||||
[ 10, 40, 0, "chip", 10, 5, 1, grey(20)],
|
||||
[ 5, 50, 0, "led", LED3mm, "red"],
|
||||
[ 12, 50, 0, "led", LED5mm, "orange"],
|
||||
[ 25, 50, 0, "led", LED10mm, "yellow"],
|
||||
[ 10, 65, 180, "rj45"],
|
||||
[ 8, 85, 180, "usb_A"],
|
||||
[ 8, 105, 180, "usb_Ax2"],
|
||||
[ 3, 140, 180, "usb_uA"],
|
||||
[ 8, 155, 180, "usb_B"],
|
||||
[ 8.5, 125, 180, "molex_usb_Ax2"],
|
||||
[ 25, 200, 0, "buzzer", 4.5, 8.5],
|
||||
[ 25, 218, 0, "buzzer"],
|
||||
[ 8, 190, 180, "jack"],
|
||||
[ 6, 200, 180, "barrel_jack"],
|
||||
[ 5, 218, 180, "hdmi"],
|
||||
[ 3, 235, 180, "mini_hdmi"],
|
||||
[ 6, 175, 180, "uSD", [12, 11.5, 1.4]],
|
||||
|
||||
[ 65, 9, 0, "link", inch(0.4)],
|
||||
[ 65, 12, 0, "ax_res", res1_8, 1000],
|
||||
[ 65, 17, 0, "ax_res", res1_4, 10000],
|
||||
[ 65, 22, 0, "ax_res", res1_2, 100000],
|
||||
|
||||
[ 80, 9, 0, "link", inch(0.2), inch(0.4)],
|
||||
[ 80, 12, 0, "ax_res", res1_8, 1000000, 1, inch(0.1)],
|
||||
[ 80, 17, 0, "ax_res", res1_4, 100, 2, inch(0.1)],
|
||||
[ 80, 22, 0, "ax_res", res1_2, 10, 10, inch(0.2)],
|
||||
|
||||
[ 60, 3, 0, "flex"],
|
||||
[ 50, 15, -90, "flat_flex"],
|
||||
[ 40, 15, -90, "flat_flex", true],
|
||||
[ 60, 35, 0, "D_plug", DCONN9],
|
||||
|
||||
[ 50, 50, 0, "molex_hdr", 2],
|
||||
[ 50, 60, 0, "jst_xh", 2],
|
||||
[ 50, 70, 180, "term254", 3],
|
||||
[ 63, 70, 180, "term254", 3, undef, grey(20)],
|
||||
[ 75, 70, 180, "gterm508",2, undef, "blue"],
|
||||
|
||||
[ 50, 90, 180, "gterm35", 4, [1,2]],
|
||||
[ 63, 90, 180, "gterm35", 4, [1,2], "red"],
|
||||
[ 75, 90, 180, "gterm", gt_5x11, 3],
|
||||
[ 90, 90, 180, "gterm", gt_5x17, 3, [1], "red"],
|
||||
|
||||
[ 55, 110, 180, "gterm635", 2],
|
||||
[ 75, 110, 180, "gterm635", 2, undef, "blue"],
|
||||
[ 90, 110, 180, "gterm", gt_5x17, 2, undef, grey(20)],
|
||||
|
||||
[ 50, 130, 180, "term35", 4],
|
||||
[ 70, 130, 180, "term35", 3, "lime"],
|
||||
[ 50, 150, 0, "transition", 5],
|
||||
[ 50, 160, 0, "block", 10, 5, 8, "orange"],
|
||||
[ 45, 170, 0, "button_6mm"],
|
||||
[ 55, 170, 0, "button_4p5mm"],
|
||||
[ 50, 185, 0, "microswitch", small_microswitch],
|
||||
[ 52, 200, 0, "pcb", 11, TMC2130 ],
|
||||
[ 80, 200, 0, "pdip", 24, "27C32", true, inch(0.6) ],
|
||||
[ 80, 170, 0, "pdip", 8, "NE555" ],
|
||||
[ 52, 206, 0, "2p54socket", 8, 1 ],
|
||||
[ 52, 194, 0, "2p54socket", 8, 1, false, 0, false, "red" ],
|
||||
[ 50, 220, 0, "standoff", 5, 4.5, 12.5, 2.54],
|
||||
[ 50, 240, 0, "potentiometer"],
|
||||
[ 75, 240, 0, "potentiometer", 7, 8],
|
||||
],
|
||||
// accessories
|
||||
[]
|
||||
];
|
||||
|
||||
|
||||
if($preview)
|
||||
let($show_threads = true)
|
||||
pcb(test_pcb);
|
35
tests/PCB_mount.scad
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2019
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../utils/core/core.scad>
|
||||
use <../printed/pcb_mount.scad>
|
||||
|
||||
PI_IO = ["PI_IO", "PI_IO V2", 35.56, 25.4, 1.6, 0, 0, 0, "green", true, [],
|
||||
[[(3.015 - 2.7) * 25.4 - 3.5 /2, (4.5 - 3.685) * 25.4, 90, "term35", 2],
|
||||
[(3.46 - 2.7) * 25.4 - 3.5 /2, (4.5 - 3.69) * 25.4, 90, "term35", 2],
|
||||
[(3.91 - 2.7) * 25.4 - 3.5 /2, (4.5 - 3.69) * 25.4, 90, "term35", 2],
|
||||
[(3.4 - 2.7) * 25.4, (4.5 - 4.15) * 25.4, 0, "2p54socket", 13, 2],
|
||||
], []];
|
||||
|
||||
module pcb_mounts()
|
||||
if($preview)
|
||||
pcb_mount_assembly(PI_IO, 3);
|
||||
else
|
||||
pcb_mount(PI_IO);
|
||||
|
||||
pcb_mounts();
|
@@ -17,16 +17,20 @@
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/d_connectors.scad>
|
||||
include <../vitamins/pcbs.scad>
|
||||
|
||||
module pcbs()
|
||||
layout([for(p = pcbs) pcb_width(p)], 15)
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module pcbs() {
|
||||
layout([for(p = pcbs) pcb_width(p)], 10)
|
||||
translate([0, pcb_length(pcbs[$i]) / 2])
|
||||
rotate(90)
|
||||
pcb_assembly(pcbs[$i], 5 + $i, 3);
|
||||
|
||||
translate([0, 120])
|
||||
layout([for(p = perfboards) pcb_length(p)], 10)
|
||||
translate([0, -pcb_width(perfboards[$i]) / 2])
|
||||
pcb_assembly(perfboards[$i], 5 + $i, 3);
|
||||
}
|
||||
if($preview)
|
||||
pcbs();
|
37
tests/PSU_shroud.scad
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2018
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/psus.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
use <../printed/psu_shroud.scad>
|
||||
|
||||
thickness = 3;
|
||||
|
||||
module psu_shrouds()
|
||||
layout([for(p = psus) psu_width(p)], 10) let(psu = psus[$i])
|
||||
if(psu_terminals(psu))
|
||||
rotate(90) {
|
||||
if($preview)
|
||||
psu_shroud_fastened_assembly(psu, 6, thickness, psu[0]);
|
||||
else
|
||||
psu_shroud(psu, 6, psu[0]);
|
||||
}
|
||||
|
||||
psu_shrouds();
|
@@ -17,11 +17,10 @@
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/psus.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module psus()
|
||||
layout([for(p = psus) psu_width(p)], 10) let(p = psus[$i])
|
||||
rotate(atx_psu(p) ? 0 : 90) {
|
35
tests/SCS_bearing_blocks.scad
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/scs_bearing_blocks.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module scs_bearing_blocks()
|
||||
layout([for(s = scs_bearing_blocks) scs_size(s).x], 10) {
|
||||
part_thickness = 5;
|
||||
scs_bearing_block_assembly(scs_bearing_blocks[$i], part_thickness);
|
||||
|
||||
if($i > 0) // skip $i==0, since no SCS6LUU long variant to match SCS6UU
|
||||
translate([0, 60])
|
||||
scs_bearing_block_assembly(scs_bearing_blocks_long[$i - 1], part_thickness);
|
||||
}
|
||||
|
||||
if($preview)
|
||||
scs_bearing_blocks();
|
39
tests/SK_brackets.scad
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2018
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/sk_brackets.scad>
|
||||
include <../vitamins/extrusions.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module sk_brackets() {
|
||||
screws = [M4_dome_screw, M4_cap_screw, M5_cap_screw, M5_cap_screw];
|
||||
nuts = [M4_hammer_nut, M4_sliding_t_nut, M5_sliding_t_nut, undef];
|
||||
// channel depth = 6 for 2020 extrusion, 9 for 3030 extrusion
|
||||
depths = [6, 6, 9, 0];
|
||||
layout([for(s = sk_brackets) 1.5 * sk_size(s)[1]]) {
|
||||
sk_bracket_assembly(sk_brackets[$i], screw_type = screws[$i], nut_type = nuts[$i], max_screw_depth = depths[$i]);
|
||||
translate([0, -sk_hole_offset(sk_brackets[$i]) - extrusion_width($i < 2 ? E2020 : E3030) / 2, 0])
|
||||
rotate([0, 90, 0])
|
||||
extrusion($i < 2 ? E2020 : E3030, 20, false);
|
||||
}
|
||||
}
|
||||
|
||||
if($preview)
|
||||
sk_brackets();
|
34
tests/SMDs.scad
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../utils/core/core.scad>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/smds.scad>
|
||||
|
||||
module smds() {
|
||||
layout([for(r = smd_resistors) smd_res_size(r).x], 1)
|
||||
smd_resistor(smd_resistors[$i], ["1R0", "10M", "100K"][$i % 3]);
|
||||
|
||||
translate([0, 3])
|
||||
layout([for(l = smd_leds) smd_led_size(l).x], 1)
|
||||
smd_led(smd_leds[$i], ["green", "blue", "red"][$i % 3]);
|
||||
}
|
||||
|
||||
if($preview)
|
||||
smds();
|