12

I am using the new TikZ/PGF 3.0 pic feature. I use them heavily, including using a pic inside another pic. The problem is that Tikz sometimes cannot find the node names of nested pics.

I expect the following to work:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}
\tikzset{complex/.pic={
  \node(re) {re};
  \node[right=0mm of re](im) {im};
}}
\tikzset{su3v/.pic={
  \path pic(c0) {complex};
  \path pic[right=1mm of c0im](c1) {complex};
  \path pic[right=1mm of c1im](c2) {complex};
}}
\tikzset{spinor/.pic={
  \path pic(v0) {su3v};
  \path pic[below=2mm of v0c2re](v1) {su3v};
  \path pic[below=2mm of v1c2re](v2) {su3v};
  \path pic[below=2mm of v2c2re](v3) {su3v};
}}

\path pic {su3v};       % Works
\path pic(name) {su3v}; % ! Package pgf Error: No shape named namec0im is known.
\path pic {spinor};     % ! Package pgf Error: No shape named v0c1im is known.
\end{tikzpicture}
\end{document}

What is wrong here?

  • name prefix .. could be a clue here. For the first level, it seems to work: \tikzset{su3v/.pic={\path pic(c0) {complex};\path pic[name prefix ..,right=1mm of c0im](c1) {complex};\path pic[name prefix ..,right=1mm of c1im](c2) {complex};}}. – cjorssen May 13 '14 at 14:42
  • Interesting; I wasn't aware of name prefix .. – Meinersbur May 13 '14 at 15:33
5

I'm going for bug on this one.

When drawing a pic, it is possible to use subnodes (and coordinates). If the outer pic is given a name then that name is passed on to all subnodes (and coordinates) as a prefix. This is a Good Thing as it means you don't risk name clash. You're nesting pics, so the names ought to accumulate. We can draw the nesting and naming as a tree, illustrating what ought to happen. The name is the name of the current pic, and the prefix is the assigned prefix for all sub pics.

pic {su3v};                    <no prefix>   <no name>
 |
 -- pic (c0) {complex}          c0            c0
     |
     -- node (re) {re}                        c0re
     -- node (im) {im}                        c0im

pic (name) {su3v};              name          name
 |
 -- pic (c0) {complex}          namec0        namec0
     |
     -- node (re) {re}                        namec0re
     -- node (im) {im}                        namec0im

pic {spinor};                  <no prefix>   <no name>
 |
 -- pic (v0) {su3v}             v0            v0
     |
     -- pic (c0) {complex}      v0c0          v0c0
         |
         -- node (re) {re}                    v0c0re
         -- node (im) {im}                    v0c0im

(For those to whom this is new, the idea is that to refer to nodes, we use the full name minus the current prefix. So inside su3v we can refer to c0im and c1im as you do, even when su3v is named or is used inside spinor.)

Now, I said that this is what ought to happen. It doesn't. What actually happens is that when a pic starts then the name prefix has the current full name set as its prefix. That is to say, the name prefix itself has a prefix and said prefix is the full name of the current pic. The key is that that name has already had its own prefix appended.

So when nesting pics, the following happens. The first named pic, let's call it outer, sets the prefix of name prefix to its name, in this case outer. So whenever name prefix is called, outer is prefixed to its value. Inside outer, let's have another pic, say inner. As this is inside outer, its real name is outerinner. The name prefix's prefix is set to this, but name prefix itself retains its value of outer. Thus the effective name prefix is outerinnerouter. Thus the actual tree is as follows:

pic {su3v};                    <no prefix>   <no name>
 |
 -- pic (c0) {complex}          c0            c0
     |
     -- node (re) {re}                        c0re
     -- node (im) {im}                        c0im

pic (name) {su3v};              name          name
 |
 -- pic (c0) {complex}          namec0name    namec0
     |
     -- node (re) {re}                        namec0namere
     -- node (im) {im}                        namec0nameim

pic {spinor};                  <no prefix>   <no name>
 |
 -- pic (v0) {su3v}             v0            v0
     |
     -- pic (c0) {complex}      v0c0v0        v0c0
         |
         -- node (re) {re}                    v0c0v0re
         -- node (im) {im}                    v0c0v0im

I should mention a useful diagnostic at this point. This is to put the following in your preamble:

\makeatletter
\tikzset{show fig name/.code={\tikz@addmode{\message{Fig name is: |\tikz@fig@name|}}}}
\makeatother

and then use the key show fig name on various elements.

I guess that the point of this is that if you manually set name prefix then you want to preserve that and to append all of the automatic prefixes. If you never do this, one fix is to copy the code for \def\tikz@subpicture@handle@ into your preamble (between the obligatory \makeatletter .. \makeatother pairing) and change

  \ifx\tikz@fig@name\pgfutil@empty\else%
    \tikzset{name prefix/.prefix/.expanded=\tikz@fig@name}%
  \fi%

to

  \ifx\tikz@fig@name\pgfutil@empty\else%
    \tikzset{name prefix/.expanded=\tikz@fig@name}%
  \fi%

A better solution would be that instead of using \tikz@fig@name, then it used the declared name (which isn't currently saved - the prefix and suffix are added when the node/pic/coordinate is first named) and added it as a suffix to the prefix to name prefix. That is to say, the crucial line might be:

\tikzset{name prefix/.prefix/.suffix/.expanded=\tikz@fig@declaredname}

and the name key would have to become:

\tikzset{
  name/.code={\edef\tikz@fig@name{\tikz@pp@name{#1}}\edef\tikz@fig@declaredname{#1}}%
 }

Unfortunately, nesting /.prefix/.suffix doesn't work so a proper fix will have to wait for the PGF people to figure out what would be a good solution. In the meantime, if you never call name prefix itself then modifying \tikz@subpicture@handle@ seems to work.

No guarantees, though.


Actually, I just thought of an alternative fix. That is to set name prefix/.prefix to \tikz@fig@name as the original code does, but then to clear name prefix itself since whatever the current name prefix is, it has been taken into account in \tikz@fig@name. Further calls to name prefix will keep the prefix to name prefix as intended.

Also, I've filed this on the PGF bug list with a link back here.

  • The \def\tikz@subpicture@handle@ solution works as I wasn't even aware of the name prefix key. Thanks for the extensive answer and filing the bug. – Meinersbur May 14 '14 at 21:13
  • \tikzset{name prefix/.expanded=\tikz@fig@name} seems to work fine. I haven't found any case yet where this breaks. – Henri Menke Nov 4 at 2:00
  • If this works, this will be in PGF 3.1.5. github.com/hmenke/pgf/commit/… – Henri Menke Nov 4 at 2:00

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.