7

I have a question about \saveddimen usage inside of a \pgfdeclareshape command. My code is as follows:

\documentclass{article}
\usepackage{tikz}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
\makeatletter
\pgfdeclareshape{myshape}{
   \savedanchor{\centerpoint}{\pgf@x=0pt\pgf@y=0pt}
   \saveddimen{\mydima}{%
      \pgf@x=20pt
   }
   \saveddimen{\mydimb}{%
      \pgf@x=20pt  %% =\mydima
      \pgf@x=-\pgf@x
   }
   %   
   \anchor{center}{\centerpoint}
   \backgroundpath{%
        \pgf@xa=\mydima \pgf@ya=\mydima
        \pgf@xb=\mydimb \pgf@yb=\mydimb
        \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@ya}}
        \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@ya}}
        \pgfpathmoveto{\pgfqpoint{\pgf@xb}{\pgf@yb}}
        \pgfpathlineto{\pgfqpoint{\pgf@xa}{\pgf@yb}}
   }     
}
\makeatother
\begin{document}
\begin{tikzpicture}
   \draw[help lines] (0,0) grid (4,4);
   \node[myshape,draw] at (2,2) {test};
\end{tikzpicture}
\end{document}

This produces a node, with top and bottom horizontal lines. In my real world example, I want to have a dimension \dimenb which is a function of \dimena. If I uncomment the %% in the \saveddimen{\dimenb} command the setting \pgf@x=\dimmena I get an error message.

Question: Is it legal to use a saved dimension in the definition of another saved dimension, and if yes, how?

Werner
  • 603,163
Bertfried Fauser
  • 570
  • 3
  • 10
  • Welcome to TeX.sx! You don't have to sign with your name since it automatically appears in the lower right corner of your post. – Werner Mar 08 '12 at 22:11
  • Only \saveddimen{\mydima}{\pgf@x=20pt}\saveddimen{\mydimb}{\pgf@x=-\pgf@x} would be enough. Since \pgf@x is overwritten by \mydima previously. – percusse Mar 08 '12 at 22:49
  • Thank you for this answer! You tell me (correct?) that I should use the fact that after defining \mydima the register \pgf@x still holds the dimension, and I can use it in the definition of \mydimb. That is easily breakable code. Add additional code bewtween the statements to break it \saveddimen{\mydima}{ --set \pgf@x here} \some-code-manipulating \pgf@x \saveddimen{\mydimb}{ --\pgf@x here is no longer set to \mydima} I still wonder why I cannot use the \mydima macro in another \saveddimen command, as it works in the \backgroundpath ? – Bertfried Fauser Mar 09 '12 at 15:18
  • @BertfriedFauser I'm really sorry for the delay. If you want to ping someone in the comments you can put an @ character and the nickname just as I did in this comment. There is even an autocomplete feature if you type a few characters and press TAB key. I will try to address your question as soon as possible. – percusse Mar 22 '12 at 16:27
  • @percusse No worries, I see your answer `works' for me, so I am not stuck. As I am new to stack exchange, I'll need to learn how it properly works :-\ – Bertfried Fauser Mar 23 '12 at 17:52

1 Answers1

5

In the comments, percusse suggests writing:

\saveddimen{\mydima}{\pgf@x=20pt}
\saveddimen{\mydimb}{\pgf@x=-\pgf@x}

To see why this works, we need to trace through the definitions. Within \pgfdeclareshape then \saveddimen is actually \pgf@sh@saveddimen whose definition is:

\def\pgf@sh@saveddimen#1#2{%
  \expandafter\pgfutil@g@addto@macro\csname pgf@sh@s@\shape@name\endcsname{\pgf@sh@resaveddimen{#1}{#2}}}

So when the shape is declared, we see that \pgf@sh@s@<shapename> gains the command \pgf@sh@resaveddimen{#1}{#2}. Presumably, the macro \pgf@sh@s@<shapename> is run when the shape is used to set all the dimensions and so forth. So to find out what happens when it is run, we need to look at \pgf@sh@resaveddimen. This is:

\def\pgf@sh@resaveddimen#1#2{%
  {#2\global\pgf@x=\pgf@x}%
  \edef\pgf@sh@marshal{%
    \noexpand\pgfutil@g@addto@macro\noexpand\pgf@sh@savedpoints{%
      \noexpand\def\noexpand#1{\the\pgf@x}%
    }}%
  \pgf@sh@marshal%
}

So #2 (which was the execution code) gets run, then \pgf@x gets set to a global version of itself (this is important). Now this doesn't get saved as the given name, but the code \def\<given name>{<the actual value of \pgf@x>} gets saved to \pgf@sh@savedpoints. Note that this is the actual value, not the macro \pgf@x.

We have three stages, therefore: declaration when the template is set up, definition where a shape is defined, and use where the various parts are used (parts of which are in the definition stage, so this can get confusing).

Let's trace this through with the above code, and its variations.

  1. Declaration. So here we run the \saveddimen commands which do the code appendages. At the end of this, \pgf@sh@s@myshape has (in addition to whatever else it has):

    \pgf@sh@resaveddimen{\mydima}{\pgf@x=20pt}
    \pgf@sh@resaveddimen{\mydimb}{\pgf@x=-\pgf@x}
    
  2. Definition. Now \pgf@sh@s@myshape is run. This executes the above two commands (amongst other things). After the first is run, the state of play is the following: \pgf@x is globally set to 20pt and \pgf@sh@savedpoints has \def\mydima{20pt} in it. The first of these is the reason why we can use \pgf@x in the definition of \mydimb. The second is the reason why we cannot use mydima.

    Then at the end, \pgf@sh@savedpoints contains \def\mydima{20pt}\def\mydimb{-20pt}.

  3. Use. This is where \pgf@sh@savedpoints is executed and all the macros are finally defined. So everything is set up before the assignments are made.

In conclusion, \mydima is not available when the code for \mydimb is set up but so long as they are declared one after the other then when the definition code for \mydimb is executed then the value of \pgf@x is whatever \mydima would have been set to.

Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751