15

I want to draw a graph on a sphere using TikZ. Something like this (please excuse my crude drawing): enter image description here

Is there a way this can be done automatically? I'm sure there's a way to perform a change of coordinates mathematically, but still I'm not sure how to do whole line segments this way. I appreciate any assistance.

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}
    \begin{figure}
        $\begin{array}{c}
            \begin{tikzpicture}            
                % Nodes
                \tikzset{every node/.style = {circle, draw=black, thick, inner sep = 2pt}}
                \node (1) at (0,0) {}; 
                \node (2) at (1,0) {};
                \node (3) at (1,1) {};
                \node (4) at (0,1) {};
                \node (5) at ($(4) + (60:1)$) {};  
                \node (6) at ($(5) + (150:1)$) {};
                \node (7) at ($(4) + (150:1)$) {}; 
                \node (8) at ($(5) + (30:1)$) {}; 
                \node (9) at ($(3) + (30:1)$) {}; 

                % Edges
                \draw[thick] (1) -- (2) -- (3) -- (4) -- (1);
                \draw[thick] (4) -- (5) -- (6) -- (7) -- (4);
                \draw[thick] (3) -- (5) -- (8) -- (9) -- (3);

                % Faces
                \tikzset{every node/.style = {}}
                \node at (0.5, 1.3) {$F_1$};
                \node at (-0.2, 1.65) {$F_2$};
                \node at (1.2, 1.65) {$F_3$};
                \node at (0.5, 0.5) {$F_4$};
                \node at (1.8, 0.6) {$F_5$};
            \end{tikzpicture}
        \end{array}
        \quad
        \stackrel{\pi^{-1}}{\longmapsto}
        \quad
        \begin{array}{c}
            \begin{tikzpicture}
                % Sphere and plane
                \filldraw[white, draw=black] (0,1) circle (2cm);
                \shade[ball color = white, opacity = 0.1] (0,1) circle (2cm);
            \end{tikzpicture}
        \end{array}
        $               
    \end{figure}
\end{document}

Ideally I'd like to be able to reposition the graph on the sphere at different points, e.g. I'd like to place F_1 at the north pole so that the sphere looks like this: enter image description here

  • "please excuse my crude drawing" 😄 – thymaro Jun 16 '18 at 18:43
20

DISCLAIMER: When writing the answer below, I was not aware of Fritz' great answer. I guess it will be straightforward to use his methods to obtain the analogous results in a much more elegant way. Nevertheless I do also not think that this answer is completely pointless because it does things without pgfplots, so it might still have applications.

UPDATE: Now with hiding the hidden lines.

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{tikz-3dplot}
\pgfkeys{/tikz/.cd,
    hidden opacity/.store in=\HiddenOpacity,
    hidden opacity=0.3,
}

\makeatletter

% from https://tex.stackexchange.com/a/375604/121799
%along x axis
\define@key{x sphericalkeys}{radius}{\def\myradius{#1}}
\define@key{x sphericalkeys}{theta}{\def\mytheta{#1}}
\define@key{x sphericalkeys}{phi}{\def\myphi{#1}}
\tikzdeclarecoordinatesystem{x spherical}{% 
    \setkeys{x sphericalkeys}{#1}%
    \pgfpointxyz{\myradius*cos(\mytheta)}{\myradius*sin(\mytheta)*cos(\myphi)}{\myradius*sin(\mytheta)*sin(\myphi)}}

%along y axis
\define@key{y sphericalkeys}{radius}{\def\myradius{#1}}
\define@key{y sphericalkeys}{theta}{\def\mytheta{#1}}
\define@key{y sphericalkeys}{phi}{\def\myphi{#1}}
\tikzdeclarecoordinatesystem{y spherical}{% 
    \setkeys{y sphericalkeys}{#1}%
    \pgfpointxyz{\myradius*sin(\mytheta)*cos(\myphi)}{\myradius*cos(\mytheta)}{\myradius*sin(\mytheta)*sin(\myphi)}}


%along z axis
\define@key{z sphericalkeys}{radius}{\def\myradius{#1}}
\define@key{z sphericalkeys}{theta}{\def\mytheta{#1}}
\define@key{z sphericalkeys}{phi}{\def\myphi{#1}}
\tikzdeclarecoordinatesystem{z spherical}{% 
    \setkeys{z sphericalkeys}{#1}%
    \pgfmathsetmacro{\Xtest}{cos(90-\tdplotmaintheta)*cos(\tdplotmainphi-90)*cos(\mytheta)*cos(\myphi)
    +cos(90-\tdplotmaintheta)*sin(\tdplotmainphi-90)*cos(\mytheta)*sin(\myphi)
    +sin(90-\tdplotmaintheta)*sin(\mytheta)}
    % \Xtest is the projection of the coordinate on the normal vector of the visible plane
    \pgfmathsetmacro{\ntest}{ifthenelse(\Xtest<0,0,1)}
    \ifnum\ntest=0
    \xdef\MCheatOpa{\HiddenOpacity}
    \else
    \xdef\MCheatOpa{1}
    \fi
    %\typeout{\mytheta,\tdplotmaintheta;\myphi,\tdplotmainphi:\ntest}
    \pgfpointxyz{\myradius*cos(\mytheta)*cos(\myphi)}{%
    \myradius*cos(\mytheta)*sin(\myphi)}{\myradius*sin(\mytheta)}}

%%%%%%%%%%%%%%%%%
\tikzoption{spherical smooth}[]{\let\tikz@plot@handler=\pgfplothandlersphericalcurveto}


\pgfdeclareplothandler{\pgfplothandlersphericalcurveto}{}{%
  point macro=\pgf@plot@curveto@handler@spherical@initial,
  jump macro=\pgf@plot@smooth@next@spherical@moveto,
  end macro=\pgf@plot@curveto@handler@spherical@finish
}

\def\pgf@plot@smooth@next@spherical@moveto{%
  \pgf@plot@curveto@handler@spherical@finish%
  \global\pgf@plot@startedfalse%
  \global\let\pgf@plotstreampoint\pgf@plot@curveto@handler@spherical@initial%
}

\def\pgf@plot@curveto@handler@spherical@initial#1{%
  \pgf@process{#1}%
  \ifx\tikz@textcolor\pgfutil@empty%
  \else
  \pgfsetstrokecolor{\tikz@textcolor}
  \fi
  \pgf@xa=\pgf@x%
  \pgf@ya=\pgf@y%
  \pgf@plot@first@action{\pgfqpoint{\pgf@xa}{\pgf@ya}}%
  \xdef\pgf@plot@curveto@first{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
  \global\let\pgf@plot@curveto@first@support=\pgf@plot@curveto@first%
  \global\let\pgf@plotstreampoint=\pgf@plot@curveto@handler@spherical@second%
}

\def\pgf@plot@curveto@handler@spherical@second#1{%
  \pgf@process{#1}%
  \xdef\pgf@plot@curveto@second{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%
  \global\let\pgf@plotstreampoint=\pgf@plot@curveto@handler@spherical@third%
  \global\pgf@plot@startedtrue%
}

\def\pgf@plot@curveto@handler@spherical@third#1{%
  \pgf@process{#1}%
  \xdef\pgf@plot@curveto@current{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%
  % compute difference vector:
  \pgf@xa=\pgf@x%
  \pgf@ya=\pgf@y%
  \pgf@process{\pgf@plot@curveto@first}
  \advance\pgf@xa by-\pgf@x%
  \advance\pgf@ya by-\pgf@y%
  % compute support directions:
  \pgf@xa=\pgf@plottension\pgf@xa%
  \pgf@ya=\pgf@plottension\pgf@ya%
  % first marshal:
  \pgf@process{\pgf@plot@curveto@second}%
  \pgf@xb=\pgf@x%
  \pgf@yb=\pgf@y%
  \pgf@xc=\pgf@x%
  \pgf@yc=\pgf@y%
  \advance\pgf@xb by-\pgf@xa%
  \advance\pgf@yb by-\pgf@ya%
  \advance\pgf@xc by\pgf@xa%
  \advance\pgf@yc by\pgf@ya%
  \@ifundefined{MCheatOpa}{}{%
  \pgf@plotstreamspecial{\pgfsetstrokeopacity{\MCheatOpa}}}
  \edef\pgf@marshal{\noexpand\pgfsetstrokeopacity{\noexpand\MCheatOpa}
  \noexpand\pgfpathcurveto{\noexpand\pgf@plot@curveto@first@support}%
    {\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}}{\noexpand\pgf@plot@curveto@second}
    \noexpand\pgfusepathqstroke
    \noexpand\pgfpathmoveto{\noexpand\pgf@plot@curveto@second}}%
  {\pgf@marshal}%
  %\pgfusepathqstroke%
  % Prepare next:
  \global\let\pgf@plot@curveto@first=\pgf@plot@curveto@second%
  \global\let\pgf@plot@curveto@second=\pgf@plot@curveto@current%
  \xdef\pgf@plot@curveto@first@support{\noexpand\pgfqpoint{\the\pgf@xc}{\the\pgf@yc}}%
}

\def\pgf@plot@curveto@handler@spherical@finish{%
  \ifpgf@plot@started%
    \pgfpathcurveto{\pgf@plot@curveto@first@support}{\pgf@plot@curveto@second}{\pgf@plot@curveto@second}%
  \fi%
}


\makeatother
\begin{document}
\pgfmathsetmacro{\RadiusSphere}{3}

\foreach \X in {-180,-170,...,170}
{\begin{tikzpicture}
\shade[ball color = gray!40, opacity = 0.5] (0,0,0) circle (\RadiusSphere);

\tdplotsetmaincoords{72}{\X}

\begin{scope}[tdplot_main_coords,samples=60]
% \draw[-latex,orange] (0,0,0) -- (z spherical cs: radius=\RadiusSphere,
% phi={\tdplotmainphi-90},theta={90-\tdplotmaintheta});
% \draw[-latex] (0,0,0) -- (\RadiusSphere,0,0) node[below]{$x$};
% \draw[-latex] (0,0,0) -- (0,\RadiusSphere,0) node[left]{$y$};
% \draw[-latex] (0,0,0) -- (0,0,\RadiusSphere) node[left]{$z$};

\pgfmathsetmacro{\ThetaNod}{00}
\begin{scope}[blue]
%lower 4-angle
\draw plot[spherical smooth,variable=\x,domain=-20:20] 
(z spherical cs: radius=\RadiusSphere,phi=\x,theta=\ThetaNod);
\draw plot[spherical smooth,variable=\x,domain=\ThetaNod:{\ThetaNod-40}] 
(z spherical cs: radius=\RadiusSphere,phi=20,theta=\x);
\draw plot[spherical smooth,variable=\x,domain=\ThetaNod:{\ThetaNod-40}] 
(z spherical cs: radius=\RadiusSphere,phi=-20,theta=\x);
\draw plot[spherical smooth,variable=\x,domain=-20:20] 
(z spherical cs: radius=\RadiusSphere,phi=\x,theta={\ThetaNod-40});
% left 4-angle
\draw plot[variable=\x,domain=0:40]
(z spherical cs:
radius=\RadiusSphere,phi={20+\x*sin(60)},theta={\ThetaNod+\x*cos(60)}) ;
\draw[red] plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={20+40*sin(60)-\x/2},theta={\ThetaNod+40*cos(60)+\x*tan(60)/2}) ;
\draw plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={\x*sin(60)},theta={\ThetaNod+40*tan(60)/2+\x*cos(60)}) ;
% right 4-angle
\draw plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={-20-\x*sin(60)},theta={\ThetaNod+\x*cos(60)}) ;
\draw plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={-20-40*sin(60)+\x/2},theta={\ThetaNod+40*cos(60)+\x*tan(60)/2}) ;
\draw plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={-\x*sin(60)},theta={\ThetaNod+40*tan(60)/2+\x*cos(60)}) ;
% middle triangle
\draw plot[spherical smooth,variable=\x,domain=-20:20] 
(z spherical cs: radius=\RadiusSphere,phi=\x,theta=\ThetaNod);
\draw plot[spherical smooth,variable=\x,domain=0:40]
(z spherical cs: radius=\RadiusSphere,phi={20-\x/2},theta={\ThetaNod+\x*tan(60)/2}) ;
\draw plot[spherical smooth,variable=\x,domain=0:40]
(z spherical cs: radius=\RadiusSphere, phi={-20+\x/2},theta={\ThetaNod+\x*tan(60)/2}) ;
\end{scope}
\end{scope}

\end{tikzpicture}
}
\end{document}

enter image description here

Now it also runs over the poles.

\foreach \X in {-180,-170,...,170}
{\begin{tikzpicture}
\shade[ball color = gray!40, opacity = 0.5] (0,0,0) circle (\RadiusSphere);

\tdplotsetmaincoords{\X}{110}

\begin{scope}[tdplot_main_coords,samples=60]
% \draw[-latex,orange] (0,0,0) -- (z spherical cs: radius=\RadiusSphere,
% phi={\tdplotmainphi-90},theta={90-\tdplotmaintheta});
% \draw[-latex] (0,0,0) -- (\RadiusSphere,0,0) node[below]{$x$};
% \draw[-latex] (0,0,0) -- (0,\RadiusSphere,0) node[left]{$y$};
% \draw[-latex] (0,0,0) -- (0,0,\RadiusSphere) node[left]{$z$};

\pgfmathsetmacro{\ThetaNod}{00}
\begin{scope}[blue]
%lower 4-angle
\draw plot[spherical smooth,variable=\x,domain=-20:20] 
(z spherical cs: radius=\RadiusSphere,phi=\x,theta=\ThetaNod);
\draw plot[spherical smooth,variable=\x,domain=\ThetaNod:{\ThetaNod-40}] 
(z spherical cs: radius=\RadiusSphere,phi=20,theta=\x);
\draw plot[spherical smooth,variable=\x,domain=\ThetaNod:{\ThetaNod-40}] 
(z spherical cs: radius=\RadiusSphere,phi=-20,theta=\x);
\draw plot[spherical smooth,variable=\x,domain=-20:20] 
(z spherical cs: radius=\RadiusSphere,phi=\x,theta={\ThetaNod-40});
% left 4-angle
\draw plot[variable=\x,domain=0:40]
(z spherical cs:
radius=\RadiusSphere,phi={20+\x*sin(60)},theta={\ThetaNod+\x*cos(60)}) ;
\draw[red] plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={20+40*sin(60)-\x/2},theta={\ThetaNod+40*cos(60)+\x*tan(60)/2}) ;
\draw plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={\x*sin(60)},theta={\ThetaNod+40*tan(60)/2+\x*cos(60)}) ;
% right 4-angle
\draw plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={-20-\x*sin(60)},theta={\ThetaNod+\x*cos(60)}) ;
\draw plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={-20-40*sin(60)+\x/2},theta={\ThetaNod+40*cos(60)+\x*tan(60)/2}) ;
\draw plot[spherical smooth,variable=\x,domain=00:40]
(z spherical cs:
radius=\RadiusSphere,phi={-\x*sin(60)},theta={\ThetaNod+40*tan(60)/2+\x*cos(60)}) ;
% middle triangle
\draw plot[spherical smooth,variable=\x,domain=-20:20] 
(z spherical cs: radius=\RadiusSphere,phi=\x,theta=\ThetaNod);
\draw plot[spherical smooth,variable=\x,domain=0:40]
(z spherical cs: radius=\RadiusSphere,phi={20-\x/2},theta={\ThetaNod+\x*tan(60)/2}) ;
\draw plot[spherical smooth,variable=\x,domain=0:40]
(z spherical cs: radius=\RadiusSphere, phi={-20+\x/2},theta={\ThetaNod+\x*tan(60)/2}) ;
\end{scope}
\end{scope}

\end{tikzpicture}
}

enter image description here

OLD ANSWER: Here is a proposal: use spherical coordinates and draw the boundaries as plots of "lines" in those.

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{tikz-3dplot}
\makeatletter
\pgfqkeys{/tikz/cs}{ % https://tex.stackexchange.com/a/114158/121799
  latitude/.store in=\tikz@cs@latitude,% not needed with '3d' library
  longitude/.style={angle={#1}},% not needed with '3d' library
  theta/.style={latitude={#1}},
  rho/.style={angle={#1}}
}
\tikzdeclarecoordinatesystem{xyz spherical}{% needed even with '3d' library!
  \pgfqkeys{/tikz/cs}{angle=0,radius=0,latitude=0,#1}%
  \pgfpointspherical{\tikz@cs@angle}{\tikz@cs@latitude}{\tikz@cs@xradius}% fix \tikz@cs@radius to \tikz@cs@xradius
}
\makeatother

\tdplotsetmaincoords{70}{155}
\begin{document}
\begin{tikzpicture}
\def\RadiusSphere{3}
\shade[ball color = gray!40, opacity = 0.5] (0,0) circle (\RadiusSphere);
\begin{scope}[tdplot_main_coords]
% comment these out if you want to know where the axes point
% \draw[->] (0,0,0) -- ({1.2*\RadiusSphere},0,0) coordinate(Y) node[below] {$x$};
% \draw[->] (0,0,0) -- (0,{1.2*\RadiusSphere},0) coordinate(Z) node[below] {$y$};
% \draw[->] (0,0,0) -- (0,0,{2.2*\RadiusSphere}) coordinate(X) node[left] {$z$};
% middle triangle
\draw plot[variable=\x,domain=-20:20] 
(xyz spherical cs: radius=\RadiusSphere,angle=\x,latitude=0);
\draw plot[variable=\x,domain=0:40]
(xyz spherical cs: radius=\RadiusSphere,angle={20-\x/2},latitude={\x*tan(60)/2}) ;
\draw plot[variable=\x,domain=0:40]
(xyz spherical cs: radius=\RadiusSphere, angle={-20+\x/2},latitude={\x*tan(60)/2}) ;
\node at (xyz spherical cs: radius=\RadiusSphere,angle=0,latitude={20/sqrt(3)}) {$F_1$};
% bottom 4-angle (these are not rectangles on a sphere ;-)
\draw plot[variable=\x,domain=00:-40]
(xyz spherical cs: radius=\RadiusSphere, angle=20,latitude=\x);
\draw plot[variable=\x,domain=00:-40]
(xyz spherical cs: radius=\RadiusSphere, angle=-20,latitude=\x);
\draw plot[variable=\x,domain=-20:20]
(xyz spherical cs: radius=\RadiusSphere, angle=\x,latitude=-40);
\node at (xyz spherical cs: radius=\RadiusSphere,angle=0,latitude=-20) {$F_4$};
% left 3-angle
\draw plot[variable=\x,domain=00:40]
(xyz spherical cs:
radius=\RadiusSphere,angle={20+\x*sin(60)},latitude={\x*cos(60)}) ;
\draw plot[variable=\x,domain=00:40]
(xyz spherical cs:
radius=\RadiusSphere,angle={20+40*sin(60)-\x/2},latitude={40*cos(60)+\x*tan(60)/2}) ;
\draw plot[variable=\x,domain=00:40]
(xyz spherical cs:
radius=\RadiusSphere,angle={\x*sin(60)},latitude={40*tan(60)/2+\x*cos(60)}) ;
\node at (xyz spherical cs: radius=\RadiusSphere,angle={20+10*sin(60)},
latitude={20+20*cos(60)/sqrt(3)}) {$F_2$};
% right 4-angle
\draw plot[variable=\x,domain=00:40]
(xyz spherical cs:
radius=\RadiusSphere,angle={-20-\x*sin(60)},latitude={\x*cos(60)}) ;
\draw plot[variable=\x,domain=00:40]
(xyz spherical cs:
radius=\RadiusSphere,angle={-20-40*sin(60)+\x/2},latitude={40*cos(60)+\x*tan(60)/2}) ;
\draw plot[variable=\x,domain=00:40]
(xyz spherical cs:
radius=\RadiusSphere,angle={-\x*sin(60)},latitude={40*tan(60)/2+\x*cos(60)}) ;
\node at (xyz spherical cs: radius=\RadiusSphere,angle={-20-10*sin(60)},
latitude={20+20*cos(60)/sqrt(3)}) {$F_3$};
\end{scope}
\end{tikzpicture}
\end{document}

enter image description here

I am loading tikz-3dplot because it makes is rather easy to adjust the viewing angle.

  • Woah! This is awesome - I'll read through the code soon and give you some feedback. – Luke Collins Jun 16 '18 at 19:54

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.