15

I want to plot geodesics on a cylinder. These are straight lines, circles or helices. I can plot the first two, but I don't know how to draw the helices.

To be precise, I would like to know how to draw an helix on this cylinder (passing thru two points A and B, but the location of the points is not relevant), using TikZ exclusively (maybe with pgfplots, but without using pstricks):

enter image description here

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric}
\begin{document}
\begin{tikzpicture}
\node [cylinder,rotate=90,draw,aspect=2,minimum width=2cm,minimum height=3.5cm](C){};
\draw[fill] (-0.5,-0.5) circle [radius=0.045]node[below]{$A$};
\draw[fill] (0.5,0.75) circle [radius=0.045]node[below]{$B$};
\end{tikzpicture}
\end{document}
5
  • Would this help? tex.stackexchange.com/a/73584/34618
    – Jesse
    Aug 24, 2013 at 9:21
  • @Jesse I already tried that, but the plots of the cylinder and the helix (using \begin{axis} and \end{axis}) appear separated, instead of superimposed...
    – Grimolatto
    Aug 24, 2013 at 9:25
  • Use begin{tikzpicture}[overlay] and try.
    – Jesse
    Aug 24, 2013 at 9:30
  • OK. With the overlay option the plots superimpose, but with different scalings, and different origins. Note that the axis of my cylinder is the z axis. The addplot+3 figure seems to have its own system of coordinates. The cylinder has radius 1, so a parameterization of the helix with (cos(t),sin(t),t/5) should fit on it. But it does not...
    – Grimolatto
    Aug 24, 2013 at 10:20
  • I think the problem comes from the fact that the cylinder is actually a 2D object (drawn with straight lines and ellipses), and these 2D and 3D descriptions collide...
    – Grimolatto
    Aug 24, 2013 at 10:27

5 Answers 5

9

I like @Gromolatto 's idea. However I believe that drawing hundreds of segments is an overkill. Here is his/her code using "\pgfplotfunction" which simplifies the code and increases precisicion since all points are ploted as they are without defining hunderes of sub-segments.

I left the original code in place and commented the lines that were replaced.

\documentclass{standalone}
\usepackage{tikz}
\usepackage{tikz-3dplot}
\usepackage{pgfplots}
\usetikzlibrary{shapes}
\tdplotsetmaincoords{60}{110}


\begin{document}
\begin{tikzpicture}[tdplot_main_coords]
  \node [cylinder,rotate=90,draw,aspect=2,minimum width=2cm,minimum height=3.5cm](C){};

  %\foreach \t in {-90,-75,...,0}{%
  %  \draw ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+7)},{sin(\t +7)},{-0.23+\t/360});
  %}

  \begin{scope}[color=black, dashed]
  \pgfplothandlerlineto
  \pgfplotfunction{\t}{-90,-89,...,15}
       {\pgfpointxyz {cos(\t)}{sin(\t)}{-0.25+\t/360}} 
       \pgfusepath{stroke}
  \end{scope}

%  \foreach \t in {15,16,...,98}{%
%    \draw[line width=1.5pt,color=red] ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+1)},{sin(\t +1)},{-0.22+\t/360});
%  }

  \begin{scope}[color=red]
  \pgfplothandlerlineto
  \pgfplotfunction{\t}{15,16,...,110}
       {\pgfpointxyz {cos(\t)}{sin(\t)}{-0.25+\t/360}} 
       \pgfusepath{stroke}
  \end{scope}



  %\foreach \t in {110,125,...,280}{%
  %  \draw[line width=1pt,color=red] ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+7)},{sin(\t +7)},{-0.22+\t/360});
  %}


  \begin{scope}[color=red, dashed]
  \pgfplothandlerlineto
  \pgfplotfunction{\t}{110,111,...,303}
       {\pgfpointxyz {cos(\t)}{sin(\t)}{-0.25+\t/360}} 
       \pgfusepath{stroke}
  \end{scope}

 % \foreach \t in {303,304,...,340}{%
 %   \draw[line width=1.6pt,color=red] ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+1)},{sin(\t +1)},{-0.19+\t/360});
 % }


  \begin{scope}[color=red]
  \pgfplothandlerlineto
  \pgfplotfunction{\t}{303,304,...,340}
       {\pgfpointxyz {cos(\t)}{sin(\t)}{-0.25+\t/360}} 
       \pgfusepath{stroke}
  \end{scope}

  %\foreach \t in {355,370}{%
  %  \draw ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+7)},{sin(\t +7)},{-0.23+\t/360});
  %}


  \begin{scope}[color=black, dashed]
  \pgfplothandlerlineto
  \pgfplotfunction{\t}{340,341,...,370}
       {\pgfpointxyz {cos(\t)}{sin(\t)}{-0.25+\t/360}} 
       \pgfusepath{stroke}
  \end{scope}

  \def\ang{340}
  \pgfmathsetmacro\bx{cos(\ang)}
  \pgfmathsetmacro\by{sin(\ang)}
  \pgfmathsetmacro\bz{-0.24+ \ang/360}

  \coordinate (B) at (\bx,\by,\bz);



  \draw[fill] (0.9922,0.25,-0.2) circle [x=1cm,y=1cm,radius=0.045]node[below]{$A$};
  \draw[fill] (B) circle [x=1cm,y=1cm,radius=0.045]node[below]{$B$};
