add inset and inside to attach(), tutorial updates

This commit is contained in:
Adrian Mariano 2024-04-28 23:52:53 -04:00
parent c6d2676fb4
commit e3225cb697
2 changed files with 158 additions and 54 deletions

View file

@ -708,11 +708,19 @@ module align(anchor,align=CENTER,inside=false,inset=0,shiftout=0,overlap)
thisedge = two_d? _force_anchor_2d(edge) : point3d(edge);
dummy=assert(all_zero(v_mul(thisedge,thisface)),
str("align (",thisedge,") cannot include component parallel to anchor ",thisface));
thisface_anch = _find_anchor(thisface, $parent_geom);
inset_dir = two_d ? -thisface
: unit(thisface_anch[1]-_find_anchor([thisedge.x,0,0]+thisface, $parent_geom)[1],CTR)
+unit(thisface_anch[1]-_find_anchor([0,thisedge.y,0]+thisface, $parent_geom)[1],CTR)
+unit(thisface_anch[1]-_find_anchor([0,0,thisedge.z]+thisface, $parent_geom)[1],CTR);
pos_anch = _find_anchor(thisface+thisedge, $parent_geom);
$pos = pos_anch[1];
$attach_alignment = thisedge-factor*thisface;
$attach_anchor=list_set(pos_anch,2,UP);
translate(pos_anch[1]-inset*thisedge+shiftout*(thisedge-factor*thisface)-overlap*thisface)
translate(pos_anch[1]
+inset*inset_dir
+shiftout*(thisface_anch[2]-inset_dir)
-overlap*thisface_anch[2])
default_tag("remove",inside) children();
}
}
@ -724,7 +732,7 @@ function _quant_anch(x) = approx(x,0) ? 0 : sign(x);
// Make arbitrary anchor legal for a given geometry
function _make_anchor_legal(anchor,geom) =
in_list(geom[0], ["prismoid","trapzeoid"]) ? [for(v=anchor) _quant_anch(v)]
: in_list(geom[0], ["conoid", "extrusion_extent"]) ? [$anchor.x,anchor.y, _quant_anch(anchor.z)]
: in_list(geom[0], ["conoid", "extrusion_extent"]) ? [anchor.x,anchor.y, _quant_anch(anchor.z)]
: anchor;
@ -735,18 +743,19 @@ function _make_anchor_legal(anchor,geom) =
// Topics: Attachments
// See Also: attachable(), position(), align(), face_profile(), edge_profile(), corner_profile()
// Usage:
// PARENT() attach(parent, child, [align=], [spin=], [overlap=]) CHILDREN;
// PARENT() attach(parent, child, [align=], [spin=], [overlap=], [inside=]) 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
// the anchor direction, which generally means it places the children on the surface of a parent.
// There are two modes of operation, single argument and double argument.
// There are two modes of operation, parent anchor (single argument) and parent-child anchor (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 `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
// The parent-child anchor (double argument) version is usually easier to use, and it is more powerful because it supports
// alignment. You provide an anchor on the parent (`parent`) and an anchor on the child (`child`).
// This module connects the `child` anchor on the child to the `parent` anchor on the parent.
// Imagine pointing the parent and child anchor arrows at each other and pushing the objects
// together until they meet at the anchor point. 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.
// When an object is attached to the top or bottom its BACK direction will remaing pointing BACK.
@ -767,6 +776,10 @@ 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.
// .
// If you give `inside=true` then the anchor arrows are lined up so they are pointing the same direction and
// the child object will be located inside the parent. In this case a default "remove" tag is applied to
// the children.
// .
// 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
@ -803,7 +816,7 @@ function _make_anchor_legal(anchor,geom) =
// attach(FRONT, BOTTOM, overlap=1.5) cyl(l=11.5, d1=10, d2=5);
// }
module attach(parent, child, overlap, align, spin=0, norot, from, to)
module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0, inside=false, from, to)
{
dummy3=
assert(num_defined([to,child])<2, "Cannot combine deprecated 'to' argument with 'child' parameter")
@ -820,15 +833,20 @@ module attach(parent, child, overlap, align, spin=0, norot, from, 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(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(parent)||is_string(parent))? [parent] : parent;
two_d = _attach_geom_2d($parent_geom);
parent = one_defined([parent,from],"parent,from");
parent = first_defined([parent,from]);
dummy4 = assert(is_string(parent) || is_list(parent), "Invalid parent anchor or anchor list");
child = two_d ? _force_anchor_2d(child) : child;
child_temp = first_defined([child,to]);
child = two_d ? _force_anchor_2d(child_temp) : child_temp;
align = is_undef(align) ? undef
: two_d ? _force_anchor_2d(align) : point3d(align);
dummy2=assert(is_undef(align) || is_def(child), "Cannot use 'align' without 'child'");
dummy2=assert(is_undef(align) || is_def(child), "Cannot use 'align' without 'child'")
assert(!inside || is_def(child), "Cannot use 'inside' without 'child'")
assert(inset==0 || is_def(child), "Cannot specify 'inset' without 'child'")
assert(shiftout==0 || is_def(child), "Cannot specify 'shiftout' without 'child'");
for ($idx = idx(anchors)) {
dummy2=
assert(is_string(anchors[$idx]) || (is_vector(anchors[$idx]) && (len(anchors[$idx])==2 || len(anchors[$idx])==3)),
@ -842,30 +860,32 @@ module attach(parent, child, overlap, align, spin=0, norot, from, to)
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 = child;
factor = inside?-1:1;
$attach_to = u_mul(factor,child);
$attach_anchor = list_set(anch, 1, pos); ///
startdir = anchr==UP || anchr==DOWN ? BACK : UP;
enddir = is_undef(child) || child.z==0 ? UP : BACK;
anchor_adjustment = is_undef(align)? CTR
: two_d ? zrot(spin, rot(to=child,from=-anchr,p=align))
: apply( frame_map(x=child, z=enddir)
: two_d ? zrot(spin, rot(to=factor*child,from=-anchr,p=align))
: apply( frame_map(x=factor*child, z=enddir)
*frame_map(x=-anchr, z=startdir, reverse=true)
*rot(v=parent,-spin), align);
$anchor_override=all_zero(anchor_adjustment)?undef
$anchor_override=all_zero(anchor_adjustment)? inside?child:undef
:child+anchor_adjustment;
olap = two_d? [0,-overlap,0] : [0,0,-overlap];
anchrvec = two_d? BACK : UP;
reference = two_d? BACK : UP;
offsetdir = is_undef(align) ? CTR
: apply(zrot(-spin)*frame_map(x=reference, z=BACK)*frame_map(x=anchr, z=startdir, reverse=true),
align);
spinaxis = two_d? UP : anch[2];
if (norot || (approx(anch[2],anchrvec) && anch[3]==0)) {
translate(pos) rot(v=spinaxis,a=spin) translate(olap) children();
olap = - overlap * reference - inset*offsetdir - shiftout * (-offsetdir - reference);
if (norot || (approx(anch[2],reference) && anch[3]==0)) {
translate(pos) rot(v=spinaxis,a=spin) translate(olap) default_tag("remove",inside) children();
} else {
translate(pos)
rot(v=spinaxis,a=spin)
rot(anch[3],from=anchrvec,to=anch[2])
rot(anch[3],from=reference,to=anch[2]){
translate(olap)
children();
}
default_tag("remove",inside) children();}}
}
}

View file

@ -722,7 +722,7 @@ right(60)cylinder(d1=30,d2=15,h=25) attach(BOT,BOT) anchor_arrow(30);
```openscad-3D
include <BOSL2/std.scad>
prismoid([50,50],[35,35],h=50,anchor=BOT)
attach(RIGHT,BOT) ylinder(d1=30,d2=15,h=25);
attach(RIGHT,BOT) cylinder(d1=30,d2=15,h=25);
```
In this case we attach the curved side of the cone to a cube by lining
@ -841,7 +841,32 @@ color_this("orange")
}
```
The last feature provided by the double argument form of `attach()` is
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);
```
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.
@ -889,51 +914,92 @@ cube(30)
}
```
Attachment with CENTER anchors can be surprising because the anchors
point upwards, so in the example below, the child's CENTER anchor
points up, so it is inverted when it is attached to the parent cone.
Note that the anchors are CENTER anchors, so the bases of the anchors are
hidden in the middle of the objects.
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
the specified amount.
```openscad-3D
include <BOSL2/std.scad>
cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
right(40)cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
prismoid([50,50],[50,25],25){
attach(FWD,BOT,align=TOP,inset=3) color("lavender")cuboid(5);
attach(FWD,BOT,align=BOT+RIGHT,inset=3) color("purple")cuboid(5);
}
```
The last capability provided by `attach()` is to attach the child
**inside** the parent object. This is useful if you want to subtract
the child from the parent. Doing this requires using tagged
operations with `diff()` which is explained in more detail below.
For the examples here, note that the `diff()` and `tag()` operations
that appear cause the child to be subtracted. We return to the
example that started this section, with anchor arrows shown on the two
objects.
```openscad-3D
include <BOSL2/std.scad>
cylinder(d1=30,d2=15,h=25)
attach(CENTER,CENTER)
cube(50,anchor=BOT) attach(TOP) anchor_arrow(30);
right(60)cylinder(d1=30,d2=15,h=25) attach(TOP) anchor_arrow(30);
```
Inside attachment is activated using `inside=true` and it lines up the
anchor arrows so they point together the **same** direction instead of
opposite directions like regular outside attachment. The result in
this case is appears below, where we have cut away the front half to
show the interior:
```openscad-3D
include <BOSL2/std.scad>
back_half(s=200)
diff()
cube(50,anchor=BOT)
attach(TOP,TOP,inside=true)
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.
The top of the cavity has a thin layer on it, which occurs because the
two objects share a face in the difference. To fix this you can use
the `shiftout` parameter to `attach()`. In this case you could also
use a negative `overlay` value, but the `shiftout` parameter shifts
out in every direction that is needed, which may be three directions
if you align the child at a corner. The above example looks like this
with with the shift added:
```openscad-3D
include <BOSL2/std.scad>
back_half(s=200)
diff()
cube(50,anchor=BOT)
attach(TOP,TOP,inside=true,shiftout=0.01)
cylinder(d1=30,d2=15,h=25);
```
Here is an example of connecting the same object on the right, but
this time with the BOTTOM anchor. Note how the BOTTOM anchor is
aligned to the RIGHT so it is parallel and pointing in the same
direction as the RIGHT anchor.
```openscad-3D
include <BOSL2/std.scad>
back_half(s=200)
diff()
cuboid(50)
attach(TOP,BOT,overlap=15)
color("green")cuboid(20);
attach(RIGHT,BOT,inside=true,shiftout=0.01)
cylinder(d1=30,d2=15,h=25);
```
Here is an example where alignment moves the object into the corner,
and we benefit from shiftout providing 3 dimensions of adjustment:
```openscad-3D
include <BOSL2/std.scad>
cube(50,center=true)
attach(TOP,BOT,overlap=-20)
cyl(d=20,h=20);
diff()
cuboid(10)
attach(TOP,TOP,align=RIGHT+FWD,inside=true,shiftout=.01)
cuboid([2,5,9]);
```
As with `position()`, you can still apply your own translations and
As with `position()`, with any use of `attach()` 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
@ -966,6 +1032,24 @@ cuboid(50){
}
```
Attachment with CENTER anchors can be surprising because the anchors
point upwards, so in the example below, the child's CENTER anchor
points up, so it is inverted when it is attached to the parent cone.
Note that the anchors are CENTER anchors, so the bases of the anchors are
hidden in the middle of the objects.
```openscad-3D
include <BOSL2/std.scad>
cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
right(40)cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
```
```openscad-3D
include <BOSL2/std.scad>
cylinder(d1=30,d2=15,h=25)
attach(CENTER,CENTER)
cylinder(d1=30,d2=15,h=25);
```
## Parent Anchor Attachment (Single Argument Attachment)