Compare commits
358 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9b40e0dcef | ||
|
ec07d95657 | ||
|
0dab0dca08 | ||
|
6e6cd45295 | ||
|
3a7fde6c56 | ||
|
c5bb898856 | ||
|
11ebe2225d | ||
|
a1e25bb878 | ||
|
9689683b7e | ||
|
08946e3d70 | ||
|
15c2946e91 | ||
|
34019196cd | ||
|
436fc71cf3 | ||
|
fd67c166f7 | ||
|
634deab911 | ||
|
e2feceb608 | ||
|
d1258e0b0c | ||
|
bd61a1dc55 | ||
|
055e83902f | ||
|
feec1e7ae5 | ||
|
be76af2fc4 | ||
|
9c1a9bf357 | ||
|
9647fb474b | ||
|
49fdfea792 | ||
|
ac0bacfeda | ||
|
6288059d99 | ||
|
0f1dff230a | ||
|
e379fa8ada | ||
|
313d7508df | ||
|
083caca4e8 | ||
|
49329df00c | ||
|
edc0b86bb1 | ||
|
5fbff060b0 | ||
|
b94ca4ad3a | ||
|
bc5515d35e | ||
|
44d213deaa | ||
|
145c5d9b1a | ||
|
7abbbd9b96 | ||
|
5b160cee88 | ||
|
3f31607840 | ||
|
1efed649cf | ||
|
b70c2f993c | ||
|
56e252f3dc | ||
|
f12b36ea04 | ||
|
bd5811e69b | ||
|
ede4da6f1d | ||
|
51cc2fd679 | ||
|
4ce2f53e20 | ||
|
e338c47e73 | ||
|
8328a70f42 | ||
|
cc794cd7c3 | ||
|
df28bd5116 | ||
|
61de6041d8 | ||
|
b2d712bca9 | ||
|
f3376edaf1 | ||
|
c073419c0b | ||
|
608168de8e | ||
|
fc45a40bd3 | ||
|
52e9c1d7fd | ||
|
ca1b34e9ca | ||
|
cbab9cea02 | ||
|
69cf998862 | ||
|
08bce9ec03 | ||
|
4aa7dbb416 | ||
|
7c7c2e5d3f | ||
|
f6f6664c0d | ||
|
2fd2e118a0 | ||
|
22c6fef113 | ||
|
dcf0c949b9 | ||
|
9ded315801 | ||
|
42e03679b4 | ||
|
d2c795f5f5 | ||
|
83b4ab2374 | ||
|
573c50774b | ||
|
4b93623492 | ||
|
d496898c80 | ||
|
544e69c71b | ||
|
240334784d | ||
|
516b225275 | ||
|
e46e6b6e5b | ||
|
4925979519 | ||
|
298d1f9284 | ||
|
fcf2f5f7f0 | ||
|
491c3b4ea8 | ||
|
94cb50f725 | ||
|
1d8275c061 | ||
|
3c605d608b | ||
|
9ba48c7e1a | ||
|
a6ebc5267b | ||
|
e9554ccffe | ||
|
0d179f3728 | ||
|
a94e462f34 | ||
|
7ce991e625 | ||
|
3e15be852f | ||
|
7c1c8e92f2 | ||
|
54c3b4f600 | ||
|
d80fc5709e | ||
|
30236046a8 | ||
|
93aeb4cf2c | ||
|
f62ca35c86 | ||
|
2320cbdbb8 | ||
|
769cb44207 | ||
|
f327df95a3 | ||
|
09956b6219 | ||
|
f8c9adca5a | ||
|
b83e56713f | ||
|
4c12d5fca4 | ||
|
da4f9fbdc3 | ||
|
614e5f3a72 | ||
|
a7eae4f549 | ||
|
3cd5769708 | ||
|
7b770abe12 | ||
|
31ab8562a7 | ||
|
db703379a3 | ||
|
cd925bc049 | ||
|
4a2951e22f | ||
|
a93a8f99fb | ||
|
73c436ee15 | ||
|
c69100bd71 | ||
|
e0b89359aa | ||
|
041d720c39 | ||
|
03a0c2fe98 | ||
|
7c2df8d36d | ||
|
8474718884 | ||
|
fe0f32ddc5 | ||
|
f191b9b0f4 | ||
|
2b3908b6fd | ||
|
0a84bf0927 | ||
|
da825b17ab | ||
|
ca153c971d | ||
|
0199587907 | ||
|
60350eb228 | ||
|
4f9729cf86 | ||
|
26f1338ca2 | ||
|
fc44b43638 | ||
|
182f39876a | ||
|
055e90cbb3 | ||
|
832380f893 | ||
|
929d082b25 | ||
|
57212b5701 | ||
|
1c3f136657 | ||
|
cfd2fd32a1 | ||
|
f573a91a09 | ||
|
d75aff2ccd | ||
|
491d85156c | ||
|
c89ce6843f | ||
|
1915dae034 | ||
|
05e8055ce2 | ||
|
21822b9abb | ||
|
d83d4b89bf | ||
|
613152f589 | ||
|
d90c00d140 | ||
|
b52ca9589a | ||
|
0d024060fc | ||
|
c4fe1d1098 | ||
|
a3ced6de45 | ||
|
1cdfe3267c | ||
|
b67cf4ce97 | ||
|
5bac2bf46d | ||
|
079168142b | ||
|
cb30f0c63d | ||
|
812fbc106c | ||
|
dd38fa6e5d | ||
|
5cdcd4ad37 | ||
|
4cb324ed37 | ||
|
eb997aa18c | ||
|
f09343a285 | ||
|
54aab027bd | ||
|
cc61a11602 | ||
|
9cf2e9d7c3 | ||
|
9da8a3cb26 | ||
|
36521cf0b9 | ||
|
22b7aa956c | ||
|
f760aaa20a | ||
|
1c445385b4 | ||
|
45f3fc275f | ||
|
f7fbbd5fe4 | ||
|
a769a38dff | ||
|
0485eeeb34 | ||
|
0a7208ff5a | ||
|
f32182d6df | ||
|
84fbd05f9b | ||
|
2a1fa3fe6b | ||
|
176dc3231c | ||
|
e7ac18e3c0 | ||
|
74b8dcb6d8 | ||
|
6269575fd4 | ||
|
2923e35725 | ||
|
5a1b06fbb9 | ||
|
fd174bbfb4 | ||
|
186f31bb09 | ||
|
80f0490d58 | ||
|
c9e9942070 | ||
|
e41366e20c | ||
|
0595fb5a92 | ||
|
abb676b75c | ||
|
19bae20e24 | ||
|
a7d181ffc4 | ||
|
6b2f8d282b | ||
|
5bb95b2406 | ||
|
dc3905b284 | ||
|
bd2ea3f284 | ||
|
62183bcadc | ||
|
7f9122ae66 | ||
|
93b260b7b9 | ||
|
c33876530e | ||
|
8fea8f437b | ||
|
bc3ef607af | ||
|
6bcdc24cd3 | ||
|
b8b4232882 | ||
|
0394d9981f | ||
|
e4e7096e3c | ||
|
d71bb4bdfa | ||
|
89c3767f21 | ||
|
c837de721d | ||
|
f8c87bd8e3 | ||
|
ef58fe5818 | ||
|
9c0c0c0acf | ||
|
ac6e8e040a | ||
|
accf4f7bc1 | ||
|
6bd4638e7d | ||
|
4cca9d5285 | ||
|
c3fc352288 | ||
|
a8b634de39 | ||
|
1a55a08c59 | ||
|
8ca4bb5adc | ||
|
53e3378efe | ||
|
888199f698 | ||
|
f51e93969d | ||
|
757b808f07 | ||
|
eca8f2eccc | ||
|
450224ec39 | ||
|
dcd2624a12 | ||
|
83e8502ecb | ||
|
a722df0b2b | ||
|
6d223c8d1f | ||
|
6d59ea2bc8 | ||
|
b74cbee151 | ||
|
9f7efd50fb | ||
|
0b14d3fbc4 | ||
|
8243b244af | ||
|
1a2e04367d | ||
|
abc38f2744 | ||
|
f97070099e | ||
|
729891b675 | ||
|
4b533cffd2 | ||
|
a40a2190dc | ||
|
6556d14a11 | ||
|
c7d12b20c9 | ||
|
545329b875 | ||
|
760e3a890d | ||
|
bd4f7b155b | ||
|
16c1eeef27 | ||
|
800bb89921 | ||
|
41a0723362 | ||
|
6b8ea9685e | ||
|
0cf8cb7d28 | ||
|
7c439a687f | ||
|
4a95ce528e | ||
|
138f45730b | ||
|
8d1ff3584c | ||
|
26b2b63b6e | ||
|
611772d960 | ||
|
b6e648b485 | ||
|
e224ee0ad2 | ||
|
10c3df466b | ||
|
3e4eedc25f | ||
|
029265e1b9 | ||
|
7449857ab7 | ||
|
49f6da767a | ||
|
171dff723f | ||
|
85a7494813 | ||
|
46937e403e | ||
|
8f2532d61b | ||
|
84fa528ff7 | ||
|
807850aac5 | ||
|
4ea0883965 | ||
|
103ad1827e | ||
|
816adb07e7 | ||
|
06e1c1a3da | ||
|
46ff632bbb | ||
|
fc47b1d9db | ||
|
de6589a2ef | ||
|
c2b3eb6580 | ||
|
ba586b3685 | ||
|
8d22940506 | ||
|
f6f4fcc7e8 | ||
|
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 |
1853
CHANGELOG.md
Normal file
133
docs/usage.md
@@ -1,7 +1,7 @@
|
||||
# NopSCADlib usage
|
||||
|
||||
## Requirements
|
||||
1. OpenSCAD 2019.05 or later, download it from here: https://www.openscad.org/downloads.html
|
||||
1. OpenSCAD 2021.01 or later, download it from here: https://www.openscad.org/downloads.html
|
||||
1. Python 2.7+ or 3.6+ from https://www.python.org/downloads/
|
||||
1. ImageMagick 7 www.imagemagick.org
|
||||
|
||||
@@ -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, ```PATH``` on Windows and ```path``` on Linux and Mac.
|
||||
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,38 @@ 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) 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).
|
||||
|
||||
### Project Description
|
||||
|
||||
A description of the project can be placed in comments in Markdown format before the first include file.
|
||||
By default this is followed by a picture of the main assembly unless the description contains any pictures.
|
||||
|
||||
The description can be divided into two or three sections using //! ***, which is a Markdown horizontal rule in a comment.
|
||||
If there is a second section it is placed after the table of contents and a third section will be placed after the parts list.
|
||||
|
||||
If an actual horizontal rule is desired the alternative Markdown markup //! --- can be used.
|
||||
|
||||
### 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 +154,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.
|
||||
@@ -162,7 +172,7 @@ This is achieved by having a pair of modules: -
|
||||
//! Place inserts in the bottom of the posts and push them home with a soldering iron with a conical bit heated to 200°C.
|
||||
//
|
||||
module handle_assembly() pose([225, 0, 150], [0, 0, 14]) //! Printed part with inserts in place
|
||||
assembly("handle") {
|
||||
assembly("handle", ngb = true) {
|
||||
translate_z(handle_height())
|
||||
stl_colour(pp1_colour) vflip() handle_stl();
|
||||
|
||||
@@ -172,7 +182,7 @@ This is achieved by having a pair of modules: -
|
||||
}
|
||||
|
||||
module handle_fastened_assembly(thickness) { //! Assembly with fasteners in place
|
||||
screw_length = screw_longer_than(thickness + insert_length(insert) + 2 * washer_thickness(screw_washer(screw)));
|
||||
screw_length = screw_length(screw, thickness, 2, true, longer = true);
|
||||
|
||||
handle_assembly();
|
||||
|
||||
@@ -182,26 +192,29 @@ 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.
|
||||
|
||||
Setting `ngb = true` in the `assembly()` prevents the handle assembly appearing as a columun in the top level BOM in the build instructions.
|
||||
Instead its parts are merged into the parent BOM so the correct quantites are listed.
|
||||
|
||||
### 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,50 +229,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)```.
|
||||
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 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.
|
||||
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```.
|
||||
`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>```.
|
||||
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.
|
||||
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: 21 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
@@ -2,6 +2,8 @@
|
||||
{
|
||||
"name": "base_assembly",
|
||||
"big": null,
|
||||
"ngb": false,
|
||||
"zoomed": 0,
|
||||
"count": 1,
|
||||
"assemblies": {},
|
||||
"vitamins": {
|
||||
@@ -20,6 +22,8 @@
|
||||
{
|
||||
"name": "feet_assembly",
|
||||
"big": null,
|
||||
"ngb": false,
|
||||
"zoomed": 0,
|
||||
"count": 1,
|
||||
"assemblies": {
|
||||
"base_assembly": 1
|
||||
@@ -46,6 +50,8 @@
|
||||
{
|
||||
"name": "mains_in_assembly",
|
||||
"big": null,
|
||||
"ngb": false,
|
||||
"zoomed": 0,
|
||||
"count": 1,
|
||||
"assemblies": {
|
||||
"feet_assembly": 1
|
||||
@@ -82,6 +88,8 @@
|
||||
{
|
||||
"name": "main_assembly",
|
||||
"big": null,
|
||||
"ngb": false,
|
||||
"zoomed": 0,
|
||||
"count": 1,
|
||||
"assemblies": {
|
||||
"mains_in_assembly": 1
|
||||
|
@@ -15,6 +15,7 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
|
||||

|
||||
|
||||
<span></span>
|
||||
|
||||
---
|
||||
## Table of Contents
|
||||
@@ -24,6 +25,7 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
1. [Mains In Assembly](#mains_in_assembly)
|
||||
1. [Main Assembly](#main_assembly)
|
||||
|
||||
<span></span>
|
||||
[Top](#TOP)
|
||||
|
||||
---
|
||||
@@ -54,6 +56,7 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
| 1 | . | . | . | 1 | socket_box.stl |
|
||||
| 1 | 4 | . | . | 5 | Total 3D printed parts count |
|
||||
|
||||
<span></span>
|
||||
[Top](#TOP)
|
||||
|
||||
---
|
||||
@@ -81,6 +84,7 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
|
||||

|
||||
|
||||
<span></span>
|
||||
[Top](#TOP)
|
||||
|
||||
---
|
||||
@@ -117,6 +121,7 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
|
||||

|
||||
|
||||
<span></span>
|
||||
[Top](#TOP)
|
||||
|
||||
---
|
||||
@@ -156,6 +161,7 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
|
||||

|
||||
|
||||
<span></span>
|
||||
[Top](#TOP)
|
||||
|
||||
---
|
||||
@@ -199,4 +205,5 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
|
||||

|
||||
|
||||
<span></span>
|
||||
[Top](#TOP)
|
||||
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 290 KiB |
Before Width: | Height: | Size: 293 KiB After Width: | Height: | Size: 293 KiB |
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 287 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 235 KiB |
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 180 KiB |
Before Width: | Height: | Size: 215 KiB After Width: | Height: | Size: 252 KiB |
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 202 KiB |
@@ -1,40 +1,30 @@
|
||||
# A gallery of projects made with NopSCADlib
|
||||
<a name="TOP"></a>
|
||||
## ArduinoThermostat
|
||||
Arduino thermostat to control a beer fridge to use it as an environmental chamber.
|
||||
|
||||

|
||||
|
||||
|
||||
<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.
|
||||
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
## IOT 50V PSU
|
||||
WiFi controllable PSU
|
||||
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
## Lab ATX PSU
|
||||
Bench power supply built around an ATX PSU.
|
||||
|
||||
@@ -43,19 +33,15 @@ Bench power supply built around an ATX PSU.
|
||||
* The green LED shows the power good signal.
|
||||
* Dummy loads keep the outputs in range.
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
|
||||
## Laser Load
|
||||
15kV dummy load for testing CO2 laser PSUs
|
||||
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
## MainsBreakOutBox
|
||||
13A socket break out box with 4mm jacks to measure voltage and / or load current and earth leakage current.
|
||||
|
||||
@@ -72,36 +58,29 @@ Earth leakage can be measured Canadian CSA style by disconnected the neutral lin
|
||||
|
||||

|
||||
|
||||
|
||||
<a name="TOP"></a>
|
||||
## Mains Box
|
||||
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
|
||||
|
||||

|
||||
|
||||
Was actually made from DiBond but shown made with carbon fibre here.
|
||||
|
||||
<a name="TOP"></a>
|
||||
## Variac
|
||||
Motorised variac with WiFi control, see [hydraraptor.blogspot.com/2018/04/esp8266-spi-spy](https://hydraraptor.blogspot.com/2018/04/esp8266-spi-spy.html)
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
@@ -26,17 +26,17 @@
|
||||
// Setting $bom after including lib overrides bom in the libs but not in the local file.
|
||||
// Setting $_bom in the local file overrides it in the local file but not in the libs.
|
||||
//
|
||||
//function is_undef(x) = x == undef;
|
||||
rr_green = [0, 146/255, 0]; // RepRap logo colour
|
||||
|
||||
$_bom = is_undef($bom) ? 0 : $bom; // 0 no bom, 1 assemblies and stls, 2 vitamins as well
|
||||
$exploded = is_undef($explode) ? 0 : $explode; // 1 for exploded view
|
||||
layer_height = is_undef($layer_height) ? 0.25 : $layer_height; // layer heigth when printing
|
||||
layer_height = is_undef($layer_height) ? 0.25 : $layer_height; // layer height 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; // 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) ? "red" : $pp2_colour; // printed part colour 2
|
||||
pp3_colour = is_undef($pp3_colour) ? "blue" : $pp3_colour; // printed part colour 3
|
||||
pp1_colour = is_undef($pp1_colour) ? rr_green : $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
|
||||
|
7
lib.scad
@@ -26,7 +26,9 @@ include <vitamins/psus.scad>
|
||||
include <vitamins/pcbs.scad>
|
||||
|
||||
include <vitamins/batteries.scad>
|
||||
include <vitamins/bearing_blocks.scad>
|
||||
include <vitamins/blowers.scad>
|
||||
include <vitamins/box_sections.scad>
|
||||
include <vitamins/bulldogs.scad>
|
||||
include <vitamins/buttons.scad>
|
||||
include <vitamins/cameras.scad>
|
||||
@@ -35,10 +37,8 @@ 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>
|
||||
@@ -48,12 +48,12 @@ include <vitamins/mains_sockets.scad>
|
||||
include <vitamins/modules.scad>
|
||||
include <vitamins/panel_meters.scad>
|
||||
include <vitamins/pillars.scad>
|
||||
include <vitamins/pillow_blocks.scad>
|
||||
include <vitamins/pin_headers.scad>
|
||||
include <vitamins/pulleys.scad>
|
||||
include <vitamins/ring_terminals.scad>
|
||||
include <vitamins/rails.scad>
|
||||
include <vitamins/rod.scad>
|
||||
include <vitamins/scs_bearing_blocks.scad>
|
||||
include <vitamins/shaft_couplings.scad>
|
||||
include <vitamins/sheets.scad>
|
||||
include <vitamins/sk_brackets.scad>
|
||||
@@ -90,6 +90,7 @@ use <utils/gears.scad>
|
||||
use <utils/hanging_hole.scad>
|
||||
use <utils/fillet.scad>
|
||||
use <utils/rounded_polygon.scad>
|
||||
use <utils/rounded_triangle.scad>
|
||||
use <utils/layout.scad>
|
||||
use <utils/round.scad>
|
||||
use <utils/offset.scad>
|
||||
|
BIN
libtest.png
Before Width: | Height: | Size: 841 KiB After Width: | Height: | Size: 894 KiB |
43
libtest.scad
@@ -33,6 +33,8 @@
|
||||
//!
|
||||
//! See [usage](docs/usage.md) for requirements, installation instructions and a usage guide.
|
||||
//!
|
||||
//! A list of changes classified as breaking, additions or fixes is maintained in [CHANGELOG.md](CHANGELOG.md).
|
||||
//!
|
||||
//! <img src="libtest.png" width="100%"/>
|
||||
//
|
||||
// This file shows all the parts in the library.
|
||||
@@ -41,8 +43,10 @@ include <lib.scad>
|
||||
|
||||
use <tests/ball_bearings.scad>
|
||||
use <tests/batteries.scad>
|
||||
use <tests/bearing_blocks.scad>
|
||||
use <tests/belts.scad>
|
||||
use <tests/blowers.scad>
|
||||
use <tests/box_sections.scad>
|
||||
use <tests/bulldogs.scad>
|
||||
use <tests/buttons.scad>
|
||||
use <tests/cable_strips.scad>
|
||||
@@ -52,6 +56,7 @@ 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>
|
||||
@@ -61,7 +66,6 @@ use <tests/hot_ends.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/LDRs.scad>
|
||||
use <tests/LEDs.scad>
|
||||
@@ -77,6 +81,8 @@ use <tests/opengrab.scad>
|
||||
use <tests/panel_meters.scad>
|
||||
use <tests/PCBs.scad>
|
||||
use <tests/pillars.scad>
|
||||
use <tests/pillow_blocks.scad>
|
||||
use <tests/press_fit.scad>
|
||||
use <tests/PSUs.scad>
|
||||
use <tests/pulleys.scad>
|
||||
use <tests/rails.scad>
|
||||
@@ -84,7 +90,6 @@ 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>
|
||||
@@ -115,7 +120,9 @@ use <tests/flat_hinge.scad>
|
||||
use <tests/foot.scad>
|
||||
use <tests/handle.scad>
|
||||
use <tests/PCB_mount.scad>
|
||||
use <tests/pocket_handle.scad>
|
||||
use <tests/printed_box.scad>
|
||||
use <tests/printed_pulleys.scad>
|
||||
use <tests/ribbon_clamp.scad>
|
||||
use <tests/screw_knob.scad>
|
||||
use <tests/socket_box.scad>
|
||||
@@ -136,9 +143,12 @@ cable_grommets_y = 0;
|
||||
translate([x5, cable_grommets_y])
|
||||
cable_grommets();
|
||||
|
||||
translate([x5 + 80, cable_grommets_y])
|
||||
translate([x5 + 50, cable_grommets_y])
|
||||
ribbon_clamps();
|
||||
|
||||
translate([x5 + 95, cable_grommets_y])
|
||||
press_fits();
|
||||
|
||||
translate([x5, cable_grommets_y + 60])
|
||||
fixing_blocks();
|
||||
|
||||
@@ -165,13 +175,19 @@ translate([x5, cable_grommets_y + 370])
|
||||
translate([x5 + 60, cable_grommets_y + 200])
|
||||
strap_handles();
|
||||
|
||||
translate([640, cable_grommets_y + 200])
|
||||
printed_pulley_test();
|
||||
|
||||
translate([x5, cable_grommets_y + 250])
|
||||
handle();
|
||||
|
||||
translate([950, 600])
|
||||
box_test();
|
||||
|
||||
translate([890, 750])
|
||||
translate([830, 770])
|
||||
pocket_handles();
|
||||
|
||||
translate([950, 750])
|
||||
printed_boxes();
|
||||
|
||||
translate([850, 1330])
|
||||
@@ -192,8 +208,8 @@ 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 + 100;
|
||||
pcbs_y = sheets_y + 40;
|
||||
sheets_y = linear_bearings_y + 90;
|
||||
pcbs_y = sheets_y + 60;
|
||||
displays_y = pcbs_y + 170;
|
||||
fans_y = displays_y + 80;
|
||||
transformers_y = fans_y + 120;
|
||||
@@ -346,17 +362,17 @@ modules_y = iecs_y + 60;
|
||||
ssrs_y = modules_y + 80;
|
||||
blowers_y = ssrs_y + 60;
|
||||
batteries_y = blowers_y + 100;
|
||||
steppers_y = batteries_y + 70;
|
||||
steppers_y = batteries_y + 55;
|
||||
panel_meters_y = steppers_y + 70;
|
||||
extrusions_y = panel_meters_y + 80;
|
||||
|
||||
translate([x3, veroboard_y])
|
||||
veroboard_test();
|
||||
|
||||
translate([x3 + 70, veroboard_y + 30])
|
||||
translate([x3 + 60, veroboard_y + 20])
|
||||
geared_steppers();
|
||||
|
||||
translate([x3 + 140, veroboard_y + 20])
|
||||
translate([x3 + 160, ssrs_y])
|
||||
pcb_mounts();
|
||||
|
||||
translate([x3 + 170, veroboard_y + 16])
|
||||
@@ -411,18 +427,23 @@ 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;
|
||||
box_sections_y = sheets_y;
|
||||
|
||||
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([800, fans_y + 50])
|
||||
translate([770, fans_y + 50])
|
||||
cable_strips();
|
||||
|
||||
translate([x4, kp_pillow_blocks_y])
|
||||
@@ -440,6 +461,8 @@ translate([x4 + 120, extrusion_brackets_y])
|
||||
translate([x4, scs_bearing_blocks_y])
|
||||
scs_bearing_blocks();
|
||||
|
||||
translate([x4, box_sections_y])
|
||||
box_sections();
|
||||
|
||||
translate([x6, 125])
|
||||
light_strips();
|
||||
|
237
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:
|
||||
@@ -75,9 +75,9 @@ function box_corner_overlap(type) = box_wall(type);
|
||||
function box_corner_rad(type) = box_sheet_slot(type) - sheet_slot_clearance / 2 + box_corner_gap(type) + box_corner_overlap(type);
|
||||
function box_sheet_r(type) = box_corner_rad(type) - box_sheet_slot(type) - box_corner_overlap(type);
|
||||
|
||||
function box_screw_length(type, top) = screw_longer_than(2 * washer_thickness(box_washer(type))
|
||||
+ sheet_thickness(top ? box_top_sheet(type) : box_base_sheet(type))
|
||||
+ box_corner_gap(type) + box_profile_overlap(type) + box_insert_l(type) - 1);
|
||||
function box_screw_length(type, top) =
|
||||
let(s = top ? box_top_sheet(type) : box_base_sheet(type))
|
||||
screw_length(box_screw(type), sheet_thickness(s) + box_corner_gap(type) + box_profile_overlap(type) - 1, washers = 2, insert = true, longer = true);
|
||||
|
||||
function box_wall_clearance(type) = box_sheet_slot(type) / 2 - sheet_thickness(box_sheets(type)) / 2;
|
||||
function box_margin(type) = box_profile_overlap(type) + box_corner_gap(type); //! How much the bezel intrudes on the specified height
|
||||
@@ -109,7 +109,7 @@ module grill_hole_positions(width, height, r = 1000) {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
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)
|
||||
if(poly)
|
||||
grill_hole_positions(width, height, r)
|
||||
@@ -121,11 +121,12 @@ module grill(width, height, r = 1000, poly = false, h = 0) { //! A staggered arr
|
||||
|
||||
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
|
||||
}
|
||||
@@ -138,17 +139,17 @@ module box_corner_profile_2D(type) { //! The 2D shape of the corner profile.
|
||||
}
|
||||
|
||||
module box_corner_profile(type) { //! Generates the corner profile STL for 3D printing.
|
||||
stl("box_corner_profile");
|
||||
|
||||
length = box_height(type) - 2 * box_margin(type);
|
||||
difference() {
|
||||
linear_extrude(length, center = true, convexity = 5)
|
||||
box_corner_profile_2D(type);
|
||||
|
||||
for(z = [-1, 1])
|
||||
translate([box_hole_inset(type), box_hole_inset(type), z * length / 2])
|
||||
insert_hole(box_insert(type), 5);
|
||||
}
|
||||
stl("box_corner_profile")
|
||||
difference() {
|
||||
linear_extrude(length, center = true, convexity = 5)
|
||||
box_corner_profile_2D(type);
|
||||
|
||||
for(z = [-1, 1])
|
||||
translate([box_hole_inset(type), box_hole_inset(type), z * length / 2])
|
||||
insert_hole(box_insert(type), 5);
|
||||
}
|
||||
}
|
||||
|
||||
module box_corner_profile_section(type, section, sections) { //! Generates interlocking sections of the corner profile to allow it to be taller than the printer
|
||||
@@ -208,72 +209,78 @@ module box_corner_quadrants(type, width, depth)
|
||||
}
|
||||
|
||||
module box_bezel(type, bottom) { //! Generates top and bottom bezel STLs
|
||||
stl(bottom ? "bottom_bezel" : "top_bezel");
|
||||
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() {
|
||||
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);
|
||||
//
|
||||
// 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);
|
||||
stl(bottom ? "bottom_bezel" : "top_bezel")
|
||||
difference() {
|
||||
w = box_width(type);
|
||||
d = box_depth(type);
|
||||
translate_z(-box_profile_overlap(type)) difference() {
|
||||
tw = w + 2 * outset;
|
||||
td = d + 2 * outset;
|
||||
rounded_rectangle([tw, td, feet ? foot_height : height], box_corner_rad(type));
|
||||
//
|
||||
// Remove edges between the feet
|
||||
//
|
||||
if(feet)
|
||||
hull() {
|
||||
translate_z(height + 0.5)
|
||||
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);
|
||||
}
|
||||
if(feet)
|
||||
hull() {
|
||||
translate_z(height + 0.5)
|
||||
cube([box_width(type) + 2 * outset + 1, box_depth(type) - 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);
|
||||
}
|
||||
}
|
||||
//
|
||||
// slots for side panels
|
||||
//
|
||||
translate_z(-box_profile_overlap(type))
|
||||
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([0, i * (box_depth(type) / 2 + t / 2 - sheet_slot_clearance / 2)])
|
||||
square([box_width(type) - 2 * box_corner_gap(type), 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);
|
||||
//
|
||||
// leave plastic over the corner profiles
|
||||
//
|
||||
translate_z(-box_profile_overlap(type) - 1)
|
||||
linear_extrude(box_profile_overlap(type) + box_corner_gap(type) + 2)
|
||||
union() {
|
||||
difference() {
|
||||
square([box_width(type) - 2 * box_inset(type),
|
||||
box_depth(type) - 2 * box_inset(type)], center = true);
|
||||
|
||||
box_corner_quadrants(type, box_width(type), box_depth(type));
|
||||
translate_z(foot_height + 1)
|
||||
cube([w - 2 * (foot_length - foot_extension), td + 1, 1], center = true);
|
||||
}
|
||||
box_screw_hole_positions(type)
|
||||
poly_circle(screw_clearance_radius(box_screw(type)));
|
||||
}
|
||||
}
|
||||
if(feet)
|
||||
hull() {
|
||||
translate_z(height + 0.5)
|
||||
cube([tw + 1, d - 2 * foot_length, 1], center = true);
|
||||
|
||||
translate_z(foot_height + 1)
|
||||
cube([tw + 1, d - 2 * (foot_length - foot_extension), 1], center = true);
|
||||
}
|
||||
}
|
||||
//
|
||||
// slots for side panels
|
||||
//
|
||||
translate_z(-box_profile_overlap(type))
|
||||
linear_extrude(2 * box_profile_overlap(type), center = true)
|
||||
for(i = [-1, 1]) {
|
||||
translate([i * (w + t - sheet_slot_clearance) / 2, 0])
|
||||
square([t, d - 2 * cgap], 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(cgap)
|
||||
rounded_rectangle([w + bezel_clearance, d + bezel_clearance, height], inner_r + bezel_clearance / 2);
|
||||
//
|
||||
// leave plastic over the corner profiles
|
||||
//
|
||||
translate_z(-box_profile_overlap(type) - 1)
|
||||
linear_extrude(box_profile_overlap(type) + cgap + 2)
|
||||
union() {
|
||||
difference() {
|
||||
square([w - 2 * inset,
|
||||
d - 2 * inset], center = true);
|
||||
|
||||
box_corner_quadrants(type, w, d);
|
||||
}
|
||||
box_screw_hole_positions(type)
|
||||
poly_circle(screw_clearance_radius(box_screw(type)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dowel_length = 20;
|
||||
@@ -291,7 +298,9 @@ module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlo
|
||||
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() {
|
||||
@@ -299,14 +308,14 @@ module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlo
|
||||
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() {
|
||||
@@ -318,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,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([tw / 2 - x * w, th / 2 - 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)
|
||||
@@ -419,10 +428,14 @@ module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlo
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -472,7 +485,6 @@ module box_shelf_screw_positions(type, screw_positions, thickness = 0, wall = un
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -500,44 +512,45 @@ module box_shelf_bracket(type, screw_positions, wall = undef) { //! Generates a
|
||||
square([lip, eps]);
|
||||
}
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(w)
|
||||
difference() {
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
|
||||
round(2) offset(-width)
|
||||
stl("shelf_bracket")
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(w)
|
||||
difference() {
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
}
|
||||
|
||||
linear_extrude(lip)
|
||||
difference() {
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
round(2) offset(-width)
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
}
|
||||
|
||||
offset(-w)
|
||||
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)
|
||||
boss();
|
||||
insert_hole(insert, counterbore = 1, horizontal = true);
|
||||
}
|
||||
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
|
||||
@@ -551,25 +564,25 @@ module box_shelf_bracket_section(type, rows, cols, x, y) { //! Generates section
|
||||
children();
|
||||
}
|
||||
|
||||
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_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);
|
||||
//
|
||||
|
@@ -21,7 +21,7 @@
|
||||
//! 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
|
||||
|
@@ -49,84 +49,84 @@ module ribbon_grommet_hole(ways, h = 50, expand = true) { //! Generate a hole fo
|
||||
}
|
||||
|
||||
module ribbon_grommet(ways, thickness) { //! Generate the STL for a printed ribbon grommet
|
||||
stl(str("ribbon_grommet_", ways, "_", thickness));
|
||||
|
||||
width = 2 * (wall + clearance) + thickness;
|
||||
slot_length = ribbon_clamp_slot(ways);
|
||||
length = slot_length + 2 * wall + 2 * overlap;
|
||||
|
||||
rotate([90, 0, 0])
|
||||
union() {
|
||||
for(side = [-1, 1])
|
||||
translate_z(side * (width - wall) / 2)
|
||||
linear_extrude(wall, center = true, convexity = 5)
|
||||
difference() {
|
||||
hull() {
|
||||
translate([-length / 2, 0])
|
||||
square([length, base]);
|
||||
stl(str("ribbon_grommet_", ways, "_", thickness))
|
||||
rotate([90, 0, 0])
|
||||
union() {
|
||||
for(side = [-1, 1])
|
||||
translate_z(side * (width - wall) / 2)
|
||||
linear_extrude(wall, center = true, convexity = 5)
|
||||
difference() {
|
||||
hull() {
|
||||
translate([-length / 2, 0])
|
||||
square([length, base]);
|
||||
|
||||
for(end = [-1, 1])
|
||||
translate([end * (length / 2 - rad), height - rad])
|
||||
semi_circle(rad);
|
||||
for(end = [-1, 1])
|
||||
translate([end * (length / 2 - rad), height - rad])
|
||||
semi_circle(rad);
|
||||
}
|
||||
translate([-slot_length / 2, base])
|
||||
square([slot_length, slot_height]);
|
||||
}
|
||||
translate([-slot_length / 2, base])
|
||||
square([slot_length, slot_height]);
|
||||
}
|
||||
|
||||
linear_extrude(width -1, center = true)
|
||||
difference() {
|
||||
ribbon_grommet_hole(ways, expand = false, h = 0);
|
||||
linear_extrude(width -1, center = true)
|
||||
difference() {
|
||||
ribbon_grommet_hole(ways, expand = false, h = 0);
|
||||
|
||||
translate([-slot_length / 2, base])
|
||||
square([slot_length, slot_height]);
|
||||
}
|
||||
}
|
||||
translate([-slot_length / 2, base])
|
||||
square([slot_length, slot_height]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module round_grommet_top(diameter, thickness, od = undef) { //! Generate the STL for a round grommet top half
|
||||
stl(str("round_grommet_top_", round(diameter * 10), "_", thickness));
|
||||
chamfer = layer_height;
|
||||
h = wall + thickness + wall;
|
||||
r1 = diameter / 2;
|
||||
r2 = od == undef ? corrected_radius(r1) + wall : od / 2;
|
||||
r3 = r2 + overlap;
|
||||
r0 = r1 + 1;
|
||||
union() {
|
||||
rotate_extrude()
|
||||
polygon([
|
||||
[r0, 0],
|
||||
[r3 - chamfer, 0],
|
||||
[r3, chamfer],
|
||||
[r3, wall],
|
||||
[r2, wall],
|
||||
[r2, h - chamfer],
|
||||
[r2 - chamfer, h],
|
||||
[r0, h],
|
||||
]);
|
||||
stl(str("round_grommet_top_", round(diameter * 10), "_", thickness))
|
||||
union() {
|
||||
rotate_extrude()
|
||||
polygon([
|
||||
[r0, 0],
|
||||
[r3 - chamfer, 0],
|
||||
[r3, chamfer],
|
||||
[r3, wall],
|
||||
[r2, wall],
|
||||
[r2, h - chamfer],
|
||||
[r2 - chamfer, h],
|
||||
[r0, h],
|
||||
]);
|
||||
|
||||
render() difference() {
|
||||
cylinder(r = r0 + eps, h = h);
|
||||
render() difference() {
|
||||
cylinder(r = r0 + eps, h = h);
|
||||
|
||||
poly_cylinder(r = r1, h = 100, center = true);
|
||||
poly_cylinder(r = r1, h = 100, center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module round_grommet_bottom(diameter, od = undef) { //! Generate the STL for a round grommet bottom half
|
||||
stl(str("round_grommet_bottom_", round(diameter * 10)));
|
||||
chamfer = layer_height;
|
||||
r1 = diameter / 2;
|
||||
r2 = od == undef ? corrected_radius(r1) + wall : od / 2;
|
||||
r3 = r2 + max(overlap, wall + chamfer);
|
||||
rotate_extrude()
|
||||
polygon([
|
||||
[r2, chamfer],
|
||||
[r2 + chamfer, 0],
|
||||
[r3, 0],
|
||||
[r3, wall - chamfer],
|
||||
[r3 - chamfer, wall],
|
||||
[r2, wall],
|
||||
]);
|
||||
stl(str("round_grommet_bottom_", round(diameter * 10)))
|
||||
rotate_extrude()
|
||||
polygon([
|
||||
[r2, chamfer],
|
||||
[r2 + chamfer, 0],
|
||||
[r3, 0],
|
||||
[r3, wall - chamfer],
|
||||
[r3 - chamfer, wall],
|
||||
[r2, wall],
|
||||
]);
|
||||
}
|
||||
|
||||
module round_grommet_hole(diameter, h = 100) //! Make a hole for a round grommet
|
||||
@@ -161,30 +161,30 @@ module mouse_grommet_hole(r, h = 50, z = undef, expand = wall + clearance) //! M
|
||||
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));
|
||||
|
||||
width = 2 * (wall + clearance) + thickness;
|
||||
length = 2 * r + 2 * wall + 2 * overlap;
|
||||
|
||||
rotate([90, 0, 0])
|
||||
union() {
|
||||
for(side = [-1, 1])
|
||||
translate_z(side * (width - wall) / 2)
|
||||
linear_extrude(wall, center = true)
|
||||
difference() {
|
||||
mouse_grommet_hole(r, z = r + wall, h = 0, expand = wall + overlap);
|
||||
stl(str("mouse_grommet_", r * 10, "_", thickness))
|
||||
rotate([90, 0, 0])
|
||||
union() {
|
||||
for(side = [-1, 1])
|
||||
translate_z(side * (width - wall) / 2)
|
||||
linear_extrude(wall, center = true)
|
||||
difference() {
|
||||
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(width - 1, center = true)
|
||||
difference() {
|
||||
mouse_grommet_hole(r, h = 0, z = r + wall, expand = wall);
|
||||
|
||||
translate([0, wall])
|
||||
mouse_grommet_hole(r, h = 0, expand = 0);
|
||||
}
|
||||
linear_extrude(width - 1, center = true)
|
||||
difference() {
|
||||
mouse_grommet_hole(r, h = 0, z = r + wall, expand = wall);
|
||||
|
||||
translate([0, wall])
|
||||
mouse_grommet_hole(r, h = 0, expand = 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module mouse_grommet_assembly(r, thickness)
|
||||
|
@@ -68,7 +68,7 @@ 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));
|
||||
) screw_length(screw, front.z - nut_trap_depth(nut), 1, nyloc = true);
|
||||
|
||||
function hinge_z(cam) = cam_screw_length(cam) - hinge_r;
|
||||
|
||||
@@ -82,7 +82,6 @@ module cam_holes(cam) {
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -93,58 +92,58 @@ module rpi_camera_focus_ring_stl() { //! Focus ring the glue onto RPI lens
|
||||
x = rad / (sin(angle / 2) + cos(angle / 2));
|
||||
r = x * sin(angle / 2);
|
||||
|
||||
difference() {
|
||||
linear_extrude(height = thickness, convexity = 5)
|
||||
difference() {
|
||||
union() {
|
||||
circle(x);
|
||||
stl("rpi_camera_focus_ring")
|
||||
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])
|
||||
rotate([0, 0, 2 * angle * i + angle])
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
stl(str("camera_back_", cam[0]))
|
||||
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, -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([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);
|
||||
}
|
||||
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);
|
||||
@@ -170,70 +169,71 @@ module camera_front(cam, hinge = 0) { //! Make the STL for a camera case front
|
||||
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);
|
||||
stl(str("camera_front_", cam[0]))
|
||||
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);
|
||||
translate_z(rad)
|
||||
sphere(rad);
|
||||
|
||||
cylinder(r = rad * (sqrt(2) - 1), h = eps);
|
||||
}
|
||||
cylinder(r = rad * (sqrt(2) - 1), h = eps);
|
||||
}
|
||||
|
||||
hinge_pos()
|
||||
hull() {
|
||||
rotate([-90, 0, -90])
|
||||
teardrop(r = hinge_r, h = hinge_h, center = false);
|
||||
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([0, -10, -hinge_r])
|
||||
cube([hinge_h, eps, 2 * hinge_r]);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
hinge_pos()
|
||||
rotate([90, 0, 90])
|
||||
teardrop_plus(r = screw_clearance_radius(hinge_screw), h = 100, 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(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(cam_back_clearance + layer_height)
|
||||
cam_holes(cam)
|
||||
rotate(90)
|
||||
poly_cylinder(r = screw_clearance_radius(screw), h = 100, 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);
|
||||
|
||||
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
|
||||
}
|
||||
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));
|
||||
@@ -253,38 +253,39 @@ module camera_bracket_position(cam) //! Position children at the bracket positio
|
||||
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);
|
||||
stl(str("camera_bracket_", cam[0])) union() {
|
||||
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);
|
||||
}
|
||||
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()
|
||||
linear_extrude(t)
|
||||
difference() {
|
||||
hull()
|
||||
camera_bracket_screw_positions(cam)
|
||||
circle(washer_radius(screw_washer(bracket_screw)) + 0.5);
|
||||
|
||||
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));
|
||||
}
|
||||
poly_circle(screw_clearance_radius(bracket_screw));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module camera_assembly(cam, angle = 0) //! Camera case assembly
|
||||
assembly(str("camera_", cam[0])) {
|
||||
assembly(str("camera_", cam[0]), ngb = true) {
|
||||
front = cam_front_size(cam);
|
||||
screw = pcb_screw(camera_pcb(cam));
|
||||
nut = screw_nut(screw);
|
||||
@@ -344,9 +345,8 @@ module camera_fastened_assembly(cam, thickness, angle = 0) {
|
||||
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));
|
||||
screw_length = screw_length(bracket_screw, thickness + t, 2, nyloc = true);
|
||||
vflip()
|
||||
translate_z(thickness)
|
||||
screw_and_washer(bracket_screw, screw_length);
|
||||
|
@@ -27,7 +27,6 @@ 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");
|
||||
pins = 8;
|
||||
pitch1 = 2;
|
||||
pitch2 = 2.54;
|
||||
@@ -43,29 +42,29 @@ module ESP12F_carrier_stl() { //! Generate the STL for an ESP12 carrier
|
||||
width1 = wpitch1 + hole + squeezed_wall * 2;
|
||||
width2 = wpitch2 + hole2 + squeezed_wall * 2;
|
||||
|
||||
difference() {
|
||||
hull() {
|
||||
translate_z(height - eps / 2)
|
||||
cube([width1, length1, eps], center = true);
|
||||
stl("ESP12F_carrier")
|
||||
difference() {
|
||||
hull() {
|
||||
translate_z(height - eps / 2)
|
||||
cube([width1, length1, eps], center = true);
|
||||
|
||||
translate_z(eps / 2)
|
||||
cube([width2, length2, eps], center = true);
|
||||
translate_z(eps / 2)
|
||||
cube([width2, length2, eps], center = true);
|
||||
}
|
||||
|
||||
for(side = [-1, 1])
|
||||
for(i = [0 : pins - 1])
|
||||
hull() {
|
||||
translate([side * wpitch1 / 2, i * pitch1 - (pins - 1) * pitch1 / 2, height])
|
||||
cube([hole, hole, eps], center = true);
|
||||
|
||||
translate([side * wpitch2 / 2, i * pitch2 - (pins - 1) * pitch2 / 2])
|
||||
cube([hole2, hole2, eps], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
for(side = [-1, 1])
|
||||
for(i = [0 : pins - 1])
|
||||
hull() {
|
||||
translate([side * wpitch1 / 2, i * pitch1 - (pins - 1) * pitch1 / 2, height])
|
||||
cube([hole, hole, eps], center = true);
|
||||
|
||||
translate([side * wpitch2 / 2, i * pitch2 - (pins - 1) * pitch2 / 2])
|
||||
cube([hole2, hole2, eps], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -78,30 +77,30 @@ module TP4056_carrier_stl() { //! Generate the STL for an TP4056 carrier, two re
|
||||
width = hole + squeezed_wall * 2;
|
||||
spacing = inch(0.9);
|
||||
|
||||
difference() {
|
||||
hull() {
|
||||
translate_z(height - eps / 2)
|
||||
cube([width, length1, eps], center = true);
|
||||
stl("TP4056_carrier")
|
||||
difference() {
|
||||
hull() {
|
||||
translate_z(height - eps / 2)
|
||||
cube([width, length1, eps], center = true);
|
||||
|
||||
translate_z(eps / 2)
|
||||
cube([width, length2, 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -113,21 +112,22 @@ module MT3608_carrier_stl() { //! Generate the STL for an MT3608 carrier, two re
|
||||
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])
|
||||
stl("MT3608_carrier")
|
||||
difference() {
|
||||
hull() {
|
||||
translate([offset, side * w_pitch_top / 2, height])
|
||||
cube([hole, hole, eps], center = true);
|
||||
translate([offset, 0, height - eps / 2])
|
||||
rounded_rectangle([width, pcb_width - 2, eps], 1, true);
|
||||
|
||||
translate([0, side * w_pitch_bot / 2])
|
||||
cube([hole, hole, eps], center = true);
|
||||
translate_z(eps / 2)
|
||||
rounded_rectangle([width, pcb_width - 2, eps], 1, true);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -25,10 +25,10 @@
|
||||
//!
|
||||
//! 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.
|
||||
//! 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.
|
||||
//! Star washers can be omitted by setting `star_washers` to false.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
@@ -72,8 +72,6 @@ module corner_block_holes(screw = def_screw) //! Place children at all the holes
|
||||
children();
|
||||
|
||||
module corner_block(screw = def_screw, name = false) { //! Generate the STL for a printed corner block
|
||||
stl(name ? name : str("corner_block", "_M", screw_radius(screw) * 20));
|
||||
|
||||
r = 1;
|
||||
cb_width = corner_block_width(screw);
|
||||
cb_height = cb_width;
|
||||
@@ -81,43 +79,45 @@ module corner_block(screw = def_screw, name = false) { //! Generate the STL for
|
||||
insert = screw_insert(screw);
|
||||
corner_rad = insert_outer_d(insert) / 2 + wall;
|
||||
offset = corner_block_hole_offset(screw);
|
||||
difference() {
|
||||
hull() {
|
||||
translate([r, r])
|
||||
rounded_cylinder(r = r, h = cb_height, r2 = r);
|
||||
|
||||
translate([r, cb_depth - r])
|
||||
cylinder(r = r, h = cb_height - corner_rad);
|
||||
stl(name ? name : str("corner_block", "_M", screw_radius(screw) * 20))
|
||||
difference() {
|
||||
hull() {
|
||||
translate([r, r])
|
||||
rounded_cylinder(r = r, h = cb_height, r2 = r);
|
||||
|
||||
translate([cb_width - r, r])
|
||||
cylinder(r = r, h = cb_height - corner_rad);
|
||||
translate([r, cb_depth - r])
|
||||
cylinder(r = r, h = cb_height - corner_rad);
|
||||
|
||||
translate([offset, offset, offset])
|
||||
sphere(corner_rad);
|
||||
translate([cb_width - r, r])
|
||||
cylinder(r = r, h = cb_height - corner_rad);
|
||||
|
||||
translate([offset, offset])
|
||||
cylinder(r = corner_rad, h = offset);
|
||||
translate([offset, offset, offset])
|
||||
sphere(corner_rad);
|
||||
|
||||
translate([offset, r, offset])
|
||||
rotate([-90, 0, 180])
|
||||
rounded_cylinder(r = corner_rad, h = r, r2 = r);
|
||||
translate([offset, offset])
|
||||
cylinder(r = corner_rad, h = offset);
|
||||
|
||||
translate([r, offset, offset])
|
||||
rotate([0, 90, 180])
|
||||
rounded_cylinder(r = corner_rad, h = r, r2 = r);
|
||||
translate([offset, r, offset])
|
||||
rotate([-90, 0, 180])
|
||||
rounded_cylinder(r = corner_rad, h = r, r2 = r);
|
||||
|
||||
translate([r, offset, offset])
|
||||
rotate([0, 90, 180])
|
||||
rounded_cylinder(r = corner_rad, h = r, r2 = r);
|
||||
}
|
||||
corner_block_v_hole(screw)
|
||||
insert_hole(insert, overshoot);
|
||||
|
||||
corner_block_h_holes(screw)
|
||||
insert_hole(insert, overshoot, true);
|
||||
|
||||
children();
|
||||
}
|
||||
corner_block_v_hole(screw)
|
||||
insert_hole(insert, overshoot);
|
||||
|
||||
corner_block_h_holes(screw)
|
||||
insert_hole(insert, overshoot, true);
|
||||
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
module corner_block_assembly(screw = def_screw, name = false) //! The printed block with inserts
|
||||
assembly(str("corner_block_M", 20 * screw_radius(screw))) {
|
||||
assembly(str("corner_block_M", 20 * screw_radius(screw)), ngb = true) {
|
||||
insert = screw_insert(screw);
|
||||
|
||||
stl_colour(name ? pp2_colour : pp1_colour)
|
||||
@@ -133,12 +133,10 @@ assembly(str("corner_block_M", 20 * screw_radius(screw))) {
|
||||
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);
|
||||
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);
|
||||
function screw_len(t) = screw_length(screw, t + overshoot, star_washers ? 2 : 1, true);
|
||||
screw_length = screw_len(thickness);
|
||||
screw_length2 = screw_len(thickness2);
|
||||
screw_length3 = screw_len(thickness3);
|
||||
|
||||
if(show_block)
|
||||
corner_block_assembly(screw, name) children();
|
||||
|
@@ -54,44 +54,44 @@ module door_hinge_hole_positions(dir = 0) { //! Position chidren
|
||||
}
|
||||
|
||||
module door_hinge(door_thickness) { //! Generates STL for the moving part of the hinge
|
||||
stl(str("door_hinge_", door_thickness));
|
||||
|
||||
hole_pitch = width - 10;
|
||||
|
||||
union() {
|
||||
rotate([90, 0, 0])
|
||||
linear_extrude(width, center = true)
|
||||
stl(str("door_hinge_", door_thickness))
|
||||
union() {
|
||||
rotate([90, 0, 0])
|
||||
linear_extrude(width, center = true)
|
||||
difference() {
|
||||
hull() {
|
||||
translate([dia / 2, thickness + door_thickness / 2])
|
||||
intersection() {
|
||||
rotate(180)
|
||||
teardrop(r = dia / 2, h = 0, truncate = false);
|
||||
|
||||
square([dia + 1, 2 * thickness + door_thickness], center = true);
|
||||
}
|
||||
|
||||
square([1, thickness + door_thickness]);
|
||||
}
|
||||
translate([dia / 2, thickness + door_thickness / 2])
|
||||
teardrop_plus(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
}
|
||||
linear_extrude(thickness)
|
||||
difference() {
|
||||
hull() {
|
||||
translate([dia / 2, thickness + door_thickness / 2])
|
||||
intersection() {
|
||||
rotate(180)
|
||||
teardrop(r = dia / 2, h = 0, truncate = false);
|
||||
translate([0, -width / 2])
|
||||
square([1, width]);
|
||||
|
||||
square([dia + 1, 2 * thickness + door_thickness], center = true);
|
||||
}
|
||||
|
||||
square([1, thickness + door_thickness]);
|
||||
for(side = [-1, 1])
|
||||
translate([-width + rad, side * (width / 2 - rad)])
|
||||
circle4n(rad);
|
||||
}
|
||||
translate([dia / 2, thickness + door_thickness / 2])
|
||||
teardrop_plus(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
rotate(180)
|
||||
vflip()
|
||||
door_hinge_hole_positions()
|
||||
poly_circle(screw_clearance_radius(screw));
|
||||
}
|
||||
linear_extrude(thickness)
|
||||
difference() {
|
||||
hull() {
|
||||
translate([0, -width / 2])
|
||||
square([1, width]);
|
||||
|
||||
for(side = [-1, 1])
|
||||
translate([-width + rad, side * (width / 2 - rad)])
|
||||
circle4n(rad);
|
||||
}
|
||||
rotate(180)
|
||||
vflip()
|
||||
door_hinge_hole_positions()
|
||||
poly_circle(screw_clearance_radius(screw));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module door_hinge_6_stl() door_hinge(6);
|
||||
@@ -136,8 +136,7 @@ module door_hinge_assembly(top, door_thickness = 6) { //! The moving assembly th
|
||||
dir = top ? -1 : 1;
|
||||
pin_x = door_hinge_pin_x();
|
||||
pin_y = door_hinge_pin_y();
|
||||
washer = screw_washer(screw);
|
||||
screw_length = screw_shorter_than(thickness + door_thickness + washer_thickness(washer));
|
||||
screw_length = screw_length(screw, thickness + door_thickness, 1);
|
||||
|
||||
translate([0, pin_y - (thickness + door_thickness / 2), dir * width / 2]) {
|
||||
rotate([90, 0, 180])
|
||||
@@ -148,20 +147,20 @@ module door_hinge_assembly(top, door_thickness = 6) { //! The moving assembly th
|
||||
screw_and_washer(screw, screw_length);
|
||||
}
|
||||
|
||||
translate([pin_x, pin_y, top ? 0 : -washer_thickness(screw_washer(pin_screw))])
|
||||
washer(screw_washer(pin_screw));
|
||||
washer = screw_washer(pin_screw);
|
||||
wt = washer_thickness(washer);
|
||||
translate([pin_x, pin_y, top ? 0 : -wt])
|
||||
washer(washer);
|
||||
|
||||
translate([pin_x, pin_y, top ? washer_thickness(screw_washer(pin_screw)) + stat_width : width])
|
||||
screw_and_washer(pin_screw, screw_longer_than(2 * washer_thickness(screw_washer(pin_screw)) + width + stat_width));
|
||||
translate([pin_x, pin_y, top ? wt + stat_width : width])
|
||||
screw_and_washer(pin_screw, screw_length(pin_screw, width + stat_width, 2, longer = true));
|
||||
}
|
||||
|
||||
module door_hinge_static_assembly(top, sheet_thickness = 3) { //! The stationary assembly
|
||||
dir = top ? -1 : 1;
|
||||
pin_x = door_hinge_pin_x();
|
||||
|
||||
stat_washer = screw_washer(stat_screw);
|
||||
stat_nut = screw_nut(stat_screw);
|
||||
stat_screw_length = screw_longer_than(thickness + sheet_thickness + 2 * washer_thickness(stat_washer) + nut_thickness(stat_nut, true));
|
||||
stat_screw_length = screw_length(stat_screw, thickness + sheet_thickness, 2, nyloc = true);
|
||||
|
||||
translate([pin_x, 0, -dir * (stat_width / 2 + washer_thickness(screw_washer(pin_screw)))])
|
||||
rotate([90, 0, 0]) {
|
||||
@@ -169,9 +168,10 @@ module door_hinge_static_assembly(top, sheet_thickness = 3) { //! The stationary
|
||||
|
||||
door_hinge_stat_hole_positions() {
|
||||
screw_and_washer(stat_screw, stat_screw_length);
|
||||
|
||||
translate_z(-thickness - sheet_thickness)
|
||||
vflip()
|
||||
nut_and_washer(stat_nut, true);
|
||||
nut_and_washer(screw_nut(stat_screw), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -38,30 +38,30 @@ function door_latch_offset() = width / 2 + 1; //! Offset of the axle from the do
|
||||
nut_trap_depth = round_to_layer(screw_head_height(screw)) + 4 * layer_height;
|
||||
|
||||
module door_latch_stl() { //! Generates the STL for the printed part
|
||||
stl("door_latch");
|
||||
|
||||
ridge = 4;
|
||||
difference() {
|
||||
union() {
|
||||
hull() {
|
||||
rounded_rectangle([length, width, thickness - tan(30) * (width - ridge) / 2], rad, center = false);
|
||||
|
||||
translate_z(thickness / 2)
|
||||
cube([length, ridge, thickness], center = true);
|
||||
stl("door_latch")
|
||||
difference() {
|
||||
union() {
|
||||
hull() {
|
||||
rounded_rectangle([length, width, thickness - tan(30) * (width - ridge) / 2], rad);
|
||||
|
||||
translate_z(thickness / 2)
|
||||
cube([length, ridge, thickness], center = true);
|
||||
}
|
||||
|
||||
cylinder(d = width, h = height);
|
||||
}
|
||||
|
||||
cylinder(d = width, h = height);
|
||||
hanging_hole(nut_trap_depth, screw_clearance_radius(screw))
|
||||
circle(r = nut_trap_radius(screw_nut(screw)), $fn = 6);
|
||||
}
|
||||
hanging_hole(nut_trap_depth, screw_clearance_radius(screw))
|
||||
circle(r = nut_trap_radius(screw_nut(screw)), $fn = 6);
|
||||
}
|
||||
}
|
||||
|
||||
module door_latch_assembly(sheet_thickness = 3) { //! The assembly for a specified sheet thickess
|
||||
washer = screw_washer(screw);
|
||||
nut = screw_nut(screw);
|
||||
|
||||
screw_length = screw_longer_than(height - nut_trap_depth + sheet_thickness + 2 * washer_thickness(washer) + nut_thickness(nut, true));
|
||||
screw_length = screw_length(screw, height - nut_trap_depth + sheet_thickness, 2, nyloc = true);
|
||||
|
||||
translate([0, -height - washer_thickness(washer)])
|
||||
rotate([-90, 0, 0]) {
|
||||
|
@@ -22,13 +22,21 @@
|
||||
//!
|
||||
//! Each link has a maximum bend angle of 45°, so the mininium radius is proportional to the link length.
|
||||
//!
|
||||
//! The travel prpoery 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 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
|
||||
@@ -38,6 +46,8 @@ 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);
|
||||
@@ -49,13 +59,11 @@ function drag_chain_z(type) = //! Outside dimension of a 180 bend
|
||||
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];
|
||||
|
||||
clearance = 0.1;
|
||||
|
||||
function drag_chain_outer_size(type) = //! Link outer dimensions
|
||||
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 if a screw lug
|
||||
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
|
||||
@@ -73,23 +81,18 @@ module screw_lug(screw, h = 0) //! Create a D shaped lug for a 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
|
||||
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 = i % 2;
|
||||
y = bool2int(i > 1);
|
||||
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([(x0 + x1) / 2, 0])
|
||||
mirror([x, 0])
|
||||
mirror([0, y])
|
||||
translate([(x1 - x0) / 2, s.y / 2 + r])
|
||||
children();
|
||||
}
|
||||
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
|
||||
@@ -100,8 +103,7 @@ function drag_chain_cam_x(type) = // how far the cam sticks out
|
||||
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) { //! One link of the chain, special case for start and end
|
||||
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);
|
||||
@@ -210,14 +212,23 @@ module drag_chain_link(type, start = false, end = false) { //! One link of the c
|
||||
translate([floor_x, -os.y / 2 + 0.5, os.z - bwall])
|
||||
cube([s.x - floor_x - clearance, os.y -1, bwall]);
|
||||
|
||||
if(start || end)
|
||||
if(start || end) {
|
||||
drag_chain_screw_positions(type, end)
|
||||
screw_lug(drag_chain_screw(type), os.z);
|
||||
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)
|
||||
poly_cylinder(r = screw_clearance_radius(drag_chain_screw(type)), h = os.z + 2 * eps, center = false);
|
||||
rotate($a)
|
||||
poly_cylinder(r = screw_clearance_radius(drag_chain_screw(type)), h = os.z + 2 * eps, center = false);
|
||||
|
||||
}
|
||||
|
||||
@@ -239,11 +250,11 @@ module drag_chain_link(type, start = false, end = false) { //! One link of the c
|
||||
}
|
||||
}
|
||||
|
||||
//! 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
|
||||
// Need to use a wrapper because can't define nested modules in an assembly
|
||||
module _drag_chain_assembly(type, pos = 0, render = false) {
|
||||
s = drag_chain_size(type);
|
||||
r = drag_chain_radius(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;
|
||||
@@ -255,31 +266,59 @@ module drag_chain_assembly(type, pos = 0) { //! Drag chain assembly
|
||||
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 ? s.x : -s.x,
|
||||
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, s.x, c, r))
|
||||
q.x <= c.x ? [p.x - sqrt(sqr(s.x) - sqr(p.z - zb)), 0, zb] // Transition back to straight
|
||||
: 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 % 2 ? pp1_colour : pp2_colour)
|
||||
drag_chain_link(type, start = n == -1, end = n == npoints - 1);
|
||||
translate([-z / 2, 0, -z / 2]) {
|
||||
stl_colour(n < 0 || n == npoints - 1 ? pp3_colour : n % 2 ? pp1_colour : pp2_colour)
|
||||
render_if(render)
|
||||
drag_chain_link(type, start = n == -1, end = n == npoints - 1, check_kids = false)
|
||||
let($fasteners = 0)
|
||||
children();
|
||||
|
||||
assembly(str(drag_chain_name(type), "_drag_chain")) {
|
||||
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);
|
||||
let($fasteners = 1) children();
|
||||
}
|
||||
|
||||
translate(points[0] - [s.x, 0, 0])
|
||||
link(-1);
|
||||
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));
|
||||
|
||||
translate(points[npoints - 1])
|
||||
hflip()
|
||||
link(npoints - 1);
|
||||
}
|
||||
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, render = false) //! Drag chain assembly
|
||||
assembly(str(drag_chain_name(type), "_drag_chain"), big = true, ngb = true)
|
||||
if($children == 2)
|
||||
_drag_chain_assembly(type, pos, render) {
|
||||
children(0);
|
||||
children(1);
|
||||
}
|
||||
else if($children == 1)
|
||||
_drag_chain_assembly(type, pos, render)
|
||||
children(0);
|
||||
else
|
||||
_drag_chain_assembly(type, pos, render);
|
||||
|
@@ -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.
|
||||
//
|
||||
|
@@ -25,10 +25,10 @@
|
||||
//!
|
||||
//! 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.
|
||||
//! 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.
|
||||
//! Star washers can be omitted by setting `star_washers` to false.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
@@ -71,42 +71,42 @@ module fixing_block_h_hole_2D(screw = def_screw) //! Position 2D child on the ho
|
||||
children();
|
||||
|
||||
module fixing_block(screw = def_screw) { //! Generate the STL
|
||||
stl(str("fixing_block_M", screw_radius(screw) * 20));
|
||||
r = 1;
|
||||
r = 1;
|
||||
insert = screw_insert(screw);
|
||||
corner_rad = insert_outer_d(insert) / 2 + wall;
|
||||
fb_width = fixing_block_width(screw);
|
||||
fb_height = fixing_block_height(screw);
|
||||
fb_depth = fixing_block_depth(screw);
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(fb_height, convexity = 5)
|
||||
difference() {
|
||||
hull() {
|
||||
for(side = [-1, 1]) {
|
||||
translate([side * (fb_width / 2 - corner_rad), fb_depth - corner_rad])
|
||||
circle4n(corner_rad);
|
||||
stl(str("fixing_block_M", screw_radius(screw) * 20))
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(fb_height, convexity = 5)
|
||||
difference() {
|
||||
hull() {
|
||||
for(side = [-1, 1]) {
|
||||
translate([side * (fb_width / 2 - corner_rad), fb_depth - corner_rad])
|
||||
circle4n(corner_rad);
|
||||
|
||||
translate([side * (fb_width / 2 - r), r])
|
||||
circle4n(r);
|
||||
translate([side * (fb_width / 2 - r), r])
|
||||
circle4n(r);
|
||||
}
|
||||
}
|
||||
fixing_block_v_holes(screw)
|
||||
poly_circle(screw_clearance_radius(screw));
|
||||
}
|
||||
fixing_block_v_holes(screw)
|
||||
poly_circle(screw_clearance_radius(screw));
|
||||
}
|
||||
}
|
||||
translate_z(fb_height)
|
||||
fixing_block_v_holes(screw)
|
||||
insert_hole(insert);
|
||||
}
|
||||
translate_z(fb_height)
|
||||
fixing_block_v_holes(screw)
|
||||
insert_hole(insert);
|
||||
|
||||
fixing_block_h_hole(screw)
|
||||
insert_hole(insert, 10, true);
|
||||
}
|
||||
fixing_block_h_hole(screw)
|
||||
insert_hole(insert, 10, true);
|
||||
}
|
||||
}
|
||||
|
||||
module fixing_block_assembly(screw = def_screw) pose([55, 180, 25], [0, 4.8, 4.8]) //! Printed part with the inserts inserted
|
||||
assembly(str("fixing_block_M", 20 * screw_radius(screw))) {
|
||||
assembly(str("fixing_block_M", 20 * screw_radius(screw)), ngb = true) {
|
||||
translate_z(fixing_block_height(screw))
|
||||
rotate([0, 180, 0])
|
||||
stl_colour(pp1_colour) render() fixing_block(screw);
|
||||
@@ -122,9 +122,7 @@ assembly(str("fixing_block_M", 20 * screw_radius(screw))) {
|
||||
|
||||
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((star_washers ? 2 : 1) * washer_thickness(washer) + thickness + insert_length(insert));
|
||||
screw_length = screw_length(screw, thickness, star_washers ? 2 : 1, true, longer = true);
|
||||
|
||||
if(thickness)
|
||||
translate_z(thickness)
|
||||
|
@@ -25,8 +25,8 @@
|
||||
//!
|
||||
//! 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.
|
||||
//! 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>
|
||||
@@ -65,8 +65,6 @@ module hinge_screw_positions(type) { //! Place children at the screw positions
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -85,37 +83,40 @@ module hinge_male(type, female = false) { //! The half with the stationary
|
||||
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);
|
||||
stl(str("hinge_", female ? "fe": "", "male_", type[0]))
|
||||
union() {
|
||||
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)));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -129,7 +130,7 @@ module hinge_both(type) { //! Both parts together for printing
|
||||
}
|
||||
|
||||
module hinge_assembly(type, angle = 0)
|
||||
assembly(str("hinge_", type[0])) { //! Assembled hinge
|
||||
assembly(str("hinge_", type[0]), ngb = true) { //! Assembled hinge
|
||||
kr = hinge_knuckle_dia(type) / 2;
|
||||
hr = hinge_pin_dia(type) / 2;
|
||||
w = hinge_width(type);
|
||||
@@ -155,9 +156,7 @@ module hinge_fastened_assembly(type, thickness1, thickness2, angle, 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;
|
||||
|
||||
@@ -165,7 +164,7 @@ module hinge_fastened_assembly(type, thickness1, thickness2, angle, show_hinge =
|
||||
if(thickness)
|
||||
hinge_screw_positions(type) {
|
||||
translate_z(t)
|
||||
screw_and_washer(screw, screw_longer_than(t + thickness + washer_t + nut_t));
|
||||
screw_and_washer(screw, screw_length(screw, t + thickness, 2, nyloc = true));
|
||||
|
||||
translate_z(-thickness)
|
||||
vflip()
|
||||
|
@@ -39,7 +39,6 @@ function foot_screw(type = foot) = type[4]; //! Screw type
|
||||
function foot_slant(type = foot) = type[5]; //! Taper angle
|
||||
|
||||
module foot(type = foot) { //! Generate STL
|
||||
stl("foot");
|
||||
h = foot_height(type);
|
||||
t = foot_thickness(type);
|
||||
r1 = washer_radius(screw_washer(foot_screw(type)));
|
||||
@@ -47,32 +46,32 @@ module foot(type = foot) { //! Generate STL
|
||||
r2 = r3 - h * tan(foot_slant(type));
|
||||
r = foot_rad(type);
|
||||
|
||||
union() {
|
||||
rotate_extrude(convexity = 3) {
|
||||
hull() {
|
||||
translate([r1, 0])
|
||||
square([r3 - r1, eps]);
|
||||
stl("foot")
|
||||
union() {
|
||||
rotate_extrude(convexity = 3) {
|
||||
hull() {
|
||||
translate([r1, 0])
|
||||
square([r3 - r1, eps]);
|
||||
|
||||
for(x = [r1 + r, r2 - r])
|
||||
translate([x, h - r])
|
||||
circle4n(r);
|
||||
for(x = [r1 + r, r2 - r])
|
||||
translate([x, h - r])
|
||||
circle4n(r);
|
||||
}
|
||||
}
|
||||
linear_extrude(t)
|
||||
difference() {
|
||||
circle(r1 + eps);
|
||||
|
||||
poly_circle( screw_clearance_radius(foot_screw(type)));
|
||||
}
|
||||
}
|
||||
linear_extrude(t)
|
||||
difference() {
|
||||
circle(r1 + eps);
|
||||
|
||||
poly_circle( screw_clearance_radius(foot_screw(type)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
squeeze = 0.5;
|
||||
screw_length = screw_longer_than(foot_thickness(type) + t + 2 * washer_thickness(washer) + nut_thickness(nut, true) - squeeze);
|
||||
screw_length = screw_length(screw, foot_thickness(type) + t - squeeze, 2, nyloc = true);
|
||||
|
||||
vflip() explode(15, true) {
|
||||
stl_colour(pp4_colour) foot(type);
|
||||
@@ -94,7 +93,6 @@ module foot_assembly(t = 0, type = foot, flip = false) { //! Assembly with faste
|
||||
}
|
||||
|
||||
module insert_foot(type = insert_foot) { //! Generate STL for foot with insert
|
||||
stl("insert_foot");
|
||||
h = foot_height(type);
|
||||
r3 = foot_diameter(type) / 2;
|
||||
r2 = r3 - h * tan(foot_slant(type));
|
||||
@@ -104,36 +102,37 @@ module insert_foot(type = insert_foot) { //! Generate STL for foot with insert
|
||||
h2 = insert_hole_length(insert);
|
||||
r4 = insert_hole_radius(insert);
|
||||
r5 = r4 + 1;
|
||||
union() {
|
||||
rotate_extrude() {
|
||||
union() {
|
||||
hull() {
|
||||
translate([r5, 0]) {
|
||||
square([r3 - r5, eps]);
|
||||
square([eps, h]);
|
||||
}
|
||||
stl("insert_foot")
|
||||
union() {
|
||||
rotate_extrude() {
|
||||
union() {
|
||||
hull() {
|
||||
translate([r5, 0]) {
|
||||
square([r3 - r5, eps]);
|
||||
square([eps, h]);
|
||||
}
|
||||
|
||||
translate([r2 - r, h - r])
|
||||
circle4n(r);
|
||||
translate([r2 - r, h - r])
|
||||
circle4n(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
linear_extrude(h2 + eps)
|
||||
difference() {
|
||||
circle(r5 + eps);
|
||||
|
||||
poly_circle(r4);
|
||||
}
|
||||
|
||||
translate_z(h2)
|
||||
cylinder(r = r5 + eps, h = h - h2);
|
||||
}
|
||||
linear_extrude(h2 + eps)
|
||||
difference() {
|
||||
circle(r5 + eps);
|
||||
|
||||
poly_circle(r4);
|
||||
}
|
||||
|
||||
translate_z(h2)
|
||||
cylinder(r = r5 + eps, h = h - h2);
|
||||
}
|
||||
}
|
||||
//
|
||||
//! Place the insert in the bottom of the foot and push home with a soldering iron with a conical bit heated to 200°C.
|
||||
//
|
||||
module insert_foot_assembly(type = insert_foot) //! Printed part with insert in place
|
||||
assembly("insert_foot") {
|
||||
assembly("insert_foot", ngb = true) {
|
||||
screw = foot_screw(type);
|
||||
insert = screw_insert(screw);
|
||||
|
||||
@@ -146,9 +145,7 @@ assembly("insert_foot") {
|
||||
|
||||
module fastened_insert_foot_assembly(t = 3, type = insert_foot) { //! Assembly with fasteners in place for specified sheet thickness
|
||||
screw = foot_screw(type);
|
||||
washer = screw_washer(screw);
|
||||
insert = screw_insert(screw);
|
||||
screw_length = screw_shorter_than(insert_length(insert) + t + 2 * washer_thickness(washer));
|
||||
screw_length = screw_length(screw, t, 2, insert = true);
|
||||
|
||||
explode(-10) insert_foot_assembly(type);
|
||||
|
||||
|
@@ -46,8 +46,6 @@ module handle_holes(h = 100) //! Drills holes for the screws
|
||||
drill(screw_clearance_radius(screw), h);
|
||||
|
||||
module handle_stl() { //! generate the STL
|
||||
stl("handle");
|
||||
|
||||
module end(end)
|
||||
translate([end * pitch / 2, 0])
|
||||
rotate_extrude()
|
||||
@@ -59,29 +57,30 @@ module handle_stl() { //! generate the STL
|
||||
square([dia / 2 + 1, dia + 1]);
|
||||
}
|
||||
|
||||
translate_z(dia / 2)
|
||||
union() {
|
||||
hull() {
|
||||
end(-1);
|
||||
stl("handle")
|
||||
translate_z(dia / 2)
|
||||
union() {
|
||||
hull() {
|
||||
end(-1);
|
||||
|
||||
end(1);
|
||||
}
|
||||
|
||||
handle_screw_positions()
|
||||
render() difference() {
|
||||
h = height + dia / 2;
|
||||
cylinder(d = dia, h = h);
|
||||
|
||||
translate_z(h)
|
||||
insert_hole(insert, 6);
|
||||
end(1);
|
||||
}
|
||||
}
|
||||
|
||||
handle_screw_positions()
|
||||
render() difference() {
|
||||
h = height + dia / 2;
|
||||
cylinder(d = dia, h = h);
|
||||
|
||||
translate_z(h)
|
||||
insert_hole(insert, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//! Place inserts in the bottom of the posts and push them home with a soldering iron with a conical bit heated to 200°C.
|
||||
//
|
||||
module handle_assembly() pose([225, 0, 150], [0, 0, 14]) //! Printed part with inserts in place
|
||||
assembly("handle") {
|
||||
assembly("handle", ngb = true) {
|
||||
translate_z(handle_height())
|
||||
stl_colour(pp1_colour) vflip() handle_stl();
|
||||
|
||||
@@ -91,7 +90,7 @@ assembly("handle") {
|
||||
}
|
||||
|
||||
module handle_fastened_assembly(thickness) { //! Assembly with fasteners in place
|
||||
screw_length = screw_longer_than(thickness + insert_length(insert) + 2 * washer_thickness(screw_washer(screw)));
|
||||
screw_length = screw_length(screw, thickness, 2, true, longer = true);
|
||||
|
||||
handle_assembly();
|
||||
|
||||
|
@@ -66,35 +66,35 @@ module pcb_mount_washer_stl() //! A plastic washer to clamp a PCB
|
||||
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();
|
||||
stl(str("pcb_mount_", pcb[0], "_", height)) union() {
|
||||
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(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);
|
||||
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_screw_positions(pcb)
|
||||
linear_extrude(height)
|
||||
pcb_mount_ring();
|
||||
|
||||
square([pcb_length(pcb) + 2 * clearance, pcb_width(pcb) + 2 * clearance], center = true);
|
||||
}
|
||||
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
|
||||
@@ -103,10 +103,9 @@ module pcb_mount_assembly(pcb, thickness, height = 5) { //! A PCB mount assembly
|
||||
|
||||
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));
|
||||
screw_length = screw_length(screw, height + t + washer_thickness + thickness, 1, nyloc = true);
|
||||
|
||||
pcb_mount_screw_positions(pcb) {
|
||||
translate_z(height + t) {
|
||||
|
154
printed/pocket_handle.scad
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2021
|
||||
// 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/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! Customisable pocket handle
|
||||
//
|
||||
include <../core.scad>
|
||||
|
||||
function pocket_handle(hand_size = [90, 40, 40], slant = 35, screw = M3_cs_cap_screw, panel_t = 3, wall = 4, rad = 4) = //! Construct a pocket_handle property list
|
||||
[hand_size, slant, screw, panel_t, wall, rad];
|
||||
|
||||
function pocket_handle_hand_size(type) = type[0]; //! Size of the hole for the fingers
|
||||
function pocket_handle_slant(type) = type[1]; //! Upward slant of the hand hole
|
||||
function pocket_handle_screw(type) = type[2]; //! Screw type, can be countersunk or not
|
||||
function pocket_handle_panel_t(type) = type[3]; //! Thickness of the panel it is mounted in
|
||||
function pocket_handle_wall(type) = type[4]; //! Wall thickness
|
||||
function pocket_handle_rad(type) = type[5]; //! Min corner rad
|
||||
|
||||
function pocket_handle_flange(type) = //! Size of the flange
|
||||
let(w = pocket_handle_wall(type),
|
||||
f = washer_diameter(screw_washer(pocket_handle_screw(type))) + 2 + w,
|
||||
s = pocket_handle_hand_size(type))
|
||||
[s.x + 2 * f, s.y + 2 * f, w];
|
||||
|
||||
module pocket_handle_hole_positions(type) { //! Place children at screw hole positions
|
||||
f = pocket_handle_flange(type);
|
||||
h = pocket_handle_hand_size(type);
|
||||
x_pitch = (f.x + h.x) / 4;
|
||||
y_pitch = (f.y + h.y) / 4;
|
||||
|
||||
for(x = [-1, 1], y = [-1, 1])
|
||||
translate([x * x_pitch, y * y_pitch])
|
||||
children();
|
||||
}
|
||||
|
||||
module pocket_handle_holes(type, h = 0) { //! Panel cutout and screw holes
|
||||
hand = pocket_handle_hand_size(type);
|
||||
w = pocket_handle_wall(type);
|
||||
slot = [hand.x + 2 * w, hand.y + 2 * w];
|
||||
t = pocket_handle_panel_t(type);
|
||||
clearance = norm([slot.y, t]) - slot.y + 0.2; // has to be enough clearance for the diagonal to swing it in
|
||||
|
||||
extrude_if(h) {
|
||||
pocket_handle_hole_positions(type)
|
||||
drill(screw_clearance_radius(pocket_handle_screw(type)), 0);
|
||||
|
||||
rounded_square([slot.x + clearance, slot.y + clearance], pocket_handle_rad(type) + w + clearance / 2);
|
||||
}
|
||||
}
|
||||
|
||||
module pocket_handle(type) { //! Generate STL for pocket_handle
|
||||
f = pocket_handle_flange(type);
|
||||
r = pocket_handle_rad(type);
|
||||
s = pocket_handle_slant(type);
|
||||
o = f.z * tan(s);
|
||||
h = pocket_handle_hand_size(type);
|
||||
t = pocket_handle_panel_t(type);
|
||||
w = pocket_handle_wall(type);
|
||||
screw = pocket_handle_screw(type) ;
|
||||
|
||||
stl("pocket_handle")
|
||||
union() {
|
||||
difference() {
|
||||
hull() {
|
||||
rounded_rectangle(f, r);
|
||||
|
||||
translate_z(f.z - eps)
|
||||
rounded_rectangle([f.x + 2 * o, f.y + 2 * o, eps], r + o);
|
||||
}
|
||||
hull() {
|
||||
rounded_rectangle([h.x, h.y, f.z + eps], r);
|
||||
|
||||
translate_z(-eps)
|
||||
rounded_rectangle([h.x + 2 * o, h.y + 2 * o, eps], r + o);
|
||||
}
|
||||
pocket_handle_hole_positions(type) {
|
||||
if(screw_head_height(screw))
|
||||
translate_z(-eps)
|
||||
poly_cylinder(r = screw_clearance_radius(screw), h = f.z + 2 * eps, center = false);
|
||||
else
|
||||
screw_polysink(screw, h = 2 * f.z + eps, alt = true);
|
||||
}
|
||||
}
|
||||
|
||||
translate_z(f.z)
|
||||
linear_extrude(t)
|
||||
difference() {
|
||||
rounded_square([h.x + 2 * w, h.y + 2 * w], r + w);
|
||||
|
||||
rounded_square([h.x, h.y], r);
|
||||
}
|
||||
|
||||
translate_z(f.z + t)
|
||||
difference() {
|
||||
height = h.z - f.z - t;
|
||||
hull() {
|
||||
rounded_rectangle([h.x + 2 * w, h.y + 2 * w, eps], r + w);
|
||||
|
||||
translate((height + w) * [0, sin(s), cos(s)])
|
||||
rounded_rectangle([h.x + 2 * w, h.y + 2 * w, eps], r + w);
|
||||
}
|
||||
|
||||
hull() {
|
||||
translate_z(-eps)
|
||||
rounded_rectangle([h.x, h.y, eps], r);
|
||||
|
||||
translate(height * [0, sin(s), cos(s)])
|
||||
rounded_rectangle([h.x, h.y, eps], r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module pocket_handle_assembly(type) { //! Assembly with fasteners in place
|
||||
f = pocket_handle_flange(type);
|
||||
screw = pocket_handle_screw(type);
|
||||
nut = screw_nut(screw);
|
||||
t = pocket_handle_panel_t(type);
|
||||
washers = screw_head_height(screw) ? 2 : 1;
|
||||
screw_length = screw_length(screw, f.z + t, washers, nyloc = true);
|
||||
|
||||
translate_z(f.z + t / 2) hflip() {
|
||||
stl_colour(pp1_colour)
|
||||
pocket_handle(type);
|
||||
|
||||
pocket_handle_hole_positions(type) {
|
||||
translate_z(f.z + t)
|
||||
explode(15, true)
|
||||
nut_and_washer(nut, true);
|
||||
|
||||
vflip()
|
||||
if(washers == 2)
|
||||
screw_and_washer(screw, screw_length);
|
||||
else
|
||||
screw(screw, screw_length);
|
||||
}
|
||||
}
|
||||
}
|
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);
|
||||
}
|
@@ -65,10 +65,8 @@ function pbox_insert(type) = screw_insert(pbox_screw(type)); //! The insert for
|
||||
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));
|
||||
let(foot = pbox_foot(type), screw = pbox_screw(type))
|
||||
screw_length(screw, pbox_base(type) + (foot ? foot_thickness(foot) : panel_thickness), 1, true);
|
||||
|
||||
function pbox_mid_offset(type) = pbox_ridges(type).y + pbox_wall(type) / 2; // Offset to wall midpoint
|
||||
|
||||
@@ -131,28 +129,27 @@ 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);
|
||||
stl(str(pbox_name(type),"_base"))
|
||||
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);
|
||||
}
|
||||
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);
|
||||
@@ -161,60 +158,61 @@ module pbox(type) { //! Generate the STL for the main case
|
||||
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);
|
||||
}
|
||||
stl(pbox_name(type))
|
||||
difference() {
|
||||
translate_z(top_thickness)
|
||||
union() {
|
||||
linear_extrude(height + eps)
|
||||
offset(-wall / 2) pbox_mid_shape(type);
|
||||
union() {
|
||||
linear_extrude(total_height)
|
||||
pbox_outer_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);
|
||||
if($children > 2)
|
||||
children(2);
|
||||
}
|
||||
difference() {
|
||||
translate_z(top_thickness)
|
||||
union() {
|
||||
linear_extrude(height + eps)
|
||||
offset(-wall / 2) pbox_mid_shape(type);
|
||||
|
||||
hull() {
|
||||
linear_extrude(ledge_h + eps)
|
||||
offset(ledge_inset)
|
||||
translate_z(height) // Recess for the base
|
||||
linear_extrude(total_height - height)
|
||||
offset(base_outset)
|
||||
pbox_inner_shape(type);
|
||||
|
||||
linear_extrude(eps)
|
||||
offset(ledge_outset)
|
||||
pbox_inner_shape(type);
|
||||
}
|
||||
pbox_screw_positions(type)
|
||||
insert_hole(pbox_insert(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);
|
||||
|
||||
// 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);
|
||||
hull() {
|
||||
linear_extrude(ledge_h + eps)
|
||||
offset(ledge_inset)
|
||||
pbox_inner_shape(type);
|
||||
|
||||
if($children > 0)
|
||||
children(0);
|
||||
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);
|
||||
}
|
||||
if($children > 1)
|
||||
children(1);
|
||||
}
|
||||
}
|
||||
|
||||
module pbox_inserts(type) //! Place the inserts for the base screws
|
||||
|
200
printed/printed_pulleys.scad
Normal file
@@ -0,0 +1,200 @@
|
||||
//
|
||||
// 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/>.
|
||||
//
|
||||
//! Printed pulleys are a remix of droftarts's (see <https://www.thingiverse.com/droftarts/designs>) Parametric Pulleys
|
||||
//! on Thingiverse (see <https://www.thingiverse.com/thing:16627>) and are licensed under the
|
||||
//! Creative Commons - Attribution - Share Alike license (see <https://creativecommons.org/licenses/by-sa/3.0/>)
|
||||
//
|
||||
|
||||
include <../core.scad>
|
||||
include <../vitamins/pulleys.scad>
|
||||
|
||||
printed_pulley_GT2_profile = [[0.747183,-0.5],[0.747183,0],[0.647876,0.037218],[0.598311,0.130528],[0.578556,0.238423],[0.547158,0.343077],[0.504649,0.443762],[0.451556,0.53975],[0.358229,0.636924],[0.2484,0.707276],[0.127259,0.750044],[0,0.76447],[-0.127259,0.750044],[-0.2484,0.707276],[-0.358229,0.636924],[-0.451556,0.53975],[-0.504797,0.443762],[-0.547291,0.343077],[-0.578605,0.238423],[-0.598311,0.130528],[-0.648009,0.037218],[-0.747183,0],[-0.747183,-0.5]];
|
||||
|
||||
function printed_pulley_inverted(type) = pulley_hub_dia(type) < pulley_flange_dia(type); //! Need to print upside down to prevent overhang
|
||||
|
||||
function printed_pulley_od(tooth_count, tooth_pitch, pitch_line_offset)
|
||||
= tooth_count * tooth_pitch / PI - 2 * pitch_line_offset;
|
||||
|
||||
module printed_pulley_teeth_from_profile(tooth_count, tooth_depth, tooth_width, tooth_profile) {
|
||||
pulley_od = printed_pulley_od(tooth_count, 2, 0.254);
|
||||
|
||||
difference() {
|
||||
rotate (90 / tooth_count)
|
||||
circle(r = pulley_od / 2, $fn = tooth_count * 4);
|
||||
tooth_distance_from_centre = sqrt(pulley_od * pulley_od - (tooth_width + 0.2) * (tooth_width + 0.2)) / 2;
|
||||
for(i = [1 : tooth_count])
|
||||
rotate(i * 360 / tooth_count)
|
||||
translate([0, -tooth_distance_from_centre])
|
||||
scale([(tooth_width + 0.2) / tooth_width, 1])
|
||||
polygon(tooth_profile);
|
||||
}
|
||||
}
|
||||
|
||||
module printed_pulley_GT2_teeth(type) {
|
||||
tooth_count = pulley_teeth(type);
|
||||
if (tooth_count == 0)
|
||||
circle(r = pulley_od(type) / 2);
|
||||
else
|
||||
printed_pulley_teeth_from_profile(tooth_count, 0.764, 1.494, printed_pulley_GT2_profile);
|
||||
}
|
||||
|
||||
|
||||
module printed_pulley_teeth(type) { //! Draw the pulley's teeth
|
||||
tooth_count = pulley_teeth(type);
|
||||
tw = pulley_od(type) * PI / (tooth_count * 2);
|
||||
ir = pulley_ir(type);
|
||||
or = pulley_od(type) / 2;
|
||||
|
||||
T_angle = 40;
|
||||
GT_r = 0.555;
|
||||
for (i = [0 : 1 : tooth_count - 1])
|
||||
rotate(i * 360 / tooth_count)
|
||||
if (pulley_type(type)[0] == "G")
|
||||
translate([0, ir + GT_r])
|
||||
hull() {
|
||||
circle(GT_r);
|
||||
translate([0, GT_r])
|
||||
square(2 * GT_r, center = true);
|
||||
}
|
||||
else
|
||||
translate([0, (ir + or) / 2])
|
||||
hull() {
|
||||
for(side = [-1, 1])
|
||||
translate([side * tw / 2, 0])
|
||||
rotate(-side * T_angle / 2)
|
||||
square([eps, (or - ir)], center = true);
|
||||
|
||||
translate([0, 1])
|
||||
square([tw, eps], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
module printed_pulley(type) { //! Draw a printable pulley
|
||||
ft = pulley_flange_thickness(type);
|
||||
hl = pulley_hub_length(type);
|
||||
w = pulley_width(type);
|
||||
r1 = pulley_bore(type) / 2;
|
||||
or = pulley_od(type) / 2;
|
||||
screw_z = pulley_screw_z(type);
|
||||
|
||||
|
||||
module core() {
|
||||
translate_z(pulley_hub_length(type) + ft)
|
||||
linear_extrude(w + 1) let($fa = 1, $fs = 0.1)
|
||||
if ("GT2" == str(pulley_type(type)[0], pulley_type(type)[1], pulley_type(type)[2]))
|
||||
difference() {
|
||||
printed_pulley_GT2_teeth(type);
|
||||
circle(d = pulley_bore(type));
|
||||
}
|
||||
else
|
||||
difference() {
|
||||
circle(or);
|
||||
printed_pulley_teeth(type);
|
||||
circle(d = pulley_bore(type));
|
||||
}
|
||||
}
|
||||
|
||||
module screw_holes() {
|
||||
if(pulley_screws(type))
|
||||
translate_z(screw_z)
|
||||
for(i = [0 : pulley_screws(type) - 1])
|
||||
rotate([-90, 180, i * -90])
|
||||
if(show_supports())
|
||||
teardrop(r = screw_pilot_hole(pulley_screw(type)), h = pulley_flange_dia(type) / 2 + 1, center = false);
|
||||
else
|
||||
cylinder(r = screw_radius(pulley_screw(type)), h = pulley_flange_dia(type) / 2 + 1);
|
||||
}
|
||||
|
||||
module hub()
|
||||
linear_extrude(hl)
|
||||
difference() {
|
||||
circle(d= pulley_hub_dia(type));
|
||||
circle(d = pulley_bore(type));
|
||||
}
|
||||
|
||||
stl(str("printed_pulley_", type[0]))
|
||||
translate_z(printed_pulley_inverted(type) ? - hl : 0) {
|
||||
// hub
|
||||
if(hl)
|
||||
translate_z(printed_pulley_inverted(type) ? hl + w + 2 * ft : 0)
|
||||
if(screw_z && screw_z < hl)
|
||||
render()
|
||||
difference() {
|
||||
hub();
|
||||
|
||||
screw_holes();
|
||||
}
|
||||
else
|
||||
hub();
|
||||
|
||||
// bottom flange
|
||||
translate_z(hl)
|
||||
linear_extrude(ft)
|
||||
difference() {
|
||||
circle(d = pulley_flange_dia(type));
|
||||
circle(d = pulley_bore(type));
|
||||
}
|
||||
|
||||
// top flange
|
||||
translate_z(hl + ft + w) {
|
||||
// inner part, supported by core
|
||||
linear_extrude(ft)
|
||||
difference() {
|
||||
circle(r = or);
|
||||
circle(d = pulley_bore(type));
|
||||
}
|
||||
// outer part at 45 degrees for printing
|
||||
rotate_extrude()
|
||||
translate([or - eps, ft])
|
||||
vflip()
|
||||
right_triangle(ft, ft);
|
||||
}
|
||||
|
||||
if(screw_z && screw_z > hl)
|
||||
render()
|
||||
difference() { // T5 pulleys have screws through the teeth
|
||||
core();
|
||||
|
||||
translate_z(printed_pulley_inverted(type) ? pulley_height(type) + hl - 2 * screw_z : 0)
|
||||
screw_holes();
|
||||
}
|
||||
else
|
||||
core();
|
||||
}
|
||||
}
|
||||
|
||||
module printed_pulley_assembly(type, colour = pp1_colour) //! Draw a printed pulley with its grub screws in place
|
||||
assembly(str("printed_pulley_", type[0]), ngb = true) {
|
||||
translate_z(pulley_offset(type)) {
|
||||
stl_colour(colour)
|
||||
if(printed_pulley_inverted(type))
|
||||
translate_z(pulley_height(type))
|
||||
hflip()
|
||||
printed_pulley(type);
|
||||
else
|
||||
printed_pulley(type);
|
||||
|
||||
if(pulley_screws(type))
|
||||
translate_z(pulley_screw_z(type))
|
||||
for(i = [0 : pulley_screws(type) - 1])
|
||||
rotate([-90, 0, i * -90])
|
||||
translate_z(pulley_bore(type) / 2 + pulley_screw_length(type))
|
||||
screw(pulley_screw(type), pulley_screw_length(type));
|
||||
}
|
||||
}
|
@@ -79,8 +79,6 @@ module psu_shroud_holes(type, cable_d, cables = 1) {
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -109,41 +107,43 @@ module psu_shroud(type, cable_d, name, cables = 1) { //! Generate the STL file f
|
||||
}
|
||||
}
|
||||
|
||||
// base and sides
|
||||
translate([centre_x, -centre_y]) {
|
||||
rounded_rectangle([depth - eps, width - eps, top], rad, center = false);
|
||||
stl(str("psu_shroud_", name)) {
|
||||
// base and sides
|
||||
translate([centre_x, -centre_y]) {
|
||||
rounded_rectangle([depth - eps, width - eps, top], rad);
|
||||
|
||||
linear_extrude(height)
|
||||
difference() {
|
||||
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();
|
||||
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
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)) {
|
||||
assembly(str("PSU_shroud_", name), ngb = true) {
|
||||
|
||||
translate_z(psu_shroud_height(type))
|
||||
vflip()
|
||||
@@ -156,8 +156,7 @@ assembly(str("PSU_shroud_", name)) {
|
||||
|
||||
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);
|
||||
screw_length = screw_length(screw,thickness + counter_bore, 2, true);
|
||||
|
||||
psu_shroud_assembly(type, cable_d, name, cables);
|
||||
|
||||
|
@@ -47,7 +47,6 @@ module ribbon_clamp_holes(ways, h = 20, screw = screw) //! Drill screw holes
|
||||
|
||||
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, screw);
|
||||
d = ribbon_clamp_width(screw);
|
||||
@@ -55,34 +54,35 @@ module ribbon_clamp(ways, screw = screw) { //! Generate STL for given number of
|
||||
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, screw), d, t], center = true);
|
||||
stl(str("ribbon_clamp_", ways, screw_d != 3 ? str("_", screw_d) : ""))
|
||||
difference() {
|
||||
union() {
|
||||
hull() {
|
||||
translate_z(h - t / 2)
|
||||
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);
|
||||
translate_z(1)
|
||||
cube([pitch, max(wall, d - 2 * (h - t)), 2], center = true);
|
||||
}
|
||||
ribbon_clamp_hole_positions(ways, screw, -1)
|
||||
cylinder(d = d, h = h);
|
||||
|
||||
ribbon_clamp_hole_positions(ways, screw, 1)
|
||||
cylinder(d = d, h = h);
|
||||
}
|
||||
ribbon_clamp_hole_positions(ways, screw, -1)
|
||||
cylinder(d = d, h = h);
|
||||
|
||||
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, screw)
|
||||
translate_z(h)
|
||||
rotate(22.5)
|
||||
insert_hole(insert, ribbon_clamp_screw_depth(screw) - insert_length(insert));
|
||||
}
|
||||
cube([ribbon_clamp_slot(ways), d + 1, ribbon_clamp_slot_depth() * 2], center = true);
|
||||
|
||||
ribbon_clamp_hole_positions(ways, screw)
|
||||
translate_z(h)
|
||||
rotate(22.5)
|
||||
insert_hole(insert, ribbon_clamp_screw_depth(screw) - insert_length(insert));
|
||||
}
|
||||
}
|
||||
|
||||
module ribbon_clamp_assembly(ways, 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) : "")) {
|
||||
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) : ""), ngb = true) {
|
||||
h = ribbon_clamp_height(screw);
|
||||
insert = screw_insert(screw);
|
||||
|
||||
@@ -101,8 +101,7 @@ 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 + ribbon_clamp_screw_depth(screw));
|
||||
screw_length = screw_length(screw, thickness + ribbon_clamp_screw_depth(screw), 2);
|
||||
|
||||
ribbon_clamp_assembly(ways, screw);
|
||||
|
||||
|
@@ -34,32 +34,31 @@ knob_height = knob_stem_h + knob_thickness;
|
||||
function knob_height() = knob_height;
|
||||
|
||||
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;
|
||||
|
||||
function wave(a) = knob_r + sin(a * knob_waves) * knob_wave;
|
||||
|
||||
union() {
|
||||
render() difference() {
|
||||
cylinder(r = knob_stem_r, h = knob_thickness + knob_stem_h);
|
||||
stl(str("screw_knob_M", screw_radius(screw) * 20))
|
||||
union() {
|
||||
render() difference() {
|
||||
cylinder(r = knob_stem_r, h = knob_thickness + knob_stem_h);
|
||||
|
||||
hanging_hole(knob_nut_trap_depth(screw), screw_clearance_radius(screw))
|
||||
rotate(45)
|
||||
circle(r = nut_trap_radius(screw_nut(screw)), $fn = 6);
|
||||
}
|
||||
linear_extrude(knob_thickness, convexity = 3)
|
||||
difference() {
|
||||
polygon(points = [for(a = [0 : 359]) [wave(a) * sin(a), wave(a) * cos(a)]]);
|
||||
|
||||
circle(knob_stem_r - eps);
|
||||
hanging_hole(knob_nut_trap_depth(screw), screw_clearance_radius(screw))
|
||||
rotate(45)
|
||||
circle(r = nut_trap_radius(screw_nut(screw)), $fn = 6);
|
||||
}
|
||||
}
|
||||
linear_extrude(knob_thickness, convexity = 3)
|
||||
difference() {
|
||||
polygon(points = [for(a = [0 : 359]) [wave(a) * sin(a), wave(a) * cos(a)]]);
|
||||
|
||||
circle(knob_stem_r - eps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Place the screw through the printed part
|
||||
module screw_knob_assembly(screw, length) //! Assembly with the screw in place
|
||||
assembly(str("screw_knob_M", 20 * screw_radius(screw), "_", length)) {
|
||||
assembly(str("screw_knob_M", 20 * screw_radius(screw), "_", length), ngb = true) {
|
||||
translate_z(knob_height)
|
||||
vflip()
|
||||
stl_colour(pp1_colour) screw_knob(screw);
|
||||
|
@@ -41,7 +41,6 @@ height = base_thickness + box_height;
|
||||
function socket_box_depth() = height; //! Outside depth of the backbox
|
||||
|
||||
module socket_box(type) { //! Generate STL of the backbox for the specified socket
|
||||
stl(str("socket_box_",type[0]));
|
||||
|
||||
screw = mains_socket_screw(type);
|
||||
screw_clearance_radius = screw_clearance_radius(screw);
|
||||
@@ -51,37 +50,38 @@ module socket_box(type) { //! Generate STL of the backbox for the specified sock
|
||||
insert_boss = mains_socket_insert_boss(type);
|
||||
insert_hole_radius = insert_hole_radius(insert);
|
||||
|
||||
difference() {
|
||||
linear_extrude(height, convexity = 5)
|
||||
face_plate(type);
|
||||
|
||||
stl(str("socket_box_",type[0]))
|
||||
difference() {
|
||||
translate_z(base_thickness)
|
||||
linear_extrude(height, convexity = 5)
|
||||
offset(-wall) offset(1) face_plate(type);
|
||||
linear_extrude(height, convexity = 5)
|
||||
face_plate(type);
|
||||
|
||||
for(side = [-1, 1])
|
||||
hull()
|
||||
for(x = [1, 2])
|
||||
translate([side * mains_socket_pitch(type) / x, 0])
|
||||
cylinder(d = insert_boss, h = 100);
|
||||
}
|
||||
//
|
||||
// Socket holes
|
||||
//
|
||||
translate_z(height)
|
||||
mains_socket_hole_positions(type) {
|
||||
poly_cylinder(r = screw_clearance_radius, h = 2 * box_height, center = true);
|
||||
difference() {
|
||||
translate_z(base_thickness)
|
||||
linear_extrude(height, convexity = 5)
|
||||
offset(-wall) offset(1) face_plate(type);
|
||||
|
||||
poly_cylinder(r = insert_hole_radius, h = 2 * insert_length, center = true);
|
||||
for(side = [-1, 1])
|
||||
hull()
|
||||
for(x = [1, 2])
|
||||
translate([side * mains_socket_pitch(type) / x, 0])
|
||||
cylinder(d = insert_boss, h = 100);
|
||||
}
|
||||
//
|
||||
// Cable hole
|
||||
//
|
||||
translate([cable_x, cable_y(type), cable_z])
|
||||
rotate([90, 0, 0])
|
||||
teardrop_plus(r = cable_d / 2, h = 30);
|
||||
}
|
||||
//
|
||||
// Socket holes
|
||||
//
|
||||
translate_z(height)
|
||||
mains_socket_hole_positions(type) {
|
||||
poly_cylinder(r = screw_clearance_radius, h = 2 * box_height, center = true);
|
||||
|
||||
poly_cylinder(r = insert_hole_radius, h = 2 * insert_length, center = true);
|
||||
}
|
||||
//
|
||||
// Cable hole
|
||||
//
|
||||
translate([cable_x, cable_y(type), cable_z])
|
||||
rotate([90, 0, 0])
|
||||
teardrop_plus(r = cable_d / 2, h = 30);
|
||||
}
|
||||
}
|
||||
|
||||
module socket_box_MKLOGIC_stl() socket_box(MKLOGIC);
|
||||
@@ -105,8 +105,7 @@ module socket_box_MKLOGIC_assembly() socket_box_assembly(MKLOGIC);
|
||||
|
||||
module socket_box_fastened_assembly(type, thickness) { //! The socket and backbox on each side of the specified panel thickness
|
||||
screw = mains_socket_screw(type);
|
||||
insert = screw_insert(screw);
|
||||
screw_length = screw_longer_than(mains_socket_height(type) + thickness + insert_length(insert));
|
||||
screw_length = screw_length(screw, mains_socket_height(type) + thickness, 0, true, longer = true);
|
||||
|
||||
explode(-50)
|
||||
translate_z(-height - thickness)
|
||||
|
@@ -61,53 +61,54 @@ module ssr_shroud_holes(type, cable_d) { //! Drill the screw and ziptie holes
|
||||
}
|
||||
|
||||
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_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) difference() {
|
||||
round(or = wall / 2 - eps, ir = 0) difference() {
|
||||
rounded_square([depth, width], rad);
|
||||
stl(str("ssr_shroud_", name)) {
|
||||
// base and sides
|
||||
translate([center_x, 0]) {
|
||||
rounded_rectangle([depth - eps, width - eps, top], rad);
|
||||
|
||||
rounded_square([depth - 2 * wall, width - 2 * wall], rad - wall);
|
||||
linear_extrude(height) difference() {
|
||||
round(or = wall / 2 - eps, ir = 0) difference() {
|
||||
rounded_square([depth, width], rad);
|
||||
|
||||
translate([depth / 2, 0])
|
||||
square([2 * rad, width], center = true);
|
||||
rounded_square([depth - 2 * wall, width - 2 * wall], rad - wall);
|
||||
|
||||
translate([depth / 2, 0])
|
||||
square([2 * rad, width], center = true);
|
||||
|
||||
}
|
||||
translate([cable_x - center_x, 0])
|
||||
square([cable_d, width + 1], center = true);
|
||||
}
|
||||
translate([cable_x - center_x, 0])
|
||||
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(wall, center = true)
|
||||
difference() {
|
||||
square([cable_d + eps, height], center = true);
|
||||
// cable slots
|
||||
for(side = [-1, 1])
|
||||
translate([cable_x, side * (width / 2 - wall / 2), height / 2])
|
||||
rotate([90, 0, 0])
|
||||
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 boss
|
||||
ssr_shroud_hole_positions(type)
|
||||
vflip()
|
||||
translate_z(height)
|
||||
rotate($side * 90)
|
||||
insert_lug(insert, wall, counter_bore);
|
||||
translate([0, height / 2])
|
||||
vertical_tearslot(h = 0, r = cable_d / 2, l = cable_d);
|
||||
}
|
||||
// insert boss
|
||||
ssr_shroud_hole_positions(type)
|
||||
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), ngb = true) {
|
||||
|
||||
translate_z(ssr_shroud_height(type))
|
||||
vflip()
|
||||
@@ -119,8 +120,7 @@ assembly(str("SSR_shroud_", name)) {
|
||||
|
||||
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);
|
||||
screw_length = screw_length(screw, thickness + counter_bore, 2, true);
|
||||
|
||||
ssr_shroud_assembly(type, cable_d, name);
|
||||
|
||||
|
@@ -75,32 +75,29 @@ module strap_holes(length, type = strap, h = 100) //! The panel cut outs
|
||||
strap_boss_shape(type);
|
||||
|
||||
module strap(length, type = strap) { //! Generate the STL for the rubber strap
|
||||
stl("strap");
|
||||
|
||||
len = length - 2 * (wall + clearance);
|
||||
w = strap_width(type);
|
||||
|
||||
linear_extrude(strap_thickness(type), convexity = 3)
|
||||
difference() {
|
||||
rounded_square([len, w], w / 2 - eps);
|
||||
stl("strap")
|
||||
linear_extrude(strap_thickness(type), convexity = 3)
|
||||
difference() {
|
||||
rounded_square([len, w], w / 2 - eps);
|
||||
|
||||
for(end = [-1, 1])
|
||||
translate([end * (len / 2 - strap_min_width(type) - strap_boss_r(type) - clearance), 0])
|
||||
rotate(end * 90 + 90)
|
||||
hull() {
|
||||
offset(clearance)
|
||||
strap_boss_shape(type);
|
||||
|
||||
translate([strap_extension(type) / 2, 0])
|
||||
for(end = [-1, 1])
|
||||
translate([end * (len / 2 - strap_min_width(type) - strap_boss_r(type) - clearance), 0])
|
||||
rotate(end * 90 + 90)
|
||||
hull() {
|
||||
offset(clearance)
|
||||
strap_boss_shape(type);
|
||||
}
|
||||
}
|
||||
|
||||
translate([strap_extension(type) / 2, 0])
|
||||
offset(clearance)
|
||||
strap_boss_shape(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module strap_end(type = strap) { //! Generate the STL for end piece
|
||||
stl("strap_end");
|
||||
|
||||
z1 = strap_height(type) - strap_thickness(type) - clearance;
|
||||
z2 = strap_height(type) + strap_key(type);
|
||||
r1 = strap_boss_r(type) - 1;
|
||||
@@ -121,48 +118,49 @@ module strap_end(type = strap) { //! Generate the STL for end piece
|
||||
circle(r1);
|
||||
}
|
||||
|
||||
union() {
|
||||
linear_extrude(z1)
|
||||
with_hole()
|
||||
outer();
|
||||
|
||||
translate_z(z1)
|
||||
linear_extrude(strap_height(type) - z1)
|
||||
difference() {
|
||||
stl("strap_end")
|
||||
union() {
|
||||
linear_extrude(z1)
|
||||
with_hole()
|
||||
outer();
|
||||
|
||||
hull() {
|
||||
translate([0, -strap_width(type) / 2 - clearance])
|
||||
square([strap_boss_r(type) + overlap, strap_width(type) + 2 * clearance]);
|
||||
translate_z(z1)
|
||||
linear_extrude(strap_height(type) - z1)
|
||||
difference() {
|
||||
outer();
|
||||
|
||||
translate([-strap_extension(type) / 2, 0])
|
||||
circle(d = strap_width(type) + 2 * clearance);
|
||||
hull() {
|
||||
translate([0, -strap_width(type) / 2 - clearance])
|
||||
square([strap_boss_r(type) + overlap, strap_width(type) + 2 * clearance]);
|
||||
|
||||
translate([-strap_extension(type) / 2, 0])
|
||||
circle(d = strap_width(type) + 2 * clearance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
linear_extrude(strap_height(type) - layer_height)
|
||||
with_hole()
|
||||
strap_boss_shape(type);
|
||||
linear_extrude(strap_height(type) - layer_height)
|
||||
with_hole()
|
||||
strap_boss_shape(type);
|
||||
|
||||
linear_extrude(z2)
|
||||
with_hole()
|
||||
offset(cnc_bit_r)
|
||||
offset(-step - cnc_bit_r)
|
||||
strap_boss_shape(type);
|
||||
linear_extrude(z2)
|
||||
with_hole()
|
||||
offset(cnc_bit_r)
|
||||
offset(-step - cnc_bit_r)
|
||||
strap_boss_shape(type);
|
||||
|
||||
render() difference() {
|
||||
cylinder(r = r1 + eps, h = z2);
|
||||
render() difference() {
|
||||
cylinder(r = r1 + eps, h = z2);
|
||||
|
||||
translate_z(z2)
|
||||
insert_hole(strap_insert(type), counterbore);
|
||||
translate_z(z2)
|
||||
insert_hole(strap_insert(type), counterbore);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//! * Place the insert into the hole and push home with a soldering iron with a tapered bit heated to 200°C.
|
||||
//
|
||||
module strap_end_assembly(type = strap)
|
||||
assembly("strap_end") {
|
||||
assembly("strap_end", ngb = true) {
|
||||
stl_colour(pp1_colour)
|
||||
strap_end(type);
|
||||
|
||||
@@ -172,11 +170,9 @@ assembly("strap_end") {
|
||||
|
||||
module strap_assembly(length, type = strap) { //! Assembly with screws in place
|
||||
screw = strap_screw(type);
|
||||
washer = screw_washer(screw);
|
||||
penny = penny_washer(washer);
|
||||
insert = strap_insert(type);
|
||||
penny = penny_washer(screw_washer(screw));
|
||||
|
||||
screw_length = screw_shorter_than(washer_thickness(washer) + washer_thickness(penny) + insert_length(insert) + panel_clearance + counterbore);
|
||||
screw_length = screw_length(screw, washer_thickness(penny) + panel_clearance + counterbore, 1, true);
|
||||
|
||||
stl_colour(pp4_colour) strap(length, type);
|
||||
|
||||
|
@@ -39,8 +39,6 @@ def _scrape_blurb(lines):
|
||||
if b:
|
||||
break
|
||||
text += t
|
||||
if len(text):
|
||||
text += '\n'
|
||||
return text
|
||||
|
||||
def scrape_blurb(scad_file):
|
||||
@@ -49,6 +47,16 @@ def scrape_blurb(scad_file):
|
||||
lines = file.readlines()
|
||||
return _scrape_blurb(lines)
|
||||
|
||||
def split_blurb(lines):
|
||||
""" Split blurb on horizontal rules."""
|
||||
blurbs = [""]
|
||||
for line in lines.split('\n')[:-1]:
|
||||
if re.match(r'\*{3,}',line):
|
||||
blurbs.append("")
|
||||
else:
|
||||
blurbs[-1] += line + '\n'
|
||||
return blurbs
|
||||
|
||||
def scrape_module_blurb(lines):
|
||||
""" Find the Markup lines before the last function or module. """
|
||||
text = ""
|
||||
|
@@ -60,6 +60,8 @@ class BOM:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.big = None
|
||||
self.ngb = False
|
||||
self.zoomed = 0
|
||||
self.count = 1
|
||||
self.vitamins = {}
|
||||
self.printed = {}
|
||||
@@ -73,6 +75,8 @@ class BOM:
|
||||
return {
|
||||
"name" : self.name,
|
||||
"big" : self.big,
|
||||
"ngb" : self.ngb,
|
||||
"zoomed" : self.zoomed,
|
||||
"count" : self.count,
|
||||
"assemblies" : assemblies,
|
||||
"vitamins" : {v : self.vitamins[v].data() for v in self.vitamins},
|
||||
@@ -249,7 +253,7 @@ def boms(target = None, assembly = None):
|
||||
#
|
||||
# Run openscad
|
||||
#
|
||||
openscad.run("-D", "$bom=2", "-D", "$preview=true", "--hardwarnings", "-o", "openscad.echo", "-d", bom_dir + "/bom.deps", bom_maker_name)
|
||||
openscad.run("-D", "$bom=2", "-D", "$preview=true", "-o", "openscad.echo", "-d", bom_dir + "/bom.deps", bom_maker_name)
|
||||
os.remove(bom_maker_name)
|
||||
print("Generating bom ...", end=" ")
|
||||
|
||||
|
164
scripts/changelog.py
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# NopSCADlib Copyright Chris Palmer 2021
|
||||
# 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/>.
|
||||
#
|
||||
#! Creates the changelog from the git log
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
filename = 'CHANGELOG.md'
|
||||
|
||||
def tag_version(t):
|
||||
""" Format a version tag """
|
||||
return 'v%d.%d.%d' % t
|
||||
|
||||
def initials(name):
|
||||
""" Convert full name to initials with a tooltip """
|
||||
i = ''.join([n[0].upper() + '.' for n in name.split(' ')])
|
||||
return '[%s](# "%s")' % (i, name)
|
||||
|
||||
def get_remote_url():
|
||||
""" Get the git remote URL for the repository """
|
||||
url = subprocess.check_output(["git", "config", "--get", "remote.origin.url"]).decode("utf-8").strip("\n")
|
||||
if url.startswith("git@"):
|
||||
url = url.replace(":", "/", 1).replace("git@", "https://", 1)
|
||||
if url.endswith(".git"):
|
||||
url = url[:-4]
|
||||
return url
|
||||
|
||||
def iscode(word):
|
||||
""" try to guess if the word is code """
|
||||
endings = ['()', '*']
|
||||
starts = ['$', '--']
|
||||
anywhere = ['.', '_', '=', '[', '/']
|
||||
words = ['center', 'false', 'true', 'ngb']
|
||||
|
||||
for w in words:
|
||||
if word == w:
|
||||
return True
|
||||
|
||||
for end in endings:
|
||||
if word.endswith(end):
|
||||
return True
|
||||
|
||||
for start in starts:
|
||||
if word.startswith(start):
|
||||
return True
|
||||
|
||||
for any in anywhere:
|
||||
if word.find(any) >= 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def codify(word, url):
|
||||
""" if a word is deemed code enclose it backticks """
|
||||
if word:
|
||||
if re.match(r'#[0-9]+', word):
|
||||
return '[%s](%s "show issue")' % (word, url + '/issues/' + word[1:])
|
||||
if iscode(word):
|
||||
return '`' + word + '`'
|
||||
return word
|
||||
|
||||
|
||||
def fixup_comment(comment, url):
|
||||
""" markup code words and fix new paragraphs """
|
||||
result = ''
|
||||
word = ''
|
||||
code = False
|
||||
for i, c in enumerate(comment):
|
||||
if c == '`' or code: # Already a code block
|
||||
result += c # Copy verbatim
|
||||
if c == '`': code = not code # Keep track of state
|
||||
else:
|
||||
if c in ' \n' or (c == '.' and (i + 1 == len(comment) or comment[i + 1] in ' \n')): # if a word terminator
|
||||
result += codify(word, url) + c # Add codified word before terminator
|
||||
word = ''
|
||||
else:
|
||||
word += c # Accumulate next word
|
||||
result += codify(word, url) # In case comment ends without a terminator
|
||||
return result.replace('\n\n','\n\n * ') # Give new paragraphs a bullet point
|
||||
|
||||
class Commit(): # members dynamically added from commit_fields
|
||||
pass
|
||||
|
||||
blurb = """
|
||||
# %s Changelog
|
||||
This changelog is generated by `changelog.py` using manually added semantic version tags to classify commits as breaking changes, additions or fixes.
|
||||
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
url = get_remote_url()
|
||||
|
||||
commit_fields = {
|
||||
'hash': "%H|", # commit commit_hash
|
||||
'tag': "%D|", # tag
|
||||
'author': "%aN|", # author name
|
||||
'date': " %as|", # author date short form
|
||||
'comment': "%B~" # body
|
||||
}
|
||||
|
||||
# Produce the git log
|
||||
format = ''.join([v for k, v in commit_fields.items()])
|
||||
text = subprocess.check_output(["git", "log", "--topo-order", "--format=" + format]).decode("utf-8")
|
||||
|
||||
# Process the log into a list of Commit objects
|
||||
commits = []
|
||||
for line in text.split('~'):
|
||||
line = line.strip('\n')
|
||||
if line:
|
||||
fields = line.split('|')
|
||||
commit = Commit()
|
||||
for i, k in enumerate(commit_fields):
|
||||
exec('commit.%s = """%s"""' % (k, fields[i]), locals())
|
||||
# Convert version tag to tuple
|
||||
if commit.tag:
|
||||
match = re.match(r'.*tag: v([0-9]+)\.([0-9]+)\.([0-9]+).*', commit.tag)
|
||||
commit.tag = (int(match.group(1)), int(match.group(2)), int(match.group(3))) if match else ''
|
||||
commits.append(commit)
|
||||
|
||||
# Format the results from the Commit objects
|
||||
with open(filename, "wt") as file:
|
||||
print(blurb % url.split('/')[-1], file = file)
|
||||
for i, c in enumerate(commits):
|
||||
if c.tag:
|
||||
ver = tag_version(c.tag)
|
||||
level, type = (3, 'Fixes') if c.tag[2] else (2, 'Additions') if c.tag[1] else (1, 'Breaking Changes') if c.tag[0] else (1, 'First publicised version')
|
||||
|
||||
# Find the previous tagged commit
|
||||
j = i + 1
|
||||
diff = ''
|
||||
while j < len(commits):
|
||||
if commits[j].tag:
|
||||
last_ver = tag_version(commits[j].tag)
|
||||
diff = '[...](%s "diff with %s")' % (url + '/compare/' + last_ver + '...' + ver, last_ver)
|
||||
break
|
||||
j += 1
|
||||
|
||||
# Print verson info
|
||||
print('%s [%s](%s "show release") %s %s' % ('#' * (level + 1), ver, url + '/releases/tag/' + ver, type, diff), file = file)
|
||||
|
||||
# Print commits excluding merges
|
||||
|
||||
if not c.comment.startswith('Merge branch') and not c.comment.startswith('Merge pull') and not re.match(r'U..ated changelog.*', c.comment):
|
||||
print('* %s [`%s`](%s "show commit") %s %s\n' % (c.date, c.hash[:7], url + '/commit/' + c.hash, initials(c.author), fixup_comment(c.comment, url)), file = file)
|
@@ -18,6 +18,7 @@
|
||||
#
|
||||
import os
|
||||
from set_config import source_dir
|
||||
from colorama import Fore
|
||||
|
||||
def mtime(file):
|
||||
if os.path.isfile(file):
|
||||
@@ -41,13 +42,13 @@ def read_deps(dname):
|
||||
def check_deps(target, dname):
|
||||
target_mtime = mtime(target)
|
||||
if not target_mtime:
|
||||
return target + " missing"
|
||||
return Fore.CYAN + target + " missing" + Fore.WHITE
|
||||
if not os.path.isfile(dname):
|
||||
return "no deps"
|
||||
return Fore.CYAN + "no deps" + Fore.WHITE
|
||||
deps = read_deps(dname)
|
||||
for dep in deps:
|
||||
if mtime(dep) > target_mtime:
|
||||
return dep + ' changed'
|
||||
return Fore.CYAN + dep + ' changed' + Fore.WHITE
|
||||
return None
|
||||
|
||||
def source_dirs(bom_dir):
|
||||
|
@@ -36,7 +36,7 @@ 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.
|
||||
|
||||
@@ -60,7 +60,8 @@ 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)
|
||||
else:
|
||||
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)
|
||||
|
@@ -28,7 +28,10 @@ from set_config import *
|
||||
import time
|
||||
import times
|
||||
from deps import *
|
||||
from tmpdir import *
|
||||
import json
|
||||
import shutil
|
||||
from colorama import Fore, init
|
||||
|
||||
def bom_to_parts(bom_dir, part_type, assembly = None):
|
||||
#
|
||||
@@ -43,7 +46,7 @@ def bom_to_parts(bom_dir, part_type, assembly = None):
|
||||
if words:
|
||||
last_word = words[-1]
|
||||
if last_word.endswith(suffix):
|
||||
part_files.append(last_word[:-4] + '.' + part_type)
|
||||
part_files.append(last_word[:-4] + '.' + part_type)
|
||||
return part_files
|
||||
|
||||
def usage(t):
|
||||
@@ -62,20 +65,29 @@ def make_parts(target, part_type, parts = None):
|
||||
#
|
||||
top_dir = set_config(target, lambda: usage(part_type))
|
||||
target_dir = top_dir + part_type + 's'
|
||||
deps_dir = top_dir + "deps"
|
||||
deps_dir = target_dir + "/deps"
|
||||
bom_dir = top_dir + "bom"
|
||||
tmp_dir = mktmpdir(top_dir)
|
||||
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
|
||||
if not os.path.isdir(deps_dir):
|
||||
os.makedirs(deps_dir)
|
||||
|
||||
old_deps = top_dir + 'deps' #old location
|
||||
if os.path.isdir(old_deps):
|
||||
shutil.rmtree(old_deps)
|
||||
|
||||
times.read_times(target_dir)
|
||||
#
|
||||
# Decide which files to make
|
||||
#
|
||||
all_parts = bom_to_parts(bom_dir, part_type)
|
||||
if parts:
|
||||
targets = list(parts) #copy the list so we dont modify the list passed in
|
||||
else:
|
||||
targets = bom_to_parts(bom_dir, part_type)
|
||||
targets = list(all_parts)
|
||||
for file in os.listdir(target_dir):
|
||||
if file.endswith('.' + part_type):
|
||||
if not file in targets:
|
||||
@@ -119,18 +131,18 @@ def make_parts(target, part_type, parts = None):
|
||||
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"
|
||||
changed = Fore.CYAN + "No bounds" + Fore.WHITE
|
||||
if changed:
|
||||
print(changed)
|
||||
#
|
||||
# make a file to use the module
|
||||
#
|
||||
part_maker_name = part_type + ".scad"
|
||||
part_maker_name = tmp_dir + '/' + part_type + ".scad"
|
||||
with open(part_maker_name, "w") as f:
|
||||
f.write("use <%s/%s>\n" % (dir, filename))
|
||||
f.write("use <%s/%s>\n" % (reltmp(dir, target), filename))
|
||||
f.write("%s();\n" % module);
|
||||
t = time.time()
|
||||
openscad.run("-D$bom=1", "-d", dname, "-o", part_file, part_maker_name)
|
||||
openscad.run("-o", part_file, part_maker_name, "-D$bom=1", "-d", dname)
|
||||
times.add_time(part, t)
|
||||
if part_type == 'stl':
|
||||
bounds = c14n_stl.canonicalise(part_file)
|
||||
@@ -144,10 +156,14 @@ def make_parts(target, part_type, parts = None):
|
||||
with open(bounds_fname, 'w') as outfile:
|
||||
json.dump(bounds_map, outfile, indent = 4)
|
||||
#
|
||||
# Remove tmp dir
|
||||
#
|
||||
rmtmpdir(tmp_dir)
|
||||
#
|
||||
# List the ones we didn't find
|
||||
#
|
||||
if targets:
|
||||
for part in targets:
|
||||
print("Could not find a module called", part[:-4] + module_suffix, "to make", part)
|
||||
usage(part_type)
|
||||
times.print_times()
|
||||
times.print_times(all_parts)
|
||||
|
@@ -52,7 +52,7 @@ def gallery(force):
|
||||
if os.path.isfile(document):
|
||||
with open(document, 'rt') as readme:
|
||||
for line in readme.readlines():
|
||||
match = re.match(r"^.*!(\[.*\]\(.*\)).*$", line)
|
||||
match = re.search(r"!(\[.*\]\(.*\))", line)
|
||||
if match:
|
||||
image = match.group(0)
|
||||
if image.startswith(':
|
||||
@@ -68,9 +68,10 @@ def gallery(force):
|
||||
match = re.match(r"^(#+).*$", line)
|
||||
if match:
|
||||
line = '#' + line
|
||||
if line == '---\n':
|
||||
break;
|
||||
print(line[:-1], file = output_file)
|
||||
if line == '---\n' or line == '<span></span>\n':
|
||||
break
|
||||
if line != '<a name="TOP"></a>\n':
|
||||
print(line[:-1], file = output_file)
|
||||
else:
|
||||
print(Fore.MAGENTA + "Can't find", document, Fore.WHITE);
|
||||
with open(target_dir + "/readme.html", "wt") as html_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
|
||||
|
||||
|
@@ -18,28 +18,37 @@
|
||||
#
|
||||
|
||||
#
|
||||
# Run openscad
|
||||
#! Run `openscad.exe` and capture `stdout` and `stderr` in `openscad.log` as well as printing to the console.
|
||||
#
|
||||
from __future__ import print_function
|
||||
|
||||
import subprocess, sys
|
||||
|
||||
def run_list(args, silent = False):
|
||||
cmd = ["openscad"] + args
|
||||
def run_list(args, silent = False, verbose = False):
|
||||
cmd = ["openscad"] + args + ["--hardwarnings"]
|
||||
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:
|
||||
log_file = "openscad.echo" if "openscad.echo" in cmd else "openscad.log"
|
||||
bad = False
|
||||
for line in open(log_file, "rt"):
|
||||
if verbose or 'ERROR:' in line or 'WARNING:' in line:
|
||||
bad = True
|
||||
print(line[:-1])
|
||||
if rc:
|
||||
sys.exit(rc)
|
||||
|
||||
if bad:
|
||||
sys.exit(1)
|
||||
|
||||
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)
|
||||
|
@@ -20,6 +20,7 @@
|
||||
# Set command line options from enviroment variables and check if they have changed
|
||||
|
||||
import json, os, deps
|
||||
from colorama import Fore, init
|
||||
|
||||
def check_options(dir = '.'):
|
||||
global options, options_mtime
|
||||
@@ -37,7 +38,7 @@ def check_options(dir = '.'):
|
||||
|
||||
def have_changed(changed, target):
|
||||
if not changed and deps.mtime(target) < options_mtime:
|
||||
return "command line options changed"
|
||||
return Fore.CYAN + "command line options changed" + Fore.WHITE
|
||||
return changed
|
||||
|
||||
def list():
|
||||
|
@@ -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/>.
|
||||
#
|
||||
#! Panelises DXF files so they can be routed together by running scad files found in the ```panels``` directory.
|
||||
#! 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
|
||||
|
@@ -26,8 +26,10 @@ import sys
|
||||
import c14n_stl
|
||||
from set_config import *
|
||||
from deps import *
|
||||
from shutil import copyfile
|
||||
import shutil
|
||||
import re
|
||||
import time
|
||||
import times
|
||||
|
||||
source_dirs = { "stl" : "platters", "dxf" : "panels" }
|
||||
target_dirs = { "stl" : "printed", "dxf" : "routed" }
|
||||
@@ -41,22 +43,32 @@ def plateup(target, part_type, usage = None):
|
||||
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_used = []
|
||||
all_sources = []
|
||||
all_parts = []
|
||||
read_times = False
|
||||
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)
|
||||
|
||||
if not read_times:
|
||||
times.read_times(target_dir)
|
||||
read_times = True
|
||||
#
|
||||
# Make the deps dir
|
||||
#
|
||||
deps_dir = dir + "/deps"
|
||||
deps_dir = parts_dir + "/deps"
|
||||
if not os.path.isdir(deps_dir):
|
||||
os.makedirs(deps_dir)
|
||||
|
||||
if os.path.isdir(dir + '/deps'): #old deps
|
||||
shutil.rmtree(dir + '/deps')
|
||||
#
|
||||
# Decide which files to make
|
||||
#
|
||||
@@ -65,40 +77,55 @@ def plateup(target, part_type, usage = None):
|
||||
#
|
||||
# Run OpenSCAD on the source files to make the targets
|
||||
#
|
||||
target_def = ['-D$target="%s"' % target] if target else []
|
||||
cwd_def = ['-D$cwd="%s"' % os.getcwd().replace('\\', '/')]
|
||||
for src in sources:
|
||||
src_file = dir + '/' + src
|
||||
part_file = target_dir + '/' + src[:-4] + part_type
|
||||
part = src[:-4] + part_type
|
||||
all_parts.append(part)
|
||||
part_file = target_dir + '/' + part
|
||||
uses_file = deps_dir + '/' + src[:-4] + 'txt'
|
||||
dname = deps_name(deps_dir, src)
|
||||
changed = check_deps(part_file, dname)
|
||||
oldest = part_file if mtime(part_file) < mtime(uses_file) else uses_file
|
||||
changed = check_deps(oldest, dname)
|
||||
used = []
|
||||
if changed:
|
||||
print(changed)
|
||||
openscad.run("-D$bom=1", "-d", dname, "-o", part_file, src_file)
|
||||
t = time.time()
|
||||
openscad.run_list(["-D$bom=1"] + target_def + cwd_def + ["-d", dname, "-o", part_file, src_file])
|
||||
if part_type == 'stl':
|
||||
c14n_stl.canonicalise(part_file)
|
||||
times.add_time(part, t)
|
||||
log_name = 'openscad.log'
|
||||
#
|
||||
# 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))
|
||||
with open(uses_file, "wt") as file:
|
||||
for part in used:
|
||||
print(part, file = file)
|
||||
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))
|
||||
with open(uses_file, "rt") as file:
|
||||
for line in file:
|
||||
used.append(line[:-1])
|
||||
all_used += used
|
||||
|
||||
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:
|
||||
if file.endswith('.' + part_type) and not file in all_used:
|
||||
src = parts_dir + '/' + file
|
||||
dst = target_dir + '/' + file
|
||||
if mtime(src) > mtime(dst):
|
||||
print("Copying %s to %s" % (src, dst))
|
||||
copyfile(src, dst)
|
||||
shutil.copyfile(src, dst)
|
||||
copied.append(file)
|
||||
#
|
||||
# Remove any cruft
|
||||
@@ -109,3 +136,12 @@ def plateup(target, part_type, usage = None):
|
||||
if not file in targets and not file in copied:
|
||||
print("Removing %s" % file)
|
||||
os.remove(target_dir + '/' + file)
|
||||
|
||||
targets = [file[:-4] + 'txt' for file in all_sources]
|
||||
for file in os.listdir(deps_dir):
|
||||
if file.endswith('.' + 'txt'):
|
||||
if not file in targets:
|
||||
print("Removing %s" % file)
|
||||
os.remove(deps_dir + '/' + file)
|
||||
|
||||
times.print_times(all_parts)
|
||||
|
@@ -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 build plates of STL files for efficient printing by running scad files found in the ```platters``` directory.
|
||||
#! 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
|
||||
|
@@ -1,22 +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```. |
|
||||
| ```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. |
|
||||
| `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. |
|
||||
| `changelog.py` | Creates the changelog from the git log |
|
||||
| `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`. |
|
||||
| `openscad.py` | Run `openscad.exe` and capture `stdout` and `stderr` in `openscad.log` as well as printing to the console. |
|
||||
| `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. |
|
||||
|
@@ -30,6 +30,7 @@ from tests import do_cmd, update_image, colour_scheme, background
|
||||
from deps import mtime
|
||||
from colorama import init
|
||||
import json
|
||||
from tmpdir import *
|
||||
|
||||
def usage():
|
||||
print("\nusage:\n\trender [target_config] - Render images of the stl and dxf files.");
|
||||
@@ -40,6 +41,7 @@ def render(target, type):
|
||||
# Make the target directory
|
||||
#
|
||||
top_dir = set_config(target, usage)
|
||||
tmp_dir = mktmpdir(top_dir)
|
||||
target_dir = top_dir + type + 's'
|
||||
bom_dir = top_dir + 'bom'
|
||||
if not os.path.isdir(target_dir):
|
||||
@@ -80,7 +82,7 @@ def render(target, type):
|
||||
# make a file to import the stl
|
||||
#
|
||||
if mtime(part_file) > mtime(png_name):
|
||||
png_maker_name = "png.scad"
|
||||
png_maker_name = tmp_dir + "/png.scad"
|
||||
pp1 = [0, 146/255, 0]
|
||||
colour = pp1
|
||||
if part in colours:
|
||||
@@ -88,15 +90,19 @@ def render(target, type):
|
||||
if not '[' in colour:
|
||||
colour = '"' + colour + '"'
|
||||
with open(png_maker_name, "w") as f:
|
||||
f.write('color(%s) import("%s");\n' % (colour, part_file))
|
||||
f.write('color(%s) import("%s");\n' % (colour, reltmp(part_file, target)))
|
||||
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' 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);
|
||||
tmp_name = tmp_dir + '/' + part[:-4] + '.png'
|
||||
openscad.run("-o", tmp_name, png_maker_name, colour_scheme, "--projection=p", "--imgsize=4096,4096", cam, render, "--autocenter", "--viewall");
|
||||
do_cmd(("magick "+ tmp_name + " -trim -resize 280x280 -background %s -gravity Center -extent 280x280 -bordercolor %s -border 10 %s"
|
||||
% (background, background, tmp_name)).split())
|
||||
update_image(tmp_name, png_name)
|
||||
os.remove(png_maker_name)
|
||||
#
|
||||
# Remove tmp dir
|
||||
#
|
||||
rmtmpdir(tmp_dir)
|
||||
|
||||
if __name__ == '__main__':
|
||||
init()
|
||||
|
@@ -69,17 +69,22 @@ def set_config(target, usage = None):
|
||||
sys.exit(1)
|
||||
|
||||
fname = source_dir + "/target.scad"
|
||||
text = "include <config_%s.scad>\n" % target;
|
||||
line = ""
|
||||
text = ['include <config_%s.scad>\n' % target,
|
||||
'$target = "%s";\n' % target,
|
||||
'$cwd="%s";\n' % os.getcwd().replace('\\', '/')
|
||||
]
|
||||
|
||||
lines = [""]
|
||||
try:
|
||||
with open(fname,"rt") as f:
|
||||
line = f.read()
|
||||
lines = f.readlines()
|
||||
except:
|
||||
pass
|
||||
|
||||
if line != text:
|
||||
if lines != text:
|
||||
with open(fname,"wt") as f:
|
||||
f. write(text);
|
||||
for t in text:
|
||||
f. write(t);
|
||||
return target + "/"
|
||||
|
||||
def usage():
|
||||
|
@@ -34,6 +34,7 @@ import shutil
|
||||
from deps import *
|
||||
from blurb import *
|
||||
from colorama import Fore
|
||||
from tmpdir import *
|
||||
|
||||
w = 4096
|
||||
h = w
|
||||
@@ -50,13 +51,17 @@ def do_cmd(cmd, output = sys.stdout):
|
||||
return subprocess.call(cmd, stdout = output, stderr = output)
|
||||
|
||||
def compare_images(a, b, c):
|
||||
if not os.path.isfile(b):
|
||||
print(Fore.MAGENTA + "Failed to generate %s while making %s" % (b, a), Fore.WHITE)
|
||||
sys.exit(1)
|
||||
if not os.path.isfile(a):
|
||||
return -1
|
||||
log_name = 'magick.log'
|
||||
with open(log_name, 'w') as output:
|
||||
do_cmd(("magick compare -metric AE -fuzz %d%% %s %s %s" % (fuzz, a, b, c)).split(), output = output)
|
||||
with open(log_name, 'r') as f:
|
||||
pixels = int(f.read().strip())
|
||||
pixels = f.read().strip()
|
||||
pixels = int(float(pixels if pixels.isnumeric() else -1))
|
||||
os.remove(log_name)
|
||||
return pixels
|
||||
|
||||
@@ -91,6 +96,7 @@ def usage():
|
||||
|
||||
def tests(tests):
|
||||
scad_dir = "tests"
|
||||
tmp_dir = mktmpdir(scad_dir + '/')
|
||||
deps_dir = scad_dir + "/deps"
|
||||
png_dir = scad_dir + "/png"
|
||||
bom_dir = scad_dir + "/bom"
|
||||
@@ -111,7 +117,7 @@ def tests(tests):
|
||||
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);
|
||||
openscad.run(scad_name, "-o", png_name, colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall");
|
||||
do_cmd(["magick", png_name, "-trim", "-resize", "1280", "-bordercolor", background, "-border", "10", png_name])
|
||||
else:
|
||||
#
|
||||
@@ -126,14 +132,13 @@ def tests(tests):
|
||||
#
|
||||
# List of individual part files
|
||||
#
|
||||
|
||||
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:
|
||||
done.append(base_name)
|
||||
print('\n'+base_name)
|
||||
print(base_name)
|
||||
cap_name = base_name[0].capitalize() + base_name[1:]
|
||||
base_name = base_name.lower()
|
||||
scad_name = scad_dir + '/' + scad
|
||||
@@ -168,7 +173,7 @@ def tests(tests):
|
||||
impl_name = None
|
||||
|
||||
if libtest:
|
||||
vsplit = "AJR" + chr(ord('Z') + 1)
|
||||
vsplit = "AIR" + 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:
|
||||
@@ -219,7 +224,7 @@ 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)]
|
||||
@@ -232,14 +237,15 @@ def tests(tests):
|
||||
if changed:
|
||||
print(changed)
|
||||
t = time.time()
|
||||
tmp_name = 'tmp.png'
|
||||
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]);
|
||||
tmp_name = tmp_dir + '/tmp.png'
|
||||
openscad.run_list([scad_name, "-o", tmp_name] + 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]);
|
||||
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)
|
||||
BOM = bom.parse_bom()
|
||||
with open(bom_name, 'wt') as outfile:
|
||||
json.dump(BOM.flat_data(), outfile, indent = 4)
|
||||
print()
|
||||
|
||||
with open(bom_name, "rt") as bom_file:
|
||||
BOM = json.load(bom_file)
|
||||
@@ -252,7 +258,7 @@ 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
|
||||
@@ -300,6 +306,11 @@ def tests(tests):
|
||||
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()
|
||||
#
|
||||
# Remove tmp dir
|
||||
#
|
||||
rmtmpdir(tmp_dir)
|
||||
|
||||
do_cmd(('codespell -L od ' + doc_name).split())
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@@ -44,7 +44,7 @@ def got_time(name):
|
||||
|
||||
def check_have_time(changed, name):
|
||||
if not changed and not got_time(name):
|
||||
changed = "no previous time"
|
||||
changed = Fore.CYAN + "no previous time" + Fore.WHITE
|
||||
return changed
|
||||
|
||||
def add_time(name, start):
|
||||
@@ -52,23 +52,34 @@ def add_time(name, start):
|
||||
del times[name.lower()]
|
||||
times[name] = round(time.time() - start, 3)
|
||||
|
||||
def print_times():
|
||||
write_times()
|
||||
def print_times(files = None):
|
||||
sorted_times = sorted(times.items(), key=lambda kv: kv[1])
|
||||
total = 0
|
||||
old_total = 0
|
||||
for entry in sorted_times:
|
||||
colour = Fore.WHITE
|
||||
key = entry[0]
|
||||
new = entry[1]
|
||||
delta = 0
|
||||
if key in last_times:
|
||||
old = last_times[key]
|
||||
delta = new - old
|
||||
if delta > 0.3:
|
||||
colour = Fore.RED
|
||||
if delta < -0.3:
|
||||
colour = Fore.GREEN
|
||||
print(colour + "%5.1f %5.1f %s" % (new, delta, key))
|
||||
total += new
|
||||
if files and not key in files:
|
||||
del times[key]
|
||||
else:
|
||||
new = entry[1]
|
||||
delta = 0
|
||||
colour = Fore.WHITE
|
||||
if key in last_times:
|
||||
old = last_times[key]
|
||||
old_total += old
|
||||
delta = new - old
|
||||
if delta > 0.3:
|
||||
colour = Fore.RED
|
||||
if delta < -0.3:
|
||||
colour = Fore.GREEN
|
||||
print(colour + "%6.1f %5.1f %s" % (new, delta, key))
|
||||
total += new
|
||||
write_times()
|
||||
if sorted_times:
|
||||
print(Fore.WHITE + "%5.1f" % total)
|
||||
colour = Fore.WHITE
|
||||
delta = total - old_total
|
||||
if delta > 1:
|
||||
colour = Fore.RED
|
||||
if delta < -1:
|
||||
colour = Fore.GREEN
|
||||
print(colour + "%6.1f %5.1f TOTAL%s" % (total, delta, Fore.WHITE))
|
||||
|
40
scripts/tmpdir.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#
|
||||
# NopSCADlib Copyright Chris Palmer 2021
|
||||
# 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/>.
|
||||
#
|
||||
"""
|
||||
Make a directory for tmp files.
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
|
||||
def mktmpdir(top_dir):
|
||||
tmp_dir = top_dir + 'tmp'
|
||||
if not os.path.isdir(tmp_dir):
|
||||
os.makedirs(tmp_dir)
|
||||
else:
|
||||
for file in os.listdir(tmp_dir):
|
||||
os.remove(tmp_dir + '/' + file)
|
||||
return tmp_dir
|
||||
|
||||
def reltmp(dir, target):
|
||||
return dir if os.path.isabs(dir) else '../../' + dir if target else '../' + dir
|
||||
|
||||
def rmtmpdir(tmp_dir):
|
||||
os.rmdir(tmp_dir)
|
||||
while os.path.isdir(tmp_dir):
|
||||
time.sleep(0.1)
|
446
scripts/views.py
@@ -35,7 +35,10 @@ import json
|
||||
import blurb
|
||||
import bom
|
||||
import shutil
|
||||
import re
|
||||
import copy
|
||||
from colorama import Fore
|
||||
from tmpdir import *
|
||||
|
||||
def is_assembly(s):
|
||||
return s[-9:] == '_assembly' or s[-11:] == '_assemblies'
|
||||
@@ -73,19 +76,17 @@ def bom_to_assemblies(bom_dir, bounds_map):
|
||||
# Remove the main assembly if it is a shell
|
||||
#
|
||||
if flat_bom:
|
||||
ass = flat_bom[-1]
|
||||
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):
|
||||
if print_mode:
|
||||
print('\n<div style="page-break-after: always;"></div>', file = doc_file)
|
||||
else:
|
||||
if not first:
|
||||
print('[Top](#TOP)', file = doc_file)
|
||||
if not last:
|
||||
print("\n---", file = doc_file)
|
||||
def eop(doc_file, last = False, first = False):
|
||||
print('<span></span>', file = doc_file) # An invisable marker for page breaks because markdown takes much longer if the document contains a div
|
||||
if not first:
|
||||
print('[Top](#TOP)', file = doc_file)
|
||||
if not last:
|
||||
print("\n---", file = doc_file)
|
||||
|
||||
def pad(s, before, after = 0):
|
||||
return ' ' * before + str(s) + ' ' * after
|
||||
@@ -102,14 +103,36 @@ def usage():
|
||||
print("\nusage:\n\t views [target_config] [<name1>_assembly] ... [<nameN>_assembly] - Create assembly images and readme.")
|
||||
sys.exit(1)
|
||||
|
||||
types = ["vitamins", "printed", "routed"]
|
||||
|
||||
def merged(bom):
|
||||
bom = copy.deepcopy(bom)
|
||||
for aname in bom["assemblies"]:
|
||||
count = bom["assemblies"][aname]
|
||||
for ass in flat_bom:
|
||||
if ass['name'] == aname and ass['ngb']:
|
||||
merged_assembly = merged(ass)
|
||||
total = ass['count']
|
||||
for t in types:
|
||||
for thing in merged_assembly[t]:
|
||||
items = merged_assembly[t][thing]['count'] * count // total
|
||||
if thing in bom[t]:
|
||||
bom[t][thing]['count'] += items
|
||||
else:
|
||||
bom[t][thing] = merged_assembly[t][thing]
|
||||
bom[t][thing]['count'] = items
|
||||
break
|
||||
return bom
|
||||
|
||||
def views(target, do_assemblies = None):
|
||||
done_assemblies = []
|
||||
#
|
||||
# Make the target directory
|
||||
#
|
||||
top_dir = set_config(target, usage)
|
||||
tmp_dir = mktmpdir(top_dir)
|
||||
target_dir = top_dir + 'assemblies'
|
||||
deps_dir = top_dir + "deps"
|
||||
deps_dir = target_dir + "/deps"
|
||||
bom_dir = top_dir + "bom"
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
@@ -138,6 +161,7 @@ def views(target, do_assemblies = None):
|
||||
# Find all the scad files
|
||||
#
|
||||
main_blurb = None
|
||||
pngs = []
|
||||
for dir in source_dirs(bom_dir):
|
||||
if os.path.isdir(dir):
|
||||
for filename in os.listdir(dir):
|
||||
@@ -161,33 +185,43 @@ def views(target, do_assemblies = None):
|
||||
#
|
||||
for ass in flat_bom:
|
||||
if ass["name"] == real_name:
|
||||
zoomed = ass['zoomed']
|
||||
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]:
|
||||
#
|
||||
# Run openscad on the created file
|
||||
# Generate png name
|
||||
#
|
||||
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')
|
||||
png_name = target_dir + '/' + real_name + '.png'
|
||||
if not explode:
|
||||
png_name = png_name.replace('_assembly', '_assembled')
|
||||
pngs.append(png_name)
|
||||
|
||||
if not do_assemblies or real_name in do_assemblies:
|
||||
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'
|
||||
tmp_name = tmp_dir + '/' + real_name + '.png'
|
||||
if changed:
|
||||
print(changed)
|
||||
#
|
||||
# make a file to use the module
|
||||
#
|
||||
png_maker_name = 'png.scad'
|
||||
png_maker_name = tmp_dir + '/png.scad'
|
||||
with open(png_maker_name, "w") as f:
|
||||
f.write("use <%s/%s>\n" % (dir, filename))
|
||||
f.write("use <%s/%s>\n" % (reltmp(dir, target), 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]);
|
||||
target_def = ['-D$target="%s"' % target] if target else []
|
||||
cwd_def = ['-D$cwd="%s"' % os.getcwd().replace('\\', '/')]
|
||||
view_def = ['--viewall', '--autocenter'] if not (zoomed & (1 << explode)) else ['--camera=0,0,0,55,0,25,140']
|
||||
openscad.run_list(["-o", tmp_name, png_maker_name] + options.list() + target_def + cwd_def + view_def + ["-D$pose=1", "-D$explode=%d" % explode, colour_scheme, "--projection=p", "--imgsize=4096,4096", "-d", dname]);
|
||||
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)
|
||||
@@ -201,187 +235,221 @@ def views(target, do_assemblies = None):
|
||||
if module == 'main_assembly':
|
||||
main_blurb = blurb.scrape_module_blurb(lines[:line_no])
|
||||
line_no += 1
|
||||
times.print_times()
|
||||
#
|
||||
# Build the document
|
||||
#
|
||||
for print_mode in [True, False]:
|
||||
doc_name = top_dir + "readme.md"
|
||||
with open(doc_name, "wt") as doc_file:
|
||||
#
|
||||
# Title, description and picture
|
||||
#
|
||||
project = ' '.join(word[0].upper() + word[1:] for word in os.path.basename(os.getcwd()).split('_'))
|
||||
print('<a name="TOP"></a>\n# %s' % project, file = doc_file)
|
||||
main_file = bom.find_scad_file('main_assembly')
|
||||
if not main_file:
|
||||
raise Exception("can't find source for main_assembly")
|
||||
text = blurb.scrape_blurb(source_dir + '/' + main_file)
|
||||
if len(text):
|
||||
print(text, file = doc_file, end = '')
|
||||
else:
|
||||
if print_mode:
|
||||
print(Fore.MAGENTA + "Missing project description" + Fore.WHITE)
|
||||
doc_name = top_dir + "readme.md"
|
||||
with open(doc_name, "wt") as doc_file:
|
||||
#
|
||||
# Title, description and picture
|
||||
#
|
||||
project = ' '.join(word[0].upper() + word[1:] for word in os.path.basename(os.getcwd()).split('_'))
|
||||
print('<a name="TOP"></a>', file = doc_file)
|
||||
print('# %s' % project, file = doc_file)
|
||||
main_file = bom.find_scad_file('main_assembly')
|
||||
if not main_file:
|
||||
raise Exception("can't find source for main_assembly")
|
||||
text = blurb.scrape_blurb(source_dir + '/' + main_file)
|
||||
blurbs = blurb.split_blurb(text)
|
||||
if len(text):
|
||||
print(blurbs[0], file = doc_file)
|
||||
else:
|
||||
print(Fore.MAGENTA + "Missing project description" + Fore.WHITE)
|
||||
#
|
||||
# Only add the image if the first blurb section doesn't contain one.
|
||||
#
|
||||
if not re.search(r'\!\[.*\]\(.*\)', blurbs[0], re.MULTILINE):
|
||||
print('\n' % flat_bom[-1]["name"].replace('_assembly', '_assembled'), file = doc_file)
|
||||
eop(print_mode, doc_file, first = True)
|
||||
#
|
||||
# Build TOC
|
||||
#
|
||||
print('## Table of Contents', file = doc_file)
|
||||
print('1. [Parts list](#Parts_list)', file = doc_file)
|
||||
for ass in flat_bom:
|
||||
name = ass["name"]
|
||||
cap_name = titalise(name)
|
||||
print('1. [%s](#%s)' % (cap_name, name), file = doc_file)
|
||||
print(file = doc_file)
|
||||
eop(print_mode, doc_file)
|
||||
#
|
||||
# Global BOM
|
||||
#
|
||||
print('<a name="Parts_list"></a>\n## Parts list', file = doc_file)
|
||||
types = ["vitamins", "printed", "routed"]
|
||||
headings = {"vitamins" : "vitamins", "printed" : "3D printed parts", "routed" : "CNC routed parts"}
|
||||
things = {}
|
||||
eop(doc_file, first = True)
|
||||
#
|
||||
# Build TOC
|
||||
#
|
||||
print('## Table of Contents', file = doc_file)
|
||||
print('1. [Parts list](#Parts_list)', file = doc_file)
|
||||
for ass in flat_bom:
|
||||
name = ass["name"]
|
||||
cap_name = titalise(name)
|
||||
print('1. [%s](#%s)' % (cap_name, name), file = doc_file)
|
||||
print(file = doc_file)
|
||||
if len(blurbs) > 1:
|
||||
print(blurbs[1], file = doc_file)
|
||||
eop(doc_file)
|
||||
#
|
||||
# Global BOM
|
||||
#
|
||||
global_bom = [merged(ass) for ass in flat_bom if not ass['ngb']]
|
||||
print('<a name="Parts_list"></a>\n## Parts list', file = doc_file)
|
||||
headings = {"vitamins" : "vitamins", "printed" : "3D printed parts", "routed" : "CNC routed parts"}
|
||||
things = {}
|
||||
for t in types:
|
||||
things[t] = {}
|
||||
for ass in flat_bom:
|
||||
for t in types:
|
||||
things[t] = {}
|
||||
for ass in flat_bom:
|
||||
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 = 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)
|
||||
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 global_bom:
|
||||
name = titalise(ass["name"][:-9]).replace(' ',' ')
|
||||
if ass["count"] > 1:
|
||||
name = "%d x %s" % (ass["count"], name)
|
||||
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(global_bom) + '|---:|:---|'), 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:
|
||||
for t in types:
|
||||
if things[t]:
|
||||
totals = {}
|
||||
grand_total2 = 0
|
||||
heading = headings[t][0].upper() + headings[t][1:]
|
||||
print(('| ' * len(global_bom) + '| | **%s** |') % heading, file = doc_file)
|
||||
for thing in sorted(things[t], key = lambda s: s.split(":")[-1]):
|
||||
for ass in global_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"]
|
||||
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)
|
||||
if name in totals:
|
||||
totals[name] += count
|
||||
else:
|
||||
totals[name] = count
|
||||
grand_total2 += count
|
||||
print('| %s | %s |' % (pad(things[t][thing], 2, 1), pad(thing.split(":")[-1], 2)), file = doc_file)
|
||||
|
||||
print(file = doc_file)
|
||||
eop(print_mode, doc_file)
|
||||
#
|
||||
# Assembly instructions
|
||||
#
|
||||
for ass in flat_bom:
|
||||
name = ass["name"]
|
||||
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)
|
||||
else:
|
||||
print('<a name="%s"></a>\n## %s' % (name, cap_name), file = doc_file)
|
||||
vitamins = ass["vitamins"]
|
||||
if vitamins:
|
||||
print("### Vitamins", file = doc_file)
|
||||
print("|Qty|Description|", 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]["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)
|
||||
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)
|
||||
print('\n', file = doc_file)
|
||||
print('\n', file = doc_file)
|
||||
|
||||
routed = ass["routed"]
|
||||
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]["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)
|
||||
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)
|
||||
print('\n', file = doc_file)
|
||||
print('\n', file = doc_file)
|
||||
|
||||
sub_assemblies = ass["assemblies"]
|
||||
if sub_assemblies:
|
||||
print("### Sub-assemblies", file = doc_file)
|
||||
keys = sorted(list(sub_assemblies.keys()))
|
||||
for i in range(len(keys)):
|
||||
a = keys[i]
|
||||
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)
|
||||
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)
|
||||
print('\n', file = doc_file)
|
||||
print('\n', file = doc_file)
|
||||
|
||||
small = not ass["big"]
|
||||
suffix = '_tn.png' if small else '.png'
|
||||
print('### Assembly instructions', file = doc_file)
|
||||
print('\n' % (name, name + suffix), file = doc_file)
|
||||
|
||||
if "blurb" in ass and ass["blurb"]:
|
||||
print(ass["blurb"], file = doc_file)
|
||||
else:
|
||||
if print_mode:
|
||||
print(Fore.MAGENTA + "Missing instructions for %s" % name, Fore.WHITE)
|
||||
|
||||
name = name.replace('_assembly', '_assembled')
|
||||
print('\n' % (name, name + suffix), file = doc_file)
|
||||
eop(print_mode, doc_file, last = ass == flat_bom[-1] and not main_blurb)
|
||||
#
|
||||
# If main module is suppressed print any blurb here
|
||||
#
|
||||
if main_blurb:
|
||||
print(main_blurb, file = doc_file)
|
||||
eop(print_mode, doc_file, last = True)
|
||||
grand_total = 0
|
||||
for ass in global_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)
|
||||
assert grand_total == grand_total2
|
||||
print(file = doc_file)
|
||||
if len(blurbs) > 2:
|
||||
print(blurbs[2], file = doc_file)
|
||||
eop(doc_file)
|
||||
#
|
||||
# Convert to HTML
|
||||
# Assembly instructions
|
||||
#
|
||||
html_name = "printme.html" if print_mode else "readme.html"
|
||||
with open(top_dir + html_name, "wt") as html_file:
|
||||
do_cmd(("python -m markdown -x tables -x sane_lists " + doc_name).split(), html_file)
|
||||
for ass in flat_bom:
|
||||
name = ass["name"]
|
||||
cap_name = titalise(name)
|
||||
|
||||
print('<a name="%s"></a>' % name, file = doc_file)
|
||||
if ass["count"] > 1:
|
||||
print('## %d x %s' % (ass["count"], cap_name), file = doc_file)
|
||||
else:
|
||||
print('## %s' % cap_name, file = doc_file)
|
||||
vitamins = ass["vitamins"]
|
||||
if vitamins:
|
||||
print("### Vitamins", file = doc_file)
|
||||
print("|Qty|Description|", 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, 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)
|
||||
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)
|
||||
print('\n', file = doc_file)
|
||||
print('\n', file = doc_file)
|
||||
|
||||
routed = ass["routed"]
|
||||
if routed:
|
||||
print("### CNC Routed parts", file = doc_file)
|
||||
keys = sorted(list(routed.keys()))
|
||||
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)
|
||||
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)
|
||||
print('\n', file = doc_file)
|
||||
print('\n', file = doc_file)
|
||||
|
||||
sub_assemblies = ass["assemblies"]
|
||||
if sub_assemblies:
|
||||
print("### Sub-assemblies", file = doc_file)
|
||||
keys = sorted(list(sub_assemblies.keys()))
|
||||
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)
|
||||
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)
|
||||
print('\n', file = doc_file)
|
||||
print('\n', file = doc_file)
|
||||
|
||||
small = not ass["big"]
|
||||
suffix = '_tn.png' if small else '.png'
|
||||
print('### Assembly instructions', file = doc_file)
|
||||
print('\n' % (name, name + suffix), file = doc_file)
|
||||
|
||||
if "blurb" in ass and ass["blurb"]:
|
||||
print(ass["blurb"], file = doc_file)
|
||||
else:
|
||||
print(Fore.MAGENTA + "Missing instructions for %s" % name, Fore.WHITE)
|
||||
|
||||
name = name.replace('_assembly', '_assembled')
|
||||
print('\n' % (name, name + suffix), file = doc_file)
|
||||
eop(doc_file, last = ass == flat_bom[-1] and not main_blurb)
|
||||
#
|
||||
# If main module is suppressed print any blurb here
|
||||
#
|
||||
if main_blurb:
|
||||
print(main_blurb, file = doc_file)
|
||||
eop(doc_file, last = True)
|
||||
#
|
||||
# Convert to HTML
|
||||
#
|
||||
html_name = top_dir + 'readme.html'
|
||||
t = time.time()
|
||||
with open(html_name, "wt") as html_file:
|
||||
do_cmd(("python -m markdown -x tables -x sane_lists " + doc_name).split(), html_file)
|
||||
times.add_time(html_name, t)
|
||||
times.print_times(pngs + [html_name])
|
||||
#
|
||||
# Make the printme.html by replacing empty spans that invisbly mark the page breaks by page break divs.
|
||||
#
|
||||
with open(html_name, 'rt') as src:
|
||||
lines = src.readlines()
|
||||
|
||||
i = 0
|
||||
with open(top_dir + 'printme.html', 'wt') as dst:
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
if line.startswith('<p><span></span>'): # Empty span used to mark page breaks
|
||||
i += 1
|
||||
if lines[i].startswith('<a href="#TOP">Top</a>'): # The first page break won't have one
|
||||
i += 1
|
||||
if i < len(lines) and lines[i] == '<hr />\n': # The last page break doesn't have one
|
||||
dst.write('<div style="page-break-after: always;"></div>\n')
|
||||
i += 1
|
||||
else:
|
||||
dst.write(line)
|
||||
i += 1
|
||||
#
|
||||
# Remove tmp dir
|
||||
#
|
||||
rmtmpdir(tmp_dir)
|
||||
#
|
||||
# Spell check
|
||||
#
|
||||
do_cmd('codespell -L od readme.md'.split())
|
||||
do_cmd(('codespell -L od ' + top_dir + 'readme.md').split())
|
||||
#
|
||||
# List the ones we didn't find
|
||||
#
|
||||
|
@@ -20,17 +20,16 @@
|
||||
//
|
||||
//! BOM and assembly demonstration
|
||||
//
|
||||
$explode = 1; // Normally set on the command line when generating assembly views with views.py
|
||||
include <../core.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;
|
||||
sheet = PMMA3;
|
||||
height = 10;
|
||||
|
||||
insert = screw_insert(screw);
|
||||
washer = screw_washer(screw);
|
||||
|
||||
module widget(thickness) {
|
||||
vitamin(str("widget(", thickness, "): Rivit like thing for ", thickness, "mm sheets"));
|
||||
@@ -44,31 +43,29 @@ module widget(thickness) {
|
||||
}
|
||||
}
|
||||
|
||||
module widgit_stl() {
|
||||
stl("widget");
|
||||
module widget_stl() {
|
||||
stl("widget")
|
||||
union() {
|
||||
rounded_rectangle([30, 30, 3], 2, true);
|
||||
|
||||
union() {
|
||||
rounded_rectangle([30, 30, 3], 2);
|
||||
|
||||
render() insert_boss(insert, height, 2.2);
|
||||
}
|
||||
render() insert_boss(insert, height, 2.2);
|
||||
}
|
||||
}
|
||||
|
||||
module widgit_dxf() {
|
||||
dxf("widget");
|
||||
module widget_dxf() {
|
||||
dxf("widget")
|
||||
difference() {
|
||||
sheet_2D(sheet, 20, 20, 1);
|
||||
|
||||
difference() {
|
||||
sheet_2D(sheet, 20, 20, 1);
|
||||
|
||||
drill(screw_clearance_radius(screw), 0);
|
||||
}
|
||||
drill(screw_clearance_radius(screw), 0);
|
||||
}
|
||||
}
|
||||
|
||||
//! * Push the insert into the base with a soldering iron heated to 200°C
|
||||
module widgit_base_assembly()
|
||||
assembly("widgit_base") {
|
||||
module widget_base_assembly()
|
||||
assembly("widget_base") {
|
||||
stl_colour(pp1_colour)
|
||||
widgit_stl();
|
||||
widget_stl();
|
||||
|
||||
translate_z(height)
|
||||
insert(insert);
|
||||
@@ -81,18 +78,18 @@ assembly("widget_top") {
|
||||
widget(sheet_thickness(sheet));
|
||||
|
||||
render_2D_sheet(sheet) // Must be last because it is transparent
|
||||
widgit_dxf();
|
||||
widget_dxf();
|
||||
}
|
||||
|
||||
//! * Screw the two assemblies together
|
||||
module widgit_assembly()
|
||||
assembly("wigdit") {
|
||||
module widget_assembly()
|
||||
assembly("widget") {
|
||||
|
||||
widgit_base_assembly(); // Note this is not exloded because it is sub-assembly
|
||||
widget_base_assembly(); // Note this is not exloded because it is sub-assembly
|
||||
|
||||
translate_z(height) {
|
||||
translate_z(sheet_thickness(sheet))
|
||||
screw_and_washer(screw, screw_longer_than(sheet_thickness(sheet) + 2 * washer_thickness(washer) + 3), true);
|
||||
screw_and_washer(screw, screw_length(screw, sheet_thickness(sheet) + 3, 2, longer = true), true);
|
||||
|
||||
explode(5)
|
||||
translate_z(sheet_thickness(sheet) / 2 + eps)
|
||||
@@ -101,7 +98,7 @@ assembly("wigdit") {
|
||||
}
|
||||
|
||||
module boms() {
|
||||
widgit_assembly();
|
||||
widget_assembly();
|
||||
}
|
||||
|
||||
boms();
|
||||
|
@@ -16,6 +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 <../vitamins/ldrs.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
@@ -21,16 +21,22 @@ include <../vitamins/pcbs.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
||||
function spacing(p) = let(w = pcb_width(p)) w < 22 ? w + 3 : w + 10;
|
||||
|
||||
module pcbs() {
|
||||
layout([for(p = pcbs) pcb_width(p)], 10)
|
||||
layout([for(p = pcbs) spacing(p)], 0)
|
||||
translate([0, pcb_length(pcbs[$i]) / 2])
|
||||
rotate(90)
|
||||
pcb_assembly(pcbs[$i], 5 + $i, 3);
|
||||
|
||||
translate([0, 120])
|
||||
translate([0, 110])
|
||||
layout([for(p = perfboards) pcb_length(p)], 10)
|
||||
translate([0, -pcb_width(perfboards[$i]) / 2])
|
||||
pcb_assembly(perfboards[$i], 5 + $i, 3);
|
||||
|
||||
for(p = pcbs_not_shown)
|
||||
hidden()
|
||||
pcb(p);
|
||||
}
|
||||
if($preview)
|
||||
pcbs();
|
||||
|
@@ -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>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/ball_bearings.scad>
|
||||
|
@@ -17,7 +17,7 @@
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/scs_bearing_blocks.scad>
|
||||
include <../vitamins/bearing_blocks.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
@@ -25,9 +25,9 @@ use <../utils/layout.scad>
|
||||
module belt_test() {
|
||||
p2 = [-75, -50];
|
||||
p3 = [-75, 100];
|
||||
p4 = [75, 100];
|
||||
p4 = [ 75, 100];
|
||||
|
||||
p5 = [75 + pulley_pr(GT2x20ob_pulley) - pulley_pr(GT2x16_plain_idler), +pulley_pr(GT2x16_plain_idler)];
|
||||
p5 = [ 75 + pulley_pr(GT2x20ob_pulley) - pulley_pr(GT2x16_plain_idler), +pulley_pr(GT2x16_plain_idler)];
|
||||
p6 = [-75 + pulley_pr(GT2x20ob_pulley) + pulley_pr(GT2x16_plain_idler), -pulley_pr(GT2x16_plain_idler)];
|
||||
|
||||
module pulleys(flip = false) {
|
||||
@@ -39,7 +39,7 @@ module belt_test() {
|
||||
screw = find_screw(hs_cs_cap, pulley_bore(pulley));
|
||||
insert = screw_insert(screw);
|
||||
|
||||
rotate([0, flip ? 180 : 0, 0]) {
|
||||
hflip(flip) {
|
||||
pulley_assembly(pulley);
|
||||
translate_z(pulley_height(pulley) + pulley_offset(pulley) + screw_head_depth(screw, pulley_bore(pulley)))
|
||||
screw(screw, 20);
|
||||
@@ -52,19 +52,21 @@ module belt_test() {
|
||||
translate(p6) pulley_assembly(GT2x16_plain_idler);
|
||||
}
|
||||
|
||||
path = [ [p5.x, p5.y, pulley_pr(GT2x16_plain_idler)],
|
||||
path = [ [-40, 0, 0],
|
||||
[p6.x, p6.y, -pulley_pr(GT2x16_plain_idler)],
|
||||
[p2.x, p2.y, pulley_pr(GT2x20ob_pulley)],
|
||||
[p3.x, p3.y, pulley_pr(GT2x20ob_pulley)],
|
||||
[p4.x, p4.y, pulley_pr(GT2x20ob_pulley)]
|
||||
[p4.x, p4.y, pulley_pr(GT2x20ob_pulley)],
|
||||
[p5.x, p5.y, pulley_pr(GT2x16_plain_idler)],
|
||||
[40, 0, 0],
|
||||
];
|
||||
|
||||
belt = GT2x6;
|
||||
belt(belt, path, 80, [0, 0]);
|
||||
belt(belt, path, open = true);
|
||||
pulleys();
|
||||
translate_z(20)
|
||||
hflip() {
|
||||
belt(belt, path, 80, [0, 0], belt_colour = grey(90), tooth_colour = grey(50));
|
||||
belt(belt, path, open = true, belt_colour = grey(90), tooth_colour = grey(50));
|
||||
pulleys(flip=true);
|
||||
}
|
||||
|
||||
@@ -72,6 +74,31 @@ module belt_test() {
|
||||
layout([for(b = belts) belt_width(b)], 10)
|
||||
rotate([0, 90, 0])
|
||||
belt(belts[$i], [[0, 0, 20], [0, 1, 20]], belt_colour = $i%2==0 ? grey(90) : grey(20), tooth_colour = $i%2==0 ? grey(70) : grey(50));
|
||||
|
||||
// new example with open loop - this is a simplified example of the style used for example for the BLV 3D printer
|
||||
pulley = GT2x20ob_pulley;
|
||||
idler = GT2x16_plain_idler;
|
||||
corners = [[-75,-50],[75,100]];
|
||||
carriagepos = [0,0];
|
||||
carriagew = 80;
|
||||
|
||||
points = [
|
||||
[carriagepos.x - carriagew / 2, carriagepos.y, 0],
|
||||
[corners[0].x + belt_pulley_pr(belt, pulley) + belt_pulley_pr(belt, idler), carriagepos.y - belt_pulley_pr(belt, idler), idler],
|
||||
[corners[0].x, corners[0].y, pulley],
|
||||
[corners[0].x, corners[1].y, idler],
|
||||
[corners[1].x, corners[1].y, idler],
|
||||
[corners[1].x, carriagepos.y + belt_pulley_pr(belt, idler), idler],
|
||||
[carriagepos.x + carriagew / 2, carriagepos.y, 0]
|
||||
];
|
||||
translate_z(-30) {
|
||||
belt(belt, points, open=true, auto_twist=true);
|
||||
for (p = points)
|
||||
if (is_list(p.z))
|
||||
translate([p.x, p.y, 0])
|
||||
pulley_assembly(p.z);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($preview)
|
||||
|
@@ -22,18 +22,15 @@ use <../utils/layout.scad>
|
||||
include <../vitamins/blowers.scad>
|
||||
|
||||
module blowers()
|
||||
layout([for(b = blowers) blower_width(b)], 10, true) let(b = blowers[$i]){
|
||||
layout([for(b = blowers) blower_width(b)], 5, true) let(b = blowers[$i]){
|
||||
screw = blower_screw(b);
|
||||
washer = screw_washer(screw);
|
||||
h = blower_lug(b);
|
||||
|
||||
blower(b);
|
||||
|
||||
blower_hole_positions(b)
|
||||
translate_z(h)
|
||||
screw_and_washer(screw, screw_longer_than(h + washer_thickness(washer) + 5));
|
||||
|
||||
|
||||
screw_and_washer(screw, screw_length(screw, h + 5, 1, longer = true));
|
||||
}
|
||||
|
||||
if($preview)
|
||||
|
30
tests/box_sections.scad
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2021
|
||||
// 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/box_sections.scad>
|
||||
|
||||
module box_sections() {
|
||||
layout([for(b = box_sections) box_section_size(b).x], 20)
|
||||
box_section(box_sections[$i], 100);
|
||||
}
|
||||
|
||||
if($preview)
|
||||
box_sections();
|
134
tests/core_xy.scad
Normal file
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// 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/pulleys.scad>
|
||||
include <../vitamins/screws.scad>
|
||||
include <../vitamins/stepper_motors.scad>
|
||||
include <../vitamins/washers.scad>
|
||||
|
||||
include <../utils/core_xy.scad>
|
||||
|
||||
|
||||
module coreXY_belts_test() {
|
||||
coreXY_type = coreXY_GT2_20_16;
|
||||
plain_idler = coreXY_plain_idler(coreXY_type);
|
||||
toothed_idler = coreXY_toothed_idler(coreXY_type);
|
||||
|
||||
coreXYPosBL = [0, 0, 0];
|
||||
coreXYPosTR = [200, 150, 0];
|
||||
separation = [0, coreXY_coincident_separation(coreXY_type).y, pulley_height(plain_idler) + washer_thickness(M3_washer)];
|
||||
pos = [100, 50];
|
||||
|
||||
upper_drive_pulley_offset = [40, 10];
|
||||
lower_drive_pulley_offset = [0, 0];
|
||||
|
||||
coreXY_belts(coreXY_type,
|
||||
carriagePosition = pos,
|
||||
coreXYPosBL = coreXYPosBL,
|
||||
coreXYPosTR = coreXYPosTR,
|
||||
separation = separation,
|
||||
x_gap = 10,
|
||||
upper_drive_pulley_offset = upper_drive_pulley_offset,
|
||||
lower_drive_pulley_offset = lower_drive_pulley_offset,
|
||||
show_pulleys = true);
|
||||
|
||||
|
||||
translate([coreXYPosBL.x + separation.x/2, coreXYPosTR.y + upper_drive_pulley_offset.y, separation.z/2]) {
|
||||
// add the upper drive pulley stepper motor
|
||||
translate([coreXY_drive_pulley_x_alignment(coreXY_type) + upper_drive_pulley_offset.x, 0, -pulley_height(coreXY_drive_pulley(coreXY_type))])
|
||||
NEMA(NEMA17M);
|
||||
|
||||
// add the screws for the upper drive offset idler pulleys if required
|
||||
if (upper_drive_pulley_offset.x > 0) {
|
||||
translate(coreXY_drive_plain_idler_offset(coreXY_type))
|
||||
translate_z(-pulley_offset(plain_idler))
|
||||
screw(M3_cap_screw, 20);
|
||||
translate(coreXY_drive_toothed_idler_offset(coreXY_type))
|
||||
translate_z(-pulley_offset(toothed_idler))
|
||||
screw(M3_cap_screw, 20);
|
||||
} else if (upper_drive_pulley_offset.x < 0) {
|
||||
translate([-pulley_od(plain_idler), coreXY_drive_plain_idler_offset(coreXY_type).y])
|
||||
translate_z(-pulley_offset(plain_idler))
|
||||
screw(M3_cap_screw, 20);
|
||||
translate([2*coreXY_drive_pulley_x_alignment(coreXY_type), coreXY_drive_toothed_idler_offset(coreXY_type).y])
|
||||
translate_z(-pulley_offset(toothed_idler))
|
||||
screw(M3_cap_screw, 20);
|
||||
}
|
||||
}
|
||||
|
||||
translate([coreXYPosTR.x - separation.x/2, coreXYPosTR.y + lower_drive_pulley_offset.y, -separation.z/2]) {
|
||||
// add the lower drive pulley stepper motor
|
||||
translate([-coreXY_drive_pulley_x_alignment(coreXY_type) + lower_drive_pulley_offset.x, 0, -pulley_height(coreXY_drive_pulley(coreXY_type))])
|
||||
NEMA(NEMA17M);
|
||||
|
||||
// add the screws for the lower drive offset idler pulleys if required
|
||||
if (lower_drive_pulley_offset.x < 0) {
|
||||
translate([-coreXY_drive_plain_idler_offset(coreXY_type).x, coreXY_drive_plain_idler_offset(coreXY_type).y])
|
||||
translate_z(-pulley_offset(plain_idler))
|
||||
screw(M3_cap_screw, 20);
|
||||
translate(coreXY_drive_toothed_idler_offset(coreXY_type))
|
||||
translate_z(-pulley_offset(toothed_idler))
|
||||
screw(M3_cap_screw, 20);
|
||||
} else if (lower_drive_pulley_offset.x > 0) {
|
||||
translate([pulley_od(plain_idler), coreXY_drive_plain_idler_offset(coreXY_type).y])
|
||||
translate_z(-pulley_offset(plain_idler))
|
||||
screw(M3_cap_screw, 20);
|
||||
translate([-2*coreXY_drive_pulley_x_alignment(coreXY_type), coreXY_drive_toothed_idler_offset(coreXY_type).y])
|
||||
translate_z(-pulley_offset(toothed_idler))
|
||||
screw(M3_cap_screw, 20);
|
||||
}
|
||||
}
|
||||
|
||||
// add the screw for the left upper idler pulley
|
||||
translate([coreXYPosBL.x + separation.x/2, coreXYPosBL.y, separation.z])
|
||||
screw(M3_cap_screw, 20);
|
||||
|
||||
// add the screw for the right upper idler pulley
|
||||
translate([coreXYPosTR.x + separation.x/2, coreXYPosBL.y, separation.z])
|
||||
screw(M3_cap_screw, 20);
|
||||
|
||||
if (separation.x != 0) {
|
||||
// add the screw for the left lower idler pulley
|
||||
translate([coreXYPosBL.x - separation.x/2, coreXYPosBL.y, 0])
|
||||
screw(M3_cap_screw, 20);
|
||||
|
||||
// add the screw for the right lower idler pulley
|
||||
translate([coreXYPosTR.x - separation.x/2, coreXYPosBL.y, 0])
|
||||
screw(M3_cap_screw, 20);
|
||||
}
|
||||
|
||||
translate([-separation.x/2, pos.y + coreXYPosBL.y -separation.y/2, -separation.z/2 + pulley_height(plain_idler)/2]) {
|
||||
// add the screw for the left Y carriage toothed idler
|
||||
translate([coreXYPosBL.x, coreXY_toothed_idler_offset(coreXY_type).y, 0])
|
||||
screw(M3_cap_screw, 20);
|
||||
// add the screw for the left Y carriage plain idler
|
||||
translate([coreXYPosBL.x + separation.x + coreXY_plain_idler_offset(coreXY_type).x, separation.y + coreXY_plain_idler_offset(coreXY_type).y, separation.z])
|
||||
screw(M3_cap_screw, 20);
|
||||
// add the screw for the right Y carriage toothed idler
|
||||
translate([coreXYPosTR.x + separation.x, coreXY_toothed_idler_offset(coreXY_type).y, separation.z])
|
||||
screw(M3_cap_screw, 20);
|
||||
// add the screw for the right Y carriage plain idler
|
||||
translate([coreXYPosTR.x - coreXY_plain_idler_offset(coreXY_type).x, separation.y + coreXY_plain_idler_offset(coreXY_type).y, 0])
|
||||
screw(M3_cap_screw, 20);
|
||||
}
|
||||
}
|
||||
|
||||
if ($preview)
|
||||
coreXY_belts_test();
|
@@ -16,6 +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>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/displays.scad>
|
||||
|
@@ -26,6 +26,12 @@ module dogbones() {
|
||||
#translate([15, 0])
|
||||
dogbone_rectangle([10, 20, 5], center = false);
|
||||
|
||||
#translate([30, 0])
|
||||
dogbone_rectangle_x([10, 20, 5], center = false);
|
||||
|
||||
#translate([45, 0])
|
||||
dogbone_rectangle_y([10, 20, 5], center = false);
|
||||
|
||||
sq = 3;
|
||||
translate([-5 + sq / 2 + eps, -10 + sq / 2 + eps])
|
||||
%cube([sq, sq, 1], center = true);
|
||||
|
@@ -16,12 +16,13 @@
|
||||
// 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/geared_steppers.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module geared_steppers()
|
||||
layout([for(g = geared_steppers) gs_diameter(g)], 5)
|
||||
layout([for(g = geared_steppers) max(gs_diameter(g), gs_pitch(g) + gs_lug_w(g) / 2)], 5)
|
||||
geared_stepper(geared_steppers[$i]);
|
||||
|
||||
geared_steppers();
|
||||
|
@@ -16,6 +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>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/green_terminals.scad>
|
||||
|
@@ -56,7 +56,7 @@ module horiholes_stl(t = thickness) {
|
||||
}
|
||||
if(t == thickness)
|
||||
translate([length / 2, 0])
|
||||
rounded_rectangle([length + 2 * overlap_x, thickness + 2 * overlap_y, 2], 5);
|
||||
rounded_rectangle([length + 2 * overlap_x, thickness + 2 * overlap_y, 2], 5, true);
|
||||
}
|
||||
|
||||
module horiholes() {
|
||||
|
@@ -16,6 +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>
|
||||
use <../vitamins/hygrometer.scad>
|
||||
|
||||
if($preview)
|
||||
|
@@ -16,6 +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>
|
||||
use <../vitamins/microview.scad>
|
||||
|
||||
microview(!$preview);
|
||||
|
@@ -16,6 +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>
|
||||
use <../vitamins/o_ring.scad>
|
||||
|
||||
module o_rings()
|
||||
|
@@ -17,7 +17,7 @@
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/kp_pillow_blocks.scad>
|
||||
include <../vitamins/pillow_blocks.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
BIN
tests/png/bearing_blocks.png
Normal file
After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 162 KiB |