36

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.

Marco Daniel
  • 95,681
Nazia
  • 585
  • 1
  • 5
  • 7
  • I would use \newcommand*{\Name}{<value>} to declare it so that you can be sure it is not already been defined elsewhere. Definitely do not use \let -- it is not for this purpose. – Peter Grill Mar 07 '12 at 21:17
  • Do you plan to adjust these from the command line for multiple runs? Or is this tikzpicture included in another document and if so, do you want to adjust them from that document? – Peter Grill Mar 07 '12 at 21:24
  • @PeterGrill: Thanks. The tikzpicture is included from another document, but I only need to change the constants in the document containing the tikzpicture (the deepest document). – Nazia Mar 07 '12 at 21:34
  • @PeterGrill: There is a let operation in TikZ, I guess the OP doesn’t mean TeX’s \let macro. – Tobi Mar 07 '12 at 21:58
  • @Tobi: Ok, that's probably what the OP meant. And yes, the TeX \let is different than the TiKZ let. – Peter Grill Mar 07 '12 at 22:00

2 Answers2

39

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}
Peter Grill
  • 223,288
  • 3
    I prefer the shorter \def\xx{2.0} which should harmless since I use it inside the tikzpicuture environment (group). The definition is only valid inside the group an won’t cause conflict outside. Certainly this will only work if one needs the parameters in one picture. – Tobi Mar 07 '12 at 22:00
  • 2
    @Tobi: Personal preference I guess. I often put my constant definitions outside the tikzpicture, so prefer \newcommand*{}{} as it is safer. And if there are many of these to define, copy-paste is faster anyway. – Peter Grill Mar 07 '12 at 22:03
  • What are the safety issues with \def? – Nazia Mar 07 '12 at 22:07
  • 2
    @Nazia: \newcommand will produce an error if you attempt to redefine an existing name, \def will not. See What is the difference between \def and \newcommand? – Peter Grill Mar 07 '12 at 22:09
  • @ Peter: Surly, this is my preference … :-) – Tobi Mar 07 '12 at 22:17
  • @PeterGrill TikZ works with plain TeX and LaTeX, and \pgfmathsetmacro uses \edef. Then if you want something quicker in loops I think in this case \def is more efficient (look at the def of \pgfmathsetmacro . And in a group, macros are local, so \def can be used instead of newcommand. – Alain Matthes Mar 07 '12 at 23:33
  • @Altermundus: Have updated to provide a \pgfmathsetnewmacro{<name>}{<expression>} that issues an error if the <name> is already defined, also noted that if one is not concerned about over writing existing names, then \def is just fine. – Peter Grill Mar 10 '12 at 17:21
  • @Tobi: Updated to show that there is a possible problem in using \def. – Peter Grill Mar 22 '12 at 02:44
  • @PeterGrill: Ok, thanks. I see the problem but it makes no difference if I use \renewcommand instead of \def – Tobi Mar 23 '12 at 13:39
  • @Tobi: Well with \renewcommand you know you are redefining something, with \def you don't. – Peter Grill Mar 23 '12 at 14:19
  • @PeterGrill: But with \def I know that I define a variable (in the current scope/group) ;-) So this decision is subjective since both have the same problem as shown in your example. – Tobi Mar 23 '12 at 20:59
  • @Tobi: I don't use \renewcommand, but \newcommand to be safe. – Peter Grill Mar 23 '12 at 22:05
  • Yes, but the example you give to show the disadvantage of \def has the same problem with \(re)newcommand ;-) The benefit of using \newcommand is that it gives an error if your variable macro already exists. – Tobi Mar 24 '12 at 11:43
  • @Tobi: I think you should post an answer to summarize what you are recommending, as it might be confusing to someone who comes across this question. I am recommending not using \def, and not using \renewcommand. – Peter Grill Mar 24 '12 at 16:13
  • @PeterGrill: I added my answer … But I think that your example “Code: Problem in using \def within tikzpicture:” doesn’t show the disadvantage of \def against \newcommand since it produces the same error when I replace \def by \newcommand ;-) – Tobi Mar 25 '12 at 09:22
  • @Tobi: If you use \newcommand in the Problem with \def (with the last one uncommented) then you get a nice error that explains exactly what the problem is. With \def it just hangs and you don't know what went wrong. It took my quite some time to diagnose my problem as it was nested several macros deep.. – Peter Grill Mar 25 '12 at 18:03
  • 1
    Another note on \def: don't use it for "complex expressions", only for "constant literal" https://tex.stackexchange.com/questions/235444/tikz-how-to-define-a-constant-and-then-use-it-in-drawing#comment1605472_235446 – user202729 May 15 '22 at 03:27
7

Using \def

I normally prefer the shorter \def than \newcommand. The problem is, that \def doesn’t check wether the macro already exist, so it’s maybe overwritten. But I use the \definitions only inside {tikzpicture} and for a possible overwriting only exists in the current group of {tikzpicture}

\documentclass[border=1cm]{standalone}

\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
   \def\radius{1cm}
   \draw circle (\radius);
\end{tikzpicture}
\end{document}

In the above example \radius is only defined inside of {tikzpicture} and can only be used inside of it.

The next example shows that \def maybe produce an undesired result

\documentclass[border=1cm]{standalone}

\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
   \def\bfseries{1mm}
   \draw [line width=\bfseries] circle (2cm);
   \node {\bfseries That's the problem.};
\end{tikzpicture}
\end{document}

Using \newcommand

If I need a global variable I prefer \newcommand since it gives an error if I try to use an already existing macro name.

\documentclass[border=1cm]{standalone}

\usepackage{tikz}

\newcommand\radius{1cm}

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

\begin{tikzpicture}
   \draw [red] circle (\radius);
\end{tikzpicture}
\end{document}

The same example as the second one in “Using \def” but with \newcommand. If we now try to use an existing command (\bfseries) as TikZ variable we get the error message that \bfseries is already defined. So the user must decide wether to redefine it or use an other name which is the recommend way in this case.

\documentclass[border=1cm]{standalone}

\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
   \newcommand\bfseries{1mm}
   \draw [line width=\bfseries] circle (2cm);
   \node {\bfseries That's the problem.};
\end{tikzpicture}
\end{document}
Tobi
  • 56,353
  • @PeterGrill: This is my answer. What do you think about it? – Tobi Mar 25 '12 at 09:20
  • I don't think your 2nd example shows a problem. One would do that if they did not know (or did not want) to use \bfseries, and hence the correct result was produced. – Peter Grill Mar 25 '12 at 18:04
  • @PeterGrill: I guess I used the wrong words … with “problem” I mean that there will be a conflict because \bfseries is defined already and cant be uses with \newcommand so the user will be warned that he maybe does something he don’t want (overwrite \bfseries assuming he doesn’t know that it exist already). So the LaTeX gives the right result (error message) but it’s a problem from the users point of view. Hope that I made more clear what I wanted to say :-) – Tobi Mar 26 '12 at 09:44