这是来自使用 tikz 外部功能与 beamer \only。安德鲁的回答非常出色,几乎完美地完成了任务。
\tikzsetnextfilename
当我尝试与它一起使用时,我发现边缘很粗糙。
我为什么要这么做?因为在 beamer 中,可能需要重新排序幻灯片,或添加覆盖等。在这种情况下,即使图片没有改变,大多数图片也需要重新生成。
问题是什么?下面的 M(n)WE 展示了使用 的通常方式\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}
这不管用。我对 latex、beamer 和 tikz 机制不够熟悉,但我怀疑问题在于tikz externalize
尝试生成具有相同文件名的两张图片。
事实上,下面的代码片段确实有效。
\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}
现在它还不够完善,比如需要精确知道每张图片需要多少个叠加层。
因此问题是:有没有更好、更自然的方法来实现这一点?如果没有,有没有办法让这种方法对用户透明(这样用户就不需要自己计算覆盖层了)?
答案1
您可以使用以下代码自动将覆盖编号添加到文件名中访问 beamer 中的当前覆盖编号。如果您只想对某些图片执行此操作,请使用如下方法:
\tikzsetnextfilename{figure-\overlaynumber}
要将其添加到每张图片中,您可以将其作为前缀的一部分:
\tikzexternalize[prefix=picture-\overlaynumber-]
使用后缀表示法, ( \tikzset{external/figure name/.add={}{-\overlaynumber}}
) 似乎不起作用,因为figure name
被 覆盖\tikzsetnextfilename
。如果要自动添加后缀,则必须破解\tikzsetnextfilename
:
\let\orig@tikzsetnextfilename=\tikzsetnextfilename
\renewcommand\tikzsetnextfilename[1]{\orig@tikzsetnextfilename{#1-\overlaynumber}}
(在\makeatletter ... \makeatother
钻头内部)。
尽管这种方法最为黑客化,但最终却得到了最漂亮的文件名,因此我选择了这种方法。
\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}
答案2
作为 Andrew 出色回答的扩展,可以采用他的解决方案来克服原始方法使用带有 tikz 外部的 beamer 叠加的主要瓶颈。原始解决方案需要全局禁用 tikz 外部优化(即external/optimize=false
在序言中设置)。因此,在外部化任何 tikzpicture 期间,文档中的所有其他 tikzpicture 也会排版,即使它们最终将被丢弃。对于包含许多 tikzpicture 的文档,这会花费大量的编译时间。
作为替代方案,我们可以使用\tikzsetnextfilename
宏来本地禁用 tikz 外部优化,仅针对要外部化的 tikzpicture 和所有具有相同基本名称的先前 tikzpicture。这将大大加快具有许多 tikzpicture 的演示文稿的速度。
\documentclass{beamer}
\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
}{}%
}%
},
every picture/.style={beamer externalizing}
}
% remove the `-\overlaynumber` suffix from the jobname
% the name is stored in `\job@no@suffix` and the number in `\job@suffix@number`
\def\strip@suffix#1{%
\begingroup%
\edef\@tempa{#1-}%
\expandafter\endgroup%
\expandafter\@strip@suffix\@tempa\relax%
}
\def\@strip@suffix#1-#2\relax{%
\ifx\relax#2\relax%
\edef\job@suffix@number{#1}%
\def\next{\relax}%
\else%
\expandafter\ifx\expandafter\relax\job@no@suffix\relax%
\def\job@no@suffix{#1}%
\else%
\edef\job@no@suffix{\job@no@suffix-#1}%
\fi%
\def\next{\@strip@suffix#2\relax}%
\fi%
\next%
}
% check if the next tikzpicture is the to-be-externalized one
% or has the same basename and precedes it
\def\if@is@current@pic#1{%
\begingroup%
\def\job@no@suffix{}%
\strip@suffix\pgfactualjobname%
\ifnum\job@suffix@number<\overlaynumber\relax%
\def\job@no@suffix{}%
\fi%
\edef\current@pic@basename{#1}%
\edef\current@pic@basename{%
\expandafter\detokenize\expandafter{\current@pic@basename}%
}%
\ifx\job@no@suffix\current@pic@basename%
\expandafter\endgroup\expandafter\@firstoftwo%
\else%
\expandafter\endgroup\expandafter\@secondoftwo%
\fi%
}
% add a `-\overlaynumber` suffix to the next tikzpicture name
% and disable tikz external optimization where needed
\let\orig@tikzsetnextfilename=\tikzsetnextfilename
\renewcommand\tikzsetnextfilename[1]{%
\orig@tikzsetnextfilename{#1-\overlaynumber}%
\tikzifexternalizing{
\if@is@current@pic{\tikzexternal@filenameprefix#1}{%
\tikzset{external/optimize=false}%
}{%
\tikzset{external/optimize=true}%
}%
}{}%
}
\makeatother
\tikzexternalize[only named=true]
\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}
答案3
我对解决方案做了一些优化https://tex.stackexchange.com/a/119440,主要是为了避免在不改变的情况下多次生成相同的图形。不幸的是,它不是很自动化,仍然需要一些手动输入。
\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
这个想法是指定图形不变的框架集。因此,在 5 张幻灯片的框架中,如果我们有以下内容:
\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}
指定自定义文件名应以“my-graph”为前缀,并指定 3 组幻灯片:{1,3}、{2,5} 和 {4}。请注意,任何未指定的幻灯片都会分组在一起,因此为 {1,3}。
对于每个集合,我们对外部化使用相同的文件名,以便可以重复使用。在此示例中,将为 {1,3} 生成“my-graph-0.pdf”,为 {2,5} 生成“my-graph-1.pdf”,为 {4} 生成“my-graph-2.pdf”。
一个小缺点是你需要手动指定哪些幻灯片是相同的。但我还没有找到一种自动的方法来做到这一点。
这实际上是我一生中编写的第一段严肃的低级 LaTeX 代码,所以我真的希望更有知识的人可以清理它并使其符合惯用语言。