40

The problem of stuff jumping in beamer is a common one, (see e.g. Avoiding jumping frames in beamer) and we've seen it here from time to time (I imagine the "related" list at the side will be quite long on this one!). With a TikZ picture, one method of fixing this is to manually specify the bounding box. I'd like to get rid of the word "manually" in that sentence!

This oughtn't to be too hard to implement, my imagined scheme would involve writing out the final bounding box to the .aux file so that it is available for use at the start of the tikzpicture environment.

First question: has anyone implemented this yet?

If (as I suspect) not, I'd like the second question to be "could someone do it for me?" but that's not in the spirit of the site. So here's my idea for how this would be done:

  1. On each slide of a frame, the tikzpicture saves its bounding box.
  2. At the end of the frame, the tikzpicture writes out the largest bounding box to the .aux file (note that the largest may not be the last).
  3. On subsequent runs, the way that the bounding box was written out tells the tikzpicture what its bounding box ought to be and it draws a \useasboundingbox path accordingly (at the start of the picture).

So second question, which is a bit open-ended but hopefully I can get away with it: what are the pitfalls of the above.

And third question, (yup, I'm really stretching things here): if there is a part of the above that someone just happens to know, please put the code in an answer. I'm sufficiently confident of my TeX skills that I'll be able to take the pieces and put them together, and also to adapt pieces. So I'm not asking anyone to code this for me, just help me find the pieces needed.

(though if anyone does code it ...)

Here's a very simple example of what I mean (you have to compile it to see, though). As everyone knows, the earth goes around the sun. But not in this presentation. Cosmology according to this presentation is a ... little bit weird.

\documentclass{beamer}
\usepackage{tikz}

\begin{document}
\begin{frame}
\begin{tikzpicture}
\foreach \k in {1,...,8}
{
  \fill<\k>[orange] (0,0) circle[radius=.5];
  \fill<\k>[blue] (\k * 45:3) circle[radius=.2] coordinate (a);
}
\draw (0,0) -- (a);
\end{tikzpicture}
\end{frame}
\end{document}

Note: The above example is a very simple example, it is not a live example. I don't want an answer that fixes that particular example, I'm looking for a system of fixing every possible example.

3
  • 2
    Would you mind adding an example that's suitably jumpy? Commented May 20, 2011 at 18:37
  • You also have to check on subsequent runs whether the largest bounding box has changed.
    – Caramdir
    Commented May 20, 2011 at 20:31
  • If you only have a TikZ picture on the slide, you could also do absolute positioning on the page (by using a coordinate system relative to (current page.center)).
    – Caramdir
    Commented May 20, 2011 at 20:33

5 Answers 5

25

I finally (!) got round to implementing this. It's now part of the package rulercompass, since it seems particularly useful with that package.


Here's my code:

\documentclass{beamer}
% \url{http://tex.stackexchange.com/q/18704/86}
\usepackage{tikz}

\newcounter{jumping}
\resetcounteronoverlays{jumping}

\makeatletter
\tikzset{
  stop jumping/.style={
    execute at end picture={%
      \stepcounter{jumping}%
      \immediate\write\pgfutil@auxout{%
        \noexpand\jump@setbb{\the\value{jumping}}{\noexpand\pgfpoint{\the\pgf@picminx}{\the\pgf@picminy}}{\noexpand\pgfpoint{\the\pgf@picmaxx}{\the\pgf@picmaxy}}
      },
      \csname jump@\the\value{jumping}@maxbb\endcsname
      \path (\the\pgf@x,\the\pgf@y);
      \csname jump@\the\value{jumping}@minbb\endcsname
      \path (\the\pgf@x,\the\pgf@y);
    },
  }
}
\def\jump@setbb#1#2#3{%
  \@ifundefined{jump@#1@maxbb}{%
    \expandafter\gdef\csname jump@#1@maxbb\endcsname{#3}%
  }{%
    \csname jump@#1@maxbb\endcsname
    \pgf@xa=\pgf@x
    \pgf@ya=\pgf@y
    #3
    \pgfmathsetlength\pgf@x{max(\pgf@x,\pgf@xa)}%
    \pgfmathsetlength\pgf@y{max(\pgf@y,\pgf@ya)}%
    \expandafter\xdef\csname jump@#1@maxbb\endcsname{\noexpand\pgfpoint{\the\pgf@x}{\the\pgf@y}}%
  }
  \@ifundefined{jump@#1@minbb}{%
    \expandafter\gdef\csname jump@#1@minbb\endcsname{#2}%
  }{%
    \csname jump@#1@minbb\endcsname
    \pgf@xa=\pgf@x
    \pgf@ya=\pgf@y
    #2
    \pgfmathsetlength\pgf@x{min(\pgf@x,\pgf@xa)}%
    \pgfmathsetlength\pgf@y{min(\pgf@y,\pgf@ya)}%
    \expandafter\xdef\csname jump@#1@minbb\endcsname{\noexpand\pgfpoint{\the\pgf@x}{\the\pgf@y}}%
  }
}
\makeatother

\begin{document}
\begin{frame}
\begin{tikzpicture}[stop jumping]
\foreach \k in {1,...,7}
{
   \fill<\k>[orange] (0,0) circle[radius=.5];
   \fill<\k>[blue] (\k * 45:\k) circle[radius=.2] coordinate (a);
}
\draw (0,0) -- (a);
\end{tikzpicture}
\end{frame}

\end{document}

Here's how it works. We have a global counter, jumping, which is stable under overlays. That is, beamer implements overlays by reprocessing the code several times. Normally, each time through would lead to a counter being incremented, but so long as beamer is told, it can take that into account and resets the counter for each run through. The upshot of this is that this counter can be used to label (in the non-TeXnical sense) tikzpictures in such a way that the different copies of the picture on different slides get the same label.

We use that label to save the bounding box of each version of the picture to the aux file. We do this at the end of the picture to ensure that we get the right bounding box. When the aux file is read in next time, it computes the maximum and minimum extends of the various bounding boxes for this picture and saves these as PGF points. Back in the picture, we use these computed points to adjust our bounding box to the maximum extent.

Some further remarks:

  1. We save the bounding box of each picture before comparing it with the saved maximum. This means that the maximum is always computed on the actual sizes of the pictures and doesn't take into account any previous maximum. This means that it is sensitive to changes in the picture, which is how it should be.

  2. The bounding box is adjusted by placing coordinates in the picture. This is to ensure that the picture doesn't jump around inside its box. Simply resetting the bounding box lengths would ensure that the picture took up the same amount of space on each slide, but not that the picture stayed in the same place in that box.

7
  • Just out of curiosity: Does this \begin{tikzpicture}[remember picture,overlay] \pgftransformshift{(current page.center)} \foreach \k in {1,...,8} { \fill<\k>[orange] (0,0) circle[radius=.5]; \fill<\k>[blue] (\k * 45:3) circle[radius=.2] coordinate (a); } \draw (0,0) -- (a); \end{tikzpicture} too much of a hack? I'm not even sure why it works. :)
    – percusse
    Commented Apr 12, 2012 at 9:56
  • @percusse That ensures that your picture is positioned absolutely on the page, which is fine if the picture is all that there is. However, there may be occasions where the picture is only part of a slide and should be placed according to the normal flow, but should take up enough space that it doesn't jump around. What reminded me of it was this question: tex.stackexchange.com/q/51582/86 where the picture on the left jumps slightly due to the changing size of the nodes. Commented Apr 12, 2012 at 10:03
  • Ah, sure. What I wanted to show that if you fix something in the recurring picture relative to page and draw everything to that anchor, you can get a fixed position of elements. Having said that, I see where you are going and indeed this hack might get messy after sometime. For the record :P, this is a little bit cleaner \begin{tikzpicture}[remember picture,overlay] \coordinate (c) at (current page.center); \foreach \k in {1,...,8} { \fill<\k>[orange] (c) circle[radius=.5]; \fill<\k>[blue] (c)++(\k * 45:3) circle[radius=.2] coordinate (a); } \draw (c) -- (a);
    – percusse
    Commented Apr 12, 2012 at 10:09
  • I prefer the first version because it can be added to a picture without needing to change anything in the picture, so is easier if the picture is cut-and-pasted from elsewhere. You should post it as an answer. So long as it is clear under what conditions it could be used then it's a valid answer. Commented Apr 12, 2012 at 10:16
  • This is really great! Can I include your code in a package (with attribution and a link to this question)? See github.com/rvf0068/graphiso.sty, where I was using a frame and a bounding box to avoid jumping. Your code works well there.
    – rvf0068
    Commented Apr 12, 2012 at 23:50
17

The \overprint, \onslide duo also seems to do the job:

\documentclass{beamer}
\usepackage{tikz}

\begin{document}
\begin{frame}
\begin{overprint}
\begin{tikzpicture}
\foreach \k in {1,...,8}
{
  \onslide<\k>{\fill[orange] (0,0) circle[radius=.5];
  \fill[blue] (\k * 45:3) circle[radius=.2];}
}
\end{tikzpicture}
\end{overprint}
\end{frame}

\end{document}

EDIT: in fact, in this particular example, there's no need to use overprint.

3
  • Thanks for the answer, but I'm not really interested in this particular example. I want a robust method that will work for just about any tikz picture. Commented May 20, 2011 at 21:02
  • Actually, I take that back. I need to investigate \onslide a little more carefully. Commented May 20, 2011 at 21:18
  • Investigated, and found an example that doesn't work with \onslide: namely, where the varying path also defines a coordinate. Commented May 21, 2011 at 17:04
15

This solution was updated on another post.

One particularity of Andrew's MWE is that the position of the elements change on each slide, which makes it indeed difficult to calculate the resulting bounding box – which his own answer covers perfectly.

However, in many cases the goal is just some sort of piecewise uncovering of the elements of a beamer graphics. In such situations I have stopped using overlay specifications for the commands itself (\node<...>, \fill<...>), but instead always draw all elements, but hidden. To specify the visibility, I use a visible on=<...> TikZ style as follows:

\documentclass{beamer}
\usepackage{tikz}

% Keys to support piece-wise uncovering of elements in TikZ pictures:
% \node[visible on=<2->](foo){Foo}
%
% Internally works by setting opacity=0 when invisible, which has the 
% adavantage (compared to \node<2->(foo){Foo} that the node is always there, hence
% always consumes space that (foo) is always available.
%
% The actual command that implements the invisibility can be overriden
% by altering the style invisible. For instance \tikzsset{invisible/.style={opacity=0.2}}
% would dim the "invisible" parts. Alternatively, the color might be set to white, if the
% output driver does not support transparencies (e.g., PS) 
%
\tikzset{
  invisible/.style={opacity=0},
  alt/.code args={<#1>#2#3}{%
    \alt<#1>{\pgfkeysalso{#2}}{\pgfkeysalso{#3}} 
  },
  visible on/.style={alt={#1{}{invisible}}},
}

\begin{document}

\begin{frame}{Uncovering TikZ elements piecewise (1)}
  Invisble –– but already taking space:
  \par
  \bigskip
  \fbox{  % to visualize bounding box
  \begin{tikzpicture}[every node/.style={fill=red!30, draw=red}]
    \node{Foo}
      child[visible on=<2->]{node {Bar}}
      child[visible on=<3->]{node {Baz}}
    ;  
  \end{tikzpicture}
  }
\end{frame}

\begin{frame}{Uncovering TikZ elements piecewise (2)}
  % Change "invsibility" style to dimmed  
  \tikzset{invisible/.style={opacity=0.3}}
  Dimmed –– and obviously taking space:
  \par
  \bigskip

  \fbox{  % to visualize bounding box
  \begin{tikzpicture}[every node/.style={fill=red!30, draw=red}]
    \node{Foo}
      child[visible on=<2->]{node {Bar}}
      child[visible on=<3->]{node {Baz}}
    ;  
  \end{tikzpicture}
  }
\end{frame}

\end{document}

visible on=< ovspec > is implemented by applying the style invisible on all slides that are not contained in ovspec. The default implementation of invisible just sets opacity=0; however, as demonstrated in the example, this can easily be changed so other kinds of "invsibility" can easily be installed (dimming, gray filling, ...).

Note: If ovspec itself contains a comma, either it or the complete argument has to be put inside curly braces (like in, visible on=<{1,3-4,8}> or `visible on={<1,3-4,8>}) in order to not confuse the pgfkeys parser.

This approach (besides being, IMHO, much better readable) also has another advantage: All named elements (especially nodes) are always there, so you can use them for coordinate calcualtions even on slides they are not visible.

4
  • 1
    I would create fake accounts to upvote this answer as many times as I used it. Thanks a lot. One limitation though: you cannot use complex arguments as <1-3, 5-> unless you change your macro to change the <...> for {...}. Comment if you want more precisions.
    – Clément
    Commented May 21, 2015 at 21:09
  • 1
    Thanks @Clément :-) Honestly, I almost forgot about this answer because of this newer one, which basically uses the same trick, but with some slight improvements for complex expressions. I probably should add this here as well.
    – Daniel
    Commented May 22, 2015 at 11:04
  • Oh, cool. I proposed an edition to your answer, providing a link to this new solution.
    – Clément
    Commented May 22, 2015 at 15:12
  • Is there a way to make that the default for the (more concise) \node<2->... syntax, and offer an alternative for when one really wants to have the not not be there? Commented Apr 21, 2016 at 8:19
12

use it this way:

\begin{tikzpicture}
\foreach \k in {1,...,8}
{
  \uncover<\k>{\fill[orange] (0,0) circle[radius=.5];}
  \uncover<\k>{\fill[blue] (\k * 45:3) circle[radius=.2];}
}
\end{tikzpicture}

or shorter

\begin{tikzpicture}
\foreach \k in {1,...,8}
{
  \uncover<\k>{\fill[orange] (0,0) circle[radius=.5];
               \fill[blue] (\k * 45:3) circle[radius=.2];}
}
\end{tikzpicture}
4
  • Thanks for the answer, but I'm not really interested in this particular example. I want a robust method that will work for just about any tikz picture. Commented May 20, 2011 at 21:02
  • Actually, I take that back. I need to investiage \uncover a little more carefully. Commented May 20, 2011 at 21:17
  • uncover_is_ "robust" ...
    – user2478
    Commented May 21, 2011 at 5:10
  • Herbert, 'fraid not. See my modified example. Because I'm now specifying a coordinate on a path which changes with the frame, using \uncover will only remember the last definition regardless of the frame. Of course, I could adapt the example to not do this, but that's not what I'm trying to do here. Commented May 21, 2011 at 17:03
6

This barely counts as an answer, but here goes:

  1. Not a problem. I think you can use the execute at end picture key here. The only pitfall I can think of is that the slide number is surprisingly hard to get at. See my answer here: LaTeX Beamer: How to get distinct page numbers when using overlays?.

  2. Because of the non-standard way beamer expands the frame code, I think getting code executed at the end of the frame is hard. But you could take care of this in step 1 by writing a bounding-box line to the aux file at the first slide and then each time the bounding box gets bigger. Then when the aux file is read the last bounding-box line is the biggest.

  3. This shouldn't be a problem either. I'd try the execute at begin picture key.

1
  • I think it counts. Commented May 20, 2011 at 20:56

You must log in to answer this question.

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