8000 Simplified unmatrix, getting rid of 4x4 matrix math · xfq/csswg-drafts@2a85dc1 · GitHub
Skip to content

Commit 2a85dc1

Browse files
author
cmarrin
committed
Simplified unmatrix, getting rid of 4x4 matrix math
1 parent 1d0b404 commit 2a85dc1

2 files changed

Lines changed: 87 additions & 355 deletions

File tree

css3-2d-transforms/Overview.html

Lines changed: 50 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@
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 &lt; 4; i++)
735-
for (j = 0; j &lt; 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 &lt; 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 &lt; 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) &lt; 0)
816-
for (i = 0; i &lt; 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

Comments
 (0)