0

I'm trying to create a command to show the distributive property of multiplication (for school effects) using tikzmarks and xparse. So far so good, the code is a bit long and I would like to know if I can accomplish this in a more summarized way (the right way ...my code is not very neat).

The subject is like this, I have defined a command \tikzmkpd[...]{...}{...} which receives an optional argument [key=val] and two mandatory arguments separated by commas. With the mandatory arguments it works perfect, the problem I have when trying to implement [key=val]. My idea is that [key=val]works like this:

config-A={yshift={#1},end-yshift={#1,#2,#3},color={#1,#2,#3},distance={#1,#2,#3}}, 
config-B={yshift={#1},end-yshift={#1,#2,#3},color={#1,#2,#3},distance={#1,#2,#3}}

For now of config-A I only have written the yshift and end-yshift keys that control the spaces of the beginning and end of the arrows that I am drawing with tikz.

My problem is with the key end-yshift={#1,#2,#3} which calls the command auxiliary \endyshift{...}. By using the command \endyshift{...} directly it accepts:

\endyshift{#1}
\endyshift{#1,#2}
\endyshift{#1,#2,#3}

but if I use it in [key=val] it becomes obligatory to pass the three parameters:

end-yshift={#1}       % error
end-yshift={#1,#2}    % error
end-yshift={#1,#2,#3} % correct

I've tried {} or ,, ` as "empty" arguments, but it doesn't work, besides, I think the code is pretty long and maybe it can be achieved using another strategy.

Here's my question then, can you pass key={v,a,l} where v, a and l execute different code?

This is my example document:

\documentclass{article}
\usepackage{xparse,tikz,etoolbox}
\usetikzlibrary{arrows.meta,bending,calc,tikzmark}
\setlength{\parindent}{0pt} % just for the example
\pagestyle{empty}
% short :)
\newcommand*{\TkM}[2]{\tikzmarknode{#1}{#2}}
% active : for expl3
\makeatletter
\def\@colon{:}
\ExplSyntaxOn

\keys_define:nn { tikzmkpd }
 {
  config-A  .code:n = { \keys_set:nn { tikzmkpd / config-A } { #1 } },
 }
% config-A={yshift={#1},end-yshift={#1,#2,#3},color={#1,#2,#3},distance={#1,#2,#3}}
\keys_define:nn { tikzmkpd / config-A }
 {
  yshift     .dim_set:N = \l_yshift_A_dim,
  yshift     .initial:n = 1.7ex,
  yshift-AC  .dim_set:N = \l_yshift_AC_dim,
  yshift-AC  .initial:n = 1.7ex,
  yshift-AD  .dim_set:N = \l_yshift_AD_dim,
  yshift-AD  .initial:n = 1.7ex,
  yshift-AE  .dim_set:N = \l_yshift_AE_dim,
  yshift-AE  .initial:n = 1.7ex,
  distan-AC  .dim_set:N = \l_distance_AC_dim,
  distan-AC  .initial:n = 1.00ex,
  distan-AD  .dim_set:N = \l_distance_AD_dim,
  distan-AD  .initial:n = 2.00ex,
  distan-AE  .dim_set:N = \l_distance_AE_dim,
  distan-AE  .initial:n = 3.00ex,
  colour-AC  .tl_set:N  = \l_color_AC_tl,
  colour-AC  .initial:n = red,
  colour-AD  .tl_set:N  = \l_color_AD_tl,
  colour-AD  .initial:n = green,
  colour-AE  .tl_set:N  = \l_color_AE_tl,
  colour-AE  .initial:n = blue,
  end-yshift .code:n    = \endyshift{#1},
%  yshiftall  .meta:n    = {end-yshift={#1}, yshift = #1,}, % not work
 }

\NewDocumentCommand { \endyshift } { m }
 {
  \int_case:nnF { \clist_count:n { #1 } }
   {
    {1}{\EndACshift {#1}} %
    {2}{\EndADshift {#1}} %
    {3}{\EndAEshift {#1}} %
   }{}
 }

\NewDocumentCommand{ \EndACshift }{m}
 {
  \EndAC#1
 }

\cs_new_protected:Npn \EndAC #1 
 {
  \keys_set:nn { tikzmkpd / config-A } 
   { 
    yshift-AC = #1,
   }
 }

\NewDocumentCommand{ \EndADshift }{>{\SplitArgument{1}{,}}m}
 {
  \EndAD#1
 }

\cs_new_protected:Npn \EndAD #1 #2
 {
  \keys_set:nn { tikzmkpd / config-A } 
   {
   yshift-AC = #1,  
   yshift-AD = #2,
   }
 }

\NewDocumentCommand{\EndAEshift}{>{\SplitArgument{2}{,}}m}
 {
  \EndAE#1
 }

\cs_new_protected:Npn \EndAE #1 #2 #3
 {
  \keys_set:nn { tikzmkpd / config-A } 
   { 
    yshift-AC = #1, 
    yshift-AD = #2,
    yshift-AE = #3,
   }
 }

% case 1: a(c+d)  A1(B1+B2)
\cs_new_protected:Npn \_case_one:n
 {
  \draw[->,color=\l_color_AC_tl] ([yshift=\l_yshift_A_dim]$(pic ~ cs\@colon\csuse{A1})$) 
   to[out=60,in=120,distance=\l_distance_AC_dim] 
   ([yshift=\l_yshift_AC_dim]$(pic ~ cs\@colon\csuse{B1})$);
  \draw[->,,color=\l_color_AD_tl] ([yshift=\l_yshift_A_dim]$(pic ~ cs\@colon\csuse{A1})$) 
   to[out=60,in=120,distance=\l_distance_AD_dim] 
   ([yshift=\l_yshift_AD_dim]$(pic ~ cs\@colon\csuse{B2})$);
 }
% case 2: a(c+d+e) A1(B1+B2+B3)
\cs_new_protected:Npn \_case_two:n
 {
  \_case_one:n
  \draw[->,color=\l_color_AE_tl] ([yshift=\l_yshift_A_dim]$(pic ~ cs\@colon\csuse{A1})$) 
   to[out=60,in=120,distance=\l_distance_AE_dim] 
   ([yshift=\l_yshift_AE_dim]$(pic ~ cs\@colon\csuse{B3})$);
 }
% case 3: (a+b)(c+d) (A1+A2)(B1+B2)
\cs_new_protected:Npn \_case_tree:n
 {
  \_case_one:n
  \draw[->] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{A2})$) 
   to[out=-60,in=-120,distance=1ex] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{B1})$);
  \draw[->] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{A2})$) 
   to[out=-60,in=-120,distance=2ex] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{B2})$);
 }
% case 4: (a+b)(c+d+e) (A1+A2)(B1+B2+B3)
\cs_new_protected:Npn \_case_fourth:n 
 {
  \_case_tree:n
  \draw[->,color=\l_color_AE_tl] ([yshift=1.7ex]$(pic ~ cs\@colon\csuse{A1})$) 
   to[out=60,in=120,distance=3.0ex] ([yshift=1.7ex]$(pic ~ cs\@colon\csuse{B3})$);
  \draw[->] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{A2})$) 
   to[out=-60,in=-120,distance=3ex] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{B3})$);
 }
% user command
\NewDocumentCommand{\tikzmkpd}{O{} m m}
 {
  \group_begin:
  \IfNoValueF { #1 } { \keys_set:nn { tikzmkpd }{ #1 } }
   \foreach \x [count=\n] in {#2} { \csxdef{A\n}{\x} }%  save in A<n>
   \foreach \y [count=\m] in {#3} { \csxdef{B\m}{\y} }%  save in B<m>
   \tikzset{>={Straight ~ Barb[length=1.5pt,round,bend]}}%
   \begin{tikzpicture}[overlay,remember ~ picture]%
    \bool_if:nTF { \int_compare_p:n { \clist_count:n { #2 } = 1 }   && 
                   \int_compare_p:n { \clist_count:n { #3 } = 2 } } { \_case_one:n }{  }
    \bool_if:nTF { \int_compare_p:n { \clist_count:n { #2 } = 1 }   && 
                   \int_compare_p:n { \clist_count:n { #3 } = 3 } } { \_case_two:n }{  }
    \bool_if:nTF { \int_compare_p:n { \clist_count:n { #2 } = 2 }   && 
                   \int_compare_p:n { \clist_count:n { #3 } = 2 } } { \_case_tree:n }{  }
    \bool_if:nTF { \int_compare_p:n { \clist_count:n { #2 } = 2 }   && 
                   \int_compare_p:n { \clist_count:n { #3 } = 3 } } { \_case_fourth:n }{  }
   \end{tikzpicture} 
  \group_end:
 }
\ExplSyntaxOff
\makeatother
\begin{document}
\section{case 1}

$\TkM{A}{2a^2}(\TkM{B}{b}+\TkM{D}{3q})$%
\tikzmkpd[config-A={end-yshift={2ex,2ex,2ex}}]{A}{B,D}

\section{case 2}

$\TkM{a1}{2p}\left(\TkM{b1}{3q}+\TkM{c1}{4r}+\TkM{d1}{1}\right)$
\tikzmkpd{a1}{b1, c1, d1}

\section{case 3}

$(\TkM{X1}{2a^2}+\TkM{X2}{n})(\TkM{Y1}{b}+\TkM{Y2}{3q})$%
\tikzmkpd{X1,X2}{Y1,Y2}

\section{case 4}

$(\TkM{a}{x}-\TkM{b}{2})(\TkM{c}{x^2}+\TkM{d}{2x}+\TkM{e}{4})$
\tikzmkpd{a,b}{c, d, e}

\end{document}

And an example of the output that produces:

output that produces

Any help is appreciated.

PS: The idea of the command is NOT to write the tikzpicture environment directly into the document.

5
  • 1
    As far as I understand, the all that TikZ code isn't really relevant to the problem. Could you boil down the example to only the code that is necessary to understand the parameter parsing problem? – siracusa Jun 8 '19 at 9:51
  • 1
    You can probably pass whatever you need as key={some,thing} by defining key with l3keys' .code:n or .tl_set:N, but then for the passed-in contents, you'll have to do the parsing work yourself (unless you delegate to l3keys again...). Wouldn't it be more natural to use hierarchical keys in the style mykey/mysubkey=... (see Sub-dividing keys in the l3keys documentation)? – frougon Jun 8 '19 at 10:33
  • Unlike the others I feel you should drop xparse and etoolbox. That way you avoid mixing pgf keys with other keys. Of course, I do not fully understand what's going on here but the splitting of the arguments can be achieved with the /.list key handler. – user121799 Jun 8 '19 at 16:01
  • Unfortunately I can't reduce the example code, I think the question would be misunderstood (writing using expl3 is always extensive). Going straight to pgfkeys is not an option for me, I don't know it well and it would cause me more problems (I'm new to tikz), subdividing the keys seems a good idea to me, I'll try. – Pablo González L Jun 8 '19 at 18:18
  • Reviewing well in the forum I found the answer (tex.stackexchange.com/a/423119/7832) , to the moderators I ask to leave this question as closed (duplicate) – Pablo González L Jun 9 '19 at 6:42