- From: Chris Marrin via cvs-syncmail <cvsmail@w3.org>
- Date: Tue, 06 Sep 2011 22:59:50 +0000
- To: public-css-commits@w3.org
Update of /sources/public/csswg/css3-2d-transforms In directory hutz:/tmp/cvs-serv27282 Modified Files: Overview.html Overview.src.html Log Message: Simplified unmatrix, getting rid of 4x4 matrix math Index: Overview.html =================================================================== RCS file: /sources/public/csswg/css3-2d-transforms/Overview.html,v retrieving revision 1.18 retrieving revision 1.19 diff -u -d -r1.18 -r1.19 --- Overview.html 3 Aug 2011 00:51:04 -0000 1.18 +++ Overview.html 6 Sep 2011 22:59:48 -0000 1.19 @@ -21,14 +21,15 @@ <h1>CSS 2D Transforms</h1> - <h2 class="no-num no-toc" id=longstatus-date>Editor's Draft 02 August 2011</h2> + <h2 class="no-num no-toc" id=longstatus-date>Editor's Draft 6 September + 2011</h2> <dl> <dt>This version: <dd> <a - href="http://www.w3.org/TR/2011/ED-css3-2d-transforms-20110802/">http://dev.w3.org/csswg/css3-2d-transforms/</a> - <!--http://www.w3.org/TR/2011/WD-css3-2d-transforms-20110802--> + href="http://www.w3.org/TR/2011/ED-css3-2d-transforms-20110906/">http://dev.w3.org/csswg/css3-2d-transforms/</a> + <!--http://www.w3.org/TR/2011/WD-css3-2d-transforms-20110906--> <dt>Latest version: @@ -148,15 +149,12 @@ <li><a href="#matrix-decomposition"><span class=secno>7. </span> Matrix decomposition for animation </a> <ul class=toc> - <li><a href="#d-matrix-to-4x4-matrix-conversion"><span class=secno>7.1. - </span>2D matrix to 4x4 matrix conversion</a> - - <li><a href="#unmatrix"><span class=secno>7.2. </span>Unmatrix</a> + <li><a href="#unmatrix"><span class=secno>7.1. </span>Unmatrix</a> - <li><a href="#animating-the-components"><span class=secno>7.3. + <li><a href="#animating-the-components"><span class=secno>7.2. </span>Animating the components</a> - <li><a href="#recomposing-the-matrix"><span class=secno>7.4. + <li><a href="#recomposing-the-matrix"><span class=secno>7.3. </span>Recomposing the matrix</a> </ul> @@ -645,77 +643,27 @@ decomposition for animation</h2> <p> When interpolating between 2 matrices, each is decomposed into the - corresponding translation, rotation, scale, skew, and perspective values. - Not all matrices can be accurately described by these values. Those that - can't are decomposed into the most accurate representation possible, using - the technique below. This technique is taken from The "unmatrix" method in - "Graphics Gems II, edited by Jim Arvo". The pseudocode below works on a - 4x4 homogeneous matrix. A 3x2 2D matrix is therefore first converted to - 4x4 homogeneous form. - - <h3 id=d-matrix-to-4x4-matrix-conversion><span class=secno>7.1. </span>2D - matrix to 4x4 matrix conversion</h3> - - <div class=issue> - <p> The pseudocode here is written with column-major matrices, as would be - used in C/C++, but the SVG spec illustrates matrixes using row-major - notation. Change this pseudocode to avoid confusion?</p> - - <p> It's also unfortunate that we have to include math for 4x4 matrices - into the 2D transforms spec. Find a way to avoid that?</p> - </div> - - <pre> - Input: matrix2d ; a 3x2 transformation matrix - ; rotation matrix in m11,m12,m21,m22 - ; x,y translation in m31,m32 - Output: matrix ; a 4x4 homgeneous matrix - - matrix[0][0] = matrix2d[0][0] - matrix[0][1] = matrix2d[0][1] - matrix[0][2] = 0 - matrix[0][3] = 0 - - matrix[1][0] = matrix2d[1][0] - matrix[1][1] = matrix2d[1][1] - matrix[1][2] = 0 - matrix[1][3] = 0 - - matrix[2][0] = 0 - matrix[2][1] = 0 - matrix[2][2] = 1 - matrix[2][3] = 0 - - matrix[3][0] = matrix2d[2][0] - matrix[3][1] = matrix2d[2][1] - matrix[3][2] = 0 - matrix[3][3] = 1 - - return matrix - </pre> + corresponding translation, rotation, scale and skew values. Not all + matrices can be accurately described by these values. Those that can't are + decomposed into the most accurate representation possible, using the + technique below. This technique is taken from The "unmatrix" method in + "Graphics Gems II, edited by Jim Arvo", simplified for the 2D case. - <h3 id=unmatrix><span class=secno>7.2. </span>Unmatrix</h3> + <h3 id=unmatrix><span class=secno>7.1. </span>Unmatrix</h3> <pre> - Input: matrix ; a 4x4 matrix - Output: translate ; a 3 component vector - rotate ; Euler angles, represented as 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 + Input: a, b, c, d ; 2x2 matrix (rotate, scale, shear) components + tx, ty ; translation components + Output: translate ; a 2 component vector + rotate ; an angle + scale ; a 2 component vector + skew ; skew factor 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): - float 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 + Supporting functions (point is a 2 component vector): float length(point) returns the length of the passed vector point normalize(point) normalizes the length of the passed point to 1 float dot(point, point) returns the dot product of the passed points - float cos(float) returns the cosine of the passed angle in radians - float asin(float) returns the arcsine in radians of the passed value float atan2(float y, float x) returns the principal value of the arc tangent of y/x, using the signs of both arguments to determine the quadrant of the return value @@ -724,152 +672,68 @@ point combine(point a, point b, float ascl, float 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) - - // Clear the perspective partition - matrix[0][3] = matrix[1][3] = matrix[2][3] = 0 - matrix[3][3] = 1 - else - // No perspective. - perspective[0] = perspective[1] = perspective[2] = 0 - perspective[3] = 1 - - // Next take care of translation - translate[0] = matrix[3][0] - matrix[3][0] = 0 + // Make sure the matrix is invertible + if ((a * d - b * c) == 0) + return false + + // Take care of translation + translate[0] = tx translate[1] = matrix[3][1] - matrix[3][1] = 0 - translate[2] = matrix[3][2] - matrix[3][2] = 0 - - // 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][1] = matrix[i][1] - row[i][2] = matrix[i][2] + + // Put the components into a 2x2 matrix 'mat' + mat[0][0] = a + mat[0][1] = b + mat[1][0] = c + mat[1][1] = d // 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]) + // Compute shear factor and make 2nd row orthogonal to 1st. + skew = dot(row[0], row[1]) + row[1] = combine(row[1], row[0], 1.0, -skew) // 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, make 3rd row orthogonal - 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[i] *= -1; - row[i][0] *= -1 - row[i][1] *= -1 - row[i][2] *= -1 + skew /= scale[1]; - // Now, get the rotations out - rotate[1] = asin(-row[0][2]); - if (cos(rotate[1]) != 0) - rotate[0] = atan2(row[1][2], row[2][2]); - rotate[2] = atan2(row[0][1], row[0][0]); - else - rotate[0] = atan2(-row[2][0], row[1][1]); - rotate[2] = 0; + // Now, get the rotation out + rotate = atan2(mat[0][1], mat[0][0]) return true;</pre> - <h3 id=animating-the-components><span class=secno>7.3. </span>Animating the + <h3 id=animating-the-components><span class=secno>7.2. </span>Animating the components</h3> <p> Once decomposed, each component of each returned value of the source matrix is linearly interpolated with the corresponding component of the - destination matrix. For instance, the translate[0], translate[1] and - translate[2] values are interpolated numerically, and the result is used - to set the translation of the animating element. + destination matrix. For instance, the translate[0] and translate[1] values + are interpolated numerically, and the result is used to set the + translation of the animating element. - <h3 id=recomposing-the-matrix><span class=secno>7.4. </span>Recomposing the + <h3 id=recomposing-the-matrix><span class=secno>7.3. </span>Recomposing the matrix</h3> <p><em>This section is not normative.</em> <p> After interpolation the resulting values are used to position the - element. One way to use these values is to recompose them into a 4x4 + element. One way to use these values is to recompose them into a 3x2 matrix. This can be done using the Transformation Functions of the <a href="#effects"><em>transform</em></a> property. The following JavaScript example produces a string for this purpose. The values passed in are the output of the Unmatrix function above: <pre> -function compose(translate, rotate, scale, skew, perspective, elementID) +function compose(translate, rotate, scale, skew, elementID) { - var s = "matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, " + - perspective[0] + ", " + perspective[1] + ", " + - perspective[2] + ", " + perspective[3] + ")"; - - s += " translate3d(" + translate[0] + ", " + translate[1] + ", " + translate[2] + ")"; - - s += " rotateX(" + rotate[0] + ")"; - s += " rotateY(" + rotate[1] + ")"; - s += " rotateZ(" + rotate[2] + ")"; - - s += " matrix3d(1,0,0,0, 0,1,0,0, 0," + skew[2] + ",1,0, 0,0,0,1)"; - s += " matrix3d(1,0,0,0, 0,1,0,0, " + skew[1] + ",0,1,0, 0,0,0,1)"; - s += " matrix3d(1,0,0,0, " + skew[0] + ",1,0,0, 0,0,1,0, 0,0,0,1)"; - - s += " scale3d(" + scale[0] + ", " + scale[1] + ", " + scale[2] + ")"; - + var s = " translate(" + translate[0] + ", " + translate[1] + ")"; + s += " rotate(" + rotate + ")"; + s += " skewX(" + skew + ")"; + s += " scale(" + scale[0] + ", " + scale[1] + ")"; document.getElementById(elementID).style.transform = s; }</pre> Index: Overview.src.html =================================================================== RCS file: /sources/public/csswg/css3-2d-transforms/Overview.src.html,v retrieving revision 1.18 retrieving revision 1.19 diff -u -d -r1.18 -r1.19 --- Overview.src.html 3 Aug 2011 00:51:04 -0000 1.18 +++ Overview.src.html 6 Sep 2011 22:59:48 -0000 1.19 @@ -580,76 +580,28 @@ <p> When interpolating between 2 matrices, each is decomposed into the - corresponding translation, rotation, scale, skew, and perspective + corresponding translation, rotation, scale and skew values. Not all matrices can be accurately described by these values. Those that can't are decomposed into the most accurate representation possible, using the technique below. This technique is taken from The - "unmatrix" method in "Graphics Gems II, edited by Jim Arvo". The - pseudocode below works on a 4x4 homogeneous matrix. A 3x2 2D matrix is - therefore first converted to 4x4 homogeneous form. + "unmatrix" method in "Graphics Gems II, edited by Jim Arvo", simplified + for the 2D case. </p> - <h3>2D matrix to 4x4 matrix conversion</h3> - <div class="issue"> - <p> - The pseudocode here is written with column-major matrices, as would be used in C/C++, but the SVG spec - illustrates matrixes using row-major notation. Change this pseudocode to avoid confusion? - </p> - <p> - It's also unfortunate that we have to include math for 4x4 matrices into the 2D transforms spec. Find - a way to avoid that? - </p> - </div> - <pre> - Input: matrix2d ; a 3x2 transformation matrix - ; rotation matrix in m11,m12,m21,m22 - ; x,y translation in m31,m32 - Output: matrix ; a 4x4 homgeneous matrix - - matrix[0][0] = matrix2d[0][0] - matrix[0][1] = matrix2d[0][1] - matrix[0][2] = 0 - matrix[0][3] = 0 - - matrix[1][0] = matrix2d[1][0] - matrix[1][1] = matrix2d[1][1] - matrix[1][2] = 0 - matrix[1][3] = 0 - - matrix[2][0] = 0 - matrix[2][1] = 0 - matrix[2][2] = 1 - matrix[2][3] = 0 - - matrix[3][0] = matrix2d[2][0] - matrix[3][1] = matrix2d[2][1] - matrix[3][2] = 0 - matrix[3][3] = 1 - - return matrix - </pre> - <h3 id="unmatrix">Unmatrix</h3> <pre> - Input: matrix ; a 4x4 matrix - Output: translate ; a 3 component vector - rotate ; Euler angles, represented as 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 + Input: a, b, c, d ; 2x2 matrix (rotate, scale, shear) components + tx, ty ; translation components + Output: translate ; a 2 component vector + rotate ; an angle + scale ; a 2 component vector + skew ; skew factor 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): - float 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 + Supporting functions (point is a 2 component vector): float length(point) returns the length of the passed vector point normalize(point) normalizes the length of the passed point to 1 float dot(point, point) returns the dot product of the passed points - float cos(float) returns the cosine of the passed angle in radians - float asin(float) returns the arcsine in radians of the passed value float atan2(float y, float x) returns the principal value of the arc tangent of y/x, using the signs of both arguments to determine the quadrant of the return value @@ -658,147 +610,63 @@ point combine(point a, point b, float ascl, float 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) - - // Clear the perspective partition - matrix[0][3] = matrix[1][3] = matrix[2][3] = 0 - matrix[3][3] = 1 - else - // No perspective. - perspective[0] = perspective[1] = perspective[2] = 0 - perspective[3] = 1 - - // Next take care of translation - translate[0] = matrix[3][0] - matrix[3][0] = 0 + // Make sure the matrix is invertible + if ((a * d - b * c) == 0) + return false + + // Take care of translation + translate[0] = tx translate[1] = matrix[3][1] - matrix[3][1] = 0 - translate[2] = matrix[3][2] - matrix[3][2] = 0 - - // 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][1] = matrix[i][1] - row[i][2] = matrix[i][2] + + // Put the components into a 2x2 matrix 'mat' + mat[0][0] = a + mat[0][1] = b + mat[1][0] = c + mat[1][1] = d // 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]) + // Compute shear factor and make 2nd row orthogonal to 1st. + skew = dot(row[0], row[1]) + row[1] = combine(row[1], row[0], 1.0, -skew) // 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, make 3rd row orthogonal - 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[i] *= -1; - row[i][0] *= -1 - row[i][1] *= -1 - row[i][2] *= -1 + skew /= scale[1]; - // Now, get the rotations out - rotate[1] = asin(-row[0][2]); - if (cos(rotate[1]) != 0) - rotate[0] = atan2(row[1][2], row[2][2]); - rotate[2] = atan2(row[0][1], row[0][0]); - else - rotate[0] = atan2(-row[2][0], row[1][1]); - rotate[2] = 0; + // Now, get the rotation out + rotate = atan2(mat[0][1], mat[0][0]) return true;</pre> <h3>Animating the components</h3> <p> Once decomposed, each component of each returned value of the source matrix is linearly interpolated - with the corresponding component of the destination matrix. For instance, the translate[0], - translate[1] and translate[2] values are interpolated numerically, and the result is used to set the + with the corresponding component of the destination matrix. For instance, the translate[0] and + translate[1] values are interpolated numerically, and the result is used to set the translation of the animating element. </p> <h3>Recomposing the matrix</h3> <p><em>This section is not normative.</em></p> <p> After interpolation the resulting values are used to position the element. One way to use these values - is to recompose them into a 4x4 matrix. This can be done using the Transformation Functions of the + is to recompose them into a 3x2 matrix. This can be done using the Transformation Functions of the <em>transform</em> property. The following JavaScript example produces a string for this purpose. The values passed in are the output of the Unmatrix function above: </p> <pre> -function compose(translate, rotate, scale, skew, perspective, elementID) +function compose(translate, rotate, scale, skew, elementID) { - var s = "matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, " + - perspective[0] + ", " + perspective[1] + ", " + - perspective[2] + ", " + perspective[3] + ")"; - - s += " translate3d(" + translate[0] + ", " + translate[1] + ", " + translate[2] + ")"; - - s += " rotateX(" + rotate[0] + ")"; - s += " rotateY(" + rotate[1] + ")"; - s += " rotateZ(" + rotate[2] + ")"; - - s += " matrix3d(1,0,0,0, 0,1,0,0, 0," + skew[2] + ",1,0, 0,0,0,1)"; - s += " matrix3d(1,0,0,0, 0,1,0,0, " + skew[1] + ",0,1,0, 0,0,0,1)"; - s += " matrix3d(1,0,0,0, " + skew[0] + ",1,0,0, 0,0,1,0, 0,0,0,1)"; - - s += " scale3d(" + scale[0] + ", " + scale[1] + ", " + scale[2] + ")"; - + var s = " translate(" + translate[0] + ", " + translate[1] + ")"; + s += " rotate(" + rotate + ")"; + s += " skewX(" + skew + ")"; + s += " scale(" + scale[0] + ", " + scale[1] + ")"; document.getElementById(elementID).style.transform = s; }</pre>
Received on Tuesday, 6 September 2011 22:59:57 UTC