12

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

Thanks!

enter image description here

2 Answers 2

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+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.

\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

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} 
1
  • 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. Jan 22, 2019 at 1:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .