Your problem is that you wrote
\edef\temp{\noexpand\foreach \noexpand\i/\noexpand\j in {#5}}
% Here is what #5 is in \temp:
\def\alist{
\foreach \i in {1,2,3}{
\i/1,
}4/1
}
which requires that the stuff in the braces be "fully expandable". Alas, \foreach
is not expandable, and it is part of \alist
. So when \temp
is defined, it expands "as much as possible" and hits some non-expandable part of the code of \foreach
, which in this case is \let \pgffor@assign@before@code = ...
; \let
is an assignment, and not expandable, so is not changed by \edef
. Thus, the assignment is not actually performed, and \edef
goes on to try to interpret the following control sequence \pgffor@assign@before@code
directly. Not surprisingly, it is not defined, since it was the \let
that is supposed to define it, so you get an error.
To do this, you have to create a macro that contains the output of \alist
. For example,
\gdef\alist{}
\foreach \i in {1,2,3} {
\xdef\alist{\alist \i/1,}
}
\xdef\alist{\alist 4/1}
Then you can use \alist
directly instead of \temp.
(I have used global assignments for \alist
because \foreach
executes its contents in a group.)
To answer the more important question first: No, nesting tikzpictures is not the right way to do this. The simplest way to achieve the effect that you seem to want is to use the fit
library. Here's an example:
\documentclass[12pt, a4paper]{scrartcl}
\usepackage{tikz}
\usetikzlibrary{fit}
\begin{document}
Before
\begin{tikzpicture}[baseline=(inbox.base)]%
\node[draw,rectangle](inbox){\textcolor{gray}{in}};
\node[draw,rounded corners,fit=(inbox)](outbox) {%
};
\end{tikzpicture}
After
\end{document}
The general principle here is that TikZ/PGF does not need to know about the hierarchy that you wish to specify (namely that inbox
is inside outbox
), it just needs to know how they relate in terms of positioning. So you figure out inbox
first and then put outbox
around it. TikZ is happy as it knows where to put things. If you really, really need to have a hierarchy, us the matrix
node type.
Now for the explanation of what goes wrong. It's all down to the fact that tikzpicture
s are not meant to be stacked inside each other so there is no regard for grouping or scoping. There should be just one tikzpicture
(or \tikz
) containing all the elements and that's what the code expects. This is a great example of this (and one I'll probably refer to many times in explaining why this is a Bad Idea as it comes up quite often).
Let's look at the key command: the baseline
option on the outer tikzpicture
. This says, "I want the following point to be on the baseline of the surrounding text.". The problem with that is that TikZ (or rather, PGF) doesn't know where that point is until it has drawn the picture. So it has to say "Okay, I'll remember that point and at the end of the picture I'll work it out.". So now it goes about its business, figuring out the picture, and at the end it computes the coordinates of the point that you specified.
Here's where the scoping issue comes in. That baseline
option sets some macro which will get evaluated as part of the stuff that happens in \end{tikzpicture}
. If the macro is empty, nothing happens. If it is not empty, PGF uses it to work out the baseline. At the start of the document, the macro is empty. Since \begin{tikzpicture} ... \end{tikzpicture}
happens inside a group, setting it in the options to \begin{tikzpicture}
doesn't change what happens with other pictures unless they are in the same group. So what it happening here is that the inner tikzpicture is inheriting the baseline
command from the outer one, and complaining vociferously because it doesn't know where outer
is!. By the time the inner picture finishes, the outer
node hasn't been positioned yet.
You could say that the \begin{tikzpicture}
should wipe that macro inside its group (which wouldn't affect the outer picture because it is in the outer group). But this would mean that you couldn't say \tikzset{baseline=0pt}
at the start and have that apply to all your pictures in your document - something that is far more useful than nesting tikzpicture
s. So it's a feature, not a bug.
To see that this is the case, run the following code:
\documentclass[12pt, a4paper]{scrartcl}
\usepackage{tikz}
\makeatletter
\def\helpme#1{%
\message{#1}%
\show\pgf@sh@ns@outbox
\show\pgf@baseline
}
\makeatother
\begin{document}
Before
\begin{tikzpicture}[baseline=(outbox.base)]%
\helpme{Start of picture:}
\node[draw,rounded corners](outbox) {%
\helpme{Start of node:}
\begin{tikzpicture}[scale=1]%
\helpme{Start of inner picture:}
\node[draw,rectangle](inbox){\textcolor{gray}{in}};
\end{tikzpicture}
\helpme{After inner picture:}
};
\helpme{End of picture:}
\end{tikzpicture}
After
\end{document}
The diagnostics are a bit crude, I'll admit, but they show what's going on. Actually, you don't need to run it as I'll put (a condensed version of) the output here:
Start of picture:
> \pgf@sh@ns@outbox=undefined.
> \pgf@baseline=macro:
->\tikz@scan@one@point \pgfutil@firstofone (outbox.base).
Start of node:
> \pgf@sh@ns@outbox=undefined.
> \pgf@baseline=macro:
->\tikz@scan@one@point \pgfutil@firstofone (outbox.base).
Start of inner picture:
> \pgf@sh@ns@outbox=undefined.
> \pgf@baseline=macro:
->\tikz@scan@one@point \pgfutil@firstofone (outbox.base).
! Package pgf Error: No shape named outbox is known.
l.20 \end{tikzpicture}
After inner picture:
> \pgf@sh@ns@outbox=undefined.
> \pgf@baseline=macro:
->\tikz@scan@one@point \pgfutil@firstofone (outbox.base).
End of picture:
> \pgf@sh@ns@outbox=macro:
->rectangle.
> \pgf@baseline=macro:
->\tikz@scan@one@point \pgfutil@firstofone (outbox.base).
Notice that \pgf@sh@ns@outbox
is only set after the node has been fully defined (that's what is directly causing the error as it's used to test if a node has been declared). Notice that the error occurs when the inner picture finishes, not the outer one. Notice also that \pgf@baseline
is the same throughout, including in the inner picture.
The moral of the story is: don't nest tikzpictures. They don't like it.
Best Answer
\foreach
strips spaces before each list item, and then collects everything up to the next comma (or the list terminating token) before assigning to the relevant variables.The
\foreach
problem arises in this case because of the extra space given before the closing brace in the list. Consider the following:This produces:
the same effect is found if there is a space before the comma in the first list item:
which produces
Note, this effect will also appear if there is a space after the
/
when using multiple assignment.which produces
By removing these spaces the problems disappear...
which produces
Should all spaces be stripped automatically? Automatically stripping spaces at the beginning is trivial (
\pgfutil@ifnextchar x{...}{...}
is used all over the place in PGF parsing), however it is a relatively expensive operation.Stripping space after items is hard and would involve character by character parsing, which would be very slow. So, it is easier (and not that inconvenient) just to be careful about spaces.
The other problem is the coordinate specification. Anything that isn't obviously one of the known coordinate systems is assumed to be a node, so
00
,14
are treated as node names. There are many ways to split this specification up, here is one using the.expanded
key handler: