Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

The following MWE displays 2 nested boxes. The alignment is done by refering to the inside box, through the name of the node "inbox"

\documentclass[12pt, a4paper]{scrartcl}
\usepackage{tikz}
\begin{document}
Before
\begin{tikzpicture}[baseline=(inbox.base)]%
   \node[draw,rounded corners](outbox) {%
   \begin{tikzpicture}[scale=1]%
        \node[draw,rectangle](inbox){\textcolor{gray}{in}};
   \end{tikzpicture}        
    };
\end{tikzpicture}
After
\end{document}

But replacing baseline=(inbox.base) by baseline=(outbox.base) produces the compilation error : Package pgf Error: No shape named outbox is known. Can someone explain me this ?

By the way, is it the right way, as i did, to nest nodes inside nodes ?

share|improve this question
    
It is a valid question however you might wish to use \tikz[baseline=(outbox.base)] \node[draw,rounded corners,double,double distance=1mm](outbox) {in}; for the picture. –  percusse Jan 5 '12 at 23:14
    
Sorry, but i don't see the point. \tikz blabla ; should be completely equivalent to \tikz{blabla} and to \begin{tikzpicture} blabla \end{tikzpicture}. Isn't it ? –  nicolas roy Jan 6 '12 at 8:09
    
Sorry I was not clear enough.What I meant is that instead of nesting, the double line gives the same effect. Just put it between Before and after, and what I meant would be more visible. –  percusse Jan 6 '12 at 11:37

2 Answers 2

up vote 10 down vote accepted

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 tikzpictures 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 tikzpictures. 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.

share|improve this answer
    
Great answer, Andrew! :) –  Paulo Cereda Jan 6 '12 at 9:35
    
i wanted to say it ! Thanks a lot. But would like to produce a more complicated picture, with nested boxes, and in that case i don't see how to use "fit". See tex.stackexchange.com/q/40234/10157. –  nicolas roy Jan 6 '12 at 10:57

The error would go away if you would specify a baseline for the outbox node, here referring to inbox.base:

\documentclass[12pt, a4paper]{scrartcl}
\usepackage{tikz}
\begin{document}
Before
\begin{tikzpicture}[baseline=(outbox.base)]%
   \node[draw,rounded corners,baseline=(inbox.base)](outbox) {%
   \begin{tikzpicture}[scale=1]%
        \node[draw,rectangle](inbox){\textcolor{gray}{in}};
   \end{tikzpicture}        
    };
\end{tikzpicture}
After
\end{document}

enter image description here

share|improve this answer
    
Thanks for the tipp, although i do not quite understand the logic behind this. But i noticed also, that it works, if you put your baseline=(inbox.base) in the options of the second \begin{tikzpicture} instead of the first node. It would sound even slightly more logical for me. –  nicolas roy Jan 6 '12 at 7:57

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.