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); thisedge = two_d? _force_anchor_2d(edge) : point3d(edge);
dummy=assert(all_zero(v_mul(thisedge,thisface)), dummy=assert(all_zero(v_mul(thisedge,thisface)),
str("align (",thisedge,") cannot include component parallel to anchor ",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_anch = _find_anchor(thisface+thisedge, $parent_geom);
$pos = pos_anch[1];
$attach_alignment = thisedge-factor*thisface; $attach_alignment = thisedge-factor*thisface;
$attach_anchor=list_set(pos_anch,2,UP); $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(); 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 // Make arbitrary anchor legal for a given geometry
function _make_anchor_legal(anchor,geom) = function _make_anchor_legal(anchor,geom) =
in_list(geom[0], ["prismoid","trapzeoid"]) ? [for(v=anchor) _quant_anch(v)] 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; : anchor;
@ -735,18 +743,19 @@ function _make_anchor_legal(anchor,geom) =
// Topics: Attachments // Topics: Attachments
// See Also: attachable(), position(), align(), face_profile(), edge_profile(), corner_profile() // See Also: attachable(), position(), align(), face_profile(), edge_profile(), corner_profile()
// Usage: // Usage:
// PARENT() attach(parent, child, [align=], [spin=], [overlap=]) CHILDREN; // PARENT() attach(parent, child, [align=], [spin=], [overlap=], [inside=]) CHILDREN;
// PARENT() attach(parent, [overlap=], [spin=]) CHILDREN; // PARENT() attach(parent, [overlap=], [spin=]) CHILDREN;
// Description: // Description:
// Attaches children to a parent object at an anchor point or points, oriented in the anchor direction. // 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 // 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. // 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 // 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`. // 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. // This module 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 // 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 // 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. // 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. // When an object is attached to the top or bottom its BACK direction will remaing pointing BACK.
@ -765,7 +774,11 @@ function _make_anchor_legal(anchor,geom) =
// Because the attachment process forces an orientation and anchor point for the child, it overrides // Because the attachment process forces an orientation and anchor point for the child, it overrides
// any such specifications you give to the child: both `anchor=` and `orient=` given to the child are // any such specifications you give to the child: both `anchor=` and `orient=` given to the child are
// ignored with the **double argument** version of `attach()`. As noted above, you can give `spin=` to the // 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. // 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 // 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 // is not permitted. In this case the child is placed at the specified parent anchor point
@ -803,7 +816,7 @@ function _make_anchor_legal(anchor,geom) =
// attach(FRONT, BOTTOM, overlap=1.5) cyl(l=11.5, d1=10, d2=5); // 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= dummy3=
assert(num_defined([to,child])<2, "Cannot combine deprecated 'to' argument with 'child' parameter") 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(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(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="); assert(is_undef(align) || !is_string(child), "child is a named anchor. Named anchors are not supported with align=");
overlap = (overlap!=undef)? overlap : $overlap; overlap = (overlap!=undef)? overlap : $overlap;
anchors = (is_vector(parent)||is_string(parent))? [parent] : parent; anchors = (is_vector(parent)||is_string(parent))? [parent] : parent;
two_d = _attach_geom_2d($parent_geom); 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"); 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 align = is_undef(align) ? undef
: two_d ? _force_anchor_2d(align) : point3d(align); : 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)) { for ($idx = idx(anchors)) {
dummy2= dummy2=
assert(is_string(anchors[$idx]) || (is_vector(anchors[$idx]) && (len(anchors[$idx])==2 || len(anchors[$idx])==3)), 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,")")); str("align (",align,") cannot include component parallel to parent anchor (",anchr,")"));
anch = _find_anchor(anchr, $parent_geom); anch = _find_anchor(anchr, $parent_geom);
pos = is_undef(align) ? anch[1] : _find_anchor(anchr+align, $parent_geom)[1]; 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); /// $attach_anchor = list_set(anch, 1, pos); ///
startdir = anchr==UP || anchr==DOWN ? BACK : UP; startdir = anchr==UP || anchr==DOWN ? BACK : UP;
enddir = is_undef(child) || child.z==0 ? UP : BACK; enddir = is_undef(child) || child.z==0 ? UP : BACK;
anchor_adjustment = is_undef(align)? CTR anchor_adjustment = is_undef(align)? CTR
: two_d ? zrot(spin, rot(to=child,from=-anchr,p=align)) : two_d ? zrot(spin, rot(to=factor*child,from=-anchr,p=align))
: apply( frame_map(x=child, z=enddir) : apply( frame_map(x=factor*child, z=enddir)
*frame_map(x=-anchr, z=startdir, reverse=true) *frame_map(x=-anchr, z=startdir, reverse=true)
*rot(v=parent,-spin), align); *rot(v=parent,-spin), align);
$anchor_override=all_zero(anchor_adjustment)? inside?child:undef
$anchor_override=all_zero(anchor_adjustment)?undef
:child+anchor_adjustment; :child+anchor_adjustment;
olap = two_d? [0,-overlap,0] : [0,0,-overlap]; reference = two_d? BACK : UP;
anchrvec = 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]; spinaxis = two_d? UP : anch[2];
if (norot || (approx(anch[2],anchrvec) && anch[3]==0)) { olap = - overlap * reference - inset*offsetdir - shiftout * (-offsetdir - reference);
translate(pos) rot(v=spinaxis,a=spin) translate(olap) children(); if (norot || (approx(anch[2],reference) && anch[3]==0)) {
translate(pos) rot(v=spinaxis,a=spin) translate(olap) default_tag("remove",inside) children();
} else { } else {
translate(pos) translate(pos)
rot(v=spinaxis,a=spin) rot(v=spinaxis,a=spin)
rot(anch[3],from=anchrvec,to=anch[2]) rot(anch[3],from=reference,to=anch[2]){
translate(olap) 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 ```openscad-3D
include <BOSL2/std.scad> include <BOSL2/std.scad>
prismoid([50,50],[35,35],h=50,anchor=BOT) 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 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 alignment, which works in a similar way to `align()`. You can specify
`align=` to align the attached child to an edge or corner. The `align=` to align the attached child to an edge or corner. The
example below shows five different alignments. example below shows five different alignments.
@ -889,51 +914,92 @@ cube(30)
} }
``` ```
Attachment with CENTER anchors can be surprising because the anchors When using the `align` option to `attach()` you can also set `inset`,
point upwards, so in the example below, the child's CENTER anchor which works the same way as the `inset` parameter to `align()`. It
points up, so it is inverted when it is attached to the parent cone. shifts the child away from the edge or edges where it is aligned by
Note that the anchors are CENTER anchors, so the bases of the anchors are the specified amount.
hidden in the middle of the objects.
```openscad-3D ```openscad-3D
include <BOSL2/std.scad> include <BOSL2/std.scad>
cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40); prismoid([50,50],[50,25],25){
right(40)cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40); 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 ```openscad-3D
include <BOSL2/std.scad> include <BOSL2/std.scad>
cylinder(d1=30,d2=15,h=25) cube(50,anchor=BOT) attach(TOP) anchor_arrow(30);
attach(CENTER,CENTER) right(60)cylinder(d1=30,d2=15,h=25) attach(TOP) anchor_arrow(30);
cylinder(d1=30,d2=15,h=25);
``` ```
By default, `attach()` places the child exactly flush with the surface Inside attachment is activated using `inside=true` and it lines up the
of the parent. Sometimes it's useful to have the child overlap the anchor arrows so they point together the **same** direction instead of
parent by translating it into the parent. You can do this with the opposite directions like regular outside attachment. The result in
`overlap=` argument to `attach()`. A positive value will cause the this case is appears below, where we have cut away the front half to
child to overlap the parent, and a negative value will move the child show the interior:
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 ```openscad-3D
include <BOSL2/std.scad> 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);
```
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) cuboid(50)
attach(TOP,BOT,overlap=15) attach(RIGHT,BOT,inside=true,shiftout=0.01)
color("green")cuboid(20); 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 ```openscad-3D
include <BOSL2/std.scad> include <BOSL2/std.scad>
cube(50,center=true) diff()
attach(TOP,BOT,overlap=-20) cuboid(10)
cyl(d=20,h=20); 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 other transformations even after attaching an object. However, the
order of operations now matters. If you apply a translation outside 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 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) ## Parent Anchor Attachment (Single Argument Attachment)