如何使用 pgfplots 包创建电磁波谱(连同颜色图)

如何使用 pgfplots 包创建电磁波谱(连同颜色图)

我用 LaTeX 寻找好的电磁波谱时,没有找到合适的,因此我需要创建自己的图像。下面是我创建图像的方法。

答案1

%
\documentclass[12pt]{article}
\usepackage[dvipsnames,table]{xcolor}
\usepackage{siunitx} % SI-units
\usepackage{pgfplots}
\usepgfplotslibrary{units} % to add units easily to axis
\usepgfplotslibrary{fillbetween} % to fill inbetween curves
\usepgfplotslibrary{colormaps} % to create colormaps
\pgfplotsset{width=12.2cm, height=7cm}
\pgfplotsset{compat=newest} %(making it only compatalbe with
%new releases of pgfplots)
\pgfdeclarehorizontalshading{visiblelight}{50bp}{
color(0.00000000000000bp)=(violet);
color(8.33333333333333bp)=(blue);
color(16.66666666666670bp)=(cyan);
color(25.00000000000000bp)=(green);
color(33.33333333333330bp)=(yellow);
color(41.66666666666670bp)=(orange);
color(50.00000000000000bp)=(red)
}%

\begin{document}
\begin{tikzpicture}[fill between/on layer={axis grid}]
\begin{axis}[
xlabel={Wavelength},
xticklabel style = {font=\tiny,yshift=0.2ex},
xmin=10^-5,
xmax=10^9,
x unit=\si{\micro\meter},
xmode=log,
ymin=0,
ymax=1,
height=3cm,
yticklabels={},
ytick=\empty,
legend cell align=left,
legend style={at={(0.85,-0.77)},anchor=north}
]
\addplot[draw=none, name path=start, forget plot] coordinates{(10^-5,0)(10^-5,1)};
\addplot[draw=none, name path=gamma, forget plot] coordinates{(10^-3,0)(10^-3,1)};
\addplot[draw=none, name path=xrays, forget plot] coordinates{(10^-2,0)(10^-2,1)};
\addplot[draw=none, name path=uv, forget plot] coordinates{(0.4,0)(0.4,1)};
\addplot[draw=none, name path=visible, forget plot] coordinates{(0.7,0)(0.7,1)};
\addplot[draw=none, name path=ir, forget plot] coordinates{(10^2.5,0)(10^2.5,1)};
\addplot[draw=none, name path=microwave, forget plot] coordinates{(10^5,0)(10^5,1)};
\addplot[draw=none, name path=radiowave, forget plot] coordinates{(10^9,0)(10^9,1)};
\addplot[violet!20, area legend] fill between[of=start and gamma];
\addlegendentry{$\gamma$-ray}
\addplot[violet!60, area legend] fill between[of=gamma and xrays];
\addlegendentry{X-ray}
\addplot[violet, area legend] fill between[of=xrays and uv];
\addlegendentry{Ultra violet}
\addplot[shading=visiblelight, area legend] fill between[of=uv and visible];
\addlegendentry{Visible light}
\addplot[red, area legend] fill between[of=visible and ir];
\addlegendentry{Infrared}
\addplot[Bittersweet, area legend] fill between[of=ir and microwave];
\addlegendentry{Micro wave}
\addplot[Brown, area legend] fill between[of=microwave and radiowave];
\addlegendentry{Radio wave}
\end{axis}
\end{tikzpicture}
\end{document}

示例输出:

电磁频谱

答案2

基于现有的答案,我还创建了一种方法。它有一些问题可能需要解决,尤其是对于“可见光谱”刻度和底部颜色的名称。除了代码中已经指定的波长指数之外,它基本上对任何其他波长指数都不起作用。

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{calc, positioning, shapes, backgrounds, fit, arrows}
\usepackage{pgf-spectra}

\usepackage{siunitx}

\usepackage{contour}

\begin{document}

\pgfdeclarehorizontalshading{visiblelight}{50bp}{% https://tex.stackexchange.com/a/348492/120853
    color(0bp)=(violet!25);
    color(8.33bp)=(blue!25);
    color(16.67bp)=(cyan!25);
    color(25bp)=(green!25);
    color(33.33bp)=(yellow!25);
    color(41.5bp)=(orange!25);
    color(50bp)=(red!25)
}%

\begin{tikzpicture}[%
        raylabel/.style={font=\scriptsize}
    ]
    \def\minexponent{-6}
    \def\maxexponent{6}
    \def\spectrumheight{9em}

    \pgfmathtruncatemacro{\nextminexponent}{\minexponent + 1}

    % Main foreach loop, drawing the wavelengths as powers of 10 in an alternating fashion: even on top, odd at bottom. Then connects them with help lines
    \foreach [count=\i, remember=\exponent as \previousexponent, evaluate=\i as \currentposition using int(\i/2)] \exponent in {\minexponent, \nextminexponent, ..., \maxexponent}{
        \ifodd\exponent
            \def\height{0}
        \else
            \def\height{\spectrumheight}
        \fi

        % Anchor at baseline to get all nodes on same baseline.
        % https://tex.stackexchange.com/questions/133227/how-to-align-text-in-tikz-nodes-by-baseline#comment300863_133227
        \node[anchor=base] (WAVELENGTH_\exponent) at (\exponent, \height) {\contour{white}{\num{e\exponent}}};

        \ifnum\i > 1
            \ifodd\i
                \node (LABEL_\currentposition)
                    at ($(WAVELENGTH_\exponent)!0.5!(WAVELENGTH_\previousexponent)$)
                    {};% This is left as a node as opposed to coordinate: fill it out with '\currentposition' for debugging
            \else
                % Do not draw connection at exponent 1:
                \pgfmathparse{\exponent != 1}% \pgfmathparse stores result (0 or 1) in macro \pgfmathresult
                \ifnum\pgfmathresult = 1
                    \draw[help lines]
                        (WAVELENGTH_\previousexponent) --(WAVELENGTH_\exponent)
                        node[midway] (CONNECTION_\currentposition) {}% This is left as a node as opposed to coordinate: fill it out with '\currentposition' for debugging
                        coordinate[at start] (CONNECTION_\currentposition_START)
                        coordinate[at end] (CONNECTION_\currentposition_END);
                \fi
            \fi
        \fi
    }

    % Create an arrow shape that fits around all relevant nodes, but do not draw it.
    % Draw it manually later to leave out the 'bottom' of the arrow.
    % We still need this invisible arrow for lining up of coordinates
    \node[
        single arrow,
        single arrow head extend=0pt,
        single arrow tip angle=150,% Inner angle of arrow tip
        fit={([xshift=-3em]CONNECTION_1_START)(CONNECTION_1_END)(CONNECTION_\maxexponent_START)([xshift=5em]CONNECTION_\maxexponent_END)},
        inner sep=0pt
    ]
    (ARROW) {};

    \node[align=center] (THERM) at ([yshift=3em]WAVELENGTH_1|-ARROW.after tail) {thermal\\radiation};% Only works because exponent 1 is between -1 and 3
    \draw (THERM) -| ([yshift=-1.5em]WAVELENGTH_-1|-THERM);
    \draw (THERM) -| ([yshift=-1.5em]WAVELENGTH_3|-THERM);

    % On background layer so already drawn arrow and scale lines cover it up nicely
    \begin{scope}[on background layer]
        \node[
            inner sep=0pt,
            outer sep=0pt,
            fit={([xshift=-2.2em]WAVELENGTH_0|-ARROW.after tail)([xshift=-2.2em]WAVELENGTH_1|-ARROW.before tail)}, shading=visiblelight]
            (SMALL_VISIBLE_LIGHT) {};

        \shade[
            left color=white,
            right color=violet!25,
            middle color=violet!5,
            outer sep=0pt
            ]
            (CONNECTION_3_START) -- (CONNECTION_3_END) -- ([xshift=\pgflinewidth]SMALL_VISIBLE_LIGHT.south west) -- ([xshift=\pgflinewidth]SMALL_VISIBLE_LIGHT.north west) -- cycle;

        \shade[
            left color=red!25,
            right color=white,
            middle color=red!5,
            outer sep=0pt,
            ]
            (CONNECTION_5_START) -- (CONNECTION_5_END) -- ([xshift=-\pgflinewidth]SMALL_VISIBLE_LIGHT.south east) -- ([xshift=-\pgflinewidth]SMALL_VISIBLE_LIGHT.north east) -- cycle;
    \end{scope}

    % Some labels can be drawn automatically at the designated label coordinates:
    \foreach [count=\i] \label in {
            {Gamma\\rays},
            {X-rays},
            {},%Skip this one
            {infrared}
        }{
            \node[raylabel, align=center] at (LABEL_\i) {\label};
        }

    % These do not fit the loop and are drawn manually:
    \node[raylabel, align=right, anchor=north] at ([yshift=-1em]$(WAVELENGTH_-2)!0.45!(WAVELENGTH_0)$) {Ultra-\\violet};

    \node[raylabel, fill=white] at (CONNECTION_6) {radio waves};

    \node[raylabel, left=0.1em of CONNECTION_1, align=right] {cosmic\\rays};

    \node[
        draw,
        fill=black!20,
        below=4em of SMALL_VISIBLE_LIGHT,
        align=center,
        label=above:{\textbf{Visible Spectrum}}
        ] (FULL_VISIBLE_LIGHT) {%
        \pgfspectra[width=13em,height=3em]\\%pgfspectra also has a builtin axis which of course much better than this terrible approach, but it is in nanometer
            {\num{0.38} \hfill\num{0.48} \hfill\num{0.58}\hfill \num{0.68} \hfill\num{0.78}}\\
        {\hfill blue \hspace{0.1em} green yellow \hfill red \hfill}
    };

    % Draw 'magnifying' trapeze, on background so it is covered by scale labels
    \begin{scope}[on background layer]
        \filldraw[help lines, fill=black!10] (FULL_VISIBLE_LIGHT.north east) -- (SMALL_VISIBLE_LIGHT.south east) -- (SMALL_VISIBLE_LIGHT.south west) -- (FULL_VISIBLE_LIGHT.north west) -- cycle;
    \end{scope}

    % Draw around arrow manually, leaving its tail open
    \draw[draw, thick] (ARROW.after tail) -- (ARROW.before head) -- (ARROW.before tip) -- (ARROW.tip) -- (ARROW.after tip) -- (ARROW.after head) -- (ARROW.before tail);
