[Tex/LaTex] Repeating a previous figure in a beamer presentation

beamerfloats

What I would like to achieve is, in a later frame, I would want a specific figure to reappear so that I can talk more about it, preferably keeping the original figure counter number. In both occurrences, there would be some text or other stuff accompanying the figure.

This question is similar to this, this, or this.
The solution in the first link seems to be too manual, the second seems nice but I couldn't get it to compile in a beamer document, and the third only touches the issue about figure counter. I tried to use a lrbox environment to save the contents of the figure, but this also does not seem to work:

\documentclass{beamer}
\usepackage{tikz}
\setbeamertemplate{caption}{{\small\bfseries\MakeUppercase{\insertcaptionname~\insertcaptionnumber}}\quad\insertcaption}
\newsavebox{\myfigurebox}

\begin{document}
\begin{frame}{First Frame}
  \begin{lrbox}{\myfigurebox}
    \begin{tikzpicture}
      \draw (0,0) -- ++(75:1 cm) -- ++(-75:1 cm);
    \end{tikzpicture}
  \end{lrbox}

  \begin{figure}
    \centering
    \usebox{\myfigurebox}
    \caption{Description.}
  \end{figure}
  Some text.
\end{frame}
\begin{frame}{Other Frame}
  \begin{figure}
    \centering
    \usebox{\myfigurebox}
    \caption{Description (again).}
  \end{figure}
  More text.
\end{frame}
\end{document}

Other variations are also tried, e.g. surrounding the figure environment with lrbox, moving lrbox outside a frame etc, but these didn't work either.

Right now, the only close solution I had is to use overlay specifications and an \againframe, but this messes up the equation numbers or other counters.

\documentclass{beamer}
\usepackage{tikz}
\setbeamertemplate{caption}{{\small\bfseries\MakeUppercase{\insertcaptionname~\insertcaptionnumber}}\quad\insertcaption}

\begin{document}
\begin{frame}<1>[label=myframe]
\frametitle<1>{First Frame}
\frametitle<2>{Other Frame}
  \begin{figure}
    \centering
    \begin{tikzpicture}
      \draw (0,0) -- ++(75:1 cm) -- ++(-75:1 cm);
    \end{tikzpicture}
    \caption{Description.}
  \end{figure}
\begin{overprint}
\onslide<1>
  Some text. 
\onslide<2>
  More text. % imagine there are some equations or other counters here; the equation numbering would be wrong
\end{overprint}
\end{frame}
% later...
\againframe<2>{myframe}
\end{document}

And this way of "hacking" might get out of control if the same figure is repeated a lot of different times…

