11

I would like to draw something like the following diagram:

enter image description here

My problem is drawing the grey curved path that fades away. The direction of the fading continually changes so that it is always perpendicular to the path. How can I draw a curved fade like this in tikz?

11

I looked at some similar answers, but since my knowledge of the inner workings of TikZ is not great, I went for the naive answer - draw several lines, of increasing thickness and increasing transparency.

Well, that's the idea, the code actually draws lines of decreasing thickness and of decreasing transparency. And since that produces a line with a gradient to both edges, half of it has to be hidden. I'm not completely satisfied with this solution, but I can't come up with anything better. Here's the code:

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{fp}

\begin{document}
\begin{tikzpicture}
% Set parameters here
\newcommand\linethick{6}      % How thick, in pt, the curve will be
\newcommand\fineness{20}      % How many shades between grey and white to draw
\coordinate (start) at (0,0); % Where the curve will start
\coordinate (end) at (3,0);   % Where the curve will end
\newcommand\outangle{300}     % Angle at which the curve leaves "start"
\newcommand\outstrength{1.5}  % Strength at which the curve leaves "start"
\newcommand\inangle{210}      % Angle at which the curve arrives at "end"
\newcommand\instrength{1}     % Strength at which the curve arrives at "end"
\newcommand\safeguard{3}      % A number > 0, to be increased, in case unwanted half of curve isn't hidden
% Loop of lines
\foreach \x in {0,...,\fineness}{
  \FPeval\shader{\x*100/\fineness}
  \FPeval\thicker{\linethick*2-\x*\linethick*2/\fineness}
  \draw[line width=\thicker,draw=gray!\shader] (start) .. controls +(\outangle:\outstrength) and +(\inangle:\instrength) .. (end);
}
% Hide other side of curve
\FPeval\newlinethick{\linethick+\safeguard}
\draw[fill=white,draw=white,line width=.3pt] (start) .. controls +(\outangle:\outstrength) and +(\inangle:\instrength) .. (end)--++(\inangle-90:\newlinethick pt) .. controls +(\inangle:\instrength) and +(\outangle:\outstrength) .. ($(start)+(\outangle+90:\newlinethick pt)$)--(start)--cycle;
\end{tikzpicture}
\end{document}

The result:

enter image description here

This approach only works for a curve between two points. I hope the descriptions of the input parameters are clear.

Note the last parameter, \safeguard, that deals with the fact that when a curve is very thick, the strength at which it leaves on the outside of the curve is not the same as the strength at which it leaves from the middle of the curve. So increasing this parameter makes the white shape that hides the other half thicker on the side away from the middle of the curve.

Also, note that the hiding shape has a non zero line width, it's not just \fill, and that's because using \fill produces slivers of lines on both ends of the curve, and to hide them I had to slightly blow up the shape. This also takes away a little bit from the darker side of the gradient of the curve, but it's not noticeable.

7

I believe there is a way to generate a curved gradient of high quality. This kind of shading is currently unavailable to tikz/pgf, but the library pgfplots which is built on top of it can generate them.

enter image description here

\documentclass[tikz]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.9}
\usepgfplotslibrary{patchplots}
\begin{document}
\begin{tikzpicture}
  \begin{axis}[
    hide axis,
    x=1pt,y=1pt]

  \addplot[patch,
    patch type=biquadratic,
    shader=interp,
    colormap={bw}{gray=(1) gray=(0.7)},
    nodes near coords=\coordindex,
    point meta=explicit,
    ]
  coordinates 
  {
    (3,-36)    [0]   % 0: lower left corner
    (626,-41)  [0]  % 1: lower right corner
    (604,-10) [1]   % 2: upper right corner
    (40,-17) [1]  % 3: upper left corner
    %
    (236,-180) [0] % 4: lower middle
    (617,-25) [0] % 5: right middle
    (240,-140) [0] % 6: upper middle
    (23,-27) [0] % 7: left middle
    %
    (238,-160) [0] % 8: middle
  };
  \end{axis}

\end{tikzpicture}
\end{document}

Here, we have a hidden axis of pgfplots which contains a shaded surface plot. This, in turn, makes use of patch type=biquadratic which is one of many supported curved gradients. A biquadratic patch is defined by 9 points in a particular ordering: first coordinates of vertexes, then coordinates of mid points, then coordinate of center as illustrated by the nodes near coords instruction. The color values are provided explicitly in square brackets after each coordinate (this is the effect of point meta=explicit).

Note that I chose to use a colormap here, that means the color values are actually scalar values which are mapped into the colormap={bw}{gray=(1) gray=(0.7)}. This mapping strategy easily allows to customize the gradient. For example, the default colormap configured by pgfplots yields the following output (i.e. if you uncomment colormap=...):

enter image description here

The downside of my approach is that pgfplots is meant to visualize data, so it involves more work to integrate such a shading seamlessly into a tikzpicture (there is a section about TikZ Interoperability in the pgfplots manual).

Note that the coordinates above came from an image program: I made a screenshot of your picture and used gimp to read the pixel coordinates.

The approach to use patch plots with shader=interp allows to define shaded gradients with smooth but complicated geometries easily. In addition, multiple of these shadings can be placed next to each other (by providing another set of 9 coordinates). There are also shadings which accept bezier coordinates, triangular patches, or bicubic ones, compare the documentation of "Patch plots" in pgfplots.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.