4

I would like to draw :

enter image description here

To draw the above torus, I have used the following codes :

\documentclass[margin=2mm,tikz]{standalone}
\usepackage{pgfplots}
\begin{document}
%Oberflächenproblem
\begin{tikzpicture}[rotate=180]
%Torus
\draw (0,0) ellipse (1.6 and .9);
%Hole
\begin{scope}[scale=.8]
\path[rounded corners=24pt] (-.9,0)--(0,.6)--(.9,0) (-.9,0)--(0,-.56)--(.9,0);
\draw[rounded corners=28pt] (-1.1,.1)--(0,-.6)--(1.1,.1);
\draw[rounded corners=24pt] (-.9,0)--(0,.6)--(.9,0);
\end{scope}
%Cut 1
\draw[densely dashed] (0,-.9) arc (270:90:.2 and .365);
\draw (0,-.9) arc (-90:90:.2 and .365);
%Cut 2
\draw (0,.9) arc (90:270:.2 and .348);
\draw[densely dashed] (0,.9) arc (90:-90:.2 and .348);
\end{tikzpicture}
\end{document}  

It produces:

enter image description here

This is not the same what I want. How can I do the desired torus?

4
  • I have no idea about asymptote. If the figure is possible with an asymptote, then I have no problem@ marmot.
    – MKS
    Mar 9, 2019 at 2:55
  • This site is full of examples using asymptote for that. I did a quick search, and for instance tex.stackexchange.com/a/135438/121799 looks already rather close to what you want. If you do a google image search for site:tex.stackexchange.com asymptote torus you will find many more examples.
    – user121799
    Mar 9, 2019 at 3:16
  • Given the number of different questions and answers here relating to drawing tori, I think this question would benefit from a bit of clarity as to what aspects of your picture you regard as crucial and which aren't important. For example, is the shading important? Is the position of the ellipses important? What, exactly, is wrong with what you've produced to date? Mar 9, 2019 at 9:52
  • Why is "your" code so similar to tex.stackexchange.com/a/155724/121799 ?
    – user121799
    Mar 9, 2019 at 23:09

1 Answer 1

11

The question of how to draw a torus with TikZ is a rather old one and has several excellent answers. And the most spectacular outputs have (IMHO) been achieved with asymptote, which has, unlike TikZ, a 3d engine. However, it turns out that if one aims at 3d vector graphics, the efforts required drawing 3d tori are more substantial than one may naively expect.

This raises the question of whether or not it is possible to make TikZ discriminate between visible and "hidden" points on the torus surface. After all, the analogous discrimination has been achieved for spheres. The answer is yes.

