6

The following code compiles in both DVI and PDF format. However, if I draw anything after the \end{scope} command, it won't compile in DVI, and I get the following error message:

Latex> ! Undefined control sequence.
Latex> \tikz@intersect@namedpaths ...t@path@name@line \x 
                                                          \endcsname {\pgfsyssoftpat...

The PDF registers the same error but the compilation goes through and the image is rendered. What is causing this error? I have so much more to add to this figure!

UPDATE: As requested, I've added a random draw command \draw (0,0) circle [radius=1cm]; which then triggers the error. Using path name global=line \x does not solve the problem.

\documentclass{article}

\usepackage{tikz} 
\usetikzlibrary{intersections,backgrounds}

\begin{document}
\begin{tikzpicture} 
%axis 
\draw (-.5,0) -- (6.5,0);  

%curve  
\draw[yshift=1cm,name path=curve] (-.5,0) %vertically shiftable
   to[out=70,in=180] (.7,1.5)
   to[out=0,in=180] (2,.5)
   to[out=0,in=180] (4.5,2.5)
   to[out=0,in=160] (6.5,1);

%rectangles
\begin{scope}[on background layer]
\foreach \x in {0,1,2,5}{
    \path[name path=line \x] (\x,0) -- (\x,4);
    \path[name intersections={of=curve and line \x, by={isect \x}}];    
    \draw[fill=gray!50] (isect \x) rectangle (\x+1,0);
    \draw[fill] (isect \x) circle [radius=2pt];
    }
\end{scope}   

\draw (0,0) circle [radius=1cm]; % Won't compile with this added draw

\end{tikzpicture}
\end{document}
  • If I add \draw (0,0)--(1,1) I get the same error both with pdflatex and latex – egreg Dec 18 '13 at 18:13
  • @egreg Upon closer examination, I see that I also get the pdflatex error, but the compilation goes through and the image is rendered. – steven_nevets Dec 18 '13 at 18:36
  • Please make the example into one that produces an error. – egreg Dec 18 '13 at 18:42
  • I do not get any error with a \draw (0,0)--(1,1); afetr end{scope}. Current TL2013. It looks like that you used \x after the scope end –  Dec 18 '13 at 18:49
  • @Herbert What is TL2013? I definitely did not use \x after \end{scope}. – steven_nevets Dec 18 '13 at 19:12
  • From the error message, it seems that you're trying access a path name that is only available within the scope. Try changing name path=line \x to name path global=line \x. By the way, TL2013 stands for TeXLive 2013. – Herr K. Dec 18 '13 at 19:16
  • I can’t reproduce your error here. Can you edit your question so that your code produces the error. You will either need name path global and/or the .expanded handler (to expand \x properly). – Qrrbrbirlbel Dec 18 '13 at 22:13
  • @Qrrbrbirlbel Per request, I've edited my code to include a draw command which generates the error. Thank you for your feedback. – steven_nevets Dec 18 '13 at 22:35
  • @steven_nevets: again: no problem for me with an up-to-date TeXLive2013 –  Dec 18 '13 at 22:38
  • Now I'm getting the error message you got from your MWE. As @Qrrbrbirlbel suggests, changing name path=... to name path/.expanded=... solves the problem. – Herr K. Dec 19 '13 at 04:20
  • @KevinC Problem solved! What does the `.expanded' key actually do? Thank you everyone for your persistence. – steven_nevets Dec 19 '13 at 06:04
  • Ps. I would like to indicate that this question has been successfully answered, but I don't see an option to do that. – steven_nevets Dec 19 '13 at 06:10

1 Answers1

7

This is, I think, a bug in the intersections library in TikZ. Although it does not produce an error with the CVS version of TikZ, the underlying bug is still there.

When TikZ names a path then it has to save that path to a macro. Because the path might undergo considerable changes as it is being processed, the best time to save it is right at the end. So the intersection library hooks in to the macro \tikz@finish which is called last of all. So it stores up all the necessary intersection code into a macro, \tikz@intersect@namedpaths, which it then executes right at the end. The addition of code looks like this:

\def\tikz@intersect@finish{%    
        \ifx\tikz@intersect@namedpaths\pgfutil@empty%
        \else%
                \tikz@intersect@namedpaths%
                \let\tikz@intersect@namedpaths=\pgfutil@empty%
        \fi%
}

This is hooked into \tikz@finish to run after all the other stuff.

The problem with this is that \let. The macro \tikz@intersect@namedpaths is defined globally. This is because inside a path then there can be all manner of groupings involved and TikZ doesn't want to have to bother with them. So it defines this globally. However, it only clears it locally. Normally, this is alright because the clearing happens at the same level as the \path command. In this situation, though, that is inside the scope meaning that when the scope ends then \tikz@intersect@namedpaths is restored to its previous global value. This is the value it had during the \path[name path=line \x] (\x,0) -- (\x,4); when it saved the path with name line \x. So when the \draw command after the scope is executed, \tikz@intersect@namedpaths is not empty and so is processed.

This is definitely a bug, because it means that any paths named inside the scope are now available outside it - so if you use the same path name before the scope and inside it then the path after the scope is the one defined inside, not the previous one.

The reason for the complaint in the code is that in an earlier version of TikZ then the macro \tikz@intersect@namedpaths had the path names stored as they were specified. So it stored the path name (in your example) as line \x. This means that when \tikz@intersect@namedpaths is executed in the \draw command outside the scope, it tries to figure out the meaning of \x and fails, because it is no longer defined. Later versions of TikZ expand the path name first, meaning that line \x is stored as, for example, line 1. This is why there is no longer an error produced with later versions of TikZ, or when the path name is expanded before it is passed in to the code (this is what the .expanded does). However, the underlying bug is still present and still detectable by looking at what paths are saved.

The real fix is to make the reset of \tikz@intersect@namedpaths into a global reset. This is simple enough: prepend \global to that \let. Thus the correct solution is to add the following to your preamble:

\makeatletter
\def\tikz@intersect@finish{%    
        \ifx\tikz@intersect@namedpaths\pgfutil@empty%
        \else%
                \tikz@intersect@namedpaths%
                \global\let\tikz@intersect@namedpaths=\pgfutil@empty%
        \fi%
}
\makeatother

Here's an example showing the persistence of paths. The key is the blue circle. This ought to be at the intersection of the lower path, but due to using the same name inside the scope, it gets shifted to the upper path (after one extra path command since we need \tikz@intersect@namedpaths to be processed). Redefining \tikz@intersect@finish as I describe fixes this.

\documentclass{article}
%\url{http://tex.stackexchange.com/q/150598/86}
\usepackage{tikz} 
\usetikzlibrary{intersections}

\begin{document}

\begin{tikzpicture}
\draw[name path=abc] (-3,-3) -- (3,3);
\draw[name path=def] (3,-3) -- (-3,3);
\draw[green, ultra thick, name intersections={of=abc and def}] (intersection-1) circle[radius=6pt];
\begin{scope}
\draw[name path=abc] (-3,-2) -- (2,3);
\draw[green, ultra thick, name intersections={of=abc and def}] (intersection-1) circle[radius=6pt];
\end{scope}
\path (0,0); % needed to invoke \tikz@intersect@namedpath from inside the scope
\draw[blue, ultra thick, name intersections={of=abc and def}] (intersection-1) circle[radius=10pt];
\end{tikzpicture}

\makeatletter
\def\tikz@intersect@finish{%    
        \ifx\tikz@intersect@namedpaths\pgfutil@empty%
        \else%
                \tikz@intersect@namedpaths%
                \global\let\tikz@intersect@namedpaths=\pgfutil@empty%
        \fi%
}
\makeatother

\begin{tikzpicture}
\draw[name path=abc] (-3,-3) -- (3,3);
\draw[name path=def] (3,-3) -- (-3,3);
\draw[green, ultra thick, name intersections={of=abc and def}] (intersection-1) circle[radius=6pt];
\begin{scope}
\draw[name path=abc] (-3,-2) -- (2,3);
\draw[green, ultra thick, name intersections={of=abc and def}] (intersection-1) circle[radius=6pt];
\end{scope}
\path (0,0); % needed to invoke \tikz@intersect@namedpath from inside the scope
\draw[blue, ultra thick, name intersections={of=abc and def}] (intersection-1) circle[radius=10pt];
\end{tikzpicture}
\end{document}

Intersection bug

Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
  • 2
    I've reported this on the PGF bug tracker: https://sourceforge.net/p/pgf/bugs/285/ – Andrew Stacey Dec 19 '13 at 08:24
  • Thank you, Andrew, for your definitive and well-expressed solution to the problem - I'm duly awed by your grasp of the underlying mechanics. It is appreciated. – steven_nevets Dec 19 '13 at 08:59
  • @steven_nevets It's actually close to something I'd come across before (I think for http://tex.stackexchange.com/a/21426/86) so it didn't actually take all that much detective work! – Andrew Stacey Dec 19 '13 at 10:41