\end{tikzpicture}
\end{document}

Here is the figure obtained:

Helix on a Cylinder

If you make a "zoom" you will see how this figure icreases precision.

1
  • 1
    Wow! Excellent, it solves the resolution problems in my example. I think this version looks much better.
    – Grimolatto
    Nov 8, 2015 at 17:41
17

Here is a tikz-3dplot solution. In case people are interested in drawing a more general helix (not just a geodesic) between two points of the cylinder, I've included a macro called \n to specify the number of additional turns around the cylinder.

Edit: thanks to Qrrbrbirlbel for his very helpful comment.

enter image description here

\documentclass{article}
\usepackage{tikz}
\usepackage{tikz-3dplot}
\begin{document}
\tdplotsetmaincoords{70}{15}
\tikzset{every circle/.append style={x=1cm, y=1cm}}
\begin{tikzpicture}[tdplot_main_coords]

% --- Independent parameters ---
\def\h{3}                          % cylinder height
\pgfmathtruncatemacro\tA{350}      % A angle
\def\zA{1}                         % A applicate
\pgfmathtruncatemacro\tB{150}      % B angle
\def\zB{2}                         % B applicate
\pgfmathtruncatemacro\n{0}         % number of additional turns
\pgfmathtruncatemacro\NbPt{51}     % number of dots for drawing the helix portion
\def\rhelixdots{0.02}              % radius of dots forming helix
\def\rAB{0.05}                     % radius of A and B dots

% --- Draw cylinder ---
% peripheral spokes
\foreach \t in {20,40,...,360} 
{ 
    \draw[gray,very thin,dashed] ({cos(\t)},{sin(\t)},0)
        --({cos(\t)},{sin(\t)},\h);
}

% lower circle
\draw[black,very thin] (1,0,0) 
    \foreach \t in {2,3,...,360}
    {
        --({cos(\t)},{sin(\t)},0)
    }
    --cycle;

% upper circle
\draw[black,very thin] (1,0,\h) 
    \foreach \t in {2,4,...,360}
    {
        --({cos(\t)},{sin(\t)},\h)
    }
    --cycle;


% --- Draw helix ---
\pgfmathsetmacro\tone{\tA}
\pgfmathsetmacro\tlast{\tB+\n*360}
\pgfmathsetmacro\ttwo{\tone+(\tlast-\tone)/(\NbPt-1)}
\pgfmathsetmacro\p{360*(\zB-\zA)/(\tB-\tA+360*\n)}
\foreach \t in {\tone,\ttwo,...,\tlast}{%
    \fill[red] ({cos(\t)},{sin(\t)},{\p*(\t-\tA)/360+\zA}) circle[radius=\rhelixdots];
}

% --- Draw A and B ---
\fill[blue] ({cos(\tA)},{sin(\tA)},\zA) circle [radius=\rAB]node[right]{$A$};
\fill[blue] ({cos(\tB)},{sin(\tB)},\zB) circle [radius=\rAB]node[left]{$B$};

\end{tikzpicture}
\end{document}
2
  • 2
    I'm no expert in tikz-3dplot but without a unit the radius of the circles is taken in the x/y coordinate system of TikZ (which is changed by tikz-3dplot so that coordinates like (<x>, <y>, <z>) work as set up. Either use units (e.g. radius=3pt) or reset the coordinate system: circle[x=1cm, y=1cm, radius=\rAB]. A fix for all circles would be issuing \tikzset{every circle/.append style={x=1cm, y=1cm}}. Aug 24, 2013 at 19:13
  • I see what happens (incompatibility between coordinates)... The problem is that, as already said, I have drawn the other curves without problem, just with TikZ, and this approach forces me to redo everything, including the cylinder. I am surprised that such a simple thing requires such a sophisticated construction. But I'll give it a try!...
    – Grimolatto
    Aug 24, 2013 at 20:23
10

Here is the solution exactly as I wanted it (it is based on the replies by Jubobs and Qrrbrbirlbel, but in a more simplified form). There are 5 different sectors depending on the color and the type of line (dashed or not). The points coordinates are computed so they lie on the helix:

Helix on a cylinder using <code>TikZ</code> and <code>3dplot</code>

\documentclass{standalone}
\usepackage{tikz}
\usepackage{3dplot}
\usetikzlibrary{shapes.geometric}
\tdplotsetmaincoords{60}{110}
\begin{document}
\begin{tikzpicture}[tdplot_main_coords]
\node [cylinder,rotate=90,draw,aspect=2,minimum width=2cm,minimum height=3.5cm](C){};
\foreach \t in {-90,-75,...,0}{%
\draw ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+7)},{sin(\t +7)},{-0.23+\t/360});
}
\foreach \t in {15,16,...,98}{%
\draw[line width=1.5pt,color=red] ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+1)},{sin(\t +1)},{-0.22+\t/360});
}
\foreach \t in {110,125,...,280}{%
\draw[line width=1pt,color=red] ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+7)},{sin(\t +7)},{-0.22+\t/360});
}
\foreach \t in {303,304,...,340}{%
\draw[line width=1.6pt,color=red] ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+1)},{sin(\t +1)},{-0.19+\t/360});
}
\foreach \t in {355,370}{%
\draw ({cos(\t)},{sin(\t)},{-0.25+\t/360})--({cos(\t+7)},{sin(\t +7)},{-0.23+\t/360});
}
\draw[fill] (0.9922,0.25,-0.2) circle [x=1cm,y=1cm,radius=0.045]node[below]{$A$};
\draw[fill] (0.2739,-0.5,0.32) circle [x=1cm,y=1cm,radius=0.045]node[below]{$B$};
\end{tikzpicture}
\end{document}

There are some issues related to the density of lines near the turning points, but these can be easily adjusted by hand. When printed, this is OK:

1
  • Well done. Best of luck on your future tikz adventures.
    – jub0bs
    Aug 27, 2013 at 10:14
8

Here is a solution with pstricks in answer to a similar question:

\documentclass[x11names]{standalone}%
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\pagestyle{empty}
\usepackage{pstricks-add, pst-3dplot}
\usepackage{auto-pst-pdf}
\def\localbasis{\psline{<->}(1,0)(0,0)(0,1)}
\def\\M{6*\pstPI1}
\begin{document}

