[Tex/LaTex] How to automate the plotting of step functions

pgfplotstikz-pgf

I'm using a lot of step functions in my master's thesis and would like to automate the plotting. I can get them to look just as I want by hand using this method, but when I try to automate it like this there are two problems (see picture).

  1. The vertical jump is sometimes plotted instead of just being a jump.
  2. The dotted lines to indicate vertical jumps are only drawn the last time I call the command.

How can I fix this?

In the \stepfunction command I define the function to be plotted locally in the for-loop. Like this:

  \foreach \xStart/\xEnd  in {\minx/\s, \s/\e, \e/\maxx} {
    \addplot[domain=\xStart:\xEnd, blue, ultra thick,
        declare function={
            pj(\x) = and(\x >= \s, \x < \e)*(\val) + 
                and(\x >= \e, 1)*(\b) + 
                and(1, \x < \s)*(\b);}] 
        {pj(x)};

Is this a good idea? I want to be able to draw many functions in a single picture so I can't put the whole axis environment in the newcommand, right?

Left: automated plot, right: plot by hand.

Code

\documentclass[border=3pt]{standalone}

\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.9}


\pgfkeys{
  stepfunction/.is family,
  stepfunction,
  plotstart/.initial=0,
  plotend/.initial=5,
  start/.initial=1,
  end/.initial=2,
  value/.initial=1,
  base/.initial=0
}
\newcommand\stepfunctionset[1]{\pgfkeys{stepfunction,#1}}
\newcommand\stepfunction[1][]{
  \stepfunctionset{#1,
    plotstart/.get=\minx,
    plotend/.get=\maxx,
    start/.get=\s,
    end/.get=\e,
    value/.get=\val,
    base/.get=\b
  }
  % plot from \minx to \maxx
  \foreach \xStart/\xEnd  in {\minx/\s, \s/\e, \e/\maxx} {
    \addplot[domain=\xStart:\xEnd, blue, ultra thick,
        declare function={
            pj(\x) = and(\x >= \s, \x < \e)*(\val) + 
                and(\x >= \e, 1)*(\b) + 
                and(1, \x < \s)*(\b);}] 
        {pj(x)};
  };
  \draw[dotted] (axis cs:\s,\b) -- (axis cs:\s,\val);
  \draw[dotted] (axis cs:\e,\b) -- (axis cs:\e,\val);
  \addplot[color=blue,fill=white,only marks,mark=*] 
    coordinates{(\s,\b)(\e,\val)};
  \addplot[color=blue,only marks,mark=*] 
    coordinates{(\s,\val)(\e,\b)};
}

\begin{document}
\begin{tikzpicture}[scale=1.5, thick]
  \begin{axis}[xmin=-1, xmax=6, ymax=3.5]
    % p_{j-1,1}
    \stepfunction[plotstart=-1, plotend=2.5, 
        start=0, end=2, value=1, base=0]
    % p_{j+1,6}
    \stepfunction[plotstart=2.5, plotend=3.75, 
        start=3, end=3.5, value=3, base=0]
    % p_{j,4}
    \stepfunction[plotstart=3.75, plotend=6, start=4, end=5, value=2]
  \end{axis}
\end{tikzpicture}

% Plot by hand
\begin{tikzpicture}[
  scale=1.5,
  thick,
  soldot/.style={color=blue,only marks,mark=*},
  holdot/.style={color=blue,fill=white,only marks,mark=*},
  declare function={
    pj(\x)=and(\x >= 0, \x < 1)*(1) + 
        and(\x >= 1, 1)*(0) + 
        and(1, \x < 0)*(0);
    }]
  \begin{axis}[xmin=-1, xmax=6, ymax=3.5]

    % p_{j-1,1}
    \foreach \xStart/\xEnd  in {-1/0, 0/2, 2/3} {
        \addplot[domain=\xStart:\xEnd, blue, ultra thick] {pj(0.5*x)};
    }
    \draw[dotted] (axis cs:0,0) -- (axis cs:0,1);
    \draw[dotted] (axis cs:2,0) -- (axis cs:2,1);
    \addplot[holdot] coordinates{(0,0)(2,1)};
    \addplot[soldot] coordinates{(0,1)(2,0)};


    % p_{j+1,6}
    \foreach \xStart/\xEnd  in {3/3.5, 3.5/4} {
        \addplot[domain=\xStart:\xEnd, blue, ultra thick]
            {pj(2*(x-3))*3};
    }
    \draw[dotted] (axis cs:3,0) -- (axis cs:3,3);
    \draw[dotted] (axis cs:3.5,0) -- (axis cs:3.5,3);
    \addplot[holdot] coordinates{(3,0)(3.5,3)};
    \addplot[soldot] coordinates{(3,3)(3.5,0)};

    % p_{j,4}
    \foreach \xStart/\xEnd  in {4/5, 5/6} {
        \addplot[domain=\xStart:\xEnd, blue, ultra thick] 
            {pj((x-4))*2};
    }
    \draw[dotted] (axis cs:4,0) -- (axis cs:4,2);
    \draw[dotted] (axis cs:5,0) -- (axis cs:5,2);
    \addplot[holdot] coordinates{(4,0)(5,2)};
    \addplot[soldot] coordinates{(4,2)(5,0)};

  \end{axis}
\end{tikzpicture}

\end{document}

Edit

Using the option jump mark left for plotting solves the first problem and makes the foor-loop unnecessary:

% plot from \minx to \maxx
\addplot[domain=\minx:\maxx, blue, ultra thick, jump mark left,
        declare function={
            pj(\x) = and(\x >= \s, \x < \e)*(\val) + 
                and(\x >= \e, 1)*(\b) + 
                and(1, \x < \s)*(\b);}] 
        {pj(x)};

Thanks Paul Stiverson!

Best Answer

rather than specify the step function as a sum of booleans, seems easier to specify it as a list of start/end/value triplets and then just loop through:

enter image description here

\documentclass[border=3pt]{standalone}

\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.14}

\begin{document}
\def\lasty{}
\begin{tikzpicture}[
  scale=1.5,
  thick,
  soldot/.style={color=blue,only marks,mark=*},
  holdot/.style={color=blue,fill=white,only marks,mark=*}
]
  \begin{axis}[xmin=-1, xmax=6, ymax=3.5]
    \foreach \xStart/\xEnd/\yVal  in 
    {-1/0/0, 0/2/1, 2/3/0, 3/3.5/3, 3.5/4/0, 4/5/2, 5/6/0} {
        \addplot[domain=\xStart:\xEnd, blue, ultra thick] {\yVal}; 
        \ifx\lasty\empty\else
         \addplot[holdot](\xStart,\lasty);
         \addplot[soldot](\xStart,\yVal);
         \edef\tmp{\noexpand\draw[dotted]
           (axis cs: \xStart,\lasty) -- (axis cs: \xStart,\yVal);}
         \tmp
        \fi
        \global\let\lasty\yVal
}
  \end{axis}
\end{tikzpicture}

\end{document}