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