1# Attachments Tutorial
   2
   3<!-- TOC -->
   4
   5## Attachables
   6BOSL2 introduces the concept of attachables.  You can do the following
   7things with attachable shapes:
   8
   9* Control where the shape appears and how it is oriented by anchoring and specifying orientation and spin
  10* Position or attach shapes relative to parent objects
  11* Tag objects and then control boolean operations based on their tags.
  12* Change the color of objects so that child objects are different colors than their parents
  13
  14The various attachment features may seem complex at first, but 
  15attachability is one of the most important features of the BOSL2
  16library.  It enables you to position objects relative to other objects
  17in your model instead of having to keep track of absolute positions.
  18It makes models simpler, more intuitive, and easier to maintain.
  19
  20Almost all objects defined by BOSL2 are attachable.  In addition,
  21BOSL2 overrides the built-in definitions for `cube()`, `cylinder()`,
  22`sphere()`, `square()`, `circle()` and `text()` and makes them attachable as
  23well.  However, some basic OpenSCAD built-in definitions are not
  24attachable and will not work with the features described in this
  25tutorial.  The non-attachables are `polyhedron()`, `linear_extrude()`,
  26`rotate_extrude()`, `surface()`, `projection()` and `polygon()`.
  27Some of these have attachable alternatives: `vnf_polyhedron()`,
  28`linear_sweep()`, `rotate_sweep()`, and `region()`.  
  29
  30
  31## Anchoring
  32Anchoring allows you to align a specified part of an object or point
  33on an object with the origin.  The alignment point can be the center
  34of a side, the center of an edge, a corner, or some other
  35distinguished point on the object.  This is done by passing a vector
  36or text string into the `anchor=` argument.  For roughly cubical
  37or prismoidal shapes, that vector points in the general direction of the side, edge, or
  38corner that will be aligned to.  For example, a vector of [1,0,-1] refers to the lower-right
  39edge of the shape.  Each vector component should be -1, 0, or 1:
  40
  41```openscad-3D
  42include <BOSL2/std.scad>
  43// Anchor at upper-front-left corner
  44cube([40,30,50], anchor=[-1,-1,1]);
  45```
  46
  47```openscad-3D
  48include <BOSL2/std.scad>
  49// Anchor at upper-right edge
  50cube([40,30,50], anchor=[1,0,1]);
  51```
  52
  53```openscad-3D
  54include <BOSL2/std.scad>
  55// Anchor at bottom face
  56cube([40,30,50], anchor=[0,0,-1]);
  57```
  58
  59Since manually written vectors are not very intuitive, BOSL2 defines some standard directional
  60vector constants that can be added together:
  61
  62Constant | Direction | Value
  63-------- | --------- | -----------
  64`LEFT`   | X-        | `[-1, 0, 0]`
  65`RIGHT`  | X+        | `[ 1, 0, 0]`
  66`FRONT`/`FORWARD`/`FWD` | Y− | `[ 0, −1, 0]`
  67`BACK`   | Y+        | `[ 0, 1, 0]`
  68`BOTTOM`/`BOT`/`DOWN` | Z− (Y− in 2D) | `[ 0, 0, −1]` (`[0, −1]` in 2D.)
  69`TOP`/`UP` | Z+ (Y+ in 2D)      | `[ 0, 0, 1]` (`[0, 1]` in 2D.)
  70`CENTER`/`CTR` | Centered | `[ 0, 0, 0]`
  71
  72If you want a vector pointing towards the bottom−left edge, just add the `BOTTOM` and `LEFT` vector
  73constants together like `BOTTOM + LEFT`.  This will result in a vector of `[−1,0,−1]`.  You can pass
  74that to the `anchor=` argument for a clearly understandable anchoring:  
  75
  76```openscad-3D
  77include <BOSL2/std.scad>
  78cube([40,30,50], anchor=BACK+TOP);
  79```
  80
  81```openscad-3D
  82include <BOSL2/std.scad>
  83cube([40,30,50], anchor=FRONT);
  84```
  85
  86---
  87
  88For cylindrical type attachables, the Z component of the vector will be −1, 0, or 1, referring
  89to the bottom rim, the middle side, or the top rim of the cylindrical or conical shape.
  90The X and Y components can be any value, pointing towards the circular perimeter of the cone.
  91These combined let you point at any place on the bottom or top rims, or at an arbitrary
  92side wall:
  93
  94```openscad-3D
  95include <BOSL2/std.scad>
  96cylinder(r1=25, r2=15, h=60, anchor=TOP+LEFT);
  97```
  98
  99```openscad-3D
 100include <BOSL2/std.scad>
 101cylinder(r1=25, r2=15, h=60, anchor=BOTTOM+FRONT);
 102```
 103
 104```openscad-3D
 105include <BOSL2/std.scad>
 106cylinder(r1=25, r2=15, h=60, anchor=UP+spherical_to_xyz(1,30,90));
 107```
 108
 109---
 110
 111For Spherical type attachables, you can pass a vector that points at any arbitrary place on
 112the surface of the sphere:
 113p
 114```openscad-3D
 115include <BOSL2/std.scad>
 116sphere(r=50, anchor=TOP);
 117```
 118
 119```openscad-3D
 120include <BOSL2/std.scad>
 121sphere(r=50, anchor=TOP+FRONT);
 122```
 123
 124```openscad-3D
 125include <BOSL2/std.scad>
 126sphere(r=50, anchor=spherical_to_xyz(1,-30,60));
 127```
 128
 129---
 130
 131Some attachable shapes may provide specific named anchors for shape-specific anchoring.  These
 132will be given as strings and will be specific to that type of
 133attachable.  When named anchors are supported, they are listed in a
 134"Named Anchors" section of the documentation for the module.  The
 135`teardrop()` attachable, for example, has a named anchor called "cap" and in 2D the
 136`star()` attachable has anchors labeled by tip number: 
 137
 138```openscad-3D
 139include <BOSL2/std.scad>
 140teardrop(d=100, l=20, anchor="cap");
 141```
 142
 143```openscad-2D
 144include <BOSL2/std.scad>
 145star(n=7, od=30, id=20, anchor="tip2");
 146```
 147
 148---
 149
 150Some shapes, for backwards compatibility reasons, can take a `center=` argument.  This just
 151overrides the `anchor=` argument.  A `center=true` argument is the same as `anchor=CENTER`.
 152A `center=false` argument chooses the anchor to match the behavior of
 153the builtin version:  for a cube it is the same as `anchor=[-1,-1,-1]` but for a
 154cylinder, it is the same as `anchor=BOTTOM`.
 155
 156```openscad-3D
 157include <BOSL2/std.scad>
 158cube([50,40,30],center=true);
 159```
 160
 161```openscad-3D
 162include <BOSL2/std.scad>
 163cube([50,40,30],center=false);
 164```
 165
 166---
 167
 168Most 2D shapes provided by BOSL2 are also anchorable.  The built-in `square()` and `circle()`
 169modules have been overridden to make them attachable..  The `anchor=` options for 2D
 170shapes treat 2D vectors as expected.  Special handling occurs with 3D
 171vectors:  if the Y coordinate is zero and the Z coordinate is nonzero,
 172then the Z coordinate is used to replace the Y coordinate.  This is
 173done so that you can use the TOP and BOTTOM names as anchor for 2D
 174shapes.  
 175
 176
 177```openscad-2D
 178include <BOSL2/std.scad>
 179square([40,30], anchor=BACK+LEFT);
 180```
 181
 182```openscad-2D
 183include <BOSL2/std.scad>
 184circle(d=50, anchor=BACK);
 185```
 186
 187```openscad-2D
 188include <BOSL2/std.scad>
 189hexagon(d=50, anchor=LEFT);
 190```
 191
 192```openscad-2D
 193include <BOSL2/std.scad>
 194ellipse(d=[50,30], anchor=FRONT);
 195
 196This final 2D example shows using the 3D anchor, TOP, with a 2D
 197object.  Also notice how the pentagon anchors to its most extreme point on
 198the Y+ axis.  
 199
 200```openscad-2D
 201include <BOSL2/std.scad>
 202pentagon(d=50, anchor=TOP);
 203```
 204
 205
 206## Spin
 207You can spin attachable objects around the origin using the `spin=`
 208argument.  The spin applies **after** anchoring, so depending on how
 209you anchor an object, its spin may not be about its center.  This
 210means that spin can have an effect even on rotationally symmetric
 211objects like spheres and cylinders.  You specify the spin in degrees.
 212A positive number will result in a counter-clockwise spin around the Z
 213axis (as seen from above), and a negative number will make a clockwise
 214spin:
 215
 216```openscad-3D
 217include <BOSL2/std.scad>
 218cube([20,20,40], center=true, spin=45);
 219```
 220
 221You can also spin around other axes, or multiple axes at once, by giving 3 angles (in degrees) to
 222`spin=` as a vector, like [Xang,Yang,Zang].  Similarly to `rotate()`,
 223the rotations apply in the order given, X-axis spin, then Y-axis, then Z-axis:
 224
 225```openscad-3D
 226include <BOSL2/std.scad>
 227cube([20,20,40], center=true, spin=[10,20,30]);
 228```
 229
 230This example shows a cylinder which has been anchored at its FRONT,
 231with a rotated copy in gray.  The rotation is performed around the
 232origin, but the cylinder is off the origin, so the rotation **does**
 233have an effect on the cylinder, even though the cylinder has
 234rotational symmetry.
 235
 236```openscad-3D
 237include <BOSL2/std.scad>
 238cylinder(h=40,d=20,anchor=FRONT+BOT);
 239%cylinder(h=40.2,d=20,anchor=FRONT+BOT,spin=40);
 240```
 241
 242
 243
 244You can also apply spin to 2D shapes from BOSL2, though only by scalar angle:
 245
 246```openscad-2D
 247include <BOSL2/std.scad>
 248square([40,30], spin=30);
 249```
 250
 251```openscad-2D
 252include <BOSL2/std.scad>
 253ellipse(d=[40,30], spin=30);
 254```
 255
 256
 257## Orientation
 258Another way to specify a rotation for an attachable shape, is to pass a 3D vector via the
 259`orient=` argument.  This lets you specify what direction to tilt the top of the shape towards.
 260For example, you can make a cone that is tilted up and to the right like this:
 261
 262```openscad-3D
 263include <BOSL2/std.scad>
 264cylinder(h=100, r1=50, r2=20, orient=UP+RIGHT);
 265```
 266
 267More precisely, the Z direction of the shape is rotated to align with
 268the vector you specify.  Two dimensional attachables, which have no Z vector,
 269do not accept the `orient=` argument.  
 270
 271
 272## Mixing Anchoring, Spin, and Orientation
 273When giving `anchor=`, `spin=`, and `orient=`, they are applied anchoring first, spin second,
 274then orient last.  For example, here's a cube:
 275
 276```openscad-3D
 277include <BOSL2/std.scad>
 278cube([20,20,50]);
 279```
 280
 281You can center it with an `anchor=CENTER` argument:
 282
 283```openscad-3D
 284include <BOSL2/std.scad>
 285cube([20,20,50], anchor=CENTER);
 286```
 287
 288Add a 45 degree spin:
 289
 290```openscad-3D
 291include <BOSL2/std.scad>
 292cube([20,20,50], anchor=CENTER, spin=45);
 293```
 294
 295Now tilt the top up and forward:
 296
 297```openscad-3D
 298include <BOSL2/std.scad>
 299cube([20,20,50], anchor=CENTER, spin=45, orient=UP+FWD);
 300```
 301
 302For 2D shapes, you can mix `anchor=` with `spin=`, but not with `orient=`.
 303
 304```openscad-2D
 305include <BOSL2/std.scad>
 306square([40,30], anchor=BACK+LEFT, spin=30);
 307```
 308
 309## Positioning Children
 310
 311Positioning is a powerful method for placing an object relative to
 312another object.  You do this by making the second object a child of
 313the first object.  By default, the child's anchor point will be
 314aligned with the center of the parent.  The default anchor for `cyl()`
 315is CENTER, and in this case, the cylinder is centered on the cube's center
 316
 317```openscad-3D
 318include <BOSL2/std.scad>
 319up(13) cube(50)
 320    cyl(d=25,l=95);
 321```
 322
 323With `cylinder()` the default anchor is BOTTOM.  It's hard to tell,
 324but the cylinder's bottom is placed at the center of the cube.  
 325
 326```openscad-3D
 327include <BOSL2/std.scad>
 328cube(50)
 329    cylinder(d=25,h=75);
 330```
 331
 332If you explicitly anchor the child object then the anchor you choose will be aligned
 333with the center point of the parent object.  In this example the right
 334side of the cylinder is aligned with the center of the cube.  
 335
 336
 337```openscad-3D
 338include <BOSL2/std.scad>
 339cube(50,anchor=FRONT)     
 340    cylinder(d=25,h=95,anchor=RIGHT);
 341```
 342
 343The `position()` module enables you to specify where on the parent to
 344position the child object.  You give `position()` an anchor point on
 345the parent, and the child's anchor point is aligned with the specified
 346parent anchor point.  In this example the LEFT anchor of the cylinder is positioned on the
 347RIGHT anchor of the cube.  
 348
 349```openscad-3D
 350include <BOSL2/std.scad>
 351cube(50,anchor=FRONT)     
 352    position(RIGHT) cylinder(d=25,h=75,anchor=LEFT);
 353```
 354
 355Using this mechanism you can position objects relative to other
 356objects which are in turn positioned relative to other objects without
 357having to keep track of the transformation math.
 358
 359```openscad-3D
 360include <BOSL2/std.scad>
 361cube([50,50,30],center=true)
 362    position(TOP+RIGHT) cube([25,40,10], anchor=RIGHT+BOT)
 363       position(LEFT+FRONT+TOP) cube([12,12,8], anchor=LEFT+FRONT+BOT)
 364         cylinder(h=10,r=3);
 365```
 366
 367The positioning mechanism is not magical: it simply applies a
 368`translate()` operation to the child.  You can still apply your own
 369additional translations or other transformations if you wish.  For
 370example, you can position an object 5 units from the right edge:
 371
 372```openscad-3D
 373include<BOSL2/std.scad>
 374cube([50,50,20],center=true)
 375    position(TOP+RIGHT) left(5) cube([4,50,10], anchor=RIGHT+BOT);
 376```
 377
 378
 379
 380Positioning objects works the same way in 2D.
 381
 382```openscad-2D
 383include<BOSL2/std.scad>
 384square(10)
 385    position(RIGHT) square(3,anchor=LEFT);
 386```
 387
 388## Using position() with orient()
 389
 390When positioning an object near an edge or corner you may wish to
 391orient the object relative to some face other than the TOP face that
 392meets at that edge or corner.  You can always apply a `rotation()` to 
 393change the orientation of the child object, but in order to do this,
 394you need to figure out the correct rotation.  The `orient()` module provides a
 395mechanism for re-orienting the child() that eases this burden: 
 396it can orient the child relative to the parent anchor directions.  This is different
 397than giving an `orient=` argument to the child, because that orients
 398relative to the parent's global coordinate system by just using the vector
 399directly, instead of orienting to the parent's anchor, which takes
 400account of face orientation.  A series of three
 401examples shows the different results.  In the first example, we use
 402only `position()`.  The child cube is erected pointing upwards, in the
 403Z direction.  In the second example we use `orient=RIGHT` in the child
 404and the result is that the child object points in the X+ direction,
 405without regard for the shape of the parent object.  In the final
 406example we apply `orient(RIGHT)` and the child is oriented
 407relative to the slanted right face of the parent using the parent
 408RIGHT anchor.   
 409
 410```openscad-3D
 411include<BOSL2/std.scad>
 412prismoid([50,50],[30,30],h=40)
 413  position(RIGHT+TOP)
 414     cube([15,15,25],anchor=RIGHT+BOT);
 415```
 416
 417
 418```openscad-3D
 419include<BOSL2/std.scad>
 420prismoid([50,50],[30,30],h=40)
 421  position(RIGHT+TOP)
 422     cube([15,15,25],orient=RIGHT,anchor=LEFT+BOT);
 423```
 424
 425
 426```openscad-3D
 427include<BOSL2/std.scad>
 428prismoid([50,50],[30,30],h=40)
 429  position(RIGHT+TOP)
 430     orient(RIGHT)
 431        cube([15,15,25],anchor=BACK+BOT);
 432```
 433
 434You may have noticed that the children in the above three examples
 435have different anchors.  Why is that?  The first and second examples
 436differ because anchoring up and anchoring to the right require
 437anchoring on opposite sides of the child.  But the third case differs
 438because the spin has changed.  The examples below show the same models
 439but with arrows replacing the child cube.  The red flags on the arrows
 440mark the zero spin direction.  Examine the red flags to see how the spin
 441changes.  The Y+ direction of the child will point towards that red
 442flag.  
 443
 444```openscad-3D
 445include<BOSL2/std.scad>
 446prismoid([50,50],[30,30],h=40)
 447  position(RIGHT+TOP)
 448     anchor_arrow(40);
 449```
 450
 451
 452```openscad-3D
 453include<BOSL2/std.scad>
 454prismoid([50,50],[30,30],h=40)
 455  position(RIGHT+TOP)
 456     anchor_arrow(40, orient=RIGHT);
 457```
 458
 459```openscad-3D
 460include<BOSL2/std.scad>
 461prismoid([50,50],[30,30],h=40)
 462  position(RIGHT+TOP)
 463     orient(RIGHT)
 464        anchor_arrow(40);
 465```
 466
 467
 468## Aligning children with align()
 469
 470You may have noticed that with position() and orient(), specifying the
 471child anchors to position objects flush with their parent can be
 472annoying, or sometimes even tricky.  You can simplify this task by
 473using the align() module.  This module positions children at specified
 474anchor points on the parent while picking the correct anchor points on
 475the children so that they line up with faces on the parent object.
 476
 477In the simplest case, if you want to place a child on the RIGHT side
 478of its parent, you need to anchor the child to its LEFT anchor:
 479
 480```openscad-3D
 481include<BOSL2/std.scad>
 482cuboid([50,40,15])
 483    position(RIGHT)
 484        color("lightblue")cuboid(5,anchor=LEFT);
 485```
 486
 487Using align(), the determination of the anchor is automatic.  Any
 488anchor you do specify is ignored.
 489
 490```openscad-3D
 491include<BOSL2/std.scad>
 492cuboid([50,40,15])
 493    align(RIGHT)
 494        color("lightblue")cuboid(5);
 495```
 496
 497To place the child on top of the parent in the corner you can do use
 498align as shown below instead of specifying the RIGHT+FRONT+BOT anchor
 499with position(): 
 500
 501```openscad-3D
 502include<BOSL2/std.scad>
 503cuboid([50,40,15])
 504    align(RIGHT+FRONT+TOP)
 505        color("lightblue")prismoid([10,5],[7,4],height=4);
 506```
 507
 508Both position() and align() can accept a list of anchor locations and
 509makes several copies of the children, but
 510if you want the children positioned flush, each copy 
 511requires a different anchor, so it is impossible to do this with a
 512single call to position(), but easily done using align():
 513
 514```openscad-3D
 515include<BOSL2/std.scad>
 516cuboid([50,40,15])
 517    align([RIGHT+TOP,LEFT+TOP])
 518        color("lightblue")prismoid([10,5],[7,4],height=4);
 519```
 520
 521Align also accepts a spin argument, which lets you spin the child
 522while still aligning it:
 523
 524```openscad-3D
 525include<BOSL2/std.scad>
 526cuboid([50,40,15])
 527    align(RIGHT+TOP,spin=90)
 528        color("lightblue")prismoid([10,5],[7,4],height=4);
 529```
 530
 531Note that this is different than using the spin argument to the child
 532object, which will apply after alignment has been done.
 533
 534
 535```openscad-3D
 536include<BOSL2/std.scad>
 537cuboid([50,40,15])
 538    align(RIGHT+TOP)
 539        color("lightblue")prismoid([10,5],[7,4],height=4,spin=90);
 540```
 541
 542If you orient the object DOWN it will be attached from its top anchor:
 543
 544```openscad-3D
 545include<BOSL2/std.scad>
 546cuboid([50,40,15])
 547    align(RIGHT+TOP,DOWN)
 548        color("lightblue")prismoid([10,5],[7,4],height=4);
 549```
 550
 551When placing children on the RIGHT and LEFT, there is a spin applied.
 552This means that setting spin=0 changes the orientation.  Here we have
 553one object with the default and one object with zero spin:
 554
 555```openscad-3D
 556include<BOSL2/std.scad>
 557prismoid(50,30,25){
 558  align(RIGHT+TOP,RIGHT,spin=0)
 559    color("lightblue")prismoid([10,5],[7,4],height=4);
 560  align(RIGHT+BOT,RIGHT)
 561    color("green")prismoid([10,5],[7,4],height=4);
 562}
 563```
 564
 565
 566## Attachment overview
 567
 568Attachables get their name from their ability to be attached to each
 569other.  Unlike with positioning, attaching changes the orientation of
 570the child object.  When you attach an object, it appears on the parent
 571relative to the local coordinate system of the parent at the anchor point.  To understand
 572what this means, imagine the perspective of an ant walking on a
 573sphere.  The meaning of UP varies depending on where on the sphere the
 574ant is standing.  If you **attach** a cylinder to the sphere then the cylinder will
 575be "up" from the ant's perspective.   The first example shows a
 576cylinder placed with `position()` so it points up in the global parent
 577coordinate system.  The second example shows how `attach()` points the
 578cylinder UP from the perspective of an ant standing at the anchor
 579point on the sphere.  
 580
 581```openscad-3D
 582include<BOSL2/std.scad>
 583sphere(40)
 584    position(RIGHT+TOP) cylinder(r=8,h=20);
 585```
 586
 587
 588```openscad-3D
 589include<BOSL2/std.scad>
 590sphere(40)
 591    attach(RIGHT+TOP) cylinder(r=8,h=20);
 592```
 593
 594In the example above, the cylinder's center point is attached to the
 595sphere, pointing "up" from the perspective of the sphere's surface.
 596For a sphere, a surface normal is defined everywhere that specifies
 597what "up" means.  But for other objects, it may not be so obvious.
 598Usually at edges and corners the direction is the average of the
 599direction of the faces that meet there.
 600
 601When you specify an anchor you are actually specifying both an anchor
 602point but also an anchor direction.  If you want to visualize this
 603direction you can use anchor arrows.  
 604
 605
 606## Anchor Directions and Anchor Arrows
 607For the ant on the sphere it is obvious which direction is UP; that
 608direction corresponds to the Z+ axis.  The location of the X and Y
 609axes is less clear and in fact it may be arbitrary.  One way that is
 610useful to show the position and orientation of an anchor point is by
 611attaching an anchor arrow to that anchor.  As noted before, the small
 612red flag points in the direction of the anchor's Y+ axis when the spin
 613is zero.
 614
 615```openscad-3D
 616include <BOSL2/std.scad>
 617cube(18, center=true)
 618    attach(LEFT+TOP)
 619        anchor_arrow();
 620```
 621
 622For large objects, you can change the size of the arrow with the `s=` argument.
 623
 624```openscad-3D
 625include <BOSL2/std.scad>
 626sphere(d=100)
 627    attach(LEFT+TOP)
 628        anchor_arrow(s=50);
 629```
 630
 631To show all the standard cardinal anchor points, you can use the `show_anchors()` module.
 632
 633```openscad-3D
 634include <BOSL2/std.scad>
 635cube(40, center=true)
 636    show_anchors();
 637```
 638
 639```openscad-3D
 640include <BOSL2/std.scad>
 641cylinder(h=40, d=40, center=true)
 642    show_anchors();
 643```
 644
 645```openscad-3D
 646include <BOSL2/std.scad>
 647sphere(d=40)
 648    show_anchors();
 649```
 650
 651For large objects, you can again change the size of the arrows with the `s=` argument.
 652
 653```openscad-3D
 654include <BOSL2/std.scad>
 655cylinder(h=100, d=100, center=true)
 656    show_anchors(s=30);
 657```
 658
 659## Basic Attachment
 660
 661The simplest form of attachment is to attach using the `attach()`
 662module with a single argument, which specifies the anchor on the parent
 663where the child will attach.  This will attach the bottom of the child
 664to the given anchor point on the parent.  The child appears on the parent with its
 665Z direction aligned parallel to the parent's anchor direction, and
 666its Y direction pointing in the zero spin direction for the
 667parent anchor.  The anchor direction of the child does not affect the result in this
 668case.
 669
 670```openscad-3D
 671include <BOSL2/std.scad>
 672cube(50,center=true)
 673    attach(RIGHT)cylinder(d1=30,d2=15,h=25);
 674```
 675
 676```openscad-3D
 677include <BOSL2/std.scad>
 678cube(50,center=true)
 679    attach(RIGHT+TOP)cylinder(d1=30,d2=15,h=25);
 680```
 681
 682In the second example, the child object points diagonally away
 683from the cube.  If you want the child at at edge of the parent it's
 684likely that this result will not be what you want.  To get a different
 685result, use `position()` with `orient()`, if needed. 
 686
 687If you give an anchor point to the child object it moves the child
 688around (in the attached coordinate system).  Or alternatively you can
 689think that it moves the object first, and then it gets attached.
 690
 691```openscad-3D
 692include <BOSL2/std.scad>
 693cube(50,center=true)
 694    attach(RIGHT)cylinder(d1=30,d2=15,h=25,anchor=FRONT);
 695```
 696
 697In the above example we anchor the child to its FRONT and then attach
 698it to the RIGHT.  An ambiguity exists regarding the spin of the
 699parent's coordinate system.  How is this resolved?   The small flags
 700on the anchor arrows show the position of zero spin by pointing
 701towards the local Y+ direction, which is also the BACK direction of the child.  For the above
 702cube, the arrow looks like this:
 703
 704```openscad-3D
 705include <BOSL2/std.scad>
 706cube(50,center=true)
 707    attach(RIGHT)anchor_arrow(30);
 708```
 709
 710The red flag points up, which explains why the attached cylinder
 711appeared above the anchor point.  The CENTER anchor generally has a
 712direction that points upward, so an attached object will keep its
 713orientation if attached to the CENTER of a parent.
 714
 715By default, `attach()` places the child exactly flush with the surface of the parent.  Sometimes
 716it's useful to have the child overlap the parent by insetting a bit.  You can do this with the
 717`overlap=` argument to `attach()`.  A positive value will inset the child into the parent, and
 718a negative value will outset out from the parent, which may be helpful
 719when doing differences.  
 720
 721```openscad-3D
 722include <BOSL2/std.scad>
 723cube(50,center=true)
 724    attach(TOP,overlap=10)
 725        cylinder(d=20,h=20);
 726```
 727
 728```openscad-3D
 729include <BOSL2/std.scad>
 730cube(50,center=true)
 731    attach(TOP,overlap=-20)
 732        cylinder(d=20,h=20);
 733```
 734
 735As with `position()`, you can still apply your own translations and
 736other transformations even after attaching an object.  However, the
 737order of operations now matters.  If you apply a translation outside
 738of the anchor then it acts in the parent's global coordinate system, so the
 739child moves up in this example:
 740
 741```openscad-3D
 742include <BOSL2/std.scad>
 743cube(50,center=true)
 744    up(13)
 745        attach(RIGHT)
 746            cylinder(d1=30,d2=15,h=25);
 747```
 748
 749On the other hand, if you put the translation between the attach and
 750the object in your code, then it will act in the local coordinate system of
 751the parent at the parent's anchor, so in the example below it moves to the right.  
 752
 753```openscad-3D
 754include <BOSL2/std.scad>
 755cube(50,center=true)
 756    attach(RIGHT)
 757        up(13)
 758            cylinder(d1=30,d2=15,h=25);
 759```
 760
 761
 762## Attachment With Parent and Child Anchors
 763
 764The `attach()` module can also take a second argument, the child anchor.
 765In this case, the attachment behavior
 766is quite different.  The objects are still attached with their anchor
 767points aligned, but the child is reoriented so that its anchor
 768direction is the opposite of the parent anchor direction.  It's like
 769you assemble the parts by pushing them together in the direction of
 770their anchor arrows.  Two examples appear below, where first we show
 771two objects with their anchors and then we show the result of
 772attaching with those anchors. 
 773
 774```openscad-3D
 775include <BOSL2/std.scad>
 776cube(50,anchor=BOT) attach(TOP) anchor_arrow(30);
 777right(60)cylinder(d1=30,d2=15,h=25) attach(TOP) anchor_arrow(30);
 778```
 779
 780```openscad-3D
 781include <BOSL2/std.scad>
 782cube(50,anchor=BOT)
 783  attach(TOP,TOP) cylinder(d1=30,d2=15,h=25);
 784```
 785
 786```openscad-3D
 787include <BOSL2/std.scad>
 788cube(50,center=true) attach(RIGHT) anchor_arrow(30);
 789right(80)cylinder(d1=30,d2=15,h=25) attach(LEFT) anchor_arrow(30);
 790```
 791
 792```openscad-3D
 793include <BOSL2/std.scad>
 794cube(50,center=true)
 795  attach(RIGHT,LEFT) cylinder(d1=30,d2=15,h=25);
 796```
 797
 798Note that when you attach with two anchors like this, the attachment
 799operation **overrides any anchor or orientation specified in the
 800child**.  That means the child's `anchor=` and `orient=` options are
 801ignored.
 802
 803Attachment with CENTER anchors can be surprising because the anchors
 804point upwards, so in the example below, the child's CENTER anchor
 805points up, so it is inverted when it is attached to the parent cone.
 806Note that the anchors are CENTER anchors, so the bases of the anchors are
 807hidden in the middle of the objects. 
 808
 809```openscad-3D
 810include <BOSL2/std.scad>
 811cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
 812right(40)cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
 813```
 814
 815```openscad-3D
 816include <BOSL2/std.scad>
 817cylinder(d1=30,d2=15,h=25)
 818    attach(CENTER,CENTER)
 819        cylinder(d1=30,d2=15,h=25);
 820```
 821
 822
 823## Positioning and Attaching Multiple Children
 824
 825You can attach or position more than one child at a time by enclosing them all in braces:
 826
 827```openscad-3D
 828include <BOSL2/std.scad>
 829cube(50, center=true) {
 830    attach(TOP) cylinder(d1=50,d2=20,h=20);
 831    position(RIGHT) cylinder(d1=50,d2=20,h=20);
 832}
 833```
 834
 835If you want to attach the same shape to multiple places on the same parent, you can pass the
 836desired anchors as a list to the `attach()` or `position()` modules:
 837
 838```openscad-3D
 839include <BOSL2/std.scad>
 840cube(50, center=true)
 841    attach([RIGHT,FRONT],TOP) cylinder(d1=35,d2=20,h=25);
 842```
 843
 844```openscad-3D
 845include <BOSL2/std.scad>
 846cube(50, center=true)
 847    position([TOP,RIGHT,FRONT]) cylinder(d1=35,d2=20,h=25);
 848```
 849
 850
 851## Attaching 2D Children
 852You can use attachments in 2D as well.  As usual for the 2D case you
 853can use TOP and BOTTOM as alternative to BACK and FORWARD.  
 854
 855```openscad-2D
 856include <BOSL2/std.scad>
 857square(50,center=true)
 858    attach(RIGHT,FRONT)
 859        trapezoid(w1=30,w2=0,h=30);
 860```
 861
 862```openscad-2D
 863include <BOSL2/std.scad>
 864circle(d=50)
 865    attach(TOP,BOT,overlap=5)
 866        trapezoid(w1=30,w2=0,h=30);
 867```
 868
 869
 870
 871
 872## Tagged Operations
 873BOSL2 introduces the concept of tags.  Tags are names that can be given to attachables, so that
 874you can refer to them when performing `diff()`, `intersect()`, and `conv_hull()` operations.
 875Each object can have no more than one tag at a time.  
 876
 877### `diff([remove], [keep])`
 878The `diff()` operator is used to difference away all shapes marked with the tag(s) given to
 879`remove`, from the other shapes.  
 880
 881For example, to difference away a child cylinder from the middle of a parent cube, you can
 882do this:
 883
 884```openscad-3D
 885include <BOSL2/std.scad>
 886diff("hole")
 887cube(100, center=true)
 888    tag("hole")cylinder(h=101, d=50, center=true);
 889```
 890
 891The `keep=` argument takes tags for shapes that you want to keep in the output.
 892
 893```openscad-3D
 894include <BOSL2/std.scad>
 895diff("dish", keep="antenna")
 896cube(100, center=true)
 897    attach([FRONT,TOP], overlap=33) {
 898        tag("dish") cylinder(h=33.1, d1=0, d2=95);
 899        tag("antenna") cylinder(h=33.1, d=10);
 900    }
 901```
 902
 903Remember that tags are inherited by children.  In this case, we need to explicitly
 904untag the first cylinder (or change its tag to something else), or it
 905will inherit the "keep" tag and get kept.  
 906
 907```openscad-3D
 908include <BOSL2/std.scad>
 909diff("hole", "keep")
 910tag("keep")cube(100, center=true)
 911    attach([RIGHT,TOP]) {
 912        tag("") cylinder(d=95, h=5);
 913        tag("hole") cylinder(d=50, h=11, anchor=CTR);
 914    }
 915```
 916
 917
 918You can of course apply `tag()` to several children.
 919
 920```openscad-3D
 921include <BOSL2/std.scad>
 922diff("hole")
 923cube(100, center=true)
 924    attach([FRONT,TOP], overlap=20)
 925        tag("hole") {
 926            cylinder(h=20.1, d1=0, d2=95);
 927            down(10) cylinder(h=30, d=30);
 928        }
 929```
 930
 931Many of the modules that use tags have default values for their tags.  For diff the default
 932remove tag is "remove" and the default keep tag is "keep".  In this example we rely on the
 933default values:
 934
 935```openscad-3D
 936include <BOSL2/std.scad>
 937diff()
 938sphere(d=100) {
 939    tag("keep")xcyl(d=40, l=120);
 940    tag("remove")cuboid([40,120,100]);
 941}
 942```
 943
 944
 945The parent object can be differenced away from other shapes.  Tags are inherited by children,
 946though, so you will need to set the tags of the children as well as the parent.
 947
 948```openscad-3D
 949include <BOSL2/std.scad>
 950diff("hole")
 951tag("hole")cube([20,11,45], center=true)
 952    tag("body")cube([40,10,90], center=true);
 953```
 954
 955Tags (and therefore tag-based operations like `diff()`) only work correctly with attachable
 956children.  However, a number of built-in modules for making shapes are *not* attachable.
 957Some notable non-attachable modules are `text()`, `linear_extrude()`, `rotate_extrude()`,
 958`polygon()`, `polyhedron()`, `import()`, `surface()`, `union()`, `difference()`,
 959`intersection()`, `offset()`, `hull()`, and `minkowski()`.
 960
 961To allow you to use tags-based operations with non-attachable shapes, you can wrap them with the
 962`force_tag()` module to specify their tags.  For example:
 963
 964```openscad-3D
 965include <BOSL2/std.scad>
 966diff("hole")
 967cuboid(50)
 968  attach(TOP)
 969    force_tag("hole")
 970      rotate_extrude()
 971        right(15)
 972          square(10,center=true);
 973```
 974
 975### `intersect([intersect], [keep])`
 976
 977To perform an intersection of attachables, you can use the `intersect()` module.  This is
 978specifically intended to address the situation where you want intersections involving a parent
 979and a child, something that is impossible with the native `intersection()` module.  This module
 980treats the children in three groups: objects matching the `intersect` tags, objects matching
 981the tags listed in `keep` and the remaining objects that don't match any listed tags.  The
 982intersection is computed between the union of the `intersect` tagged objects and the union of
 983the objects that don't match any listed tags.  Finally the objects listed in `keep` are union
 984ed with the result.  
 985
 986In this example the parent is intersected with a conical bounding shape.  
 987
 988```openscad-3D
 989include <BOSL2/std.scad>
 990intersect("bounds")
 991cube(100, center=true)
 992    tag("bounds") cylinder(h=100, d1=120, d2=95, center=true, $fn=72);
 993```
 994
 995In this example the child objects are intersected with the bounding box parent.  
 996
 997```openscad-3D
 998include <BOSL2/std.scad>
 999intersect("pole cap")
