我正在使用 TiKZ 绘制同一图形的两个副本,现在我想在一个副本中的顶点和另一个副本中的顶点之间添加边。我突然想到,如果我可以将每个副本放在单独的范围内,为每个范围命名,并以“面向对象”的方式引用范围内的节点,将节点称为 ,那就太好了<scope name>.<node name>
。为了说明这一点,如果我可以做这样的事情,那就太酷了:
\documentclass{article}
\usepackage{tikz}
\begin{document}
\tikzstyle{vertex}=[circle,draw,fill=black!20]
\begin{tikzpicture}
% ---- Copy 1
\begin{scope}[yshift=-32pt,name=G1]
\node[vertex] (u) at (0, 0) {u};
\node[vertex] (v) at (0, 0) {v};
\end{scope}
% ---- Copy 2
\begin{scope}[yshift=32pt,name=G2]
\node[vertex] (u) at (0, 0) {u};
\node[vertex] (v) at (0, 0) {v};
\end{scope}
\draw (G1.u) -- (G2.v);
\end{tikzpicture}
\end{document}
在这里,我将u
范围的节点G1
称为G1.u
,将v
范围的节点G2
称为G2.v
。
在 TiKZ 中可能发生这样的事情吗?
答案1
这是一个简单的 hack,它重新定义了范围内的命名代码以附加前缀。.
但是您不能使用 a 作为分隔符,因为这会使解析器感到困惑。我使用了空格,但您可以使用其他东西(一些标点符号,例如.
is special,这里有一个列表)。
\documentclass{article}
%\url{http://tex.stackexchange.com/q/128049/86}
\usepackage{tikz}
\begin{document}
\tikzstyle{vertex}=[circle,draw,fill=black!20]
\makeatletter
\tikzset{%
prefix node name/.code={%
\tikzset{%
name/.code={\edef\tikz@fig@name{#1 ##1}}
}%
}%
}
\makeatother
\begin{tikzpicture}
% ---- Copy 1
\begin{scope}[yshift=-32pt,prefix node name=G1]
\node[vertex] (u) at (0, 0) {u};
\node[vertex] (v) at (0, 0) {v};
\end{scope}
% ---- Copy 2
\begin{scope}[yshift=32pt,prefix node name=G2]
\node[vertex] (u) at (0, 0) {u};
\node[vertex] (v) at (0, 0) {v};
\end{scope}
\draw (G1 u) -- (G2 v);
\end{tikzpicture}
\end{document}
请注意,这适用于节点的隐式和显式命名(即通过name=<name>
和\node (name) ...
)。
答案2
scopes
在 TiKZ 3.0 中可以使用而不是pics
。如果将 aname
分配给 a pic
,则该名称将充当name prefix
并附加在任何内部节点名称之前。
举个例子比我的英语更清楚:
\documentclass{standalone}
\usepackage{tikz}
\begin{document}
%\tikzstyle{vertex}=[circle,draw,fill=black!20]
\tikzset{vertex/.style={circle, draw, fill=black!20},
myscope/.pic={
\node[vertex] (-u) at (0,0) {u};
\node[vertex] (-v) at (0,1) {v};
}
}
\begin{tikzpicture}
\pic[yshift=-32pt] (G1) {myscope};
\pic[yshift= 32pt] (G2) {myscope};
\draw (G1-u) to[out=30,in=-30] (G2-v);
\draw (G2-u) to[out=210,in=150] (G1-v);
\end{tikzpicture}
\end{document}
答案3
编辑:看起来我下面的回答完全没有必要,因为 TikZ 有一个标准name prefix
键,其作用与我写的非常相似(尽管它没有用空格将前缀与名称分开)。
手册中对这个name prefix
钥匙有这样的说法:
此键的值是当前范围内每个节点的前缀。这包括节点的命名(通过 name 键或通过隐式 (⟨name⟩) 语法)以及对节点的任何引用。在范围之外,可以(并且需要)使用由前缀和节点名称组成的“全名”来引用节点。
这样做的最终效果是,您可以将范围开头的名称前缀设置为某个值,然后对范围内的节点使用简短的名称。稍后,在范围之外,您可以通过其全名引用节点:
\tikz { \begin{scope}[name prefix = top-] \node (A) at (0,1) {A}; \node (B) at (1,1) {B}; \draw (A) -- (B); \end{scope} \begin{scope}[name prefix = bottom-] \node (A) at (0,0) {A}; \node (B) at (1,0) {B}; \draw (A) -- (B); \end{scope} \draw [red] (top-A) -- (bottom-B); }
使用内置功能几乎肯定是最好的选择。
原始答案:
循环空间给出了一个很好的答案,只要您不需要通过无前缀的名称来引用节点,它就可以起作用。
例如,如果你\draw (u) -- (v);
在每个作用域内添加一个,就会出现一个有点神秘的错误,Package pgf Error: No shape named u is known.
更好的选择是改变定义,prefix node name
不改变名称的记录方式,而是添加带有前缀的别名。
\tikzset{
prefix node name/.style={%
/tikz/name/.append style={%
/tikz/alias={#1 ##1}%
}%
}
}
本质上,添加prefix node name=foo
到范围将导致name=bar
范围内的所有内容都表现得好像它是一样name=bar,alias={foo bar}
。
这里是对 Loop Space 示例的修改,使用了上面的定义,prefix node name
并对图片进行了轻微的修改(因此节点不会彼此重叠),并\draw
添加了一些 s 来显示效果。
\documentclass{article}
\usepackage{tikz}
\tikzstyle{vertex}=[circle,draw,fill=black!20]
\tikzset{
prefix node name/.style={%
/tikz/name/.append style={%
/tikz/alias={#1 ##1}%
}%
}
}
\begin{document}
\begin{tikzpicture}
% ---- Copy 1
\begin{scope}[yshift=32pt,prefix node name=G1]
\node[vertex] (u) at (0, 0) {u};
\node[vertex] (v) at (2, 0) {v};
\draw (u) -- (v);
\end{scope}
% ---- Copy 2
\begin{scope}[yshift=-32pt,prefix node name=G2]
\node[vertex] (u) at (0, 0) {u};
\node[vertex] (v) at (2, 0) {v};
\draw (u) -- (v);
\end{scope}
\draw[->] (G1 u) -- (G2 v);
\end{tikzpicture}
\end{document}