From c6d2676fb48c4f769b04674add4daddd749744b9 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sat, 27 Apr 2024 10:27:02 -0400 Subject: [PATCH] tutorials updates and bugfix --- attachments.scad | 58 +++-- shapes3d.scad | 3 +- tutorials/Attachments.md | 459 ++++++++++++++++++++++++++++----------- 3 files changed, 372 insertions(+), 148 deletions(-) diff --git a/attachments.scad b/attachments.scad index 9add0f3..b20e7bf 100644 --- a/attachments.scad +++ b/attachments.scad @@ -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]; diff --git a/shapes3d.scad b/shapes3d.scad index 0fe27b7..b5687d9 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -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]); diff --git a/tutorials/Attachments.md b/tutorials/Attachments.md index 0a2fc78..b39e207 100644 --- a/tutorials/Attachments.md +++ b/tutorials/Attachments.md @@ -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 -cube(50,center=true) - attach(RIGHT)cylinder(d1=30,d2=15,h=25); -``` - -```openscad-3D -include -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 -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 -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 -cube(50,center=true) - attach(TOP,overlap=10) - cylinder(d=20,h=20); -``` - -```openscad-3D -include -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 -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 -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 -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 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 +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 -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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +cuboid(50) + attach(TOP,BOT,overlap=15) + color("green")cuboid(20); +``` + +```openscad-3D +include +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 +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 +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 +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 +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 +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 +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 +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 +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