5

I would like to define a style matrix of node keys such that

\input tikz
\usetikzlibrary{matrix}
\tikzpicture
\matrix [matrix of node keys]
  {red,node contents=a & blue,node contents=b \\};
\endtikzpicture
\bye

compiles to the same result as

\input tikz
\usetikzlibrary{matrix}
\tikzpicture
\matrix [matrix of nodes]
  {|[red]|a & |[blue]|b \\};
\endtikzpicture
\bye

I am now studying tikzlibrarymatrix.code.tex: it seems to me that the bulk of the code is devoted to parsing the optional syntax. Since I need nothing of that, there might be a smarter way to do what I want. In fact, the example from the manual implementing an essential version of the matrix library is just

\begin{tikzpicture}
  [matrix of nodes/.style={
    execute at begin cell=\node\bgroup,
    execute at end cell=\egroup;%
  }]
  \matrix [matrix of nodes]
  {
    8 & 1 & 6 \\
    3 & 5 & 7 \\
    4 & 9 & 2 \\
  };
\end{tikzpicture}

An utterly naive attempt could be

\input tikz
\let\bsquare[
\let\esquare]
\tikzpicture[matrix of node keys/.style={
  execute at begin cell=\node\bsquare,
  execute at end cell=\esquare;,
}]
\matrix [matrix of node keys]
  {red,node contents=a & blue,node contents=b \\};
\endtikzpicture
\bye

but it fails because the use of \tikz@fig@scan@options mismatches its definition (it expects the form [#1]).

What is the safest and soundest way to define matrix of node keys?

6

Does this work for you?

\makeatletter
\tikzset{matrix of node keys/.code={
    \pgfkeysalso{
        matrix,%
        cells={anchor=base},%
        execute at begin cell=\matrix@ofnodekeys@start@cell,
        execute at empty cell=\tikz@lib@matrix@empty@cell
    }
}}

\bgroup
\catcode`&=\active
\gdef\monk@nil{\pgfutil@gobble{unique expansion text}}
    \gdef\matrix@ofnodekeys@slsl{
    \pgfutil@ifnextchar\monk@nil
        {\expandafter\pgfmatrixendrow\pgfutil@gobble}
        {\pgfmatrixendrow}
}
\gdef\matrix@ofnodekeys@start@cell{
    \let\\\matrix@ofnodekeys@slsl
    \pgfutil@ifnextchar\relax{}{\matrix@ofnodekeys@start@cell@}
}
\gdef\matrix@ofnodekeys@start@cell@#1\\{
    %\def\temp{#1}\show\temp
    \pgfutil@ifnextchar\monk@nil
        {\matrix@ofnodekeys@start@cell@@ #1\\}
        {\matrix@ofnodekeys@start@cell@@ #1&\monk@nil\\\monk@nil}
}
\gdef\matrix@ofnodekeys@start@cell@@#1&{
  \node[name=\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn,#1]{};
  \pgfutil@ifnextchar\monk@nil{\pgfutil@gobble}{&}
}
\egroup
\makeatother

And here's a fully plain TeX example (using pgfutil macros isn't strictly necessary):

\input tikz

\catcode`@=11 %\makeatletter

\tikzset{matrix of node keys/.code={
    \pgfkeysalso{
        matrix,%
        cells={anchor=base},%
        execute at begin cell=\matrix@ofnodekeys@start@cell,
        execute at empty cell=\tikz@lib@matrix@empty@cell
    }
}}

\bgroup
\catcode`&=\active
\gdef\monk@nil{\pgfutil@gobble{unique expansion text}}
\gdef\matrix@ofnodekeys@slsl{
    \pgfutil@ifnextchar\monk@nil
        {\expandafter\pgfmatrixendrow\pgfutil@gobble}
        {\pgfmatrixendrow}
}
\gdef\matrix@ofnodekeys@start@cell{
    \let\\\matrix@ofnodekeys@slsl
    \pgfutil@ifnextchar\relax{}{\matrix@ofnodekeys@start@cell@}
}
\gdef\matrix@ofnodekeys@start@cell@#1\\{
    \pgfutil@ifnextchar\monk@nil
        {\matrix@ofnodekeys@start@cell@@ #1\\}
        {\matrix@ofnodekeys@start@cell@@ #1&\monk@nil\\\monk@nil}
}
\gdef\matrix@ofnodekeys@start@cell@@#1&{
  \node[name=\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn,#1]{};
  \pgfutil@ifnextchar\monk@nil{\pgfutil@gobble}{&}
}
\egroup  
\catcode`@=12 %\makeatother

\tikzpicture [
  every odd row/.style={red},
  every even column/.style={font=\bf}
]
\matrix [matrix of node keys] {
  node contents=a & node contents=b & fill=orange,node contents=c \\
  blue,node contents=d & rectangle,draw,node contents=e & magenta,node contents=f \\
  rotate=60,node contents=g & node contents=h & node contents=i \\
};
\endtikzpicture

\bye

The output:

ecce matrix

12
  • 1
    The only extra detail worth mentioning is that normally \\ is \let to \pgfmatrixendrow, but in this case my code inserts a \@nil after the last \\ to make sure that it doesn't keep inserting more terminal &\@nil's. But \pgfmatrixendrow checks if the next character is an end brace and uses that to decide when to quit. Without changing the value of \\ , \pgfmatrixendrow checks what the next character is and sees my \@nil, and decides to start a new cell. This generates an "error missing close brace" because it never ends the matrix. – Hood Chatham Jan 9 '16 at 20:33
  • 1
    This is not how the usual matrix of math nodes works. The usual one works by setting "execute at begin cell" to roughly \node[some options] \bgroup and "execute at end cell" to roughly "\egroup;". This can't work for options because instead of using TeX delimiters, when tikz sees an option it calls a command that has been defined like \def\getoption[#1]{do stuff}. Because of this, the ] character cannot be hidden in a macro. This is why your naive approach didn't work too. – Hood Chatham Jan 9 '16 at 20:39
  • 1
    I edited your answer to keep the first block as you first wrote it, and the second one a bit stricter using \pgfutil@ifnextchar and \pgfutil@gobble (actually neither is necessary, it's just for good form). Also I've made the example a bit more interesting and added an image. – Paolo Brasolin Jan 9 '16 at 21:34
  • 2
    I think it is easier to see why \node\bgroup works since path parsing of TikZ doesn't immediately grab the argument when it encounters a brace but waits until the whole context is completed. – percusse Jan 10 '16 at 15:06
  • 1
    @PaoloBrasolin Okay so the problem is that the command that cleans up the \@nil is \matrix@ofnodekeys@start@cell, but it only deletes it when it's called at the beginning of the last cell. However, if the last cell is empty, instead the execute at empty cell command is called, and this command doesn't clean up the \@nil. Unfortunately the execute at empty cell command is called inside an extra pair of braces and cannot scan for the \\ like execute at begin cell does. – Hood Chatham Jan 26 '16 at 1:44

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.