6

Branch then merge then branch then merge??

Thanks to Zarko and marmot for getting me going on "forest". The next question is how do I merge after branching? The attached Fig shows what I would like to do next. To be clear, I am trying to build a triptych -- the 3rd frame combines elements from this one and from my "Lost in the Forest?" first posting that Zarko and marmot were kind enough to show the way! Ah, I need to tell you that I do not have any code that does this. I do have the excellent code that branches -- please see my "Lost in the Forest" posting for that (and don't bother with my code as it was pretty bad, but both marmot and Zarko had some great solutions!).

  • 2
    Can you at least post links to the code you're talking about? And what do you mean by "merging after branching"? This diagram is not a tree, since there are multiple parents to a single node. – Alan Munn Jun 29 '18 at 17:27
  • 1
    I think this would be easier without Forest. Maybe a matrix of nodes? What does @AlanMunn think? – cfr Jun 29 '18 at 21:50
  • 1
    @AlanMunn Though the Forest route turns out to not be so bad with a little help from the aunts :-). – cfr Jun 30 '18 at 0:03
10

Here's my take on the diagram. Rather than use matrix of nodes I've used nodes multipart.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,arrows,shapes.multipart,shapes.geometric,arrows.meta}
\tikzset{multi/.style={
rectangle split,rectangle split horizontal, rectangle split parts=6,align=center,
text width=2cm,
draw=black!15!yellow,
fill=yellow!20}
}
\begin{document}
\begin{tikzpicture}[rounded corners,very thick,-Stealth]
\node(A) [draw=black!15!green,very thick,fill=green!30,minimum height=1cm,rounded corners] {Full Data Set};
\node(B) [multi,below=1cm of A]{%
Middle
\nodepart{two}
Bottom\\Support
\nodepart{three}
Bottom\\Carry
\nodepart{four}
Bottom\\Other
\nodepart{five}
Top
\nodepart{six}
Jungle
};
\node(C)[circle,draw=purple!70!blue,below=1 cm of B,fill=purple!30] {EFA};
\node(D)[multi,below=1cm of C]{%
Middle
\nodepart{two}
Bottom\\Support
\nodepart{three}
Bottom\\Carry
\nodepart{four}
Bottom\\Other
\nodepart{five}
Top
\nodepart{six}
Jungle
};
\node(E)[diamond,draw=purple!70!blue,below=of D,fill=purple!30,sharp corners={}] {L/R};

\foreach \x in {one,two,three,four,five,six}{
\draw(A.south) -- ++(0,-1.5ex) -| (B.\x\space north);
\draw(B.\x\space south) -- ++(0,-4.ex) -| (C.north);
\draw(C.south) -- ++(0,-1.5ex) -| (D.\x\space north);
\draw(D.\x\space south) -- ++(0,-4.ex) -| (E.north);
}
\end{tikzpicture}
\end{document}

