diff options
author | Steven <steven.vasilogianis@gmail.com> | 2022-03-16 14:26:00 -0400 |
---|---|---|
committer | Steven <steven.vasilogianis@gmail.com> | 2022-03-16 14:26:00 -0400 |
commit | a5ba57218ddfe0c0fb09ca388d76da38b3df9e26 (patch) | |
tree | 767bd3f19ef28aeb89d2ee67cad3d859c5aabc63 |
initial public repository
-rw-r--r-- | .gitmodules | 3 | ||||
m--------- | BOSL2 | 0 | ||||
-rw-r--r-- | Makefile | 23 | ||||
-rw-r--r-- | README.md | 313 | ||||
-rw-r--r-- | cubbies.scad | 209 | ||||
-rwxr-xr-x | cubbies.sh | 65 | ||||
-rw-r--r-- | description | 1 | ||||
-rwxr-xr-x | shopenscad.sh | 238 | ||||
m--------- | svasilogianis.github.io | 0 | ||||
-rwxr-xr-x | test_fits.sh | 28 |
10 files changed, 880 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..faf109d --- /dev/null +++ b/.gitmodules | |||
@@ -0,0 +1,3 @@ | |||
1 | [submodule "BOSL2"] | ||
2 | path = BOSL2 | ||
3 | url = https://github.com/revarbat/BOSL2 | ||
diff --git a/BOSL2 b/BOSL2 new file mode 160000 | |||
Subproject 9f5214adc0476f29aa9437dc401c01e8f1cd5da | |||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4275aa8 --- /dev/null +++ b/Makefile | |||
@@ -0,0 +1,23 @@ | |||
1 | README.html: pandoc.css README.md | ||
2 | pandoc --standalone --css=$^ -o $@ | ||
3 | cp README.html svasilogianis.github.io/index.html | ||
4 | |||
5 | stls: reference-stls test-fit-stls | ||
6 | |||
7 | reference-stls: bottom.stl side.stl | ||
8 | |||
9 | bottom.stl: shopenscad.sh cubbies.scad | ||
10 | part='"bottom"' ./shopenscad.sh cubbies.scad -o $@ | ||
11 | |||
12 | side.stl: shopenscad.sh cubbies.scad | ||
13 | part='"side"' ./shopenscad.sh cubbies.scad -o $@ | ||
14 | |||
15 | test_fit_stls = male_tabs-40-120-90-5-5-0-10-5-0.5-true.stl \ | ||
16 | female_tabs-40-120-90-5-5-0-10-5-0.1-true.stl \ | ||
17 | female_tabs-40-120-90-5-5-0-10-5-0.2-true.stl \ | ||
18 | female_tabs-40-120-90-5-5-0-10-5-0.3-true.stl \ | ||
19 | female_tabs-40-120-90-5-5-0-10-5-0.4-true.stl \ | ||
20 | female_tabs-40-120-90-5-5-0-10-5-0.5-true.stl | ||
21 | |||
22 | test-fit-stls: $(wildcard $test_fit_stls) | ||
23 | ./test_fits.sh | ||
diff --git a/README.md b/README.md new file mode 100644 index 0000000..10a8611 --- /dev/null +++ b/README.md | |||
@@ -0,0 +1,313 @@ | |||
1 | # 3D Printable Parametric Cubbies # | ||
2 | |||
3 | ::: { .parts } | ||
4 | |||
5 | Bottom Part: | ||
6 | : [![Bottom Part](pictures/bottom.png "Bottom part")](pictures/side.png) | ||
7 | |||
8 | Side Part: | ||
9 | : [![Side](pictures/side.png "Side part"){#fig:side}](pictures/side.png) | ||
10 | |||
11 | ::: | ||
12 | |||
13 | 3D printable parametric cubbies to store your 3D things. There are two distinct | ||
14 | shapes — a `bottom` and a `side` — that are generated to the dimensions of your | ||
15 | things, 3D printed, then assembled into modular storage units. The parts are | ||
16 | optimized for printing by using [sparse | ||
17 | structures](https://github.com/revarbat/BOSL2/wiki/walls.scad#module-sparse_strut) | ||
18 | (to save material) that lay flat on the print bed (for ease of printing). | ||
19 | |||
20 | ## Pictures ## | ||
21 | |||
22 | ::: { .figs } | ||
23 | |||
24 | ![[Bottom part (printed)](pictures/bottom-printed-full.jpg "Bottom part, printed (full resolution)")](pictures/bottom-printed-thumbnail.jpg) | ||
25 | |||
26 | ![[Side part (printed)](pictures/side-printed-full.jpg "Side part, printed (full resolution)")](pictures/side-printed-thumbnail.jpg "Side part, printed") | ||
27 | |||
28 | ![[The Smurftruder with a fresh print](pictures/smurfstrusion-full.jpg "Smurfstrusion (full resolution)")](pictures/smurfstrusion-thumbnail.jpg) | ||
29 | |||
30 | ![[Mounted Cubbies (empty)](pictures/cubbies-mounted-empty-full.jpg "Mounted Cubbies (full resolution)")](pictures/cubbies-mounted-empty-thumbnail.jpg) | ||
31 | |||
32 | ![[Mounted Cubbies (empty, viewed from an angle)](pictures/cubbies-mounted-empty-angle-view-full.jpg "Empty cubbies, mounted (full resolution)")](pictures/cubbies-mounted-empty-angle-view-thumbnail.jpg) | ||
33 | |||
34 | ![[Mounted Cubbies (filled)](pictures/cubbies-full-full.jpg "Mounted cubbies, filled (full resolution)")](pictures/cubbies-full-thumbnail.jpg) | ||
35 | |||
36 | ![[Cubby Development](pictures/cubby-development-full.jpg "Cubby Development (full resolution)")](pictures/cubby-development-thumbnail.jpg) | ||
37 | |||
38 | ::: | ||
39 | |||
40 | ## Instructions ## | ||
41 | |||
42 | ### 🚫 Do NOT Scale in a Slicer 🚫 ### | ||
43 | |||
44 | All models should be generated by specifying, at minimum, the parameters | ||
45 | `cubby_width`, `cubby_depth`, and `cubby_height` in `cubbies.scad` (ways to do | ||
46 | this are described below.) The dovetail joints are generated to a constant size | ||
47 | (controlled through the parameters `tab_width`, `tab_padding`, and | ||
48 | `tab_tolerance`) and should not be expected to fit after being scaled. Cubbies | ||
49 | can be generated in in one of the following ways: | ||
50 | |||
51 | * Using the Thingiverse Customizer | ||
52 | |||
53 | * Using [OpenScad](https://openscad.org "Homepage for OpenSCAD \"The | ||
54 | Programmers Solid 3D Cad Modeller\"") -- either by using its builtin | ||
55 | customizer (choose `Window -> Customizer` from the menu bar if it is not | ||
56 | visible), or by modifying the variables at the top of the `cubbies.scad` | ||
57 | file. As mentioned above, the minimum paramaters to generate a custom sized | ||
58 | cubby are `cubby_width`, `cubby_depth`, and `cubby_height`. (Even those | ||
59 | arguments can be omitted, and your cubby will be whatever abritrary size I | ||
60 | decided as defaults (`40`,`120`,`90`).) | ||
61 | |||
62 | * There are two scripts for generating cubbies from the commandline: | ||
63 | `cubbies.sh` (run [`./cubbies.sh -h` for details](#cubbies.sh) can generate | ||
64 | entire sets of cubbies, and `shopenscad.sh` (run `./shopenscad.sh -h` for | ||
65 | details[^1]) generates individual parts. | ||
66 | |||
67 | For a single cubby, you will need to print the side piece twice, and the bottom | ||
68 | piece once. To add additional cubbies, print one bottom and one side. The bottom | ||
69 | and side pieces can be flipped upside down so that the bottom piece's tabs fit | ||
70 | into the extra unused holes of the side piece. You can also use this to control | ||
71 | whether the screw tabs end up on the inside or outside of the cubby. | ||
72 | |||
73 | You can print tolerance test fit pieces (which are the minimum material | ||
74 | necessary to confirm that the pieces will fit with your printer and slicer | ||
75 | settings). These test pieces were generated using `dovetail_test_fits.sh`, which | ||
76 | you can use to generate your own at different tolerances (run | ||
77 | `dovetail_test_fits.sh -h` for details). You can also generate them in `OpenSCAD` | ||
78 | by specifying either `male_tabs` or `female_tabs` for the `part` paramaters (the | ||
79 | `cubby_depth` paramater is respected as the length of those dovetail test | ||
80 | pieces). | ||
81 | |||
82 | ## Parameters ## | ||
83 | |||
84 | `part` Controls which part will be generated, and can be any of: | ||
85 | |||
86 | : `bottom` — a bottom part whose dimensions will be `cubby_width` x `cubby_depth`. | ||
87 | |||
88 | : `side` — a side part whose dimensions will be `cubby_depth` x `cubby_height`. | ||
89 | |||
90 | : `female_tabs` — generates a shape consisting of the minimum amount of material | ||
91 | needed to test the dovetail joint fit. `cubby_depth` is respected as the | ||
92 | length that the tab holes (mortises) should run. (I have found that although | ||
93 | my printer could print a single tab accurately, spanning the entire length of | ||
94 | `cubby_depth` gave me the most accurate test. | ||
95 | |||
96 | : `male_tabs` — as `female_tabs` above, except with male tabs (tenons) | ||
97 | |||
98 | : `view_fit` — used as a debugging aid during development -- displays two sets | ||
99 | of tops and bottoms posiitioned to inspect tab fit. | ||
100 | |||
101 | : `both` — a `bottom` and a `side` laid next each other. (These may look like | ||
102 | two seperate models, but is only one model, and obnixious to work with in a | ||
103 | slicer. Generating them seperately will allow you to manipulate them | ||
104 | individually). | ||
105 | |||
106 | ### Cubby Dimensions ### | ||
107 | |||
108 | `cubby_width` | ||
109 | : How wide the cubby should be, used as one dimension of the `bottom` and `male_tabs` `part`. | ||
110 | |||
111 | `cubby_depth` | ||
112 | : How deep the cubby should be, used as one dimension of the `side` and `female_tabs` `part`. | ||
113 | |||
114 | `cubby_height` | ||
115 | : How deep the cubby should be, used as one dimension of the `side`. | ||
116 | |||
117 | `thickness` | ||
118 | : How thick the `bottom`, `side`, or test fit pieces (`male_tabs`, | ||
119 | `female_tabs`) should be. When either part is eventually laid flat on the | ||
120 | print bed, this effectively becomes the `z` axis height. (The `tab_depth` is | ||
121 | dictated by this `thickness` dimension.) | ||
122 | |||
123 | `strut_thickness` | ||
124 | : The thickness of the strut members in the sparse structures. Applies to both | ||
125 | `bottom` and `side`. | ||
126 | |||
127 | `max_bridge` | ||
128 | : Maximum bridging distance between cross-braces of sparse struts. Applies to | ||
129 | `bottom`, and `side`. | ||
130 | |||
131 | ### Tab/Dovetail Dimensions ### | ||
132 | |||
133 | `tab_width` | ||
134 | : The width of each tab. | ||
135 | |||
136 | `tab_padding` | ||
137 | : The distance of the first tab from the edge, as well as between each tab. | ||
138 | |||
139 | `tab_tolerance` | ||
140 | : How much bigger the hole (or mortise) should be than the tab (tenon) | ||
141 | |||
142 | ![[Dovetail Test Fit](pictures/dovetail_test_fit.jpg-full "Dovetail Test Fit (full resolution)")](pictures/dovetail_test_fit-thumbnail.jpg) | ||
143 | |||
144 | ## Source Repository ## | ||
145 | |||
146 | The source repository is a Git repository that is hosted both on a read-only | ||
147 | HTTPS URL and on a read-write SSH url. | ||
148 | |||
149 | You can push to the read-write URL simply without creating any user account. | ||
150 | You only need to generate a SSH public key on your own system. | ||
151 | |||
152 | ### Read-Only Clone ### | ||
153 | |||
154 | The read-only clone command is: | ||
155 | |||
156 | git clone --recurse-submodules https://git.cryptonomic.net/cubbies.git | ||
157 | |||
158 | ### Read-Write Clone ### | ||
159 | |||
160 | To clone from the read-write URL, you first need to have your own ssh identity. | ||
161 | Then you can run `git clone`. | ||
162 | |||
163 | # Generate an SSH key if needed | ||
164 | command -v ssh-keygen || { echo Please Install OpenSSH; exit; } | ||
165 | [ -d ~/.ssh ] || mkdir ~/.ssh | ||
166 | [ -e ~/.ssh/id_ed25519.pub ] || ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N | ||
167 | |||
168 | # Add the public key for cryptonomic.net | ||
169 | echo cryptonomic.net ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPK7K8KsygvXtjw0yH4h43bwBGKq9oWBdCa1eY5rzT5D >> ~/.ssh/known_hosts | ||
170 | |||
171 | # Perform the clone | ||
172 | git clone --recurse-submodules ssh://d@cryptonomic.net:public_git/cubbies.git | ||
173 | |||
174 | You can push your changes back simply: | ||
175 | |||
176 | git add . | ||
177 | git commit -a -m 'commit message' | ||
178 | git push | ||
179 | |||
180 | ## Cubbies on the Web ## | ||
181 | |||
182 | * thingiverse | ||
183 | * thangs | ||
184 | * cults3d | ||
185 | |||
186 | ## Dependencies/Credits ## | ||
187 | |||
188 | * [OpenSCAD](https://openscad.org "OpenSCAD") | ||
189 | * [Belfry OpenSCAD Library](https://github.com/revarbat/BOSL "Belfry OpenSCAD Library") | ||
190 | |||
191 | ## Contributions ## | ||
192 | |||
193 | ![Megachurch Pastor](pictures/kenneth-copeland.jpg) ![Donation Basket](pictures/basket.jpg) | ||
194 | |||
195 | |||
196 | - PayPal: <https://paypal.me/svasilogianis> | ||
197 | - Venmo: <https://venmo.com/u/Steven-Vasilogianis> | ||
198 | - CashApp: <https://cash.app/$svasi> | ||
199 | |||
200 | ## Shell Scripts ## | ||
201 | |||
202 | ### cubbies.sh ### | ||
203 | |||
204 | ``` | ||
205 | $ ./cubbies.sh -h | ||
206 | ./cubbies.sh: generate sets of cubbies. You will need print the side piece twice for your | ||
207 | first bottom piece, then a single side piece for each additional bottom piece. | ||
208 | |||
209 | Usage: | ||
210 | ./cubbies.sh cubby-depth cubby-height cubby-width1 [cubby-width2 ... cubby-widthN] | ||
211 | |||
212 | Examples: | ||
213 | |||
214 | ./cubbies.sh 150 90 20 | ||
215 | |||
216 | Generates two total pieces: a side piece (150mm depth, 90mm height) and a | ||
217 | bottom piece (150mm depth and 20mm width). You will need to print the side | ||
218 | piece twice and bottom piece once. | ||
219 | |||
220 | ./cubbies.sh 180 120 20 30 35 | ||
221 | |||
222 | Generates four total pieces, which can be assembled to form a row of | ||
223 | cubbies: one side piece (180mm depth, 120mm height) and three bottom pieces, | ||
224 | with widths of 20mm, 30mm, and 35mm (and each sharing the same depth of | ||
225 | 180mm). The side piece will need to be printed 4 times, and each bottom | ||
226 | piece printed once. | ||
227 | |||
228 | thickness=3 strut_thickness=4 ./cubbies.sh 200 140 20 | ||
229 | |||
230 | You can override any of the other variables from your scad file by passing | ||
231 | them into ./cubbies.sh in this manner. | ||
232 | ``` | ||
233 | |||
234 | ### shopenscad.sh ### | ||
235 | |||
236 | ``` | ||
237 | $ ./shopenscad.sh -h | ||
238 | ./shopenscad.sh: parse and execute OpenSCAD files, allowing default variables from input scad | ||
239 | to be overridden with values supplied from the shell. (The parsing of scad | ||
240 | files is done from the shell mostly with sed, and is very primitive) | ||
241 | |||
242 | Usage: ./shopenscad.sh [-h] [-n] [-p] [-i] [-o OUTPUT-FILENAME] INPUT.scad | ||
243 | |||
244 | -h, --help display this help | ||
245 | -p, --print-vars print variables parsed from INPUT.scad | ||
246 | -i, --interactive show output command and query for execution | ||
247 | -n, --no-act show output command, do not execute | ||
248 | -s, --shell-skel output a skeleton shell script for generating models | ||
249 | -o, --output-filename Specify an output filename. The following variables are | ||
250 | available to you: | ||
251 | |||
252 | $VALUES - all values from INPUT.scad. This is the | ||
253 | default (-o '${VALUES}.stl'): | ||
254 | cube-15-10-5.stl | ||
255 | |||
256 | $ALL - all variable names and values from INPUT.scad | ||
257 | (-o '${ALL}.stl'): | ||
258 | shape=cube,width=15,height=10,depth=5.stl | ||
259 | |||
260 | Additionally, all variables parsed from INPUT.scad | ||
261 | are available in the associatve array , e.g.: | ||
262 | -o '${SCAD[part]}.stl' | ||
263 | |||
264 | Usage examples: | ||
265 | |||
266 | 1) Run openscad with default values from input scad file: | ||
267 | |||
268 | $ ./shopenscad.sh input.scad | ||
269 | |||
270 | 2) You can override variables in the input file with variables passed in | ||
271 | through the shell; this will override "part" and "cubby_width" | ||
272 | paramaters from input scad file. (Note that if the part is a string, | ||
273 | you need to include quotes.): | ||
274 | |||
275 | $ cubby_width=200 part='"bottom"' ./shopenscad.sh input.scad | ||
276 | |||
277 | |||
278 | 3) Generate multiple models: | ||
279 | |||
280 | $ for w in 250 300 350 400; do | ||
281 | cubby_width="" part="bottom" ./shopenscad.sh cubbies.scad | ||
282 | done | ||
283 | ``` | ||
284 | |||
285 | ### test_fits.sh ### | ||
286 | |||
287 | ``` | ||
288 | $ ./test_fits.sh -h | ||
289 | Usage: | ||
290 | ./test_fits.sh | ||
291 | ./test_fits.sh [depth] [tolerance1 tolerance2 .. toleranceN] | ||
292 | |||
293 | By default five test fit pieces are generated with a length of 120mm at the | ||
294 | tolerances 0.1mm, 0.2mm, 0.3mm, 0.4mm, and 0.5mm. | ||
295 | |||
296 | Over ride the defaults by supplying paramaters; the first parameter the depth and the rest are | ||
297 | taken as tolerances. | ||
298 | ``` | ||
299 | |||
300 | ## GPLv3 License ## | ||
301 | |||
302 | Copyright (C) Steven Vasilogianis | ||
303 | |||
304 | This program is free software: you can redistribute it and/or modify it under | ||
305 | the terms of the GNU General Public License as published by the Free Software | ||
306 | Foundation, either version 3 of the License, or (at your option) any later | ||
307 | version. | ||
308 | |||
309 | [^1]: OpenSCAD already allows you to generate models with varying paramaters | ||
310 | through, e.g., `OpenSCAD -D "param1=100" -D "param2=10" scadfile.scad -o | ||
311 | output.stl`. Now you're going to want to change the output.stl filename to avoid | ||
312 | overwriting a previously generated model; generally, the information going into | ||
313 | your variables is useful to have in the output filename. | ||
diff --git a/cubbies.scad b/cubbies.scad new file mode 100644 index 0000000..9497c5e --- /dev/null +++ b/cubbies.scad | |||
@@ -0,0 +1,209 @@ | |||
1 | include <BOSL2/std.scad>; // https://github.com/revarbat/BOSL2 | ||
2 | include <BOSL2/walls.scad>; | ||
3 | include <BOSL2/fnliterals.scad>; | ||
4 | include <BOSL2/joiners.scad>; | ||
5 | |||
6 | $fn=20; | ||
7 | |||
8 | part="both"; // [bottom::Bottom Only,side:Side Only,female_tabs:Holes only from side,male_tabs:Tabs only from bottom,view_fit:Debugging view to confirm tab fit,both:Bottom + Side] | ||
9 | |||
10 | /* [Cubby dimensions] */ | ||
11 | cubby_width=40; | ||
12 | cubby_depth=120; | ||
13 | cubby_height=90; | ||
14 | |||
15 | // thickness of bottom and side | ||
16 | thickness=5; | ||
17 | |||
18 | // thickness of struts (used in both bottom and side) | ||
19 | strut_thickness=5; | ||
20 | |||
21 | // If set (to non zero), this is used in the max_bridge paramater for the | ||
22 | // sparse_struct BOSL 2 function (which generates the wireframe rectangles). By | ||
23 | // default, it is calculated as max_bridge=(cubby_height + cubby_width)/5. | ||
24 | max_bridge=0; | ||
25 | |||
26 | /* [Tab Dimensions] */ | ||
27 | // width of each tab | ||
28 | tab_width=10; | ||
29 | // material between tabs | ||
30 | tab_padding=5; | ||
31 | // this value will be added to the mortises for additional tolerance | ||
32 | tab_tolerance=0.5; | ||
33 | |||
34 | /* [Screw Tabs] */ | ||
35 | screw_tabs=true; | ||
36 | |||
37 | /* [Hidden] */ | ||
38 | tab_depth=thickness; | ||
39 | tab_height=thickness; | ||
40 | default_max_bridge=(cubby_height + cubby_width) / 5; | ||
41 | |||
42 | if ( part == "both" ) { | ||
43 | both(); | ||
44 | } else if ( part =="bottom") { | ||
45 | cubby_bottom(cubby_width, cubby_depth); | ||
46 | } else if ( part == "side" ) { | ||
47 | cubby_side(cubby_depth, cubby_height, screw_tabs); | ||
48 | } else if ( part == "view_fit") { | ||
49 | view_tab_fit(cubby_width, cubby_depth, cubby_height); | ||
50 | } else if ( part == "male_tabs" ) { | ||
51 | male_tabs(cubby_width, cubby_depth); | ||
52 | } else if ( part == "female_tabs" ) { | ||
53 | female_tabs(cubby_depth, cubby_height); | ||
54 | } | ||
55 | |||
56 | function assert_cubby_width() = | ||
57 | let (min_width = strut_thickness + 1) // I'm not sure why I need the +1 | ||
58 | assert(cubby_width >= min_width, | ||
59 | str("Cubby width must be atleast ", min_width, "mm")); | ||
60 | |||
61 | function assert_cubby_depth() = | ||
62 | let (min_depth = tab_width * 2) | ||
63 | assert(cubby_depth >= min_depth, | ||
64 | str("Cubby width must be atleast ", min_depth, "mm")); | ||
65 | |||
66 | function assert_cubby_height() = | ||
67 | let (min_height = cubby_side_bracket_height()) | ||
68 | assert(cubby_height >= min_height, | ||
69 | str("Cubby width must be atleast ", min_height, "mm")); | ||
70 | |||
71 | function double (x) = x * 2; | ||
72 | function half (x) = x / 2; | ||
73 | |||
74 | module both () { | ||
75 | gap=5; | ||
76 | cubby_bottom(cubby_width, cubby_depth); | ||
77 | left(((cubby_width + cubby_height) / 2) + (tab_height * 2) + gap) | ||
78 | cubby_side(cubby_depth, cubby_height, screw_tabs); | ||
79 | } | ||
80 | |||
81 | module cubby_bottom (width, depth) { | ||
82 | let (_=assert_cubby_width()); | ||
83 | let (_=assert_cubby_depth()); | ||
84 | |||
85 | rot(90) yrot(90) color("blue") bottom_wall() yrot(90) down(tab_height / 2) { | ||
86 | move_dist=(width / 2) + (tab_depth / 2); | ||
87 | fwd(move_dist) | ||
88 | alternating_tabs(false, "male", depth, tab_width, | ||
89 | tab_depth, tab_height, tab_padding, tab_tolerance); | ||
90 | back(move_dist) | ||
91 | alternating_tabs(true, "male", depth, tab_width, | ||
92 | tab_depth, tab_height, tab_padding, tab_tolerance); | ||
93 | } | ||
94 | children(); | ||
95 | module bottom_wall () { | ||
96 | strut_wall(width, depth) children(); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | // this calculation needs to be referenced by the view_tab_fit function | ||
101 | function cubby_side_bracket_height() = double(tab_height) + tab_tolerance; | ||
102 | module cubby_side (depth, height, screw_tabs=true) { | ||
103 | let (_=assert_cubby_depth()); | ||
104 | let (_=assert_cubby_height()); | ||
105 | |||
106 | screw_tab_width=15; | ||
107 | screw_tab_height=20; | ||
108 | screw_tab_thickness=thickness; | ||
109 | |||
110 | yrot(-90) color("red") side_wall() { | ||
111 | if ( screw_tabs ) { | ||
112 | position(TOP+FRONT+LEFT) screw_tab(); | ||
113 | up(screw_tab_height) position(BOTTOM+FRONT+LEFT) screw_tab(); | ||
114 | } | ||
115 | attach(BOTTOM, TOP) zrot(90) tab_bracket(); | ||
116 | attach(TOP, BOTTOM) zrot(90) tab_bracket(); | ||
117 | } | ||
118 | children(); | ||
119 | |||
120 | module side_wall () { | ||
121 | strut_wall(depth, height - cubby_side_bracket_height() * 2) children(); | ||
122 | } | ||
123 | |||
124 | module tab_bracket () { | ||
125 | difference() { | ||
126 | cuboid([depth,thickness,cubby_side_bracket_height()]); | ||
127 | alternating_tabs(true, "female", depth, tab_width, tab_depth, | ||
128 | tab_height, tab_padding, tab_tolerance); | ||
129 | alternating_tabs(false, "female", depth, tab_width, tab_depth, | ||
130 | tab_height, tab_padding, tab_tolerance); | ||
131 | } | ||
132 | children(); | ||
133 | } | ||
134 | |||
135 | module screw_tab () { | ||
136 | difference() { | ||
137 | cuboid([screw_tab_width, screw_tab_thickness,screw_tab_height], | ||
138 | anchor=LEFT+TOP); | ||
139 | right(screw_tab_width * (2 / 3)) zrot(90) yrot(90) { | ||
140 | right(screw_tab_height * (1 / 3)) cyl(l=15, r=2, anchor=RIGHT); | ||
141 | right(screw_tab_height * (2 / 3)) cyl(l=15, r=2, anchor=LEFT); | ||
142 | } | ||
143 | } | ||
144 | children(); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | module strut_wall(w, h) { | ||
149 | bridge=max_bridge ? max_bridge : default_max_bridge; | ||
150 | sparse_strut( | ||
151 | l=w, | ||
152 | h=h, | ||
153 | thick=thickness, | ||
154 | maxang=45, | ||
155 | strut=5, | ||
156 | max_bridge=bridge | ||
157 | ) children(); | ||
158 | } | ||
159 | |||
160 | module male_tabs (w, d) { | ||
161 | keep=(w / 2) - thickness; | ||
162 | left_half(x=-keep) cubby_bottom(cubby_width, cubby_depth); | ||
163 | } | ||
164 | |||
165 | module female_tabs (d, h) { | ||
166 | // text inscribed (when printing tolerances) | ||
167 | text_inscription_depth=0.6; | ||
168 | text_size=thickness * 0.9; | ||
169 | text=str(tab_tolerance); | ||
170 | text_pos=[-h/2 + (thickness * 0.95), | ||
171 | -d / 2 + (thickness / 2), | ||
172 | (thickness / 2)]; | ||
173 | |||
174 | xrot(180) left_half(x=(h / -2 + (thickness * 3))) | ||
175 | cubby_side(d, h, screw_tabs=false); | ||
176 | translate(text_pos) zrot(90) linear_extrude(text_inscription_depth) | ||
177 | color("green") text(size=text_size, text, font="Bitstream Vera Sans:style=Bold"); | ||
178 | } | ||
179 | |||
180 | module view_tab_fit (w, d, h) { | ||
181 | // position for testing tab fit | ||
182 | module position_fit_test (left=true) { | ||
183 | cubby_bottom(w, d); | ||
184 | dist=(w / 2) + (thickness / -2) + tab_depth - 2; | ||
185 | down=half(h) - half(thickness) - | ||
186 | tab_height - half(tab_tolerance); | ||
187 | yflip() yrot(90) down(dist * (left ? 1 : -1)) left(down) | ||
188 | cubby_side(d, h, screw_tabs); | ||
189 | } | ||
190 | |||
191 | position_fit_test(left=true); | ||
192 | yrot(180) up(h) position_fit_test(left=false); | ||
193 | } | ||
194 | |||
195 | module alternating_tabs (first, gender, length, tab_width, tab_depth, | ||
196 | tab_height, tab_padding, tolerance) { | ||
197 | dist=length - (tab_padding * 2); | ||
198 | total_tabs=dist / (tab_width + tab_padding + tolerance); | ||
199 | half_tabs=total_tabs / 2; | ||
200 | tab_qty = total_tabs % 2 == 0 ? half_tabs : first ? | ||
201 | ceil(half_tabs) : floor(half_tabs); | ||
202 | start = first ? tab_padding : (tab_padding * 2) + tab_width; | ||
203 | spacing = (tab_width + tab_padding) * 2; | ||
204 | xcopies(spacing, tab_qty, dist, sp=((dist / -2) + start)) | ||
205 | dovetail(gender, angle=0, slide=add_tol(tab_depth), | ||
206 | width=add_tol(tab_width), height=add_tol(tab_height)); | ||
207 | |||
208 | function add_tol (x) = gender == "male" ? x : x + tolerance; | ||
209 | } | ||
diff --git a/cubbies.sh b/cubbies.sh new file mode 100755 index 0000000..43a2416 --- /dev/null +++ b/cubbies.sh | |||
@@ -0,0 +1,65 @@ | |||
1 | #!/bin/bash | ||
2 | # this script started off generated using "shopenscad.sh -s cubbies.scad" | ||
3 | |||
4 | usage() { | ||
5 | cat <<EOF | ||
6 | $0: generate sets of cubbies. You will need print the side piece twice for your | ||
7 | first bottom piece, then a single side piece for each additional bottom piece. | ||
8 | |||
9 | Usage: | ||
10 | $0 cubby-depth cubby-height cubby-width1 [cubby-width2 ... cubby-widthN] | ||
11 | |||
12 | Examples: | ||
13 | |||
14 | $0 150 90 20 | ||
15 | |||
16 | Generates two total pieces: a side piece (150mm depth, 90mm height) and a | ||
17 | bottom piece (150mm depth and 20mm width). You will need to print the side | ||
18 | piece twice and bottom piece once. | ||
19 | |||
20 | $0 180 120 20 30 35 | ||
21 | |||
22 | Generates four total pieces, which can be assembled to form a row of | ||
23 | cubbies: one side piece (180mm depth, 120mm height) and three bottom pieces, | ||
24 | with widths of 20mm, 30mm, and 35mm (and each sharing the same depth of | ||
25 | 180mm). The side piece will need to be printed 4 times, and each bottom | ||
26 | piece printed once. | ||
27 | |||
28 | thickness=3 strut_thickness=4 $0 200 140 20 | ||
29 | |||
30 | You can override any of the other variables from your scad file by passing | ||
31 | them into $0 in this manner. | ||
32 | EOF | ||
33 | } | ||
34 | |||
35 | [ "-h" == "$1" ] && usage && exit; | ||
36 | if [ "$#" -lt 3 ]; then | ||
37 | echo "$0: Needs atleast three paramaters. See usage:"; usage; exit 1; | ||
38 | fi; | ||
39 | |||
40 | shopenscad_cmd="./shopenscad.sh cubbies.scad " | ||
41 | |||
42 | export $(echo $($shopenscad_cmd -p)) | ||
43 | # # $ ./shopenscad.sh -p cubbies.scad | ||
44 | # part='"both"' | ||
45 | # cubby_width=40 | ||
46 | # cubby_depth=120 | ||
47 | # cubby_height=90 | ||
48 | # thickness=5 | ||
49 | # strut_thickness=5 | ||
50 | # max_bridge=0 | ||
51 | # tab_width=10 | ||
52 | # tab_padding=5 | ||
53 | # tab_tolerance=0.5 | ||
54 | # screw_tabs=true | ||
55 | |||
56 | cubby_depth=$1; shift | ||
57 | cubby_height=$1; shift | ||
58 | |||
59 | # generate the single side piece | ||
60 | part='"side"' ${shopenscad_cmd} | ||
61 | |||
62 | # generate each bottom piece | ||
63 | for i in "$@"; do | ||
64 | part='"bottom"' cubby_width="$i" ${shopenscad_cmd} | ||
65 | done; | ||
diff --git a/description b/description new file mode 100644 index 0000000..b1639de --- /dev/null +++ b/description | |||
@@ -0,0 +1 @@ | |||
3d Printable Parametric Cubbies | |||
diff --git a/shopenscad.sh b/shopenscad.sh new file mode 100755 index 0000000..3c71b1f --- /dev/null +++ b/shopenscad.sh | |||
@@ -0,0 +1,238 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | #set -x | ||
4 | |||
5 | OPENSCAD=${OPENSCAD:-"openscad"} | ||
6 | RUN=y | ||
7 | INTERACTIVE= | ||
8 | PRINT_VARS= | ||
9 | USER_OUTPUT_FILENAME= | ||
10 | SCAD_FILE= | ||
11 | SHELL_SKEL= | ||
12 | |||
13 | help() | ||
14 | { | ||
15 | cat <<EOF | ||
16 | $0: parse and execute OpenSCAD files, allowing default variables from input scad | ||
17 | to be overridden with values supplied from the shell. (The parsing of scad | ||
18 | files is done from the shell mostly with sed, and is very primitive) | ||
19 | |||
20 | Usage: $0 [-h] [-n] [-p] [-i] [-o OUTPUT-FILENAME] INPUT.scad | ||
21 | |||
22 | -h, --help display this help | ||
23 | -p, --print-vars print variables parsed from INPUT.scad | ||
24 | -i, --interactive show output command and query for execution | ||
25 | -n, --no-act show output command, do not execute | ||
26 | -s, --shell-skel output a skeleton shell script for generating models | ||
27 | -o, --output-filename Specify an output filename. The following variables are | ||
28 | available to you: | ||
29 | |||
30 | \$VALUES - all values from INPUT.scad. This is the | ||
31 | default (-o '\${VALUES}.stl'): | ||
32 | cube-15-10-5.stl | ||
33 | |||
34 | \$ALL - all variable names and values from INPUT.scad | ||
35 | (-o '\${ALL}.stl'): | ||
36 | shape=cube,width=15,height=10,depth=5.stl | ||
37 | |||
38 | Additionally, all variables parsed from INPUT.scad | ||
39 | are available in the associatve array $SCAD, e.g.: | ||
40 | -o '\${SCAD[part]}.stl' | ||
41 | |||
42 | Usage examples: | ||
43 | |||
44 | 1) Run $OPENSCAD with default values from input scad file: | ||
45 | |||
46 | $ $0 input.scad | ||
47 | |||
48 | 2) You can override variables in the input file with variables passed in | ||
49 | through the shell; this will override "part" and "cubby_width" | ||
50 | paramaters from input scad file. (Note that if the part is a string, | ||
51 | you need to include quotes.): | ||
52 | |||
53 | $ cubby_width=200 part='"bottom"' $0 input.scad | ||
54 | |||
55 | |||
56 | 3) Generate multiple models: | ||
57 | |||
58 | $ for w in 250 300 350 400; do | ||
59 | cubby_width="$w" part="bottom" $0 cubbies.scad | ||
60 | done | ||
61 | EOF | ||
62 | } | ||
63 | |||
64 | parse_options() | ||
65 | { | ||
66 | OPTS=$(getopt \ | ||
67 | --options 'hpinso:' \ | ||
68 | --longoptions 'help,print-vars,interactive,no-act,shell-skel,output-filename:' \ | ||
69 | -- "$@") | ||
70 | eval set -- "$OPTS" | ||
71 | unset OPTS | ||
72 | |||
73 | if [ $# = 0 ]; then | ||
74 | help | ||
75 | exit | ||
76 | fi | ||
77 | |||
78 | while true; do | ||
79 | case "$1" in | ||
80 | -h | --help ) help; exit;; | ||
81 | -p | --print-vars ) PRINT_VARS=y;; | ||
82 | -i | --interactive ) INTERACTIVE=y;; | ||
83 | -n | --no-act ) RUN=;; | ||
84 | -s | --shell-skel ) SHELL_SKEL=y;; | ||
85 | -o | --output-filename ) shift; USER_OUTPUT_FILENAME=$1;; | ||
86 | -- ) shift; break;; | ||
87 | esac | ||
88 | shift | ||
89 | done | ||
90 | |||
91 | SCAD_FILE="$*"; | ||
92 | [ -f "$SCAD_FILE" ] || die "$0: Needs an input scad file" | ||
93 | } | ||
94 | |||
95 | die() { printf 'Error: %s\n' "$*" >&2; exit 1; } | ||
96 | warn() { printf 'Warning: %s\n' "$*" >&2; } | ||
97 | |||
98 | strip_multiline_comments () { | ||
99 | # https://stackoverflow.com/questions/13061785/remove-multi-line-comments | ||
100 | # https://stackoverflow.com/users/751863/steve | ||
101 | sed -r ':a; s%(.*)/\*.*\*/%\1%; ta; /\/\*/ !b; N; ba' | ||
102 | } | ||
103 | |||
104 | strip_trailing_semicolon () { | ||
105 | sed -r 's/;$//' | ||
106 | } | ||
107 | |||
108 | strip_leading_spaces () { | ||
109 | sed -r 's/^ +//' | ||
110 | } | ||
111 | |||
112 | strip_trailing_spaces () { | ||
113 | sed -r 's/ +$//' | ||
114 | } | ||
115 | |||
116 | strip_nonassignments () { | ||
117 | grep -e '^[A-Za-z0-9_]\+\s*=' | ||
118 | } | ||
119 | |||
120 | chomp () { | ||
121 | strip_leading_spaces |strip_trailing_spaces | ||
122 | } | ||
123 | |||
124 | parse_variable () { | ||
125 | #sed -r 's/^([a-zA-Z0-9_]+).*/\1/' | ||
126 | sed -r 's/^([^=]+)=.*/\1/' | ||
127 | } | ||
128 | |||
129 | parse_value () { | ||
130 | #sed -r 's/^.*?=\s*([^;]+);.*$/\1/' | ||
131 | #sed -r 's/^[^=]+\s*=\s*([^;]+);.*$/\1/' | ||
132 | sed -r 's/.*=\s*([^;]+?).*/\1/' | ||
133 | } | ||
134 | |||
135 | val_to_filename () { | ||
136 | echo "$1" |sed -r 's/"//g' |sed -r 's/ /_/g' | ||
137 | } | ||
138 | |||
139 | strip_after_hidden () { | ||
140 | sed -n '/\/\* \[Hidden\] \*\//q;p' "$1" | ||
141 | } | ||
142 | |||
143 | strip_trailing () { | ||
144 | trailing="$1" | ||
145 | input="$2" | ||
146 | echo ${input%${trailing}} | ||
147 | } | ||
148 | |||
149 | main() | ||
150 | { | ||
151 | parse_options "$@" | ||
152 | |||
153 | output_params="" | ||
154 | default_output_filename="" | ||
155 | ALL="" | ||
156 | VALUES="" | ||
157 | val_sep="-" | ||
158 | var_sep="," | ||
159 | declare -A SCAD | ||
160 | declare -a scad_var_order | ||
161 | |||
162 | while IFS= read -r line; do | ||
163 | [ "$line" ] || continue; | ||
164 | clean="$(echo -n "$line" |chomp)" | ||
165 | var=$(echo -n "$clean" |parse_variable |chomp) | ||
166 | |||
167 | # use value provided on command line preferentially to any values in the .scad file | ||
168 | val="" | ||
169 | if [[ -n "${!var}" ]]; then | ||
170 | val=${!var} | ||
171 | else | ||
172 | val=$(echo -n "$clean" |parse_value |chomp) | ||
173 | fi | ||
174 | |||
175 | if [ "$var" ] && [ "$val" ]; then | ||
176 | scad_var_order+=($var); | ||
177 | SCAD[$var]="$val" | ||
178 | output_params+="-D '$var=$val' " | ||
179 | VALUES+="$(val_to_filename "$val")${val_sep}" | ||
180 | ALL+="${var}=$(val_to_filename "$val")${var_sep}" | ||
181 | fi; | ||
182 | done < <(strip_after_hidden $SCAD_FILE | \ | ||
183 | strip_multiline_comments |strip_nonassignments |strip_trailing_semicolon) | ||
184 | |||
185 | if [ "$PRINT_VARS" ]; then | ||
186 | for k in "${scad_var_order[@]}"; do | ||
187 | # Make sure quotes surround openscad strings | ||
188 | case "${SCAD[$k]:0:1}" in | ||
189 | \" ) line="${k}='${SCAD[$k]}'";; | ||
190 | \' ) line=${k}="\"${SCAD[$k]}\"";; | ||
191 | * ) line="${k}=${SCAD[$k]}";; | ||
192 | esac | ||
193 | echo $line | ||
194 | done | ||
195 | exit | ||
196 | fi | ||
197 | |||
198 | if [ "$SHELL_SKEL" ]; then | ||
199 | all_vars=$($0 $SCAD_FILE -p |sed -e 's/^/# /') | ||
200 | cat <<EOF | ||
201 | #!/bin/bash | ||
202 | shopenscad_cmd="$0 $SCAD_FILE " | ||
203 | export \$(echo \$(\$shopenscad_cmd -p)) | ||
204 | ${all_vars} | ||
205 | \$shopenscad_cmd | ||
206 | EOF | ||
207 | exit; | ||
208 | fi; | ||
209 | |||
210 | ALL=$(strip_trailing "$var_sep" "$ALL") | ||
211 | VALUES=$(strip_trailing "$val_sep" "$VALUES") | ||
212 | |||
213 | default_output_filename="${VALUES}.stl" | ||
214 | output_filename="$default_output_filename" | ||
215 | if [ "$USER_OUTPUT_FILENAME" ]; then | ||
216 | output_filename=$(echo $(eval "echo $USER_OUTPUT_FILENAME")) | ||
217 | fi | ||
218 | |||
219 | openscad_str="$OPENSCAD '$SCAD_FILE' "$output_params" -o '$output_filename'" | ||
220 | echo "${openscad_str}" | ||
221 | |||
222 | [ ! "$RUN" ] && [ ! "$INTERACTIVE" ] && exit | ||
223 | |||
224 | if [ "$INTERACTIVE" ]; then | ||
225 | eval_prompt="Evaulate openscad command? (type y for yes, anything else to exit): " | ||
226 | read -p "$eval_prompt" -n1 yes_eval_read | ||
227 | echo | ||
228 | case "$yes_eval_read" in | ||
229 | [Yy]) RUN=y ;; # still true | ||
230 | *) RUN=;; | ||
231 | esac | ||
232 | fi | ||
233 | |||
234 | [ "$RUN" ] && eval "$(echo "$openscad_str")" | ||
235 | } | ||
236 | |||
237 | main "$@" | ||
238 | #parse_options "$@" | ||
diff --git a/svasilogianis.github.io b/svasilogianis.github.io new file mode 160000 | |||
Subproject eb3190880ecf556e030131a8a2be879662ea4c6 | |||
diff --git a/test_fits.sh b/test_fits.sh new file mode 100755 index 0000000..003cb3d --- /dev/null +++ b/test_fits.sh | |||
@@ -0,0 +1,28 @@ | |||
1 | #!/bin/bash | ||
2 | usage() { | ||
3 | cat <<EOF | ||
4 | Usage: | ||
5 | $0 | ||
6 | $0 [depth] [tolerance1 tolerance2 .. toleranceN] | ||
7 | |||
8 | By default five test fit pieces are generated with a length of 120mm at the | ||
9 | tolerances 0.1mm, 0.2mm, 0.3mm, 0.4mm, and 0.5mm. | ||
10 | |||
11 | Over ride the defaults by supplying paramaters; the first parameter the depth and the rest are | ||
12 | taken as tolerances. | ||
13 | EOF | ||
14 | } | ||
15 | |||
16 | [ "-h" == "$1" ] || [ "--help" == "$1" ]&& usage && exit; | ||
17 | |||
18 | depth=${1:-120}; shift | ||
19 | tolerances=${*:-0.1 0.2 0.3 0.4 0.5}; | ||
20 | |||
21 | male='"male_tabs"' | ||
22 | female='"female_tabs"' | ||
23 | shopen="./shopenscad.sh cubbies.scad" | ||
24 | |||
25 | part="$male" $shopen | ||
26 | for t in ${tolerances[@]}; do | ||
27 | tab_tolerance=$t part="$female" $shopen | ||
28 | done; | ||