30

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

0

3 Answers 3

31

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

2
  • It would be interesting to have only the hidden part of the ellipses (circles) dotted. Apr 20, 2013 at 7:35
  • Very nice example! I added the second code example to the TeXample TikZ gallery.
    – Stefan Kottwitz
    Jul 29, 2014 at 16:11
20

enter image description here

Asymptote solution. It uses a function (from the Wiki) to calculate the curve of intersection. Adjust the placement of the circles as needed. s.asy:

size(300,300);
size3(300,300,300);
import graph3;
currentprojection=orthographic(-5,-4,2,center=true);

guide3 sphere_x_cyl(real a, real r, real R, int n=10){
// return a closed curve of the Sphere–cylinder intersection (top part)
// only for the case when a cylinder is completely inside
// except  special case when the x-shift a=0
  assert(a>0,"***** Positive a expected.");

  real b=(R^2-r^2-a^2)/(2a);  
  guide3 g;
  int n=30;
  real phi, dphi;

  phi=0;
  dphi=360/n;

  triple t;
  for(int i=0;i<n;++i){
    t=(r*Cos(phi),r*Sin(phi),sqrt(2a*(b+r*Cos(phi))));
    g=g--t;
    phi+=dphi;
  }
  g=g--cycle;
  return g;
}

void Draw3(guide3 g,pen p=currentpen){
  draw(project(g),p);
}

void FillDraw3(guide3 g,pen fillPen=currentpen, pen linePen=nullpen){
  filldraw(project(g),fillPen,linePen);
}

int nr=11;
real r=1;
real R=nr*r;

real a; // distance between the centers 

triple O=(0,0,0);

guide3 circ(triple sh=O, real r){
  guide3 uc=(r,0,0)..(0,r,0)..(-r,0,0)..(0,-r,0)..cycle;
  return shift(sh)*uc;
}

guide3 semicirc(triple sh=O, real r){
  guide3 uc=(r,0,0)..(0,r,0)..(-r,0,0);
  return shift(sh)*uc;
}

guide3 Parallel(real h, real R){
  return circ((0,0,h),sqrt(R^2-h^2));
}

pen dashed=linetype(new real[] {4,3}); // set up dashed pattern

pen dashCircPen=gray(0.4)+dashed+opacity(1)+0.32pt;
pen boldLine=darkblue+1.2pt;
pen floorCircPen=olive+opacity(0.75);
pen sphereSpotPen=blue+opacity(0.382);


Draw3(O--(0,0,R),boldLine);
Draw3(O--(R,0,0),boldLine);
Draw3(O--(0,R,0),boldLine);
Draw3(O--(-R,0,0),boldLine);
Draw3(O--(0,-R,0),boldLine);
Draw3(circ(R),boldLine);

real h=0;
real dphi=10;
real phi=dphi;
for(int i=1;i<9;++i){
  h=R*Sin(phi);
  Draw3(Parallel(h,R),dashCircPen);
  phi+=dphi;
}

for(int i=0;i<6;++i){
  Draw3(rotate(30*i,(0,0,1))*rotate(90,(1,0,0))*semicirc(R),dashCircPen);
}

int nd=floor((nr-1)/2);
transform3 tr;
for(int j=0;j<3;++j){ 
  a=2r;
  for(int i=0;i<nd;++i){
    tr=rotate(60*j,(0,0,1))*shift((-a,0,0));
    FillDraw3(tr*circ(r),floorCircPen,red);
    FillDraw3(tr*sphere_x_cyl(a,r,R),sphereSpotPen);
    a+=2r;
  }
}
FillDraw3(circ(r),floorCircPen,red);
FillDraw3(shift((0,0,sqrt(R^2-r^2)))*circ(r),sphereSpotPen);

To get a standalone s.pdf run asy -f pdf s.asy.

0
12

Here's a way of doing this using PGFPlots:

\documentclass{article}
\usepackage{pgfplots}
\begin{document}
\begin{tikzpicture}
\begin{axis}[hide axis, axis equal, samples=15]
\foreach \posx/\posy in {0/-0.1, 0.6/0.1, 0.6/-0.4, 0.1/-0.7}{
    \addplot3 [red, thick, domain=0:360, samples=20] (
        {cos(x)*0.2+\posx},
        {sin(x)*0.2+\posy},
        {0}
    );
}

\addplot3 [surf, gray, faceted color=gray, opacity=0.5, samples=20, z buffer=sort, domain=0:360, y domain=0:1] ({cos(x)*y},{sin(x)*y},
{sqrt(1-(cos(x)*y)^2-(sin(x)*y)^2))});
\foreach \posx/\posy in {0/-0.1, 0.6/0.1, 0.6/-0.4, 0.1/-0.7}{
    \addplot3 [red, thick, domain=0:360, samples=20] (
        {cos(x)*0.2+\posx},
        {sin(x)*0.2+\posy},
        {sqrt(1-((cos(x)*0.2+\posx))^2-((sin(x)*0.2+\posy))^2))}
    );
}
\end{axis}
\end{tikzpicture}
\end{document}

You must log in to answer this question.

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