我怎样才能修复投影机中跳跃的 TikZ 图片?

我怎样才能修复投影机中跳跃的 TikZ 图片?

投影机中物体跳跃的问题很常见,(参见避免投影机跳帧)我们时不时在这里看到它(我想这边的“相关”列表会很长!)。对于 TikZ 图片,解决这个问题的一种方法是手动指定边界框。我想把那句话中的“手动”这个词去掉!

这应该不太难实现,我想象的方案将涉及将最终的边界框写入文件,.aux以便它可以在 tikzpicture 环境启动时使用。

第一个问题:有人已经实现这个了吗?

如果(我怀疑)不是的话,我会喜欢第二个问题是“有人能帮我做吗?”但这不符合网站的精神。因此,以下是我的想法:

  1. 在每一帧的幻灯片上,tikzpicture 都会保存其边界框。
  2. 在帧的末尾,tikzpicture 将最大的边界框写入文件.aux(请注意,最大的可能不是最后一个)。
  3. 在后续运行中,边界框的写出方式会告诉 tikzpicture 它的边界框应该是什么,并据此绘制一条\useasboundingbox路径(在图片的开始处)。

所以第二个问题,有点开放式,但希望我能回答清楚:上述内容的缺陷是什么。

第三个问题(是的,我在这里确实有点夸大其词):如果有人认为上述内容中有一部分恰巧知道,请将代码放在答案中。我对我的 TeX 技能很有信心,我能够把这些部分拼凑在一起,也可以调整这些部分。所以我不是要求任何人为我编写这个代码,只需帮助我找到所需的部分。

(尽管如果有人编码...)

这里有一个非常简单的例子来说明我的意思(不过你必须编译它才能看到)。众所周知,地球绕着太阳转。但不是演示。根据这个演示,宇宙学有点奇怪。

\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}

笔记:上面的例子是一个非常简单的例子,它是不是一个活生生的例子。我不想要一个解决这个特定例子的答案,我正在寻找一个修复系统每一个可能的例子

答案1

我终于 (!) 有时间实现这个了。它现在是软件包的一部分尺子指南针,因为它看起来对于该包特别有用。


这是我的代码:

\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}

它的工作原理如下。我们有一个全局计数器,jumping它在覆盖下是稳定的。也就是说,beamer通过多次重新处理代码来实现覆盖。通常,每次执行都会导致计数器递增,但只要beamer被告知,它就可以考虑到这一点并在每次运行时重置计数器。这样做的结果是,这个计数器可以用来标记(非技术意义上的) ,tikzpicture这样不同幻灯片上图片的不同副本就会获得相同的标签。

我们使用该标签将图片每个版本的边界框保存到文件中aux。我们在图片末尾执行此操作以确保获得正确的边界框。aux下次读取文件时,它会计算此图片各个边界框的最大和最小延伸,并将其保存为 PGF 点。回到图片中,我们使用这些计算出的点将边界框调整到最大程度。

进一步说明:

  1. 我们保存每张图片的边界框将其与保存的最大值进行比较。这意味着最大值始终是根据图片的实际大小计算的,而不考虑任何先前的最大值。这意味着它对图片的变化很敏感,这是理所当然的。

  2. 通过在图片中放置坐标来调整边界框。这是为了确保图片不会在框内跳来跳去。只需重置边界框长度即可确保图片在每张幻灯片上占用相同的空间,但图片不会停留在框中的同一位置。

答案2

\overprint\onslide 二重奏似乎也能完成这个工作:

\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}

编辑:事实上,在这个特定的例子中,没有必要使用overprint

答案3

此解决方案已更新在另一篇文章中

Andrew 的 MWE 的一个特殊性是元素的位置在每张幻灯片上都会发生变化,这确实使计算最终的边界框变得困难——而他自己的答案完美地涵盖了这一点。

然而,在许多情况下,目标只是某种分段揭示投影仪图形的元素。在这种情况下,我已停止使用命令本身的叠加规范(\node<...>\fill<...>),而是总是绘制所有元素,但隐藏。为了指定可见性,我使用visible on=<...>TikZ 样式,如下所示:

\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 >通过将样式应用于invisible不包含在ovspec。默认实现invisible只是设置opacity=0;但是,如示例中所示,这可以轻松更改,以便轻松安装其他类型的“不可见性”(调光、灰色填充,...)。

笔记:如果ovspec本身包含逗号,它或完整的参数必须放在花括号内(如 invisible on=<{1,3-4,8}>或 `visible on={<1,3-4,8>}),以免混淆 pgfkeys 解析器。

这种方法(除了在我看来可读性更好之外)还有另一个优点:所有命名元素(尤其是节点)始终存在,因此您可以使用它们进行坐标计算,即使在幻灯片上它们不可见。

答案4

这几乎不能算是一个答案,但我们还是想说:

  1. 没问题。我认为你可以execute at end picture在这里使用密钥。我能想到的唯一缺陷是幻灯片编号出奇地难以获得。请参阅我的答案:LaTeX Beamer:如何在使用覆盖时获取不同的页码?

  2. 由于 beamer 扩展帧代码的方式不标准,我认为在帧末尾执行代码很困难。但您可以在第 1 步中解决这个问题,方法是在第一张幻灯片中将边界框线写入辅助文件,然后每次边界框都会变大。然后,当读取辅助文件时,最后一个边界框线是最大的。

  3. 这应该也不是问题。我会试试这个execute at begin picture钥匙。

相关内容