3

In an effort to understand @Qrrbrbirlbel answer here: https://tex.stackexchange.com/a/725849/8650

I made this code:

\documentclass{article}
\usepackage{tikz}
\begin{document}
~\\
A text line\\
%
\begin{tikzpicture}[remember picture]
\node[draw] (n) {First Node};
\end{tikzpicture}\\
%
\begin{tikzpicture}[remember picture]
\path (0,0) node[draw, align=center, anchor=north] at (n.south|-0,0) {A new node in a new picture\\that should take up vertical space\\and be centered under First Node};
\end{tikzpicture}\\
%
A new text line\\
\end{document}

Text and two boxes with text

I can only make the node position correct by using the overlay option, but then the picture does not take up space.

The goal is not to avoid overlay, but to understand how the referenced code works and mine does not.

Expected/desired output:

Tex and two vertical centered boxes

2
  • 1
    Hmm, the second node would need to protrude into the margin which is similar to my "TikZ can't reach a point" (or rather TeX will not place any box so that it will protrude the margin). Interesting use-case … Commented Sep 5 at 15:13
  • @Qrrbrbirlbel when there s a remember picture overlay TeX sees an\hbox to 0pt{\vbox to 0pt}, so after the code vskip20pt... etc. It it simialr to drawing something to a LaTeX picture environment.
    – yannisl
    Commented Sep 5 at 15:49

3 Answers 3

4

The \path (0,0) part only worked because the diagram in the linked Q&A doesn't protrude into the left margin. By specifying any coordinate with an x value that is not dependent on another picture, we give PGF/TikZ a reference point that it can use to place the new nodes in relation to. It can now calculate the horizontal distance to an object in another diagram and place the new node a specific instance in relation to that coordinate.

In your case, it does the same, however it can't place the node as far left as it needs because TeX will always place the diagram too far to the right because it needs to actually protrude into the margin.

It is better to use trim left instead of a coordinate. It has the same effect but it allows PGF/TikZ to adjust the horizontal part of the bounding box as needed.

The specific value doesn't matter. Try it with trim left=1000pt: on the first run, the diagram isn't even visible, on the second, it is perfectly centered under the other because it then knows where x = 1000pt is (it's at the start of the TeX line, that's where we put it!) and can then place the new node properly in relation to both the other diagram and its own.

\begin{tikzpicture}[remember picture, trim left=0pt]
\node[draw, align=center, anchor=north] (m) at (n.south)
  {A new node in a new picture\\that should take up vertical space\\and be centered under First Node};
\end{tikzpicture}

This is actually a less extreme of the overlay option applied to the whole diagram without anything actually supplying anything to the bounding box.

The overlay option basically has the same effect as trim left = 0pt, trim right = 0pt, baseline = 0pt and no height/depth.

Here, too, on the second run, PGF/TikZ knows where the origin (or any other coordinate in this remembered picture) is and now knows how objects in other diagrams are placed relevant to it. And this is all that remember picture does, really.

Setting overlay is not necessary to use remember picture.

Usually (and in the examples in the manual, unfortunately) the remember picture is used to connect to an object in another diagram. Only this needs overlay on the connection.

Generalizing this, you can do trim left=0pt, trim right=\linewidth (or \linewidth-\parindent) and then you will have a TikZ diagram that is horizontally aligned, protrudes into the margin as needed and doesn't leave room for anything else on that TeX line.

Of course, you won't always want to have something protrude the margin but then the objects in the other diagram shouldn't be placed where they are.


To reiterate:

Both the horizontal and the vertical position of a TikZ diagram are controlled by TeX, the only thing PGF/TikZ can control is

  • whether PGF/TikZ knows about how TeX has placed a diagram in a previous run (→ remember picture) and
  • what TeX sees from the TikZ picture, i.e. its height, depth and width, these are solely controlled by baseline, overlay (as well as the pgfinterruptboundinbox environment), trim left and trim right as well as the coordinates we reference inside a picture.

We just need to make sure that TeX has enough room to actually place the diagrams as we want.

Code

\documentclass{article}
\usepackage{tikz}
\begin{document}
~\\
A text line\\
%
\begin{tikzpicture}[remember picture]
\node[draw] (n) {First Node};
\end{tikzpicture}\\
%
\null\begin{tikzpicture}[remember picture, trim left=0pt]
\node[draw, align=center, anchor=north] (m) at (n.south)
  {A new node in a new picture\\that should take up vertical space\\and be centered under First Node};
\end{tikzpicture}\\
%
A new text line\\

\noindent
\rule{3cm}{2em}\\
\rule{7cm}{3em}

\bigskip

\noindent\begin{tikzpicture}[remember picture]
\node[draw] (n) {First Node};
\end{tikzpicture}

