1//////////////////////////////////////////////////////////////////////
   2// LibFile: distributors.scad
   3//   Functions and modules to distribute children or copies of children onto
   4//   a line, a grid, or an arbitrary path.  The $idx mechanism means that
   5//   the "copies" of children can vary.  Also includes shortcuts for mirroring.  
   6// Includes:
   7//   include <BOSL2/std.scad>
   8// FileGroup: Basic Modeling
   9// FileSummary: Copy or distribute objects onto a line, grid, or path.  Mirror shortcuts. 
  10// FileFootnotes: STD=Included in std.scad
  11//////////////////////////////////////////////////////////////////////
  12
  13// Section: Adaptive Children Using `$` Variables
  14//   The distributor methods create multiple copies of their children and place them in various ways.  While many models 
  15//   require multiple identical copies of an object, this framework is more powerful than
  16//   might be immediately obvious because of `$` variables.  The distributors set `$` variables that the children can use to change their
  17//   behavior from one child to the next within a single distributor invocation.  This means the copies need not be identical.  
  18//   The {{xcopies()}} module sets `$idx` to the index number of the copy, and in the examples below we use `$idx`, but the various
  19//   distributors offer a variety of `$` variables that you can use in your children.  Check the "Side Effects" section for each module
  20//   to learn what variables that module provides.
  21//   .
  22//   Two gotchas may lead to models that don't behave as expected.  While `if` statements work to control modules, you cannot
  23//   use them to make variable assignments in your child object.  If you write a statement like 
  24//   ```
  25//   if (condition) { c="red";}
  26//   else {c="green";}
  27//   ```
  28//   then the `c` variable is set only in the scope of the `if` and `else` clauses and is not available later on when you actually
  29//   try to use it.  Instead you must use the ternary operator and write:
  30//   ```
  31//   c = condition ? "red" : "green";
  32//   ```
  33//   The second complication is
  34//   that in OpenSCAD version 2021.01 and earlier, assignments in children were executed before their parent.  This means 
  35//   that `$` variables like `$idx` are not available in assignments because the parent hasn't run to set them, so if you use them
  36//   you will get a warning about an unknown variable.
  37//   Two workarounds exist, neither of which are needed in newer versions of OpenSCAD.  The workarounds solve the problem because
  38//   **modules** execute after their parent, so the `$` variables **are** available in modules.  You can put your assignments
  39//   in a `let()` module, or you can wrap your child in a `union()`.  Both methods appear below.
  40// Figure(2D,NoScales): This example shows how we can use `$idx` to produce **different** geometry at each index.
  41//   xcopies(n=10, spacing=10)
  42//     text(str($idx));
  43// Continues:
  44//   ```
  45//   xcopies(n=10, spacing=10)
  46//     text(str($idx));
  47//   ```
  48// Figure(2D,NoScales): Here the children are sometimes squares and sometimes circles as determined by the conditional `if` module. This use of `if` is OK because no variables are assigned.  
  49//   xcopies(n=4, spacing=10)
  50//     if($idx%2==0) circle(r=3,$fn=16);
  51//     else rect(6);
  52// Continues:
  53//   ```
  54//   xcopies(n=4, spacing=10)
  55//     if($idx%2==0) circle(r=3,$fn=16);
  56//     else rect(6);
  57//   ```
  58// Figure(2D,NoScales): Suppose we would like to color odd and even index copies differently.  In this example we compute the color for a given child from `$idx` using the ternary operator.  The `let()` module is a module that sets variables and makes them available to its children.  Note that multiple assignments in `let()` are separated by commas, not semicolons.  
  59//   xcopies(n=6, spacing=10){
  60//       let(c = $idx % 2 == 0 ? "red" : "green")
  61//           color(c) rect(6);
  62//   }
  63// Continues:
  64//   ```
  65//   xcopies(n=6, spacing=10){
  66//       let(c = $idx % 2 == 0 ? "red" : "green")
  67//           color(c) rect(6);
  68//   }
  69//   ```
  70// Figure(2D,NoScales):  This example shows how you can change the position of children adaptively.  If you want to avoid repeating your code for each case, this requires storing a transformation matrix in a variable and then applying it using `multmatrix()`.  We wrap our code in `union()` to ensure that it works in OpenSCAD 2021.01.  
  71//   xcopies(n=5,spacing=10)
  72//     union()
  73//     {
  74//       shiftback = $idx%2==0 ? back(10) : IDENT;
  75//       spin = zrot(180*$idx/4);
  76//       multmatrix(shiftback*spin) stroke([[-4,0],[4,0]],endcap2="arrow2",width=3/4, color="red");
  77//     }
  78// Continues:
  79//   ```
  80//   xcopies(n=5,spacing=10)
  81//     union()
  82//     {
  83//       shiftback = $idx%2==0 ? back(10) : IDENT;
  84//       spin = zrot(180*$idx/4);
  85//       multmatrix(shiftback*spin) stroke([[-4,0],[4,0]],endcap2="arrow2",width=3/4,color="red");
  86//     }
  87//   ```
  88
  89
  90//////////////////////////////////////////////////////////////////////
  91// Section: Translating copies of all the children
  92//////////////////////////////////////////////////////////////////////
  93
  94// Function&Module: move_copies()
  95// Synopsis: Translates copies of all children.
  96// SynTags: MatList, Trans
  97// Topics: Transformations, Distributors, Translation, Copiers
  98// See Also: xcopies(), ycopies(), zcopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies()
  99//
 100// Usage:
 101//   move_copies(a) CHILDREN;
 102// Usage: As a function to translate points, VNF, or Bezier patches
 103//   copies = move_copies(a, p=);
 104// Usage: Get Translation Matrices
 105//   mats = move_copies(a);
 106// Description:
 107//   When called as a module, translates copies of all children to each given translation offset.
 108//   When called as a function, with no `p=` argument, returns a list of transformation matrices, one for each copy.
 109//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
 110//
 111// Arguments:
 112//   a = Array of XYZ offset vectors. Default `[[0,0,0]]`
 113//   ---
 114//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 115//
 116// Side Effects:
 117//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 118//   `$idx` is set to the index number of each child being copied.
 119//
 120//
 121// Example:
 122//   #sphere(r=10);
 123//   move_copies([[-25,-25,0], [25,-25,0], [0,0,50], [0,25,0]]) sphere(r=10);
 124module move_copies(a=[[0,0,0]])
 125{
 126    req_children($children);
 127    assert(is_list(a));
 128    for ($idx = idx(a)) {
 129        $pos = a[$idx];
 130        assert(is_vector($pos),"move_copies offsets should be a 2d or 3d vector.");
 131        translate($pos) children();
 132    }
 133}
 134
 135function move_copies(a=[[0,0,0]],p=_NO_ARG) =
 136    assert(is_list(a))
 137    let(
 138        mats = [
 139            for (pos = a)
 140            assert(is_vector(pos),"move_copies offsets should be a 2d or 3d vector.")
 141            translate(pos)
 142        ]
 143    )
 144    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
 145
 146
 147// Function&Module: xcopies()
 148// Synopsis: Places copies of children along the X axis. 
 149// SynTags: MatList, Trans
 150// Topics: Transformations, Distributors, Translation, Copiers
 151// See Also: move_copies(), ycopies(), zcopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies()
 152//
 153// Usage:
 154//   xcopies(spacing, [n], [sp=]) CHILDREN;
 155//   xcopies(l=, [n=], [sp=]) CHILDREN;
 156//   xcopies(LIST) CHILDREN;
 157// Usage: As a function to translate points, VNF, or Bezier patches
 158//   copies = xcopies(spacing, [n], [sp=], p=);
 159//   copies = xcopies(l=, [n=], [sp=], p=);
 160//   copies = xcopies(LIST, p=);
 161// Usage: Get Translation Matrices
 162//   mats = xcopies(spacing, [n], [sp=]);
 163//   mats = xcopies(l=, [n=], [sp=]);
 164//   mats = xcopies(LIST);
 165// Description:
 166//   When called as a module, places `n` copies of the children along a line on the X axis.
 167//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
 168//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
 169//
 170// Arguments:
 171//   spacing = Given a scalar, specifies a uniform spacing between copies. Given a list of scalars, each one gives a specific position along the line. (Default: 1.0)
 172//   n = Number of copies to place. (Default: 2)
 173//   ---
 174//   l = If given, the length to place copies over.
 175//   sp = If given as a point, copies will be placed on a line to the right of starting position `sp`.  If given as a scalar, copies will be placed on a line segment to the right of starting position `[sp,0,0]`.  If not given, copies will be placed along a line segment that is centered at [0,0,0].
 176//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 177//
 178// Side Effects:
 179//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 180//   `$idx` is set to the index number of each child being copied.
 181//
 182// Examples:
 183//   xcopies(20) sphere(3);
 184//   xcopies(20, n=3) sphere(3);
 185//   xcopies(spacing=15, l=50) sphere(3);
 186//   xcopies(n=4, l=30, sp=[0,10,0]) sphere(3);
 187// Example:
 188//   xcopies(10, n=3) {
 189//       cube(size=[1,3,1],center=true);
 190//       cube(size=[3,1,1],center=true);
 191//   }
 192// Example:
 193//   xcopies([1,2,3,5,7]) sphere(d=1);
 194module xcopies(spacing, n, l, sp)
 195{
 196    assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
 197    assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")  
 198    req_children($children);
 199    dir = RIGHT;
 200    sp = is_finite(sp)? (sp*dir) : sp;
 201    if (is_vector(spacing)) {
 202        translate(default(sp,[0,0,0])) {
 203            for (i = idx(spacing)) {
 204                $idx = i;
 205                $pos = spacing[i]*dir;
 206                translate($pos) children();
 207            }
 208        }
 209    } else {
 210        line_copies(
 211            l=u_mul(l,dir),
 212            spacing=u_mul(spacing,dir),
 213            n=n, p1=sp
 214        ) children();
 215    }
 216}
 217
 218
 219function xcopies(spacing, n, l, sp, p=_NO_ARG) =
 220    assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
 221    assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")  
 222    let(
 223        dir = RIGHT,
 224        sp = is_finite(sp)? (sp*dir) : sp,
 225        mats = is_vector(spacing)
 226          ? let(sp = default(sp,[0,0,0])) [for (n = spacing) translate(sp + n*dir)]
 227          : line_copies(l=u_mul(l,dir), spacing=u_mul(spacing,dir), n=n, p1=sp)
 228    )
 229    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
 230
 231
 232// Function&Module: ycopies()
 233// Synopsis: Places copies of children along the Y axis. 
 234// SynTags: MatList, Trans
 235// Topics: Transformations, Distributors, Translation, Copiers
 236// See Also: move_copies(), xcopies(), zcopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies()
 237//
 238// Usage:
 239//   ycopies(spacing, [n], [sp=]) CHILDREN;
 240//   ycopies(l=, [n=], [sp=]) CHILDREN;
 241//   ycopies(LIST) CHILDREN;
 242// Usage: As a function to translate points, VNF, or Bezier patches
 243//   copies = ycopies(spacing, [n], [sp=], p=);
 244//   copies = ycopies(l=, [n=], [sp=], p=);
 245//   copies = ycopies(LIST, p=);
 246// Usage: Get Translation Matrices
 247//   mats = ycopies(spacing, [n], [sp=]);
 248//   mats = ycopies(l=, [n=], [sp=]);
 249//   mats = ycopies(LIST);
 250// Description:
 251//   When called as a module, places `n` copies of the children along a line on the Y axis.
 252//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
 253//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
 254//
 255// Arguments:
 256//   spacing = Given a scalar, specifies a uniform spacing between copies. Given a list of scalars, each one gives a specific position along the line. (Default: 1.0)
 257//   n = Number of copies to place on the line. (Default: 2)
 258//   ---
 259//   l = If given, the length to place copies over.
 260//   sp = If given as a point, copies will be place on a line back from starting position `sp`.  If given as a scalar, copies will be placed on a line back from starting position `[0,sp,0]`.  If not given, copies will be placed along a line that is centered at [0,0,0].
 261//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 262//
 263// Side Effects:
 264//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 265//   `$idx` is set to the index number of each child being copied.
 266//
 267// Examples:
 268//   ycopies(20) sphere(3);
 269//   ycopies(20, n=3) sphere(3);
 270//   ycopies(spacing=15, l=50) sphere(3);
 271//   ycopies(n=4, l=30, sp=[10,0,0]) sphere(3);
 272// Example:
 273//   ycopies(10, n=3) {
 274//       cube(size=[1,3,1],center=true);
 275//       cube(size=[3,1,1],center=true);
 276//   }
 277// Example:
 278//   ycopies([1,2,3,5,7]) sphere(d=1);
 279module ycopies(spacing, n, l, sp)
 280{
 281    assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
 282    assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")  
 283    req_children($children);  
 284    dir = BACK;
 285    sp = is_finite(sp)? (sp*dir) : sp;
 286    if (is_vector(spacing)) {
 287        translate(default(sp,[0,0,0])) {
 288            for (i = idx(spacing)) {
 289                $idx = i;
 290                $pos = spacing[i]*dir;
 291                translate($pos) children();
 292            }
 293        }
 294    } else {
 295        line_copies(
 296            l=u_mul(l,dir),
 297            spacing=u_mul(spacing,dir),
 298            n=n, p1=sp
 299        ) children();
 300    }
 301}
 302
 303
 304function ycopies(spacing, n, l, sp, p=_NO_ARG) =
 305    assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
 306    assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")  
 307    let(
 308        dir = BACK,
 309        sp = is_finite(sp)? (sp*dir) : sp,
 310        mats = is_vector(spacing)
 311          ? let(sp = default(sp,[0,0,0])) [for (n = spacing) translate(sp + n*dir)]
 312          : line_copies(l=u_mul(l,dir), spacing=u_mul(spacing,dir), n=n, p1=sp)
 313    )
 314    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
 315
 316
 317// Function&Module: zcopies()
 318// Synopsis: Places copies of children along the Z axis. 
 319// SynTags: MatList, Trans
 320// Topics: Transformations, Distributors, Translation, Copiers
 321// See Also: move_copies(), xcopies(), ycopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies()
 322//
 323// Usage:
 324//   zcopies(spacing, [n], [sp=]) CHILDREN;
 325//   zcopies(l=, [n=], [sp=]) CHILDREN;
 326//   zcopies(LIST) CHILDREN;
 327// Usage: As a function to translate points, VNF, or Bezier patches
 328//   copies = zcopies(spacing, [n], [sp=], p=);
 329//   copies = zcopies(l=, [n=], [sp=], p=);
 330//   copies = zcopies(LIST, p=);
 331// Usage: Get Translation Matrices
 332//   mats = zcopies(spacing, [n], [sp=]);
 333//   mats = zcopies(l=, [n=], [sp=]);
 334//   mats = zcopies(LIST);
 335// Description:
 336//   When called as a module, places `n` copies of the children along a line on the Z axis.
 337//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
 338//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
 339//
 340// Arguments:
 341//   spacing = Given a scalar, specifies a uniform spacing between copies. Given a list of scalars, each one gives a specific position along the line. (Default: 1.0)
 342//   n = Number of copies to place. (Default: 2)
 343//   ---
 344//   l = If given, the length to place copies over.
 345//   sp = If given as a point, copies will be placed on a line up from starting position `sp`.  If given as a scalar, copies will be placed on a line up from starting position `[0,0,sp]`.  If not given, copies will be placed on a line that is centered at [0,0,0].
 346//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 347//
 348// Side Effects:
 349//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 350//   `$idx` is set to the index number of each child being copied.
 351//
 352// Examples:
 353//   zcopies(20) sphere(3);
 354//   zcopies(20, n=3) sphere(3);
 355//   zcopies(spacing=15, l=50) sphere(3);
 356//   zcopies(n=4, l=30, sp=[10,0,0]) sphere(3);
 357// Example:
 358//   zcopies(10, n=3) {
 359//       cube(size=[1,3,1],center=true);
 360//       cube(size=[3,1,1],center=true);
 361//   }
 362// Example: Cubic sphere packing
 363//   s = 20;
 364//   s2 = s * sin(45);
 365//   zcopies(s2,n=8)
 366//       grid_copies([s2,s2],n=8,stagger=($idx%2)? true : "alt")
 367//          sphere(d=s);
 368// Example: Hexagonal sphere packing
 369//   s = 20;
 370//   xyr = adj_ang_to_hyp(s/2,30);
 371//   h = hyp_adj_to_opp(s,xyr);
 372//   zcopies(h,n=8)
 373//       back(($idx%2)*xyr*cos(60))
 374//           grid_copies(s,n=[12,7],stagger=($idx%2)? "alt" : true)
 375//               sphere(d=s);
 376// Example:
 377//   zcopies([1,2,3,5,7]) sphere(d=1);
 378module zcopies(spacing, n, l, sp)
 379{
 380    assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
 381    assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")  
 382    req_children($children);  
 383    dir = UP;
 384    sp = is_finite(sp)? (sp*dir) : sp;
 385    if (is_vector(spacing)) {
 386        translate(default(sp,[0,0,0])) {
 387            for (i = idx(spacing)) {
 388                $idx = i;
 389                $pos = spacing[i]*dir;
 390                translate($pos) children();
 391            }
 392        }
 393    } else {
 394        line_copies(
 395            l=u_mul(l,dir),
 396            spacing=u_mul(spacing,dir),
 397            n=n, p1=sp
 398        ) children();
 399    }
 400}
 401
 402
 403function zcopies(spacing, n, l, sp, p=_NO_ARG) =
 404    assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
 405    assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")  
 406    let(
 407        dir = UP,
 408        sp = is_finite(sp)? (sp*dir) : sp,
 409        mats = is_vector(spacing)
 410          ? let(sp = default(sp,[0,0,0])) [for (n = spacing) translate(sp + n*dir)]
 411          : line_copies(l=u_mul(l,dir), spacing=u_mul(spacing,dir), n=n, p1=sp)
 412    )
 413    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
 414
 415
 416
 417// Function&Module: line_copies()
 418// Synopsis: Places copies of children along an arbitrary line. 
 419// SynTags: MatList, Trans
 420// Topics: Transformations, Distributors, Translation, Copiers
 421// See Also: move_copies(), xcopies(), ycopies(), zcopies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies()
 422//
 423// Usage: Place `n` copies at a given spacing along the line
 424//   line_copies(spacing, [n], [p1=]) CHILDREN;
 425// Usage: Place as many copies as will fit at a given spacing
 426//   line_copies(spacing, [l=], [p1=]) CHILDREN;
 427// Usage: Place `n` copies along the length of the line
 428//   line_copies([n=], [l=], [p1=]) CHILDREN;
 429// Usage: Place `n` copies along the line from `p1` to `p2`
 430//   line_copies([n=], [p1=], [p2=]) CHILDREN;
 431// Usage: Place copies at the given spacing, centered along the line from `p1` to `p2`
 432//   line_copies([spacing], [p1=], [p2=]) CHILDREN;
 433// Usage: As a function to translate points, VNF, or Bezier patches
 434//   copies = line_copies([spacing], [n], [p1=], p=);
 435//   copies = line_copies([spacing], [l=], [p1=], p=);
 436//   copies = line_copies([n=], [l=], [p1=], p=);
 437//   copies = line_copies([n=], [p1=], [p2=], p=);
 438//   copies = line_copies([spacing], [p1=], [p2=], p=);
 439// Usage: Get Translation Matrices
 440//   mats = line_copies([spacing], [n], [p1=]);
 441//   mats = line_copies([spacing], [l=], [p1=]);
 442//   mats = line_copies([n=], [l=], [p1=]);
 443//   mats = line_copies([n=], [p1=], [p2=]);
 444//   mats = line_copies([spacing], [p1=], [p2=]);
 445// Description:
 446//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
 447//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
 448//   When called as a module, copies `children()` at one or more evenly spaced positions along a line.
 449//   By default, the line will be centered at the origin, unless the starting point `p1` is given.
 450//   The line will be pointed towards `RIGHT` (X+) unless otherwise given as a vector in `l`,
 451//   `spacing`, or `p1`/`p2`.  The psotion of the copies is specified in one of several ways:
 452//   .
 453//   If You Know...                   | Then Use Something Like...
 454//   -------------------------------- | --------------------------------
 455//   Spacing distance, Count          | `line_copies(spacing=10, n=5) ...` or `line_copies(10, n=5) ...`
 456//   Spacing vector, Count            | `line_copies(spacing=[10,5], n=5) ...` or `line_copies([10,5], n=5) ...`
 457//   Spacing distance, Line length    | `line_copies(spacing=10, l=50) ...` or `line_copies(10, l=50) ...`
 458//   Spacing distance, Line vector    | `line_copies(spacing=10, l=[50,30]) ...` or `line_copies(10, l=[50,30]) ...`
 459//   Spacing vector, Line length      | `line_copies(spacing=[10,5], l=50) ...` or `line_copies([10,5], l=50) ...`
 460//   Line length, Count               | `line_copies(l=50, n=5) ...`
 461//   Line vector, Count               | `line_copies(l=[50,40], n=5) ...`
 462//   Line endpoints, Count            | `line_copies(p1=[10,10], p2=[60,-10], n=5) ...`
 463//   Line endpoints, Spacing distance | `line_copies(p1=[10,10], p2=[60,-10], spacing=10) ...`
 464//
 465// Arguments:
 466//   spacing = Either the scalar spacing distance along the X+ direction, or the vector giving both the direction and spacing distance between each set of copies.
 467//   n = Number of copies to distribute along the line. (Default: 2)
 468//   ---
 469//   l = Either the scalar length of the line, or a vector giving both the direction and length of the line.
 470//   p1 = If given, specifies the starting point of the line.
 471//   p2 = If given with `p1`, specifies the ending point of line, and indirectly calculates the line length.
 472//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 473//
 474// Side Effects:
 475//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 476//   `$idx` is set to the index number of each child being copied.
 477//
 478// Examples:
 479//   line_copies(10) sphere(d=1.5);
 480//   line_copies(10, n=5) sphere(d=3);
 481//   line_copies([10,5], n=5) sphere(d=3);
 482//   line_copies(spacing=10, n=6) sphere(d=3);
 483//   line_copies(spacing=[10,5], n=6) sphere(d=3);
 484//   line_copies(spacing=10, l=50) sphere(d=3);
 485//   line_copies(spacing=10, l=[50,30]) sphere(d=3);
 486//   line_copies(spacing=[10,5], l=50) sphere(d=3);
 487//   line_copies(l=50, n=4) sphere(d=3);
 488//   line_copies(l=[50,-30], n=4) sphere(d=3);
 489// Example(FlatSpin,VPD=133):
 490//   line_copies(p1=[0,0,0], p2=[5,5,20], n=6) cuboid([3,2,1]);
 491// Example(FlatSpin,VPD=133):
 492//   line_copies(p1=[0,0,0], p2=[5,5,20], spacing=6) cuboid([3,2,1]);
 493// Example: All children are copied to each position
 494//   line_copies(l=20, n=3) {
 495//       cube(size=[1,3,1],center=true);
 496//       cube(size=[3,1,1],center=true);
 497//   }
 498// Example(2D): The functional form of line_copies() returns a list of transform matrices.
 499//   mats = line_copies([10,5],n=5);
 500//   for (m = mats) multmatrix(m)  circle(d=3);
 501// Example(2D): The functional form of line_copies() returns a list of points if given a point.
 502//   pts = line_copies([10,5],n=5,p=[0,0,0]);
 503//   move_copies(pts) circle(d=3);
 504
 505module line_of(spacing, n, l, p1, p2) {
 506    deprecate("line_copies");
 507    line_copies(spacing, n, l, p1, p2) children();
 508}
 509
 510module line_copies(spacing, n, l, p1, p2)
 511{
 512    req_children($children);
 513    pts = line_copies(spacing=spacing, n=n, l=l, p1=p1, p2=p2, p=[0,0,0]);
 514    for (i=idx(pts)) {
 515        $idx = i;
 516        $pos = pts[i];
 517        translate($pos) children();
 518    }
 519}
 520
 521function line_copies(spacing, n, l, p1, p2, p=_NO_ARG) =
 522    assert(is_undef(spacing) || is_finite(spacing) || is_vector(spacing))
 523    assert(is_undef(n) || is_finite(n))
 524    assert(is_undef(l) || is_finite(l) || is_vector(l))
 525    assert(is_undef(p1) || is_vector(p1))
 526    assert(is_undef(p2) || is_vector(p2))
 527    assert(is_undef(p2) || is_def(p1), "If p2 is given must also give p1")
 528    assert(is_undef(p2) || is_undef(l), "Cannot give both p2 and l")
 529    assert(is_undef(n) || num_defined([l,spacing,p2])==1,"If n is given then must give exactly one of 'l', 'spacing', or the 'p1'/'p2' pair")
 530    assert(is_def(n) || num_defined([l,spacing,p2])>=1,"If n is given then must give at least one of 'l', 'spacing', or the 'p1'/'p2' pair")    
 531    let(
 532        ll = is_def(l)? scalar_vec3(l, 0)
 533           : is_def(spacing) && is_def(n)? (n-1) * scalar_vec3(spacing, 0)
 534           : is_def(p1) && is_def(p2)? point3d(p2-p1)
 535           : undef,
 536        cnt = is_def(n)? n
 537            : is_def(spacing) && is_def(ll) ? floor(norm(ll) / norm(scalar_vec3(spacing, 0)) + 1.000001)
 538            : 2,
 539        spc = cnt<=1? [0,0,0]
 540            : is_undef(spacing) && is_def(ll)? ll/(cnt-1) 
 541            : is_num(spacing) && is_def(ll)? (ll/(cnt-1)) 
 542            : scalar_vec3(spacing, 0)
 543    )
 544    assert(!is_undef(cnt), "Need two of `spacing`, 'l', 'n', or `p1`/`p2` arguments in `line_copies()`.")
 545    let( spos = !is_undef(p1)? point3d(p1) : -(cnt-1)/2 * spc )
 546    [for (i=[0:1:cnt-1]) translate(i * spc + spos, p=p)];
 547
 548
 549
 550// Function&Module: grid_copies()
 551// Synopsis: Places copies of children in an [X,Y] grid. 
 552// SynTags: MatList, Trans
 553// Topics: Transformations, Distributors, Translation, Copiers
 554// See Also: move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies()
 555//
 556// Usage:
 557//   grid_copies(spacing, size=, [stagger=], [scale=], [inside=]) CHILDREN;
 558//   grid_copies(n=, size=, [stagger=], [scale=], [inside=]) CHILDREN;
 559//   grid_copies(spacing, [n], [stagger=], [scale=], [inside=]) CHILDREN;
 560//   grid_copies(n=, inside=, [stagger], [scale]) CHILDREN;
 561// Usage: As a function to translate points, VNF, or Bezier patches
 562//   copies = grid_copies(spacing, size=, [stagger=], [scale=], [inside=], p=);
 563//   copies = grid_copies(n=, size=, [stagger=], [scale=], [inside=], p=);
 564//   copies = grid_copies(spacing, [n], [stagger=], [scale=], [inside=], p=);
 565//   copies = grid_copies(n=, inside=, [stagger], [scale], p=);
 566// Usage: Get Translation Matrices
 567//   mats = grid_copies(spacing, size=, [stagger=], [scale=], [inside=]);
 568//   mats = grid_copies(n=, size=, [stagger=], [scale=], [inside=]);
 569//   mats = grid_copies(spacing, [n], [stagger=], [scale=], [inside=]);
 570//   mats = grid_copies(n=, inside=, [stagger], [scale]);
 571// Description:
 572//   When called as a module, makes a square or hexagonal grid of copies of children, with an optional masking polygon or region.
 573//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
 574//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
 575//
 576// Arguments:
 577//   spacing = Distance between copies in [X,Y] or scalar distance.
 578//   n = How many columns and rows of copies to make.  Can be given as `[COLS,ROWS]`, or just as a scalar that specifies both.  If staggered, count both staggered and unstaggered columns and rows.  Default: 2 (3 if staggered)
 579//   size = The [X,Y] size to spread the copies over.
 580//   ---
 581//   stagger = If true, make a staggered (hexagonal) grid.  If false, make square grid.  If `"alt"`, makes alternate staggered pattern.  Default: false
 582//   inside = If given a list of polygon points, or a region, only creates copies whose center would be inside the polygon or region.  Polygon can be concave and/or self crossing.
 583//   nonzero = If inside is set to a polygon with self-crossings then use the nonzero method for deciding if points are in the polygon.  Default: false
 584//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 585//
 586// Side Effects:
 587//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
 588//   `$col` is set to the integer column number for each child.
 589//   `$row` is set to the integer row number for each child.
 590//   `$idx` is set to a unique index for each child, progressing across rows first, from the bottom
 591//
 592// Examples:
 593//   grid_copies(size=50, spacing=10) cylinder(d=10, h=1);
 594//   grid_copies(size=50, spacing=[10,15]) cylinder(d=10, h=1);
 595//   grid_copies(spacing=10, n=[13,7], stagger=true) cylinder(d=6, h=5);
 596//   grid_copies(spacing=10, n=[13,7], stagger="alt") cylinder(d=6, h=5);
 597//   grid_copies(size=50, n=11, stagger=true) cylinder(d=5, h=1);
 598//
 599// Example:
 600//   poly = [[-25,-25], [25,25], [-25,25], [25,-25]];
 601//   grid_copies(spacing=5, stagger=true, inside=poly)
 602//      zrot(180/6) cylinder(d=5, h=1, $fn=6);
 603//   %polygon(poly);
 604//
 605// Example: Using `$row` and `$col`
 606//   grid_copies(spacing=8, n=8)
 607//       color(($row+$col)%2?"black":"red")
 608//           cube([8,8,0.01], center=false);
 609//
 610// Example: Makes a grid of hexagon pillars whose tops are all angled to reflect light at [0,0,50], if they were shiny.
 611//   hexregion = circle(r=50.01,$fn=6);
 612//   grid_copies(spacing=10, stagger=true, inside=hexregion)
 613//     union() {   // Needed for OpenSCAD 2021.01 as noted above
 614//       ref_v = (unit([0,0,50]-point3d($pos)) + UP)/2;
 615//       half_of(v=-ref_v, cp=[0,0,5])
 616//           zrot(180/6)
 617//               cylinder(h=20, d=10/cos(180/6)+0.01, $fn=6);
 618//     }
 619
 620module grid2d(spacing, n, size, stagger=false, inside=undef, nonzero)
 621{
 622   deprecate("grid_copies");
 623   grid_copies(spacing, n, size, stagger, inside, nonzero) children();
 624}   
 625
 626module grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero)
 627{
 628    req_children($children);    
 629    dummy = assert(in_list(stagger, [false, true, "alt"]));
 630    bounds = is_undef(inside)? undef :
 631        is_path(inside)? pointlist_bounds(inside) :
 632        assert(is_region(inside))
 633        pointlist_bounds(flatten(inside));
 634    nonzero = is_path(inside) ? default(nonzero,false)
 635            : assert(is_undef(nonzero), "nonzero only allowed if inside is a polygon")
 636              false;
 637    size = is_num(size)? [size, size] :
 638        is_vector(size)? assert(len(size)==2) size :
 639        bounds!=undef? [
 640            for (i=[0:1]) 2*max(abs(bounds[0][i]),bounds[1][i])
 641        ] : undef;
 642    spacing = is_num(spacing)? (
 643            stagger!=false? polar_to_xy(spacing,60) :
 644            [spacing,spacing]
 645        ) :
 646        is_vector(spacing)? assert(len(spacing)==2) spacing :
 647        size!=undef? (
 648            is_num(n)? v_div(size,(n-1)*[1,1]) :
 649            is_vector(n)? assert(len(n)==2) v_div(size,n-[1,1]) :
 650            v_div(size,(stagger==false? [1,1] : [2,2]))
 651        ) :
 652        undef;
 653    n = is_num(n)? [n,n] :
 654        is_vector(n)? assert(len(n)==2) n :
 655        size!=undef && spacing!=undef? v_floor(v_div(size,spacing))+[1,1] :
 656        [2,2];
 657    offset = v_mul(spacing, n-[1,1])/2;
 658
 659    poslist = 
 660      stagger==false ? 
 661               [for (row = [0:1:n.y-1], col = [0:1:n.x-1])
 662                   let(
 663                       pos = v_mul([col,row],spacing) - offset
 664                   )
 665                   if (
 666                           is_undef(inside) ||
 667                           (is_path(inside) && point_in_polygon(pos, inside, nonzero=nonzero)>=0) ||
 668                           (is_region(inside) && point_in_region(pos, inside)>=0)
 669                   )
 670                   [pos,row,col]
 671               ]
 672      :
 673        let(  // stagger == true or stagger == "alt"
 674            staggermod = (stagger == "alt")? 1 : 0,
 675            cols1 = ceil(n.x/2),
 676            cols2 = n.x - cols1
 677         )
 678         [for (row = [0:1:n.y-1])
 679              let(
 680                rowcols = ((row%2) == staggermod)? cols1 : cols2
 681              )
 682              if (rowcols > 0) 
 683                for (col = [0:1:rowcols-1])
 684                  let(
 685                    rowdx = (row%2 != staggermod)? spacing.x : 0,
 686                    pos = v_mul([2*col,row],spacing) + [rowdx,0] - offset
 687                  )
 688                    if (
 689                        is_undef(inside) ||
 690                        (is_path(inside) && point_in_polygon(pos, inside, nonzero=nonzero)>=0) ||
 691                        (is_region(inside) && point_in_region(pos, inside)>=0)
 692                    )
 693                    [pos, row, col * 2 + ((row%2!=staggermod)? 1 : 0)]
 694        ];
 695    for(i=idx(poslist)){
 696      $idx=i;
 697      $pos=poslist[i][0];
 698      $row=poslist[i][1];
 699      $col=poslist[i][2];
 700      translate(poslist[i][0])children();
 701    }
 702}
 703
 704
 705function grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero, p=_NO_ARG) =
 706    let(
 707        dummy = assert(in_list(stagger, [false, true, "alt"])),
 708        bounds = is_undef(inside)? undef :
 709            is_path(inside)? pointlist_bounds(inside) :
 710            assert(is_region(inside))
 711            pointlist_bounds(flatten(inside)),
 712        nonzero = is_path(inside) ? default(nonzero,false)
 713                : assert(is_undef(nonzero), "nonzero only allowed if inside is a polygon")
 714                  false,
 715        size = is_num(size)? [size, size] :
 716            is_vector(size)? assert(len(size)==2) size :
 717            bounds!=undef? [
 718                for (i=[0:1]) 2*max(abs(bounds[0][i]),bounds[1][i])
 719            ] : undef,
 720        spacing = is_num(spacing)? (
 721                stagger!=false? polar_to_xy(spacing,60) :
 722                [spacing,spacing]
 723            ) :
 724            is_vector(spacing)? assert(len(spacing)==2) spacing :
 725            size!=undef? (
 726                is_num(n)? v_div(size,(n-1)*[1,1]) :
 727                is_vector(n)? assert(len(n)==2) v_div(size,n-[1,1]) :
 728                v_div(size,(stagger==false? [1,1] : [2,2]))
 729            ) :
 730            undef,
 731        n = is_num(n)? [n,n] :
 732            is_vector(n)? assert(len(n)==2) n :
 733            size!=undef && spacing!=undef? v_floor(v_div(size,spacing))+[1,1] :
 734            [2,2],
 735        offset = v_mul(spacing, n-[1,1])/2,
 736        mats = stagger == false
 737          ? [
 738                for (row = [0:1:n.y-1], col = [0:1:n.x-1])
 739                let( pos = v_mul([col,row],spacing) - offset )
 740                if (
 741                    is_undef(inside) ||
 742                    (is_path(inside) && point_in_polygon(pos, inside, nonzero=nonzero)>=0) ||
 743                    (is_region(inside) && point_in_region(pos, inside)>=0)
 744                )
 745                translate(pos)
 746            ]
 747          : // stagger == true or stagger == "alt"
 748            let(
 749                staggermod = (stagger == "alt")? 1 : 0,
 750                cols1 = ceil(n.x/2),
 751                cols2 = n.x - cols1
 752            )
 753            [
 754                for (row = [0:1:n.y-1])
 755                let( rowcols = ((row%2) == staggermod)? cols1 : cols2 )
 756                if (rowcols > 0)
 757                for (col = [0:1:rowcols-1])
 758                let(
 759                    rowdx = (row%2 != staggermod)? spacing.x : 0,
 760                    pos = v_mul([2*col,row],spacing) + [rowdx,0] - offset
 761                )
 762                if (
 763                    is_undef(inside) ||
 764                    (is_path(inside) && point_in_polygon(pos, inside, nonzero=nonzero)>=0) ||
 765                    (is_region(inside) && point_in_region(pos, inside)>=0)
 766                )
 767                translate(pos)
 768            ]
 769    )
 770    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
 771
 772
 773//////////////////////////////////////////////////////////////////////
 774// Section: Rotating copies of all children
 775//////////////////////////////////////////////////////////////////////
 776
 777// Function&Module: rot_copies()
 778// Synopsis: Rotates copies of children.
 779// SynTags: MatList, Trans
 780// Topics: Transformations, Distributors, Rotation, Copiers
 781// See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
 782//
 783// Usage:
 784//   rot_copies(rots, [cp=], [sa=], [delta=], [subrot=]) CHILDREN;
 785//   rot_copies(rots, v, [cp=], [sa=], [delta=], [subrot=]) CHILDREN;
 786//   rot_copies(n=, [v=], [cp=], [sa=], [delta=], [subrot=]) CHILDREN;
 787// Usage: As a function to translate points, VNF, or Bezier patches
 788//   copies = rot_copies(rots, [cp=], [sa=], [delta=], [subrot=], p=);
 789//   copies = rot_copies(rots, v, [cp=], [sa=], [delta=], [subrot=], p=);
 790//   copies = rot_copies(n=, [v=], [cp=], [sa=], [delta=], [subrot=], p=);
 791// Usage: Get Translation Matrices
 792//   mats = rot_copies(rots, [cp=], [sa=], [delta=], [subrot=]);
 793//   mats = rot_copies(rots, v, [cp=], [sa=], [delta=], [subrot=]);
 794//   mats = rot_copies(n=, [v=], [cp=], [sa=], [delta=], [subrot=]);
 795// Description:
 796//   When called as a module:
 797//   - Given a list of [X,Y,Z] rotation angles in `rots`, rotates copies of the children to each of those angles, regardless of axis of rotation.
 798//   - Given a list of scalar angles in `rots`, rotates copies of the children to each of those angles around the axis of rotation.
 799//   - If given a vector `v`, that becomes the axis of rotation.  Default axis of rotation is UP.
 800//   - If given a count `n`, makes that many copies, rotated evenly around the axis.
 801//   - If given an offset `delta`, translates each child by that amount before rotating them into place.  This makes rings.
 802//   - If given a centerpoint `cp`, centers the ring around that centerpoint.
 803//   - If `subrot` is true, each child will be rotated in place to keep the same size towards the center when making rings.
 804//   - The first (unrotated) copy will be placed at the relative starting angle `sa`.
 805//   .
 806//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
 807//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
 808//
 809// Arguments:
 810//   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`.
 811//   v = If given, this is the vector of the axis to rotate around.
 812//   cp = Centerpoint to rotate around.  Default: `[0,0,0]`
 813//   ---
 814//   n = Optional number of evenly distributed copies, rotated around the axis.
 815//   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise.  Default: 0
 816//   delta = [X,Y,Z] amount to move away from cp before rotating.  Makes rings of copies.  Default: `[0,0,0]`
 817//   subrot = If false, don't sub-rotate children as they are copied around the ring.  Only makes sense when used with `delta`.  Default: `true`
 818//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 819//
 820// Side Effects:
 821//   `$ang` is set to the rotation angle (or XYZ rotation triplet) of each child copy, and can be used to modify each child individually.
 822//   `$idx` is set to the index value of each child copy.
 823//   `$axis` is set to the axis to rotate around, if `rots` was given as a list of angles instead of a list of [X,Y,Z] rotation angles.
 824//
 825//
 826// Example:
 827//   #cylinder(h=20, r1=5, r2=0);
 828//   rot_copies([[45,0,0],[0,45,90],[90,-45,270]]) cylinder(h=20, r1=5, r2=0);
 829//
 830// Example:
 831//   rot_copies([45, 90, 135], v=DOWN+BACK)
 832//       yrot(90) cylinder(h=20, r1=5, r2=0);
 833//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
 834//
 835// Example:
 836//   rot_copies(n=6, v=DOWN+BACK)
 837//       yrot(90) cylinder(h=20, r1=5, r2=0);
 838//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
 839//
 840// Example:
 841//   rot_copies(n=6, v=DOWN+BACK, delta=[10,0,0])
 842//       yrot(90) cylinder(h=20, r1=5, r2=0);
 843//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
 844//
 845// Example:
 846//   rot_copies(n=6, v=UP+FWD, delta=[10,0,0], sa=45)
 847//       yrot(90) cylinder(h=20, r1=5, r2=0);
 848//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
 849//
 850// Example:
 851//   rot_copies(n=6, v=DOWN+BACK, delta=[20,0,0], subrot=false)
 852//       yrot(90) cylinder(h=20, r1=5, r2=0);
 853//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
 854module rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true)
 855{
 856    req_children($children);  
 857    sang = sa + offset;
 858    angs = !is_undef(n)?
 859        (n<=0? [] : [for (i=[0:1:n-1]) i/n*360+sang]) :
 860        rots==[]? [] :
 861        assert(!is_string(rots), "Argument rots must be an angle, a list of angles, or a range of angles.")
 862        assert(!is_undef(rots[0]), "Argument rots must be an angle, a list of angles, or a range of angles.")
 863        [for (a=rots) a];
 864    for ($idx = idx(angs)) {
 865        $ang = angs[$idx];
 866        $axis = v;
 867        translate(cp) {
 868            rotate(a=$ang, v=v) {
 869                translate(delta) {
 870                    rot(a=(subrot? sang : $ang), v=v, reverse=true) {
 871                        translate(-cp) {
 872                            children();
 873                        }
 874                    }
 875                }
 876            }
 877        }
 878    }
 879}
 880
 881
 882function rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true, p=_NO_ARG) =
 883    let(
 884        sang = sa + offset,
 885        angs = !is_undef(n)?
 886            (n<=0? [] : [for (i=[0:1:n-1]) i/n*360+sang]) :
 887            rots==[]? [] :
 888            assert(!is_string(rots), "Argument rots must be an angle, a list of angles, or a range of angles.")
 889            assert(!is_undef(rots[0]), "Argument rots must be an angle, a list of angles, or a range of angles.")
 890            [for (a=rots) a],
 891        mats = [
 892            for (ang = angs)
 893            translate(cp) *
 894                rot(a=ang, v=v) *
 895                translate(delta) *
 896                rot(a=(subrot? sang : ang), v=v, reverse=true) *
 897                translate(-cp)
 898        ]
 899    )
 900    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
 901
 902
 903// Function&Module: xrot_copies()
 904// Synopsis: Rotates copies of children around the X axis.
 905// SynTags: MatList, Trans
 906// Topics: Transformations, Distributors, Rotation, Copiers
 907// See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
 908//
 909// Usage:
 910//   xrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=]) CHILDREN;
 911//   xrot_copies(n=, [cp=], [r=|d=], [sa=], [subrot=]) CHILDREN;
 912// Usage: As a function to translate points, VNF, or Bezier patches
 913//   copies = xrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=], p=);
 914//   copies = xrot_copies(n=, [cp=], [r=|d=], [sa=], [subrot=], p=);
 915// Usage: Get Translation Matrices
 916//   mats = xrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=]);
 917//   mats = xrot_copies(n=, [cp=], [r=|d=], [sa=], [subrot=]);
 918// Description:
 919//   When called as a module:
 920//   - Given an array of angles, rotates copies of the children to each of those angles around the X axis.
 921//   - If given a count `n`, makes that many copies, rotated evenly around the X axis.
 922//   - If given a radius `r` (or diameter `d`), distributes children around a ring of that size around the X axis.
 923//   - If given a centerpoint `cp`, centers the rotation around that centerpoint.
 924//   - If `subrot` is true, each child will be rotated in place to keep the same size towards the center when making rings.
 925//   - The first (unrotated) copy will be placed at the relative starting angle `sa`.
 926//   .
 927//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
 928//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
 929//
 930// Arguments:
 931//   rots = Optional array of rotation angles, in degrees, to make copies at.
 932//   cp = Centerpoint to rotate around.
 933//   ---
 934//   n = Optional number of evenly distributed copies to be rotated around the ring.
 935//   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.
 936//   r = If given, makes a ring of child copies around the X axis, at the given radius.  Default: 0
 937//   d = If given, makes a ring of child copies around the X axis, at the given diameter.
 938//   subrot = If false, don't sub-rotate children as they are copied around the ring.
 939//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 940//
 941// Side Effects:
 942//   `$idx` is set to the index value of each child copy.
 943//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
 944//   `$axis` is set to the axis vector rotated around.
 945//
 946//
 947// Example:
 948//   xrot_copies([180, 270, 315])
 949//       cylinder(h=20, r1=5, r2=0);
 950//   color("red",0.333) cylinder(h=20, r1=5, r2=0);
 951//
 952// Example:
 953//   xrot_copies(n=6)
 954//       cylinder(h=20, r1=5, r2=0);
 955//   color("red",0.333) cylinder(h=20, r1=5, r2=0);
 956//
 957// Example:
 958//   xrot_copies(n=6, r=10)
 959//       xrot(-90) cylinder(h=20, r1=5, r2=0);
 960//   color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0);
 961//
 962// Example:
 963//   xrot_copies(n=6, r=10, sa=45)
 964//       xrot(-90) cylinder(h=20, r1=5, r2=0);
 965//   color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0);
 966//
 967// Example:
 968//   xrot_copies(n=6, r=20, subrot=false)
 969//       xrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
 970//   color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
 971module xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
 972{
 973    req_children($children);  
 974    r = get_radius(r=r, d=d, dflt=0);
 975    rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot) children();
 976}
 977
 978
 979function xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
 980    let( r = get_radius(r=r, d=d, dflt=0) )
 981    rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot, p=p);
 982
 983
 984// Function&Module: yrot_copies()
 985// Synopsis: Rotates copies of children around the Y axis.
 986// SynTags: MatList, Trans
 987// Topics: Transformations, Distributors, Rotation, Copiers
 988// See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
 989//
 990// Usage:
 991//   yrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=]) CHILDREN;
 992//   yrot_copies(n=, [cp=], [r=|d=], [sa=], [subrot=]) CHILDREN;
 993// Usage: As a function to translate points, VNF, or Bezier patches
 994//   copies = yrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=], p=);
 995//   copies = yrot_copies(n=, [cp=], [r=|d=], [sa=], [subrot=], p=);
 996// Usage: Get Translation Matrices
 997//   mats = yrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=]);
 998//   mats = yrot_copies(n=, [cp=], [r=|d=], [sa=], [subrot=]);
 999// Description:
1000//   When called as a module:
1001//   - Given an array of angles, rotates copies of the children to each of those angles around the Y axis.
1002//   - If given a count `n`, makes that many copies, rotated evenly around the Y axis.
1003//   - If given a radius `r` (or diameter `d`), distributes children around a ring of that size around the Y axis.
1004//   - If given a centerpoint `cp`, centers the rotation around that centerpoint.
1005//   - If `subrot` is true, each child will be rotated in place to keep the same size towards the center when making rings.
1006//   - The first (unrotated) copy will be placed at the relative starting angle `sa`.
1007//   .
1008//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
1009//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
1010//
1011// Arguments:
1012//   rots = Optional array of rotation angles, in degrees, to make copies at.
1013//   cp = Centerpoint to rotate around.
1014//   ---
1015//   n = Optional number of evenly distributed copies to be rotated around the ring.
1016//   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise from X-, when facing the origin from Y+.
1017//   r = If given, makes a ring of child copies around the Y axis, at the given radius.  Default: 0
1018//   d = If given, makes a ring of child copies around the Y axis, at the given diameter.
1019//   subrot = If false, don't sub-rotate children as they are copied around the ring.
1020//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
1021//
1022// Side Effects:
1023//   `$idx` is set to the index value of each child copy.
1024//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
1025//   `$axis` is set to the axis vector rotated around.
1026//
1027//
1028// Example:
1029//   yrot_copies([180, 270, 315])
1030//       cylinder(h=20, r1=5, r2=0);
1031//   color("red",0.333) cylinder(h=20, r1=5, r2=0);
1032//
1033// Example:
1034//   yrot_copies(n=6)
1035//       cylinder(h=20, r1=5, r2=0);
1036//   color("red",0.333) cylinder(h=20, r1=5, r2=0);
1037//
1038// Example:
1039//   yrot_copies(n=6, r=10)
1040//       yrot(-90) cylinder(h=20, r1=5, r2=0);
1041//   color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0);
1042//
1043// Example:
1044//   yrot_copies(n=6, r=10, sa=45)
1045//       yrot(-90) cylinder(h=20, r1=5, r2=0);
1046//   color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0);
1047//
1048// Example:
1049//   yrot_copies(n=6, r=20, subrot=false)
1050//       yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1051//   color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1052module yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
1053{
1054    req_children($children);
1055    r = get_radius(r=r, d=d, dflt=0);
1056    rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot) children();
1057}
1058
1059
1060function yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
1061    let( r = get_radius(r=r, d=d, dflt=0) )
1062    rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot, p=p);
1063
1064
1065// Function&Module: zrot_copies()
1066// Synopsis: Rotates copies of children around the Z axis.
1067// SynTags: MatList, Trans
1068// Topics: Transformations, Distributors, Rotation, Copiers
1069// See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
1070//
1071// Usage:
1072//   zrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=]) CHILDREN;
1073//   zrot_copies(n=, [cp=], [r=|d=], [sa=], [subrot=]) CHILDREN;
1074// Usage: As a function to translate points, VNF, or Bezier patches
1075//   copies = zrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=], p=);
1076//   copies = zrot_copies(n=, [cp=], [r=|d=], [sa=], [subrot=], p=);
1077// Usage: Get Translation Matrices
1078//   mats = zrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=]);
1079//   mats = zrot_copies(n=, [cp=], [r=|d=], [sa=], [subrot=]);
1080//
1081// Description:
1082//   When called as a module:
1083//   - Given an array of angles, rotates copies of the children to each of those angles around the Z axis.
1084//   - If given a count `n`, makes that many copies, rotated evenly around the Z axis.
1085//   - If given a radius `r` (or diameter `d`), distributes children around a ring of that size around the Z axis.
1086//   - If given a centerpoint `cp`, centers the rotation around that centerpoint.
1087//   - If `subrot` is true, each child will be rotated in place to keep the same size towards the center when making rings.
1088//   - The first (unrotated) copy will be placed at the relative starting angle `sa`.
1089//   .
1090//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
1091//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
1092//
1093// Arguments:
1094//   rots = Optional array of rotation angles, in degrees, to make copies at.
1095//   cp = Centerpoint to rotate around.  Default: [0,0,0]
1096//   ---
1097//   n = Optional number of evenly distributed copies to be rotated around the ring.
1098//   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise from X+, when facing the origin from Z+.  Default: 0
1099//   r = If given, makes a ring of child copies around the Z axis, at the given radius.  Default: 0
1100//   d = If given, makes a ring of child copies around the Z axis, at the given diameter.
1101//   subrot = If false, don't sub-rotate children as they are copied around the ring.  Default: true
1102//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
1103//
1104// Side Effects:
1105//   `$idx` is set to the index value of each child copy.
1106//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
1107//   `$axis` is set to the axis vector rotated around.
1108//
1109//
1110// Example:
1111//   zrot_copies([180, 270, 315])
1112//       yrot(90) cylinder(h=20, r1=5, r2=0);
1113//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1114//
1115// Example:
1116//   zrot_copies(n=6)
1117//       yrot(90) cylinder(h=20, r1=5, r2=0);
1118//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1119//
1120// Example:
1121//   zrot_copies(n=6, r=10)
1122//       yrot(90) cylinder(h=20, r1=5, r2=0);
1123//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
1124//
1125// Example:
1126//   zrot_copies(n=6, r=20, sa=45)
1127//       yrot(90) cylinder(h=20, r1=5, r2=0, center=true);
1128//   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0, center=true);
1129//
1130// Example:
1131//   zrot_copies(n=6, r=20, subrot=false)
1132//       yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1133//   color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
1134module zrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
1135{
1136    r = get_radius(r=r, d=d, dflt=0);
1137    rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot) children();
1138}
1139
1140
1141function zrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
1142    let( r = get_radius(r=r, d=d, dflt=0) )
1143    rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot, p=p);
1144
1145
1146// Function&Module: arc_copies()
1147// Synopsis: Distributes duplicates of children along an arc.
1148// SynTags: MatList, Trans
1149// Topics: Transformations, Distributors, Rotation, Copiers
1150// See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
1151//
1152// Usage:
1153//   arc_copies(n, r|d=, [sa=], [ea=], [rot=]) CHILDREN;
1154//   arc_copies(n, rx=|dx=, ry=|dy=, [sa=], [ea=], [rot=]) CHILDREN;
1155// Usage: As a function to translate points, VNF, or Bezier patches
1156//   copies = arc_copies(n, r|d=, [sa=], [ea=], [rot=], p=);
1157//   copies = arc_copies(n, rx=|dx=, ry=|dy=, [sa=], [ea=], [rot=], p=);
1158// Usage: Get Translation Matrices
1159//   mats = arc_copies(n, r|d=, [sa=], [ea=], [rot=]);
1160//   mats = arc_copies(n, rx=|dx=, ry=|dy=, [sa=], [ea=], [rot=]);
1161//
1162//
1163// Description:
1164//   When called as a module, evenly distributes n duplicate children around an ovoid arc on the XY plane.
1165//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
1166//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
1167//
1168// Arguments:
1169//   n = number of copies to distribute around the circle. (Default: 6)
1170//   r = radius of circle (Default: 1)
1171//   ---
1172//   rx = radius of ellipse on X axis. Used instead of r.
1173//   ry = radius of ellipse on Y axis. Used instead of r.
1174//   d = diameter of circle. (Default: 2)
1175//   dx = diameter of ellipse on X axis. Used instead of d.
1176//   dy = diameter of ellipse on Y axis. Used instead of d.
1177//   rot = whether to rotate the copied children.  (Default: true)
1178//   sa = starting angle. (Default: 0.0)
1179//   ea = ending angle. Will distribute copies CCW from sa to ea. (Default: 360.0)
1180//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
1181//
1182// Side Effects:
1183//   `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
1184//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
1185//   `$idx` is set to the index value of each child copy.
1186//
1187//
1188// Example:
1189//   #cube(size=[10,3,3],center=true);
1190//   arc_copies(d=40, n=5) cube(size=[10,3,3],center=true);
1191//
1192// Example:
1193//   #cube(size=[10,3,3],center=true);
1194//   arc_copies(d=40, n=5, sa=45, ea=225) cube(size=[10,3,3],center=true);
1195//
1196// Example:
1197//   #cube(size=[10,3,3],center=true);
1198//   arc_copies(r=15, n=8, rot=false) cube(size=[10,3,3],center=true);
1199//
1200// Example:
1201//   #cube(size=[10,3,3],center=true);
1202//   arc_copies(rx=20, ry=10, n=8) cube(size=[10,3,3],center=true);
1203// Example(2D): Using `$idx` to alternate shapes
1204//   arc_copies(r=50, n=19, sa=0, ea=180)
1205//       if ($idx % 2 == 0) rect(6);
1206//       else circle(d=6);
1207
1208module arc_of(n=6,r,rx,ry,d,dx,dy,sa=0,ea=360,rot=true){
1209    deprecate("arc_copies");
1210    arc_copies(n,r,rx,ry,d,dx,dy,sa,ea,rot) children();
1211}    
1212
1213
1214module arc_copies(
1215    n=6,
1216    r=undef,
1217    rx=undef, ry=undef,
1218    d=undef, dx=undef, dy=undef,
1219    sa=0, ea=360,
1220    rot=true
1221) {
1222    req_children($children);  
1223    rx = get_radius(r1=rx, r=r, d1=dx, d=d, dflt=1);
1224    ry = get_radius(r1=ry, r=r, d1=dy, d=d, dflt=1);
1225    sa = posmod(sa, 360);
1226    ea = posmod(ea, 360);
1227    n = (abs(ea-sa)<0.01)?(n+1):n;
1228    delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1);
1229    for ($idx = [0:1:n-1]) {
1230        $ang = sa + ($idx * delt);
1231        $pos =[rx*cos($ang), ry*sin($ang), 0];
1232        translate($pos) {
1233            zrot(rot? atan2(ry*sin($ang), rx*cos($ang)) : 0) {
1234                children();
1235            }
1236        }
1237    }
1238}
1239
1240
1241function arc_copies(
1242    n=6,
1243    r=undef,
1244    rx=undef, ry=undef,
1245    d=undef, dx=undef, dy=undef,
1246    sa=0, ea=360,
1247    rot=true,
1248    p=_NO_ARG
1249) =
1250    let(
1251        rx = get_radius(r1=rx, r=r, d1=dx, d=d, dflt=1),
1252        ry = get_radius(r1=ry, r=r, d1=dy, d=d, dflt=1),
1253        sa = posmod(sa, 360),
1254        ea = posmod(ea, 360),
1255        n = (abs(ea-sa)<0.01)?(n+1):n,
1256        delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1),
1257        mats = [
1258            for (i = [0:1:n-1])
1259            let(
1260                ang = sa + (i * delt),
1261                pos =[rx*cos(ang), ry*sin(ang), 0],
1262                ang2 = rot? atan2(ry*sin(ang), rx*cos(ang)) : 0
1263            )
1264            translate(pos) * zrot(ang2)
1265        ]
1266    )
1267    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
1268
1269
1270
1271// Function&Module: sphere_copies()
1272// Synopsis: Distributes copies of children over the surface of a sphere. 
1273// SynTags: MatList, Trans
1274// Topics: Transformations, Distributors, Rotation, Copiers
1275// See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
1276//
1277// Usage:
1278//   sphere_copies(n, r|d=, [cone_ang=], [scale=], [perp=]) CHILDREN;
1279// Usage: As a function to translate points, VNF, or Bezier patches
1280//   copies = sphere_copies(n, r|d=, [cone_ang=], [scale=], [perp=], p=);
1281// Usage: Get Translation Matrices
1282//   mats = sphere_copies(n, r|d=, [cone_ang=], [scale=], [perp=]);
1283//
1284// Description:
1285//   When called as a module, spreads children semi-evenly over the surface of a sphere or ellipsoid.
1286//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
1287//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
1288//
1289// Arguments:
1290//   n = How many copies to evenly spread over the surface.
1291//   r = Radius of the sphere to distribute over
1292//   ---
1293//   d = Diameter of the sphere to distribute over
1294//   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
1295//   scale = The [X,Y,Z] scaling factors to reshape the sphere being covered.
1296//   perp = If true, rotate children to be perpendicular to the sphere surface.  Default: true
1297//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
1298//
1299// Side Effects:
1300//   `$pos` is set to the relative post-scaled centerpoint of each child copy, and can be used to modify each child individually.
1301//   `$theta` is set to the theta angle of the child from the center of the sphere.
1302//   `$phi` is set to the pre-scaled phi angle of the child from the center of the sphere.
1303//   `$rad` is set to the pre-scaled radial distance of the child from the center of the sphere.
1304//   `$idx` is set to the index number of each child being copied.
1305//
1306//
1307// Example:
1308//   sphere_copies(n=250, d=100, cone_ang=45, scale=[3,3,1])
1309//       cylinder(d=10, h=10, center=false);
1310//
1311// Example:
1312//   sphere_copies(n=500, d=100, cone_ang=180)
1313//       color(unit(point3d(v_abs($pos))))
1314//           cylinder(d=8, h=10, center=false);
1315
1316module ovoid_spread(n=100, r=undef, d=undef, cone_ang=90, scale=[1,1,1], perp=true)
1317{
1318  deprecate("sphere_copies");
1319  sphere_copies(n,r,d,cone_ang,scale,perp) children();
1320}  
1321
1322
1323module sphere_copies(n=100, r=undef, d=undef, cone_ang=90, scale=[1,1,1], perp=true)
1324{
1325    req_children($children);  
1326    r = get_radius(r=r, d=d, dflt=50);
1327    cnt = ceil(n / (cone_ang/180));
1328
1329    // Calculate an array of [theta,phi] angles for `n` number of
1330    // points, almost evenly spaced across the surface of a sphere.
1331    // This approximation is based on the golden spiral method.
1332    theta_phis = [for (x=[0:1:n-1]) [180*(1+sqrt(5))*(x+0.5)%360, acos(1-2*(x+0.5)/cnt)]];
1333
1334    for ($idx = idx(theta_phis)) {
1335        tp = theta_phis[$idx];
1336        xyz = spherical_to_xyz(r, tp[0], tp[1]);
1337        $pos = v_mul(xyz,point3d(scale,1));
1338        $theta = tp[0];
1339        $phi = tp[1];
1340        $rad = r;
1341        translate($pos) {
1342            if (perp) {
1343                rot(from=UP, to=xyz) children();
1344            } else {
1345                children();
1346            }
1347        }
1348    }
1349}
1350
1351
1352function sphere_copies(n=100, r=undef, d=undef, cone_ang=90, scale=[1,1,1], perp=true, p=_NO_ARG) =
1353    let(
1354        r = get_radius(r=r, d=d, dflt=50),
1355        cnt = ceil(n / (cone_ang/180)),
1356
1357        // Calculate an array of [theta,phi] angles for `n` number of
1358        // points, almost evenly spaced across the surface of a sphere.
1359        // This approximation is based on the golden spiral method.
1360        theta_phis = [for (x=[0:1:n-1]) [180*(1+sqrt(5))*(x+0.5)%360, acos(1-2*(x+0.5)/cnt)]],
1361
1362        mats = [
1363            for (tp = theta_phis)
1364            let(
1365                xyz = spherical_to_xyz(r, tp[0], tp[1]),
1366                pos = v_mul(xyz,point3d(scale,1))
1367            )
1368            translate(pos) *
1369            (perp? rot(from=UP, to=xyz) : ident(4))
1370        ]
1371    )
1372    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
1373
1374
1375
1376// Section: Placing copies of all children on a path
1377
1378
1379// Function&Module: path_copies()
1380// Synopsis: Uniformly distributes copies of children along a path.
1381// SynTags: MatList, Trans
1382// Topics: Transformations, Distributors, Copiers
1383// See Also: line_copies(), move_copies(), xcopies(), ycopies(), zcopies(), grid_copies(), xflip_copy(), yflip_copy(), zflip_copy(), mirror_copy()
1384//
1385// Usage: Uniformly distribute copies 
1386//   path_copies(path, [n], [spacing], [sp], [rotate_children], [closed=]) CHILDREN;
1387// Usage: Place copies at specified locations
1388//   path_copies(path, dist=, [rotate_children=], [closed=]) CHILDREN;
1389// Usage: As a function to translate points, VNF, or Bezier patches
1390//   copies = path_copies(path, [n], [spacing], [sp], [rotate_children], [closed=], p=);
1391//   copies = path_copies(path, dist=, [rotate_children=], [closed=], p=);
1392// Usage: Get Translation Matrices
1393//   mats = path_copies(path, [n], [spacing], [sp], [rotate_children], [closed=]);
1394//   mats = path_copies(path, dist=, [rotate_children=], [closed=]);
1395//
1396// Description:
1397//   When called as a module:
1398//   - Place copies all of the children at points along the path based on path length.  You can specify `dist` as
1399//   - a scalar or distance list and the children will be placed at the specified distances from the start of the path.  Otherwise the children are
1400//   - placed at uniformly spaced points along the path.  If you specify `n` but not `spacing` then `n` copies will be placed
1401//   - with one at path[0] if `closed` is true, or spanning the entire path from start to end if `closed` is false.
1402//   - If you specify `spacing` but not `n` then copies will spread out starting from one set at path[0] for `closed=true` or at the path center for open paths.
1403//   - If you specify `sp` then the copies will start at distance `sp` from the start of the path.
1404//   .
1405//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
1406//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
1407//
1408// Arguments:
1409//   path = path or 1-region where children are placed
1410//   n = number of copies
1411//   spacing = space between copies
1412//   sp = if given, copies will start distance sp from the path start and spread beyond that point
1413//   rotate_children = if true, rotate children to line up with curve normal.  Default: true
1414//   ---
1415//   dist = Specify a list of distances to determine placement of children.  
1416//   closed = If true treat path as a closed curve.  Default: false
1417//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
1418//
1419// Side Effects:
1420//   `$pos` is set to the center of each copy
1421//   `$idx` is set to the index number of each copy.  In the case of closed paths the first copy is at `path[0]` unless you give `sp`.
1422//   `$dir` is set to the direction vector of the path at the point where the copy is placed.
1423//   `$normal` is set to the direction of the normal vector to the path direction that is coplanar with the path at this point
1424//
1425//
1426// Example(2D):
1427//   spiral = [for(theta=[0:360*8]) theta * [cos(theta), sin(theta)]]/100;
1428//   stroke(spiral,width=.25);
1429//   color("red") path_copies(spiral, n=100) circle(r=1);
1430// Example(2D):
1431//   circle = regular_ngon(n=64, or=10);
1432//   stroke(circle,width=1,closed=true);
1433//   color("green") path_copies(circle, n=7, closed=true) circle(r=1+$idx/3);
1434// Example(2D):
1435//   heptagon = regular_ngon(n=7, or=10);
1436//   stroke(heptagon, width=1, closed=true);
1437//   color("purple") path_copies(heptagon, n=9, closed=true) rect([0.5,3],anchor=FRONT);
1438// Example(2D): Direction at the corners is the average of the two adjacent edges
1439//   heptagon = regular_ngon(n=7, or=10);
1440//   stroke(heptagon, width=1, closed=true);
1441//   color("purple") path_copies(heptagon, n=7, closed=true) rect([0.5,3],anchor=FRONT);
1442// Example(2D):  Don't rotate the children
1443//   heptagon = regular_ngon(n=7, or=10);
1444//   stroke(heptagon, width=1, closed=true);
1445//   color("red") path_copies(heptagon, n=9, closed=true, rotate_children=false) rect([0.5,3],anchor=FRONT);
1446// Example(2D): Open path, specify `n`
1447//   sinwav = [for(theta=[0:360]) 5*[theta/180, sin(theta)]];
1448//   stroke(sinwav,width=.1);
1449//   color("red") path_copies(sinwav, n=5) rect([.2,1.5],anchor=FRONT);
1450// Example(2D): Open path, specify `n` and `spacing`
1451//   sinwav = [for(theta=[0:360]) 5*[theta/180, sin(theta)]];
1452//   stroke(sinwav,width=.1);
1453//   color("red") path_copies(sinwav, n=5, spacing=1) rect([.2,1.5],anchor=FRONT);
1454// Example(2D): Closed path, specify `n` and `spacing`, copies centered around circle[0]
1455//   circle = regular_ngon(n=64,or=10);
1456//   stroke(circle,width=.1,closed=true);
1457//   color("red") path_copies(circle, n=10, spacing=1, closed=true) rect([.2,1.5],anchor=FRONT);
1458// Example(2D): Open path, specify `spacing`
1459//   sinwav = [for(theta=[0:360]) 5*[theta/180, sin(theta)]];
1460//   stroke(sinwav,width=.1);
1461//   color("red") path_copies(sinwav, spacing=5) rect([.2,1.5],anchor=FRONT);
1462// Example(2D): Open path, specify `sp`
1463//   sinwav = [for(theta=[0:360]) 5*[theta/180, sin(theta)]];
1464//   stroke(sinwav,width=.1);
1465//   color("red") path_copies(sinwav, n=5, sp=18) rect([.2,1.5],anchor=FRONT);
1466// Example(2D): Open path, specify `dist`
1467//   sinwav = [for(theta=[0:360]) 5*[theta/180, sin(theta)]];
1468//   stroke(sinwav,width=.1);
1469//   color("red") path_copies(sinwav, dist=[1,4,9,16]) rect([.2,1.5],anchor=FRONT);
1470// Example(2D):
1471//   wedge = arc(angle=[0,100], r=10, $fn=64);
1472//   difference(){
1473//     polygon(concat([[0,0]],wedge));
1474//     path_copies(wedge,n=5,spacing=3) fwd(.1) rect([1,4],anchor=FRONT);
1475//   }
1476// Example(Spin,VPD=115): 3d example, with children rotated into the plane of the path
1477//   tilted_circle = lift_plane([[0,0,0], [5,0,5], [0,2,3]],regular_ngon(n=64, or=12));
1478//   path_sweep(regular_ngon(n=16,or=.1),tilted_circle);
1479//   path_copies(tilted_circle, n=15,closed=true) {
1480//      color("blue") cyl(h=3,r=.2, anchor=BOTTOM);      // z-aligned cylinder
1481//      color("red") xcyl(h=10,r=.2, anchor=FRONT+LEFT); // x-aligned cylinder
1482//   }
1483// Example(Spin,VPD=115): 3d example, with rotate_children set to false
1484//   tilted_circle = lift_plane([[0,0,0], [5,0,5], [0,2,3]], regular_ngon(n=64, or=12));
1485//   path_sweep(regular_ngon(n=16,or=.1),tilted_circle);
1486//   path_copies(tilted_circle, n=25,rotate_children=false,closed=true) {
1487//      color("blue") cyl(h=3,r=.2, anchor=BOTTOM);       // z-aligned cylinder
1488//      color("red") xcyl(h=10,r=.2, anchor=FRONT+LEFT);  // x-aligned cylinder
1489//   }
1490
1491module path_spread(path, n, spacing, sp=undef, rotate_children=true, dist, closed){
1492  deprecate("path_copes");
1493  path_copies(path,n,spacing,sp,dist,rotate_children,dist, closed) children();
1494}  
1495
1496
1497module path_copies(path, n, spacing, sp=undef, dist, rotate_children=true, dist, closed)
1498{
1499    req_children($children);  
1500    is_1reg = is_1region(path);
1501    path = is_1reg ? path[0] : path;
1502    closed = default(closed, is_1reg);
1503    length = path_length(path,closed);
1504    distind = is_def(dist) ? sortidx(dist) : undef;
1505    distances =
1506        is_def(dist) ? assert(is_undef(n) && is_undef(spacing) && is_undef(sp), "Can't use n, spacing or undef with dist")
1507                       select(dist,distind)
1508      : is_def(sp)? (   // Start point given
1509            is_def(n) && is_def(spacing)? count(n,sp,spacing) :
1510            is_def(n)? lerpn(sp, length, n) :
1511            list([sp:spacing:length])
1512        )
1513      : is_def(n) && is_undef(spacing)? lerpn(0,length,n,!closed) // N alone given
1514      : (      // No start point and spacing is given, N maybe given
1515        let(
1516            n = is_def(n)? n : floor(length/spacing)+(closed?0:1),
1517            ptlist = count(n,0,spacing),
1518            listcenter = mean(ptlist)
1519        ) closed?
1520            sort([for(entry=ptlist) posmod(entry-listcenter,length)]) :
1521            [for(entry=ptlist) entry + length/2-listcenter ]
1522    );
1523    distOK = min(distances)>=0 && max(distances)<=length;
1524    dummy = assert(distOK,"Cannot fit all of the copies");
1525    cutlist = path_cut_points(path, distances, closed, direction=true);
1526    planar = len(path[0])==2;
1527    for(i=[0:1:len(cutlist)-1]) {
1528        $pos = cutlist[i][0];
1529        $idx = is_def(dist) ? distind[i] : i;
1530        $dir = !rotate_children ? (planar?[1,0]:[1,0,0]) : cutlist[i][2];
1531        $normal = !rotate_children? (planar?[0,1]:[0,0,1]) : cutlist[i][3];
1532        translate($pos) {
1533            if (rotate_children) {
1534                if(planar) {
1535                    rot(from=[0,1],to=cutlist[i][3]) children();
1536                } else {
1537                    frame_map(x=cutlist[i][2], z=cutlist[i][3])
1538                        children();
1539                }
1540            } else {
1541                children();
1542            }
1543        }
1544    }
1545}
1546
1547
1548function path_copies(path, n, spacing, sp=undef, dist, rotate_children=true, dist, closed, p=_NO_ARG) =
1549    let(
1550        is_1reg = is_1region(path),
1551        path = is_1reg ? path[0] : path,
1552        closed = default(closed, is_1reg),
1553        length = path_length(path,closed),
1554        distind = is_def(dist) ? sortidx(dist) : undef,
1555        distances =
1556            is_def(dist) ? assert(is_undef(n) && is_undef(spacing) && is_undef(sp), "Can't use n, spacing or undef with dist")
1557                           select(dist,distind)
1558          : is_def(sp)? (   // Start point given
1559                is_def(n) && is_def(spacing)? count(n,sp,spacing) :
1560                is_def(n)? lerpn(sp, length, n) :
1561                list([sp:spacing:length])
1562            )
1563          : is_def(n) && is_undef(spacing)? lerpn(0,length,n,!closed) // N alone given
1564          : (      // No start point and spacing is given, N maybe given
1565            let(
1566                n = is_def(n)? n : floor(length/spacing)+(closed?0:1),
1567                ptlist = count(n,0,spacing),
1568                listcenter = mean(ptlist)
1569            ) closed?
1570                sort([for(entry=ptlist) posmod(entry-listcenter,length)]) :
1571                [for(entry=ptlist) entry + length/2-listcenter ]
1572          ),
1573        distOK = min(distances)>=0 && max(distances)<=length,
1574        dummy = assert(distOK,"Cannot fit all of the copies"),
1575        cutlist = path_cut_points(path, distances, closed, direction=true),
1576        planar = len(path[0])==2,
1577        mats = [
1578            for(i=[0:1:len(cutlist)-1])
1579            translate(cutlist[i][0]) * (
1580                !rotate_children? ident(4) :
1581                planar? rot(from=[0,1],to=cutlist[i][3]) :
1582                frame_map(x=cutlist[i][2], z=cutlist[i][3])
1583            )
1584        ]
1585    )
1586    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
1587
1588
1589
1590//////////////////////////////////////////////////////////////////////
1591// Section: Making a copy of all children with reflection
1592//////////////////////////////////////////////////////////////////////
1593
1594// Function&Module: xflip_copy()
1595// Synopsis: Makes a copy of children mirrored across the X axis.
1596// SynTags: MatList, Trans
1597// Topics: Transformations, Distributors, Translation, Copiers
1598// See Also: yflip_copy(), zflip_copy(), mirror_copy(), path_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
1599//
1600// Usage:
1601//   xflip_copy([offset], [x]) CHILDREN;
1602// Usage: As a function to translate points, VNF, or Bezier patches
1603//   copies = xflip_copy([offset], [x], p=);
1604// Usage: Get Translation Matrices
1605//   mats = xflip_copy([offset], [x]);
1606//
1607// Description:
1608//   When called as a module, makes a copy of the children, mirrored across the X axis.
1609//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
1610//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
1611//
1612// Arguments:
1613//   offset = Distance to offset children right, before copying.
1614//   x = The X coordinate of the mirroring plane.  Default: 0
1615//   ---
1616//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
1617//
1618// Side Effects:
1619//   `$orig` is true for the original instance of children.  False for the copy.
1620//   `$idx` is set to the index value of each copy.
1621//
1622//
1623// Example:
1624//   xflip_copy() yrot(90) cylinder(h=20, r1=4, r2=0);
1625//   color("blue",0.25) cube([0.01,15,15], center=true);
1626//
1627// Example:
1628//   xflip_copy(offset=5) yrot(90) cylinder(h=20, r1=4, r2=0);
1629//   color("blue",0.25) cube([0.01,15,15], center=true);
1630//
1631// Example:
1632//   xflip_copy(x=-5) yrot(90) cylinder(h=20, r1=4, r2=0);
1633//   color("blue",0.25) left(5) cube([0.01,15,15], center=true);
1634module xflip_copy(offset=0, x=0)
1635{
1636    req_children($children);  
1637    mirror_copy(v=[1,0,0], offset=offset, cp=[x,0,0]) children();
1638}
1639
1640
1641function xflip_copy(offset=0, x=0, p=_NO_ARG) =
1642    mirror_copy(v=[1,0,0], offset=offset, cp=[x,0,0], p=p);
1643
1644
1645// Function&Module: yflip_copy()
1646// Synopsis: Makes a copy of children mirrored across the Y axis.
1647// SynTags: MatList, Trans
1648// Topics: Transformations, Distributors, Translation, Copiers
1649// See Also: xflip_copy(), zflip_copy(), mirror_copy(), path_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
1650//
1651// Usage:
1652//   yflip_copy([offset], [y]) CHILDREN;
1653// Usage: As a function to translate points, VNF, or Bezier patches
1654//   copies = yflip_copy([offset], [y], p=);
1655// Usage: Get Translation Matrices
1656//   mats = yflip_copy([offset], [y]);
1657//
1658// Description:
1659//   When called as a module, makes a copy of the children, mirrored across the Y axis.
1660//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
1661//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
1662//
1663// Arguments:
1664//   offset = Distance to offset children back, before copying.
1665//   y = The Y coordinate of the mirroring plane.  Default: 0
1666//   ---
1667//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
1668//
1669// Side Effects:
1670//   `$orig` is true for the original instance of children.  False for the copy.
1671//   `$idx` is set to the index value of each copy.
1672//
1673//
1674// Example:
1675//   yflip_copy() xrot(-90) cylinder(h=20, r1=4, r2=0);
1676//   color("blue",0.25) cube([15,0.01,15], center=true);
1677//
1678// Example:
1679//   yflip_copy(offset=5) xrot(-90) cylinder(h=20, r1=4, r2=0);
1680//   color("blue",0.25) cube([15,0.01,15], center=true);
1681//
1682// Example:
1683//   yflip_copy(y=-5) xrot(-90) cylinder(h=20, r1=4, r2=0);
1684//   color("blue",0.25) fwd(5) cube([15,0.01,15], center=true);
1685module yflip_copy(offset=0, y=0)
1686{
1687    req_children($children);
1688    mirror_copy(v=[0,1,0], offset=offset, cp=[0,y,0]) children();
1689}
1690
1691
1692function yflip_copy(offset=0, y=0, p=_NO_ARG) =
1693    mirror_copy(v=[0,1,0], offset=offset, cp=[0,y,0], p=p);
1694
1695
1696// Function&Module: zflip_copy()
1697// Synopsis: Makes a copy of children mirrored across the Z axis.
1698// SynTags: MatList, Trans
1699// Topics: Transformations, Distributors, Translation, Copiers
1700// See Also: xflip_copy(), yflip_copy(), mirror_copy(), path_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
1701//
1702// Usage:
1703//   zflip_copy([offset], [z]) CHILDREN;
1704// Usage: As a function to translate points, VNF, or Bezier patches
1705//   copies = zflip_copy([offset], [z], p=);
1706// Usage: Get Translation Matrices
1707//   mats = zflip_copy([offset], [z]);
1708//
1709// Description:
1710//   When called as a module, makes a copy of the children, mirrored across the Z axis.
1711//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
1712//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
1713//
1714// Arguments:
1715//   offset = Distance to offset children up, before copying.
1716//   z = The Z coordinate of the mirroring plane.  Default: 0
1717//   ---
1718//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
1719//
1720// Side Effects:
1721//   `$orig` is true for the original instance of children.  False for the copy.
1722//   `$idx` is set to the index value of each copy.
1723//
1724//
1725// Example:
1726//   zflip_copy() cylinder(h=20, r1=4, r2=0);
1727//   color("blue",0.25) cube([15,15,0.01], center=true);
1728//
1729// Example:
1730//   zflip_copy(offset=5) cylinder(h=20, r1=4, r2=0);
1731//   color("blue",0.25) cube([15,15,0.01], center=true);
1732//
1733// Example:
1734//   zflip_copy(z=-5) cylinder(h=20, r1=4, r2=0);
1735//   color("blue",0.25) down(5) cube([15,15,0.01], center=true);
1736module zflip_copy(offset=0, z=0)
1737{
1738    req_children($children);  
1739    mirror_copy(v=[0,0,1], offset=offset, cp=[0,0,z]) children();
1740}
1741
1742
1743function zflip_copy(offset=0, z=0, p=_NO_ARG) =
1744    mirror_copy(v=[0,0,1], offset=offset, cp=[0,0,z], p=p);
1745
1746
1747// Function&Module: mirror_copy()
1748// Synopsis: Makes a copy of children mirrored across a given plane.
1749// SynTags: MatList, Trans
1750// Topics: Transformations, Distributors, Translation, Copiers
1751// See Also: xflip_copy(), yflip_copy(), zflip_copy(), path_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() 
1752//
1753// Usage:
1754//   mirror_copy(v, [cp], [offset]) CHILDREN;
1755// Usage: As a function to translate points, VNF, or Bezier patches
1756//   copies = mirror_copy(v, [cp], [offset], p=);
1757// Usage: Get Translation Matrices
1758//   mats = mirror_copy(v, [cp], [offset]);
1759//
1760// Description:
1761//   When called as a module, makes a copy of the children, mirrored across the given plane.
1762//   When called as a function, *without* a `p=` argument, returns a list of transformation matrices, one for each copy.
1763//   When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`.
1764//
1765// Arguments:
1766//   v = The normal vector of the plane to mirror across.
1767//   offset = distance to offset away from the plane.
1768//   cp = A point that lies on the mirroring plane.
1769//   ---
1770//   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
1771//
1772// Side Effects:
1773//   `$orig` is true for the original instance of children.  False for the copy.
1774//   `$idx` is set to the index value of each copy.
1775//
1776//
1777// Example:
1778//   mirror_copy([1,-1,0]) zrot(-45) yrot(90) cylinder(d1=10, d2=0, h=20);
1779//   color("blue",0.25) zrot(-45) cube([0.01,15,15], center=true);
1780//
1781// Example:
1782//   mirror_copy([1,1,0], offset=5) rot(a=90,v=[-1,1,0]) cylinder(d1=10, d2=0, h=20);
1783//   color("blue",0.25) zrot(45) cube([0.01,15,15], center=true);
1784//
1785// Example:
1786//   mirror_copy(UP+BACK, cp=[0,-5,-5]) rot(from=UP, to=BACK+UP) cylinder(d1=10, d2=0, h=20);
1787//   color("blue",0.25) translate([0,-5,-5]) rot(from=UP, to=BACK+UP) cube([15,15,0.01], center=true);
1788module mirror_copy(v=[0,0,1], offset=0, cp)
1789{
1790    req_children($children);  
1791    cp = is_vector(v,4)? plane_normal(v) * v[3] :
1792        is_vector(cp)? cp :
1793        is_num(cp)? cp*unit(v) :
1794        [0,0,0];
1795    nv = is_vector(v,4)? plane_normal(v) : unit(v);
1796    off = nv*offset;
1797    if (cp == [0,0,0]) {
1798        translate(off) {
1799            $orig = true;
1800            $idx = 0;
1801            children();
1802        }
1803        mirror(nv) translate(off) {
1804            $orig = false;
1805            $idx = 1;
1806            children();
1807        }
1808    } else {
1809        translate(off) children();
1810        translate(cp) mirror(nv) translate(-cp) translate(off) children();
1811    }
1812}
1813
1814
1815function mirror_copy(v=[0,0,1], offset=0, cp, p=_NO_ARG) =
1816    let(
1817        cp = is_vector(v,4)? plane_normal(v) * v[3] :
1818            is_vector(cp)? cp :
1819            is_num(cp)? cp*unit(v) :
1820            [0,0,0],
1821        nv = is_vector(v,4)? plane_normal(v) : unit(v),
1822        off = nv*offset,
1823        mats = [
1824            translate(off),
1825            translate(cp) *
1826                mirror(nv) *
1827                translate(-cp) *
1828                translate(off)
1829        ]
1830    )
1831    p==_NO_ARG? mats : [for (m = mats) apply(m, p)];
1832
1833
1834
1835////////////////////
1836// Section: Distributing children individually along a line
1837///////////////////
1838// Module: xdistribute()
1839// Synopsis: Distributes each child, individually, out along the X axis.
1840// SynTags: Trans
1841// See Also: xdistribute(), ydistribute(), zdistribute(), distribute()
1842//
1843// Usage:
1844//   xdistribute(spacing, [sizes]) CHILDREN;
1845//   xdistribute(l=, [sizes=]) CHILDREN;
1846//
1847//
1848// Description:
1849//   Spreads out the children individually along the X axis. 
1850//   Every child is placed at a different position, in order.
1851//   This is useful for laying out groups of disparate objects
1852//   where you only really care about the spacing between them.
1853//
1854// Arguments:
1855//   spacing = spacing between each child. (Default: 10.0)
1856//   sizes = Array containing how much space each child will need.
1857//   l = Length to distribute copies along.
1858//
1859// Side Effects:
1860//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
1861//   `$idx` is set to the index number of each child being copied.
1862//
1863// Example:
1864//   xdistribute(sizes=[100, 10, 30], spacing=40) {
1865//       sphere(r=50);
1866//       cube([10,20,30], center=true);
1867//       cylinder(d=30, h=50, center=true);
1868//   }
1869module xdistribute(spacing=10, sizes=undef, l=undef)
1870{
1871    req_children($children);  
1872    dir = RIGHT;
1873    gaps = ($children < 2)? [0] :
1874        !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
1875        [for (i=[0:1:$children-2]) 0];
1876    spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
1877    gaps2 = [for (gap = gaps) gap+spc];
1878    spos = dir * -sum(gaps2)/2;
1879    spacings = cumsum([0, each gaps2]);
1880    for (i=[0:1:$children-1]) {
1881        $pos = spos + spacings[i] * dir;
1882        $idx = i;
1883        translate($pos) children(i);
1884    }
1885}
1886
1887
1888// Module: ydistribute()
1889// Synopsis: Distributes each child, individually, out along the Y axis.
1890// SynTags: Trans
1891// See Also: xdistribute(), ydistribute(), zdistribute(), distribute()
1892//
1893// Usage:
1894//   ydistribute(spacing, [sizes]) CHILDREN;
1895//   ydistribute(l=, [sizes=]) CHILDREN;
1896//
1897// Description:
1898//   Spreads out the children individually along the Y axis. 
1899//   Every child is placed at a different position, in order.
1900//   This is useful for laying out groups of disparate objects
1901//   where you only really care about the spacing between them.
1902//
1903// Arguments:
1904//   spacing = spacing between each child. (Default: 10.0)
1905//   sizes = Array containing how much space each child will need.
1906//   l = Length to distribute copies along.
1907//
1908// Side Effects:
1909//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
1910//   `$idx` is set to the index number of each child being copied.
1911//
1912// Example:
1913//   ydistribute(sizes=[30, 20, 100], spacing=40) {
1914//       cylinder(d=30, h=50, center=true);
1915//       cube([10,20,30], center=true);
1916//       sphere(r=50);
1917//   }
1918module ydistribute(spacing=10, sizes=undef, l=undef)
1919{
1920    req_children($children);  
1921    dir = BACK;
1922    gaps = ($children < 2)? [0] :
1923        !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
1924        [for (i=[0:1:$children-2]) 0];
1925    spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
1926    gaps2 = [for (gap = gaps) gap+spc];
1927    spos = dir * -sum(gaps2)/2;
1928    spacings = cumsum([0, each gaps2]);
1929    for (i=[0:1:$children-1]) {
1930        $pos = spos + spacings[i] * dir;
1931        $idx = i;
1932        translate($pos) children(i);
1933    }
1934}
1935
1936
1937// Module: zdistribute()
1938// Synopsis: Distributes each child, individually, out along the Z axis.
1939// SynTags: Trans
1940// See Also: xdistribute(), ydistribute(), zdistribute(), distribute()
1941//
1942// Usage:
1943//   zdistribute(spacing, [sizes]) CHILDREN;
1944//   zdistribute(l=, [sizes=]) CHILDREN;
1945//
1946// Description:
1947//   Spreads out each individual child along the Z axis.
1948//   Every child is placed at a different position, in order.
1949//   This is useful for laying out groups of disparate objects
1950//   where you only really care about the spacing between them.
1951//
1952// Arguments:
1953//   spacing = spacing between each child. (Default: 10.0)
1954//   sizes = Array containing how much space each child will need.
1955//   l = Length to distribute copies along.
1956//
1957// Side Effects:
1958//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
1959//   `$idx` is set to the index number of each child being copied.
1960//
1961// Example:
1962//   zdistribute(sizes=[30, 20, 100], spacing=40) {
1963//       cylinder(d=30, h=50, center=true);
1964//       cube([10,20,30], center=true);
1965//       sphere(r=50);
1966//   }
1967module zdistribute(spacing=10, sizes=undef, l=undef)
1968{
1969    req_children($children);  
1970    dir = UP;
1971    gaps = ($children < 2)? [0] :
1972        !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
1973        [for (i=[0:1:$children-2]) 0];
1974    spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
1975    gaps2 = [for (gap = gaps) gap+spc];
1976    spos = dir * -sum(gaps2)/2;
1977    spacings = cumsum([0, each gaps2]);
1978    for (i=[0:1:$children-1]) {
1979        $pos = spos + spacings[i] * dir;
1980        $idx = i;
1981        translate($pos) children(i);
1982    }
1983}
1984
1985
1986
1987// Module: distribute()
1988// Synopsis: Distributes each child, individually, out along an arbitrary line.
1989// SynTags: Trans
1990// See Also: xdistribute(), ydistribute(), zdistribute(), distribute()
1991//
1992// Usage:
1993//   distribute(spacing, sizes, dir) CHILDREN;
1994//   distribute(l=, [sizes=], [dir=]) CHILDREN;
1995//
1996// Description:
1997//   Spreads out the children individually along the direction `dir`.
1998//   Every child is placed at a different position, in order.
1999//   This is useful for laying out groups of disparate objects
2000//   where you only really care about the spacing between them.
2001//
2002// Arguments:
2003//   spacing = Spacing to add between each child. (Default: 10.0)
2004//   sizes = Array containing how much space each child will need.
2005//   dir = Vector direction to distribute copies along.  Default: RIGHT
2006//   l = Length to distribute copies along.
2007//
2008// Side Effects:
2009//   `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
2010//   `$idx` is set to the index number of each child being copied.
2011//
2012// Example:
2013//   distribute(sizes=[100, 30, 50], dir=UP) {
2014//       sphere(r=50);
2015//       cube([10,20,30], center=true);
2016//       cylinder(d=30, h=50, center=true);
2017//   }
2018module distribute(spacing=undef, sizes=undef, dir=RIGHT, l=undef)
2019{
2020    req_children($children);  
2021    gaps = ($children < 2)? [0] :
2022        !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
2023        [for (i=[0:1:$children-2]) 0];
2024    spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
2025    gaps2 = [for (gap = gaps) gap+spc];
2026    spos = dir * -sum(gaps2)/2;
2027    spacings = cumsum([0, each gaps2]);
2028    for (i=[0:1:$children-1]) {
2029        $pos = spos + spacings[i] * dir;
2030        $idx = i;
2031        translate($pos) children(i);
2032    }
2033}
2034
2035
2036
2037// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap