3

Back in the day (2014), I've had some code posted in Drawing a styled node invisibly (phantom / off-screen)?, which is reposted below. Then it apparently compiled; with my current texlive 2014 (updated a month ago, maybe), now it fails with:

! Package pgf Error: Sorry, the requested layer 'invisible' is not part of the 
layer list. Please verify that you provided \pgfsetlayers and that 'invisible' 
is part of this list.

See the pgf package documentation for explanation.

So my guess (then), that:

just declare a pgf layer, but don't set it - and it will keep everything you draw on it invisible;

... is not valid anymore - as now it obviously raises an error.

So is it somehow possible to have an invisible layer in tikz with the current API?

The code:

\documentclass[varwidth]{standalone}

\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{positioning}

\pagecolor{yellow!15}
% just declare the layer, and don't do
% \pgfsetlayers ; then it will be invisible:
\pgfdeclarelayer{invisible}
\begin{document}
\begin{tikzpicture}
\makeatletter
\pgfdeclareshape{testshape}{ %
  \inheritsavedanchors[from={rectangle}] %
  \inheritbackgroundpath[from={rectangle}] %
  \inheritanchorborder[from={rectangle}] %
  \foreach \x in {center,north,north east,north west,south,south east,south west,east,west}{ %
    \inheritanchor[from={rectangle}]{\x} %
  } %
  %\backgroundpath{} %
  \foregroundpath{ %
    \draw[] (\tikz@fig@name.south west) -- (\tikz@fig@name.north east)
      (\tikz@fig@name.north west) -- (\tikz@fig@name.south east);
  }
}
\makeatother

\tikzstyle{mynode} = [testshape,draw=gray,line width=2pt,inner sep=2pt, outer sep=5pt, minimum width=2cm,minimum height=0pt,align=center]

\node[mynode] (Starter) {Testing the node\\(a bit)};
\node[mynode] (Ender) [right=2cm of Starter] {Also test\\(even more)};

\begin{pgfonlayer}{invisible}
\node[mynode] (tester) [at=(current bounding box.center)] {Trying to measure this one};
\end{pgfonlayer}

\path let \p1=(tester.north east), \p2=(tester.south west)
  in coordinate (testerSize) at (\x1-\x2,\y1-\y2);

% \pgfpointanchor{testerSize}{center} % "returns"/sets a (last) pgfpoint
\path(testerSize.center); % a bit easier than \pgfpointanchor: https://tex.stackexchange.com/a/33706/2595

\pgfgetlastxy{\testerWidth}{\testerHeight} % ... and globalize:
\global\let\testerWidth\testerWidth
\global\let\testerHeight\testerHeight

\typeout{tester size is: \testerWidth\space X \testerHeight}

\end{tikzpicture}

tester size is: \testerWidth\ X \testerHeight

\end{document}
3

Each layer is stored in its own box, and each box is inserted in order at the end of the picture. Here is a hack of the code that inserts only the layers/boxes that are not declared invisible. It should be OK, although I cannot guarantee it is 100% robust.

Layers must be declared as invisible before a picture is started, but different layers can be invisible for each picture. In addition, the bounding box of the whole picture includes the contents of the invisible layer(s).

\documentclass[border=5]{standalone}
\usepackage{tikz}

\makeatletter

\def\pgfsetinvisiblelayers#1{\def\pgf@layers@invisible{#1}}
\def\pgfshowalllayers{\let\pgf@layers@invisible=\pgfutil@empty}
\pgfshowalllayers

\def\pgf@dolayer#1,#2,\relax{%
  % Is layer declared as invisible...?
  \edef\pgf@marshal{\noexpand\pgfutil@in@{,#1,}{,\pgf@layers@invisible,}}%
  \pgf@marshal%
  \ifpgfutil@in@% Yep. So do nothing.
  \else% Nope. So, insert box.
    \def\pgf@test{#1}%
    \ifx\pgf@test\pgf@maintext%
      \box\pgf@layerbox@main%
    \else%
      \pgfsys@beginscope%
        \expandafter\box\csname pgf@layerbox@#1\endcsname%
      \pgfsys@endscope%
    \fi%
  \fi
  \def\pgf@test{#2}%
  \ifx\pgf@test\pgfutil@empty%
  \else%
    \pgf@dolayer#2,\relax%
  \fi%
}

\pgfdeclarelayer{layer 1}
\pgfdeclarelayer{layer 2}
\pgfdeclarelayer{layer 3}
\pgfdeclarelayer{label}
\pgfsetlayers{main,layer 1,layer 2,layer 3,label}

\begin{document}

\foreach \l in {main,layer 1,layer 2,layer 3,{main,layer 2}}{
\pgfsetinvisiblelayers{\l}
\begin{tikzpicture}
\begin{pgfonlayer}{label}
  \node at (0,-2) [above] {Invisible: \ttfamily\l};
\end{pgfonlayer}
\fill [gray] (-2,-2) rectangle (2,2);
\begin{pgfonlayer}{layer 1}
  \fill [red, opacity=0.75] circle [radius=1.5];
\end{pgfonlayer}
\begin{pgfonlayer}{layer 2}
  \fill [green, opacity=0.75]
    (90:2) -- (210:2) -- (330:2) -- cycle;
\end{pgfonlayer}
\begin{pgfonlayer}{layer 3}
  \fill [blue, opacity=0.75]
    (225:1) rectangle (45:1);
\end{pgfonlayer}

\end{tikzpicture}
}
\end{document}

enter image description here

1
  • In case anyone is wondering, you can specify the visible layers instead of the invisible layers contained in the list at line 40, simply by moving the block of code under the \else statement on line 17 under the \if statement on the above line. Then for sanity's sake simply replace all appearances of the word invisible with visible. – NauticalMile Sep 3 '16 at 18:32
2

K, here is a little hack which makes the above example compile now; the example as it is seems OK, not sure about the approach in general though. But until a more erudite answer comes: we can just "bypass" the raising of the error inside the tikzpicture with \let\pgfonlayer@assert@fail\relax; and outside of the tikzpicture the original \pgfonlayer@assert@fail macro will be restored.

Or, as a whole MWE:

\documentclass[varwidth]{standalone}

\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{positioning}

\pagecolor{yellow!15}
% just declare the layer, and don't do
% \pgfsetlayers ; then it will be invisible:
\pgfdeclarelayer{invisible}
\begin{document}
\begin{tikzpicture}
\makeatletter
\pgfdeclareshape{testshape}{ %
  \inheritsavedanchors[from={rectangle}] %
  \inheritbackgroundpath[from={rectangle}] %
  \inheritanchorborder[from={rectangle}] %
  \foreach \x in {center,north,north east,north west,south,south east,south west,east,west}{ %
    \inheritanchor[from={rectangle}]{\x} %
  } %
  %\backgroundpath{} %
  \foregroundpath{ %
    \draw[] (\tikz@fig@name.south west) -- (\tikz@fig@name.north east)
      (\tikz@fig@name.north west) -- (\tikz@fig@name.south east);
  }
}
\makeatother

\tikzstyle{mynode} = [testshape,draw=gray,line width=2pt,inner sep=2pt, outer sep=5pt, minimum width=2cm,minimum height=0pt,align=center]

\node[mynode] (Starter) {Testing the node\\(a bit)};
\node[mynode] (Ender) [right=2cm of Starter] {Also test\\(even more)};

\makeatletter
\let\pgfonlayer@assert@fail\relax
\makeatother
\begin{pgfonlayer}{invisible}
\node[mynode] (tester) [at=(current bounding box.center)] {Trying to measure this one};
\end{pgfonlayer}

\path let \p1=(tester.north east), \p2=(tester.south west)
  in coordinate (testerSize) at (\x1-\x2,\y1-\y2);

% \pgfpointanchor{testerSize}{center} % "returns"/sets a (last) pgfpoint
\path(testerSize.center); % a bit easier than \pgfpointanchor: http://tex.stackexchange.com/a/33706/2595

\pgfgetlastxy{\testerWidth}{\testerHeight} % ... and globalize:
\global\let\testerWidth\testerWidth
\global\let\testerHeight\testerHeight

\typeout{tester size is: \testerWidth\space X \testerHeight}

\end{tikzpicture}

\makeatletter % original pgfonlayer@assert@fail is restored here:
\typeout{MACRO pgfonlayer@assert@fail is: \meaning\pgfonlayer@assert@fail}
\makeatother
tester size is: \testerWidth\ X \testerHeight

\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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