% no \noindent!
% the space between the nodes is exactly \baselineskip
% because the upper diagram is placed *on* the baseline
% and the second diagram is placed *below* the baseline.
\smash{\color{gray}\rule{.1pt}{\baselineskip}}% 
\begin{tikzpicture}[remember picture, trim left=0pt, baseline=(m.north)]
\node[draw, align=center, anchor=north] (m) at (n.south)
  {A new node in a new picture\\that should take up vertical space\\and be centered under First Node};
\end{tikzpicture}

\end{document}

Output

enter image description here


If you don't want the bottom picture to protrude into the margin, you either have to place the bottom one first:

\documentclass{article}
\usepackage{tikz}
\usepackage[showframe, pass]{geometry}
\begin{document}
\noindent
\begin{tikzpicture}[remember picture]
\node[draw, align=center] (M)
  {A new node in a new picture\\that should take up vertical space\\and be centered under First Node};
\end{tikzpicture}%
\begin{tikzpicture}[remember picture, trim left=0pt, trim right=0pt, baseline=0pt]
\node[draw, anchor=south] (N) at ([yshift=\baselineskip]M.north) {First Node};
\end{tikzpicture}
\end{document}

or deploy some tikzmark tactics:

\documentclass{article}
\usepackage{tikz}
\usepackage[showframe, pass]{geometry}
\usetikzlibrary{tikzmark}
\tikzset{restore nodes from file}
\begin{document}
\noindent
\begin{tikzpicture}[remember picture, trim left=0pt]
\node[draw] at (M.north) {First Node};
\end{tikzpicture}

\noindent
\begin{tikzpicture}[
  remember picture,
  save nodes to file,
  baseline=(M.north)]
\node[draw, align=center, anchor=north, save node] (M)
  {A new node in a new picture\\that should take up vertical space\\and be centered under First Node};
\end{tikzpicture}
\end{document}
0
3

What happens when PGF uses remember picture, overlay, TeX will see a box of 0pt width and height. It is pretty much similar to what happens when you do an rlap or llap or makebox[0pt]{}. So if you want it to take physical space then you need the space back with a vskip or similar. I am actually amazed that there is no PGF build-in solution, for in order to automate it, you need to put the contents in a box, measure it and then add the height a a \vskip, probably best with an aftergroup.

\documentclass{article}
\usepackage[showframe,margin=2cm]{geometry}
\usepackage{tikz}
\begin{document}
\null\parindent0pt
A text line

\begin{tikzpicture}[remember picture,overlay]
\node[draw] (n) {First Node};
\end{tikzpicture}
\vskip13.5pt
\par

\begin{tikzpicture}[remember picture,overlay]
\path (0cm,0) node[draw, align=center, anchor=east,right,xshift=-2cm] at (n.south|-0,0) {A new node in a new picture\\that should take up vertical space\\and be centered under First Node};
\end{tikzpicture}
\vskip30pt
A new text line

\end{document}

See the next example without the overlay in the second node.

\documentclass[10pt]{article}
\usepackage[showframe,margin=2cm]{geometry}
\usepackage{tikz}
\begin{document}
\clearpage\null
\parindent0pt\parskip0pt\nointerlineskip
A text line\\
\begin{tikzpicture}[remember picture,overlay]
\node[draw,outer sep=0pt] (n) {First Node};
\end{tikzpicture}
\vskip6.5pt
\begin{tikzpicture}[remember picture]
\path (0cm,0) node[draw, align=center, anchor=east,right,xshift=-2cm,outer sep=0pt] at (n.south|-0,0) {A new node in a new picture\\that should take up vertical space\\and be centered under First Node};
\end{tikzpicture}

A new text line

\end{document}

If you replace with \begin{tikzpicture}[remember picture,overlay,yshift=-6pt] the vskip can be removed. I normally eye the length, but best is to measure up everything.

3
  • 1
    TikZ can do this itself using the trim keys. Might need a bit of calculation to get the numbers right for centring but shouldn't be too hard. Commented Sep 5 at 17:18
  • Yes, either use the trim, or yshift the amount within the \tikzpicture options. Bottom line though contents have to be put in a box and measured, but then you might have a better idea.
    – yannisl
    Commented Sep 5 at 17:50
  • 1
    @AndrewStacey It doesn't actually. That's what Tikz already does with overlay. Just choose any trim value and let Tikz do it work. (See my answer and comment.) Commented Sep 5 at 18:47
2

This aligns the top picture to the bottom picture. The savebox is used to create the named node, which is stored globally. The origin location is saved (remember picture) at shipout.

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{center}
A text line\\
%
\sbox1{\begin{tikzpicture}[remember picture]
\path (0,0) node (n) [draw, align=center]  {A new node in a new picture\\that should take up vertical space\\and be centered under First Node};
\end{tikzpicture}}%
%
\begin{tikzpicture}[remember picture]
\node[draw,anchor=south] at (n.south|-0,0) {First Node};
\end{tikzpicture}\par
%
\usebox1\par
%
A new text line
\end{center}
\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .