我尝试在这个问题中在 图书馆 里 使用 一些 简写scopes
.
我发现了一些困难,但我无法解释为什么会遇到问题。(我使用 pgf 2.1 cvs)
主要代码来自pgfmanual
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{scopes}
\begin{tikzpicture}
{ [ultra thick]
{ [red]
\draw (0mm,10mm) -- (10mm,10mm);
\draw (0mm,8mm) -- (10mm,8mm);
}
\draw (0mm,6mm) -- (10mm,6mm);
}
{ [green]
\draw (0mm,4mm) -- (10mm,4mm);
\draw (0mm,2mm) -- (10mm,2mm);
\draw[blue] (0mm,0mm) -- (10mm,0mm);
}
\end{tikzpicture}
\end{document}
这太完美了。现在我想把这些线画三遍
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{scopes}
\begin{document}
\begin{tikzpicture}
\foreach \i in {0,5,10}{%
\begin{scope}[xshift=\i cm]
{ [ultra thick]
{ [red]
\draw (0mm,10mm) -- (10mm,10mm);
\draw (0mm,8mm) -- (10mm,8mm);
}
\draw (0mm,6mm) -- (10mm,6mm);
}
{ [green]
\draw (0mm,4mm) -- (10mm,4mm);
\draw (0mm,2mm) -- (10mm,2mm);
\draw[blue] (0mm,0mm) -- (10mm,0mm);
}
\end{scope}}
\end{tikzpicture}
\end{document}
这始终是完美的,但是现在如果我用 替换\begin{scope} ...\end{scope}
,{..}
代码可以编译但范围会消失。
\begin{tikzpicture}
\foreach \i in {0,5,10}{%
{[xshift=\i cm]
{ [ultra thick]
{ [red]
\draw (0mm,10mm) -- (10mm,10mm);
\draw (0mm,8mm) -- (10mm,8mm);
}
\draw (0mm,6mm) -- (10mm,6mm);
}
{ [green]
\draw (0mm,4mm) -- (10mm,4mm);
\draw (0mm,2mm) -- (10mm,2mm);
\draw[blue] (0mm,0mm) -- (10mm,0mm);
}
}}
\end{tikzpicture}
我知道只有在 {..}`之后{
才是 TikZ 范围的开始,才是一个简单的 TeX 组。[
{ otherwise
可以解释一下这个问题吗?
答案1
要回答这个问题,您需要知道 TikZ 何时寻找{[...] ... }
作用域快捷方式。TikZ 解析器有许多不同的状态,它必须处于正确的状态才能识别某些语法,否则它要么忽略它,要么大声抱怨。
作用域快捷方式的检查由宏处理\tikz@lib@scope@check
。如果没有scopes
库,则为空。有了scopes
库,它就等同于:
- 查看流中的下一个(非空格)标记。
- 是吗
\tikz@intersect@finish
?如果是,请执行此操作并再次检查。 - 是吗
\par
?如果是,请执行此操作并再次检查。 - 是吗
\bgroup
?如果是,则查找[
,如果找到,则进入范围。
- 是吗
需要注意的一点是,这是对下一个令牌的测试,并且对于延迟测试仅提供有限的支持。
现在让我们看看何时调用它:
- 当
tikzpicture
开始时。因此 TikZ 图片中的第一个元素可以是{[red]
,并且将被正确检测到。 - 当 a
scope
开始时。这可以是隐式的,也可以是显式的(因此语法嵌套{[..] ..}
是可以的,就像与普通\begin{scope} .. \end{scope}
对混合一样)。 当 a
scope
结束时。实际上,这并不像乍一看那么有用。问题在于显式作用域在组内结束,因此下一个标记不是用户认为的下一个东西,而可能是\endgroup
(或代码中隐藏的其他东西\end{environment}
)。要利用这一点,前一个作用域必须是隐式作用域或形式为\scope ... \endscope
。在下面,第一行不是红色。请注意,这可能会产生连锁反应:如果第一个隐式作用域无法识别,而另一个作用域紧随其后,则也无法识别。\begin{scope} \end{scope} {[red] \draw[ultra thick] (0,0) -- (0,1); } \scope \endscope {[red] \draw[ultra thick] (2,0) -- (2,1); } {[red] \draw[ultra thick] (1,0) -- (1,1); }
在路径之后。因此隐式作用域可以跟在路径命令之后。
很遗憾,没有任何其中的任何一个都符合您的语法,因为在处理时\foreach
,在图片开头进行的检查已经完成并失败(因为它找到了\foreach
)。因此,要让初始隐式范围得到识别(顺便说一句,第一个没有得到识别的事实会产生连锁反应,导致其他任何一个都没有得到识别)[ultra thick]
:没有跟随范围的开头,所以 也不跟随[red]
,[green]
不跟随范围的结尾,所以无法识别)我们需要调用识别代码。 有两种方法可以做到这一点,即确保满足其中一个条件。 要么显式但不分组的范围必须以\foreach
(so \scope\endscope
) 开头,要么以空路径 (so \path;
) 开头。
这些可能被认为有点“黑客”。还有另一种选择。这是为了确保实际检查在命令开始时执行\foreach
。由于pgffor
例程旨在站在 TikZ 的一边,因此这不会 - 也不应该 - 明确地出现在文件中pgffor.code.tex
。但是有一些可以调用的钩子:\pgffor@beginhook
和\pgffor@endhook
(和\pgffor@afterhook
)。实际上,这些钩子被 TikZ 用于\foreach
在路径内遇到时。但它们不用于外部遇到。由于内部钩子会覆盖这些钩子,我们可以将它们设置为图片。由于我们想确保范围检查是添加到这些钩子的最后一件事,我选择了一种稍微谨慎的方式:
\tikzset{every picture/.append style={
execute at begin picture={
\expandafter\def\expandafter\pgffor@beginhook\expandafter{\pgffor@beginhook\tikz@lib@scope@check}
}
}
}
这意味着当图片启动时,添加代码的代码将添加到图片启动时执行的代码中。因此它将在全局添加或图片启动时在选项中添加的任何内容之后添加。我相信可以做得更好!
这样,你的代码就可以工作了:
\documentclass{article}
%\url{http://tex.stackexchange.com/q/57289/86}
\usepackage{tikz}
%\usepackage{trace-pgfkeys}
\usetikzlibrary{scopes}
\makeatletter
\tikzset{every picture/.append style={
execute at begin picture={
\expandafter\def\expandafter\pgffor@beginhook\expandafter{\pgffor@beginhook\tikz@lib@scope@check}
}
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\foreach \i in {0,5,10}{%
{[xshift=\i cm]
{ [ultra thick]
{ [red]
\draw (0mm,10mm) -- (10mm,10mm);
\draw (0mm,8mm) -- (10mm,8mm);
}
\draw (0mm,6mm) -- (10mm,6mm);
}
{ [green]
\draw (0mm,4mm) -- (10mm,4mm);
\draw (0mm,2mm) -- (10mm,2mm);
\draw[blue] (0mm,0mm) -- (10mm,0mm);
}
}
}
\end{tikzpicture}
\end{document}
最后,trace-pgfkeys
在这里非常有用,因为它直接显示出范围根本无法被看到。