8

I'm trying to show a transition between two trees. Right now I'm using minipages, but the spacing is all off: badly spaced trees

The trees should be closer, and ideally the arrow should be longer. Finally, for extra bonus points, I would like the circle outlines and lines associated with the red nodes to be red as well if possible, but that's not absolutely necessary.

Here's another place where I'm trying to do it, and it won't even fit on the page. I want to scale it down to where it will, and get the spacing right. The tree on the second row should be at the end of the first row: more badly spaced trees

Here's a MWE:

\documentclass{article}
\usepackage{forest, color}
\begin{document}
\begin{minipage}[c]{0.32\hsize}\flushright
  \begin{forest}
    for tree={edge=->}
    [{$+$}, circle, draw, text width=1em, text centered
      [{\color{red}$\times$}, circle, draw, text width=1em, text centered
        [{\color{red}$C_0$}, circle, draw, text width=1em, text centered]
        [{\color{red}$x$}, circle, draw, text width=1em, text centered]
      ]
      [{$\log$}, circle, draw, text width=1em, text centered
        [{$+$}, circle, draw, text width=1em, text centered
          [{$x$}, circle, draw, text width=1em, text centered]
          [{$C_1$}, circle, draw, text width=1em, text centered]
        ]
      ]
    ]
  \end{forest}
\end{minipage}
\begin{minipage}[c]{0.32\hsize}\centering
  $$\longrightarrow$$
\end{minipage}
\begin{minipage}[c]{0.32\hsize}\centering
  \begin{forest}
    for tree={edge=->}
    [{$+$}, circle, draw, text width=1em, text centered
      [{\color{red}$\sin$}, circle, draw, text width=1em, text centered
        [{\color{red}$x$}, circle, draw, text width=1em, text centered]
      ]
      [{$\log$}, circle, draw, text width=1em, text centered
        [{$+$}, circle, draw, text width=1em, text centered
          [{$x$}, circle, draw, text width=1em, text centered]
          [{$C_1$}, circle, draw, text width=1em, text centered]
        ]
      ]
    ]
  \end{forest}
\end{minipage}

\begin{minipage}[c]{0.2\hsize}\centering
  \begin{forest}
    for tree={edge=->}
    [{$+$}, circle, draw, text width=1em, text centered
      [{\color{red}$\times$}, circle, draw, text width=1em, text centered
        [{\color{red}$C_0$}, circle, draw, text width=1em, text centered]
        [{\color{red}$x$}, circle, draw, text width=1em, text centered]
      ]
      [{$C_1$}, circle, draw, text width=1em, text centered]
    ]
    ]
  \end{forest}
\end{minipage}
\begin{minipage}[c]{0.2\hsize}\centering
  \begin{forest}
    for tree={edge=->}
    [{$+$}, circle, draw, text width=1em, text centered
      [{\color{red}$\sin$}, circle, draw, text width=1em, text centered
        [{\color{red}$x$}, circle, draw, text width=1em, text centered]
      ]
      [{$C_1$}, circle, draw, text width=1em, text centered]
    ]
  \end{forest}
\end{minipage}
\begin{minipage}[c]{0.2\hsize}\centering
  $$\longrightarrow$$
\end{minipage}
\begin{minipage}[c]{0.2\hsize}\centering
  \begin{forest}
    for tree={edge=->}
    [{$+$}, circle, draw, text width=1em, text centered
      [{\color{red}$\sin$}, circle, draw, text width=1em, text centered
        [{\color{red}$x$}, circle, draw, text width=1em, text centered]
      ]
      [{$C_1$}, circle, draw, text width=1em, text centered]
    ]
    ]
  \end{forest}
\end{minipage}
\begin{minipage}[c]{0.2\hsize}\centering
  \begin{forest}
    for tree={edge=->}
    [{$+$}, circle, draw, text width=1em, text centered
      [{\color{red}$\times$}, circle, draw, text width=1em, text centered
        [{\color{red}$C_0$}, circle, draw, text width=1em, text centered]
        [{\color{red}$x$}, circle, draw, text width=1em, text centered]
      ]
      [{$C_1$}, circle, draw, text width=1em, text centered]
    ]
    ]

  \end{forest}
\end{minipage}
\end{document}

And here's what it compiles to. Note that the 4-tree diagram works better here because the margins are wider, but for my poster I need to be able to scale the diagram down since it won't fit in the column at that size:

enter image description here

  • Instead of posting a large code fragment, can you turn this into a smaller self-contained document. The principles of placement will not change with more code. – Alan Munn Jul 21 '16 at 3:17
  • @AlanMunn I'm on it! Gimme a few minutes – Elliot Gorokhovsky Jul 21 '16 at 3:17
  • @AlanMunn Done! – Elliot Gorokhovsky Jul 21 '16 at 3:24
  • would you consider using a single tikzpicture with both trees and the arrow (as an edge or a node with \rightarrow) instead of two separate minipage-wrapped pictures? – Bordaigorl Jul 21 '16 at 9:06
  • @bordaigorl that would actually be preferable; I would have to use the tree library instead of forest, though. – Elliot Gorokhovsky Jul 21 '16 at 15:03
3

It is most unwise to next tikzpicture environments. Since a forest environment is simply a wrapper for a tikzpicture, it is therefore most unwise to put forest environments inside \node{} within tikzpictures.

It is, however, possible to put everything within a single forest environment.

First, though, a variation on Bordaigorl's alternative solution, which just tidies up the style a bit and automatises things a bit more.

This changes the circles style to add the $...$ wrapper automatically, together with a \strut for more even circle sizes, and automatically sets the width of each node to be sufficiently large to accommodate the widest node in the tree. This avoids needing to fiddle with the value of text width to get the sizing right.

I've also specified the mid anchor so that nodes are aligned on the baseline, which is probably what you want.

I then define a new style, colour me=<colour> which colours all node borders, contents and edges in the specified colour for the current node's sub-tree. This means that unless you later change the colouring, the current node and all its descendants will be coloured in the specified way.

\documentclass{article}
\usepackage{forest}
% Code  from Bordaigorl's answer at https://tex.stackexchange.com/a/320421/, based on Alan Munn's answer at https://tex.stackexchange.com/a/320316/, based on René G's question at https://tex.stackexchange.com/q/320312/
\begin{document}
\forestset{%
  declare dimen register=circles width,
  circles width'=0pt,
  circles/.style={%
    for tree={%
      circle,
      draw,
      text centered,
      edge=->,
      anchor=mid,
      delay={%
        content/.wrap value=\strut$##1$,
      }
    },
    before typesetting nodes={%
      circles width/.max={width("\foresteoption{content}")}{tree},
      delay={%
        for tree={%
          text width/.register=circles width,
        },
      },
    },
  },
  colour me/.style={%
    for tree={%
      +edge=#1,
      text=#1,
      draw=#1,
    },
  },
}
%
\scalebox{.5}{%
\begin{forest}
  circles
  [+
    [\times, colour me=red
      [C_0]
      [x]
    ]
    [\log
      [+
        [x]
        [C_1]
      ]
    ]
  ]
\end{forest}
%
\raisebox{2cm}{$\longrightarrow$\quad}
%
\begin{forest}
  circles
  [+
    [\sin, colour me=red
      [x]
    ]
    [\log
      [+
        [x]
        [C_1]
      ]
    ]
  ]
\end{forest}%
}
\end{document}

nicer nodes?

Getting a bit more complex, we can include both trees inside a single forest environment by using a phantom root.

To avoid complications with the phantom, we adjust the changes made to the content of nodes by circles:

  delay={%
    if={strequal(content(),"")&&(level()==0)}{}{%
      content/.wrap value=\strut$##1$,
    }
  }

This won't change anything if the root has content, but if the root has no content, it won't try to alter it.

We can then write our trees as follows

\begin{forest}
  circles
  [, phantom, for children={fit=band}
    [+
      [\times, colour me=red
        [C_0]
        [x]
      ]
      [\log
        [+
          [x]
          [C_1]
        ]
      ]
    ]
% \raisebox{2cm}{$\longrightarrow$\quad}
    [+
      [\sin, colour me=red
        [x]
      ]
      [\log
        [+
          [x]
          [C_1]
        ]
      ]
    ]
  ]
\end{forest}

trees with phantom

Obviously we need to put the arrow back in. We can use the calc and fit TikZ libraries to good effect here, modifying our phantom root node a bit:

  [, phantom, for children={fit=band}, s sep+=5mm,
    before drawing tree={%
      tikz+={%
        \node (a) [inner sep=0pt, fit=(!1) (!1L) (!1F)] {};
        \node (b) [inner sep=0pt, fit=(!l) (!lL) (!lF)] {};
        \node [anchor=center] at ($(a.east)!1/2!(b.west)$) {$\longrightarrow\quad$};
      },
    },

to produce

replace arrow

which is better. However, it is a bit of a pain to have to do it this way.

It would be nicer if we didn't need to specify the phantom root explicitly and if we could just add the arrow by specifying say, the node the arrow should be drawn from (or to).

We'll deal with the arrows first by adding the following to \forestset:

  declare boolean={made room}{0},
  arrow to/.style={%
    for parent={%
      if made room={}{%
        for children={fit=band},
        s sep+=5mm,
        made room,
      },
    },
    before drawing tree={%
      tikz+={%
        \node (a) [inner sep=0pt, fit=() (!L) (!F)] {};
        \node (b) [inner sep=0pt, fit=(!#1) (!#1L) (!#1F)] {};
        \node [anchor=center] at ($(a.east)!1/2!(b.west)$) {$\longrightarrow\quad$};
      },
    },
  },
  arrow to/.default=n,

This allows us to specify our phantom root simply with

  [, phantom

and we can create the arrow by adding our style to the first child

    [+, arrow to

This will use n by default, for the next child. If we wanted something else, we could specify it. (I'm not sure what else would make sense, but who knows?)

If you need arrows in the other direction, obviously you could add an arrow from style along similar lines.

We add

  arrow symbol/.store in=\myarrowsymbol,
  arrow symbol=\longrightarrow,

so that we can use myarrowsymbol in the node rather than hard-coding \longrightarrow\quad. We can then add

, arrow symbol=\longrightarrow\quad

to the first child to add the spacing adjustment relevant for this particular case.

Doing away with the need to specifying the phantom root is a bit more involved. The easiest approach is probably to use the environ package to define a new environment as follows.

\environbodyname\circlestreebody
\bracketset{action character=@}
\NewEnviron{circlestree}{%
  \forest
  circles
  [, phantom @\circlestreebody]
  \endforest
}

We can then write our tree simply as

\begin{circlestree}
  [+, arrow to, arrow symbol=\longrightarrow\quad
    [\times, colour me=red
      [C_0]
      [x]
    ]
    [\log
      [+
        [x]
        [C_1]
      ]
    ]
  ]
  [+
    [\sin, colour me=red
      [x]
    ]
    [\log
      [+
        [x]
        [C_1]
      ]
    ]
  ]
\end{circlestree}

to produce the output shown above.

The 4 tree case then becomes

\begin{circlestree}
  [+
    [\times, colour me=red
      [C_0]
      [x]
    ]
    [C_1]
  ]
  [+, arrow to
    [\sin, colour me=red
      [x]
    ]
    [C_1]
  ]
  [+
    [\sin, colour me=red
      [x]
    ]
    [C_1]
  ]
  [+
    [\times, colour me=red
      [C_0]
      [x]
    ]
    [C_1]
  ]
\end{circlestree}

and produces

4 tree case

I'm not sure what spacing you want, but you can tweak the s sep+ as you wish, of course.

Complete code:

\documentclass[border=10pt,multi,tikz]{standalone}
\usepackage{forest}
\usetikzlibrary{calc,fit}
\environbodyname\circlestreebody
\bracketset{action character=@}
\NewEnviron{circlestree}{%
  \forest
  circles
  [, phantom @\circlestreebody]
  \endforest
}
% Code  from Bordaigorl's answer at https://tex.stackexchange.com/a/320421/, based on Alan Munn's answer at https://tex.stackexchange.com/a/320316/, based on René G's question at https://tex.stackexchange.com/q/320312/
\begin{document}
\forestset{%
  declare dimen register=circles width,
  circles width'=0pt,
  declare boolean={made room}{0},
  circles/.style={%
    for tree={%
      circle,
      draw,
      text centered,
      edge=->,
      anchor=mid,
      delay={%
        if={strequal(content(),"")&&(level()==0)}{}{%
          content/.wrap value=\strut$##1$,
        }
      }
    },
    before typesetting nodes={%
      circles width/.max={width("\foresteoption{content}")}{tree},
      delay={%
        for tree={%
          text width/.register=circles width,
        },
      },
    },
  },
  colour me/.style={%
    for tree={%
      +edge=#1,
      text=#1,
      draw=#1,
    },
  },
  arrow to/.style={%
    for parent={%
      if made room={}{%
        for children={fit=band},
        s sep+=5mm,
        made room,
      },
    },
    before drawing tree={%
      tikz+={%
        \node (a) [inner sep=0pt, fit=() (!L) (!F)] {};
        \node (b) [inner sep=0pt, fit=(!#1) (!#1L) (!#1F)] {};
        \node [anchor=center] at ($(a.east)!1/2!(b.west)$) {$\myarrowsymbol$};
      },
    },
  },
  arrow to/.default=n,
  arrow symbol/.store in=\myarrowsymbol,
  arrow symbol=\longrightarrow,
}

\begin{circlestree}
  [+, arrow to, arrow symbol=\longrightarrow\quad
    [\times, colour me=red
      [C_0]
      [x]
    ]
    [\log
      [+
        [x]
        [C_1]
      ]
    ]
  ]
  [+
    [\sin, colour me=red
      [x]
    ]
    [\log
      [+
        [x]
        [C_1]
      ]
    ]
  ]
\end{circlestree}

\begin{circlestree}
  [+
    [\times, colour me=red
      [C_0]
      [x]
    ]
    [C_1]
  ]
  [+, arrow to
    [\sin, colour me=red
      [x]
    ]
    [C_1]
  ]
  [+
    [\sin, colour me=red
      [x]
    ]
    [C_1]
  ]
  [+
    [\times, colour me=red
      [C_0]
      [x]
    ]
    [C_1]
  ]
\end{circlestree}
\end{document}
7

Instead of using minipage use the adjustbox package, which gives you much simpler control over the spacing. Also, you can save yourself a lot of typing by defining a global style for your trees:

\documentclass{article}
\usepackage{forest, color}
\usepackage{adjustbox}
\begin{document}
% Globally set style for your trees 
\forestset{circles/.style={for tree={circle,draw, text width =1em,text centered,edge=->}}}
\begin{adjustbox}{valign=m}
  \begin{forest}
    circles
    [{$+$}
      [{$\times$}, red
        [{$C_0$}, red]
        [{$x$}, red]
      ]
      [{$\log$}
        [{$+$}
          [{$x$}]
          [{$C_1$}]
        ]
      ]
    ]
  \end{forest}
\end{adjustbox}
\begin{adjustbox}{valign=m,margin=5pt}
% Use tikz for your arrow too
\tikz\draw[thick,->] (0,0) -- (1,0);
\end{adjustbox}
\begin{adjustbox}{valign=m}
  \begin{forest}
    circles
    [{$+$}
      [{$\sin$}, red
        [{$x$},red]
      ]
      [{$\log$}
        [{$+$}
          [{$x$}]
          [{$C_1$}]
        ]
      ]
    ]
  \end{forest}
\end{adjustbox}
\end{document}

output of code

  • Is the adjustbox around the arrow necessary? – egreg Jul 21 '16 at 8:06
  • @egreg The adjustbox around the arrow isn't strictly necessary, but it allows a quick way of adjusting the spacing of the arrow relative to the trees. – Alan Munn Jul 21 '16 at 14:44
4

One approach is to avoid minipages and simply wrap everything in a tikzpicture environment.

Although this works just fine in this case, you should be careful in using this technique in other instances since nesting tikz pictures is generally discouraged and may cause strange errors. In the cases where it works though, it offers a very simple and intuitive solution.

The two forests become the contents of two nodes and the arrow is typeset in a node placed between them. Taking @Alan Munn's simplified version for the first two forests:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usepackage{forest, color}

\begin{document}
\forestset{circles/.style={for tree={circle,draw, text width =1em,text centered,edge=->}}}
\begin{tikzpicture}
    \node (a) {
      \begin{forest}
        circles
        [{$+$}
          [{$\times$}, red
            [{$C_0$}, red]
            [{$x$}, red]
          ]
          [{$\log$}
            [{$+$}
              [{$x$}]
              [{$C_1$}]
            ]
          ]
        ]
      \end{forest}
    };
    \node[left=of a] (b) {
      \begin{forest}
        circles
        [{$+$}
          [{$\sin$}, red
            [{$x$},red]
          ]
          [{$\log$}
            [{$+$}
              [{$x$}]
              [{$C_1$}]
            ]
          ]
        ]
      \end{forest}
    };
    \path (a) -- node{$\longrightarrow$} (b);
\end{tikzpicture}
\end{document}

I believe this gives you a very flexible solution: you can tweak every aspect of the spacing very intuitively.

  • I like this solution, but you should have at least used my cleaned up code for the trees. Feel free to take it. – Alan Munn Jul 21 '16 at 16:10
  • @AlanMunn Nesting TikZ pictures is never a good solution. This is really not a good way to go even if you are lucky and it works in this particular case. – cfr Jul 21 '16 at 22:32
  • 1
    @cfr That's what I thought, (original comment deleted from the question) but this does seem to work. We actually have quite a few questions on the issue: Problem with overlay when a tikzpicture is inside another tikzpicture and tikz : how to refer to a node, in nested nodes and How to typeset a TikZ picture inside a node? and links there. So René should prefer my solution (unless you have a better one?) – Alan Munn Jul 21 '16 at 22:37
  • @AlanMunn I have a different one. Whether it is better is not for me to say. (That is, I prefer it, but I'm biased and your kilometres may vary :-).) I pinched the cleaned up code, of course. – cfr Jul 22 '16 at 2:27
  • @AlanMunn I don't know which original comment you meant was deleted. But maybe this doesn't matter since you (?) deleted it? – cfr Jul 22 '16 at 3:09
1

Another solution that allows fine grained tweaking of spacing, while allowing easy use of \scalebox if needed. The idea is that the forest environment already gives you a box, you can put the three boxes one after the other on the same line and adjust the baseline as needed. Since the two forests have different width some extra space (a \quad) after the arrow is needed to give a balanced spacing.

\documentclass{article}
\usepackage{forest, color}

\begin{document}
\forestset{circles/.style={for tree={circle,draw, text width =1em,text centered,edge=->}}}
%
\scalebox{.5}{
\begin{forest}
  circles
  [{$+$}
    [{$\times$}, red
      [{$C_0$}, red]
      [{$x$}, red]
    ]
    [{$\log$}
      [{$+$}
        [{$x$}]
        [{$C_1$}]
      ]
    ]
  ]
\end{forest}
%
\raisebox{2cm}{$\longrightarrow$\quad}
%
\begin{forest}
  circles
  [{$+$}
    [{$\sin$}, red
      [{$x$},red]
    ]
    [{$\log$}
      [{$+$}
        [{$x$}]
        [{$C_1$}]
      ]
    ]
  ]
\end{forest}
}
\end{document}

the scalebox is there only for illustration.

0

I'm hoping someone will post a better answer, but for now, I've found replacing

\begin{minipage}[c]{0.32\hsize}\centering
  $$\longrightarrow$$
\end{minipage}

with

\begin{minipage}[c]{0.05\hsize}\centering
  $$\longrightarrow$$
\end{minipage}

However, this is really just a hack, and also doesn't handle scaling, so while it worked in this case, it won't work for larger trees.

  • 4
    A very complicated way to do $\longrightarrow$ – egreg Jul 21 '16 at 8:03

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.