[Tex/LaTex] TikZ tree whose children are located at specified positions

tikz-pgftikz-trees

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}

minimal working example of progress toward tree growing into matrix

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}

enter image description here

This approach has several drawbacks:

  1. 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 the sibling distance for the tree.

  2. 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:

\documentclass[parskip]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}
\usetikzlibrary{trees,fit,shapes,calc}

\begin{document}

\begin{tikzpicture} 
\tikzstyle{level 1}=[sibling distance=40mm] 
\tikzstyle{level 2}=[sibling distance=20mm] 
\tikzstyle{level 3}=[sibling distance=10mm] 
 \node (S) {S} 
      child{node {N}  child{node (M1) {mary}} }
      child{node {VP}     
            child{node[fill=red,circle,text=green] (V) {V} child{node{brought }}}
            child{node[fill=blue,text=yellow,circle] (NP2) at ($(V)+(2,-2)$) {NP}  child{node{D} child{node (A2){a}}} child{node{N} child{node (C2) {cat}}}  }
            child{node[fill=blue,text=yellow,circle] (PP) at ($(NP2)+(4,1)$) {PP}  child{node{IN} child{node{to}}}     child{node{N}  child{node{school}}} }                          }
;
\end{tikzpicture}

\end{document}

enter image description here