4

Currently, I draw a TikZ picture with lots of TikZ elements and subelements. To do this, I use TikZ pic. Sometimes, I need to know the size of a pic prior to its drawing for the correct positioning. This information comes from its local bounding box. But in order to get this information, I have to draw it, using opacity = 0.

Is there an option to simulate the drawing of a pic element (or any TikZ element) without having to go via opacity = 0?

Thank you.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
    \begin{tikzpicture}
        \tikzset{
            subtikz/.pic={
                \draw (0, 0) -- (1, 0);
            }
        }
        % Simulate pic to get its local bounding box.
        \pic [local bounding box = Subtikz, opacity = 0] {subtikz};
        % Calculate position depending on other input.
        \path let \p1 = (Subtikz.north east), \p2 = (1, 2), \p3 = (3, 4) in coordinate (NorthEast) at ({max(\x1, \x2, \x3)}, \y1);
        % Draw pic taking into account other input.
        \pic at (NorthEast) {subtikz};
    \end{tikzpicture}
\end{document}
lkegel
  • 41
  • 1
    Welcome to TeX.SX! You can do this with the command \savebox. See 2nd part of this answer. – dexteritas Nov 18 '20 at 11:01
  • You can also get the height from the (current bounding box) (see https://tex.stackexchange.com/questions/137356/how-can-i-access-the-size-of-a-tikzpictures-bounding-box-outside-the-tikzpictur) but these days I always use a savebox. – John Kormylo Nov 20 '20 at 03:07
  • Thank you for your reply. If I understand correctly, \savebox works for tikzpicture but not for a specific pic in a tikzpicture. While I get the correct tikzpicture's height (px) with @dexteritas suggestion I could not get the pic's height or width with it. Thus, I still have to stick to my approach where the pic is drawn twice. – lkegel Nov 22 '20 at 22:32

2 Answers2

3

You can use the special discard layer after you've set it up with

\pgfdeclarelayer{discard}

When it is declared as the layer to be used with pgfonlayer, the content will be placed on that layer but since you don't set it with

\pgfsetlayers{discard, main}

it won't show up in the picture. (Only discard works that way, every other name needs to be specified in \pgfsetlayers.)

With that setup you can just do

\begin{pgfonlayer}{discard}
  \pic [local bounding box = Subtikz] {subtikz};
\end{pgfonlayer}

This obviously won't work if you want to use the pic as part of another path that should show up but that can be arranged as well (especially if you don't use any foreground or background code in your pic).


I've added a simple \pgfshowdiscardlayeranyway command that uses that layer anyway which may be useful for debugging purposes.

Since that layer is not setip with \pgfsetlayers it will accumulate and will not be emptied at the start of the next picture which I show off in the code below since I'm just using \pgfshowdiscardlayeranyway in a second TikZ picture which then contains your original pic.

This had advantages but maybe it's not desirable, for this I'll add a discard discard layer key that can be used as an option to a tikzpicture (or a scope) to globally empty out the layer. It might be worth to do

\tikzset{every picture/.append style={discard discard layer}}

to not drag around that layer through out your whole document.


As an additional note, the way it is setup now, your discarded pic still contributes to the bounding box in the original picture but not if you use the \pgfshowdiscardlayeranyway in another picture. (This isn't noticeable in the example below because I added a grid to show the positions better.)

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\pgfdeclarelayer{discard} % ← declare discard layer
\makeatletter
\tikzset{
  discard discard layer/.style={
    execute at end scope=%
      \global\let\pgf@layerbox@discard\pgfutil@voidb@x}}
\newcommand*\pgfshowdiscardlayeranyway{\box\pgf@layerbox@discard}
\makeatother
\begin{document}
\begin{tikzpicture}[
  % discard discard layer,
  subtikz/.pic={\draw (0, 0) -- (1, 0);}]
\draw[help lines] (-.5,-.5) grid (4.5,.5);

\begin{pgfonlayer}{discard} % ← place on discard layer \pic [local bounding box = Subtikz] {subtikz}; \end{pgfonlayer}

% Calculate position depending on other input. \path let \p1 = (Subtikz.north east), \p2 = (1, 2), \p3 = (3, 4) in coordinate (NorthEast) at ({max(\x1, \x2, \x3)}, \y1); % Draw pic taking into account other input. \pic at (NorthEast) {subtikz};

%\pgfshowdiscardlayeranyway % ← show layer anyway \end{tikzpicture}

\begin{tikzpicture} \draw[help lines] (-.5,-.5) grid (4.5,.5); \pgfshowdiscardlayeranyway \end{tikzpicture}

\end{document}

Outpu

enter image description here

enter image description here

Qrrbrbirlbel
  • 119,821
2

If you need to insert content for calculation purposes only, place it in a \savebox without actually using it:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
    \begin{tikzpicture}
        \tikzset{
            subtikz/.pic={
                \draw (0, 0) -- (1, 0);
            }
        }
        % Simulate pic to get its local bounding box.
        \savebox{0}{\pic [local bounding box = Subtikz, opacity = 1] {subtikz};}
        % Calculate position depending on other input.
        \path let \p1 = (Subtikz.north east), \p2 = (1, 2), \p3 = (3, 4) in coordinate (NorthEast) at ({max(\x1, \x2, \x3)}, \y1);
        % Draw pic taking into account other input.
        \pic at (NorthEast) {subtikz};
        \usebox{0} % use it anyway (placing it at its original position)
    \end{tikzpicture}
\end{document}
AlexG
  • 54,894