4

The \DrawBoundingBox macro draws bounding box around a figure with a 1pt border.

In the first figure, produced with \MyNode\MyNode, no tweaks are done to tikz's bounding box. Due to the control points of the loop, this results in a slightly wider bounding box on the left and right and a much larger one at the top. Spacing at the bottom seems correct. In this case, where the default bounding box is used, the two figures are placed next to each other as desired.

In the second figure, I use \MyNode* which interrupts the bounding box of the loop and manually adjusts the height to obtain the correct bounding box. This figure shows that the bounding box tweak was correct as it has the desired 1pt spacing on all four sides.

However, when I put two figures with the bounding box tweak next to each other, \MyNode*\MyNode*, they seem to overlap (third figure). It seems as it the second instance of use as bounding box in the \DrawBoundingBox macro has been ignored.

enter image description here

I would prefer to do this in two steps as this allows each figure to tweak its own bounding box before invoking \DrawBoundingBox too add additional tweaks as desired.

What changes do I need to make so that the figures with the tweaked bounding box are placed next to each other without overlapping the bounding boxes.

Code:

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}

\newcommand*{\DrawBoundingBox}[1][]{%
    \draw [
        use as bounding box,% <-- this seems to have no effect
        draw=gray,
    ]
    ([shift={(-1pt,-1pt)}]current bounding box.south west)
    rectangle
    ([shift={(1pt,1pt)}]current bounding box.north east);
}

\tikzset{Loop Style/.style={loop above, ultra thick, red, distance=1.0cm, ->}}
\newcommand*{\BBoxTweakForLoopDistance}{0.75cm}%

\NewDocumentCommand{\MyNode}{s}{%
    \begin{tikzpicture}[baseline]
        \node [draw=black, circle] (A) at (0,0) {a};

        \IfBooleanTF{#1}{% Interrupt bounding box for * variant
            \begin{pgfinterruptboundingbox}
                \path (A) edge[Loop Style] (A);
            \end{pgfinterruptboundingbox}

            \path [use as bounding box]%% Expand bounding box to include the loop path
                (current bounding box.south west) 
                rectangle 
                ([shift={(0pt,\BBoxTweakForLoopDistance)}]current bounding box.north east);
        }{%
                \path (A) edge[Loop Style] (A);
        }%

        \DrawBoundingBox
    \end{tikzpicture}%
}


\begin{document}
    \MyNode\MyNode
    \quad
    \MyNode*
    \quad
    \MyNode*\MyNode*
\end{document}
  • p. 175 of the pgfmanual: "Normally, when this option is given on a path, the bounding box of the present path is used to determine the size of the picture and the size of all subsequent paths are ignored." and " Consider using \useasboundingbox together with \pgfresetboundingbox in order to replace the bounding box with a new one." – user121799 May 21 '18 at 20:25
  • 1
    @marmot: Thanks for the pointer. I was not able to make it work with \pgfresetboundingbox. However, if the \path [use as bounding box] ... is placed within a scope things work as desired. Seems to me that that should have been the default behavior, but the tikz/pgf authors must have had their reasons. Do you want to post an answer? – Peter Grill May 21 '18 at 21:25
  • Thanks, but I didn't know/think about the trick with the scope. All I knew was that repeated setting of the bounding box doesn't work "as expected". So I think you deserve to answer your own question. ;-) (BTW, percusse taught me that putting things into a pgfinterruptboundingbox environment is equivalent to adding the overlay option, i.e. \path[overlay] (A) edge[Loop Style] (A); would be shorter. ;-) – user121799 May 21 '18 at 21:35
2

This is not an answer to the question, and I do not yet fully understand how resetting the boundary box works. However, it seems to me that you want to draw a tight bounding box around a Bezier curve, and unfortunately TikZ takes into account the control points, thus blowing the bounding box up. This has been discussed here. In the case at hand, the tight bounding box may be established with a simple trick:

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}

\newcommand*{\DrawBoundingBox}[1][]{%
    \draw [
        draw=gray,
    ]
    ([shift={(-1pt,-1pt)}]current bounding box.south west)
    rectangle ([shift={(1pt,1pt)}]current bounding box.north east);
}

\tikzset{Loop Style/.style={loop above, ultra thick, red, distance=1.0cm, ->}}
\newcommand*{\BBoxTweakForLoopDistance}{0.75cm}%

\NewDocumentCommand{\MyNode}{s}{%
    \begin{tikzpicture}[baseline]
        \node [draw=black, circle] (A) at (0,0) {a};

        \IfBooleanTF{#1}{% Interrupt bounding box for * variant
            \path[overlay] (A) edge[Loop Style] coordinate[midway](X) (A) ;
            \path (X);
        }{%
                \path (A) edge[Loop Style] (A);
        }%

        \DrawBoundingBox
    \end{tikzpicture}%
}


\begin{document}
    \MyNode\MyNode
    \quad
    \MyNode*
    \quad
    \MyNode*\MyNode*
\end{document}

enter image description here

I understand that this does not really answer the question you have, but might allow you to achieve the goal I suspect you have more simply.

  • Nice.I like the ...coordinate[midway](X)/\path (X) trick. – Peter Grill May 22 '18 at 2:14
2

As marmot pointed out, Section 15.8: Establishing a Bounding Box (p. 175) states that use as bounding box:

...the bounding box of the present path is used to determine the size of the picture and the size of all subsequent paths are ignored. However, if there were previous path operations that have already established a larger bounding box, it will not be made smaller by this operation (consider the \pgfresetboundingbox command to reset the previous bounding box). In a sense, use as bounding box has the same effect as clipping all subsequent drawing against the current path—without actually doing the clipping, only making pgf treat everything as if it were clipped.

which is in contrast to my thinking which was that this would set the current bounding box and future paths would alter the bounding box as they would have without the use as bounding box.

However, further in the documentation is

Note: If this option is used on a path inside a TEX group (scope), the effect “lasts” only until the end of the scope. Again, this behavior is the same as for clipping. Consider using \useasboundingbox together with \pgfresetboundingbox in order to replace the bounding box with a new one.

Thus, if I always employ use as bounding box within a scope

\begin{scope}
    \path [use as bounding box]%% Expand bounding box to include the loop path
            (current bounding box.south west) 
            rectangle 
            ([shift={(0pt,\BBoxTweakForLoopDistance)}]current bounding box.north east);
\end{scope}

I get the behavior consistent with what I thought the use as bounding box should do.:

enter image description here

Code:

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}

\newcommand*{\DrawBoundingBox}[1][]{%
    \draw [
        use as bounding box,% <-- this seems to have no effect
        draw=gray,
    ]
    ([shift={(-1pt,-1pt)}]current bounding box.south west)
    rectangle
    ([shift={(1pt,1pt)}]current bounding box.north east);
}

\tikzset{Loop Style/.style={loop above, ultra thick, red, distance=1.0cm, ->}}
\newcommand*{\BBoxTweakForLoopDistance}{0.75cm}%

\NewDocumentCommand{\MyNode}{s}{%
    \begin{tikzpicture}[baseline]
        \node [draw=black, circle] (A) at (0,0) {a};

        \IfBooleanTF{#1}{% Interrupt bounding box for * variant
            \begin{pgfinterruptboundingbox}
                \path (A) edge[Loop Style] (A);
            \end{pgfinterruptboundingbox}

            \begin{scope}
            \path [use as bounding box]%% Expand bounding box to include the loop path
                (current bounding box.south west) 
                rectangle 
                ([shift={(0pt,\BBoxTweakForLoopDistance)}]current bounding box.north east);
            \end{scope}
        }{%
                \path (A) edge[Loop Style] (A);
        }%

        \DrawBoundingBox
    \end{tikzpicture}%
}


\begin{document}
    \MyNode\MyNode
    \quad
    \MyNode*
    \quad
    \MyNode*\MyNode*
\end{document}
  • I was not able to get the \useasboundingbox together with \pgfresetboundingbox so would welcome another answer that shows how to use that. – Peter Grill May 21 '18 at 23:43

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.