[Tex/LaTex] How to remove leading and trailing spaces from a macro argument

macrosspacingtikz-pgf

Consider this minimal working example:

\documentclass[a4paper]{amsart}

\usepackage{tikz}

\tikzset{graph/.style = {every node/.style = { draw,
                                               shape = circle,
                                               fill = black,
                                               minimum size = 0.8mm,
                                               inner sep = 0mm,
                                               label distance = 0.8mm
                                               }}}

\newcommand{\circumferencenode}[4][]{\node (#1#2) at (#4: #3) [label = #4: $#2$] {};}

\newlength{\gr} % graph radius
\setlength{\gr}{15mm}

\begin{document}
    \begin{tikzpicture}[graph]
        \circumferencenode{0}{\gr}{ 90}
        \circumferencenode{1}{\gr}{ 30}
        \circumferencenode{2}{\gr}{330}
        \circumferencenode{3}{\gr}{270}
        \circumferencenode{4}{\gr}{210}
        \circumferencenode{5}{\gr}{150}
        \draw (0) -- (1) -- (2) -- (3) -- (4) -- (5) -- (0);
    \end{tikzpicture}

    \vspace*{10mm}

    \begin{tikzpicture}[graph]
        \circumferencenode{02}{\gr}{ 90}
        \circumferencenode{ 3}{\gr}{  0}
        \circumferencenode{ 4}{\gr}{270}
        \circumferencenode{15}{\gr}{180}
        \draw (02) -- (3) -- (4) -- (15) -- (02);
    \end{tikzpicture}
\end{document}

This is what the output looks like:

As you can see, the second example is weird-looking. Something has gone wrong with the placement of nodes. I could ask why this is happening, but some experimentation shows that if the second picture is changed to the following:

\begin{tikzpicture}[graph]
    \circumferencenode{02}{\gr}{90}
    \circumferencenode{3}{\gr}{0}
    \circumferencenode{4}{\gr}{270}
    \circumferencenode{15}{\gr}{180}
    \draw (02) -- (3) -- (4) -- (15) -- (02);
\end{tikzpicture}

or even to this:

\begin{tikzpicture}[graph]
    \circumferencenode{02}{\gr} {90}
    \circumferencenode {3}{\gr}  {0}
    \circumferencenode {4}{\gr}{270}
    \circumferencenode{15}{\gr}{180}
    \draw (02) -- (3) -- (4) -- (15) -- (02);
\end{tikzpicture}

then the problem disappears, and the second graph appears the way one would expect.

So the problem is apparently the leading spaces in the arguments to the macro. I could get rid of them and do just fine. However, it is pleasing to me to arrange the calls to the \circumferencenode macro the way I did originally, so that the numbers and the sets of braces are vertically aligned. So a better solution would be to find out how to strip leading and trailing spaces from macro arguments.

  • Why does the original example not work properly?
  • How can I strip leading and trailing spaces from the arguments to macros I define?

Best Answer

Trimming spaces can be difficult with kernel macros; here LaTeX3 can come to the rescue:

\documentclass[a4paper]{amsart}

\usepackage{tikz,xparse}

\tikzset{graph/.style = {every node/.style = { draw,
                                               shape = circle,
                                               fill = black,
                                               minimum size = 0.8mm,
                                               inner sep = 0mm,
                                               label distance = 0.8mm
                                               }}}

%%% We need a "classical" definition, because of the colons in the replacement text
\newcommand{\circumferencenodeinner}[4]{\node (#1#2) at (#4: #3) [label = #4: $#2$] {};}
%%% Now we go with LaTeX3
\ExplSyntaxOn
\NewDocumentCommand{\circumferencenode}{ O{} m m m }
 {
  \hammerite_circumference_node:nxnx
   { #1 }
   { \tl_trim_spaces:n { #2 } }
   { #3 }
   { \tl_trim_spaces:n { #4 } }
 }
\cs_set_eq:NN \hammerite_circumference_node:nnnn \circumferencenodeinner
\cs_generate_variant:Nn \hammerite_circumference_node:nnnn { nxnx }
\ExplSyntaxOff

\newlength{\gr} % graph radius
\setlength{\gr}{15mm}

\begin{document}

\noindent\begin{minipage}{.4\textwidth}
%%% Just to give an example we use the old definition
\newcommand{\circumferencenodeold}[4][]{\node (#1#2) at (#4: #3) [label = #4: $#2$] {};}

\textbf{Old}

    \begin{tikzpicture}[graph]
        \circumferencenodeold{0}{\gr}{ 90}
        \circumferencenodeold{1}{\gr}{ 30}
        \circumferencenodeold{2}{\gr}{330}
        \circumferencenodeold{3}{\gr}{270}
        \circumferencenodeold{4}{\gr}{210}
        \circumferencenodeold{5}{\gr}{150}
        \draw (0) -- (1) -- (2) -- (3) -- (4) -- (5) -- (0);
    \end{tikzpicture}

    \vspace*{10mm}

    \begin{tikzpicture}[graph]
        \circumferencenodeold{02}{\gr}{ 90}
        \circumferencenodeold{ 3}{\gr}{  0}
        \circumferencenodeold{ 4}{\gr}{270}
        \circumferencenodeold{15}{\gr}{180}
        \draw (02) -- (3) -- (4) -- (15) -- (02);
    \end{tikzpicture}
\end{minipage}\vrule\qquad
\noindent\begin{minipage}{.4\textwidth}
\textbf{New}

    \begin{tikzpicture}[graph]
        \circumferencenode{0}{\gr}{ 90}
        \circumferencenode{1}{\gr}{ 30}
        \circumferencenode{2}{\gr}{330}
        \circumferencenode{3}{\gr}{270}
        \circumferencenode{4}{\gr}{210}
        \circumferencenode{5}{\gr}{150}
        \draw (0) -- (1) -- (2) -- (3) -- (4) -- (5) -- (0);
    \end{tikzpicture}

    \vspace*{10mm}

    \begin{tikzpicture}[graph]
        \circumferencenode{02}{\gr}{ 90}
        \circumferencenode{ 3}{\gr}{  0}
        \circumferencenode{ 4}{\gr}{270}
        \circumferencenode{15}{\gr}{180}
        \draw (02) -- (3) -- (4) -- (15) -- (02);
    \end{tikzpicture}
\end{minipage}
\end{document}

The command \circumferencenodeinner is defined in the standard way because of the colons in its replacement text; notice that no optional argument is specified; it's only a helper macro that will be \let to a macro with a LaTeX3 name, so that the variant with suffix nxnx can be created; the x means that the corresponding argument is completely expanded. Indeed when we finally call it, arguments #2 and #4 are passed to \tl_trim_spaces:n whose job is exactly to trim leading and trailing spaces, then passing the rest as is.

enter image description here