tutorials updates and bugfix

This commit is contained in:
Adrian Mariano 2024-04-27 10:27:02 -04:00
parent 76c8f18fd4
commit c6d2676fb4
3 changed files with 372 additions and 148 deletions

View file

@ -735,8 +735,8 @@ function _make_anchor_legal(anchor,geom) =
// Topics: Attachments
// See Also: attachable(), position(), align(), face_profile(), edge_profile(), corner_profile()
// Usage:
// PARENT() attach(from, to, [align=], [spin=], [overlap=]) CHILDREN;
// PARENT() attach(from, [overlap=], [spin=]) CHILDREN;
// PARENT() attach(parent, child, [align=], [spin=], [overlap=]) CHILDREN;
// PARENT() attach(parent, [overlap=], [spin=]) CHILDREN;
// Description:
// Attaches children to a parent object at an anchor point or points, oriented in the anchor direction.
// This module differs from {{position()}} and {{align()}} in that it rotates the children to
@ -744,8 +744,8 @@ function _make_anchor_legal(anchor,geom) =
// There are two modes of operation, single argument and double argument.
// .
// The double argument version is usually easier to use, and it is more powerful because it supports
// alignment. You provide an anchor on the parent `from` and an anchor on the child `to`.
// This connects the `to` anchor on the child to the `from` anchor on the parent.
// alignment. You provide an anchor on the parent `parent` and an anchor on the child `child`.
// This connects the `child` anchor on the child to the `parent` anchor on the parent.
// They are connected to the parent by pointing their anchor arrows at each other. The most basic case
// is `attach(TOP,BOT)` which puts the bottom of the child onto the top of the parent. If you
// do `attach(RIGHT,BOT)` this puts the bottom of the child onto the right anchor of the parent.
@ -767,7 +767,7 @@ function _make_anchor_legal(anchor,geom) =
// ignored with the **double argument** version of `attach()`. As noted above, you can give `spin=` to the
// child but using the `spin=` parameter to `attach()` is more likely to be useful.
// .
// For the single parameter version of `attach()` you give only the `from` anchor. The `align` direction
// For the single parameter version of `attach()` you give only the `parent` anchor. The `align` direction
// is not permitted. In this case the child is placed at the specified parent anchor point
// and rotated to the anchor direction. For example, `attach(TOP) cuboid(2);` will place a small
// cube **with its center** located at the TOP anchor of the parent, so just half the cube will project
@ -786,10 +786,10 @@ function _make_anchor_legal(anchor,geom) =
// For a step-by-step explanation of
// attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// from = The parent anchor point to attach to or a list of parent anchor points.
// to = Optional name of the child anchor point. If given, orients the child to connect this anchor point to the parent anchor.
// parent = The parent anchor point to attach to or a list of parent anchor points.
// child = Optional child anchor point. If given, orients the child to connect this anchor point to the parent anchor.
// ---
// align = If `to` is given you can specify alignment to shift the child to an edge or corner of the parent.
// align = If `child` is given you can specify alignment to shift the child to an edge or corner of the parent.
// overlap = Amount to sink child into the parent. Equivalent to `down(X)` after the attach. This defaults to the value in `$overlap`, which is `0` by default.
// spin = Amount to rotate the parent around the axis of the parent anchor.
// Side Effects:
@ -803,45 +803,57 @@ function _make_anchor_legal(anchor,geom) =
// attach(FRONT, BOTTOM, overlap=1.5) cyl(l=11.5, d1=10, d2=5);
// }
module attach(from, to, overlap, align, spin=0, norot)
module attach(parent, child, overlap, align, spin=0, norot, from, to)
{
req_children($children);
dummy3=
assert(num_defined([to,child])<2, "Cannot combine deprecated 'to' argument with 'child' parameter")
assert(num_defined([from,parent])<2, "Cannot combine deprecated 'from' argument with 'parent' parameter");
if (is_def(to))
echo("The 'to' option to attach() is deprecated and will be removed in the future. Use 'child' instead.");
if (is_def(from))
echo("The 'from' option to attach(0 is deprecated and will be removed in the future. Use 'parent' instead");
if (norot)
echo("The 'norot' option to attach() is deprecated and will be removed in the future. Use position() instead.");
req_children($children);
dummy=assert($parent_geom != undef, "No object to attach to!")
assert(is_undef(align) || (is_vector(align) && (len(align)==2 || len(align)==3)), "align must be a 2-vector or 3-vector")
assert(is_undef(to) || is_string(to) || (is_vector(to) && (len(to)==2 || len(to)==3)), "to must be a named anchor (a string) or a 2-vector or 3-vector")
assert(is_undef(align) || !is_string(to), "to is a named anchor. Named anchors are not supported with align=");
assert(is_undef(child) || is_string(child) || (is_vector(child) && (len(child)==2 || len(child)==3)), "child must be a named anchor (a string) or a 2-vector or 3-vector")
assert(is_undef(align) || !is_string(child), "child is a named anchor. Named anchors are not supported with align=");
overlap = (overlap!=undef)? overlap : $overlap;
anchors = (is_vector(from)||is_string(from))? [from] : from;
anchors = (is_vector(parent)||is_string(parent))? [parent] : parent;
two_d = _attach_geom_2d($parent_geom);
to = two_d ? _force_anchor_2d(to) : to;
parent = one_defined([parent,from],"parent,from");
dummy4 = assert(is_string(parent) || is_list(parent), "Invalid parent anchor or anchor list");
child = two_d ? _force_anchor_2d(child) : child;
align = is_undef(align) ? undef
: two_d ? _force_anchor_2d(align) : point3d(align);
dummy2=assert(is_undef(align) || is_def(to), "Cannot use 'align' without 'to'");
dummy2=assert(is_undef(align) || is_def(child), "Cannot use 'align' without 'child'");
for ($idx = idx(anchors)) {
dummy2=
assert(is_string(anchors[$idx]) || (is_vector(anchors[$idx]) && (len(anchors[$idx])==2 || len(anchors[$idx])==3)),
str("from[",$idx,"] is ",anchors[$idx]," but it must be a named anchor (string) or a 2-vector or 3-vector"))
str("parent[",$idx,"] is ",anchors[$idx]," but it must be a named anchor (string) or a 2-vector or 3-vector"))
assert(is_undef(align) || !is_string(anchors[$idx]),
str("from[",$idx,"] is a named anchor (",anchors[$idx],"), but named anchors are not wupported with align="));
str("parent[",$idx,"] is a named anchor (",anchors[$idx],"), but named anchors are not wupported with align="));
anchr = is_string(anchors[$idx])? anchors[$idx]
: two_d?_force_anchor_2d(anchors[$idx])
:anchors[$idx];
dummy=assert(is_undef(align) || all_zero(v_mul(anchr,align)),
str("align (",align,") cannot include component parallel to anchor (",anchr,")"));
str("align (",align,") cannot include component parallel to parent anchor (",anchr,")"));
anch = _find_anchor(anchr, $parent_geom);
pos = is_undef(align) ? anch[1] : _find_anchor(anchr+align, $parent_geom)[1];
$attach_to = to;
$attach_to = child;
$attach_anchor = list_set(anch, 1, pos); ///
startdir = anchr==UP || anchr==DOWN ? BACK : UP;
enddir = is_undef(to) || to.z==0 ? UP : BACK;
enddir = is_undef(child) || child.z==0 ? UP : BACK;
anchor_adjustment = is_undef(align)? CTR
: two_d ? zrot(spin, rot(to=to,from=-anchr,p=align))
: zrot(spin,frame_map(x=to, z=enddir,p=frame_map(x=-anchr, z=startdir, reverse=true, p=align)));
: two_d ? zrot(spin, rot(to=child,from=-anchr,p=align))
: apply( frame_map(x=child, z=enddir)
*frame_map(x=-anchr, z=startdir, reverse=true)
*rot(v=parent,-spin), align);
$anchor_override=all_zero(anchor_adjustment)?undef
:to+anchor_adjustment;
:child+anchor_adjustment;
olap = two_d? [0,-overlap,0] : [0,0,-overlap];
anchrvec = two_d? BACK : UP;
spinaxis = two_d? UP : anch[2];

