From 0aad39c7e51a83d447d6decf45748e6de6a9c147 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 6 Sep 2021 18:07:03 -0400 Subject: [PATCH] Move affine3d_frame_map to transforms.scad and rename to frame_map. Add module version. Change all calls. --- affine.scad | 58 ------------------------------- coords.scad | 4 +-- paths.scad | 8 ++--- skin.scad | 8 ++--- threading.scad | 2 +- transforms.scad | 92 +++++++++++++++++++++++++++++++++++++++++++++++-- turtle3d.scad | 2 +- 7 files changed, 102 insertions(+), 72 deletions(-) diff --git a/affine.scad b/affine.scad index 0c27e47..f510022 100644 --- a/affine.scad +++ b/affine.scad @@ -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]]; diff --git a/coords.scad b/coords.scad index 7f4630d..ffd0973 100644 --- a/coords.scad +++ b/coords.scad @@ -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( diff --git a/paths.scad b/paths.scad index 2adb204..0eddd19 100644 --- a/paths.scad +++ b/paths.scad @@ -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); diff --git a/skin.scad b/skin.scad index b9752aa..ab2fb69 100644 --- a/skin.scad +++ b/skin.scad @@ -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 = idepth && _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], diff --git a/transforms.scad b/transforms.scad index 2056aae..67de0e4 100644 --- a/transforms.scad +++ b/transforms.scad @@ -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 ////////////////////////////////////////////////////////////////////// @@ -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 diff --git a/turtle3d.scad b/turtle3d.scad index 200c6e2..0b4c943 100644 --- a/turtle3d.scad +++ b/turtle3d.scad @@ -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)