10

Any suggestions on how to draw something like this picture using TikZ?

Thanks!

enter image description here

17

Here is a slight extension of Milo's nice answer. The main differences (improvements?) are:

  1. I rewrote Alain Matthes' nice answer to allow for arbitrary functions determining the line width. Previously it was a linearly dependence, now it can be an arbitrary function such as a sine. All you need to do is to say declare function={varyinglw(\x)=1+6*sin(1.8*\x);}. Apart from that, I made the code a bit more general, increased its speed (I think) and got rid of \makeatletter since there was nothing that cannot be achieved with commands not containing @s.
  2. This variation also does not nest tikzpictures. Rather, the arrows a re attached in the usual way, and can be even bent.

Here's how this works

  • You need to find a function that determines how thick the line is at a given position of the path. This function has the name varyinglw. It's argument runs from 0 to 100. So if the function has a maximum at 50, the path will have reached its maximal width in the middle. An example for a function with this feature is declare function={varyinglw(\x)=1+6*sin(1.8*\x);} below. If you want to use different functions, use scopes. Unfortunately I do not now how one can reset functions that are declared with declare functions. Therefore you should keep your functions local.
  • If you have a very long path, you may want to increase the number of segments since otherwise it won't look smooth any more. This can be done by saying e.g. /pgf/decoration/varying line width steps=180 as in the examples below.
  • Other than that you can use pretty much any path.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{decorations,arrows.meta,bending}
\begin{document}

\pgfkeys{/pgf/decoration/.cd,
         start color/.store in=\startcolor,
         start color=black,
         end color/.store in=\endcolor,
         end color=black,
         varying line width steps/.initial=100
}
\pgfdeclaredecoration{width and color change}{initial}{
 \state{initial}[width=0pt, next state=line, persistent precomputation={%
   \pgfmathparse{\pgfdecoratedpathlength/\pgfkeysvalueof{/pgf/decoration/varying line width steps}}%
   \let\increment=\pgfmathresult%
   \def\x{0}%
 }]{}
 \state{line}[width=\increment pt,   persistent postcomputation={%
   \pgfmathsetmacro{\x}{\x+\increment}
   },next state=line]{%
   \pgfmathparse{ifthenelse(\x<\pgfdecoratedpathlength-5mm,varyinglw(100*(\x/\pgfdecoratedpathlength)),
    varyinglw(100*((\pgfdecoratedpathlength-5mm)/\pgfdecoratedpathlength))*(\pgfdecoratedpathlength-\x)/14) )}
   \pgfsetlinewidth{\pgfmathresult pt}%
   \pgfpathmoveto{\pgfpointorigin}%
   \pgfmathsetmacro{\steplength}{1.4*\increment}
   \pgfpathlineto{\pgfqpoint{\steplength pt}{0pt}}%
   \pgfmathsetmacro{\y}{100*(\x/\pgfdecoratedpathlength)}
   \pgfsetstrokecolor{\endcolor!\y!\startcolor}%
   \pgfusepath{stroke}%
 }
 \state{final}{%
   \pgfsetlinewidth{\pgflinewidth}%
   \pgfpathmoveto{\pgfpointorigin}%
   \pgfmathsetmacro{\y}{100*(\x/\pgfdecoratedpathlength)}
   \color{\endcolor!\y!\startcolor}%
   \pgfusepath{stroke}% 
 }
}


\begin{tikzpicture}[varying arrow/.style={-{Stealth[length=5mm,width=3.2mm,bend]},color=\endcolor,
postaction={/utils/exec=\pgfsetarrows{-},decorate,decoration={width and color change}}
}]
\begin{scope}[declare function={varyinglw(\x)=1+6*sin(1.8*\x);}]
\draw[varying arrow] (0,0) to[out=45,in=150] ++ (1.5,0)  to[out=-30,in=-135] ++ (3,0);
\draw[varying arrow,/pgf/decoration/varying line width steps=180]
(0,-3) to[out=45,in=150] ++ (1.5,0)  to[out=-30,in=-135] ++ (3,0) 
 to[out=45,in=45] ++ (-2,1);
\draw[varying arrow,/pgf/decoration/varying line width steps=180,
/pgf/decoration/start color=red,/pgf/decoration/end color=blue]
(0,-5) to[out=15,in=165] ++ (2,0)  to[out=-15,in=90] ++ (1,-1) 
 to[out=-90,in=-20] ++ (-1.5,0);
\end{scope}

\begin{scope}[declare function={varyinglw(\x)=4-3*cos(7.2*\x);},xshift=6cm]
\draw[varying arrow] (0,0) to[out=45,in=150] ++ (1.5,0)  to[out=-30,in=-135] ++ (3,0);
\draw[varying arrow,/pgf/decoration/varying line width steps=180]
(0,-3) to[out=45,in=150] ++ (1.5,0)  to[out=-30,in=-135] ++ (3,0) 
 to[out=45,in=45] ++ (-2,1);
\draw[varying arrow,/pgf/decoration/varying line width steps=180,
/pgf/decoration/start color=yellow,/pgf/decoration/end color=red]
(0,-5) to[out=15,in=165] ++ (2,0)  to[out=-15,in=90] ++ (1,-1) 
 to[out=-90,in=-20] ++ (-1.5,0);
\end{scope}

\begin{scope}[declare function={varyinglw(\x)=4-3*cos(5.6*\x);},xshift=12cm]
\draw[varying arrow] (0,0) to[out=45,in=150] ++ (1.5,0)  to[out=-30,in=-135] ++ (3,0);
\draw[varying arrow,/pgf/decoration/varying line width steps=180]
(0,-3) to[out=45,in=150] ++ (1.5,0)  to[out=-30,in=-135] ++ (3,0) 
 to[out=45,in=45] ++ (-2,1);
\draw[varying arrow,/pgf/decoration/varying line width steps=180,
/pgf/decoration/start color=yellow,/pgf/decoration/end color=blue]
(0,-5) to[out=15,in=165] ++ (2,0)  to[out=-15,in=90] ++ (1,-1) 
 to[out=-90,in=-20] ++ (-1.5,0);
\end{scope}
\end{tikzpicture}
\end{document} 

enter image description here

FUN: The mandatory animation.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{decorations,arrows.meta,bending}
\begin{document}

\pgfkeys{/pgf/decoration/.cd,
         start color/.store in=\startcolor,
         start color=black,
         end color/.store in=\endcolor,
         end color=black,
         varying line width steps/.initial=100
}
\pgfdeclaredecoration{width and color change}{initial}{
 \state{initial}[width=0pt, next state=line, persistent precomputation={%
   \pgfmathparse{\pgfdecoratedpathlength/\pgfkeysvalueof{/pgf/decoration/varying line width steps}}%
   \let\increment=\pgfmathresult%
   \def\x{0}%
 }]{}
 \state{line}[width=\increment pt,   persistent postcomputation={%
   \pgfmathsetmacro{\x}{\x+\increment}
   },next state=line]{%
   \pgfmathparse{ifthenelse(\x<\pgfdecoratedpathlength-5mm,varyinglw(100*(\x/\pgfdecoratedpathlength)),
    varyinglw(100*((\pgfdecoratedpathlength-5mm)/\pgfdecoratedpathlength))*(\pgfdecoratedpathlength-\x)/14) )}
   \pgfsetlinewidth{\pgfmathresult pt}%
   \pgfpathmoveto{\pgfpointorigin}%
   \pgfmathsetmacro{\steplength}{1.4*\increment}
   \pgfpathlineto{\pgfqpoint{\steplength pt}{0pt}}%
   \pgfmathsetmacro{\y}{100*(\x/\pgfdecoratedpathlength)}
   \pgfsetstrokecolor{\endcolor!\y!\startcolor}%
   \pgfusepath{stroke}%
 }
 \state{final}{%
   \pgfsetlinewidth{\pgflinewidth}%
   \pgfpathmoveto{\pgfpointorigin}%
   \pgfmathsetmacro{\y}{100*(\x/\pgfdecoratedpathlength)}
   \color{\endcolor!\y!\startcolor}%
   \pgfusepath{stroke}% 
 }
}

\foreach \Z in {0,10,...,720}
{\begin{tikzpicture}[varying arrow/.style={-{Stealth[length=5mm,width=3.2mm,bend]},color=\endcolor,
postaction={/utils/exec=\pgfsetarrows{-},decorate,decoration={width and color change}}
}]
\path[use as bounding box] (-4,-4) rectangle (4,4);
\begin{scope}[declare function={varyinglw(\x)=1+6*sin(1.8*\x);}]
\draw[varying arrow]  plot[variable=\z,domain={\Z+90}:{\Z+180}]
(\z:{1+sqrt(\z/90)});
\end{scope}

\end{tikzpicture}}
\end{document} 

enter image description here

ALTERNATIVE: Some additional possibilities arise with the calligraphy library. (If I understand correctly, currently you need to download the latest version from this repository.)

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{spath3}
\usetikzlibrary{calligraphy} % need to download the repository from https://github.com/loopspace/spath3
% run "tex spath3.dtx" and then copy the spath3.sty file in the same directory
% as the tex file
\usetikzlibrary{arrows.meta,bending,decorations.pathreplacing}
\tikzset{calligraph/.style={postaction={decorate,decoration={show path construction,
    moveto code={},
    lineto code={},
    curveto code={
      \calligraphy (\tikzinputsegmentfirst) .. controls
        (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb)
        ..(\tikzinputsegmentlast);
    },
    closepath code={}}}}
}
\begin{document}
\begin{tikzpicture}[line width=1pt]
\pen (-135:.125) --  (45:.125) ;
\draw[calligraph,{Stealth[length=5mm,width=3.2mm,bend]}-] (0,0) to[out=45,in=150] ++ (1.5,0)  to[out=-30,in=-135] ++ (3,0);
\end{tikzpicture}

\begin{tikzpicture}[line width=1pt]
\pen (-135:.125) --  (45:.125) ;
\draw[calligraph,{Stealth[length=5mm,width=3.2mm,bend]}-] (0,0) to[out=45,in=150]
++ (1.5,0)  to[out=-30,in=45] ++ (1,-2);
\end{tikzpicture}
\end{document}

enter image description here

16

This isn't a complete answer. But as a proof of concept, I tried adapting this answer from Stroke with variable thickness to make something like the arrows you want.

enter image description here

\documentclass[tikz,margin=0.5cm]{standalone}
\usetikzlibrary{decorations,arrows.meta}

\makeatletter
\pgfkeys{/pgf/decoration/.cd,
    start color/.store in =\startcolor,
    end color/.store in   =\endcolor
}
\pgfdeclaredecoration{width and color change}{initial}{
    \state{initial}[width=2pt, next state=line, persistent precomputation={%
        \pgfmathdivide{50}{\pgfdecoratedpathlength}%
        \let\increment=\pgfmathresult%
        \def\x{0}%
    }]{}
    \state{line}[width=.5pt,   persistent postcomputation={%
        \pgfmathadd@{\x}{\increment}%
        \let\x=\pgfmathresult%
    }]{%
        \pgfsetlinewidth{\x/20*0.005pt+\pgflinewidth}%
        \pgfsetarrows{-}%
        \pgfpathmoveto{\pgfpointorigin}%
        \pgfpathlineto{\pgfqpoint{.75pt}{0pt}}%
        \pgfsetstrokecolor{\endcolor!\x!\startcolor}%
        \pgfusepath{stroke}%
    }
    \state{final}{%
        \pgfsetlinewidth{\pgflinewidth}%
        \pgfpathmoveto{\pgfpointorigin}%
        \color{\endcolor!\x!\startcolor}%
        \pgfusepath{stroke}% 
    }
}
\makeatother

\begin{document}

\begin{tikzpicture}

\draw [line width=2pt, decoration={width and color change,start color=black, end color=black}, decorate,] plot [smooth, tension=1] coordinates { (0,0) (2,0.5) (5,0.7) (3,1.5) (1,1.5) } node [rotate=100] {\tikz \draw [-{Stealth[length=7mm, width=10mm]}](0,0);} ;

\end{tikzpicture}

\end{document} 
  • 4
    @marmot I'm actually thinking about forbidding nesting in a future version of PGF. It's just not fixable and always calls for trouble. – Henri Menke Jan 22 at 1:26

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.