output of code

  • Very nice. I thought about using multipart nodes myself, but they seem buggier than the matrix stuff. (That is, I've run into more multipart bugs than matrix bugs so far myself!) However, it does make things really elegant, here. – cfr Jun 30 '18 at 22:12
10

This is not a tree. Hence, it is not necessarily wise to choose forest as the means to draw it. Where only slight deviations from a tree are involved, forest can make good sense. But this diagram involves more deviance than non-deviance, so forest isn't the best option.

Nonetheless, if you do wish to use the package to draw a non-tree for some reason, I would certainly try to do it using forest's facilities within a single picture.

I present two solutions. The first uses a matrix of nodes. The second uses forest.

Matrix

The idea is to draw the yellow boxes using a matrix and then to use the names those nodes get automatically to place the remaining three nodes. Because the matrix's nodes are automatically named in a systematic way, it is then easy to loop through and draw the arrows.

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, positioning, matrix, shapes.geometric}
\begin{document}
\begin{tikzpicture}[thick, >/.tip=Stealth, my node/.style={font=\sffamily, draw,  fill=#1!15, thick}]
  \matrix (m) [matrix of nodes, every node/.append style={my node=yellow, minimum height=7ex, anchor=center, text centered, text width=15mm}]
  {
    Middle & Bottom support & Bottom carry & Bottom other & Top & Jungle \\[15ex]
    Middle & Bottom support & Bottom carry & Bottom other & Top & Jungle \\
  } ;
  \node (r) [above=7.5ex of  m-1-3.north east, anchor=center, my node=green] {Full data set};
  \node (e) [above= 7.5ex of m-2-3.north east, anchor=center, my node=red, circle] {EPA};
  \node (t) [below= 7.5ex of m-2-3.south east, anchor=center, my node=red, diamond] {LR};
  \begin{scope}[rounded corners, ->]
    \foreach \i in {1,...,6} {
      \draw (r.south) -- ++(0,-1.5ex) -| (m-1-\i.north);
      \draw (e.south) -- ++(0,-1.5ex) -| (m-2-\i.north);
      \draw (m-1-\i.south) -- ++(0,-1.5ex) -| (e.north); 
      \draw (m-2-\i.south) -- ++(0,-1.5ex) -| (t.north); 
    }
  \end{scope}
\end{tikzpicture}
\end{document}

matrix-based non-tree

Forest

In this case, the yellow layer is repeated, so it seems a shame not to use forest's facility for copying bits of trees from one place to another. repeat aunts is a style which repeats all of a node's aunts as children of that node. join aunts joins a node to all of its aunts. not a tree just sets the style of the tree: colouring by level and so on. I just didn't know what to call it, so not a tree did.

The upshot is that

\begin{forest}
  not a tree,
  [Full data set
    [Middle]
    [Bottom\\support]
    [Bottom\\carry]
    [
      [EPA, repeat aunts, join aunts [ [LR, join aunts]]]
    ]
    [Bottom\\other]
    [Top]
    [Jungle]
  ]
\end{forest}

produces the following non-tree result

non-tree output

Complete code:

\documentclass[border=10pt]{standalone}
\usepackage[edges]{forest}
\usetikzlibrary{arrows.meta}
\forestset{
  declare keylist={copy list a}{},
  declare keylist={copy list b}{},
  declare keylist={join list}{},
  not a tree/.style={
    forked edges,
    for tree={
      edge+={thick, rounded corners, -Stealth},
      thick,
      draw,
      font=\sffamily,
      align=c, 
      parent anchor=children,
      child anchor=parent,
      anchor=parent,
      l sep'+=10pt,
      fork sep'+=5pt,
    },
    where level=0{fill=green}{ if={> O_= {!u.n children}{1}} {fill=pink, circle}{fill=yellow}, },
    delay={ where content={}{phantom, before computing xy={tempdima/.min={>O{min y}}{siblings},  l-/.register=tempdima }}{} },
  },
  repeat aunts/.style={
    before typesetting nodes={tempkeylista'=, tempkeylistb'=, for nodewalk={fake=u,siblings}{%append={[, phantom]},
        if={ > OOw+n< {n}{!u.n children}{(##1+1)/2}}{ tempkeylista/.option=name }{ tempkeylistb/.option=name }
      }, copy list a/.register=tempkeylista, copy list b/.register=tempkeylistb, split option={copy list a}{,}{prepend'}, split option={copy list b}{,}{append'} }
  },
  join aunts/.style={
    before drawing tree={
      tempkeylista'=,
      for nodewalk={fake=u, siblings}{tempkeylista/.option=name},
      join list/.register=tempkeylista,
      tikz+/.process={ OOw2 {join list} {fork sep} { \draw  [thick, rounded corners, Stealth-]\foreach \i in {##1} { (.child anchor) -- ++(0,##2) -| (\i.parent anchor) }; } },
    }
  }
}
\begin{document}
\begin{forest}
  not a tree,
  [Full data set
    [Middle]
    [Bottom\\support]
    [Bottom\\carry]
    [
      [EPA, repeat aunts, join aunts [,  phantom[LR, join aunts]]]
    ]
    [Bottom\\other]
    [Top]
    [Jungle]
  ]
\end{forest}
\end{document}
  • 1
    I really love "not a tree" but would like to argue that "true trees" may also have something as true trees have a narrow trunk, widen up and get narrow towards the top ;-) – user121799 Jun 30 '18 at 0:20
  • 1
    @marmot That's the fallacy of equivocation :-). – cfr Jun 30 '18 at 0:24
  • 1
    +1 I would also have drawn this without forest, and the matrix of nodes is a straightforward way to do this. – Alan Munn Jun 30 '18 at 0:39
  • @AlanMunn I just noticed I have my appends and prepends muddled somewhere. Or something. But only for part of it. – cfr Oct 5 at 23:18
6

EDIT: I leave my original answer only to show how much better @cfr's answer is. This is clearly the winner.

ORIGINAL FOREST ANSWER (DON'T USE IT!!!): It is possible to do that using Ulrike Fischer's trick, yet it requires some adjustments since tikzmark's \subnode command is not really designed for that application.

\documentclass{article}
\usepackage{forest}
\usetikzlibrary{shadows.blur,arrows.meta,positioning}
\usetikzlibrary{tikzmark} % subnode trick from https://tex.stackexchange.com/a/393656/121799
\tikzset{yellowbox/.style={fill=yellow},
greenbox/.style={fill=green,rounded corners=3pt},
purplecirc/.style={fill=purple!30,circle}}
\begin{document}
\begin{center}
\begin{forest}
  for tree={
      font=\sffamily\bfseries,
      line width=2pt,
      draw=black, % adds the border to all nodes
      align=center,
      child anchor=north,
      parent anchor=south,
      blur shadow,
      l sep+=12.5pt,
      s sep=0.1cm,
      edge={rounded corners=5pt, -{Stealth[length=10pt]}, line width=1pt},
      edge path={
        \noexpand\path[\forestoption{edge}]
          (!u.parent anchor) -- +(0,-10pt) -|
          (.child anchor)\forestoption{edge label};
        },
      where level={1}{minimum width=2cm,yellowbox,text width=1.3cm,align=center}{},     
     }
[Full data set,greenbox
 [\subnode{t1}{Middle\vphantom{p}}]
 [Bottom\\ \subnode{t2}{support}]
 [Bottom\\ \subnode{t3}{carry}]
 [Bottom\\ \subnode{t4}{other\vphantom{p}}]
 [\subnode{t5}{Top}]
 [\subnode{t6}{Jungle}]]     
\end{forest}\\[1.5cm]
\begin{forest}
  for tree={
      font=\sffamily\bfseries,
      line width=2pt,
      draw=black, % adds the border to all nodes
      align=center,
      child anchor=north,
      parent anchor=south,
      blur shadow,
      l sep+=12.5pt,
      s sep=0.1cm,
      edge={rounded corners=5pt, -{Stealth[length=10pt]}, line width=1pt},
      edge path={
        \noexpand\path[\forestoption{edge}]
          (!u.parent anchor) -- +(0,-10pt) -|
          (.child anchor)\forestoption{edge label};
        },
      where level={1}{minimum width=2cm,yellowbox}{},       
     }
[\subnode{EPA}{EPA},purplecirc
 [\subnode{b1}{Middle\vphantom{p}}]
 [Bottom\\ \subnode{b2}{support}]
 [Bottom\\ \subnode{b3}{carry}]
 [Bottom\\ \subnode{b4}{other\vphantom{p}}]
 [\subnode{b5}{Top}]
 [\subnode{b6}{Jungle}]]     
\end{forest}\\[2cm]
\end{center}
\begin{tikzpicture}[overlay,remember picture]
\node[below=3.2cm of EPA,fill=purple!30,diamond,draw,line width=2pt,blur
shadow](LR){LR};
\foreach \X in {1,...,6}
{
\draw[{Stealth[length=10pt]}-,line width=1.2pt,rounded corners] ([yshift=6pt]EPA.north) -- ++(0,18pt) -| (t\X.south);
\draw[{Stealth[length=10pt]}-,line width=1.2pt,rounded corners] (LR.north) -- ++(0,18pt) -| (b\X.south);
}
\end{tikzpicture}
\end{document}

enter image description here

EDIT: Since all the experts are saying that it might be advantageous not to use forest here, I add a plain TikZ solution. It is certainly less fragile than the forest solution.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{shapes.geometric,shadows.blur,positioning,arrows.meta}
\tikzset{yellowbox/.style={fill=yellow,minimum height=0.8cm},
greenbox/.style={fill=green,rounded corners=3pt,minimum height=18pt},
purplecirc/.style={fill=purple!30,circle}}
\begin{document}
\begin{tikzpicture}[
  sibling distance=2cm,
  level distance=2.2cm,
  every node/.style = { 
    align=center,blur shadow,draw,line width=1pt,rounded corners=3pt,
  },edge from parent path=
{(\tikzparentnode.south)  -- ++(0,-14pt) -|  (\tikzchildnode.north)},
edge from parent/.style={draw,-{Stealth[length=10pt]},line width=1.2pt,rounded corners}]
%
\node[greenbox] (fds) {Full data set}
  child { 
         node[yellowbox] {Middle}   
         }
  child {
         node[yellowbox] {Bottom\\ support}
        }
  child {
         node[yellowbox] {Bottom\\ carry}
        }
  child {
         node[yellowbox] {Bottom\\ other}
        }
  child {
         node[yellowbox] {Top}
        }
  child {
         node[yellowbox] {Jungle}
        };
%
\node[below=3.6cm of fds,purplecirc] (EPA) {EPA}
  child { 
         node[yellowbox] {Middle}   
         }
  child {
         node[yellowbox] {Bottom\\ support}
        }
  child {
         node[yellowbox] {Bottom\\ carry}
        }
  child {
         node[yellowbox] {Bottom\\ other}
        }
  child {
         node[yellowbox] {Top}
        }
  child {
         node[yellowbox] {Jungle}
        };
%       
\node[below=3.6cm of EPA,fill=purple!30,diamond](LR){LR};       
%
\foreach \X in {1,...,6}
{
\draw[{Stealth[length=10pt]}-,line width=1.2pt,rounded corners] (EPA.north) --
++(0,16pt) -| (fds-\X.south);
\draw[{Stealth[length=10pt]}-,line width=1.2pt,rounded corners] (LR.north) --
++(0,16pt) -| (EPA-\X.south);
}
\end{tikzpicture}
\end{document}

enter image description here

  • (+1) for the non-Forest solution. But I really wouldn't do it this way if using Forest. – cfr Jun 30 '18 at 0:02
  • @cfr I 100% agree, and edited my "answer" accordingly. I'll also be happy to remove it. (+1 of course) – user121799 Jun 30 '18 at 0:06
  • I didn't mean you to remove the forest bit. (But I prefer mine ;).) I like your non-forest answer a lot. I didn't know that the trees stuff got you automatic naming like that (if I've understood correctly how it works). – cfr Jun 30 '18 at 0:16
  • @cfr Yes, TikZ is not very progressive: by default children inherit their name from their parent (but you can request a name change;-) – user121799 Jun 30 '18 at 0:23

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.