1//////////////////////////////////////////////////////////////////////
   2// LibFile: transforms.scad
   3//   This is the file that the most commonly used transformations, distributors, and mutator are in.
   4//   To use, add the following lines to the beginning of your file:
   5//   ```
   6//   include <BOSL/constants.scad>
   7//   use <BOSL/transforms.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 <math.scad>
  41include <compat.scad>
  42include <constants.scad>
  43
  44
  45
  46//////////////////////////////////////////////////////////////////////
  47// Section: Translations
  48//////////////////////////////////////////////////////////////////////
  49
  50
  51// Module: move()
  52//
  53// Description:
  54//   Moves/translates children.
  55//
  56// Usage:
  57//   move([x], [y], [z]) ...
  58//   move([x,y,z]) ...
  59//
  60// Arguments:
  61//   x = X axis translation.
  62//   y = Y axis translation.
  63//   z = Z axis translation.
  64//
  65// Example:
  66//   #sphere(d=10);
  67//   move([0,20,30]) sphere(d=10);
  68//
  69// Example:
  70//   #sphere(d=10);
  71//   move(y=20) sphere(d=10);
  72//
  73// Example:
  74//   #sphere(d=10);
  75//   move(x=-10, y=-5) sphere(d=10);
  76module move(a=[0,0,0], x=0, y=0, z=0)
  77{
  78	translate(a+[x,y,z]) children();
  79}
  80
  81
  82// Module: xmove()
  83//
  84// Description:
  85//   Moves/translates children the given amount along the X axis.
  86//
  87// Usage:
  88//   xmove(x) ...
  89//
  90// Arguments:
  91//   x = Amount to move right along the X axis.  Negative values move left.
  92//
  93// Example:
  94//   #sphere(d=10);
  95//   xmove(20) sphere(d=10);
  96module xmove(x=0) translate([x,0,0]) children();
  97
  98
  99// Module: ymove()
 100//
 101// Description:
 102//   Moves/translates children the given amount along the Y axis.
 103//
 104// Usage:
 105//   ymove(y) ...
 106//
 107// Arguments:
 108//   y = Amount to move back along the Y axis.  Negative values move forward.
 109//
 110// Example:
 111//   #sphere(d=10);
 112//   ymove(20) sphere(d=10);
 113module ymove(y=0) translate([0,y,0]) children();
 114
 115
 116// Module: zmove()
 117//
 118// Description:
 119//   Moves/translates children the given amount along the Z axis.
 120//
 121// Usage:
 122//   zmove(z) ...
 123//
 124// Arguments:
 125//   z = Amount to move up along the Z axis.  Negative values move down.
 126//
 127// Example:
 128//   #sphere(d=10);
 129//   zmove(20) sphere(d=10);
 130module zmove(z=0) translate([0,0,z]) children();
 131
 132
 133// Module: left()
 134//
 135// Description:
 136//   Moves children left (in the X- direction) by the given amount.
 137//
 138// Usage:
 139//   left(x) ...
 140//
 141// Arguments:
 142//   x = Scalar amount to move left.
 143//
 144// Example:
 145//   #sphere(d=10);
 146//   left(20) sphere(d=10);
 147module left(x=0) translate([-x,0,0]) children();
 148
 149
 150// Module: right()
 151//
 152// Description:
 153//   Moves children right (in the X+ direction) by the given amount.
 154//
 155// Usage:
 156//   right(x) ...
 157//
 158// Arguments:
 159//   x = Scalar amount to move right.
 160//
 161// Example:
 162//   #sphere(d=10);
 163//   right(20) sphere(d=10);
 164module right(x=0) translate([x,0,0]) children();
 165
 166
 167// Module: fwd() / forward()
 168//
 169// Description:
 170//   Moves children forward (in the Y- direction) by the given amount.
 171//
 172// Usage:
 173//   fwd(y) ...
 174//   forward(y) ...
 175//
 176// Arguments:
 177//   y = Scalar amount to move forward.
 178//
 179// Example:
 180//   #sphere(d=10);
 181//   fwd(20) sphere(d=10);
 182module forward(y=0) translate([0,-y,0]) children();
 183module fwd(y=0) translate([0,-y,0]) children();
 184
 185
 186// Module: back()
 187//
 188// Description:
 189//   Moves children back (in the Y+ direction) by the given amount.
 190//
 191// Usage:
 192//   back(y) ...
 193//
 194// Arguments:
 195//   y = Scalar amount to move back.
 196//
 197// Example:
 198//   #sphere(d=10);
 199//   back(20) sphere(d=10);
 200module back(y=0) translate([0,y,0]) children();
 201
 202
 203// Module: down()
 204//
 205// Description:
 206//   Moves children down (in the Z- direction) by the given amount.
 207//
 208// Usage:
 209//   down(z) ...
 210//
 211// Arguments:
 212//   z = Scalar amount to move down.
 213//
 214// Example:
 215//   #sphere(d=10);
 216//   down(20) sphere(d=10);
 217module down(z=0) translate([0,0,-z]) children();
 218
 219
 220// Module: up()
 221//
 222// Description:
 223//   Moves children up (in the Z+ direction) by the given amount.
 224//
 225// Usage:
 226//   up(z) ...
 227//
 228// Arguments:
 229//   z = Scalar amount to move up.
 230//
 231// Example:
 232//   #sphere(d=10);
 233//   up(20) sphere(d=10);
 234module up(z=0) translate([0,0,z]) children();
 235
 236
 237
 238//////////////////////////////////////////////////////////////////////
 239// Section: Rotations
 240//////////////////////////////////////////////////////////////////////
 241
 242
 243// Module: rot()
 244//
 245// Description:
 246//   Rotates children around an arbitrary axis by the given number of degrees.
 247//   Can be used as a drop-in replacement for `rotate()`, with extra features.
 248//
 249// Usage:
 250//   rot(a, [cp], [reverse]) ...
 251//   rot([X,Y,Z], [cp], [reverse]) ...
 252//   rot(a, v, [cp], [reverse]) ...
 253//   rot(from, to, [a], [reverse]) ...
 254//
 255// Arguments:
 256//   a = Scalar angle or vector of XYZ rotation angles to rotate by, in degrees.
 257//   v = vector for the axis of rotation.  Default: [0,0,1] or V_UP
 258//   cp = centerpoint to rotate around. Default: [0,0,0]
 259//   from = Starting vector for vector-based rotations.
 260//   to = Target vector for vector-based rotations.
 261//   reverse = If true, exactly reverses the rotation, including axis rotation ordering.  Default: false
 262//
 263// Example:
 264//   #cube([2,4,9]);
 265//   rot([30,60,0], cp=[0,0,9]) cube([2,4,9]);
 266//
 267// Example:
 268//   #cube([2,4,9]);
 269//   rot(30, v=[1,1,0], cp=[0,0,9]) cube([2,4,9]);
 270//
 271// Example:
 272//   #cube([2,4,9]);
 273//   rot(from=V_UP, to=V_LEFT+V_BACK) cube([2,4,9]);
 274module rot(a=0, v=undef, cp=undef, from=undef, to=undef, reverse=false)
 275{
 276	if (is_def(cp)) {
 277		translate(cp) rot(a=a, v=v, from=from, to=to, reverse=reverse) translate(-cp) children();
 278	} else if (is_def(from)) {
 279		assertion(is_def(to), "`from` and `to` should be used together.");
 280		axis = vector_axis(from, to);
 281		ang = vector_angle(from, to);
 282		if (ang < 0.0001 && a == 0) {
 283			children();  // May be slightly faster?
 284		} else if (reverse) {
 285			rotate(a=-ang, v=axis) rotate(a=-a, v=from) children();
 286		} else {
 287			rotate(a=ang, v=axis) rotate(a=a, v=from) children();
 288		}
 289	} else if (a == 0) {
 290		children();  // May be slightly faster?
 291	} else if (reverse) {
 292		if (is_def(v)) {
 293			rotate(a=-a, v=v) children();
 294		} else if (is_scalar(a)) {
 295			rotate(-a) children();
 296		} else {
 297			rotate([-a[0],0,0]) rotate([0,-a[1],0]) rotate([0,0,-a[2]]) children();
 298		}
 299	} else {
 300		rotate(a=a, v=v) children();
 301	}
 302}
 303
 304
 305// Module: xrot()
 306//
 307// Description:
 308//   Rotates children around the X axis by the given number of degrees.
 309//
 310// Usage:
 311//   xrot(a, [cp]) ...
 312//
 313// Arguments:
 314//   a = angle to rotate by in degrees.
 315//   cp = centerpoint to rotate around. Default: [0,0,0]
 316//
 317// Example:
 318//   #cylinder(h=50, r=10, center=true);
 319//   xrot(90) cylinder(h=50, r=10, center=true);
 320module xrot(a=0, cp=undef)
 321{
 322	if (a==0) {
 323		children();  // May be slightly faster?
 324	} else if (is_def(cp)) {
 325		translate(cp) rotate([a, 0, 0]) translate(-cp) children();
 326	} else {
 327		rotate([a, 0, 0]) children();
 328	}
 329}
 330
 331
 332// Module: yrot()
 333//
 334// Description:
 335//   Rotates children around the Y axis by the given number of degrees.
 336//
 337// Usage:
 338//   yrot(a, [cp]) ...
 339//
 340// Arguments:
 341//   a = angle to rotate by in degrees.
 342//   cp = centerpoint to rotate around. Default: [0,0,0]
 343//
 344// Example:
 345//   #cylinder(h=50, r=10, center=true);
 346//   yrot(90) cylinder(h=50, r=10, center=true);
 347module yrot(a=0, cp=undef)
 348{
 349	if (a==0) {
 350		children();  // May be slightly faster?
 351	} else if (is_def(cp)) {
 352		translate(cp) rotate([0, a, 0]) translate(-cp) children();
 353	} else {
 354		rotate([0, a, 0]) children();
 355	}
 356}
 357
 358
 359// Module: zrot()
 360//
 361// Description:
 362//   Rotates children around the Z axis by the given number of degrees.
 363//
 364// Usage:
 365//   zrot(a, [cp]) ...
 366//
 367// Arguments:
 368//   a = angle to rotate by in degrees.
 369//   cp = centerpoint to rotate around. Default: [0,0,0]
 370//
 371// Example:
 372//   #cube(size=[60,20,40], center=true);
 373//   zrot(90) cube(size=[60,20,40], center=true);
 374module zrot(a=0, cp=undef)
 375{
 376	if (a==0) {
 377		children();  // May be slightly faster?
 378	} else if (is_def(cp)) {
 379		translate(cp) rotate(a) translate(-cp) children();
 380	} else {
 381		rotate(a) children();
 382	}
 383}
 384
 385
 386
 387//////////////////////////////////////////////////////////////////////
 388// Section: Scaling and Mirroring
 389//////////////////////////////////////////////////////////////////////
 390
 391
 392// Module: xscale()
 393//
 394// Description:
 395//   Scales children by the given factor on the X axis.
 396//
 397// Usage:
 398//   xscale(x) ...
 399//
 400// Arguments:
 401//   x = Factor to scale by along the X axis.
 402//
 403// Example:
 404//   xscale(3) sphere(r=10);
 405module xscale(x) scale([x,1,1]) children();
 406
 407
 408// Module: yscale()
 409//
 410// Description:
 411//   Scales children by the given factor on the Y axis.
 412//
 413// Usage:
 414//   yscale(y) ...
 415//
 416// Arguments:
 417//   y = Factor to scale by along the Y axis.
 418//
 419// Example:
 420//   yscale(3) sphere(r=10);
 421module yscale(y) scale([1,y,1]) children();
 422
 423
 424// Module: zscale()
 425//
 426// Description:
 427//   Scales children by the given factor on the Z axis.
 428//
 429// Usage:
 430//   zscale(z) ...
 431//
 432// Arguments:
 433//   z = Factor to scale by along the Z axis.
 434//
 435// Example:
 436//   zscale(3) sphere(r=10);
 437module zscale(z) scale([1,1,z]) children();
 438
 439
 440// Module: xflip()
 441//
 442// Description:
 443//   Mirrors the children along the X axis, like `mirror([1,0,0])` or `xscale(-1)`
 444//
 445// Usage:
 446//   xflip([cp]) ...
 447//
 448// Arguments:
 449//   cp = A point that lies on the plane of reflection.
 450//
 451// Example:
 452//   xflip() yrot(90) cylinder(d1=10, d2=0, h=20);
 453//   color("blue", 0.25) cube([0.01,15,15], center=true);
 454//   color("red", 0.333) yrot(90) cylinder(d1=10, d2=0, h=20);
 455//
 456// Example:
 457//   xflip(cp=[-5,0,0]) yrot(90) cylinder(d1=10, d2=0, h=20);
 458//   color("blue", 0.25) left(5) cube([0.01,15,15], center=true);
 459//   color("red", 0.333) yrot(90) cylinder(d1=10, d2=0, h=20);
 460module xflip(cp=[0,0,0]) translate(cp) mirror([1,0,0]) translate(-cp) children();
 461
 462
 463// Module: yflip()
 464//
 465// Description:
 466//   Mirrors the children along the Y axis, like `mirror([0,1,0])` or `yscale(-1)`
 467//
 468// Usage:
 469//   yflip([cp]) ...
 470//
 471// Arguments:
 472//   cp = A point that lies on the plane of reflection.
 473//
 474// Example:
 475//   yflip() xrot(90) cylinder(d1=10, d2=0, h=20);
 476//   color("blue", 0.25) cube([15,0.01,15], center=true);
 477//   color("red", 0.333) xrot(90) cylinder(d1=10, d2=0, h=20);
 478//
 479// Example:
 480//   yflip(cp=[0,5,0]) xrot(90) cylinder(d1=10, d2=0, h=20);
 481//   color("blue", 0.25) back(5) cube([15,0.01,15], center=true);
 482//   color("red", 0.333) xrot(90) cylinder(d1=10, d2=0, h=20);
 483module yflip(cp=[0,0,0]) translate(cp) mirror([0,1,0]) translate(-cp) children();
 484
 485
 486// Module: zflip()
 487//
 488// Description:
 489//   Mirrors the children along the Z axis, like `mirror([0,0,1])` or `zscale(-1)`
 490//
 491// Usage:
 492//   zflip([cp]) ...
 493//
 494// Arguments:
 495//   cp = A point that lies on the plane of reflection.
 496//
 497// Example:
 498//   zflip() cylinder(d1=10, d2=0, h=20);
 499//   color("blue", 0.25) cube([15,15,0.01], center=true);
 500//   color("red", 0.333) cylinder(d1=10, d2=0, h=20);
 501//
 502// Example:
 503//   zflip(cp=[0,0,-5]) cylinder(d1=10, d2=0, h=20);
 504//   color("blue", 0.25) down(5) cube([15,15,0.01], center=true);
 505//   color("red", 0.333) cylinder(d1=10, d2=0, h=20);
 506module zflip(cp=[0,0,0]) translate(cp) mirror([0,0,1]) translate(-cp) children();
 507
 508
 509
 510//////////////////////////////////////////////////////////////////////
 511// Section: Skewing
 512//////////////////////////////////////////////////////////////////////
 513
 514
 515// Module: skew_xy() / skew_z()
 516//
 517// Description:
 518//   Skews children on the X-Y plane, keeping constant in Z.
 519//
 520// Usage:
 521//   skew_xy([xa], [ya]) ...
 522//   skew_z([xa], [ya]) ...
 523//
 524// Arguments:
 525//   xa = skew angle towards the X direction.
 526//   ya = skew angle towards the Y direction.
 527//   planar = If true, this becomes a 2D operation.
 528//
 529// Example(FlatSpin):
 530//   #cube(size=10);
 531//   skew_xy(xa=30, ya=15) cube(size=10);
 532// Example(2D):
 533//   skew_xy(xa=15,ya=30,planar=true) square(30);
 534module skew_xy(xa=0, ya=0, planar=false) multmatrix(m = planar? matrix3_skew(xa, ya) : matrix4_skew_xy(xa, ya)) children();
 535module zskew(xa=0, ya=0, planar=false) multmatrix(m = planar? matrix3_skew(xa, ya) : matrix4_skew_xy(xa, ya)) children();
 536
 537
 538// Module: skew_yz() / skew_x()
 539//
 540// Description:
 541//   Skews children on the Y-Z plane, keeping constant in X.
 542//
 543// Usage:
 544//   skew_yz([ya], [za]) ...
 545//   skew_x([ya], [za]) ...
 546//
 547// Arguments:
 548//   ya = skew angle towards the Y direction.
 549//   za = skew angle towards the Z direction.
 550//
 551// Example(FlatSpin):
 552//   #cube(size=10);
 553//   skew_yz(ya=30, za=15) cube(size=10);
 554module skew_yz(ya=0, za=0) multmatrix(m = matrix4_skew_yz(ya, za)) children();
 555module xskew(ya=0, za=0) multmatrix(m = matrix4_skew_yz(ya, za)) children();
 556
 557
 558// Module: skew_xz() / skew_y()
 559//
 560// Description:
 561//   Skews children on the X-Z plane, keeping constant in Y.
 562//
 563// Usage:
 564//   skew_xz([xa], [za]) ...
 565//   skew_y([xa], [za]) ...
 566//
 567// Arguments:
 568//   xa = skew angle towards the X direction.
 569//   za = skew angle towards the Z direction.
 570//
 571// Example(FlatSpin):
 572//   #cube(size=10);
 573//   skew_xz(xa=15, za=-10) cube(size=10);
 574module skew_xz(xa=0, za=0) multmatrix(m = matrix4_skew_xz(xa, za)) children();
 575module yskew(xa=0, za=0) multmatrix(m = matrix4_skew_xz(xa, za)) children();
 576
 577
 578
 579//////////////////////////////////////////////////////////////////////
 580// Section: Translational Distributors
 581//////////////////////////////////////////////////////////////////////
 582
 583
 584// Module: place_copies()
 585//
 586// Description:
 587//   Makes copies of the given children at each of the given offsets.
 588//
 589// Usage:
 590//   place_copies(a) ...
 591//
 592// Arguments:
 593//   a = array of XYZ offset vectors. Default [[0,0,0]]
 594//
 595// Side Effects:
 596//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 597//
 598// Example:
 599//   #sphere(r=10);
 600//   place_copies([[-25,-25,0], [25,-25,0], [0,0,50], [0,25,0]]) sphere(r=10);
 601module place_copies(a=[[0,0,0]])
 602{
 603	for (off = a) translate(off) children();
 604}
 605
 606
 607// Module: translate_copies()
 608// Status: DEPRECATED, use `place_copies()` instead.
 609//
 610// Description:
 611//   Makes copies of the given children at each of the given offsets.
 612//
 613// Usage:
 614//   translate_copies(a) ...
 615//
 616// Arguments:
 617//   a = array of XYZ offset vectors. Default [[0,0,0]]
 618//
 619// Side Effects:
 620//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 621module translate_copies(a=[[0,0,0]])
 622{
 623	deprecate("translate_copies()", "place_copies()");
 624	place_copies(a) children();
 625}
 626
 627
 628// Module: line_of()
 629// Status: DEPRECATED, use `spread(p1,p2)` instead
 630//
 631// Description:
 632//   Evenly distributes n duplicate children along an XYZ line.
 633//
 634// Usage:
 635//   line_of(p1, p2, [n]) ...
 636//
 637// Arguments:
 638//   p1 = starting point of line.  (Default: [0,0,0])
 639//   p2 = ending point of line.  (Default: [10,0,0])
 640//   n = number of copies to distribute along the line. (Default: 2)
 641//
 642// Side Effects:
 643//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 644module line_of(p1=[0,0,0], p2=[10,0,0], n=2)
 645{
 646	deprecate("line_of()", "spread()");
 647	spread(p1=p1, p2=p2, n=n) children();
 648}
 649
 650
 651
 652// Module: spread()
 653//
 654// Description:
 655//   Evenly distributes `n` copies of all children along a line.
 656//   Copies every child at each position.
 657//
 658// Usage:
 659//   spread(l, [n], [p1]) ...
 660//   spread(l, spacing, [p1]) ...
 661//   spread(spacing, [n], [p1]) ...
 662//   spread(p1, p2, [n]) ...
 663//   spread(p1, p2, spacing) ...
 664//
 665// Arguments:
 666//   p1 = Starting point of line.
 667//   p2 = Ending point of line.
 668//   l = Length to spread copies over.
 669//   spacing = A 3D vector indicating which direction and distance to place each subsequent copy at.
 670//   n = Number of copies to distribute along the line. (Default: 2)
 671//
 672// Side Effects:
 673//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 674//   `$idx` is set to the index number of each child being copied.
 675//
 676// Example(FlatSpin):
 677//   spread([0,0,0], [5,5,20], n=6) cube(size=[3,2,1],center=true);
 678// Examples:
 679//   spread(l=40, n=6) cube(size=[3,2,1],center=true);
 680//   spread(l=[15,30], n=6) cube(size=[3,2,1],center=true);
 681//   spread(l=40, spacing=10) cube(size=[3,2,1],center=true);
 682//   spread(spacing=[5,5,0], n=5) cube(size=[3,2,1],center=true);
 683// Example:
 684//   spread(l=20, n=3) {
 685//       cube(size=[1,3,1],center=true);
 686//       cube(size=[3,1,1],center=true);
 687//   }
 688module spread(p1, p2, spacing, l, n)
 689{
 690	ll = (
 691		is_def(l)? scalar_vec3(l, 0) :
 692		(is_def(spacing) && is_def(n))? (n * scalar_vec3(spacing, 0)) :
 693		(is_def(p1) && is_def(p2))? point3d(p2-p1) :
 694		undef
 695	);
 696	cnt = (
 697		is_def(n)? n :
 698		(is_def(spacing) && is_def(ll))? floor(norm(ll) / norm(scalar_vec3(spacing, 0)) + 1.000001) :
 699		2
 700	);
 701	spc = (
 702		!is_def(spacing)? (ll/(cnt-1)) :
 703		is_scalar(spacing) && is_def(ll)? (ll/(cnt-1)) :
 704		scalar_vec3(spacing, 0)
 705	);
 706	assertion(is_def(cnt), "Need two of `spacing`, 'l', 'n', or `p1`/`p2` arguments in `spread()`.");
 707	spos = is_def(p1)? point3d(p1) : -(cnt-1)/2 * spc;
 708	for (i=[0 : cnt-1]) {
 709		pos = i * spc + spos;
 710		$pos = pos;
 711		$idx = i;
 712		translate(pos) children();
 713	}
 714}
 715
 716
 717// Module: xspread()
 718//
 719// Description:
 720//   Spreads out `n` copies of the children along a line on the X axis.
 721//
 722// Usage:
 723//   xspread(spacing, [n], [sp]) ...
 724//   xspread(l, [n], [sp]) ...
 725//
 726// Arguments:
 727//   spacing = spacing between copies. (Default: 1.0)
 728//   n = Number of copies to spread out. (Default: 2)
 729//   l = Length to spread copies over.
 730//   sp = If given, copies will be spread on a line to the right of starting position `sp`.  If not given, copies will be spread along a line that is centered at [0,0,0].
 731//
 732// Side Effects:
 733//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 734//   `$idx` is set to the index number of each child being copied.
 735//
 736// Examples:
 737//   xspread(20) sphere(3);
 738//   xspread(20, n=3) sphere(3);
 739//   xspread(spacing=15, l=50) sphere(3);
 740//   xspread(n=4, l=30, sp=[0,10,0]) sphere(3);
 741// Example:
 742//   xspread(10, n=3) {
 743//       cube(size=[1,3,1],center=true);
 744//       cube(size=[3,1,1],center=true);
 745//   }
 746module xspread(spacing=undef, n=undef, l=undef, sp=undef)
 747{
 748	spread(
 749		l=is_undef(l)? l : l*V_RIGHT,
 750		spacing=is_undef(spacing)? spacing : spacing*V_RIGHT,
 751		n=n, p1=sp
 752	) children();
 753}
 754
 755
 756// Module: yspread()
 757//
 758// Description:
 759//   Spreads out `n` copies of the children along a line on the Y axis.
 760//
 761// Usage:
 762//   yspread(spacing, [n], [sp]) ...
 763//   yspread(l, [n], [sp]) ...
 764//
 765// Arguments:
 766//   spacing = spacing between copies. (Default: 1.0)
 767//   n = Number of copies to spread out. (Default: 2)
 768//   l = Length to spread copies over.
 769//   sp = If given, copies will be spread on a line back from starting position `sp`.  If not given, copies will be spread along a line that is centered at [0,0,0].
 770//
 771// Side Effects:
 772//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 773//   `$idx` is set to the index number of each child being copied.
 774//
 775// Examples:
 776//   yspread(20) sphere(3);
 777//   yspread(20, n=3) sphere(3);
 778//   yspread(spacing=15, l=50) sphere(3);
 779//   yspread(n=4, l=30, sp=[10,0,0]) sphere(3);
 780// Example:
 781//   yspread(10, n=3) {
 782//       cube(size=[1,3,1],center=true);
 783//       cube(size=[3,1,1],center=true);
 784//   }
 785module yspread(spacing=undef, n=undef, l=undef, sp=undef)
 786{
 787	spread(
 788		l=is_undef(l)? l : l*V_BACK,
 789		spacing=is_undef(spacing)? spacing : spacing*V_BACK,
 790		n=n, p1=sp
 791	) children();
 792}
 793
 794
 795// Module: zspread()
 796//
 797// Description:
 798//   Spreads out `n` copies of the children along a line on the Z axis.
 799//
 800// Usage:
 801//   zspread(spacing, [n], [sp]) ...
 802//   zspread(l, [n], [sp]) ...
 803//
 804// Arguments:
 805//   spacing = spacing between copies. (Default: 1.0)
 806//   n = Number of copies to spread out. (Default: 2)
 807//   l = Length to spread copies over.
 808//   sp = If given, copies will be spread on a line up from starting position `sp`.  If not given, copies will be spread along a line that is centered at [0,0,0].
 809//
 810// Side Effects:
 811//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 812//   `$idx` is set to the index number of each child being copied.
 813//
 814// Examples:
 815//   zspread(20) sphere(3);
 816//   zspread(20, n=3) sphere(3);
 817//   zspread(spacing=15, l=50) sphere(3);
 818//   zspread(n=4, l=30, sp=[10,0,0]) sphere(3);
 819// Example:
 820//   zspread(10, n=3) {
 821//       cube(size=[1,3,1],center=true);
 822//       cube(size=[3,1,1],center=true);
 823//   }
 824module zspread(spacing=undef, n=undef, l=undef, sp=undef)
 825{
 826	spread(
 827		l=is_undef(l)? l : l*V_UP,
 828		spacing=is_undef(spacing)? spacing : spacing*V_UP,
 829		n=n, p1=sp
 830	) children();
 831}
 832
 833
 834
 835// Module: distribute()
 836//
 837// Description:
 838//   Spreads out each individual child along the direction `dir`.
 839//   Every child is placed at a different position, in order.
 840//   This is useful for laying out groups of disparate objects
 841//   where you only really care about the spacing between them.
 842//
 843// Usage:
 844//   distribute(spacing, dir, [sizes]) ...
 845//   distribute(l, dir, [sizes]) ...
 846//
 847// Arguments:
 848//   spacing = Spacing to add between each child. (Default: 10.0)
 849//   sizes = Array containing how much space each child will need.
 850//   dir = Vector direction to distribute copies along.
 851//   l = Length to distribute copies along.
 852//
 853// Side Effect:
 854//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 855//   `$idx` is set to the index number of each child being copied.
 856//
 857// Example:
 858//   distribute(sizes=[100, 30, 50], dir=V_UP) {
 859//       sphere(r=50);
 860//       cube([10,20,30], center=true);
 861//       cylinder(d=30, h=50, center=true);
 862//   }
 863module distribute(spacing=undef, sizes=undef, dir=V_RIGHT, l=undef)
 864{
 865	gaps = ($children < 2)? [0] :
 866		is_def(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
 867		[for (i=[0:$children-2]) 0];
 868	spc = is_def(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
 869	gaps2 = [for (gap = gaps) gap+spc];
 870	spos = dir * -sum(gaps2)/2;
 871	for (i=[0:$children-1]) {
 872		totspc = sum(concat([0], slice(gaps2, 0, i)));
 873		$pos = spos + totspc * dir;
 874		$idx = i;
 875		translate($pos) children(i);
 876	}
 877}
 878
 879
 880// Module: xdistribute()
 881//
 882// Description:
 883//   Spreads out each individual child along the X axis.
 884//   Every child is placed at a different position, in order.
 885//   This is useful for laying out groups of disparate objects
 886//   where you only really care about the spacing between them.
 887//
 888// Usage:
 889//   xdistribute(spacing, [sizes]) ...
 890//   xdistribute(l, [sizes]) ...
 891//
 892// Arguments:
 893//   spacing = spacing between each child. (Default: 10.0)
 894//   sizes = Array containing how much space each child will need.
 895//   l = Length to distribute copies along.
 896//
 897// Side Effect:
 898//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 899//   `$idx` is set to the index number of each child being copied.
 900//
 901// Example:
 902//   xdistribute(sizes=[100, 10, 30], spacing=40) {
 903//       sphere(r=50);
 904//       cube([10,20,30], center=true);
 905//       cylinder(d=30, h=50, center=true);
 906//   }
 907module xdistribute(spacing=10, sizes=undef, l=undef)
 908{
 909	dir = V_RIGHT;
 910	gaps = ($children < 2)? [0] :
 911		is_def(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
 912		[for (i=[0:$children-2]) 0];
 913	spc = is_def(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
 914	gaps2 = [for (gap = gaps) gap+spc];
 915	spos = dir * -sum(gaps2)/2;
 916	for (i=[0:$children-1]) {
 917		totspc = sum(concat([0], slice(gaps2, 0, i)));
 918		$pos = spos + totspc * dir;
 919		$idx = i;
 920		translate($pos) children(i);
 921	}
 922}
 923
 924
 925// Module: ydistribute()
 926//
 927// Description:
 928//   Spreads out each individual child along the Y axis.
 929//   Every child is placed at a different position, in order.
 930//   This is useful for laying out groups of disparate objects
 931//   where you only really care about the spacing between them.
 932//
 933// Usage:
 934//   ydistribute(spacing, [sizes])
 935//   ydistribute(l, [sizes])
 936//
 937// Arguments:
 938//   spacing = spacing between each child. (Default: 10.0)
 939//   sizes = Array containing how much space each child will need.
 940//   l = Length to distribute copies along.
 941//
 942// Side Effect:
 943//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 944//   `$idx` is set to the index number of each child being copied.
 945//
 946// Example:
 947//   ydistribute(sizes=[30, 20, 100], spacing=40) {
 948//       cylinder(d=30, h=50, center=true);
 949//       cube([10,20,30], center=true);
 950//       sphere(r=50);
 951//   }
 952module ydistribute(spacing=10, sizes=undef, l=undef)
 953{
 954	dir = V_BACK;
 955	gaps = ($children < 2)? [0] :
 956		is_def(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
 957		[for (i=[0:$children-2]) 0];
 958	spc = is_def(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
 959	gaps2 = [for (gap = gaps) gap+spc];
 960	spos = dir * -sum(gaps2)/2;
 961	for (i=[0:$children-1]) {
 962		totspc = sum(concat([0], slice(gaps2, 0, i)));
 963		$pos = spos + totspc * dir;
 964		$idx = i;
 965		translate($pos) children(i);
 966	}
 967}
 968
 969
 970// Module: zdistribute()
 971//
 972// Description:
 973//   Spreads out each individual child along the Z axis.
 974//   Every child is placed at a different position, in order.
 975//   This is useful for laying out groups of disparate objects
 976//   where you only really care about the spacing between them.
 977//
 978// Usage:
 979//   zdistribute(spacing, [sizes])
 980//   zdistribute(l, [sizes])
 981//
 982// Arguments:
 983//   spacing = spacing between each child. (Default: 10.0)
 984//   sizes = Array containing how much space each child will need.
 985//   l = Length to distribute copies along.
 986//
 987// Side Effect:
 988//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 989//   `$idx` is set to the index number of each child being copied.
 990//
 991// Example:
 992//   zdistribute(sizes=[30, 20, 100], spacing=40) {
 993//       cylinder(d=30, h=50, center=true);
 994//       cube([10,20,30], center=true);
 995//       sphere(r=50);
 996//   }
 997module zdistribute(spacing=10, sizes=undef, l=undef)
 998{
 999	dir = V_UP;
1000	gaps = ($children < 2)? [0] :
1001		is_def(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
1002		[for (i=[0:$children-2]) 0];
1003	spc = is_def(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
1004	gaps2 = [for (gap = gaps) gap+spc];
1005	spos = dir * -sum(gaps2)/2;
1006	for (i=[0:$children-1]) {
1007		totspc = sum(concat([0], slice(gaps2, 0, i)));
1008		$pos = spos + totspc * dir;
1009		$idx = i;
1010		translate($pos) children(i);
1011	}
1012}
1013
1014
1015
1016// Module: grid2d()
1017//
1018// Description:
1019//   Makes a square or hexagonal grid of copies of children.
1020//
1021// Usage:
1022//   grid2d(size, spacing, [stagger], [scale], [in_poly], [orient], [align]) ...
1023//   grid2d(size, cols, rows, [stagger], [scale], [in_poly], [orient], [align]) ...
1024//   grid2d(spacing, cols, rows, [stagger], [scale], [in_poly], [orient], [align]) ...
1025//   grid2d(spacing, in_poly, [stagger], [scale], [orient], [align]) ...
1026//   grid2d(cols, rows, in_poly, [stagger], [scale], [orient], [align]) ...
1027//
1028// Arguments:
1029//   size = The [X,Y] size to spread the copies over.
1030//   spacing = Distance between copies in [X,Y] or scalar distance.
1031//   cols = How many columns of copies to make.  If staggered, count both staggered and unstaggered columns.
1032//   rows = How many rows of copies to make.  If staggered, count both staggered and unstaggered rows.
1033//   stagger = If true, make a staggered (hexagonal) grid.  If false, make square grid.  If "alt", makes alternate staggered pattern.  Default: false
1034//   scale = [X,Y] scaling factors to reshape grid.
1035//   in_poly = If given a list of polygon points, only creates copies whose center would be inside the polygon.  Polygon can be concave and/or self crossing.
1036//   orient = Orientation axis for the grid.  Orientation is NOT applied to individual children.
1037//   align = Alignment of the grid.  Alignment is NOT applies to individual children.
1038//
1039// Side Effects:
1040//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
1041//   `$col` is set to the integer column number for each child.
1042//   `$row` is set to the integer row number for each child.
1043//
1044// Examples:
1045//   grid2d(size=50, spacing=10, stagger=false) cylinder(d=10, h=1);
1046//   grid2d(spacing=10, rows=7, cols=13, stagger=true) cylinder(d=6, h=5);
1047//   grid2d(spacing=10, rows=7, cols=13, stagger="alt") cylinder(d=6, h=5);
1048//   grid2d(size=50, rows=11, cols=11, stagger=true) cylinder(d=5, h=1);
1049//
1050// Example:
1051//   poly = [[-25,-25], [25,25], [-25,25], [25,-25]];
1052//   grid2d(spacing=5, stagger=true, in_poly=poly)
1053//      zrot(180/6) cylinder(d=5, h=1, $fn=6);
1054//   %polygon(poly);
1055//
1056// Example:
1057//   // Makes a grid of hexagon pillars whose tops are all angled
1058//   // to reflect light at [0,0,50], if they were reflective.
1059//   use <BOSL/math.scad>
1060//   hexregion = [for (a = [0:60:359.9]) 50.01*[cos(a), sin(a)]];
1061//   grid2d(spacing=10, stagger=true, in_poly=hexregion) {
1062//       // Note: You must use for(var=[val]) or let(var=val)
1063//       // to set vars from $pos or other special vars in this scope.
1064//       let (ref_v = (normalize([0,0,50]-point3d($pos)) + V_UP)/2)
1065//           half_of(v=-ref_v, cp=[0,0,5])
1066//               zrot(180/6)
1067//                   cylinder(h=20, d=10/cos(180/6)+0.01, $fn=6);
1068//   }
1069module grid2d(size=undef, spacing=undef, cols=undef, rows=undef, stagger=false, scale=[1,1,1], in_poly=undef, orient=ORIENT_Z, align=V_CENTER)
1070{
1071	assert_in_list("stagger", stagger, [false, true, "alt"]);
1072	scl = vmul(scalar_vec3(scale, 1), (stagger!=false? [0.5, sin(60), 0] : [1,1,0]));
1073	if (is_def(size)) {
1074		siz = scalar_vec3(size);
1075		if (is_def(spacing)) {
1076			spc = vmul(scalar_vec3(spacing), scl);
1077			maxcols = ceil(siz[0]/spc[0]);
1078			maxrows = ceil(siz[1]/spc[1]);
1079			grid2d(spacing=spacing, cols=maxcols, rows=maxrows, stagger=stagger, scale=scale, in_poly=in_poly, orient=orient, align=align) children();
1080		} else {
1081			spc = [siz[0]/cols, siz[1]/rows, 0];
1082			grid2d(spacing=spc, cols=cols, rows=rows, stagger=stagger, scale=scale, in_poly=in_poly, orient=orient, align=align) children();
1083		}
1084	} else {
1085		spc = is_array(spacing)? spacing : vmul(scalar_vec3(spacing), scl);
1086		bounds = is_def(in_poly)? pointlist_bounds(in_poly) : undef;
1087		bnds = is_def(bounds)? [for (a=[0:1]) 2*max(vabs([ for (i=[0,1]) bounds[i][a] ]))+1 ] : undef;
1088		mcols = is_def(cols)? cols : (is_def(spc) && is_def(bnds))? quantup(ceil(bnds[0]/spc[0])-1, 4)+1 : undef;
1089		mrows = is_def(rows)? rows : (is_def(spc) && is_def(bnds))? quantup(ceil(bnds[1]/spc[1])-1, 4)+1 : undef;
1090		siz = vmul(spc, [mcols-1, mrows-1, 0]);
1091		staggermod = (stagger == "alt")? 1 : 0;
1092		if (stagger == false) {
1093			orient_and_align(siz, orient, align) {
1094				for (row = [0:mrows-1]) {
1095					for (col = [0:mcols-1]) {
1096						pos = [col*spc[0], row*spc[1]] - point2d(siz/2);
1097						if (!is_def(in_poly) || point_in_polygon(pos, in_poly)>=0) {
1098							$col = col;
1099							$row = row;
1100							$pos = pos;
1101							translate(pos) rot(orient,reverse=true) children();
1102						}
1103					}
1104				}
1105			}
1106		} else {
1107			// stagger == true or stagger == "alt"
1108			orient_and_align(siz, orient, align) {
1109				cols1 = ceil(mcols/2);
1110				cols2 = mcols - cols1;
1111				for (row = [0:mrows-1]) {
1112					rowcols = ((row%2) == staggermod)? cols1 : cols2;
1113					if (rowcols > 0) {
1114						for (col = [0:rowcols-1]) {
1115							rowdx = (row%2 != staggermod)? spc[0] : 0;
1116							pos = [2*col*spc[0]+rowdx, row*spc[1]] - point2d(siz/2);
1117							if (!is_def(in_poly) || point_in_polygon(pos, in_poly)>=0) {
1118								$col = col * 2 + ((row%2!=staggermod)? 1 : 0);
1119								$row = row;
1120								$pos = pos;
1121								translate(pos) rot(orient,reverse=true) children();
1122							}
1123						}
1124					}
1125				}
1126			}
1127		}
1128	}
1129}
1130
1131
1132
1133// Module: grid3d()
1134//
1135// Description:
1136//   Makes a 3D grid of duplicate children.
1137//
1138// Usage:
1139//   grid3d(n, spacing) ...
1140//   grid3d(n=[Xn,Yn,Zn], spacing=[dX,dY,dZ]) ...
1141//   grid3d([xa], [ya], [za]) ...
1142//
1143// Arguments:
1144//   xa = array or range of X-axis values to offset by. (Default: [0])
1145//   ya = array or range of Y-axis values to offset by. (Default: [0])
1146//   za = array or range of Z-axis values to offset by. (Default: [0])
1147//   n = Optional number of copies to have per axis.
1148//   spacing = spacing of copies per axis. Use with `n`.
1149//
1150// Side Effect:
1151//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
1152//   `$idx` is set to the [Xidx,Yidx,Zidx] index values of each child copy, when using `count` and `n`.
1153//
1154// Examples(FlatSpin):
1155//   grid3d(xa=[0:25:50],ya=[0,40],za=[-20:40:20]) sphere(r=5);
1156//   grid3d(n=[3, 4, 2], spacing=[60, 50, 40]) sphere(r=10);
1157// Examples:
1158//   grid3d(ya=[-60:40:60],za=[0,70]) sphere(r=10);
1159//   grid3d(n=3, spacing=30) sphere(r=10);
1160//   grid3d(n=[3, 1, 2], spacing=30) sphere(r=10);
1161//   grid3d(n=[3, 4], spacing=[80, 60]) sphere(r=10);
1162// Examples:
1163//   grid3d(n=[10, 10, 10], spacing=50) color($idx/9) cube(50, center=true);
1164module grid3d(xa=[0], ya=[0], za=[0], n=undef, spacing=undef)
1165{
1166	n = scalar_vec3(n, 1);
1167	spacing = scalar_vec3(spacing, undef);
1168	if (is_def(n) && is_def(spacing)) {
1169		for (xi = [0:n.x-1]) {
1170			for (yi = [0:n.y-1]) {
1171				for (zi = [0:n.z-1]) {
1172					$idx = [xi,yi,zi];
1173					$pos = vmul(spacing, $idx - (n-[1,1,1])/2);
1174					translate($pos) children();
1175				}
1176			}
1177		}
1178	} else {
1179		for (xoff = xa, yoff = ya, zoff = za) {
1180			$pos = [xoff, yoff, zoff];
1181			translate($pos) children();
1182		}
1183	}
1184}
1185
1186
1187
1188// Module: grid_of()
1189// Status: DEPRECATED, use `grid3d()` instead.
1190//
1191// Description:
1192//   Makes a 3D grid of duplicate children.
1193//
1194// Usage:
1195//   grid_of(n, spacing) ...
1196//   grid_of(n=[Xn,Yn,Zn], spacing=[dX,dY,dZ]) ...
1197//   grid_of([xa], [ya], [za]) ...
1198//
1199// Arguments:
1200//   xa = array or range of X-axis values to offset by. (Default: [0])
1201//   ya = array or range of Y-axis values to offset by. (Default: [0])
1202//   za = array or range of Z-axis values to offset by. (Default: [0])
1203//   n = Optional number of copies to have per axis.
1204//   spacing = spacing of copies per axis. Use with `n`.
1205//
1206// Side Effect:
1207//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
1208//   `$idx` is set to the [Xidx,Yidx,Zidx] index values of each child copy, when using `count` and `n`.
1209module grid_of(xa=[0], ya=[0], za=[0], count=undef, spacing=undef)
1210{
1211	deprecate("grid_of()", "grid3d()");
1212	grid3d(xa=xa, ya=ya, za=za, n=count, spacing=spacing) children();
1213}
1214
1215
1216
1217//////////////////////////////////////////////////////////////////////
1218// Section: Rotational Distributors
1219//////////////////////////////////////////////////////////////////////
1220
1221
1222// Module: rot_copies()
1223//
1224// Description:
1225//   Given a number of XYZ rotation angles, or a list of angles and an axis `v`,
1226//   rotates copies of the children to each of those angles.
1227//
1228// Usage:
1229//   rot_copies(rots, [cp], [sa], [delta], [subrot]) ...
1230//   rot_copies(rots, v, [cp], [sa], [delta], [subrot]) ...
1231//   rot_copies(n, [v], [cp], [sa], [delta], [subrot]) ...
1232//
1233// Arguments:
1234//   rots = A list of [X,Y,Z] rotation angles in degrees.  If `v` is given, this will be a list of scalar angles in degrees to rotate around `v`.
1235//   v = If given, this is the vector to rotate around.
1236//   cp = Centerpoint to rotate around.
1237//   n = Optional number of evenly distributed copies, rotated around the ring.  If given, overrides `rots` argument.
1238//   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise.
1239//   delta = [X,Y,Z] amount to move away from cp before rotating.  Makes rings of copies.
1240//   subrot = If false, don't sub-rotate children as they are copied around the ring.
1241//
1242// Side Effects:
1243//   `$ang` is set to the rotation angle (or XYZ rotation triplet) of each child copy, and can be used to modify each child individually.
1244//   `$idx` is set to the index value of each child copy.
1245//
1246// Example:
1247//   #cylinder(h=20, r1=5, r2=0);
1248//   rot_copies([[45,0,0],[0,45,90],[90,-45,270]]) cylinder(h=20, r1=5, r2=0);
1249//
1250// Example:
1251//   rot_copies([45, 90, 135], v=V_DOWN+V_BACK)
1252//       yrot(90) cylinder(h=20, r1=5, r2=0);
1253//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1254//
1255// Example:
1256//   rot_copies(n=6, v=V_DOWN+V_BACK)
1257//       yrot(90) cylinder(h=20, r1=5, r2=0);
1258//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1259//
1260// Example:
1261//   rot_copies(n=6, v=V_DOWN+V_BACK, delta=[10,0,0])
1262//       yrot(90) cylinder(h=20, r1=5, r2=0);
1263//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1264//
1265// Example:
1266//   rot_copies(n=6, v=V_UP+V_FWD, delta=[10,0,0], sa=45)
1267//       yrot(90) cylinder(h=20, r1=5, r2=0);
1268//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1269//
1270// Example:
1271//   rot_copies(n=6, v=V_DOWN+V_BACK, delta=[20,0,0], subrot=false)
1272//       yrot(90) cylinder(h=20, r1=5, r2=0);
1273//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1274module rot_copies(rots=[], v=undef, cp=[0,0,0], count=undef, n=undef, sa=0, offset=0, delta=[0,0,0], subrot=true)
1275{
1276	cnt = first_defined([count, n]);
1277	sang = sa + offset;
1278	angs = is_def(cnt)? (cnt<=0? [] : [for (i=[0:cnt-1]) i/cnt*360+sang]) : rots;
1279	if (cp != [0,0,0]) {
1280		translate(cp) rot_copies(rots=rots, v=v, n=cnt, sa=sang, delta=delta, subrot=subrot) children();
1281	} else if (subrot) {
1282		for ($idx = [0:len(angs)-1]) {
1283			$ang = angs[$idx];
1284			rotate(a=$ang,v=v) translate(delta) rot(a=sang,v=v,reverse=true) children();
1285		}
1286	} else {
1287		for ($idx = [0:len(angs)-1]) {
1288			$ang = angs[$idx];
1289			rotate(a=$ang,v=v) translate(delta) rot(a=$ang,v=v,reverse=true) children();
1290		}
1291	}
1292}
1293
1294
1295// Module: xrot_copies()
1296//
1297// Description:
1298//   Given an array of angles, rotates copies of the children
1299//   to each of those angles around the X axis.
1300//
1301// Usage:
1302//   xrot_copies(rots, [r], [cp], [sa], [subrot]) ...
1303//   xrot_copies(n, [r], [cp], [sa], [subrot]) ...
1304//
1305// Arguments:
1306//   rots = Optional array of rotation angles, in degrees, to make copies at.
1307//   cp = Centerpoint to rotate around.
1308//   n = Optional number of evenly distributed copies to be rotated around the ring.  If given, overrides `rots` argument.
1309//   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise from Y+, when facing the origin from X+.  First unrotated copy is placed at that angle.
1310//   r = Radius to move children back, away from cp, before rotating.  Makes rings of copies.
1311//   subrot = If false, don't sub-rotate children as they are copied around the ring.
1312//
1313// Side Effects:
1314//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
1315//
1316// Example:
1317//   xrot_copies([180, 270, 315])
1318//       cylinder(h=20, r1=5, r2=0);
1319//   color("red",0.333) cylinder(h=20, r1=5, r2=0);
1320//
1321// Example:
1322//   xrot_copies(n=6)
1323//       cylinder(h=20, r1=5, r2=0);
1324//   color("red",0.333) cylinder(h=20, r1=5, r2=0);
1325//
1326// Example:
1327//   xrot_copies(n=6, r=10)
1328//       xrot(-90) cylinder(h=20, r1=5, r2=0);
1329//   color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0);
1330//
1331// Example:
1332//   xrot_copies(n=6, r=10, sa=45)
1333//       xrot(-90) cylinder(h=20, r1=5, r2=0);
1334//   color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0);
1335//
1336// Example:
1337//   xrot_copies(n=6, r=20, subrot=false)
1338//       xrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1339//   color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1340module xrot_copies(rots=[], cp=[0,0,0], n=undef, count=undef, sa=0, offset=0, r=0, subrot=true)
1341{
1342	cnt = first_defined([count, n]);
1343	sang = sa + offset;
1344	rot_copies(rots=rots, v=V_RIGHT, cp=cp, n=cnt, sa=sang, delta=[0, r, 0], subrot=subrot) children();
1345}
1346
1347
1348// Module: yrot_copies()
1349//
1350// Description:
1351//   Given an array of angles, rotates copies of the children
1352//   to each of those angles around the Y axis.
1353//
1354// Usage:
1355//   yrot_copies(rots, [r], [cp], [sa], [subrot]) ...
1356//   yrot_copies(n, [r], [cp], [sa], [subrot]) ...
1357//
1358// Arguments:
1359//   rots = Optional array of rotation angles, in degrees, to make copies at.
1360//   cp = Centerpoint to rotate around.
1361//   n = Optional number of evenly distributed copies to be rotated around the ring.  If given, overrides `rots` argument.
1362//   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise from X-, when facing the origin from Y+.
1363//   r = Radius to move children left, away from cp, before rotating.  Makes rings of copies.
1364//   subrot = If false, don't sub-rotate children as they are copied around the ring.
1365//
1366// Side Effects:
1367//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
1368//
1369// Example:
1370//   yrot_copies([180, 270, 315])
1371//       cylinder(h=20, r1=5, r2=0);
1372//   color("red",0.333) cylinder(h=20, r1=5, r2=0);
1373//
1374// Example:
1375//   yrot_copies(n=6)
1376//       cylinder(h=20, r1=5, r2=0);
1377//   color("red",0.333) cylinder(h=20, r1=5, r2=0);
1378//
1379// Example:
1380//   yrot_copies(n=6, r=10)
1381//       yrot(-90) cylinder(h=20, r1=5, r2=0);
1382//   color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0);
1383//
1384// Example:
1385//   yrot_copies(n=6, r=10, sa=45)
1386//       yrot(-90) cylinder(h=20, r1=5, r2=0);
1387//   color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0);
1388//
1389// Example:
1390//   yrot_copies(n=6, r=20, subrot=false)
1391//       yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1392//   color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1393module yrot_copies(rots=[], cp=[0,0,0], n=undef, count=undef, sa=0, offset=0, r=0, subrot=true)
1394{
1395	cnt = first_defined([count, n]);
1396	sang = sa + offset;
1397	rot_copies(rots=rots, v=V_BACK, cp=cp, n=cnt, sa=sang, delta=[-r, 0, 0], subrot=subrot) children();
1398}
1399
1400
1401// Module: zrot_copies()
1402//
1403// Description:
1404//   Given an array of angles, rotates copies of the children
1405//   to each of those angles around the Z axis.
1406//
1407// Usage:
1408//   zrot_copies(rots, [r], [cp], [sa], [subrot]) ...
1409//   zrot_copies(n, [r], [cp], [sa], [subrot]) ...
1410//
1411// Arguments:
1412//   rots = Optional array of rotation angles, in degrees, to make copies at.
1413//   cp = Centerpoint to rotate around.
1414//   n = Optional number of evenly distributed copies to be rotated around the ring.  If given, overrides `rots` argument.
1415//   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise from X+, when facing the origin from Z+.
1416//   r = Radius to move children right, away from cp, before rotating.  Makes rings of copies.
1417//   subrot = If false, don't sub-rotate children as they are copied around the ring.
1418//
1419// Side Effects:
1420//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
1421//
1422// Example:
1423//   zrot_copies([180, 270, 315])
1424//       yrot(90) cylinder(h=20, r1=5, r2=0);
1425//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1426//
1427// Example:
1428//   zrot_copies(n=6)
1429//       yrot(90) cylinder(h=20, r1=5, r2=0);
1430//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1431//
1432// Example:
1433//   zrot_copies(n=6, r=10)
1434//       yrot(90) cylinder(h=20, r1=5, r2=0);
1435//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1436//
1437// Example:
1438//   zrot_copies(n=6, r=20, sa=45)
1439//       yrot(90) cylinder(h=20, r1=5, r2=0, center=true);
1440//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0, center=true);
1441//
1442// Example:
1443//   zrot_copies(n=6, r=20, subrot=false)
1444//       yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1445//   color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1446module zrot_copies(rots=[], cp=[0,0,0], n=undef, count=undef, sa=0, offset=0, r=0, subrot=true)
1447{
1448	cnt = first_defined([count, n]);
1449	sang = sa + offset;
1450	rot_copies(rots=rots, v=V_UP, cp=cp, n=cnt, sa=sang, delta=[r, 0, 0], subrot=subrot) children();
1451}
1452
1453
1454// Module: xring()
1455// 
1456// Description:
1457//   Distributes `n` copies of the given children on a circle of radius `r`
1458//   around the X axis.  If `rot` is true, each copy is rotated in place to keep
1459//   the same side towards the center.  The first, unrotated copy will be at the
1460//   starting angle `sa`.
1461//
1462// Usage:
1463//   xring(n, r, [sa], [cp], [rot]) ...
1464//
1465// Arguments:
1466//   n = Number of copies of children to distribute around the circle. (Default: 2)
1467//   r = Radius of ring to distribute children around. (Default: 0)
1468//   sa = Start angle for first (unrotated) copy.  (Default: 0)
1469//   cp = Centerpoint of ring.  Default: [0,0,0]
1470//   rot = If true, rotate each copy to keep the same side towards the center of the ring.  Default: true.
1471//
1472// Side Effects:
1473//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
1474//   `$idx` is set to the index value of each child copy.
1475//
1476// Examples:
1477//   xring(n=6, r=10) xrot(-90) cylinder(h=20, r1=5, r2=0);
1478//   xring(n=6, r=10, sa=45) xrot(-90) cylinder(h=20, r1=5, r2=0);
1479//   xring(n=6, r=20, rot=false) cylinder(h=20, r1=6, r2=0, center=true);
1480module xring(n=2, r=0, sa=0, cp=[0,0,0], rot=true)
1481{
1482	xrot_copies(count=n, r=r, sa=sa, cp=cp, subrot=rot) children();
1483}
1484
1485
1486// Module: yring()
1487// 
1488// Description:
1489//   Distributes `n` copies of the given children on a circle of radius `r`
1490//   around the Y axis.  If `rot` is true, each copy is rotated in place to keep
1491//   the same side towards the center.  The first, unrotated copy will be at the
1492//   starting angle `sa`.
1493//
1494// Usage:
1495//   yring(n, r, [sa], [cp], [rot]) ...
1496//
1497// Arguments:
1498//   n = Number of copies of children to distribute around the circle. (Default: 2)
1499//   r = Radius of ring to distribute children around. (Default: 0)
1500//   sa = Start angle for first (unrotated) copy.  (Default: 0)
1501//   cp = Centerpoint of ring.  Default: [0,0,0]
1502//   rot = If true, rotate each copy to keep the same side towards the center of the ring.  Default: true.
1503//
1504// Side Effects:
1505//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
1506//   `$idx` is set to the index value of each child copy.
1507//
1508// Examples:
1509//   yring(n=6, r=10) yrot(-90) cylinder(h=20, r1=5, r2=0);
1510//   yring(n=6, r=10, sa=45) yrot(-90) cylinder(h=20, r1=5, r2=0);
1511//   yring(n=6, r=20, rot=false) cylinder(h=20, r1=6, r2=0, center=true);
1512module yring(n=2, r=0, sa=0, cp=[0,0,0], rot=true)
1513{
1514	yrot_copies(count=n, r=r, sa=sa, cp=cp, subrot=rot) children();
1515}
1516
1517
1518// Module: zring()
1519//
1520// Description:
1521//   Distributes `n` copies of the given children on a circle of radius `r`
1522//   around the Z axis.  If `rot` is true, each copy is rotated in place to keep
1523//   the same side towards the center.  The first, unrotated copy will be at the
1524//   starting angle `sa`.
1525//
1526// Usage:
1527//   zring(r, n, [sa], [cp], [rot]) ...
1528//
1529// Arguments:
1530//   n = Number of copies of children to distribute around the circle. (Default: 2)
1531//   r = Radius of ring to distribute children around. (Default: 0)
1532//   sa = Start angle for first (unrotated) copy.  (Default: 0)
1533//   cp = Centerpoint of ring.  Default: [0,0,0]
1534//   rot = If true, rotate each copy to keep the same side towards the center of the ring.  Default: true.
1535//
1536// Side Effects:
1537//   `$ang` is set to the relative angle from `cp` of each child copy, and can be used to modify each child individually.
1538//   `$idx` is set to the index value of each child copy.
1539//
1540// Examples:
1541//   zring(n=6, r=10) yrot(90) cylinder(h=20, r1=5, r2=0);
1542//   zring(n=6, r=10, sa=45) yrot(90) cylinder(h=20, r1=5, r2=0);
1543//   zring(n=6, r=20, rot=false) yrot(90) cylinder(h=20, r1=6, r2=0, center=true);
1544module zring(n=2, r=0, sa=0, cp=[0,0,0], rot=true)
1545{
1546	zrot_copies(count=n, r=r, sa=sa, cp=cp, subrot=rot) children();
1547}
1548
1549
1550// Module: arc_of()
1551//
1552// Description:
1553//   Evenly distributes n duplicate children around an ovoid arc on the XY plane.
1554//
1555// Usage:
1556//   arc_of(r|d, n, [sa], [ea], [rot]
1557//   arc_of(rx|dx, ry|dy, n, [sa], [ea], [rot]
1558//
1559// Arguments:
1560//   n = number of copies to distribute around the circle. (Default: 6)
1561//   r = radius of circle (Default: 1)
1562//   rx = radius of ellipse on X axis. Used instead of r.
1563//   ry = radius of ellipse on Y axis. Used instead of r.
1564//   d = diameter of circle. (Default: 2)
1565//   dx = diameter of ellipse on X axis. Used instead of d.
1566//   dy = diameter of ellipse on Y axis. Used instead of d.
1567//   rot = whether to rotate the copied children.  (Default: false)
1568//   sa = starting angle. (Default: 0.0)
1569//   ea = ending angle. Will distribute copies CCW from sa to ea. (Default: 360.0)
1570//
1571// Side Effects:
1572//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
1573//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
1574//   `$idx` is set to the index value of each child copy.
1575//
1576// Example:
1577//   #cube(size=[10,3,3],center=true);
1578//   arc_of(d=40, n=5) cube(size=[10,3,3],center=true);
1579//
1580// Example:
1581//   #cube(size=[10,3,3],center=true);
1582//   arc_of(d=40, n=5, sa=45, ea=225) cube(size=[10,3,3],center=true);
1583//
1584// Example:
1585//   #cube(size=[10,3,3],center=true);
1586//   arc_of(r=15, n=8, rot=false) cube(size=[10,3,3],center=true);
1587//
1588// Example:
1589//   #cube(size=[10,3,3],center=true);
1590//   arc_of(rx=20, ry=10, n=8) cube(size=[10,3,3],center=true);
1591module arc_of(
1592	n=6,
1593	r=undef, rx=undef, ry=undef,
1594	d=undef, dx=undef, dy=undef,
1595	sa=0, ea=360,
1596	rot=true
1597) {
1598	rx = get_radius(rx, r, dx, d, 1);
1599	ry = get_radius(ry, r, dy, d, 1);
1600	sa = posmod(sa, 360);
1601	ea = posmod(ea, 360);
1602	n = (abs(ea-sa)<0.01)?(n+1):n;
1603	delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1);
1604	for ($idx = [0:n-1]) {
1605		$ang = sa + ($idx * delt);
1606		$pos =[rx*cos($ang), ry*sin($ang), 0];
1607		translate($pos) {
1608			zrot(rot? atan2(ry*sin($ang), rx*cos($ang)) : 0) {
1609				children();
1610			}
1611		}
1612	}
1613}
1614
1615
1616
1617// Module: ovoid_spread()
1618//
1619// Description:
1620//   Spreads children semi-evenly over the surface of a sphere.
1621//
1622// Usage:
1623//   ovoid_spread(r|d, n, [cone_ang], [scale], [perp]) ...
1624//
1625// Arguments:
1626//   r = Radius of the sphere to distribute over
1627//   d = Diameter of the sphere to distribute over
1628//   n = How many copies to evenly spread over the surface.
1629//   cone_ang = Angle of the cone, in degrees, to limit how much of the sphere gets covered.  For full sphere coverage, use 180.  Measured pre-scaling.  Default: 180
1630//   scale = The [X,Y,Z] scaling factors to reshape the sphere being covered.
1631//   perp = If true, rotate children to be perpendicular to the sphere surface.  Default: true
1632//
1633// Side Effects:
1634//   `$pos` is set to the relative post-scaled centerpoint of each child copy, and can be used to modify each child individually.
1635//   `$theta` is set to the theta angle of the child from the center of the sphere.
1636//   `$phi` is set to the pre-scaled phi angle of the child from the center of the sphere.
1637//   `$rad` is set to the pre-scaled radial distance of the child from the center of the sphere.
1638//   `$idx` is set to the index number of each child being copied.
1639//
1640// Example:
1641//   ovoid_spread(n=250, d=100, cone_ang=45, scale=[3,3,1])
1642//       cylinder(d=10, h=10, center=false);
1643//
1644// Example:
1645//   ovoid_spread(n=500, d=100, cone_ang=180)
1646//       color(normalize(point3d(vabs($pos))))
1647//           cylinder(d=8, h=10, center=false);
1648module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=true)
1649{
1650	r = get_radius(r=r, d=d, dflt=50);
1651	cnt = ceil(n / (cone_ang/180));
1652
1653	// Calculate an array of [theta,phi] angles for `n` number of
1654	// points, almost evenly spaced across the surface of a sphere.
1655	// This approximation is based on the golden spiral method.
1656	theta_phis = [for (x=[0:n-1]) [180*(1+sqrt(5))*(x+0.5)%360, acos(1-2*(x+0.5)/cnt)]];
1657
1658	for ($idx = [0:len(theta_phis)-1]) {
1659	    tp = theta_phis[$idx];
1660		xyz = spherical_to_xyz(r, tp[0], tp[1]);
1661		$pos = vmul(xyz,scale);
1662		$theta = tp[0];
1663		$phi = tp[1];
1664		$rad = r;
1665		translate($pos) {
1666			if (perp) {
1667				rot(from=V_UP, to=xyz) children();
1668			} else {
1669				children();
1670			}
1671		}
1672	}
1673}
1674
1675
1676
1677//////////////////////////////////////////////////////////////////////
1678// Section: Reflectional Distributors
1679//////////////////////////////////////////////////////////////////////
1680
1681
1682// Module: mirror_copy()
1683//
1684// Description:
1685//   Makes a copy of the children, mirrored across the given plane.
1686//
1687// Usage:
1688//   mirror_copy(v, [cp], [offset]) ...
1689//
1690// Arguments:
1691//   v = The normal vector of the plane to mirror across.
1692//   offset = distance to offset away from the plane.
1693//   cp = A point that lies on the mirroring plane.
1694//
1695// Side Effects:
1696//   `$orig` is true for the original instance of children.  False for the copy.
1697//   `$idx` is set to the index value of each copy.
1698//
1699// Example:
1700//   mirror_copy([1,-1,0]) zrot(-45) yrot(90) cylinder(d1=10, d2=0, h=20);
1701//   color("blue",0.25) zrot(-45) cube([0.01,15,15], center=true);
1702//
1703// Example:
1704//   mirror_copy([1,1,0], offset=5) rot(a=90,v=[-1,1,0]) cylinder(d1=10, d2=0, h=20);
1705//   color("blue",0.25) zrot(45) cube([0.01,15,15], center=true);
1706//
1707// Example:
1708//   mirror_copy(V_UP+V_BACK, cp=[0,-5,-5]) rot(from=V_UP, to=V_BACK+V_UP) cylinder(d1=10, d2=0, h=20);
1709//   color("blue",0.25) translate([0,-5,-5]) rot(from=V_UP, to=V_BACK+V_UP) cube([15,15,0.01], center=true);
1710module mirror_copy(v=[0,0,1], offset=0, cp=[0,0,0])
1711{
1712	nv = v/norm(v);
1713	off = nv*offset;
1714	if (cp == [0,0,0]) {
1715		translate(off) {
1716			$orig = true;
1717			$idx = 0;
1718			children();
1719		}
1720		mirror(nv) translate(off) {
1721			$orig = false;
1722			$idx = 1;
1723			children();
1724		}
1725	} else {
1726		translate(off) children();
1727		translate(cp) mirror(nv) translate(-cp) translate(off) children();
1728	}
1729}
1730
1731
1732// Module: xflip_copy()
1733//
1734// Description:
1735//   Makes a copy of the children, mirrored across the X axis.
1736//
1737// Usage:
1738//   xflip_copy([cp], [offset]) ...
1739//
1740// Arguments:
1741//   offset = Distance to offset children right, before copying.
1742//   cp = A point that lies on the mirroring plane.
1743//
1744// Side Effects:
1745//   `$orig` is true for the original instance of children.  False for the copy.
1746//   `$idx` is set to the index value of each copy.
1747//
1748// Example:
1749//   xflip_copy() yrot(90) cylinder(h=20, r1=4, r2=0);
1750//   color("blue",0.25) cube([0.01,15,15], center=true);
1751//
1752// Example:
1753//   xflip_copy(offset=5) yrot(90) cylinder(h=20, r1=4, r2=0);
1754//   color("blue",0.25) cube([0.01,15,15], center=true);
1755//
1756// Example:
1757//   xflip_copy(cp=[-5,0,0]) yrot(90) cylinder(h=20, r1=4, r2=0);
1758//   color("blue",0.25) left(5) cube([0.01,15,15], center=true);
1759module xflip_copy(offset=0, cp=[0,0,0])
1760{
1761	mirror_copy(v=[1,0,0], offset=offset, cp=cp) children();
1762}
1763
1764
1765// Module: yflip_copy()
1766//
1767// Description:
1768//   Makes a copy of the children, mirrored across the Y axis.
1769//
1770// Usage:
1771//   yflip_copy([cp], [offset]) ...
1772//
1773// Arguments:
1774//   offset = Distance to offset children back, before copying.
1775//   cp = A point that lies on the mirroring plane.
1776//
1777// Side Effects:
1778//   `$orig` is true for the original instance of children.  False for the copy.
1779//   `$idx` is set to the index value of each copy.
1780//
1781// Example:
1782//   yflip_copy() xrot(-90) cylinder(h=20, r1=4, r2=0);
1783//   color("blue",0.25) cube([15,0.01,15], center=true);
1784//
1785// Example:
1786//   yflip_copy(offset=5) xrot(-90) cylinder(h=20, r1=4, r2=0);
1787//   color("blue",0.25) cube([15,0.01,15], center=true);
1788//
1789// Example:
1790//   yflip_copy(cp=[0,-5,0]) xrot(-90) cylinder(h=20, r1=4, r2=0);
1791//   color("blue",0.25) fwd(5) cube([15,0.01,15], center=true);
1792module yflip_copy(offset=0, cp=[0,0,0])
1793{
1794	mirror_copy(v=[0,1,0], offset=offset, cp=cp) children();
1795}
1796
1797
1798// Module: zflip_copy()
1799//
1800// Description:
1801//   Makes a copy of the children, mirrored across the Z axis.
1802//
1803// Usage:
1804//   zflip_copy([cp], [offset]) ...
1805//   `$idx` is set to the index value of each copy.
1806//
1807// Arguments:
1808//   offset = Distance to offset children up, before copying.
1809//   cp = A point that lies on the mirroring plane.
1810//
1811// Side Effects:
1812//   `$orig` is true for the original instance of children.  False for the copy.
1813//
1814// Example:
1815//   zflip_copy() cylinder(h=20, r1=4, r2=0);
1816//   color("blue",0.25) cube([15,15,0.01], center=true);
1817//
1818// Example:
1819//   zflip_copy(offset=5) cylinder(h=20, r1=4, r2=0);
1820//   color("blue",0.25) cube([15,15,0.01], center=true);
1821//
1822// Example:
1823//   zflip_copy(cp=[0,0,-5]) cylinder(h=20, r1=4, r2=0);
1824//   color("blue",0.25) down(5) cube([15,15,0.01], center=true);
1825module zflip_copy(offset=0, cp=[0,0,0])
1826{
1827	mirror_copy(v=[0,0,1], offset=offset, cp=cp) children();
1828}
1829
1830
1831//////////////////////////////////////////////////////////////////////
1832// Section: Mutators
1833//////////////////////////////////////////////////////////////////////
1834
1835
1836// Module: half_of()
1837//
1838// Usage:
1839//   half_of(v, [cp], [s]) ...
1840//
1841// Description:
1842//   Slices an object at a cut plane, and masks away everything that is on one side.
1843//
1844// Arguments:
1845//   v = Normal of plane to slice at.  Keeps everything on the side the normal points to.  Default: [0,0,1] (V_UP)
1846//   cp = If given as a scalar, moves the cut plane along the normal by the given amount.  If given as a point, specifies a point on the cut plane.  This can be used to shift where it slices the object at.  Default: [0,0,0]
1847//   s = Mask size to use.  Use a number larger than twice your object's largest axis.  If you make this too large, it messes with centering your view.  Default: 100
1848//   planar = If true, this becomes a 2D operation.  When planar, a `v` of `V_UP` or `V_DOWN` becomes equivalent of `V_BACK` and `V_FWD` respectively.
1849//
1850// Examples:
1851//   half_of(V_DOWN+V_BACK, cp=[0,-10,0]) cylinder(h=40, r1=10, r2=0, center=false);
1852//   half_of(V_DOWN+V_LEFT, s=200) sphere(d=150);
1853// Example(2D):
1854//   half_of([1,1], planar=true) circle(d=50);
1855module half_of(v=V_UP, cp=[0,0,0], s=100, planar=false)
1856{
1857	cp = is_scalar(cp)? cp*normalize(v) : cp;
1858	if (cp != [0,0,0]) {
1859		translate(cp) half_of(v=v, s=s, planar=planar) translate(-cp) children();
1860	} else if (planar) {
1861		v = (v==V_UP)? V_BACK : (v==V_DOWN)? V_FWD : v;
1862		ang = atan2(v.y, v.x);
1863		difference() {
1864			children();
1865			rotate(ang+90) {
1866				back(s/2) square(s, center=true);
1867			}
1868		}
1869	} else {
1870		difference() {
1871			children();
1872			rot(from=V_UP, to=-v) {
1873				up(s/2) cube(s, center=true);
1874			}
1875		}
1876	}
1877}
1878
1879
1880// Module: top_half()
1881//
1882// Usage:
1883//   top_half([z|cp], [s]) ...
1884//
1885// Description:
1886//   Slices an object at a horizontal X-Y cut plane, and masks away everything that is below it.
1887//
1888// Arguments:
1889//   cp = If given as a scalar, moves the cut plane up by the given amount.  If given as a point, specifies a point on the cut plane.  Default: [0,0,0]
1890//   z = The Z coordinate of the cut-plane, if given.  Use instead of `cp`.
1891//   s = Mask size to use.  Use a number larger than twice your object's largest axis.  If you make this too large, it messes with centering your view.  Default: 100
1892//   planar = If true, this becomes equivalent to a planar `back_half()`.
1893//
1894// Examples(Spin):
1895//   top_half() sphere(r=20);
1896//   top_half(z=5) sphere(r=20);
1897//   top_half(cp=5) sphere(r=20);
1898//   top_half(cp=[0,0,-8]) sphere(r=20);
1899// Example(2D):
1900//   top_half(planar=true) circle(r=20);
1901module top_half(s=100, z=undef, cp=[0,0,0], planar=false)
1902{
1903	dir = planar? V_BACK : V_UP;
1904	cp = is_scalar(z)? [0,0,z] : is_scalar(cp)? cp*dir : cp;
1905	translate(cp) difference() {
1906		translate(-cp) children();
1907		translate(-dir*s/2) {
1908			if (planar) {
1909				square(s, center=true);
1910			} else {
1911				cube(s, center=true);
1912			}
1913		}
1914	}
1915}
1916
1917
1918
1919// Module: bottom_half()
1920//
1921// Usage:
1922//   bottom_half([z|cp], [s]) ...
1923//
1924// Description:
1925//   Slices an object at a horizontal X-Y cut plane, and masks away everything that is above it.
1926//
1927// Arguments:
1928//   cp = If given as a scalar, moves the cut plane down by the given amount.  If given as a point, specifies a point on the cut plane.  Default: [0,0,0]
1929//   z = The Z coordinate of the cut-plane, if given.  Use instead of `cp`.
1930//   s = Mask size to use.  Use a number larger than twice your object's largest axis.  If you make this too large, it messes with centering your view.  Default: 100
1931//   planar = If true, this becomes equivalent to a planar `front_half()`.
1932//
1933// Examples:
1934//   bottom_half() sphere(r=20);
1935//   bottom_half(z=-10) sphere(r=20);
1936//   bottom_half(cp=-10) sphere(r=20);
1937//   bottom_half(cp=[0,0,10]) sphere(r=20);
1938// Example(2D):
1939//   bottom_half(planar=true) circle(r=20);
1940module bottom_half(s=100, z=undef, cp=[0,0,0], planar=false)
1941{
1942	dir = planar? V_FWD : V_DOWN;
1943	cp = is_scalar(z)? [0,0,z] : is_scalar(cp)? cp*dir : cp;
1944	translate(cp) difference() {
1945		translate(-cp) children();
1946		translate(-dir*s/2) {
1947			if (planar) {
1948				square(s, center=true);
1949			} else {
1950				cube(s, center=true);
1951			}
1952		}
1953	}
1954}
1955
1956
1957
1958// Module: left_half()
1959//
1960// Usage:
1961//   left_half([x|cp], [s]) ...
1962//
1963// Description:
1964//   Slices an object at a vertical Y-Z cut plane, and masks away everything that is right of it.
1965//
1966// Arguments:
1967//   cp = If given as a scalar, moves the cut plane left by the given amount.  If given as a point, specifies a point on the cut plane.  Default: [0,0,0]
1968//   x = The X coordinate of the cut-plane, if given.  Use instead of `cp`.
1969//   s = Mask size to use.  Use a number larger than twice your object's largest axis.  If you make this too large, it messes with centering your view.  Default: 100
1970//   planar = If true, this becomes a 2D operation.
1971//
1972// Examples:
1973//   left_half() sphere(r=20);
1974//   left_half(x=-8) sphere(r=20);
1975//   left_half(cp=-8) sphere(r=20);
1976//   left_half(cp=[8,0,0]) sphere(r=20);
1977// Example(2D):
1978//   left_half(planar=true) circle(r=20);
1979module left_half(s=100, x=undef, cp=[0,0,0], planar=false)
1980{
1981	dir = V_LEFT;
1982	cp = is_scalar(x)? [x,0,0] : is_scalar(cp)? cp*dir : cp;
1983	translate(cp) difference() {
1984		translate(-cp) children();
1985		translate(-dir*s/2) {
1986			if (planar) {
1987				square(s, center=true);
1988			} else {
1989				cube(s, center=true);
1990			}
1991		}
1992	}
1993}
1994
1995
1996
1997// Module: right_half()
1998//
1999// Usage:
2000//   right_half([x|cp], [s]) ...
2001//
2002// Description:
2003//   Slices an object at a vertical Y-Z cut plane, and masks away everything that is left of it.
2004//
2005// Arguments:
2006//   cp = If given as a scalar, moves the cut plane right by the given amount.  If given as a point, specifies a point on the cut plane.  Default: [0,0,0]
2007//   x = The X coordinate of the cut-plane, if given.  Use instead of `cp`.
2008//   s = Mask size to use.  Use a number larger than twice your object's largest axis.  If you make this too large, it messes with centering your view.  Default: 100
2009//   planar = If true, this becomes a 2D operation.
2010//
2011// Examples(FlatSpin):
2012//   right_half() sphere(r=20);
2013//   right_half(x=-5) sphere(r=20);
2014//   right_half(cp=-5) sphere(r=20);
2015//   right_half(cp=[-5,0,0]) sphere(r=20);
2016// Example(2D):
2017//   right_half(planar=true) circle(r=20);
2018module right_half(s=100, x=undef, cp=[0,0,0], planar=false)
2019{
2020	dir = V_RIGHT;
2021	cp = is_scalar(x)? [x,0,0] : is_scalar(cp)? cp*dir : cp;
2022	translate(cp) difference() {
2023		translate(-cp) children();
2024		translate(-dir*s/2) {
2025			if (planar) {
2026				square(s, center=true);
2027			} else {
2028				cube(s, center=true);
2029			}
2030		}
2031	}
2032}
2033
2034
2035
2036// Module: front_half()
2037//
2038// Usage:
2039//   front_half([y|cp], [s]) ...
2040//
2041// Description:
2042//   Slices an object at a vertical X-Z cut plane, and masks away everything that is behind it.
2043//
2044// Arguments:
2045//   cp = If given as a scalar, moves the cut plane forward by the given amount.  If given as a point, specifies a point on the cut plane.  Default: [0,0,0]
2046//   y = The Y coordinate of the cut-plane, if given.  Use instead of `cp`.
2047//   s = Mask size to use.  Use a number larger than twice your object's largest axis.  If you make this too large, it messes with centering your view.  Default: 100
2048//   planar = If true, this becomes a 2D operation.
2049//
2050// Examples(FlatSpin):
2051//   front_half() sphere(r=20);
2052//   front_half(y=5) sphere(r=20);
2053//   front_half(cp=5) sphere(r=20);
2054//   front_half(cp=[0,5,0]) sphere(r=20);
2055// Example(2D):
2056//   front_half(planar=true) circle(r=20);
2057module front_half(s=100, y=undef, cp=[0,0,0], planar=false)
2058{
2059	dir = V_FWD;
2060	cp = is_scalar(y)? [0,y,0] : is_scalar(cp)? cp*dir : cp;
2061	translate(cp) difference() {
2062		translate(-cp) children();
2063		translate(-dir*s/2) {
2064			if (planar) {
2065				square(s, center=true);
2066			} else {
2067				cube(s, center=true);
2068			}
2069		}
2070	}
2071}
2072
2073
2074
2075// Module: back_half()
2076//
2077// Usage:
2078//   back_half([y|cp], [s]) ...
2079//
2080// Description:
2081//   Slices an object at a vertical X-Z cut plane, and masks away everything that is in front of it.
2082//
2083// Arguments:
2084//   cp = If given as a scalar, moves the cut plane back by the given amount.  If given as a point, specifies a point on the cut plane.  Default: [0,0,0]
2085//   y = The Y coordinate of the cut-plane, if given.  Use instead of `cp`.
2086//   s = Mask size to use.  Use a number larger than twice your object's largest axis.  If you make this too large, it messes with centering your view.  Default: 100
2087//   planar = If true, this becomes a 2D operation.
2088//
2089// Examples:
2090//   back_half() sphere(r=20);
2091//   back_half(y=8) sphere(r=20);
2092//   back_half(cp=8) sphere(r=20);
2093//   back_half(cp=[0,-10,0]) sphere(r=20);
2094// Example(2D):
2095//   back_half(planar=true) circle(r=20);
2096module back_half(s=100, y=undef, cp=[0,0,0], planar=false)
2097{
2098	dir = V_BACK;
2099	cp = is_scalar(y)? [0,y,0] : is_scalar(cp)? cp*dir : cp;
2100	translate(cp) difference() {
2101		translate(-cp) children();
2102		translate(-dir*s/2) {
2103			if (planar) {
2104				square(s, center=true);
2105			} else {
2106				cube(s, center=true);
2107			}
2108		}
2109	}
2110}
2111
2112
2113
2114// Module: chain_hull()
2115//
2116// Usage:
2117//   chain_hull() ...
2118//
2119// Description:
2120//   Performs hull operations between consecutive pairs of children,
2121//   then unions all of the hull results.  This can be a very slow
2122//   operation, but it can provide results that are hard to get
2123//   otherwise.
2124//
2125// Example:
2126//   chain_hull() {
2127//       cube(5, center=true);
2128//       translate([30, 0, 0]) sphere(d=15);
2129//       translate([60, 30, 0]) cylinder(d=10, h=20);
2130//       translate([60, 60, 0]) cube([10,1,20], center=false);
2131//   }
2132module chain_hull()
2133{
2134	union() {
2135		if ($children == 1) {
2136			children();
2137		} else if ($children > 1) {
2138			for (i =[1:$children-1]) {
2139				hull() {
2140					children(i-1);
2141					children(i);
2142				}
2143			}
2144		}
2145	}
2146}
2147
2148
2149// Module: extrude_arc()
2150//
2151// Description:
2152//   Extrudes 2D shapes around a partial circle arc, with optional rounded caps.
2153//   This is mostly useful for backwards compatability with older OpenSCAD versions
2154//   without the `angle` argument in rotate_extrude.
2155//
2156// Usage:
2157//   extrude_arc(arc, r|d, [sa], [caps], [orient], [align], [masksize]) ...
2158//
2159// Arguments:
2160//   arc = Number of degrees to traverse.
2161//   sa = Start angle in degrees.
2162//   r = Radius of arc.
2163//   d = Diameter of arc.
2164//   orient = The axis to align to.  Use `ORIENT_` constants from `constants.scad`
2165//   align = The side of the origin the part should be aligned with.  Use `V_` constants from `constants.scad`
2166//   masksize = size of mask used to clear unused part of circle arc.  should be larger than height or width of 2D shapes to extrude.
2167//   caps = If true, spin the 2D shapes to make rounded caps the ends of the arc.
2168//   convexity = Max number of times a ray passes through the 2D shape's walls.
2169//
2170// Example(Med):
2171//   pts=[[-5/2, -5], [-5/2, 0], [-5/2-3, 5], [5/2+3, 5], [5/2, 0], [5/2, -5]];
2172//   #polygon(points=pts);
2173//   extrude_arc(arc=270, sa=45, r=40, caps=true, convexity=4, $fa=2, $fs=2) {
2174//       polygon(points=pts);
2175//   }
2176module extrude_arc(arc=90, sa=0, r=undef, d=undef, orient=ORIENT_Z, align=V_CENTER, masksize=100, caps=false, convexity=4)
2177{
2178	eps = 0.001;
2179	r = get_radius(r=r, d=d, dflt=100);
2180	orient_and_align([2*r, 2*r, 0], orient, align) {
2181		zrot(sa) {
2182			if (caps) {
2183				place_copies([[r,0,0], cylindrical_to_xyz(r, arc, 0)]) {
2184					rotate_extrude(convexity=convexity) {
2185						difference() {
2186							children();
2187							left(masksize/2) square(masksize, center=true);
2188						}
2189					}
2190				}
2191			}
2192			difference() {
2193				rotate_extrude(angle=arc, convexity=convexity*2) {
2194					right(r) {
2195						children();
2196					}
2197				}
2198				if(version_num() < 20190000) {
2199					maxd = r + masksize;
2200					if (arc<180) rotate(arc) back(maxd/2) cube([2*maxd, maxd, masksize+0.1], center=true);
2201					difference() {
2202						fwd(maxd/2) cube([2*maxd, maxd, masksize+0.2], center=true);
2203						if (arc>180) rotate(arc-180) back(maxd/2) cube([2*maxd, maxd, masksize+0.1], center=true);
2204					}
2205				}
2206			}
2207		}
2208	}
2209}
2210
2211
2212//////////////////////////////////////////////////////////////////////
2213// Section: 2D Mutators
2214//////////////////////////////////////////////////////////////////////
2215
2216
2217// Module: round2d()
2218// Usage:
2219//   round2d(r) ...
2220//   round2d(or) ...
2221//   round2d(ir) ...
2222//   round2d(or, ir) ...
2223// Description:
2224//   Rounds an arbitrary 2d objects.  Giving `r` rounds all concave and
2225//   convex corners.  Giving just `ir` rounds just concave corners.
2226//   Giving just `or` rounds convex corners.  Giving both `ir` and `or`
2227//   can let you round to different radii for concave and convex corners.
2228//   The 2d object must not have any parts narrower than twice the `or`
2229//   radius.  Such parts will disappear.
2230// Arguments:
2231//   r = Radius to round all concave and convex corners to.
2232//   or = Radius to round only outside (convex) corners to.  Use instead of `r`.
2233//   ir = Radius to round/fillet only inside (concave) corners to.  Use instead of `r`.
2234// Examples(2D):
2235//   round2d(r=10) {square([40,100], center=true); square([100,40], center=true);}
2236//   round2d(or=10) {square([40,100], center=true); square([100,40], center=true);}
2237//   round2d(ir=10) {square([40,100], center=true); square([100,40], center=true);}
2238//   round2d(or=16,ir=8) {square([40,100], center=true); square([100,40], center=true);}
2239module round2d(r, or, ir)
2240{
2241	or = get_radius(r1=or, r=r, dflt=0);
2242	ir = get_radius(r1=ir, r=r, dflt=0);
2243	offset(or) offset(-ir-or) offset(delta=ir) children();
2244}
2245
2246
2247// Module: shell2d()
2248// Usage:
2249//   shell2d(thickness, [or], [ir], [fill], [round])
2250// Description:
2251//   Creates a hollow shell from 2d children, with optional rounding.
2252// Arguments:
2253//   thickness = Thickness of the shell.  Positive to expand outward, negative to shrink inward, or a two-element list to do both.
2254//   or = Radius to round convex corners/pointy bits on the outside of the shell.
2255//   ir = Radius to round/fillet concave corners on the outside of the shell.
2256//   round = Radius to round convex corners/pointy bits on the inside of the shell.
2257//   fill = Radius to round/fillet concave corners on the inside of the shell.
2258// Examples(2D):
2259//   shell2d(10) {square([40,100], center=true); square([100,40], center=true);}
2260//   shell2d(-10) {square([40,100], center=true); square([100,40], center=true);}
2261//   shell2d([-10,10]) {square([40,100], center=true); square([100,40], center=true);}
2262//   shell2d(10,or=10) {square([40,100], center=true); square([100,40], center=true);}
2263//   shell2d(10,ir=10) {square([40,100], center=true); square([100,40], center=true);}
2264//   shell2d(10,round=10) {square([40,100], center=true); square([100,40], center=true);}
2265//   shell2d(10,fill=10) {square([40,100], center=true); square([100,40], center=true);}
2266//   shell2d(8,or=16,ir=8,round=16,fill=8) {square([40,100], center=true); square([100,40], center=true);}
2267module shell2d(thickness, or=0, ir=0, fill=0, round=0)
2268{
2269	thickness = is_scalar(thickness)? (
2270		thickness<0? [thickness,0] : [0,thickness]
2271	) : (thickness[0]>thickness[1])? (
2272		[thickness[1],thickness[0]]
2273	) : thickness;
2274	difference() {
2275		round2d(or=or,ir=ir)
2276			offset(delta=thickness[1])
2277				children();
2278		round2d(or=fill,ir=round)
2279			offset(delta=thickness[0])
2280				children();
2281	}
2282}
2283
2284
2285
2286//////////////////////////////////////////////////////////////////////
2287// Section: Miscellaneous
2288//////////////////////////////////////////////////////////////////////
2289
2290
2291// Module: orient_and_align()
2292//
2293// Description:
2294//   Takes a vertically oriented shape, and re-orients and aligns it.
2295//   This is useful for making a custom shape available in various
2296//   orientations and alignments without extra translate()s and rotate()s.
2297//   Children should be vertically (Z-axis) oriented, and centered.
2298//   Non-extremity alignment points should be named via the `alignments` arg.
2299//   Named alignments, as well as `ALIGN_NEG`/`ALIGN_POS` are aligned pre-rotation.
2300//
2301// Usage:
2302//   orient_and_align(size, [orient], [align], [center], [noncentered], [orig_orient], [orig_align], [alignments]) ...
2303//
2304// Arguments:
2305//   size = The size of the part.
2306//   orient = The axis to align to.  Use ORIENT_ constants from constants.scad
2307//   align = The side of the origin the part should be aligned with.
2308//   center = If given, overrides `align`.  If true, centers vertically.  If false, `align` will be set to the value in `noncentered`.
2309//   noncentered = The value to set `align` to if `center` == `false`.  Default: `V_UP`.
2310//   orig_orient = The original orientation of the part.  Default: `ORIENT_Z`.
2311//   orig_align = The original alignment of the part.  Default: `V_CENTER`.
2312//   alignments = A list of `["name", [X,Y,Z]]` alignment-label/offset pairs.
2313//
2314// Example:
2315//   #cylinder(d=5, h=10);
2316//   orient_and_align([5,5,10], orient=ORIENT_Y, align=V_BACK, orig_align=V_UP) cylinder(d=5, h=10);
2317module orient_and_align(
2318	size=undef, orient=ORIENT_Z, align=V_CENTER,
2319	center=undef, noncentered=ALIGN_POS,
2320	orig_orient=ORIENT_Z, orig_align=V_CENTER,
2321	alignments=[]
2322) {
2323	algn = is_def(center)? (center? V_CENTER : noncentered) : align;
2324    if (orig_align != V_CENTER) {
2325		orient_and_align(size=size, orient=orient, align=algn) {
2326			translate(vmul(size/2, -orig_align)) children();
2327		}
2328    } else if (orig_orient != ORIENT_Z) {
2329		rotsize = (
2330			(orig_orient==ORIENT_X)? [size[1], size[2], size[0]] :
2331			(orig_orient==ORIENT_Y)? [size[0], size[2], size[1]] :
2332			vabs(rotate_points3d([size], orig_orient, reverse=true)[0])
2333		);
2334		orient_and_align(size=rotsize, orient=orient, align=algn) {
2335			rot(orig_orient,reverse=true) children();
2336		}
2337	} else if (is_scalar(algn)) {
2338		// If align is a number and not a vector, then translate PRE-rotation.
2339		orient_and_align(size=size, orient=orient) {
2340			translate(vmul(size/2, algn*V_UP)) children();
2341		}
2342	} else if (is_str(algn)) {
2343		// If align is a string, look for an alignments label that matches.
2344		found = search([algn], alignments, num_returns_per_match=1);
2345		if (found != [[]]) {
2346			orient_and_align(size=size, orient=orient) {
2347				idx = found[0];
2348				delta = alignments[idx][1];
2349				translate(-delta) children();
2350			}
2351		} else {
2352			assertion(1==0, str("Alignment label '", algn, "' is not known.", (alignments? str("  Try one of ", [for (v=alignments) v[0]], ".") : "")));
2353		}
2354	} else if (orient != ORIENT_Z) {
2355		rotsize = (
2356			(orient==ORIENT_X)? [size[2], size[0], size[1]] :
2357			(orient==ORIENT_Y)? [size[0], size[2], size[1]] :
2358			vabs(rotate_points3d([size], orient)[0])
2359		);
2360		orient_and_align(size=rotsize, align=algn) {
2361			rotate(orient) children();
2362		}
2363	} else if (is_def(algn) && algn != [0,0,0]) {
2364		translate(vmul(size/2, algn)) children();
2365	} else {
2366		children();
2367	}
2368}
2369
2370
2371
2372// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap