1

My code using forest package and compiling with pdflatex works fine for narrow text and upper parts of split nodes. Please provide your forest based input to

1.) avoid overlaps in nodes with wider text. I assume wrong use of

before computing xy={s/.average={s}{siblings}}

2.) access the lower part of split nodes. When trying

[narrow\\text 1\nodepart{lower}text, splitRec, draw, before computing xy={s/.average={s}{siblings}}]

pdflatex generates the error:

! Missing \cr inserted.

Code:

\documentclass[tikz,border=10pt]{standalone}


\usepackage{verbatim}
\usepackage[edges]{forest}
\usepackage{flexisym}
\usetikzlibrary{svg.path}


\forestset{
    qtree/.style={for tree={fit=band, parent anchor=south, child anchor=north, align=center, inner sep=3pt}},
    splitRec/.style={rectangle, rectangle split, rectangle split parts=2, draw}
}


\begin{comment}
     :compile using pdflatex
\end{comment}


\begin{document}
    \begin{forest}, baseline, qtree, forked edges
        [start\\node, ellipse, draw
    [first\\diamond, diamond, draw
        [second\\diamond, diamond, draw, edge path'={ (!u) |- (.parent)} % 
            [, phantom] % phantom node left so that the subsequent child has a node leaving the parent on the west
            [third\\diamond, diamond, draw, s sep+=0.25cm, edge path'={ (!u) |- (.parent)}
                [, phantom]
                [narrow\\text 1, splitRec, draw, before computing xy={s/.average={s}{siblings}}]
                [forth\\diamond, diamond, draw, edge path'={ (!u) |- (.parent)}
                    [, phantom]
                    [narrow\\text 2, ellipse, draw, before computing xy={s/.average={s}{siblings}}]
                    [fifth\\diamond, diamond, draw, edge path'={ (!u) |- (.parent)}
                        [, phantom]
                        [wide text 3\\Lorem ipsum dolor sit amet consectetur adipiscing\\elit sed do eiusmod tempor incididunt ut\\labore et dolore magna aliqua ut enim ad minim veniam\\quis nostrud exercitation ullamco laboris nisi ut aliquip\\ex ea commodo consequat duis aute irure dolor in reprehenderit, rectangle, draw, before computing xy={s/.average={s}{siblings}}]
                        [sixth\\diamond, diamond, draw, edge path'={ (!u) |- (.parent)}
                            [, phantom]
                            [wide text 4\\Lorem ipsum dolor sit amet consectetur adipiscing\\elit sed do eiusmod tempor incididunt ut\\labore et dolore mag    na aliqua ut enim ad minim veniam\\quis nostrud exercitation ullamco laboris nisi ut aliquip\\ex ea commodo consequat duis aute irure dolor in reprehenderit, rectangle, draw, before computing xy={s/.average={s}{siblings}}]
                            [, phantom]
                        ]
                    ]
                ]
            ]
        ]
        [narrow\\text 0\\right below, rectangle, draw, before computing xy={s/.average={s}{siblings}}]
        [, phantom]
    ]
]
    \end{forest}
\end{document}

.pdf: enter image description here

2
  • There seems to be no predefined migration path from superuser to tex.se. Either flag "in need of moderator intervention" or delete it here and ask a new question on tex.se (the later is probably faster and easier) Oct 23 '19 at 16:02
  • 1
    @Schrödinger'scat That answer is rather outdated now and could be done much more efficiently with current Forest.
    – cfr
    Oct 23 '19 at 22:49
3

The overlaps are easiest to avoid by letting forest do its job of avoiding them. In this case, this means omitting fit=band. We can easily get the desired extra space (between, e.g. children of the first diamond) by a larger s sep. (See the comments increase/decrease the horizontal distance between children in the code.)

Specifying nodeparts in the OP's code does not work because of the align key used by the qtree key defined in the preamble. This key differs from the tikz key with the same name. It works by inserting the content in a tabular environment. Inside it, \nodepart makes no sense, thus the Missing \cr errors.

The solution is to use tikz's align key on multipart nodes (or to manually create the tabular environment for each text part), which is achieved by /tikz/align. The \nodepart command in the content then works as intended, see narrow text 2 node. (By the way, the second part of rectangle split is called two, not lower.)

A bit of forest candy.

  • nodepart style defined in the preamble lets you write nodepart={nodepart name}{text}, see narrow text 1 node.
  • Styles for specific nodeparts can be easily defined, see the definition of part two and its usage in wide text 3.
  • nodeparts style parses the content into parts using an arbitrary separator. Even \\ works, see narrow text 0. Separating by comma requires the content in braces, see wide text 4. For me, a semicolon is usually the best candidate for the separator. How does the nodeparts style work?
    • First, it defines styles for specific nodeparts and constructs a temporary keylist starting with content' followed by all the defined nodepart styles. This is achieved by splitting the given list of nodepart names. In narrow text 0 it defines nodepart styles nodepart@two and nodepart@three; the temporary keylist is content',nodepart@two,nodepart@three. (A better implementation might pre-compute the temporary keylist (which depends on the shape of the node.)
    • The temporary keylist is used as the third argument of split option, which actually splits the content. The keys of the temp keylist and the parts of the content are "zipped": content' gets the main part and simply truncates the content option to it; nodepart@two gets the second part and calls nodepart={two}{second part of content} on it, effectively replacing the first separator by \nodepart{two}; etc.

The code:

\documentclass[tikz,border=10pt]{standalone}

\usepackage{verbatim}
\usepackage[edges]{forest}
\usepackage{flexisym}
\usetikzlibrary{svg.path}
\usetikzlibrary{shapes.multipart}

\forestset{
  qtree/.style={for tree={
      parent anchor=south,
      child anchor=north,
      /tikz/align=center,
      inner sep=3pt,
      s sep=5em,
    }},
  splitRec/.style={rectangle, rectangle split, rectangle split parts=2, draw},
  right below/.style={
    calign with current,
  },
  my diamond/.style={
    diamond,
    draw,
    edge path'={ (!u) |- (.parent)},
  },
  nodepart/.style 2 args={% generic nodepart
    content+=\nodepart{#1}#2,
  },
  part two/.style={nodepart={two}{#1}}, % specific nodepart
  % parse the content into nodeparts:
  nodeparts/.style 2 args={% #1 = part names, #2 = content separator
    tempkeylista'={content'},
    split={#1}{,}{define@nodepart},
    split option/.process=Rw1{tempkeylista}{{content}{#2}{##1}},
  },
  define@nodepart/.style={
    nodepart@#1/.style={nodepart={#1}{##1}},
    tempkeylista=nodepart@#1,
  },
}


\begin{comment}
  :compile using pdflatex
\end{comment}

\begin{document}
\begin{forest}, baseline, qtree, forked edges
  [start\\node, ellipse, draw
    [first\\diamond, diamond, draw,
      s sep=30em, % increase the horizontal distance between children
      [second\\diamond, my diamond, 
        [,right below, phantom]
        [third\\diamond, my diamond, 
          [narrow\\text 1, nodepart={two}{text}, splitRec, draw, right below]
          [forth\\diamond, my diamond, 
            [narrow\\text 2\nodepart{lower}text, ellipse split, draw, right below]
            [fifth\\diamond, my diamond,
              s sep=2em, % decrease the horizontal distance between children
              [wide text 3, splitRec, part two={Lorem ipsum dolor sit amet consectetur adipiscing\\elit sed do eiusmod tempor incididunt ut\\labore et dolore magna aliqua ut enim ad minim veniam\\quis nostrud exercitation ullamco laboris nisi ut aliquip\\ex ea commodo consequat duis aute irure dolor in reprehenderit}, right below]
              [sixth\\diamond, my diamond, 
                [{wide text 4,Lorem ipsum dolor sit amet consectetur adipiscing\\elit sed do eiusmod tempor incididunt ut\\labore et dolore mag    na aliqua ut enim ad minim veniam\\quis nostrud exercitation ullamco laboris nisi ut aliquip\\ex ea commodo consequat duis aute irure dolor in reprehenderit},
                  rectangle split, rectangle split parts=2, nodeparts={two}{,},
                  draw, right below]
              ]
            ]
          ]
        ]
      ]
      [narrow\\text 0\\right below,
        rectangle split, rectangle split parts=3, nodeparts={two,three}{\\},
        draw, right below]
    ]
  ]
\end{forest}
\end{document}
4
  • I noticed my personal version of forest.sty has changes not yet in the version on CTAN. What's the status of the code at the moment? (And any news on name spaces?)
    – cfr
    Oct 23 '19 at 22:04
  • (+1) of course, but I really don't like multipart nodes.
    – cfr
    Oct 23 '19 at 22:48
  • 1
    I wish I had more time to work on Forest ... Oct 24 '19 at 7:11
  • That's some beautiful code! Thank's a lot! I am on it, to understand what just happened ... However, I will start with the answer from @cfr since it seems to need less manual adjustments such as % increase the horizontal distance between children
    – wba
    Oct 25 '19 at 5:03
2

Usually, I would use Sašo's code on the grounds that it is certainly better than anything I might dream up. However, I really don't like multipart nodes, so I'd probably do something like this.

[Translation: I'd done most of this before Sašo answered, so I thought I might as well finish.]

  • I almost always start on questions like this by removing all manual adjustments, on the grounds that they are almost certainly doing more harm than good.

  • I see almost no reason to use multipart nodes, which are known to be buggy even in ordinary tikzpictures, when any Forest node can be a tabular with any specification you like.

  • Instead, I would adjust set the inner xsep to zero and put compensatory space on each side of a centred column, so that an ordinary \hline can be used to separate the node 'parts'. I set this to 3pt and set inner ysep to 3pt so the spacing is consistent with the original intent.

  • I use Forest anchors and some conditionals to get the edges to play nicely with the diamonds and so forth.

\documentclass[border=10pt]{standalone}
\usepackage{forest,array}
\newcolumntype{C}{@{\hspace{3pt}}c@{\hspace{3pt}}}

\begin{document}
  \begin{forest}, baseline,  for tree={draw, inner xsep=0pt, inner ysep=3pt, fit=rectangle, align=C},
  where n children=0{calign with current edge, for siblings={if n=1{edge path'={(!u.first)-|(.parent)}}{edge path'={(!u.last)-|(.parent)}}}}{}
    [start\\node, ellipse, 
    [first\\diamond, diamond, 
        [second\\diamond, diamond, 
            [third\\diamond, diamond, 
                [narrow\\text 1\\\hline lower part\\of node ]
                [forth\\diamond, diamond, 
                    [narrow\\text 2, ellipse,  ]
                    [fifth\\diamond, diamond,  
                        [wide text 3\\Lorem ipsum dolor sit amet consectetur adipiscing\\elit sed do eiusmod tempor incididunt ut\\labore et dolore magna aliqua ut enim ad minim veniam\\quis nostrud exercitation ullamco laboris nisi ut aliquip\\ex ea commodo consequat duis aute irure dolor in reprehenderit ]
                        [sixth\\diamond, diamond,  
                            [wide text 4\\Lorem ipsum dolor sit amet consectetur adipiscing\\elit sed do eiusmod tempor incididunt ut\\labore et dolore mag    na aliqua ut enim ad minim veniam\\quis nostrud exercitation ullamco laboris nisi ut aliquip\\ex ea commodo consequat duis aute irure dolor in reprehenderit,  ]
                        ]
                    ]
                ]
            ]
        ]
        [narrow\\text 0\\right below, ]
    ]
]
\end{forest}
\end{document}

output 'tree'

6
  • Thank you for the answer! I am on it to understand the code and most certainly may come back for further clarifications; sorry for that.
    – wba
    Oct 25 '19 at 5:10
  • line where n children=0{calign with current edge, for siblings={if n=1{edge path'={(!u.first)-|(.parent)}}{edge path'={(!u.last)-|(.parent)}}}}{} in my words:
    – wba
    Oct 25 '19 at 6:22
  • 1.) when there is 1 child children=0, it will be aligned under its parent calign with current edge
    – wba
    Oct 25 '19 at 6:23
  • 2.) in case of siblings for siblings, 2.1) sibling 1 n=1' is aligned first sideways then down what results to a left down position in a left to right order 2.2) subsequent siblings are aligned in a standard way as children are aligned what is under its parent 2.3) the last sibling is aligned sideways then down what results to a right down position since left down and center are already taken.
    – wba
    Oct 25 '19 at 6:23
  • In my understanding, single children can not be positioned left down or right down; is that correct?
    – wba
    Oct 25 '19 at 6:23
1

To my opinion your graph is simpler to draw as pure tikz picture:

\documentclass[tikz,border=10pt]{standalone}
\usetikzlibrary{positioning,
                shapes}

\begin{document}
    \begin{tikzpicture}[
    node distance=3mm and 4mm,
    box/.style = {draw, text width=#1, align=center,inner sep=2pt},
    box/.default = 24em,
    dia/.style = {diamond, aspect=1.5, draw, align=center, inner xsep=0pt},
    elp/.style = {ellipse, aspect=1.5, draw, align=center, inner xsep=0pt}
    mpv/.style = {rectangle split, rectangle split parts=2, 
                  rectangle split empty part height=2ex, draw,
                  text width=#1, align=center},
    mpv/.default = 4em
                        ]
% from bottom to top
\node[box]  (n1) {wide text 4\\Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore mag na aliqua ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat duis aute irure dolor in reprehenderit, rectangle, draw, before computing};
\node (n2) [dia, above=of n1]           {sixth\\diamond};
\node (n3) [box, left=of n2 -| n1.west] {wide text 3\\Lorem ipsum dolor sit amet consectetur adipiscing\\elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat duis aute irure dolor in reprehenderit};
\node (n4) [dia, above=of n3]           {fifth\\diamond};
\node (n5) [elp,right=of n3.west |- n4] {narrow\\text 2};
\node (n6) [dia, above=of n5]           {forth\\diamond};
\node (n7) [mpv, left=of n6]            {narrow\\text 1};     
\node (n8) [dia, above=of n7]           {third\\diamond};
\node (n9) [dia,above left=of n8]       {second\\diamond};
\node (n10) [dia,above right=of n9.north -| n4]     {first\\diamond};
\node (n11) [box=6em,below=of n10]      {narrow\\text 0\\right below};
\node (n12) [elp,above=of n10]          {start\\ node};
%
\draw   (n12) -- (n10) -- (n11)
        (n10) -| (n9)
        (n9)  -| (n8)
        (n8)  -- (n7)
        (n8)  -| (n6)
        (n6)  -- (n5)
        (n6)  -| (n4)
        (n4)  -- (n3)
        (n4)  -| (n2)
        (n2)  -- (n1)
        ;
   \end{tikzpicture}
\end{document}    

Edit: I add a node with multi part shape on which I forgot in the first version of the answer.

enter image description here

6
  • I'm a Forest fan, as you know, but it doesn't lend itself to this one :-).
    – cfr
    Oct 23 '19 at 20:59
  • Which isn't to say it can't be done :-).
    – cfr
    Oct 23 '19 at 22:39
  • @cfr, to me is this particular tree simpler to draw with pure tikz (i'm not mastered forest yet :-) ), however I hope that someone, who is more skilled in forest show, how to do this. In fact, this has already happened!
    – Zarko
    Oct 24 '19 at 6:14
  • Indeed. Twice, in fact. But I think it is really as easy to do in TikZ. Doing it in Forest is more a 'because it's there' kind of thing :-).
    – cfr
    Oct 24 '19 at 23:03
  • Thank you for the Ti k Z version. Since the graph is just a twig in a forest full of massive trees, I opt for Forest. @cfr how did you format the italic k in TikZ from your previous comment without ending up with a space between Ti and Z?
    – wba
    Oct 25 '19 at 4:56

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.