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

  • 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 3 '17 at 8: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 3 '17 at 9: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 3 '17 at 12:03
  • 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 3 '17 at 12:20
  • 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 4 '17 at 8:20
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.

  • 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 4 '17 at 7: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 4 '17 at 7: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 4 '17 at 9:13
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.

  • 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 3 '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 4 '17 at 3: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 4 '17 at 5:28

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.