Skip to content

Commit 7eee646

Browse files
committed
2012-02-10 dschulze@adobe.com
Backported 3D decomposing code from FX Transforms. Backported "Transitions and animations between transform values" from CSS 3D Transforms. Added 'skew' to the list of transformation functions for transitions and animations. Replaced bogus link to C file by an internal link to decomposing code.
1 parent e5a482d commit 7eee646

3 files changed

Lines changed: 288 additions & 116 deletions

File tree

css3-transforms/ChangeLog

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
2012-02-10 dschulze@adobe.com
2+
Backported 3D decomposing code from FX Transforms.
3+
Backported "Transitions and animations between transform values" from CSS 3D Transforms.
4+
Added 'skew' to the list of transformation functions for transitions and animations.
5+
Replaced bogus link to C file by an internal link to decomposing code.
6+
17
2012-02-09 dschulze@adobe.com
28
Introduce new term 'bounding box'.
39
Specify transform origin for SVG elements.
410
Change float to double for CSSOM.
511
Added two optional offset arguments for rotate().
612
Changed Z argument type to <length> in translateZ() and translate3d().
13+
Removed link from translateZ() to SVG (was 2D translate).
714
Removed solved issues.
815

916
2012-02-07 simon.fraser@apple.com

css3-transforms/Overview.html

Lines changed: 143 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,21 +1553,21 @@ <h2 id=animation><span class=secno>14. </span> Transitions and animations
15531553
&lsquo;<code class=property>to</code>&rsquo; 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 &lsquo;<code
15951595
class=property>from</code>&rsquo; 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 &lsquo;<code class=property>from</code>&rsquo; 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

Comments
 (0)