新版本
\begin{pgfonlayer}
不能在矩阵内部使用。如果我尝试,节点会完全放错位置:
我尝试在这里提交 tikz 上的错误https://github.com/pgf-tikz/pgf/issues/1282,你们当中有人知道如何修复这个 tikz 错误吗?请注意,即使我很想了解解决方法,我还是更希望找到一种真正修复 tikz 的方法,因此我理想情况下希望将赏金分配给真正解决 Tikz 错误的人。
平均能量损失
\documentclass[]{article}
\usepackage{tikz}
\begin{document}
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground} %% some additional layers for demo
\begin{tikzpicture}
\node [matrix] (my matrix) at (2,1)
{
\node[fill=pink,name=hello]{Hello}; & \node{A}; \\
\node{B}; & \node[fill=green,name=bye]{Bye}; \\
};
\draw[very thick, red] (hello.center) -- (bye.center);
\end{tikzpicture}
Adding pgfonlayer only: I would expect the line to be above Hello, but below Bye. Instead, the node is completely misplaced:
\begin{tikzpicture}
\node [matrix] (my matrix) at (2,1)
{
\node[fill=pink,name=hello]{Hello}; & \node{A};\\
\node{B}; &
\begin{pgfonlayer}{foreground}
\node[fill=green,name=bye]{Bye};
\end{pgfonlayer}
\\
};
\draw[very thick, red] (hello.center) -- (bye.center);
\end{tikzpicture}
\end{document}
以下是原始问题(当时我以为问题出在标签和自定义node on layer
样式上……事实上,问题更为普遍)。如果你能同时解决这个问题,那就太棒了。
旧版本:
我一直在尝试这个答案https://tex.stackexchange.com/a/20426/116348仅使用其样式将节点放在图层上,就像在 中一样\node[node on layer=front]{A};
。它工作得很好……除了在 tikz 矩阵中(使用 tikzcd 测试)。在这种情况下,节点会超出矩阵……对于带有标签的节点来说,情况更糟。有什么想法可以解决它吗?
平均能量损失
\documentclass{article}
%\url{https://tex.stackexchange.com/q/46957/86}
\usepackage{tikz}
\usetikzlibrary{cd}
%\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}
No layer:
\begin{tikzcd}
|[fill=green, label={[fill=red,circle,outer sep=-1mm]Lab}]| A \rar & B
\end{tikzcd}
One layer, no label:
\begin{tikzcd}
|[fill=green, node on layer=front]| A \rar & B
\end{tikzcd}
Both:
\begin{tikzcd}
|[fill=green, node on layer=front, label={[fill=red,circle,outer sep=-1mm,node on layer=back]Lab}]| A \rar & B
\end{tikzcd}
\end{document}
答案1
矩阵很奇怪
PGF 矩阵使用原始矩阵,并且比 PGF/TikZ 中的任何其他构造都\halign
更接近于。tabular
只有在构建矩阵后才能知道每个节点的最终位置,为此,每个节点名称将收集在一个大宏中,然后循环两次遍历所有节点并更正它们的位置。然而,尽管矩阵本身将被放置在正确的位置,但通过环境,pgfonlayer
您将它的内容放在不同的框中,而这些框不会正确地转换到正确的位置。
(其内部结构很奇怪,它是盒子的组合,\halign
PGF 跟踪节点的位置的方式是……)
我们如何才能追踪我们自己的东西?
由于没有很好的方法来挂钩这个循环机制,我们使用我们自己的坐标作为这个挂钩系统的辅助。
我们不能使用原始的图层框 - 是的,每个图层只是另一个 TeX 框,它会随着时间的推移而被填充,并且在图片的末尾,图层将按照正确的顺序解包 - 但我们需要每个矩阵的每个单元至少一个框,然后将这些存储的迷你图片插入正确的图层框中。
上述坐标的位置用于在 PGF 的帮助下放置我们自己的 TeX 框\pgfqboxsynced
。
盒子
起初,我根据行号和列号为每个框(即我们存储的迷你图片)赋予一个唯一的名称(这仍然是 的pgfmatrixlayer
可选参数的默认值)。这可确保您不能对不同的单元格使用相同的框。但是,这可能会造成浪费,因为它会分配很多您可能只会使用一次的框。
但是,如果你总共只需要一到两个盒子(但每次它们都在不同的单元格中)或者如果你想把一个单元格的不同内容放在不同的层上,你可以或者必须使用可选参数并赋予它们自己的名称。不要在一个矩阵中两次使用与不同单元格中的
可选参数相同的名称。pgfmatrixlayer
放置\pgfplacematrixbox
所述盒子(希望正确)然后再次清空盒子以用于下一个矩阵。没有进行其他“垃圾收集”,以确保您不会意外地在两个矩阵中使用盒子而不在中间放置内容。
使用ext-layers
上述手动方法
我现在将上述方法与我的实验1)库结合起来 ext.layers
tikz-ext
包裹。
使用的关键是matrix node on layer = <name>:<layer>
where <name>
(和冒号)是可选的,<row>-<column>
如果没有给出,则与以前一样。因此,如果您想在不同层上的一个单元中使用两个节点,则需要提供自定义的<name>
s。
every matrix
设置样式,以便在矩阵之后重置和使用集合,而键将matrix node on layer
根据其参数填充该列表。
这感觉非常接近 PGF 所描述的延迟节点定位但我还没有尝试过。
标签位于其父节点后面?
也就是说,如果您想要的只是将一个节点放在其父节点后面,那就容易多了:
每个 TikZ 路径(即整个\path … ;
)2)已经带有三层:
- 唯一的那个
behind path
, - 路径本身和
- 唯一的那个
in front of path
这是默认设置。
这些层和键将与节点、图片、边和绘图标记一起使用:基本上所有具有其自己的路径(但没有箭头)的东西。
除非默认值已被更改,否则只需使用label={[behind path]Lab}
即可将标签置于其父标签后面。
1)事实上,它只是实验性的,我只发现了两到三个错误。2
)请记住,一切\node
都\path node
在路径上,即使该路径是空的。
代码
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{cd, ext.layers}
\makeatletter
%%%BEGIN_FOLD manual way
\def\pgfplacematrixbox#1{%
\pgfutil@IfUndefined{pgf@sh@nt@pgf@matrixlayer@#1}{%
\pgfutil@packagewarning{PGF layer matrices}{No PGF matrix layer #1 known.}%
}{%
\pgfsys@beginscope
\pgfsettransform{\csname pgf@sh@nt@pgf@matrixlayer@#1\endcsname}%
\expandafter\pgfqboxsynced\csname pgf@matrixlayer@#1\endcsname
\pgfsys@endscope
\global\expandafter\setbox\csname pgf@matrixlayer@#1\endcsname
\box\pgfutil@voidb@x
}%
}
\newenvironment*{pgfmatrixlayer}[1][\the\pgfmatrixcurrentrow
-\the\pgfmatrixcurrentcolumn]{%
\edef\pgf@m{pgf@matrixlayer@#1}%
\pgfutil@ifundefined{\pgf@m}{%
\csname pgf@newbox\expandafter\endcsname\csname\pgf@m\endcsname
}{}%
\begingroup % quick version of
% \path[name prefix=,name suffix=,reset cm]coordinate(\pgf@m);
% the \pgf@nodecallback is called by \pgfmultipartnode
% which is used by TikZ node and coordinate but not by \pgfcoordinate
% this is our hook into the whole matrix node callback stuff
% we're just using a coordinate that's handled by PGF
% so we don't have to deal with the two (!) corrections by ourselves
\pgftransformreset
\pgfcoordinate{\pgf@m}{\pgfpointorigin}%
\expandafter\pgf@nodecallback\expandafter{\pgf@m}%
\endgroup
% the rest is basically a clone of pgfcorelayers.code.tex's
% implementation of \pgfonlayer
\pgfsetlinewidth{.4pt}%
% pgfonlayers boxes are also global,
% we need this too because we're inside a cell inside a matrix
\global\expandafter\setbox\csname\pgf@m\endcsname=\hbox to 0pt\bgroup
\expandafter\box\csname\pgf@m\endcsname
\begingroup
}{%
\endgroup
\hss
\egroup
}
%%%END_FOLD
%%%BEGIN_FOLD using ext.layers and a few macros from the manual way
\tikzset{
/tikz-ext/layers/patch=node, % enable patch (should be scoped to necessary path)
reset matrix boxes/.code=%
\global\let\tikzext@layers@matrixcollection\pgfutil@empty,
every matrix/.append style={
reset matrix boxes,
append after command=\pgfextra{%
\pgfinterruptpath\tikzext@layers@matrixcollection\endpgfinterruptpath}},
@matrix node on layer/.code args={#1:#2}{%
\edef\pgf@m{pgf@matrixlayer@#1}% same as pgfmatrixlayer environment
\pgfutil@ifundefined{\pgf@m}{% but uses ext.layers interface
\csname pgf@newbox\expandafter\endcsname\csname\pgf@m\endcsname}{}%
\begingroup\pgftransformreset\pgfcoordinate{\pgf@m}{\pgfpointorigin}%
\expandafter\pgf@nodecallback\expandafter{\pgf@m}\endgroup
\pgfkeysalso{/tikz-ext/layers/in box/.expand once=\csname\pgf@m\endcsname}%
\pgfutil@g@addto@macro\tikzext@layers@matrixcollection{%
\pgfonlayer{#2}\pgfplacematrixbox{#1}\endpgfonlayer}
},
matrix node on layer/.code={% <name>:<layer> or <layer>
\pgfutil@in@{:}{#1}%
\ifpgfutil@in@ % .expanded just to be safe
\tikzset{@matrix node on layer/.expanded={#1}}%
\else % .expanded needed
\tikzset{@matrix node on layer/.expanded=%
\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn:{#1}}%
\fi}}
%%%END_FOLD
\makeatother
%%% Layer seup
\pgfdeclarelayer{back}
\pgfdeclarelayer{front}
\pgfsetlayers{back, main, front}
\begin{document}
\begin{tikzpicture}
\node [matrix, draw] (my matrix) at (2,1) {
\begin{pgfmatrixlayer}
\node[fill=pink] (back) {back};
\end{pgfmatrixlayer}
& \node{A}; \\
\node{B};
& \begin{pgfmatrixlayer}
\node[fill=green] (front) {front};
\end{pgfmatrixlayer}
\\};
\begin{pgfonlayer}{front}
\pgfplacematrixbox{2-2}
\end{pgfonlayer}
% after front but should be below
% before back but should be above
\draw[very thick, red] (back.center) -- (front.center);
\begin{pgfonlayer}{back}
\pgfplacematrixbox{1-1}
\end{pgfonlayer}
\end{tikzpicture}
\tikzset{every label/.append style={fill=red, shape=circle, label distance=+-1mm}}
\begin{tikzcd} % normal
|[fill=green, label=Lab]| A \rar & B
\end{tikzcd}
\begin{tikzcd} % one node in front (uses box 1-1)
|[fill=green, matrix node on layer=front]| A \rar & B
\end{tikzcd}
\begin{tikzcd} % one node in front, one node in back
|[fill=green,
matrix node on layer=front:front,
label={[matrix node on layer=back:back]Lab}]| A \rar & B
\end{tikzcd}
\begin{tikzcd} % label behind node is much easier
|[fill=green, label={[behind path]Lab}]| A \rar & B
\end{tikzcd}
\end{document}
输出
答案2
一种解决方案是使用 apreaction
或 apostaction
作为要插入另一层的节点。不过标签的行为很奇怪。当保留在主层上时,它可以正常工作。
评论。node on layer=...
当尝试通过您定义的不带 的命令引入节点时preaction
,输出是位于边界框中心的节点。
我包含了上面示例的代码,但主要是您原来的代码和一些preactions
添加的代码。
代码
\documentclass{article}
%\url{https://tex.stackexchange.com/q/46957/86}
\usepackage{tikz}
\usetikzlibrary{cd}
\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}
No layer:
\begin{tikzcd}
|[fill=green, label={[fill=yellow, circle, outer sep=-1mm]Lab}]| A \rar
& B
\end{tikzcd}
\vspace{1cm}
Many layers, no label:
\begin{tikzcd}[outer sep=1ex, column sep=4em]
|[postaction={fill=green, node on layer=back}]| A
\arrow{d} \arrow{r}
& B \\
C \arrow{r}
& |[shift={(0, .5)},
preaction={fill=blue!50, node on layer=front}]| D \arrow{r}
\arrow{d}
& D' \\
E \arrow{r}
& F
\end{tikzcd}
\vspace{1cm}
Two layers and label on \emph{main}:
\begin{tikzcd}
|[postaction={fill=green, node on layer=front}]
[label={%
[fill=yellow, circle, outer sep=-1mm] 90:Lab
}]| A \rar & B
\end{tikzcd}
\vspace{1cm}
Two layers and label on \emph{front} outside the preaction (or post):
\begin{tikzcd}
|[postaction={fill=green, node on layer=front}]
[label={%
[fill=yellow, circle, node on layer=front, outer sep=-1mm] 90:Lab
}]| A \rar & B
\end{tikzcd}
\vspace{1cm}
Two layers and label on \emph{front}:
\begin{tikzcd}
|[postaction={fill=green, node on layer=front}]
[postaction={
label={%
[fill=yellow, circle, node on layer=front, outer sep=-1mm] 90:Lab
}
}]| A \rar & B
\end{tikzcd}
\end{document}