View file

@ -2935,7 +2935,7 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) =
// Usage:
// text3d(text, [h], [size], [font], [language=], [script=], [direction=], [atype=], [anchor=], [spin=], [orient=]);
// Description:
// Creates a 3D text block that supports anchoring and attachment to attachable objects. You cannot attach children to text.
// Creates a 3D text block that supports anchoring and single-parameter attachment to attachable objects. You cannot attach children to text.
// .
// Historically fonts were specified by their "body size", the height of the metal body
// on which the glyphs were cast. This means the size was an upper bound on the size
@ -2988,6 +2988,7 @@ module text3d(text, h, size=10, font="Helvetica", spacing=1.0, direction="ltr",
h = one_defined([h,height,thickness],"h,height,thickness",dflt=1);
assert(is_undef(atype) || in_list(atype,["ycenter","baseline"]), "atype must be \"ycenter\" or \"baseline\"");
assert(is_bool(center));
assert(is_undef($attach_to),"text3d() does not support parent-child anchor attachment with two parameters");
atype = default(atype, center?"ycenter":"baseline");
anchor = default(anchor, center?CENTER:LEFT);
geom = attach_geom(size=[size,size,h]);

View file

@ -389,7 +389,7 @@ square(10)
When positioning an object near an edge or corner you may wish to
orient the object relative to some face other than the TOP face that
meets at that edge or corner. You can always apply a `rotation()` to
meets at that edge or corner. You can always apply `rot()` to
change the orientation of the child object, but in order to do this,
you need to figure out the correct rotation. The `orient()` module provides a
mechanism for re-orienting the child() that eases this burden:
@ -594,7 +594,8 @@ cyl(h=20,d=10,$fn=128)
Attachables get their name from their ability to be attached to each
other. Unlike with positioning, attaching changes the orientation of
the child object. When you attach an object, it appears on the parent
the child object. Think of it like sticking two objects together:
when you attach an object, it appears on the parent
relative to the local coordinate system of the parent at the anchor point. 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
@ -683,137 +684,54 @@ cylinder(h=100, d=100, center=true)
show_anchors(s=30);
```
## Basic Attachment
## Parent-Child Anchor Attachment (Double Argument Attachment)
The simplest form of attachment is to attach using the `attach()`
module with a single argument, which specifies the anchor on the parent
where the child will attach. This will attach the bottom of the child
to the given anchor point on the parent. The child appears on the parent with its
Z direction aligned parallel to the parent's anchor direction, and
its Y direction pointing in the zero spin direction for the
parent anchor. The anchor direction of the child does not affect the result in this
case.
The `attach()` module has two different modes of operation,
parent-child anchor attachment and parent anchor attachment. These
are also called double argument attachment and single argument
attachment. The parent-child anchor attachment, with two arguments,
is usually easier to use and is more powerful because it supports
alignment. When you use parent-child anchor attachment you give a
parent anchor and a child anchor. Imagine pointing the anchor arrows
on the two objects directly at each other and pushing them together in
the direction of the arrows until they touch. In many of the examples
below we show first the two objects with their anchor arrows and then
the result of the attach operation using those anchors.
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
attach(RIGHT)cylinder(d1=30,d2=15,h=25);
```
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
attach(RIGHT+TOP)cylinder(d1=30,d2=15,h=25);
```
In the second example, the child object points diagonally away
from the cube. If you want the child at at edge of the parent it's
likely that this result will not be what you want. To get a different
result, use `position()` with `orient()`, if needed.
If you give an anchor point to the child object it moves the child
around (in the attached coordinate system). Or alternatively you can
think that it moves the object first, and then it gets attached.
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
attach(RIGHT)cylinder(d1=30,d2=15,h=25,anchor=FRONT);
```
In the above example we anchor the child to its FRONT and then attach
it to the RIGHT. An ambiguity exists regarding the spin of the
parent's coordinate system. How is this resolved? The small flags
on the anchor arrows show the position of zero spin by pointing
towards the local Y+ direction, which is also the BACK direction of the child. For the above
cube, the arrow looks like this:
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
attach(RIGHT)anchor_arrow(30);
```
The red flag points up, which explains why the attached cylinder
appeared above the anchor point. The CENTER anchor generally has a
direction that points upward, so an attached object will keep its
orientation if attached to the CENTER of a parent.
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 insetting a bit. You can do this with the
`overlap=` argument to `attach()`. A positive value will inset the child into the parent, and
a negative value will outset out from the parent, which may be helpful
when doing differences.
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
attach(TOP,overlap=10)
cylinder(d=20,h=20);
```
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
attach(TOP,overlap=-20)
cylinder(d=20,h=20);
```
As with `position()`, you can still apply your own translations and
other transformations even after attaching an object. However, the
order of operations now matters. If you apply a translation outside
of the anchor then it acts in the parent's global coordinate system, so the
child moves up in this example:
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
up(13)
attach(RIGHT)
cylinder(d1=30,d2=15,h=25);
```
On the other hand, if you put the translation between the attach and
the object in your code, then it will act in the local coordinate system of
the parent at the parent's anchor, so in the example below it moves to the right.
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
attach(RIGHT)
up(13)
cylinder(d1=30,d2=15,h=25);
```
## Attachment With Parent and Child Anchors
The `attach()` module can also take a second argument, the child anchor.
In this case, the attachment behavior
is quite different. The objects are still attached with their anchor
points aligned, but the child is reoriented so that its anchor
direction is the opposite of the parent anchor direction. It's like
you assemble the parts by pushing them together in the direction of
their anchor arrows. Two examples appear below, where first we show
two objects with their anchors and then we show the result of
attaching with those anchors.
```openscad-3D
include <BOSL2/std.scad>
cube(50,anchor=BOT) attach(TOP) anchor_arrow(30);
right(60)cylinder(d1=30,d2=15,h=25) attach(TOP) anchor_arrow(30);
cube(50,anchor=BOT) attach(TOP,BOT) anchor_arrow(30);
right(60)cylinder(d1=30,d2=15,h=25) attach(BOT,BOT) anchor_arrow(30);
```
```openscad-3D
include <BOSL2/std.scad>
cube(50,anchor=BOT)
attach(TOP,TOP) cylinder(d1=30,d2=15,h=25);
attach(TOP,BOT) cylinder(d1=30,d2=15,h=25);
```
This example produces the same result as using `align()`, but if the
parent anchor is not horizontal, then the child is reoriented:
```openscad-3D
include <BOSL2/std.scad>
prismoid([50,50],[35,35],h=50,anchor=BOT) attach(RIGHT,BOT) anchor_arrow(30);
right(60)cylinder(d1=30,d2=15,h=25) attach(BOT,BOT) anchor_arrow(30);
```
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true) attach(RIGHT) anchor_arrow(30);
right(80)cylinder(d1=30,d2=15,h=25) attach(LEFT) anchor_arrow(30);
prismoid([50,50],[35,35],h=50,anchor=BOT)
attach(RIGHT,BOT) ylinder(d1=30,d2=15,h=25);
```
In this case we attach the curved side of the cone to a cube by lining
up the anchor arrows:
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true) attach(RIGHT,BOT) anchor_arrow(30);
right(80)cylinder(d1=30,d2=15,h=25) attach(LEFT,BOT) anchor_arrow(30);
```
```openscad-3D
@ -822,10 +740,154 @@ cube(50,center=true)
attach(RIGHT,LEFT) cylinder(d1=30,d2=15,h=25);
```
Note that when you attach with two anchors like this, the attachment
operation **overrides any anchor or orientation specified in the
child**. That means the child's `anchor=` and `orient=` options are
ignored.
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.
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 (approximately) UP, in accordance with rule 2 from above.
```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);
}
```
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);
}
```
The last 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
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);
}
```
Attachment with CENTER anchors can be surprising because the anchors
point upwards, so in the example below, the child's CENTER anchor
@ -846,6 +908,155 @@ cylinder(d1=30,d2=15,h=25)
cylinder(d1=30,d2=15,h=25);
```
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, which may be helpful when
doing differences. 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);
```
As with `position()`, you can still apply your own translations and
other transformations even after attaching an object. However, the
order of operations now matters. If you apply a translation outside
of the anchor then it acts in the parent's global coordinate system, so the
child moves up in this example, where the light gray shows the
untranslated object.
```openscad-3D
include <BOSL2/std.scad>
cuboid(50){
%attach(RIGHT,BOT)
cyl(d1=30,d2=15,h=25);
up(13)
color("green") attach(RIGHT,BOT)
cyl(d1=30,d2=15,h=25);
}
```
On the other hand, if you put the translation between the attach and
the object in your code, then it will act in the local coordinate system of
the parent at the parent's anchor, so in the example below it moves to the right.
```openscad-3D
include <BOSL2/std.scad>
cuboid(50){
%attach(RIGHT,BOT)
cyl(d1=30,d2=15,h=25);
color("green") attach(RIGHT,BOT)
up(13)
cyl(d1=30,d2=15,h=25);
}
```
## Parent Anchor Attachment (Single Argument Attachment)
The second form of attachment is parent anchor attachment, which just
uses a single argument. This form of attachment is less useful in
general and does not provide alignment. When you give `attach()` a parent anchor but no child anchor it
orients the child according to the pafrent anchor direction but then
simply places the child based on its internally defined anchor at the
parent anchor position. For most objects the default anchor is the
CENTER anchor, so objects will appear sunk half-way into the parent.
```openscad-3D
include <BOSL2/std.scad>
cuboid(30)
attach(TOP)
color("green")cuboid(10);
```
Some objects such as `cylinder()`, `prismoid()`, and `anchor_arrow()` have default anchors on the bottom, so they will appear
on the surface. For objects like this you can save a little bit of
typing by using parent anchor attachment. But in the case of `cube()`
the anchor is not centered, so the result is:
```openscad-3D
include <BOSL2/std.scad>
cube(30)
attach(TOP)
color("green")cube(10);
```
In order to make single argument attachment produce the results you
need you will probably need to change the child anchor. Note that unlike
parent-child anchor attachment, **with parent anchor attachment the `anchor=` and `orient=` arguments
are respected.** We could therefore place a cuboid like this:
```openscad-3D
include <BOSL2/std.scad>
cuboid(30)
attach(RIGHT)
color("green")cuboid(10,anchor=BOT);
```
If you need to place a cuboid at the anchor point but need it anchored
relative to one of the bottom edge or corner anchors then you can do
that with parent anchor attachment:
```openscad-3D
include <BOSL2/std.scad>
cuboid(30)
attach(RIGHT)
color("green")cuboid(10,anchor=BOT+FWD);
```
Another case where single argument attachment is useful is when the
child doesn't have proper attachment support.
If you use double argument attachment in such cases the results will
be incorrect because the child doesn't properly respond to the
internally propagated anchor directives. With single argument
attachment, this is not a problem: the origin
of the child will be placed at the parent anchor point. One module
without attachment support is `linear_extrude()`.
```openscad-3D
include <BOSL2/std.scad>
cuboid(20)
attach(RIGHT)
color("red")linear_extrude(height=2) star(n=7,ir=3,or=7);
```
As noted earlier, you can set `orient=` for children with parent
anchor attachment, though the behavior may not be intuitive because
the attachment process transforms the coordinate system and the
orientation is done in the attached coordinate system. It may be
helpful to start with the object attached to TOP and recall the rules
from the previous section about how orientation works. The same rules
apply here. Note that the forward arrow is pointing down after
attaching the object on the RIGHT face.
```openscad-3D
include <BOSL2/std.scad>
cuboid(20){
attach(RIGHT)
color_this("red")cuboid([2,4,8],orient=RIGHT,anchor=RIGHT)
attach(FWD) anchor_arrow();
attach(TOP)
color_this("red")cuboid([2,4,8],orient=RIGHT,anchor=RIGHT)
attach(FWD) anchor_arrow();
}
```
## Positioning and Attaching Multiple Children