7

I am quite new to TikZ and to be honest to this site (I am writing this question with the aid of a more experienced friend), and I am trying to recreate the ensuing photo

enter image description here

What I have put together so far from searching this site and the intimidating manual of TikZ is as follows

\documentclass[11pt]{book}
\usepackage{amsmath}
\usepackage{tikz}
\usepackage{tikz-qtree}
\usepackage{forest}
\usetikzlibrary{arrows.meta}
\begin{document}
    \begin{tikzpicture}[every tree node/.style={circle,draw,inner sep=1.2,fill=black},
        level distance=1.5cm,sibling distance=0.5cm, edge from parent/.append style={->,>={Latex[length=6pt,width=3pt]}},grow'=up
        ]
        \Tree[
        .\node (g) [label=below:{$g$}]{};
        [.\node (e) [label=left:{$e$}]{};
        [.\node (c) [label=left:{$c$}]{};
        [.\node (a) [label=left:{$a$}]{};]
        [.\node (b) [label=right:{$b$}]{};]
        ]
        [.\node (d) [label=right:{$d$}]{};]
        ]
        [.\node (f) [label=right:{$f$}]{};]
        ];
    \end{tikzpicture}
\end{document}

Which gives the below picture enter image description here

I tried to use forest environment as well

\begin{forest}
        for tree={
            circle,
            draw,
            fill=black,
            edge={<-,>={Latex[length=6pt,width=3pt]}},
            grow'=north,
            parent anchor=north,
            child anchor=south,
            calign=fixed edge angles,
            inner sep=0.5pt
        }
        [g, label=below:{$g$}
        [e, label=left:{$e$}
        [c, label=left:{$c$}
        [a, label=right:{$a$}]
        [b, label=right:{$b$}]
        ]
        [d, label=right:{$d$}]
        ]
        [f, label=right:{$f$}]
        ]
    \end{forest}

Which produced the ensuing photo enter image description here

I cannot understand why the nodes are of different size and how to resolve this. What suggestions do you propose to recreate the original image in which the edges seem to appear on one line?

1 Answer 1

5

Please use my subsequent post rather than the code below. The linked answer demonstrates a problem with this version, as well as providing code to address it.


You get different sized nodes because g has greater depth than a, say, and f has greater height. If you imagine drawing a tight-fitting circle around a letter, you can see the size will depend on the letter. One way to address this is to remove the content of the node before the node is typeset. Since these are identical to the labels, we might as well use the content for the label and simplify the input syntax, too.

What we then need is a style which figures out where to place the label and formats it.

I've also removed some formatting directives which were actually making your output less like your target image and amended the arrow specification so they point in the correct direction.

resulting tree with uniformly sized nodes and formatted labels

\documentclass[11pt]{book}
% ateb: https://tex.stackexchange.com/a/703351/ i gwestiwn Arian: https://tex.stackexchange.com/q/703321/
\usepackage{amsmath}
\usepackage{forest}
\usetikzlibrary{arrows.meta}
\begin{document}
\begin{forest}
  for tree={
    circle,
    fill=black,
    edge={{Latex[length=6pt,width=3pt]}-},
    grow'=north,
    calign=fixed edge angles,
    inner sep=0.5pt,
  },
  before typesetting nodes={
    for tree={%
      my label/.option=content,
      content={.},
    }
  },
  my label/.style={%
    label/.process={ O+nn= ? p w1 {level}{0} {_{below}} {On= ? {n}{1} {left}{right}} {##1:{$#1$}} },
%   if level=0{label=below:{$#1$}}{if n=1{label=left:{$#1$}}{label=right:{$#1$}}},
  },
  [g 
    [e 
      [c 
        [a ]
        [b]
      ]
      [d ]
    ]
    [f ]
  ]
\end{forest}
\end{document}

Note that the complicated looking

    label/.process={ O+nn= ? p w1 {level}{0} {_{below}} {On= ? {n}{1} {left}{right}} {##1:{$#1$}} },

is simpler than it looks. At least, it is simpler to read. I do not find it simpler to write when it includes ?. Reading from left-to-right, O+nn= ? p w1 specifies the nature of the arguments which follow and how to process them. {level}{0} {_{below}} {On= ? {n}{1} {left}{right}} {##1:{$#1$}} are the arguments to be processed.

O says the first argument is the name of an option and Forest should get the value of the option. n says the second is a number. = asks Forest to determine whether the first argument equals the second. Looking at the arguments, this is equivalent to asking Forest is the level of the current node is 0. This is true for the root node, but not the others.

The ? is conditional. It says if the test Forest just did passed, it should use the very next argument (_{below}); otherwise, it should use the one after (On= ? {n}{1} {left}{right}). So, for root, we'll get _{below}. For any other node, we'll get On= ? {n}{1} {left}{right}.

p tells Forest to apply argument processing to this argument. (This confused me - I thought I needed a +p, but an example in the manual suggests not and + gives an error.)

In the case of root, _{below} just results in below.

In the case of any other node, On= ? {n}{1} {left}{right} we have the same structure as before: get an option, see if it is equal to a number and use the relevant argument. In this case, we tell Forest to see if the node's n is equal to 1. This is true for left-hand nodes, where we use left, and false for right-hand nodes, where we use right.

Finally, w1 wraps the value we've got as a result of all the tests and uses it as the argument ##1 in ##1:{$#1$}. So root's label will go below and the other nodes' will go left, for left-hand nodes, or right, for right-hand nodes. #1 is the argument we're passing to my label, which happens to be what was the content of the node.

If you prefer, you can write the code using nested ifs. For example,

if level=0{label=below:{$#1$}}{if n=1{label=left:{$#1$}}{label=right:{$#1$}}},

would do the same thing.

7
  • Thanks a lot. Could you please specify where in the forest manual should I refer for further reading?
    – Arian
    Commented Dec 7, 2023 at 12:34
  • Supposing I want to add a child to the node "f" on the same line which connects node "g" to "f". If we were in tree environment, we could easily use "missing" child. However, as far as I have searched, I could not find a similar concept to that in forest. Do you know of any way to achieve this or how to construct a missing child in forest?
    – mali1234
    Commented Dec 7, 2023 at 14:49
  • 1
    @mali1234 Page 39: phantom.
    – cfr
    Commented Dec 7, 2023 at 20:11
  • 1
    @Arian That's hard to answer in general. I tend to consult the manual when I'm trying to solve particular problems. So I use the reference section most. But to get a general sense of how it works and what's possible, I'd start with section 2 which is a tutorial. Try copying the code there and simple modifications. If you're a linguist, there's also a quickstart guide on CTAN, which might make for an easier beginning.
    – cfr
    Commented Dec 7, 2023 at 20:15
  • Sorry to be bothering you so much. An augmented version of this picture has been proposed in which a number is written to the left of each node (e.g. 1 to the left of g, 2 to the left of e and so forth), however the numbers are placed on top of each other (They have the same x-coordinate). And some nodes have been added in the middle of the edges. For example a node e' has been added halfway between g and e and a number 1.5 has been added to the left of it below 2 and above 1. Is there a way to adjust this code so that it meets this change?
    – Arian
    Commented Dec 7, 2023 at 21:22

You must log in to answer this question.

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