4

I wanted to create a macro that can be invoked in the middle of a tikz path and behave differently, conditional on one of the arguments.

Here is an example document:

\documentclass[a4paper]{amsart}

\usepackage{ifthen, tikz}

\newcommand{\nameandlabel}[2]{%
    node [midway, \ifthenelse{\equal{#1}{a}}{above}{below}] {#2}%
}

\begin{document}

\begin{tikzpicture}[xscale = 25mm]
    \node (a) at (0, 0) {$A$};
    \node (b) at (1, 0) {$B$};
    \node (c) at (2, 0) {$C$};
    \draw (a) -- (b) node [midway, above] {$f$};
    \draw (b) -- (c) \nameandlabel{a}{$g$};
\end{tikzpicture}

\end{document}

This document does not compile. It results in this error message, which is surprising because \equal is a macro provided by ifthen.

! Undefined control sequence.
<argument> \equal 
                  {a}{a}
l.16     \draw (b) -- (c) \nameandlabel{a}{$g$}
                                               ;

I saw discussion on the TeX-LaTeX Stack Exchange that said that ifthen was obsolete and that the \IfStrEq macro from the xstring package might be used instead. So I changed the example to the following:

\documentclass[a4paper]{amsart}

\usepackage{xstring, tikz}

\newcommand{\nameandlabel}[2]{%
    node [midway, \IfStrEq{#1}{a}{above}{below}] {#2}%
}

\begin{document}

\begin{tikzpicture}[xscale = 25mm]
    \node (a) at (0, 0) {$A$};
    \node (b) at (1, 0) {$B$};
    \node (c) at (2, 0) {$C$};
    \draw (a) -- (b) node [midway, above] {$f$};
    \draw (b) -- (c) \nameandlabel{a}{$g$};
\end{tikzpicture}

\end{document}

But this results in an even more incomprehensible error message:

! Argument of \XC@definec@lor has an extra }.
<inserted text> 
                \par 
l.16     \draw (b) -- (c) \nameandlabel{a}{$g$}
                                               ;

Why does this still not work?

Hammerite
  • 3,596
  • 2
    TikZ takes charge of parsing and expanding things in the paths so I guess in this case \nameandlabel does not get expanded before TikZ tries to make sense of the path. You may sidestep the problem by proper use of key-val: with \tikzset{a/.style={above}, mystyle/.style={midway,below}} you can do ...(c) node[mystyle,a] {$g$} or node[mystyle] for defaulting to below... – Bordaigorl Nov 14 '13 at 18:30
  • Bordaigorl, the following macro works perfectly in my main document: \newcommand{\isomorphismDecoration}[2]{node [midway, #1 = -2.3mm] {$\widetilde{\phantom{#2}}$}} – Hammerite Nov 14 '13 at 20:09
  • Yes you are right, it seems to be a genuine conflict with the conditionals commands...just as a temporary workaround you could define a as an alias for above and then change the def of your macro to node [midway, #1] {#2} – Bordaigorl Nov 14 '13 at 20:38
  • Is using \ifx unacceptable? If you do not need anything fancier than checking it is an a than you could get away with \ifx#1a{above}\else{below}\fi (which does work in your code) – Bordaigorl Nov 14 '13 at 20:41
  • 2
    The \IfStrEq is not fully expandable (meaning it will leave traces besides left and right in the options which clutter up the key parser from PGFkeys). Similar to the problem in (x)ifthen in TikZ (and related question). You will need to do conditional outside of the keys (either outside of the path, inside of \pgfextra{…} or with the /utils/exec key). – Qrrbrbirlbel Nov 14 '13 at 23:49
  • By the way, you are using xscale incorrectly. This key should only get a value, not a length. What you are essentially doing is xscale=71.13188. Why don’t you simply define a style with the name a (or similar) that is defined as a/.style={above}, another key like b for below can be defined as well. By the way, do you know the auto and the swap options/keys? – Qrrbrbirlbel Nov 14 '13 at 23:53
  • Yes, I wrote the document without paying much attention. It should be x, not xscale I guess. I decided the easiest thing to do is just to define two different macros. Actually, in the real task I wanted 4 different possibilities (above, below, left and right) so I just created 4 different macros. – Hammerite Nov 15 '13 at 17:59

2 Answers2

3

Neither ifthen nor xstring tests work by expansion they both make internal definitions.

If you use a purely expandable test it works as you had intended.

enter image description here

\documentclass[a4paper]{amsart}

\usepackage{ifthen, tikz}

\newcommand{\nameandlabel}[2]{%
    node [midway, \ifx a#1above\else below\fi] {#2}%
}

\begin{document}

\begin{tikzpicture}[x = 25mm]
    \node (a) at (0, 0) {$A$};
    \node (b) at (1, 0) {$B$};
    \node (c) at (2, 0) {$C$};
    \draw (a) -- (b) node [midway, above] {$f$};
    \draw (b) -- (c) \nameandlabel{a}{$g$};
\end{tikzpicture}

\begin{tikzpicture}[x = 25mm]
    \node (a) at (0, 0) {$A$};
    \node (b) at (1, 0) {$B$};
    \node (c) at (2, 0) {$C$};
    \draw (a) -- (b) node [midway, above] {$f$};
    \draw (b) -- (c) \nameandlabel{b}{$g$};
\end{tikzpicture}

\end{document}
David Carlisle
  • 757,742
3

The David Carlisle's answer is a direct answer to your question. But I think that in your case in place to use conditional it is more in the spirit of TikZ to use a .is choice key handler. Here is an example how to do it.

\documentclass[varwidth]{standalone}
\usepackage{tikz}

\tikzset{
  ab/.is choice,
  ab/a/.style={above},
  ab/b/.style={below},
}
\newcommand{\nameandlabel}[2]{%
    node [midway,ab=#1] {#2}%
}

\begin{document}

  \begin{tikzpicture}[x = 25mm]
      \node (a) at (0, 0) {$A$};
      \node (b) at (1, 0) {$B$};
      \node (c) at (2, 0) {$C$};
      \draw (a) -- (b) node [midway, above] {$f$};
      \draw (b) -- (c) \nameandlabel{a}{$g$};
  \end{tikzpicture}

  \begin{tikzpicture}[x = 25mm]
      \node (a) at (0, 0) {$A$};
      \node (b) at (1, 0) {$B$};
      \node (c) at (2, 0) {$C$};
      \draw (a) -- (b) node [midway, above] {$f$};
      \draw (b) -- (c) \nameandlabel{b}{$g$};
  \end{tikzpicture}

\end{document}

enter image description here

Kpym
  • 23,002