[Tex/LaTex] Projection of Circle onto Spherical Surface

3dasymptotetikz-3dplottikz-pgf

enter image description here

I want to draw shapes looks like 15 degrees tilted-semi-sphere and there are circles, placed in a triangular shaped array, on the base area. Then these circles are projected onto surface of sphere.

I can drive small circles on the base-plane. However, projections of circles have different shapes. Therefore I don't know how to solve it. I think, there are cylindrical holes and I must find their intersections on the surface of sphere.

Thank you all for your response. I wonder is there any way to specify centers of circles or projections in cartesian and not in polar coordinates. Since it is difficult to specify polar coordinates of each small circles, shown below, in an automatic way, in other words using \foreach command.

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{pgfplots}
\newcommand\pgfmathsinandcos[3]{%
\pgfmathsetmacro#1{sin(#3)}%
\pgfmathsetmacro#2{cos(#3)}%
}
\newcommand\LongitudePlane[3][current
plane]{%
\pgfmathsinandcos\sinEl\cosEl{#2} % elevation
\pgfmathsinandcos\sint\cost{#3} % azimuth
\tikzset{#1/.estyle={cm={\cost,\sint*\sinEl,0,\cosEl,(0,0)}}}
}
\newcommand\LatitudePlane[3][current plane]{%
\pgfmathsinandcos\sinEl\cosEl{#2} % elevation
\pgfmathsinandcos\sint\cost{#3} % latitude
\pgfmathsetmacro\yshift{\cosEl*\sint}
\tikzset{#1/.estyle={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}} %
}
\newcommand\DrawLongitudeCircle[2][2]{
  \LongitudePlane{\angEl}{#2}
  \tikzset{current plane/.prefix  style={scale=#1}}
  % angle of "visibility"
  \pgfmathsetmacro\angVis{atan(sin(#2)*cos(\angEl)/sin(\angEl))} %
  \draw[current plane,dotted] (\angVis:1) arc (\angVis:180:1);
  \draw[current plane,dotted] (\angVis+90:1) arc (\angVis+90:0:1);
}
\newcommand\DrawLatitudeCircle[2][3]{
  \LatitudePlane{\angEl}{#2}
  \tikzset{current plane/.prefix style={scale=#1}}
  \pgfmathsetmacro\sinVis{sin(#2)/cos(#2)*sin(\angEl)/cos(\angEl)}
  \pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}
  \draw[current plane,dotted] (\angVis:1) arc (\angVis:-\angVis-180:1);
  \draw[current plane,dotted] (180-\angVis:1) arc (180-\angVis:\angVis:1);
}
\newcommand\DrawMidCircle[2][4]{
  \LatitudePlane{\angEl}{#2}
  \tikzset{current plane/.prefix style={scale=#1}}
  \pgfmathsetmacro\sinVis{sin(#2)/cos(#2)*sin(\angEl)/cos(\angEl)}
  % angle of "visibility"
  \pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}
  \draw[current plane] (\angVis:1) arc (\angVis:-\angVis-180:1);
  \draw[current plane] (180-\angVis:1) arc (180-\angVis:\angVis:1);
}
\newcommand\DrawAxis[2][5]{
  \LongitudePlane{\angEl}{#2}
  \tikzset{current plane/.prefix style={scale=#1}}
  % angle of "visibility"
  \pgfmathsetmacro\angVis{atan(sin(#2)*cos(\angEl)/sin(\angEl))} %
  \draw[current plane] (1,0) -- (-1,0);
}
\newcommand\DrawSmallCircle[2][6]{
  \LatitudePlane{\angEl}{0}
  \tikzset{current plane/.prefix style={scale=1}}
  \pgfmathsetmacro\sinVis{sin(0)/cos(0)*sin(\angEl)/cos(\angEl)}
  % angle of "visibility"
  \pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}

  \pgfmathsetmacro\x{(#1*2*\r)*cos(\angRot)-(#2*2*\r*cos(30))*sin(\angRot)}
  \pgfmathsetmacro\y{(#1*2*\r)*sin(\angRot)+(#2*2*\r*cos(30))*cos(\angRot)}
  \draw[current plane,green!85!black, fill=green!85!black, fill opacity=0.25] ($(\x,\y)+(0:\r)$) arc (\angVis:360+\angVis:\r);
}
\newcommand\DrawSmallCircleShift[2][7]{
  \LatitudePlane{\angEl}{0}
  \tikzset{current plane/.prefix style={scale=1}}
  \pgfmathsetmacro\sinVis{sin(0)/cos(0)*sin(\angEl)/cos(\angEl)}
  % angle of "visibility"
  \pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}

  \pgfmathsetmacro\x{(#1*2*\r+\r)*cos(\angRot)-(#2*2*\r*cos(30))*sin(\angRot)}
  \pgfmathsetmacro\y{(#1*2*\r+\r)*sin(\angRot)+(#2*2*\r*cos(30))*cos(\angRot)}
  \draw[current plane,green!85!black, fill=green!85!black, fill opacity=0.25] ($(\x,\y)+(0:\r)$) arc (\angVis:360+\angVis:\r);
}
\begin{document}
  \begin{tikzpicture}
  \def\R{4}
  \def\r{0.35}
  \def\angRot{30}
  \node (C) at (0,0) {};
  \draw (C) -- (0,1);
  \def\angEl{20} % elevation angle

   \LongitudePlane{\angEl}{\angRot}
   \tikzset{current plane/.prefix style={scale=\R}}
   % angle of "visibility"
   \pgfmathsetmacro\angVis{atan(sin(\angRot)*cos(\angEl)/sin(\angEl))} %
   \draw[current plane] (-1,0) node[below left]{$u$} -- (1,0);
   \draw[current plane] (0,0,0) -- (0,1,0);
   \LongitudePlane{\angEl}{90+\angRot}
   \tikzset{current plane/.prefix style={scale=\R}}
    % angle of "visibility"
   \pgfmathsetmacro\angVis{atan(sin(90+\angRot)*cos(\angEl)/sin(\angEl))} %
   \draw[current plane] (-1,0) node[below right]{$v$} -- (1,0);

   \foreach \n in {0,-2}
    {\foreach \m in {-4,-3,...,0}
        {\DrawSmallCircle[\m]{\n}}}
   \foreach \n in {-1,-3}
    {\foreach \m in {-5,-4,...,-1}
        {\DrawSmallCircleShift[\m]{\n}}}
   \foreach \n in {-4}
    {\foreach \m in {-3,...,0}
        {\DrawSmallCircle[\m]{\n}}}
   \foreach \n in {-5}
    {\foreach \m in {-3,-2,-1}
        {\DrawSmallCircleShift[\m]{\n}}}

   \draw[current plane] (0,0,0) -- (0,1,0);
   \foreach \t in {0,10,...,80} { \DrawLatitudeCircle[\R]{\t} }
   \foreach \t in {0} { \DrawMidCircle[\R]{\t} }
   \foreach \t in {0,\angRot,...,150} { \DrawLongitudeCircle[\R]{\t} }
  \end{tikzpicture}
\end{document}

enter image description here

Best Answer

Well, no prizes for speed (not just because there is some duplication of calculations: TikZ is slow for this kind of stuff), but this shows one way of doing it. I expect asymptote/PSTricks could do it quicker, but I don't see any other way of doing it in TikZ.

The maths is straightforward "back-of-the-envelope" trigonometry (in this case literally). The only "insight" is to draw the circles manually.

EDIT 1 inspired by Jake's missing (at the time of writing) PGF Plots answer, I've changed most of the \foreach statements to TikZ plot commands which makes things a bit quicker.

EDIT 2 changed the critical function to sqrt(\R^2-(\cx+\r*cos(\t))^2-(\cy+\r*sin(\t))^2) as this provides better accuracy than veclen when the circles get near to the edge.

EDIT 3 a second version has been added which uses layers to add both circles at the same time.

\documentclass{standalone}
\usepackage{tikz}
\begin{document}

\begin{tikzpicture}[x=(-30:4cm),y=(30:4cm),z=(90:4cm)]

\def\R{1}

\draw (-\R,0,0) -- (\R,0,0);
\draw (0,-\R,0) -- (0,\R,0);
\draw (0,0,0) -- (0,0,\R);
\draw plot [domain=0:360, samples=60, variable=\i] 
    (\R*cos \i, \R*sin \i, 0) -- cycle;

\def\r{0.075}

\foreach \cr in {0.3, 0.5, 0.7, 0.9}
    \foreach \ca [evaluate={\cx=\cr*cos \ca; \cy=\cr*sin \ca;}]in {-90,-60,-30,0,30, 60, 90}
        \draw [green!85!black, fill=green!85!black, fill opacity=0.25]
            plot [domain=0:360, samples=40, variable=\i] 
                (\cx+\r*cos \i, \cy+\r*sin \i, 0) -- cycle;

\foreach \i in {0, 30,...,150}
    \draw [dotted] plot [domain=-90:90, samples=30, variable=\j]        
        (\R*cos \i*sin \j,\R*sin \i*sin \j, \R*cos \j);

\foreach \j in {0, 15,...,90}
    \draw [dotted] plot [domain=0:360, samples=60, variable=\i]  
            (\R*cos \i*sin \j,\R*sin \i*sin \j, \R*cos \j);

\foreach \cr in {0.3, 0.5, 0.7, 0.9}
    \foreach \ca [evaluate={\cx=\cr*cos \ca; \cy=\cr*sin \ca;}]in {-90,-60,-30,0,30, 60, 90}
        \draw [red, fill=red, fill opacity=.25] 
            plot [domain=0:360, samples=40, variable=\t] 
                (\cx+\r*cos \t,\cy+\r*sin \t, {sqrt(\R^2-(\cx+\r*cos(\t))^2-(\cy+\r*sin(\t))^2)})
                    -- cycle;

\end{tikzpicture}

\end{document}

enter image description here

And here's a version using layers and Cartesian rather than polar coordinates for the circles:

\documentclass{standalone}
\usepackage{tikz}
\begin{document}

\pgfdeclarelayer{dome floor}
\pgfdeclarelayer{dome}
\pgfdeclarelayer{dome surface}
\pgfsetlayers{dome floor,main,dome,dome surface}

\def\addcircle#1#2#3#4{%
    \begingroup%
        \pgfmathparse{#1}\let\R=\pgfmathresult
        \pgfmathparse{#2}\let\cx=\pgfmathresult
        \pgfmathparse{#3}\let\cy=\pgfmathresult
        \pgfmathparse{#4}\let\r=\pgfmathresult
        \begin{pgfonlayer}{dome floor}
            \draw [blue!45!black, fill=blue!45, fill opacity=0.25]
                            plot [domain=0:360, samples=40, variable=\i] 
                                (\cx+\r*cos \i, \cy+\r*sin \i, 0) -- cycle;
        \end{pgfonlayer}
        \begin{pgfonlayer}{dome surface}
            \draw [red!75!black, fill=red!75, fill opacity=0.25] 
                plot [domain=0:360, samples=60, variable=\t] 
                    (\cx+\r*cos \t,\cy+\r*sin \t, {sqrt(max(\R^2-(\cx+\r*cos(\t))^2-(\cy+\r*sin(\t))^2, 0))})
                        -- cycle;
        \end{pgfonlayer}
    \endgroup%
}
\begin{tikzpicture}[x=(-30:1cm),y=(30:1cm),z=(90:1cm)]


\def\R{6}

\begin{pgfonlayer}{dome floor}
    \draw (-\R,0,0) -- (\R,0,0);
    \draw (0,-\R,0) -- (0,\R,0);
    \draw plot [domain=0:360, samples=90, variable=\i] 
        (\R*cos \i, \R*sin \i, 0) -- cycle;
\end{pgfonlayer}

\draw (0,0,0) -- (0,0,\R);

\begin{pgfonlayer}{dome surface}
    \foreach \i in {0, 30,...,150}
            \draw [dotted] plot [domain=-90:90, samples=60, variable=\j]        
                (\R*cos \i*sin \j,\R*sin \i*sin \j, \R*cos \j);
    \foreach \j in {0, 15,...,90}
            \draw [dotted] plot [domain=0:360, samples=60, variable=\i]  
                    (\R*cos \i*sin \j,\R*sin \i*sin \j, \R*cos \j);
\end{pgfonlayer}

\def\r{0.5}
\foreach \m [evaluate={\N=max(-4, \m-7);}]in {0,...,5}{
    \foreach \n in {0,-1,...,\N}
        {\addcircle{\R}{\m*sin 60}{\n-mod(abs(\m),2)*\r}{\r}}}


\end{tikzpicture}

\end{document}

enter image description here