# PGFplots 3d: Creating a filled solid of revolution

I have searched throughout the internet for a solution, but since I'm not that skilled with pgfplots, I need your help. For several examples I want to draw solids of revolution, but filled ones. I started with this one:

\documentclass{scrartcl}

\usepackage{mathtools}
\usepackage{amssymb}
\usepackage{pgfplots}
\pgfplotsset{width=10cm, compat=1.8}

\begin{document}

\begin{center}
\begin{tikzpicture}
\begin{axis}[
title={Rotation um die x-Achse},
colormap/greenyellow,
view={15}{30}]

surf,
samples=50,
domain=0:6,y domain=0:3*pi/2,
z buffer=sort]
(x,{(((2*x^4)/27) - ((4*x^3)/9)) * cos(deg(y))}, {(((2*x^4)/27) - ((4*x^3)/9)) * sin(deg(y))});
\end{axis}
\end{tikzpicture}
\end{center}
\end{document}


It's open on the upper side and you can see "into" the shape. Since I need the volume of the shape in my examples, I wanted them cuts to be solid areas, like you cut them away with a knife. In the end, I like this quarter area to be half opaque, so you know it's the full object, but also be able to see if it's solid inside or hollow.

I have seen this question and I have almost the same problem, but those links and other questions here didn't fit.

I also have some examples, where you need to calculate the volume of two rotated functions, one subtracted from the other. So there would be a hollow inside.

I'm not quite sure how to explain it, maybe I can create a decent drawing to make it clear. I hope you can help me! Thanks!

• Welcome to the site! This sounds like a duplicate of the question you have linked..... I can certainly understand the frustration of not seeing an answer, but to keep the site tidy, we try not to have too many repeated questions. The main thing that prevents an answer to linked question is that the parametrization of the solid is quite tricky; perhaps you could ask for help on that aspect on the math.stackexchange.com, which would enable you to move forward Apr 26, 2014 at 15:45
• Thanks for your quick response. I tried to figure it out on my own, since I just would need to draw two filled 2d-functions of ((2*x^4)/27) - ((4*x^3)/9) into this graph aswell, 90 degrees twisted around x, so it looks like it's "filled". I just can't manage to draw a filled 2d-graph into this 3d-plot Apr 28, 2014 at 16:24
• Here are 2 pictures, to clarify what I mean. The blue and the red graph are 2 filled plots of the 2D function. They are aligned with the "cut out" of the solid of revolution plot, so they are rotated 90° from each other. The second picture shows a version, where the bubble is not filled. Picture 1 Picture 2 Apr 28, 2014 at 17:11
• I'll take a look at these at some point - it should be doable. It might not be for a while, though, perhaps someone else will have some luck :) I'll edit them into your question.. Where did you get these images from? You might contact the author... Apr 28, 2014 at 17:24
• Thank you very much for your effort! Maybe someone here knows how to solve this problem. I made them myself, so there should be no problem. =) Apr 28, 2014 at 19:26

I believe the best way to address the problem of "solid of revolution" would be to use a suitable coordinate system - one in which you can enter the radius, the angle, and the protrusion along the axis.

Here is an attempt to reproduce your first picture:

\documentclass{standalone}

\usepackage{pgfplots}

\pgfplotsset{compat=1.9}

% \usetikzlibrary{}
% \usepgfplotslibrary{}

% defines a new value for 'data cs'.
%
% On input, this "data coordinate system" consists of
% y=<angle>
% z=distance along axis
%
% On output, it will show the z value on the X axis and the radius on
% the Y axis.
% First, swap axis such that we can apply polarrad->cart.
\pgfkeysgetvalue{/data point/x}\X% copy value of /data point/x into \X
\pgfkeysgetvalue{/data point/y}\Y
\pgfkeyslet{/data point/y}\X% copy value of \X into /data point/y
\pgfkeyslet{/data point/x}\Y
\pgfplotsaxistransformcs
{cart}%
%
% Ok, now we have cartesian. Swap axes such that we have them
% along X:
\pgfkeysgetvalue{/data point/x}\X
\pgfkeysgetvalue{/data point/y}\Y
\pgfkeysgetvalue{/data point/z}\Z
\pgfkeyslet{/data point/y}\X
\pgfkeyslet{/data point/z}\Y
\pgfkeyslet{/data point/x}\Z
}%