Best Answer

  • Having the \begin{lrbox} etc within a frame makes it local to the frame, hence it cannot be recalled later, unless defining a globallrbox environment and using that instead. Such a definition might be done by

    \usepackage{etoolbox}
    \usepackage{letltxmacro}
    \LetLtxMacro{\globallrbox}{\lrbox}
    \LetLtxMacro{\endgloballrbox}{\endlrbox}
    \patchcmd{\globallrbox}{\setbox}{\global\setbox}{}{\showtokens{patching globallrbox failed}}
    
  • The figure numbers will be different because the \caption command is responsible for assigning the numbers, and you have two \caption commands. Probably trying to put the whole figure environment within a box is not going to be easy (and you can't put the caption in the box without the figure environment either), and does not allow you to append the text "(again)" to the caption the second time, so better to fiddle with the labels/counters, as I have done below, extending the example to show the effect on having other figures in between.

In the following I have avoided using the globallrbox environment and moved the lrbox outside of the frame to better illustrate the various approaches. You could probably tidy this up further by providing additional macros/environments for saving and recalling figures.

\documentclass{beamer}

\usepackage{tikz}
\setbeamertemplate{caption}{{\small\bfseries\MakeUppercase{\insertcaptionname~\insertcaptionnumber}}\quad\insertcaption}
\newsavebox{\myfigurebox}

\makeatletter
\newcommand*{\localsetcounter}[2]{% same as LaTeX's \setcounter, but removed the \global
    \@ifundefined {c@#1}{\@nocounterr {#1}}%
    {\csname c@#1\endcsname #2\relax}%
}

\newcommand*{\reusefigurecounter}[1]{%only use this within your figure environment to keep the changes local to that environment, and before the \caption command!
    \localsetcounter{figure}{\getrefnumber{#1}}%
    \let\refstepcounter\@gobble
}
\makeatother

\setcounter{errorcontextlines}{\maxdimen}

\begin{document}

  \begin{lrbox}{\myfigurebox}% the lrbox is outside any frames, but after \begin{document} so that the right fonts and things are set up, and before we first want to use it
    \centering
    \begin{tikzpicture}
      \draw (0,0) -- ++(75:1 cm) -- ++(-75:1 cm);
    \end{tikzpicture}
  \end{lrbox}

\begin{frame}{First Frame}
  \begin{figure}
    \usebox{\myfigurebox}
    \caption{Description.}
    \label{myfigure}
  \end{figure}
  Some text.
\end{frame}

\begin{frame}{Another Frame}
  \begin{figure}
    something else
    \caption{Description.}
  \end{figure}
  Some text.
\end{frame}

\begin{frame}{Other Frame}
  \begin{figure}
    \centering
    \usebox{\myfigurebox}
    \reusefigurecounter{myfigure}
    \caption{Description (again).}
  \end{figure}
  More text.
\end{frame}


\begin{frame}{Another Frame}
  \begin{figure}
    something else entirely
    \caption{Description.}
  \end{figure}
  Some text.
\end{frame}
\end{document}

Update: slightly better interface?

With this version, use a savefigure environment anywhere before you want the first copy of that figure to appear, providing a label in the first argument by which to recall it later. The figure is then called up or recalled with either the \figurefirstuse or \reusefigure command, depending whether it has been shown already or not. The arguments to these commands are the label name and the caption, which need not be the same for all recalls of the same image. It does not currently save any previously used caption(s) automatically.

Not sure if all this stuff is necessary or elegant, but seems to work, now also when there are overlays. Note that there are some limitations with regards to error handling at the very least. It is not easy to automatically determine if the figure is being used for the first time or not because beamer resets the figure counter when it reprocesses the frame for multiple slides (ie overlays and pauses), hence I have given up on trying to do that for now.

There is also a figureandsave environment which saves the figure and shows it in the same place, internally calling the above commands. Takes the same arguments as for \figurefirstuse in this case.

I have left the previous version above as it may be clearer what's actually going on. The following example should hopefully illustrate the new interface better than my description of it.

\documentclass{beamer}

\usepackage{tikz}
\setbeamertemplate{caption}{{\small\bfseries\MakeUppercase{\insertcaptionname~\insertcaptionnumber}}\quad\insertcaption}

\makeatletter
\newcommand*{\localsetcounter}[2]{% same as LaTeX's \setcounter, but removed the \global
    \@ifundefined {c@#1}{\@nocounterr {#1}}%
    {\csname c@#1\endcsname #2\relax}%
}

\newcommand*{\reusefigurecounter}[1]{%#1=label, only use this within your figure environment to keep the changes local to that environment, and before the \caption command!
    \localsetcounter{figure}{\getrefnumber{#1}}%
    \let\refstepcounter\@gobble
}

\usepackage{etoolbox}
\usepackage{letltxmacro}
\LetLtxMacro{\globallrbox}{\lrbox}
\LetLtxMacro{\endgloballrbox}{\endlrbox}
\patchcmd{\globallrbox}{\setbox}{\global\setbox}{}{\showtokens{patching globallrbox failed}}

\newenvironment{figureandsave}[2]{% parameters #1=label name,#2=caption
    \def\currsavefigurecaption{#2}%
    \def\currsavefigurename{#1}%
    \begin{savefigure}{#1}}%
    {\end{savefigure}%
    \figurefirstuse{\currsavefigurename}{\currsavefigurecaption}%
    }

% can use savefigure within or outside of a frame, but always after \begin{document}
\newenvironment*{savefigure}[1]{% parameters #1=label name
    \expandafter\newbox\expandafter{\csname savedfigure#1box\endcsname}%use \newbox instead of \newsavebox as we have to allow beamer to process the same figure multiple times if a frame contains multiple slides
    \begin{globallrbox}{\csname savedfigure#1box\endcsname}%
    }
    {\end{globallrbox}}

% use \figurefirstuse the first time you want to show the figure
\newcommand\figurefirstuse[2]{% parameters #1=label name,#2=caption
    \@ifundefined{savedfigure#1box}{\errmessage{figure #1 hasn't been saved yet}}{%
    % calling this multiple times for the same figure will result in it having multiple numbers assigned to it and multiply defined label issues
    \begin{figure}%
        \usebox{\csname savedfigure#1box\endcsname}%
        \caption{#2}%
        \label{savedfigure#1}%
    \end{figure}}}

% use \reusefigure if the figure has already been shown, and you want to show it again
\newcommand\reusefigure[2]{% parameters #1=label name, #2=new caption
    \@ifundefined{savedfigure#1box}{\errmessage{figure #1 hasn't been saved yet}}{%
    % no easy way to test if the figure has been used or not yet, but will be numbered Figure 0 if not!
    \begin{figure}%
        \usebox{\csname savedfigure#1box\endcsname}%
        \reusefigurecounter{savedfigure#1}%
        \caption{#2}%
    \end{figure}}}
\makeatother


\begin{document}

\begin{savefigure}{test}
    World.
\end{savefigure}

\begin{frame}{First Frame}
    \begin{figureandsave}{myfigure}{Description.}
        \centering
        \begin{tikzpicture}
            \draw (0,0) -- ++(75:1 cm) -- ++(-75:1 cm);
        \end{tikzpicture}
    \end{figureandsave}
    \pause
    Some text after a pause.
\end{frame}

\begin{frame}{In between}
    \begin{figure}
        A different figure
        \caption{For testing}
    \end{figure}

    \pause
    \figurefirstuse{test}{Hello}
    \reusefigure{test}{Hello again}
\end{frame}

\begin{frame}{Other Frame}
    \reusefigure{myfigure}{Description (again).}

    \begin{figure}
        A second figure on the same frame.
        \caption{With a number}
    \end{figure}

    More text.
\end{frame}

\end{document}
Related Question