# TikZ: radial shading of a ring

I would like to have a radial shading of a ring, from inner color (red) at inner radius r1 > 0 to outer color (white) at outer radius r2 > r1. But with

\filldraw[even odd rule,inner color=red,outer color=white]
(0,0) circle (2.2)
(0,0) circle (1.8);


the inner circle only masks the shading of the outer circle:

How to choose a finite inner radius for the shading such that the full color starts at r1 = 1.8?

-

## 3 Answers

Do you mean something like the first or the second circle?

The code in which they are realized is:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\filldraw[even odd rule,inner color=red,outer color=white] (0,0) circle (2.2);
\draw(0,0) circle (1.8);
\begin{scope}[xshift=6cm]
\filldraw[even odd rule,inner color=red,outer color=red!5] (0,0) circle (1.8);
\draw(0,0) circle (2.2);
\end{scope}
\end{tikzpicture}
\end{document}


As said by percusse, the only possible approach is to define a new radial shading. Here is an example:

\documentclass{article}
\usepackage{tikz}

\pgfdeclareradialshading{ring}{\pgfpoint{0cm}{0cm}}%
{rgb(0cm)=(1,1,1);
rgb(0.7cm)=(1,1,1);
rgb(0.719cm)=(1,1,1);
rgb(0.72cm)=(0.975,0,0);
rgb(0.9cm)=(1,1,1)}

\begin{document}
\begin{tikzpicture}
\filldraw[shading=ring] (0,0) circle (2.2);
\draw[fill=white](0,0) circle (1.8);

\begin{scope}[xshift=6cm]
\filldraw[shading=ring] (0,0) circle (2.2);
\draw(0,0) circle (1.8);
\end{scope}
\end{tikzpicture}
\end{document}


This gives you:

which I guess is your purpose. Notice that the option fill=white is not really needed, but it has been used to compare the two results.

A simple add to customize colors: the option is identical to what defined in How to shade mindmap concepts?.

The code:

\documentclass{article}
\usepackage{tikz}

\makeatletter
\pgfdeclareradialshading[tikz@ball]{ring}{\pgfpoint{0cm}{0cm}}%
{rgb(0cm)=(1,1,1);
rgb(0.719cm)=(1,1,1);
color(0.72cm)=(tikz@ball);
rgb(0.9cm)=(1,1,1)}
\tikzoption{ring color}{\pgfutil@colorlet{tikz@ball}{#1}\def\tikz@shading{ring}\tikz@addmode{\tikz@mode@shadetrue}}
\makeatother

\begin{document}
\begin{tikzpicture}
\filldraw[shading=ring, ring color=red] (0,0) circle (2.2cm);
\draw(0,0) circle (1.8cm);

\begin{scope}[xshift=7cm]
\filldraw[shading=ring] (0,0) circle (3) circle (2.45);
\end{scope}
\end{tikzpicture}
\end{document}


The result:

-
Dear Claudio, thanks, but neither solution is the one I am looking for. The shading should be only inside the ring between the two circles, nothing inside the inner circle, nor outside the outer circle. – Tiong Bahru Nov 12 '12 at 16:59
Ok, now it's more clear :) I'm editing my answer. – Claudio Fiandrino Nov 12 '12 at 17:24
Perfect. Thanks! – Tiong Bahru Nov 12 '12 at 17:44
@TiongBahru: beware! I would advise that the ring shading is really target with the dimension you provide in your MWE. Thus in case you wonder to reduce/increase the radius you should also change a bit the position of the shading in which the color starts to be red rgb(0.72cm)=(0.975,0,0);. To change the position, just adjust (0.72cm) which is simply the distance from the circle center. – Claudio Fiandrino Nov 12 '12 at 17:50

You need to create the region at one shot and use even odd rule or nonzero rule with some trickery for these applications. Here is one example:

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{fadings}
\pgfdeclareradialshading{myring}{\pgfpointorigin}
{
color(0cm)=(transparent!0);
color(5mm)=(pgftransparent!50);
color(1cm)=(pgftransparent!100)
}
\pgfdeclarefading{ringo}{\pgfuseshading{myring}}

\begin{document}
\begin{tikzpicture}
\filldraw[even odd rule,red ,path fading=ringo] (0,0) circle (16mm) (0,0) circle (2cm);
\filldraw[even odd rule,blue,path fading=ringo] (0,0) circle (3mm) (0,0) circle (0.5cm);
\end{tikzpicture}
\end{document}


-
Thanks. But this example renders a ball shading. I need a color map that depends only on the radial coordinate r, not the angle. What would be a (simple) custom shading that does this job? - I couldn't find the answer in previous posts. – Tiong Bahru Nov 12 '12 at 17:25

Here is a solution that extends Claudio Fiandrino's solution. This solution uses pgfkeys to computes automatically a new shading with different inner and outer radii. The syntax is:

ring shading={from <color> at <inner radius> to <color> at <outer radius>}

(Note: The only thing I still do not understand is: why should we use the value 0.8818cm to get the correct result?)

\documentclass{standalone}
\usepackage{tikz}

\tikzset{
ring shading/.code args={from #1 at #2 to #3 at #4}{
\def\colin{#1}
\def\radin{#2}
\def\colout{#3}
\def\radout{#4}
\pgfmathsetmacro{\proportion}{\radin/\radout}
\pgfmathsetmacro{\outer}{.8818cm}
\pgfmathsetmacro{\inner}{.8818cm*\proportion}
\pgfmathsetmacro{\innerlow}{\inner-0.01pt}
\pgfdeclareradialshading{ring}{\pgfpoint{0cm}{0cm}}%
{
color(0pt)=(white);
color(\innerlow)=(white);
color(\inner)=(#1);
color(\outer)=(#3)
}
\pgfkeysalso{/tikz/shading=ring}
},
}

\begin{document}
\begin{tikzpicture}
\shade[even odd rule,ring shading={from red at 1.8 to white at 2.2}]
(0,0) circle (2.2) circle (1.8);

\shade[even odd rule,ring shading={from lime at 0.5 to blue at 1.3}]
(2,0) circle (0.5) circle (1.3);
\end{tikzpicture}
\end{document}


Added by Andrew Stacey - comments are lousy for posting code here's a condensed version based on investigations into where the .8818cm came from.

\tikzset{
ring shading/.code args={from #1 at #2 to #3 at #4}{
\pgfmathsetmacro{\inner}{25*#2/#4}
\pgfmathsetmacro{\innerlow}{\inner-1}
\pgfdeclareradialshading{ring}{\pgfpoint{0cm}{0cm}}%
{
color(0bp)=(white);
color(\innerlow bp)=(white);
color(\inner bp)=(#1);
color(25bp)=(#3);
color(50bp)=(black)
}
\pgfkeysalso{/tikz/shading=ring}
},
}

-
That's absolutely fantastic. :) – Claudio Fiandrino Nov 13 '12 at 17:45
(This is based on experiment.) A shading needs to be able to shade a generic path. To do that, the shading is resized to be sure to cover the entire path then is clipped against said path. So when the shading is trying to shade the circles, it is actually trying to shade a surrounding rectangle. It is also then clear that the actual distances don't matter, just their proportions. Moreover, since PGF is trying to shade some larger rectangle, it stands to reason that the size of the shading might be bigger than first thought. (ctd) – Loop Space Nov 13 '12 at 18:36
(ctd) Looking at the code for the ball shading, it stops at 50bp. So we can take 50bp as a reasonable guess at the size of the rectangle that PGF will try to shade. It would also seem (from looking at the ball shading) that 25bp is the key length for shading a circle: that is where the edge of the circle will lie in the shading. Now we see where the .8818cm comes from! .8818cm is almost 25pt so \pgfmathparse{.8818cm} results in about 25 (as pgfmath converts lengths to pts). – Loop Space Nov 13 '12 at 18:38
(Hope the edit is okay - posting code in comments doesn't work.) – Loop Space Nov 13 '12 at 18:41
@AndrewStacey Thank for your investigations and experiments. I feel that the method used by PGF to adapt fadings and shadings to the size of the current path is far from optimal. In any case, there is a lack of clear explanations in pgfmanual. Besides, my answer to tex.stackexchange.com/questions/74087/… was asked the same questions... – Paul Gaborit Nov 13 '12 at 22:13