From Trammell Hudson's Projects

Change of Basis

Change of basis

Sometimes you need to change the frame of reference for an OpenSCAD group given three points on the new plane. Neither of the Minimum Rotation Problem technique and the Rotation Rule help examples in the OpenSCAD manual preserve the orientation when they do the transform, so it is necessary to generate a 4x4 Rotation matrix that does the translation and rotation to align to the new plane.

function unit(v) = norm(v)>0 ? v/norm(v) : undef;

// Generate a 4x4 matrix that causes a change of basis
// such that the origin is at point p0, the x axis is aligned
// from p0 to p1 and point p2 is in the first quadrant.
module align_to(p0,p1,p2)
        x = unit(p1-p0);
        yp = unit(p2-p0);
        z = unit(cross(x,yp));
        y = unit(cross(z,x));

                [x[0], y[0], z[0], p0[0]],
                [x[1], y[1], z[1], p0[1]],
                [x[2], y[2], z[2], p0[2]],
                [ 0,   0,    0,    1]

While it is somewhat complicated, this is much simpler than trying to deduce the Euler angles for the rotate() command since it does not perform any trigonometry. The math is suprisingly simple - once the X axis unit vector is computed, the Z axis is found by the cross-product with the third point (which will perpendicular to the plane), and then the Y axis is found by crossing the X with the Z. The fourth column performs a translation of the origin.

As a thought exercise, consider what happens when the augmented unit vector [math]\hat i = (1,0,0,1)[/math] is multiplied by this transformation matrix. Ideally it would result in the product of [math]\vec p0 + \hat{p_1 - p_0} + \vec {p_0}[/math]. This results in [math](x_0 + p_{00}, x_1 + p_{01}, x_2 + p_{02}, 1)[/math], which is the same as [math]\vec {p_0} + \hat x[/math] as desired.