# TikZ: "local bounding box" in nested scopes

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}


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}


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

• Welcome to TeX.SX! Would it not be possible to just use a node with a border? A node can contain arbitrary contents if you put a minipage in it for example. Aug 5, 2021 at 14:21
• @leonidovich Can you give a specific figure that appears that situation? Aug 5, 2021 at 15:02
• @JasperHabicht Didn't know it can work, let me try that one. Aug 5, 2021 at 15:14
• @BlackMild It is actually at the bottom of the post, right after the second piece of the code. What I want to get is the first picture, after the first piece of the code. Aug 5, 2021 at 15:15
• Node names in general are global. It's not limited to local bounding box. Aug 5, 2021 at 16:23

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)] {};
}

\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}

• Could you maybe briefly explain why this is working? I guess, it has to do with expansion, as you added \.expanded to local bounding box. Sep 6, 2021 at 11:37
• @JasperHabicht With the ./expanded handler, the name of the local bounding box with the value of the counter is immediately evaluated and stored for its future usage (the coutner can be modified by a nested environment before its use). The second use of /.expanded is not strictly necessary. Sep 6, 2021 at 12:04
• @JasperHabicht I removed the second use of /.expanded and added an even simpler solution with a simple macro. Sep 6, 2021 at 22:35
• I knew it must be something simple! Thank you @PaulGaborit, this works like a charm. Sep 8, 2021 at 10:18

One of the main principle of TikZ drawing is: putting nodes and pics along the paths. There is so-called balance between them: node is with full anchors and names, limited shapes; and pic is with any shape but "hackery" anchors and names.

Your question can be answered via like-node pic as described in this answer. I define a pic with 3 parameters: #1 is the text for the above right, #2 is the text for the below left, #3 is the name of the covering node that can be referred later.

Hope this helps! PS: local bounding box is always of rectangle shapes, so it is limited.

\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\begin{tikzpicture}
\tikzset{pics/leonidovic/.style args=
{above right #1 below left #2 nodename #3}{code={%
\path
(0,0) node[draw,minimum height=15mm,minimum width=20mm] (#3) {}
(#3.north east)+(-.6,-.35) node[draw,minimum height=5mm,minimum width=10mm]{#1}
(#3.south west)+(.6,.35) node[draw,minimum height=5mm,minimum width=10mm]{#2};%
}}}

\path
(0,0) pic{leonidovic=above right test1 below left test2 nodename A}
(5,2) pic{leonidovic=above right test3 below left test4 nodename B}
;
\draw (A)--(B);
\draw[->] (A) -| (B);
\end{tikzpicture}
\end{document}

• This is not how understand OP question. OP writes "every possible nesting level". A node placed "above right" and "below left" is just an example for something that needs to be boxed!? (I can not solve the problem! - is it solvable?) Aug 7, 2021 at 11:49
• @hpekristiansen Thanks, that's exactly it, those pictures are just examples to demonstrate the problem. Aug 9, 2021 at 11:46
• I'm developing a package for building diagrams like this one: c4model.com/img/bigbankplc-LiveDeployment.png I have completed almost all I need: new shapes, styles, commands, and environments. Everything works perfectly except for one thing - those bounding rectangles, called Deployment Nodes. The only way to create them I can see is using scopes and a local bounding box. But as I stated, they mess up when nested. Aug 9, 2021 at 12:13