1//////////////////////////////////////////////////////////////////////
   2// LibFile: shapes.scad
   3//   Common useful shapes and structured objects.
   4//   To use, add the following lines to the beginning of your file:
   5//   ```
   6//   include <BOSL/constants.scad>
   7//   use <BOSL/shapes.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 <math.scad>
  42include <compat.scad>
  43include <constants.scad>
  44
  45
  46// Section: Cuboids
  47
  48
  49// Module: cuboid()
  50//
  51// Description:
  52//   Creates a cube or cuboid object, with optional chamfering or filleting/rounding.
  53//
  54// Arguments:
  55//   size = The size of the cube.
  56//   chamfer = Size of chamfer, inset from sides.  Default: No chamferring.
  57//   fillet = Radius of fillet for edge rounding.  Default: No filleting.
  58//   edges = Edges to chamfer/fillet.  Use `EDGE` constants from constants.scad. Default: `EDGES_ALL`
  59//   trimcorners = If true, rounds or chamfers corners where three chamferred/filleted edges meet.  Default: `true`
  60//   p1 = Align the cuboid's corner at `p1`, if given.  Forces `align=V_UP+V_BACK+V_RIGHT`.
  61//   p2 = If given with `p1`, defines the cornerpoints of the cuboid.
  62//   align = The side of the origin to align to.  Use `V_` constants from `constants.scad`.  Default: `V_CENTER`
  63//   center = If given, overrides `align`.  A true value sets `align=V_CENTER`, false sets `align=V_UP+V_BACK+V_RIGHT`.
  64//
  65// Example: Simple regular cube.
  66//   cuboid(40);
  67// Example: Cube with minimum cornerpoint given.
  68//   cuboid(20, p1=[10,0,0]);
  69// Example: Rectangular cube, with given X, Y, and Z sizes.
  70//   cuboid([20,40,50]);
  71// Example: Rectangular cube defined by opposing cornerpoints.
  72//   cuboid(p1=[0,10,0], p2=[20,30,30]);
  73// Example: Rectangular cube with chamferred edges and corners.
  74//   cuboid([30,40,50], chamfer=5);
  75// Example: Rectangular cube with chamferred edges, without trimmed corners.
  76//   cuboid([30,40,50], chamfer=5, trimcorners=false);
  77// Example: Rectangular cube with rounded edges and corners.
  78//   cuboid([30,40,50], fillet=10);
  79// Example: Rectangular cube with rounded edges, without trimmed corners.
  80//   cuboid([30,40,50], fillet=10, trimcorners=false);
  81// Example: Rectangular cube with only some edges chamferred.
  82//   cuboid([30,40,50], chamfer=5, edges=EDGE_TOP_FR+EDGE_TOP_RT+EDGE_FR_RT, $fn=24);
  83// Example: Rectangular cube with only some edges rounded.
  84//   cuboid([30,40,50], fillet=5, edges=EDGE_TOP_FR+EDGE_TOP_RT+EDGE_FR_RT, $fn=24);
  85module cuboid(
  86	size=[1,1,1],
  87	p1=undef, p2=undef,
  88	chamfer=undef,
  89	fillet=undef,
  90	edges=EDGES_ALL,
  91	trimcorners=true,
  92	align=[0,0,0],
  93	center=undef
  94) {
  95	size = scalar_vec3(size);
  96	if (is_def(p1)) {
  97		if (is_def(p2)) {
  98			translate([for (v=array_zip([p1,p2],fill=0)) min(v)]) {
  99				cuboid(size=vabs(p2-p1), chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=V_ALLPOS);
 100			}
 101		} else {
 102			translate(p1) {
 103				cuboid(size=size, chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=V_ALLPOS);
 104			}
 105		}
 106	} else {
 107		majrots = [[0,90,0], [90,0,0], [0,0,0]];
 108		
 109		// Not the most elegant, but should work fine.
 110		// Size for edge E can be ignored if there are only zeros in both edges !E
 111		relevantsize = [for(a=[0:2]) if(max(edges[(a+1)%3]+edges[(a+2)%3])>0) size[a]];
 112		
 113		if (chamfer != undef && len(relevantsize) > 0) assertion(chamfer <= min(relevantsize)/2, "chamfer must be smaller than half the cube width, length, or height.");
 114		if (fillet != undef && len(relevantsize) > 0 )  assertion(fillet <= min(relevantsize)/2, "fillet must be smaller than half the cube width, length, or height.");
 115		algn = (!is_def(center))? (is_scalar(align)? align*V_UP : align) : (center==true)? V_CENTER : V_ALLPOS;
 116		translate(vmul(size/2, algn)) {
 117			if (chamfer != undef) {
 118				isize = [for (v = size) max(0.001, v-2*chamfer)];
 119				if (edges == EDGES_ALL && trimcorners) {
 120					hull() {
 121						cube([size[0], isize[1], isize[2]], center=true);
 122						cube([isize[0], size[1], isize[2]], center=true);
 123						cube([isize[0], isize[1], size[2]], center=true);
 124					}
 125				} else {
 126					difference() {
 127						cube(size, center=true);
 128
 129						// Chamfer edges
 130						for (i = [0:3], axis=[0:2]) {
 131							if (edges[axis][i]>0) {
 132								translate(vmul(EDGE_OFFSETS[axis][i], size/2)) {
 133									rotate(majrots[axis]) {
 134										zrot(45) cube([chamfer*sqrt(2), chamfer*sqrt(2), size[axis]+0.01], center=true);
 135									}
 136								}
 137							}
 138						}
 139
 140						// Chamfer triple-edge corners.
 141						if (trimcorners) {
 142							for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
 143								if (corner_edge_count(edges, [xa,ya,za]) > 2) {
 144									translate(vmul([xa,ya,za]/2, size-[1,1,1]*chamfer*4/3)) {
 145										rot(from=V_UP, to=[xa,ya,za]) {
 146											upcube(chamfer*3);
 147										}
 148									}
 149								}
 150							}
 151						}
 152					}
 153				}
 154			} else if (fillet != undef) {
 155				sides = quantup(segs(fillet),4);
 156				sc = 1/cos(180/sides);
 157				isize = [for (v = size) max(0.001, v-2*fillet)];
 158				if (edges == EDGES_ALL) {
 159					minkowski() {
 160						cube(isize, center=true);
 161						if (trimcorners) {
 162							rotate_extrude(convexity=2,$fn=sides) {
 163								polygon([for (i=[0:1:sides/2]) let(a=i*360/sides-90) fillet*sc*[cos(a),sin(a)]]);
 164							}
 165						} else {
 166							intersection() {
 167								zrot(180/sides) cylinder(r=fillet*sc, h=fillet*2, center=true, $fn=sides);
 168								rotate([90,0,0]) zrot(180/sides) cylinder(r=fillet*sc, h=fillet*2, center=true, $fn=sides);
 169								rotate([0,90,0]) zrot(180/sides) cylinder(r=fillet*sc, h=fillet*2, center=true, $fn=sides);
 170							}
 171						}
 172					}
 173				} else {
 174					difference() {
 175						cube(size, center=true);
 176
 177						// Round edges.
 178						for (i = [0:3], axis=[0:2]) {
 179							if (edges[axis][i]>0) {
 180								difference() {
 181									translate(vmul(EDGE_OFFSETS[axis][i], size/2)) {
 182										rotate(majrots[axis]) cube([fillet*2, fillet*2, size[axis]+0.1], center=true);
 183									}
 184									translate(vmul(EDGE_OFFSETS[axis][i], size/2 - [1,1,1]*fillet)) {
 185										rotate(majrots[axis]) zrot(180/sides) cylinder(h=size[axis]+0.2, r=fillet*sc, center=true, $fn=sides);
 186									}
 187								}
 188							}
 189						}
 190
 191						// Round triple-edge corners.
 192						if (trimcorners) {
 193							for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
 194								if (corner_edge_count(edges, [xa,ya,za]) > 2) {
 195									difference() {
 196										translate(vmul([xa,ya,za], size/2)) {
 197											cube(fillet*2, center=true);
 198										}
 199										translate(vmul([xa,ya,za], size/2-[1,1,1]*fillet)) {
 200											zrot(180/sides) sphere(r=fillet*sc*sc, $fn=sides);
 201										}
 202									}
 203								}
 204							}
 205						}
 206					}
 207				}
 208			} else {
 209				cube(size=size, center=true);
 210			}
 211		}
 212	}
 213}
 214
 215
 216
 217// Module: cube2pt()
 218// Status: DEPRECATED, use `cuboid(p1,p2)` instead.
 219//
 220// Usage:
 221//   cube2pt(p1,p2)
 222//
 223// Description:
 224//   Creates a cube between two points.
 225//
 226// Arguments:
 227//   p1 = Coordinate point of one cube corner.
 228//   p2 = Coordinate point of opposite cube corner.
 229module cube2pt(p1,p2) {
 230	deprecate("cube2pt()", "cuboid(p1,p2)");
 231	cuboid(p1=p1, p2=p2);
 232}
 233
 234
 235
 236// Module: span_cube()
 237//
 238// Description:
 239//   Creates a cube that spans the X, Y, and Z ranges given.
 240// 
 241// Arguments:
 242//   xspan = [min, max] X axis range.
 243//   yspan = [min, max] Y axis range.
 244//   zspan = [min, max] Z axis range.
 245//
 246// Example:
 247//   span_cube([0,15], [5,10], [0, 10]);
 248module span_cube(xspan, yspan, zspan) {
 249	span = [xspan, yspan, zspan];
 250	cuboid(p1=array_subindex(span,0), p2=array_subindex(span,1));
 251}
 252
 253
 254
 255// Module: offsetcube()
 256// Status: DEPRECATED, use `cuboid(..., align)` instead.
 257//
 258// Description:
 259//   Makes a cube that is offset along the given vector by half the cube's size.
 260//   For example, if `v=[-1,1,0]`, the cube's front right edge will be centered at the origin.
 261//
 262// Arguments:
 263//   size = size of cube.
 264//   v = vector to offset along.
 265module offsetcube(size=[1,1,1], v=[0,0,0]) {
 266	deprecate("offsetcube()", "cuboid()");
 267	cuboid(size=size, align=v);
 268}
 269
 270
 271// Module: leftcube()
 272//
 273// Description:
 274//   Makes a cube that is aligned on the left side of the origin.
 275//
 276// Usage:
 277//   leftcube(size);
 278// 
 279// Arguments:
 280//   size = The size of the cube to make.
 281//
 282// Example:
 283//   leftcube([20,30,40]);
 284module leftcube(size=[1,1,1]) {siz = scalar_vec3(size); left(siz[0]/2) cube(size=size, center=true);}
 285
 286
 287// Module: rightcube()
 288//
 289// Description:
 290//   Makes a cube that is aligned on the right side of the origin.
 291//
 292// Usage:
 293//   rightcube(size);
 294// 
 295// Arguments:
 296//   size = The size of the cube to make.
 297//
 298// Example:
 299//   rightcube([20,30,40]);
 300module rightcube(size=[1,1,1]) {siz = scalar_vec3(size); right(siz[0]/2) cube(size=size, center=true);}
 301
 302
 303// Module: fwdcube()
 304//
 305// Description:
 306//   Makes a cube that is aligned on the front side of the origin.
 307//
 308// Usage:
 309//   fwdcube(size);
 310// 
 311// Arguments:
 312//   size = The size of the cube to make.
 313//
 314// Example:
 315//   fwdcube([20,30,40]);
 316module fwdcube(size=[1,1,1]) {siz = scalar_vec3(size); fwd(siz[1]/2) cube(size=size, center=true);}
 317
 318
 319// Module: backcube()
 320//
 321// Description:
 322//   Makes a cube that is aligned on the front side of the origin.
 323//
 324// Usage:
 325//   backcube(size);
 326// 
 327// Arguments:
 328//   size = The size of the cube to make.
 329//
 330// Example:
 331//   backcube([20,30,40]);
 332module backcube(size=[1,1,1]) {siz = scalar_vec3(size); back(siz[1]/2) cube(size=size, center=true);}
 333
 334
 335// Module: downcube()
 336//
 337// Description:
 338//   Makes a cube that is aligned on the bottom side of the origin.
 339//
 340// Usage:
 341//   downcube(size);
 342// 
 343// Arguments:
 344//   size = The size of the cube to make.
 345//
 346// Example:
 347//   downcube([20,30,40]);
 348module downcube(size=[1,1,1]) {siz = scalar_vec3(size); down(siz[2]/2) cube(size=size, center=true);}
 349
 350
 351// Module: upcube()
 352//
 353// Description:
 354//   Makes a cube that is aligned on the top side of the origin.
 355//
 356// Usage:
 357//   upcube(size);
 358// 
 359// Arguments:
 360//   size = The size of the cube to make.
 361//
 362// Example:
 363//   upcube([20,30,40]);
 364module upcube(size=[1,1,1]) {siz = scalar_vec3(size); up(siz[2]/2) cube(size=size, center=true);}
 365
 366
 367// Module: chamfcube()
 368// Status: DEPRECATED, use `cuboid(..., chamfer, edges, trimcorners)` instead.
 369//
 370// Description:
 371//   Makes a cube with chamfered edges.
 372//
 373// Arguments:
 374//   size = Size of cube [X,Y,Z].  (Default: `[1,1,1]`)
 375//   chamfer = Chamfer inset along axis.  (Default: `0.25`)
 376//   chamfaxes = Array [X,Y,Z] of boolean values to specify which axis edges should be chamfered.
 377//   chamfcorners = Boolean to specify if corners should be flat chamferred.
 378module chamfcube(size=[1,1,1], chamfer=0.25, chamfaxes=[1,1,1], chamfcorners=false) {
 379	deprecate("chamfcube()", "cuboid()");
 380	cuboid(
 381		size=size,
 382		chamfer=chamfer,
 383		trimcorners=chamfcorners,
 384		edges = (
 385			(chamfaxes[0]? EDGES_X_ALL : EDGES_NONE) +
 386			(chamfaxes[1]? EDGES_Y_ALL : EDGES_NONE) +
 387			(chamfaxes[2]? EDGES_Z_ALL : EDGES_NONE)
 388		)
 389	);
 390}
 391
 392
 393// Module: rrect()
 394// Status: DEPRECATED, use `cuboid(..., fillet, edges)` instead.
 395//
 396// Description:
 397//   Makes a cube with rounded (filletted) vertical edges. The `r` size will be
 398//   limited to a maximum of half the length of the shortest XY side.
 399//
 400// Arguments:
 401//   size = Size of cube [X,Y,Z].  (Default: `[1,1,1]`)
 402//   r = Radius of edge/corner rounding.  (Default: `0.25`)
 403//   center = If true, object will be centered.  If false, sits on top of XY plane.
 404module rrect(size=[1,1,1], r=0.25, center=false) {
 405	deprecate("rrect()", "cuboid()");
 406	cuboid(size=size, fillet=r, edges=EDGES_Z_ALL, align=center? V_CENTER : V_UP);
 407}
 408
 409
 410// Module: rcube()
 411// Status: DEPRECATED, use `cuboid(..., fillet)` instead.
 412//
 413// Description:
 414//   Makes a cube with rounded (filletted) edges and corners.  The `r` size will be
 415//   limited to a maximum of half the length of the shortest cube side.
 416//
 417// Arguments:
 418//   size = Size of cube [X,Y,Z].  (Default: `[1,1,1]`)
 419//   r = Radius of edge/corner rounding.  (Default: `0.25`)
 420//   center = If true, object will be centered.  If false, sits on top of XY plane.
 421module rcube(size=[1,1,1], r=0.25, center=false) {
 422	deprecate("rcube()", "cuboid()");
 423	cuboid(size=size, fillet=r, align=center? V_CENTER : V_UP);
 424}
 425
 426
 427
 428// Section: Prismoids
 429
 430
 431// Module: prismoid()
 432//
 433// Description:
 434//   Creates a rectangular prismoid shape.
 435//
 436// Usage:
 437//   prismoid(size1, size2, h, [shift], [orient], [align|center]);
 438//
 439// Arguments:
 440//   size1 = [width, length] of the axis-negative end of the prism.
 441//   size2 = [width, length] of the axis-positive end of the prism.
 442//   h = Height of the prism.
 443//   shift = [x, y] amount to shift the center of the top with respect to the center of the bottom.
 444//   orient = Orientation of the prismoid.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Z`.
 445//   align = Alignment of the prismoid by the axis-negative (size1) end.  Use the `V_` constants from `constants.scad`.  Default: `ALIGN_POS`.
 446//   center = If given, overrides `align`.  A true value sets `align=V_CENTER`, false sets `align=ALIGN_POS`.
 447//
 448// Example: Rectangular Pyramid
 449//   prismoid(size1=[40,40], size2=[0,0], h=20);
 450// Example: Prism
 451//   prismoid(size1=[40,40], size2=[0,40], h=20);
 452// Example: Truncated Pyramid
 453//   prismoid(size1=[35,50], size2=[20,30], h=20);
 454// Example: Wedge
 455//   prismoid(size1=[60,35], size2=[30,0], h=30);
 456// Example: Truncated Tetrahedron
 457//   prismoid(size1=[10,40], size2=[40,10], h=40);
 458// Example: Inverted Truncated Pyramid
 459//   prismoid(size1=[15,5], size2=[30,20], h=20);
 460// Example: Right Prism
 461//   prismoid(size1=[30,60], size2=[0,60], shift=[-15,0], h=30);
 462// Example(FlatSpin): Shifting/Skewing
 463//   prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]);
 464module prismoid(
 465	size1=[1,1], size2=[1,1], h=1, shift=[0,0],
 466	orient=ORIENT_Z, align=ALIGN_POS, center=undef
 467) {
 468	eps = 0.001;
 469	s1 = [max(size1[0], eps), max(size1[1], eps)];
 470	s2 = [max(size2[0], eps), max(size2[1], eps)];
 471	shiftby = point3d(shift);
 472	orient_and_align([s1[0], s1[1], h], orient, align, center, noncentered=ALIGN_POS) {
 473		polyhedron(
 474			points=[
 475				[+s2[0]/2, +s2[1]/2, +h/2] + shiftby,
 476				[+s2[0]/2, -s2[1]/2, +h/2] + shiftby,
 477				[-s2[0]/2, -s2[1]/2, +h/2] + shiftby,
 478				[-s2[0]/2, +s2[1]/2, +h/2] + shiftby,
 479				[+s1[0]/2, +s1[1]/2, -h/2],
 480				[+s1[0]/2, -s1[1]/2, -h/2],
 481				[-s1[0]/2, -s1[1]/2, -h/2],
 482				[-s1[0]/2, +s1[1]/2, -h/2],
 483			],
 484			faces=[
 485				[0, 1, 2],
 486				[0, 2, 3],
 487				[0, 4, 5],
 488				[0, 5, 1],
 489				[1, 5, 6],
 490				[1, 6, 2],
 491				[2, 6, 7],
 492				[2, 7, 3],
 493				[3, 7, 4],
 494				[3, 4, 0],
 495				[4, 7, 6],
 496				[4, 6, 5],
 497			],
 498			convexity=2
 499		);
 500	}
 501}
 502
 503
 504// Module: trapezoid()
 505// Status: DEPRECATED, use `prismoid()` instead.
 506//
 507// Description:
 508//   Creates a rectangular prismoid shape.
 509//
 510// Usage:
 511//   trapezoid(size1, size2, h, [shift], [orient], [align|center]);
 512//
 513// Arguments:
 514//   size1 = [width, length] of the axis-negative end of the prism.
 515//   size2 = [width, length] of the axis-positive end of the prism.
 516//   h = Height of the prism.
 517//   shift = [x, y] amount to shift the center of the top with respect to the center of the bottom.
 518//   orient = Orientation of the prismoid.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Z`.
 519//   align = Alignment of the prismoid by the axis-negative (size1) end.  Use the `V_` constants from `constants.scad`.  Default: `V_UP`
 520//   center = If given, overrides `align`.  A true value sets `align=V_CENTER`, false sets `align=V_UP`.
 521module trapezoid(size1=[1,1], size2=[1,1], h=1, center=false) {
 522	deprecate("trapezoid()", "prismoid()");
 523	prismoid(size1=size1, size2=size2, h=h, center=center);
 524}
 525
 526
 527// Module: rounded_prismoid()
 528//
 529// Description:
 530//   Creates a rectangular prismoid shape with rounded vertical edges.
 531//
 532// Arguments:
 533//   size1 = [width, length] of the bottom of the prism.
 534//   size2 = [width, length] of the top of the prism.
 535//   h = Height of the prism.
 536//   r = radius of vertical edge fillets.
 537//   r1 = radius of vertical edge fillets at bottom.
 538//   r2 = radius of vertical edge fillets at top.
 539//   shift = [x, y] amount to shift the center of the top with respect to the center of the bottom.
 540//   orient = Orientation of the prismoid.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Z`.
 541//   align = Alignment of the prismoid by the axis-negative (`size1`) end.  Use the `V_` constants from `constants.scad`.  Default: `V_UP`.
 542//   center = vertically center the prism.  Overrides `align`.
 543//
 544// Example: Rounded Pyramid
 545//   rounded_prismoid(size1=[40,40], size2=[0,0], h=25, r=5);
 546// Example: Centered Rounded Pyramid
 547//   rounded_prismoid(size1=[40,40], size2=[0,0], h=25, r=5, center=true);
 548// Example: Disparate Top and Bottom Radii
 549//   rounded_prismoid(size1=[40,60], size2=[40,60], h=20, r1=3, r2=10, $fn=24);
 550// Example(FlatSpin): Shifting/Skewing
 551//   rounded_prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5], r=5);
 552module rounded_prismoid(
 553	size1, size2, h, shift=[0,0],
 554	r=undef, r1=undef, r2=undef,
 555	align=V_UP, orient=ORIENT_Z, center=undef
 556) {
 557	eps = 0.001;
 558	maxrad1 = min(size1[0]/2, size1[1]/2);
 559	maxrad2 = min(size2[0]/2, size2[1]/2);
 560	rr1 = min(maxrad1, (r1!=undef)? r1 : r);
 561	rr2 = min(maxrad2, (r2!=undef)? r2 : r);
 562	shiftby = point3d(shift);
 563	orient_and_align([size1.x, size1.y, h], orient, align, center, noncentered=ALIGN_POS) {
 564		down(h/2)
 565		hull() {
 566			linear_extrude(height=eps, center=false, convexity=2) {
 567				offset(r=rr1) {
 568					square([max(eps, size1[0]-2*rr1), max(eps, size1[1]-2*rr1)], center=true);
 569				}
 570			}
 571			up(h-0.01) {
 572				translate(shiftby) {
 573					linear_extrude(height=eps, center=false, convexity=2) {
 574						offset(r=rr2) {
 575							square([max(eps, size2[0]-2*rr2), max(eps, size2[1]-2*rr2)], center=true);
 576						}
 577					}
 578				}
 579			}
 580		}
 581	}
 582}
 583
 584
 585
 586// Module: pyramid()
 587// Status: DEPRECATED, use `cyl(, r2=0, $fn=N)` instead.
 588//
 589// Usage:
 590//   pyramid(n, h, l|r|d, [circum]);
 591//
 592// Description:
 593//   Creates a pyramidal prism with a given number of sides.
 594//
 595// Arguments:
 596//   n = number of pyramid sides.
 597//   h = height of the pyramid.
 598//   l = length of one side of the pyramid. (optional)
 599//   r = radius of the base of the pyramid. (optional)
 600//   d = diameter of the base of the pyramid. (optional)
 601//   circum = base circumscribes the circle of the given radius or diam.
 602module pyramid(n=4, h=1, l=1, r=undef, d=undef, circum=false)
 603{
 604	deprecate("pyramid()", "cyl()");
 605	radius = get_radius(r=r, d=d, dflt=l/2/sin(180/n));
 606	cyl(r1=radius, r2=0, l=h, circum=circum, $fn=n, realign=true, align=ALIGN_POS);
 607}
 608
 609
 610// Module: prism()
 611// Status: DEPRECATED, use `cyl(..., $fn=N)` instead.
 612//
 613// Usage:
 614//   prism(n, h, l|r|d, [circum]);
 615//
 616// Description:
 617//   Creates a vertical prism with a given number of sides.
 618//
 619// Arguments:
 620//   n = number of sides.
 621//   h = height of the prism.
 622//   l = length of one side of the prism. (optional)
 623//   r = radius of the prism. (optional)
 624//   d = diameter of the prism. (optional)
 625//   circum = prism circumscribes the circle of the given radius or diam.
 626module prism(n=3, h=1, l=1, r=undef, d=undef, circum=false, center=false)
 627{
 628	deprecate("prism()", "cyl()");
 629	radius = get_radius(r=r, d=d, dflt=l/2/sin(180/n));
 630	cyl(r=radius, l=h, circum=circum, $fn=n, realign=true, center=center);
 631}
 632
 633
 634// Module: right_triangle()
 635//
 636// Description:
 637//   Creates a 3D right triangular prism.
 638//
 639// Usage:
 640//   right_triangle(size, [orient], [align|center]);
 641//
 642// Arguments:
 643//   size = [width, thickness, height]
 644//   orient = The axis to place the hypotenuse along.  Only accepts `ORIENT_X`, `ORIENT_Y`, or `ORIENT_Z` from `constants.scad`.  Default: `ORIENT_Y`.
 645//   align = The side of the origin to align to.  Use `V_` constants from `constants.scad`.  Default: `V_UP+V_BACK+V_RIGHT`.
 646//   center = If given, overrides `align`.  A true value sets `align=V_CENTER`, false sets `align=V_UP+V_BACK+V_RIGHT`.
 647//
 648// Example: Centered
 649//   right_triangle([60, 10, 40], center=true);
 650// Example: *Non*-Centered
 651//   right_triangle([60, 10, 40]);
 652module right_triangle(size=[1, 1, 1], orient=ORIENT_Y, align=V_ALLPOS, center=undef)
 653{
 654	siz = scalar_vec3(size);
 655	orient_and_align(siz, align=align, center=center) {
 656		if (orient == ORIENT_X) {
 657			ang = atan2(siz[1], siz[2]);
 658			masksize = [siz[0], siz[1], norm([siz[1],siz[2]])] + [1,1,1];
 659			xrot(ang) {
 660				difference() {
 661					xrot(-ang) cube(siz, center=true);
 662					back(masksize[1]/2) cube(masksize, center=true);
 663				}
 664			}
 665		} else if (orient == ORIENT_Y) {
 666			ang = atan2(siz[0], siz[2]);
 667			masksize = [siz[0], siz[1], norm([siz[0],siz[2]])] + [1,1,1];
 668			yrot(-ang) {
 669				difference() {
 670					yrot(ang) cube(siz, center=true);
 671					right(masksize[0]/2) cube(masksize, center=true);
 672				}
 673			}
 674		} else if (orient == ORIENT_Z) {
 675			ang = atan2(siz[0], siz[1]);
 676			masksize = [norm([siz[0],siz[1]]), siz[1], siz[2]] + [1,1,1];
 677			zrot(-ang) {
 678				difference() {
 679					zrot(ang) cube(siz, center=true);
 680					back(masksize[1]/2) cube(masksize, center=true);
 681				}
 682			}
 683		}
 684	}
 685}
 686
 687
 688
 689// Section: Cylindroids
 690
 691
 692// Module: cyl()
 693//
 694// Description:
 695//   Creates cylinders in various alignments and orientations,
 696//   with optional fillets and chamfers. You can use `r` and `l`
 697//   interchangably, and all variants allow specifying size
 698//   by either `r`|`d`, or `r1`|`d1` and `r2`|`d2`.
 699//   Note that that chamfers and fillets cannot cross the
 700//   midpoint of the cylinder's length.
 701//
 702// Usage: Normal Cylinders
 703//   cyl(l|h, r|d, [circum], [realign], [orient], [align], [center]);
 704//   cyl(l|h, r1|d1, r2/d2, [circum], [realign], [orient], [align], [center]);
 705//
 706// Usage: Chamferred Cylinders
 707//   cyl(l|h, r|d, chamfer, [chamfang], [from_end], [circum], [realign], [orient], [align], [center]);
 708//   cyl(l|h, r|d, chamfer1, [chamfang1], [from_end], [circum], [realign], [orient], [align], [center]);
 709//   cyl(l|h, r|d, chamfer2, [chamfang2], [from_end], [circum], [realign], [orient], [align], [center]);
 710//   cyl(l|h, r|d, chamfer1, chamfer2, [chamfang1], [chamfang2], [from_end], [circum], [realign], [orient], [align], [center]);
 711//
 712// Usage: Rounded/Filleted Cylinders
 713//   cyl(l|h, r|d, fillet, [circum], [realign], [orient], [align], [center]);
 714//   cyl(l|h, r|d, fillet1, [circum], [realign], [orient], [align], [center]);
 715//   cyl(l|h, r|d, fillet2, [circum], [realign], [orient], [align], [center]);
 716//   cyl(l|h, r|d, fillet1, fillet2, [circum], [realign], [orient], [align], [center]);
 717//
 718// Arguments:
 719//   l / h = Length of cylinder along oriented axis. (Default: 1.0)
 720//   r = Radius of cylinder.
 721//   r1 = Radius of the negative (X-, Y-, Z-) end of cylinder.
 722//   r2 = Radius of the positive (X+, Y+, Z+) end of cylinder.
 723//   d = Diameter of cylinder.
 724//   d1 = Diameter of the negative (X-, Y-, Z-) end of cylinder.
 725//   d2 = Diameter of the positive (X+, Y+, Z+) end of cylinder.
 726//   circum = If true, cylinder should circumscribe the circle of the given size.  Otherwise inscribes.  Default: `false`
 727//   chamfer = The size of the chamfers on the ends of the cylinder.  Default: none.
 728//   chamfer1 = The size of the chamfer on the axis-negative end of the cylinder.  Default: none.
 729//   chamfer2 = The size of the chamfer on the axis-positive end of the cylinder.  Default: none.
 730//   chamfang = The angle in degrees of the chamfers on the ends of the cylinder.
 731//   chamfang1 = The angle in degrees of the chamfer on the axis-negative end of the cylinder.
 732//   chamfang2 = The angle in degrees of the chamfer on the axis-positive end of the cylinder.
 733//   from_end = If true, chamfer is measured from the end of the cylinder, instead of inset from the edge.  Default: `false`.
 734//   fillet = The radius of the fillets on the ends of the cylinder.  Default: none.
 735//   fillet1 = The radius of the fillet on the axis-negative end of the cylinder.
 736//   fillet2 = The radius of the fillet on the axis-positive end of the cylinder.
 737//   realign = If true, rotate the cylinder by half the angle of one face.
 738//   orient = Orientation of the cylinder.  Use the `ORIENT_` constants from `constants.scad`.  Default: vertical.
 739//   align = Alignment of the cylinder.  Use the `V_` constants from `constants.scad`.  Default: centered.
 740//   center = If given, overrides `align`.  A true value sets `align=V_CENTER`, false sets `align=ALIGN_POS`.
 741//
 742// Example: By Radius
 743//   xdistribute(30) {
 744//       cyl(l=40, r=10);
 745//       cyl(l=40, r1=10, r2=5);
 746//   }
 747//
 748// Example: By Diameter
 749//   xdistribute(30) {
 750//       cyl(l=40, d=25);
 751//       cyl(l=40, d1=25, d2=10);
 752//   }
 753//
 754// Example: Chamferring
 755//   xdistribute(60) {
 756//       // Shown Left to right.
 757//       cyl(l=40, d=40, chamfer=7);  // Default chamfang=45
 758//       cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=false);
 759//       cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=true);
 760//   }
 761//
 762// Example: Rounding/Filleting
 763//   cyl(l=40, d=40, fillet=10);
 764//
 765// Example: Heterogenous Chamfers and Fillets
 766//   ydistribute(80) {
 767//       // Shown Front to Back.
 768//       cyl(l=40, d=40, fillet1=15, orient=ORIENT_X);
 769//       cyl(l=40, d=40, chamfer2=5, orient=ORIENT_X);
 770//       cyl(l=40, d=40, chamfer1=12, fillet2=10, orient=ORIENT_X);
 771//   }
 772//
 773// Example: Putting it all together
 774//   cyl(l=40, d1=25, d2=15, chamfer1=10, chamfang1=30, from_end=true, fillet2=5);
 775module cyl(
 776	l=undef, h=undef,
 777	r=undef, r1=undef, r2=undef,
 778	d=undef, d1=undef, d2=undef,
 779	chamfer=undef, chamfer1=undef, chamfer2=undef,
 780	chamfang=undef, chamfang1=undef, chamfang2=undef,
 781	fillet=undef, fillet1=undef, fillet2=undef,
 782	circum=false, realign=false, from_end=false,
 783	orient=ORIENT_Z, align=V_CENTER, center=undef
 784) {
 785	r1 = get_radius(r1, r, d1, d, 1);
 786	r2 = get_radius(r2, r, d2, d, 1);
 787	l = first_defined([l, h, 1]);
 788	sides = segs(max(r1,r2));
 789	sc = circum? 1/cos(180/sides) : 1;
 790	orient_and_align([r1*2,r1*2,l], orient, align, center=center) {
 791		zrot(realign? 180/sides : 0) {
 792			if (!any_defined([chamfer, chamfer1, chamfer2, fillet, fillet1, fillet2])) {
 793				cylinder(h=l, r1=r1*sc, r2=r2*sc, center=true, $fn=sides);
 794			} else {
 795				vang = atan2(l, r1-r2)/2;
 796				chang1 = 90-first_defined([chamfang1, chamfang, vang]);
 797				chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]);
 798				cham1 = (chamfer != undef || chamfer1 != undef)?first_defined([chamfer1, chamfer]) * (from_end? 1 : tan(chang1)):undef;
 799				cham2 = (chamfer != undef || chamfer2 != undef)?first_defined([chamfer2, chamfer]) * (from_end? 1 : tan(chang2)):undef;
 800				fil1 = first_defined([fillet1, fillet]);
 801				fil2 = first_defined([fillet2, fillet]);
 802				if (chamfer != undef) {
 803					assertion(chamfer <= r1,  "chamfer is larger than the r1 radius of the cylinder.");
 804					assertion(chamfer <= r2,  "chamfer is larger than the r2 radius of the cylinder.");
 805					assertion(chamfer <= l/2, "chamfer is larger than half the length of the cylinder.");
 806				}
 807				if (cham1 != undef) {
 808					assertion(cham1 <= r1,  "chamfer1 is larger than the r1 radius of the cylinder.");
 809					assertion(cham1 <= l/2, "chamfer1 is larger than half the length of the cylinder.");
 810				}
 811				if (cham2 != undef) {
 812					assertion(cham2 <= r2,  "chamfer2 is larger than the r2 radius of the cylinder.");
 813					assertion(cham2 <= l/2, "chamfer2 is larger than half the length of the cylinder.");
 814				}
 815				if (fillet != undef) {
 816					assertion(fillet <= r1,  "fillet is larger than the r1 radius of the cylinder.");
 817					assertion(fillet <= r2,  "fillet is larger than the r2 radius of the cylinder.");
 818					assertion(fillet <= l/2, "fillet is larger than half the length of the cylinder.");
 819				}
 820				if (fil1 != undef) {
 821					assertion(fil1 <= r1,  "fillet1 is larger than the r1 radius of the cylinder.");
 822					assertion(fil1 <= l/2, "fillet1 is larger than half the length of the cylinder.");
 823				}
 824				if (fil2 != undef) {
 825					assertion(fil2 <= r2,  "fillet2 is larger than the r1 radius of the cylinder.");
 826					assertion(fil2 <= l/2, "fillet2 is larger than half the length of the cylinder.");
 827				}
 828
 829				dy1 = first_defined([cham1, fil1, 0]);
 830				dy2 = first_defined([cham2, fil2, 0]);
 831				maxd = max(r1,r2,l);
 832
 833				rotate_extrude(convexity=2) {
 834					hull() {
 835						difference() {
 836							union() {
 837								difference() {
 838									back(l/2) {
 839										if (cham2!=undef && cham2>0) {
 840											rr2 = sc * (r2 + (r1-r2)*dy2/l);
 841											chlen2 = min(rr2, cham2/sin(chang2));
 842											translate([rr2,-cham2]) {
 843												rotate(-chang2) {
 844													translate([-chlen2,-chlen2]) {
 845														square(chlen2, center=false);
 846													}
 847												}
 848											}
 849										} else if (fil2!=undef && fil2>0) {
 850											translate([r2-fil2*tan(vang),-fil2]) {
 851												circle(r=fil2);
 852											}
 853										} else {
 854											translate([r2-0.005,-0.005]) {
 855												square(0.01, center=true);
 856											}
 857										}
 858									}
 859
 860									// Make sure the corner fiddly bits never cross the X axis.
 861									fwd(maxd) square(maxd, center=false);
 862								}
 863								difference() {
 864									fwd(l/2) {
 865										if (cham1!=undef && cham1>0) {
 866											rr1 = sc * (r1 + (r2-r1)*dy1/l);
 867											chlen1 = min(rr1, cham1/sin(chang1));
 868											translate([rr1,cham1]) {
 869												rotate(chang1) {
 870													left(chlen1) {
 871														square(chlen1, center=false);
 872													}
 873												}
 874											}
 875										} else if (fil1!=undef && fil1>0) {
 876											right(r1) {
 877												translate([-fil1/tan(vang),fil1]) {
 878													fsegs1 = quantup(segs(fil1),4);
 879													circle(r=fil1,$fn=fsegs1);
 880												}
 881											}
 882										} else {
 883											right(r1-0.01) {
 884												square(0.01, center=false);
 885											}
 886										}
 887									}
 888
 889									// Make sure the corner fiddly bits never cross the X axis.
 890									square(maxd, center=false);
 891								}
 892
 893								// Force the hull to extend to the axis
 894								right(0.01/2) square([0.01, l], center=true);
 895							}
 896
 897							// Clear anything left of the Y axis.
 898							left(maxd/2) square(maxd, center=true);
 899
 900							// Clear anything right of face
 901							right((r1+r2)/2) {
 902								rotate(90-vang*2) {
 903									fwd(maxd/2) square(maxd, center=false);
 904								}
 905							}
 906						}
 907					}
 908				}
 909			}
 910		}
 911	}
 912}
 913
 914
 915
 916// Module: downcyl()
 917//
 918// Description:
 919//   Creates a cylinder aligned below the origin.
 920//
 921// Usage:
 922//   downcyl(l|h, r|d);
 923//   downcyl(l|h, r1|d1, r2|d2);
 924//
 925// Arguments:
 926//   l / h = Length of cylinder. (Default: 1.0)
 927//   r = Radius of cylinder.
 928//   r1 = Bottom radius of cylinder.
 929//   r2 = Top radius of cylinder.
 930//   d = Diameter of cylinder. (use instead of r)
 931//   d1 = Bottom diameter of cylinder.
 932//   d2 = Top diameter of cylinder.
 933//
 934// Example: Cylinder
 935//   downcyl(r=20, h=40);
 936// Example: Cone
 937//   downcyl(r1=10, r2=20, h=40);
 938module downcyl(r=undef, h=undef, l=undef, d=undef, d1=undef, d2=undef, r1=undef, r2=undef)
 939{
 940	h = first_defined([l, h, 1]);
 941	down(h/2) {
 942		cylinder(r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, h=h, center=true);
 943	}
 944}
 945
 946
 947
 948// Module: xcyl()
 949//
 950// Description:
 951//   Creates a cylinder oriented along the X axis.
 952//
 953// Usage:
 954//   xcyl(l|h, r|d, [align|center]);
 955//   xcyl(l|h, r1|d1, r2|d2, [align|center]);
 956//
 957// Arguments:
 958//   l / h = Length of cylinder along oriented axis. (Default: `1.0`)
 959//   r = Radius of cylinder.
 960//   r1 = Optional radius of left (X-) end of cylinder.
 961//   r2 = Optional radius of right (X+) end of cylinder.
 962//   d = Optional diameter of cylinder. (use instead of `r`)
 963//   d1 = Optional diameter of left (X-) end of cylinder.
 964//   d2 = Optional diameter of right (X+) end of cylinder.
 965//   align = The side of the origin to align to.  Use `V_` constants from `constants.scad`. Default: `V_CENTER`
 966//   center = If given, overrides `align`.  A `true` value sets `align=V_CENTER`, `false` sets `align=ALIGN_POS`.
 967//
 968// Example: By Radius
 969//   ydistribute(50) {
 970//       xcyl(l=35, r=10);
 971//       xcyl(l=35, r1=15, r2=5);
 972//   }
 973//
 974// Example: By Diameter
 975//   ydistribute(50) {
 976//       xcyl(l=35, d=20);
 977//       xcyl(l=35, d1=30, d2=10);
 978//   }
 979module xcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=V_CENTER, center=undef)
 980{
 981	cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_X, align=align, center=center);
 982}
 983
 984
 985
 986// Module: ycyl()
 987//
 988// Description:
 989//   Creates a cylinder oriented along the Y axis.
 990//
 991// Usage:
 992//   ycyl(l|h, r|d, [align|center]);
 993//   ycyl(l|h, r1|d1, r2|d2, [align|center]);
 994//
 995// Arguments:
 996//   l / h = Length of cylinder along oriented axis. (Default: `1.0`)
 997//   r = Radius of cylinder.
 998//   r1 = Radius of front (Y-) end of cone.
 999//   r2 = Radius of back (Y+) end of one.
