Move affine3d_frame_map to transforms.scad and rename to frame_map.

Add module version.  Change all calls.
This commit is contained in:
Adrian Mariano 2021-09-06 18:07:03 -04:00
parent 4fee72f517
commit 0aad39c7e5
7 changed files with 102 additions and 72 deletions

View file

@ -882,64 +882,6 @@ function affine3d_rot_from_to(from, to) =
];
// Function: affine3d_frame_map()
// Usage:
// map = affine3d_frame_map(v1, v2, v3, [reverse=]);
// map = affine3d_frame_map(x=VECTOR1, y=VECTOR2, [reverse=]);
// map = affine3d_frame_map(x=VECTOR1, z=VECTOR2, [reverse=]);
// map = affine3d_frame_map(y=VECTOR1, z=VECTOR2, [reverse=]);
// Topics: Affine, Matrices, Transforms, Rotation
// See Also: rot(), xrot(), yrot(), zrot(), affine2d_zrot()
// Description:
// Returns a transformation that maps one coordinate frame to another. You must specify two or
// three of `x`, `y`, and `z`. The specified axes are mapped to the vectors you supplied. If you
// give two inputs, the third vector is mapped to the appropriate normal to maintain a right hand
// coordinate system. If the vectors you give are orthogonal the result will be a rotation and the
// `reverse` parameter will supply the inverse map, which enables you to map two arbitrary
// coordinate systems to each other by using the canonical coordinate system as an intermediary.
// You cannot use the `reverse` option with non-orthogonal inputs.
// Arguments:
// x = Destination 3D vector for x axis.
// y = Destination 3D vector for y axis.
// z = Destination 3D vector for z axis.
// reverse = reverse direction of the map for orthogonal inputs. Default: false
// Example:
// T = affine3d_frame_map(x=[1,1,0], y=[-1,1,0]); // This map is just a rotation around the z axis
// Example:
// T = affine3d_frame_map(x=[1,0,0], y=[1,1,0]); // This map is not a rotation because x and y aren't orthogonal
// Example:
// // The next map sends [1,1,0] to [0,1,1] and [-1,1,0] to [0,-1,1]
// T = affine3d_frame_map(x=[0,1,1], y=[0,-1,1]) * affine3d_frame_map(x=[1,1,0], y=[-1,1,0],reverse=true);
function affine3d_frame_map(x,y,z, reverse=false) =
assert(num_defined([x,y,z])>=2, "Must define at least two inputs")
let(
xvalid = is_undef(x) || (is_vector(x) && len(x)==3),
yvalid = is_undef(y) || (is_vector(y) && len(y)==3),
zvalid = is_undef(z) || (is_vector(z) && len(z)==3)
)
assert(xvalid,"Input x must be a length 3 vector")
assert(yvalid,"Input y must be a length 3 vector")
assert(zvalid,"Input z must be a length 3 vector")
let(
x = is_undef(x)? undef : unit(x,RIGHT),
y = is_undef(y)? undef : unit(y,BACK),
z = is_undef(z)? undef : unit(z,UP),
map = is_undef(x)? [cross(y,z), y, z] :
is_undef(y)? [x, cross(z,x), z] :
is_undef(z)? [x, y, cross(x,y)] :
[x, y, z]
)
reverse? (
let(
ocheck = (
approx(map[0]*map[1],0) &&
approx(map[0]*map[2],0) &&
approx(map[1]*map[2],0)
)
)
assert(ocheck, "Inputs must be orthogonal when reverse==true")
[for (r=map) [for (c=r) c, 0], [0,0,0,1]]
) : [for (r=transpose(map)) [for (c=r) c, 0], [0,0,0,1]];

View file

