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