8

I would like to define the \DrawLine macro below so that it can be invoked from within a {tikzpicture} environment or from outside:

\newcommand*{\DrawLine}[1]{%
  \IfInTikzPic{}{\tikzpicture[remember picture]}
      \draw[ultra thick, ->, #1] (0,) -- (1,1) node [right] {output of DrawLine};
  \IfInTikzPic{}{\endtikzpicture}%
}

This works fine when invoked from within a {tikzpicture}, but not when it is invoked from outside (line is commented in the MWE). Invoking it from outside yields the error:

Missing \endgroup inserted.

enter image description here

The MWE below uses the first reference below. The other two references also fail on the commented out test case.

References:

Code:

\documentclass{article}
\usepackage{tikz}

\makeatletter
\newcommand{\IfInTikzPic}[2]{% https://tex.stackexchange.com/a/121309/4301
  \ifx\pgfpictureid\@undefined#2\else#1\fi%
}
\makeatother

\newcommand*{\DrawLine}[1]{%
  \IfInTikzPic{}{\tikzpicture[remember picture]}
      \draw[ultra thick, ->, #1] (0,) -- (1,1) node [right] {output of DrawLine};
  \IfInTikzPic{}{\endtikzpicture}%
}

\begin{document}
%%% The commented out code here is to show that \IfInTikzPic works as desired
%%% (in a tikzpicture, outside of a \node).
%%%

%\textbf{IfInTikzPic}\par
%\IfInTikzPic{inside}{outside}
%
%\begin{tikzpicture}
%  \IfInTikzPic{\draw [red, ultra thick]}{\draw [blue, ultra thick]} (0,0) -- (1,0);
%\end{tikzpicture}
%
%\medskip% --------------------------
\textbf{DrawLine}: Actual Output\par

%\DrawLine{blue}% <---- How do I get this case to work?

\begin{tikzpicture}
  \DrawLine{orange}
\end{tikzpicture}

\medskip% --------------------------
\textbf{DrawLine}: Desired Output\par

\begin{tikzpicture}
  \DrawLine{blue}
\end{tikzpicture}

\begin{tikzpicture}
  \DrawLine{orange}
\end{tikzpicture}
\end{document}
6

The following example defines \IfInTikzPic to check the current environment against tikzpicture and define a \tikzstart...\tikzend pair accordingly:

enter image description here

\documentclass{article}

\usepackage{tikz}

\makeatletter
\def\@tikzenvironment{tikzpicture}

\newcommand{\IfInTikzPic}{%
  \edef\tikzstart{%
    \ifx\@currenvir\@tikzenvironment\else
      \noexpand\tikzpicture[remember picture]%
    \fi
  }%
  \edef\tikzend{%
    \ifx\@currenvir\@tikzenvironment\else
      \noexpand\endtikzpicture%
    \fi
  }%
}

\newcommand*{\DrawLine}[1]{%
  \IfInTikzPic
  \tikzstart
    \draw[ultra thick, ->, #1] (0,) -- (1,1) node [right] {output of DrawLine};
  \tikzend%
}
\makeatother

\begin{document}

\textbf{DrawLine}: Actual Output\par

\DrawLine{blue}

\begin{tikzpicture}
  \DrawLine{orange}
\end{tikzpicture}

\medskip% --------------------------

\textbf{DrawLine}: Desired Output\par

\begin{tikzpicture}
  \DrawLine{blue}
\end{tikzpicture}

\begin{tikzpicture}
  \DrawLine{orange}
\end{tikzpicture}

\end{document}

You could also group the tikzpicture if needed:

\newcommand{\IfInTikzPic}{%
  \edef\tikzstart{%
    \ifx\@currenvir\@tikzenvironment\else
      \noexpand\begin{tikzpicture}[remember picture]%
    \fi
  }%
  \edef\tikzend{%
    \ifx\@currenvir\@tikzenvironment\else
      \noexpand\end{tikzpicture}%
    \fi
  }%
}
6
\documentclass{article}
\usepackage{tikz}

\makeatletter
\newcommand{\IfInTikzPic}{% https://tex.stackexchange.com/a/121309/4301
  \ifx\pgfpictureid\@undefined
    \expandafter\@firstoftwo
     \else
    \expandafter\@secondoftwo
    \fi
}
\makeatother

\newcommand*{\DrawLine}[1]{%
  \IfInTikzPic
   {\begin{tikzpicture}[remember picture]
    \draw[ultra thick, ->, #1] (0,) -- (1,1) node [right] {output of DrawLine};
   \end{tikzpicture}}%
    {\draw[ultra thick, ->, #1] (0,) -- (1,1) node [right] {output of DrawLine};}%
}

\begin{document}
%%% The commented out code here is to show that \IfInTikzPic works as desired
%%% (in a tikzpicture, outside of a \node).
%%%

\textbf{IfInTikzPic}\par
\IfInTikzPic{inside}{outside}

\begin{tikzpicture}
  \IfInTikzPic{\draw [red, ultra thick]}{\draw [blue, ultra thick]} (0,0) -- (1,0);
\end{tikzpicture}

\medskip% --------------------------
\textbf{DrawLine}: Actual Output\par

\DrawLine{blue}% <---- How do I get this case to work?

\begin{tikzpicture}
  \DrawLine{orange}
\end{tikzpicture}

\medskip% --------------------------
\textbf{DrawLine}: Desired Output\par

\begin{tikzpicture}
  \DrawLine{blue}
\end{tikzpicture}

\begin{tikzpicture}
  \DrawLine{orange}
\end{tikzpicture}
\end{document}

Or this version which directly forces tikz rather than having an if-then-else construct

\documentclass{article}
\usepackage{tikz}

\makeatletter
\newcommand{\ensuretikz}{% https://tex.stackexchange.com/a/121309/4301
  \ifx\pgfpictureid\@undefined
    \expandafter\tikzify
     \else
    \expandafter\@firstofone
    \fi
}
\makeatother

\def\tikzify#1{\begin{tikzpicture}#1\end{tikzpicture}}

\newcommand*{\DrawLine}[1]{%
    \ensuretikz
    {\draw[ultra thick, ->, #1] (0,) -- (1,1) node [right] {output of DrawLine};}%
}

\begin{document}
%%% The commented out code here is to show that \IfInTikzPic works as desired
%%% (in a tikzpicture, outside of a \node).
%%%




\medskip% --------------------------
\textbf{DrawLine}: Actual Output\par

\DrawLine{blue}% <---- How do I get this case to work?

\begin{tikzpicture}
  \DrawLine{orange}
\end{tikzpicture}

\medskip% --------------------------
\textbf{DrawLine}: Desired Output\par

\begin{tikzpicture}
  \DrawLine{blue}
\end{tikzpicture}

\begin{tikzpicture}
  \DrawLine{orange}
\end{tikzpicture}
\end{document}
  • I thought it might have something to dot with the dreaded expansion issues! – Peter Grill Nov 7 '18 at 20:11
  • 2
    @PeterGrill I think the main error was \IfInTikzPic{}{\endtikzpicture}% as by that point you are always in a tikz picture, you need to know if you started it or if you were already in one. – David Carlisle Nov 7 '18 at 20:17
  • Opsss..., thats a bit embarrassing. I'd prefer to not duplicate the drawing code though. – Peter Grill Nov 7 '18 at 20:33
  • @PeterGrill duplicating it isn't needed, I could post a variant without, i just started by simplifying it – David Carlisle Nov 7 '18 at 21:13
  • @PeterGrill plan b posted – David Carlisle Nov 7 '18 at 21:18

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.