0

This is the third and final post in the line of questioning regarding the order of execution of options, pdfextra code and other elements along a TikZ path. The other posts being this and this.

Consider the following LaTeX manuscript

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
    \begin{tikzpicture}
        \def\x{outside}

        \path[draw=blue,font=\Huge]

                 \pgfextra{  \def\x{inside}
                             \draw (2,2) circle(5pt)
                                   node[anchor=south west]{xtra};}

                 (0,0) node[draw] {\x}
         ;
    \end{tikzpicture}
\end{document}

The manuscript features a TikZ picture. The first action to be executed inside the picture is defining the TeX macro \x to be the string 'outside'. Then a TikZ path is defined with two options: the draw color is set to 'blue', and the font size is set to '\Huge'. The path is composed of two elements: (1) a \pgfextra block. The block redefines the macro \x as the string 'inside'. It then draws a circle and next to it - a node with the display text 'xtra'. (2) A node whose display text is \x's replacement text. The node's outline is drawn.

This manuscript typesets thus:

                                         1st manuscript

Note that the path's font option applied to both the \pdfextra code as well as the node, however the draw options applied only to the node. Why?

One possible theory in answer to this question is that, for whatever reasons, the code is executed in the following order: 1. font option, 2. \pdfextra, 3. draw option, 4. node.

Let's put this theory to the test by moving the \pgfextra element to after the node:

\path[draw=blue,font=\Huge]

     (0,0) node[draw] {\x}

     \pgfextra{  \def\x{inside}
                 \draw (2,2) circle(5pt)
                 node[anchor=south west]{xtra};}
     ;

I expect the node's display text to be 'outside', and the circle's stroke color to be blue. However, I'm only partly right:

                                         2nd manuscript

Evan Aad
  • 11,066
  • I don't expect to get a straight answer to this question at the present time, but maybe someday in the future someone, possibly even me, will be able to answer this satisfactorily. – Evan Aad Aug 03 '17 at 08:45
  • 2
    Every \path option resets the colors locally hence if a new path is started colors are reset to black. There is no execution order in TeX. Only the scope that the macros are defined are valid. Current values of the macros are the only thing that matters. That's why expansion is central to TeX language. – percusse Aug 03 '17 at 09:37
  • @percusse: This sounds like a good theory. Unfortunately, the same behavior w.r.t. the circle's draw color is observed when I replace the code inside the \pgfextra with an equivalent code in PGF: \def\x{inside}\pgftransformshift{\pgfpoint{2cm}{2cm}}\pgfpathcircle{\pgfpointorigin}{5pt}\pgfusepath{stroke}\pgfnode{rectangle}{south west}{xtra}{}{\pgfusepath{discard}}\pgftransformshift{\pgfpoint{-2cm}{-2cm}} – Evan Aad Aug 03 '17 at 12:03
  • @percusse: However, interestingly, when the code is in PGF, the \pgfextra font isn't inherited from the path's font option. – Evan Aad Aug 03 '17 at 12:04
  • 1
    They are not equivalent. You are mixing 5 lines of PGF with a way more compicated \path node command. No keys are invoked in PGF in that code and it's not a theory. Again, you can see it in the source code. – percusse Aug 03 '17 at 12:20
  • @percusse: You're right, they are not equivalent. I did not express myself properly. What I meant to say was this. You wrote that "every \path operation resets the colors locally." This may be so. I don't know, but I trust your word. At any rate I do realize that TikZ commands do a lot more behind the scenes than meets the eyes. – Evan Aad Aug 03 '17 at 13:52
  • @percusse: However, PGF commands are straightforward. What you see is what you get. So I replaced the TikZ heavy machinery with lightweight PGF code where you can see exactly what's going on, and, in particular, you can see that the draw color is not reset. The picture that I draw with PGF is immaterial; I could've drawn something else entirely. The point was the draw color was nevertheless "reset" to black. – Evan Aad Aug 03 '17 at 13:52
  • When I say \path I mean the TikZ \path command. I don't mean every path including PGF ones. – percusse Aug 03 '17 at 14:05
  • @percusse: I never understood it differently. – Evan Aad Aug 03 '17 at 14:07
  • @percusse: I got to say, though, that with the PGF code inside the \pgfextra the overall behavior of the \path command makes sense to me, since then the \pgfextra code picks up none of the options: neither the draw option nor the font option. What baffles me in the original version is that the font option is heeded. – Evan Aad Aug 03 '17 at 14:18
  • @percusse: But I gradually come to resign to the fact that I simply should stay away of \pgfextra and not stir up dormant spooks from their graves. – Evan Aad Aug 03 '17 at 14:26
  • 1
    To understand the observed behavior, you can trace the calls to \pgfinterruptpath: insert \let\orig\pgfinterruptpath\def\pgfinterruptpath{\typeout{pgfinterruptpath}\orig} in your preamble... – Paul Gaborit Aug 04 '17 at 08:20

2 Answers2

4

Here is an example demonstrating why your questions are almost always impossible to answer with simple arguments. This one features an expansion delay such that we change the color of a node after it is placed. which is not possible in usual syntax but then possible with abusing and hacking.

\documentclass{article}
\usepackage{tikz}
\def\mymacro{\expandafter\global\expandafter\let\expandafter\pgfsavedstrokecolor\csname\string\color@pgfstrokecolor\endcsname}
\begin{document}
\begin{tikzpicture}
\path[draw=blue,font=\Huge]
     \pgfextra{
     \def\x{inside}\mymacro\show\pgfsavedstrokecolor% <-- look at the log it's black again 
     \expandafter\show\csname tikz@strokecolor\endcsname% <-- this one is still blue
     \draw (2,2) circle(5pt)%
     node[draw,anchor=south west]{xtra}
     % we'll change the color of the node above to red with the next line!!
     \pgfextra{\csname tikz@addoption\endcsname{\pgfsetstrokecolor{red}}}
     node[draw]{xtra2}; %<-- draws red
     }
     (0,0) node[draw] {\x};
\end{tikzpicture}
\end{document}

enter image description here

And on top of this black magic, if you comment out the last \pgfextra both of them are not blue but black. Not because a bug! but because our hack leaves out half of the macros needed for this to work properly. This is what you have been doing and getting confused. There are too many macros to be set/reset to mimic TikZ behavior.

In summary, you can't expect things to work at the low levels without taking extreme precautions such as proper TeX groups and expansion control. That's the whole idea of TikZ frontend such that you don't need to do these yourself. Pausing in the middle of a path and doing nontrivial things require TeX knowledge which is often beyond me and proper order of PDF literals. Otherwise you get voodoo magic as above.

In short, things are not that simple to theorize and as commented there is no execution order until to the very end when things are finally fully expanded.

percusse
  • 157,807
  • I won't accept this answer, because it doesn't answer my question, but I like it, and you've made your point across. – Evan Aad Aug 03 '17 at 15:48
  • @EvanAad If an explanation of why your question cannot be reasonably answered in an answer here is not acceptable, then should your question be closed? ('Too broad', maybe?) People often give answers of this kind, when no other answer can be given, simply because the question is based on too many mistaken assumptions. Not just questions which cannot be answered for technical reasons, but questions which cannot be answered for site-based-limitations-combined-with-technical reasons. – cfr Aug 04 '17 at 03:12
  • @cfr: An explanation of why my question cannot be reasonably answered here would be acceptable. But the above answer is nothing of the sort. It is a straw man. It makes up an example that I did not ask about, to make a point. And I appreciate the point made and take it to heart. But I still hope at some point to get an answer to my original question with the specific example that I gave. Maybe it will be I who will eventually answer this question as I get more familiar with the source code. – Evan Aad Aug 04 '17 at 05:28
4

If you look at the source code in tikz.code.tex you find the definition for the code that is executed when \pgfextra is encountered on a path:

\def\tikz@extra{\pgfutil@ifnextchar\bgroup\tikz@@extra\relax}
\long\def\tikz@@extra#1{#1\tikz@scan@next@command}
\let\endpgfextra=\tikz@scan@next@command

Basically, whatever is the argument to \pgfextra is executed immediately and scanning continues. Nothing is done to save or restore any graphic or font properties.

When something like this is executed:

\path [...properties1...] ... \pgfextra { \path [] ...; };

Any property that has been set in properties1 may or may not affect the path inside \pgfextra. It depends on the property, when it is set, whether it is set globally, and possibly some other things that I haven't thought of.

Consider the basic node font (i.e., the font used to typeset a node). This is stored in \tikz@textfont and is initially assigned (effictively) globally like this:

\let\tikz@textfont=\pgfutil@empty

so it contains no value. This means that any text in a node will take the document font. When the font key is used (e.g., font=\Huge) the following key is executed:

\tikzoption{font}{\def\tikz@textfont{#1}}

Now, \tikz@textfont contains \Huge and any node without a specific font will use this value within the current scope. When the font key is used in a node, its value will "survive" until the end of the node. But when the font key is used in a path, its value will survive until the end of the path. Any node which doesn't specify a font should take this value. But, this may or may not happen with things like trees or graphs embedded in a path (if they set up a scope and reset the font then probably not).

That is just for fonts. It may be different for line widths, colors, etc. PGF was implemented over a number of years and different people have been involved. There will be some consistency and possibly some inconsistency, due to:

  1. a particular feature requires different functionality
  2. strong feelings about what should happen
  3. misunderstandings about what already happens
  4. some other things I haven't though of

The most important thing is none of this really matters.

Firstly, \pgfextra is not designed for arbitrary code. It is executed immediately without out any thought for what may or may not be expected to happen with any graphic or font properties. In particular, casually bunging a \path inside \pgfextra without using \pgfinterruptpath results in undefined behaviour and an expectation of consistency in this case is somewhat baffling.

Secondly, even if \pgfinterruptpath is used and some properties is inherited and some properties are not, explicitly setting the properties on any path inside \pgfextra is not that hard, particularly since multiple properties can be tied up in styles.

Mark Wibrow
  • 70,437
  • Thank you! Why does \pgfinterruptpath make the use of \pgfextra safer? If I understand correctly, based on the name and on the description in the manual, what \pgfinterruptpath does is simply saving the current path at the beginning of the environment, possibly setting it to empty, executing the contents of the environment, and then restoring the saved path as the current path at the end. But \pgfinterruptpath leaves the rest of the settings and graphics state intact. In particular, all the options retain their values/non-values, resp., across the \pgfinterruptpath boundary. – Evan Aad Aug 04 '17 at 07:24
  • And another question, please. What you wrote makes perfect sense, but it leaves me wondering: since the path's font settings prevail over the \pgfextra code despite the inner \path, then why don't they prevail when the \pgfextra code is replaced with PGF? With the PGF code, the font "reverts" back to normal size, even though the PGF code does not (explicitly) resets it. – Evan Aad Aug 04 '17 at 07:34
  • The basic layer (commands prefixed with pgf) does not use any front end commands (commands prefixed with tikz). So \pgfnode doesn't set the font with \tikz@textfont - in fact it doesn't install any specific font settings at all. – Mark Wibrow Aug 04 '17 at 09:13