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?

  • 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
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}
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

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.