7

I have a number of 'decision tree' type of charts I am trying to produce dynamically (rather than in Illustrator or OmniGraffle). Each chart contains a few hundred nodes. Because of the number of nodes, it would be particularly useful to be able to use 360 degrees of space surrounding the root node (much like a mindmap or a circular phylogenetic tree).

I don't have strong preferences about where every single node is exactly placed. The dynamic layout possibilities of LaTeX are therefore great (I don't want to place hundreds of nodes by hand). But the layout will make a lot more sense and be more aesthetically appealing if I can, at times, 'nudge' the angle between parent and child nodes.

I have seen there is a way to do what I want in TikZ with grow cyclic but I can't figure out how to incorporate that into a tree made with Forest (I am new to LaTeX, but my understanding is that Forest is TikZ-based, so my intuition is that this should be possible).

Is there a way to radially layout trees in Forest, or should I be using a different tool?

As a quick (and very simple) example, this code:

\documentclass[tikz,border=5pt]{standalone}
\usepackage{forest}

\usepackage[LGR,T1]{fontenc}           % these three lines added
\usepackage[utf8]{inputenc}             % so that greek symbols will show
\newcommand{\greek}[1]{\begingroup\fontencoding{LGR}\selectfont#1\endgroup}
                            % write greek like this -->  \greek{α}

\begin{document}
  \begin{forest}
    for tree={
      rectangle,
      rounded corners=0.3em,
      draw,
      minimum width=2.5em,
      l sep+=1.5em,
      s sep+=1em,
      anchor=center,
      grow=0,
     },
    delay={
       ,
    }
[kansas city
    [west
        [los angeles,
            [santa monica]
            [west hollywood]
        ]
        [san francisco,
            [the castro]
        ]
    ]
    [south
        [texas]
        [mexico]
    ]
    [east
        [north carolina]
        [south carolina]
    ]
    [north
        [wisconsin]
        [minnesota]
    ]
]
\end{forest}
\end{document}

Produces this image... I set grow to 0 so image would fit better here

But really, to make more sense to a person viewing, the first level nodes (east, west, etc) should be distributed radially around the root node, sort of like this extremely crude mockup ... horrid layout ... but 360 degrees

...and if any given node has 8+ children, using more than 180 degrees of arc to position those children really would make sense, IMO. For example, see the 'l' node here with 9 children:

see node with 9 children

I will also note that I am aware of calign but using it to align child nodes to anywhere over 179 degrees apart will cause a 'divide by zero' error for Package PGF. Anything over 160 degrees looks aesthetically horrible because of small angle issues that cause the node connections to lengthen significantly, but it does work up to 179 degrees. Example code snippet:

  \begin{forest}
    calign=fixed edge angles,
    calign primary angle=-89,calign secondary angle=89,
    for tree={
      rectangle,

I am okay with switching to TikZ or something else if I need to, it's just that I have sunk costs with Forest and it does otherwise seem to be ideal for my uses. (The only workaround I can think of is to make each chart actually 4 "fractional charts," where each of the four covers one quadrant, and then merge the outputs afterward. But this seems like such poor practice I figured it was time to ask here.)

7
  • 2
    Welcome to TeX.SX! Aug 2, 2018 at 4:50
  • 2
    Forest is not designed for this kind of tree. Have you considered the graph facilities of TikZ? Is LuaTeX an option?
    – cfr
    Aug 3, 2018 at 0:37
  • @marmot ???????
    – cfr
    Aug 3, 2018 at 2:25
  • 2
    @marmot Thanks. There's no real issue moving sub-trees into radial positions. It's not out-of-the-box, but it should be straightforward enough, even if you want to calculate the angles automatically based on the number of children and/or trigger the style when the number of children exceeds a threshold. That's all fine. But, doing that will lose the automated packing, so there will be no guarantee, to put it mildly, that things won't overlap or end up hugely gappy. If it were just the NESW nodes, that would be manageable, likely. Less so if it happens repeatedly in trees of hundreds of nodes.
    – cfr
    Aug 3, 2018 at 2:56
  • 2
    @marmot So, given the description of the trees in the OP's real document, it isn't clear to me that it is worth trying to concoct a Forest solution, which I suspect will always be sub-optimal. It is also worth saying that it will take a really, really, really long time to compile a tree with so many nodes. Forest is basically very slow. That might not be a lethal problem, depending on the context, but it doesn't seem worth going for a solution which is both far from satisfactory and extremely slow. Better use the Lua stuff instead.
    – cfr
    Aug 3, 2018 at 3:00

2 Answers 2

5

This answer is based on How to combine a top-down and bottom-up binary tree in one picture?

It draws five trees, one each for the north, east, south, west and northwest groups of cities. Each tree is saved using \savebox. Each tree has customized growth directions and anchors to suit its design. For example, the north tree uses grow'=north, child anchor=south, parent anchor=north, and so on for each tree. For the tree growing north west, it may be necessary to play with the anchors to get a preferred arrangement of the nodes and edges. The individual trees are then drawn using \node, and positioned using the positioning library syntax. Each tree is connected to the root node, Kansas City, using \draw commands.

This is the result:

enter image description here

This is the MWE:

% http://tex.stackexchange.com/questions/213770/how-to-combine-a-top-down-and-bottom-up-binary-tree-in-one-picture
\documentclass[12pt,crop=true,border=1cm]{standalone}

\usepackage[edges]{forest}
\usepackage[none]{hyphenat}
\usetikzlibrary{shapes,positioning,arrows.meta,calc}
\tikzset{
    mynodes/.style={inner sep=0pt,draw=none}
}

\forestset{
mytree/.style={
  forked edges,
    for tree={
    draw,
    rounded corners,
    node options={align=center},
    text width=2cm
    }
  }
}

\newsavebox\North
\newsavebox\East
\newsavebox\South
\newsavebox\West
\newsavebox\NorthWest

\savebox\North{\begin{forest}
    mytree,
    for tree={
        child anchor=south,
        parent anchor=north,
        grow'=north,
        anchor=south,
}
    [North
            [Minnesota]
            [Wisconsin]
    ]
   \end{forest}
}
\savebox\East{\begin{forest}
    mytree,
    for tree={
        child anchor=west,
        parent anchor=east,
        grow'=east,
        anchor=west,
}
        [East
          [South Carolina]
          [North Carolina]
        ]
    \end{forest}
}
\savebox\South{\begin{forest}
    mytree,
    for tree={
        child anchor=north,
        parent anchor=south,
        grow'=south,
        anchor=north,
}
        [South
          [Texas]
          [Mexico]
    ]
    \end{forest}
}
\savebox\West{\begin{forest}
    mytree,
    for tree={
        child anchor=east,
        parent anchor=west,
        grow'=west,
        anchor=east,
}
        [West
            [Los Angeles
              [Santa Monica]
              [West Hollywood]
            ]
            [San Francisco
              [The Castro]
            ]
        ]
    \end{forest}
}
\savebox\NorthWest{\begin{forest}
    mytree,
    for tree={
        child anchor=south east,
        parent anchor=north west,
        grow'=north west,
        anchor=east,
}
        [North West
            [Portland]
            [Seattle
              [Redmond]
            ]
        ]
    \end{forest}
}

\begin{document}

\begin{tikzpicture}[node distance=2cm]
    \node (Kansas) at (0,0) [shape=ellipse,x radius=2cm,draw,align=center] {Kansas City};
    \node (North) [mynodes,above=of Kansas] {\usebox\North};
    \node (East)  [mynodes,right=of Kansas] {\usebox\East};
    \node (South) [mynodes,below=of Kansas] {\usebox\South};
    \node (West)  [mynodes,left=of Kansas,xshift=3mm,yshift=-3mm] {\usebox\West};
    \node (NorthWest)  [mynodes,above left=1.85cm and 2.0cm of Kansas] {\usebox\NorthWest};
%
    \draw[thick] (Kansas.north) --  (North.south);
    \draw[thick] (Kansas.east)  --  (East.west);
    \draw[thick] (Kansas.south) --  (South.north);
    \draw[thick] (Kansas.west)  -- ++(-1.85,0) ;%  (West.east);
    \draw[thick] (Kansas.north west) -- ++(-2.2,1.9) ;%  (NorthWest.south east);
\end{tikzpicture}

\end{document}
3
  • This is inline so that is more elegant than my 'quadrant' idea. I played with it and it seems difficult to get the central node to link to the child node in each of the saveboxes (eg KC--East). That is, it looks like it might be required to have each savebox be visually 'boxed' or have a gap, which is not ideal (for me). I also edited the post to make clear there are nodes at various levels (with 8+ children) where 'more than 180 deg' of spread would be handy. I will play with your example until I understand it better and see if I can surmount the gap issue.
    – Addie
    Aug 2, 2018 at 11:05
  • Addie. I suggest you look at @Ignasi's answer on the question I linked. That answer uses a matrix of nodes. Maybe just put your nodes in a big matrix to get the packing you desire, assuming you know what you want your graph to look like.
    – Ross
    Aug 2, 2018 at 11:19
  • 1
    Addie. I added some fudges to drawing the connections to the boxes so everything is connected. I still think matrix of nodes is the way to go, rather than wrestle with Forest's clever drawing and packing algorithms.
    – Ross
    Aug 2, 2018 at 12:53
4

Conceptionally very similar to Ross' nice answer but instead of saveboxes I use Ulrike Fischers subnode trick. This allows one to connect the nodes rather than saveboxes. Still some manual adjustments necessary. EDIT: replaced the grows by grow' such that north carolina is north of south carolina, and also added makeboxes to save some space.

\documentclass[border=5pt]{standalone}
\usepackage{forest}
\usetikzlibrary{tikzmark} % subnode trick from https://tex.stackexchange.com/a/393656/121799
\usepackage[LGR,T1]{fontenc}           % these three lines added
\usepackage[utf8]{inputenc}             % so that greek symbols will show
\begin{document}
\forestset{universal/.style={rounded corners=0.3em,
      draw,rectangle,
      minimum width=2.5em,
      l sep+=1.5em,
      s sep+=1em,
      anchor=center,}
}
$
\begin{array}{ccc}
&\makebox[3.5cm][c]{\begin{forest}
for tree={
      universal,
      grow'=north
     },
    [\subnode{north}{north}
        [wisconsin]
        [minnesota]
    ]
\end{forest}} & \\
\vcenter{\hbox{\begin{forest}
for tree={
      universal,
      grow'=west
     },
    [\subnode{west}{west}
        [los angeles,
            [santa monica]
            [west hollywood]
        ]
        [san francisco,
            [the castro]
        ]
    ]
\end{forest}}}   
& 
\vcenter{\hbox{\begin{forest}
for tree={
      universal,
      grow'=0
     },
[\subnode{kansas}{kansas city}   
]
\end{forest}}} &
\vcenter{\hbox{\begin{forest}
for tree={
      universal,
      grow'=east
     },
    [\subnode{east}{east}
        [north carolina]
        [south carolina]
    ]
\end{forest}}} \\
&
\makebox[3em][c]{\begin{forest}
for tree={
      universal,
      grow'=south
     },
    [\subnode{south}{south}
        [texas]
        [mexico]
    ]
\end{forest}}
 &
\end{array}$
\begin{tikzpicture}[remember picture,overlay]
\draw (kansas) -- (north);
\draw (kansas) -- (west);
\draw (kansas) -- (south);
\draw (kansas) -- (east);
\end{tikzpicture}
\end{document}

enter image description here

0

You must log in to answer this question.

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