1000cube(100, center=true)
1001    attach([TOP,RIGHT]) {
1002        tag("pole")cube([40,40,80],center=true);
1003        tag("cap")sphere(d=40*sqrt(2));
1004    }
1005```
1006
1007The default `intersect` tag is "intersect" and the default `keep` tag is "keep".  Here is an
1008example where "keep" is used to keep the pole from being removed by the intersection. 
1009
1010```openscad-3D
1011include <BOSL2/std.scad>
1012intersect()
1013cube(100, center=true) {
1014    tag("intersect")cylinder(h=100, d1=120, d2=95, center=true, $fn=72);
1015    tag("keep")zrot(45) xcyl(h=140, d=20, $fn=36);
1016}
1017```
1018
1019### `conv_hull([keep])`
1020You can use the `conv_hull()` module to hull shapes together.  Objects
1021marked with the keep tags are excluded from the hull and unioned into the final result.
1022The default keep tag is "keep".  
1023
1024
1025```openscad-3D
1026include <BOSL2/std.scad>
1027conv_hull()
1028cube(50, center=true) {
1029    cyl(h=100, d=20);
1030    tag("keep")xcyl(h=100, d=20);
1031}
1032```
1033
1034
1035## 3D Masking Attachments
1036To make it easier to mask away shapes from various edges of an attachable parent shape, there
1037are a few specialized alternatives to the `attach()` and `position()` modules.
1038
1039### `edge_mask()`
1040If you have a 3D mask shape that you want to difference away from various edges, you can use
1041the `edge_mask()` module.  This module will take a vertically oriented shape, and will rotate
1042and move it such that the BACK, RIGHT (X+,Y+) side of the shape will be aligned with the given
1043edges.  The shape will be tagged as a "remove" so that you can use
1044`diff()` with its default "remove" tag.  For example,
1045here's a shape for rounding an edge:
1046
1047```openscad-3D
1048include <BOSL2/std.scad>
1049module round_edge(l,r) difference() {
1050    translate([-1,-1,-l/2])
1051        cube([r+1,r+1,l]);
1052    translate([r,r])
1053        cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
1054}
1055round_edge(l=30, r=19);
1056```
1057
1058You can use that mask to round various edges of a cube:
1059
1060```openscad-3D
1061include <BOSL2/std.scad>
1062module round_edge(l,r) difference() {
1063    translate([-1,-1,-l/2])
1064        cube([r+1,r+1,l]);
1065    translate([r,r])
1066        cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
1067}
1068diff()
1069cube([50,60,70],center=true)
1070    edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT])
1071        round_edge(l=71,r=10);
1072```
1073
1074### `corner_mask()`
1075If you have a 3D mask shape that you want to difference away from various corners, you can use
1076the `corner_mask()` module.  This module will take a shape and rotate and move it such that the
1077BACK RIGHT TOP (X+,Y+,Z+) side of the shape will be aligned with the given corner.  The shape
1078will be tagged as a "remove" so that you can use `diff()` with its
1079default "remove" tag.  For example, here's a shape for
1080rounding a corner:
1081
1082```openscad-3D
1083include <BOSL2/std.scad>
1084module round_corner(r) difference() {
1085    translate(-[1,1,1])
1086        cube(r+1);
1087    translate([r,r,r])
1088        spheroid(r=r, style="aligned", $fn=quantup(segs(r),4));
1089}
1090round_corner(r=10);
1091```
1092
1093You can use that mask to round various corners of a cube:
1094
1095```openscad-3D
1096include <BOSL2/std.scad>
1097module round_corner(r) difference() {
1098    translate(-[1,1,1])
1099        cube(r+1);
1100    translate([r,r,r])
1101        spheroid(r=r, style="aligned", $fn=quantup(segs(r),4));
1102}
1103diff()
1104cube([50,60,70],center=true)
1105    corner_mask([TOP,FRONT],LEFT+FRONT+TOP)
1106        round_corner(r=10);
1107```
1108
1109### Mix and Match Masks
1110You can use `edge_mask()` and `corner_mask()` together as well:
1111
1112```openscad-3D
1113include <BOSL2/std.scad>
1114module round_corner(r) difference() {
1115    translate(-[1,1,1])
1116        cube(r+1);
1117    translate([r,r,r])
1118        spheroid(r=r, style="aligned", $fn=quantup(segs(r),4));
1119}
1120module round_edge(l,r) difference() {
1121    translate([-1,-1,-l/2])
1122        cube([r+1,r+1,l]);
1123    translate([r,r])
1124        cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
1125}
1126diff()
1127cube([50,60,70],center=true) {
1128    edge_mask("ALL") round_edge(l=71,r=10);
1129    corner_mask("ALL") round_corner(r=10);
1130}
1131```
1132
1133## 2D Profile Mask Attachments
1134While 3D mask shapes give you a great deal of control, you need to make sure they are correctly
1135sized, and you need to provide separate mask shapes for corners and edges.  Often, a single 2D
1136profile could be used to describe the edge mask shape (via `linear_extrude()`), and the corner
1137mask shape (via `rotate_extrude()`).  This is where `edge_profile()`, `corner_profile()`, and
1138`face_profile()` come in.
1139
1140### `edge_profile()`
1141Using the `edge_profile()` module, you can provide a 2D profile shape and it will be linearly
1142extruded to a mask of the appropriate length for each given edge.  The resultant mask will be
1143tagged with "remove" so that you can difference it away with `diff()`
1144with the default "remove" tag.  The 2D profile is
1145assumed to be oriented with the BACK, RIGHT (X+,Y+) quadrant as the "cutter edge" that gets
1146re-oriented towards the edges of the parent shape.  A typical mask profile for chamfering an
1147edge may look like:
1148
1149```openscad-2D
1150include <BOSL2/std.scad>
1151mask2d_roundover(10);
1152```
1153
1154Using that mask profile, you can mask the edges of a cube like:
1155
1156```openscad-3D
1157include <BOSL2/std.scad>
1158diff()
1159cube([50,60,70],center=true)
1160   edge_profile("ALL")
1161       mask2d_roundover(10);
1162```
1163
1164### `corner_profile()`
1165You can use the same profile to make a rounded corner mask as well:
1166
1167```openscad-3D
1168include <BOSL2/std.scad>
1169diff()
1170cube([50,60,70],center=true)
1171   corner_profile("ALL", r=10)
1172       mask2d_roundover(10);
1173```
1174
1175### `face_profile()`
1176As a simple shortcut to apply a profile mask to all edges and corners of a face, you can use the
1177`face_profile()` module:
1178
1179```openscad-3D
1180include <BOSL2/std.scad>
1181diff()
1182cube([50,60,70],center=true)
1183   face_profile(TOP, r=10)
1184       mask2d_roundover(10);
1185```
1186
1187
1188## Coloring Attachables
1189Usually, when coloring a shape with the `color()` module, the parent color overrides the colors of
1190all children.  This is often not what you want:
1191
1192```openscad-3D
1193include <BOSL2/std.scad>
1194$fn = 24;
1195color("red") spheroid(d=3) {
1196    attach(CENTER,BOT) color("white") cyl(h=10, d=1) {
1197        attach(TOP,BOT) color("green") cyl(h=5, d1=3, d2=0);
1198    }
1199}
1200```
1201
1202If you use the `recolor()` module, however, the child's color
1203overrides the color of the parent.  This is probably easier to understand by example:
1204
1205```openscad-3D
1206include <BOSL2/std.scad>
1207$fn = 24;
1208recolor("red") spheroid(d=3) {
1209    attach(CENTER,BOT) recolor("white") cyl(h=10, d=1) {
1210        attach(TOP,BOT) recolor("green") cyl(h=5, d1=3, d2=0);
1211    }
1212}
1213```
1214
1215Be aware that `recolor()` will only work if you avoid using the native
1216`color()` module.  Also note that `recolor()` still affects all its
1217children.  If you want to color an object without affecting the
1218children you can use `color_this()`.  See the difference below:
1219
1220```openscad-3D
1221include <BOSL2/std.scad>
1222$fn = 24;
1223recolor("red") spheroid(d=3) {
1224    attach(CENTER,BOT) recolor("white") cyl(h=10, d=1) {
1225        attach(TOP,BOT)  cyl(h=5, d1=3, d2=0);
1226    }
1227}
1228right(5)
1229recolor("red") spheroid(d=3) {
1230    attach(CENTER,BOT) color_this("white") cyl(h=10, d=1) {
1231        attach(TOP,BOT)  cyl(h=5, d1=3, d2=0);
1232    }
1233}
1234```
1235
1236As with all of the attachable features, these color modules only work
1237on attachable objects, so they will have no effect on objects you
1238create using `linear_extrude()` or `rotate_extrude()`.  
1239
1240
1241## Making Attachables
1242To make a shape attachable, you just need to wrap it with an `attachable()` module with a
1243basic description of the shape's geometry.  By default, the shape is expected to be centered
1244at the origin.  The `attachable()` module expects exactly two children.  The first will be
1245the shape to make attachable, and the second will be `children()`, literally.
1246
1247### Prismoidal/Cuboidal Attachables
1248To make a cuboidal or prismoidal shape attachable, you use the `size`, `size2`, and `offset`
1249arguments of `attachable()`.
1250
1251In the most basic form, where the shape is fully cuboid, with top and bottom of the same size,
1252and directly over one another, you can just use `size=`.
1253
1254```openscad-3D
1255include <BOSL2/std.scad>
1256module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
1257    attachable(anchor,spin,orient, size=[s*3,s,s]) {
1258        union() {
1259            xcopies(2*s) cube(s, center=true);
1260            xcyl(h=2*s, d=s/4);
1261        }
1262        children();
1263    }
1264}
1265cubic_barbell(100) show_anchors(30);
1266```
1267
1268When the shape is prismoidal, where the top is a different size from the bottom, you can use
1269the `size2=` argument as well. While `size=` takes all three axes sizes, the `size2=` argument
1270only takes the [X,Y] sizes of the top of the shape.
1271
1272```openscad-3D
1273include <BOSL2/std.scad>
1274module prismoidal(size=[100,100,100], scale=0.5, anchor=CENTER, spin=0, orient=UP) {
1275    attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale) {
1276        hull() {
1277            up(size.z/2-0.005)
1278                linear_extrude(height=0.01, center=true)
1279                    square([size.x,size.y]*scale, center=true);
1280            down(size.z/2-0.005)
1281                linear_extrude(height=0.01, center=true)
1282                    square([size.x,size.y], center=true);
1283        }
1284        children();
1285    }
1286}
1287prismoidal([100,60,30], scale=0.5) show_anchors(20);
1288```
1289
1290When the top of the prismoid can be shifted away from directly above the bottom, you can use
1291the `shift=` argument.  The `shift=` argument takes an [X,Y] vector of the offset of the center
1292of the top from the XY center of the bottom of the shape.
1293
1294```openscad-3D
1295include <BOSL2/std.scad>
1296module prismoidal(size=[100,100,100], scale=0.5, shift=[0,0], anchor=CENTER, spin=0, orient=UP) {
1297    attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale, shift=shift) {
1298        hull() {
1299            translate([shift.x, shift.y, size.z/2-0.005])
1300                linear_extrude(height=0.01, center=true)
1301                    square([size.x,size.y]*scale, center=true);
1302            down(size.z/2-0.005)
1303                linear_extrude(height=0.01, center=true)
1304                    square([size.x,size.y], center=true);
1305        }
1306        children();
1307    }
1308}
1309prismoidal([100,60,30], scale=0.5, shift=[-30,20]) show_anchors(20);
1310```
1311
1312In the case that the prismoid is not oriented vertically, (ie, where the `shift=` or `size2=`
1313arguments should refer to a plane other than XY) you can use the `axis=` argument.  This lets
1314you make prismoids naturally oriented forwards/backwards or sideways.
1315
1316```openscad-3D
1317include <BOSL2/std.scad>
1318module yprismoidal(
1319    size=[100,100,100], scale=0.5, shift=[0,0],
1320    anchor=CENTER, spin=0, orient=UP
1321) {
1322    attachable(
1323        anchor, spin, orient,
1324        size=size, size2=point2d(size)*scale,
1325        shift=shift, axis=BACK
1326    ) {
1327        xrot(-90) hull() {
1328            translate([shift.x, shift.y, size.z/2-0.005])
1329                linear_extrude(height=0.01, center=true)
1330                    square([size.x,size.y]*scale, center=true);
1331            down(size.z/2-0.005)
1332                linear_extrude(height=0.01, center=true)
1333                    square([size.x,size.y], center=true);
1334        }
1335        children();
1336    }
1337}
1338yprismoidal([100,60,30], scale=1.5, shift=[20,20]) show_anchors(20);
1339```
1340
1341
1342### Cylindrical Attachables
1343To make a cylindrical shape attachable, you use the `l`, and `r`/`d`, args of `attachable()`.
1344
1345```openscad-3D
1346include <BOSL2/std.scad>
1347module twistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
1348    r = get_radius(r=r,d=d,dflt=1);
1349    attachable(anchor,spin,orient, r=r, l=l) {
1350        linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
1351            star(n=20, r=r, ir=r*0.9);
1352        children();
1353    }
1354}
1355twistar(l=100, r=40) show_anchors(20);
1356```
1357
1358If the cylinder is elipsoidal in shape, you can pass the unequal X/Y sizes as a 2-item vector
1359to the `r=` or `d=` argument.
1360
1361```openscad-3D
1362include <BOSL2/std.scad>
1363module ovalstar(l,rx,ry, anchor=CENTER, spin=0, orient=UP) {
1364    attachable(anchor,spin,orient, r=[rx,ry], l=l) {
1365        linear_extrude(height=l, center=true, convexity=4)
1366            scale([1,ry/rx,1])
1367                star(n=20, r=rx, ir=rx*0.9);
1368        children();
1369    }
1370}
1371ovalstar(l=100, rx=50, ry=30) show_anchors(20);
1372```
1373
1374For cylindrical shapes that aren't oriented vertically, use the `axis=` argument.
1375
1376```openscad-3D
1377include <BOSL2/std.scad>
1378module ytwistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
1379    r = get_radius(r=r,d=d,dflt=1);
1380    attachable(anchor,spin,orient, r=r, l=l, axis=BACK) {
1381        xrot(-90)
1382            linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
1383                star(n=20, r=r, ir=r*0.9);
1384        children();
1385    }
1386}
1387ytwistar(l=100, r=40) show_anchors(20);
1388```
1389
1390### Conical Attachables
1391To make a conical shape attachable, you use the `l`, `r1`/`d1`, and `r2`/`d2`, args of
1392`attachable()`.
1393
1394```openscad-3D
1395include <BOSL2/std.scad>
1396module twistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
1397    r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
1398    r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
1399    attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
1400        linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
1401            star(n=20, r=r1, ir=r1*0.9);
1402        children();
1403    }
1404}
1405twistar(l=100, r1=40, r2=20) show_anchors(20);
1406```
1407
1408If the cone is ellipsoidal in shape, you can pass the unequal X/Y sizes as a 2-item vectors
1409to the `r1=`/`r2=` or `d1=`/`d2=` arguments.
1410
1411```openscad-3D
1412include <BOSL2/std.scad>
1413module ovalish(l,rx1,ry1,rx2,ry2, anchor=CENTER, spin=0, orient=UP) {
1414    attachable(anchor,spin,orient, r1=[rx1,ry1], r2=[rx2,ry2], l=l) {
1415        hull() {
1416            up(l/2-0.005)
1417                linear_extrude(height=0.01, center=true)
1418                    ellipse([rx2,ry2]);
1419            down(l/2-0.005)
1420                linear_extrude(height=0.01, center=true)
1421                    ellipse([rx1,ry1]);
1422        }
1423        children();
1424    }
1425}
1426ovalish(l=100, rx1=50, ry1=30, rx2=30, ry2=50) show_anchors(20);
1427```
1428
1429For conical shapes that are not oriented vertically, use the `axis=` argument to indicate the
1430direction of the primary shape axis:
1431
1432```openscad-3D
1433include <BOSL2/std.scad>
1434module ytwistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
1435    r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
1436    r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
1437    attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK) {
1438        xrot(-90)
1439            linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
1440                star(n=20, r=r1, ir=r1*0.9);
1441        children();
1442    }
1443}
1444ytwistar(l=100, r1=40, r2=20) show_anchors(20);
1445```
1446
1447### Spherical Attachables
1448To make a spherical shape attachable, you use the `r`/`d` args of `attachable()`.
1449
1450```openscad-3D
1451include <BOSL2/std.scad>
1452module spikeball(r, d, anchor=CENTER, spin=0, orient=UP) {
1453    r = get_radius(r=r,d=d,dflt=1);
1454    attachable(anchor,spin,orient, r=r*1.1) {
1455        union() {
1456            sphere_copies(r=r, n=512, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
1457            sphere(r=r);
1458        }
1459        children();
1460    }
1461}
1462spikeball(r=50) show_anchors(20);
1463```
1464
1465If the shape is an ellipsoid, you can pass a 3-item vector of sizes to `r=` or `d=`.
1466
1467```openscad-3D
1468include <BOSL2/std.scad>
1469module spikeball(r, d, scale, anchor=CENTER, spin=0, orient=UP) {
1470    r = get_radius(r=r,d=d,dflt=1);
1471    attachable(anchor,spin,orient, r=r*1.1*scale) {
1472        union() {
1473            sphere_copies(r=r, n=512, scale=scale, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
1474            scale(scale) sphere(r=r);
1475        }
1476        children();
1477    }
1478}
1479spikeball(r=50, scale=[0.75,1,1.5]) show_anchors(20);
1480```
1481
1482### VNF Attachables
1483If the shape just doesn't fit into any of the above categories, and you constructed it as a
1484[VNF](vnf.scad), you can use the VNF itself to describe the geometry with the `vnf=` argument.
1485
1486There are two variations to how anchoring can work for VNFs. When `extent=true`, (the default)
1487then a plane is projected out from the origin, perpendicularly in the direction of the anchor,
1488to the furthest distance that intersects with the VNF shape.  The anchor point is then the
1489center of the points that still intersect that plane.
1490
1491```openscad-FlatSpin,VPD=500
1492include <BOSL2/std.scad>
1493module stellate_cube(s=100, anchor=CENTER, spin=0, orient=UP) {
1494    s2 = 3 * s;
1495    verts = [
1496        [0,0,-s2*sqrt(2)/2],
1497        each down(s/2, p=path3d(square(s,center=true))),
1498        each zrot(45, p=path3d(square(s2,center=true))),
1499        each up(s/2, p=path3d(square(s,center=true))),
1500        [0,0,s2*sqrt(2)/2]
1501    ];
1502    faces = [
1503        [0,2,1], [0,3,2], [0,4,3], [0,1,4],
1504        [1,2,6], [1,6,9], [6,10,9], [2,10,6],
1505        [1,5,4], [1,9,5], [9,12,5], [5,12,4],
1506        [4,8,3], [4,12,8], [12,11,8], [11,3,8],
1507        [2,3,7], [3,11,7], [7,11,10], [2,7,10],
1508        [9,10,13], [10,11,13], [11,12,13], [12,9,13]
1509    ];
1510    vnf = [verts, faces];
1511    attachable(anchor,spin,orient, vnf=vnf) {
1512        vnf_polyhedron(vnf);
1513        children();
1514    }
1515}
1516stellate_cube(25) {
1517    attach(UP+RIGHT) {
1518        anchor_arrow(20);
1519        %cube([100,100,0.1],center=true);
1520    }
1521}
1522```
1523
1524When `extent=false`, then the anchor point will be the furthest intersection of the VNF with
1525the anchor ray from the origin. The orientation of the anchor point will be the normal of the
1526face at the intersection.  If the intersection is at an edge or corner, then the orientation
1527will bisect the angles between the faces.
1528
1529```openscad-VPD=1250
1530include <BOSL2/std.scad>
1531module stellate_cube(s=100, anchor=CENTER, spin=0, orient=UP) {
1532    s2 = 3 * s;
1533    verts = [
1534        [0,0,-s2*sqrt(2)/2],
1535        each down(s/2, p=path3d(square(s,center=true))),
1536        each zrot(45, p=path3d(square(s2,center=true))),
1537        each up(s/2, p=path3d(square(s,center=true))),
1538        [0,0,s2*sqrt(2)/2]
1539    ];
1540    faces = [
1541        [0,2,1], [0,3,2], [0,4,3], [0,1,4],
1542        [1,2,6], [1,6,9], [6,10,9], [2,10,6],
1543        [1,5,4], [1,9,5], [9,12,5], [5,12,4],
1544        [4,8,3], [4,12,8], [12,11,8], [11,3,8],
1545        [2,3,7], [3,11,7], [7,11,10], [2,7,10],
1546        [9,10,13], [10,11,13], [11,12,13], [12,9,13]
1547    ];
1548    vnf = [verts, faces];
1549    attachable(anchor,spin,orient, vnf=vnf, extent=false) {
1550        vnf_polyhedron(vnf);
1551        children();
1552    }
1553}
1554stellate_cube() show_anchors(50);
1555```
1556
1557```openscad-3D
1558include <BOSL2/std.scad>
1559$fn=32;
1560R = difference(circle(10), right(2, circle(9)));
1561linear_sweep(R,height=10,atype="hull")
1562    attach(RIGHT) anchor_arrow();
1563```
1564
1565
1566## Making Named Anchors
1567While vector anchors are often useful, sometimes there are logically extra attachment points that
1568aren't on the perimeter of the shape.  This is what named string anchors are for.  For example,
1569the `teardrop()` shape uses a cylindrical geometry for it's vector anchors, but it also provides
1570a named anchor "cap" that is at the tip of the hat of the teardrop shape.
1571
1572Named anchors are passed as an array of `named_anchor()`s to the `anchors=` argument of `attachable()`.
1573The `named_anchor()` call takes a name string, a positional point, an orientation vector, and a spin.
1574The name is the name of the anchor.  The positional point is where the anchor point is at.  The
1575orientation vector is the direction that a child attached at that anchor point should be oriented.
1576The spin is the number of degrees that an attached child should be rotated counter-clockwise around
1577the orientation vector.  Spin is optional, and defaults to 0.
1578
1579To make a simple attachable shape similar to a `teardrop()` that provides a "cap" anchor, you may
1580define it like this:
1581
1582```openscad-3D
1583include <BOSL2/std.scad>
1584module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
1585    anchors = [
1586        named_anchor("cap", [0,r/sin(45),0], BACK, 0)
1587    ];
1588    attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
1589        linear_extrude(height=thick, center=true) {
1590            circle(r=r);
1591            back(r*sin(45)) zrot(45) square(r, center=true);
1592        }
1593        children();
1594    }
1595}
1596raindrop(r=25, thick=20, anchor="cap");
1597```
1598
1599If you want multiple named anchors, just add them to the list of anchors:
1600
1601```openscad-FlatSpin,VPD=150
1602include <BOSL2/std.scad>
1603module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
1604    anchors = [
1605        named_anchor("captop", [0,r/sin(45), thick/2], BACK+UP,   0),
1606        named_anchor("cap",    [0,r/sin(45), 0      ], BACK,      0),
1607        named_anchor("capbot", [0,r/sin(45),-thick/2], BACK+DOWN, 0)
1608    ];
1609    attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
1610        linear_extrude(height=thick, center=true) {
1611            circle(r=r);
1612            back(r*sin(45)) zrot(45) square(r, center=true);
1613        }
1614        children();
1615    }
1616}
1617raindrop(r=15, thick=10) show_anchors();
1618```
1619
1620Sometimes the named anchor you want to add may be at a point that is reached through a complicated
1621set of translations and rotations.  One quick way to calculate that point is to reproduce those
1622transformations in a transformation matrix chain.  This is simplified by how you can use the
1623function forms of almost all the transformation modules to get the transformation matrices, and
1624chain them together with matrix multiplication.  For example, if you have:
1625
1626```
1627scale([1.1, 1.2, 1.3]) xrot(15) zrot(25) right(20) sphere(d=1);
1628```
1629
1630and you want to calculate the center point of the sphere, you can do it like:
1631
1632```
1633sphere_pt = apply(
1634    scale([1.1, 1.2, 1.3]) * xrot(15) * zrot(25) * right(20),
1635    [0,0,0]
1636);
1637```
1638
1639