1//////////////////////////////////////////////////////////////////////
  2// LibFile: masks3d.scad
  3//   This file defines 3D masks for applying chamfers, roundovers, and teardrop roundovers to straight edges and circular
  4//   edges in three dimensions.  
  5// Includes:
  6//   include <BOSL2/std.scad>
  7// FileGroup: Basic Modeling
  8// FileSummary: 3D masks for rounding or chamfering edges and corners.
  9// FileFootnotes: STD=Included in std.scad
 10//////////////////////////////////////////////////////////////////////
 11
 12
 13// Section: Chamfer Masks
 14
 15
 16// Module: chamfer_edge_mask()
 17// Synopsis: Creates a shape to chamfer a 90° edge.
 18// SynTags: Geom
 19// Topics: Masking, Chamfers, Shapes (3D)
 20// See Also: chamfer_corner_mask(), chamfer_cylinder_mask(), chamfer_edge_mask(), default_tag(), diff()
 21// Usage:
 22//   chamfer_edge_mask(l|h=|length=|height=, chamfer, [excess]) [ATTACHMENTS];
 23// Description:
 24//   Creates a shape that can be used to chamfer a 90° edge.
 25//   Difference it from the object to be chamfered.  The center of
 26//   the mask object should align exactly with the edge to be chamfered.
 27// Arguments:
 28//   l/h/length/height = Length of mask.
 29//   chamfer = Size of chamfer.
 30//   excess = The extra amount to add to the length of the mask so that it differences away from other shapes cleanly.  Default: `0.1`
 31//   ---
 32//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 33//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 34//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 35// Side Effects:
 36//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
 37// Example:
 38//   chamfer_edge_mask(l=50, chamfer=10);
 39// Example:
 40//   difference() {
 41//       cube(50, anchor=BOTTOM+FRONT);
 42//       #chamfer_edge_mask(l=50, chamfer=10, orient=RIGHT);
 43//   }
 44// Example: Masking by Attachment
 45//   diff()
 46//   cube(50, center=true) {
 47//       edge_mask(TOP+RIGHT)
 48//           #chamfer_edge_mask(l=50, chamfer=10);
 49//   }
 50function chamfer_edge_mask(l, chamfer=1, excess=0.1, h, length, height, anchor=CENTER, spin=0, orient=UP) = no_function("chamfer_edge_mask");
 51module chamfer_edge_mask(l, chamfer=1, excess=0.1, h, length, height, anchor=CENTER, spin=0, orient=UP) {
 52    l = one_defined([l, h, height, length], "l,h,height,length");
 53    default_tag("remove") {
 54        attachable(anchor,spin,orient, size=[chamfer*2, chamfer*2, l]) {
 55            cylinder(r=chamfer, h=l+excess, center=true, $fn=4);
 56            children();
 57        }
 58    }
 59}
 60
 61
 62// Module: chamfer_corner_mask()
 63// Synopsis: Creates a shape to chamfer a 90° corner.
 64// SynTags: Geom
 65// Topics: Masking, Chamfers, Shapes (3D)
 66// See Also: chamfer_corner_mask(), chamfer_cylinder_mask(), chamfer_edge_mask(), default_tag(), diff()
 67// Usage:
 68//   chamfer_corner_mask(chamfer) [ATTACHMENTS];
 69// Description:
 70//   Creates a shape that can be used to chamfer a 90° corner.
 71//   Difference it from the object to be chamfered.  The center of
 72//   the mask object should align exactly with the corner to be chamfered.
 73// Arguments:
 74//   chamfer = Size of chamfer.
 75//   ---
 76//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 77//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 78//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 79// Side Effects:
 80//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
 81// Example:
 82//   chamfer_corner_mask(chamfer=10);
 83// Example:
 84//   difference() {
 85//       cuboid(50, chamfer=10, trimcorners=false);
 86//       move(25*[1,-1,1]) #chamfer_corner_mask(chamfer=10);
 87//   }
 88// Example: Masking by Attachment
 89//   diff()
 90//   cuboid(100, chamfer=20, trimcorners=false) {
 91//       corner_mask(TOP+FWD+RIGHT)
 92//           chamfer_corner_mask(chamfer=20);
 93//   }
 94// Example: Anchors
 95//   chamfer_corner_mask(chamfer=20)
 96//       show_anchors();
 97function chamfer_corner_mask(chamfer=1, anchor=CENTER, spin=0, orient=UP) = no_function("chamfer_corner_mask");
 98module chamfer_corner_mask(chamfer=1, anchor=CENTER, spin=0, orient=UP) {
 99    default_tag("remove") {
100        octahedron(chamfer*4, anchor=anchor, spin=spin, orient=orient) children();
101    }
102}
103
104
105// Module: chamfer_cylinder_mask()
106// Synopsis: Creates a shape to chamfer the end of a cylinder.
107// SynTags: Geom
108// Topics: Masking, Chamfers, Cylinders
109// See Also: chamfer_corner_mask(), chamfer_cylinder_mask(), chamfer_edge_mask(), default_tag(), diff()
110// Usage:
111//   chamfer_cylinder_mask(r|d=, chamfer, [ang], [from_end]) [ATTACHMENTS];
112// Description:
113//   Create a mask that can be used to bevel/chamfer the end of a cylindrical region.
114//   Difference it from the end of the region to be chamfered.  The center of the mask
115//   object should align exactly with the center of the end of the cylindrical region
116//   to be chamfered.
117// Arguments:
118//   r = Radius of cylinder to chamfer.
119//   chamfer = Size of the edge chamfered, inset from edge.
120//   ---
121//   d = Diameter of cylinder to chamfer. Use instead of r.
122//   ang = Angle of chamfer in degrees from the horizontal.  (Default: 45)
123//   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)
124//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
125//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
126//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
127// Side Effects:
128//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
129// Example:
130//   difference() {
131//       cylinder(r=50, h=100, center=true);
132//       up(50) #chamfer_cylinder_mask(r=50, chamfer=10);
133//   }
134// Example:
135//   difference() {
136//       cylinder(r=50, h=100, center=true);
137//       up(50) chamfer_cylinder_mask(r=50, chamfer=10);
138//   }
139// Example: Changing the chamfer angle
140//   difference() {
141//       cylinder(r=50, h=100, center=true);
142//       up(50) #chamfer_cylinder_mask(r=50, chamfer=10, ang=70);
143//   }
144// Example:
145//   difference() {
146//       cylinder(r=50, h=100, center=true);
147//       up(50) chamfer_cylinder_mask(r=50, chamfer=10, ang=70);
148//   }
149// Example: Masking by Attachment
150//   diff()
151//   cyl(d=100,h=40)
152//      attach([TOP,BOT])
153//         tag("remove")chamfer_cylinder_mask(d=100, chamfer=10);
154function chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTER, spin=0, orient=UP) = no_function("chamfer_cylinder_mask");
155module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTER, spin=0, orient=UP)
156{
157    r = get_radius(r=r, d=d, dflt=1);
158    dummy = assert(all_nonnegative([chamfer]), "Chamfer must be a nonnegative number");
159    ch = from_end? chamfer : opp_ang_to_adj(chamfer,90-ang);
160    default_tag("remove"){
161        attachable(anchor,spin,orient, r=r, l=ch*2) {
162            difference() {
163                cyl(r=r+chamfer, l=ch*2, anchor=CENTER);
164                cyl(r=r, l=ch*3, chamfer=chamfer, chamfang=ang, from_end=from_end, anchor=TOP);
165            }
166            children();
167        }
168    }
169}
170
171
172
173// Section: Rounding Masks
174
175// Module: rounding_edge_mask()
176// Synopsis: Creates a shape to round a 90° edge.
177// SynTags: Geom
178// Topics: Masks, Rounding, Shapes (3D)
179// See Also: rounding_angled_edge_mask(), rounding_corner_mask(), rounding_angled_corner_mask(), default_tag(), diff() 
180// Usage:
181//   rounding_edge_mask(l|h=|length=|height=, r|d=, [excess=]) [ATTACHMENTS];
182//   rounding_edge_mask(l|h=|length=|height=, r1=|d1=, r2=|d2=, [excess=]) [ATTACHMENTS];
183// Description:
184//   Creates a shape that can be used to round a vertical 90° edge.
185//   Difference it from the object to be rounded.  The center of the mask
186//   object should align exactly with the edge to be rounded.
187// Arguments:
188//   l/h/length/height = Length of mask.
189//   r = Radius of the rounding.
190//   ---
191//   r1 = Bottom radius of rounding.
192//   r2 = Top radius of rounding.
193//   d = Diameter of the rounding.
194//   d1 = Bottom diameter of rounding.
195//   d2 = Top diameter of rounding.
196//   excess = Extra size for the mask.  Defaults: 0.1
197//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
198//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
199//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
200// Side Effects:
201//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
202// Example(VPD=200,VPR=[55,0,120]):
203//   rounding_edge_mask(l=50, r1=10, r2=25);
204// Example:
205//   difference() {
206//       cube(size=100, center=false);
207//       #rounding_edge_mask(l=100, r=25, orient=UP, anchor=BOTTOM);
208//   }
209// Example: Varying Rounding Radius
210//   difference() {
211//       cube(size=50, center=false);
212//       #rounding_edge_mask(l=50, r1=25, r2=10, orient=UP, anchor=BOTTOM);
213//   }
214// Example: Masking by Attachment
215//   diff()
216//   cube(100, center=true)
217//       edge_mask(FRONT+RIGHT)
218//           #rounding_edge_mask(l=$parent_size.z+0.01, r=25);
219// Example: Multiple Masking by Attachment
220//   diff()
221//   cube([80,90,100], center=true) {
222//       let(p = $parent_size*1.01) {
223//           edge_mask(TOP)
224//               rounding_edge_mask(l=p.z, r=25);
225//       }
226//   }
227function rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("rounding_edge_mask");
228module rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length)
229{
230    l = one_defined([l, h, height, length], "l,h,height,length");
231    r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1);
232    r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1);
233    sides = quantup(segs(max(r1,r2)),4);
234    default_tag("remove") {
235        attachable(anchor,spin,orient, size=[2*r1,2*r1,l], size2=[2*r2,2*r2]) {
236            if (r1<r2) {
237                zflip() {
238                    linear_extrude(height=l, convexity=4, center=true, scale=r1/r2) {
239                        difference() {
240                            translate(-excess*[1,1]) square(r2+excess);
241                            translate([r2,r2]) circle(r=r2, $fn=sides);
242                        }
243                    }
244                }
245            } else {
246                linear_extrude(height=l, convexity=4, center=true, scale=r2/r1) {
247                    difference() {
248                        translate(-excess*[1,1]) square(r1+excess);
249                        translate([r1,r1]) circle(r=r1, $fn=sides);
250                    }
251                }
252            }
253            children();
254        }
255    }
256}
257
258
259// Module: rounding_corner_mask()
260// Synopsis: Creates a shape to round 90° corners.
261// SynTags: Geom
262// Topics: Masking, Rounding, Shapes (3D)
263// See Also: rounding_angled_corner_mask(), rounding_edge_mask(), rounding_angled_edge_mask(), default_tag(), diff()
264// Usage:
265//   rounding_corner_mask(r|d, [excess=], [style=]) [ATTACHMENTS];
266// Description:
267//   Creates a shape that you can use to round 90° corners.
268//   Difference it from the object to be rounded.  The center of the mask
269//   object should align exactly with the corner to be rounded.
270// Arguments:
271//   r = Radius of corner rounding.
272//   ---
273//   d = Diameter of corner rounding.
274//   excess = Extra size for the mask.  Defaults: 0.1
275//   style = The style of the sphere cutout's construction. One of "orig", "aligned", "stagger", "octa", or "icosa".  Default: "octa"
276//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
277//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
278//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
279// Side Effects:
280//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
281// Example:
282//   rounding_corner_mask(r=20.0);
283// Example:
284//   difference() {
285//       cube(size=[50, 60, 70], center=true);
286//       translate([-25, -30, 35])
287//           #rounding_corner_mask(r=20, spin=90, orient=DOWN);
288//       translate([25, -30, 35])
289//           #rounding_corner_mask(r=20, orient=DOWN);
290//       translate([25, -30, -35])
291//           #rounding_corner_mask(r=20, spin=90);
292//   }
293// Example: Masking by Attachment
294//   diff()
295//   cube(size=[50, 60, 70]) {
296//       corner_mask(TOP)
297//           #rounding_corner_mask(r=20);
298//   }
299function rounding_corner_mask(r, d, style="octa", excess=0.1, anchor=CENTER, spin=0, orient=UP) = no_function("rounding_corner_mask");
300module rounding_corner_mask(r, d, style="octa", excess=0.1, anchor=CENTER, spin=0, orient=UP)
301{
302    r = get_radius(r=r, d=d, dflt=1);
303    default_tag("remove") {
304        attachable(anchor,spin,orient, size=[2,2,2]*r) {
305            difference() {
306                translate(-excess*[1,1,1])
307                    cube(size=r+excess, center=false);
308                translate([r,r,r])
309                    spheroid(r=r, style=style);
310            }
311            children();
312        }
313    }
314}
315
316
317// Module: rounding_angled_edge_mask()
318// Synopsis: Creates a shape to round edges of any angle.
319// SynTags: Geom
320// Topics: Masks, Rounding
321// See Also: rounding_angled_corner_mask(), rounding_edge_mask(), rounding_corner_mask(), default_tag(), diff()
322// Usage:
323//   rounding_angled_edge_mask(h|l=|length=|height=, r|d=, [ang=]) [ATTACHMENTS];
324//   rounding_angled_edge_mask(h|l=|length=|height=, r1=|d1=, r2=|d2=, [ang=]) [ATTACHMENTS];
325// Description:
326//   Creates a vertical mask that can be used to round the edge where two face meet, at any arbitrary
327//   angle.  Difference it from the object to be rounded.  The center of the mask should align exactly
328//   with the edge to be rounded.
329// Arguments:
330//   h/l/height/length = Height of vertical mask.
331//   r = Radius of the rounding.
332//   ---
333//   r1 = Bottom radius of rounding.
334//   r2 = Top radius of rounding.
335//   d = Diameter of the rounding.
336//   d1 = Bottom diameter of rounding.
337//   d2 = Top diameter of rounding.
338//   ang = Angle that the planes meet at. Default: 90
339//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
340//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
341//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
342// Side Effects:
343//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
344// Example:
345//   difference() {
346//       pie_slice(ang=70, h=50, d=100, center=true);
347//       #rounding_angled_edge_mask(h=51, r=20.0, ang=70, $fn=32);
348//   }
349// Example: Varying Rounding Radius
350//   difference() {
351//       pie_slice(ang=70, h=50, d=100, center=true);
352//       #rounding_angled_edge_mask(h=51, r1=10, r2=25, ang=70, $fn=32);
353//   }
354function rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, spin=0, orient=UP,l,height,length) = no_function("rounding_angled_edge_mask");
355module rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, spin=0, orient=UP,l,height,length)
356{
357    function _mask_shape(r) = [
358        for (i = [0:1:n]) let (a=90+ang+i*sweep/n) [r*cos(a)+x, r*sin(a)+r],
359        for (i = [0:1:n]) let (a=90+i*sweep/n) [r*cos(a)+x, r*sin(a)-r],
360        [min(-1, r*cos(270-ang)+x-1), r*sin(270-ang)-r],
361        [min(-1, r*cos(90+ang)+x-1), r*sin(90+ang)+r],
362    ];
363    h = one_defined([l, h, height, length], "l,h,height,length");
364    sweep = 180-ang;
365    r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1);
366    r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1);
367    n = ceil(segs(max(r1,r2))*sweep/360);
368    x = sin(90-(ang/2))/sin(ang/2) * (r1<r2? r2 : r1);
369    if(r1<r2) {
370        default_tag("remove") {
371            attachable(anchor,spin,orient, size=[2*x*r1/r2,2*r1,h], size2=[2*x,2*r2]) {
372                zflip() {
373                    linear_extrude(height=h, convexity=4, center=true, scale=r1/r2) {
374                        polygon(_mask_shape(r2));
375                    }
376                }
377                children();
378            }
379        }
380    } else {
381        default_tag("remove") {
382            attachable(anchor,spin,orient, size=[2*x,2*r1,h], size2=[2*x*r2/r1,2*r2]) {
383                linear_extrude(height=h, convexity=4, center=true, scale=r2/r1) {
384                    polygon(_mask_shape(r1));
385                }
386                children();
387            }
388        }
389    }
390}
391
392
393// Module: rounding_angled_corner_mask()
394// Synopsis: Creates a shape to round the corner of an arbitrary angle.
395// SynTags: Geom
396// Topics: Masks, Rounding, Shapes (3D)
397// See Also: rounding_angled_edge_mask(), rounding_corner_mask(), rounding_edge_mask(), default_tag(), diff()
398// Usage:
399//   rounding_angled_corner_mask(r|d=, [ang]) [ATTACHMENTS];
400// Description:
401//   Creates a shape that can be used to round the corner of an angle.
402//   Difference it from the object to be rounded.  The center of the mask
403//   object should align exactly with the point of the corner to be rounded.
404// Arguments:
405//   r = Radius of the rounding.
406//   ---
407//   d = Diameter of the rounding.
408//   ang = Angle between planes that you need to round the corner of.  Default: 90
409//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
410//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
411//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
412// Side Effects:
413//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
414// Example(Med):
415//   ang=60;
416//   difference() {
417//       pie_slice(ang=ang, h=50, r=200, center=true);
418//       up(50/2) #rounding_angled_corner_mask(r=20, ang=ang);
419//   }
420function rounding_angled_corner_mask(r, ang=90, d, anchor=CENTER, spin=0, orient=UP) = no_function("rounding_angled_corner_mask");
421module rounding_angled_corner_mask(r, ang=90, d, anchor=CENTER, spin=0, orient=UP)
422{
423    r = get_radius(r=r, d=d, dflt=1);
424    dx = r / tan(ang/2);
425    dx2 = dx / cos(ang/2) + 1;
426    fn = quantup(segs(r), 4);
427    default_tag("remove") {
428        attachable(anchor,spin,orient, d=dx2, l=2*r) {
429            difference() {
430                down(r) cylinder(r=dx2, h=r+1, center=false);
431                yflip_copy() {
432                    translate([dx, r, -r]) {
433                        hull() {
434                            sphere(r=r, $fn=fn);
435                            down(r*3) sphere(r=r, $fn=fn);
436                            zrot_copies([0,ang]) {
437                                right(r*3) sphere(r=r, $fn=fn);
438                            }
439                        }
440                    }
441                }
442            }
443            children();
444        }
445    }
446}
447
448
449// Module: rounding_cylinder_mask()
450// Synopsis: Creates a shape to round the end of a cylinder.
451// SynTags: Geom
452// Topics: Masking, Rounding, Cylinders
453// See Also: rounding_hole_mask(), rounding_angled_edge_mask(), rounding_corner_mask(), rounding_angled_corner_mask(), default_tag(), diff()
454// Usage:
455//   rounding_cylinder_mask(r|d=, rounding);
456// Description:
457//   Create a mask that can be used to round the end of a cylinder.
458//   Difference it from the cylinder to be rounded.  The center of the
459//   mask object should align exactly with the center of the end of the
460//   cylinder to be rounded.
461// Arguments:
462//   r = Radius of cylinder.
463//   rounding = Radius of the edge rounding.
464//   ---
465//   d = Diameter of cylinder.
466//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
467//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
468//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
469// Side Effects:
470//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
471// Example:
472//   difference() {
473//     cylinder(r=50, h=50, center=false);
474//     up(50) #rounding_cylinder_mask(r=50, rounding=10);
475//   }
476// Example:
477//   difference() {
478//     cylinder(r=50, h=50, center=false);
479//     up(50) rounding_cylinder_mask(r=50, rounding=10);
480//   }
481// Example: Masking by Attachment
482//   diff()
483//   cyl(h=30, d=30) {
484//       attach(TOP)
485//         #tag("remove")
486//           rounding_cylinder_mask(d=30, rounding=5);
487//   }
488function rounding_cylinder_mask(r, rounding, d, anchor, spin, orient) = no_function("rounding_cylinder_mask");
489module rounding_cylinder_mask(r, rounding, d, anchor=CENTER, spin=0, orient=UP)
490{
491    r = get_radius(r=r, d=d, dflt=1);
492    default_tag("remove") {
493        attachable(anchor,spin,orient, r=r+rounding, l=rounding*2) {
494            difference() {
495                cyl(r=r+rounding, l=rounding*2, anchor=CENTER);
496                cyl(r=r, l=rounding*3, rounding=rounding, anchor=TOP);
497            }
498            children();
499        }
500    }
501}
502
503
504
505// Module: rounding_hole_mask()
506// Synopsis: Creates a shape to round the edge of a round hole.
507// SynTags: Geom
508// Topics: Masking, Rounding
509// See Also: rounding_cylinder_mask(), rounding_hole_mask(), rounding_angled_edge_mask(), rounding_corner_mask(), rounding_angled_corner_mask(), default_tag(), diff()
510// Usage:
511//   rounding_hole_mask(r|d, rounding, [excess]) [ATTACHMENTS];
512// Description:
513//   Create a mask that can be used to round the edge of a circular hole.
514//   Difference it from the hole to be rounded.  The center of the
515//   mask object should align exactly with the center of the end of the
516//   hole to be rounded.
517// Arguments:
518//   r = Radius of hole.
519//   rounding = Radius of the rounding.
520//   excess = The extra thickness of the mask.  Default: `0.1`.
521//   ---
522//   d = Diameter of hole to rounding.
523//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
524//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
525//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
526// Side Effects:
527//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
528// Example:
529//   rounding_hole_mask(r=40, rounding=20, $fa=2, $fs=2);
530// Example(Med):
531//   difference() {
532//     cube([150,150,100], center=true);
533//     cylinder(r=50, h=100.1, center=true);
534//     up(50) #rounding_hole_mask(r=50, rounding=10);
535//   }
536// Example(Med):
537//   difference() {
538//     cube([150,150,100], center=true);
539//     cylinder(r=50, h=100.1, center=true);
540//     up(50) rounding_hole_mask(r=50, rounding=10);
541//   }
542function rounding_hole_mask(r, rounding, excess=0.1, d, anchor=CENTER, spin=0, orient=UP) = no_function("rounding_hole_mask");
543module rounding_hole_mask(r, rounding, excess=0.1, d, anchor=CENTER, spin=0, orient=UP)
544{
545    r = get_radius(r=r, d=d, dflt=1);
546    default_tag("remove") {
547        attachable(anchor,spin,orient, r=r+rounding, l=2*rounding) {
548            rotate_extrude(convexity=4) {
549                difference() {
550                    right(r-excess) fwd(rounding) square(rounding+excess, center=false);
551                    right(r+rounding) fwd(rounding) circle(r=rounding);
552                }
553            }
554            children();
555        }
556    }
557}
558
559
560// Section: Teardrop Masking
561
562// Module: teardrop_edge_mask()
563// Synopsis: Creates a shape to round a 90° edge but limit the angle of overhang.
564// SynTags: Geom
565// Topics: Masking, Rounding, Shapes (3D), FDM Optimized
566// See Also: teardrop_corner_mask(), teardrop_edge_mask(), default_tag(), diff()
567// Usage:
568//   teardrop_edge_mask(l|h=|length=|height=, r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS];
569// Description:
570//   Makes an apropriate 3D edge rounding mask that keeps within `angle` degrees of vertical.
571// Arguments:
572//   l/h/length/height = length of mask
573//   r = Radius of the mask rounding.
574//   angle = Maximum angle from vertical. Default: 45
575//   excess = Excess mask size.  Default: 0.1
576//   ---
577//   d = Diameter of the mask rounding.
578//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
579//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
580//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
581// Side Effects:
582//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
583// Example(VPD=50,VPR=[55,0,120]):
584//   teardrop_edge_mask(l=20, r=10, angle=40);
585// Example(VPD=300,VPR=[75,0,25]):
586//   diff()
587//   cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) {
588//       edge_mask(BOT)
589//           teardrop_edge_mask(l=max($parent_size)+1, r=10, angle=40);
590//       corner_mask(BOT)
591//           teardrop_corner_mask(r=10, angle=40);
592//   }
593function teardrop_edge_mask(l, r, angle=45, excess=0.1, d, anchor, spin, orient,h,height,length) = no_function("teardrop_edge_mask");
594module teardrop_edge_mask(l, r, angle=45, excess=0.1, d, anchor=CTR, spin=0, orient=UP,h,height,length)
595{
596    l = one_defined([l, h, height, length], "l,h,height,length");
597    check = 
598      assert(is_num(l) && l>0, "Length of mask must be positive")
599      assert(is_num(angle) && angle>0 && angle<90, "Angle must be a number between 0 and 90")
600      assert(is_num(excess));
601    r = get_radius(r=r, d=d, dflt=1);
602    path = mask2d_teardrop(r=r, angle=angle, excess=excess);
603    default_tag("remove") {
604        linear_sweep(path, height=l, center=true, atype="bbox", anchor=anchor, spin=spin, orient=orient) children();
605    }
606}
607
608
609// Module: teardrop_corner_mask()
610// Synopsis: Creates a shape to round a 90° corner but limit the angle of overhang.
611// SynTags: Geom
612// Topics: Masking, Rounding, Shapes (3D), FDM Optimized
613// See Also: teardrop_corner_mask(), teardrop_edge_mask(), default_tag(), diff()
614// Usage:
615//   teardrop_corner_mask(r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS];
616// Description:
617//   Makes an apropriate 3D corner rounding mask that keeps within `angle` degrees of vertical.
618// Arguments:
619//   r = Radius of the mask rounding.
620//   angle = Maximum angle from vertical. Default: 45
621//   excess = Excess mask size.  Default: 0.1
622//   ---
623//   d = Diameter of the mask rounding.
624//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
625//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
626//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
627// Side Effects:
628//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
629// Example:
630//   teardrop_corner_mask(r=20, angle=40);
631// Example:
632//   diff()
633//   cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) {
634//       edge_profile(BOT)
635//           mask2d_teardrop(r=10, angle=40);
636//       corner_mask(BOT)
637//           teardrop_corner_mask(r=10, angle=40);
638//   }
639function teardrop_corner_mask(r, angle=45, excess=0.1, d, anchor, spin, orient) = no_function("teardrop_corner_mask");
640module teardrop_corner_mask(r, angle=45, excess=0.1, d, anchor=CTR, spin=0, orient=UP)
641{  
642    assert(is_num(angle));
643    assert(is_num(excess));
644    assert(angle>0 && angle<90);
645    r = get_radius(r=r, d=d, dflt=1);
646    size = (r+excess) * [1,1,1];
647    midpt = (r-excess)/2 * [1,1,1];
648    default_tag("remove") {
649        attachable(anchor,spin,orient, size=size, offset=midpt) {
650            difference() {
651                translate(-[1,1,1]*excess) cube(r+excess, center=false);
652                translate([1,1,1]*r) onion(r=r, ang=angle, orient=DOWN);
653            }
654            children();
655        }
656    }
657}
658
659
660
661// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap