Skip to content

Commit da7a8a5

Browse files
author
cmarrin
committed
Clarified Unmatrix, added section on converting from 3x2 matrix to 4x4 matrix and added example of recomposing components
1 parent 7f560e9 commit da7a8a5

2 files changed

Lines changed: 409 additions & 289 deletions

File tree

css3-2d-transforms/Overview.html

Lines changed: 214 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -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 &lt; 4; i++)
672-
for (j = 0; j &lt; 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 &lt; 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. &#x27;row&#x27; is a 3 element array of 3 component vectors
718-
for (i = 0; i &lt; 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) &lt; 0)
753-
for (i = 0; i &lt; 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

Comments
 (0)