TikZ 中的“Z 级别”

TikZ 中的“Z 级别”

这是一个无聊的问题,但尽管如此,我认为它很有趣。

TikZ 是否有一种简单的方法来实现“z 级别”规范。这里,我不是指 3D 绘图,而是指指定图片的哪些部分位于哪些部分之上。

因此我会写类似这样的内容:

\draw[fill, blue,zlevel=1] (0,0) circle (2);
\draw[fill,red,zlevel=2] (0,0) circle (1);

并让红色圆圈出现在蓝色圆圈的上方(因为它的 z 级别更高)。我知道我可以反转顺序,但我可以想象,能够以这种方式指定事物可能会通过简化覆盖规范而使 Beamer 中对 TikZ 图片的增量更改变得更容易……

我想你可以做些什么remember picture,但似乎“z 级”键是最简单的解决方案(从最终用户的角度来看)。

有这样的事吗?有可能吗?

答案1

您可以使用第 113 节中描述的图层分层图形pgfmanual。您需要先使用 声明图层\pgfdeclarelayer,然后在\pgfsetlayers图片外部使用它们。使用{pgfonlayer}{<layer name>}环境,您可以包装要在此图层上绘制的 TikZ 或 PGF 命令。该backgroundon background layer为 s 提供了一种样式,因此也scope应该可以定义一种样式:zlevel

