2121
2222 < h1 > CSS 2D Transforms</ h1 >
2323
24- < h2 class ="no-num no-toc " id =longstatus-date > Editor's Draft 02 August 2011</ h2 >
24+ < h2 class ="no-num no-toc " id =longstatus-date > Editor's Draft 6 September
25+ 2011</ h2 >
2526
2627 < dl >
2728 < dt > This version:
2829
2930 < dd > < a
30- href ="http://www.w3.org/TR/2011/ED-css3-2d-transforms-20110802 / "> http://dev.w3.org/csswg/css3-2d-transforms/</ a >
31- <!--http://www.w3.org/TR/2011/WD-css3-2d-transforms-20110802 -->
31+ href ="http://www.w3.org/TR/2011/ED-css3-2d-transforms-20110906 / "> http://dev.w3.org/csswg/css3-2d-transforms/</ a >
32+ <!--http://www.w3.org/TR/2011/WD-css3-2d-transforms-20110906 -->
3233
3334 < dt > Latest version:
3435
@@ -148,15 +149,12 @@ <h2 class="no-num no-toc" id=contents>Table of contents</h2>
148149 < li > < a href ="#matrix-decomposition "> < span class =secno > 7. </ span > Matrix
149150 decomposition for animation </ a >
150151 < ul class =toc >
151- < li > < a href ="#d-matrix-to-4x4-matrix-conversion "> < span class =secno > 7.1.
152- </ span > 2D matrix to 4x4 matrix conversion</ a >
152+ < li > < a href ="#unmatrix "> < span class =secno > 7.1. </ span > Unmatrix</ a >
153153
154- < li > < a href ="#unmatrix "> < span class =secno > 7.2. </ span > Unmatrix</ a >
155-
156- < li > < a href ="#animating-the-components "> < span class =secno > 7.3.
154+ < li > < a href ="#animating-the-components "> < span class =secno > 7.2.
157155 </ span > Animating the components</ a >
158156
159- < li > < a href ="#recomposing-the-matrix "> < span class =secno > 7.4 .
157+ < li > < a href ="#recomposing-the-matrix "> < span class =secno > 7.3 .
160158 </ span > Recomposing the matrix</ a >
161159 </ ul >
162160
@@ -645,77 +643,27 @@ <h2 id=matrix-decomposition><span class=secno>7. </span> Matrix
645643 decomposition for animation</ h2 >
646644
647645 < p > When interpolating between 2 matrices, each is decomposed into the
648- corresponding translation, rotation, scale, skew, and perspective values.
649- Not all matrices can be accurately described by these values. Those that
650- can't are decomposed into the most accurate representation possible, using
651- the technique below. This technique is taken from The "unmatrix" method in
652- "Graphics Gems II, edited by Jim Arvo". The pseudocode below works on a
653- 4x4 homogeneous matrix. A 3x2 2D matrix is therefore first converted to
654- 4x4 homogeneous form.
655-
656- < h3 id =d-matrix-to-4x4-matrix-conversion > < span class =secno > 7.1. </ span > 2D
657- matrix to 4x4 matrix conversion</ h3 >
658-
659- < div class =issue >
660- < p > The pseudocode here is written with column-major matrices, as would be
661- used in C/C++, but the SVG spec illustrates matrixes using row-major
662- notation. Change this pseudocode to avoid confusion?</ p >
663-
664- < p > It's also unfortunate that we have to include math for 4x4 matrices
665- into the 2D transforms spec. Find a way to avoid that?</ p >
666- </ div >
646+ corresponding translation, rotation, scale and skew values. Not all
647+ matrices can be accurately described by these values. Those that can't are
648+ decomposed into the most accurate representation possible, using the
649+ technique below. This technique is taken from The "unmatrix" method in
650+ "Graphics Gems II, edited by Jim Arvo", simplified for the 2D case.
667651
668- < pre >
669- Input: matrix2d ; a 3x2 transformation matrix
670- ; rotation matrix in m11,m12,m21,m22
671- ; x,y translation in m31,m32
672- Output: matrix ; a 4x4 homgeneous matrix
673-
674- matrix[0][0] = matrix2d[0][0]
675- matrix[0][1] = matrix2d[0][1]
676- matrix[0][2] = 0
677- matrix[0][3] = 0
678-
679- matrix[1][0] = matrix2d[1][0]
680- matrix[1][1] = matrix2d[1][1]
681- matrix[1][2] = 0
682- matrix[1][3] = 0
683-
684- matrix[2][0] = 0
685- matrix[2][1] = 0
686- matrix[2][2] = 1
687- matrix[2][3] = 0
688-
689- matrix[3][0] = matrix2d[2][0]
690- matrix[3][1] = matrix2d[2][1]
691- matrix[3][2] = 0
692- matrix[3][3] = 1
693-
694- return matrix
695- </ pre >
696-
697- < h3 id =unmatrix > < span class =secno > 7.2. </ span > Unmatrix</ h3 >
652+ < h3 id =unmatrix > < span class =secno > 7.1. </ span > Unmatrix</ h3 >
698653
699654 < pre >
700- Input: matrix ; a 4x4 matrix
701- Output: translate ; a 3 component vector
702- rotate ; Euler angles, represented as a 3 component vector
703- scale ; a 3 component vector
704- skew ; skew factors XY,XZ,YZ represented as a 3 component vector
705- perspective ; a 4 component vector
655+ Input: a, b, c, d ; 2x2 matrix (rotate, scale, shear) components
656+ tx, ty ; translation components
657+ Output: translate ; a 2 component vector
658+ rotate ; an angle
659+ scale ; a 2 component vector
660+ skew ; skew factor
706661 Returns false if the matrix cannot be decomposed, true if it can
707662
708- Supporting functions (point is a 3 component vector, matrix is a 4x4 matrix):
709- float determinant(matrix) returns the 4x4 determinant of the matrix
710- matrix inverse(matrix) returns the inverse of the passed matrix
711- matrix transpose(matrix) returns the transpose of the passed matrix
712- point multVecMatrix(point, matrix) multiplies the passed point by the passed matrix
713- and returns the transformed point
663+ Supporting functions (point is a 2 component vector):
714664 float length(point) returns the length of the passed vector
715665 point normalize(point) normalizes the length of the passed point to 1
716666 float dot(point, point) returns the dot product of the passed points
717- float cos(float) returns the cosine of the passed angle in radians
718- float asin(float) returns the arcsine in radians of the passed value
719667 float atan2(float y, float x) returns the principal value of the arc tangent of
720668 y/x, using the signs of both arguments to determine
721669 the quadrant of the return value
@@ -724,152 +672,68 @@ <h3 id=unmatrix><span class=secno>7.2. </span>Unmatrix</h3>
724672 point combine(point a, point b, float ascl, float bscl)
725673 result[0] = (ascl * a[0]) + (bscl * b[0])
726674 result[1] = (ascl * a[1]) + (bscl * b[1])
727- result[2] = (ascl * a[2]) + (bscl * b[2])
728675 return result
729676
730- // Normalize the matrix.
731- if (matrix[3][3] == 0)
732- return false
733-
734- for (i = 0; i < 4; i++)
735- for (j = 0; j < 4; j++)
736- matrix[i][j] /= matrix[3][3]
737-
738- // perspectiveMatrix is used to solve for perspective, but it also provides
739- // an easy way to test for singularity of the upper 3x3 component.
740- perspectiveMatrix = matrix
741-
742- for (i = 0; i < 3; i++)
743- perspectiveMatrix[i][3] = 0
744-
745- perspectiveMatrix[3][3] = 1
746-
747- if (determinant(perspectiveMatrix) == 0)
748- return false
749-
750- // First, isolate perspective.
751- if (matrix[0][3] != 0 || matrix[1][3] != 0 || matrix[2][3] != 0)
752- // rightHandSide is the right hand side of the equation.
753- rightHandSide[0] = matrix[0][3];
754- rightHandSide[1] = matrix[1][3];
755- rightHandSide[2] = matrix[2][3];
756- rightHandSide[3] = matrix[3][3];
757-
758- // Solve the equation by inverting perspectiveMatrix and multiplying
759- // rightHandSide by the inverse.
760- inversePerspectiveMatrix = inverse(perspectiveMatrix)
761- transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix)
762- perspective = multVecMatrix(rightHandSide, transposedInversePerspectiveMatrix)
763-
764- // Clear the perspective partition
765- matrix[0][3] = matrix[1][3] = matrix[2][3] = 0
766- matrix[3][3] = 1
767- else
768- // No perspective.
769- perspective[0] = perspective[1] = perspective[2] = 0
770- perspective[3] = 1
771-
772- // Next take care of translation
773- translate[0] = matrix[3][0]
774- matrix[3][0] = 0
677+ // Make sure the matrix is invertible
678+ if ((a * d - b * c) == 0)
679+ return false
680+
681+ // Take care of translation
682+ translate[0] = tx
775683 translate[1] = matrix[3][1]
776- matrix[3][1] = 0
777- translate[2] = matrix[3][2]
778- matrix[3][2] = 0
779-
780- // Now get scale and shear. 'row' is a 3 element array of 3 component vectors
781- for (i = 0; i < 3; i++)
782- row[i][0] = matrix[i][0]
783- row[i][1] = matrix[i][1]
784- row[i][2] = matrix[i][2]
684+
685+ // Put the components into a 2x2 matrix 'mat'
686+ mat[0][0] = a
687+ mat[0][1] = b
688+ mat[1][0] = c
689+ mat[1][1] = d
785690
786691 // Compute X scale factor and normalize first row.
787692 scale[0] = length(row[0])
788693 row[0] = normalize(row[0])
789694
790- // Compute XY shear factor and make 2nd row orthogonal to 1st.
791- skew[0] = dot(row[0], row[1])
792- row[1] = combine(row[1], row[0], 1.0, -skew[0] )
695+ // Compute shear factor and make 2nd row orthogonal to 1st.
696+ skew = dot(row[0], row[1])
697+ row[1] = combine(row[1], row[0], 1.0, -skew)
793698
794699 // Now, compute Y scale and normalize 2nd row.
795700 scale[1] = length(row[1])
796701 row[1] = normalize(row[1])
797- skew[0] /= scale[1];
798-
799- // Compute XZ and YZ shears, make 3rd row orthogonal
800- skew[1] = dot(row[0], row[2])
801- row[2] = combine(row[2], row[0], 1.0, -skew[1])
802- skew[2] = dot(row[1], row[2])
803- row[2] = combine(row[2], row[1], 1.0, -skew[2])
804-
805- // Next, get Z scale and normalize 3rd row.
806- scale[2] = length(row[2])
807- row[2] = normalize(row[2])
808- skew[1] /= scale[2]
809- skew[2] /= scale[2]
810-
811- // At this point, the matrix (in rows) is orthonormal.
812- // Check for a coordinate system flip. If the determinant
813- // is -1, then negate the matrix and the scaling factors.
814- pdum3 = cross(row[1], row[2])
815- if (dot(row[0], pdum3) < 0)
816- for (i = 0; i < 3; i++) {
817- scale[i] *= -1;
818- row[i][0] *= -1
819- row[i][1] *= -1
820- row[i][2] *= -1
821-
822- // Now, get the rotations out
823- rotate[1] = asin(-row[0][2]);
824- if (cos(rotate[1]) != 0)
825- rotate[0] = atan2(row[1][2], row[2][2]);
826- rotate[2] = atan2(row[0][1], row[0][0]);
827- else
828- rotate[0] = atan2(-row[2][0], row[1][1]);
829- rotate[2] = 0;
702+ skew /= scale[1];
703+
704+ // Now, get the rotation out
705+ rotate = atan2(mat[0][1], mat[0][0])
830706
831707 return true;</ pre >
832708
833- < h3 id =animating-the-components > < span class =secno > 7.3 . </ span > Animating the
709+ < h3 id =animating-the-components > < span class =secno > 7.2 . </ span > Animating the
834710 components</ h3 >
835711
836712 < p > Once decomposed, each component of each returned value of the source
837713 matrix is linearly interpolated with the corresponding component of the
838- destination matrix. For instance, the translate[0], translate[1] and
839- translate[2] values are interpolated numerically, and the result is used
840- to set the translation of the animating element.
714+ destination matrix. For instance, the translate[0] and translate[1] values
715+ are interpolated numerically, and the result is used to set the
716+ translation of the animating element.
841717
842- < h3 id =recomposing-the-matrix > < span class =secno > 7.4 . </ span > Recomposing the
718+ < h3 id =recomposing-the-matrix > < span class =secno > 7.3 . </ span > Recomposing the
843719 matrix</ h3 >
844720
845721 < p > < em > This section is not normative.</ em >
846722
847723 < p > After interpolation the resulting values are used to position the
848- element. One way to use these values is to recompose them into a 4x4
724+ element. One way to use these values is to recompose them into a 3x2
849725 matrix. This can be done using the Transformation Functions of the < a
850726 href ="#effects "> < em > transform</ em > </ a > property. The following JavaScript
851727 example produces a string for this purpose. The values passed in are the
852728 output of the Unmatrix function above:
853729
854730 < pre >
855- function compose(translate, rotate, scale, skew, perspective, elementID)
731+ function compose(translate, rotate, scale, skew, elementID)
856732{
857- var s = "matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, " +
858- perspective[0] + ", " + perspective[1] + ", " +
859- perspective[2] + ", " + perspective[3] + ")";
860-
861- s += " translate3d(" + translate[0] + ", " + translate[1] + ", " + translate[2] + ")";
862-
863- s += " rotateX(" + rotate[0] + ")";
864- s += " rotateY(" + rotate[1] + ")";
865- s += " rotateZ(" + rotate[2] + ")";
866-
867- s += " matrix3d(1,0,0,0, 0,1,0,0, 0," + skew[2] + ",1,0, 0,0,0,1)";
868- s += " matrix3d(1,0,0,0, 0,1,0,0, " + skew[1] + ",0,1,0, 0,0,0,1)";
869- s += " matrix3d(1,0,0,0, " + skew[0] + ",1,0,0, 0,0,1,0, 0,0,0,1)";
870-
871- s += " scale3d(" + scale[0] + ", " + scale[1] + ", " + scale[2] + ")";
872-
733+ var s = " translate(" + translate[0] + ", " + translate[1] + ")";
734+ s += " rotate(" + rotate + ")";
735+ s += " skewX(" + skew + ")";
736+ s += " scale(" + scale[0] + ", " + scale[1] + ")";
873737 document.getElementById(elementID).style.transform = s;
874738}</ pre >
875739
0 commit comments