[Tex/LaTex] Tikz curved arrows overlapping nodes

tikz-arrowstikz-pgf

I hope somebody can help me with this. I did already search for it, but none of the solutions seem to work. I'm trying to plot a simple graph, but the bend edges do overlap my nodes. That is, of course, undesirable. What I have so far:

\documentclass[10pt]{article}

\usepackage{tikz}
\usetikzlibrary{arrows}
\usetikzlibrary{positioning}

\begin{document}

\begin{tikzpicture}[->,>=stealth',auto,node distance=2cm,
  thick,main node/.style={draw,font=\sffamily\Large\bfseries}]

  \node[main node, rectangle, align=center] (1) {Crawling \\ Application};
  \node[main node, circle, align=center] (2) [right =of 1] {Crawl \\ Manager};
  \node[main node, rectangle] (3) [right =of 2] {Downloader};
  \node[main node, rectangle] (4) [right =of 2, below =of 3] {Downloader};
  \node[main node, rectangle] (5) [right =of 2, above =of 3] {Downloader};
  \node[main node, rectangle, align=center] (6) [below =of 2] {DNS \\ resolver};

  \path[every node/.style={font=\sffamily\small}]
    (1) edge node [right] {URL requests} (2)
    (2) edge node [right] {} (3)
    (2) edge node [right] {} (4.west)
    (2) edge node [right] {} (5.west)
    (2) edge node [below] {} (6)
    (3.east) edge[bend right, out=180,in=270] node [left] {} (2.north)
    (4.east) edge[bend right, out=180,in=270] node [left] {} (2.north)
    (5.east) edge[bend right, out=180,in=270] node [left] {} (2.north)
    (3.east) edge[bend left, out=180,in=90] node [left] {} (1.south)
    (4.east) edge[bend left, out=180,in=90] node [left] {} (1.south)
    (5.east) edge[bend left, out=180,in=90] node [left] {} (1.south);
\end{tikzpicture}
\end{document}

And the resulting graph:
enter image description here

I would like to have the curved arrows bend so that the ones going into the circle are above the highest "Downloader" rectangle and the ones going into the rectangle are below the "DNS resolver" rectangle. If possible, they should converge on their way, so that it is only one line. But that's optional. And, if some Tex-magic is necessary, I need to place a label on top of the upper arrows and one below the lower arrows later.

Thanks for the help already!

Best Answer

Not a completly automated solution, but one which shows some (hopefully) useful tricks.

The figure is drawn in three phases:

  1. The first phase draws the nodes and give them names. The code for this part is identical to your code, but I also define two extra auxiliar coordinates, one at the top of the uppermost "Downloader", and another below the "DNS Resolver" box.
  2. The edges unrelated to nodes 3, 4, and 5 are drawn separately, using to syntax, instead of edge node [right] {}, since most of your edges are not labeled and do not require those artificial empty nodes. The line (1) to (2) is labeled with a midway node.
  3. The edges related to nodes 3, 4 and 5 are drawn in a loop. For each "Downloader", three edges are drawn:
    1. One straight line from (2)
    2. One curved line to (2.north) passing above all "Downloaders"
    3. One curved line to (1.south) passing below "DNS Resolver"

These curved lines are the tricky ones. I used out, in and looseness to control the curvature, but also made use of the auxiliar coordinates top and bottom to have more control of the general shape. Each "top" curve passes through two horizontally aligned points, at coordinates (top) and (top-|bottom), plus a small offest with depends on the loop iteration, to avoid all lines overlapping in a single one. A similar strategy is used for the bottom curves.

This is the code:

\documentclass[tikz, border=5pt]{standalone}

\usepackage{tikz}
\usetikzlibrary{arrows}
\usetikzlibrary{positioning,calc}

\begin{document}

\begin{tikzpicture}[->,>=stealth',auto,node distance=2cm,
thick,main node/.style={draw,font=\sffamily\Large\bfseries}]

% Phase 1
\node[main node, rectangle, align=center] (1) {Crawling \\ Application};
\node[main node, circle, align=center] (2) [right =of 1] {Crawl \\ Manager};
\node[main node, rectangle] (4) [right =of 2] {Downloader};
\node[main node, rectangle] (3) [right =of 2, below =of 4] {Downloader};
\node[main node, rectangle] (5) [right =of 2, above =of 4] {Downloader};
\node[main node, rectangle, align=center] (6) [below =of 2] {DNS \\ resolver};
\coordinate[above=of 5.north east] (top);
\coordinate[below=of 6.south east] (bottom);

% Phase 2
\draw (1) edge node[above, midway, font=\sffamily\small] {URL requests} (2) ;
\draw (2) to (6);

% Phase 3
\foreach \n in {3,4,5} {
    \draw (2) to (\n.west);
    \draw (\n.east) to[out=10,in=0,looseness=1.8] ($(top)+(0,-\n/3)$) to ($(top-|bottom)+(0,-\n/3)$) to[out=180,in=90] (2.north);
    \draw (\n.east) to[out=-20,in=0,looseness=1.2] ($(bottom-|top)+(0,2-\n/3)$) to ($(bottom)+(0,2-\n/3)$)
    to[out=180,in=-90] (1.south);
}

\end{tikzpicture}
\end{document}

And this is the result: Result

Update. I've just noticed the optional requirement of the OP "If possible, they should converge on their way, so that it is only one line. But that's optional."

Indeed, the solution is simpler, because no calc is required to "move" the auxiliar coordinates for each iteration of the loop:

\documentclass[tikz, border=5pt]{standalone}

\usepackage{tikz}
\usetikzlibrary{arrows}
\usetikzlibrary{positioning,calc}

\begin{document}

\begin{tikzpicture}[->,>=stealth',auto,node distance=2cm,
  thick,main node/.style={draw,font=\sffamily\Large\bfseries}]

  \node[main node, rectangle, align=center] (1) {Crawling \\ Application};
  \node[main node, circle, align=center] (2) [right =of 1] {Crawl \\ Manager};
  \node[main node, rectangle] (4) [right =of 2] {Downloader};
  \node[main node, rectangle] (3) [right =of 2, below =of 4] {Downloader};
  \node[main node, rectangle] (5) [right =of 2, above =of 4] {Downloader};
  \node[main node, rectangle, align=center] (6) [below =of 2] {DNS \\ resolver};
  \coordinate[above=of 5.north east] (top);
  \coordinate[below=of 6.south east] (bottom);

  % Phase 2
  \draw (1) edge node[above, midway, font=\sffamily\small] {URL requests} (2) ;
  \draw (2) to (6);

  % Phase 3
  \foreach \n in {3,4,5} {
      \draw (2) to (\n.west);
      \draw (\n.east) to[out=10,in=0,looseness=1] (top) to (top-|bottom) to[out=180,in=90, looseness=0.7] (2.north);
      \draw (\n.east) to[out=-20,in=0,looseness=1] (bottom-|top) to (bottom) to[out=180,in=-90, looseness=1] (1.south);
  }
\end{tikzpicture}
\end{document}

Result2

Related Question