Is there a way to construct a tree whose children are located at
specified positions, so that TikZ
computes the locations of the
other nodes in the tree based on the level distance
and sibling
?
distance
I ask because, ultimately, I have a matrix of nodes
and would like
to grow a tree into the second column's vector from the righthand
side. A minimal working example of what I have so far is:
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning,matrix}
\begin{document}
\tikzset{
img/.initial={},
img/.value required,
%
image placeholder/.style={%
execute at end node=\phantom{\pgfuseimage{\pgfkeysvalueof{/tikz/img}}}}
}
\pgfdeclareimage[height=7em]{ballot}{fptp-ballot}
\begin{tikzpicture}[every node/.style={draw=black},
every matrix/.style={ampersand replacement=\&}]
\matrix (ballot counts)
[matrix of nodes, nodes in empty cells, cells={right},
column 1/.append style={%
nodes={scale=0.5}, image placeholder, img=ballot}]
{
\& Alice: $0$ \\
\& Bob: $0$ \\
\& Charlie: $0$ \\
\& Dave: $0$ \\
};
\matrix (ballot box)
[node distance=2 and 2, right=of ballot counts,
matrix of nodes, nodes in empty cells,
cells={image placeholder, img=ballot}]
{
\& \\
\& \\
};
\matrix (max tree)
[matrix anchor=west]
at (ballot counts.east)
{
\node {root} [grow=left, level/.style={sibling distance=7em/#1}]
child {node {left}
child {node {}}
child {node {}}}
child {node {right}
child {node {}}
child {node {}}}; \\
};
\end{tikzpicture}
\end{document}
I would like to modify this diagram so that the leaves of max tree
attach to the four entries in second column of ballot counts
(i.e., Alice: 0, …).
In an earlier version of this question, @TomBombadil suggested that I could use at
to explicitly position the leaves of the tree. The problem with that is that then the interior nodes will also have to be positioned explicitly. I was hoping for some way to leverage level distance
and sibling distance
.
Edit: Based on @percusse's comment that "growing the tree and putting nodes left of each sibling" might be an answer, I came up with the following MWE that does, more or less, what I want. (@percusse: Is this what you were getting at, or were you thinking something different?)
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,positioning,matrix,fit}
\begin{document}
\tikzset{
img/.initial={},
img/.value required,
%
image placeholder/.style={%
execute at end node=\phantom{\pgfuseimage{\pgfkeysvalueof{/tikz/img}}}}
}
\pgfdeclareimage[height=7em]{ballot}{fptp-ballot}
\tikzstyle{ballot count} = [matrix of nodes, nodes in empty cells,
right, inner sep=0em,
cells={right, inner sep=0.3333em},
column 1/.append style={%
image placeholder, img=ballot, nodes={scale=0.5}}]
\begin{tikzpicture}[every node/.style={draw},
every matrix/.style={ampersand replacement=\&}]
% Construct a fake ballot count ``leaf'' to measure its height.
\matrix (tmp) [ballot count, overlay,
draw=none, nodes={draw=none}]
{ \\ };
% Measure the height of the fake ``leaf''.
\path let \p{childheight} = ($(tmp.north)-(tmp.south)$) in
% Use the height to set sibling distance so that
% there is no gap between the leaves of the tree.
[level/.append style={sibling distance=2*\y{childheight}/#1,
level distance=2cm*#1}]
node {root} [grow=left]
child {node {left}
child [child anchor=east] foreach \name in {Alice, Bob}
{node (\name's count) [matrix, ballot count]
{ \& \name: $0$ \\ }}}
child {node {right}
child [child anchor=east] foreach \name in {Charlie, Dave}
{node (\name's count) [matrix, ballot count]
{ \& \name: $0$ \\ }}};
% Build the bounding box that contains all leaves.
\node (ballot counts)
[inner sep=0em,
fit=(Alice's count) (Bob's count)
(Charlie's count) (Dave's count)] {};
% Position a matrix to the right of the leaves.
\matrix (ballot box)
[node distance=2 and 2, right=of ballot counts,
matrix of nodes, nodes in empty cells,
cells={image placeholder, img=ballot}]
{
\& \\
\& \\
};
\end{tikzpicture}
\end{document}
This approach has several drawbacks:
-
I have to create a fake leaf, named
tmp
, to measure the height of an arbitrary leaf. It would be better if I could somehow measure the height of a leaf and then retroactively pass this height to thesibling distance
for the tree. -
Since the leaves are no longer within a matrix, I have to explicitly construct a bounding box in order to position
ballot box
where I want it to be. Also, the code for the leaves is now spread among the two subtrees; this complicates the description of the tree edges and non-leaf nodes, and requires that some of the code is duplicated across subtrees.
I guess, in general, the code is not as clean as I would have liked; I'm still hoping that there is a cleaner solution. But perhaps I just need to learn to be less greedy. :)
Best Answer
You can name nodes and then use these names for relative positioning. You can also use absolute position and do all the stuff you usually do with nodes. In the example, the automatically positiones node is red, the relative ones are blue: