mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-15 08:59:40 +00:00
Compare commits
2 commits
7da26d32cd
...
c88319ca8f
Author | SHA1 | Date | |
---|---|---|---|
|
c88319ca8f | ||
|
55aafee003 |
2 changed files with 135 additions and 20 deletions
106
attachments.scad
106
attachments.scad
|
@ -12,6 +12,7 @@
|
|||
// FileFootnotes: STD=Included in std.scad
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
include<structs.scad>
|
||||
|
||||
// Default values for attachment code.
|
||||
$tags=undef; // for backward compatibility
|
||||
|
@ -778,7 +779,12 @@ function _make_anchor_legal(anchor,geom) =
|
|||
// Note that spin is not permitted for
|
||||
// 2D objects because it would change the child orientation so that the anchors are no longer parallel.
|
||||
// When you use `align=` you can also adjust the position using `inset=`, which shifts the child
|
||||
// away from the edge or corner it is aligned to.
|
||||
// away from the edge or corner it is aligned to.
|
||||
// .
|
||||
// Note that the concept of alignment doesn't always make sense for objects without corners, such as spheres or cylinders.
|
||||
// In same cases the alignments using such children will be odd because the alignment computation is trying to
|
||||
// place a non-existent corner somewhere. Because attach() doesn't have in formation about the child when
|
||||
// it runs it cannot handle curved shapes differently from cubes, so this behavior cannot be changed.
|
||||
// .
|
||||
// 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
|
||||
|
@ -831,6 +837,8 @@ function _make_anchor_legal(anchor,geom) =
|
|||
// `$attach_anchor` for each anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor.
|
||||
// if inside is true then set default tag to "remove"
|
||||
// `$attach_to` is set to the value of the `child` argument, if given. Otherwise, `undef`
|
||||
// `$edge_angle` is set to the angle of the edge if the anchor is on an edge and the parent is a prismoid or vnf with "hull" anchoring
|
||||
// `$edge_length` is set to the length of the edge if the anchor is on an edge and the parent is a prismoid or vnf with "hull" anchoring
|
||||
// Example: Cylinder placed on top of cube:
|
||||
// cuboid(50)
|
||||
// attach(TOP,BOT) cylinder(d1=30,d2=15,h=25);
|
||||
|
@ -881,6 +889,29 @@ function _make_anchor_legal(anchor,geom) =
|
|||
// attach(RIGHT+FRONT, TOP, inside=true) cuboid([10,3,5]);
|
||||
// attach(RIGHT+FRONT, TOP, inside=true, align=TOP,shiftout=.01) cuboid([5,1,2]);
|
||||
// }
|
||||
// Example: Attaching a 3d edge mask. Simple 2d masks can be done using {{edge_profile()}} but this mask varies along its length.
|
||||
// module wavy_edge(length,cycles, r, steps, n)
|
||||
// {
|
||||
// rmin = is_vector(r) ? r[0] : 0.01;
|
||||
// rmax = is_vector(r) ? r[1] : r;
|
||||
// layers = [for(z=[0:steps])
|
||||
// let(
|
||||
// r=rmin+(rmax-rmin)/2*(cos(z*360*cycles/steps)+1),ff=echo(r=r)
|
||||
// )
|
||||
// path3d( concat([[0,0]],
|
||||
// arc(corner=path2d([BACK,CTR,RIGHT]), n=n, r=r)),
|
||||
// z/steps*length-length/2)
|
||||
// ];
|
||||
// attachable([rmax,rmax,length]){
|
||||
// skin(layers,slices=0);
|
||||
// children();
|
||||
// }
|
||||
// }
|
||||
// diff()
|
||||
// cuboid(25)
|
||||
// attach([TOP+RIGHT,TOP+LEFT,TOP+FWD, FWD+RIGHT], FWD+LEFT, inside=true, shiftout=.01)
|
||||
// wavy_edge(length=25.1,cycles=1.4,r=4,steps=24,n=15);
|
||||
|
||||
|
||||
module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0, inside=false, from, to)
|
||||
{
|
||||
|
@ -905,10 +936,11 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
|||
assert(is_undef(align) || !is_string(child), "child is a named anchor. Named anchors are not supported with align=");
|
||||
|
||||
two_d = _attach_geom_2d($parent_geom);
|
||||
basegeom = $parent_geom[0]=="conoid" ? attach_geom(r=2,h=2)
|
||||
: $parent_geom[0]=="spheroid" ? echo("here")attach_geom(r=2)
|
||||
basegeom = $parent_geom[0]=="conoid" ? attach_geom(r=2,h=2,axis=$parent_geom[5])
|
||||
: $parent_geom[0]=="prismoid" ? attach_geom(size=[2,2,2],axis=$parent_geom[4])
|
||||
: attach_geom(size=[2,2,2]);
|
||||
child_abstract_anchor = is_vector(child) && !two_d ? _find_anchor(child, basegeom) : undef;
|
||||
childgeom = attach_geom([2,2,2]);
|
||||
child_abstract_anchor = is_vector(child) && !two_d ? _find_anchor(_make_anchor_legal(child,childgeom), childgeom) : undef;
|
||||
overlap = (overlap!=undef)? overlap : $overlap;
|
||||
parent = first_defined([parent,from]);
|
||||
anchors = is_vector(parent) || is_string(parent) ? [parent] : parent;
|
||||
|
@ -935,21 +967,49 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
|||
: point3d(anchors[anch_ind]);
|
||||
$anchor=anchor;
|
||||
anchor_data = _find_anchor(anchor, $parent_geom);
|
||||
$edge_angle = len(anchor_data)==5 ? struct_val(anchor_data[4],"edge_angle") : undef;
|
||||
$edge_length = len(anchor_data)==5 ? struct_val(anchor_data[4],"edge_length") : undef;
|
||||
anchor_pos = anchor_data[1];
|
||||
anchor_dir = factor*anchor_data[2];
|
||||
anchor_spin = two_d || !inside || anchor==TOP || anchor==BOT ? anchor_data[3]
|
||||
: let(spin_dir = rot(anchor_data[3],from=UP, to=-anchor_dir, p=BACK))
|
||||
_compute_spin(anchor_dir,spin_dir);
|
||||
parent_abstract_anchor = is_vector(anchor) && !two_d ? _find_anchor(anchor,basegeom) : undef;
|
||||
parent_abstract_anchor = is_vector(anchor) && !two_d ? _find_anchor(_make_anchor_legal(anchor,basegeom),basegeom) : undef;
|
||||
for(align_ind = idx(align_list)){
|
||||
align = is_undef(align_list[align_ind]) ? undef
|
||||
: assert(is_vector(align_list[align_ind],2) || is_vector(align_list[align_ind],3), "align direction must be a 2-vector or 3-vector")
|
||||
two_d ? _force_anchor_2d(align_list[align_ind])
|
||||
: point3d(align_list[align_ind]);
|
||||
spin = is_num(spin) ? spin
|
||||
: align==CENTER ? 0
|
||||
: sum(v_abs(anchor))==1 ? // parent anchor is a face
|
||||
let(
|
||||
spindir = in_list(anchor,[TOP,BOT]) ? BACK : UP,
|
||||
proj = project_plane(point4d(anchor),[spindir,align]),
|
||||
ang = v_theta(proj[1])-v_theta(proj[0])
|
||||
)
|
||||
ang
|
||||
: // parent anchor is not a face, so must be an edge (corners not allowed)
|
||||
let(
|
||||
nativeback = apply(rot(to=parent_abstract_anchor[2],from=UP)
|
||||
*affine3d_zrot(parent_abstract_anchor[3]), BACK)
|
||||
)
|
||||
nativeback*align<0 ? -180:0;
|
||||
$idx = align_ind+len(align_list)*anch_ind;
|
||||
$align=align;
|
||||
goodcyl = $parent_geom[0] != "conoid" || is_undef(align) || align==CTR ? true
|
||||
: let(
|
||||
align=rot(from=$parent_geom[5],to=UP,p=align),
|
||||
anchor=rot(from=$parent_geom[5],to=UP,p=anchor)
|
||||
)
|
||||
anchor==TOP || anchor==BOT || align==TOP || align==BOT;
|
||||
badcorner = !in_list($parent_geom[0],["conoid","spheroid"]) && !is_undef(align) && align!=CTR && sum(v_abs(anchor))==3;
|
||||
badsphere = $parent_geom[0]=="spheroid" && !is_undef(align) && align!=CTR;
|
||||
dummy=assert(is_undef(align) || all_zero(v_mul(anchor,align)),
|
||||
str("Invalid alignment: align value (",align,") includes component parallel to parent anchor (",anchor,")"));
|
||||
str("Invalid alignment: align value (",align,") includes component parallel to parent anchor (",anchor,")"))
|
||||
assert(goodcyl, str("Cannot use align with an anchor on a curved edge or surface of a cylinder at parent anchor (",anchor,")"))
|
||||
assert(!badcorner, str("Cannot use align at a corner anchor (",anchor,")"))
|
||||
assert(!badsphere, "Cannot use align on spheres.");
|
||||
// Now compute position on the parent (including alignment but not inset) where the child will be anchored
|
||||
pos = is_undef(align) ? anchor_data[1] : _find_anchor(anchor+align, $parent_geom)[1];
|
||||
$attach_anchor = list_set(anchor_data, 1, pos); // Never used; For user informational use? Should this be set at all?
|
||||
|
@ -963,7 +1023,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
|||
* affine3d_zrot(-parent_abstract_anchor[3])
|
||||
* rot(from=parent_abstract_anchor[2],to=UP)
|
||||
* rot(v=anchor,-spin),
|
||||
align);
|
||||
align);
|
||||
// The $anchor_override anchor value forces an override of the *position* only for the anchor
|
||||
// used when attachable() places the child
|
||||
$anchor_override = all_zero(child_adjustment)? inside?child:undef
|
||||
|
@ -3659,7 +3719,7 @@ function _get_cp(geom) =
|
|||
/// Arguments:
|
||||
/// anchor = Vector or named anchor string.
|
||||
/// geom = The geometry description of the shape.
|
||||
function _find_anchor(anchor, geom) =
|
||||
function _find_anchor(anchor, geom)=
|
||||
is_string(anchor)? (
|
||||
anchor=="origin"? [anchor, CENTER, UP, 0] // Ok that this returns 3d anchor in the 2d case?
|
||||
: let(
|
||||
|
@ -3687,8 +3747,10 @@ function _find_anchor(anchor, geom) =
|
|||
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
|
||||
assert(all_comps_good, "All components of an anchor for a cuboid/prismoid must be -1, 0, or 1")
|
||||
let(
|
||||
size=geom[1], size2=geom[2],
|
||||
shift=point2d(geom[3]), axis=point3d(geom[4]),
|
||||
size=geom[1],
|
||||
size2=geom[2],
|
||||
shift=point2d(geom[3]),
|
||||
axis=point3d(geom[4]),
|
||||
override = geom[5](anchor)
|
||||
)
|
||||
let(
|
||||
|
@ -3722,22 +3784,21 @@ function _find_anchor(anchor, geom) =
|
|||
v3 = unit(line[1]-line[0],UP) * anch.z
|
||||
)
|
||||
unit(v3,UP),
|
||||
edgeang = len(facevecs)==2 ? 180-vector_angle(facevecs[0], facevecs[1]) : undef,
|
||||
final_dir = default(override[1],anch==CENTER?UP:rot(from=UP, to=axis, p=dir)),
|
||||
final_pos = default(override[0],rot(from=UP, to=axis, p=pos)),
|
||||
|
||||
// If the anchor is on a face or horizontal edge we take the oang value for spin
|
||||
// If the anchor is on a vertical or sloped edge or corner we want to align the spin to point upward along the edge
|
||||
|
||||
spin = anch.x!=0 && anch.y!=0 ? _compute_spin(final_dir, rot(from=UP, to=axis, p=edge)) // Set "vertical" edge and corner anchors point along the edge
|
||||
: anch.z!=0 && sum(v_abs(anch))==2 ? _compute_spin(final_dir, rot(from=UP, to=axis, p=anch.z*[anch.y,-anch.x,0])) // Horizontal anchors point clockwise
|
||||
// Set "vertical" edge and corner anchors point along the edge
|
||||
spin = anch.x!=0 && anch.y!=0 ? _compute_spin(final_dir, rot(from=UP, to=axis, p=edge))
|
||||
// Horizontal anchors point clockwise
|
||||
: anch.z!=0 && sum(v_abs(anch))==2 ? _compute_spin(final_dir, rot(from=UP, to=axis, p=anch.z*[anch.y,-anch.x,0]))
|
||||
: norm(anch)==3 ? _compute_spin(final_dir, final_dir==DOWN || final_dir==UP ? BACK : UP)
|
||||
: oang // face anchors point UP/BACK
|
||||
//spin = anchor.x!=0 && anchor.y!=0 ? _compute_spin(dir, edge)
|
||||
// : anchor.z!=0 && (anchor.x!=0 || anchor.y!=0) ? _compute_spin(dir, _canonical_edge([anchor.y,anchor.x,0]))
|
||||
// : oang
|
||||
) [anchor, final_pos, final_dir, default(override[2],spin)]
|
||||
) [anchor, final_pos, final_dir, default(override[2],spin), if (is_def(edgeang)) [["edge_angle",edgeang],["edge_length",norm(edge)]]]
|
||||
) : type == "conoid"? ( //r1, r2, l, shift
|
||||
assert(anchor.z == sign(anchor.z), "The Z component of an anchor for a cylinder/cone must be -1, 0, or 1")
|
||||
let(
|
||||
rr1=geom[1],
|
||||
rr2=geom[2],
|
||||
|
@ -3747,6 +3808,11 @@ function _find_anchor(anchor, geom) =
|
|||
r1 = is_num(rr1)? [rr1,rr1] : point2d(rr1),
|
||||
r2 = is_num(rr2)? [rr2,rr2] : point2d(rr2),
|
||||
anch = rot(from=axis, to=UP, p=anchor),
|
||||
axisname = axis==UP ? "Z"
|
||||
: axis==RIGHT ? "X"
|
||||
: axis==BACK ? "Y"
|
||||
: "",
|
||||
dummy = assert(anch.z == sign(anch.z), str("The ",axisname," component of an anchor for the cylinder/cone must be -1, 0, or 1")),
|
||||
offset = rot(from=axis, to=UP, p=offset),
|
||||
u = (anch.z+1)/2,
|
||||
// Returns [point,tangent_dir]
|
||||
|
@ -3908,8 +3974,8 @@ function _find_anchor(anchor, geom) =
|
|||
edgedir = edge[1]-edge[0]
|
||||
)
|
||||
_compute_spin(direction, flip*edgedir)
|
||||
)
|
||||
[direction,spin]
|
||||
)
|
||||
[direction,spin,[["edge_angle",ang],["edge_length",norm(edge[0]-edge[1])]]]
|
||||
: let( // This section handles corner anchors, currently spins just point up
|
||||
vertices = vnf[0],
|
||||
faces = vnf[1],
|
||||
|
@ -3928,7 +3994,7 @@ function _find_anchor(anchor, geom) =
|
|||
avep = sum(select(rpts,idxs))/len(idxs),
|
||||
mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : avep,
|
||||
pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt)
|
||||
) [anchor, default(override[0],pos),default(override[1],dir[0]),default(override[2],dir[1])]
|
||||
) [anchor, default(override[0],pos),default(override[1],dir[0]),default(override[2],dir[1]),if (len(dir)==3) dir[2]]
|
||||
) : type == "trapezoid"? ( //size, size2, shift, override
|
||||
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
|
||||
assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1")
|
||||
|
|
|
@ -681,6 +681,55 @@ prismoid(150,60,100)
|
|||
show_anchors(s=45);
|
||||
```
|
||||
|
||||
Is is also possible to attach to edges and corners of the parent
|
||||
object. The anchors for edges spin the child so its BACK direction is
|
||||
aligned with the edge. If the edge belongs to a top or bottom
|
||||
horizontal face, then the BACK directions will point clockwise around
|
||||
the face, as seen from outside the shape. (This is the same direction
|
||||
required for construction of valid faces in OpenSCAD.) Otherwise, the
|
||||
BACK direction will point upwards.
|
||||
|
||||
Examine the red flags below, where only edge anchors appear on a
|
||||
prismoid. The top face shows the red flags pointing clockwise.
|
||||
The sloped side edges point along the edges, generally upward, and
|
||||
the bottom ones appear to point counter-clockwise, but if we viewed
|
||||
the shape from the bottom they would also appear clockwise.
|
||||
|
||||
```openscad-3D;Big
|
||||
include <BOSL2/std.scad>
|
||||
prismoid([100,175],[55,88], h=55)
|
||||
for(i=[-1:1], j=[-1:1], k=[-1:1])
|
||||
let(anchor=[i,j,k])
|
||||
if (sum(v_abs(anchor))==2)
|
||||
attach(anchor,BOT)anchor_arrow(40);
|
||||
```
|
||||
|
||||
In this example cylinders sink half-way into the top edges of the
|
||||
prismoid:
|
||||
|
||||
```openscad-3D;Big
|
||||
include <BOSL2/std.scad>
|
||||
$fn=16;
|
||||
r=6;
|
||||
prismoid([100,175],[55,88], h=55){
|
||||
attach([TOP+RIGHT,TOP+LEFT],LEFT,overlap=r/2) cyl(r=r,l=88+2*r,rounding=r);
|
||||
attach([TOP+FWD,TOP+BACK],LEFT,overlap=r/2) cyl(r=r,l=55+2*r, rounding=r);
|
||||
}
|
||||
```
|
||||
|
||||
This type of edge attachment is useful for attaching 3d edge masks to
|
||||
edges:
|
||||
|
||||
```openscad-3D;Big
|
||||
include <BOSL2/std.scad>
|
||||
$fn=32;
|
||||
diff()
|
||||
cuboid(75)
|
||||
attach([FRONT+LEFT, FRONT+RIGHT, BACK+LEFT, BACK+RIGHT],
|
||||
FWD+LEFT,inside=true)
|
||||
rounding_edge_mask(l=76, r1=8,r2=28);
|
||||
```
|
||||
|
||||
## Parent-Child Anchor Attachment (Double Argument Attachment)
|
||||
|
||||
The `attach()` module has two different modes of operation,
|
||||
|
|
Loading…
Reference in a new issue