Randomly curved arrows in TikZ
Here is a slight extension of Milo's nice answer. The main differences (improvements?) are:
- 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. - This variation also does not nest
tikzpicture
s. 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 isdeclare 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 withdeclare 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}
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}
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}
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.
\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}