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_corner_mask(), default_tag(), diff() 
180// Usage:
181//   rounding_edge_mask(l|h=|length=|height=, r|d=, [ang], [excess=]) [ATTACHMENTS];
182//   rounding_edge_mask(l|h=|length=|height=, r1=|d1=, r2=|d2=, [ang=], [excess=]) [ATTACHMENTS];
183// Description:
184//   Creates a shape that can be used to round a straight edge at any angle.  
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.  You can use it with {{diff()}} and
187//   {{edge_mask()}} to attach masks automatically to objects.  The default "remove" tag is set
188//   automatically.  
189//   
190// Arguments:
191//   l/h/length/height = Length of mask.
192//   r = Radius of the rounding.
193//   ang = Angle between faces for rounding.  Default: 90
194//   ---
195//   r1 = Bottom radius of rounding.
196//   r2 = Top radius of rounding.
197//   d = Diameter of the rounding.
198//   d1 = Bottom diameter of rounding.
199//   d2 = Top diameter of rounding.
200//   excess = Extra size for the mask.  Defaults: 0.1
201//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
202//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
203//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
204// Side Effects:
205//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
206// Example(VPD=200,VPR=[55,0,120]):
207//   rounding_edge_mask(l=50, r=15);
208// Example(VPD=200,VPR=[55,0,120]): With different radii at each end
209//   rounding_edge_mask(l=50, r1=10, r2=25);
210// Example(VPD=200,VPR=[55,0,120]): Acute angle
211//   rounding_edge_mask(l=50, r=10, ang=45);
212// Example(VPD=200,VPR=[55,0,120]): A large excess
213//   rounding_edge_mask(l=50, r=15,excess=4);
214// Example: Subtracting from a cube
215//   difference() {
216//       cube(size=100, center=false);
217//       #rounding_edge_mask(l=100, r=25, anchor=BOTTOM);
218//   }
219// Example: Varying Rounding Radius
220//   difference() {
221//       cube(size=50, center=false);
222//       down(1)rounding_edge_mask(l=52, r1=25, r2=10, anchor=BOTTOM);
223//   }
224// Example: Angle not 90 degrees
225//   difference() {
226//       pie_slice(ang=70, h=50, d=100, center=true);
227//       #rounding_edge_mask(h=51, r=20.0, ang=70, $fn=32);
228//   }
229// Example: Varying Rounding Radius
230//   difference() {
231//       pie_slice(ang=70, h=50, d=100, center=true);
232//       #rounding_edge_mask(h=51, r1=10, r2=25, ang=70, $fn=32);
233//   }
234// Example: Rounding a non-right angled edge, with a zero radius at the bottom.  
235//   difference(){
236//     linear_extrude(height=50)xflip(x=25)right_triangle([50,50]);
237//     rounding_edge_mask(l=51, ang=45, r1=0, r2=15, anchor=BOT);
238//   }
239// Example: Masking by Attachment
240//   diff()
241//   cube(100, center=true)
242//       edge_mask(FRONT+RIGHT)
243//           #rounding_edge_mask(l=$parent_size.z+0.01, r=25);
244// Example: Multiple Masking by Attachment
245//   diff()
246//   cube([80,90,100], center=true) {
247//       let(p = $parent_size*1.01) {
248//           edge_mask(TOP)
249//               rounding_edge_mask(l=p.z, r=25);
250//       }
251//   }
252
253function rounding_edge_mask(l, r, ang=90, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("rounding_edge_mask");
254module rounding_edge_mask(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,r,length, h, height, anchor=CENTER, spin=0, orient=UP,
255                         _remove_tag=true)
256{
257    length = one_defined([l,length,h,height],"l,length,h,height");
258    r1 = get_radius(r1=r1, d1=d1,d=d,r=r);
259    r2 = get_radius(r2=r2, d1=d2,d=d,r=r);
260    dummy = assert(all_nonnegative([r1,r2]), "radius/diameter value(s) must be nonnegative")
261            assert(all_positive([length]), "length/l/h/height must be a positive value")
262            assert(is_finite(ang) && ang>0 && ang<180, "ang must be a number between 0 and 180");
263    steps = ceil(segs(max(r1,r2))*(180-ang)/360);
264    function make_path(r) =
265        let(
266             arc = r==0 ? repeat([0,0],steps+1)
267                        : arc(n=steps+1, r=r, corner=[polar_to_xy(r,ang),[0,0],[r,0]]),
268             maxx = last(arc).x,
269             maxy = arc[0].y,
270             cp = [-excess/tan(ang/2),-excess]
271        )
272        [
273          [maxx, -excess],
274          cp, 
275          arc[0] + polar_to_xy(excess, 90+ang),
276          each arc
277        ];
278    path1 = path3d(make_path(r1),-length/2);
279    path2 = path3d(make_path(r2),length/2);
280    left_normal = cylindrical_to_xyz(1,90+ang,0);
281    left_dir = cylindrical_to_xyz(1,ang,0);
282    zdir = unit([length, 0,-(r2-r1)/tan(ang/2)]);
283    cutfact = 1/sin(ang/2)-1;
284
285    v=unit(zrot(ang,zdir)+left_normal);
286    ref = UP - (v*UP)*v;
287    backleft_spin=-vector_angle(rot(from=UP,to=v,p=BACK),ref);
288
289    override = [
290       [CENTER, [CENTER,UP]],
291       [TOP, [[0,0,length/2]]],
292       [BOT, [[0,0,-length/2]]],
293       [FWD, [[(r1+r2)/tan(ang/2)/4,0,0]]],
294       [FWD+BOT, [[r1/tan(ang/2)/2,0,-length/2]]],
295       [FWD+TOP, [[r2/tan(ang/2)/2,0,length/2]]],
296       [LEFT, [(r1+r2)/tan(ang/2)/4*left_dir, left_normal,ang-180]],
297       [LEFT+BOT, [down(length/2,r1/tan(ang/2)/2*left_dir), rot(v=left_dir,-45,p=left_normal),ang-180]],
298       [LEFT+TOP, [up(length/2,r2/tan(ang/2)/2*left_dir), rot(v=left_dir, 45, p=left_normal),ang-180]],
299       [LEFT+FWD, [CENTER, left_normal+FWD,ang/2-90]],
300       [LEFT+FWD+TOP, [[0,0,length/2], left_normal+FWD+UP,ang/2-90]],
301       [LEFT+FWD+BOT, [[0,0,-length/2], left_normal+FWD+DOWN,ang/2-90]],
302       [RIGHT, [[(r1+r2)/2/tan(ang/2),0,0],zdir]],
303       [RIGHT+TOP, [[r2/tan(ang/2),0,length/2],zdir+UP]],
304       [RIGHT+BOT, [[r1/tan(ang/2),0,-length/2],zdir+DOWN]],
305       [RIGHT+FWD, [[(r1+r2)/2/tan(ang/2),0,0],zdir+FWD]],
306       [RIGHT+TOP+FWD, [[r2/tan(ang/2),0,length/2],zdir+UP+FWD]],
307       [RIGHT+BOT+FWD, [[r1/tan(ang/2),0,-length/2],zdir+DOWN+FWD]],
308       [BACK, [ (r1+r2)/2/tan(ang/2)*left_dir,zrot(ang,zdir),ang+90]],
309       [BACK+BOT, [ down(length/2,r1/tan(ang/2)*left_dir),zrot(ang,zdir)+DOWN,ang+90]],
310       [BACK+UP, [ up(length/2,r2/tan(ang/2)*left_dir),zrot(ang,zdir)+UP,ang+90]],              
311       [BACK+LEFT, [ (r1+r2)/2/tan(ang/2)*left_dir,zrot(ang,zdir)+left_normal, backleft_spin]],
312       [BACK+BOT+LEFT, [ down(length/2,r1/tan(ang/2)*left_dir),zrot(ang,zdir)+left_normal+DOWN,backleft_spin]],
313       [BACK+UP+LEFT, [ up(length/2,r2/tan(ang/2)*left_dir),zrot(ang,zdir)+left_normal+UP,backleft_spin]],
314       [BACK+RIGHT, [cylindrical_to_xyz(cutfact*(r1+r2)/2,ang/2,0), zrot(ang/2,zdir),ang/2+90]],
315       [BACK+RIGHT+TOP, [cylindrical_to_xyz(cutfact*r2,ang/2,length/2), zrot(ang/2,zdir)+UP,ang/2+90]],
316       [BACK+RIGHT+BOT, [cylindrical_to_xyz(cutfact*r1,ang/2,-length/2), zrot(ang/2,zdir)+DOWN,ang/2+90]],
317       ];
318    vnf = vnf_vertex_array([path1,path2],caps=true,col_wrap=true);
319    default_tag("remove", _remove_tag)
320      attachable(anchor,spin,orient,size=[1,1,length],override=override){
321        vnf_polyhedron(vnf);
322        children();
323      }
324}
325
326
327
328// Module: rounding_corner_mask()
329// Synopsis: Creates a shape to round 90° corners.
330// SynTags: Geom
331// Topics: Masking, Rounding, Shapes (3D)
332// See Also: rounding_edge_mask(), default_tag(), diff()
333// Usage:
334//   rounding_corner_mask(r|d, [ang], [excess=], [style=]) [ATTACHMENTS];
335// Description:
336//   Creates a shape that you can use to round corners where the top and bottom faces are parallel and the two side
337//   faces are perpendicular to the top and bottom, e.g. cubes or pie_slice corners.  
338//   Difference it from the object to be rounded.  The center of the mask
339//   object should align exactly with the corner to be rounded.
340// Arguments:
341//   r = Radius of corner rounding.
342//   ang = Angle of corner (measured around the z axis).  Default: 90
343//   ---
344//   d = Diameter of corner rounding.
345//   excess = Extra size for the mask.  Defaults: 0.1
346//   style = The style of the sphere cutout's construction. One of "orig", "aligned", "stagger", "octa", or "icosa".  Default: "octa"
347//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
348//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
349//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
350// Side Effects:
351//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
352// Example:
353//   rounding_corner_mask(r=20);
354// Example: Adding a huge excess
355//   rounding_corner_mask(r=20, excess=5);
356// Example: Position masks manually
357//   difference() {
358//       cube(size=[50, 60, 70], center=true);
359//       translate([-25, -30, 35])
360//           #rounding_corner_mask(r=20, spin=90, orient=DOWN);
361//       translate([25, -30, 35])
362//           #rounding_corner_mask(r=20, orient=DOWN);
363//       translate([25, -30, -35])
364//           #rounding_corner_mask(r=20, spin=90);
365//   }
366// Example: Masking by Attachment
367//   diff()
368//   cube(size=[50, 60, 70]) {
369//       corner_mask(TOP)
370//           #rounding_corner_mask(r=20);
371//   }
372// Example(VPR=[71.8,0,345.8],VPT=[57.0174,43.8496,24.5863],VPD=263.435,NoScales): Acute angle 
373//   ang=60;
374//   difference() {
375//       pie_slice(ang=ang, h=50, r=100);
376//       zflip_copy(z=25)
377//          #rounding_corner_mask(r=20, ang=ang);
378//   }
379// Example(VPR=[62.7,0,5.4],VPT=[6.9671,22.7592,20.7513],VPD=192.044): Obtuse angle 
380//   ang=120;
381//   difference() {
382//       pie_slice(ang=ang, h=50, r=30);
383//       zflip_copy(z=25)
384//          #rounding_corner_mask(r=20, ang=ang);
385//   }
386
387function rounding_corner_mask(r, ang, d, style="octa", excess=0.1, anchor=CENTER, spin=0, orient=UP) = no_function("rounding_corner_mask");
388module rounding_corner_mask(r, ang=90, d, style="octa", excess=0.1, anchor=CENTER, spin=0, orient=UP)
389{
390    r = get_radius(r=r, d=d, dflt=1);
391    joint = r/tan(ang/2);
392    path = [
393             [joint,r],
394             [joint,-excess],
395             [-excess/tan(ang/2),-excess],
396             polar_to_xy(joint,ang)+polar_to_xy(excess,90+ang)
397           ];
398    default_tag("remove") {
399        attachable(anchor,spin,orient, size=[2,2,2]*r) {
400            difference() {
401                down(excess)
402                    linear_extrude(height=r+excess) polygon(path);
403                translate([joint,r,r])
404                    spheroid(r=r, style=style);
405            }
406            children();
407        }
408    }
409}
410
411
412function 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");
413module rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, spin=0, orient=UP,l,height,length)
414{
415    deprecate("angled_edge_mask");
416    rounding_edge_mask(h=h,r=r,r1=r1,r2=r2,d=d,d1=d1,d2=d1,ang=ang,anchor=anchor,spin=spin,orient=orient,l=l,height=height,length=length)
417      children();
418}
419
420
421function rounding_angled_corner_mask(r, ang=90, d, anchor=CENTER, spin=0, orient=UP) = no_function("rounding_angled_corner_mask");
422module rounding_angled_corner_mask(r, ang=90, d, anchor=CENTER, spin=0, orient=UP)
423{
424    deprecate("rounding_corner_mask");
425    zflip()rounding_corner_mask(r=r,ang=ang,d=d,anchor=anchor,spin=spin,orient=orient)
426       children();
427}
428
429
430// Module: rounding_cylinder_mask()
431// Synopsis: Creates a shape to round the end of a cylinder.
432// SynTags: Geom
433// Topics: Masking, Rounding, Cylinders
434// See Also: rounding_hole_mask(), rounding_corner_mask(), default_tag(), diff()
435// Usage:
436//   rounding_cylinder_mask(r|d=, rounding);
437// Description:
438//   Create a mask that can be used to round the end of a cylinder.
439//   Difference it from the cylinder to be rounded.  The center of the
440//   mask object should align exactly with the center of the end of the
441//   cylinder to be rounded.
442// Arguments:
443//   r = Radius of cylinder.
444//   rounding = Radius of the edge rounding.
445//   ---
446//   d = Diameter of cylinder.
447//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
448//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
449//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
450// Side Effects:
451//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
452// Example:
453//   difference() {
454//     cylinder(r=50, h=50, center=false);
455//     up(50) #rounding_cylinder_mask(r=50, rounding=10);
456//   }
457// Example:
458//   difference() {
459//     cylinder(r=50, h=50, center=false);
460//     up(50) rounding_cylinder_mask(r=50, rounding=10);
461//   }
462// Example: Masking by Attachment
463//   diff()
464//   cyl(h=30, d=30) {
465//       attach(TOP)
466//         #tag("remove")
467//           rounding_cylinder_mask(d=30, rounding=5);
468//   }
469function rounding_cylinder_mask(r, rounding, d, anchor, spin, orient) = no_function("rounding_cylinder_mask");
470module rounding_cylinder_mask(r, rounding, d, anchor=CENTER, spin=0, orient=UP)
471{
472    r = get_radius(r=r, d=d, dflt=1);
473    default_tag("remove") {
474        attachable(anchor,spin,orient, r=r+rounding, l=rounding*2) {
475            difference() {
476                cyl(r=r+rounding, l=rounding*2, anchor=CENTER);
477                cyl(r=r, l=rounding*3, rounding=rounding, anchor=TOP);
478            }
479            children();
480        }
481    }
482}
483
484
485
486// Module: rounding_hole_mask()
487// Synopsis: Creates a shape to round the edge of a round hole.
488// SynTags: Geom
489// Topics: Masking, Rounding
490// See Also: rounding_cylinder_mask(), rounding_hole_mask(), rounding_corner_mask(), default_tag(), diff()
491// Usage:
492//   rounding_hole_mask(r|d, rounding, [excess]) [ATTACHMENTS];
493// Description:
494//   Create a mask that can be used to round the edge of a circular hole.
495//   Difference it from the hole to be rounded.  The center of the
496//   mask object should align exactly with the center of the end of the
497//   hole to be rounded.
498// Arguments:
499//   r = Radius of hole.
500//   rounding = Radius of the rounding.
501//   excess = The extra thickness of the mask.  Default: `0.1`.
502//   ---
503//   d = Diameter of hole to rounding.
504//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
505//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
506//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
507// Side Effects:
508//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
509// Example:
510//   rounding_hole_mask(r=40, rounding=20, $fa=2, $fs=2);
511// Example(Med):
512//   difference() {
513//     cube([150,150,100], center=true);
514//     cylinder(r=50, h=100.1, center=true);
515//     up(50) #rounding_hole_mask(r=50, rounding=10);
516//   }
517// Example(Med):
518//   difference() {
519//     cube([150,150,100], center=true);
520//     cylinder(r=50, h=100.1, center=true);
521//     up(50) rounding_hole_mask(r=50, rounding=10);
522//   }
523function rounding_hole_mask(r, rounding, excess=0.1, d, anchor=CENTER, spin=0, orient=UP) = no_function("rounding_hole_mask");
524module rounding_hole_mask(r, rounding, excess=0.1, d, anchor=CENTER, spin=0, orient=UP)
525{
526    r = get_radius(r=r, d=d, dflt=1);
527    default_tag("remove") {
528        attachable(anchor,spin,orient, r=r+rounding, l=2*rounding) {
529            rotate_extrude(convexity=4) {
530                difference() {
531                    right(r-excess) fwd(rounding) square(rounding+excess, center=false);
532                    right(r+rounding) fwd(rounding) circle(r=rounding);
533                }
534            }
535            children();
536        }
537    }
538}
539
540
541// Section: Teardrop Masking
542
543// Module: teardrop_edge_mask()
544// Synopsis: Creates a shape to round a 90° edge but limit the angle of overhang.
545// SynTags: Geom
546// Topics: Masking, Rounding, Shapes (3D), FDM Optimized
547// See Also: teardrop_corner_mask(), teardrop_edge_mask(), default_tag(), diff()
548// Usage:
549//   teardrop_edge_mask(l|h=|length=|height=, r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS];
550// Description:
551//   Makes an apropriate 3D edge rounding mask that keeps within `angle` degrees of vertical.
552// Arguments:
553//   l/h/length/height = length of mask
554//   r = Radius of the mask rounding.
555//   angle = Maximum angle from vertical. Default: 45
556//   excess = Excess mask size.  Default: 0.1
557//   ---
558//   d = Diameter of the mask rounding.
559//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
560//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
561//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
562// Side Effects:
563//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
564// Example(VPD=50,VPR=[55,0,120]):
565//   teardrop_edge_mask(l=20, r=10, angle=40);
566// Example(VPD=300,VPR=[75,0,25]):
567//   diff()
568//   cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) {
569//       edge_mask(BOT)
570//           teardrop_edge_mask(l=max($parent_size)+1, r=10, angle=40);
571//       corner_mask(BOT)
572//           teardrop_corner_mask(r=10, angle=40);
573//   }
574function teardrop_edge_mask(l, r, angle=45, excess=0.1, d, anchor, spin, orient,h,height,length) = no_function("teardrop_edge_mask");
575module teardrop_edge_mask(l, r, angle=45, excess=0.1, d, anchor=CTR, spin=0, orient=UP,h,height,length)
576{
577    l = one_defined([l, h, height, length], "l,h,height,length");
578    check = 
579      assert(is_num(l) && l>0, "Length of mask must be positive")
580      assert(is_num(angle) && angle>0 && angle<90, "Angle must be a number between 0 and 90")
581      assert(is_num(excess));
582    r = get_radius(r=r, d=d, dflt=1);
583    path = mask2d_teardrop(r=r, angle=angle, excess=excess);
584    default_tag("remove") {
585        linear_sweep(path, height=l, center=true, atype="bbox", anchor=anchor, spin=spin, orient=orient) children();
586    }
587}
588
589
590// Module: teardrop_corner_mask()
591// Synopsis: Creates a shape to round a 90° corner but limit the angle of overhang.
592// SynTags: Geom
593// Topics: Masking, Rounding, Shapes (3D), FDM Optimized
594// See Also: teardrop_corner_mask(), teardrop_edge_mask(), default_tag(), diff()
595// Usage:
596//   teardrop_corner_mask(r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS];
597// Description:
598//   Makes an apropriate 3D corner rounding mask that keeps within `angle` degrees of vertical.
599// Arguments:
600//   r = Radius of the mask rounding.
601//   angle = Maximum angle from vertical. Default: 45
602//   excess = Excess mask size.  Default: 0.1
603//   ---
604//   d = Diameter of the mask rounding.
605//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
606//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
607//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
608// Side Effects:
609//   Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
610// Example:
611//   teardrop_corner_mask(r=20, angle=40);
612// Example:
613//   diff()
614//   cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) {
615//       edge_profile(BOT)
616//           mask2d_teardrop(r=10, angle=40);
617//       corner_mask(BOT)
618//           teardrop_corner_mask(r=10, angle=40);
619//   }
620function teardrop_corner_mask(r, angle=45, excess=0.1, d, anchor, spin, orient) = no_function("teardrop_corner_mask");
621module teardrop_corner_mask(r, angle=45, excess=0.1, d, anchor=CTR, spin=0, orient=UP)
622{  
623    assert(is_num(angle));
624    assert(is_num(excess));
625    assert(angle>0 && angle<90);
626    r = get_radius(r=r, d=d, dflt=1);
627    size = (r+excess) * [1,1,1];
628    midpt = (r-excess)/2 * [1,1,1];
629    default_tag("remove") {
630        attachable(anchor,spin,orient, size=size, offset=midpt) {
631            difference() {
632                translate(-[1,1,1]*excess) cube(r+excess, center=false);
633                translate([1,1,1]*r) onion(r=r, ang=angle, orient=DOWN);
634            }
635            children();
636        }
637    }
638}
639
640
641
642// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap