0

I'm trying to adapt the solution given here (this is also relevant) where legend entries can be added via a loop. For instance if we have a collection of N curves depending on more than one parameter it is nice to compare each collection within a groupplot environment. The following adaptation

\documentclass[margin=5mm]{standalone}
\usepackage{pgfplots}
\usetikzlibrary{matrix}
\usepgfplotslibrary{groupplots}
\pgfplotsset{compat=newest}
\begin{document}

\newcommand{\getLab}[1]{plot_#1}
\newcommand{\getLeg}[1]{curve #1}

\begin{tikzpicture}
    \begin{groupplot}[cycle list name=color list,
        group style={group name=myplot, group size= 2 by 2}] 


        \nextgroupplot[ylabel=throughput, 
                every axis y label/.append style={at=(ticklabel cs:0)}] % Common y label 
        %\addlegendimage{empty legend}
        %\addlegendentryexpanded{param 1} % very convenient
        \foreach \n in {1,...,3} {
            \addplot {x^(\n)};  
            \label{\getLab{\n}}
        }

        \nextgroupplot
        \foreach \n in {1,...,3} {
            \addplot {x^(\n + 1)};  
        }
        \nextgroupplot
        \foreach \n in {1,...,3} {
            \addplot {x^(\n + 2)};  
        }
        \nextgroupplot
        \foreach \n in {1,...,3} {
            \addplot {x^(\n + 3)};  
        }

\end{groupplot}

\path (myplot c1r1.north west|-current bounding box.north)--
      coordinate(legendpos)
      (myplot c2r1.north east|-current bounding box.north);
\matrix[
    matrix of nodes,
    anchor=south,
    draw,
    inner sep=0.2em,
    draw
  ] at ([yshift=1ex] legendpos) {
    % Problem starts:
    %\foreach \m in {1,...,3} { % ?? this just looks silly
    %    \ref{\getLab{\m}} & \getLeg{\m} &
    %}
    %
    % This compiles surprisingly
    %\foreach \m in {1,...,3} { \ref{\getLab{\m}} } & 
    %\foreach \m in {1,...,3} { \ref{\getLeg{\m}} } &

    % Working
    \ref{plot_2} & curve 2 &
    \ref{plot_2} & curve 2 &
    \ref{plot_3} & curve 3
\\};
\end{tikzpicture}
\end{document}

gives:

enter image description here

which is what I want in the end, but would like to 'functionally' generate the matrix structure used at the end similar to the way the labels were generated. Is that possible?

Stefan Pinnow
  • 29,535
algae
  • 285

1 Answers1

1

The problem is that \foreach creates a TeX group around the loop code, which prevents the TikZ matrix from considering the &s as column delimiters. In order to solve this, you can generate the code using an expl3 function such as \int_step_inline:nnnn, which does not create a group around the user code.

Note: TikZ matrices expect the column delimiter & with category code 13 (active). This is not the standard category code for &, which is 4 (alignment tab). That is why my definition of \myAddLegendEntries uses \pgfmatrixnextcell instead of & (at the place where \myAddLegendEntries is defined, & has its standard category code 4, which wouldn't work as expected when inserted inside the TikZ matrix).

The delimiter between entries is the optional argument of \myAddLegendEntries. It defaults to \pgfmatrixnextcell, but you might want to change it in order to modify the appearance of the legend. For instance, you could add things before or after the \pgfmatrixnextcell, or you could use this to obtain a multiline legend:

\myAddLegendEntries[\\]{1}{1}{3} % initial value, step, final value
  {\ref{\getLab{#1}} & \getLeg{#1}}

enter image description here

Full code with a single-line legend, as in the MWE:

\documentclass[tikz, margin=2mm]{standalone}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{matrix}
\usepackage{pgfplots}
\usepgfplotslibrary{groupplots}
\pgfplotsset{compat=1.16}

\ExplSyntaxOn
\seq_new:N \l__algae_legend_entries_seq

% Arguments: separator, initial, step, final
\NewDocumentCommand \myAddLegendEntries { O{\pgfmatrixnextcell} m m m m }
 {
   \seq_clear:N \l__algae_legend_entries_seq
   \int_step_inline:nnnn {#2} {#3} {#4}
     { \seq_put_right:Nn \l__algae_legend_entries_seq {#5} }
   \seq_use:Nn \l__algae_legend_entries_seq {#1}
 }
\ExplSyntaxOff

\newcommand{\getLab}[1]{plot_#1}
\newcommand{\getLeg}[1]{curve #1}

\begin{document}

\begin{tikzpicture}
  \begin{groupplot}[cycle list name=color list,
    group style={group name=myplot, group size= 2 by 2}]
    \nextgroupplot[
      % Common y label
      ylabel=throughput,
      every axis y label/.append style={at=(ticklabel cs:0)}]
    \foreach \n in {1,...,3} {
        \addplot {x^(\n)};
        \label{\getLab{\n}}
    }
    \nextgroupplot
    \foreach \n in {1,...,3} {
        \addplot {x^(\n + 1)};
    }
    \nextgroupplot
    \foreach \n in {1,...,3} {
        \addplot {x^(\n + 2)};
    }
    \nextgroupplot
    \foreach \n in {1,...,3} {
        \addplot {x^(\n + 3)};
    }
\end{groupplot}

\path (myplot c1r1.north west|-current bounding box.north)--
      coordinate(legendpos)
      (myplot c2r1.north east|-current bounding box.north);

\matrix[matrix of nodes, anchor=south, draw, inner sep=0.2em]
  at ([yshift=1ex] legendpos)
  {
    \myAddLegendEntries{1}{1}{3} % initial value, step, final value
      {\ref{\getLab{#1}} & \getLeg{#1}}
    \\
  };
\end{tikzpicture}

\end{document}

enter image description here

frougon
  • 24,283
  • 1
  • 32
  • 55