4

Using TikZ, how to have an arrow that automatically extends to accommodate text above and below it? Here is my code:

\documentclass{article}
\usepackage{amsmath}
\usepackage[edges]{forest}

\begin{document}
\begin{forest}
for tree={forked edge, anchor=west}
    [a, for tree={grow'=east}
        [b]
        [c]
        [d, name=out1
            [, name=in1, l sep=6pt, inner sep=-0.6pt
                [x, fork sep=0pt]
                [y, fork sep=0pt]
                [z, fork sep=0pt]
            ]
        ]
        [e]
    ]
\draw [->] (out1) -- (in1) node[midway, above] {now} node [midway, below]{united};
\end{forest}

\end{document}

enter image description here

2 Answers 2

6

If you want to automatically stretch the arrow to the size of the text of the labels above and below it, you cannot first draw the tree and only later draw the arrow, because at the time you draw the arrow, the child tree is already drawn. Instead, you somehow need to tell TikZ how long the arrow should be already before the child tree is drawn where the arrow points to.

You can do this using the following custom style connect that takes three arguments: the name of the node where the arrows is drawn from, the text for the label above the arrow, and the text for the label below the arrow. The style calculates the length of the text in the labels above and below the arrow (taking the longest as basis), adds some padding (maybe you need to change this, it is the 20pt that just happened to fit in this example) and automatically sets the l option for the relevant child node to the calculated length. It then draws the arrow and the labels.

\documentclass{article}
\usepackage{amsmath}
\usepackage[edges]{forest}

\forestset{
    connect/.style n args={3}{
        l={((max(width("#2"), width("#3"))+20)*1pt},
        append after command={
            \pgfextra{
                \draw[->] (#1) -- (\tikzlastnode) 
                    node[midway, above] {#2} 
                    node[midway, below] {#3};
            }
        }
    }
}

\begin{document}

\begin{forest}
for tree={forked edge, anchor=west}
    [a, for tree={grow'=east}
        [b]
        [c]
        [d, name=out1
            [, name=in1, l sep=6pt, inner sep=-0.6pt, connect={out1}{now}{united}
                [x, fork sep=0pt]
                [y, fork sep=0pt]
                [z, fork sep=0pt]
            ]
        ]
        [e]
    ]
\end{forest}

\begin{forest}
for tree={forked edge, anchor=west}
    [a, for tree={grow'=east}
        [b]
        [c]
        [d, name=out1
            [, name=in1, l sep=6pt, inner sep=-0.6pt, connect={out1}{now}{united in harmony}
                [x, fork sep=0pt]
                [y, fork sep=0pt]
                [z, fork sep=0pt]
            ]
        ]
        [e]
    ]
\end{forest}

\end{document}

enter image description here

5
  • 1
    +1 I was working with a similar solution, but I was using xshift (because I could not remember l or l sep) as a start. I could not make xshift work in combination with max(.... Do you know why that is? Aug 8 at 16:14
  • 1
    @hpekristiansen Indeed, I get a weird error "I do not know the key '/tikz/width" when using xshift. It looks like the calculation cannot be parsed correctly then. But I am unsure why that is. Aug 8 at 16:21
  • 1
    @hpekristiansen If you use \tikzset instead of \forestset to define the style, then it works. I needed to use \forestset here, because otherwise the l option would not be known. Aug 8 at 16:32
  • @JasperHabicht Great solution! Congratulations for your impressive knowledge on these LaTeX packages. I've been reading the forest manual, but it's not so reader friendly. I think I need a Tikz basis to better explore it. Can you see how the solution you presented here works better for problem in tex.stackexchange.com/questions/651395/… ? Another solution would be to use \xrightarrow from the amsmath package.
    – JJSilva
    Aug 9 at 11:10
  • 1
    @JJSilva I think, both solutions are valid, but if you want the arrow to automatically expand, you should use the version presented here. The other solution cannot easily be changed accordingly, because the arrows are drawn too late. Aug 9 at 11:20
2

Because you're drawing the arrow and adding after the trees have been drawn, there's no automatic way for tikz to know it needs to expand that distance for your text to fit. Assuming you want to keep that arrow outside of the forest environment, you have to set a distance yourself.

You want to use the l parameter to set the distance between levels:

\documentclass[tikz]{standalone}
\usepackage[edges]{forest}

\begin{document}
\begin{forest}
for tree={forked edge, anchor=west}
    [a, for tree={grow'=east}
        [b]
        [c]
        [d, name=out1
            [, name=in1, l=1in, inner sep=-0.6pt
                [x, fork sep=0pt]
                [y, fork sep=0pt]
                [z, fork sep=0pt]
            ]
        ]
        [e]
    ]
\draw [->] (out1) -- (in1) node[midway, above] {now} node [midway, below]{united};
\end{forest}

\end{document}

Fixed with distance between trees

See figure 22 in https://texdoc.org/serve/forest/0 for a similar example, and more explanation.

3
  • I'd observe that this solution is not automatic according to text contents. In fact, the node distance should be large enough to fit the text.
    – Sigur
    Aug 8 at 16:00
  • It's because the arrow and text he's adding aren't part of the forest environment, so it can't automatically set their distance. Sometimes there are reasons to have something outside of that environment, although if this is all he wants to do, I'm sure there's a better answer.
    – kmacinnis
    Aug 8 at 16:10
  • @kmacinnis Thanks a bunch for your contribution. Yes, that is a solution, although I'd really like to have an arrow that automatically extends according to the text above and/or below it.
    – JJSilva
    Aug 9 at 10:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.