[Tex/LaTex] the preferred way of defining a TikZ constant

best practicestikz-pgf

I'm drawing a TikZ figure and am going to need a couple of constant that I could easily change between compilations, e.g. a main radius, a proportion, a number of lines, etc. What is the preferred way of defining these?

It's not just one \draw command, so I suppose I can't use let.

Best Answer

Using \newcommand and \pgfmathsetmacro:

Since you are only planning on changing the parameter within the deepest .tex file containing the tikzpicture, I would use \newcommand*{\Name}{}% to set the values and the just use \Name to access them.

However, if the value of some of the parameters are mathematically computed from another, then I would recommend using \pgfmathsetmacro{\NewName}{}. For example:

\newcommand*{\Diameter}{3.0}%
\pgfmathsetmacro{\Radius}{\Diameter/2}%
\pgfmathsetmacro{\Circumference}{2*pi*\Radius}%

This will be very useful when the calculation is a bit more complex.

However, should you want to consider the ability to change them from the parent document, you could instead use \providecommand{}{} in the tikzpicture document instead to set the parameter. This will then allow for the possibility that you could change the parameter in the parent document. One downside of this approach is that there is a risk that the value is defined somewhere else also no error will be triggered in the tikzpicture.

Using \pgfmathsetnewmacro:

As pointed out in the comments \pgfmathsetmacro uses a \def so no error is produced if you are overwriting an existing macro name. So, instead you could use \pgfmathsetnewmacro as defined below -- this issues a \newcommand to check that the macro does not already exist, before calling \pgfmathsetmacro:

\newcommand*{\pgfmathsetnewmacro}[2]{%
    \newcommand*{#1}{}% Error if already defined
    \pgfmathsetmacro{#1}{#2}%
}%

If you are defining the values within the tikzpicture and are not worried about overriding any previously defined ones, then you can use \def and \pgfmathsetmacro.

Update: Problems with using \def:

The comments suggest that if you define these constants within the tikzpicture environment, any case of accidentally overwriting an existing macro via a \def or \pgfmathsetmacro will not result in any problems outside of the tikzpicture as the re-definitions would only be local to the tikzpicture environment. This is NOT entirely accurate, as it can result in problems inside the tikzpicture environment, as I just discovered after several hours of debugging.

With the wrong thinking that there is no problems in using \def within a local scope, I decide to change the hard to read (and hard to modify if additional parameters are added at the beginning) macros such as:

\newcommand*{\DrawXAxis}[3][]{%
    \draw [ultra thick, #1] (#2,0) -- (#3,0)%
}%

to something like this:

\newcommand*{\DrawXAxisReadable}[3][]{%
    \def\XAxisStyle{#1}
    \def\XAxisMin{#2}
    \def\XAxisMax{#3}
    \draw [ultra thick, \XAxisStyle] (\XAxisMin,0) -- (\XAxisMax,0)%
}%

thinking that there is no problem if I overwrite \XAxisMin within this macro as I don't need what ever has been set previously and it will be restored anyway upon the end of this macro.

Well, this is ok in most cases -- but not ok if some parent macro had defined \XAxisMin and had passed it to this macro as a parameter. The code below illustrates this. It compiles just fine as is, but if you un comment the last tikzpicture then pdflatex seems to be stuck in an infinite loop.


Test Code:

\documentclass{standalone}
\usepackage{tikz}

%\def\Diameter{}% Error from \newcommand if this is uncommented
%\def\Radius{}% Error from \pgfmathsetnewmacro if this is uncommented
%\def\Circumference{}% Error from \pgfmathsetnewmacro if this is uncommented


% This bascially automates a \newcommand{<name>}{} to ensure
% that a command with the given <name> does not already exist
\newcommand*{\pgfmathsetnewmacro}[2]{%
    \newcommand*{#1}{}% Error if already defined
    \pgfmathsetmacro{#1}{#2}%
}%

\begin{document}
\newcommand*{\Diameter}{3.0}%

\pgfmathsetnewmacro{\Radius}{\Diameter/2}%
\pgfmathsetnewmacro{\Circumference}{2*pi*\Radius}%

\newcommand{\Origin}{(0,0)}%

\begin{tikzpicture}
    \draw \Origin circle (\Radius);
\end{tikzpicture}
\end{document}

Code: Problem in using \def within tikzpicture:

\documentclass{article}
\usepackage{tikz}

\newcommand*{\XAxisMin}{0}%
\newcommand*{\XAxisMax}{5}%

\newcommand*{\DrawXAxis}[3][]{%
    \draw [ultra thick, #1] (#2,0) -- (#3,0)%
}%

\newcommand*{\DrawXAxisReadable}[3][]{%
    \def\XAxisStyle{#1}
    \def\XAxisMin{#2}
    \def\XAxisMax{#3}
    \draw [ultra thick, \XAxisStyle] (\XAxisMin,0) -- (\XAxisMax,0)%
}%

\begin{document}
\begin{tikzpicture}
    \DrawXAxis[black]{\XAxisMin}{\XAxisMax};
\end{tikzpicture}

\begin{tikzpicture}
    \DrawXAxisReadable[blue]{0}{5};
\end{tikzpicture}

%%% Un comment this to see the problem
%\begin{tikzpicture}
%   \DrawXAxisReadable[red]{\XAxisMin}{\XAxisMax};
%\end{tikzpicture}
\end{document}