@@ -1553,21 +1553,21 @@ <h2 id=animation><span class=secno>14. </span> Transitions and animations
15531553 ‘< code class =property > to</ code > ’ transforms are both single
15541554 functions of the same type:
15551555 < ul >
1556- < li > For translate, translateX, translateY, scale, scaleX, scaleY,
1557- rotate, skew, skewX and skewY functions:
1556+ < li > For translate, translate3d, translateX, translateY, translateZ,
1557+ scale, scale3d, scaleX, scaleY, scaleZ, rotate, rotateX, rotateY,
1558+ rotateZ, skew, skewX and skewY functions:
15581559 < ul >
15591560 < li > the individual components of the function are interpolated
15601561 numerically.
15611562 </ ul >
15621563
1563- < li > For matrix:
1564+ < li > For perspective, matrix, matrix3d and rotate3d :
15641565 < ul >
1565- < li > the matrix is decomposed using < a
1566- href ="http://tog.acm.org/GraphicsGems/gemsii/unmatrix.c "> the method
1567- described by unmatrix</ a > into separate translation, scale, rotation
1568- and skew matrices, then each decomposed matrix is interpolated
1569- numerically, and finally combined in order to produce a resulting 3x2
1570- matrix.
1566+ < li > the values are first converted to a 4x4 matrix, then decomposed
1567+ using < a href ="#unmatrix "> the method described by unmatrix</ a > into
1568+ separate translation, scale, rotation, skew and perspective matrices,
1569+ then each decomposed matrix is interpolated numerically, and finally
1570+ combined in order to produce a resulting 4x4 matrix.
15711571 </ ul >
15721572 </ ul >
15731573
@@ -1594,9 +1594,12 @@ <h2 id=animation><span class=secno>14. </span> Transitions and animations
15941594 rotate(50deg)" then the animation will execute as if the ‘< code
15951595 class =property > from</ code > ’ value is "scale(1) rotate(0)".</ p >
15961596
1597- < p > The identity functions are translate(0), translateX(0),
1598- translateY(0), scale(1), scaleX(1), scaleY(1), rotate(0), skewX(0),
1599- skewY(0) and matrix(1, 0, 0, 1, 0, 0).</ p >
1597+ < p > The identity functions are translate(0), translate3d(0, 0, 0),
1598+ translateX(0), translateY(0), translateZ(0), scale(1), scale3d(1, 1,
1599+ 1), scaleX(1), scaleY(1), scaleZ(1), rotate(0), rotate3d(1, 1, 1, 0),
1600+ rotateX(0), rotateY(0), rotateZ(0), skew(0), skewX(0), skewY(0),
1601+ matrix(1, 0, 0, 1, 0, 0) and matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
1602+ 0, 0, 0, 0, 1).</ p >
16001603 </ ul >
16011604
16021605 < li > If both the ‘< code class =property > from</ code > ’ and
@@ -1606,14 +1609,14 @@ <h2 id=animation><span class=secno>14. </span> Transitions and animations
16061609 < ul >
16071610 < li > Each transform function is animated with its corresponding
16081611 destination function in isolation using the rules described above. The
1609- individual values are then applied as a list to produce the resulting
1612+ individual values are then applied as a list to produce resulting
16101613 transform value.
16111614 </ ul >
16121615
16131616 < li > Otherwise:
16141617 < ul >
16151618 < li > The transform function lists are each converted into the equivalent
1616- matrix value and animation proceeds using the rule for a single
1619+ matrix3d value and animation proceeds using the rule for a single
16171620 function above.
16181621 </ ul >
16191622 </ ul >
@@ -1627,66 +1630,147 @@ <h2 id=matrix-decomposition><span class=secno>15. </span> Matrix
16271630 decomposition for animation</ h2 >
16281631
16291632 < p > When interpolating between 2 matrices, each is decomposed into the
1630- corresponding translation, rotation, scale and skew values. Not all
1631- matrices can be accurately described by these values. Those that can't are
1632- decomposed into the most accurate representation possible, using the
1633- technique below. This technique is taken from The "unmatrix" method in
1634- "Graphics Gems II, edited by Jim Arvo", simplified for the 2D case.
1633+ corresponding translation, rotation, scale, skew and perspective values.
1634+ Not all matrices can be accurately described by these values. Those that
1635+ can't are decomposed into the most accurate representation possible, using
1636+ the technique below. This technique is taken from the "unmatrix" method in
1637+ "Graphics Gems II, edited by Jim Arvo". The pseudocode below works on a
1638+ 4x4 homogeneous matrix. A 3x2 2D matrix is therefore first converted to
1639+ 4x4 homogeneous form.
16351640
16361641 < h3 id =unmatrix > < span class =secno > 15.1. </ span > Unmatrix</ h3 >
16371642
16381643 < pre >
1639- Input: a, b, c, d ; 2x2 matrix (rotate, scale, shear) components
1640- tx, ty ; translation components
1641- Output: translate ; a 2 component vector
1642- rotate ; an angle
1643- scale ; a 2 component vector
1644- skew ; skew factor
1644+ Input: matrix ; a 4x4 matrix
1645+ Output: translation ; a 3 component vector
1646+ rotation ; Euler angles, represented as a 3 component vector
1647+ scale ; a 3 component vector
1648+ skew ; skew factors XY,XZ,YZ represented as a 3 component vector
1649+ perspective ; a 4 component vector
16451650 Returns false if the matrix cannot be decomposed, true if it can
16461651
1647- Supporting functions (point is a 2 component vector):
1648- float length(point) returns the length of the passed vector
1652+ Supporting functions (point is a 3 component vector, matrix is a 4x4 matrix):
1653+ double determinant(matrix) returns the 4x4 determinant of the matrix
1654+ matrix inverse(matrix) returns the inverse of the passed matrix
1655+ matrix transpose(matrix) returns the transpose of the passed matrix
1656+ point multVecMatrix(point, matrix) multiplies the passed point by the passed matrix
1657+ and returns the transformed point
1658+ double length(point) returns the length of the passed vector
16491659 point normalize(point) normalizes the length of the passed point to 1
1650- float dot(point, point) returns the dot product of the passed points
1651- float atan2(float y, float x) returns the principal value of the arc tangent of
1660+ double dot(point, point) returns the dot product of the passed points
1661+ double cos(double) returns the cosine of the passed angle in radians
1662+ double asin(double) returns the arcsine in radians of the passed value
1663+ double atan2(double y, double x) returns the principal value of the arc tangent of
16521664 y/x, using the signs of both arguments to determine
16531665 the quadrant of the return value
16541666
16551667 Decomposition also makes use of the following function:
1656- point combine(point a, point b, float ascl, float bscl)
1668+ point combine(point a, point b, double ascl, double bscl)
16571669 result[0] = (ascl * a[0]) + (bscl * b[0])
16581670 result[1] = (ascl * a[1]) + (bscl * b[1])
1671+ result[2] = (ascl * a[2]) + (bscl * b[2])
16591672 return result
16601673
1661- // Make sure the matrix is invertible
1662- if ((a * d - b * c) == 0)
1663- return false
1664-
1665- // Take care of translation
1666- translate[0] = tx
1674+ // Normalize the matrix.
1675+ if (matrix[3][3] == 0)
1676+ return false
1677+
1678+ for (i = 0; i < 4 ; i++)
1679+ for (j = 0; j < 4; j++)
1680+ matrix[i][j] /= matrix[3][3]
1681+
1682+ // perspectiveMatrix is used to solve for perspective, but it also provides
1683+ // an easy way to test for singularity of the upper 3x3 component.
1684+ perspectiveMatrix = matrix
1685+
1686+ for (i = 0; i < 3 ; i++)
1687+ perspectiveMatrix[i][3] = 0
1688+
1689+ perspectiveMatrix[3][3] = 1
1690+
1691+ if (determinant(perspectiveMatrix) == 0)
1692+ return false
1693+
1694+ // First, isolate perspective.
1695+ if (matrix[0][3] != 0 || matrix[1][3] ! = 0 || matrix[2][3] ! = 0)
1696+ // rightHandSide is the right hand side of the equation.
1697+ rightHandSide[0] = matrix[0][3];
1698+ rightHandSide[1] = matrix[1][3];
1699+ rightHandSide[2] = matrix[2][3];
1700+ rightHandSide[3] = matrix[3][3];
1701+
1702+ // Solve the equation by inverting perspectiveMatrix and multiplying
1703+ // rightHandSide by the inverse.
1704+ inversePerspectiveMatrix = inverse(perspectiveMatrix)
1705+ transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix)
1706+ perspective = multVecMatrix(rightHandSide, transposedInversePerspectiveMatrix)
1707+
1708+ // Clear the perspective partition
1709+ matrix[0][3] = matrix[1][3] = matrix[2][3] = 0
1710+ matrix[3][3] = 1
1711+ else
1712+ // No perspective.
1713+ perspective[0] = perspective[1] = perspective[2] = 0
1714+ perspective[3] = 1
1715+
1716+ // Next take care of translation
1717+ translate[0] = matrix[3][0]
1718+ matrix[3][0] = 0
16671719 translate[1] = matrix[3][1]
1720+ matrix[3][1] = 0
1721+ translate[2] = matrix[3][2]
1722+ matrix[3][2] = 0
16681723
1669- // Put the components into a 2x2 matrix 'mat'
1670- mat[0][0] = a
1671- mat[0][1 ] = b
1672- mat[1][0 ] = c
1673- mat[1][1 ] = d
1724+ // Now get scale and shear. 'row' is a 3 element array of 3 component vectors
1725+ for (i = 0; i < 3; i++)
1726+ row[i][0 ] = matrix[i][0]
1727+ row[i][1 ] = matrix[i][1]
1728+ row[i][2 ] = matrix[i][2]
16741729
16751730 // Compute X scale factor and normalize first row.
16761731 scale[0] = length(row[0])
16771732 row[0] = normalize(row[0])
16781733
1679- // Compute shear factor and make 2nd row orthogonal to 1st.
1680- skew = dot(row[0], row[1])
1681- row[1] = combine(row[1], row[0], 1.0, -skew)
1734+ // Compute XY shear factor and make 2nd row orthogonal to 1st.
1735+ skew[0] = dot(row[0], row[1])
1736+ row[1] = combine(row[1], row[0], 1.0, -skew[0] )
16821737
16831738 // Now, compute Y scale and normalize 2nd row.
16841739 scale[1] = length(row[1])
16851740 row[1] = normalize(row[1])
1686- skew /= scale[1];
1687-
1688- // Now, get the rotation out
1689- rotate = atan2(mat[0][1], mat[0][0])
1741+ skew[0] /= scale[1];
1742+
1743+ // Compute XZ and YZ shears, orthogonalize 3rd row
1744+ skew[1] = dot(row[0], row[2])
1745+ row[2] = combine(row[2], row[0], 1.0, -skew[1])
1746+ skew[2] = dot(row[1], row[2])
1747+ row[2] = combine(row[2], row[1], 1.0, -skew[2])
1748+
1749+ // Next, get Z scale and normalize 3rd row.
1750+ scale[2] = length(row[2])
1751+ row[2] = normalize(row[2])
1752+ skew[1] /= scale[2]
1753+ skew[2] /= scale[2]
1754+
1755+ // At this point, the matrix (in rows) is orthonormal.
1756+ // Check for a coordinate system flip. If the determinant
1757+ // is -1, then negate the matrix and the scaling factors.
1758+ pdum3 = cross(row[1], row[2])
1759+ if (dot(row[0], pdum3) < 0)
1760+ for (i = 0; i < 3; i++) {
1761+ scale[0] *= -1;
1762+ row[i][0] *= -1
1763+ row[i][1] *= -1
1764+ row[i][2] *= -1
1765+
1766+ // Now, get the rotations ou
1767+ rotate[1] = asin(-row[0][2]);
1768+ if (cos(rotate[1]) != 0)
1769+ rotate[0] = atan2(row[1][2], row[2][2]);
1770+ rotate[2] = atan2(row[0][1], row[0][0]);
1771+ else
1772+ rotate[0] = atan2(-row[2][0], row[1][1]);
1773+ rotate[2] = 0;
16901774
16911775 return true;</ pre >
16921776
@@ -1705,21 +1789,20 @@ <h3 id=recomposing-the-matrix><span class=secno>15.3. </span>Recomposing
17051789 < p > < em > This section is not normative.</ em >
17061790
17071791 < p > After interpolation the resulting values are used to position the
1708- element. One way to use these values is to recompose them into a 3x2
1792+ element. One way to use these values is to recompose them into a 4x4
17091793 matrix. This can be done using the Transformation Functions of the < a
1710- href ="#effects "> < em > transform</ em > </ a > property. The following JavaScript
1711- example produces a string for this purpose . The values passed in are the
1712- output of the Unmatrix function above:
1794+ href ="#effects "> < em > transform</ em > </ a > property. This can be done by the
1795+ following pseudo code . The values passed in are the output of the Unmatrix
1796+ function above:
17131797
17141798 < pre >
1715- function compose(translate, rotate, scale, skew, elementID)
1716- {
1717- var s = " translate(" + translate[0] + ", " + translate[1] + ")";
1718- s += " rotate(" + rotate + ")";
1719- s += " skewX(" + skew + ")";
1720- s += " scale(" + scale[0] + ", " + scale[1] + ")";
1721- document.getElementById(elementID).style.transform = s;
1722- }</ pre >
1799+ matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, perspective[0], perspective[1], perspective[2], perspective[3])
1800+ translate3d(translation[0], translation[1], translation[2])
1801+ rotateX(rotation[0]) rotateY(rotation[1]) rotateZ(rotation[2])
1802+ matrix3d(1,0,0,0, 0,1,0,0, 0,skew[2],1,0, 0,0,0,1)
1803+ matrix3d(1,0,0,0, 0,1,0,0, skew[1],0,1,0, 0,0,0,1)
1804+ matrix3d(1,0,0,0, skew[0],1,0,0, 0,0,1,0, 0,0,0,1)
1805+ scale3d(scale[0], scale[1], scale[2])</ pre >
17231806
17241807 < h2 id =dom-interfaces > < span class =secno > 16. </ span > DOM Interfaces</ h2 >
17251808
0 commit comments