\end{tikzpicture}
\end{document}

给出一个带有一些注释的电磁波谱,半自动生成:

带注释的电磁波谱

答案3

这里我给出了一些已经很棒的答案的“改进”版本。主要区别在于,随着pgf-spectrav2.1.1 现在也提供对数光谱。

由于图像中可见光的光谱非常小,因此差异很小,但在其他应用中可能非常有用。


\documentclass[border=5pt]{standalone}
\usepackage[dvipsnames,table]{xcolor}
\usepackage{siunitx}
\usepackage{pgf-spectra}
\usepackage{pgfplots}
    \usepgfplotslibrary{
        colormaps,
        fillbetween,
        units,
    }
    \pgfplotsset{
        compat=1.18,
        width=12.2cm,
        height=3cm,
    }
%    % make the horizontal shading and set the UV and IR colors -->
%    \pgfspectraStyle[gamma=.6] % uncomment to change the gamma
    \wlcolor{380}\colorlet{UV}{wlcolor}%
    \wlcolor{780}\colorlet{IR}{wlcolor}%
    \pgfspectraplotshade[logarithmic, UVcolor=UV]{logvisiblelight}
    \pgfspectraplotshade{visiblelight}
%   \pgfspectraStyleReset% uncomment to reset the style
\begin{document}
\begin{tikzpicture}
    \begin{axis}[
        xmode=log,
        xmin=1e-5,
        xmax=1e9,
        x unit=\si{\micro\meter},
        xlabel={Wavelength},
        xticklabel style={font=\tiny,yshift=0.2ex},
        ymin=0,
        ymax=1,
        ytick=\empty,
        legend cell align=left,
        legend style={at={(0.85,-0.77)},anchor=north},
        axis on top,
        area legend,
    ]
        \addplot [draw=none,forget plot,name path=start]     coordinates {(1e-5,0)(1e-5,1)};
        \addplot [draw=none,forget plot,name path=gamma]     coordinates {(1e-3,0)(1e-3,1)};
        \addplot [draw=none,forget plot,name path=xrays]     coordinates {(1e-2,0)(1e-2,1)};
        \addplot [draw=none,forget plot,name path=uv]        coordinates {(0.38,0)(0.38,1)};
        \addplot [draw=none,forget plot,name path=visible]   coordinates {(0.78,0)(0.78,1)};
        \addplot [draw=none,forget plot,name path=ir]        coordinates {(10^2.5,0)(10^2.5,1)};
        \addplot [draw=none,forget plot,name path=microwave] coordinates {(1e5,0)(1e5,1)};
        \addplot [draw=none,forget plot,name path=radiowave] coordinates {(1e9,0)(1e9,1)};

        \addplot [violet!20] fill between [of=start and gamma];
            \addlegendentry{$\gamma$-ray}
        \addplot [violet!60] fill between [of=gamma and xrays];
            \addlegendentry{X-ray}
        \addplot [UV!50,area legend] fill between [of=xrays and uv];
            \addlegendentry{Ultra violet}

        % forget this plot ...
        \addplot [shading=logvisiblelight,forget plot] fill between [of=uv and visible];
            % ... and add a legend image with the "normal"/non-log shading
            % (why? Because we can :))
            \addlegendimage{shading=visiblelight}
            \addlegendentry{Visible light}

        \addplot [IR!50,area legend] fill between [of=visible and ir];
            \addlegendentry{Infrared}
        \addplot [IR!50!Bittersweet] fill between [of=ir and microwave];
            \addlegendentry{Micro wave}
        \addplot [Brown] fill between [of=microwave and radiowave];
            \addlegendentry{Radio wave}
    \end{axis}
\end{tikzpicture}
\end{document}

该图显示了上述代码的结果,该代码是 https://tex.stackexchange.com/a/348492/95441 的改进版本


\documentclass[border=5pt]{standalone}
\usepackage{contour}
\usepackage{tikz}
\usepackage{siunitx}
    \usetikzlibrary{
        arrows,
        backgrounds,
        calc,
        fit,
        positioning,
        shapes,
    }
\usepackage{pgf-spectra}
\begin{document}
\begin{tikzpicture}[
    raylabel/.style={font=\scriptsize}
]

%    % make the horizontal shading and set the UV and IR colors -->
%    \pgfspectraStyle[gamma=.6]     % uncomment to change the gamma
    \wlcolor{380}\colorlet{UV}{wlcolor}
    \wlcolor{780}\colorlet{IR}{wlcolor}
    \pgfspectraplotshade[logarithmic,shade opacity=0.3]{visiblelight}
%    \pgfspectraStyleReset          % uncomment to reset the style

    \def\minexponent{-6}
    \def\maxexponent{6}
    \def\spectrumheight{9em}

    \pgfmathtruncatemacro{\nextminexponent}{\minexponent + 1}

    % Main foreach loop, drawing the wavelengths as powers of 10 in an alternating
    % fashion: even on top, odd at bottom. Then connects them with help lines
    \foreach [
        count=\i,
        remember=\exponent as \previousexponent,
        evaluate=\i as \currentposition using int(\i/2),
    ] \exponent in {\minexponent,\nextminexponent,...,\maxexponent}{
        \ifodd\exponent
            \def\height{0}
        \else
            \def\height{\spectrumheight}
        \fi

        \node [anchor=base] (WAVELENGTH_\exponent) at (\exponent, \height)
            {\contour{white}{\num[print-unity-mantissa = false]{e\exponent}}};

        \ifnum\i > 1
            \ifodd\i
                \node (LABEL_\currentposition)
                    at ($(WAVELENGTH_\exponent)!0.5!(WAVELENGTH_\previousexponent)$)
                    % This is left as a node as opposed to coordinate:
                    % fill it out with '\currentposition' for debugging
                    {}
                ;
            \else
                % Do not draw connection at exponent 1:
                % (`\pgfmathparse` stores result (0 or 1) in macro `\pgfmathresult`)
                \pgfmathparse{\exponent != 1}
                \ifnum\pgfmathresult = 1
                    \draw[help lines]
                        (WAVELENGTH_\previousexponent) --(WAVELENGTH_\exponent)
                        % This is left as a node as opposed to coordinate:
                        % fill it out with '\currentposition' for debugging
                        node [midway] (CONNECTION_\currentposition) {}
                        coordinate [at start] (CONNECTION_\currentposition_START)
                        coordinate [at end] (CONNECTION_\currentposition_END);
                \fi
            \fi
        \fi
    }

    % Create an arrow shape that fits around all relevant nodes, but do not draw it.
    % Draw it manually later to leave out the 'bottom' of the arrow.
    % We still need this invisible arrow for lining up of coordinates
    \node [
        single arrow,
        single arrow head extend=0pt,
        % Inner angle of arrow tip
        single arrow tip angle=150,
        fit={
            ([xshift=-3em]CONNECTION_1_START)
            (CONNECTION_1_END)
            (CONNECTION_\maxexponent_START)
            ([xshift=5em]CONNECTION_\maxexponent_END)
        },
        inner sep=0pt
    ]
    (ARROW) {};

    % Only works because exponent 1 is between -1 and 3
    \node [align=center] (THERM) at ([yshift=3em]WAVELENGTH_1|-ARROW.after tail)
        {thermal\\radiation};
    \draw (THERM) -| ([yshift=-1.5em]WAVELENGTH_-1|-THERM);
    \draw (THERM) -| ([yshift=-1.5em]WAVELENGTH_3|-THERM);

    % On background layer so already drawn arrow and scale lines cover it up nicely
    \begin{scope}[on background layer]
        \node [
            inner sep=0pt,
            outer sep=0pt,
            fit={
                ([xshift=-1.9em]WAVELENGTH_0|-ARROW.after tail)
                ([xshift=-3em]WAVELENGTH_1|-ARROW.before tail)
            },
            shading=visiblelight,
        ] (SMALL_VISIBLE_LIGHT) {};
        \shade [
            left color=white,
            right color=UV!25,
            middle color=UV!5,
            outer sep=0pt
        ] (CONNECTION_3_START)
            -- (CONNECTION_3_END)
            -- ([xshift=\pgflinewidth]SMALL_VISIBLE_LIGHT.south west)
            -- ([xshift=\pgflinewidth]SMALL_VISIBLE_LIGHT.north west)
            -- cycle
        ;
        \shade [
            left color=IR!25,
            right color=white,
            middle color=IR!5,
            outer sep=0pt,
        ] (CONNECTION_5_START)
            -- (CONNECTION_5_END)
            -- ([xshift=-\pgflinewidth]SMALL_VISIBLE_LIGHT.south east)
            -- ([xshift=-\pgflinewidth]SMALL_VISIBLE_LIGHT.north east)
            -- cycle
        ;
    \end{scope}

    % Some labels can be drawn automatically at the designated label coordinates:
    \foreach [count=\i] \label in {
        {Gamma\\rays},
        {X-rays},
        {},%Skip this one
        {infrared}
    }{
        \node[raylabel, align=center] at (LABEL_\i) {\label};
    }

    % These do not fit the loop and are drawn manually:
    \node [raylabel, align=right, anchor=north] at
        ([yshift=-1em,xshift=-2.5pt]$(WAVELENGTH_-2)!0.45!(WAVELENGTH_0)$)
            {Ultra-\\violet};
    \node [raylabel, fill=white] at (CONNECTION_6) {radio waves};
    \node [raylabel, left=0.1em of CONNECTION_1, align=right] {cosmic\\rays};
    \node [
        draw,
        fill=black!20,
        below=4em of SMALL_VISIBLE_LIGHT,
        align=center,
        label=above:{\textbf{Visible Spectrum}}
    ] (FULL_VISIBLE_LIGHT) {%
        \pgfspectra[
            width=13em,height=3em,axis,axis unit=micron,axis step=100,
            axis ticks=4,axis unit precision=2,axis color=black!20,
            axis font=\normalsize,axis font color=black,
        ]\\%
            \footnotesize
        blue \hspace{0.5em}
        green
        yellow \hspace{1.5em}
        red
    };

    % Draw 'magnifying' trapeze, on background so it is covered by scale labels
    \begin{scope}[on background layer]
        \filldraw [help lines, fill=black!10] (FULL_VISIBLE_LIGHT.north east)
            -- (SMALL_VISIBLE_LIGHT.south east)
            -- (SMALL_VISIBLE_LIGHT.south west)
            -- (FULL_VISIBLE_LIGHT.north west)
            -- cycle
        ;
    \end{scope}

    % Draw around arrow manually, leaving its tail open
    \draw [draw, thick] (ARROW.after tail)
        -- (ARROW.before head)
        -- (ARROW.before tip)
        -- (ARROW.tip)
        -- (ARROW.after tip)
        -- (ARROW.after head)
        -- (ARROW.before tail)
    ;
\end{tikzpicture}
\end{document}

该图显示了上述代码的结果,该代码是 https://tex.stackexchange.com/a/498765/95441 的改进版本

相关内容