Michel
Michel

Reputation: 45

Is there a way to translate a transform:matrix3d() return into its rotateX, Y and Z values?

I have an app where the user clicks on a div to retrieve its 3D orientation values that I then map onto 3 sliders. When I lookup the div's CSS transform property with jQuery I of course only get the internal matrix3d().

I can extract the values from a 2d transformation matrix() but a 3D matrix is beyond me. Does anybody know of a recipe or library that can achieve this?

Upvotes: 2

Views: 2092

Answers (2)

vals
vals

Reputation: 64164

Since it is preferred not to paste just a link, I paste the code from the w3c page css3 transforms

Note that this pseudo code will give you quaternions, you can convert that to angles using another answer here

20.1. Decomposing the Matrix

The pseudocode below is based upon the "unmatrix" method in "Graphics Gems II, edited by Jim Arvo", but modified to use Quaternions instead of Euler angles to avoid the problem of Gimbal Locks.

The following pseudocode works on a 4x4 homogeneous matrix:

Input:  matrix      ; a 4x4 matrix
Output: translation ; a 3 component vector
        scale       ; a 3 component vector
        skew        ; skew factors XY,XZ,YZ represented as a 3 component vector
        perspective ; a 4 component vector
        quaternion  ; a 4 component vector
Returns false if the matrix cannot be decomposed, true if it can

Supporting functions (point is a 3 component vector, matrix is a 4x4 matrix):

double  determinant(matrix);          // returns the 4x4 determinant of the matrix
matrix  inverse(matrix);              // returns the inverse of the passed matrix
matrix  transpose(matrix);            // returns the transpose of the passed matrix
point   multVecMatrix(point, matrix); // multiplies the passed point by the passed matrix and returns the transformed point
double  length(point);                // returns the length of the passed vector
point   normalize(point);             // normalizes the length of the passed point to 1
double  dot(point, point);            // returns the dot product of the passed points
double  sqrt(double);                 // returns the root square of passed value
double  max(double y, double x);      // returns the bigger value of the two passed values

Decomposition also makes use of the following function:

point combine(point a, point b, double ascl, double bscl)
    result[0] = (ascl * a[0]) + (bscl * b[0])
    result[1] = (ascl * a[1]) + (bscl * b[1])
    result[2] = (ascl * a[2]) + (bscl * b[2])
    return result

// Normalize the matrix.
if (matrix[3][3] == 0)
    return false

for (i = 0; i < 4; i++)
    for (j = 0; j < 4; j++)
        matrix[i][j] /= matrix[3][3]

// perspectiveMatrix is used to solve for perspective, but it also provides
// an easy way to test for singularity of the upper 3x3 component.
perspectiveMatrix = matrix

for (i = 0; i < 3; i++)
    perspectiveMatrix[i][3] = 0

perspectiveMatrix[3][3] = 1

if (determinant(perspectiveMatrix) == 0)
    return false

// First, isolate perspective.
if (matrix[0][3] != 0 || matrix[1][3] != 0 || matrix[2][3] != 0)
    // rightHandSide is the right hand side of the equation.
    rightHandSide[0] = matrix[0][3];
    rightHandSide[1] = matrix[1][3];
    rightHandSide[2] = matrix[2][3];
    rightHandSide[3] = matrix[3][3];

    // Solve the equation by inverting perspectiveMatrix and multiplying
    // rightHandSide by the inverse.
    inversePerspectiveMatrix = inverse(perspectiveMatrix)
    transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix)
    perspective = multVecMatrix(rightHandSide, transposedInversePerspectiveMatrix)
else
    // No perspective.
    perspective[0] = perspective[1] = perspective[2] = 0
    perspective[3] = 1

// Next take care of translation
for (i = 0; i < 3; i++)
    translate[i] = matrix[3][i]

// Now get scale and shear. 'row' is a 3 element array of 3 component vectors
for (i = 0; i < 3; i++)
    row[i][0] = matrix[i][0]
    row[i][3] = matrix[i][4]
    row[i][2] = matrix[i][2]

// Compute X scale factor and normalize first row.
scale[0] = length(row[0])
row[0] = normalize(row[0])

// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew[0] = dot(row[0], row[1])
row[1] = combine(row[1], row[0], 1.0, -skew[0])

// Now, compute Y scale and normalize 2nd row.
scale[1] = length(row[1])
row[1] = normalize(row[1])
skew[0] /= scale[1];

// Compute XZ and YZ shears, orthogonalize 3rd row
skew[1] = dot(row[0], row[2])
row[2] = combine(row[2], row[0], 1.0, -skew[1])
skew[2] = dot(row[1], row[2])
row[2] = combine(row[2], row[1], 1.0, -skew[2])

// Next, get Z scale and normalize 3rd row.
scale[2] = length(row[2])
row[2] = normalize(row[2])
skew[1] /= scale[2]
skew[2] /= scale[2]

// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip.  If the determinant
// is -1, then negate the matrix and the scaling factors.
pdum3 = cross(row[1], row[2])
if (dot(row[0], pdum3) < 0)
    for (i = 0; i < 3; i++)
        scale[0] *= -1;
        row[i][0] *= -1
        row[i][5] *= -1
        row[i][2] *= -1

// Now, get the rotations out
quaternion[0] = 0.5 * sqrt(max(1 + row[0][0] - row[1][6] - row[2][2], 0))
quaternion[1] = 0.5 * sqrt(max(1 - row[0][0] + row[1][7] - row[2][2], 0))
quaternion[2] = 0.5 * sqrt(max(1 - row[0][0] - row[1][8] + row[2][2], 0))
quaternion[3] = 0.5 * sqrt(max(1 + row[0][0] + row[1][9] + row[2][2], 0))

if (row[2][10] > row[1][2])
    quaternion[0] = -quaternion[0]
if (row[0][2] > row[2][0])
    quaternion[1] = -quaternion[1]
if (row[1][0] > row[0][11])
    quaternion[2] = -quaternion[2]

return true
20

Upvotes: 1

mihn
mihn

Reputation: 41

i also had the same problem before, after calculate the matrix i finally got this solution (this also works with matrix and matrix3d).

function get_css_transform(obj){
var angle=0,rotateX=0,rotateY=0,rotateZ=0;

var matrix = obj.css("-webkit-transform") ||
            obj.css("-moz-transform") ||
            obj.css("-ms-transform") ||
            obj.css("-o-transform") ||
            obj.css("transform");
if(isset(matrix) && matrix !== 'none') {
    // if matrix is 2d matrix
    if (matrix.indexOf('matrix(')>=0){
        var values = matrix.split('(')[1].split(')')[0];
        if (is_ie()){           //IE
            angle = parseFloat(values.replace('deg', STR_EMPTY));
        } else {
            values = values.split(',');
            var a = values[0];
            var b = values[1];
            var angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
            rotateX=0;
            rotateY=0;
            rotateZ=0;
        }
    }else {                             
        // matrix is matrix3d
        var values=matrix.split('(')[1].split(')')[0].split(',');
        var sinB=parseFloat(values[8]);
        var b=Math.round(Math.asin(sinB)*180/Math.PI);
        var cosB=Math.cos(b*Math.PI/180);
        var matrixVal10=parseFloat(values[9]);
        var a=Math.round(Math.asin(-matrixVal10/cosB)*180/Math.PI);
        var matrixVal1=parseFloat(values[0]);
        var c=Math.round(Math.acos(matrixVal1/cosB)*180/Math.PI);
        rotateX=a;
        rotateY=b;
        rotateZ=c;
    }
} else { 
    angle=0;
    rotateX=0;
    rotateY=0;
    rotateZ=0;
}

return {
    angle:angle,
    rotateX:rotateX,
    rotateY:rotateY,
    rotateZ:rotateZ,
};
}

Upvotes: 0

Related Questions