12

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

Thanks!

enter image description here

Milo
  • 9,440
sorciotto
  • 147

2 Answers2

19

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+6sin(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-3cos(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-3cos(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+6sin(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.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{calligraphy}
\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

Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
15

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} 
Milo
  • 9,440
  • 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 '19 at 01:26