Zarko addressed your first question, so this answer focuses on your second,
Why is the middle node across and not straight down?
The reason this happens is, basically, because forest
doesn't align children to parents in general. Rather, it aligns parents to children. More specifically, by default it aligns parents so that they are centred between their first and last children. In effect, this means the first and last child are equidistant from the parent.
The position of the middle child is then determined based on s sep
and the position of its preceding sibling. So, in this case, the child is placed a minimum of s sep
from the sibling before. Since both siblings are themselves the roots of subtrees with quite wide bases, this means the middle child's alignment ends up skewed. If it had a child itself, this would not happen because its child would be spaced relative to the other terminal children and the node would be aligned relative to that child. Since it is childless, there is no 'reason' for forest
to increase the spacing.
You might think that calign with current
or calign with current edge
would work here, because these options tell forest
to align the current node (or the current node's edge) with the parent. (I ignore the complication of different anchors here because the nodes are small enough for the differences to be relatively minor.)
\begin{forest}
for tree={circle, fill,s sep'=1cm,inner sep=1.8pt}
[[[][][]][,calign with current][[][][]]]
\end{forest}
\begin{forest}
for tree={circle, fill,s sep'=1cm,inner sep=1.8pt}
[[[][][]][,calign with current edge][[][][]]]
\end{forest}
But, of course, this doesn't work because it tells forest
to align the parent to the child and not the child to the parent.
The simplest solution is to provide the node with that missing child, but tell forest
the node is not a real one and shouldn't actually be drawn.
\begin{forest}
for tree={%
circle,
fill,
s sep'=0.8cm,
inner sep=1.8pt,
}
[[[][][]][[,phantom]][[][][]]]
\end{forest}
This does tend to make the tree rather wider than one might like. One option would be to reduce s sep
for the affected node, but, even setting it to zero would mean the nodes each side would be at least twice the minimum separation apart. When s sep
is small, that's less of a concern and may even be preferable, since it helps distinguish the branches. But when s sep
is large, it seems less than ideal.
The same issue arises with fit=band
.
\begin{forest}
for tree={%
circle,
fill,
s sep'=0.8cm,
inner sep=1.8pt,
}
[[[][][]][,fit=band][[][][]]]
\end{forest}
I suggest instead using a custom style to evenly spread the root node's children before the tree is drawn. This makes conflicts (e.g. overlaps) more likely because the node's position won't be the one forest
believes it has when packing the tree. So, I would make the style conservative. Better it not correct misalignment than that it miscorrect alignment.
So the style only does anything if the following conditions hold:
- The node has no children.
- The node is not the root.
- The node is not the first child.
- The node is not the last child.
If all four conditions are satisfied and the style is applied to a node, then
forest
will calculate a new alignment for the node.
\forestset{%
spread/.style={%
before computing xy={%
if={ > O_= O_=! O_> O_> &&& {n children}{0}{level}{0} {n}{1} {n'}{1} }{%
s/.process={ OSOw2+dOOw2+PLw3 {!u1.s}{!ul.s}{##2-##1}{!u.n children}{n}{(##2-1)/(##1-1)}{##3+(##2*##1)}},
}{},
},
},
}
This is broken into multiple steps because I cannot figure out how to get forest
's argument processor to play nicely with dimensions and pgfmath
, but I need pgfmath
for the multiplication. (At least, I think I do.) There is probably a more elegant solution, but it eludes me.
This can still go wrong. If siblings other than the first and last have children, they will not be moved and a node may end up overlapping or edges may end up conflicting. A better solution would be to position the node relative to its immediate neighbours, but, if those neighbours move themselves, we'd have to come back and do further adjustments. That's entirely possible, but time-consuming. So I would recommend implementing that only if it turns out to be necessary and only for those trees where it is needed. Even the style applied here slows compilation, so is best applied to the nodes which need it rather than entire trees. It will work for entire trees, but it will do a lot of processing for nodes which don't get moved, so applying it more selectively will make the code more efficient.
\begin{forest}
for tree={%
circle,
fill,
s sep'=0.8cm,
inner sep=1.8pt,
}
[[[][][]][,spread][[][][]]]
\end{forest}
Finally, here's a conditionally coloured one just for fun.
\begin{forest}
for tree={%
circle,
fill,
s sep'=0.8cm,
inner sep=1.8pt,
if n=2{spread,blue,edge+=blue!50!black}{magenta,edge+=magenta!50!black},
}
[[[][][]][][[][][]]]
\end{forest}
Complete code:
\documentclass[tikz,border=5pt]{standalone}
% ateb: https://tex.stackexchange.com/a/720192/ addaswyd o gwestiwn VD3: https://tex.stackexchange.com/q/720081/
\usepackage{calc}
\usepackage{forest}
\forestset{%
spread/.style={%
before computing xy={%
if={ > O_= O_=! O_> O_> &&& {n children}{0}{level}{0} {n}{1} {n'}{1} }{%
s/.process={ OSOw2+dOOw2+PLw3 {!u1.s}{!ul.s}{##2-##1}{!u.n children}{n}{(##2-1)/(##1-1)}{##3+(##2*##1)}},
}{},
},
},
}
\begin{document}
\begin{forest}
for tree={circle, fill,s sep'=1cm,inner sep=1.8pt}
[[[][]][[][]]]
\end{forest}
\begin{forest}
for tree={%
circle,
fill,
s sep'=0.8cm,
inner sep=1.8pt,
}
[[[][][]][,fit=band][[][][]]]
\end{forest}
\begin{forest}
for tree={circle, fill,s sep'=1cm,inner sep=1.8pt}
[[[][][]][,calign with current][[][][]]]
\end{forest}
\begin{forest}
for tree={circle, fill,s sep'=1cm,inner sep=1.8pt}
[[[][][]][,calign with current edge][[][][]]]
\end{forest}
\begin{forest}
for tree={%
circle,
fill,
s sep'=0.8cm,
inner sep=1.8pt,
}
[[[][][]][[,phantom]][[][][]]]
\end{forest}
\begin{forest}
for tree={%
circle,
fill,
s sep'=0.8cm,
inner sep=1.8pt,
}
[[[][][]][,spread][[][][]]]
\end{forest}
\begin{forest}
for tree={%
circle,
fill,
s sep'=0.8cm,
inner sep=1.8pt,
if n=2{spread,blue,edge+=blue!50!black}{magenta,edge+=magenta!50!black},
}
[[[][][]][][[][][]]]
\end{forest}
\end{document}
[, yellow]
. An edge to a parent can be colored with theedge
key, say[, edge=yellow]
. Your other problem has an example in the manual on pp. 43f.