@ -224,7 +224,7 @@ function project_plane(plane,p) =
y = unit(plane[1]-plane[0]), // y axis goes to point b
x = unit(v-(v*y)*y) // x axis
)
affine3d_frame_map(x,y) * move(-plane[0])
frame_map(x,y) * move(-plane[0])
: is_vector(plane,4) && is_undef(p) ? // no data, plane given in "plane"
assert(_valid_plane(plane), "Plane is not valid")
let(
@ -280,7 +280,7 @@ function lift_plane(plane, p) =
y = unit(plane[1]-plane[0]), // y axis goes to point b
x = unit(v-(v*y)*y) // x axis
)
move(plane[0]) * affine3d_frame_map(x,y,reverse=true)
move(plane[0]) * frame_map(x,y,reverse=true)
: is_vector(plane,4) && is_undef(p) ? // no data, plane given in "plane"
assert(_valid_plane(plane), "Plane is not valid")
let(

View file

@ -1630,7 +1630,7 @@ module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed=fals
if(planar) {
rot(from=[0,1],to=cutlist[i][3]) children();
} else {
multmatrix(affine3d_frame_map(x=cutlist[i][2], z=cutlist[i][3]))
frame_map(x=cutlist[i][2], z=cutlist[i][3])
children();
}
} else {
@ -1777,9 +1777,9 @@ module path_text(path, text, font, size, thickness=1, lettersize, offset=0, reve
)
move(pts[i][0])
multmatrix(affine3d_frame_map(x=pts[i][2]-adjustment,
z=usetop ? undef : normpts[i],
y=usetop ? toppts[i] : undef))
frame_map(x=pts[i][2]-adjustment,
z=usetop ? undef : normpts[i],
y=usetop ? toppts[i] : undef)
up(offset-thickness/2)
linear_extrude(height=thickness)
left(lsize[0]/2)text(text[i], font=font, size=size);

View file

@ -1355,7 +1355,7 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
let(rotations =
[for( i = 0,
ynormal = normal - (normal * tangents[0])*tangents[0],
rotation = affine3d_frame_map(y=ynormal, z=tangents[0])
rotation = frame_map(y=ynormal, z=tangents[0])
;
i < len(tangents) + (closed?1:0) ;
rotation = i<len(tangents)-1+(closed?1:0)? rot(from=tangents[i],to=tangents[(i+1)%L])*rotation : undef,
@ -1374,7 +1374,7 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
last_tangent = last(tangents),
lastynormal = last_normal - (last_normal * last_tangent) * last_tangent
)
affine3d_frame_map(y=lastynormal, z=last_tangent),
frame_map(y=lastynormal, z=last_tangent),
mismatch = transpose(last(rotations)) * reference_rot,
correction_twist = atan2(mismatch[1][0], mismatch[0][0]),
// Spread out this extra twist over the whole sweep so that it doesn't occur
@ -1387,7 +1387,7 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
[for(i=[0:L-(closed?0:1)]) let(
ynormal = relaxed ? normals[i%L] : normals[i%L] - (normals[i%L] * tangents[i%L])*tangents[i%L],
znormal = relaxed ? tangents[i%L] - (normals[i%L] * tangents[i%L])*normals[i%L] : tangents[i%L],
rotation = affine3d_frame_map(y=ynormal, z=znormal)
rotation = frame_map(y=ynormal, z=znormal)
)
assert(approx(ynormal*znormal,0),str("Supplied normal is parallel to the path tangent at point ",i))
translate(path[i%L])*rotation*zrot(-twist*pathfrac[i]),
@ -1400,7 +1400,7 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
dummy = min(testnormals) < .5 ? echo("WARNING: ***** Abrupt change in normal direction. Consider a different method *****") :0
)
[for(i=[0:L-(closed?0:1)]) let(
rotation = affine3d_frame_map(x=pathnormal[i%L], z=tangents[i%L])
rotation = frame_map(x=pathnormal[i%L], z=tangents[i%L])
)
translate(path[i%L])*rotation*zrot(-twist*pathfrac[i])
] :

View file

@ -918,7 +918,7 @@ module generic_threaded_rod(
dummy1 = assert(_r1>depth && _r2>depth, "Screw profile deeper than rod radius");
map_threads = right((_r1 + _r2) / 2) // Shift profile out to thread radius
* affine3d_skew(sxz=(_r2-_r1)/l) // Skew correction for tapered threads
* affine3d_frame_map(x=[0,0,1], y=[1,0,0]) // Map profile to 3d, parallel to z axis
* frame_map(x=[0,0,1], y=[1,0,0]) // Map profile to 3d, parallel to z axis
* scale(pitch); // scale profile by pitch
hig_table = [
[-twist/2-0.0001, 0],

View file

@ -1,6 +1,14 @@
//////////////////////////////////////////////////////////////////////
// LibFile: transforms.scad
// Functions and modules that provide shortcuts for translation, rotation, mirror and skew operations. The shortcuts can act on geometry, like the usual OpenSCAD rotate() and translate(). They also work as functions that operate on lists of points in various forms: paths, VNFS and bezier patches. Lastly, the function form of the shortcuts can return a matrix representing the operation the shortcut performs. The rotation and scaling shortcuts accept an optional centerpoint for the rotation or scaling operation.
// Functions and modules that provide shortcuts for translation,
// rotation and mirror operations. Also provided are skew and frame_map
// which remaps the coordinate axes. The shortcuts can act on
// geometry, like the usual OpenSCAD rotate() and translate(). They
// also work as functions that operate on lists of points in various
// forms: paths, VNFS and bezier patches. Lastly, the function form
// of the shortcuts can return a matrix representing the operation
// the shortcut performs. The rotation and scaling shortcuts accept
// an optional centerpoint for the rotation or scaling operation.
// Includes:
// include <BOSL2/std.scad>
//////////////////////////////////////////////////////////////////////
@ -771,6 +779,8 @@ module xyzrot(a=0, p, cp)
function xyzrot(a=0, p, cp) = rot(a=a, v=[1,1,1], cp=cp, p=p);
//////////////////////////////////////////////////////////////////////
// Section: Scaling and Mirroring
//////////////////////////////////////////////////////////////////////
@ -1439,9 +1449,87 @@ function yzflip(p, cp=0) =
//////////////////////////////////////////////////////////////////////
// Section: Skewing
// Section: Other Transformations
//////////////////////////////////////////////////////////////////////
// Function&Module: frame_map()
// Usage: As module
// frame_map(v1, v2, v3, [reverse=]) { ... }
// Usage: As function to remap points
// transformed = frame_map(v1, v2, v3, p=points, [reverse=]);
// Usage: As function to return a transformation matrix:
// map = frame_map(v1, v2, v3, [reverse=]);
// map = frame_map(x=VECTOR1, y=VECTOR2, [reverse=]);
// map = frame_map(x=VECTOR1, z=VECTOR2, [reverse=]);
// map = frame_map(y=VECTOR1, z=VECTOR2, [reverse=]);
// Topics: Affine, Matrices, Transforms, Rotation
// See Also: rot(), xrot(), yrot(), zrot(), affine2d_zrot()
// Description:
// Maps one coordinate frame to another. You must specify two or
// three of `x`, `y`, and `z`. The specified axes are mapped to the vectors you supplied, so if you
// specify x=[1,1] then the x axis will be mapped to the line y=x. If you
// give two inputs, the third vector is mapped to the appropriate normal to maintain a right hand
// coordinate system. If the vectors you give are orthogonal the result will be a rotation and the
// `reverse` parameter will supply the inverse map, which enables you to map two arbitrary
// coordinate systems to each other by using the canonical coordinate system as an intermediary.
// You cannot use the `reverse` option with non-orthogonal inputs. Note that only the direction
// of the specified vectors matters: the transformation will not apply scaling, though it can
// skew if your provide non-orthogonal axes.
// Arguments:
// x = Destination 3D vector for x axis.
// y = Destination 3D vector for y axis.
// z = Destination 3D vector for z axis.
// reverse = reverse direction of the map for orthogonal inputs. Default: false
// Example: Remap axes after linear extrusion
// frame_map(x=[0,1,0], y=[0,0,1]) linear_extrude(height=10) square(3);
// Example: This map is just a rotation around the z axis
// mat = frame_map(x=[1,1,0], y=[-1,1,0]);
// Example: This map is not a rotation because x and y aren't orthogonal
// mat = frame_map(x=[1,0,0], y=[1,1,0]);
// Example: This sends [1,1,0] to [0,1,1] and [-1,1,0] to [0,-1,1]
// mat = frame_map(x=[0,1,1], y=[0,-1,1]) * frame_map(x=[1,1,0], y=[-1,1,0],reverse=true);
function frame_map(x,y,z, p, reverse=false) =
is_def(p)
? apply(frame_map(x,y,z,reverse=reverse), p)
:
assert(num_defined([x,y,z])>=2, "Must define at least two inputs")
let(
xvalid = is_undef(x) || (is_vector(x) && len(x)==3),
yvalid = is_undef(y) || (is_vector(y) && len(y)==3),
zvalid = is_undef(z) || (is_vector(z) && len(z)==3)
)
assert(xvalid,"Input x must be a length 3 vector")
assert(yvalid,"Input y must be a length 3 vector")
assert(zvalid,"Input z must be a length 3 vector")
let(
x = is_undef(x)? undef : unit(x,RIGHT),
y = is_undef(y)? undef : unit(y,BACK),
z = is_undef(z)? undef : unit(z,UP),
map = is_undef(x)? [cross(y,z), y, z] :
is_undef(y)? [x, cross(z,x), z] :
is_undef(z)? [x, y, cross(x,y)] :
[x, y, z]
)
reverse? (
let(
ocheck = (
approx(map[0]*map[1],0) &&
approx(map[0]*map[2],0) &&
approx(map[1]*map[2],0)
)
)
assert(ocheck, "Inputs must be orthogonal when reverse==true")
[for (r=map) [for (c=r) c, 0], [0,0,0,1]]
) : [for (r=transpose(map)) [for (c=r) c, 0], [0,0,0,1]];
module frame_map(x,y,z,p,reverse=false)
{
assert(is_undef(p), "Module form `frame_map()` does not accept p= argument.");
multmatrix(frame_map(x,y,z,reverse=reverse))
children();
}
// Function&Module: skew()
// Usage: As Module

View file

@ -433,7 +433,7 @@ function turtle3d(commands, state=RIGHT, transforms=false, full_state=false, rep
state = is_matrix(state,4,4) ? [[state],[yrot(90)],1,90,0] :
is_vector(state,3) ?
let( updir = UP - (UP * state) * state / (state*state) )
[[affine3d_frame_map(x=state, z=approx(norm(updir),0) ? FWD : updir)], [yrot(90)],1, 90, 0]
[[frame_map(x=state, z=approx(norm(updir),0) ? FWD : updir)], [yrot(90)],1, 90, 0]
: assert(_turtle3d_state_valid(state), "Supplied state is not valid")
state,
finalstate = _turtle3d_repeat(commands, state, repeat)