7

Buildup

Basic setting

Using the intersection library, I encountered some behaviour of path names that I do not understand. The following works in the intended way:

\documentclass{scrartcl}

\usepackage{tikz}
\usetikzlibrary{intersections}

\begin{document}
\begin{tikzpicture}
\draw[name path=pos] (0, -1) -- (2, 1);
\draw[name path=neg] (0,  1) -- (2, -1);

\fill[name intersections={of=pos and neg}] (intersection-1) circle (2pt);
\end{tikzpicture}
\end{document}

Looping into a problem

But I have multiple paths to intersect, so lets change it up a bit:

\documentclass{scrartcl}

\usepackage{tikz}
\usetikzlibrary{intersections}

\begin{document}
\begin{tikzpicture}
\draw[name path=pos] (0, -1) -- (2, 1);
% Only change: A senseless “loop”
\foreach \i in {0} {
    \draw[name path=neg] (0, 1) -- (2, -1);
}

\fill[name intersections={of=pos and neg}] (intersection-1) circle (2pt);
\end{tikzpicture}
\end{document}

This looks to me as if it should be completely equivalent, but now I get error messages:

Package tikz Error: I do not know the path named `neg'. Perhaps you misspelt it. ...[red, name intersections={of=pos and neg}]
Package pgf Error: No shape named intersection-1 is known. ...ections={of=pos and neg}] (intersection-1)

The circle is drawn anyway, since pgf simply assumes the coordinates of (intersection-1) to be (0, 0), but this was not the behaviour I expected or hoped for.

A strange workaround

By drawing anything afterwards, we can get it to run smoothly again:

\documentclass{scrartcl}

\usepackage{tikz}
\usetikzlibrary{intersections}

\begin{document}
\begin{tikzpicture}
\draw[name path=pos] (0, -1) -- (2, 1);
\foreach \i in {0} {
    \draw[name path=neg] (0, 1) -- (2, -1);
}

% Only change: Senseless path of vanishing length
\path (5, -1);

\fill[name intersections={of=pos and neg}] (intersection-1) circle (2pt);
\end{tikzpicture}
\end{document}

Question

I would like to know why this happens.

Solutions and workarounds are of interest, too, but might be better suited as answers to this similar question, even though the existing answers are mostly concerned with the expansion of options.

Non-Explanations

As the third variant works without any problems, there is no general problem exporting path names out of loops, even though using name path global provides another workaround.

I did not encounter the same behaviour when declaring nodes, so this seems to be a rather specific problem of the intersection library.

  • 2
    Yes, that's an interesting observation. I guess to really understand that you need to look at libraries/tikzlibraryintersections.code.tex. There you can find all sorts of FIXME statements and questions whether or not one needs to reset the path. Once you will have the choice to either rewrite this library or just to use name path global, which worked perfectly fine in all situations I needed it so far. – user121799 Aug 14 '18 at 14:54
  • It's also curious that, if you move the \path (5, -1); into the loop, the error reappears. – user121799 Aug 14 '18 at 15:51
  • @marmot I do not actually think that is curious. It seems to be a bug in the handling of the loop. There is no reason to assume that moving more stuff into the loop makes the list of path names update the same way pretty much any command after the loop seems to do. – Hermann Döppes Aug 14 '18 at 15:58
  • I had forgotten to add the x-offset from my experiments into the question before posting. This is fixed now. – Hermann Döppes Aug 14 '18 at 16:03
  • 2
    I would argue that the fix shouldn't work, and that it may stop working without warning after an update. – John Kormylo Aug 14 '18 at 20:44
4

The problem is global vs local as suggested by @marmot in his comment.

The body of \foreach is executed in a group, so all paths named with name path (or the equivalent name path local) are supposed to be non accessible outside the group because they are defined with \def and not with \gdef (as is the case for name path global).

You can check that the problem is group related and not specific to \foreach in the following example where typeout is used to display to the console the "neg" path stored in \tikz@intersect@path@name@neg.

\documentclass[tikz,border=7pt]{standalone}
\usetikzlibrary{intersections}
\makeatletter
\begin{document}
  \begin{tikzpicture}
    \draw[name path=pos] (0, -1) -- (2, 1);
    {
      \draw[name path=neg] (0,  1) -- (2, -1);
      \typeout{=== inside === : \meaning\tikz@intersect@path@name@neg}
    }
    \typeout{=== outside === : \meaning\tikz@intersect@path@name@neg}

    \fill[name intersections={of=pos and neg}] (intersection-1) circle (2pt);
  \end{tikzpicture}
\end{document}

where the output is

=== inside === : macro:->\pgfsyssoftpath@movetotoken {0.0pt}{28.45274pt}\pgfsyssoftpath@linetotoken {56.90549pt}{-28.45274pt}
=== outside === : undefined
! Package tikz Error: I do not know the path named `neg'. Perhaps you misspelt it.

So what is happening when we add some path command outside of the group ?

The TikZ library intersections redefine \tikz@finish that is executed at every tikz command and run the GLOBAL \tikz@intersect@namedpaths (which contains the definition of the last path) if there is no name intersections option. So any \path command outside the group redefine the last path in the current group.

This is a bug. Which is probably known because we can see in the code :

% FIXME : it is reasonable to reset this globally as it is global
% in its nature. But the reset instruction should be moved to
% \endscope or something related. Resetting it here breaks the
% manual 

We can check all of this in the following code

\documentclass[tikz,border=7pt]{standalone}
\usetikzlibrary{intersections}
\makeatletter
\begin{document}
  \begin{tikzpicture}
    \draw[name path=pos] (0, -1) -- (2, 1);
    {
      \draw[name path=neg] (0,  1) -- (2, -1);
      \typeout{=== inside, neg === : \meaning\tikz@intersect@path@name@neg}
    }
    \typeout{=== outside, before path === : \meaning\tikz@intersect@path@name@neg}
    \typeout{=== the tikz@intersect@namedpaths === : \meaning\tikz@intersect@namedpaths}
    \path;
    \typeout{=== outside, after path === : \meaning\tikz@intersect@path@name@neg}

    \fill[name intersections={of=pos and neg}] (intersection-1) circle (2pt);
  \end{tikzpicture}
\end{document}

where the output is

=== inside, neg === : macro:->\pgfsyssoftpath@movetotoken {0.0pt}{28.45274pt}\pgfsyssoftpath@linetotoken {56.90549pt}{-28.45274pt}
=== outside, before path === : undefined
=== the tikz@intersect@namedpaths === : macro:->\def \tikz@intersect@path@name@neg {\pgfsyssoftpath@movetotoken {0.0pt}{28.45274pt}\pgfsyssoftpath@linetotoken{56.90549pt}{-28.45274pt}}
=== outside, after path === : macro:->\pgfsyssoftpath@movetotoken {0.0pt}{28.45274pt}\pgfsyssoftpath@linetotoken {56.90549pt}{-28.45274pt}

Conclusion

The normal behavior is that if you name a path with name path inside a group (for example inside \foreach) it should not be available outside the group. If you want to make it available outside the group you should use name path global instead (as indicated by @marmot in his commentary).

The fact that using a simple \path outside the group make last named path available in the current group is a bug.

Comment: Actually this bug is already reported.

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.