0

After a good time searching the forum I gave up (I thought I could do it by myself), I have a problem passing a key from l3keys (separated by commas) to tikz. I've created a command that accepts comma-separated arguments and key=val that encapsulates tikz(mark) to generate school examples like these:

output

The command is as follows

\tikzmkpd[config-A={yshift-A={#1}, end-yshift={#1,#2,#3},
                    color={#1,#2,#3}, yshift-A={#1}, distance={#1,#2,#3}
                    angle-star={#1,#2,#3}, angle-end={#1,#2,#3},
                    }]{A,B}{C,D,E}

Keys that accept arguments separated by comma are all passed through the same function and everything works OK, with the exception of color key, when activated I get the error:

! Package PGF Math Error: Unknown function `gray' (in 'gray').

See the PGF Math package documentation for explanation.
Type H <return> for immediate help.
 ...                                              

l.126 ...,yshift-A=15pt,color={gray,red}}]{A}{B,D}

The function does not delimit it correctly and absorbs more than the account, passing the rest of the arguments to tikz, the strange thing is that if I define the same code within \keys_define:nn it works correctly:

\keys_define:nn { tikzmkpd / config-A }
 {
  color .code:n = {
                   \clist_set:Nn \l_tmpa_clist { #1 }
                   \bool_if:nT { \int_compare_p:n { \clist_count:n { #1 } = 1 } }
                    {
                     \tl_set:Nx \l_colour_AC_tl { \clist_item:Nn \l_tmpa_clist {1} }
                    }
                   \bool_if:nT { \int_compare_p:n { \clist_count:n { #1 } = 2 } }
                    {
                     \tl_set:Nx \l_colour_AC_tl { \clist_item:Nn \l_tmpa_clist {1} }
                     \tl_set:Nx \l_colour_AD_tl { \clist_item:Nn \l_tmpa_clist {2} }
                    }
                   \bool_if:nT { \int_compare_p:n { \clist_count:n { #1 } = 3 } }
                    {
                     \tl_set:Nx \l_colour_AC_tl { \clist_item:Nn \l_tmpa_clist {1} }
                     \tl_set:Nx \l_colour_AD_tl { \clist_item:Nn \l_tmpa_clist {2} }
                     \tl_set:Nx \l_colour_AE_tl { \clist_item:Nn \l_tmpa_clist {3} }
                    }
                  },
 }

This is the code I own, I have minimized it as much as possible so that it is understood (in my original document there is also tikzmkpd / config-B)

\documentclass{article}
\usepackage{xparse,tikz,etoolbox}
\usetikzlibrary{arrows.meta,bending,calc,tikzmark}
\setlength{\parindent}{0pt} % just for the example
\pagestyle{empty}
\newcommand*{\TkM}[2]{\tikzmarknode{#1}{#2}} % short :)
\makeatletter
\def\@colon{:} % active : for expl3
\ExplSyntaxOn
\cs_new_protected:Npn \__define_numeric_keys:nnnnnnnnn #1#2#3#4#5#6#7#8#9
 {
  \keys_define:nn { tkmdraw/config-#1 }
   {
    yshift-#1C .tl_set:c = {l_yshift_#1C_tl}, yshift-#1C .initial:n = #2,
    yshift-#1D .tl_set:c = {l_yshift_#1D_tl}, yshift-#1D .initial:n = #3,
    yshift-#1E .tl_set:c = {l_yshift_#1E_tl}, yshift-#1E .initial:n = #4,
    distan-#1C .tl_set:c = {l_distan_#1C_tl}, distan-#1C .initial:n = #5,
    distan-#1D .tl_set:c = {l_distan_#1D_tl}, distan-#1D .initial:n = #6,
    distan-#1E .tl_set:c = {l_distan_#1E_tl}, distan-#1E .initial:n = #7,
    angles-#1C .tl_set:c = {l_angles_#1C_tl}, angles-#1C .initial:n = #8,
    angles-#1D .tl_set:c = {l_angles_#1D_tl}, angles-#1D .initial:n = #8,
    angles-#1E .tl_set:c = {l_angles_#1E_tl}, angles-#1E .initial:n = #8,
    anglee-#1C .tl_set:c = {l_anglee_#1C_tl}, anglee-#1C .initial:n = #9,
    anglee-#1D .tl_set:c = {l_anglee_#1D_tl}, anglee-#1D .initial:n = #9,
    anglee-#1E .tl_set:c = {l_anglee_#1E_tl}, anglee-#1E .initial:n = #9,
   }
}
\__define_numeric_keys:nnnnnnnnn{A}{1.7ex}{1.7ex}{1.7ex}{1ex}{2ex}{3ex}{60}{120}

\cs_new_protected:Npn \__define_colour_keys:nnnn #1#2#3#4
 {
  \keys_define:nn { tkmdraw/config-#1 }
   {
    colour-#1C .tl_set:c = {l_colour_#1C_tl}, colour-#1C .initial:n = #2,
    colour-#1D .tl_set:c = {l_colour_#1D_tl}, colour-#1D .initial:n = #3,
    colour-#1E .tl_set:c = {l_colour_#1E_tl}, colour-#1E .initial:n = #4,
   }
 }
\__define_colour_keys:nnnn{A}{red}{green}{blue}

\keys_define:nn { tikzmkpd / config-A }
 {
  yshift-A   .tl_set:c  = {l_yshift_A_tl},%
  yshift-A   .initial:n = 1.7ex,
  end-yshift .code:n    = \__testkeys:nnn{A}{yshift}{#1},
  angle-star .code:n    = \__testkeys:nnn{A}{angles}{#1},
  angle-end  .code:n    = \__testkeys:nnn{A}{anglee}{#1},
  distance   .code:n    = \__testkeys:nnn{A}{distan}{#1},
  color      .code:n    = \__testkeys:nnn{A}{colour}{#1},
 }

\tl_new:N \l_tmpc_tl
\cs_new_protected:Npn \__testkeys:nnn #1#2#3
 {
  \clist_set:Nn \l_tmpa_clist { #3 }
  \bool_if:nT { \int_compare_p:n { \clist_count:n { #3 } = 1 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \keys_set:nn { tkmdraw/config-#1 }
     { #2-#1C= \l_tmpa_tl, }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n { #3 } = 2 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \keys_set:nn { tkmdraw/config-#1 }
     { #2-#1C= \l_tmpa_tl, #2-#1D= \l_tmpb_tl, }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n { #3 } = 3 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \tl_set:Nx \l_tmpc_tl { \clist_item:Nn \l_tmpa_clist {3} }
    \keys_set:nn { tkmdraw/config-#1 }
    { #2-#1C= \l_tmpa_tl, #2-#1D= \l_tmpb_tl, #2-#1E= \l_tmpc_tl,}
   }
 }

% case 1: a(c+d)  A1(B1+B2)
\cs_new_protected:Npn \_case_one:n
 {
  \draw[->,\use:c{l_colour_AC_tl}]
  ([yshift=\use:c{l_yshift_A_tl}]$(pic ~ cs\@colon\csuse{A1})$)
   to[out=\use:c{l_angles_AC_tl},in=\use:c{l_anglee_AC_tl},distance=\use:c{l_distan_AC_tl}]
   ([yshift=\use:c{l_yshift_AC_tl}]$(pic ~ cs\@colon\csuse{B1})$);
  \draw[->,\use:c{l_colour_AD_tl}] %,
  ([yshift=\use:c{l_yshift_A_tl}]$(pic ~ cs\@colon\csuse{A1})$)
   to[out=\use:c{l_angles_AD_tl},in=\use:c{l_anglee_AD_tl},distance=\use:c{l_distan_AD_tl}]
   ([yshift=\use:c{l_yshift_AD_tl}]$(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[->,\use:c{l_colour_AE_tl}]
  ([yshift=\use:c{l_yshift_A_tl}]$(pic ~ cs\@colon\csuse{A1})$)
   to[out=\use:c{l_angles_AE_tl},in=\use:c{l_anglee_AE_tl},distance=\use:c{l_distan_AE_tl}]
   ([yshift=\use:c{l_yshift_AE_tl}]$(pic ~ cs\@colon\csuse{B3})$);
 }

\keys_define:nn { tikzmkpd }
 {
  config-A .code:n = { \keys_set:nn { tikzmkpd / config-A } { #1 } },
 }

\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:nT { \int_compare_p:n { \clist_count:n { #2 } = 1 }   &&
                   \int_compare_p:n { \clist_count:n { #3 } = 2 } } { \_case_one:n }
     \bool_if:nT { \int_compare_p:n { \clist_count:n { #2 } = 1 }   &&
                   \int_compare_p:n { \clist_count:n { #3 } = 3 } } { \_case_two: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={15pt,15pt},yshift-A=15pt,color={gray,red}}]{A}{B,D}
\section{case 2}
$\TkM{a1}{2p}\left(\TkM{b1}{3q}+\TkM{c1}{4r}+\TkM{d1}{1}\right)$
\tikzmkpd[config-A={color={gray,gray,gray},end-yshift={5pt,5pt,5pt}}]{a1}{b1,c1,d1}
\end{document}

Any idea how to solve it?....(my idea is to try to repeat similar code as little as possible)

Greetings.

1 Answers1

2

This is not a minimal example, there are ten different questions here! The code given below works. Following the request in your comment, I have made more changes than strictly necessary in order to better adhere to LaTeX3 coding conventions (to the best of my knowledge).

Note that the _private_ naming scheme in \keys_define:nn { tikzmkpd/_private_/config-#1 } is an invention of mine; I don't know if there is a standard naming scheme for such situations. I did this way because I believe the keys defined under this hierarchy are an implementation detail of your macros and shouldn't be used directly by “users”.

LaTeX3 coding conventions are presented in expl3.pdf, l3styleguide.pdf and at the beginning of interface3.pdf. The indentation style suggested there is a bit different from yours (in particular, it uses a basic offset of two spaces). But the code below is already fairly readable, so I let you decide for yourself whether you want to strictly follow the indentation guidelines too.

For functions, I used:

  • a \__tikzmkpd_ prefix for private ones;

  • a \tikzmkpd_ prefix for public ones (only \tikzmkpd:nnn, which I added as the programming-layer backend function corresponding to the user-layer \tikzmkpd command you had already defined).

This way, it is very easy to turn all this code into a package that will play well with the rest of the LaTeX ecosystem. Your \_case_one:n and \_case_two:n functions had an incorrect name for two reasons:

  • the prefix;
  • the :n suffix which indicated they were supposed to take an argument, whereas they actually don't.

I renamed these two functions to \__tikzmkpd_case_one: and \__tikzmkpd_case_two:, respectively.

\documentclass{article}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, bending, calc, tikzmark}

\ExplSyntaxOn

\cs_new_protected:Npn \__tikzmkpd_define_numeric_keys:nnnnnnnnn
                      #1#2#3#4#5#6#7#8#9
 {
  \keys_define:nn { tikzmkpd/_private_/config-#1 }
   {
    yshift-#1C .tl_set:c = { l_yshift_#1C_tl }, yshift-#1C .initial:n = {#2},
    yshift-#1D .tl_set:c = { l_yshift_#1D_tl }, yshift-#1D .initial:n = {#3},
    yshift-#1E .tl_set:c = { l_yshift_#1E_tl }, yshift-#1E .initial:n = {#4},
    distan-#1C .tl_set:c = { l_distan_#1C_tl }, distan-#1C .initial:n = {#5},
    distan-#1D .tl_set:c = { l_distan_#1D_tl }, distan-#1D .initial:n = {#6},
    distan-#1E .tl_set:c = { l_distan_#1E_tl }, distan-#1E .initial:n = {#7},
    angles-#1C .tl_set:c = { l_angles_#1C_tl }, angles-#1C .initial:n = {#8},
    angles-#1D .tl_set:c = { l_angles_#1D_tl }, angles-#1D .initial:n = {#8},
    angles-#1E .tl_set:c = { l_angles_#1E_tl }, angles-#1E .initial:n = {#8},
    anglee-#1C .tl_set:c = { l_anglee_#1C_tl }, anglee-#1C .initial:n = {#9},
    anglee-#1D .tl_set:c = { l_anglee_#1D_tl }, anglee-#1D .initial:n = {#9},
    anglee-#1E .tl_set:c = { l_anglee_#1E_tl }, anglee-#1E .initial:n = {#9},
   }
}

\__tikzmkpd_define_numeric_keys:nnnnnnnnn
 { A } { 1.7ex } { 1.7ex } { 1.7ex } { 1ex } { 2ex } { 3ex } { 60 } { 120 }
\__tikzmkpd_define_numeric_keys:nnnnnnnnn
 { B }{ -0.5ex }{ -0.5ex }{ -0.5ex } { 1ex } { 2ex } { 3ex } {-60 } {-120 }

\cs_new_protected:Npn \__tikzmkpd_define_colour_keys:nnnn #1#2#3#4
 {
  \keys_define:nn { tikzmkpd/_private_/config-#1 }
   {
    colour-#1C .tl_set:c = { l_colour_#1C_tl }, colour-#1C .initial:n = {#2},
    colour-#1D .tl_set:c = { l_colour_#1D_tl }, colour-#1D .initial:n = {#3},
    colour-#1E .tl_set:c = { l_colour_#1E_tl }, colour-#1E .initial:n = {#4},
   }
 }

\__tikzmkpd_define_colour_keys:nnnn { A } { red } { green } { blue }
\__tikzmkpd_define_colour_keys:nnnn { B } { red } { green } { blue }

\keys_define:nn { tikzmkpd / config-A }
 {
  yshift-A   .tl_set:c  = { l_yshift_A_tl },
  yshift-A   .initial:n = { 1.7ex },
  end-yshift .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { yshift } {#1} },
  angle-star .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { angles } {#1} },
  angle-end  .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { anglee } {#1} },
  distance   .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { distan } {#1} },
  color      .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { colour } {#1} },
  raise      .meta:n    = { yshift-A = {#1}, end-yshift = {#1,#1,#1} },
 }

\keys_define:nn { tikzmkpd / config-B }
 {
  yshift-B   .tl_set:c  = { l_yshift_B_tl },
  yshift-B   .initial:n = { -0.5ex },
  end-yshift .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { yshift } {#1} },
  angle-star .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { angles } {#1} },
  angle-end  .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { anglee } {#1} },
  distance   .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { distan } {#1} },
  color      .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { colour } {#1} },
  raise      .meta:n    = { yshift-B = {#1}, end-yshift = {#1,#1,#1} },
 }

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { \exp_not:n { #1-#2C = {#3} } }

\cs_new:Npn \__tikzmkpd_subs_ii:nnnn #1#2#3#4
 { \exp_not:n { #1-#2C= {#3}, #1-#2D= {#4} } }

\cs_new:Npn \__tikzmkpd_subs_iii:nnnnn #1#2#3#4#5
 { \exp_not:n { #1-#2C= {#3}, #1-#2D= {#4}, #1-#2E= {#5} } }

\cs_generate_variant:Nn \__tikzmkpd_subs_i:nnn { nnV }
\cs_generate_variant:Nn \__tikzmkpd_subs_ii:nnnn { nnVV }
\cs_generate_variant:Nn \__tikzmkpd_subs_iii:nnnnn { nnVVV }
\cs_generate_variant:Nn \keys_set:nn { nx }

\tl_new:N \l__tikzmkpd_tmp_tl

\cs_new_protected:Npn \__tikzmkpd_set_subkeys:nnn #1#2#3
 {
  \clist_set:Nn \l_tmpa_clist {#3}
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 1 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_i:nnV {#2} {#1} \l_tmpa_tl }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 2 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_ii:nnVV {#2} {#1} \l_tmpa_tl \l_tmpb_tl }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 3 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \tl_set:Nx \l__tikzmkpd_tmp_tl { \clist_item:Nn \l_tmpa_clist {3} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_iii:nnVVV {#2} {#1} \l_tmpa_tl \l_tmpb_tl
                                  \l__tikzmkpd_tmp_tl }
   }
 }

\seq_new:N \l__tikzmkpd_mycmd_seq

\cs_new_protected:Npn \__tikzmkpd_store_node_names:nn #1#2
  {
    \seq_set_from_clist:Nn \l__tikzmkpd_mycmd_seq {#2}
    \seq_indexed_map_inline:Nn \l__tikzmkpd_mycmd_seq
      {
        \tl_clear_new:c { l__tikzmkpd_nodename_#1##1_tl }
        \tl_set:cn { l__tikzmkpd_nodename_#1##1_tl } {##2}
      }
  }

% case 1: a(c+d)  A1(B1+B2)
\cs_new_protected:Npn \__tikzmkpd_case_one:
 {
  \draw[->, \use:c{l_colour_AC_tl}]
   ($(\tl_use:c { l__tikzmkpd_nodename_A1_tl }) + (0, \use:c{l_yshift_A_tl})$)
   to[out=\use:c{l_angles_AC_tl}, in=\use:c{l_anglee_AC_tl},
      distance=\use:c{l_distan_AC_tl}]
   ([yshift=\use:c{l_yshift_AC_tl}] \tl_use:c { l__tikzmkpd_nodename_B1_tl });
  \draw[->,\use:c{l_colour_AD_tl}]
   ($(\tl_use:c { l__tikzmkpd_nodename_A1_tl }) + (0, \use:c{l_yshift_A_tl})$)
   to[out=\use:c{l_angles_AD_tl}, in=\use:c{l_anglee_AD_tl},
      distance=\use:c{l_distan_AD_tl}]
   ([yshift=\use:c{l_yshift_AD_tl}] \tl_use:c { l__tikzmkpd_nodename_B2_tl });
 }

% case 2: a(c+d+e)  A1(B1+B2+B3)
\cs_new_protected:Npn \__tikzmkpd_case_two:
 {
  \__tikzmkpd_case_one:
  \draw[->, \use:c{l_colour_AE_tl}]
   ($(\tl_use:c { l__tikzmkpd_nodename_A1_tl }) + (0, \use:c{l_yshift_A_tl})$)
   to[out=\use:c{l_angles_AE_tl}, in=\use:c{l_anglee_AE_tl},
      distance=\use:c{l_distan_AE_tl}]
   ([yshift=\use:c{l_yshift_AE_tl}] \tl_use:c { l__tikzmkpd_nodename_B3_tl });
 }

\keys_define:nn { tikzmkpd }
 {
  config-A .code:n = { \keys_set:nn { tikzmkpd / config-A } {#1} },
  config-B .code:n = { \keys_set:nn { tikzmkpd / config-B } {#1} },
 }

\cs_new_protected:Npn \tikzmkpd:nnn #1#2#3
 {
  \group_begin:
  \keys_set:nn { tikzmkpd } {#1}
  \__tikzmkpd_store_node_names:nn {A} {#2}
  \__tikzmkpd_store_node_names:nn {B} {#3}
  \tikzset{>={Straight ~ Barb[length=1.5pt,round,bend]}}

  \begin{tikzpicture}[overlay, remember ~ picture]
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 1 } &&
          \int_compare_p:n { \clist_count:n {#3} = 2 }
        }
        { \__tikzmkpd_case_one: }
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 1 } &&
          \int_compare_p:n { \clist_count:n {#3} = 3 }
        }
        { \__tikzmkpd_case_two: }
  \end{tikzpicture}
  \group_end:
 }

\NewDocumentCommand \tikzmkpd { O{} m m }
 {
  \tikzmkpd:nnn {#1} {#2} {#3}
 }

\ExplSyntaxOff

\newcommand*{\TkM}[2]{\tikzmarknode{#1}{#2}} % short :)
\setlength{\parindent}{0pt} % just for the example
\pagestyle{empty}

\begin{document}

\section{case 1}

$\TkM{A}{2a^2}(\TkM{B}{b}+\TkM{D}{3q})$%
\tikzmkpd[config-A={end-yshift={8pt,9pt}, yshift-A=8pt, color={gray,red}}]%
         {A}{B, D}

\section{case 2}

$\TkM{a1}{2p}\left(\TkM{b1}{3q}+\TkM{c1}{4r}+\TkM{d1}{1}\right)$%
\tikzmkpd[config-A={color={gray,gray,gray}, end-yshift={5pt,5pt,5pt}}]%
         {a1}{b1, c1, d1}

\end{document}

I tested the preceding code, it gives the screenshot below. At the end of the post, you'll find a variant with improvements from Pablo that deals with more cases of the distributive property of multiplication (cases “one” to “four” instead of just “one” and “two”). However, I can't completely validate that version myself, as it apparently relies on recent bug fixes or features of PGF/TikZ—too recent for the version I have here, which is 2019/02/02 v3.1.1.

Screenshot

I believe you can make progress regarding your debugging methodology: had you tested the output of every single function of yours, you'd have done:

\tl_show:N \l_yshift_AC_tl
\tl_show:N \l_yshift_AD_tl
\tl_show:N \l_colour_AC_tl
\tl_show:N \l_colour_AD_tl

right after the \keys_set:nn { tikzmkpd } {#1} call, which would have shown that your \__testkeys:nnn (now called \__tikzmkpd_set_subkeys:nnn) was not working properly. The main problem there was that you were passing single tokens such as \l_tmpa_tl in arguments where you actually wanted their value (something equivalent to the result of expanding \l_tmpa_tl or \tl_use:N \l_tmpa_tl; see how I did). Also, more bracing was desirable in some places in case some parameter values were to contain commas. For instance, this:

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { #1-#2C = {#3}, }

is preferable to this:

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { #1-#2C= #3, }

in case #3 were to contain commas. In fact, unless you plan to use macros in #1, #2 or #3, the following is maybe preferable in order to avoid unwanted expansions (since we later use it inside an x argument), which is what I did above:

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { \exp_not:n { #1-#2C = {#3} } }

The other problem that prevented the code from working is that for nodes created with \tikzmarknode, the pic cs syntax you used doesn't work—at least in my setup. The good news is, this can be simplified a lot (see my changes to \_case_one:n and \_case_two:n [now renamed to \__tikzmkpd_case_one: and \__tikzmkpd_case_two:]).

Your question is so complex with all the parameters, the TikZ code mixed up with the l3keys code, that I fear no one is going to reuse my answer. :-( I don't criticize the use of l3keys for TikZ work (for one, I'm much more comfortable myself with l3keys than with pgfkeys), but in the interest of other users of this site, I think you should have isolated the parsing problem from the TikZ issue—as people already told you.

Your LaTeX coding skills seem good; my main advice would be:

  • Read a bit more documentation, in particular on the LaTeX3 naming conventions, expansion and variants (see the first chapters of interface3.pdf).

  • Whenever you face again a problem like that, use \tracingmacros=1 \tracingonline=1\relax, and if this isn't enough to understand what is happening, print the value of every “thing” that your functions prepare1 (for each of your functions). For example, before even trying to typeset the tikzpicture, print all options and all parameters that you are going to use therein. This method would have helped you find the errors in your \__testkeys:nnn (now renamed to \__tikzmkpd_set_subkeys:nnn).

As promised, here is the expanded code with additions from Pablo:

\documentclass{article}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, bending, calc, tikzmark}

\ExplSyntaxOn

\cs_new_protected:Npn \__tikzmkpd_define_numeric_keys:nnnnnnnnn
                      #1#2#3#4#5#6#7#8#9
 {
  \keys_define:nn { tikzmkpd/_private_/config-#1 }
   {
    yshift-#1C .tl_set:c = { l_yshift_#1C_tl }, yshift-#1C .initial:n = {#2},
    yshift-#1D .tl_set:c = { l_yshift_#1D_tl }, yshift-#1D .initial:n = {#3},
    yshift-#1E .tl_set:c = { l_yshift_#1E_tl }, yshift-#1E .initial:n = {#4},
    distan-#1C .tl_set:c = { l_distan_#1C_tl }, distan-#1C .initial:n = {#5},
    distan-#1D .tl_set:c = { l_distan_#1D_tl }, distan-#1D .initial:n = {#6},
    distan-#1E .tl_set:c = { l_distan_#1E_tl }, distan-#1E .initial:n = {#7},
    angles-#1C .tl_set:c = { l_angles_#1C_tl }, angles-#1C .initial:n = {#8},
    angles-#1D .tl_set:c = { l_angles_#1D_tl }, angles-#1D .initial:n = {#8},
    angles-#1E .tl_set:c = { l_angles_#1E_tl }, angles-#1E .initial:n = {#8},
    anglee-#1C .tl_set:c = { l_anglee_#1C_tl }, anglee-#1C .initial:n = {#9},
    anglee-#1D .tl_set:c = { l_anglee_#1D_tl }, anglee-#1D .initial:n = {#9},
    anglee-#1E .tl_set:c = { l_anglee_#1E_tl }, anglee-#1E .initial:n = {#9},
   }
}

\__tikzmkpd_define_numeric_keys:nnnnnnnnn
 { A } { 1.7ex } { 1.7ex } { 1.7ex } { 1ex } { 2ex } { 3ex } { 60 } { 120 }
\__tikzmkpd_define_numeric_keys:nnnnnnnnn
 { B }{ -0.5ex }{ -0.5ex }{ -0.5ex } { 1ex } { 2ex } { 3ex } {-60 } {-120 }

\cs_new_protected:Npn \__tikzmkpd_define_colour_keys:nnnn #1#2#3#4
 {
  \keys_define:nn { tikzmkpd/_private_/config-#1 }
   {
    colour-#1C .tl_set:c = { l_colour_#1C_tl }, colour-#1C .initial:n = {#2},
    colour-#1D .tl_set:c = { l_colour_#1D_tl }, colour-#1D .initial:n = {#3},
    colour-#1E .tl_set:c = { l_colour_#1E_tl }, colour-#1E .initial:n = {#4},
   }
 }

\__tikzmkpd_define_colour_keys:nnnn { A } { red } { green } { blue }
\__tikzmkpd_define_colour_keys:nnnn { B } { red } { green } { blue }

\keys_define:nn { tikzmkpd / config-A }
 {
  yshift-A   .tl_set:c  = { l_yshift_A_tl },
  yshift-A   .initial:n = { 1.7ex },
  end-yshift .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { yshift } {#1} },
  angle-star .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { angles } {#1} },
  angle-end  .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { anglee } {#1} },
  distance   .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { distan } {#1} },
  color      .code:n    = { \__tikzmkpd_set_subkeys:nnn { A } { colour } {#1} },
  raise      .meta:n    = { yshift-A = {#1}, end-yshift = {#1,#1,#1} },
 }

\keys_define:nn { tikzmkpd / config-B }
 {
  yshift-B   .tl_set:c  = { l_yshift_B_tl },
  yshift-B   .initial:n = { -0.5ex },
  end-yshift .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { yshift } {#1} },
  angle-star .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { angles } {#1} },
  angle-end  .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { anglee } {#1} },
  distance   .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { distan } {#1} },
  color      .code:n    = { \__tikzmkpd_set_subkeys:nnn { B } { colour } {#1} },
  raise      .meta:n    = { yshift-B = {#1}, end-yshift = {#1,#1,#1} },
 }

\cs_new:Npn \__tikzmkpd_subs_i:nnn #1#2#3
 { \exp_not:n { #1-#2C = {#3} } }

\cs_new:Npn \__tikzmkpd_subs_ii:nnnn #1#2#3#4
 { \exp_not:n { #1-#2C= {#3}, #1-#2D= {#4} } }

\cs_new:Npn \__tikzmkpd_subs_iii:nnnnn #1#2#3#4#5
 { \exp_not:n { #1-#2C= {#3}, #1-#2D= {#4}, #1-#2E= {#5} } }

\cs_generate_variant:Nn \__tikzmkpd_subs_i:nnn { nnV }
\cs_generate_variant:Nn \__tikzmkpd_subs_ii:nnnn { nnVV }
\cs_generate_variant:Nn \__tikzmkpd_subs_iii:nnnnn { nnVVV }
\cs_generate_variant:Nn \keys_set:nn { nx }

\tl_new:N \l__tikzmkpd_tmp_tl

\cs_new_protected:Npn \__tikzmkpd_set_subkeys:nnn #1#2#3
 {
  \clist_set:Nn \l_tmpa_clist {#3}
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 1 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_i:nnV {#2} {#1} \l_tmpa_tl }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 2 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_ii:nnVV {#2} {#1} \l_tmpa_tl \l_tmpb_tl }
   }
  \bool_if:nT { \int_compare_p:n { \clist_count:n {#3} = 3 } }
   {
    \tl_set:Nx \l_tmpa_tl { \clist_item:Nn \l_tmpa_clist {1} }
    \tl_set:Nx \l_tmpb_tl { \clist_item:Nn \l_tmpa_clist {2} }
    \tl_set:Nx \l__tikzmkpd_tmp_tl { \clist_item:Nn \l_tmpa_clist {3} }
    \keys_set:nx
     { tikzmkpd/_private_/config-#1 }
     { \__tikzmkpd_subs_iii:nnVVV {#2} {#1} \l_tmpa_tl \l_tmpb_tl
                                  \l__tikzmkpd_tmp_tl }
   }
 }

\seq_new:N \l__tikzmkpd_mycmd_seq

\cs_new_protected:Npn \__tikzmkpd_store_node_names:nn #1#2
  {
    \seq_set_from_clist:Nn \l__tikzmkpd_mycmd_seq {#2}
    \seq_indexed_map_inline:Nn \l__tikzmkpd_mycmd_seq
      {
        \tl_clear_new:c { l__tikzmkpd_nodename_#1##1_tl }
        \tl_set:cn { l__tikzmkpd_nodename_#1##1_tl } {##2}
      }
  }

% case 1: a(c+d)  A1(B1+B2)
\cs_new_protected:Npn \__tikzmkpd_case_one:
 {
  \draw[->, \tl_use:c{l_colour_AC_tl}]
   ([yshift=\tl_use:c{l_yshift_A_tl}]$(pic ~ cs  \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_A1_tl})$)
   to[out=\tl_use:c{l_angles_AC_tl}, in=\tl_use:c{l_anglee_AC_tl},
      distance=\tl_use:c{l_distan_AC_tl}]
   ([yshift=\tl_use:c{l_yshift_AC_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B1_tl})$);
  \draw[->,\tl_use:c{l_colour_AD_tl}]
   ([yshift=\tl_use:c{l_yshift_A_tl}]$(pic ~ cs  \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_A1_tl})$)
   to[out=\tl_use:c{l_angles_AD_tl}, in=\tl_use:c{l_anglee_AD_tl},
      distance=\tl_use:c{l_distan_AD_tl}]
   ([yshift=\tl_use:c{l_yshift_AD_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B2_tl})$);
 }

% case 2: a(c+d+e)  A1(B1+B2+B3)
\cs_new_protected:Npn \__tikzmkpd_case_two:
 {
  \__tikzmkpd_case_one:
  \draw[->, \tl_use:c{l_colour_AE_tl}]
   ([yshift=\tl_use:c{l_yshift_A_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_A1_tl})$)
   to[out=\tl_use:c{l_angles_AE_tl}, in=\tl_use:c{l_anglee_AE_tl},
      distance=\tl_use:c{l_distan_AE_tl}]
   ([yshift=\tl_use:c{l_yshift_AE_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B3_tl})$);
 }

% case 3: (a+b)(c+d) (A1+A2)(B1+B2)
\cs_new_protected:Npn \__tikzmkpd_case_three:
 {
  \__tikzmkpd_case_one:
  \draw[->,\tl_use:c{l_colour_BC_tl}]
   ([yshift=\tl_use:c{l_yshift_B_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_A2_tl})$)
   to[out=\tl_use:c{l_angles_BC_tl},in=\tl_use:c{l_anglee_BC_tl},
      distance=\tl_use:c{l_distan_BC_tl}]
   ([yshift=\tl_use:c{l_yshift_BC_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B1_tl})$);
  \draw[->,\tl_use:c{l_colour_BD_tl}]
   ([yshift=\tl_use:c{l_yshift_B_tl}]$(pic ~ cs \tl_to_str:n { : }  \tl_use:c { l__tikzmkpd_nodename_A2_tl})$)
   to[out=\tl_use:c{l_angles_BD_tl},in=\tl_use:c{l_anglee_BD_tl},
      distance=\tl_use:c{l_distan_BD_tl}]
   ([yshift=\tl_use:c{l_yshift_BD_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B2_tl})$);
 }

% case 4: (a+b)(c+d+e) (A1+A2)(B1+B2+B3)
\cs_new_protected:Npn \__tikzmkpd_case_four:
 {
  \__tikzmkpd_case_three:
  \draw[->,\tl_use:c{l_colour_AE_tl}]
   ([yshift=\tl_use:c{l_yshift_A_tl}]$(pic ~ cs \tl_to_str:n { : }  \tl_use:c { l__tikzmkpd_nodename_A1_tl})$)
   to[out=\tl_use:c{l_angles_AE_tl},in=\tl_use:c{l_anglee_AE_tl},
      distance=\tl_use:c{l_distan_AE_tl}]
   ([yshift=\tl_use:c{l_yshift_AE_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B3_tl})$);
  \draw[->,\tl_use:c{l_colour_BE_tl}]
   ([yshift=\tl_use:c{l_yshift_B_tl}]$(pic ~ cs \tl_to_str:n { : }  \tl_use:c { l__tikzmkpd_nodename_A2_tl})$)
   to[out=\tl_use:c{l_angles_BE_tl},in=\tl_use:c{l_anglee_BE_tl},
      distance=\tl_use:c{l_distan_BE_tl}]
   ([yshift=\tl_use:c{l_yshift_BE_tl}]$(pic ~ cs \tl_to_str:n { : } \tl_use:c { l__tikzmkpd_nodename_B3_tl})$);
 }

\keys_define:nn { tikzmkpd }
 {
  config-A .code:n = { \keys_set:nn { tikzmkpd / config-A } {#1} },
  config-B .code:n = { \keys_set:nn { tikzmkpd / config-B } {#1} },
 }

\cs_new_protected:Npn \tikzmkpd:nnn #1#2#3
 {
  \group_begin:
  \keys_set:nn { tikzmkpd } {#1}
  \__tikzmkpd_store_node_names:nn {A} {#2}
  \__tikzmkpd_store_node_names:nn {B} {#3}
  \tikzset{>={Straight ~ Barb[length=1.5pt,round,bend]}}

  \begin{tikzpicture}[overlay, remember ~ picture]
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 1 } &&
          \int_compare_p:n { \clist_count:n {#3} = 2 }
        }
        { \__tikzmkpd_case_one: }
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 1 } &&
          \int_compare_p:n { \clist_count:n {#3} = 3 }
        }
        { \__tikzmkpd_case_two: }
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 2 } &&
          \int_compare_p:n { \clist_count:n {#3} = 2 }
        }
        { \__tikzmkpd_case_three: }
      \bool_if:nT
        {
          \int_compare_p:n { \clist_count:n {#2} = 2 }   &&
          \int_compare_p:n { \clist_count:n {#3} = 3 }
        }
        { \__tikzmkpd_case_four: }
  \end{tikzpicture}
  \group_end:
 }

\NewDocumentCommand \tikzmkpd { O{} m m }
 {
  \tikzmkpd:nnn {#1} {#2} {#3}
 }

\ExplSyntaxOff

\newcommand*{\TkM}[2]{\tikzmarknode{#1}{#2}} % short :)
\setlength{\parindent}{0pt} % just for the example
\pagestyle{empty}

\begin{document}

\section{case 1}

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

\section{case 2}

$\TkM{a1}{2p}\left(\TkM{b1}{3q}+\TkM{c1}{4r}+\TkM{d1}{1}\right)$%
\tikzmkpd[config-A={color={gray,gray,gray}, end-yshift={5pt,5pt,5pt}}]%
         {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[config-B={end-yshift={-15pt,-15pt},color={gray,red}}]{a,b}{c, d, e}

\tikzmkpd[config-A={raise=4pt,color={gray,gray,gray}}]%
         {A}{B, D}
\end{document}

Screenshot


Footnote

  1. For such temporary code, you can use TeX or LaTeX2e commands such as \show or \typeout, as well as LaTeX3 ones such as \tl_show:N or \box_show:N.
frougon
  • 24,283
  • 1
  • 32
  • 55
  • Great, can you modify your response to fit LaTeX3 naming conventions, I'll adapt the rest of my code (including config-B). Related to (pic cs), in my case it works well, the problem is in tikzmark (https://github.com/loopspace/tikzmark/issues/9) in my case I have installed the version from git. And it's true, mixing pgfkeys with l3key doesn't seem like a good idea, but, I just want to modify those parameters and not the rest of the tikz options, so from my point of view it's fine. I must study more about the use of variants and expansion in expl3. Gracias Totales :) – Pablo González L Jun 10 '19 at 12:51
  • Please see my last edits. As said there, I don't criticize the use of l3keys for TikZ work; what I'm saying is that you could have completely separated the parsing issue from the TikZ one as part of reducing your example to something minimal. And probably you would have found the problem yourself while doing so, because you would likely have seen then what was happening when preparing the options destined to TikZ. – frougon Jun 10 '19 at 16:20
  • You are absolutely right, I tried to separate and reduce a little the code, but, I do not remain as a MWE (thousand apologies). Debugging the keys and code using \debug_on:n { check-declarations , deprecation } and \tl_show:N along with a more accurate reading of the expl3 documentation should have been my starting point. The yshift-A key doesn't work, I'll add the use of (pic cs) and this solves my problem. Thanks again. – Pablo González L Jun 10 '19 at 16:52
  • 1
    I've fixed yshift-A too. It was a TikZ parsing (syntax) problem—don't ask me why the other quite similar cases work. As can be seen with \tl_show:N, \l_yshift_A_tl was quite fine in \__tikzmkpd_case_one: and \__tikzmkpd_case_two: even before this edit (i.e., all the LaTeX3 option parsing code was working as designed). – frougon Jun 10 '19 at 18:59
  • Now everything is going great....In my first attempt I had left yshift-A inside \__tikzmkpd_define_numeric and also failed, TikZ is a bit dark and difficult to understand in certain points. Until next time and thank you for the programming lesson and debug in LaTeX|expl3. – Pablo González L Jun 10 '19 at 19:43
  • Hi, I could update your response by removing the dependency of the etoolbox package for the use of \seq_indexed_map_inline:Nn from the response given by @egreg at (https://tex.stackexchange.com/a/496604/7832), just with the idea of having one more code in the expl3 line. Saludos – Pablo González L Jun 23 '19 at 16:49
  • Hi, I'm not against \seq_indexed_map_inline:Nn in principle, but I don't remember introducing the dependency on etoolbox myself here. I see it's used for \csxdef (which could be replaced by expl3 stuff but not by \seq_indexed_map_inline:Nn), however this command was in your original code, it's not from me either. What else is there that you'd like to convert? Can you post the new file somewhere, or a diff for instance on https://pastebin.com (or an additional answer you'll cancel, I don't know...)? Thanks. – frougon Jun 23 '19 at 17:21
  • Ah right, I think I see what you mean, this is around the \csxdef (sorry, there was some disturbance around...). It seems okay to me in principle. – frougon Jun 23 '19 at 17:28
  • I have added the modifications, I published them in temporary code at the beginning of the post, of course, they are better in their response for the rest of the users who review the forum :)...if you have some time you can give me a hand with (https://tex.stackexchange.com/q/497079/7832). Gracias – Pablo González L Jun 23 '19 at 18:02
  • I'm going to review it (for the other link, we'll see...). There are a few problems: 1) Already existing: I forgot to add braces around several of your :n values in the key definitions. 2) Private function names (\__tikzmkpd_pass_comma_args:nn) start with two underscores after the backslash. 3) \l__tikzmkpd_mycmd_seq wasn't declared. I'll fix these and see the rest, no need to fix them on your side. – frougon Jun 23 '19 at 18:28
  • Also, l_tikzmkpd_#1##1_tl means the variable is public (and local), is it what you intended? Otherwise, use l__tikzmkpd_#1##1_tl. And I would add something else (fixed string) to the name to avoid any clash with the other cs names in case of particular user input. – frougon Jun 23 '19 at 18:35
  • A thousand apologies, it was a quick adaptation and I still get lost with the use of _ and __. – Pablo González L Jun 23 '19 at 18:37
  • Private (for functions as well as variables) is always with two consecutive underscores. For l_tikzmkpd_#1##1_tl, search&replace on l_tikzmkpd_ seems safe. To avoid clashes (as said) and declare it as private, I propose the name l__tikzmkpd_nodename_#1##1_tl. What do you think? – frougon Jun 23 '19 at 18:46
  • Nice, l__tikzmkpd_nodename_#1##1_tl is much more natural for me :) – Pablo González L Jun 23 '19 at 18:48
  • Okay, please see the last edit. I renamed a few more things. I can't completely validate your code because of the pic cs problem, but since you say it is fixed in PGF/TikZ Git, I have included yours at the end of the post, with my fixes applied. You can retest it and add a screenshot if you want (at the end, right after the code). I can compile your version but the rendering is not correct, so no screenshot. I included as much as I could in my version, though, so the only differences are in the TikZ code and the new cases, “three” and “four” (+ settings in the first tests that would... – frougon Jun 23 '19 at 19:43
  • ... render my screenshot invalid). – frougon Jun 23 '19 at 19:44
  • Great, I added an image of the result, ....you know, in my updated distribution, I compile it without problems (and without using the git version of tikzmark). Grateful for your time :) – Pablo González L Jun 23 '19 at 19:57
  • All right, you're welcome. I've added your screenshot, but you could have done the same via an edit. :) For the other little formatting fixes I did, there was stuff like { #2 }{#2} and \cs_new_protected:Npn \__tikzmkpd_store_node_names:nn #1 #2\cs_new_protected:Npn \__tikzmkpd_store_node_names:nn #1#2 (spaces removed). These are from the recommendations in l3styleguide.pdf. – frougon Jun 23 '19 at 20:03
  • Oops, you're right, now that I read the title of my question right, it looks more like an "XY problem." Do you have a better idea for the title of the question? – Pablo González L Jun 23 '19 at 20:17
  • “XY problem” isn't very clear to me... Maybe something like “Recursive use of l3keys: key1 = {subkey1=value1, subkey2=value2, ...}, ...” (note: backquotes don't “work” in titles, AFAIK). BTW, I suppose that renaming the question would break the link one can find on your board (I know one can have a permalink for the question, though...). – frougon Jun 23 '19 at 20:45
  • (or maybe TeX.SE keeps the link with the old title as an alias, I don't know). – frougon Jun 23 '19 at 20:51
  • I think the current version of the forum updates the links...I better leave it like this:) – Pablo González L Jun 23 '19 at 20:58
  • This could be a question for meta (whether the old link is kept as an alias). :-) (maybe it has already been asked...) – frougon Jun 23 '19 at 21:02
  • Hi, I saw your answer on (https://tex.stackexchange.com/a/530808/7832) and I have a question related to this (https://tex.stackexchange.com/q/565026/7832), you can give me a hand here :-) – Pablo González L Oct 02 '20 at 13:47