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?

2
  • There are a particular formula in the answer below. Is there are any reason not to use an equation?
    – m0nhawk
    Jul 21, 2014 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, 2014 at 14:17

2 Answers 2

22

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

3
  • 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, 2014 at 21:02
  • @StefanKottwitz thanks for noting that - I believe you'll get consistent results with the updated code :)
    – cmhughes
    Jul 21, 2014 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. Aug 3, 2014 at 11:47
1

I can't stand drawing this twisted torus with Assymptote.

enter image description here

// Run on http://asymptote.ualberta.ca/
size(6cm);  
import graph3;
import palette;
currentprojection=orthographic(3,2,2.2,zoom=.9);

triple f(pair M) {
real t=M.x, s=M.y;
real d = 1 + sin(4t+8s);  
real a = (6+d*cos(t))*cos(s);
real b = (6+d*cos(t))*sin(s);
real c =    d*sin(t);
return (a,b,c);
}  

pen[] p=Rainbow(30);
//pen[] p=Gradient(magenta,yellow);
surface s=surface(f,(0,0),(2pi,2pi),50,30,Spline);
draw(s,mean(palette(s.map(zpart),p)),black);

More palette can be found here.

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .