22

I'm working on this plot with pgfplots:

Twisted surface

The image is from our sister-site Mathematica.SE and there's also a nice explanation how to do it with Mathematica.

I follow these steps:

  1. Parametrize the 2d cut
  2. Embed it in 3d
  3. Rotate both in a circle and around itself

I originally posted the question on the TeXwelt LaTeX forum in German, my first steps which I wrote in "Drehtransformation mit pgfplots" are:

  1. The 2d cut:

    \documentclass[border=10pt]{standalone}
    \usepackage{pgfplots}
    \usepgfplotslibrary{polar}
    \begin{document}
    \begin{tikzpicture}
      \begin{polaraxis}
        \addplot[mark=none, domain=0:360, samples=100] 
          {sin(3*x) + 1.25}; 
      \end{polaraxis}
    \end{tikzpicture}
    \end{document}
    

    2d plot

  2. Embedding in 3d for better visualizing with some temporary filling:

    \documentclass[border=10pt]{standalone}
    \usepackage{pgfplots}
    \begin{document}
    \begin{tikzpicture}
      \begin{axis}
        \addplot3 [domain=0:360, samples=60, fill=blue!30, opacity=0.8]
          ( {cos(x)*(sin(3*x) + 1.25)},
            {sin(x)*(sin(3*x) + 1.25)}, 0 );
      \end{axis}
    \end{tikzpicture}  
    \end{document}
    

    2d plot embedded in 3d

    No problem also to get a surface with simple 3d expansion:

    \documentclass[border=10pt]{standalone}
    \usepackage{pgfplots}
    \begin{document}
    \begin{tikzpicture}
      \begin{axis}
        \addplot3 [
            surf,
            domain    = 0:360,
            y domain  = 0:360,
            samples   = 50,
            samples y = 20,
          ]
          ( {cos(x)*(sin(3*x) + 1.25)},
            {y}, {sin(x)*(sin(3*x) + 1.25)} );
      \end{axis}
    \end{tikzpicture}  
    \end{document}
    

    3d surface plot

  3. That's my question, because I still need to bend and twist it:

    How can I rotate that 2d plot around a circle, while rotating it at the same time around its origin, to get a surface plot like in the image at the top?

  • There are a particular formula in the answer below. Is there are any reason not to use an equation? – m0nhawk Jul 21 '14 at 14:00
  • @m0nhawk Yes - though a conjured up ready-to-plot but complex function is nice, I would like to learn about applying rotation transformations in 3d. It would be great if we could work out even a generic macro for it. – Stefan Kottwitz Jul 21 '14 at 14:17
21

When I saw your first picture, it immediately made me think of a torus; of course, it's a bit of a twisted torus, but it does have quite a lot in common. As detailed in How to draw a torus (for example), a torus can be parametrized as a surface using (for example)

x(t,s) = (2+cos(t))*cos(s+pi/2) 
y(t,s) = (2+cos(t))*sin(s+pi/2) 
z(t,s) = sin(t)

where t and s both lie on the interval [0,2\pi].

So, how can we apply this to your shape?

Let's start by trying to plot the curve given by the following parametrization in three dimensions:

x(t) = sin(3t)cos(t)
y(t) = sin(3t)sin(t)

A little bit of thought, and application of the torus idea tells us that in your example we can use the following, for example,

  x(t,s) = (4+(sin(3*(t))+1.25)*cos(t))*cos(s) 
  y(t,s) = (4+(sin(3*(t))+1.25)*cos(t))*sin(s) 
  z(t,s) = ((sin(3*(t))+1.25)*sin(t))

Which gives, for a fixed value of s and t\in[0,2\pi]

screenshot

From here, we can allow s to vary - for example, if we allow s\in[0,\pi] then we achieve:

screenshot

This is close, but it doesn't twist enough - for that we can choose, for example

  x(t,s) = (4+(sin(3*(t+Ns))+1.25)*cos(t))*cos(s) 
  y(t,s) = (4+(sin(3*(t+Ns))+1.25)*cos(t))*sin(s) 
  z(t,s) = ((sin(3*(t+Ns))+1.25)*sin(t))

The number N can be changed to increase/decrease the 'twistyness'; for example with N=10 and a fairly low resolution, then we achieve:

screenshot

Finally, you crank up the resolution as much as you can before exhausting memory (running LuaLaTeX really helps - thanks Stefan), and you get the following:

screenshot

This can be improved by using, for example, shader=interp. Here's the complete code to play with :)

% arara: lualatex
% !arara: indent: {overwrite: yes}
\documentclass[border=10pt]{standalone}
\usepackage{pgfplots}

\begin{document}

\begin{tikzpicture}
    \begin{axis}[
            axis equal,
        ]
        \addplot3[
            surf,
            samples=100, samples y=70,
            colormap/cool,
            domain=0:360,y domain=0:360,
            z buffer=sort,
        ]
        ({(4+(sin(3*(x+2*y))+1.25)*cos(x))*cos(y)},
        {(4+(sin(3*(x+2*y))+1.25)*cos(x))*sin(y)},
        {((sin(3*(x+2*y))+1.25)*sin(x))});
    \end{axis}
\end{tikzpicture}

\end{document}

Once we have the template, we can play with it a little more; for example, changing the parametrisation from sin(3(x+2y)) to sin(4(x+2y)) and using the hot2 colormap as follows

    \addplot3[
        surf,
        samples=100, samples y=70,
        colormap/hot2,
        domain=0:360,y domain=0:360,
        z buffer=sort,
    ]
    ({(4+(sin(4*(x+2*y))+1.25)*cos(x))*cos(y)},
    {(4+(sin(4*(x+2*y))+1.25)*cos(x))*sin(y)},
    {((sin(4*(x+2*y))+1.25)*sin(x))});

gives a pleasing picture with a few more twists:

screenshot

  • Very nice! Btw. the memory exhaustion can be avoided simply using LuaLaTeX. I did, and I got surprisingly different results just by changing samples y to 30 and 50. – Stefan Kottwitz Jul 21 '14 at 21:02
  • @StefanKottwitz thanks for noting that - I believe you'll get consistent results with the updated code :) – cmhughes Jul 21 '14 at 21:31
  • 4
    You should consider the additional options shader=flat, miter limit=1. The fine grid lines result in a relatively hard look which is smoothened by shader=flat, and miter limit` avoids the tiny overshoots near edges. – Christian Feuersänger Aug 3 '14 at 11:47

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.