8

I am using TiKZ to draw two copies of the same graph above each other, and now I want to add edges between a vertex in one copy and a vertex in the other copy. It suddenly occurred to me that it would be nice if I could put each copy in a separate scope, give each scope a name, and refer to nodes inside the scope in an "object oriented" way by referring to the node as <scope name>.<node name>. To illustrate, it would be cool if I could do something like this:

\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}

Here, I refer to node u of scope G1 as G1.u, and to node v of scope G2 as G2.v.

Is something like this possible in TiKZ?

  • You can use the name key in a style that reads the name of the scope and adds the name to it. But then you have to supply names in the options. It can be possible to hack into the naming but the question then becomes is it worth it. Also if you are feeling adventurous you can have a look at the OOP section in the manual. – percusse Aug 13 '13 at 17:32
11

Here's a simple hack that redefines the naming code inside the scope to append a prefix. You can't use a . as a separator though as that would confuse the parser. I've used a space, but you could use something else (some punctuation, such as . is special, there's a list somewhere here).

\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}

Note that this works both with implicit and explicit naming of nodes (ie via name=<name> and \node (name) ...).

  • haha much simpler than I thought. – percusse Aug 13 '13 at 20:29
  • Ha, this is why i love tex.stackexchange! This is exactly what I meant. Thanks, Andrew! – yyzz Aug 14 '13 at 7:43
  • 2
    There's some explanation on what's allowed in node names in the answers at tex.stackexchange.com/q/28228/86 That's the best I can find so far. – Loop Space Aug 14 '13 at 8:33
5

Instead of scopes, with TiKZ 3.0 is possible to use pics. If a name is assigned to a pic, this name acts as name prefix and is appended before any internal node name.

An example will be more clear than my english:

\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}

enter image description here

1

Edit: It looks like my answer below is completely unnecessary since TikZ has a standard name prefix key that acts much the same way as what I wrote (although it doesn't separate with the prefix from the name with spaces).

The manual has this to say about the name prefix key:

The value of this key is prefixed to every node inside the current scope. This includes both the naming of the node (via the name key or via the implicit (⟨name⟩) syntax) as well as any referencing of the node. Outside the scope, the nodes can (and need to) be referenced using “full name” consisting of the prefix and the node name.

The net effect of this is that you can set the name prefix at the beginning of a scope to some value and then use short and simple names for the nodes inside the scope. Later, outside the scope, you can reference the nodes via their full 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);
}

Using built-in functionality is almost certainly the best choice.


Original answer:

Loop Space gave a nice answer that works as long as you don't need to refer to the nodes by their unprefixed names.

For example, if you add a \draw (u) -- (v); inside each of those scopes, you get the somewhat mysterious error, Package pgf Error: No shape named u is known.

A better option is to change the definition of prefix node name to not change how names are recorded but to add an alias with the prefix.

\tikzset{
  prefix node name/.style={%
    /tikz/name/.append style={%
      /tikz/alias={#1 ##1}%
    }%
  }
}

In essence, adding prefix node name=foo to a scope will cause all name=bar inside the scope to act as if it were name=bar,alias={foo bar}.

Here's a modification of Loop Space's example using the above definition of prefix node name and a slight modification to the picture (so the nodes aren't on top of each other) and with some \draws added to show the effect.

\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}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.