\tikzset{zlevel/.style={%
    execute at begin scope={\pgfonlayer{#1}},
    execute at end scope={\endpgfonlayer}
}}

但您需要将所有命令放入scope.

答案2

是的。它们被称为“层”。我在各种解决方案中使用过几次:

还有其他一些人也这么说:

https://tex.stackexchange.com/search?q=pgfonlayer

更新:为了“超越”TikZ,Martin 不遗余力地推出了一个版本,可以作为密钥在一条道路上。它通过一些狡猾的黑客技术以及对特定命令执行时间的仔细观察来工作。我们需要在路径的开头和结尾安装一些代码。开头还可以,因为 TikZ 选项会在早期进行解析。结尾有点棘手,这就是狡猾的黑客技术发挥作用的地方。基本上,\tikz@path@do@at@end正如它所说,宏在结尾完成。它发生在之后,\endgroup并且路径上的键在该组内进行检查,因此乍一看,似乎组内的键无法在\tikz@path@do@at@end没有进行全局分配的情况下挂接到该键(我不愿意这样做)。但是,如果我们引入一个新的,那么TikZ 路径设置例程末尾的\begingroup原始代码不再结束主分组,而是结束我们的内部分组,从而允许我们访问。只要我们小心并结束外部组,然后再次使用(现在回到了它的原始含义),那么我们可以将我们的层结束代码放在那里。\endgroup\tikz@path@do@at@end\tikz@path@do@at@end

\documentclass{article}
\usepackage{tikz}
\pgfdeclarelayer{back}
\pgfsetlayers{back,main}

\makeatletter
\pgfkeys{%
  /tikz/on layer/.code={
    \def\tikz@path@do@at@end{\endpgfonlayer\endgroup\tikz@path@do@at@end}%
    \pgfonlayer{#1}\begingroup%
  }%
}
\makeatother

\begin{document}
\begin{tikzpicture}
\fill[green!50!white,opacity=.5] (0,0) rectangle (8,8);
\begin{pgfonlayer}{back}
\draw[ultra thick,red,line width=1cm] (-1,-1) -- (9,9);
\end{pgfonlayer}
\draw[ultra thick,red,line width=1cm,on layer=back] (-1,9) -- (9,-1);
\end{tikzpicture}
\end{document}

结果两个都红线位于绿色矩形后面。

(与我的许多黑客答案一样,这带有“使用风险自负”警告,并且也与黑客作为特定应用程序。事实上,我一直在寻找一种方法来将该宏连接到\tikz@path@do@at@end其他东西上,这可能就是我能做到的。)

编辑:(再次)我不知道修改有多“安全”,\tikz@path@do@at@end所以这里有一个替代方法\aftergroup。它使用相同的 hack 想法:开始一个新组,以便将现有组\endgroup作为内部组的结尾。这是对上一个示例中位的替换\pgfkeys

\pgfkeys{%
  /tikz/on layer/.code={
    \pgfonlayer{#1}\begingroup
    \aftergroup\endpgfonlayer
    \aftergroup\endgroup
  },
  /tikz/node on layer/.code={
    \pgfonlayer{#1}\begingroup
    \expandafter\def\expandafter\tikz@node@finish\expandafter{\expandafter\endgroup\expandafter\endpgfonlayer\tikz@node@finish}%
  }
}

更新:正如 Bruce 在评论中指出的那样,在命令on layer中不起作用\node(尽管\path[on layer=back] node {node};有效)。这是因为\node[options]成为\path node[options]和不是\path[options] node所以钩子在不同的地方。经过一番侦查工作,我找到了一个钩子来挂起层命令的结束位。所以\node[node on layer=back] {on back layer};有效。我仍然需要进一步寻找可以与一起使用的东西\draw (0,0) -- node[put this on the back layer but not the path] {back} (8,0);

(不再需要\makeatletter!)

更新(2012-03-07):重新审视这一点,我发现node on layer不再起作用了(如果它曾经起作用 - 我现在很怀疑,因为我认为 TikZ 代码没有改变)。我找到了一组新的钩子来挂接节点。新方法的一个显著优势是可以将节点放在与其周围路径不同的级别上。我觉得这相当巧妙。

这是当前的代码:

\documentclass{article}
%\url{https://tex.stackexchange.com/q/46957/86}
\usepackage{tikz}
%\usepackage[tracelevel=silent]{trace-pgfkeys}
\pgfdeclarelayer{back}
\pgfdeclarelayer{front}
\pgfsetlayers{back,main,front}

\makeatletter
\pgfkeys{%
  /tikz/on layer/.code={
    \pgfonlayer{#1}\begingroup
    \aftergroup\endpgfonlayer
    \aftergroup\endgroup
  },
  /tikz/node on layer/.code={
    \gdef\node@@on@layer{%
      \setbox\tikz@tempbox=\hbox\bgroup\pgfonlayer{#1}\unhbox\tikz@tempbox\endpgfonlayer\egroup}
    \aftergroup\node@on@layer
  },
  /tikz/end node on layer/.code={
    \endpgfonlayer\endgroup\endgroup
  }
}

\def\node@on@layer{\aftergroup\node@@on@layer}

\makeatother
\begin{document}
\begin{tikzpicture}
\draw[line width=1cm,red] (2,1) -- (2,-1);
\draw[ultra thick,white,preaction={on layer=back,line width=1cm,blue,draw}] (0,0) -- (4,0);
\draw[line width=1cm,red] (2,-2) -- (2,-4);
\draw[ultra thick,white,postaction={on layer=back,line width=1cm,blue,draw}] (0,-3) -- (4,-3);
\begin{scope}[xshift=5cm]
\draw[line width=1cm,red] (2,1) -- (2,-1);
\draw[ultra thick,white,preaction={line width=1cm,blue,draw}] (0,0) -- (4,0);
\draw[line width=1cm,red] (2,-2) -- (2,-4);
\draw[ultra thick,white,postaction={line width=1cm,blue,draw}] (0,-3) -- (4,-3);
\end{scope}
\begin{scope}[yshift=-5.2cm,xshift=2cm]
\draw[line width=1cm,red] (0,1) -- (0,-1);
\path[on layer=back] node[draw] (a) {At the back of beyond};
\node[node on layer=front] at (0,-2) (b) {At the front of beyond};
\draw[line width=1cm,red] (b.north) -- (b.south);
\draw[line width=1cm,red] node[thin,black,draw,node on layer=back] at (0,-4) (c) {At the side of beyond} (c.north) -- (c.south);
\begin{scope}[xshift=5cm]
\draw[line width=1cm,red] (0,1) -- (0,-1);
\path node[draw] (a) {At the back of beyond};
\node at (0,-2) (b) {At the front of beyond};
\draw[line width=1cm,red] (b.north) -- (b.south);
\draw[line width=1cm,red] node[thin,black,draw] at (0,-4) (c) {At the side of beyond} (c.north) -- (c.south);
\end{scope}
\end{scope}
\end{tikzpicture}
\end{document}

(注:注释掉的包是故意留下的,因为它对于弄清楚发生了什么非常有用。参见我如何调试 pgfkeys?了解有关该软件包的更多详细信息。

这是上述结果。在每种情况下,分层都发生在左侧,而不是右侧。左侧和右侧不同这一事实证明有某些东西从一层转移到了另一层。

TikZ 内联图层

答案3

这仅仅是Loop Space 的精彩答案,这使得它能够像我期望的那样与 TikZbackgrounds库一起工作。我重命名背景层和前景层的唯一原因是为了与backgrounds库以及我自己的扩展保持一致。

\documentclass[border=10pt,multi,tikz]{standalone}

\usetikzlibrary{backgrounds}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}

\makeatletter
% addasu o tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex
\tikzset{%
  on foreground layer/.style={%
    execute at begin scope={%
      \pgfonlayer{foreground}%
      \let\tikz@options=\pgfutil@empty%
      \tikzset{every on foreground layer/.try,#1}%
      \tikz@options%
    },
    execute at end scope={\endpgfonlayer}
  },
  % addasu o ateb Loop Space: https://tex.stackexchange.com/a/20426/
  %\url{https://tex.stackexchange.com/q/46957/86}
  on layer/.code={%
    \pgfonlayer{#1}\begingroup
    \aftergroup\endpgfonlayer
    \aftergroup\endgroup
  },
  node on layer/.code={%
    \gdef\node@@on@layer{%
      \setbox\tikz@tempbox=\hbox\bgroup\pgfonlayer{#1}\unhbox\tikz@tempbox\endpgfonlayer\egroup}
    \aftergroup\node@on@layer
  },
}

\def\node@on@layer{\aftergroup\node@@on@layer}

\makeatother

\begin{document}
\begin{tikzpicture}
  \draw[line width=1cm,red] (2,1) -- (2,-1);
  \draw[ultra thick,white,preaction={on layer=background,line width=1cm,blue,draw}] (0,0) -- (4,0);
  \draw[line width=1cm,red] (2,-2) -- (2,-4);
  \draw[ultra thick,white,postaction={on layer=background,line width=1cm,blue,draw}] (0,-3) -- (4,-3);
  \begin{scope}[xshift=5cm]
    \draw[line width=1cm,red] (2,1) -- (2,-1);
    \draw[ultra thick,white,preaction={line width=1cm,blue,draw}] (0,0) -- (4,0);
    \draw[line width=1cm,red] (2,-2) -- (2,-4);
    \draw[ultra thick,white,postaction={line width=1cm,blue,draw}] (0,-3) -- (4,-3);
  \end{scope}
  \begin{scope}[yshift=-5.2cm,xshift=2cm]
    \draw[line width=1cm,red] (0,1) -- (0,-1);
    \path[on layer=background] node[draw] (a) {At the background of beyond};
    \node[node on layer=foreground] at (0,-2) (b) {At the front of beyond};
    \draw[line width=1cm,red] (b.north) -- (b.south);
    \draw[line width=1cm,red] node[thin,black,draw,node on layer=background] at (0,-4) (c) {At the side of beyond} (c.north) -- (c.south);
    \begin{scope}[xshift=5cm]
      \draw[line width=1cm,red] (0,1) -- (0,-1);
      \path node[draw] (a) {At the background of beyond};
      \node at (0,-2) (b) {At the front of beyond};
      \draw[line width=1cm,red] (b.north) -- (b.south);
      \draw[line width=1cm,red] node[thin,black,draw] at (0,-4) (c) {At the side of beyond} (c.north) -- (c.south);
    \end{scope}
  \end{scope}
  \path (current bounding box.north west) -- (current bounding box.north east) \foreach \i [count=\j] in {0,.225,.275,.5,.725,.775,1} {coordinate [pos=\i] (p\j)};
  \begin{scope}[on background layer]
    \fill [yellow, fill opacity=.3] (current bounding box.north west) rectangle (current bounding box.south -| p2) (p7) rectangle (p6 |- current bounding box.south);
  \end{scope}
  \begin{scope}[on foreground layer]
    \fill [green, fill opacity=.3] (p2) rectangle (current bounding box.south -| p3) (p6) rectangle (p5 |- current bounding box.south);
  \end{scope}
   \fill [magenta, fill opacity=.3] (p3) rectangle (current bounding box.south -| p4) (p5) rectangle (p4 |- current bounding box.south);
\end{tikzpicture}
\end{document}

分层

相关内容