update puzzle to latest version to match solution instructions
[ozzloy@gmail.com/3d-printables] / spin-data.scad
1 /* GNU AGPLv3 (or later at your option)
2 see bottom for more license info */
3
4 /* spin thing that erin likes */
5 $fn = 75;
6
7 layer_height = 0.35;
8
9 weight = "penny";
10 // weight = "608zz";
11
12 //bearing = "608zz";
13 bearing = "sr188";
14 // bearing = "625rs";
15
16 weight_lip_overhang = 0.3;
17 bearing_lip_overhang = weight_lip_overhang;
18 wall = 3;
19 penny_thickness = 1.52;
20 penny_radius = 19.05 / 2;
21
22 sr188_radius = 12.74 / 2; // 0.5 / 2
23 sr188_inner_radius = 6.3 / 2; // 0.2470 / 2
24 sr188_thickness = 4.8; // 0.1895 inch
25 sr188_cover_radius = sr188_radius;
26 sr188_cap_footprint_radius = 12 / 2;
27
28 _608zz_radius = 22 / 2;
29 _608zz_inner_radius = 8.1 / 2;
30 _608zz_cover_radius = _608zz_radius;
31 _608zz_cap_footprint_radius = 12 / 2;
32 _608zz_thickness = 7;
33
34 _625rs_radius = 16 / 2;
35 _625rs_inner_radius = 5 / 2;
36 _625rs_thickness = 5;
37 _625rs_cover_radius = _625rs_radius;
38 _625rs_cap_footprint_radius = _625rs_inner_radius + 1;
39
40 weight_radius = (weight == "penny") ? penny_radius : _608zz_radius;
41 weight_thickness = (weight == "penny") ?
42 penny_thickness * 5 : _608zz_thickness;
43
44 bearing_radius =
45 (bearing == "608zz") ? _608zz_radius
46 : (bearing == "625rs") ? _625rs_radius
47 : (bearing == "sr188") ? sr188_radius
48 : 1/0;
49 bearing_window_radius = bearing_radius - bearing_lip_overhang - 1;
50 bearing_inner_radius =
51 (bearing == "608zz") ? _608zz_inner_radius
52 : (bearing == "625rs") ? _625rs_inner_radius
53 : (bearing == "sr188") ? sr188_inner_radius
54 : 1/0;
55 bearing_cover_radius =
56 (bearing == "608zz") ? _608zz_cover_radius
57 : (bearing == "625rs") ? _625rs_cover_radius
58 : (bearing == "sr188") ? sr188_cover_radius
59 : 1/0;
60 bearing_cap_footprint_radius =
61 (bearing == "608zz") ? _608zz_cap_footprint_radius
62 : (bearing == "625rs" ) ? _625rs_cap_footprint_radius
63 : (bearing == "sr188" ) ? sr188_cap_footprint_radius
64 : 1/0;
65 bearing_thickness =
66 (bearing == "608zz") ? _608zz_thickness
67 : (bearing == "625rs") ? _625rs_thickness
68 : (bearing == "sr188") ? sr188_thickness
69 : 1/0;
70
71 spinner_height = penny_thickness * 5 + 2;
72 arms = 3;
73
74 module cap(bearing_inner_radius,
75 bearing_cap_footprint_radius,
76 bearing_cover_radius,
77 bearing_thickness,
78 bearing_window_radius) {
79 footprint_height = 4.5;
80 footprint_radius_safety = 0.2;
81 cap_height = 3;
82 bearing_thickness_safety = 0.6;
83 finger_spot_height = cap_height * 2 / 3;
84 stripes = 3;
85
86 difference() {
87 union() {
88 cylinder(r1 = bearing_cover_radius - tan(30) * cap_height,
89 r2 = bearing_cover_radius,
90 h = cap_height);
91 linear_extrude(height = cap_height
92 + footprint_height
93 - 1.05) {
94 circle(bearing_window_radius - 1); }
95 linear_extrude(height = cap_height + footprint_height) {
96 circle(bearing_cap_footprint_radius - footprint_radius_safety); }
97 linear_extrude(height = cap_height
98 + footprint_height
99 + bearing_thickness / 2
100 - bearing_thickness_safety) {
101 circle(bearing_inner_radius + 0.1); } }
102 translate([0, 0, -0.01]) {
103 cylinder(r1 = bearing_inner_radius,
104 r2 = bearing_inner_radius - tan(30) * finger_spot_height,
105 h = finger_spot_height);
106 for(stripe = [0 : stripes - 1]) {
107 rotate((stripe / stripes) * 360) {
108 linear_extrude(height = finger_spot_height) {
109 polygon([[0, 0],
110 [bearing_cover_radius * 2, 0],
111 [cos(3 + 360 / (stripes * 2))
112 * bearing_cover_radius * 2,
113 sin(3 + 360 / (stripes * 2))
114 * bearing_cover_radius * 2]]); } } } } } }
115
116 module donut(height, footprint_radius) {
117 bread_radius = height / 2;
118 rotate_extrude() {
119 translate([footprint_radius, 0]) {
120 circle(bread_radius); } } }
121
122 module donut_hole(height, footprint_radius) {
123 difference() {
124 cylinder(r = footprint_radius, h = height, center = true);
125 donut(height, footprint_radius); } }
126
127 module jelly_filled(height, footprint_radius) {
128 cylinder(r = footprint_radius, h = height, center = true);
129 donut(height, footprint_radius); }
130
131 module fillet(r) {
132 offset(r = -r) { offset(delta = r) { children(); } } }
133
134 module mirrored(axis) {
135 children();
136 mirror(axis) children(); }
137
138 module spin_slice(weight_radius,
139 bearing_radius,
140 round_extra,
141 wall,
142 arms) {
143 joiner_radius = (bearing_radius + weight_radius) / 2;
144
145 // a = side along x axis
146 a = bearing_radius + weight_radius + wall;
147 // b = side from center to joiner
148 b = bearing_radius + joiner_radius + round_extra;
149 // c = side between joiner and arm center
150 c = joiner_radius + weight_radius + round_extra;
151
152 cos_C = (pow(a, 2) + pow(b, 2) - pow(c, 2)) / (2 * a * b);
153 sin_C = sqrt(1 - pow(cos_C, 2));
154
155 bearing_xy = [0, 0];
156 weight_xy = [a, 0];
157 joiner_xy = [cos_C, sin_C] * b;
158
159 for(arm = [0 : arms - 1]) {
160 rotate(arm * (360 / arms)) {
161 difference() {
162 union() {
163 translate(bearing_xy) {
164 circle(bearing_radius + round_extra); }
165 translate(weight_xy) {
166 circle(weight_radius + round_extra); }
167 mirrored([0, 1]) {
168 polygon([bearing_xy, weight_xy, joiner_xy]); } }
169 mirrored([0, 1]) {
170 translate(joiner_xy) {
171 circle(joiner_radius); } } } } } }
172
173 module spin_cosine_slice(weight_radius,
174 bearing_radius,
175 round_extra,
176 wall,
177 arms) {
178 /* in order to make a smooth transition from one arm to the next,
179 follow the path of a circle just barely touching both arms and
180 the center circle. this is referred to as the joiner circle.
181
182 the joiner circle's radius and position are calculated using
183 geometry. the center of the bearing, weight and joiner circle
184 create a triangle.
185
186 a = side between bearing and weight centers
187 b = side between bearing and joiner centers
188 c = side between joiner and weight centers
189
190 A = angle opposite a, inside joiner
191 B = angle opposite b, inside weight
192 C = angle opposite c, inside bearing
193 */
194
195 r0 = bearing_radius;
196 r1 = weight_radius;
197 // slightly cheated. calculated using 3 arms, C = 60.
198 r2 = ((pow(r0, 2)
199 + r0 * wall
200 + r0 * r1
201 + pow(wall, 2)
202 + 2 * r1 * wall
203 + r0 * round_extra
204 - wall * round_extra
205 - 3 * r1 * round_extra)
206 / (3 * r1 + wall - r0));
207
208 joiner_radius = r2;
209
210 // a = side along x axis
211 a = r0 + wall + r1;
212 // b = side from center to joiner
213 b = r0 + round_extra + r2;
214 // c = side between joiner and arm center
215 c = r1 + round_extra + r2;
216
217 bearing_xy = [0, 0];
218 weight_xy = [a, 0];
219 joiner_xy = [cos(60), sin(60)] * b;
220
221 translate(bearing_xy) {
222 circle(bearing_radius + round_extra); }
223 for(arm = [0 : arms - 1]) {
224 rotate(arm * (360 / arms)) {
225 translate(weight_xy) {
226 circle(weight_radius + round_extra); }
227 mirrored([0, 1]) {
228 difference() {
229 polygon([bearing_xy, weight_xy, joiner_xy]);
230 translate(joiner_xy) {
231 circle(joiner_radius); } } } } } }
232
233 module spin_slices(weight_radius,
234 weight_thickness,
235 bearing_radius,
236 bearing_thickness,
237 weight_lip_overhang = 0.3,
238 bearing_lip_overhang = 0.3,
239 wall = 3,
240 arms = 3,
241 layer_height = 0.15) {
242 thicker_thickness = (bearing_thickness > weight_thickness) ?
243 bearing_thickness : weight_thickness;
244 calculated_height = thicker_thickness + 2 * wall;
245 layers = 2 * ceil(ceil(calculated_height / layer_height) / 2);
246 actual_height = layers * layer_height;
247 round_radius = actual_height / 2;
248
249 /* rounding the outside edge of the spinner with a semi-circle leads
250 to a shape that an overhang on the second layer several times the
251 thickness of a printed extrusion width.
252
253 rather than using a full semi-circle, this code aims to use just the
254 portion in the middle, where the overhang is less severe */
255 old_start = 0;
256 old_end = (layers / 2) - 1;
257
258 /* add one to have some thickness all around weight holes
259 for first layer */
260 new_start = old_end / 16 + 1;
261 new_end = old_end;
262
263 old_range = old_end - old_start;
264 new_range = new_end - new_start;
265
266 factor = new_range / old_range;
267
268 /* initial adjacent is adjusted to (new start - 1) to allow some
269 thickness all around weight holes on first layer */
270 initial_adjacent = round_radius - ((new_start - 1) * layer_height);
271 initial_angle = acos(initial_adjacent / round_radius);
272 initial_round_extra = initial_adjacent * tan(initial_angle);
273
274 difference() {
275 mirrored([0, 0, 1]) {
276 for(layer = [0 : (layers / 2) - 1]) {
277 translate([0, 0, layer * layer_height - actual_height / 2]) {
278 linear_extrude(height = layer_height) {
279 new_layer = (layer - old_start) * factor + new_start;
280 adjacent = round_radius - (new_layer * layer_height);
281 angle = acos(adjacent / round_radius);
282 round_extra = adjacent * tan(angle) - initial_round_extra;
283 spin_slice(weight_radius,
284 bearing_radius,
285 round_extra,
286 wall,
287 arms); } } } }
288 cylinder(h = actual_height + 0.1,
289 r = bearing_radius - bearing_lip_overhang,
290 center = true);
291 cylinder(h = bearing_thickness + 0.05,
292 r = bearing_radius + 0.15,
293 center = true);
294 for(arm = [0 : arms - 1]) {
295 rotate(arm * (360 / arms)) {
296 translate([bearing_radius + wall + weight_radius, 0]) {
297 cylinder(h = actual_height + 0.1,
298 r = weight_radius - weight_lip_overhang,
299 center = true);
300 cylinder(h = weight_thickness + 0.05,
301 r = weight_radius + 0.15,
302 center = true); } } } } }
303
304 module equilateral_triangle(radius){
305 polygon([[cos(0) * radius, sin(0) * radius],
306 [cos(120) * radius,
307 sin(120) * radius],
308 [cos(240) * radius,
309 sin(240) * radius]]); }
310
311 module spin_cosine(weight_radius,
312 weight_thickness,
313 bearing_radius,
314 bearing_thickness,
315 weight_lip_overhang = 0.3,
316 bearing_lip_overhang = 0.3,
317 wall = 3,
318 arms = 3,
319 layer_height = 0.15) {
320 thicker_thickness = (bearing_thickness > weight_thickness) ?
321 bearing_thickness : weight_thickness;
322 calculated_height = thicker_thickness + 2 * wall;
323 layers = 2 * ceil(ceil(calculated_height / layer_height) / 2);
324 actual_height = layers * layer_height;
325 round_radius = actual_height / 2;
326
327 /* rounding the outside edge of the spinner with a semi-circle leads
328 to a shape that an overhang on the second layer several times the
329 thickness of a printed extrusion width.
330
331 rather than using a full semi-circle, this code aims to use just the
332 portion in the middle, where the overhang is less severe */
333 old_start = 0;
334 old_end = (layers / 2) - 1;
335
336 /* add one to have some thickness all around weight holes
337 for first layer */
338 new_start = old_end / 16 + 1;
339 new_end = old_end;
340
341 old_range = old_end - old_start;
342 new_range = new_end - new_start;
343
344 factor = new_range / old_range;
345
346 /* initial adjacent is adjusted to (new start - 1) to allow some
347 thickness all around weight holes on first layer */
348 initial_adjacent = round_radius - ((new_start - 1) * layer_height);
349 initial_angle = acos(initial_adjacent / round_radius);
350 initial_round_extra = initial_adjacent * tan(initial_angle);
351
352 difference() {
353 mirrored([0, 0, 1]) {
354 for(layer = [0 : (layers / 2) - 1]) {
355 translate([0, 0, layer * layer_height - actual_height / 2]) {
356 linear_extrude(height = layer_height) {
357 new_layer = (layer - old_start) * factor + new_start;
358 adjacent = round_radius - (new_layer * layer_height);
359 angle = acos(adjacent / round_radius);
360 round_extra = adjacent * tan(angle) - initial_round_extra;
361 spin_cosine_slice(weight_radius,
362 bearing_radius,
363 round_extra,
364 wall,
365 arms); } } } }
366 // bearing window hole
367 /*cylinder(h = actual_height + 0.1,
368 r = bearing_radius - bearing_lip_overhang,
369 center = true);*/
370 linear_extrude(height = actual_height + 0.1, center = true) {
371 intersection() {
372 equilateral_triangle(1.8 * bearing_radius);
373 circle(bearing_radius + 0.1); } }
374 // bearing cavity
375 cylinder(h = bearing_thickness + 0.05,
376 r = bearing_radius + 0.15,
377 center = true);
378 // arm holes
379 for(arm = [0 : arms - 1]) {
380 rotate(arm * (360 / arms)) {
381 translate([bearing_radius + wall + weight_radius, 0]) {
382 // weight window hole
383 mirrored([0, 0, 1]) {
384 translate([0, 0, (weight_thickness + 0.05) / 2]) {
385 linear_extrude(height = wall + 0.1) {
386 intersection(){
387 equilateral_triangle(1.8 * weight_radius);
388 circle(weight_radius + 0.2); } } } }
389 // weight cavity
390 cylinder(h = weight_thickness + 0.05,
391 r = weight_radius + 0.20,
392 center = true); } } } } }
393
394 module spin_donut(weight_radius,
395 weight_thickness,
396 bearing_radius,
397 bearing_thickness,
398 weight_lip_overhang,
399 bearing_lip_overhang,
400 wall,
401 arms) {
402 thicker_thickness = (bearing_thickness > weight_thickness)
403 ? bearing_thickness : weight_thickness;
404 height = thicker_thickness + wall * 2;
405
406 center_to_arm_center = bearing_radius + wall + weight_radius;
407
408 jelly_filled(height, bearing_radius);
409 for(arm = [0 : arms]) {
410 rotate(arm * (360 / arms)) {
411 translate([center_to_arm_center, 0, 0]) {
412 jelly_filled(height, weight_radius); } } } }
413
414 /*
415 This file is part of 3d-printables.
416
417 3d-printables is free software: you can redistribute it and/or modify
418 it under the terms of the GNU Affero General Public License as published by
419 the Free Software Foundation, either version 3 of the License, or
420 (at your option) any later version.
421
422 3d-printables is distributed in the hope that it will be useful,
423 but WITHOUT ANY WARRANTY; without even the implied warranty of
424 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
425 GNU Affero General Public License for more details.
426
427 You should have received a copy of the GNU Affero General Public License
428 along with challenge-bot. If not, see <http://www.gnu.org/licenses/>.
429 */