14

Problem

In TikZ, one can control the growing direction of a branch (and its sub-branches) by using the grow=<direction> key. In forest, however, the grow key only controls the growing direction of the sub-branches but not their parent branch.

How do I make branches on the same level in a forest tree grow in different directions? In the following MWE, for instance, how can I get child 2 to extend horizontally from root in it does in the TikZ tree?

MWE

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

\begin{document}
\texttt{grow} used in a Ti\textit{k}Z tree

\begin{tikzpicture}
  \node{root}
    child{node{child 1}}
    child[grow=east]{node{child 2}
      child child child
    }
  ;
\end{tikzpicture}

\vskip20pt
\texttt{grow} used in a Forest tree

\begin{forest}
  [root
    [child 1]
    [child 2,grow=east
      [][][]
    ]
  ]
\end{forest}
\end{document}

enter image description here

3 Answers 3

12

The desired effect can be achieved by moving the subtree of node child 2 by hand. I can see two ways of doing this.

  1. Change the relative position (l and s) of child 2 just before stage compute xy. Note that child 2's l and s are coordinates in the root's ls-coordinate system (see the documentation for options l and s). Since these coordinates are relative to the parent, it is only necessary to change them for the subtree's root, child 2.

  2. Change the absolute position (x and y) of all nodes in child 2's subtree just before stage draw tree.

    A note. In the example, y is adjusted so that child 2 is vertically aligned to root: since this is achieved by calculating the difference between root's and child 2's y, node child 2 must be moved after its descendants.

Note that in both approaches, forest first positions the root's children in the default, -90 degree growth direction, which can in principle influence both the position (note that child 1 is left of the root) and internal structure of the subtrees.

\documentclass{article}
\usepackage{forest}

\begin{document}

% the ls way
\begin{forest}
  [root
    [child 1
      [][][]
    ]
    [child 2, for tree={grow=0},
      before computing xy={l=0,s=2cm}
      [][][]
    ]
  ]
\end{forest}

% the xy way
\begin{forest}
  [root
    [child 1
      [][][]
    ]
    [child 2, for tree={grow=0},
      before drawing tree={for descendants=
        {x+=1cm, y+=y("!r")-y("!r2")},
        x+=1cm, y+=y("!r")-y("!r2")
      }
      [][][]
    ]
  ]
\end{forest}

\end{document}

To be honest, I don't find any of the two approaches elegant. My first thought was to embed two forest environments, one for each subtree, into a tikzpicture environment. Since forest works by spitting out tikz code, I reasoned it should be possible to position the root nodes manually using tikz's mechanisims (at,right of, etc.). (Note that begin draw and end draw should be emptied for this to have any chance of success.) However, the result was not as expected ... I'll investigate the reasons and try to fix the issue in some future version of forest.

