2

I have some difficulties creating a plot where the area between the curve and the x-axis is filled with a gradient. I want to plot an electromagnetic spectrum and I want the color gradient to represent the actual color at given wavelengths, like in the example below.

desired result

I managed to define the desired gradient and also filling the area with it works so far. The only problem is that the color gradient does not fit to the values on the wavelength axis.

This document represents my issue:

\documentclass{article}

\usepackage{pgfplots}
\usepgfplotslibrary{fillbetween}

\pgfdeclarehorizontalshading{visiblespectrum}{100bp}{
color(0bp)=(black);
color(50bp)=(violet);
color(56.25bp)=(blue);
color(62.5bp)=(cyan);
color(68.75bp)=(green);
color(73.75bp)=(yellow);
color(77.5bp)=(orange);
color(81.25bp)=(red);
color(100bp)=(black)
}


\begin{document}

\begin{tikzpicture}
    \begin{axis}[
            title= Photon Flux,
            xlabel={Wavelength [nm]},
            ylabel={Photon Flux [a.u.]},
            xmin=400,
            xmax=800,
        ]
        \addplot[smooth, name path=spectrum] plot[] coordinates{
            ( 400, 0 )
            ( 500, 1 )
            ( 520, 3 )
            ( 525, 8 )
            ( 530, 25 )
            ( 535, 23 )
            ( 540, 28 )
            ( 545, 34 )
            ( 550, 20 )
            ( 555, 13 )
            ( 560, 8 )
            ( 580, 9 )
            ( 600, 18 )
            ( 620, 7 )
            ( 680, 1 )
            ( 800, 0 )
        };
        \path[name path=axis] (axis cs:0,0) -- (axis cs:1,0);
        \addplot+ [
                thick,
                shading=visiblespectrum, 
                fill opacity=0.5,
            domain={400:800}
        ]
        fill between[
            of=spectrum and axis,
            %soft clip={domain=400:800}
        ];
    \end{axis}
\end{tikzpicture}

\end{document}

The respective output:

wrong filling

The gradient appears to be stretched. I played with the width in the shading's color specifications, but without success. Actually, I don't understand, how the shading dimensions are handled. Are they relative or absolute?

I also changed the domains for tikzpicture, addplot and fill between, but that also didn't work.

Which options do I have to align the color gradient with my plot, so it looks like in the first picture?

2 Answers 2

2

With the release of pgf-spectra v2.1 this is a quite easy task.

% used PGFPlots v1.18.1
\documentclass[border=5pt]{standalone}
\usepackage{pgf-spectra}
\usepackage{pgfplots}
    \usepgfplotslibrary{fillbetween}
    \pgfplotsset{compat=1.18}
\begin{document}
\begin{tikzpicture}
        % create a variable to store the `xmax` value, so we don't need to repeat ourselves
        \pgfmathsetmacro{\xmax}{800}
        % create the shade (using the created variable)
        \pgfspectraplotshade[shade end=\xmax]{visiblespectrum}
    \begin{axis}[
        title=Photon Flux,
        xlabel={Wavelength in nm},
        ylabel={Photon Flux in a.u.},
        xmin=300,
        % (use the variable here too)
        xmax=\xmax,
    ]
        \addplot [smooth, name path=spectrum] coordinates {
            (380,0) (400,5) (500,1) (520,3) (525,8) (530,25)
            (535,23) (540,28) (545,34) (550,20) (555,13)
            (560,8) (580,9) (600,18) (620,7) (680,1) (780,0)
        };
        % (because there is no `shade start` key so far the path has to start at x=0)
        \path [name path=axis] (axis cs:0,0)
            -- (axis cs:\pgfkeysvalueof{/pgfplots/xmax},0);
        \addplot+ [thick,shading=visiblespectrum]
            fill between [of=spectrum and axis];
    \end{axis}
\end{tikzpicture}
\end{document}

image showing the result of above code

1

I found a solution myself, though it is not perfect.

The shading used for filling seems to be scaled to fit between the most left and most right points of the area to be filled. So instead of filling between my curve and \path[name path=axis] (axis cs:0,0) -- (axis cs:1,0); I defined a line \addplot[name path=axis] plot[] coordinates{ (400,0)(800,0) }; as path, where x=400 and x=800 is where I want the shading's borders to be.

The disadvantage of this approach is that this only works as long as the bottom layer (helper path/fake "axis") is longer than the actual curve. In this case either the shading needs to be extended or the plotted data needs to be clipped to the desired range (working with domain or xmin/xmax does not work).

My final document looked like this:

\documentclass{article}

\usepackage{pgfplots}
\usepgfplotslibrary{fillbetween}

\pgfdeclarehorizontalshading{visiblespectrum}{100bp}{
color(0bp)=(violet);
color(6.25bp)=(blue);
color(12.5bp)=(cyan);
color(18.75bp)=(green);
color(23.75bp)=(yellow);
color(27.5bp)=(orange);
color(31.25bp)=(red);
color(50bp)=(black)
}

\begin{document}

\begin{tikzpicture}
    \begin{axis}[
            width=5cm,
            title= Photon Flux,
            xlabel={Wavelength [nm]},
            ylabel={Photon Flux [a.u.]},
            xmin=400,
            xmax=800
        ]
        \addplot[name path=spectrum] plot[] coordinates{
            ( 400, 5 )
            ( 420, 4 )
            ( 500, 9 )
            ( 520, 13 )
            ( 525, 8 )
            ( 530, 25 )
            ( 535, 23 )
            ( 540, 28 )
            ( 545, 34 )
            ( 550, 20 )
            ( 555, 13 )
            ( 560, 8 )
            ( 580, 9 )
            ( 600, 18 )
            ( 620, 7 )
            ( 680, 5 )
        };
        \addplot[ name path=axis] plot[] coordinates{
            (400,0)
            (800,0)
        };
        \addplot+ [
                thick,
                shading=visiblespectrum, 
                fill opacity=0.5,
        ]
        fill between[
            of=spectrum and axis
        ];
    \end{axis}
\end{tikzpicture}

\end{document}

...giving this output:

output

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.