# tikz: draw multicolor curve with smooth gradient

Yes, I know there are several topics about, but no one referred to my case. While colormap seems works only with plots, how can I draw a multicolor curve with smooth gradient? This is my attempt:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{makecell}
\setcellgapes{5pt}
\usepackage{booktabs}
\usepackage{amsmath,amssymb,amsfonts,systeme,mathtools}
\usepackage{pgfplots}
\usepackage{float}

\begin{document}

\begin{figure}[H]
\centering
\begin{tikzpicture}
\begin{axis}[
ticks=none,
grid=none,
xmin=0,xmax=100,
ymin=0,ymax=100,
axis lines = middle,
set layers,
xlabel={$t$},ylabel={$T$},
x label style={at={(1,0)},right},
y label style={at={(0,1)},above},
colormap={redblue}{rgb255(0cm)=(255,0,0); rgb255(1cm)=(0,0,255)}
]
\draw [ultra thick] (20,90) to[out=270,in=110] (25,60) to[out=335,in=115] (35,45) to[out=0,in=180] (70,45) to[out=305,in=150] (90,20);
\node[inner sep=0pt, label={[outer sep=-2pt]45:A}] at (20,90) {} ;
\node[label={[outer sep=-2pt]45:B}] at (25,60) {} ;
\node[label={[outer sep=-2pt]45:C}] at (35,45) {} ;
\node[label={[outer sep=-2pt]45:D}] at (70,45) {} ;
\node[label={[outer sep=-2pt]45:E}] at (90,20) {} ;
coordinates {
(20,90) (25,60) (35,45) (70,45) (90,20)
};
\end{axis}
\end{tikzpicture}
\end{figure}

\end{document}


Can you help me?

As you said, colormaps works with plots. In your code there is indeed a plot, but only for plotting the marks and hence not the line. This one is drawn with the \draw command inside the axis. Therefore, you have to add a gradient color to your line.

For instance, using the solution by Mark Wibrow to How to draw an arrow with two colors? you can add a color gradient using \path and the defined shading path option in tikset. Then with left color=<color> and right color=<color> (or top color=<color> and bottom color=<color>) you have a gradient with only two colors.

Output:

Code:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{makecell}
\setcellgapes{5pt}
\usepackage{booktabs}
\usepackage{amsmath,amssymb,amsfonts,systeme,mathtools}
\usepackage{pgfplots}
\usepackage{float}

%https://tex.stackexchange.com/questions/137357/how-to-draw-an-arrow-with-two-colors
\makeatletter

\tikzset{
% There are three circumstances in which the fading sep is needed:
% 1. Arrows which do not update the bounding box (which is most of them).
% 2. Line caps/joins and mitres that extend outside the natural bounding
%    box of the path (these are not calculated by PGF).
% 3. Other reasons that haven't been anticipated.
% Prevent this stuff happning recursively.
\else%
% \tikz@addmode installs the modes' (e.g., fill, draw, shade)
% to be applied to the path. It isn't usualy for doing more
% changes to the path's construction.
% Get the boudning box of the current path size including the fading sep
% Clear the path
\pgfsetpath\pgfutil@empty%
% Interrupt the path and picture to create a fading.
\pgfinterruptpath%
\pgfinterruptpicture%
% Set the softpath. Any transformations in #1 will have no effect.
% This will *not* update the bounding box...
% ...so it is done manually.
\expandafter\pgf@protocolsizes{\the\pgf@x}{\the\pgf@y}%
\expandafter\pgf@protocolsizes{\the\pgf@x}{\the\pgf@y}%
};
% Now get the bounding of the picture.
%
\endpgfinterruptpicture%
\endpgfinterruptpath%
% Shift the fading to the mid point of the rectangle
}%
\fi%
}
}

\begin{document}

\begin{figure}[H]
\centering
\begin{tikzpicture}
\begin{axis}[
ticks=none,
grid=none,
xmin=0,xmax=100,
ymin=0,ymax=100,
axis lines = middle,
set layers,
xlabel={$t$},ylabel={$T$},
x label style={at={(1,0)},right},
y label style={at={(0,1)},above},
%colormap={redblue}{rgb255(0cm)=(255,0,0); rgb255(1cm)=(0,0,255)}
]
\path [left color=red, right color=blue, shading path={draw=transparent!0, ultra thick,}]
(20,90) to[out=270,in=110] (25,60) to[out=335,in=115] (35,45) to[out=0,in=180] (70,45) to[out=305,in=150] (90,20);
%    \draw [ultra thick] (20,90) to[out=270,in=110] (25,60) to[out=335,in=115] (35,45) to[out=0,in=180] (70,45) to[out=305,in=150] (90,20);
\node[inner sep=0pt, label={[outer sep=-2pt]45:A}] at (20,90) {} ;
\node[label={[outer sep=-2pt]45:B}] at (25,60) {} ;
\node[label={[outer sep=-2pt]45:C}] at (35,45) {} ;
\node[label={[outer sep=-2pt]45:D}] at (70,45) {} ;
\node[label={[outer sep=-2pt]45:E}] at (90,20) {} ;
coordinates {
(20,90) (25,60) (35,45) (70,45) (90,20)
};
\end{axis}
\end{tikzpicture}
\end{figure}

\end{document}


For a multicolor gradient there is also a possibility. The aforementioned solution pass a gradient of two colors to the line color with the commands of the Shading library (Chapter 69, p.737). Therefore, if we are able to pass a multicolor gradient to Mark Wibrow´s solution, we have it done. Thankfully, Mark Wibrow gives us also the solution in the answer to How to draw gradient arrows with Tikz. Just combining both of them we can set a desired multicolor gradient.

Output with shading1:

Output with shading2:

Output with shading3:

Code:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{makecell}
\setcellgapes{5pt}
\usepackage{booktabs}
\usepackage{amsmath,amssymb,amsfonts,systeme,mathtools}
\usepackage{pgfplots}
\usepackage{float}

%https://tex.stackexchange.com/questions/137357/how-to-draw-an-arrow-with-two-colors
\makeatletter

\tikzset{
% There are three circumstances in which the fading sep is needed:
% 1. Arrows which do not update the bounding box (which is most of them).
% 2. Line caps/joins and mitres that extend outside the natural bounding
%    box of the path (these are not calculated by PGF).
% 3. Other reasons that haven't been anticipated.
% Prevent this stuff happning recursively.
\else%
% to be applied to the path. It isn't usualy for doing more
% changes to the path's construction.
% Get the boudning box of the current path size including the fading sep
% Clear the path
\pgfsetpath\pgfutil@empty%
% Interrupt the path and picture to create a fading.
\pgfinterruptpath%
\pgfinterruptpicture%
% Set the softpath. Any transformations in #1 will have no effect.
% This will *not* update the bounding box...
% ...so it is done manually.
\expandafter\pgf@protocolsizes{\the\pgf@x}{\the\pgf@y}%
\expandafter\pgf@protocolsizes{\the\pgf@x}{\the\pgf@y}%
};
% Now get the bounding of the picture.
%
\endpgfinterruptpicture%
\endpgfinterruptpath%
% Shift the fading to the mid point of the rectangle
}%
\fi%
}
}
\makeatother
%-------------------------------------------------------------------------------------
\makeatletter
\pgfutil@tempcnta=0\relax
\ifnum\pgfutil@tempcnta=1\relax%
\edef\pgf@spec{color(0)=(#3);color(100)=(#3)}%
\else%
\pgfmathparse{50/(\pgfutil@tempcnta-1)}\let\pgf@step=\pgfmathresult%
%
\pgfutil@tempcntb=1\relax%
\pgfutil@for\pgf@tmp:={#3}\do{%
\ifnum\pgfutil@tempcntb=1\relax%
\edef\pgf@spec{color(0)=(\pgf@tmp);color(25)=(\pgf@tmp)}%
\else%
\ifnum\pgfutil@tempcntb<\pgfutil@tempcnta\relax%
\pgfmathparse{25+\pgf@step/4+(\pgfutil@tempcntb-1)*\pgf@step}%
\edef\pgf@spec{\pgf@spec;color(\pgfmathresult)=(\pgf@tmp)}%
\else%
\edef\pgf@spec{\pgf@spec;color(75)=(\pgf@tmp);color(100)=(\pgf@tmp)}%
\fi%
\fi%
}%
\fi%
}
\makeatother

\begin{document}

\begin{figure}[H]
\centering
\begin{tikzpicture}
\begin{axis}[
ticks=none,
grid=none,
xmin=0,xmax=100,
ymin=0,ymax=100,
axis lines = middle,
set layers,
xlabel={$t$},ylabel={$T$},
x label style={at={(1,0)},right},
y label style={at={(0,1)},above},
%  colormap={redblue}{rgb255(0cm)=(255,0,0); rgb255(1cm)=(0,0,255)}
]
(20,90) to[out=270,in=110] (25,60) to[out=335,in=115] (35,45) to[out=0,in=180] (70,45) to[out=305,in=150] (90,20);
%    \draw [ultra thick] (20,90) to[out=270,in=110] (25,60) to[out=335,in=115] (35,45) to[out=0,in=180] (70,45) to[out=305,in=150] (90,20);
\node[inner sep=0pt, label={[outer sep=-2pt]45:A}] at (20,90) {} ;
\node[label={[outer sep=-2pt]45:B}] at (25,60) {} ;
\node[label={[outer sep=-2pt]45:C}] at (35,45) {} ;
\node[label={[outer sep=-2pt]45:D}] at (70,45) {} ;
\node[label={[outer sep=-2pt]45:E}] at (90,20) {} ;
coordinates {
(20,90) (25,60) (35,45) (70,45) (90,20)
};
\end{axis}
\end{tikzpicture}
\end{figure}

\end{document}

• Really thank you! Jun 30, 2019 at 11:24

This is a slightly different approach than in Ñako's nice answer in that you can specify the color (and the line width) as a function of the fraction of the path. You may think of this as a version of point meta, in which you can specify the meta as a function of the decorated path length. This code is based on this answer as well as this answer. The first example uses a linear dependence on the path length fraction, and the second one an oscillating function, and also features a varying line width. Please note also that you can use point meta to achieve this (see e.g. this answer), but then you cannot use bend left and friends.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\usetikzlibrary{decorations}
\usepackage{float}
\pgfkeys{/pgf/decoration/.cd,
start color/.store in=\startcolor,
start color=black,
end color/.store in=\endcolor,
end color=black,
varying line width steps/.initial=100
}
\pgfdeclaredecoration{width and color change}{initial}{
\state{initial}[width=0pt, next state=line, persistent precomputation={%
\pgfmathparse{\pgfdecoratedpathlength/\pgfkeysvalueof{/pgf/decoration/varying line width steps}}%
\let\increment=\pgfmathresult%
\def\x{0}%
}]{}
\state{line}[width=\increment pt,   persistent postcomputation={%
\pgfmathsetmacro{\x}{\x+\increment}
},next state=line]{%
\pgfmathparse{varyinglw(\x/\pgfdecoratedpathlength)}
\pgfsetlinewidth{\pgfmathresult pt}%
\pgfpathmoveto{\pgfpointorigin}%
\pgfmathsetmacro{\steplength}{1.4*\increment}
\pgfpathlineto{\pgfqpoint{\steplength pt}{0pt}}%
\pgfmathsetmacro{\y}{varyingcolor(100*(\x/\pgfdecoratedpathlength))}
\pgfsetstrokecolor{\endcolor!\y!\startcolor}%
\pgfusepath{stroke}%
}
\state{final}{%
\pgfmathparse{varyinglw(1)}
\pgfsetlinewidth{\pgfmathresult pt}%
\pgfpathmoveto{\pgfpointorigin}%
\pgfmathsetmacro{\y}{varyingcolor(100*(\x/\pgfdecoratedpathlength))}
\color{\endcolor!\y!\startcolor}%
\pgfusepath{stroke}%
}
}
\begin{document}

\begin{figure}[H]
\centering
\begin{tikzpicture}[declare function={varyinglw(\x)=1;varyingcolor(\x)=\x;}]
\begin{axis}[
ticks=none,
grid=none,
xmin=0,xmax=100,
ymin=0,ymax=100,
axis lines = middle,
set layers,
xlabel={$t$},ylabel={$T$},
x label style={at={(1,0)},right},
y label style={at={(0,1)},above},
colormap={redblue}{rgb255(0cm)=(255,0,0); rgb255(1cm)=(0,0,255)}
]
\draw [decorate,decoration={width and color change,
start color=red,end color=blue}] (20,90) to[out=270,in=110] (25,60) to[out=335,in=115] (35,45) to[out=0,in=180] (70,45) to[out=305,in=150] (90,20);
\node[inner sep=0pt, label={[outer sep=-2pt]45:A}] at (20,90) {} ;
\node[label={[outer sep=-2pt]45:B}] at (25,60) {} ;
\node[label={[outer sep=-2pt]45:C}] at (35,45) {} ;
\node[label={[outer sep=-2pt]45:D}] at (70,45) {} ;
\node[label={[outer sep=-2pt]45:E}] at (90,20) {} ;
coordinates {
(20,90) (25,60) (35,45) (70,45) (90,20)
};
\end{axis}
\end{tikzpicture}
\end{figure}

\begin{figure}[H]
\centering
\begin{tikzpicture}[declare
function={varyinglw(\x)=2+sin(360*\x);varyingcolor(\x)=50+50*sin(7.2*\x);}]
\begin{axis}[
ticks=none,
grid=none,
xmin=0,xmax=100,
ymin=0,ymax=100,
axis lines = middle,
set layers,
xlabel={$t$},ylabel={$T$},
x label style={at={(1,0)},right},
y label style={at={(0,1)},above},
colormap={redblue}{rgb255(0cm)=(255,0,0); rgb255(1cm)=(0,0,255)}
]
\draw [decorate,decoration={width and color change,
start color=red,end color=blue}] (20,90) to[out=270,in=110] (25,60) to[out=335,in=115] (35,45) to[out=0,in=180] (70,45) to[out=305,in=150] (90,20);
\node[inner sep=0pt, label={[outer sep=-2pt]45:A}] at (20,90) {} ;
\node[label={[outer sep=-2pt]45:B}] at (25,60) {} ;
\node[label={[outer sep=-2pt]45:C}] at (35,45) {} ;
\node[label={[outer sep=-2pt]45:D}] at (70,45) {} ;
\node[label={[outer sep=-2pt]45:E}] at (90,20) {} ;