7

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
shading interp
material dull 
lighting gouraud
lightangle(80,-40)
lightangle(-90,60)

enter image description here

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}
     \addplot3[
         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}

enter image description here

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
9

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}
  \begin{axis}[view={-40}{30},%<- added
         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
     \addplot3[
         surf,shader=interp, %<- added : shading
         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}

enter image description here

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

enter image description here

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
  • 1
    @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
8

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:

enter image description here

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);
        s.colors(palette(s.map(xpart),Gradient(green,blue)));

        draw(s,meshpen=black,render(merge=true));

    \end{asy}
\end{document}

I know tha is powerfull, if you want to compile with arara you can use the following YAML file. in marmot's answer, there he also explains how to compile it normally using shellscape.

You can see examples and download in the asymptote project page, to use it and install here you can obtain good information to achieve it.

  • Oh wow! This looks really great. (+1 of course) – marmot May 11 '18 at 4:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.