Mon, 28 May 2012 04:08:46 -0600
Define how exactly to apply perspective (w-parameter)
Fixes https://www.w3.org/Bugs/Public/show_bug.cgi?id=15605.
1.1 --- a/css3-transforms/ChangeLog Fri May 25 21:36:12 2012 -0700 1.2 +++ b/css3-transforms/ChangeLog Mon May 28 04:08:46 2012 -0600 1.3 @@ -1,3 +1,7 @@ 1.4 +2012-05-28 ayg@aryeh.name 1.5 + Define how exactly to apply perspective (w-parameter) 1.6 + Fixes https://www.w3.org/Bugs/Public/show_bug.cgi?id=15605 1.7 + 1.8 2012-05-26 dschulze@adobe.com 1.9 Define neutral element for addition on transforms. 1.10
2.1 --- a/css3-transforms/Overview.html Fri May 25 21:36:12 2012 -0700 2.2 +++ b/css3-transforms/Overview.html Mon May 28 04:08:46 2012 -0600 2.3 @@ -39,14 +39,14 @@ 2.4 2.5 <h1>CSS Transforms</h1> 2.6 2.7 - <h2 class="no-num no-toc" id=longstatus-date>Editor's Draft 26 May 2012</h2> 2.8 + <h2 class="no-num no-toc" id=longstatus-date>Editor's Draft 28 May 2012</h2> 2.9 2.10 <dl> 2.11 <dt>This version: 2.12 2.13 <dd> <a 2.14 - href="http://www.w3.org/TR/2012/ED-css3-transforms-20120526/">http://dev.w3.org/csswg/css3-transforms/</a> 2.15 - <!--http://www.w3.org/TR/2012/WD-css3-transforms-20120526/--> 2.16 + href="http://www.w3.org/TR/2012/ED-css3-transforms-20120528/">http://dev.w3.org/csswg/css3-transforms/</a> 2.17 + <!--http://www.w3.org/TR/2012/WD-css3-transforms-20120528/--> 2.18 2.19 <dt>Latest version: 2.20 2.21 @@ -199,6 +199,10 @@ 2.22 <ul class=toc> 2.23 <li><a href="#transform-3d-rendering"><span class=secno>6.1. </span>3D 2.24 Transform Rendering</a> 2.25 + 2.26 + <li><a href="#processing-of-perspective-transformed-boxes"><span 2.27 + class=secno>6.2. </span> Processing of Perspective-Transformed Boxes 2.28 + </a> 2.29 </ul> 2.30 2.31 <li><a href="#transform-property"><span class=secno>7. </span> The 2.32 @@ -1117,7 +1121,7 @@ 2.33 </div> 2.34 2.35 <p> Using three-dimensional transforms, it's possible to transform an 2.36 - element such that its reverse side is towards the viewer. 3D-tranformed 2.37 + element such that its reverse side is towards the viewer. 3D-transformed 2.38 elements show the same content on both sides, so the reverse side looks 2.39 like a mirror-image of the front side (as if the element were projected 2.40 onto a sheet of glass). Normally, elements whose reverse side is towards 2.41 @@ -1129,7 +1133,152 @@ 2.42 class=css><code class=css>backface-visibility: hidden</code></code>’ 2.43 were animating, such that its front and reverse sides were alternately 2.44 visible, then it would only be visible when the front side were towards 2.45 - the viewer.</p> 2.46 + the viewer. 2.47 + 2.48 + <h3 id=processing-of-perspective-transformed-boxes><span class=secno>6.2. 2.49 + </span> Processing of Perspective-Transformed Boxes</h3> 2.50 + 2.51 + <div class=issue> 2.52 + <p class=desc> This is a first pass at an attempt to precisely specify how 2.53 + exactly to transform elements using the provided matrices. It might not 2.54 + be ideal, and implementer feedback is encouraged. See <a 2.55 + href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=15605">bug 2.56 + 15605</a>.</p> 2.57 + </div> 2.58 + 2.59 + <p> The <a href="#TermAccumulated3DTransformationMatrix">accumulated 3D 2.60 + transformation matrix</a> is a 4×4 matrix, while the objects to be 2.61 + transformed are two-dimensional boxes. To transform each corner 2.62 + (<var>a</var>, <var>b</var>) of a box, the matrix must first be applied to 2.63 + (<var>a</var>, <var>b</var>, 0, 1), which will result in a 2.64 + four-dimensional point (<var>x</var>, <var>y</var>, <var>z</var>, 2.65 + <var>w</var>). This is transformed back to a three-dimensional point 2.66 + (<var>x</var>′, <var>y</var>′, <var>z</var>′) as follows: 2.67 + 2.68 + <p> If <var>w</var> > 0, (<var>x</var>′, <var>y</var>′, 2.69 + <var>z</var>′) = (<var>x</var>/<var>w</var>, <var>y</var>/<var>w</var>, 2.70 + <var>z</var>/<var>w</var>). 2.71 + 2.72 + <p> If <var>w</var> = 0, (<var>x</var>′, <var>y</var>′, 2.73 + <var>z</var>′) = (<var>x</var> ⋅ <var>n</var>, <var>y</var> ⋅ 2.74 + <var>n</var>, <var>z</var> ⋅ <var>n</var>). <var>n</var> is an 2.75 + implementation-dependent value that should be chosen so that 2.76 + <var>x</var>′ or <var>y</var>′ is much larger than the viewport size, 2.77 + if possible. For example, (5px, 22px, 0px, 0) might become (5000px, 2.78 + 22000px, 0px), with <var>n</var> = 1000, but this value of <var>n</var> 2.79 + would be too small for (0.1px, 0.05px, 0px, 0). This specification does 2.80 + not define the value of <var>n</var> exactly. Conceptually, 2.81 + (<var>x</var>′, <var>y</var>′, <var>z</var>′) is <a 2.82 + href="http://en.wikipedia.org/wiki/Plane_at_infinity">infinitely far</a> 2.83 + in the direction (<var>x</var>, <var>y</var>, <var>z</var>). 2.84 + 2.85 + <p> If <var>w</var> < 0 for all four corners of the transformed box, the 2.86 + box is not rendered. 2.87 + 2.88 + <p> If <var>w</var> < 0 for one to three corners of the transformed box, 2.89 + the box must be replaced by a polygon that has any parts with <var>w</var> 2.90 + < 0 cut out. This will in general be a polygon with three to five 2.91 + vertices, of which exactly two will have <var>w</var> = 0 and the rest 2.92 + <var>w</var> > 0. These vertices are then transformed to 2.93 + three-dimensional points using the rules just stated. Conceptually, a 2.94 + point with <var>w</var> < 0 is "behind" the viewer, so should not be 2.95 + visible. 2.96 + 2.97 + <div class=example> 2.98 + <pre><style> 2.99 +.transformed { 2.100 + height: 100px; 2.101 + width: 100px; 2.102 + background: lime; 2.103 + transform: perspective(50px) translateZ(100px); 2.104 +} 2.105 +</style></pre> 2.106 + 2.107 + <p> All of the box's corners have <var>z</var>-coordinates greater than 2.108 + the perspective. This means that the box is behind the viewer and will 2.109 + not display. Mathematically, the point (<var>x</var>, <var>y</var>) first 2.110 + becomes (<var>x</var>, <var>y</var>, 0, 1), then is translated to 2.111 + (<var>x</var>, <var>y</var>, 100, 1), and then applying the perspective 2.112 + results in (<var>x</var>, <var>y</var>, 100, −1). The 2.113 + <var>w</var>-coordinate is negative, so it does not display. An 2.114 + implementation that doesn't handle the <var>w</var> < 0 case 2.115 + separately might incorrectly display this point as (−<var>x</var>, 2.116 + −<var>y</var>, −100), dividing by −1 and mirroring the box.</p> 2.117 + </div> 2.118 + 2.119 + <div class=example> 2.120 + <pre><style> 2.121 +.transformed { 2.122 + height: 100px; 2.123 + width: 100px; 2.124 + background: radial-gradient(yellow, blue); 2.125 + transform: perspective(50px) translateZ(50px); 2.126 +} 2.127 +</style></pre> 2.128 + 2.129 + <p> Here, the box is translated upward so that it sits at the same place 2.130 + the viewer is looking from. This is like bringing the box closer and 2.131 + closer to one's eye until it fills the entire field of vision. Since the 2.132 + default transform-origin is at the center of the box, which is yellow, 2.133 + the screen will be filled with yellow.</p> 2.134 + 2.135 + <p> Mathematically, the point (<var>x</var>, <var>y</var>) first becomes 2.136 + (<var>x</var>, <var>y</var>, 0, 1), then is translated to (<var>x</var>, 2.137 + <var>y</var>, 50, 1), then becomes (<var>x</var>, <var>y</var>, 50, 0) 2.138 + after applying perspective. Relative to the transform-origin at the 2.139 + center, the upper-left corner was (−50, −50), so it becomes (−50, 2.140 + −50, 50, 0). This is transformed to something very far to the upper 2.141 + left, such as (−5000, −5000, 5000). Likewise the other corners are 2.142 + sent very far away. The radial gradient is stretched over the whole box, 2.143 + now enormous, so the part that's visible without scrolling should be the 2.144 + color of the middle pixel: yellow. However, since the box is not actually 2.145 + infinite, the user can still scroll to the edges to see the blue parts.</p> 2.146 + <!-- TODO: Maybe we should specify that the whole thing is 2.147 + yellow here somehow? Doesn't seem worth it. --> 2.148 + </div> 2.149 + 2.150 + <div class=example> 2.151 + <pre><style> 2.152 +.transformed { 2.153 + height: 50px; 2.154 + width: 50px; 2.155 + background: lime; 2.156 + border: 25px solid blue; 2.157 + transform-origin: left; 2.158 + transform: perspective(50px) rotateY(-45deg); 2.159 +} 2.160 +</style></pre> 2.161 + 2.162 + <p> The box will be rotated toward the viewer, with the left edge staying 2.163 + fixed while the right edge swings closer. The right edge will be at about 2.164 + <var>z</var> = 70.7px, which is closer than the perspective of 50px. 2.165 + Therefore, the rightmost edge will vanish ("behind" the viewer), and the 2.166 + visible part will stretch out infinitely far to the right.</p> 2.167 + 2.168 + <p> Mathematically, the top right vertex of the box was originally (100, 2.169 + −50), relative to the transform-origin. It is first expanded to (100, 2.170 + −50, 0, 1). After applying the transform specified, this will get 2.171 + mapped to about (70.71, −50, 70.71, −0.4142). This has <var>w</var> = 2.172 + −0.4142 < 0, so we need to slice away the part of the box with 2.173 + <var>w</var> < 0. This results in the new top-right vertex being (50, 2.174 + −50, 50, 0). This is then mapped to some faraway point in the same 2.175 + direction, such as (5000, −5000, 5000), which is up and to the right 2.176 + from the transform-origin. Something similar is done to the lower right 2.177 + corner, which gets mapped far down and to the right. The resulting box 2.178 + stretches far past the edge of the screen.</p> 2.179 + 2.180 + <p> Again, the rendered box is still finite, so the user can scroll to see 2.181 + the whole thing if he or she chooses. However, the right part has been 2.182 + chopped off. No matter how far the user scrolls, the rightmost 30px or so 2.183 + of the original box will not be visible. The blue border was only 25px 2.184 + wide, so it will be visible on the left, top, and bottom, but not the 2.185 + right.</p> 2.186 + 2.187 + <p> The same basic procedure would apply if one or three vertices had 2.188 + <var>w</var> < 0. However, in that case the result of truncating the 2.189 + <var>w</var> < 0 part would be a triangle or pentagon instead of a 2.190 + quadrilateral.</p> 2.191 + </div> 2.192 <!-- ======================================================================================================= --> 2.193 2.194 <h2 id=transform-property><span class=secno>7. </span> The ‘<a
3.1 --- a/css3-transforms/Transforms.src.html Fri May 25 21:36:12 2012 -0700 3.2 +++ b/css3-transforms/Transforms.src.html Mon May 28 04:08:46 2012 -0600 3.3 @@ -785,7 +785,7 @@ 3.4 3.5 <p> 3.6 Using three-dimensional transforms, it's possible to transform an element such that its reverse side 3.7 - is towards the viewer. 3D-tranformed elements show the same content on both sides, so the reverse side 3.8 + is towards the viewer. 3D-transformed elements show the same content on both sides, so the reverse side 3.9 looks like a mirror-image of the front side (as if the element were projected onto a sheet of glass). 3.10 Normally, elements whose reverse side is towards the viewer remain visible. However, the 3.11 '<code class="property">backface-visibility</code>' property allows the author to make an element invisible 3.12 @@ -795,6 +795,195 @@ 3.13 front side were towards the viewer. 3.14 </p> 3.15 3.16 + <h3 id="processing-of-perspective-transformed-boxes"> 3.17 + Processing of Perspective-Transformed Boxes 3.18 + </h3> 3.19 + 3.20 + <div class="issue"> 3.21 + <p class="desc"> 3.22 + This is a first pass at an attempt to precisely specify how 3.23 + exactly to transform elements using the provided matrices. 3.24 + It might not be ideal, and implementer feedback is 3.25 + encouraged. See <a 3.26 + href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=15605">bug 3.27 + 15605</a>. 3.28 + </p> 3.29 + </div> 3.30 + 3.31 + <p> 3.32 + The <a href="#TermAccumulated3DTransformationMatrix">accumulated 3.33 + 3D transformation matrix</a> is a 4×4 matrix, while the 3.34 + objects to be transformed are two-dimensional boxes. To 3.35 + transform each corner (<var>a</var>, <var>b</var>) of a box, the 3.36 + matrix must first be applied to (<var>a</var>, <var>b</var>, 0, 3.37 + 1), which will result in a four-dimensional point (<var>x</var>, 3.38 + <var>y</var>, <var>z</var>, <var>w</var>). This is transformed 3.39 + back to a three-dimensional point (<var>x</var>′, <var>y</var>′, 3.40 + <var>z</var>′) as follows: 3.41 + </p> 3.42 + 3.43 + <p> 3.44 + If <var>w</var> > 0, (<var>x</var>′, 3.45 + <var>y</var>′, <var>z</var>′) = 3.46 + (<var>x</var>/<var>w</var>, <var>y</var>/<var>w</var>, 3.47 + <var>z</var>/<var>w</var>). 3.48 + </p> 3.49 + 3.50 + <p> 3.51 + If <var>w</var> = 0, (<var>x</var>′, <var>y</var>′, 3.52 + <var>z</var>′) = (<var>x</var> ⋅ <var>n</var>, 3.53 + <var>y</var> ⋅ <var>n</var>, <var>z</var> ⋅ 3.54 + <var>n</var>). <var>n</var> is an implementation-dependent value 3.55 + that should be chosen so that <var>x</var>′ or 3.56 + <var>y</var>′ is much larger than the viewport size, if 3.57 + possible. For example, (5px, 22px, 0px, 0) might become (5000px, 3.58 + 22000px, 0px), with <var>n</var> = 1000, but this value of 3.59 + <var>n</var> would be too small for (0.1px, 0.05px, 0px, 0). 3.60 + This specification does not define the value of <var>n</var> 3.61 + exactly. Conceptually, (<var>x</var>′, 3.62 + <var>y</var>′, <var>z</var>′) is <a 3.63 + href="http://en.wikipedia.org/wiki/Plane_at_infinity">infinitely 3.64 + far</a> in the direction (<var>x</var>, <var>y</var>, 3.65 + <var>z</var>). 3.66 + </p> 3.67 + 3.68 + <p> 3.69 + If <var>w</var> < 0 for all four corners of the transformed 3.70 + box, the box is not rendered. 3.71 + </p> 3.72 + 3.73 + <p> 3.74 + If <var>w</var> < 0 for one to three corners of the 3.75 + transformed box, the box must be replaced by a polygon that has 3.76 + any parts with <var>w</var> < 0 cut out. This will in general 3.77 + be a polygon with three to five vertices, of which exactly two 3.78 + will have <var>w</var> = 0 and the rest <var>w</var> > 0. 3.79 + These vertices are then transformed to three-dimensional points 3.80 + using the rules just stated. Conceptually, a point with 3.81 + <var>w</var> < 0 is "behind" the viewer, so should not be 3.82 + visible. 3.83 + </p> 3.84 + 3.85 + <div class="example"> 3.86 +<pre><style> 3.87 +.transformed { 3.88 + height: 100px; 3.89 + width: 100px; 3.90 + background: lime; 3.91 + transform: perspective(50px) translateZ(100px); 3.92 +} 3.93 +</style></pre> 3.94 + 3.95 + <p> 3.96 + All of the box's corners have <var>z</var>-coordinates greater 3.97 + than the perspective. This means that the box is behind the 3.98 + viewer and will not display. Mathematically, the point 3.99 + (<var>x</var>, <var>y</var>) first becomes (<var>x</var>, 3.100 + <var>y</var>, 0, 1), then is translated to (<var>x</var>, 3.101 + <var>y</var>, 100, 1), and then applying the perspective 3.102 + results in (<var>x</var>, <var>y</var>, 100, −1). The 3.103 + <var>w</var>-coordinate is negative, so it does not display. 3.104 + An implementation that doesn't handle the <var>w</var> < 0 3.105 + case separately might incorrectly display this point as 3.106 + (−<var>x</var>, −<var>y</var>, −100), 3.107 + dividing by −1 and mirroring the box. 3.108 + </p> 3.109 + </div> 3.110 + 3.111 + <div class="example"> 3.112 +<pre><style> 3.113 +.transformed { 3.114 + height: 100px; 3.115 + width: 100px; 3.116 + background: radial-gradient(yellow, blue); 3.117 + transform: perspective(50px) translateZ(50px); 3.118 +} 3.119 +</style></pre> 3.120 + 3.121 + <p> 3.122 + Here, the box is translated upward so that it sits at the same 3.123 + place the viewer is looking from. This is like bringing the 3.124 + box closer and closer to one's eye until it fills the entire 3.125 + field of vision. Since the default transform-origin is at the 3.126 + center of the box, which is yellow, the screen will be filled 3.127 + with yellow. 3.128 + </p> 3.129 + 3.130 + <p> 3.131 + Mathematically, the point (<var>x</var>, <var>y</var>) first 3.132 + becomes (<var>x</var>, <var>y</var>, 0, 1), then is translated 3.133 + to (<var>x</var>, <var>y</var>, 50, 1), then becomes 3.134 + (<var>x</var>, <var>y</var>, 50, 0) after applying perspective. 3.135 + Relative to the transform-origin at the center, the upper-left 3.136 + corner was (−50, −50), so it becomes (−50, 3.137 + −50, 50, 0). This is transformed to something 3.138 + very far to the upper left, such as (−5000, −5000, 3.139 + 5000). Likewise the other corners are sent very far away. The 3.140 + radial gradient is stretched over the whole box, now enormous, 3.141 + so the part that's visible without scrolling should be the 3.142 + color of the middle pixel: yellow. However, since the box is 3.143 + not actually infinite, the user can still scroll to the edges 3.144 + to see the blue parts. 3.145 + </p> 3.146 + 3.147 + <!-- TODO: Maybe we should specify that the whole thing is 3.148 + yellow here somehow? Doesn't seem worth it. --> 3.149 + </div> 3.150 + 3.151 + <div class="example"> 3.152 +<pre><style> 3.153 +.transformed { 3.154 + height: 50px; 3.155 + width: 50px; 3.156 + background: lime; 3.157 + border: 25px solid blue; 3.158 + transform-origin: left; 3.159 + transform: perspective(50px) rotateY(-45deg); 3.160 +} 3.161 +</style></pre> 3.162 + 3.163 + <p> 3.164 + The box will be rotated toward the viewer, with the left edge 3.165 + staying fixed while the right edge swings closer. The right 3.166 + edge will be at about <var>z</var> = 70.7px, which is closer 3.167 + than the perspective of 50px. Therefore, the rightmost edge 3.168 + will vanish ("behind" the viewer), and the visible part will 3.169 + stretch out infinitely far to the right. 3.170 + </p> 3.171 + 3.172 + <p> 3.173 + Mathematically, the top right vertex of the box was originally 3.174 + (100, −50), relative to the transform-origin. It is first 3.175 + expanded to (100, −50, 0, 1). After applying the 3.176 + transform specified, this will get mapped to about (70.71, 3.177 + −50, 70.71, −0.4142). This has <var>w</var> = 3.178 + −0.4142 < 0, so we need to slice away the part of the 3.179 + box with <var>w</var> < 0. This results in the new 3.180 + top-right vertex being (50, −50, 50, 0). This is then 3.181 + mapped to some faraway point in the same direction, such as 3.182 + (5000, −5000, 5000), which is up and to the right from 3.183 + the transform-origin. Something similar is done to the lower 3.184 + right corner, which gets mapped far down and to the right. The 3.185 + resulting box stretches far past the edge of the screen. 3.186 + </p> 3.187 + 3.188 + <p> 3.189 + Again, the rendered box is still finite, so the user can scroll 3.190 + to see the whole thing if he or she chooses. However, the 3.191 + right part has been chopped off. No matter how far the user 3.192 + scrolls, the rightmost 30px or so of the original box will not 3.193 + be visible. The blue border was only 25px wide, so it will be 3.194 + visible on the left, top, and bottom, but not the right. 3.195 + </p> 3.196 + 3.197 + <p> 3.198 + The same basic procedure would apply if one or three vertices 3.199 + had <var>w</var> < 0. However, in that case the result of 3.200 + truncating the <var>w</var> < 0 part would be a triangle or 3.201 + pentagon instead of a quadrilateral. 3.202 + </p> 3.203 + </div> 3.204 + 3.205 <!-- ======================================================================================================= --> 3.206 3.207 <h2 id="transform-property">