5

I construct a macro named \mycal(with an optional argument) to calculate value of coordinates which will be used in \tikz\draw. I find that with this optioal arg, the code can not compile. When the optional arg become mandatory, the code works all right. But I do need this arg optional. Why and how to solve this problem?

MWE:

\documentclass{article}
\usepackage{tikz,picture}

\begin{document}
% The following part can not compile
% \newcommand{\mycal}[3][0pt]{\the\dimexpr #2 + #3 + #1}
% \newcommand\mydraw[3]{\tikz\draw(0,0)--(\mycal[#1]{#2}{#3},0);}
% \mydraw{1pt}{2cm}{3cm}

% The following part ok!
\newcommand{\mycal}[3]{\the\dimexpr #2 + #3 + #1}
\newcommand\mydraw[3]{\tikz\draw(0,0)--(\mycal{#1}{#2}{#3},0);}
\mydraw{1pt}{2cm}{3cm}
\end{document}
lyl
  • 2,727

1 Answers1

7

Your macro does not work because macros with optional arguments are not expandable and therefore cannot be used in the middle of a path construction. Tex has to perform some assignment to determine whether the optional argument is present or not. The solution may be to use \pgfextra which allows you to interrupt temporarily the construction of the path and execute some code. You have to modify your code to save the result of the computation to some register and then use it.

\documentclass{article}

\usepackage{tikz}

\newdimen\mydimen
\newcommand\mycal[3][0cm]{\mydimen=\dimexpr #2 + #3 + #1\relax}
\newcommand\mydraw[3][0cm]{\tikz\draw(0cm,0cm) \pgfextra{\mycal[#1]{#2}{#3}} --(\mydimen,0cm);}

\begin{document}
\mydraw[1cm]{2cm}{3cm}

\mydraw{2cm}{3cm}
\end{document}

By the way, in your example, \mycal does not really need to take an optional argument. \mydraw does.

\newcommand\mycal[3]{\dimexpr #2 + #3 + #1\relax}
\newcommand\mydraw[3][0cm]{\begin{tikzpicture}\draw(0cm,0cm)-- (\mycal{#1}{#2}{#3},0cm);

EDIT:

If you really insist on using \mycal with an optional argument without using \pgfextra, you can use the following trick. It defines an expandable \mycalc with an optional argument.

\documentclass{article}

\usepackage{tikz}

\makeatletter
\def\mycal#1{%
  \ifx[#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {\@mycal[}{\@mycal[0cm]{#1}}}
\def\@mycal[#1]#2#3{\dimexpr#1+#2+#3\relax}
\makeatother

\begin{document}
\tikz\draw(0cm,0cm)--(\mycal{2cm}{3cm},0cm);

\tikz\draw(0cm,0cm)--(\mycal[1cm]{2cm}{3cm},0cm);
\end{document}

NEW EDIT:

To write \mycal{2cm,3cm} or \mycal[1cm]{2cm,3cm} instead of \mycal{2cm}{3cm} or \mycal[1cm]{2cm}{3cm} you can define \mycal as follows

\documentclass{article}

\usepackage{tikz}

\makeatletter
\def\mycal#1{%
  \ifx[#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {\@mycal[}{\@mycal[0cm]{#1}}}
\def\@mycal[#1]#2{\@@mycal{#1}#2\@nil}
\def\@@mycal#1#2,#3\@nil{\dimexpr#1+#2+#3\relax}
\makeatother

\begin{document}
\tikz\draw(0cm,0cm)--(\mycal{2cm,3cm},0cm);

\tikz\draw(0cm,0cm)--(\mycal[1cm]{2cm,3cm},0cm);
\end{document}
  • Thank you @Eric. Optional args is needed in \mycal, because there are many calculations in the path, and for most of them ,#1 is 0cm. So I don't want to write so many "[...]" which is only 0cm and does not affect the result of calculation. Is there any other way to force expland of optional args? – lyl Feb 05 '19 at 10:35
  • @lyl I updated my answer to define an expandable macro \mycal with an optional argument. – Eric Domenjoud Feb 05 '19 at 10:53
  • Thank you so much for your solutions. And is there a way to construct \mycal when calling it like this: \mycal{1cm,2cm} rather than \mycal{1cm}{2cm}? – lyl Feb 06 '19 at 00:53
  • @lyl I updated again my answer. – Eric Domenjoud Feb 06 '19 at 06:33
  • Many many thanks!! Please forgive my greed. I think a macro like "\mycal[5pt]{1pt,2pt,3em,4cm,...,5mm} "(the mandatory arg is a list dilimited by ",", the number of the list member is variable ) will be the ultimate solution. Is there such a solution? – lyl Feb 06 '19 at 07:03
  • @lyl It is possible to achieve this but if you want an arbritrary number of arguments, what is the point of having an optional one? – Eric Domenjoud Feb 06 '19 at 08:11
  • @lyl Also, if you can give the mandatory argument under the form {1pt,2pt,3em,4cm,...,5mm} why not simply {1pt+2pt+3em+4cm+...+5mm}? – Eric Domenjoud Feb 06 '19 at 08:22