6

While responding to this post I was wondering if it was possible write for loops inside addplot calls. I really mean for loops inside addplot and not addplot inside for loop (just to make myself clear).

My answer to this post involves plotting multiple functions that are the sum of harmonics of a given frequency, which gives multiple addplot calls only differing by one term of the sum.

\addplot {sin(4*0.5*\x r)/0.5};    
\addplot {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5};
\addplot {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5 + sin(4*2.5*\x r)/2.5};
\addplot {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5 + sin(4*2.5*\x r)/2.5 + sin(4*3.5*\x r)/3.5};
\addplot {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5 + sin(4*2.5*\x r)/2.5 + sin(4*3.5*\x r)/3.5 + sin(4*4.5*\x r)/4.5};
\addplot {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5 + sin(4*2.5*\x r)/2.5 + sin(4*3.5*\x r)/3.5 + sin(4*4.5*\x r)/4.5 + sin(4*5.5*\x r)/5.5};

I don't think this is quite elegant, and I was wondering if there was a way to avoid this using something like

\addplot {sum[nmin,nmax,n](sin(\n*0.5*\x r)/0.5)};

where nmin/nmax would be the starting/ending indexes of the sum. This question could be generalized to products or other for loops.

Here is the MWE to test this

\documentclass[tikz,border=10pt]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.5}
\usepgfplotslibrary{colorbrewer}
\pgfplotsset{cycle list/Reds-6}

\begin{document}

\begin{tikzpicture}
\begin{axis}[%
    width=\textwidth,
    axis x line=bottom,
    axis y line=left,cycle multi list={Reds-6}]
    \addplot+[samples=300,smooth] {sin(4*0.5*\x r)/0.5};    
    \addplot+[samples=300,smooth] {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5};
    \addplot+[samples=300,smooth] {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5 + sin(4*2.5*\x r)/2.5};
    \addplot+[samples=300,smooth] {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5 + sin(4*2.5*\x r)/2.5 + sin(4*3.5*\x r)/3.5};
    \addplot+[samples=300,smooth] {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5 + sin(4*2.5*\x r)/2.5 + sin(4*3.5*\x r)/3.5 + sin(4*4.5*\x r)/4.5};
    \addplot+[samples=300,smooth] {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5 + sin(4*2.5*\x r)/2.5 + sin(4*3.5*\x r)/3.5 + sin(4*4.5*\x r)/4.5 + sin(4*5.5*\x r)/5.5};
\end{axis}
\end{tikzpicture}
\end{document}
  • Use tikzmath to define the function – percusse May 1 '18 at 13:50
  • @percusse thanks, i will have a look into that – BambOo May 1 '18 at 14:01
6

I set up a stencil and used listofitems to parse it for ? via

\readlist\termstencil{sin(4*?.5*\x r)/?.5}

Then, substitute for ? each time through the loop, a value of 0, then 1, ..., 5. A few checks had to be made not to add a + before the first term, etc. The resulting tokens are collected into the macro \myeqn, which is then passed to \addplot.

\documentclass[tikz,border=10pt]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.5}
\usepgfplotslibrary{colorbrewer}
\pgfplotsset{cycle list/Reds-6}
\usepackage{listofitems}
\makeatletter
\newcommand\loopthroughterm[1]{%
  \def\myeqn{}%
  \foreach\z in{0,...,#1}{%
    \ifnum\z=0\relax\else\g@addto@macro\myeqn{+}\fi%
    \foreachitem\zz\in\termstencil[]{%
      \ifnum\zzcnt=1\else%
        \expandafter\g@addto@macro\expandafter\myeqn\expandafter{\z}\fi%
      \expandafter\g@addto@macro\expandafter\myeqn\expandafter{\zz}%
    }%
  }%
}
\makeatother
\begin{document}
\begin{tikzpicture}
\begin{axis}[%
    width=\textwidth,
    axis x line=bottom,
    axis y line=left,cycle multi list={Reds-6}]
    \setsepchar{?}
    \readlist\termstencil{sin(4*?.5*\x r)/?.5}
    \foreach\k in{0,...,5}{
      \loopthroughterm{\k}
      \addplot+[samples=300,smooth] {\myeqn};
    }
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

Upon exiting the tikzpicture, one can \detokenize\expandafter{\myeqn} to confirm the tokens themselves used for the final loop index:

enter image description here


ADDENDUM

Seeing jbfu's use of a helper macro, I realized I could use the same concept to dispense with the listofitems approach, and not use another package in its place.

Basically, I get the \loopthroughterm macro to create a succession of tokens of the form \termstencil{0}+\termstencil{1}+\termstencil{2}.... Then, as long as \termstencil is defined by the user properly, in this case as

\def\termstencil#1{sin(4*#1.5*\x r)/#1.5}

then all works out in the end.

\documentclass[tikz,border=10pt]{standalone}
\usepackage{pgfplots}
\usepackage[T1]{fontenc}
\pgfplotsset{compat=1.5}
\usepgfplotslibrary{colorbrewer}
\pgfplotsset{cycle list/Reds-6}
\makeatletter
\newcommand\loopthroughterm[1]{%
  \def\myeqn{}%
  \foreach\z in{0,...,#1}{%
    \ifnum\z=0\relax\else\g@addto@macro\myeqn{+}\fi%
    \expandafter\g@addto@macro\expandafter\myeqn\expandafter{%
      \expandafter\termstencil\expandafter{\z}}%
  }%
}
\makeatother
\begin{document}
\begin{tikzpicture}
\begin{axis}[%
    width=\textwidth,
    axis x line=bottom,
    axis y line=left,cycle multi list={Reds-6}]
    \def\termstencil#1{sin(4*#1.5*\x r)/#1.5}
    \foreach\k in{0,...,5}{
      \loopthroughterm{\k}
      \addplot+[samples=300,smooth] {\myeqn};
    }
\end{axis}
\end{tikzpicture}
\end{document}

The plot is the same as in the prior code, but the detokenized form of \myeqn is now this:

enter image description here

Additionally, with the use of a few extra \expandafters, in the form of

\newcommand\loopthroughterm[1]{%
  \def\myeqn{}%
  \foreach\z in{0,...,#1}{%
    \ifnum\z=0\relax\else\g@addto@macro\myeqn{+}\fi%
    \expandafter\expandafter\expandafter\g@addto@macro\expandafter%
      \expandafter\expandafter\myeqn\expandafter\expandafter\expandafter{%
        \expandafter\termstencil\expandafter{\z}}%
  }%
}

the final \myeqn now contains the actual tokens desired:

enter image description here

7

Here is with xinttools. I suspect (but did not check) unexpandable loop could not be used inside \addplot argument, so I defined preliminary macro \myterm to use with the expandable utilities \xintApply and \xintListWithSep.

The external \xintFor is not expandable, and dispense us of any preliminary macro definition.

\documentclass[tikz,border=10pt]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.5}
\usepgfplotslibrary{colorbrewer}
\pgfplotsset{cycle list/Reds-6}
\usepackage{xinttools}
\begin{document}

\begin{tikzpicture}
\def\myterm#1{sin(4*#1.5*\x r)/#1.5}
\begin{axis}[%
    width=\textwidth,
    axis x line=bottom,
    axis y line=left,cycle multi list={Reds-6}]
    \xintFor #1 in {0, 1, 2, 3, 4, 5} \do 
    {%    
    \addplot+[samples=300,smooth] 
     {\xintListWithSep{+}{\xintApply\myterm{\xintSeq{0}{#1}}}}; 
    }   
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

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.