# TikZ-PGF child nodes in different locations

When I use TiKz to draw two trees, each in a nested picture, the position of the middle child of the second tree is different from the first. This is even though the code for the trees is basically identical (save the right=of test parameter).

Am I doing something wrong? Is there a way to fix this so that both middle children are positioned directly under their respective parents?

Code:

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{positioning}

\begin{document}
\begin{tikzpicture}

\tikzstyle{every node}=[draw]

\node (test) {
\begin{tikzpicture}
\node {}
child { node {} }
child { node {} }
child { node {} };
\end{tikzpicture}
};

\node [right=of test] {
\begin{tikzpicture}
\node {}
child { node {} }
child { node {} }
child { node {} };
\end{tikzpicture}
};

\end{tikzpicture}
\end{document}


As Qrrbrbirlbel explains forest can be used with a phantom root, and offers flexible options for specifying the tree layout.

Specifying l sep and s sep does not, however, guarantee that the centre child will be aligned with the parent. If the node contents are of different widths, this is likely to fail:

\begin{forest}
for tree={
l sep=1cm,
s sep=1cm,
draw,
align=center
}
[,phantom
[
[some text here]
[a multiline node\\in the middle]
[not much]
]
[
[this\\node\\needs\\five\\lines]
[this\\one\\only\\four]
[finally\\just\\three]
]
]
\end{forest}


This happens because the s sep specifies only the minimum distance. It would be possible to force s to a certain value, which would set the distance. However, it would be better to allow forest to figure out the best value and just give it some hints about what we want it to do. calign with current tells forest to align the current child with its parent and is just what is wanted in this case.

\begin{forest}
for tree={
draw,
if={isodd(n_children())}{
for children={
if={
equal(n,int((1+n_children("!u"))/2))
}{calign with current}{}
}
}{},
align=center
}
[,phantom
[
[some text here]
[a multiline node\\in the middle]
[not much]
]
[
[this\\node\\needs\\five\\lines]
[this\\one\\only\\four]
[finally\\just\\three]
]
]
\end{forest}


This works by testing to see if a node has an odd number of children. If it does, then the centre child is aligned with its parent:

It would be better, however, if the edge from the parent went to the north of the child rather than crossing the nodes. Adding child anchor=north achieves this:

If the root nodes also contain content and this content has different heights in the two cases, the vertical alignment will be thrown:

To avoid this, we can use tier to ensure that all nodes at a given level are aligned vertically. Adding tier/.wrap pgfmath arg={tier #1}{level()}, we get the following:

The complete code:

\documentclass[tikz,border=5pt,multi]{standalone}
\usepackage{forest}
\standaloneenv{forest}
\begin{document}

\begin{forest}
for tree={
draw,
if={isodd(n_children())}{
for children={
if={
equal(n,int((1+n_children("!u"))/2))
}{calign with current}{}
}
}{},
align=center,
child anchor=north,
tier/.wrap pgfmath arg={tier #1}{level()}
}
[,phantom
[some text here]
[a multiline node\\in the middle]
[not much]
]
[right tree
[this\\node\\needs\\five\\lines]
[this\\one\\only\\four]
[finally\\just\\three]
]
]
\end{forest}
\end{document}


Applied to the original tree with empty nodes, this also produces a more compact result:

\documentclass[tikz,border=5pt,multi]{standalone}
\usepackage{forest}
\standaloneenv{forest}
\begin{document}

\begin{forest}
for tree={
draw,
if={isodd(n_children())}{
for children={
if={
equal(n,int((1+n_children("!u"))/2))
}{calign with current}{}
}
}{},
align=center,
child anchor=north,
tier/.wrap pgfmath arg={tier #1}{level()}
}
[,phantom
[
[]
[]
[]
]
[
[]
[]
[]
]
]
\end{forest}

\end{document}


If you add xshift=-4.3725cm, red to the options to the right node, you'll get

The problem here is that the transformation from right=of test sneaks through to the underlying tree (or something to that effect).

Nesting TikZ picture is never a good idea.

Dependently on what you want to achieve you could do something like

\begin{tikzpicture}[nodes=draw, node distance=4cm]
\node (test)          {} child { node {} } child { node {} } child { node {} };
\node [right=of test] {} child { node {} } child { node {} } child { node {} };
\end{tikzpicture}


or (under the hood, a matrix is actually a node)

\begin{tikzpicture}[nodes=draw]
\matrix [column sep=1cm] {
\node {} child { node {} } child { node {} } child { node {} }; &
\node {} child { node {} } child { node {} } child { node {} }; \\
};
\end{tikzpicture}


With the forest package (\usepackage{forest}) you can do something like this:

\begin{forest} /tikz/nodes=draw, for tree={l sep=1cm, s sep=1cm}
[,phantom
[ [][][]]
[ [][][]]
]
\end{forest}

• (+1) But the forest solution won't work if the nodes have different widths, for example. (See my answer for an illustration.)
– cfr
May 1, 2015 at 22:19
• @cfr Yes, this should serve more as an introduction to what can be done (cf. "Dependently"). The answer is also a now-deleted comment converted to an answer. The forest manual is a must. It also gives a lot of help concerning placement and “the hairy details of vertical alignment”. ;) May 1, 2015 at 22:25