# Embed a graph on a Sphere with TikZ?

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

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:

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

## 1 Answer

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}


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}
}


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}


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