
Я хочу нарисовать сетевой график, но многие ребра накладываются друг на друга. Я хочу изменить кривизну ребер, чтобы сделать накладывающиеся ребра видимыми.
Моя первоначальная идея была сделать кривизну случайной равномерной в диапазоне 20-30 градусов. Но я не знаю, можно ли это легко реализовать в TikZ.
Решение должно работать без ручного указания кривизны, чтобы его можно было применять и к более крупным сетям.
Результат должен выглядеть примерно так:
Код МВЭ:
\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}
редактировать: Чтобы дать контекст, цель — нарисовать что-то вдохновленное этим (но меньшего размера):
(Источник:http://blog.revolutionanalytics.com/2010/12/facebooks-social-network-graph.html)
решение1
Вот метод грубой силы.
\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}
Если вы действительно хотите добавить немного случайности, это легко.
решение2
Если имена ваших узлов всегда являются целыми числами, вы можете использовать этот подход, который проверяет, совпадают ли последняя цель и источник ребра с текущими, и, если да, увеличивает значение bend right
:
Недостаток: это работает только если ребра от и к одним и тем же узлам расположены рядом друг с другом. Это 1/2, 3/2, 1/2
снова приведет к наложению ребер.
\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
\addtocounter{samesourcetarget}{\increasebend}
\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}
Редактировать:Добавлен механизм сортировки, взятый изздесь(возможно, такой список будет проще отсортировать):
\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
\addtocounter{samesourcetarget}{\increasebend}
\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}
решение3
Вот мое собственное решение (в MWE оно немного некрасивое, но как только сеть становится действительно плотной, оно может быть весьма неплохим):
\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}