5
  • 1
    So I was right here when I said that it seemed to me forest is not (currently) really designed to do this? This is useful as an example. (I find the documentation too difficult to follow when it lacks examples, I'm afraid, and so end up here...) I find it difficult to understand the 'stages' forest uses and how to use them to effectively shape a tree. (So I pretty much give up if something needs this!) It is good to hear that this may change in future versions. Thanks for all your work - forest is fantastic and I only wish I understood it!
    – cfr
    Jul 18, 2014 at 22:19
  • Is there a better way of drawing the tree in the question I linked to? I tried using before computing xy but I ran into trouble with the paths drawn between levels. I got it to look perfect except for a vertical line drawn down the middle of the root node...
    – cfr
    Jul 18, 2014 at 22:57
  • 1
    Any news on this?
    – cfr
    Nov 11, 2018 at 0:43
  • @cfr: 8 years later...I posted a more general solution. Thoughts?
    – Sandy G
    May 5, 2022 at 2:41
  • @SandyG See comment on your answer. Maybe Sašo has thoughts? I don't see a way to provide a general solution?
    – cfr
    Jul 25, 2023 at 1:06
15
+200

This is an old question, but since this functionality is not built into forest, I thought I would offer a general solution.

A new option multiple directions is defined in a \forestset. The basic idea is that for each direction that you want a subtree to grow, a duplicate root node is created and placed at the location of the actual root node. Then each subtree can grow in its own direction since it technically has its own parent.

The same \forestset defines grow subtree and grow' subtree which require a direction (angle or compass direction). These follow the same rules as grow= and grow'=, namely that grow' subtree reverses the order of the nodes in the subtree. These are nothing special: grow subtree= is just an alternate syntax for for tree={grow=}

The only formatting change from the standard forest syntax is that an empty node for each direction must be created. Here is an example:

enter image description here

The code for this tree is:

\begin{forest}
    multiple directions
    [root 
      [ 
        [child 1 [a][b]]
      ]
      [, grow subtree=150
        [child 2 [c][d][e]]
      ]
      [, grow' subtree=30
        [child 3 [f][g]]
      ]
    ]
\end{forest}

The option multiple directions also acts as a for tree, and accepts options as seen in the next example.

Note: forked edge requires \useforestlibrary{edges}.

enter image description here

The code is:

\begin{forest}
    multiple directions={minimum height=4ex, anchor=center, forked edge}
    [R 
      [, grow' subtree=east
        [1 [a][b]]
        [2 [c][d]]
        [3 [e][f]]]
      [, grow subtree=west
        [4 [g][h]]
        [5 [i][j]]
        [6 [k][l]]
      ]
    ]
\end{forest}

An additional example with the nodes drawn:

enter image description here

\begin{forest}
    multiple directions={minimum width=2.5em, anchor=center, circle, draw}
    [c
      [, grow' subtree=north
        [a[a1][a2]]
        [b[b1][b2]]]
      [ 
        [d[d1][d2]]
        [e[e1][e2]]]
    ]
\end{forest}

Note that the original root node is a phantom, so if you want to change node appearance by level, you need to increase the level by 1. Thus the (visible) root node (which is actually a copy) is at level 1 and its children are at level 2. For example:

enter image description here

\begin{forest}
    multiple directions={
        text width=20mm,
        if level=1{fill=gray!80}{fill=gray!10},
        if level=2{fill=gray!40}{},
        forked edge,
        s sep=5mm, l sep=5mm,
        fork sep=2.5mm
    }
    [Root
      [, grow subtree=west
        [West of root[Far west]]
      ]
      [
        [A[B]]
        [C[D[E]]]
        [F[G]]
      ]
      [, grow subtree=east
        [East of root[Far east]]
      ]
    ]
\end{forest}

It is also possible to use multiple directions on a subtree:

enter image description here

\begin{forest}
    multiple directions={anchor=center}, forked edges
    [0
      [, grow subtree=west
        [1[1a][1b]][2[2a][2b]]]
      [, grow' subtree=east
        [3[3a][3b]][4[4a][4b, multiple directions, phantom=false
            [, grow' subtree=east[x[x1][x2]]]
            [, grow subtree=south[y[y1][y2]]]
        ]]]
    ]
\end{forest}

Note that because of the way multiple directions hides the root node, it is necessary to set phantom=false for this usage.

Here is the complete code including some of the examples:

\documentclass{article}

\usepackage{forest}
\useforestlibrary{edges}

\forestset{multiple directions/.style={for tree={#1}, phantom, for relative level=1{no edge, delay={!c.content/.pgfmath=content("!u")}, before computing xy={l=0,s=0}}},
    multiple directions/.default={},
    grow subtree/.style={for tree={grow=#1}}, 
    grow' subtree/.style={for tree={grow'=#1}}}

\begin{document}

\begin{forest}
    multiple directions
    [root 
      [ 
        [child 1 [a][b]]
      ]
      [, grow subtree=150
        [child 2 [c][d][e]]
      ]
      [, grow' subtree=30
        [child 3 [f][g]]
      ]
    ]
\end{forest}

\vspace{1cm}
\begin{forest}
    multiple directions={minimum height=4ex, anchor=center, forked edge}
    [R 
      [, grow' subtree=east
        [1 [a][b]]
        [2 [c][d]]
        [3 [e][f]]]
      [, grow subtree=west
        [4 [g][h]]
        [5 [i][j]]
        [6 [k][l]]
      ]
    ]
\end{forest}

\vspace{1cm}
\begin{forest}
    multiple directions={minimum width=2.5em, anchor=center, circle, draw}
    [c
      [, grow' subtree=north
        [a[a1][a2]]
        [b[b1][b2]]]
      [ 
        [d[d1][d2]]
        [e[e1][e2]]]
    ]
\end{forest}

\vspace{1cm}
\begin{forest}
    multiple directions={anchor=center}, forked edges
    [0
      [, grow subtree=west
        [1[1a][1b]][2[2a][2b]]]
      [, grow' subtree=east
        [3[3a][3b]][4[4a][4b, multiple directions, phantom=false
            [, grow' subtree=east[x[x1][x2]]]
            [, grow subtree=south[y[y1][y2]]]
        ]]]
    ]
\end{forest}

\end{document}
2
  • This is neat, but I'm not sure I'd call it a 'general' solution because you effectively override various features of forest which prevent overlaps and ensure neat packing. That's not a criticism of your code - I doubt a general solution is possible here. If you were going to use this a lot, you could modify the code to make it a bit speedier. Note that !c.content seems pointless: why not just content? content/.option=!u.content would be simpler and faster. l'=0pt, s'=0pt would be quicker than l=0,s=0. For speed, avoid pgfmath whenever possible! :)
    – cfr
    Jul 25, 2023 at 1:03
  • I also don't think we'll ever have a fully general solution, but the idea which this answer embodies is sound. Look at tex.stackexchange.com/q/691576/16819 for some problems that can arise, and how to address them. Jul 25, 2023 at 4:46
2

No solution to this problem can be perfectly general. Automating the style is not necessarily better (or worse) than Sandy G's excellent manual structuring of the trees. This answer aims to be more automatic, slightly more robust and to avoid minor formatting issues, but there is no guarantee it will not eat your chocolate or steal your sock.

Caveat emptor


This is a modification of Sandy G's excellent answer. The fundamental idea is the same, with the following changes.

  1. The style is more automated.
  2. It avoids typesetting multiple copies of the same node content, which is responsible for the fake-bold effect in nodes sprouting subtrees in multiple directions. (But there are easier ways by far if this is your only concern.)
  3. It tries to be potentially slightly more robust by integrating one of the changes in Sašo Živanović's yet-more-excellent answer. (This does not mean it will work with folder style - I haven't even tried it!)

The output should be the same as using Sandy G's answer (modulo apparent font weight). Only the specification of the trees differs.

output sans fake bold at diverging growth points

Only two keys are really of interest in specifying the trees.

subtree grow=<growth direction>
subtree grow'=<growth direction>

These are intended to be precisely analogous to grow subtree and grow subtree'.

The main difference is that this is all we need to do. There is no need to insert additional nodes or to specify any style for the tree as a whole. subtree grow and subtree grow' activate the required style, which inserts any additional nodes required.

Taking Sandy G's examples for demonstration purposes, the first can be specified as simply

\begin{forest}
  [root
    [child 1 
      [a][b]
    ]
    [child 2, subtree grow=150
      [c][d][e]
    ]
    [child 3, subtree grow'=30
      [f][g]
    ]
  ]
\end{forest}

There is, of course, a downside to this simplicity. In some trees, we have to type more precisely because we don't have an additional fake node to set the growth direction for an entire subtree. Instead, we must specify the growth direction for every child which should not grow in the default direction determined by the parent. Hence, Sandy G's second tree requires six uses of subtree grow/subtree grow', whereas it required only two of grow subtree/grow subtree'.

[If the previous example was the swings, I assume we're on the roundabouts by now.]

\begin{forest}
  for tree={minimum height=4ex, anchor=center},
  forked edges,
  [R 
    [1, subtree grow'=east 
      [a][b]
    ]
    [2, subtree grow'=east 
      [c][d]
    ]
    [3, subtree grow'=east 
      [e][f]
    ]
    [4, subtree grow=west 
      [g][h]
    ]
    [5, subtree grow=west 
      [i][j]
    ]
    [6, subtree grow=west 
      [k][l]
    ]
  ]
\end{forest}

For the third example, we need two uses of subtree grow', but since we don't have to insert extra nodes or add to the preamble, this is still a bit more concise.

\begin{forest}
  for tree={minimum width=2.5em, anchor=center, circle, draw},
  [c
    [a, subtree grow'=north
      [a1][a2]
    ]
    [b, subtree grow'=north
      [b1][b2]
    ]
    [d
      [d1][d2]
    ]
    [e
      [e1][e2]
    ]
  ]
\end{forest}

Similarly, the fourth example involves a good balance of swings and roundabouts, but the payoff for multiple growth specifications is a clearer structure (at least at the level of input syntax - the actual tree is a different matter).

\begin{forest}
  for tree={anchor=center}, 
  forked edges,
  [0
    [1, subtree grow=west
      [1a][1b]
    ]
    [2, subtree grow=west
      [2a][2b]
    ]
    [3, subtree grow'=east
      [3a][3b]
    ]
    [4, subtree grow'=east
      [4a]
      [4b
        [x, subtree grow'=east[x1][x2]]
        [y, subtree grow=south[y1][y2]]
      ]
    ]
  ]
\end{forest}

Under the hood, both subtree grow and subtree grow' call a style called wild branching.

  subtree grow/.style={%
    wild branching={grow=#1},
  }, 
  subtree grow'/.style={%
    wild branching={grow'=#1},
  },

wild branching does nothing you couldn't do for yourself with Sandy G's code and a couple of tweaks. It uses a variety of custom Forest options and registers (for those unfamiliar with this terminology an 'option' is a setting for a node in a tree, while a 'register' applies to the entire tree).

Options:

  declare boolean={wild children}{0},
  declare boolean={wild child}{0},
  declare keylist={tame ones}{},
  declare keylist={wild siblings}{},
  declare boolean={wild leader}{0},

The final argument is the initial value for all nodes in all trees.

Registers:

  declare boolean register={wild tree},
  wild tree=0,

We also need a few simple styles:

  append me/.style={append={!id=#1},do dynamics},
  prepend me/.style={prepend={!id=#1},do dynamics},
  wild phantom/.style={%
    node options/.option=!u.node options,
    content/.process={Ow{!u.content}{\phantom{##1}}},
    child anchor/.option=!1.child anchor,
    anchor/.option=!1.anchor,
    parent anchor/.option=!1.parent anchor,
    opacity=0,
    no edge,
  },

and I used a custom step

  define long step={wild children by growth}{}{%
    sort by={>O{grow}},sort'={filter={children}{>O{wild child}}}
  },

I split up the styling into parts as it was getting a bit long. wild branching calls do tameness and do wildness as appropriate.

do tameness inserts an additional node for subtrees growing in the default direction, when one of their siblings uses subtree grow or subtree grow'.

  do tameness/.style={%
    where wild children={%
      tempboola=0,
      for children={%
        if wild child={}{%
          if tempboola={%
            !u.tame ones+/.option=id,
          }{%
            tempboola,
            replace by={%
              [,
                append,
                delay={%
                  if={>O_={!u.tame ones}{}}{}{%
                    split option={!u.tame ones}{,}{append me},
                  },
                  wild phantom,
                },
              ]%                  
            },
          },
        },
      },            
    }{},  
  },

do wildness figures out when to create a new additional node for a new growth direction and when the subtree should be added to an existing addition. This is where the custom step defined above gets used: it ensures we visit the siblings in order of their directions of growth, which makes it easier to compile lists of which ones belong together.

  do wildness/.style={%
    where wild children={%
      tempcounta'=9999,
      for wild children by growth={%
        if={>OR= {grow}{tempcounta} }
        {%
          tempkeylista'=,
          for children={%
            tempkeylista+/.option=id,
          },
          for nodewalk={%
            until={>O{wild leader}}{next},
            if wild siblings={}{%
              wild siblings/.register=tempkeylista
            }{%
              wild siblings+/.register=tempkeylista
            }%
          }{%
          },
          before packing={remove},
        }{%
          wild leader,
          tempcounta/.option=grow,
          before packing={%
            if wild siblings={}{}{%
              split option={wild siblings}{,}{prepend me},
            },
          },
        },
      },
    }{},  
  },

This method creates more nodes than Sandy G's, but the extras get deleted before the tree is packed. Nonetheless, the structure of the final tree differs because wild branching alters l and s before the parent node is packed. Theoretically, this should lead to fewer situations requiring manual intervention. But theory is not, as we all know, practice.

  wild branching/.style={% 
    if id=1{for tree={#1}}{%
      !u.wild children,
      delay={%
        replace by={%
          [,
            wild child,
            append,
            delay={%
              wild phantom,
              for tree={#1},          
            },
          ]%
        },
      },
      if wild tree={}{%
        wild tree,
        !root.before typesetting nodes={%
          do tameness,
          do wildness,
        },
        !root.before packing={%
          delay={
            where wild children={%
              after packing node={%
                for children={l'=0pt,s'=0pt},
              },
            }{},
          },
        },
      },
    },
  },
  wild branching/.default={},

Note that subtree grow/subtree grow' aren't designed to be set for the root node. If they are, wild branching simply applies grow or grow' to the tree, without invoking anything special.

wild branching probably shouldn't have a .default setting, but for some reason I can't now remember, I've defined one.

Code:

\documentclass[a4paper,landscape]{article}
\usepackage[scale=.8]{geometry}
% ateb: https://tex.stackexchange.com/a/705635/ addaswyd o ateb Sandy G: https://tex.stackexchange.com/a/643061/
\usepackage{forest}
\useforestlibrary{edges}

\forestset{% https://tex.stackexchange.com/a/705635/
  declare boolean={wild children}{0},
  declare boolean={wild child}{0},
  declare keylist={tame ones}{},
  declare keylist={wild siblings}{},
  declare boolean={wild leader}{0},
  declare boolean register={wild tree},
  wild tree=0,
  append me/.style={append={!id=#1},do dynamics},
  prepend me/.style={prepend={!id=#1},do dynamics},
  wild phantom/.style={%
    node options/.option=!u.node options,
    content/.process={Ow{!u.content}{\phantom{##1}}},
    child anchor/.option=!1.child anchor,
    anchor/.option=!1.anchor,
    parent anchor/.option=!1.parent anchor,
    opacity=0,
    no edge,
  },
  define long step={wild children by growth}{}{%
    sort by={>O{grow}},sort'={filter={children}{>O{wild child}}}
  },
  do tameness/.style={%
    where wild children={%
      tempboola=0,
      for children={%
        if wild child={}{%
          if tempboola={%
            !u.tame ones+/.option=id,
          }{%
            tempboola,
            replace by={%
              [,
                append,
                delay={%
                  if={>O_={!u.tame ones}{}}{}{%
                    split option={!u.tame ones}{,}{append me},
                  },
                  wild phantom,
                },
              ]%                  
            },
          },
        },
      },            
    }{},  
  },
  do wildness/.style={%
    where wild children={%
      tempcounta'=9999,
      for wild children by growth={%
        if={>OR= {grow}{tempcounta} }
        {%
          tempkeylista'=,
          for children={%
            tempkeylista+/.option=id,
          },
          for nodewalk={%
            until={>O{wild leader}}{next},
            if wild siblings={}{%
              wild siblings/.register=tempkeylista
            }{%
              wild siblings+/.register=tempkeylista
            }%
          }{%
          },
          before packing={remove},
        }{%
          wild leader,
          tempcounta/.option=grow,
          before packing={%
            if wild siblings={}{}{%
              split option={wild siblings}{,}{prepend me},
            },
          },
        },
      },
    }{},  
  },
  wild branching/.style={% 
    if id=1{for tree={#1}}{%
      !u.wild children,
      delay={%
        replace by={%
          [,
            wild child,
            append,
            delay={%
              wild phantom,
              for tree={#1},          
            },
          ]%
        },
      },
      if wild tree={}{%
        wild tree,
        !root.before typesetting nodes={%
          do tameness,
          do wildness,
        },
        !root.before packing={%
          delay={
            where wild children={%
              after packing node={%
                for children={l'=0pt,s'=0pt},
              },
            }{},
          },
        },
      },
    },
  },
  wild branching/.default={},
  subtree grow/.style={%
    wild branching={grow=#1},
  }, 
  subtree grow'/.style={%
    wild branching={grow'=#1},
  },
}
\pagestyle{empty}
\begin{document}
\centering
\begin{forest}
  [root
    [child 1 
      [a][b]
    ]
    [child 2, subtree grow=150
      [c][d][e]
    ]
    [child 3, subtree grow'=30
      [f][g]
    ]
  ]
\end{forest}
\begin{forest}
  for tree={minimum height=4ex, anchor=center},
  forked edges,
  [R 
    [1, subtree grow'=east 
      [a][b]
    ]
    [2, subtree grow'=east 
      [c][d]
    ]
    [3, subtree grow'=east 
      [e][f]
    ]
    [4, subtree grow=west 
      [g][h]
    ]
    [5, subtree grow=west 
      [i][j]
    ]
    [6, subtree grow=west 
      [k][l]
    ]
  ]
\end{forest}
\begin{forest}
  for tree={minimum width=2.5em, anchor=center, circle, draw},
  [c
    [a, subtree grow'=north
      [a1][a2]
    ]
    [b, subtree grow'=north
      [b1][b2]
    ]
    [d
      [d1][d2]
    ]
    [e
      [e1][e2]
    ]
  ]
\end{forest}
\begin{forest}
  for tree={anchor=center}, 
  forked edges,
  [0
    [1, subtree grow=west
      [1a][1b]
    ]
    [2, subtree grow=west
      [2a][2b]
    ]
    [3, subtree grow'=east
      [3a][3b]
    ]
    [4, subtree grow'=east
      [4a]
      [4b
        [x, subtree grow'=east[x1][x2]]
        [y, subtree grow=south[y1][y2]]
      ]
    ]
  ]
\end{forest}

\end{document}

You must log in to answer this question.

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