# Better use of rounded corners of links from multiple nodes to multiple nodes with tikz on \draw[->]

My question follows one of my previous questions.

Here's my current code:

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{matrix,positioning}

\begin{scope}[x=1em,y=1em]
\newdimen\xend
\newdimen\yend
\path (#2.west);
\pgfgetlastxy{\xend}{\yend}
\foreach \i in {#1} {
\newdimen\xstart
\newdimen\ystart
\path (\i.east);
\pgfgetlastxy{\xstart}{\ystart}
\coordinate (1) at ({\xend-#3 em},\ystart);
\coordinate (2) at ({\xend-#3 em},\yend);
\ifdim\ystart=\yend
\draw[->] (\i.east)--(#2.west);
\else
\draw[->,rounded corners] (\i.east)--(1)--(2)--(#2.west);
\fi
}
\end{scope}
}

% \multilinkstomultiplenodes{list of left nodes}{list of right nodes}{distance between the right nodes and the right vertical line}{distance between the left vertical line and the right vertical line}
}

\begin{document}
\tikzset{
basic/.style={
draw,
rounded corners=2pt,
thick,
text width=8em,
align=flush center,
node distance=2em
}
}
\begin{tikzpicture}[]
\matrix[row sep=2em, column sep=4em, every node/.style={basic}] {
\node(n1){Text}; & \node(n6){another text}; \\
\node(n2){one thing}; & \node(n7){again text}; \\
\node(n3){text}; & \node(n8){text}; \\
\node(n4){text}; & \node(n9){text}; \\
\node(n5){text}; & \node(n0){text}; \\
};

%to modify
\end{tikzpicture}

\end{document}


And the result:

I'm now trying to define a new command so it will look like this:

I need to be able to decide the distance between right nodes and the right vertical line, and the distance between the left vertical line and the right vertical line. The middle horizontal line must be centered relatively to the right nodes.

I'm currently totally lost on how I should proceed.

• While the target output is helpful, you may want to explain the rules for the connections in more detail. The very top one is such that the branching point is on the upper connection whereas for all other connections its vertical position is in the middle of the nodes on the left-hand side. How would TikZ know what to do? – marmot Mar 19 at 17:52
• I edited my question – Vinccool96 Mar 19 at 18:04
• Sorry, but the rules are still not clear. If you would like to have a connection for example from each of n2, n3, n4, n5 to n7 (as stated in your code) and the horizontal line of the connection shall be centred horizontally to the right node(s), then it should go from n2 directly to n7 as n7 is the only node on the right in this connection. But in your drawing, the connection is centred relatively to the left nodes (namely n2, n3, n4 and n5). Or do I not understand your point? – Jasper Habicht Mar 19 at 18:56

Here is a proposal. It comes with a style connect through that checks whether or not a stretch is horizontal (in order to avoid problems with rounded corners) and another style multiconnect that does multiple connections. (I am generally not a big fan of writing macros but I think that one should use styles for TikZ. To be clear, I have not looked at Jasper's answer for that reason. It might or might not be that some things are parallel, if they are, he was first. I am not in the mood to look through these macros, sorry.)

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{matrix,positioning,fit,calc}

\begin{document}
\tikzset{
basic/.style={
draw,
rounded corners=2pt,
thick,
text width=8em,
align=flush center,
node distance=2em
},
horizontal stretch/.initial=1em,
connect through/.style={to path={
let \p1=($(\tikztostart)-(#1)$),\p2=($(\tikztotarget)-(#1)$),
\n1={abs(\y1)},\n2={abs(\y2)} in
\ifdim\n1<1pt
(\tikztostart) -- (#1)
\else
[/utils/exec=\pgfmathsetmacro{\mysign}{sign(\x1)}]
(\tikztostart) -|
([xshift=\mysign*\pgfkeysvalueof{/tikz/horizontal stretch}/2]#1)
-- (#1)
\fi
\ifdim\n2<1pt
(#1) -- (\tikztotarget)
\else
[/utils/exec=\pgfmathsetmacro{\mysign}{sign(\x2)}]
(#1) --
([xshift=\mysign*\pgfkeysvalueof{/tikz/horizontal stretch}/2]#1)
|- (\tikztotarget)
\fi
}},
multiconnect/.style n args={3}{insert path={%
[/utils/exec={\foreach \X [count=\Y] in {#2}
{\ifnum\Y=1
\xdef\LstTargets{(\X)}
\else
\xdef\LstTargets{\LstTargets (\X)}
\fi}}]
node[fit=\LstTargets,inner sep=0pt] (auxR){}
($(#1.east)!#3!(auxR.west)$) coordinate (auxM)
foreach \Y in {#2}
{
(#1.east) edge[connect through=auxM|-auxR,-latex] (\Y)
}}}
}
\begin{tikzpicture}[]
\matrix[row sep=2em, column sep=4em, every node/.style={basic}] {
\node(n1){Text}; & \node(n6){another text}; \\
\node(n2){one thing}; & \node(n7){again text}; \\
\node(n3){text}; & \node(n8){text}; \\
\node(n4){text}; & \node(n9){text}; \\
\node(n5){text}; & \node(n0){text}; \\
};
\foreach \XX in {n1,n2}
{\draw[rounded corners,multiconnect={\XX}{n6}{0.5}] ;}
\foreach \XX in {n2,n3,n4,n5}
{\draw[rounded corners,multiconnect={\XX}{n7,n8,n9,n0}{0.5}] ;}
\end{tikzpicture}

\end{document}


I do not fully understand the rules according to which the OP wants to connect the nodes, and I am quite sure that there is a simpler way (via packages) to achieve the following result, but maybe the following code helps to come up with a nice solution.

The connecting horizontal line is not centred relatively to the nodes on the right side as wished by the OP, but to the nodes on the left side (otherwise the result would be similar to the OP's version).

(Results in somewhat ugly connections when only connecting two nodes with each other (1:1).)

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{matrix,positioning,calc}

\begin{scope}[x=1em,y=1em]
\xdef\j{#2}
\foreach \c [count=\x] in {#1} {
\ifnum\x=1
\xdef\xtop{\c}
\xdef\xbottom{\c}
\else
\xdef\xbottom{\c} % redefining \xbottom until end of loop
\fi
}
\coordinate (left) at ([xshift=#3 em]$(\xtop.east)!0.5!(\xbottom.east)$);
\coordinate (right) at ([xshift=-#3 em]$(#2.west)!0.5!(#2.west)$);
\foreach \i in {#1} {
\draw[->,rounded corners]
(\i.east)-|(left)
-|(right)
--(\j.west);
}
\end{scope}
}

\begin{document}
\tikzset{
basic/.style={
draw,
rounded corners=2pt,
thick,
text width=8em,
align=flush center,
node distance=2em
}
}
\begin{tikzpicture}[]
\matrix[row sep=2em, column sep=4em, every node/.style={basic}] {
\node(n1){Text}; & \node(n6){another text}; \\
\node(n2){one thing}; & \node(n7){again text}; \\
\node(n3){text}; & \node(n8){text}; \\
\node(n4){text}; & \node(n9){text}; \\
\node(n5){text}; & \node(n0){text}; \\
};
\end{tikzpicture}

\end{document}


Result:

Edit

I came up with a new solution where the macro takes two lists of nodes that should be connected. I encountered several problems here.

First, as the OP already found out in the linked question above, there is a problem when we try to connect nodes with identical coordinates with strokes that have rounded corners. Because the nodes are generated automatically in our case, we cannot avoid that nodes have the same coordinates. So, we need to test the y coordinate of the nodes of the connecting path.

There we encounter the problem of rounding errors. I tried to solve this problem by dividing the y coordinate by 10 in order to clip the last digit and get rid of the rounding error.

Well, maybe the code below can be simplified, but it can serve as a starting point …

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{matrix,positioning,calc}

\begin{scope}[x=1em,y=1em]
\xdef\j{#2}
\foreach \c [count=\x] in {#1} {
\ifnum\x=1
\xdef\xtop{\c}
\xdef\xbottom{\c}
\else
\xdef\xbottom{\c}
\fi
}
\foreach \d [count=\y] in {#2} {
\ifnum\y=1
\xdef\ytop{\d}
\xdef\ybottom{\d}
\else
\xdef\ybottom{\d}
\fi
}
\newdimen\xmiddle
\newdimen\ymiddle
\newdimen\xleft
\newdimen\yleft
\newdimen\xright
\newdimen\yright
\coordinate (right) at ([xshift=-#3 em]$(\ytop.west)!0.5!(\ybottom.west)$);
\coordinate (left) at ([xshift=#3 em]\xtop.east |- right);
\path(left);
\pgfgetlastxy{\xmiddle}{\ymiddle}
\pgfmathsetlengthmacro{\ymiddlex}{\ymiddle/10}
\foreach \i in {#1} {
\path(\i);
\pgfgetlastxy{\xleft}{\yleft}
\pgfmathsetlengthmacro{\yleftx}{\yleft/10}
\foreach \j in {#2} {
\path(\j);
\pgfgetlastxy{\xright}{\yright}
\pgfmathsetlengthmacro{\yrightx}{\yright/10}
\ifdim\yleftx=\ymiddlex
\draw[->](\i.east)--(\j.west);
\else
\draw[->,rounded corners]
(\i.east)-|(left)
--(right)
\ifdim\ymiddlex=\yrightx
--(\j.west);
\else
|-(\j.west);
\fi
\fi
}
}
\end{scope}
}

\begin{document}
\tikzset{
basic/.style={
draw,
rounded corners=2pt,
thick,
text width=8em,
text depth=0em,
align=flush center,
node distance=2em
}
}
\begin{tikzpicture}[]
\matrix[row sep=2em, column sep=4em, every node/.style={basic}] {
\node(n1){Text}; & \node(n6){another text}; \\
\node(n2){one thing}; & \node(n7){again text}; \\
\node(n3){text}; & \node(n8){text}; \\
\node(n4){text}; & \node(n9){text}; \\
\node(n5){text}; & \node(n0){text}; \\
\node(n10){text}; & \node(n11){text}; \\
\node(n20){text}; & \node(n21){text}; \\
\node(n30){text}; & \node(n31){text}; \\
};
\end{tikzpicture}

\end{document}


Result:

• I wanted a command that would work like this: % \multilinkstomultiplenodes{list of left nodes}{list of right nodes}{distance between the right nodes and the right vertical line}{distance between the left vertical line and the right vertical line} that you would write like this: \multilinkstomultiplenodes{n2,n3,n4,n5}{n2,n3,n4,n5}{1}{1} – Vinccool96 Mar 19 at 20:24

I used this code:

\documentclass[border=5mm]{standalone}

\usepackage{tikz}
\usepackage{xstring}

\usetikzlibrary{matrix}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}

\newcommand{\listbcs}[4]{
\tikzset{
barycentric setup/.code={\foreach \X [count=\Y] in {#1}
{\ifnum\Y=1
\xdef\baryarg{\X=1}
\else
\xdef\baryarg{\baryarg,\X=1}
\fi}},
barycentric list/.style={barycentric setup={#1},insert path={%
(barycentric cs:\baryarg)}}
}
\path[barycentric list={#1}] node[anchor=center,align=flush center,#2] (#3) {#4};
}

\begin{scope}[x=1em,y=1em]
\listbcs{#2}{}{bcright}{}
\newdimen\xright
\newdimen\ybc
\newdimen\dump
\path(bcright);
\pgfgetlastxy{\dump}{\ybc}
\getfirst{#2}

\path(#3em,0em);
\newdimen\xtemp
\pgfgetlastxy{\xtemp}{\dump}
\coordinate (midright) at ({\xnow-#3 em},\ybc);
\coordinate (midleft) at ({\xnow-#3em-#4em},\ybc);
\foreach \i in {#1} {
\foreach \j in {#2}{
\newdimen\ystart
\path (\i.east);
\pgfgetlastxy{\dump}{\ystart}
\newdimen\xmidl
\newdimen\xmidr
\path (midleft);
\pgfgetlastxy{\xmidl}{\dump}
\path (midright);
\pgfgetlastxy{\xmidr}{\dump}
\newdimen\yend
\path (\j.west);
\pgfgetlastxy{\dump}{\yend}
\coordinate (cl) at (\xmidl,\ystart);
\coordinate (cr) at (\xmidr,\yend);
\ifdim\ystart=\ybc\relax%
\ifdim\ybc=\yend\relax%
\draw[->] (\i.east)--(\j.west);%
\else\relax%
\draw[->,rounded corners] (\i.east)--(midright)--(cr)--(\j.west);%
\fi\relax%
\else\relax%
\ifdim\ybc=\yend\relax%
\draw[->,rounded corners] (\i.east)--(cl)--(midleft)--(\j.west);%
\else\relax%
\draw[->,rounded corners] (\i.east)--(cl)--(midleft)--(midright)--(cr)--(\j.west);%
\fi\relax%
\fi\relax%
}
}
\end{scope}
}

\newcommand{\getfirst}[1]{
\StrCount{#1}{,}[\numofelem]
\ifnum\numofelem>0\relax
\else
#1
\fi
}

\begin{document}
\tikzset{
basic/.style={
draw,
rounded corners=2pt,
thick,
text width=8em,
align=flush center,
}
}
\begin{tikzpicture}
\matrix[row sep=2em, column sep=4em, every node/.style={basic}] {
\node(a){text}; & \node(c){text}; \\
\node(b){text}; & \node(d){text}; \\
\node(e){text}; & \node(f){text}; \\
};