1//////////////////////////////////////////////////////////////////////
2// LibFile: joiners.scad
3// Snap-together joiners.
4// To use, add the following lines to the beginning of your file:
5// ```
6// include <BOSL/constants.scad>
7// use <BOSL/joiners.scad>
8// ```
9//////////////////////////////////////////////////////////////////////
10
11/*
12BSD 2-Clause License
13
14Copyright (c) 2017, Revar Desmera
15All rights reserved.
16
17Redistribution and use in source and binary forms, with or without
18modification, are permitted provided that the following conditions are met:
19
20* Redistributions of source code must retain the above copyright notice, this
21 list of conditions and the following disclaimer.
22
23* Redistributions in binary form must reproduce the above copyright notice,
24 this list of conditions and the following disclaimer in the documentation
25 and/or other materials provided with the distribution.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
31FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
35OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37*/
38
39
40use <transforms.scad>
41use <shapes.scad>
42include <compat.scad>
43include <constants.scad>
44
45
46// Section: Half Joiners
47
48
49// Module: half_joiner_clear()
50// Description:
51// Creates a mask to clear an area so that a half_joiner can be placed there.
52// Usage:
53// half_joiner_clear(h, w, [a], [clearance], [overlap], [orient], [align])
54// Arguments:
55// h = Height of the joiner to clear space for.
56// w = Width of the joiner to clear space for.
57// a = Overhang angle of the joiner.
58// clearance = Extra width to clear.
59// overlap = Extra depth to clear.
60// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
61// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
62// Example:
63// half_joiner_clear(orient=ORIENT_X);
64module half_joiner_clear(h=20, w=10, a=30, clearance=0, overlap=0.01, orient=ORIENT_Y, align=V_CENTER)
65{
66 dmnd_height = h*1.0;
67 dmnd_width = dmnd_height*tan(a);
68 guide_size = w/3;
69 guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
70
71 orient_and_align([w, guide_width, h], orient, align, orig_orient=ORIENT_Y) {
72 yspread(overlap, n=overlap>0? 2 : 1) {
73 difference() {
74 // Diamonds.
75 scale([w+clearance, dmnd_width/2, dmnd_height/2]) {
76 xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true);
77 }
78 // Blunt point of tab.
79 yspread(guide_width+4) {
80 cube(size=[(w+clearance)*1.05, 4, h*0.99], center=true);
81 }
82 }
83 }
84 if (overlap>0) cube([w+clearance, overlap+0.001, h], center=true);
85 }
86}
87
88
89
90// Module: half_joiner()
91// Usage:
92// half_joiner(h, w, l, [a], [screwsize], [guides], [slop], [orient], [align])
93// Description:
94// Creates a half_joiner object that can be attached to half_joiner2 object.
95// Arguments:
96// h = Height of the half_joiner.
97// w = Width of the half_joiner.
98// l = Length of the backing to the half_joiner.
99// a = Overhang angle of the half_joiner.
100// screwsize = Diameter of screwhole.
101// guides = If true, create sliding alignment guides.
102// slop = Printer specific slop value to make parts fit more closely.
103// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
104// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
105// Example:
106// half_joiner(screwsize=3, orient=ORIENT_X);
107module half_joiner(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, slop=PRINTER_SLOP, orient=ORIENT_Y, align=V_CENTER)
108{
109 dmnd_height = h*1.0;
110 dmnd_width = dmnd_height*tan(a);
111 guide_size = w/3;
112 guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
113
114 if ($children > 0) {
115 difference() {
116 children();
117 half_joiner_clear(h=h, w=w, a=a, clearance=0.1, overlap=0.01, orient=orient, align=align);
118 }
119 }
120 orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
121 difference() {
122 union() {
123 // Make base.
124 difference() {
125 // Solid backing base.
126 fwd(l/2) cube(size=[w, l, h], center=true);
127
128 // Clear diamond for tab
129 grid3d(xa=[-(w*2/3), (w*2/3)]) {
130 half_joiner_clear(h=h+0.01, w=w, clearance=slop*2, a=a);
131 }
132 }
133
134 difference() {
135 // Make tab
136 scale([w/3-slop*2, dmnd_width/2, dmnd_height/2]) xrot(45)
137 cube(size=[1,sqrt(2),sqrt(2)], center=true);
138
139 // Blunt point of tab.
140 back(guide_width/2+2)
141 cube(size=[w*0.99,4,guide_size*2], center=true);
142 }
143
144
145 // Guide ridges.
146 if (guides == true) {
147 xspread(w/3-slop*2) {
148 // Guide ridge.
149 fwd(0.05/2) {
150 scale([0.75, 1, 2]) yrot(45)
151 cube(size=[guide_size/sqrt(2), guide_width+0.05, guide_size/sqrt(2)], center=true);
152 }
153
154 // Snap ridge.
155 scale([0.25, 0.5, 1]) zrot(45)
156 cube(size=[guide_size/sqrt(2), guide_size/sqrt(2), dmnd_width], center=true);
157 }
158 }
159 }
160
161 // Make screwholes, if needed.
162 if (screwsize != undef) {
163 yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12);
164 }
165 }
166 }
167}
168//half_joiner(screwsize=3, orient=ORIENT_Z, align=V_UP);
169
170
171
172// Module: half_joiner2()
173// Usage:
174// half_joiner2(h, w, l, [a], [screwsize], [guides], [orient], [align])
175// Description:
176// Creates a half_joiner2 object that can be attached to half_joiner object.
177// Arguments:
178// h = Height of the half_joiner.
179// w = Width of the half_joiner.
180// l = Length of the backing to the half_joiner.
181// a = Overhang angle of the half_joiner.
182// screwsize = Diameter of screwhole.
183// guides = If true, create sliding alignment guides.
184// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
185// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
186// Example:
187// half_joiner2(screwsize=3, orient=ORIENT_X);
188module half_joiner2(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, orient=ORIENT_Y, align=V_CENTER)
189{
190 dmnd_height = h*1.0;
191 dmnd_width = dmnd_height*tan(a);
192 guide_size = w/3;
193 guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
194
195 if ($children > 0) {
196 difference() {
197 children();
198 half_joiner_clear(h=h, w=w, a=a, clearance=0.1, overlap=0.01, orient=orient, align=align);
199 }
200 }
201
202 orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
203 difference() {
204 union () {
205 fwd(l/2) cube(size=[w, l, h], center=true);
206 cube([w, guide_width, h], center=true);
207 }
208
209 // Subtract mated half_joiner.
210 zrot(180) half_joiner(h=h+0.01, w=w+0.01, l=guide_width+0.01, a=a, screwsize=undef, guides=guides, slop=0.0);
211
212 // Make screwholes, if needed.
213 if (screwsize != undef) {
214 xcyl(r=screwsize*1.1/2, l=w+1, $fn=12);
215 }
216 }
217 }
218}
219
220
221
222// Section: Full Joiners
223
224
225// Module: joiner_clear()
226// Description:
227// Creates a mask to clear an area so that a joiner can be placed there.
228// Usage:
229// joiner_clear(h, w, [a], [clearance], [overlap], [orient], [align])
230// Arguments:
231// h = Height of the joiner to clear space for.
232// w = Width of the joiner to clear space for.
233// a = Overhang angle of the joiner.
234// clearance = Extra width to clear.
235// overlap = Extra depth to clear.
236// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
237// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
238// Example:
239// joiner_clear(orient=ORIENT_X);
240module joiner_clear(h=40, w=10, a=30, clearance=0, overlap=0.01, orient=ORIENT_Y, align=V_CENTER)
241{
242 dmnd_height = h*0.5;
243 dmnd_width = dmnd_height*tan(a);
244 guide_size = w/3;
245 guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
246
247 orient_and_align([w, guide_width, h], orient, align, orig_orient=ORIENT_Y) {
248 up(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=clearance);
249 down(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=-0.01);
250 }
251}
252
253
254
255// Module: joiner()
256// Usage:
257// joiner(h, w, l, [a], [screwsize], [guides], [slop], [orient], [align])
258// Description:
259// Creates a joiner object that can be attached to another joiner object.
260// Arguments:
261// h = Height of the joiner.
262// w = Width of the joiner.
263// l = Length of the backing to the joiner.
264// a = Overhang angle of the joiner.
265// screwsize = Diameter of screwhole.
266// guides = If true, create sliding alignment guides.
267// slop = Printer specific slop value to make parts fit more closely.
268// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
269// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
270// Examples:
271// joiner(screwsize=3, orient=ORIENT_X);
272// joiner(w=10, l=10, h=40, orient=ORIENT_X) cuboid([10, 10*2, 40], align=V_LEFT);
273module joiner(h=40, w=10, l=10, a=30, screwsize=undef, guides=true, slop=PRINTER_SLOP, orient=ORIENT_Y, align=V_CENTER)
274{
275 if ($children > 0) {
276 difference() {
277 children();
278 joiner_clear(h=h, w=w, a=a, clearance=0.1, orient=orient, align=align);
279 }
280 }
281 orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
282 up(h/4) half_joiner(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
283 down(h/4) half_joiner2(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides);
284 }
285}
286
287
288
289// Section: Full Joiners Pairs/Sets
290
291
292// Module: joiner_pair_clear()
293// Description:
294// Creates a mask to clear an area so that a pair of joiners can be placed there.
295// Usage:
296// joiner_pair_clear(spacing, [n], [h], [w], [a], [clearance], [overlap], [orient], [align])
297// Arguments:
298// spacing = Spacing between joiner centers.
299// h = Height of the joiner to clear space for.
300// w = Width of the joiner to clear space for.
301// a = Overhang angle of the joiner.
302// n = Number of joiners (2 by default) to clear for.
303// clearance = Extra width to clear.
304// overlap = Extra depth to clear.
305// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
306// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
307// Examples:
308// joiner_pair_clear(spacing=50, n=2);
309// joiner_pair_clear(spacing=50, n=3);
310module joiner_pair_clear(spacing=100, h=40, w=10, a=30, n=2, clearance=0, overlap=0.01, orient=ORIENT_Y, align=V_CENTER)
311{
312 dmnd_height = h*0.5;
313 dmnd_width = dmnd_height*tan(a);
314 guide_size = w/3;
315 guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
316
317 orient_and_align([spacing+w, guide_width, h], orient, align, orig_orient=ORIENT_Y) {
318 xspread(spacing, n=n) {
319 joiner_clear(h=h, w=w, a=a, clearance=clearance, overlap=overlap);
320 }
321 }
322}
323
324
325
326// Module: joiner_pair()
327// Usage:
328// joiner_pair(h, w, l, [a], [screwsize], [guides], [slop], [orient], [align])
329// Description:
330// Creates a joiner_pair object that can be attached to other joiner_pairs .
331// Arguments:
332// spacing = Spacing between joiner centers.
333// h = Height of the joiners.
334// w = Width of the joiners.
335// l = Length of the backing to the joiners.
336// a = Overhang angle of the joiners.
337// n = Number of joiners in a row. Default: 2
338// alternate = If true (default), each joiner alternates it's orientation. If alternate is "alt", do opposite alternating orientations.
339// screwsize = Diameter of screwhole.
340// guides = If true, create sliding alignment guides.
341// slop = Printer specific slop value to make parts fit more closely.
342// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
343// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
344// Examples:
345// joiner_pair(spacing=50, l=10, orient=ORIENT_X) cuboid([10, 50+10-0.1, 40], align=V_LEFT);
346// joiner_pair(spacing=50, l=10, n=2, orient=ORIENT_X);
347// joiner_pair(spacing=50, l=10, n=3, alternate=false, orient=ORIENT_X);
348// joiner_pair(spacing=50, l=10, n=3, alternate=true, orient=ORIENT_X);
349// joiner_pair(spacing=50, l=10, n=3, alternate="alt", orient=ORIENT_X);
350module joiner_pair(spacing=100, h=40, w=10, l=10, a=30, n=2, alternate=true, screwsize=undef, guides=true, slop=PRINTER_SLOP, orient=ORIENT_Y, align=V_CENTER)
351{
352 if ($children > 0) {
353 difference() {
354 children();
355 joiner_pair_clear(spacing=spacing, h=h, w=w, a=a, clearance=0.1, orient=orient, align=align);
356 }
357 }
358 orient_and_align([spacing+w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
359 left((n-1)*spacing/2) {
360 for (i=[0:n-1]) {
361 right(i*spacing) {
362 yrot(180 + (alternate? (i*180+(alternate=="alt"?180:0))%360 : 0)) {
363 joiner(h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
364 }
365 }
366 }
367 }
368 }
369}
370
371
372
373// Section: Full Joiners Quads/Sets
374
375
376// Module: joiner_quad_clear()
377// Description:
378// Creates a mask to clear an area so that a pair of joiners can be placed there.
379// Usage:
380// joiner_quad_clear(spacing, [n], [h], [w], [a], [clearance], [overlap], [orient], [align])
381// Arguments:
382// spacing1 = Spacing between joiner centers.
383// spacing2 = Spacing between back-to-back pairs/sets of joiners.
384// h = Height of the joiner to clear space for.
385// w = Width of the joiner to clear space for.
386// a = Overhang angle of the joiner.
387// n = Number of joiners in a row. Default: 2
388// clearance = Extra width to clear.
389// overlap = Extra depth to clear.
390// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
391// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
392// Examples:
393// joiner_quad_clear(spacing1=50, spacing2=50, n=2);
394// joiner_quad_clear(spacing1=50, spacing2=50, n=3);
395module joiner_quad_clear(xspacing=undef, yspacing=undef, spacing1=undef, spacing2=undef, n=2, h=40, w=10, a=30, clearance=0, overlap=0.01, orient=ORIENT_Y, align=V_CENTER)
396{
397 spacing1 = first_defined([spacing1, xspacing, 100]);
398 spacing2 = first_defined([spacing2, yspacing, 50]);
399 orient_and_align([w+spacing1, spacing2, h], orient, align, orig_orient=ORIENT_Y) {
400 zrot_copies(n=2) {
401 back(spacing2/2) {
402 joiner_pair_clear(spacing=spacing1, n=n, h=h, w=w, a=a, clearance=clearance, overlap=overlap);
403 }
404 }
405 }
406}
407
408
409
410// Module: joiner_quad()
411// Usage:
412// joiner_quad(h, w, l, [a], [screwsize], [guides], [slop], [orient], [align])
413// Description:
414// Creates a joiner_quad object that can be attached to other joiner_pairs .
415// Arguments:
416// spacing = Spacing between joiner centers.
417// h = Height of the joiners.
418// w = Width of the joiners.
419// l = Length of the backing to the joiners.
420// a = Overhang angle of the joiners.
421// n = Number of joiners in a row. Default: 2
422// alternate = If true (default), each joiner alternates it's orientation. If alternate is "alt", do opposite alternating orientations.
423// screwsize = Diameter of screwhole.
424// guides = If true, create sliding alignment guides.
425// slop = Printer specific slop value to make parts fit more closely.
426// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
427// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
428// Examples:
429// joiner_quad(spacing1=50, spacing2=50, l=10, orient=ORIENT_X) cuboid([50, 50+10-0.1, 40]);
430// joiner_quad(spacing1=50, spacing2=50, l=10, n=2, orient=ORIENT_X);
431// joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate=false, orient=ORIENT_X);
432// joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate=true, orient=ORIENT_X);
433// joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate="alt", orient=ORIENT_X);
434module joiner_quad(spacing1=undef, spacing2=undef, xspacing=undef, yspacing=undef, h=40, w=10, l=10, a=30, n=2, alternate=true, screwsize=undef, guides=true, slop=PRINTER_SLOP, orient=ORIENT_Y, align=V_CENTER)
435{
436 spacing1 = first_defined([spacing1, xspacing, 100]);
437 spacing2 = first_defined([spacing2, yspacing, 50]);
438 if ($children > 0) {
439 difference() {
440 children();
441 joiner_quad_clear(spacing1=spacing1, spacing2=spacing2, h=h, w=w, a=a, clearance=0.1, orient=orient, align=align);
442 }
443 }
444 orient_and_align([w+spacing1, spacing2, h], orient, align, orig_orient=ORIENT_Y) {
445 zrot_copies(n=2) {
446 back(spacing2/2) {
447 joiner_pair(spacing=spacing1, n=n, h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
448 }
449 }
450 }
451}
452
453
454
455// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap