1

I am trying to build a Tikz picture in which there are two scopes. The size of the first scope is known as it only has one element (a picture). The size of the second scope is not known as it can contain an arbitrary number of nodes.

The problem occurs that I need to be able to position both of the scopes centrally on top of each other, which means that either I need to find a way to precalculate the size of the second scope, or need to move the second scope after it has been made.

Its useful to note that the solution needs also to work where the contents of the first scope is defined in the begin portion of a \newenvironment, and the second scope contents within the new environment. Furthermore the order in which the scopes are created cannot change (it would be trivial if it were possible to move the first scope into the end portion of the environment, but this cannot be done here due to other constraints).

Something along the lines of:

\newenvironment{anEnvironment}{
    \begin{tikzpicture}
        \begin{scope}
            %... make some nodes in first scope
        \end{scope}
        \begin{scope}
}{
        \end{scope}
        %... Set centre position of secondscope to match firstscope.
    \end{tikzpicture}
}

\begin{anEnvironment}
        %... make nodes for second environment
\end{anEnvironment}

Is it possible to move a scope (apply shift) after it has been created?

Alternatively, is there a way to create a tikzpicture that is invsibile from which I can get the size? That way the size could be precalculated and then the scope created with the correct position.

3
  • 2
    If I get what you want to do, this is not hard to achieve, but it would have been helpful if you had provided an example case (even if hard-coded) so that we might have a clearer idea of what you're aiming for.
    – Alenanno
    Commented Oct 17, 2020 at 22:49
  • Also, again based on what I understood, I'd use a \newcommand here, any particular reason for using a new environment that might help understand the purpose of it? You can include these details in your question as an edit. :) Thanks.
    – Alenanno
    Commented Oct 17, 2020 at 22:53
  • @Alenanno I'm basically trying to make a document template which uses pdftags to make an accessible PDF for our lab notes. To do that I've been making a load of wrappers (ttikzpicture = tagged tikzpicture). In order to get alt-text to work in this case, there needs to be a background image behind the tikz picture which can be tagged to display the alt text. Apologies, I accept it would have been helpful to make a demo, but it was nearing midnight at the time and I was in a rush to go to bed. Commented Oct 18, 2020 at 17:03

2 Answers 2

1

Based on a combination of this answer (using a matrix to centre a Tikz pic over a scope), and this answer (using NewEnviron and savebox), I've made up a solution which does what I need it to.

Essentially instead of using newenvironment I can get away with using NewEnviron (had to compromise on input argument handing, but I can live with that) which allows for the entire body to be known ahead of time. This means it can first be made in a savebox to allow it to be measured, then the background image node can be made based of those dimensions, followed by remaking the Tikz picture afterwards centred on the background image.

Below is an example (I've stripped a load of other bits out of it for clarity).

\newif\ifttikzpictureIsDraft

\usepackage{environ}
\environbodyname\ttikzenvironbody
\newsavebox{\ttikzpicturebody}
\newlength{\ttikzpictureheight}
\newlength{\ttikzpicturewidth}

% Include tikzpicture for tfigure
\NewEnviron{ttikzpicture}[2][]{%
% Check if in draft mode, if so, set background box to draft image (yellow box)
\ifttikzpictureIsDraft%
    \def\ttikzbackgroundimage{\templatedir/Classes/EmptyBoxDraft}%
\else%
    \def\ttikzbackgroundimage{#2}%
\fi%
% Save the picture into a tikz pic so we can reuse it later twice
    \tikzset{pics/ttikzpic/.style={code={\ttikzenvironbody}}}
% Create a fake tikzpicture inside a savebox so we can measure its size
    \begin{lrbox}{\ttikzpicturebody}%
        \begin{tikzpicture}[#1]%
            \pic {ttikzpic};%
        \end{tikzpicture}%
    \end{lrbox}%
% Calculate width and height of the tikzpicture
    \settoheight{\ttikzpictureheight}{\usebox{\ttikzpicturebody}}%
    \settowidth{\ttikzpicturewidth}{\usebox{\ttikzpicturebody}}%
% Create the real tikzpicture using same optional input arguments
    \begin{tikzpicture}[#1]%
% Put the background image in a new scope so we can reuse its bounding box for the main image.
        \begin{scope}[local bounding box = backgroundimage]
% Inside add a node using background image scaled to match the tikz picture measured
            \node (bgimage) [inner sep=0mm, anchor=center] (0,0) {\includegraphics[width=\ttikzpicturewidth, height=\ttikzpictureheight]{\ttikzbackgroundimage}};
% Finished with the effective figure
        \end{scope}
% Now we draw the user pic centred on the background image
        \matrix[at={(backgroundimage.center)}] {%
            \pic {ttikzpic};\\%
        };%
% Finish Tikz picture
    \end{tikzpicture}
}
0

I recently figured out how to use tikzmark to adjust the position of a pic (see https://tex.stackexchange.com/a/611460/86 for the origin of the idea, though the syntax has changed slightly since that answer). The core functionality of it uses the fact that a pic is surrounded by a scope and in actuality adjusts the scope position. So it could be used here. It almost worked as-is, except that because a pic is part of a path then I had made some assumptions in my original code that didn't work with a scope. Fortunately, those were fixable and so with the latest version of tikzmark (from the github repository linked above) then the following seems to do what you want.

\documentclass{article}
%\url{https://tex.stackexchange.com/q/567245/86}
\usepackage{tikz}
\usetikzlibrary{tikzmark}

\newenvironment{anEnvironment}{
    \begin{tikzpicture}
        \begin{scope}
  \fill (0,0) circle[radius=5pt];
        \end{scope}
        \begin{scope}[
          anchor=center,
          scope anchor location,
          adjust scope position,
        ]
}{
        \end{scope}
    \end{tikzpicture}
}

\begin{document}

\begin{anEnvironment}
\fill[red] (2,0) circle[radius=4pt];
\fill[red] (2,1) circle[radius=4pt];
\fill[red] (3,0) circle[radius=4pt];
\fill[red] (3,1) circle[radius=4pt];
\end{anEnvironment}

\end{document}

As with all tikzmark solutions, it needs two compilations to settle.

adjusted scope position

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .