[Tex/LaTex] Connect Tikz nodes via two parallel arrows

arrowsnodesparalleltikz-pgf

I am just starting to learn using Tikz and am a little lost within its seemingly infinite amount of options and possibilities.

What I am trying to do is visualize a few Matlab function interaction (which one calls which one, what is the in- and output). Since there are only about six subroutines, I place each one manually as a node. This is an example:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes,positioning}

\begin{document}

\begin{tikzpicture}[auto]
\node           [rounded rectangle,draw]        (node1)     {Node 1};
\coordinate [above=of node1]                                (input);
\node           [rounded rectangle,draw]    at  (-10:6cm) (node2)       {Node 2};
\draw           [->,very thick]             ([xshift=0.1cm]  input.south)  -- 
                node {right}                ([xshift=0.1cm]  node1.north);
\draw           [->,very thick]             ([xshift=-0.1cm] node1.north) -- 
                node {left}                 ([xshift=-0.1cm] input.south);
\draw                                            (node1) -- (node2); 
\end{tikzpicture}

\end{document}

Output

Now I would like to apply these double in/out arrows I (probably pretty awkwardly) created using xshift to all node connections, so in this example also for the connection between Node 1 and Node 2. Ideally, the space between these two arrows would be the same, independant of their angle and they would nicely connect to the outer line of the nodes.
Any idea how I could achieve this? Similar questions I have found only used rectangle and orthogonal lines.

Best Answer

A Minimal Code

\documentclass[tikz]{standalone}
\usetikzlibrary{arrows.meta}

\begin{document}

\pgfkeys{
  /pgf/arrow keys/.cd,
  shear/.store in=\pgfarrowshear,
  US/.style={
    length = +1.05pt 1.925 1,
    shear = 1.7pt,
  },
  UK/.style={
    length = +1.05pt 1.925 1,
    shear = -1.7pt,
  },
}
\makeatletter
\newdimen\pgfarrowshear
\let\oldmacro\pgf@arrow@drawer@shift
\def\pgf@arrow@drawer@shift{\pgftransformyshift\pgfarrowshear\oldmacro}

\begin{tikzpicture}
    \path(1,3)node(A){A}(3,1)node(B){B};;
    \draw[double,double distance=3pt,{<[US]}-{>[US]},bend left](A)to(B){};
    \draw[double,double distance=3pt,{<[UK]}-{>[UK]},bend left](B)to(A){};
\end{tikzpicture}

\end{document}

Step by Step

Without lost of generality, assume arrows are pointing from left to right.

In pgfcorearrows.code.tex, Tikz shifts arrow tips using \pgf@arrow@drawer@shift, defined as

\def\pgf@arrow@drawer@shift#1#2#3{% tip end, back end, line end, sep
  \pgf@xb#2\pgftransformxshift{-\pgf@xb}%
  \pgf@xc#1%
  \advance\pgf@xc by\pgfarrowsep%
  \advance\pgf@xc by-\pgf@xb%
}

This step is necessary because sometimes we have shorten >= or sep= (in case there are more tips). But no y shift is applied because logically arrow tips are aligned to the main path.

So we have a dirty step which rewrites this macro as follows

\def\pgf@arrow@drawer@shift#1#2#3{
  \pgftransformyshift\pgfarrowshear% add this line
  \pgf@xb#2\pgftransformxshift{-\pgf@xb}%
  \pgf@xc#1%
  \advance\pgf@xc by\pgfarrowsep%
  \advance\pgf@xc by-\pgf@xb%
}

Next we want to make shear= acts like sep=. So I modified the code of sep/.code

