8

It seems to be a bit tricky to draw outlined, transparent arrows between nodes in Tikz.

Here is my attempt at solving the simple case where the path is a straight, vertical line:

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{calc,shapes.arrows}

\begin{document}

\begin{tikzpicture}[
  fat arrow/.style={draw,red,
    every to/.style={
      to path={
        let \p1 = ($(\tikztotarget)-(\tikztostart)$),
            \n2 = {veclen(\x1,\y1)} in
        -- (\tikztotarget)
        node[draw, blue, pos=.5, inner sep=0, text width=.2cm,
             minimum height=\n2, single arrow, shape border rotate=270]
          {} \tikztonodes}
  }}]
    \fill[yellow] (-.2,.5) rectangle (2.2,1.5);
    \path (0,2) node[draw] (A) {A}
          (0,0) node[draw] (B) {B}
          (2,2) node[draw] (C) {C}
          (2,0) node[draw] (D) {D}
          ;
    \path[fat arrow] (A) to (B);
    \path[fat arrow] (C.south) to (D.north);
\end{tikzpicture}

\end{document}

It gives this result:

output from LaTeX code above

But I would like the blue arrows to be exactly between the node boxes.

The path in red from A to B goes between the edges of the two nodes, but the height of the blue node on the "to" path is calculated from B.center - A.center. Is there any "simple" way to fix this so that the arrow fits between the two nodes?

For the arrow between C and D, by adjusting the pos=.5, I can fit the arrow exactly between the two nodes. Why is this necessary? Can the correct offset be calculated automatically?

Is there a "simple" way to set the value of shape border rotate automatically?

7
  • 3
    Better: remove pos=0.5 and set anchor=tip. Commented May 17, 2018 at 20:11
  • @Kpym: the observation that the center of the shape may not be the real center was a good one, though I'm not good enough at pgf to confirm it against the code.
    – tbrk
    Commented May 17, 2018 at 20:16
  • You can use it not only for vertical arrows with -- node[pos=.999,draw, blue, inner sep=1mm, anchor=tip, minimum height=\n2, single arrow,sloped]{} (\tikztotarget)
    – Kpym
    Commented May 17, 2018 at 20:26
  • 2
    Just remembered, another possibility might be something along the lines of tex.stackexchange.com/questions/406162/…. Commented May 17, 2018 at 20:42
  • I tried to adapt that solution before posting but I had some trouble using it with beamer and [french]babel (though, I can't reproduce them in a standalone example).
    – tbrk
    Commented May 18, 2018 at 4:12

2 Answers 2

9

This implements rotation (had to use rotate, not shape border rotate, don't know why) and sets the position correctly (by removing pos=0.5 and adding anchor=tip). I used inner sep to set the width of the arrow, because text width/minimum width didn't work (don't know why).

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{calc,shapes.arrows}

\begin{document}

\begin{tikzpicture}[
  fat arrow/.style={draw,red,
    every to/.style={
      to path={
        let \p1 = ($(\tikztotarget)-(\tikztostart)$),
            \n1 = {veclen(\x1,\y1)},
            \n2 = {mod(scalar(atan2(\y1,\x1))+360, 360)} % calculate angle in range [0,360)
        in
        -- (\tikztotarget)
        node[draw, blue,
             inner xsep=0pt,inner ysep=5pt, % use inner ysep to set width
             minimum height=\n1-\pgflinewidth,
             single arrow,
             rotate=\n2, % not shape border rotate, because that for some reason didn't work
             anchor=tip, % anchor=tip added, pos=0.5 removed
             #1          % options passed to fat arrow style are added here
             ]
          {} \tikztonodes}
  }},
  fat arrow/.default= % set empty default for argument to fat arrow
]
    \fill[yellow] (-.2,.5) rectangle (2.2,1.5);
    \path (0,2) node[draw] (A) {A}
          (1,0) node[draw] (B) {B}
          (2,2) node[draw] (C) {C}
          (2,0) node[draw] (D) {D}
          ;
    \path[fat arrow] (A.south east) to (B.north west);
    \path[fat arrow] (C.south) to (D.north);
\end{tikzpicture}

\end{document}

Assuming you only use this between nodes, Kpym's suggestion in the comment below can be used to figure out the start/end anchors of the arrow. With the code below,

\path[fat arrow] (A) to (B);
\path[fat arrow] (C) to (D);

gives:

enter image description here

\documentclass[border=5mm]{standalone}

\usepackage{tikz}
\usetikzlibrary{calc,shapes.arrows}

\begin{document}

\begin{tikzpicture}[
  fat arrow/.style={draw,red,
    every to/.style={
      to path={
        let \p1 = ($(\tikztotarget)-(\tikztostart)$),
            \n1 = {int(mod(scalar(atan2(\y1,\x1))+360, 360))}, % calculate angle in range [0,360)
            \p2 = ($(\tikztotarget.\n1+180)-(\tikztostart.\n1)$),
            \n2 = {veclen(\x2,\y2)}
        in
        -- (\tikztotarget)
        node[draw, blue,
             inner xsep=0pt,inner ysep=5pt, % use inner ysep to set width
             minimum height=\n2-\pgflinewidth,
             single arrow,
             rotate=\n1, % not shape border rotate, because that for some reason didn't work
             anchor=tip, % anchor=tip added, pos=0.5 removed
             #1          % arguments passed to fat arrow added here
             ]
          at (\tikztotarget.\n1+180)
          {} \tikztonodes}
  }},
  fat arrow/.default= % empty default for argument of fat arrow
]
    \fill[yellow] (-.2,.5) rectangle (2.2,1.5);
    \path (0,2) node[draw] (A) {A}
          (1,0) node[draw] (B) {B}
          (2,2) node[draw] (C) {C}
          (2,0) node[draw] (D) {D}
          ;
    \path[fat arrow] (A) to (B);
    \path[fat arrow] (C) to (D);
\end{tikzpicture}

\end{document}

Addendum:

Following marmot's suggestion, I added the possibility of passing options to the arrow node, with an (optional) argument to fat arrow. I did this for both code blocks above. So for example, with

\path[fat arrow={densely dotted,thick,red}] (C) to (D);

you get

enter image description here

4
  • You still have to explicitly state the start/end anchors though, I don't know how one can get around that. Commented May 17, 2018 at 20:27
  • 1
    As you have the angle you can use it to get the anchor positions of \tikztostart and \tikztotarget if you know that they are nodes. For example (\tikztostart.90) should work.
    – Kpym
    Commented May 17, 2018 at 20:30
  • @Kpym Yes, good point. I edited my answer. Commented May 17, 2018 at 20:39
  • Super! It also works, for example, with double arrow (and pos=0.5), and cylinder (with anchor=top). Thank you @Kpym and @TorbjørnT.
    – tbrk
    Commented May 18, 2018 at 4:22
8

UPDATE: Completely switching gears and drawing the arrow as a decoration. (I also made the answer a bit more concise, I hope.)

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations,decorations.text} %  decorations.text just 4 fun
\pgfkeys{/tikz/.cd,
    outlined arrow width/.store in=\OutlinedArrowWidth,
    outlined arrow width=10pt,
    outlined arrow step/.store in=\OutlinedArrowStep,
    outlined arrow step=1pt,
    outlined arrow length/.store in=\OutlinedArrowLength,
    outlined arrow length=5pt,
}

\pgfdeclaredecoration{outlined arrow}{initial}
{% initial arrow butt
\state{initial}[width=\OutlinedArrowStep,next state=cont] {
    \pgfmoveto{\pgfpoint{\OutlinedArrowStep}{\OutlinedArrowWidth/2}}
    \pgfpathlineto{\pgfpoint{0.3\pgflinewidth}{\OutlinedArrowWidth/2}}
    \pgfpathlineto{\pgfpoint{0.3\pgflinewidth}{-\OutlinedArrowWidth/2}}
    \pgfpathlineto{\pgfpoint{1pt}{-\OutlinedArrowWidth/2}}
    \pgfcoordinate{lastup}{\pgfpoint{1pt}{\OutlinedArrowWidth/2}}
    \pgfcoordinate{lastdown}{\pgfpoint{1pt}{-\OutlinedArrowWidth/2}}
    \xdef\marmotarrowstart{0}
  }
  \state{cont}[width=\OutlinedArrowStep]{
    \ifdim\pgfdecoratedremainingdistance>\OutlinedArrowLength% continue the outlined path
     \pgfmoveto{\pgfpointanchor{lastup}{center}}
     \pgfpathlineto{\pgfpoint{\OutlinedArrowStep}{\OutlinedArrowWidth/2}}
     \pgfcoordinate{lastup}{\pgfpoint{\OutlinedArrowStep}{\OutlinedArrowWidth/2}}
     \pgfmoveto{\pgfpointanchor{lastdown}{center}}
     \pgfpathlineto{\pgfpoint{\OutlinedArrowStep}{-\OutlinedArrowWidth/2}}
     \pgfcoordinate{lastdown}{\pgfpoint{\OutlinedArrowStep}{-\OutlinedArrowWidth/2}}
    \else
     \ifnum\marmotarrowstart=0% draw the arrow head
     \pgfmoveto{\pgfpointadd{\pgfpointanchor{lastup}{center}}{\pgfpoint{-0.5\pgflinewidth}{0}}}
     \pgflineto{\pgfpoint{-0.5\pgflinewidth}{\OutlinedArrowWidth}}
     \pgflineto{\pgfpointadd{\pgfpointdecoratedpathlast}{\pgfpoint{-0.5\pgflinewidth}{0}}}
     \pgflineto{\pgfpoint{-0.5\pgflinewidth}{-\OutlinedArrowWidth}}
     \pgflineto{\pgfpointadd{\pgfpointanchor{lastdown}{center}}{\pgfpoint{-0.5\pgflinewidth}{0}}}
     \xdef\marmotarrowstart{1}
     \else
     \fi
    \fi%
  }
  \state{final}[width=5pt]
  { % perhaps unnecessary but doesn't hurt either
    \pgfmoveto{\pgfpointdecoratedpathlast}
  }
}
\begin{document}


\begin{tikzpicture}[decoration=outlined arrow,font=\sffamily]
    \path (0,0) node[draw] (A) {A}
          (0,4) node[draw] (B) {B}
          (4,0) node[draw] (C) {C}
          (4,4) node[draw] (D) {D}
          ;
  \fill[green] (-1,0.5) rectangle (2.5,1.5);
  \draw(A.north west) -- (D.south east); 
  \draw[decorate,blue,opacity=0.5] (C) to (D);
  \draw[decorate,red,opacity=0.5,line width=2pt,outlined arrow length=10pt] (A) to (B);
  \draw[decorate,outlined arrow length=15pt] (A.east) to[out=0,in=-180] (D.west);
  \fill[decoration={text along path, text={~here is some text inside an arrow},
  raise=-2.5pt},decorate]
  (A.east) to[out=0,in=-180] (D.west);
\end{tikzpicture}  
\end{document}

enter image description here

This works for straight arrows and for curved arrows.

POSSIBLE IMPROVEMENTS: One may use the open arrow heads from the arrows.meta library and bending.

ORIGINAL ANSWER: There are already excellent posts on drawing outlined (or, more generally, two-colored) arrows, so you could just use them here. Then you can use opacity in the usual way.

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{calc,shapes.arrows}
\usetikzlibrary{arrows.meta}
\tikzset{
  my fat arrow/.style args={width #1 line width #2}{
    -{Triangle[length=#1,width={3*#1}]},line width=#1,, % outer arrow
    postaction={draw,-{Triangle[length={#1-2*#2},width={3*#1-4*sqrt(2)*#2}]},white,
    line width={#1-2*#2},shorten <=#2,shorten >=#2,opacity=1}, % second arrow
  }
}

\begin{document}

\begin{tikzpicture}
    \path (0,0) node[draw] (A) {A}
          (0,2) node[draw] (B) {B}
          (2,0) node[draw] (C) {C}
          (2,2) node[draw] (D) {D}
          ;
  \draw[my fat arrow=width 3mm line width 1pt,blue,opacity=0.5] (C) to (D);
  \draw[my fat arrow=width 3mm line width 0.9pt,red,opacity=0.5] (A) to (B);
\end{tikzpicture}

\end{document}

enter image description here

4
  • This solution is great! It works with general paths and allows tricky effects (and it teaches a few things about custom decorations). In this case, I've accepted TorbjørnT.'s solution because it's closer to what I wanted to do with node shapes (and SO only lets me accept one), but I think this solution is a great reference for the "fat arrow problem" and one that I will use in other situations. Thank you.
    – tbrk
    Commented May 18, 2018 at 4:20
  • +1 The bended arrow looks crazy. May you indicate which part of the code is needed for the bended arrow with the text inside? Commented May 18, 2018 at 4:44
  • 1
    @Dr.ManuelKuehner The lines \draw[decorate,outlined arrow length=15pt] (A.east) to[out=0,in=-180] (D.west); \fill[decoration={text along path, text={~here is some text inside an arrow}, raise=-2.5pt},decorate] (A.east) to[out=0,in=-180] (D.west);. Most likely it's possible to write this in one command with some postaction but I did not try doing this. Why is it crazy?
    – user121799
    Commented May 18, 2018 at 4:46
  • 1
    It's at least crazy to me. I could not do it with MS Office for example and here it's just a couple of lines of code in a free software that some German math professor started on his own. And then there is you, using it like a magician. I really enjoy browsing this site :). Commented May 18, 2018 at 4:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .