16

This is a follow-up question from Use tikz external feature with beamer \only . Andrew's answer there is excellent, and does the job (almost) perfectly.

The rough edge I have found was when I tried to use \tikzsetnextfilename together with it.

Why would I want to do that? Because in beamer, one might need to reorder one's slides, or to add an overlay, etc. In this case, most pictures will need to be regenerated, even if they weren't changed.

What is the problem? The following M(n)WE shows the usual way of using \tikzsetnextfilename.

\documentclass{beamer}

\usepackage{tikz}

\usetikzlibrary{external}

\makeatletter
\tikzset{
  beamer externalizing/.style={%
    execute at end picture={%
      \tikzifexternalizing{%
        \ifbeamer@anotherslide
        \pgfexternalstorecommand{\string\global\string\beamer@anotherslidetrue}%
        \fi
      }{}%
    }%
  },
  external/optimize=false
}
\makeatother

\tikzset{every picture/.style={beamer externalizing}}

\tikzexternalize

\begin{document}

\begin{frame}
  \tikzsetnextfilename{figure}
  \only<1>{Image 1:}
  \only<2>{Image 2:}
  \begin{tikzpicture}
    \only<1>{\node {Overlay 1};}
    \only<2>{\node {Overlay 2};}
  \end{tikzpicture}
\end{frame}

\end{document}

This doesn't work. I'm not pro enough with latex, beamer and tikz mechanics, but I suspect that the problem is tikz externalize trying to generate the two pictures with the same file name.

And indeed, the following code snippet does work.

\documentclass{beamer}

\usepackage{tikz}

\usetikzlibrary{external}

\makeatletter
\tikzset{
  beamer externalizing/.style={%
    execute at end picture={%
      \tikzifexternalizing{%
        \ifbeamer@anotherslide
        \pgfexternalstorecommand{\string\global\string\beamer@anotherslidetrue}%
        \fi
      }{}%
    }%
  },
  external/optimize=false
}
\makeatother

\tikzset{every picture/.style={beamer externalizing}}

\tikzexternalize

\begin{document}

\begin{frame}
  \only<1>{\tikzsetnextfilename{figure-1}}
  \only<2>{\tikzsetnextfilename{figure-2}}
  \only<1>{Image 1:}
  \only<2>{Image 2:}
  \begin{tikzpicture}
    \only<1>{\node {Overlay 1};}
    \only<2>{\node {Overlay 2};}
  \end{tikzpicture}
\end{frame}

\end{document}

Now it is still far from perfect. For example, it requires to know precisely how many overlays are needed for every picture.

Hence the question : is there a better, more natural way of achieving this? If not, is there a way to make this method transparent to the user (so that he doesn't need to count the overlays himself)?

23

You can automatically add the overlay number to the file name using the code from Accessing the current overlay number in beamer. If you only want to do this for some pictures, then use like:

  \tikzsetnextfilename{figure-\overlaynumber}

To add it to every picture you could make it part of the prefix:

\tikzexternalize[prefix=picture-\overlaynumber-]

Using the suffix notation, (\tikzset{external/figure name/.add={}{-\overlaynumber}}) doesn't seem to work as the figure name is overriden by the \tikzsetnextfilename. If you want to automatically add a suffix you'd have to hack \tikzsetnextfilename:

\let\orig@tikzsetnextfilename=\tikzsetnextfilename
\renewcommand\tikzsetnextfilename[1]{\orig@tikzsetnextfilename{#1-\overlaynumber}}

(inside the \makeatletter ... \makeatother bit).

Although the most hackish, this one ends up with the nicest file names so it's the one I've gone for.

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

\usetikzlibrary{external}

\makeatletter
\newcommand*{\overlaynumber}{\number\beamer@slideinframe}
\tikzset{
  beamer externalizing/.style={%
    execute at end picture={%
      \tikzifexternalizing{%
        \ifbeamer@anotherslide
        \pgfexternalstorecommand{\string\global\string\beamer@anotherslidetrue}%
        \fi
      }{}%
    }%
  },
  external/optimize=false
}
\let\orig@tikzsetnextfilename=\tikzsetnextfilename
\renewcommand\tikzsetnextfilename[1]{\orig@tikzsetnextfilename{#1-\overlaynumber}}
\makeatother

\tikzset{every picture/.style={beamer externalizing}}

\tikzexternalize

\begin{document}

\begin{frame}
  \tikzsetnextfilename{figure}
  \only<1>{Image 1:}
  \only<2>{Image 2:}
  \begin{tikzpicture}
    \only<1>{\node {Overlay 1};}
    \only<2>{\node {Overlay 2};}
  \end{tikzpicture}
\end{frame}

\end{document}
4
  • Awesome, thank you! Note that it also offers a nice workaround to the "problem" you mentioned in the other question, that you need to regenerate all overlays for a picture in case one is changed (that is with the current CTAN version of tikz with the lazy up-to-date check, and assuming you force the regeneration by deleting the appropriate .pdf).
    – T. Verron
    Jun 16 '13 at 20:19
  • This is very clever. Maybe it should be emphasized that the \overlaynumber command is not a native command, but is defined in the link given, to keep things together! Jun 14 '18 at 23:23
  • This is a really good answer, unfortunately there seems to be some incompatibility with pgfplots when using gnuplot contour. For some reason, it appears that the contours get computed for every single overlay - even for frames that do not even contain them! (Cf. tex.stackexchange.com/q/522643/119955) Do you have any idea what going on there?
    – Hyperplane
    Jan 5 '20 at 16:20
  • @Hyperplane I'm afraid I have no experience with gnuplot contour so I've no idea what's going on there. It looks as though you have asked a new question about it, which is the best way to get new ideas. Jan 5 '20 at 20:43
1

I have a slight optimization to the solution https://tex.stackexchange.com/a/119440 , mainly to avoid generating the same figure multiple times if it is not changed. Unfortunately it is not very automatic and still requires some manual input.

\usepackage{etoolbox}
\makeatletter
\newcounter{tikznamebeamer@tempcnt}
\def\tikznamebeamer@finalidx{}
\newcommand{\tikznamebeamer}[2][figure]{%
    \def\tikznamebeamer@slidesetidx{%
        \setcounter{tikznamebeamer@tempcnt}{0}%
        \let\tikznamebeamer@finalidx=0%
        \renewcommand*{\do}[1]{%
            \stepcounter{tikznamebeamer@tempcnt}%
            \let\tikznamebeamer@templist=\relax%
            \forcsvlist{\listadd\tikznamebeamer@templist}{####1}%
            \xifinlist{\number\beamer@slideinframe}{\tikznamebeamer@templist}{%
                \edef\tikznamebeamer@finalidx{\thetikznamebeamer@tempcnt}%
            }{}%
        }%
        \docsvlist{#2}%
    }%
    \tikznamebeamer@slidesetidx%
    \tikzsetnextfilename{#1-\tikznamebeamer@finalidx}%
}
\makeatother

The idea is to specify sets of frame where the figure is unchanged. So in a 5-slide frame if we have the following:

\tikznamebeamer[my-graph]{{2,5},4}
\begin{tikzpicture}
\tikzset{
    every node/.style={draw, circle}
}
\node (1) at (0, 0) {1};
\node (2) at (1cm, 0) {2};
\node (3) at (0, 1cm) {3};
\node (4) at (1cm, 1cm) {4};

\draw[visible on=<{2,5}>, ->] (1) to (2);
\draw[->] (1) to (3);
\draw[visible on=<4>,->] (2) to (3);
\draw[->] (3) to (4);
\end{tikzpicture}

\tikznamebeamer[my-graph]{{2,5},4} specifies that the custom file name should be prefixed by 'my-graph', and also 3 sets of slides: {1,3}, {2,5}, and {4}. Note that any slides not specified are grouped together, hence {1,3}.

For each set, we use the same file name for the externalization, so that it can be re-used. In this example, 'my-graph-0.pdf' for {1,3}, 'my-graph-1.pdf' for {2,5} and 'my-graph-2.pdf' for {4} will be generated.

The small downside is that you need to manually specify which slides are the same. But I haven't figure out an automatic way of doing this.

This is actually the first piece of serious low level LaTeX code I write in my life, so I really hope someone more knowledgeable could clean it and make it idiomatic.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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