There are two ways to consider a two-dimensional torus. One way is to take a parallelogram (let's say the square $[0, 1]^2$) and topologically glue the opposite edges. Another way is to look at the surface of a doughnut with one hole.

I would like to draw the zero set of a doubly-periodic function as a contour on a torus. Here is how I do it when viewing the torus as a square:

Contour on torus

However, I'd really like to see a contour on the surface of a doughnut. How can this be done? (Generally, I'd like to transplant the square $[0, 1]^2$ of any graphic onto the surface of a doughnut.)


4 Answers 4


You could also use MeshFunctions option to map the $[0,1]^2$ region:

yourFunc = Function[{u, v},
                    Re[2 Exp[2 π I (u + 2 v)] + 3 Exp[2 π I (u - 2 v)]]

                  (2 + Cos[2 π v]) Sin[2 π u],
                  (2 + Cos[2 π v]) Cos[2 π u],
                  Sin[2 π v]},
                 {u, 0, 1}, {v, 0, 1},
       MeshFunctions -> Function[{x, y, z, u, v}, yourFunc[u, v]],
       Mesh -> {{0}}, (* Because you state yourFunc[u,v] = 0 *)
       MeshStyle -> Directive[Blue, Thick],
       PlotPoints -> 50


Another fancy example:

                  (3 + Cos[2 π v]) Sin[2 π u],
                  (3 + Cos[2 π v]) Cos[2 π u],
                  Sin[2 π v]},
                 {u, 0, 1}, {v, 0, 1},
          MeshFunctions -> Function[{x, y, z, u, v}, yourFunc[v, u]],
          Mesh -> {Range[-1, 1, .1]},
          MeshStyle -> None,
          MeshShading -> Join[{None},
            ColorData["Rainbow"] /@ Rescale[Most@Range[-1, 1, .1]],
          PlotPoints -> 100,
          PlotStyle -> None,
          Lighting -> "Neutral"



One easy way is to use the Line's from your parametric curves and map them onto a torus mathematically. What basically happens here is that we interpret the points of your plot no longer as Cartesian coordinates but as angles $\phi$ and $\theta$ of the torus parametrisation. Here the function for this parametrisation:

With[{rr = 3, r = 1},
 torus[{u_, v_}] := {(rr + r*Cos[2 Pi u])*Cos[2 Pi v], 
   (rr + r*Cos[2 Pi u])*Sin[2 Pi v], r*Sin[2 Pi u]}

rr is the inner radius and r the radius of the tube. Now let's take your plot and do the transformation. In the following code, all the magic happens at where I extract the lines of gr using Cases and then transform them into Tube's using our torus function.

gr = ContourPlot[Re[2 Exp[2 Pi I (x + 2 y)] + 3 Exp[2 Pi I (x - 2 y)]] == 0, 
  {x, 0, 1}, {y, 0, 1}];
 ParametricPlot3D[torus[{u, v}], {u, 0, 1}, {v, 0, 1}, 
  PlotStyle -> Directive[Opacity[.3], Red], Mesh -> None],
   Cases[Normal[gr], Line[__], Infinity] /. 
    Line[pts_] :> Tube[torus /@ pts]}]

contour on torus


For some applications, it might be useful not to use the conventional parametrization of the torus. In particular, if one wants a conformal (angle-preserving) map from a rectangular domain to a torus, one can use the parametric equations (see e.g. Sullivan's paper):

$$\mathbf r(u,v)=\frac1{\sqrt{s^2+t^2}-t\cos\tfrac{2\pi v}{t}}\begin{pmatrix}s\cos\tfrac{2\pi u}{s}\\s\sin\tfrac{2\pi u}{s}\\t\sin\tfrac{2\pi v}{t}\end{pmatrix}$$

with parameter ranges $0\leq u\leq s,\; 0\leq v\leq t$ to conformally map an $s\times t$ rectangle onto a torus.

In particular, for your curve of interest, we can do something similar to Silvia's proposal, but using this parametrization instead. If you want to see the curve only, without the torus backdrop, you can do this:

With[{s = 1, t = 1}, 
     ParametricPlot3D[{s Cos[2 π u/s], s Sin[2 π u/s], t Sin[2 π v/t]}/
                       (Sqrt[s^2 + t^2] - t Cos[2 π v/t]),
                      {u, 0, s}, {v, 0, t}, Mesh -> {{0}},
                      MeshFunctions -> Function[{x, y, z, u, v},
                                      Re[2 Exp[2 π I (u + 2 v)] + 3 Exp[2 π I (u - 2 v)]]],
                      MeshStyle -> Directive[Thick, Blue],
                      PlotPoints -> 55, PlotStyle -> None]]

curve living on a torus

(Change s and t if you want a plot over a wider rectangle.)

If you want to see the torus as well, you can have it be translucent, so that you can still see the structure of the curve. For instance, here's the resulting picture with the setting PlotStyle -> Opacity[3/5, ColorData["Legacy", "PowderBlue"]]:

curve on a glassy torus

  • $\begingroup$ +1 for the math teaching and the reference. $\endgroup$
    – Silvia
    Jun 10, 2013 at 14:04
  • $\begingroup$ (As a tiny note: a lot of my previous Gravatars that were based on mapping parallelogram domains to tori used this parametrization. For instance, here is a conformal hexagonal tiling of a torus. Note that the 120° angles of the hexagons are preserved.) $\endgroup$ Jun 10, 2013 at 14:05
  • 4
    $\begingroup$ What previous gravatars of 0x4A4D? This chap's new here... $\endgroup$
    – rm -rf
    Jun 10, 2013 at 14:16
  • $\begingroup$ (whistles innocently) Uh... right. $\endgroup$ Jun 10, 2013 at 14:41

One way to do this is to map the image of the function onto the surface of the torus. First, let's make your plot of the function into an image:

img = Erosion[Image[
  ContourPlot[3 Cos[2 \[Pi] (u - 2 v)] + 2 Cos[2 \[Pi] (u + 2 v)] == 0,   
  {u, 0, 1}, {v, 0, 1}, Frame -> None]], 1]

enter image description here

and map it onto the surface of a torus:

colorfun = BSplineFunction[ImageData[img], SplineDegree -> 1];
    {(2 + Cos[2 Pi v]) Cos[2 Pi u], (2 + Cos[2 Pi v]) Sin[2 Pi u], Sin[2 Pi v]}, 
    {u, 0, 1}, {v, 0, 1}, ColorFunction -> {colorfun[1 - #5, #4] &}, 
    ViewPoint -> {Pi/2, Pi, 1}, PlotPoints -> 200,  Mesh -> None, Axes -> None]

enter image description here

It's a little easier using the new (version 9) Texture command:

ParametricPlot3D[{(2 + Cos[2 Pi v]) Cos[2 Pi u], 
      (2 + Cos[2 Pi v]) Sin[2 Pi u], Sin[2 Pi v]}, 
      {u, 0, 1}, {v, 0, 1}, PlotStyle -> {Opacity[1], Texture[img]}, 
      PlotPoints -> 100, Mesh -> None, Axes -> None]

enter image description here

Add the option Lighting->"Neutral" to remove the colored shading and get back to the pure white.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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