\footnotesize
\psset{xPlotpoints = 500, plotstyle=curve, linecolor = DarkSeaGreen3, algebraic, arrowinset=0.2, labelsep=3pt, dash=5pt 4pt}
\sffamily
\def\radius{2 }
\def\height {9.8}
\def\thetaend {0}
\def\thetaini {-305}
\def\sp{1.6}
\def\phase{0.7}
\psset{viewpoint=30 40 15 rtp2xyz, IIIDshowgrid = false}
\begin{pspicture}(-6,-2.5)(4,12)
    \pnodes(-4,-0.08){L1}(-4, 8.23){L2}(-2.2,-0.08){C1}(-2.2,8.23){C2}(-3,-0.08){S1}(-3, 3.36){S2}(-2,3.36){C3}(0,-0.08){O}(2,-0.08){R}
    \psCylinder[increment=5, opacity=0.3]{\radius}{\height}
    \ncline[linecolor=Coral1,linewidth=0.5pt]{*->}{O}{R}\nbput{large radius}
    \psset{linewidth=1.2pt, dimen=inner}
    \parametricplotThreeD[arrows=c-](0, 3.10){%
        \radius*cos(t - \phase) | \radius * sin(t - \phase) | t/\sp}
    \parametricplotThreeD[linecolor=DarkSeaGreen3!50!, linestyle=dashed](3.15,6.10){%
        \radius*cos(t-\phase) | \radius* sin(t-\phase) | t/\sp}
    \parametricplotThreeD(6.18, 9.36){%68
        \radius*cos(t-\phase) | \radius * sin(t-\phase) | t/\sp}
    \parametricplotThreeD[linecolor=DarkSeaGreen3!50!, linestyle=dashed](9.40, 12.35){%
        \radius*cos(t-\phase) | \radius * sin(t-\phase) | t/\sp}
    \parametricplotThreeD[arrows=-c](12.45, 15.56){%
        \radius*cos(t-\phase) | \radius * sin(t-\phase) | t/\sp}
    \psset{linewidth=0.5pt, linecolor=Coral1}
    \ncline[offset=6pt]{<->}{L2}{L1}\ncput*{length}
    \ncline{L1}{C1}\ncline{L2}{C2}
    \ncline[offset=-6pt]{<->}{S1}{S2}\ncput*{spacing}
    \ncline{S2}{C3}
\end{pspicture}

\end{document} 

enter image description here

3
+100

This is just to mention that the nice tricks of Fritz' great answer can be used here, too. The mathematics is even simpler for cylinders. This allows one to let pgfplots decide whether some stretch is on the front or in the back, and pgfplots also allows us to shade the thing as we want by just dialing an appropriate point meta.

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{pgfplots} 
\pgfplotsset{compat=1.16} 
\begin{document}
% very much like https://tex.stackexchange.com/a/199715/121799
\pgfplotsset{visible stretch/.style={restrict expr to
domain={sin(atan2(rawy,rawx)-\pgfkeysvalueof{/pgfplots/view/az})}{-1.1:0}},hidden
stretch/.style={restrict expr to
domain={sin(atan2(rawy,rawx)-\pgfkeysvalueof{/pgfplots/view/az})}{0:1.1}}}
\def\addFGBGplot[#1]#2;{
    \addplot3[#1,hidden stretch, opacity=0.25] #2;
    \addplot3[#1,visible stretch] #2;
}
\begin{tikzpicture}[declare function={R=1;H=3;}]
\begin{axis}[hide axis]
 \addplot3[surf,shader=interp,domain y=0:H,opacity=0.5,domain=
 \pgfkeysvalueof{/pgfplots/view/az}:\pgfkeysvalueof{/pgfplots/view/az}-180,
 colormap/blackwhite,point meta={sin(atan2(y,x)+40)}] 
 ({R*cos(x)},
 {R*sin(x)},{y});
 \addplot3[samples y=0,domain=0:360,smooth,fill=gray!30,draw=black] ({R*cos(x)},{R*sin(x)},{H});
 \addFGBGplot[samples y=0,color=blue,domain=0:1440,smooth,samples=251]
 ({R*cos(x)},{R*sin(x)},{0.75*x/360});
 \addplot3[samples y=0,domain=\pgfkeysvalueof{/pgfplots/view/az}:\pgfkeysvalueof{/pgfplots/view/az}-180,smooth,color=black] 
 ({R*cos(x)},{R*sin(x)},{0});
 \draw  ({R*cos(\pgfkeysvalueof{/pgfplots/view/az})},{R*sin(\pgfkeysvalueof{/pgfplots/view/az})
 },{0}) -- ({R*cos(\pgfkeysvalueof{/pgfplots/view/az})},{R*sin(\pgfkeysvalueof{/pgfplots/view/az})},{H})
  ({R*cos(\pgfkeysvalueof{/pgfplots/view/az}-180)},{R*sin(\pgfkeysvalueof{/pgfplots/view/az}-180)
 },{0}) -- ({R*cos(\pgfkeysvalueof{/pgfplots/view/az}-180)},{R*sin(\pgfkeysvalueof{/pgfplots/view/az}-180)},{H});
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

You must log in to answer this question.

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