[Tex/LaTex] How is a PGF shading applied

shadingtikz-pgf

Consider the following LaTeX manuscript featuring a TikZ picture of a tall, rainbow shaded rectangle. The code is essentially copy-and-pasted from the example in the end of subsection 109.3 ('Using Shadings') of chapter 109 ('Shadings') of the TikZ & PGF manual for version 3.0.1a, p. 1088.

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
    \pgfdeclareverticalshading{rainbow}{100bp}{color(0bp)=(red); color(25bp)=(red); color(35bp)=(yellow); color(45bp)=(green); color(55bp)=(cyan); color(65bp)=(blue); color(75bp)=(violet); color(100bp)=(violet)}
    \begin{tikzpicture}[shading=rainbow]
        \shade[shading angle=90] (3,0) rectangle +(1,2);
    \end{tikzpicture}
\end{document}

The resulting image is (not to scale)

             
             
             
        A rainbow shaded rectangle

The shading of this rectangle ranges from violet on the left to red on the right. I don't understand why this is so. In my opinion it should range from blue to yellow. I base this opinion on the shading algorithm given in the description of the \pgfshadepath{〈shading name〉}{〈angle〉} command on pp. 1085-1086:

First, PGF will set up a local scope.

Second, it uses the current path to clip everything inside this scope. However, the current path is once more available after the scope, so it can be used, for example, to stroke it.

Now, the 〈shading name〉 should be a shading whose width and height are 100 bp, that is, 100 big points. PGF has a look at the bounding box of the current path. This bounding box is computed automatically when a path is computed; however, it can sometimes be (quite a bit) too large, especially when complicated curves are involved.

Inside the scope, the low-level transformation matrix is modified. The center of the shading is translated (moved) such that it lies on the center of the bounding box of the path. The low-level coordinate system is also scaled such that the shading “covers” the path (the details are a bit more complex, see below). Then, the coordinate system is rotated by 〈angle〉.

Finally, if the macro \pgfsetadditionalshadetransform has been used, an additional transformation is applied.

After everything has been set up, the shading is inserted.

If both the path and the shadings were always rectangles and if rotations were never involved, it would be easy to scale shadings such they always cover the path. However, when a vertical shading is rotated, it must obviously be “magnified” so that it still covers the path. Things get worse when the path is not a rectangle itself.

For these reasons, things work slightly differently “in reality.” The shading is scaled and translated such that the point (50bp,50bp), which is the middle of the shading, is at the middle of the path and such that the point (25bp,25bp) is at the lower left corner of the path and that
(75bp,75bp) is at upper right corner.

To reiterate, the essential steps are:

  1. Create a shaded square of size 100bpx100pb based on the \pgfdeclareverticalshading specifications. I shall refer to this square as the background square.
  2. Shift the background square so that its center coincide with the center of the bounding box of the shape-to-be-shaded.
  3. Scale the shifted background square, so that its sub-square, whose bottom-left corner is at (25pb,25pb) and whose upper-right corner is at (75bp,75bp) (coordinates before the shift), coincides with the bounding box of the shape-to-be-shaded.
  4. Rotate the shifted and scaled background square by the specified angle.
  5. Clip the resulting background square as per the shape-to-be-shaded.

In particular, the rotation of the background square (step 4) is performed after it has been shifted and scaled (step 3).

Let's apply this to our example. Just before being rotated, the background square is transformed in such a way that the color that runs along the top of the shape-to-be-shaded is violet, and the color that runs along the bottom of the shape-to-be-shaded is red. Now the 90 degree rotation takes place, and, since the shape-to-be-shaded is a tall, thin rectangle, the violet and red colors of the rotated background square would fall outside the shape-to-be-shaded. Hence, after the clipping only the blue to yellow spectrum should remain visible.

If the rotation were the first transformation to be carried out, i.e. if steps 2, 3 & 4 of the algorithm were permuted as follows: 2->3, 3->4, 4->2, then the image obtained above would make sense to me.

Best Answer

Shades the current path, but does not discard it.

#1 - a shading (see below)

#2 - an angle

Description:

\pgfshadepath "tries" to fill the current path with a shading. The shading's original size should completely cover the area between (0,0) and (100bp,100bp). The shading will be rotated by #2 and then rescaled so that it completely covers the path. Then the path will be used (locally) for clipping and the shading is drawn.

In addition to the rotation, any transformation set by the \pgfsetadditionalshadetransform will also be applied.

After all this, the path can still be used for the normal stroking/clipping operations.

The shading is rotated around its middle. If no rotation occurs, the lower left corner of the path will lie on (25bp, 25bp), the upper right corner on (75bp, 75bp).

Example:

\pgfdeclareverticalshading{myshading}{100bp}{color(0pt)=(red); color(100bp)=(green)}

\pgfpathmoveto{\pgforigin}
\pgfpathlineto{\pgfxy(1,0)}
\pgfpathlineto{\pgfxy(1,1)}
\pgfshadepath{myshading}{0}
\pgfusepath{stroke}

--- Excerpt from tex/generic/pgf/basiclayer/pgfcoreshade.code.tex.