TikZ: “local bounding box” in nested scopes

environmentstikz-pgf

I'm trying to define a new environment that will draw a rectangle around its content. The problems begin when those environments are nested because the "local bounding box" is a kind of a global thing. Using unique names for outer and inner scopes solves the problem.

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usetikzlibrary{fit}

\newenvironment{myscope0}[0]{
    \begin{scope}[draw, local bounding box=bounding box 0]
}{
    \end{scope}
    \node [draw, fit=(bounding box 0)] {};
}

\newenvironment{myscope1}[0]{
    \begin{scope}[draw, local bounding box=bounding box 1]
}{
    \end{scope}
    \node [draw, fit=(bounding box 1)] {};
}

\begin{document}
\begin{tikzpicture}
    \begin{myscope0}
        \begin{myscope1}
            \node at (0,0) {test 1};
        \end{myscope1}

        \begin{myscope1}
            \node at (1,1) {test 2};
        \end{myscope1}
    \end{myscope0}
\end{tikzpicture}
\end{document}

expected

What I want is to avoid defining different environments for every possible nesting level because I'm going to draw complex diagrams using this.

I found another topic that solves a very similar problem: Passing current counter value to stack data structure

They define some sort of a stack to store a counter and use that counter to form a name.
The problem is that it does not really work with scopes:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usetikzlibrary{fit}

%Define stack data structure commands (\push, \pop, \splitstack)
\newtoks\mystack
\mystack={\empty}

\def\push#1#2{%
    \def\tmp{{#1}}% 
    \expandafter\expandafter\expandafter%
    #2\expandafter\expandafter\expandafter{\expandafter\tmp\the#2}%
    \ignorespaces
}

\def\pop#1#2{%
    \expandafter\splitstack\the#1\stop{#1}{#2}%
}

\def\splitstack#1#2\stop#3#4{% 
    \def\tmp{#1}
    \ifx\tmp\empty 
    \else
        \def#4{#1}\global#3={#2}%
    \fi
}

%Define bracket pair counting commands (\openbracket, \closebracket)
\newcounter{mycounter}

\newenvironment{myscope}[0]{
    \global\expandafter\edef\csname beginscopenumber\endcsname{\themycounter}%
    \push{\beginscopenumber}{\mystack}%
    \stepcounter{mycounter}%
        
    \begin{scope} [local bounding box=bounding box \beginscopenumber]
}{
    \end{scope}
    \pop{\mystack}{\endscopenumber}%
    \node [draw, fit=(bounding box \endscopenumber)] {};
}

\begin{document}
\begin{tikzpicture}
    \begin{myscope}
        \begin{myscope}
            \node at (0,0) {test 1};
        \end{myscope}

        \begin{myscope}
            \node at (1,1) {test 2};
        \end{myscope}
    \end{myscope}
\end{tikzpicture}
\end{document}

actual

The outer rectangle surrounds only the last nested scope. I will appreciate any help with this. Thank you.

Best Answer

Here is a simple method with a counter:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usetikzlibrary{fit}

\newcounter{myscopelevel}
\setcounter{myscopelevel}{0}
\newenvironment{myscope}[0]
{
  \stepcounter{myscopelevel}
  %\typeout{myscopelevel:\themyscopelevel}
  \begin{scope}[local bounding box/.expanded=bounding box \themyscopelevel]
  }{
  \end{scope}
  \node [draw, fit=(bounding box \themyscopelevel)] {};
  \addtocounter{myscopelevel}{-1}
}

\begin{document}
\begin{tikzpicture}
    \begin{myscope}
        \begin{myscope}
            \node at (0,0) {test 1};
        \end{myscope}

        \begin{myscope}
            \node at (1,1) {test 2};
        \end{myscope}
    \end{myscope}
\end{tikzpicture}
\end{document}

And here's an even simpler version using the locality of a macro within a group:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usetikzlibrary{fit}

\pgfmathsetmacro\myscopelevel{0}
\newenvironment{myscope}[0]
{
  \pgfmathsetmacro\myscopelevel{int(\myscopelevel+1)}
  %\typeout{myscopelevel:\myscopelevel}
  \begin{scope}[local bounding box/.expanded=bounding box \myscopelevel]
  }{
  \end{scope}
  \node [draw, fit=(bounding box \myscopelevel)] {};
}

\begin{document}
\begin{tikzpicture}
    \begin{myscope}
        \begin{myscope}
            \node at (0,0) {test 1};
        \end{myscope}

        \begin{myscope}
            \node at (1,1) {test 2};
        \end{myscope}
    \end{myscope}
\end{tikzpicture}
\end{document}