1//////////////////////////////////////////////////////////////////////
2// LibFile: masks.scad
3// Masking shapes.
4// To use, add the following lines to the beginning of your file:
5// ```
6// include <BOSL/constants.scad>
7// use <BOSL/masks.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>
42use <math.scad>
43include <compat.scad>
44include <constants.scad>
45
46
47// Section: General Masks
48
49// Module: angle_pie_mask()
50// Usage:
51// angle_pie_mask(r|d, l, ang, [orient], [align], [center]);
52// angle_pie_mask(r1|d1, r2|d2, l, ang, [orient], [align], [center]);
53// Description:
54// Creates a pie wedge shape that can be used to mask other shapes.
55// Arguments:
56// ang = angle of wedge in degrees.
57// l = height of wedge.
58// r = Radius of circle wedge is created from. (optional)
59// r1 = Bottom radius of cone that wedge is created from. (optional)
60// r2 = Upper radius of cone that wedge is created from. (optional)
61// d = Diameter of circle wedge is created from. (optional)
62// d1 = Bottom diameter of cone that wedge is created from. (optional)
63// d2 = Upper diameter of cone that wedge is created from. (optional)
64// orient = Orientation of the pie slice. Use the ORIENT_ constants from constants.h. Default: ORIENT_Z.
65// align = Alignment of the pie slice. Use the V_ constants from constants.h. Default: V_CENTER.
66// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
67// Example(FR):
68// angle_pie_mask(ang=30, d=100, l=20);
69module angle_pie_mask(
70 ang=45, l=undef,
71 r=undef, r1=undef, r2=undef,
72 d=undef, d1=undef, d2=undef,
73 orient=ORIENT_Z, align=V_CENTER,
74 h=undef, center=undef
75) {
76 l = first_defined([l, h, 1]);
77 r1 = get_radius(r1, r, d1, d, 10);
78 r2 = get_radius(r2, r, d2, d, 10);
79 orient_and_align([2*r1, 2*r1, l], orient, align, center=center) {
80 pie_slice(ang=ang, l=l+0.1, r1=r1, r2=r2, align=V_CENTER);
81 }
82}
83
84
85// Module: cylinder_mask()
86// Usage: Mask objects
87// cylinder_mask(l, r|d, chamfer, [chamfang], [from_end], [circum], [overage], [ends_only], [orient], [align]);
88// cylinder_mask(l, r|d, fillet, [circum], [overage], [ends_only], [orient], [align]);
89// cylinder_mask(l, r|d, [chamfer1|fillet1], [chamfer2|fillet2], [chamfang1], [chamfang2], [from_end], [circum], [overage], [ends_only], [orient], [align]);
90// Usage: Masking operators
91// cylinder_mask(l, r|d, chamfer, [chamfang], [from_end], [circum], [overage], [ends_only], [orient], [align]) ...
92// cylinder_mask(l, r|d, fillet, [circum], [overage], [ends_only], [orient], [align]) ...
93// cylinder_mask(l, r|d, [chamfer1|fillet1], [chamfer2|fillet2], [chamfang1], [chamfang2], [from_end], [circum], [overage], [ends_only], [orient], [align]) ...
94// Description:
95// If passed children, bevels/chamfers and/or rounds/fillets one or
96// both ends of the origin-centered cylindrical region specified. If
97// passed no children, creates a mask to bevel/chamfer and/or round/fillet
98// one or both ends of the cylindrical region. Difference the mask
99// from the region, making sure the center of the mask object is align
100// exactly with the center of the cylindrical region to be chamferred.
101// Arguments:
102// l = Length of the cylindrical/conical region.
103// r = Radius of cylindrical region to chamfer.
104// r1 = Radius of axis-negative end of the region to chamfer.
105// r2 = Radius of axis-positive end of the region to chamfer.
106// d = Diameter of cylindrical region to chamfer.
107// d1 = Diameter of axis-negative end of the region to chamfer.
108// d1 = Diameter of axis-positive end of the region to chamfer.
109// chamfer = Size of the chamfers/bevels. (Default: 0.25)
110// chamfer1 = Size of the chamfers/bevels for the axis-negative end of the region.
111// chamfer2 = Size of the chamfers/bevels for the axis-positive end of the region.
112// chamfang = Angle of chamfers/bevels in degrees from the length axis of the region. (Default: 45)
113// chamfang1 = Angle of chamfer/bevel of the axis-negative end of the region, in degrees from the length axis.
114// chamfang2 = Angle of chamfer/bevel of the axis-positive end of the region, in degrees from the length axis.
115// fillet = The radius of the fillets on the ends of the region. Default: none.
116// fillet1 = The radius of the fillet on the axis-negative end of the region.
117// fillet2 = The radius of the fillet on the axis-positive end of the region.
118// circum = If true, region will circumscribe the circle of the given radius/diameter.
119// from_end = If true, chamfer/bevel size is measured from end of region. If false, chamfer/bevel is measured outset from the radius of the region. (Default: false)
120// overage = The extra thickness of the mask. Default: `10`.
121// ends_only = If true, only mask the ends and not around the middle of the cylinder.
122// orient = Orientation. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
123// align = Alignment of the region. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
124// Example:
125// difference() {
126// cylinder(h=100, r1=60, r2=30, center=true);
127// cylinder_mask(l=100, r1=60, r2=30, chamfer=10, from_end=true);
128// }
129// Example:
130// cylinder_mask(l=100, r=50, chamfer1=10, fillet2=10) {
131// cube([100,50,100], center=true);
132// }
133module cylinder_mask(
134 l,
135 r=undef, r1=undef, r2=undef,
136 d=undef, d1=undef, d2=undef,
137 chamfer=undef, chamfer1=undef, chamfer2=undef,
138 chamfang=undef, chamfang1=undef, chamfang2=undef,
139 fillet=undef, fillet1=undef, fillet2=undef,
140 circum=false, from_end=false,
141 overage=10, ends_only=false,
142 orient=ORIENT_Z, align=V_CENTER
143) {
144 r1 = get_radius(r=r, d=d, r1=r1, d1=d1, dflt=1);
145 r2 = get_radius(r=r, d=d, r1=r2, d1=d2, dflt=1);
146 sides = segs(max(r1,r2));
147 sc = circum? 1/cos(180/sides) : 1;
148 vang = atan2(l, r1-r2)/2;
149 ang1 = first_defined([chamfang1, chamfang, vang]);
150 ang2 = first_defined([chamfang2, chamfang, 90-vang]);
151 cham1 = first_defined([chamfer1, chamfer, 0]);
152 cham2 = first_defined([chamfer2, chamfer, 0]);
153 fil1 = first_defined([fillet1, fillet, 0]);
154 fil2 = first_defined([fillet2, fillet, 0]);
155 maxd = max(r1,r2);
156 if ($children > 0) {
157 difference() {
158 children();
159 cylinder_mask(l=l, r1=sc*r1, r2=sc*r2, chamfer1=cham1, chamfer2=cham2, chamfang1=ang1, chamfang2=ang2, fillet1=fil1, fillet2=fil2, orient=orient, from_end=from_end);
160 }
161 } else {
162 orient_and_align([2*r1, 2*r1, l], orient, align) {
163 difference() {
164 union() {
165 chlen1 = cham1 / (from_end? 1 : tan(ang1));
166 chlen2 = cham2 / (from_end? 1 : tan(ang2));
167 if (!ends_only) {
168 cylinder(r=maxd+overage, h=l+2*overage, center=true);
169 } else {
170 if (cham2>0) up(l/2-chlen2) cylinder(r=maxd+overage, h=chlen2+overage, center=false);
171 if (cham1>0) down(l/2+overage) cylinder(r=maxd+overage, h=chlen1+overage, center=false);
172 if (fil2>0) up(l/2-fil2) cylinder(r=maxd+overage, h=fil2+overage, center=false);
173 if (fil1>0) down(l/2+overage) cylinder(r=maxd+overage, h=fil1+overage, center=false);
174 }
175 }
176 cyl(r1=sc*r1, r2=sc*r2, l=l, chamfer1=cham1, chamfer2=cham2, chamfang1=ang1, chamfang2=ang2, from_end=from_end, fillet1=fil1, fillet2=fil2);
177 }
178 }
179 }
180}
181
182
183
184// Section: Chamfers
185
186
187// Module: chamfer_mask()
188// Usage:
189// chamfer_mask(l, chamfer, [orient], [align], [center]);
190// Description:
191// Creates a shape that can be used to chamfer a 90 degree edge.
192// Difference it from the object to be chamfered. The center of
193// the mask object should align exactly with the edge to be chamfered.
194// Arguments:
195// l = Length of mask.
196// chamfer = Size of chamfer
197// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: vertical.
198// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
199// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
200// Example:
201// difference() {
202// cube(50);
203// #chamfer_mask(l=50, chamfer=10, orient=ORIENT_X, align=V_RIGHT);
204// }
205module chamfer_mask(l=1, chamfer=1, orient=ORIENT_Z, align=V_CENTER, center=undef) {
206 orient_and_align([chamfer, chamfer, l], orient, align, center=center) {
207 cylinder(d=chamfer*2, h=l+0.1, center=true, $fn=4);
208 }
209}
210
211
212// Module: chamfer_mask_x()
213// Usage:
214// chamfer_mask_x(l, chamfer, [align]);
215// Description:
216// Creates a shape that can be used to chamfer a 90 degree edge along the X axis.
217// Difference it from the object to be chamfered. The center of the mask
218// object should align exactly with the edge to be chamfered.
219// Arguments:
220// l = Height of mask
221// chamfer = size of chamfer
222// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
223// Example:
224// difference() {
225// left(40) cube(80);
226// #chamfer_mask_x(l=80, chamfer=20);
227// }
228module chamfer_mask_x(l=1.0, chamfer=1.0, align=V_CENTER) {
229 chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_X, align=align);
230}
231
232
233// Module: chamfer_mask_y()
234// Usage:
235// chamfer_mask_y(l, chamfer, [align]);
236// Description:
237// Creates a shape that can be used to chamfer a 90 degree edge along the Y axis.
238// Difference it from the object to be chamfered. The center of the mask
239// object should align exactly with the edge to be chamfered.
240// Arguments:
241// l = Height of mask
242// chamfer = size of chamfer
243// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
244// Example:
245// difference() {
246// fwd(40) cube(80);
247// right(80) #chamfer_mask_y(l=80, chamfer=20);
248// }
249module chamfer_mask_y(l=1.0, chamfer=1.0, align=V_CENTER) {
250 chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Y, align=align);
251}
252
253
254// Module: chamfer_mask_z()
255// Usage:
256// chamfer_mask_z(l, chamfer, [align]);
257// Description:
258// Creates a shape that can be used to chamfer a 90 degree edge along the Z axis.
259// Difference it from the object to be chamfered. The center of the mask
260// object should align exactly with the edge to be chamfered.
261// Arguments:
262// l = Height of mask
263// chamfer = size of chamfer
264// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
265// Example:
266// difference() {
267// down(40) cube(80);
268// #chamfer_mask_z(l=80, chamfer=20);
269// }
270module chamfer_mask_z(l=1.0, chamfer=1.0, align=V_CENTER) {
271 chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Z, align=align);
272}
273
274
275// Module: chamfer()
276// Usage:
277// chamfer(chamfer, size, [edges]) ...
278// Description:
279// Chamfers the edges of a cuboid region containing childrem, centered on the origin.
280// Arguments:
281// chamfer = Inset of the chamfer from the edge. (Default: 1)
282// size = The size of the rectangular cuboid we want to chamfer.
283// edges = Which edges do we want to chamfer. Recommend to use EDGE constants from constants.scad.
284// Description:
285// You should use `EDGE` constants from `constants.scad` with the `edge` argument.
286// However, if you must handle it raw, the edge ordering is this:
287// [
288// [Y+Z+, Y-Z+, Y-Z-, Y+Z-],
289// [X+Z+, X-Z+, X-Z-, X+Z-],
290// [X+Y+, X-Y+, X-Y-, X+Y-]
291// ]
292// Example(FR):
293// chamfer(chamfer=2, size=[20,40,30]) {
294// cube(size=[20,40,30], center=true);
295// }
296// Example(FR):
297// chamfer(chamfer=2, size=[20,40,30], edges=EDGES_TOP - EDGE_TOP_LF + EDGE_FR_RT) {
298// cube(size=[20,40,30], center=true);
299// }
300module chamfer(chamfer=1, size=[1,1,1], edges=EDGES_ALL)
301{
302 difference() {
303 children();
304 difference() {
305 cube(size, center=true);
306 cuboid(size+[1,1,1]*0.02, chamfer=chamfer+0.01, edges=edges, trimcorners=true);
307 }
308 }
309}
310
311
312// Module: chamfer_cylinder_mask()
313// Usage:
314// chamfer_cylinder_mask(r|d, chamfer, [ang], [from_end], [orient])
315// Description:
316// Create a mask that can be used to bevel/chamfer the end of a cylindrical region.
317// Difference it from the end of the region to be chamferred. The center of the mask
318// object should align exactly with the center of the end of the cylindrical region
319// to be chamferred.
320// Arguments:
321// r = Radius of cylinder to chamfer.
322// d = Diameter of cylinder to chamfer. Use instead of r.
323// chamfer = Size of the edge chamferred, inset from edge. (Default: 0.25)
324// ang = Angle of chamfer in degrees from vertical. (Default: 45)
325// from_end = If true, chamfer size is measured from end of cylinder. If false, chamfer is measured outset from the radius of the cylinder. (Default: false)
326// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: ORIENT_Z.
327// Example:
328// difference() {
329// cylinder(r=50, h=100, center=true);
330// up(50) #chamfer_cylinder_mask(r=50, chamfer=10);
331// }
332module chamfer_cylinder_mask(r=1.0, d=undef, chamfer=0.25, ang=45, from_end=false, orient=ORIENT_Z)
333{
334 r = get_radius(r=r, d=d, dflt=1);
335 rot(orient) cylinder_mask(l=chamfer*3, r=r, chamfer2=chamfer, chamfang2=ang, from_end=from_end, ends_only=true, align=V_DOWN);
336}
337
338
339// Module: chamfer_hole_mask()
340// Usage:
341// chamfer_hole_mask(r|d, chamfer, [ang], [from_end]);
342// Description:
343// Create a mask that can be used to bevel/chamfer the end of a cylindrical hole.
344// Difference it from the hole to be chamferred. The center of the mask object
345// should align exactly with the center of the end of the hole to be chamferred.
346// Arguments:
347// r = Radius of hole to chamfer.
348// d = Diameter of hole to chamfer. Use instead of r.
349// chamfer = Size of the chamfer. (Default: 0.25)
350// ang = Angle of chamfer in degrees from vertical. (Default: 45)
351// from_end = If true, chamfer size is measured from end of hole. If false, chamfer is measured outset from the radius of the hole. (Default: false)
352// overage = The extra thickness of the mask. Default: `0.1`.
353// Example:
354// difference() {
355// cube(100, center=true);
356// cylinder(d=50, h=100.1, center=true);
357// up(50) #chamfer_hole_mask(d=50, chamfer=10);
358// }
359// Example:
360// chamfer_hole_mask(d=100, chamfer=25, ang=30, overage=10);
361module chamfer_hole_mask(r=undef, d=undef, chamfer=0.25, ang=45, from_end=false, overage=0.1)
362{
363 r = get_radius(r=r, d=d, dflt=1);
364 h = chamfer * (from_end? 1 : tan(90-ang));
365 r2 = r + chamfer * (from_end? tan(ang) : 1);
366 $fn = segs(r);
367 difference() {
368 union() {
369 cylinder(r=r2, h=overage, center=false);
370 down(h) cylinder(r1=r, r2=r2, h=h, center=false);
371 }
372 cylinder(r=r-overage, h=h*2.1+overage, center=true);
373 }
374}
375
376
377
378// Section: Filleting/Rounding
379
380// Module: fillet_mask()
381// Usage:
382// fillet_mask(l|h, r, [orient], [align], [center])
383// Description:
384// Creates a shape that can be used to fillet a vertical 90 degree edge.
385// Difference it from the object to be filletted. The center of the mask
386// object should align exactly with the edge to be filletted.
387// Arguments:
388// l = Length of mask.
389// r = Radius of the fillet.
390// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: vertical.
391// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
392// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
393// Example:
394// difference() {
395// cube(size=100, center=false);
396// #fillet_mask(l=100, r=25, orient=ORIENT_Z, align=V_UP);
397// }
398module fillet_mask(l=undef, r=1.0, orient=ORIENT_Z, align=V_CENTER, h=undef, center=undef)
399{
400 l = first_defined([l, h, 1]);
401 sides = quantup(segs(r),4);
402 orient_and_align([2*r, 2*r, l], orient, align, center=center) {
403 linear_extrude(height=l+0.1, convexity=4, center=true) {
404 difference() {
405 square(2*r, center=true);
406 xspread(2*r) yspread(2*r) circle(r=r, $fn=sides);
407 }
408 }
409 }
410}
411
412
413// Module: fillet_mask_x()
414// Usage:
415// fillet_mask_x(l, r, [align], [center])
416// Description:
417// Creates a shape that can be used to fillet a 90 degree edge oriented
418// along the X axis. Difference it from the object to be filletted.
419// The center of the mask object should align exactly with the edge to
420// be filletted.
421// Arguments:
422// l = Length of mask.
423// r = Radius of the fillet.
424// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
425// Example:
426// difference() {
427// cube(size=100, center=false);
428// #fillet_mask_x(l=100, r=25, align=V_RIGHT);
429// }
430module fillet_mask_x(l=1.0, r=1.0, align=V_CENTER) fillet_mask(l=l, r=r, orient=ORIENT_X, align=align);
431
432
433// Module: fillet_mask_y()
434// Usage:
435// fillet_mask_y(l, r, [align], [center])
436// Description:
437// Creates a shape that can be used to fillet a 90 degree edge oriented
438// along the Y axis. Difference it from the object to be filletted.
439// The center of the mask object should align exactly with the edge to
440// be filletted.
441// Arguments:
442// l = Length of mask.
443// r = Radius of the fillet.
444// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
445// Example:
446// difference() {
447// cube(size=100, center=false);
448// right(100) #fillet_mask_y(l=100, r=25, align=V_BACK);
449// }
450module fillet_mask_y(l=1.0, r=1.0, align=V_CENTER) fillet_mask(l=l, r=r, orient=ORIENT_Y, align=align);
451
452
453// Module: fillet_mask_z()
454// Usage:
455// fillet_mask_z(l, r, [align], [center])
456// Description:
457// Creates a shape that can be used to fillet a 90 degree edge oriented
458// along the Z axis. Difference it from the object to be filletted.
459// The center of the mask object should align exactly with the edge to
460// be filletted.
461// Arguments:
462// l = Length of mask.
463// r = Radius of the fillet.
464// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
465// Example:
466// difference() {
467// cube(size=100, center=false);
468// #fillet_mask_z(l=100, r=25, align=V_UP);
469// }
470module fillet_mask_z(l=1.0, r=1.0, align=V_CENTER) fillet_mask(l=l, r=r, orient=ORIENT_Z, align=align);
471
472
473// Module: fillet()
474// Usage:
475// fillet(fillet, size, [edges]) ...
476// Description:
477// Fillets the edges of a cuboid region containing the given children.
478// Arguments:
479// fillet = Radius of the fillet. (Default: 1)
480// size = The size of the rectangular cuboid we want to chamfer.
481// edges = Which edges do we want to chamfer. Recommend to use EDGE constants from constants.scad.
482// Description:
483// You should use `EDGE` constants from `constants.scad` with the `edge` argument.
484// However, if you must handle it raw, the edge ordering is this:
485// [
486// [Y+Z+, Y-Z+, Y-Z-, Y+Z-],
487// [X+Z+, X-Z+, X-Z-, X+Z-],
488// [X+Y+, X-Y+, X-Y-, X+Y-]
489// ]
490// Example(FR):
491// fillet(fillet=10, size=[50,100,150], $fn=24) {
492// cube(size=[50,100,150], center=true);
493// }
494// Example(FR,FlatSpin):
495// fillet(fillet=10, size=[50,50,75], edges=EDGES_TOP - EDGE_TOP_LF + EDGE_FR_RT, $fn=24) {
496// cube(size=[50,50,75], center=true);
497// }
498module fillet(fillet=1, size=[1,1,1], edges=EDGES_ALL)
499{
500 difference() {
501 children();
502 difference() {
503 cube(size, center=true);
504 cuboid(size+[1,1,1]*0.01, fillet=fillet, edges=edges, trimcorners=true);
505 }
506 }
507}
508
509
510// Module: fillet_angled_edge_mask()
511// Usage:
512// fillet_angled_edge_mask(h, r, [ang], [center]);
513// Description:
514// Creates a vertical mask that can be used to fillet the edge where two
515// face meet, at any arbitrary angle. Difference it from the object to
516// be filletted. The center of the mask should align exactly with the
517// edge to be filletted.
518// Arguments:
519// h = height of vertical mask.
520// r = radius of the fillet.
521// ang = angle that the planes meet at.
522// center = If true, vertically center mask.
523// Example:
524// difference() {
525// angle_pie_mask(ang=70, h=50, d=100);
526// #fillet_angled_edge_mask(h=51, r=20.0, ang=70, $fn=32);
527// }
528module fillet_angled_edge_mask(h=1.0, r=1.0, ang=90, center=true)
529{
530 sweep = 180-ang;
531 n = ceil(segs(r)*sweep/360);
532 x = r*sin(90-(ang/2))/sin(ang/2);
533 linear_extrude(height=h, convexity=4, center=center) {
534 polygon(
535 points=concat(
536 [for (i = [0:n]) let (a=90+ang+i*sweep/n) [r*cos(a)+x, r*sin(a)+r]],
537 [for (i = [0:n]) let (a=90+i*sweep/n) [r*cos(a)+x, r*sin(a)-r]],
538 [
539 [min(-1, r*cos(270-ang)+x-1), r*sin(270-ang)-r],
540 [min(-1, r*cos(90+ang)+x-1), r*sin(90+ang)+r],
541 ]
542 )
543 );
544 }
545}
546
547
548// Module: fillet_angled_corner_mask()
549// Usage:
550// fillet_angled_corner_mask(fillet, ang);
551// Description:
552// Creates a shape that can be used to fillet the corner of an angle.
553// Difference it from the object to be filletted. The center of the mask
554// object should align exactly with the point of the corner to be filletted.
555// Arguments:
556// fillet = radius of the fillet.
557// ang = angle between planes that you need to fillet the corner of.
558// Example:
559// ang=60;
560// difference() {
561// angle_pie_mask(ang=ang, h=50, r=200);
562// up(50/2) {
563// #fillet_angled_corner_mask(fillet=20, ang=ang);
564// zrot_copies([0, ang]) right(200/2) fillet_mask_x(l=200, r=20);
565// }
566// fillet_angled_edge_mask(h=51, r=20, ang=ang);
567// }
568module fillet_angled_corner_mask(fillet=1.0, ang=90)
569{
570 dx = fillet / tan(ang/2);
571 fn = quantup(segs(fillet), 4);
572 difference() {
573 down(fillet) cylinder(r=dx/cos(ang/2)+1, h=fillet+1, center=false);
574 yflip_copy() {
575 translate([dx, fillet, -fillet]) {
576 hull() {
577 sphere(r=fillet, $fn=fn);
578 down(fillet*3) sphere(r=fillet, $fn=fn);
579 zrot_copies([0,ang]) {
580 right(fillet*3) sphere(r=fillet, $fn=fn);
581 }
582 }
583 }
584 }
585 }
586}
587
588
589// Module: fillet_corner_mask()
590// Usage:
591// fillet_corner_mask(r);
592// Description:
593// Creates a shape that you can use to round 90 degree corners on a fillet.
594// Difference it from the object to be filletted. The center of the mask
595// object should align exactly with the corner to be filletted.
596// Arguments:
597// r = radius of corner fillet.
598// Example:
599// fillet_corner_mask(r=20.0);
600// Example:
601// difference() {
602// cube(size=[30, 50, 80], center=true);
603// translate([0, 25, 40]) fillet_mask_x(l=31, r=15);
604// translate([15, 0, 40]) fillet_mask_y(l=51, r=15);
605// translate([15, 25, 0]) fillet_mask_z(l=81, r=15);
606// translate([15, 25, 40]) #fillet_corner_mask(r=15);
607// }
608module fillet_corner_mask(r=1.0)
609{
610 difference() {
611 cube(size=r*2, center=true);
612 grid3d(n=[2,2,2], spacing=r*2-0.05) {
613 sphere(r=r);
614 }
615 }
616}
617
618
619// Module: fillet_cylinder_mask()
620// Usage:
621// fillet_cylinder_mask(r, fillet, [xtilt], [ytilt]);
622// Description:
623// Create a mask that can be used to round the end of a cylinder.
624// Difference it from the cylinder to be filletted. The center of the
625// mask object should align exactly with the center of the end of the
626// cylinder to be filletted.
627// Arguments:
628// r = radius of cylinder to fillet. (Default: 1.0)
629// fillet = radius of the edge filleting. (Default: 0.25)
630// xtilt = angle of tilt of end of cylinder in the X direction. (Default: 0)
631// ytilt = angle of tilt of end of cylinder in the Y direction. (Default: 0)
632// Example:
633// difference() {
634// cylinder(r=50, h=50, center=false);
635// up(50) #fillet_cylinder_mask(r=50, fillet=10);
636// }
637// Example:
638// difference() {
639// cylinder(r=50, h=100, center=false);
640// up(75) fillet_cylinder_mask(r=50, fillet=10, xtilt=30);
641// }
642module fillet_cylinder_mask(r=1.0, fillet=0.25, xtilt=0, ytilt=0)
643{
644 skew_xz(za=xtilt) {
645 skew_yz(za=ytilt) {
646 cylinder_mask(l=fillet*3, r=r, fillet2=fillet, overage=fillet+2*r*sin(max(xtilt,ytilt)), ends_only=true, align=V_DOWN);
647 }
648 }
649}
650
651
652
653// Module: fillet_hole_mask()
654// Usage:
655// fillet_hole_mask(r|d, fillet, [xtilt], [ytilt]);
656// Description:
657// Create a mask that can be used to round the edge of a circular hole.
658// Difference it from the hole to be filletted. The center of the
659// mask object should align exactly with the center of the end of the
660// hole to be filletted.
661// Arguments:
662// r = Radius of hole to fillet.
663// d = Diameter of hole to fillet.
664// fillet = Radius of the filleting. (Default: 0.25)
665// xtilt = Angle of tilt of end of cylinder in the X direction. (Default: 0)
666// ytilt = Angle of tilt of end of cylinder in the Y direction. (Default: 0)
667// overage = The extra thickness of the mask. Default: `0.1`.
668// Example:
669// difference() {
670// cube([150,150,100], center=true);
671// cylinder(r=50, h=100.1, center=true);
672// up(50) #fillet_hole_mask(r=50, fillet=10);
673// }
674// Example:
675// fillet_hole_mask(r=40, fillet=20, $fa=2, $fs=2);
676module fillet_hole_mask(r=undef, d=undef, fillet=0.25, overage=0.1, xtilt=0, ytilt=0)
677{
678 r = get_radius(r=r, d=d, dflt=1);
679 skew_xz(za=xtilt) {
680 skew_yz(za=ytilt) {
681 rotate_extrude(convexity=4) {
682 difference() {
683 right(r-overage) fwd(fillet) square(fillet+overage, center=false);
684 right(r+fillet) fwd(fillet) circle(r=fillet);
685 }
686 }
687 }
688 }
689}
690
691
692// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap