考虑以下 LaTeX 手稿,其中包含两个实心圆圈的 TikZ 图片。右边的圆圈画在 内\pgfextra
。路径的填充颜色为黄色!
\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\path[fill=yellow] (-.5,0) circle(1)
\pgfextra{\fill (.5,0) circle(1);};
\end{tikzpicture}
\end{document}
它呈现为
如果我们现在\pgfextra
用替换graph
:
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{graphs}
\begin{document}
\begin{tikzpicture}
\path[fill=yellow] (-.5,0) circle(1)
graph{""[circle,at={(.5,0)},minimum size=2cm,fill]};
\end{tikzpicture}
\end{document}
我们得到图片
现在这里有一个悖论。
在两个例子中,路径具有相同的形式:
\path[fill=yellow] (-.5,0) circle(1) ...;
人们期望通过以下两种方式之一来实现这一点:要么在开始时执行该选项:
\pgfsetfillcolor{yellow}
\pgfpathcircle{\pgfpoint{-.5cm}{0cm}}{1cm}
...
\pgfusepath{fill}
或者在使用路径之前:
\pgfpathcircle{\pgfpoint{-.5cm}{0cm}}{1cm}
...
\pgfsetfillcolor{yellow}
\pgfusepath{fill}
第一个选择必须被拒绝,因为第一个例子将按如下方式实现
\pgfsetfillcolor{yellow}
\pgfpathcircle{\pgfpoint{-.5cm}{0cm}}{1cm}
% \pgfextra{\fill (.5,0) circle(1);}
\pgfpathcircle{\pgfpoint{.5cm}{0cm}}{1cm}
\pgfusepath{fill}
\pgfusepath{fill}
产生图片
但是第二种选择也必须被拒绝,因为第二个示例的实现将会看到在设置填充颜色之前创建的图形:
\pgfpathcircle{\pgfpoint{-.5cm}{0cm}}{1cm}
% graph{""[circle,at={(.5,0)},minimum size=2cm,fill]}
\pgfsetfillcolor{yellow}
\pgfusepath{fill}
并且从那时起图形在创建后被锁定在 TeX 框中,并且TeX 框的视觉外观在创建时就已确定,图形节点将填充黑色,图片看起来会像这样
我们陷入了矛盾!
答案1
\pgfextra
没有用pgfinterruptpath
环境包围它的内容,因此:
\path[fill=yellow] (-.5,0) circle [radius=1]
\pgfextra{\fill [red] (.5,0) circle [radius=1];};
“黄色”路径实际上是由 内部路径描述的路径延续的\pgfextra
。然后“红色”路径使用该路径并填充红色。请注意,在 内部添加路径\pgfextra
(无论是否带有pgfinterruptpath
环境)是真的坏主意,行为本质上是未定义的。的预期用途\pgfextra
主要是用于计算或一些简单的 TeX 内容,而不是任意的 TikZ 命令。此外,关于\pgfextra
手册注意事项(第 162 页,我的重点):
...此操作仅应由真正的专家使用,并且只应在巧妙的宏内部深处使用,而不是在正常路径上使用。
通过使用
\path[fill=yellow] (-.5,0) circle [radius=1]
\pgfextra{
\pgfinterruptpath
\fill [red] (.5,0) circle [radius=1];
\endpgfinterruptpath
};
将绘制一个黄色和红色的圆圈。
答案2
TikZ“引擎”\path
从左到右解析语句,并按文本顺序执行各部分。“执行”的含义取决于具体部分,例如
node
s 在保存到寄存器的 TeX 框内排版。在节点执行开始时,除 、 和 之外的所有路径选项font
都会被暂时清除,text
并behind path
在节点执行结束时(排版后)恢复。因此,节点的排版完全基于显式应用于节点的选项(允许上述例外情况)。- 路径构造操作,例如
circle(1)
转换为\pgf...
附加到内部列表对象的命令,称为当前路径(这是过于简单的说法,但就目前的目的而言,这样就够了)。重要的是要知道语句\path
被包装在隐式 TeX 作用域中,但当前路径列表是全局的,因此它超越了作用域。 \pgfextra{<body>}
暂时将控制权返回给普通的 TikZ 处理器(而不是路径解析器),将其<body>
作为下一步要处理的文本传递。当前 TeX 范围不会退出,也不会打开新范围。遇到时引擎的内部状态\pgfextra
不会改变,因此解析语句时创建的所有内部数据结构\path
都保持不变。当 TikZ 完成处理后
<body>
,控制权将返回到路径解析器,其光标现在位于表达式的右侧\pgfextra{<body>}
。同样,返回正常路径解析的转换不会伴随引擎状态的任何变化,也不会关闭任何范围,因为一开始就没有打开任何范围。
选项由命令执行\tikzset
。这需要执行的内容取决于选项。例如
该
behind path
选项对 TikZ 引擎的操作进行了内部更改:它使引擎将用此选项标记的节点排版到另一个框中,称为背景框,而不是其他节点排版的那个。该
fill=<color>
选项将命令附加\pgfsetfillcolor{<color>}
到特殊列表,选项列表,由类似的基础层命令组成。此列表对 TikZ 引擎的操作没有影响。它只是累积并最终按原样写入 dvi 文件(在命令\pgf...
被其\special
实现替换之后)。与当前路径不同,选项列表是隐式包围命令的 TeX 范围的本地列表\path
,并在此范围的开头设置为空列表。
因此,路径部分的执行通常意味着将其“翻译”为一系列低级命令,并将该序列保存为新对象,或将其附加到预先存在的列表中。
TikZ 路径末尾的分号的执行很特殊。它实际上需要将所有路径部分写入 dvi 文件(这是一个过于简单的说法,但对于我们的目的来说已经足够了)。由于在执行分号时,各个部分的执行已经导致它们被转换为低级命令,因此分号的主要作用是确定这些列表将被转储到 dvi 文件的顺序。
分号的简化执行版本是:
写出标有 的
node
s(包括graph
被视为美化的s) 。node
behind path
写出选项列表。
写出当前路径。
使用路径,即绘制它、填充它等等。作为副作用,当前路径对象(回想一下,它是全局的)被重置。
写出其余的
node
s(和graph
s)。关闭隐式包围路径语句的 TeX 范围,并恢复 TikZ 环境顶层的图形状态。特别是,在每个路径的末尾恢复 TikZ 环境的默认填充颜色,以及 TikZ 环境的许多其他默认值。
现在让我们将所有这些理论应用到原始帖子中的例子。
第一个例子
\path[fill=yellow] (-.5,0) circle(1) \pgfextra{\fill (.5,0) circle(1);};
执行该
fill=yellow
选项。这会导致指令\pgfsetfillcolor{yellow}
被附加到最初为空的选项列表中。(-.5,0) circle(1)
执行。这会导致指令\pgfpathcircle{\pgfpoint{-.5cm}{0pt}}{1cm}
被添加到最初为空的当前路径。\pgfextra{\fill (.5,0) circle(1);}
执行。这会导致 TikZ 暂时“忘记”它正在解析路径,并开始处理代码,\fill (.5,0) circle(1);
就好像它是在顶层编写的一样。但是引擎的内部状态保持不变,因此,特别是选项列表和当前路径保持不变。(.5,0) circle(1)
执行。这会导致指令\pgfpathcircle{\pgfpoint{.5cm}{0pt}}{1cm}
被附加到当前路径对象,该对象已包含 (-.5,0) 处的圆。遇到路径末尾的分号。这将引发以下一系列操作。
首先,
behind path
将节点写入 dvi 文件。没有节点,因此这是无操作。其次,将选项列表写入 dvi 文件。这与上面提到的列表不同,因为选项列表是语句周围隐式作用域的本地列表
\path
,并且它们在此作用域的开头设置为空列表。由于未为 path 指定任何选项\fill (.5,0) circle(1);
,因此选项列表为空,因此此步骤为无操作。第三,将当前路径(由两个圆组成)的路径构造命令写入 dvi 文件。
第四,填充路径,即,
\special
将执行命令的 s\pgfusepath{fill}
写入 dvi 文件。由于尚未应用顶级选项fill=yellow
,因此使用默认颜色。在本例中,由于没有明确指定默认颜色(无论是 TikZ 图片还是文档),因此黑色是隐式默认颜色(因为这是所有 PDF 文档的默认颜色)。在此步骤结束时,将重置(全局)当前路径对象,即当前路径变为空。第五,将其余的
node
s 写入 dvi 文件。没有,所以这是无操作。第六,隐式包围该语句的 TeX 作用域
\fill ...
被关闭,并且 TikZ 环境的默认设置被恢复。
执行顶层分号。但是,由于没有节点,并且当前路径为空,因此除了将选项列表写入 dvi 文件之外,没有其他事情可做。但是,这没有整体效果:选项列表将填充颜色更改为黄色,但是当分号执行的最后一步恢复 TikZ 环境的默认图形状态时,此更改将被推翻。
第二个例子
\path[fill=yellow] (-.5,0) circle(1)
graph{""[circle,at={(.5,0)},minimum size=2cm,fill]};
与第一个例子一样。
与第一个例子一样。
图形被执行。图形的执行类似于节点的执行。因此,图形在保存到寄存器的 TeX 框内排版。当图形开始执行时,路径选项 (
fill=yellow
) 被暂时清除,因此框内不会设置填充颜色。因此,当框的内容溢出到 dvi 文件时,填充颜色将用于渲染图形。分号执行:
behind path
节点被写入 dvi 文件。没有节点,所以这是无操作。选项列表,即
\pgfsetcolor{yellow}
,写入 dvi 文件。当前路径的路径构造命令(由以 (-.5,0) 为中心的圆组成)被写入 dvi 文件。
路径使用:将命令
\pgfusepath{fill}
写入 dvi 文件。当 dvi 文件将在屏幕上呈现时,此时会出现一个黄色圆圈。在此步骤结束时,(全局)当前路径对象将被重置,即当前路径变为空。其余节点(和图形)都写入 dvi 文件。因此,现在图形框的内容已写入 dvi 文件。由于框内未设置填充颜色,因此将使用的填充颜色是最后设置的填充颜色,即黄色。
隐式包围该语句的 TeX 作用域
\path ...
被关闭,并且 TikZ 环境的默认设置被恢复。特别是,填充颜色被重置为默认的黑色。
第三个例子(Mark Wibrow 的例子)
\path[fill=yellow] (-.5,0) circle [radius=1]
\pgfextra{
\pgfinterruptpath
\fill [red] (.5,0) circle [radius=1];
\endpgfinterruptpath
};
与第一个例子一样。
与第一个例子一样。
执行该
\pgfextra
块。这会导致 TikZ 暂时“忘记”它正在解析路径,并开始处理主体,\pgfextra
就好像它是在顶层编写的一样。但是引擎的内部状态保持不变,因此,特别是选项列表和当前路径保持不变。\pgfinterrupt
启动一个新的 TeX 块,并将当前路径(由以 (-.5,0) 为中心的圆组成)保存在本地“私有变量”中,并将(全局)当前路径重置为空路径。选项
red
已执行。这实际上将指令\pgfsetfillcolor{red}
和添加\pgfsetstrokecolor{red}
到选项列表中。这与上面提到的列表不同,因为选项列表是围绕 \path 语句的隐式范围的本地列表,并且它们在此范围的开头设置为空列表。因此,在此步骤结束时,可见的选项列表仅包含两个命令,这些命令将填充和描边颜色设置为红色。(.5,0) circle [radius=1]
执行。这会导致指令\pgfpathcircle{\pgfpoint{.5cm}{0pt}}{1cm}
被附加到空的当前路径对象。遇到路径末尾的分号。这会导致当前路径(由一个以 (.5,0) 为中心的圆圈组成)写入 dvi 文件,随后会发出指令用红色填充此圆圈。此后,当前路径将设置为空路径。当 dvi 文件在屏幕上呈现时,此处会出现一个红色圆圈。
包围命令的隐式 TeX 块
\fill [red] (.5,0) circle [radius=1];
已关闭。由于选项列表对于包围 \path 语句的隐式范围而言是本地的,因此选项列表将被前一个由单个命令组成的列表替换,该命令用于将填充颜色设置为黄色。\endpgfinterruptpath
结束打开的 TeX 块\pgfinterruptpath
,并将全局当前路径对象设置回该\pgfinterruptpath
命令保存的对象,即由以 (-.5,0) 为中心的圆组成的对象。
顶级分号执行:
behind path
节点被写入 dvi 文件。没有节点,所以这是无操作。选项列表,即
\pgfsetcolor{yellow}
,写入 dvi 文件。当前路径的路径构造命令(由以 (-.5,0) 为中心的圆组成)被写入 dvi 文件。
路径的使用:将命令
\pgfusepath{fill}
写入 dvi 文件。当 dvi 文件将在屏幕上呈现时,此时会出现一个黄色圆圈。它将绘制在之前绘制的红色圆圈的上方,略微偏左。在此步骤结束时,(全局)当前路径对象将被重置,即当前路径变为空。其余节点(和图表)将写入 dvi 文件。没有,因此这是无操作。
隐式包围该语句的 TeX 作用域
\path ...
被关闭,并且 TikZ 环境的默认设置被恢复。特别是,填充颜色被重置为默认的黑色。
生成的图像是