# Drawing sea shells using PGF/TikZ

I'm trying to draw a sea shell using PGF/TikZ. The shape of the shell is based on a set of parametric equations plotted in 3D. (Source: Math Parametric Equation for Seashell)

For those familiar with MATLAB, I've written some code which gives a working solution:

R=1;    % Radius
N=3.6;  % Number of turns
H=2;    % Height
P=2;    % Power

samples = 100;
[x,y] = meshgrid(0:2*pi/(samples-1):2*pi);

X = (x/(2*pi*R)).*cos(N*x).*(1+cos(y));
Y = (x/(2*pi*R)).*sin(N*x).*(1+cos(y));
Z = (x/(2*pi*R)).*sin(y) + H*(x/(2*pi)).^P;

% PLOTTING
surf(X,Y,Z,X)
set(gca,'ZDir','reverse')
axis off
axis equal
material dull
lighting gouraud
lightangle(80,-40)
lightangle(-90,60)


This is what I've achieved so far in LaTeX, based off this answer from How to draw a Torus:

\documentclass[border=5pt]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.8}
\begin{document}
\begin{tikzpicture}
\pgfmathsetmacro{\R}{1}
\pgfmathsetmacro{\N}{3.6}
\pgfmathsetmacro{\H}{2}
\pgfmathsetmacro{\P}{2}
\begin{axis}
surf,
colormap/cool,
samples=60,
domain=0:2*pi,
y domain=0:2*pi,
z buffer=sort]
({(x/(2*pi*\R))*cos(\N*deg(x))*(1+cos(deg(y)))},
{(x/(2*pi*\R))*sin(\N*deg(x))*(1+cos(deg(y)))},
{(x/(2*pi*\R))*sin(deg(y)) + \H*(x/(2*pi))^\P});
\end{axis}
\end{tikzpicture}
\end{document}


Can anyone help get this looking better? I've been trying to reverse the z axis, make the axes equal and remove the axes lines and labels. Additionally, I'm unsure of the capabilities of PGF/TikZ when it comes to things like shading and lighting. So would be interested to know what can be achieved.

• I'm voting to close this question as off-topic because there is only a little typo in the y component. It should be sin(\N*deg(x)) instead of cos(\N*deg(x)). Then it looks fine, see this screenshot with 100 samples. – Henri Menke May 10 '18 at 23:33
• To mimic the MATLAB figure you can also add z dir=reverse. – Henri Menke May 10 '18 at 23:36
• @HenriMenke How about just writing an answer (in which you in addition switch to a newer version, increase the sample and perhaps change the view)? Why is it off-topic? – marmot May 10 '18 at 23:36
• @marmot Because it is just a typo. I don't see how other users would benefit from me correcting a typo here. – Henri Menke May 10 '18 at 23:38
• Ah sorry I didn't spot this typo! Thanks. I will update my question. But I still have questions about shading/lighting – Milo May 10 '18 at 23:38

If Henri Menke post's his answer, I'll be happy to remove mine. I made a few adjustments, but the most crucial thing is the replacement Henri mentioned first. Other features (all minor compared to the impact of the typo) include:

• shader=interp to have a smooth surface
• adjusting the lighting angle via point meta
• axis equal and unit vector ratio={} to have the axes equal
• changed the viewing angle
• reversed the z direction simply by multiplying the z-component by -1
• removed the axis lines
• changed the color map
• increased the number of samples

Here is the code:

\documentclass[border=5pt]{standalone}
\usepackage{pgfplots}
\pgfplotsset{width=10cm,compat=1.16} %<- changed,
\begin{document}
\begin{tikzpicture}
\pgfmathsetmacro{\R}{1}
\pgfmathsetmacro{\N}{3.6}
\pgfmathsetmacro{\H}{2}
\pgfmathsetmacro{\P}{2}
axis lines=none,%<- added : removes the axes
axis equal,%<- added : makes the axes equal
unit vector ratio={} %<- why did I add this? honestly, I don't know 100%
] % I tried this because of the statement "An empty value unit vector ratio={} disables unit vector rescaling."
% on page 299 of the pgfplots manual
colormap/viridis, %<- changed : closer to the MatLAB picture (?)
samples=60, %<- changed :
domain=0:2*pi,
y domain=0:2*pi,
point meta=z-y, %<- shading : fake a light impact angle
z buffer=sort]
({(x/(2*pi*\R))*cos(\N*deg(x))*(1+cos(deg(y)))},
{(x/(2*pi*\R))*sin(\N*deg(x))*(1+cos(deg(y)))},
{-(x/(2*pi*\R))*sin(deg(y)) - \H*(x/(2*pi))^\P}); %<- minus added
\end{axis}
\end{tikzpicture}
\end{document}


Just for fun: point meta=z-sqrt(y^2+2*x^2)-1.5*y,

But I think it is at least very hard (if not impossible) to achieve something of the quality of J Leon V.'s answer with the current version of pgfplots. (Nevertheless, I am very impressed by that package, yet I'd agree with J Leon V. that asymptote is much better suited to draw such things.)

• I've retracted my downvote and removed my comment. – Henri Menke May 11 '18 at 0:10
• @HenriMenke I think the comment was fair, but thanks nevertheless. – marmot May 11 '18 at 0:11
• Thanks for this @marmot. The z-axis looks very squashed though? I've been reading this tex.stackexchange.com/questions/74680/… – Milo May 11 '18 at 0:12
• @Myles I (partly) "solved" the problem but I still must admit that I do not fully control the width of the plot. – marmot May 11 '18 at 0:39

I know you ask for a solution in PGF or Tikz, and they are good but there are also more powerful tools for this type of graphics for example using Asymptote:

RESULT:

MWE:

% arara: pdflatex: {synctex: yes, action: nonstopmode, shell: yes}
% arara: asymptote
% arara: pdflatex: {synctex: yes, action: nonstopmode, shell: yes}
\documentclass{standalone}
\usepackage{asymptote}
\begin{document}
\begin{asy}
import graph3;
import palette;
size(200,0);
currentprojection=perspective(
camera=(1,-5.3,2),
up=(0,0,1),
target=(0,0,0),
zoom=0.85);

real R=1;
real N=3.6;
real H=2;
real P=1.9;

triple f(pair t) {
return ((t.x/(2*pi*R))*cos(N*t.x)*(1+cos(t.y)),(t.x/(2*pi*R))*sin(N*t.x)*(1+cos(t.y)),(-t.x/(2*pi*R))*sin(t.y) - H*(t.x/(2*pi))^P);
}

surface s=surface(f,(0,0),(2pi,2pi),20,20,Spline);