\pgfkeys{
  /pgf/arrow keys/.cd,
  shear/.code={
    \pgfarrowsthreeparameters{#1}%
    \expandafter\pgfarrowsaddtooptions\expandafter{\expandafter\pgfarrowslinewidthdependent\pgfarrowstheparameters\pgfarrowshear\pgf@x}%
  },
  shear/.default = +0pt -.5 -.5
}

So now we can achieve the following by something like [-{>[shear=0pt .5 .5]>[shear]>[shear=0pt .5 .5]>[shear]}]

Line Width Dependent

However, \pgfarrowslinewidthdependent will never produce the right amount of y shift. If you look at the manual carefully, it calculates

wi = inner line width
wo = outer line width
wt = total line width = inner + 2*outer
w  = #3*wo + (1-#3)*wt = weighted line width
return #1 + #2*w

Which is, equivalently

#1 + #2 * [ (1-#3)*wi + (2-#3)*wo ]

While what we want is

0 + .5*wi + .5*wo

It leads to

#1 = 0
#2 = 0
#3 = ∞

I do not know why TikZ do so. Anyway, we can do it ourself.

\def\pgfarrowslinewidthdependentnew#1#2#3{%
  \pgf@x#1%
  \ifdim\pgfinnerlinewidth>0pt%
    \pgf@arrows@inner@line@width@depnew{#2}{#3}%
  \else%  
    \advance\pgf@x by#2\pgflinewidth%
  \fi%
}
\def\pgf@arrows@inner@line@width@depnew#1#2{%
  % #1 * outer line width + #2 * inner line width = our new one = the following
  % (#1/2) * full line width + (#2-#1/2) * inner line width)
  % Compute "real" line width
  \pgf@xa.5\pgflinewidth%
  \pgf@xa#1\pgf@xa%
  \advance\pgf@x by\pgf@xa%
  \pgf@xa\pgfinnerlinewidth%
  \pgf@xb.5\pgf@xa%
  \advance\pgf@x by#2\pgf@xa%
  \advance\pgf@x by-#1\pgf@xb%
}

For Convenience

We define

\pgfkeys{
  /pgf/arrow keys/.cd,
  Bidirectional/.style={
    length = +1.05pt 1.925 1,
    shear
  }
}

So we can have

\begin{tikzpicture}
    \path(0,4)(4,0);
    \node(A)at(1,3){A};
    \node(B)at(3,1){B};
    \draw[double,double distance=3pt,{<[Bidirectional]}-{>[Bidirectional]},bend left]
        (A)to(B){};
\end{tikzpicture}

Code

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\begin{document}

\makeatletter

\pgfkeys{
  /pgf/arrow keys/.cd,
  Bidirectional/.style={
    length = +1.05pt 1.925 1,
    shear
  },
  shear/.code={
    \pgfarrowsthreeparameters{#1}%
    \expandafter\pgfarrowsaddtooptions\expandafter{\expandafter\pgfarrowslinewidthdependentnew\pgfarrowstheparameters\pgfarrowshear\pgf@x}%
  },
  shear/.default = +0pt -.5 -.5
}
\newdimen\pgfarrowshear
\pgfarrowshear0pt
\def\pgfarrowslinewidthdependentnew#1#2#3{%
  \pgf@x#1%
  \ifdim\pgfinnerlinewidth>0pt%
    \pgf@arrows@inner@line@width@depnew{#2}{#3}%
  \else%  
    \advance\pgf@x by#2\pgflinewidth%
  \fi%
}
\def\pgf@arrows@inner@line@width@depnew#1#2{%
  % #1 * outer line width + #2 * inner line width = our new one = the following
  % (#1/2) * full line width + (#2-#1/2) * inner line width)
  % Compute "real" line width
  \pgf@xa.5\pgflinewidth%
  \pgf@xa#1\pgf@xa%
  \advance\pgf@x by\pgf@xa%
  \pgf@xa\pgfinnerlinewidth%
  \pgf@xb.5\pgf@xa%
  \advance\pgf@x by#2\pgf@xa%
  \advance\pgf@x by-#1\pgf@xb%
}
\def\pgf@arrow@drawer@shift#1#2#3{
  \pgftransformyshift\pgfarrowshear%
  \pgf@xb#2\pgftransformxshift{-\pgf@xb}%
  \pgf@xc#1%
  \advance\pgf@xc by\pgfarrowsep%
  \advance\pgf@xc by-\pgf@xb%
}

\begin{tikzpicture}
    \draw[-{>[sep=1pt]>}](1,1)->(3,1);
\end{tikzpicture}

\begin{tikzpicture}[>={Classical TikZ Rightarrow[length = +1.05pt 1.925 1]}]
    \draw[double distance=3pt,-{>[shear=0pt .5 .5]>[shear]>[shear=0pt .5 .5]>[shear]}]
        (1,1)->(3,1);
\end{tikzpicture}

\begin{tikzpicture}
    \path(0,4)(4,0);
    \node(A)at(1,3){A};
    \node(B)at(3,1){B};
    \draw[double,double distance=3pt,{<[Bidirectional]}-{>[Bidirectional]},bend left]
        (A)to(B){};
\end{tikzpicture}

\end{document}