The `attach()` module can stick the child object to the parent object
by matching a designated face on the child to a specified face on the
parent. Unlike `position()` and `align()`, the `attach()` module may
change the orientation and spin of the child.
## The Parent Coordinate System
When you attach a child object, it appears on the parent relative to
the local coordinate system of the parent at the anchor.
To understand what this means, imagine the perspective of an ant walking on a
sphere. The meaning of UP varies depending on where on the sphere the
ant is standing. If you **attach** a cylinder to the sphere then the cylinder will
be "up" from the ant's perspective. The first example shows a
cylinder placed with `position()` so it points up in the global parent
coordinate system. The second example shows how `attach()` points the
cylinder UP from the perspective of an ant standing at the anchor
point on the sphere.
```openscad-3D
include<BOSL2/std.scad>
sphere(40)
position(RIGHT+TOP) cylinder(r=8,h=20);
```
```openscad-3D
include<BOSL2/std.scad>
sphere(40)
attach(RIGHT+TOP) cylinder(r=8,h=20);
```
In the example above, the cylinder's center point is attached to the
sphere, pointing "up" from the perspective of the sphere's surface.
For a sphere, a surface normal is defined everywhere that specifies
what "up" means. But for other objects, it may not be so obvious.
Usually at edges and corners the direction is the average of the
direction of the faces that meet there.
It's obvious to the ant which direction is UP, and hence corresponds
to the Z axis, on the surface of the sphere she inhabits. But in
order to define the ant's local coordinate system we also need to
decide where the X and Y directions are. This is obvious an arbitrary
choice. In BOSL2 this is called the "spin" direction for the anchor.
When you specify an anchor for use with attachment you are actually
specifying both an anchor position but also an anchor direction (the Z
axis) and the anchor's spin so that you fully define the parent
coordinate systemer and a spin. If you want to visualize this
direction you can use anchor arrows.
## Anchor Directions and Anchor Arrows
For the ant on the sphere it is obvious which direction is UP; that
direction corresponds to the Z+ axis. The location of the X and Y
axes is less clear and in fact it may be arbitrary. One way that is
useful to show the position and orientation of an anchor point is by
attaching an anchor arrow to that anchor. As noted before, the small
red flag points in the direction of the anchor's Y+ axis when the spin
is zero.
```openscad-3D
include <BOSL2/std.scad>
cube(18, center=true)
attach(LEFT+TOP)
anchor_arrow();
```
For large objects, you can change the size of the arrow with the `s=` argument.
```openscad-3D
include <BOSL2/std.scad>
sphere(d=100)
attach(LEFT+TOP)
anchor_arrow(s=50);
```
To show all the standard cardinal anchor points, you can use the [show_anchors()](https://github.com/BelfrySCAD/BOSL2/wiki/attachments.scad#module-show_anchors) module.
```openscad-3D;Big
include <BOSL2/std.scad>
cube(20, center=true)
show_anchors();
```
```openscad-3D;Big
include <BOSL2/std.scad>
cylinder(h=25, d=25, center=true)
show_anchors();
```
```openscad-3D;Big
include <BOSL2/std.scad>
sphere(d=40)
show_anchors();
```
For large objects, you can again change the size of the arrows with the `s=` argument.
Note that this form of attachent overrides any anchor or orientation
specified in the child: **with parent-child anchor attachment the
`anchor=` and `orient=` parameters to the child are ignored.**
When you specify attachment using a pair of anchors, the attached
child can spin around the parent anchor while still being attached at
the designated anchors: specifying the anchors leaves one unspecified
degree of freedom. As noted earlier, this ambiguity is resolved by anchors having a
defined spin which specifies where the Y+ axis is located.
The way that BOSL2 positions objects can be understood by viewing the
anchor arrows as shown above, or you can remember these rules:
1. When attaching to the TOP or BOTTOM: the FRONT of the child points to the front if possible; otherwise the TOP of the child points BACK.
2. When attaching to other faces, if possible the child's UP anchor will point UP; otherwise, the BACK of the child points up (so the FRONT is pointed down).
To show how this works we use this prismoid where the blue arrow is
pointing to the front and the green arrow points up. Also note that
the front left edge is the only right angle.
```openscad-3D
include <BOSL2/std.scad>
color_this("orange")
prismoid([8,8],[6,6],shift=-[1,1],h=8) {
attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
attach(FWD,BOT) anchor_arrow(s=12);
}
```
If we attach this to the TOP by the LEFT side then we get the result
below. Notice how the green UP arrow is pointing back.
```openscad-3D
include <BOSL2/std.scad>
cube(30) attach(TOP,LEFT)
color_this("orange")
prismoid([8,8],[6,6],shift=-[1,1],h=8) {
attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
attach(FWD,BOT) anchor_arrow(s=12);
}
```
If we attach to the RIGHT using the same LEFT side anchor on the
prismoid then we get the result below. Note that the green UP anchor
is pointing UP, in accordance with rule 2 from above.
```openscad-3D
include <BOSL2/std.scad>
cube(30) attach(RIGHT,LEFT)
color_this("orange")
prismoid([8,8],[6,6],shift=-[1,1],h=8) {
attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
attach(FWD,BOT) anchor_arrow(s=12);
}
```
The green UP arrow can always be arranged to point up unless we attach
either the top or bottom to one of the cube's vertical faces. Here we
attach the bottom so you can still see both arrows. The blue FRONT
arrow on the object is pointing down, as expected based on rule 2.
```openscad-3D
include <BOSL2/std.scad>
cube(30) attach(RIGHT,BOT)
color_this("orange")
prismoid([8,8],[6,6],shift=-[1,1],h=8) {
attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
attach(FWD,BOT) anchor_arrow(s=12);
}
```
What do you do if the direction the child appears is not the direction
you need? To address this issue `attach()` provides a `spin=`
parameter which spins the attached child around the axis defined by
the joined anchor vectors. Here is the last example with a rotation
applied to bring the front anchor back to the front:
```openscad-3D
include <BOSL2/std.scad>
cube(30) attach(RIGHT,BOT,spin=-90)
color_this("orange")
prismoid([8,8],[6,6],shift=-[1,1],h=8) {
attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
attach(FWD,BOT) anchor_arrow(s=12);
}
```
Be aware that specifying `spin=` to `attach()` is not equivalent to
using the `spin=` argument to the child. Unlike `orient=` and
`anchor=`, which are ignored, the child `spin=` argument is still
respected, but it may be difficult to figure out which axis it will
rotate on. It is more intuitive to ignore the child spin parameter
and only use the spin parameter to `attach()`. The spin must be
scalar but need not be a multiple of 90 degrees.
```openscad-3D
include <BOSL2/std.scad>
cube(30) attach(RIGHT,BOT,spin=-37)
color_this("orange")
prismoid([8,8],[6,6],shift=-[1,1],h=8) {
attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
attach(FWD,BOT) anchor_arrow(s=12);
}
```
By default, `attach()` places the child exactly flush with the surface
of the parent. Sometimes it's useful to have the child overlap the
parent by translating it into the parent. You can do this with the
`overlap=` argument to `attach()`. A positive value will cause the
child to overlap the parent, and a negative value will move the child
away from the parent, leaving a small gap. In the first example we use a very large value of
overlap so the cube is sunk deeply into the parent. In the second
example a large negative overlap value raises the child high above the
parent.
```openscad-3D
include <BOSL2/std.scad>
cuboid(50)
attach(TOP,BOT,overlap=15)
color("green")cuboid(20);
```
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
attach(TOP,BOT,overlap=-20)
cyl(d=20,h=20);
```
Another feature provided by the double argument form of `attach()` is
alignment, which works in a similar way to `align()`. You can specify
`align=` to align the attached child to an edge or corner. The
example below shows five different alignments.
```openscad-3D;Big
include <BOSL2/std.scad>
module thing(){
color_this("orange")
prismoid([8,8],[6,6],shift=-[1,1],h=8) {
attach(TOP,BOT) anchor_arrow(color=[0,1,0],s=12);
attach(FWD,BOT) anchor_arrow(s=12);
}
}
prismoid([50,50],[35,35],h=25,anchor=BOT){
attach(TOP,BOT,align=FRONT) thing();
attach(RIGHT,BOT,align=BOT) thing();
attach(RIGHT,BACK,align=FRONT) thing();
attach(FRONT,BACK,align=BOT,spin=45) thing();
attach(TOP,RIGHT,align=RIGHT,spin=90) thing();
}
```
As with `align()` if you turn an object 90 degrees it can match up
with parallel edges, but if you turn it an arbitrary angle, a corner
of the child will contact the edge of the parent. Also like align()
the anchor points of the parent and child are aligned but this does
not necessarily mean that edges line up neatly when the shapes have
varying angles. This misalignment is visible in the object attached
at the RIGHT and aligned to the FRONT.
You may be wondering why all this fuss with align is necessary.
Couldn't you just attach an object at an anchor on an edge? When you
do this, the object will be attached using the edge anchor, which is
not perpendicular to the faces of the object. The example below shows
attachment to an edge anchor and also a corner anchor.
```openscad-3D
include <BOSL2/std.scad>
cube(30)
color("orange"){
attach(RIGHT+FRONT,BOT)
prismoid([8,8],[6,6],shift=-[1,1],h=8);
attach(TOP+LEFT+FWD,BOT)
prismoid([8,8],[6,6],shift=-[1,1],h=8);
}
```
When using the `align` option to `attach()` you can also set `inset`,
which works the same way as the `inset` parameter to `align()`. It
shifts the child away from the edge or edges where it is aligned by