# TikZ: applying width to nodes programmatically, and 'Undefined control sequence pgf'

This is what I want to do: given two (horizontal) nodes, calculate the x distance between them, and produce a rectangular node with the same width as the x-distance between the nodes, placed below them.

I have tried the following code, reusing Working with \pgfmathparse inside a path / calculations - Why is this let expression not working in TikZ (calculating a midpoint)? - TeX - LaTeX - Stack Exchange:

\usetikzlibrary{shapes.arrows,chains,positioning,matrix,calc}

\begin{tikzpicture}[font=\tt]
\node (A1) [shape=circle,draw]              {A1};
\node (A2) [shape=circle,draw,right=of A1]  {A2};

% draw a rectangular node
\draw let \p1 = (A1.west), \p2 = (A2.east)
in \pgfextra{
\pgfmathparse{\x2 - \x1}
}
node[draw,right,below=of A1,text width=\pgfmathresult pt]{test};
\end{tikzpicture}


This results with the image below:

Obviously - the box is not as wide as the distance between the A1 and A2 nodes (and neither has its right edge aligned with A1.west).

Then I wanted to try something similar to the code given in Re: tikz: getting width of node programmatically, which also uses \pgfextra, so I tried something like:

\usetikzlibrary{shapes.arrows,chains,positioning,matrix,calc}
\newdimen{\mydim}

\begin{tikzpicture}[font=\tt]
\node (A1) [shape=circle,draw]              {A1};
\node (A2) [shape=circle,draw,right=of A1]  {A2};

% draw a rectangular node
\draw let \p1 = (A1.west), \p2 = (A2.east)
in \pgfextra{
\pgf@x=\n1
\pgf@y=1cm
\divide\pgf@x by \pgf@y
\setlength{\mydim}{\pgf@x}
}
node[draw,right,below=of A1,text width=\mydim pt]{test};
\end{tikzpicture}


... however, for that code, LaTeX seems to crash when it encounters \pgf@x, since the error message is:

! Undefined control sequence.
<argument>  \pgf
@x=\n 1 \pgf @y=1cm \divide \pgf @x by \pgf @y \setlength {...
l.40      }

!  ==> Fatal error occurred, no output PDF file produced!


The only reference I found to this was kind of error was in pgfdeclareshape example not compilable - pgf-users, where the recommendation is:

Does it compile if you put \makeatletter before \pgfdeclareshape{...} and \makeatother after? (It is missing from the example in the manual, but I suspect it is necessary).

... however, I do not see how it would apply here - since the purpose of all this pgf use here, is just calculation (not actual drawing/rendering).

Well, thanks in advance for any pointers,
Cheers!

EDIT: Well, with a (sort of a) combo of the approaches above, I am starting to get somewhere:

\usetikzlibrary{shapes.arrows,chains,positioning,matrix,calc}
\newdimen{\mydim}

\begin{tikzpicture}[font=\tt]
\node (A1) [shape=circle,draw]              {A1};
\node (A2) [shape=circle,draw,right=of A1]  {A2};

% draw a rectangular node
\draw let \p1 = (A1.west), \p2 = (A2.east)
in \pgfextra{%
\pgfmathparse{\x2 - \x1}
\setlength{\mydim}{\pgfmathresult pt}
}
node[draw,right,below=of A1.west,anchor=west,text width=\mydim]{test};
\end{tikzpicture}


... but it's still not good. By using anchor=west, now there is proper alignment; and by getting rid of \pgf@x, and using \pgfmathresult to set the \mydim length - the width looks somewhat better, but longer than expected, see image:

So the remaining questions now are:

• how do I get the correct width of the box - so its left edge aligns with A2's left edge?
• Why is this 'crash' on encountering \pgf@x in \pgfextra happening?

Thanks again,
Cheers!

-
Whenever there is an @ in a command name, you need \makeatletter before that, so the advice does apply (you should put the commands before and after the \draw. –  Caramdir Dec 23 '10 at 19:25
excellent - thank you Caramdir! Just tried it out and it worked :) –  sdaau Dec 23 '10 at 20:34

I guess the reason that your first example doesn't give you the right length is that \pgfmathresult is overwritten somewhere before it is applied to the option (also you forgot subtracting the inner sep, this is the reason for the incorrect length in your edit). However, the node is placed correctly below (A1), as the default node alignment is centering.

First step: fix the \pgfmathresult. The \pgfmathparse isn't really needed here, we can simply do the following (the default inner sep between the text and the border is 0.3333em, so we need to subtract twice that for the text width):

\begin{tikzpicture}[font=\tt]
\node (A1) [shape=circle,draw]              {A1};
\node (A2) [shape=circle,draw,right=of A1]  {A2};

