# Draw edges with varying/random curvature in TikZ

I want to draw a network plot, but many edges are overlaying. I want to vary curvatures of the edges to make overlaying edges visible.

My initial idea was to make the curvature a random uniform between 20-30 degree. But I don't know if that can be easily implemented in TikZ.

The solution should work without manually specifying the curvatures, so that it could be applied to larger networks as well.

The result should look somewhat like this:

MWE code:

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}
\node at (0,0) [circle, fill=blue] (1) {};
\node at (0,10) [circle, fill=blue] (2) {};
\node at (7,10) [circle, fill=blue] (3) {};
\node at (10,4) [circle, fill=blue] (4) {};
\node at (4,3) [circle, fill=blue] (5) {};
\draw (1) to[bend right] (2);
\draw (1) to[bend right] (2);
\draw (1) to[bend right] (2);
\draw (2) to[bend right] (1);
\draw (1) to[bend right] (4);
\draw (2) to[bend right] (3);
\draw (2) to[bend right] (3);
\draw (2) to[bend right] (3);
\draw (2) to[bend right] (5);
\draw (3) to[bend right] (2);
\draw (3) to[bend right] (5);
\draw (3) to[bend right] (5);
\draw (3) to[bend right] (5);
\draw (3) to[bend right] (4);
\draw (4) to[bend right] (1);
\draw (4) to[bend right] (1);
\draw (4) to[bend right] (3);
\draw (4) to[bend right] (3);
\end{tikzpicture}
\end{document}


edit: To give context, the aim is to draw something inspired by (but smaller than) this:

• you can add how much bend to include via bend right=10. – percusse Feb 21 '18 at 10:37
• Sure, but the question is how to automatically vary the 10, so that the approach would also be feasible for larger networks without manual input – sheß Feb 21 '18 at 12:00
• for example bend right=10 + random(10,20). I believe your facebook picture is just visually pleasing random connections too. – percusse Feb 21 '18 at 12:06
• I didn't know "+random(10,30)" was possible, and I also get an error when I do \tikzset{m1/.style = {solid,bend right=10 + random(10,30)}} do I have to load anything. ("Error: I don't know the key /tikz/30") – sheß Feb 21 '18 at 14:01
• somewhere you are having 30 as a standalone key. That shouldn't happen. – percusse Feb 21 '18 at 14:02

Here is a brute force method.

\documentclass{article}
\usepackage{tikz}
\newcommand{\MultiConnect}[5][]{%
\pgfmathsetmacro{\imin}{#2-5*#3}
\pgfmathsetmacro{\imax}{#2+5*#3}
\pgfmathsetmacro{\inext}{#2-5*#3+10}
\foreach \i in {\imin,\inext,...,\imax}
\draw[#1] (#4) to[bend right=\i] (#5);
}
\begin{document}
The command
\verb|\MultiConnect{offset}{# of connections}{first node}{second node}|
allows you to draw connections.

\begin{tikzpicture}
\node at (0,0) [circle, fill=blue] (1) {};
\node at (0,10) [circle, fill=blue] (2) {};
\node at (7,10) [circle, fill=blue] (3) {};
\node at (10,4) [circle, fill=blue] (4) {};
\node at (4,3) [circle, fill=blue] (5) {};
\foreach \j in {1,...,4}{\pgfmathtruncatemacro{\nextj}{\j+1}
\foreach \k in {\nextj,...,5}
{
\MultiConnect{0}{4}{\j}{\k}
}
}
\end{tikzpicture}

\begin{tikzpicture}
\node at (0,0) [circle, fill=blue] (1) {};
\node at (0,10) [circle, fill=blue] (2) {};
\node at (7,10) [circle, fill=blue] (3) {};
\node at (10,4) [circle, fill=blue] (4) {};
\node at (4,3) [circle, fill=blue] (5) {};
\foreach \j in {1,...,4}{\pgfmathtruncatemacro{\nextj}{\j+1}
\foreach \k in {\nextj,...,5}
{
\pgfmathsetmacro{\Offset}{20+10*rand}
\typeout{\j,\k,\Offset}
\MultiConnect{\Offset}{4}{\j}{\k}
}
}
\end{tikzpicture}

\end{document}


If you really want to put some randomness in, this is easy.

• Great, I based my solution (which I post separately) on your suggestions, which is why accept your answer. – sheß Feb 23 '18 at 11:06

If your node names are always integers, you could use this approach that checks whether the last target and source of the edge are the same as the current ones and, if yes, increases the bend right value:

Shortcoming: this only works if edges from and to the same nodes are put next to each other. That is 1/2, 3/2, 1/2 would again result in overlaying edges.

\documentclass[tikz]{standalone}

\newcounter{lastsource}
\newcounter{lasttarget}
\newcounter{samesourcetarget}

\begin{document}
\begin{tikzpicture}
\node at (0,0)  [circle, fill=blue] (1) {};
\node at (0,10) [circle, fill=blue] (2) {};
\node at (7,10) [circle, fill=blue] (3) {};
\node at (10,4) [circle, fill=blue] (4) {};
\node at (4,3)  [circle, fill=blue] (5) {};

\def\defaultbend{30}
\def\increasebend{5}

\setcounter{samesourcetarget}{\defaultbend}
\foreach \source/\target in {1/2, 1/2, 1/2, 2/1, 1/4, 2/3, 2/3, 2/2, 2/5, 3/2, 3/5, 3/5, 3/5, 3/4, 4/1, 4/1, 4/3, 4/3}{
\ifnum\value{lastsource}=\source
\ifnum\value{lasttarget}=\target
\else
\setcounter{samesourcetarget}{\defaultbend}
\fi
\else
\setcounter{samesourcetarget}{\defaultbend}
\fi
\draw (\source) to[bend right=\thesamesourcetarget] (\target);
\setcounter{lastsource}{\source}
\setcounter{lasttarget}{\target}
}

\end{tikzpicture}
\end{document}


Edit: Added a sorting mechanism taken from here (maybe it is easier to sort such a list):

\documentclass[tikz]{standalone}
\usepackage{expl3,l3sort,xparse}

\ExplSyntaxOn
\prg_new_conditional:Nnn \sort_string_if_before:nn{ p,T,F,TF }{
\int_compare:nTF {\pdftex_strcmp:D{#1}{#2} < 0}{\prg_return_true:}{\prg_return_false:}
}

\NewDocumentCommand{\sortlist}{smm}{
\IfBooleanTF{#1}{
\clist_set:No \l__sort_sortlist_data_clist{#2}
}{
\clist_set:Nn \l__sort_sortlist_data_clist{#2}
}
\sort_sortlist:N \l__sort_sortlist_data_clist
\clist_set_eq:NN #3 \l__sort_sortlist_data_clist
}
\clist_new:N \l__sort_sortlist_data_clist

\cs_new_protected:Nn \sort_sortlist:N{
\clist_sort:Nn #1{
\sort_string_if_before:nnTF{##1}{##2}{\sort_ordered:}{\sort_reversed:}
}
}
\ExplSyntaxOff

\newcounter{lastsource}
\newcounter{lasttarget}
\newcounter{samesourcetarget}

\begin{document}

\begin{tikzpicture}
\node at (0,0)  [circle, fill=blue] (1) {};
\node at (0,10) [circle, fill=blue] (2) {};
\node at (7,10) [circle, fill=blue] (3) {};
\node at (10,4) [circle, fill=blue] (4) {};
\node at (4,3)  [circle, fill=blue] (5) {};

\def\defaultbend{30}
\def\increasebend{5}

\sortlist{2/5, 3/2, 3/5, 1/2, 4/1, 4/3, 1/2, 2/1, 1/4, 2/3, 2/3, 2/2, 3/5, 1/2, 3/5, 3/4, 4/1, 4/3}{\sortedcoords}

\setcounter{samesourcetarget}{\defaultbend}
\foreach \source/\target in \sortedcoords {
\ifnum\value{lastsource}=\source
\ifnum\value{lasttarget}=\target
\else
\setcounter{samesourcetarget}{\defaultbend}
\fi
\else
\setcounter{samesourcetarget}{\defaultbend}
\fi
\draw (\source) to[bend right=\thesamesourcetarget] (\target);
\setcounter{lastsource}{\source}
\setcounter{lasttarget}{\target}
}

\end{tikzpicture}
\end{document}

• Thanks, this is already a really cool solution. Unfortunately my workflow (which reads the network data from a large file) doesn't permit me to sort the edges in the way required for your solution. – sheß Feb 21 '18 at 12:05

This is my own solution (in the MWE it's a little ugly, but once the network becomes really dense it can be quite nice):

\documentclass[tikz]{standalone}
\newcommand{\drawrcurvededge}[2]{
\pgfmathsetmacro{\Offset}{20+10*rand}
\draw (#1) to[bend right=\Offset] (#2);
}
\begin{document}
\begin{tikzpicture}
\node at (0,0) [circle, fill=blue] (1) {};
\node at (0,10) [circle, fill=blue] (2) {};
\node at (7,10) [circle, fill=blue] (3) {};
\node at (10,4) [circle, fill=blue] (4) {};
\node at (4,3) [circle, fill=blue] (5) {};
\drawrcurvededge{1}{2}
\drawrcurvededge{1}{2}
\drawrcurvededge{1}{2}
\drawrcurvededge{2}{1}
\drawrcurvededge{1}{4}
\drawrcurvededge{2}{3}
\drawrcurvededge{2}{3}
\drawrcurvededge{2}{3}
\drawrcurvededge{2}{5}
\drawrcurvededge{3}{2}
\drawrcurvededge{3}{5}
\drawrcurvededge{3}{5}
\drawrcurvededge{3}{5}
\drawrcurvededge{3}{4}
\drawrcurvededge{4}{1}
\drawrcurvededge{4}{1}
\drawrcurvededge{4}{3}
\drawrcurvededge{4}{3}
\end{tikzpicture}
\end{document}