1000//   d = Diameter of cylinder.
1001//   d1 = Diameter of front (Y-) end of one.
1002//   d2 = Diameter of back (Y+) end of one.
1003//   align = The side of the origin to align to.  Use `V_` constants from `constants.scad`. Default: `V_CENTER`
1004//   center = Overrides `align` if given.  If true, `align=V_CENTER`, if false, `align=ALIGN_POS`.
1005//
1006// Example: By Radius
1007//   xdistribute(50) {
1008//       ycyl(l=35, r=10);
1009//       ycyl(l=35, r1=15, r2=5);
1010//   }
1011//
1012// Example: By Diameter
1013//   xdistribute(50) {
1014//       ycyl(l=35, d=20);
1015//       ycyl(l=35, d1=30, d2=10);
1016//   }
1017module ycyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=V_CENTER, center=undef)
1018{
1019	cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_Y, align=align, center=center);
1020}
1021
1022
1023
1024// Module: zcyl()
1025//
1026// Description:
1027//   Creates a cylinder oriented along the Z axis.
1028//
1029// Usage:
1030//   zcyl(l|h, r|d, [align|center]);
1031//   zcyl(l|h, r1|d1, r2|d2, [align|center]);
1032//
1033// Arguments:
1034//   l / h = Length of cylinder along oriented axis. (Default: 1.0)
1035//   r = Radius of cylinder.
1036//   r1 = Radius of front (Y-) end of cone.
1037//   r2 = Radius of back (Y+) end of one.
1038//   d = Diameter of cylinder.
1039//   d1 = Diameter of front (Y-) end of one.
1040//   d2 = Diameter of back (Y+) end of one.
1041//   align = The side of the origin to align to.  Use `V_` constants from `constants.scad`. Default: `V_CENTER`
1042//   center = Overrides `align` if given.  If true, `align=V_CENTER`, if false, `align=ALIGN_POS`.
1043//
1044// Example: By Radius
1045//   xdistribute(50) {
1046//       zcyl(l=35, r=10);
1047//       zcyl(l=35, r1=15, r2=5);
1048//   }
1049//
1050// Example: By Diameter
1051//   xdistribute(50) {
1052//       zcyl(l=35, d=20);
1053//       zcyl(l=35, d1=30, d2=10);
1054//   }
1055module zcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=V_CENTER, center=undef)
1056{
1057	cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_Z, align=align, center=center);
1058}
1059
1060
1061
1062// Module: chamferred_cylinder()
1063// Status: DEPRECATED, use `cyl(..., chamfer)` instead.
1064//
1065// Usage:
1066//   chamferred_cylinder(h, r|d, chamfer|chamfedge, [top], [bottom], [center])
1067//
1068// Description:
1069//   Creates a cylinder with chamferred (bevelled) edges.
1070//
1071// Arguments:
1072//   h = height of cylinder. (Default: 1.0)
1073//   r = radius of cylinder. (Default: 1.0)
1074//   d = diameter of cylinder. (use instead of r)
1075//   chamfer = radial inset of the edge chamfer. (Default: 0.25)
1076//   chamfedge = length of the chamfer edge. (Use instead of chamfer)
1077//   top = boolean.  If true, chamfer the top edges. (Default: True)
1078//   bottom = boolean.  If true, chamfer the bottom edges. (Default: True)
1079//   center = boolean.  If true, cylinder is centered. (Default: false)
1080module chamferred_cylinder(h=1, r=undef, d=undef, chamfer=0.25, chamfedge=undef, angle=45, center=false, top=true, bottom=true)
1081{
1082	deprecate("chamf_cyl()` and `chamferred_cylinder()", "cyl()");
1083	r = get_radius(r=r, d=d, dflt=1);
1084	chamf = (chamfedge == undef)? chamfer : chamfedge * cos(angle);
1085	cyl(l=h, r=r, chamfer1=bottom? chamf : 0, chamfer2=top? chamf : 0, chamfang=angle, center=center);
1086}
1087
1088
1089
1090// Module: chamf_cyl()
1091// Status: DEPRECATED, use `cyl(..., chamfer)` instead.
1092//
1093// Usage:
1094//   chamf_cyl(h, r|d, chamfer|chamfedge, [top], [bottom], [center])
1095//
1096// Description:
1097//   Creates a cylinder with chamferred (bevelled) edges.  Basically a shortcut of `chamferred_cylinder()`
1098//
1099// Arguments:
1100//   h = height of cylinder. (Default: 1.0)
1101//   r = radius of cylinder. (Default: 1.0)
1102//   d = diameter of cylinder. (use instead of r)
1103//   chamfer = radial inset of the edge chamfer. (Default: 0.25)
1104//   chamfedge = length of the chamfer edge. (Use instead of chamfer)
1105//   top = boolean.  If true, chamfer the top edges. (Default: True)
1106//   bottom = boolean.  If true, chamfer the bottom edges. (Default: True)
1107//   center = boolean.  If true, cylinder is centered. (Default: false)
1108module chamf_cyl(h=1, r=undef, d=undef, chamfer=0.25, chamfedge=undef, angle=45, center=false, top=true, bottom=true)
1109	chamferred_cylinder(h=h, r=r, d=d, chamfer=chamfer, chamfedge=chamfedge, angle=angle, center=center, top=top, bottom=bottom);
1110
1111
1112// Module: filleted_cylinder()
1113// Status: DEPRECATED, use `cyl(..., fillet)` instead.
1114//
1115// Usage:
1116//   filleted_cylinder(h, r|d, fillet, [center]);
1117//
1118// Description:
1119//   Creates a cylinder with filletted (rounded) ends.
1120//
1121// Arguments:
1122//   h = height of cylinder. (Default: 1.0)
1123//   r = radius of cylinder. (Default: 1.0)
1124//   d = diameter of cylinder. (Use instead of r)
1125//   fillet = radius of the edge filleting. (Default: 0.25)
1126//   center = boolean.  If true, cylinder is centered. (Default: false)
1127module filleted_cylinder(h=1, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, fillet=0.25, center=false) {
1128	deprecate("filleted_cylinder()", "cyl()");
1129	cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, fillet=fillet, orient=ORIENT_Z, center=center);
1130}
1131
1132
1133
1134// Module: rcylinder()
1135// Status: DEPRECATED, use `cyl(..., fillet)` instead.
1136//
1137// Usage:
1138//   rcylinder(h, r|d, fillet, [center]);
1139//
1140// Description:
1141//   Creates a cylinder with filletted (rounded) ends.
1142//   Basically a shortcut for `filleted_cylinder()`.
1143//
1144// Arguments:
1145//   h = height of cylinder. (Default: 1.0)
1146//   r = radius of cylinder. (Default: 1.0)
1147//   d = diameter of cylinder. (Use instead of r)
1148//   fillet = radius of the edge filleting. (Default: 0.25)
1149//   center = boolean.  If true, cylinder is centered. (Default: false)
1150module rcylinder(h=1, r=1, r1=undef, r2=undef, d=undef, d1=undef, d2=undef, fillet=0.25, center=false) {
1151	deprecate("rcylinder()", "cyl(..., fillet)");
1152	cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, fillet=fillet, orient=ORIENT_Z, center=center);
1153}
1154
1155
1156
1157// Module: tube()
1158//
1159// Description:
1160//   Makes a hollow tube with the given outer size and wall thickness.
1161//
1162// Usage:
1163//   tube(h, ir|id, wall, [realign], [orient], [align]);
1164//   tube(h, or|od, wall, [realign], [orient], [align]);
1165//   tube(h, ir|id, or|od, [realign], [orient], [align]);
1166//   tube(h, ir1|id1, ir2|id2, wall, [realign], [orient], [align]);
1167//   tube(h, or1|od1, or2|od2, wall, [realign], [orient], [align]);
1168//   tube(h, ir1|id1, ir2|id2, or1|od1, or2|od2, [realign], [orient], [align]);
1169//
1170// Arguments:
1171//   h = height of tube. (Default: 1)
1172//   or = Outer radius of tube.
1173//   or1 = Outer radius of bottom of tube.  (Default: value of r)
1174//   or2 = Outer radius of top of tube.  (Default: value of r)
1175//   od = Outer diameter of tube.
1176//   od1 = Outer diameter of bottom of tube.
1177//   od2 = Outer diameter of top of tube.
1178//   wall = horizontal thickness of tube wall. (Default 0.5)
1179//   ir = Inner radius of tube.
1180//   ir1 = Inner radius of bottom of tube.
1181//   ir2 = Inner radius of top of tube.
1182//   id = Inner diameter of tube.
1183//   id1 = Inner diameter of bottom of tube.
1184//   id2 = Inner diameter of top of tube.
1185//   realign = If true, rotate the tube by half the angle of one face.
1186//   orient = Orientation of the tube.  Use the `ORIENT_` constants from `constants.scad`.  Default: vertical.
1187//   align = Alignment of the tube.  Use the `V_` constants from `constants.scad`.  Default: centered.
1188//
1189// Example: These all Produce the Same Tube
1190//   tube(h=30, or=40, wall=5);
1191//   tube(h=30, ir=35, wall=5);
1192//   tube(h=30, or=40, ir=35);
1193//   tube(h=30, od=80, id=70);
1194// Example: These all Produce the Same Conical Tube
1195//   tube(h=30, or1=40, or2=25, wall=5);
1196//   tube(h=30, ir1=35, or2=20, wall=5);
1197//   tube(h=30, or1=40, or2=25, ir1=35, ir2=20);
1198// Example: Circular Wedge
1199//   tube(h=30, or1=40, or2=30, ir1=20, ir2=30);
1200module tube(
1201	h=1, wall=undef,
1202	r=undef, r1=undef, r2=undef,
1203	d=undef, d1=undef, d2=undef,
1204	or=undef, or1=undef, or2=undef,
1205	od=undef, od1=undef, od2=undef,
1206	ir=undef, id=undef, ir1=undef,
1207	ir2=undef, id1=undef, id2=undef,
1208	center=undef, orient=ORIENT_Z, align=ALIGN_POS,
1209	realign=false
1210) {
1211	r1 = first_defined([or1, od1==undef?undef:od1/2, r1, d1==undef?undef:d1/2, or, od==undef?undef:od/2, r, d==undef?undef:d/2, ir1==undef||wall==undef?undef:ir1+wall, id1==undef||wall==undef?undef:id1/2+wall, ir==undef||wall==undef?undef:ir+wall, id==undef||wall==undef?undef:id/2+wall]);
1212	r2 = first_defined([or2, od2==undef?undef:od2/2, r2, d2==undef?undef:d2/2, or, od==undef?undef:od/2, r, d==undef?undef:d/2, ir2==undef||wall==undef?undef:ir2+wall, id2==undef||wall==undef?undef:id2/2+wall, ir==undef||wall==undef?undef:ir+wall, id==undef||wall==undef?undef:id/2+wall]);
1213	ir1 = first_defined([ir1, id1==undef?undef:id1/2, ir, id==undef?undef:id/2, r1==undef||wall==undef?undef:r1-wall, d1==undef||wall==undef?undef:d1/2-wall, r==undef||wall==undef?undef:r-wall, d==undef||wall==undef?undef:d/2-wall]);
1214	ir2 = first_defined([ir2, id2==undef?undef:id2/2, ir, id==undef?undef:id/2, r2==undef||wall==undef?undef:r2-wall, d2==undef||wall==undef?undef:d2/2-wall, r==undef||wall==undef?undef:r-wall, d==undef||wall==undef?undef:d/2-wall]);
1215	assertion(ir1 <= r1, "Inner radius is larger than outer radius.");
1216	assertion(ir2 <= r2, "Inner radius is larger than outer radius.");
1217	sides = segs(max(r1,r2));
1218	orient_and_align([r1*2,r1*2,h], orient, align, center=center) {
1219		zrot(realign? 180/sides : 0) {
1220			difference() {
1221				cylinder(h=h, r1=r1, r2=r2, center=true, $fn=sides);
1222				if (ir1 == ir2) {
1223					cylinder(h=h+2, r1=ir1, r2=ir2, center=true);
1224				} else {
1225					cylinder(h=h+2, r=min(ir1,ir2), center=true);
1226					diff = abs(ir1-ir2);
1227					diff2 = diff*(h+1)/h;
1228					if (ir1 > ir2) {
1229						zmove(-0.5)
1230							cylinder(h=h+1, r1=ir2+diff2, r2=ir2, center=true);
1231					} else {
1232						zmove(0.5)
1233							cylinder(h=h+1, r1=ir1, r2=ir1+diff2, center=true);
1234					}
1235				}
1236			}
1237		}
1238	}
1239}
1240
1241
1242// Module: torus()
1243//
1244// Descriptiom:
1245//   Creates a torus shape.
1246//
1247// Usage:
1248//   torus(r|d, r2|d2, [orient], [align]);
1249//   torus(or|od, ir|id, [orient], [align]);
1250//
1251// Arguments:
1252//   r  = major radius of torus ring. (use with of 'r2', or 'd2')
1253//   r2 = minor radius of torus ring. (use with of 'r', or 'd')
1254//   d  = major diameter of torus ring. (use with of 'r2', or 'd2')
1255//   d2 = minor diameter of torus ring. (use with of 'r', or 'd')
1256//   or = outer radius of the torus. (use with 'ir', or 'id')
1257//   ir = inside radius of the torus. (use with 'or', or 'od')
1258//   od = outer diameter of the torus. (use with 'ir' or 'id')
1259//   id = inside diameter of the torus. (use with 'or' or 'od')
1260//   orient = Orientation of the torus.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Z`.
1261//   align = Alignment of the torus.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1262//
1263// Example:
1264//   // These all produce the same torus.
1265//   torus(r=22.5, r2=7.5);
1266//   torus(d=45, d2=15);
1267//   torus(or=30, ir=15);
1268//   torus(od=60, id=30);
1269module torus(
1270	r=undef,  d=undef,
1271	r2=undef, d2=undef,
1272	or=undef, od=undef,
1273	ir=undef, id=undef,
1274	orient=ORIENT_Z, align=V_CENTER, center=undef
1275) {
1276	orr = get_radius(r=or, d=od, dflt=1.0);
1277	irr = get_radius(r=ir, d=id, dflt=0.5);
1278	majrad = get_radius(r=r, d=d, dflt=(orr+irr)/2);
1279	minrad = get_radius(r=r2, d=d2, dflt=(orr-irr)/2);
1280	orient_and_align([(majrad+minrad)*2, (majrad+minrad)*2, minrad*2], orient, align, center=center) {
1281		rotate_extrude(convexity=4) {
1282			right(majrad) circle(minrad);
1283		}
1284	}
1285}
1286
1287
1288
1289// Section: Spheroids
1290
1291
1292// Module: staggered_sphere()
1293//
1294// Description:
1295//   An alternate construction to the standard `sphere()` built-in, with different triangulation.
1296//
1297// Usage:
1298//   staggered_sphere(r|d, [circum])
1299//
1300// Arguments:
1301//   r = Radius of the sphere.
1302//   d = Diameter of the sphere.
1303//   circum = If true, circumscribes the perfect sphere of the given size.
1304//
1305// Example:
1306//   staggered_sphere(d=100, circum=true, $fn=10);
1307module staggered_sphere(r=undef, d=undef, circum=false, align=V_CENTER) {
1308	r = get_radius(r=r, d=d, dflt=1);
1309	sides = segs(r);
1310	vsides = max(3, ceil(sides/2))+1;
1311	step = 360/sides;
1312	vstep = 180/(vsides-1);
1313	rr = circum? r/cos(180/sides)/cos(180/sides) : r;
1314	pts = concat(
1315		[[0,0,rr]],
1316		[
1317			for (p = [1:vsides-2], t = [0:sides-1]) let(
1318				ta = (t+(p%2/2))*step,
1319				pa = p*vstep
1320			) spherical_to_xyz(rr, ta, pa)
1321		],
1322		[[0,0,-rr]]
1323	);
1324	pcnt = len(pts);
1325	faces = concat(
1326		[
1327			for (i = [1:sides], j=[0,1])
1328			j? [0, i%sides+1, i] : [pcnt-1, pcnt-1-(i%sides+1), pcnt-1-i]
1329		],
1330		[
1331			for (p = [0:vsides-4], i = [0:sides-1], j=[0,1]) let(
1332				b1 = 1+p*sides,
1333				b2 = 1+(p+1)*sides,
1334				v1 = b1+i,
1335				v2 = b1+(i+1)%sides,
1336				v3 = b2+((i+((p%2)?(sides-1):0))%sides),
1337				v4 = b2+((i+1+((p%2)?(sides-1):0))%sides)
1338			) j? [v1,v4,v3] : [v1,v2,v4]
1339		]
1340	);
1341	zrot((floor(sides/4)%2==1)? 180/sides : 0) polyhedron(points=pts, faces=faces);
1342}
1343
1344
1345
1346// Section: 3D Printing Shapes
1347
1348
1349// Module: teardrop2d()
1350//
1351// Description:
1352//   Makes a 2D teardrop shape. Useful for extruding into 3D printable holes.
1353//
1354// Usage:
1355//   teardrop2d(r|d, [ang], [cap_h]);
1356//
1357// Arguments:
1358//   r = radius of circular part of teardrop.  (Default: 1)
1359//   d = diameter of spherical portion of bottom. (Use instead of r)
1360//   ang = angle of hat walls from the Y axis.  (Default: 45 degrees)
1361//   cap_h = if given, height above center where the shape will be truncated.
1362//
1363// Example(2D): Typical Shape
1364//   teardrop2d(r=30, ang=30);
1365// Example(2D): Crop Cap
1366//   teardrop2d(r=30, ang=30, cap_h=40);
1367// Example(2D): Close Crop
1368//   teardrop2d(r=30, ang=30, cap_h=20);
1369module teardrop2d(r=1, d=undef, ang=45, cap_h=undef)
1370{
1371	eps = 0.01;
1372	r = get_radius(r=r, d=d, dflt=1);
1373	cord = 2 * r * cos(ang);
1374	cord_h = r * sin(ang);
1375	tip_y = (cord/2)/tan(ang);
1376	cap_h = min((is_def(cap_h)? cap_h : tip_y+cord_h), tip_y+cord_h);
1377	cap_w = cord * (1 - (cap_h - cord_h)/tip_y);
1378	difference() {
1379		hull() {
1380			zrot(90) circle(r=r);
1381			back(cap_h-eps/2) square([max(eps,cap_w), eps], center=true);
1382		}
1383		back(r+cap_h) square(2*r, center=true);
1384	}
1385}
1386
1387
1388// Module: teardrop()
1389//
1390// Description:
1391//   Makes a teardrop shape in the XZ plane. Useful for 3D printable holes.
1392//
1393// Usage:
1394//   teardrop(r|d, l|h, [ang], [cap_h], [orient], [align])
1395//
1396// Arguments:
1397//   r = Radius of circular part of teardrop.  (Default: 1)
1398//   d = Diameter of circular portion of bottom. (Use instead of r)
1399//   l = Thickness of teardrop. (Default: 1)
1400//   ang = Angle of hat walls from the Z axis.  (Default: 45 degrees)
1401//   cap_h = If given, height above center where the shape will be truncated.
1402//   orient = Orientation of the shape.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Y`.
1403//   align = Alignment of the shape.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1404//
1405// Example: Typical Shape
1406//   teardrop(r=30, h=10, ang=30);
1407// Example: Crop Cap
1408//   teardrop(r=30, h=10, ang=30, cap_h=40);
1409// Example: Close Crop
1410//   teardrop(r=30, h=10, ang=30, cap_h=20);
1411module teardrop(r=undef, d=undef, l=undef, h=undef, ang=45, cap_h=undef, orient=ORIENT_Y, align=V_CENTER)
1412{
1413	r = get_radius(r=r, d=d, dflt=1);
1414	l = first_defined([l, h, 1]);
1415	orient_and_align([r*2,r*2,l], orient, align) {
1416		linear_extrude(height=l, center=true, slices=2) {
1417			teardrop2d(r=r, ang=ang, cap_h=cap_h);
1418		}
1419	}
1420}
1421
1422
1423// Module: onion()
1424//
1425// Description:
1426//   Creates a sphere with a conical hat, to make a 3D teardrop.
1427//
1428// Usage:
1429//   onion(r|d, [maxang], [cap_h], [orient], [align]);
1430//
1431// Arguments:
1432//   r = radius of spherical portion of the bottom. (Default: 1)
1433//   d = diameter of spherical portion of bottom.
1434//   cap_h = height above sphere center to truncate teardrop shape.
1435//   maxang = angle of cone on top from vertical.
1436//   orient = Orientation of the shape.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Y`.
1437//   align = Alignment of the shape.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1438//
1439// Example: Typical Shape
1440//   onion(r=30, maxang=30);
1441// Example: Crop Cap
1442//   onion(r=30, maxang=30, cap_h=40);
1443// Example: Close Crop
1444//   onion(r=30, maxang=30, cap_h=20);
1445module onion(cap_h=undef, r=undef, d=undef, maxang=45, h=undef, orient=ORIENT_Z, align=V_CENTER)
1446{
1447	r = get_radius(r=r, d=d, dflt=1);
1448	h = first_defined([cap_h, h]);
1449	maxd = 3*r/tan(maxang);
1450	orient_and_align([r*2,r*2,r*2], orient, align) {
1451		rotate_extrude(convexity=2) {
1452			difference() {
1453				teardrop2d(r=r, ang=maxang, cap_h=h);
1454				left(r) square(size=[2*r,maxd], center=true);
1455			}
1456		}
1457	}
1458}
1459
1460
1461// Module: narrowing_strut()
1462//
1463// Description:
1464//   Makes a rectangular strut with the top side narrowing in a triangle.
1465//   The shape created may be likened to an extruded home plate from baseball.
1466//   This is useful for constructing parts that minimize the need to support
1467//   overhangs.
1468//
1469// Usage:
1470//   narrowing_strut(w, l, wall, [ang], [orient], [align]);
1471//
1472// Arguments:
1473//   w = Width (thickness) of the strut.
1474//   l = Length of the strut.
1475//   wall = height of rectangular portion of the strut.
1476//   ang = angle that the trianglar side will converge at.
1477//   orient = Orientation of the length axis of the shape.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Y`.
1478//   align = Alignment of the shape.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1479//
1480// Example:
1481//   narrowing_strut(w=10, l=100, wall=5, ang=30);
1482module narrowing_strut(w=10, l=100, wall=5, ang=30, orient=ORIENT_Y, align=V_UP)
1483{
1484	h = wall + w/2/tan(ang);
1485	orient_and_align([w, h, l], orient, align, orig_orient=ORIENT_Z) {
1486		fwd(h/2) {
1487			linear_extrude(height=l, center=true, slices=2) {
1488				back(wall/2) square([w, wall], center=true);
1489				back(wall-0.001) {
1490					yscale(1/tan(ang)) {
1491						difference() {
1492							zrot(45) square(w/sqrt(2), center=true);
1493							fwd(w/2) square(w, center=true);
1494						}
1495					}
1496				}
1497			}
1498		}
1499	}
1500}
1501
1502
1503// Module: thinning_wall()
1504//
1505// Description:
1506//   Makes a rectangular wall which thins to a smaller width in the center,
1507//   with angled supports to prevent critical overhangs.
1508//
1509// Usage:
1510//   thinning_wall(h, l, thick, [ang], [strut], [wall], [orient], [align]);
1511//
1512// Arguments:
1513//   h = height of wall.
1514//   l = length of wall.  If given as a vector of two numbers, specifies bottom and top lengths, respectively.
1515//   thick = thickness of wall.
1516//   ang = maximum overhang angle of diagonal brace.
1517//   strut = the width of the diagonal brace.
1518//   wall = the thickness of the thinned portion of the wall.
1519//   orient = Orientation of the length axis of the wall.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_X`.
1520//   align = Alignment of the shape.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1521//
1522// Example: Typical Shape
1523//   thinning_wall(h=50, l=80, thick=4);
1524// Example: Trapezoidal
1525//   thinning_wall(h=50, l=[80,50], thick=4);
1526module thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orient=ORIENT_X, align=V_CENTER)
1527{
1528	l1 = (l[0] == undef)? l : l[0];
1529	l2 = (l[1] == undef)? l : l[1];
1530
1531	trap_ang = atan2((l2-l1)/2, h);
1532	corr1 = 1 + sin(trap_ang);
1533	corr2 = 1 - sin(trap_ang);
1534
1535	z1 = h/2;
1536	z2 = max(0.1, z1 - strut);
1537	z3 = max(0.05, z2 - (thick-wall)/2*sin(90-ang)/sin(ang));
1538
1539	x1 = l2/2;
1540	x2 = max(0.1, x1 - strut*corr1);
1541	x3 = max(0.05, x2 - (thick-wall)/2*sin(90-ang)/sin(ang)*corr1);
1542	x4 = l1/2;
1543	x5 = max(0.1, x4 - strut*corr2);
1544	x6 = max(0.05, x5 - (thick-wall)/2*sin(90-ang)/sin(ang)*corr2);
1545
1546	y1 = thick/2;
1547	y2 = y1 - min(z2-z3, x2-x3) * sin(ang);
1548
1549	orient_and_align([l1, thick, h], orient, align, orig_orient=ORIENT_X) {
1550		polyhedron(
1551			points=[
1552				[-x4, -y1, -z1],
1553				[ x4, -y1, -z1],
1554				[ x1, -y1,  z1],
1555				[-x1, -y1,  z1],
1556
1557				[-x5, -y1, -z2],
1558				[ x5, -y1, -z2],
1559				[ x2, -y1,  z2],
1560				[-x2, -y1,  z2],
1561
1562				[-x6, -y2, -z3],
1563				[ x6, -y2, -z3],
1564				[ x3, -y2,  z3],
1565				[-x3, -y2,  z3],
1566
1567				[-x4,  y1, -z1],
1568				[ x4,  y1, -z1],
1569				[ x1,  y1,  z1],
1570				[-x1,  y1,  z1],
1571
1572				[-x5,  y1, -z2],
1573				[ x5,  y1, -z2],
1574				[ x2,  y1,  z2],
1575				[-x2,  y1,  z2],
1576
1577				[-x6,  y2, -z3],
1578				[ x6,  y2, -z3],
1579				[ x3,  y2,  z3],
1580				[-x3,  y2,  z3],
1581			],
1582			faces=[
1583				[ 4,  5,  1],
1584				[ 5,  6,  2],
1585				[ 6,  7,  3],
1586				[ 7,  4,  0],
1587
1588				[ 4,  1,  0],
1589				[ 5,  2,  1],
1590				[ 6,  3,  2],
1591				[ 7,  0,  3],
1592
1593				[ 8,  9,  5],
1594				[ 9, 10,  6],
1595				[10, 11,  7],
1596				[11,  8,  4],
1597
1598				[ 8,  5,  4],
1599				[ 9,  6,  5],
1600				[10,  7,  6],
1601				[11,  4,  7],
1602
1603				[11, 10,  9],
1604				[20, 21, 22],
1605
1606				[11,  9,  8],
1607				[20, 22, 23],
1608
1609				[16, 17, 21],
1610				[17, 18, 22],
1611				[18, 19, 23],
1612				[19, 16, 20],
1613
1614				[16, 21, 20],
1615				[17, 22, 21],
1616				[18, 23, 22],
1617				[19, 20, 23],
1618
1619				[12, 13, 17],
1620				[13, 14, 18],
1621				[14, 15, 19],
1622				[15, 12, 16],
1623
1624				[12, 17, 16],
1625				[13, 18, 17],
1626				[14, 19, 18],
1627				[15, 16, 19],
1628
1629				[ 0,  1, 13],
1630				[ 1,  2, 14],
1631				[ 2,  3, 15],
1632				[ 3,  0, 12],
1633
1634				[ 0, 13, 12],
1635				[ 1, 14, 13],
1636				[ 2, 15, 14],
1637				[ 3, 12, 15],
1638			],
1639			convexity=6
1640		);
1641	}
1642}
1643
1644
1645// Module: braced_thinning_wall()
1646//
1647// Description:
1648//   Makes a rectangular wall with cross-bracing, which thins to a smaller width in the center,
1649//   with angled supports to prevent critical overhangs.
1650//
1651// Usage:
1652//   braced_thinning_wall(h, l, thick, [ang], [strut], [wall], [orient], [align]);
1653//
1654// Arguments:
1655//   h = height of wall.
1656//   l = length of wall.
1657//   thick = thickness of wall.
1658//   ang = maximum overhang angle of diagonal brace.
1659//   strut = the width of the diagonal brace.
1660//   wall = the thickness of the thinned portion of the wall.
1661//   orient = Orientation of the length axis of the wall.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Y`.
1662//   align = Alignment of the shape.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1663//
1664// Example: Typical Shape
1665//   braced_thinning_wall(h=50, l=100, thick=5);
1666module braced_thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orient=ORIENT_Y, align=V_CENTER)
1667{
1668	dang = atan((h-2*strut)/(l-2*strut));
1669	dlen = (h-2*strut)/sin(dang);
1670	orient_and_align([thick, l, h], orient, align, orig_orient=ORIENT_Y) {
1671		xrot_copies([0, 180]) {
1672			down(h/2) narrowing_strut(w=thick, l=l, wall=strut, ang=ang);
1673			fwd(l/2) xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang);
1674			intersection() {
1675				cube(size=[thick, l, h], center=true);
1676				xrot_copies([-dang,dang]) {
1677					zspread(strut/2) {
1678						scale([1,1,1.5]) yrot(45) {
1679							cube(size=[thick/sqrt(2), dlen, thick/sqrt(2)], center=true);
1680						}
1681					}
1682					cube(size=[thick, dlen, strut/2], center=true);
1683				}
1684			}
1685		}
1686		cube(size=[wall, l-0.1, h-0.1], center=true);
1687	}
1688}
1689
1690
1691
1692// Module: thinning_triangle()
1693//
1694// Description:
1695//   Makes a triangular wall with thick edges, which thins to a smaller width in
1696//   the center, with angled supports to prevent critical overhangs.
1697//
1698// Usage:
1699//   thinning_triangle(h, l, thick, [ang], [strut], [wall], [diagonly], [orient], [align|center]);
1700//
1701// Arguments:
1702//   h = height of wall.
1703//   l = length of wall.
1704//   thick = thickness of wall.
1705//   ang = maximum overhang angle of diagonal brace.
1706//   strut = the width of the diagonal brace.
1707//   wall = the thickness of the thinned portion of the wall.
1708//   diagonly = boolean, which denotes only the diagonal side (hypotenuse) should be thick.
1709//   orient = Orientation of the length axis of the shape.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Y`.
1710//   align = Alignment of the shape.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1711//   center = If true, centers shape.  If false, overrides `align` with `V_UP+V_BACK`.
1712//
1713// Example: Centered
1714//   thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, center=true);
1715// Example: All Braces
1716//   thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, center=false);
1717// Example: Diagonal Brace Only
1718//   thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, diagonly=true, center=false);
1719module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly=false, center=undef, orient=ORIENT_Y, align=V_CENTER)
1720{
1721	dang = atan(h/l);
1722	dlen = h/sin(dang);
1723	orient_and_align([thick, l, h], orient, align, center=center, noncentered=V_UP+V_BACK, orig_orient=ORIENT_Y) {
1724		difference() {
1725			union() {
1726				if (!diagonly) {
1727					translate([0, 0, -h/2])
1728						narrowing_strut(w=thick, l=l, wall=strut, ang=ang);
1729					translate([0, -l/2, 0])
1730						xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang);
1731				}
1732				intersection() {
1733					cube(size=[thick, l, h], center=true);
1734					xrot(-dang) yrot(180) {
1735						narrowing_strut(w=thick, l=dlen*1.2, wall=strut, ang=ang);
1736					}
1737				}
1738				cube(size=[wall, l-0.1, h-0.1], center=true);
1739			}
1740			xrot(-dang) {
1741				translate([0, 0, h/2]) {
1742					cube(size=[thick+0.1, l*2, h], center=true);
1743				}
1744			}
1745		}
1746	}
1747}
1748
1749
1750// Module: thinning_brace()
1751// Status: DEPRECATED, use `thinning_triangle(..., diagonly=true)` instead.
1752//
1753// Description:
1754//   Makes a triangular wall which thins to a smaller width in the center,
1755//   with angled supports to prevent critical overhangs.  Basically an alias
1756//   of thinning_triangle(), with diagonly=true.
1757//
1758// Usage:
1759//   thinning_brace(h, l, thick, [ang], [strut], [wall], [center])
1760//
1761// Arguments:
1762//   h = height of wall.
1763//   l = length of wall.
1764//   thick = thickness of wall.
1765//   ang = maximum overhang angle of diagonal brace.
1766//   strut = the width of the diagonal brace.
1767//   wall = the thickness of the thinned portion of the wall.
1768module thinning_brace(h=50, l=100, thick=5, ang=30, strut=5, wall=3, center=true)
1769{
1770	deprecate("thinning_brace()", "thinning_triangle(..., diagonly=true)");
1771	thinning_triangle(h=h, l=l, thick=thick, ang=ang, strut=strut, wall=wall, diagonly=true, center=center);
1772}
1773
1774
1775// Module: sparse_strut()
1776//
1777// Description:
1778//   Makes an open rectangular strut with X-shaped cross-bracing, designed to reduce
1779//   the need for support material in 3D printing.
1780//
1781// Usage:
1782//   sparse_strut(h, l, thick, [strut], [maxang], [max_bridge], [orient], [align])
1783//
1784// Arguments:
1785//   h = height of strut wall.
1786//   l = length of strut wall.
1787//   thick = thickness of strut wall.
1788//   maxang = maximum overhang angle of cross-braces.
1789//   max_bridge = maximum bridging distance between cross-braces.
1790//   strut = the width of the cross-braces.
1791//   orient = Orientation of the length axis of the shape.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Y`.
1792//   align = Alignment of the shape.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1793//
1794// Example: Typical Shape
1795//   sparse_strut(h=40, l=100, thick=3);
1796// Example: Thinner Strut
1797//   sparse_strut(h=40, l=100, thick=3, strut=2);
1798// Example: Larger maxang
1799//   sparse_strut(h=40, l=100, thick=3, strut=2, maxang=45);
1800// Example: Longer max_bridge
1801//   sparse_strut(h=40, l=100, thick=3, strut=2, maxang=45, max_bridge=30);
1802module sparse_strut(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge=20, orient=ORIENT_Y, align=V_CENTER)
1803{
1804	zoff = h/2 - strut/2;
1805	yoff = l/2 - strut/2;
1806
1807	maxhyp = 1.5 * (max_bridge+strut)/2 / sin(maxang);
1808	maxz = 2 * maxhyp * cos(maxang);
1809
1810	zreps = ceil(2*zoff/maxz);
1811	zstep = 2*zoff / zreps;
1812
1813	hyp = zstep/2 / cos(maxang);
1814	maxy = min(2 * hyp * sin(maxang), max_bridge+strut);
1815
1816	yreps = ceil(2*yoff/maxy);
1817	ystep = 2*yoff / yreps;
1818
1819	ang = atan(ystep/zstep);
1820	len = zstep / cos(ang);
1821
1822	orient_and_align([thick, l, h], orient, align, orig_orient=ORIENT_Y) {
1823		zspread(zoff*2)
1824			cube(size=[thick, l, strut], center=true);
1825		yspread(yoff*2)
1826			cube(size=[thick, strut, h], center=true);
1827		yspread(ystep, n=yreps) {
1828			zspread(zstep, n=zreps) {
1829				xrot( ang) cube(size=[thick, strut, len], center=true);
1830				xrot(-ang) cube(size=[thick, strut, len], center=true);
1831			}
1832		}
1833	}
1834}
1835
1836
1837// Module: sparse_strut3d()
1838//
1839// Usage:
1840//   sparse_strut3d(h, w, l, [thick], [maxang], [max_bridge], [strut], [orient], [align]);
1841//
1842// Description:
1843//   Makes an open rectangular strut with X-shaped cross-bracing, designed to reduce the
1844//   need for support material in 3D printing.
1845//
1846// Arguments:
1847//   h = Z size of strut.
1848//   w = X size of strut.
1849//   l = Y size of strut.
1850//   thick = thickness of strut walls.
1851//   maxang = maximum overhang angle of cross-braces.
1852//   max_bridge = maximum bridging distance between cross-braces.
1853//   strut = the width of the cross-braces.
1854//   orient = Orientation of the length axis of the shape.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Y`.
1855//   align = Alignment of the shape.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1856//
1857// Example: Typical Shape
1858//   sparse_strut3d(h=30, w=30, l=100);
1859// Example: Thinner strut
1860//   sparse_strut3d(h=30, w=30, l=100, strut=2);
1861// Example: Larger maxang
1862//   sparse_strut3d(h=30, w=30, l=100, strut=2, maxang=50);
1863// Example: Smaller max_bridge
1864//   sparse_strut3d(h=30, w=30, l=100, strut=2, maxang=50, max_bridge=20);
1865module sparse_strut3d(h=50, l=100, w=50, thick=3, maxang=40, strut=3, max_bridge=30, orient=ORIENT_Y, align=V_CENTER)
1866{
1867
1868	xoff = w - thick;
1869	yoff = l - thick;
1870	zoff = h - thick;
1871
1872	xreps = ceil(xoff/yoff);
1873	yreps = ceil(yoff/xoff);
1874	zreps = ceil(zoff/min(xoff, yoff));
1875
1876	xstep = xoff / xreps;
1877	ystep = yoff / yreps;
1878	zstep = zoff / zreps;
1879
1880	cross_ang = atan2(xstep, ystep);
1881	cross_len = hypot(xstep, ystep);
1882
1883	supp_ang = min(maxang, min(atan2(max_bridge, zstep), atan2(cross_len/2, zstep)));
1884	supp_reps = floor(cross_len/2/(zstep*sin(supp_ang)));
1885	supp_step = cross_len/2/supp_reps;
1886
1887	orient_and_align([w, l, h], orient, align, orig_orient=ORIENT_Y) {
1888		intersection() {
1889			union() {
1890				ybridge = (l - (yreps+1) * strut) / yreps;
1891				xspread(xoff) sparse_strut(h=h, l=l, thick=thick, maxang=maxang, strut=strut, max_bridge=ybridge/ceil(ybridge/max_bridge));
1892				yspread(yoff) zrot(90) sparse_strut(h=h, l=w, thick=thick, maxang=maxang, strut=strut, max_bridge=max_bridge);
1893				for(zs = [0:zreps-1]) {
1894					for(xs = [0:xreps-1]) {
1895						for(ys = [0:yreps-1]) {
1896							translate([(xs+0.5)*xstep-xoff/2, (ys+0.5)*ystep-yoff/2, (zs+0.5)*zstep-zoff/2]) {
1897								zflip_copy(offset=-(zstep-strut)/2) {
1898									xflip_copy() {
1899										zrot(cross_ang) {
1900											down(strut/2) {
1901												cube([strut, cross_len, strut], center=true);
1902											}
1903											if (zreps>1) {
1904												back(cross_len/2) {
1905													zrot(-cross_ang) {
1906														down(strut) upcube([strut, strut, zstep+strut]);
1907													}
1908												}
1909											}
1910											for (soff = [0 : supp_reps-1] ) {
1911												yflip_copy() {
1912													back(soff*supp_step) {
1913														skew_xy(ya=supp_ang) {
1914															upcube([strut, strut, zstep]);
1915														}
1916													}
1917												}
1918											}
1919										}
1920									}
1921								}
1922							}
1923						}
1924					}
1925				}
1926			}
1927			cube([w,l,h], center=true);
1928		}
1929	}
1930}
1931
1932
1933// Module: corrugated_wall()
1934//
1935// Description:
1936//   Makes a corrugated wall which relieves contraction stress while still
1937//   providing support strength.  Designed with 3D printing in mind.
1938//
1939// Usage:
1940//   corrugated_wall(h, l, thick, [strut], [wall], [orient], [align]);
1941//
1942// Arguments:
1943//   h = height of strut wall.
1944//   l = length of strut wall.
1945//   thick = thickness of strut wall.
1946//   strut = the width of the cross-braces.
1947//   wall = thickness of corrugations.
1948//   orient = Orientation of the length axis of the shape.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Y`.
1949//   align = Alignment of the shape.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
1950//
1951// Example: Typical Shape
1952//   corrugated_wall(h=50, l=100);
1953// Example: Wider Strut
1954//   corrugated_wall(h=50, l=100, strut=8);
1955// Example: Thicker Wall
1956//   corrugated_wall(h=50, l=100, strut=8, wall=3);
1957module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2, orient=ORIENT_Y, align=V_CENTER)
1958{
1959	amplitude = (thick - wall) / 2;
1960	period = min(15, thick * 2);
1961	steps = quantup(segs(thick/2),4);
1962	step = period/steps;
1963	il = l - 2*strut + 2*step;
1964	orient_and_align([thick, l, h], orient, align, orig_orient=ORIENT_Y) {
1965		linear_extrude(height=h-2*strut+0.1, slices=2, convexity=ceil(2*il/period), center=true) {
1966			polygon(
1967				points=concat(
1968					[for (y=[-il/2:step:il/2]) [amplitude*sin(y/period*360)-wall/2, y] ],
1969					[for (y=[il/2:-step:-il/2]) [amplitude*sin(y/period*360)+wall/2, y] ]
1970				)
1971			);
1972		}
1973
1974		difference() {
1975			cube([thick, l, h], center=true);
1976			cube([thick+0.5, l-2*strut, h-2*strut], center=true);
1977		}
1978	}
1979}
1980
1981
1982// Section: Miscellaneous
1983
1984
1985// Module: nil()
1986//
1987// Description:
1988//   Useful when you MUST pass a child to a module, but you want it to be nothing.
1989module nil() union() {}
1990
1991
1992// Module: noop()
1993//
1994// Description:
1995//   Passes through the children passed to it, with no action at all.
1996//   Useful while debugging when you want to replace a command.
1997module noop() children();
1998
1999
2000// Module: pie_slice()
2001//
2002// Description:
2003//   Creates a pie slice shape.
2004//
2005// Usage:
2006//   pie_slice(ang, l|h, r|d, [orient], [align|center]);
2007//   pie_slice(ang, l|h, r1|d1, r2|d2, [orient], [align|center]);
2008//
2009// Arguments:
2010//   ang = pie slice angle in degrees.
2011//   h = height of pie slice.
2012//   r = radius of pie slice.
2013//   r1 = bottom radius of pie slice.
2014//   r2 = top radius of pie slice.
2015//   d = diameter of pie slice.
2016//   d1 = bottom diameter of pie slice.
2017//   d2 = top diameter of pie slice.
2018//   orient = Orientation of the pie slice.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Z`.
2019//   align = Alignment of the pie slice.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
2020//   center = If given, overrides `align`.  A true value sets `align=V_CENTER`, false sets `align=ALIGN_POS`.
2021//
2022// Example: Cylindrical Pie Slice
2023//   pie_slice(ang=45, l=20, r=30);
2024// Example: Conical Pie Slice
2025//   pie_slice(ang=60, l=20, d1=50, d2=70);
2026module pie_slice(
2027	ang=30, l=undef,
2028	r=undef, r1=undef, r2=undef,
2029	d=undef, d1=undef, d2=undef,
2030	orient=ORIENT_Z, align=ALIGN_POS,
2031	center=undef, h=undef
2032) {
2033	l = first_defined([l, h, 1]);
2034	r1 = get_radius(r1, r, d1, d, 10);
2035	r2 = get_radius(r2, r, d2, d, 10);
2036	maxd = max(r1,r2)+0.1;
2037	orient_and_align([2*r1, 2*r1, l], orient, align, center=center) {
2038		difference() {
2039			cylinder(r1=r1, r2=r2, h=l, center=true);
2040			if (ang<180) rotate(ang) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
2041			difference() {
2042				fwd(maxd/2) cube([2*maxd, maxd, l+0.2], center=true);
2043				if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
2044			}
2045		}
2046	}
2047}
2048
2049
2050// Module: interior_fillet()
2051//
2052// Description:
2053//   Creates a shape that can be unioned into a concave joint between two faces, to fillet them.
2054//   Center this part along the concave edge to be chamferred and union it in.
2055//
2056// Usage:
2057//   interior_fillet(l, r, [ang], [overlap], [orient], [align]);
2058//
2059// Arguments:
2060//   l = length of edge to fillet.
2061//   r = radius of fillet.
2062//   ang = angle between faces to fillet.
2063//   overlap = overlap size for unioning with faces.
2064//   orient = Orientation of the fillet.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_X`.
2065//   align = Alignment of the fillet.  Use the `V_` or `ALIGN_` constants from `constants.scad`.  Default: `V_CENTER`.
2066//
2067// Example:
2068//   union() {
2069//       translate([0,2,-4]) upcube([20, 4, 24]);
2070//       translate([0,-10,-4]) upcube([20, 20, 4]);
2071//       color("green") interior_fillet(l=20, r=10, orient=ORIENT_XNEG);
2072//   }
2073//
2074// Example:
2075//   interior_fillet(l=40, r=10, orient=ORIENT_Y_90);
2076module interior_fillet(l=1.0, r=1.0, ang=90, overlap=0.01, orient=ORIENT_X, align=V_CENTER) {
2077	dy = r/tan(ang/2);
2078	orient_and_align([l,r,r], orient, align, orig_orient=ORIENT_X) {
2079		difference() {
2080			translate([0,-overlap/tan(ang/2),-overlap]) {
2081				if (ang == 90) {
2082					translate([0,r/2,r/2]) cube([l,r,r], center=true);
2083				} else {
2084					rotate([90,0,90]) pie_slice(ang=ang, r=dy+overlap, h=l, center=true);
2085				}
2086			}
2087			translate([0,dy,r]) xcyl(l=l+0.1, r=r);
2088		}
2089	}
2090}
2091
2092
2093
2094// Module: slot()
2095// 
2096// Description:
2097//   Makes a linear slot with rounded ends, appropriate for bolts to slide along.
2098//
2099// Usage:
2100//   slot(h, l, r|d, [orient], [align|center]);
2101//   slot(h, p1, p2, r|d, [orient], [align|center]);
2102//   slot(h, l, r1|d1, r2|d2, [orient], [align|center]);
2103//   slot(h, p1, p2, r1|d1, r2|d2, [orient], [align|center]);
2104//
2105// Arguments:
2106//   p1 = center of starting circle of slot.
2107//   p2 = center of ending circle of slot.
2108//   l = length of slot along the X axis.
2109//   h = height of slot shape. (default: 10)
2110//   r = radius of slot circle. (default: 5)
2111//   r1 = bottom radius of slot cone.
2112//   r2 = top radius of slot cone.
2113//   d = diameter of slot circle.
2114//   d1 = bottom diameter of slot cone.
2115//   d2 = top diameter of slot cone.
2116//
2117// Example: Between Two Points
2118//   slot([0,0,0], [50,50,0], r1=5, r2=10, h=5);
2119// Example: By Length
2120//   slot(l=50, r1=5, r2=10, h=5);
2121module slot(
2122	p1=undef, p2=undef, h=10, l=undef,
2123	r=undef, r1=undef, r2=undef,
2124	d=undef, d1=undef, d2=undef
2125) {
2126	r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=5);
2127	r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=5);
2128	sides = quantup(segs(max(r1, r2)), 4);
2129	hull() spread(p1=p1, p2=p2, l=l, n=2) cyl(l=h, r1=r1, r2=r2, center=true, $fn=sides);
2130}
2131
2132
2133// Module: arced_slot()
2134// 
2135// Description:
2136//   Makes an arced slot, appropriate for bolts to slide along.
2137//
2138// Usage:
2139//   arced_slot(h, r|d, sr|sd, [sa], [ea], [orient], [align|center], [$fn2]);
2140//   arced_slot(h, r|d, sr1|sd1, sr2|sd2, [sa], [ea], [orient], [align|center], [$fn2]);
2141//
2142// Arguments:
2143//   cp = centerpoint of slot arc. (default: [0, 0, 0])
2144//   h = height of slot arc shape. (default: 1.0)
2145//   r = radius of slot arc. (default: 0.5)
2146//   d = diameter of slot arc. (default: 1.0)
2147//   sr = radius of slot channel. (default: 0.5)
2148//   sd = diameter of slot channel. (default: 0.5)
2149//   sr1 = bottom radius of slot channel cone. (use instead of sr)
2150//   sr2 = top radius of slot channel cone. (use instead of sr)
2151//   sd1 = bottom diameter of slot channel cone. (use instead of sd)
2152//   sd2 = top diameter of slot channel cone. (use instead of sd)
2153//   sa = starting angle. (Default: 0.0)
2154//   ea = ending angle. (Default: 90.0)
2155//   orient = Orientation of the arced slot.  Use the `ORIENT_` constants from `constants.scad`.  Default: `ORIENT_Z`.
2156//   align = Alignment of the arced slot.  Use the `V_` constants from `constants.scad`.  Default: `V_CENTER`.
2157//   center = If true, centers vertically.  If false, drops flush with XY plane.  Overrides `align`.
2158//   $fn2 = The $fn value to use on the small round endcaps.  The major arcs are still based on $fn.  Default: $fn
2159//
2160// Example: Typical Arced Slot
2161//   arced_slot(d=60, h=5, sd=10, sa=60, ea=280);
2162// Example: Conical Arced Slot
2163//   arced_slot(r=60, h=5, sd1=10, sd2=15, sa=45, ea=180);
2164module arced_slot(
2165	r=undef, d=undef, h=1.0,
2166	sr=undef, sr1=undef, sr2=undef,
2167	sd=undef, sd1=undef, sd2=undef,
2168	sa=0, ea=90, cp=[0,0,0],
2169	orient=ORIENT_Z, align=V_CENTER,
2170	$fn2 = undef
2171) {
2172	r = get_radius(r=r, d=d, dflt=2);
2173	sr1 = get_radius(sr1, sr, sd1, sd, 2);
2174	sr2 = get_radius(sr2, sr, sd2, sd, 2);
2175	fn_minor = first_defined([$fn2, $fn]);
2176	da = ea - sa;
2177	orient_and_align([r+sr1, r+sr1, h], orient, align) {
2178		translate(cp) {
2179			zrot(sa) {
2180				difference() {
2181					pie_slice(ang=da, l=h, r1=r+sr1, r2=r+sr2, orient=ORIENT_Z, align=V_CENTER);
2182					cylinder(h=h+0.1, r1=r-sr1, r2=r-sr2, center=true);
2183				}
2184				right(r) cylinder(h=h, r1=sr1, r2=sr2, center=true, $fn=fn_minor);
2185				zrot(da) right(r) cylinder(h=h, r1=sr1, r2=sr2, center=true, $fn=fn_minor);
2186			}
2187		}
2188	}
2189}
2190
2191
2192
2193// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap