@@ -141,6 +141,18 @@ <h2 class="no-num no-toc" id=contents>Table of contents</h2>
141141
142142 < li > < a href ="#matrix-decomposition "> < span class =secno > 7. </ span > Matrix
143143 decomposition for animation </ a >
144+ < ul class =toc >
145+ < li > < a href ="#d-matrix-to-4x4-matrix-conversion "> < span class =secno > 7.1.
146+ </ span > 2D matrix to 4x4 matrix conversion</ a >
147+
148+ < li > < a href ="#unmatrix "> < span class =secno > 7.2. </ span > Unmatrix</ a >
149+
150+ < li > < a href ="#animating-the-components "> < span class =secno > 7.3.
151+ </ span > Animating the components</ a >
152+
153+ < li > < a href ="#recomposing-the-matrix "> < span class =secno > 7.4.
154+ </ span > Recomposing the matrix</ a >
155+ </ ul >
144156
145157 < li > < a href ="#dom-interfaces "> < span class =secno > 8. </ span > DOM Interfaces
146158 </ a >
@@ -632,156 +644,216 @@ <h2 id=matrix-decomposition><span class=secno>7. </span> Matrix
632644 4x4 homogeneous matrix. A 3x2 2D matrix is therefore first converted to
633645 4x4 homogeneous form.
634646
647+ < h3 id =d-matrix-to-4x4-matrix-conversion > < span class =secno > 7.1. </ span > 2D
648+ matrix to 4x4 matrix conversion</ h3 >
649+
635650 < pre >
636- Input: matrix ; a 4x4 matrix
637- Output: translation ; a 3 component vector
638- rotation ; Euler angles, represented as a 3 component vector
651+ Input: matrix2d ; a 3x2 transformation matrix
652+ ; rotation matrix in m11,m12,m21,m22
653+ ; x,y translation in m31,m32
654+ Output: matrix ; a 4x4 homgeneous matrix
655+
656+ matrix[0][0] = matrix2d[0][0]
657+ matrix[0][1] = matrix2d[0][1]
658+ matrix[0][2] = 0
659+ matrix[0][3] = 0
660+
661+ matrix[1][0] = matrix2d[1][0]
662+ matrix[1][1] = matrix2d[1][1]
663+ matrix[1][2] = 0
664+ matrix[1][3] = 0
665+
666+ matrix[2][0] = 0
667+ matrix[2][1] = 0
668+ matrix[2][2] = 1
669+ matrix[2][3] = 0
670+
671+ matrix[3][0] = matrix2d[2][0]
672+ matrix[3][1] = matrix2d[2][1]
673+ matrix[3][2] = 0
674+ matrix[3][3] = 1
675+
676+ return matrix
677+ </ pre >
678+
679+ < h3 id =unmatrix > < span class =secno > 7.2. </ span > Unmatrix</ h3 >
680+
681+ < pre >
682+ Input: matrix ; a 4x4 matrix
683+ Output: translate ; a 3 component vector
684+ rotate ; euler angles, represented as a 3 component vector
639685 scale ; a 3 component vector
640686 skew ; skew factors XY,XZ,YZ represented as a 3 component vector
641687 perspective ; a 4 component vector
642688 Returns false if the matrix cannot be decomposed, true if it can
689+
690+ Supporting functions (point is a 3 component vector, matrix is a 4x4 matrix):
691+ float determinant(matrix) returns the 4x4 determinant of the matrix
692+ matrix inverse(matrix) returns the inverse of the passed matrix
693+ matrix transpose(matrix) returns the transpose of the passed matrix
694+ point multVecMatrix(point, matrix) multiplies the passed point by the passed matrix
695+ and returns the transformed point
696+ float length(point) returns the length of the passed vector
697+ point normalize(point) normalizes the length of the passed point to 1
698+ float dot(point, point) returns the dot product of the passed points
699+ float cos(float) returns the cosine of the passed angle in radians
700+ float asin(float) returns the arcsine in radians of the passed value
701+ float atan2(float y, float x) returns the principal value of the arc tangent of
702+ y/x, using the signs of both arguments to determine
703+ the quadrant of the return value
704+
705+ Decomposition also makes use of the following function:
706+ point combine(point a, point b, float ascl, float bscl)
707+ result[0] = (ascl * a[0]) + (bscl * b[0])
708+ result[1] = (ascl * a[1]) + (bscl * b[1])
709+ result[2] = (ascl * a[2]) + (bscl * b[2])
710+ return result
711+
712+ // Normalize the matrix.
713+ if (matrix[3][3] == 0)
714+ return false
715+
716+ for (i = 0; i < 4 ; i++)
717+ for (j = 0; j < 4; j++)
718+ matrix[i][j] /= matrix[3][3]
719+
720+ // perspectiveMatrix is used to solve for perspective, but it also provides
721+ // an easy way to test for singularity of the upper 3x3 component.
722+ perspectiveMatrix = matrix
723+
724+ for (i = 0; i < 3 ; i++)
725+ perspectiveMatrix[i][3] = 0
726+
727+ perspectiveMatrix[3][3] = 1
728+
729+ if (determinant(perspectiveMatrix) == 0)
730+ return false
731+
732+ // First, isolate perspective.
733+ if (matrix[0][3] != 0 || matrix[1][3] ! = 0 || matrix[2][3] ! = 0)
734+ // rightHandSide is the right hand side of the equation.
735+ rightHandSide[0] = matrix[0][3];
736+ rightHandSide[1] = matrix[1][3];
737+ rightHandSide[2] = matrix[2][3];
738+ rightHandSide[3] = matrix[3][3];
739+
740+ // Solve the equation by inverting perspectiveMatrix and multiplying
741+ // rightHandSide by the inverse.
742+ inversePerspectiveMatrix = inverse(perspectiveMatrix)
743+ transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix)
744+ perspective = multVecMatrix(rightHandSide, transposedInversePerspectiveMatrix)
745+
746+ // Clear the perspective partition
747+ matrix[0][3] = matrix[1][3] = matrix[2][3] = 0
748+ matrix[3][3] = 1
749+ else
750+ // No perspective.
751+ perspective[0] = perspective[1] = perspective[2] = 0
752+ perspective[3] = 1
753+
754+ // Next take care of translation
755+ translate[0] = matrix[3][0]
756+ matrix[3][0] = 0
757+ translate[1] = matrix[3][1]
758+ matrix[3][1] = 0
759+ translate[2] = matrix[3][2]
760+ matrix[3][2] = 0
761+
762+ // Now get scale and shear. 'row' is a 3 element array of 3 component vectors
763+ for (i = 0; i < 3; i++)
764+ row[i][0] = matrix[i][0]
765+ row[i][1] = matrix[i][1]
766+ row[i][2] = matrix[i][2]
767+
768+ // Compute X scale factor and normalize first row.
769+ scale[0] = length(row[0])
770+ row[0] = normalize(row[0])
771+
772+ // Compute XY shear factor and make 2nd row orthogonal to 1st.
773+ skew[0] = dot(row[0], row[1])
774+ row[1] = combine(row[1], row[0], 1.0, -skew[0])
775+
776+ // Now, compute Y scale and normalize 2nd row.
777+ scale[1] = length(row[1])
778+ row[1] = normalize(row[1])
779+ skew[0] /= scale[1];
780+
781+ // Compute XZ and YZ shears, orthogonalize 3rd row
782+ skew[1] = dot(row[0], row[2])
783+ row[2] = combine(row[2], row[0], 1.0, -skew[1])
784+ skew[2] = dot(row[1], row[2])
785+ row[2] = combine(row[2], row[1], 1.0, -skew[2])
786+
787+ // Next, get Z scale and normalize 3rd row.
788+ scale[2] = length(row[2])
789+ row[2] = normalize(row[2])
790+ skew[1] /= scale[2]
791+ skew[2] /= scale[2]
792+
793+ // At this point, the matrix (in rows) is orthonormal.
794+ // Check for a coordinate system flip. If the determinant
795+ // is -1, then negate the matrix and the scaling factors.
796+ pdum3 = cross(row[1], row[2])
797+ if (dot(row[0], pdum3) < 0)
798+ for (i = 0; i < 3; i++) {
799+ scale[0] *= -1;
800+ row[i][0] *= -1
801+ row[i][1] *= -1
802+ row[i][2] *= -1
803+
804+ // Now, get the rotations out
805+ rotate[1] = asin(-row[0][2]);
806+ if (cos(rotate[1]) != 0)
807+ rotate[0] = atan2(row[1][2], row[2][2]);
808+ rotate[2] = atan2(row[0][1], row[0][0]);
809+ else
810+ rotate[0] = atan2(-row[2][0], row[1][1]);
811+ rotate[2] = 0;
812+
813+ return true;</ pre >
814+
815+ < h3 id =animating-the-components > < span class =secno > 7.3. </ span > Animating the
816+ components</ h3 >
817+
818+ < p > Once decomposed, each component of each returned value of the source
819+ matrix is linearly interpolated with the corresponding component of the
820+ destination matrix. For instance, the translate[0], translate[1] and
821+ translate[2] values are interpolated as any other numeric value (see CSS3
822+ Animations spec), and the result is used to set the translation of the
823+ animating element.
824+
825+ < h3 id =recomposing-the-matrix > < span class =secno > 7.4. </ span > Recomposing the
826+ matrix</ h3 >
827+
828+ < p > < em > This section is not normative.</ em >
643829
644- Supporting functions (point is a 3 component vector, matrix is a 4x4 matrix):
645- float determinant(matrix) returns the 4x4 determinant of the matrix
646- matrix inverse(matrix) returns the inverse of the passed matrix
647- matrix transpose(matrix) returns the transpose of the passed matrix
648- point multVecMatrix(point, matrix) multiplies the passed point by the passed matrix
649- and returns the transformed point
650- float length(point) returns the length of the passed vector
651- point normalize(point) normalizes the length of the passed point to 1
652- float dot(point, point) returns the dot product of the passed points
653- float cos(float) returns the cosine of the passed angle in radians
654- float asin(float) returns the arcsine in radians of the passed value
655- float atan2(float y, float x) returns the principal value of the arc tangent of
656- y/x, using the signs of both arguments to determine
657- the quadrant of the return value
658-
659- Decomposition also makes use of the following function:
660- point combine(point a, point b, float ascl, float bscl)
661- result[0] = (ascl * a[0]) + (bscl * b[0])
662- result[1] = (ascl * a[1]) + (bscl * b[1])
663- result[2] = (ascl * a[2]) + (bscl * b[2])
664- return result
665-
666-
667- // Normalize the matrix.
668- if (matrix[3][3] == 0)
669- return false
670-
671- for (i = 0; i < 4; i++)
672- for (j = 0; j < 4; j++)
673- matrix[i][j] /= matrix[3][3]
674-
675- // perspectiveMatrix is used to solve for perspective, but it also provides
676- // an easy way to test for singularity of the upper 3x3 component.
677- perspectiveMatrix = matrix
678-
679- for (i = 0; i < 3; i++)
680- perspectiveMatrix[i][3] = 0
681-
682- perspectiveMatrix[3][3] = 1
683-
684- if (determinant(perspectiveMatrix) == 0)
685- return false
686-
687- // First, isolate perspective.
688- if (matrix[0][3] != 0 || matrix[1][3] != 0 || matrix[2][3] != 0)
689- // rightHandSide is the right hand side of the equation.
690- rightHandSide[0] = matrix[0][3];
691- rightHandSide[1] = matrix[1][3];
692- rightHandSide[2] = matrix[2][3];
693- rightHandSide[3] = matrix[3][3];
694-
695- // Solve the equation by inverting perspectiveMatrix and multiplying
696- // rightHandSide by the inverse.
697- inversePerspectiveMatrix = inverse(perspectiveMatrix)
698- transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix)
699- perspective = multVecMatrix(rightHandSide, transposedInversePerspectiveMatrix)
700-
701- // Clear the perspective partition
702- matrix[0][3] = matrix[1][3] = matrix[2][3] = 0
703- matrix[3][3] = 1
704- else
705- // No perspective.
706- perspective[0] = perspective[1] = perspective[2] = 0
707- perspective[3] = 1
708-
709- // Next take care of translation
710- translate[0] = matrix[3][0]
711- matrix[3][0] = 0
712- translate[1] = matrix[3][1]
713- matrix[3][1] = 0
714- translate[2] = matrix[3][2]
715- matrix[3][2] = 0
716-
717- // Now get scale and shear. 'row' is a 3 element array of 3 component vectors
718- for (i = 0; i < 3; i++)
719- row[i][0] = matrix[i][0]
720- row[i][1] = matrix[i][1]
721- row[i][2] = matrix[i][2]
722-
723- // Compute X scale factor and normalize first row.
724- scale[0] = length(row[0])
725- row[0] = normalize(row[0])
726-
727- // Compute XY shear factor and make 2nd row orthogonal to 1st.
728- skew[0] = dot(row[0], row[1])
729- row[1] = combine(row[1], row[0], 1.0, -skew[0])
730-
731- // Now, compute Y scale and normalize 2nd row.
732- scale[1] = length(row[1])
733- row[1] = normalize(row[1])
734- skew[0] /= scale[1];
735-
736- // Compute XZ and YZ shears, orthogonalize 3rd row
737- skew[1] = dot(row[0], row[2])
738- row[2] = combine(row[2], row[0], 1.0, -skew[1])
739- skew[2] = dot(row[1], row[2])
740- row[2] = combine(row[2], row[1], 1.0, -skew[2])
741-
742- // Next, get Z scale and normalize 3rd row.
743- scale[2] = length(row[2])
744- row[2] = normalize(row[2])
745- skew[1] /= scale[2]
746- skew[2] /= scale[2]
747-
748- // At this point, the matrix (in rows) is orthonormal.
749- // Check for a coordinate system flip. If the determinant
750- // is -1, then negate the matrix and the scaling factors.
751- pdum3 = cross(row[1], row[2])
752- if (dot(row[0], pdum3) < 0)
753- for (i = 0; i < 3; i++) {
754- scale[0] *= -1;
755- row[i][0] *= -1
756- row[i][1] *= -1
757- row[i][2] *= -1
758-
759- // Now, get the rotations ou
760- rotate[1] = asin(-row[0][2]);
761- if (cos(rotate[1]) != 0)
762- rotate[0] = atan2(row[1][2], row[2][2]);
763- rotate[2] = atan2(row[0][1], row[0][0]);
764- else
765- rotate[0] = atan2(-row[2][0], row[1][1]);
766- rotate[2] = 0;
767-
768- return true;
769- </ pre >
770-
771- < p > Each component of each returned value is linearly interpolated with the
772- corresponding component of the other matrix. The resulting components are
773- then recomposed into a final matrix as though combining the following
774- transform functions:
830+ < p > After interpolation the resulting values are used to position the
831+ element. One way to use these values is to recompose them into a 4x4
832+ matrix. This can be done using the Transformation Functions of the < em > < a
833+ href ="#effects "> transform</ a > </ em > property. The following JavaScript
834+ example produces a string for this purpose:
775835
776836 < pre >
777- matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, perspective[0], perspective[1], perspective[2], perspective[3])
778- translate3d(translation[0], translation[1], translation[2])
779- rotateX(rotation[0]) rotateY(rotation[1]) rotateZ(rotation[2])
780- matrix3d(1,0,0,0, 0,1,0,0, 0,skew[2],1,0, 0,0,0,1)
781- matrix3d(1,0,0,0, 0,1,0,0, skew[1],0,1,0, 0,0,0,1)
782- matrix3d(1,0,0,0, skew[0],1,0,0, 0,0,1,0, 0,0,0,1)
783- scale3d(scale[0], scale[1], scale[2])
784- </ pre >
837+ function compose(translate, rotate, scale, skew, perspective, elementID)
838+ {
839+ var s = "matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, " +
840+ perspective[0] + ", " + perspective[1] + ", " +
841+ perspective[2] + ", " + perspective[3] + ")";
842+
843+ s += " translate3d(" + translate[0] + ", " + translate[1] + ", " + translate[2] + ")";
844+
845+ s += " rotateX(" + rotate[0] + ")";
846+ s += " rotateY(" + rotate[0] + ")";
847+ s += " rotateZ(" + rotate[0] + ")";
848+
849+ s += " matrix3d(1,0,0,0, 0,1,0,0, 0," + skew[2] + ",1,0, 0,0,0,1)";
850+ s += " matrix3d(1,0,0,0, 0,1,0,0, " + skew[1] + ",0,1,0, 0,0,0,1)";
851+ s += " matrix3d(1,0,0,0, " + skew[0] + ",1,0,0, 0,0,1,0, 0,0,0,1)";
852+
853+ s += " scale3d(" + scale[0] + ", " + scale[1] + ", " + scale[2] + ")";
854+
855+ document.getElementById(elementID).style.transform = s;
856+ }</ pre >
785857
786858 < h2 id =dom-interfaces > < span class =secno > 8. </ span > DOM Interfaces</ h2 >
787859
0 commit comments