Part I of the answer: how can one draw the contour of a torus? Given a parametization of the torus, T(\u,\v)=(cos(\u)*(\R + \r*cos(\v),(\R + \r*cos(\v))*sin(\u),\r*sin(\v)), one can compute the tangents and then the normal at a given point. The boundary of the torus is determined by the requirement that the normal be orthogonal to the normal of the screen. The resulting curve is then a function T(\u,vcrit(\u)). The critical \v values have a very simple representation:

vcrit1(\u,\th)=atan(tan(\th)*sin(\u));% first critical v value
vcrit2(\u,\th)=180+atan(tan(\th)*sin(\u));% second critical v value

They determine where the visible and/or hidden pieces of cycles wrapping the torus start or end. Note, however, that the contour vcrit2 may, depending on the view angle \tdplotmaintheta, have self-interactions. This is why there is a discriminant in the below code.

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{tikz-3dplot}
\begin{document}
\tdplotsetmaincoords{70}{0}
\tikzset{declare function={torusx(\u,\v,\R,\r)=cos(\u)*(\R + \r*cos(\v)); 
torusy(\u,\v,\R,\r)=(\R + \r*cos(\v))*sin(\u);
torusz(\u,\v,\R,\r)=\r*sin(\v);
vcrit1(\u,\th)=atan(tan(\th)*sin(\u));% first critical v value
vcrit2(\u,\th)=180+atan(tan(\th)*sin(\u));% second critical v value
disc(\th,\R,\r)=((pow(\r,2)-pow(\R,2))*pow(cot(\th),2)+% 
pow(\r,2)*(2+pow(tan(\th),2)))/pow(\R,2);% discriminant
umax(\th,\R,\r)=ifthenelse(disc(\th,\R,\r)>0,asin(sqrt(abs(disc(\th,\R,\r)))),0);
}}

\begin{tikzpicture}[tdplot_main_coords]
\pgfmathsetmacro{\R}{4}
\pgfmathsetmacro{\r}{1}
 \draw[thick,fill=gray,even odd rule,fill opacity=0.2] plot[variable=\x,domain=0:360,smooth,samples=71]
 ({torusx(\x,vcrit1(\x,\tdplotmaintheta),\R,\r)},
 {torusy(\x,vcrit1(\x,\tdplotmaintheta),\R,\r)},
 {torusz(\x,vcrit1(\x,\tdplotmaintheta),\R,\r)}) 
 plot[variable=\x,
 domain={-180+umax(\tdplotmaintheta,\R,\r)}:{-umax(\tdplotmaintheta,\R,\r)},smooth,samples=51]
 ({torusx(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusy(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusz(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)})
 plot[variable=\x,
 domain={umax(\tdplotmaintheta,\R,\r)}:{180-umax(\tdplotmaintheta,\R,\r)},smooth,samples=51]
 ({torusx(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusy(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusz(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)});
 \draw[thick] plot[variable=\x,
 domain={-180+umax(\tdplotmaintheta,\R,\r)/2}:{-umax(\tdplotmaintheta,\R,\r)/2},smooth,samples=51]
 ({torusx(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusy(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusz(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)});
 \foreach \X  in {240,300}  
 {\draw[thick,dashed] 
  plot[smooth,variable=\x,domain={360+vcrit1(\X,\tdplotmaintheta)}:{vcrit2(\X,\tdplotmaintheta)},samples=71]   
 ({torusx(\X,\x,\R,\r)},{torusy(\X,\x,\R,\r)},{torusz(\X,\x,\R,\r)});
 \draw[thick] 
  plot[smooth,variable=\x,domain={vcrit2(\X,\tdplotmaintheta)}:{vcrit1(\X,\tdplotmaintheta)},samples=71]   
 ({torusx(\X,\x,\R,\r)},{torusy(\X,\x,\R,\r)},{torusz(\X,\x,\R,\r)})
 node[below]{$C\ifnum\X=300 '\fi$};
 }
 \draw[thick] plot[smooth,variable=\x,domain=60:420,samples=71]   
 ({torusx(-15+15*cos(\x),80+45*sin(\x),\R,\r)},
 {torusy(-15+15*cos(\x),80+45*sin(\x),\R,\r)},
 {torusz(-15+15*cos(\x),80+45*sin(\x),\R,\r)})
 node[above left]{$C''$};
\end{tikzpicture}
\end{document}

enter image description here

As you can see, the visible (solid) or hidden (dashed) contours run between vcrit1 and vcrit2, which are functions of \u and the view angle.

One can then vary the positions of the cycle(s) and the view angle.

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{tikz-3dplot}
\begin{document}
\foreach \X in {0,10,...,350}
{\tdplotsetmaincoords{65+10*sin(\X)}{0}
\tikzset{declare function={torusx(\u,\v,\R,\r)=cos(\u)*(\R + \r*cos(\v)); 
torusy(\u,\v,\R,\r)=(\R + \r*cos(\v))*sin(\u);
torusz(\u,\v,\R,\r)=\r*sin(\v);
vcrit1(\u,\th)=atan(tan(\th)*sin(\u));% first critical v value
vcrit2(\u,\th)=180+atan(tan(\th)*sin(\u));% second critical v value
disc(\th,\R,\r)=((pow(\r,2)-pow(\R,2))*pow(cot(\th),2)+% 
pow(\r,2)*(2+pow(tan(\th),2)))/pow(\R,2);% discriminant
umax(\th,\R,\r)=ifthenelse(disc(\th,\R,\r)>0,asin(sqrt(abs(disc(\th,\R,\r)))),0);
}}

\begin{tikzpicture}[tdplot_main_coords]
 \pgfmathsetmacro{\R}{4}
 \pgfmathsetmacro{\r}{1}
 \path[tdplot_screen_coords,use as bounding box]
  (-1.3*\R,-1.3*\R) rectangle (1.3*\R,1.3*\R);
 \draw[thick,fill=gray,even odd rule,fill opacity=0.2] plot[variable=\x,domain=0:360,smooth,samples=71]
 ({torusx(\x,vcrit1(\x,\tdplotmaintheta),\R,\r)},
 {torusy(\x,vcrit1(\x,\tdplotmaintheta),\R,\r)},
 {torusz(\x,vcrit1(\x,\tdplotmaintheta),\R,\r)}) 
 plot[variable=\x,
 domain={-180+umax(\tdplotmaintheta,\R,\r)}:{-umax(\tdplotmaintheta,\R,\r)},smooth,samples=51]
 ({torusx(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusy(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusz(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)})
 plot[variable=\x,
 domain={umax(\tdplotmaintheta,\R,\r)}:{180-umax(\tdplotmaintheta,\R,\r)},smooth,samples=51]
 ({torusx(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusy(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusz(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)});
 \draw[thick] plot[variable=\x,
 domain={-180+umax(\tdplotmaintheta,\R,\r)/2}:{-umax(\tdplotmaintheta,\R,\r)/2},smooth,samples=51]
 ({torusx(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusy(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)},
 {torusz(\x,vcrit2(\x,\tdplotmaintheta),\R,\r)});
 \draw[thick,dashed] 
  plot[smooth,variable=\x,domain={360+vcrit1(\X,\tdplotmaintheta)}:{vcrit2(\X,\tdplotmaintheta)},samples=71]   
 ({torusx(\X,\x,\R,\r)},{torusy(\X,\x,\R,\r)},{torusz(\X,\x,\R,\r)});
 \draw[thick] 
  plot[smooth,variable=\x,domain={vcrit2(\X,\tdplotmaintheta)}:{vcrit1(\X,\tdplotmaintheta)},samples=71]   
 ({torusx(\X,\x,\R,\r)},{torusy(\X,\x,\R,\r)},{torusz(\X,\x,\R,\r)});
\end{tikzpicture}}
\end{document}

enter image description here

The current limitations are:

  1. The theta angle has to be larger than 90 degrees and large enough that the torus has hole. (This restriction has been lifted in this post.)
  2. The phi angle is 0. This is not a true limitation because of the symmetry of the torus. It could be overcome by shifting all \v values by minus \tdplotmainphi, if this is necessary (but at this point I do not see a motivation for this).

With all these preparations we can tackle the second part of the question, namely how to achieve a shading. As long as one does not insist on a realistic shading, one can use e.g. this answer. The main purpose of this discussion is not the shading, but the question how to use the above with pgfplots. To my own surprise it is absolutely straightforward. This is because pgfplots is extremely well written and all the necessary angles are stored in pgf keys.

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\tikzset{declare function={torusx(\u,\v,\R,\r)=cos(\u)*(\R + \r*cos(\v)); 
torusy(\u,\v,\R,\r)=(\R + \r*cos(\v))*sin(\u);
torusz(\u,\v,\R,\r)=\r*sin(\v);
vcrit1(\u,\th)=atan(tan(\th)*sin(\u));% first critical v value
vcrit2(\u,\th)=180+atan(tan(\th)*sin(\u));% second critical v value
disc(\th,\R,\r)=((pow(\r,2)-pow(\R,2))*pow(cot(\th),2)+% 
pow(\r,2)*(2+pow(tan(\th),2)))/pow(\R,2);% discriminant
umax(\th,\R,\r)=ifthenelse(disc(\th,\R,\r)>0,asin(sqrt(abs(disc(\th,\R,\r)))),0);
}}
\begin{document}
\begin{tikzpicture}
\pgfmathsetmacro{\R}{4}
\pgfmathsetmacro{\r}{1}
    \begin{axis}[colormap/blackwhite,
       view={30}{60},axis lines=none
       ]
       \addplot3[surf,shader=interp,
       samples=61, point meta=z+sin(2*y),
       domain=0:360,y domain=0:360,
       z buffer=sort]
       ({torusx(x,y,\R,\r)}, 
        {torusy(x,y,\R,\r)}, 
        {torusz(x,y,\R,\r)});
        \pgfplotsinvokeforeach{300,360}{%
          \draw[thick,dashed] 
            plot[smooth,variable=\x,domain={360+vcrit1(#1-\pgfkeysvalueof{/pgfplots/view/az},\pgfkeysvalueof{/pgfplots/view/el})}:{vcrit2(#1-\pgfkeysvalueof{/pgfplots/view/az},\pgfkeysvalueof{/pgfplots/view/el})},samples=71]   
            ({torusx(#1-\pgfkeysvalueof{/pgfplots/view/az},\x,\R,\r)},{torusy(#1-\pgfkeysvalueof{/pgfplots/view/az},\x,\R,\r)},{torusz(#1-\pgfkeysvalueof{/pgfplots/view/az},\x,\R,\r)});
          \draw[thick] 
           plot[smooth,variable=\x,domain={vcrit2(#1-\pgfkeysvalueof{/pgfplots/view/az},\pgfkeysvalueof{/pgfplots/view/el})}:{vcrit1(#1-\pgfkeysvalueof{/pgfplots/view/az},\pgfkeysvalueof{/pgfplots/view/el})},samples=71]   
          ({torusx(#1-\pgfkeysvalueof{/pgfplots/view/az},\x,\R,\r)},{torusy(#1-\pgfkeysvalueof{/pgfplots/view/az},\x,\R,\r)},{torusz(#1-\pgfkeysvalueof{/pgfplots/view/az},\x,\R,\r)})
          node[below]{$C\ifnum#1=360 '\fi$};
        }
        \draw[thick] plot[smooth,variable=\x,domain=60:420,samples=71]   
        ({torusx(25+15*cos(\x),80+45*sin(\x),\R,\r)},
        {torusy(25+15*cos(\x),80+45*sin(\x),\R,\r)},
        {torusz(25+15*cos(\x),80+45*sin(\x),\R,\r)})
        node[above left]{$C''$};
   \end{axis}
\end{tikzpicture}
\end{document}

enter image description here

5
  • Marmot perhaps yours and tex.stackexchange.com/questions/119386/… could be combined with tex.stackexchange.com/questions/348/… P.S. I also gave a minor tweak to tex.stackexchange.com/questions/428394/shading-a-torus-in-tikz beware I got borderline memory failures with your choice of 60 samples on the other one and this one has 61 samples which also failed for me unless dropping to 50 samples but it may differ for each users distro settings
    – user170109
    Mar 10, 2019 at 0:27
  • @KJO I am afraid I cannot combine this with asymptote code. And yes, the memory limitations are unfortunate. One may only draw the visible parts to save a bit.
    – user121799
    Mar 10, 2019 at 0:42
  • marmot I was not comparing just suggesting that all toroid answers end up as how do I draw "my" thoughts of a toroid irrespective of building technique thankfully no one has dared ask for a 3,8 knot or did I?
    – user170109
    Mar 10, 2019 at 0:47
  • @KJO Not yet. But 3.8 knots seem to be more feasible than 4-\varepsilon dimensions for noninteger \varepsilon. ;-)
    – user121799
    Mar 10, 2019 at 0:53
  • That sound like a painful condition is it catching :-)
    – user170109
    Mar 10, 2019 at 0:55

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