Add end anchors to path_sweep

This commit is contained in:
Adrian Mariano 2024-02-24 21:56:26 -05:00
parent a246027a68
commit 5034c6667a
2 changed files with 106 additions and 42 deletions

View file

@ -2774,6 +2774,41 @@ function reorient(
function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin]; function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin];
function _force_rot(T) =
[for(i=[0:3])
[for(j=[0:3]) j<3 ? T[i][j] :
i==3 ? 1
: 0]];
// Function: transform_anchor()
// Synopsis: Creates an anchor data structure from a transformation matrix
// Topics: Attachments
// See Also: reorient(), attachable()
// Usage:
// a = transform_anchor(name, transform, [flip]);
// Description:
// Creates an anchor data structure from a transformation matrix. For a step-by-step explanation of attachments,
// see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// name = The string name of the anchor. Lowercase. Words separated by single dashes. No spaces.
// transform = A rotation matrix (which may include translation)
// flip = If true, flip the anchor the opposite direction. Default: false
function transform_anchor(name, transform, flip=false) =
let(
pos = apply(transform,CTR),
rotpart = _force_rot(transform),
dir = flip ? apply(rotpart,DOWN)
: apply(rotpart,UP),
transform = flip? affine3d_rot_by_axis(apply(rotpart,BACK),180)*transform
: transform,
decode=rot_decode(rot(to=UP,from=dir)*_force_rot(transform)),
spin = decode[0]*sign(decode[1].z)
)
[name, pos,dir,spin];
// Function: attach_geom() // Function: attach_geom()
// Synopsis: Returns the internal geometry description of an attachable object. // Synopsis: Returns the internal geometry description of an attachable object.
// Topics: Attachments // Topics: Attachments
@ -3201,7 +3236,6 @@ function _attach_transform(anchor, spin, orient, geom, p) =
assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient)) assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient))
let( let(
anchor = default(anchor, CENTER), anchor = default(anchor, CENTER),
spin = default(spin, 0), spin = default(spin, 0),
orient = default(orient, UP), orient = default(orient, UP),
two_d = _attach_geom_2d(geom), two_d = _attach_geom_2d(geom),
@ -3209,12 +3243,13 @@ function _attach_transform(anchor, spin, orient, geom, p) =
let( let(
anch = _find_anchor($attach_to, geom), anch = _find_anchor($attach_to, geom),
pos = anch[1] pos = anch[1]
) two_d? ( )
assert(two_d && is_num(spin)) two_d?
affine3d_zrot(spin) * assert(is_num(spin))
rot(to=FWD, from=point3d(anch[2])) * affine3d_zrot(spin)
affine3d_translate(point3d(-pos)) * rot(to=FWD, from=point3d(anch[2]))
) : ( * affine3d_translate(point3d(-pos))
:
assert(is_num(spin) || is_vector(spin,3)) assert(is_num(spin) || is_vector(spin,3))
let( let(
ang = vector_angle(anch[2], DOWN), ang = vector_angle(anch[2], DOWN),
@ -3222,40 +3257,33 @@ function _attach_transform(anchor, spin, orient, geom, p) =
ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3], ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3],
axis2 = rot(p=axis,[0,0,ang2]) axis2 = rot(p=axis,[0,0,ang2])
) )
affine3d_rot_by_axis(axis2,ang) * ( affine3d_rot_by_axis(axis2,ang)
is_num(spin)? affine3d_zrot(ang2+spin) : ( * (is_num(spin)? affine3d_zrot(ang2+spin)
affine3d_zrot(spin.z) * : affine3d_zrot(spin.z) * affine3d_yrot(spin.y) * affine3d_xrot(spin.x)
affine3d_yrot(spin.y) * * affine3d_zrot(ang2))
affine3d_xrot(spin.x) * * affine3d_translate(point3d(-pos))
affine3d_zrot(ang2)
)
) * affine3d_translate(point3d(-pos))
)
) : ( ) : (
let( let(
pos = _find_anchor(anchor, geom)[1] pos = _find_anchor(anchor, geom)[1]
) two_d? ( )
assert(two_d && is_num(spin)) two_d?
affine3d_zrot(spin) * assert(is_num(spin))
affine3d_translate(point3d(-pos)) affine3d_zrot(spin) * affine3d_translate(point3d(-pos))
) : ( :
assert(is_num(spin) || is_vector(spin,3)) assert(is_num(spin) || is_vector(spin,3))
let( let(
axis = vector_axis(UP,orient), axis = vector_axis(UP,orient),
ang = vector_angle(UP,orient) ang = vector_angle(UP,orient)
) )
affine3d_rot_by_axis(axis,ang) * ( affine3d_rot_by_axis(axis,ang)
is_num(spin)? affine3d_zrot(spin) : ( * ( is_num(spin)? affine3d_zrot(spin)
affine3d_zrot(spin.z) * : affine3d_zrot(spin.z) * affine3d_yrot(spin.y) * affine3d_xrot(spin.x))
affine3d_yrot(spin.y) * * affine3d_translate(point3d(-pos))
affine3d_xrot(spin.x)
)
) * affine3d_translate(point3d(-pos))
)
) )
) is_undef(p)? m : )
is_vnf(p)? [(p==EMPTY_VNF? p : apply(m, p[0])), p[1]] : is_undef(p)? m
apply(m, p); : is_vnf(p) && p==EMPTY_VNF? p
: apply(m, p);
function _get_cp(geom) = function _get_cp(geom) =

View file

@ -1481,6 +1481,11 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// Anchor Types: // Anchor Types:
// "hull" = Anchors to the virtual convex hull of the shape. // "hull" = Anchors to the virtual convex hull of the shape.
// "intersect" = Anchors to the surface of the shape. // "intersect" = Anchors to the surface of the shape.
// Extra Anchors:
// start = When `closed==false`, the origin point of the shape, on the starting face of the object
// end = When `closed==false`, the origin point of the shape, on the ending face of the object
// start-centroid = When `closed==false`, the centroid of the shape, on the starting face of the object
// end-centroid = When `closed==false`, the centroid of the shape, on the ending face of the object
// Example(NoScales): A simple sweep of a square along a sine wave: // Example(NoScales): A simple sweep of a square along a sine wave:
// path = [for(theta=[-180:5:180]) [theta/10, 10*sin(theta)]]; // path = [for(theta=[-180:5:180]) [theta/10, 10*sin(theta)]];
// sq = square(6,center=true); // sq = square(6,center=true);
@ -1755,29 +1760,60 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// path_sweep(left(.05,square([1.1,1])), curve, closed=true, // path_sweep(left(.05,square([1.1,1])), curve, closed=true,
// method="manual", normal=UP); // method="manual", normal=UP);
// } // }
// Example(Med,NoScales,VPR=[78.1,0,43.2],VPT=[2.18042,-0.485127,1.90371],VPD=74.4017): The "start" and "end" anchors are located at the origin point of the swept shape.
// shape = back_half(right_half(star(n=5,id=5,od=10)),y=-1);
// path = arc(angle=[0,180],d=30);
// path_sweep(shape,path,method="natural"){
// attach(["start","end"]) anchor_arrow(s=5);
// }
// Example(Med,NoScales,VPR=[78.1,0,43.2],VPT=[2.18042,-0.485127,1.90371],VPD=74.4017): The "start" and "end" anchors are located at the origin point of the swept shape.
// shape = back_half(right_half(star(n=5,id=5,od=10)),y=-1);
// path = arc(angle=[0,180],d=30);
// path_sweep(shape,path,method="natural"){
// attach(["start-centroid","end-centroid"]) anchor_arrow(s=5);
// }
// Example(Med,NoScales,VPR=[78.1,0,43.2],VPT=[2.18042,-0.485127,1.90371],VPD=74.4017): Note that the "start" anchors are backwards compared to the direction of the sweep, so you have to attach the TOP to align the shape with its ends.
// shape = back_half(right_half(star(n=5,id=5,od=10)),y=-1);
// path = arc(angle=[0,180],d=30);
// path_sweep(shape,path,method="natural")
// recolor("red"){
// attach("start",TOP) stroke([path3d(shape[0])],width=.5);
// attach("end") stroke([path3d(last(shape))],width=.5);
// }
module path_sweep(shape, path, method="incremental", normal, closed, twist=0, twist_by_length=true, scale=1, scale_by_length=true, module path_sweep(shape, path, method="incremental", normal, closed, twist=0, twist_by_length=true, scale=1, scale_by_length=true,
symmetry=1, last_normal, tangent, uniform=true, relaxed=false, caps, style="min_edge", convexity=10, symmetry=1, last_normal, tangent, uniform=true, relaxed=false, caps, style="min_edge", convexity=10,
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull",profiles=false,width=1) anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull",profiles=false,width=1)
{ {
dummy = assert(is_region(shape) || is_path(shape,2), "shape must be a 2D path or region"); dummy = assert(is_region(shape) || is_path(shape,2), "shape must be a 2D path or region")
vnf = path_sweep(shape, path, method, normal, closed, twist, twist_by_length, scale, scale_by_length, assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
symmetry, last_normal, tangent, uniform, relaxed, caps, style); transforms = path_sweep(shape, path, method, normal, closed, twist, twist_by_length, scale, scale_by_length,
symmetry, last_normal, tangent, uniform, relaxed, caps, style, transforms=true);
vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, caps=caps,style=style);
shapecent = point3d(centroid(shape));
$transforms = transforms;
anchors = closed ? []
:
[
transform_anchor("start", transforms[0], invert=true),
transform_anchor("end", last(transforms)),
transform_anchor("start-centroid", transforms[0]*move(shapecent), invert=true),
transform_anchor("end-centroid", last(transforms)*move(shapecent))
];
if (profiles){ if (profiles){
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
tran = path_sweep(shape, path, method, normal, closed, twist, twist_by_length, scale, scale_by_length,
symmetry, last_normal, tangent, uniform, relaxed,transforms=true);
rshape = is_path(shape) ? [path3d(shape)] rshape = is_path(shape) ? [path3d(shape)]
: [for(s=shape) path3d(s)]; : [for(s=shape) path3d(s)];
attachable(anchor,spin,orient, vnf=vnf, extent=atype=="hull", cp=cp) { attachable(anchor,spin,orient, vnf=vnf, extent=atype=="hull", cp=cp, anchors=anchors) {
for(T=tran) stroke([for(part=rshape)apply(T,part)],width=width); for(T=transforms) stroke([for(part=rshape)apply(T,part)],width=width);
children(); children();
} }
} }
else else
vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp) attachable(anchor,spin,orient,vnf=vnf,extent=atype=="hull", cp=cp,anchors=anchors){
vnf_polyhedron(vnf,convexity=convexity);
children(); children();
}
} }