merge transform_anchor into named anchor; make path_sweep anchors work

with scaling.
This commit is contained in:
Adrian Mariano 2024-02-25 09:49:43 -05:00
parent 097cace9cd
commit 392b67c9c8
2 changed files with 50 additions and 47 deletions

View file

@ -2763,16 +2763,40 @@ function reorient(
// See Also: reorient(), attachable()
// Usage:
// a = named_anchor(name, pos, [orient], [spin]);
// a = named_anchor(name, [pos], rot=, [flip=]);
// Description:
// Creates an anchor data structure. For a step-by-step explanation of attachments,
// Creates an anchor data structure. You can specify the position, orient direction and spin directly.
// Alternatively for the 3D case you can give a 4x4 rotation matrix which can specify the orient and spin, and optionally
// the position, using a translation component of the matrix. If you specify `pos` along with `rot` then the position you
// give overrides any translation included in `rot`. 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.
// pos = The [X,Y,Z] position of the anchor.
// orient = A vector pointing in the direction parts should project from the anchor position. Default: UP
// spin = If needed, the angle to rotate the part around the direction vector. Default: 0
function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin];
// ---
// rot = A 4x4 rotations matrix, which may include a translation
// flip = If true, flip the anchor the opposite direction. Default: false
function named_anchor(name, pos, orient, spin, rot, flip) =
assert(num_defined([orient,spin])==0 || num_defined([rot,flip])==0, "Cannot mix orient or spin with rot or flip")
assert(num_defined([pos,rot])>0, "Must give pos or rot")
is_undef(rot) ? [name, pos, default(orient,UP), default(spin,0)]
:
let(
flip = default(flip,false),
pos = default(pos,apply(rot,CTR)),
rotpart = _force_rot(rot),
dummy = assert(approx(det4(rotpart),1), "Input rotation is not a rotation matrix"),
dir = flip ? apply(rotpart,DOWN)
: apply(rotpart,UP),
rot = flip? affine3d_rot_by_axis(apply(rotpart,BACK),180)*rot
: rot,
decode=rot_decode(rot(to=UP,from=dir)*_force_rot(rot)),
spin = decode[0]*sign(decode[1].z)
)
[name, pos, dir, spin];
function _force_rot(T) =
[for(i=[0:3])
@ -2780,35 +2804,6 @@ function _force_rot(T) =
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()
// Synopsis: Returns the internal geometry description of an attachable object.
// Topics: Attachments

View file

@ -1479,7 +1479,8 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// atype = Select "hull" or "intersect" anchor types. Default: "hull"
// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
// Side Effects:
// `$transforms` is set to the array of transformation matrices that define the swept object.
// `$transforms` is set to the array of transformation matrices that define the swept object.
// `$scales` is set to the array of scales that were applied at each point to create the swept object.
// Anchor Types:
// "hull" = Anchors to the virtual convex hull of the shape.
// "intersect" = Anchors to the surface of the shape.
@ -1775,33 +1776,38 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// 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);
// shape = back_half(right_half(star(n=5,id=5,od=10)),y=-1)[0];
// path = arc(angle=[0,180],d=30);
// path_sweep(shape,path,method="natural")
// path_sweep(shape,path,method="natural",scale=[1,1.5])
// recolor("red"){
// attach("start",TOP) stroke([path3d(shape[0])],width=.5);
// attach("end") stroke([path3d(last(shape))],width=.5);
// attach("start",TOP) stroke([path3d(shape)],width=.5);
// attach("end") stroke([path3d(yscale(1.5,shape))],width=.5);
// }
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,
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")
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
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);
trans_scale = 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,_return_scales=true);
transforms = trans_scale[0];
scales = trans_scale[1];
firstscale = [1/scales[0].x, 1/scales[0].y];
lastscale = [1/last(scales).x, 1/last(scales).y];
echo(scales=firstscale,lastscale);
vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, caps=caps,style=style);
shapecent = point3d(centroid(shape));
$transforms = transforms;
$scales = scales;
anchors = closed ? []
:
[
transform_anchor("start", transforms[0], flip=true),
transform_anchor("end", last(transforms)),
transform_anchor("start-centroid", transforms[0]*move(shapecent), flip=true),
transform_anchor("end-centroid", last(transforms)*move(shapecent))
named_anchor("start", rot=transforms[0]*scale(firstscale), flip=true),
named_anchor("end", rot=last(transforms)*scale(lastscale)),
named_anchor("start-centroid", rot=transforms[0]*move(shapecent)*scale(firstscale), flip=true),
named_anchor("end-centroid", rot=last(transforms)*move(shapecent)*scale(lastscale))
];
if (profiles){
rshape = is_path(shape) ? [path3d(shape)]
@ -1821,11 +1827,11 @@ module path_sweep(shape, path, method="incremental", normal, closed, twist=0, tw
function 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", transforms=false,
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") =
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull",_return_scales=false) =
is_1region(path) ? path_sweep(shape=shape,path=path[0], method=method, normal=normal, closed=default(closed,true),
twist=twist, scale=scale, scale_by_length=scale_by_length, twist_by_length=twist_by_length, symmetry=symmetry, last_normal=last_normal,
tangent=tangent, uniform=uniform, relaxed=relaxed, caps=caps, style=style, transforms=transforms,
anchor=anchor, cp=cp, spin=spin, orient=orient, atype=atype) :
anchor=anchor, cp=cp, spin=spin, orient=orient, atype=atype, _return_scales=_return_scales) :
let(closed=default(closed,false))
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"")
assert(!closed || twist % (360/symmetry)==0, str("For a closed sweep, twist must be a multiple of 360/symmetry = ",360/symmetry))
@ -1977,7 +1983,9 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0,
apply(transform_list[L], rshape)),
dummy = ends_match ? 0 : echo("WARNING: ***** The points do not match when closing the model in path_sweep() *****")
)
transforms ? transform_list
transforms && _return_scales
? [transform_list,scale]
: transforms ? transform_lislt
: sweep(is_path(shape)?clockwise_polygon(shape):shape, transform_list, closed=false, caps=fullcaps,style=style,
anchor=anchor,cp=cp,spin=spin,orient=orient,atype=atype);