\begin{document}
\begin{tikzpicture}
\begin{axis}[
title={Rotation um die x-Achse},
colormap/greenyellow,
view={15}{30}]

\def\generatrix{(((2*x^4)/27) - ((4*x^3)/9))}

surf,
samples=25,
domain=0:6,
domain y=0:3*pi/2,
z buffer=sort,
({\generatrix},y,x);

blue,
fill,
fill opacity=0.5,
samples=25,
samples y=1,
domain=0:6,
({\generatrix},0,x) --cycle;

orange,
fill,
fill opacity=0.5,
samples=25,
samples y=1,
domain=0:6,
({\generatrix},3*pi/2,x) --cycle;

\end{axis}
\end{tikzpicture}

\end{document}


I have made use of data cs, the feature of pgfplots which allows to provide the data in a different coordinate system than that of the axis. The function pgfplotsdefinecstransform is only documented in the source code of pgfplots at the time of this writing; it defines a transformation from some input system to some output system. Pgfplots tries to use the available transformations whenever it encounters something like data cs=polarrad along x.

I also defined a macro containing your generatrix. The remaining code draws the surface of revolution as a surface plot which resembles yours.

The second two \addplot3 instructions are 3d parametric line plots (due to samples y=1); they use the same input sequence except that they fix y (which is the angle in our custom data cs).

Regarding your second image, you can make use of the fillbetween library which is shipped with pgfplots (as of version 1.10). Its purpose is to fill the area between two other (line) plots, compare Fill the area between two curves calculated by pgfplots or Fill between two curves in pgfplots. for simple applications.

If I understand correctly, you have some inner generating function and some outer generating function and the inner one defines the hole.

My idea would be to "plot" both with draw=none, name path=<some name> in order to label the resulting paths. Then we can fill the area between them.

Here is that approach:

\documentclass{standalone}

\usepackage{pgfplots}

\pgfplotsset{compat=1.10}

% \usetikzlibrary{}
\usepgfplotslibrary{fillbetween}

% defines a new value for 'data cs'.
%
% On input, this "data coordinate system" consists of
% y=<angle>
% z=distance along axis
%
% On output, it will show the z value on the X axis and the radius on
% the Y axis.
% First, swap axis such that we can apply polarrad->cart.
\pgfkeysgetvalue{/data point/x}\X% copy value of /data point/x into \X
\pgfkeysgetvalue{/data point/y}\Y
\pgfkeyslet{/data point/y}\X% copy value of \X into /data point/y
\pgfkeyslet{/data point/x}\Y
\pgfplotsaxistransformcs
{cart}%
%
% Ok, now we have cartesian. Swap axes such that we have them
% along X:
\pgfkeysgetvalue{/data point/x}\X
\pgfkeysgetvalue{/data point/y}\Y
\pgfkeysgetvalue{/data point/z}\Z
\pgfkeyslet{/data point/y}\X
\pgfkeyslet{/data point/z}\Y
\pgfkeyslet{/data point/x}\Z
}%

\begin{document}
\begin{tikzpicture}
\begin{axis}[
title={Rotation um die x-Achse},
colormap/greenyellow,
view={15}{30}]

\def\generatrix{(((2*x^4)/27) - ((4*x^3)/9))}

surf,
samples=25,
domain=0:6,
domain y=0:3*pi/2,
z buffer=sort,
({\generatrix},y,x);

% A Helper macro such that we do not need to repeat outselfes.
%
% It can be invoked with
% \generatrix[<draw/fill options>]{<angle which defines slice>}{some unique text label}
\newcommand\generateSlice[3][]{%
draw=none,
%blue, fill, fill opacity=0.5,
name path=outline_y#3,
samples=25,
samples y=1,
domain=0:6,
({\generatrix},#2,x);

smooth,
draw=none,
%red, fill, fill opacity=0.5,
name path=inner_y#3,
samples=25,
samples y=1,
domain=0:6,
coordinates {
(0,#2,2)    (-2,#2,3)
(-2.5,#2,4.2) (0,#2,5.2)
};

% typically, the 'fill between' library tries to draw
% its paths in a background layer. Avoid that:
on layer=main,
of=outline_y#3 and inner_y#3];
}

\generateSlice[draw,blue,fill opacity=0.5]{0}{first}

\generateSlice[draw,orange,fill opacity=0.5]{3*pi/2}{second}

\end{axis}
\end{tikzpicture}

\end{document}


In order to avoid repetitions, I used some helper macro for the two slices. I only provide the angle which defines the slice, the draw options, and the unique label on input.

Note that I provided the inner function by means of \addplot3 coordinates {<list>} which is perfectly valid in this context (as is any other 3d input).

Note that this fillbetween stuff is essentially unrelated to solids of revolution as indicated by the links above - but it works seamlessly.

There might be a further useful modification in order to visualize the hole: we could add a second solid of revolution in the middle. To this end, I took my last solution, replaced \addplot3 coordinates by some random inner generatrix and arrived at

\documentclass{standalone}

\usepackage{pgfplots}

\pgfplotsset{compat=1.9}

% \usetikzlibrary{}
\usepgfplotslibrary{fillbetween}

% defines a new value for 'data cs'.
%
% On input, this "data coordinate system" consists of
% y=<angle>
% z=distance along axis
%
% On output, it will show the z value on the X axis and the radius on
% the Y axis.
% First, swap axis such that we can apply polarrad->cart.
\pgfkeysgetvalue{/data point/x}\X
\pgfkeysgetvalue{/data point/y}\Y
\pgfkeyslet{/data point/y}\X
\pgfkeyslet{/data point/x}\Y
\pgfplotsaxistransformcs
{cart}%
%
% Ok, now we have cartesian. Swap axes such that we have them
% along X:
\pgfkeysgetvalue{/data point/x}\X
\pgfkeysgetvalue{/data point/y}\Y
\pgfkeysgetvalue{/data point/z}\Z
\pgfkeyslet{/data point/y}\X
\pgfkeyslet{/data point/z}\Y
\pgfkeyslet{/data point/x}\Z
}%

\begin{document}
\begin{tikzpicture}
\begin{axis}[
title={Rotation um die x-Achse},
colormap/greenyellow,
view={15}{30}]

\def\generatrix{(((2*x^4)/27) - ((4*x^3)/9))}

surf,
samples=25,
domain=0:6,
domain y=0:3*pi/2,
z buffer=sort,
({\generatrix},y,x);

\def\innergeneratrix{-10*(x-2)/2*(1-(x-2)/2)}
surf,
colormap/violet,
samples=7,
domain=2:4,
domain y=0:3*pi/2,
z buffer=sort,
({\innergeneratrix},y,x);

% A Helper macro such that we do not need to repeat outselfes.
%
% It can be invoked with
% \generatrix[<draw/fill options>]{<angle which defines slice>}{some unique text label}
\newcommand\generateSlice[3][]{%
draw=none,
%blue, fill, fill opacity=0.5,
name path=outline_y#3,
samples=25,
samples y=1,
domain=0:6,
({\generatrix},#2,x);

draw=none,
%red, fill, fill opacity=0.5,
name path=inner_y#3,
samples=7,
samples y=1,
domain=2:4,
({\innergeneratrix},#2,x);

% typically, the 'fill between' library tries to draw
% its paths in a background layer. Avoid that:
on layer=main,
of=outline_y#3 and inner_y#3];
}

\generateSlice[draw,blue,fill opacity=0.5]{0}{first}

\generateSlice[draw,orange,fill opacity=0.5]{3*pi/2}{second}

\end{axis}
\end{tikzpicture}

\end{document}

• This is exactly what I wanted! Thank you so much for your answer! It's a pity there is no easier way to do this, but it's not that complex aswell. Thank you, I'll use that! Apr 29, 2014 at 7:44
• I'm glad it addresses your use-case. I agree that the definition on top looks frightening unless one is used to such definitions... one could, of course omit the special coordinate system and stick with cartesian coordinates. Apr 30, 2014 at 7:59
• In order to answer your question completely and in order to advertise the relatively new fillbetween library, I added a solution for your second image as well. Apr 30, 2014 at 7:59

With PSTricks and pst-solides3d. Run it with xelatex or the sequence latex->dvips->ps2pdf (is faster)

\documentclass{article}
\usepackage{pst-solides3d}

\begin{document}
\psset{linewidth=0.5\pslinewidth,viewpoint=80 -75 0 rtp2xyz,lightsrc=viewpoint,Decran=30}
\begin{pspicture}(-1,-5)(5,5)
\defFunction[algebraic]{func}(x,y)
{ x }
{ (x^4 * 2/27 - x^3 * 4/9) * cos(y)}
{ (x^4 * 2/27 - x^3 * 4/9) * sin(y)}
\psSolid[
object=surfaceparametree,
base=0 6 0 Pi 1.5 mul,
hue=0 1,
incolor=yellow,
ngrid=0.2 0.2,
function=func]
\gridIIID[Zmin=-10,Zmax=10,stepX=2,stepY=4,stepZ=2](0,10)(-10,10)
\end{pspicture}
%
\psset{viewpoint=80 -60 0 rtp2xyz}
\begin{pspicture}(-1,-5)(5,5)
\defFunction[algebraic]{func}(x,y)
{ x }
{ (x^4 * 2/27 - x^3 * 4/9) * cos(y)}
{ (x^4 * 2/27 - x^3 * 4/9) * sin(y)}
\psSolid[
object=surfaceparametree,
base=0 6 0 Pi 1.5 mul,
hue=0 1,
incolor=yellow,
ngrid=0.2 0.2,
function=func]
% \gridIIID[Zmin=-10,Zmax=10,stepX=2,stepY=4,stepZ=4](0,10)(-10,10)
\end{pspicture}

\end{document}


• Thank you for solution, but it's not quite what I was looking for. I want the reader to see this solid of revolution is "filled" (because you have to calculate the volume of this function). Apr 29, 2014 at 7:40
• That is no problem. Draw it again with z=0 and then y=0 that's all.
– user2478
Apr 29, 2014 at 7:46
• Oh, I see. I'll look into that as well, thank you! Apr 29, 2014 at 7:50

Thanks to Christian Feuersänger I could solve my problem like I wanted to. I post this answer so everybody who wants to do the same thing or something similar can use this code as a basis. Please give Christan credit for his solution!

\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=1.10}

% \usetikzlibrary{}
% \usepgfplotslibrary{}
%
% defines a new value for 'data cs'.
%
% On input, this "data coordinate system" consists of
% y=<angle>
% z=distance along axis
%
% On output, it will show the z value on the X axis and the radius on
% the Y axis.
% First, swap axis such that we can apply polarrad->cart.
\pgfkeysgetvalue{/data point/x}\X
\pgfkeysgetvalue{/data point/y}\Y
\pgfkeyslet{/data point/y}\X
\pgfkeyslet{/data point/x}\Y
\pgfplotsaxistransformcs
{cart}%
%
% Ok, now we have cartesian. Swap axes such that we have them
% along X:
\pgfkeysgetvalue{/data point/x}\X
\pgfkeysgetvalue{/data point/y}\Y
\pgfkeysgetvalue{/data point/z}\Z
\pgfkeyslet{/data point/y}\X
\pgfkeyslet{/data point/z}\Y
\pgfkeyslet{/data point/x}\Z
}%

\begin{document}
\begin{tikzpicture}
% This creates a color gradient for the filled area of the two functions
rgb(0bp)=(0.1,0.55,0);
rgb(100bp)=(0.8,0.9,0)
}
%
rgb(0bp)=(0.5,0.75,0);
rgb(100bp)=(0,0.5,0)
}
%
\begin{axis}[
title={Rotation um die x-Achse},
colormap/greenyellow,
view={12}{30}]

\def\generatrix{(((2*x^4)/27) - ((4*x^3)/9))}

surf,
samples=30,
domain=0:6,
domain y=0:3*pi/2,
z buffer=sort,
({\generatrix},y,x);

draw=none,
fill opacity=1.0,
samples=30,
samples y=1,
domain=0:6,
({\generatrix},0,x) --cycle;

draw=none,
fill opacity=1.0,
samples=30,
samples y=1,
domain=0:6,
({\generatrix},3*pi/2,x) --cycle;

surf,
opacity = 0.1,
samples=30,
samples y = 10,
domain=0:6,
domain y=3*pi/2:2*pi,
z buffer=sort,

• How can we give credit to the pgfplots author? :) Apr 29, 2014 at 18:05