% draw a rectangular node
\draw let \p1 = (A1.west), \p2 = (A2.east) in
node[draw,right,below=of A1,text width={\x2-\x1-0.6666em}]{test};
\end{tikzpicture}


Second step: fix the alignment. The right option doesn't do anything, so we can simply delete it. By default, nodes are anchored at their center. Actually, for below=of .., they are anchored at the north (center). So we have to set anchor=north west to have the node to the right. But then it is to the right of A1.center (concretely, node distance below A1.south). So we need to specify below=of A1.south west. Unfortunately, that doesn't quite work as A1 is bounded by a circle (so south west is on the circle and not as far west and south as we would like). A1.west is a first approximation:

\begin{tikzpicture}[font=\tt]
\node (A1) [shape=circle,draw]              {A1};
\node (A2) [shape=circle,draw,right=of A1]  {A2};

% draw a rectangular node
\draw let \p1 = (A1.west), \p2 = (A2.east) in
node[draw,below={of A1.west},anchor=north west,text width={\x2-\x1-0.6666em}]{test};
\end{tikzpicture}


This isn’t quite perfect, since the third node is a bit too high now. The circle shape doesn't provide the correct anchor, so we have to calculate its position (it’s easy: A1.west gives the correct x-coordinate and A1.south the correct y-coordinate). Also, let’s add align=center to center the text in the node:

\begin{tikzpicture}[font=\tt]
\node (A1) [shape=circle,draw]              {A1};
\node (A2) [shape=circle,draw,right=of A1]  {A2};

% draw a rectangular node
\draw let
\p1 = (A1.west),
\p2 = (A2.east),
\p3 = (A1.south)
in node [
draw,
below={of (\x1,\y3)},
anchor=north west,
text width={\x2-\x1-0.6666em},
align=center
] {test};
\end{tikzpicture}


To get your second example to compile, you need to add \makeatletter and \makeatother in the appropriate places. By default, @ is in class “other” and cannot be used in command names. However, it is typically used in internal commands that the user should not access. \makeatletter makes @ a ”letter”, so that it can be used in command names. See also Why do LaTeX internal commands have an @ in them?. However the code seems to have other problems too and adding \makeatletter merely changes the error. I'm not yet sufficiently familiar with the ways TeX and LaTeX handle dimensions and lengths to give you advice how that code should be corrected.

-
Wow - thanks a LOT, Caramdir - an awesome answer! Especially thanks for the step by step details of "below" and such - I have great difficulties keeping those in check.. Thanks again - cheers! ... oh yes - and the 'inner sep'? It would have taken me AGES to get to that!! :) –  sdaau Dec 23 '10 at 20:36

Thanks to @Caramdir, here is also a version with usage of \pgf@x; while \makeatletter/\makeatother is necessary to get rid of the ! Undefined control sequence, the OP code also needs to use a 'counter' instead of a 'dimension' so that some width scaling occurs at all.

The code below should build and generate an image - but its width will, this time, be somewhat shorter than the 'total' distance between A1 and A2:

\usetikzlibrary{shapes.arrows,chains,positioning,matrix,calc}
\newdimen{\mydim}
\newcounter{counterOne}

\begin{tikzpicture}[font=\tt]

\node (A1) [shape=circle,draw]              {A1};
\node (A2) [shape=circle,draw,right=of A1]  {A2};

% draw a rectangular node
\makeatletter
\draw let \p1 = (A1.west), \p2 = (A2.east)
in let \n{width} = {\x2 - \x1}
in \pgfextra{
% \pgf@x=\n1 % ! Missing number, treated as zero. \tikz@cc@n@1
% manual: "the \p, \x, and \y macros refer to the same logical point, while the \n macro has “its own namespace.”"
\pgf@x=\n{width}
\pgf@y=1cm
\divide\pgf@x by \pgf@y
\setcounter{counterOne}{\pgf@x}  % NOWORK: \setlength{\mydim}{\pgf@x}
}
node[draw,right,below=of A1.west,anchor=west,text width=\thecounterOne cm]{test};
\makeatother

\end{tikzpicture}


... with resulting image:

Well, in any case, I'm glad this question got solved :)
Cheers!

-
Why don't you simply use text width=\n{width}? Assigning a length to a counter seems to round it down to the next integer. (also again, it should be {\x2-\x1-0.6666em} (or whatever inner sep you have). –  Caramdir Dec 23 '10 at 21:00
Hi @Caramdir - heh, you're right :) The thing is, I was just so overwhelmed with this, I simply couldn't do any better, than to try and follow the original example, Re: tikz: getting width of node programmatically, as much to the letter (I was writing that while I still haven't properly gone through your answer)... Needless to say - your suggestion is much better (though I think I'll leave writing a more proper example for some other time).. Thanks again - cheers! –  sdaau Dec 23 '10 at 21:04