Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Here is my code and I will post a picture showing what it generates. I will manually draw in red where I would like to draw a line from. I am producing a family tree and I need to connect lines from one person to another. Could anyone help me get this fixed? I want to draw a line from node (kid 1) to node (kid 2), but I want the line from node (kid 1) to originate from the bottom center, not from the right as it is doing. DESPITE EXPLICITLY GIVING SOUTH:8, the line starts from the east side (or right side) of the node.

NOTE: Due to the vastness of these trees, I have to compartmentalize them, meaning there are going to be several different tikzpicture environments, hence the overlay lines in a separate tikzpicture environment.

REASONS:

  1. Adjust sibling distance
  2. Adjust level distance
  3. Move various trees (families) around (scope provides this functionality)

The scope functionality is great for moving things around, but does not give me the flexibility to squeeze trees together when possible—as far as I know (ie some families are smaller than others, which means those respective nodes could be placed closer together).

--thanks in advance!

\documentclass[12pt]{article}
\usepackage[landscape]{geometry}
\usepackage{fontspec}
\usepackage{tikz}
\usepackage{tikz-qtree}
\tikzset{font=\small,
every tree node/.style={align=center, anchor=north},text centered}
\begin{document}
\thispagestyle{empty}%suppress page number
\tikzstyle{every picture}+=[remember picture, overlay]


\begin{tikzpicture}[level distance=6cm,sibling distance=6cm,scale=1,]

\Tree
[.Parent 
    \node[](kid1){Kid 1};  
    \node[](kid2){Kid 2}; ]

%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{scope}[xshift=9cm,yshift=-6cm]
\Tree
[.Parent
    \node(kid3){Kid 3};  
    \node(kid4){Kid 4}; ]
    \end{scope}
\end{tikzpicture}


\begin{tikzpicture}[overlay]
    %\draw[->] (kid1.south)--(kid2.south);
    \draw[->] (kid1) .. controls +(south:8cm) and +(south:3cm) ..(kid4);

\end{tikzpicture}


\end{document}

share|improve this question
2  
Do you have a particular reason for using separate tikzpictures with overlay, remember picture, instead of just including the \draw commands in the same tikzpicture? The latter case works for me. –  Jake May 2 '12 at 16:14
    
Yes actually. Thanks for asking. I have a rather large family tree starting with my great grandparents, followed by my grandparents, followed by my parents. As the tree splits off, it is difficult to contain brothers and sisters of my great grandparents or grandparents in the same tree. I have the trees separated, but some names are in both trees and I would like to draw a dotted line (red in color perhaps) between those names to indicate that they are the same person. –  macmadness86 May 5 '12 at 9:36
    
Very odd. I can't even reproduce your output (for me the arrow starts about 1in to the left of "Parent" in the first tree.) TL 2011 fully updated. –  Alan Munn May 8 '12 at 12:36
    
I am using "This is TeX, Version 3.1415926 (TeX Live 2011)" on Mac OS X Lion 10.7.2. I just did a copy and paste from this blog and compiled it again. The result is again as seen above. I don't get why. The controls specifically say "south:8cm" the line should therefore start below the node. ...my thoughts: "unless the node is somehow misplaced". any experts want to spread some tex wisdom here? –  macmadness86 May 8 '12 at 15:30
    
This seems to always affect the leftmost node in a tree. \draw (kid2) -- (kid4) works fine, but \draw (kid2) -- (kid3) does not. The location of the first node seems to be offset somehow. Try \draw (kid1.south west) rectangle (kid1.north east);: The resulting rectangle has the right size, but the wrong position. –  Jake May 8 '12 at 15:36
add comment

1 Answer

up vote 11 down vote accepted
+50

I think this question reveals a bug in the tikz-qtree package. I've contacted the author about it and pointed him to this question. I'm confident that he will fix it. In the meantime, here's a temporary solution.

To understand the problem, we need to know a little about how tikz-qtree works. It renders a tree in a recursive fashion. Each subtree is actually a whole new pgfpicture. This would normally cause huge problems, except that the code squirrels away the sub-pictures in boxes. Since the position of each subtree depends on its subtrees and siblings, the subpictures' final positions cannot be known when they are constructed. So there's a system for shifting them around afterwards.

Where this causes difficulties is with referring to coordinates (nodes) inside the subpictures. The code is quite cunning: it remembers the later transformations and recursively applies them to adjust the apparent node coordinates to its actual node coordinates. So to the user, the fact that there are these subpictures is not at all obvious.

This works well until you start trying to refer to nodes in other pictures. The code for recursively applying transformations bails out at that step saying "This isn't a node in a subpicture of the current picture, we'll let PGF deal with it as it would have done originally.". However, because subpictures get shifted around after they are constructed, PGF gets it wrong about where it is. The correct (perhaps) method would be to recursively apply the shifts up to the root picture of that node and then use the PGF mechanism to relate the root picture to the current picture. That's what my code below tries to do. It might break something else, though.

(Although tikz-qtree's subpicture stuff is quite sophisticated, this does show - yet again - the dangers of using subpictures. I would therefore recommend you reconsider the necessity of using remember picture and overlay. Can't you put it all in one grand tikzpicture with scopes for the separate pieces?)

To illustrate this, here's a picture of your code (I added one more child to the first tree in testing) with all the various pictures, their origins, and their ancestors indicated. The green dots are where PGF thinks the subpictures are. The green lines indicate the hierarchy. The blue lines show where each child node's subpicture's origin lies (the ones without blue lines are perfectly aligned with their origins, it might be hard to see at this resolution but one blue line actually leaves the picture altogether).

tikz qtree subpicture origins

Here's the full code for the above. Within it is a modified version of the routine for figuring out node positions. I've indicated that segment and made it so that it can be just cut-and-pasted without all the rest of the junk around it. However, I'm leaving the junk in because I think it can be quite useful in seeing how this works.

\documentclass[12pt]{article}
\usepackage[landscape]{geometry}
\usepackage{tikz}
\usepackage{tikz-qtree}

\makeatletter

\def\parentof#1{%
  \expandafter\let\expandafter\@pid\csname pgf@sh@pi@pgfid#1\endcsname
  \ifx\@pid\relax
  \def\pid{}%
  \else
  \expandafter\@parentof\@pid\relax
  \fi
}

\def\parentofnode#1{%
  \expandafter\let\expandafter\@pid\csname pgf@sh@pi@#1\endcsname
  \ifx\@pid\relax
  \def\pid{}%
  \else
  \expandafter\@parentof\@pid\relax
  \fi
}

\def\@parentof pgfid#1\relax{%
\def\pid{#1}}

\tikzdeclarecoordinatesystem{picid}{%
  \pgfutil@in@{pgfid}{#1}%
  \ifpgfutil@in@%
  \def\@picid{#1}%
  \else
  \def\@picid{pgfid#1}%
  \fi
  \pgfsys@getposition{\@picid}\save@orig@pic%
  \pgfsys@getposition{\pgfpictureid}\save@this@pic%
  \pgf@process{\pgfpointorigin\save@this@pic}%
  \pgf@xa=\pgf@x
  \pgf@ya=\pgf@y
  \pgf@process{\pgfpointorigin\save@orig@pic}%
  \advance\pgf@x by -\pgf@xa
  \advance\pgf@y by -\pgf@ya
  }%

\def\unwind@subpic#1{%
% is #1 the current picture?
\edef\subpicid{#1}%
\ifx\subpicid\pgfpictureid
% yes, we're done
\else
% does #1 have a parent picture?
\expandafter\ifx\csname pgf@sh@pi@#1\endcsname\relax
% no, the original node was not inside the current picture
\pgf@xa=\pgf@x
\pgf@ya=\pgf@y
\pgfsys@getposition{\pgfpictureid}\pgf@shape@current@pos
\pgf@process{\pgfpointorigin\pgf@shape@current@pos}%
\advance\pgf@xa by-\pgf@x%
\advance\pgf@ya by-\pgf@y%
\pgf@process{\pgfpointorigin\subpic@parent@pos}%
\advance\pgf@xa by \pgf@x%
\advance\pgf@ya by \pgf@y%
\pgf@x=\pgf@xa
\pgf@y=\pgf@ya
\else
% yes, apply transform, save picture location, and move up to parent picture
\pgfsys@getposition{\csname pgf@sh@pi@#1\endcsname}\subpic@parent@pos%
{%
  \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}%
  \pgf@pos@transform{\pgf@x}{\pgf@y}%
  \global\pgf@x=\pgf@x
  \global\pgf@y=\pgf@y
}%
\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}%
\fi
\fi
}


\def\pgf@shape@interpictureshift#1{%
\def\subpic@parent@pos{\pgfpointorigin}%
\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}%
}

\makeatother

\tikzset{font=\small,
every tree node/.style={align=center, anchor=north},text centered}
\begin{document}
\thispagestyle{empty}%suppress page number
\tikzstyle{every picture}+=[remember picture,overlay]


\begin{tikzpicture}[remember picture,level distance=6cm,sibling distance=2cm,scale=1,]

\Tree
[.Parent
    \node[](kid1){Kid 1};  
    \node[](kid12){Kid 12};  
    \node[](kid2){Kid 2}; ]

%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{scope}[xshift=9cm,yshift=-6cm]
\Tree
[.Parent
    \node(kid3){Kid 3};  
    \node(kid4){Kid 4}; ]
    \end{scope}
\draw[ultra thick] (kid1.south west) rectangle (kid1.north east);
\draw[ultra thick] (kid12.south west) rectangle (kid12.north east);
\draw[ultra thick] (kid2.south west) rectangle (kid2.north east);
\end{tikzpicture}

\bigskip

\begin{tikzpicture}[remember picture,overlay]
    %\draw[->] (kid1.south)--(kid2.south);
    \draw[->] (kid1) .. controls +(south:8cm) and +(south:3cm) ..(kid4);
\draw[red] (kid1.south west) rectangle (kid1.north east);
\draw[red] (kid12.south west) rectangle (kid12.north east);
\draw[red] (kid2.south west) rectangle (kid2.north east);
\end{tikzpicture}

\bigskip

\begin{tikzpicture}[remember picture,overlay]
\coordinate (start) at (current page.north east);
\foreach \id in {1,...,18} {
\fill[green]  (picid cs:\id) circle[radius=2pt];
\draw[red,->] (start) ++(-5cm,-5-\id cm) node (a\id) {\id} (a\id) to[out=180,in=0] (picid cs:\id);
}
\foreach \id in {1,...,18} {
\parentof{\id}%
\ifx\pid\empty
\else
\draw[green,->,dashed] (a\id.south east) to[out=0,in=0] (a\pid.north east);
\fi
}
\foreach \nd in {kid1,kid12,kid3,kid4} {
\parentofnode{\nd}%
\ifx\pid\empty
\else
  \draw[blue,->] (\nd) to[out=90,in=-90] (picid cs:\pid);
\fi
}
\end{tikzpicture}

\end{document}

(Oh, and I removed the fontspec package as it wasn't explicitly needed.)

On second thoughts, here's the necessary code without the junk.

\documentclass[12pt]{article}
\usepackage[landscape]{geometry}
\usepackage{tikz}
\usepackage{tikz-qtree}

\makeatletter

\def\unwind@subpic#1{%
% is #1 the current picture?
\edef\subpicid{#1}%
\ifx\subpicid\pgfpictureid
% yes, we're done
\else
% does #1 have a parent picture?
\expandafter\ifx\csname pgf@sh@pi@#1\endcsname\relax
% no, the original node was not inside the current picture
\pgf@xa=\pgf@x
\pgf@ya=\pgf@y
\pgfsys@getposition{\pgfpictureid}\pgf@shape@current@pos
\pgf@process{\pgfpointorigin\pgf@shape@current@pos}%
\advance\pgf@xa by-\pgf@x%
\advance\pgf@ya by-\pgf@y%
\pgf@process{\pgfpointorigin\subpic@parent@pos}%
\advance\pgf@xa by \pgf@x%
\advance\pgf@ya by \pgf@y%
\pgf@x=\pgf@xa
\pgf@y=\pgf@ya
\else
% yes, apply transform, save picture location, and move up to parent picture
\pgfsys@getposition{\csname pgf@sh@pi@#1\endcsname}\subpic@parent@pos%
{%
  \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}%
  \pgf@pos@transform{\pgf@x}{\pgf@y}%
  \global\pgf@x=\pgf@x
  \global\pgf@y=\pgf@y
}%
\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}%
\fi
\fi
}


\def\pgf@shape@interpictureshift#1{%
\def\subpic@parent@pos{\pgfpointorigin}%
\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}%
}

\makeatother


\tikzset{font=\small,
every tree node/.style={align=center, anchor=north},text centered}
\begin{document}
\thispagestyle{empty}%suppress page number
\tikzstyle{every picture}+=[remember picture,overlay]


\begin{tikzpicture}[remember picture,level distance=6cm,sibling distance=2cm,scale=1,]

\Tree
[.Parent
    \node[](kid1){Kid 1};  
    \node[](kid2){Kid 2}; ]

%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{scope}[xshift=9cm,yshift=-6cm]
\Tree
[.Parent
    \node(kid3){Kid 3};  
    \node(kid4){Kid 4}; ]
    \end{scope}
\end{tikzpicture}

\begin{tikzpicture}[remember picture,overlay]
    %\draw[->] (kid1.south)--(kid2.south);
    \draw[->] (kid1) .. controls +(south:8cm) and +(south:3cm) ..(kid4);
\end{tikzpicture}
\end{document}

And here's the result of that:

qtrees with correct anchoring

share|improve this answer
3  
Fantastic investigation and great explanation! –  percusse May 9 '12 at 11:39
2  
@percusse Thanks. I should add some attribution: Jake's comment on the question about the rectangles gave me a huge clue as to what was going on. And having worked on tex.stackexchange.com/q/39103/86 meant that I already had an idea how tikz-qtree worked. –  Andrew Stacey May 9 '12 at 11:45
    
First of all, I would like to say that your answer is great. I especially appreciate that you have contacted the author of tikz regarding this issue. I hope that he resolves this soon to avoid the extra code. I failed to mention why I need the separate tikzpicture environments in my original post and I will later update it with specific reasons as I remember them. Anyway, superb job. I appreciate people like you who are so thorough. –  macmadness86 May 12 '12 at 20:20
1  
@macmadness86 Minor point: the author of tikz-qtree is not the author of tikz. I happened to already be in contact with him which made it easier to pass on this matter to him. Regarding your update, the ability to change the level distance and sibling distances on a per-level basis is coming soon - thanks to another question on this site! –  Andrew Stacey May 13 '12 at 18:10
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.