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