Define how exactly to apply perspective (w-parameter)

Mon, 28 May 2012 04:08:46 -0600

author
Aryeh Gregor <ayg@aryeh.name>
date
Mon, 28 May 2012 04:08:46 -0600
changeset 5901
4154be20d399
parent 5900
5831f7561bd1
child 5902
aa317e01c5ad

Define how exactly to apply perspective (w-parameter)

Fixes https://www.w3.org/Bugs/Public/show_bug.cgi?id=15605.

css3-transforms/ChangeLog file | annotate | diff | comparison | revisions
css3-transforms/Overview.html file | annotate | diff | comparison | revisions
css3-transforms/Transforms.src.html file | annotate | diff | comparison | revisions
     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> &gt; 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> &lt; 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> &lt; 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 +   &lt; 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> &gt; 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> &lt; 0 is "behind" the viewer, so should not be
    2.95 +   visible.
    2.96 +
    2.97 +  <div class=example>
    2.98 +   <pre>&lt;style&gt;
    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 +&lt;/style&gt;</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> &lt; 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>&lt;style&gt;
   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 +&lt;/style&gt;</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>&lt;style&gt;
   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 +&lt;/style&gt;</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 &lt; 0, so we need to slice away the part of the box with
   2.173 +    <var>w</var> &lt; 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> &lt; 0. However, in that case the result of truncating the
   2.189 +    <var>w</var> &lt; 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&times;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>&prime;, <var>y</var>&prime;,
    3.40 +              <var>z</var>&prime;) as follows:
    3.41 +              </p>
    3.42 +
    3.43 +              <p>
    3.44 +              If <var>w</var> &gt; 0, (<var>x</var>&prime;,
    3.45 +              <var>y</var>&prime;, <var>z</var>&prime;) =
    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>&prime;, <var>y</var>&prime;,
    3.52 +              <var>z</var>&prime;) = (<var>x</var> &sdot; <var>n</var>,
    3.53 +              <var>y</var> &sdot; <var>n</var>, <var>z</var> &sdot;
    3.54 +              <var>n</var>).  <var>n</var> is an implementation-dependent value
    3.55 +              that should be chosen so that <var>x</var>&prime; or
    3.56 +              <var>y</var>&prime; 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>&prime;,
    3.62 +              <var>y</var>&prime;, <var>z</var>&prime;) 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> &lt; 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> &lt; 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> &lt; 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> &gt; 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> &lt; 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>&lt;style&gt;
    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 +&lt;/style&gt;</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, &minus;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> &lt; 0
   3.105 +                case separately might incorrectly display this point as
   3.106 +                (&minus;<var>x</var>, &minus;<var>y</var>, &minus;100),
   3.107 +                dividing by &minus;1 and mirroring the box.
   3.108 +                </p>
   3.109 +              </div>
   3.110 +
   3.111 +              <div class="example">
   3.112 +<pre>&lt;style&gt;
   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 +&lt;/style&gt;</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 (&minus;50, &minus;50), so it becomes (&minus;50,
   3.137 +                &minus;50, 50, 0).  This is transformed to something
   3.138 +                very far to the upper left, such as (&minus;5000, &minus;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>&lt;style&gt;
   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 +&lt;/style&gt;</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, &minus;50), relative to the transform-origin.  It is first
   3.175 +                expanded to (100, &minus;50, 0, 1).  After applying the
   3.176 +                transform specified, this will get mapped to about (70.71,
   3.177 +                &minus;50, 70.71, &minus;0.4142).  This has <var>w</var> =
   3.178 +                &minus;0.4142 &lt; 0, so we need to slice away the part of the
   3.179 +                box with <var>w</var> &lt; 0.  This results in the new
   3.180 +                top-right vertex being (50, &minus;50, 50, 0).  This is then
   3.181 +                mapped to some faraway point in the same direction, such as
   3.182 +                (5000, &minus;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> &lt; 0.  However, in that case the result of
   3.200 +                truncating the <var>w</var> &lt; 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">

mercurial