2

I'm really surprised that I couldn't find an answer to this question, but here it goes: how can I draw a plane intersecting a sphere using tikz and tikz-3dplots?

I managed to do the following, using a mixture of this answer and this answer (code below):

What I have

But what I really want is something like this (it's a shame and something I totally don't understand why Geogebra does not convert 3D figures to tikz):

Goal

Clearly, I'm almost there. But the shading is wrong: the plane is being plotted as if it was covering the sphere, and not as if it is intersecting it. I found out here that apparently there is no way of doing the shading automatically, but I'm ok with doing it manually. The only problem is, I have no idea how to do it. I spent more than a day already trying to understand what was happening in the codes of the previous mentioned answers, as they were made for specific cases and I needed to transform them into a random sphere intersecting a random plane. I added some comments to the code with what I gathered. But I still am not sure how all of those commands work, so I can't properly identify where should I end and begin an arc to fill with some darker gray color on top of the plane plotting.

I know that using an external programm to generate images would work (here is a nice example using Asymptote), but I have to add many different spheres intersecting different planes to my file, and I would prefer to be able to generate them directly in Latex with tikz, instead of having multiple figures.

So my question reduces to: how to manually add the proper shading in my figure? (Notice that there is need for two shades: one for the cap of the sphere and a darker one for the back part of the sphere's cap.) Of course, an alternative code, which can be easily modified for different spheres and planes (specially planes not parallel to xy), is very welcome as well.

What I have so far:

\documentclass{article}
\usepackage{tikz}
\usepackage{tikz-3dplot}
\usetikzlibrary{backgrounds, intersections}


%I don't have a clear idea of what is happening here, but they are used for the plane construction
\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/.style={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/.style={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}} %
}
\newcommand\DrawLongitudeCircle[2][1]{
  \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] (\angVis:1) arc (\angVis:\angVis+180:1);
  \draw[current plane,dashed] (\angVis-180:1) arc (\angVis-180:\angVis:1);
}
\newcommand\DrawLatitudeCircle[2][1]{
  \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,dashed] (180-\angVis:1) arc (180-\angVis:\angVis:1);
}

\begin{document}

\begin{figure}
    \centering
    \tdplotsetmaincoords{60}{110}
    \begin{tikzpicture}[tdplot_main_coords, 
  declare function={dicri(\t,\th,\ph,\R)=sin(\th)*sin(\ph)*(\R*cos(\t)) - sin(\th)*cos(\ph)*(\R*sin(\t))+ cos(\th)*(1);}] %dicri is defined according to a parametrization n+r*cos(t)*u+r*sen(t)*v, being u and v orthonormal vectors in the intersecting plane
  \pgfmathsetmacro{\R}{5}% 
  \path  coordinate (T) at (0,0,3) %center of circle defined by the intersection
   coordinate (I) at (0,0,0); 
 
  \path[tdplot_screen_coords,shift={(I)},use as bounding box] (-1.2*\R,-1.2*\R)rectangle (1.2*\R,1.2*\R);%this limits the image position

    %draws dot+label for coordinates T and I
    \foreach \v/\position in {T/above,I/below} { 
        \draw[fill=black] (\v) circle (0.7pt) node [\position=0.2mm] {$\v$}; 
    }  
  
    \begin{scope}[tdplot_screen_coords, on background layer]
        \fill[ball color=white, opacity=0.8] (I) circle (\R); 
        % determine the zeros of dicri 
        \path[overlay,name path=dicri] plot[variable=\x,domain=0:360,samples=73] 
        ({\x*1pt},{dicri(\x,\tdplotmaintheta,\tdplotmainphi,4)}); 
        \path[overlay,name path=zero] (0,0) -- (360pt,0); 
        \path[name intersections={of=dicri and zero,total=\t}] 
        let \p1=(intersection-1),\p2=(intersection-2) in 
        \pgfextra{\xdef\tmin{\x1}\xdef\tmax{\x2}}; 
    \end{scope} %this has to do with finding the equation of the intersection circle, I believe
    
    \pgfmathsetmacro{\SmallR}{4} %the radius of the intersection circle is supposed as known
  
    % Draw dashed part of intersecting circle
    \draw[dashed] plot[variable=\t,domain=\tmin:\tmax,samples=50,smooth] 
    ({\SmallR*cos(\t)},
    {\SmallR*sin(\t)},
    {3}); 
    
    % Draw continuous part of intersecting circle
    \draw[thick,save path=\pathA] plot[variable=\t,domain=\tmax:\tmin+360,samples=50,smooth]
    ({\SmallR*cos(\t)},
    {\SmallR*sin(\t)},
    {3}); %same parametrization used in dicri. 
     
     %Plane construction
        %% some definitions
        \def\angEl{35} % elevation angle
        \def\angAz{-105} % azimuth angle
        \def\angPhi{-40} % longitude of point P
        \def\angBeta{19} % latitude of point P
        
        %% working planes
        \pgfmathsetmacro\H{\R*cos(\angEl)} % distance to north pole
        \tikzset{xyplane/.style={
          cm={cos(\angAz),sin(\angAz)*sin(\angEl),-sin(\angAz),cos(\angAz)*sin(\angEl),(0,-\H)}
          }, >=latex, % option for nice arrows
          inner sep=0pt,%
          outer sep=2pt,%
          mark coordinate/.style={inner sep=0pt,outer sep=0pt,minimum size=3pt,
          fill=black,circle}
        }
        \LatitudePlane[equator]{\angEl}{0}
                
        %% draw xy shifted plane
       \filldraw[xyplane,shift={(0,0,3)},fill=gray!10,opacity=0.2] 
          (-1.8*\R,-2.6*\R) rectangle (2.2*\R,2*\R);
\end{tikzpicture}
\end{figure}
\end{document}

EDIT: I managed to find the points where the continuous circle turns into a dashed circle using this:

\path ({\SmallR*cos(\tmin)},
       {\SmallR*sin(\tmin)},
       {3}) coordinate (pmin)
      ({\SmallR*cos(\tmax)},
       {\SmallR*sin(\tmax)},
       {3}) coordinate (pmax);

Those points are where the new shading should begin, now I'm only missing how to find the arcs of the dashed circle and of the upper sphere surface.

1 Answer 1

2

It is easy to give you what you want in this case but I am not sure if this helps you on the long term. Here are some minimal damage changes.

\documentclass{article}
\usepackage{tikz}
\usepackage{tikz-3dplot}
\usetikzlibrary{backgrounds, intersections}


%I don't have a clear idea of what is happening here, but they are used for the plane construction
\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/.style={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/.style={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}} %
}
\newcommand\DrawLongitudeCircle[2][1]{
  \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] (\angVis:1) arc (\angVis:\angVis+180:1);
  \draw[current plane,dashed] (\angVis-180:1) arc (\angVis-180:\angVis:1);
}
\newcommand\DrawLatitudeCircle[2][1]{
  \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,dashed] (180-\angVis:1) arc (180-\angVis:\angVis:1);
}

\begin{document}

\begin{figure}
    \centering
    \tdplotsetmaincoords{60}{110}
    \begin{tikzpicture}[tdplot_main_coords, 
  declare function={dicri(\t,\th,\ph,\R)=sin(\th)*sin(\ph)*(\R*cos(\t)) - sin(\th)*cos(\ph)*(\R*sin(\t))+ cos(\th)*(1);}] %dicri is defined according to a parametrization n+r*cos(t)*u+r*sen(t)*v, being u and v orthonormal vectors in the intersecting plane
  \pgfmathsetmacro{\R}{5}% 
  \path  coordinate (T) at (0,0,3) %center of circle defined by the intersection
   coordinate (I) at (0,0,0); 
 
  \path[tdplot_screen_coords,shift={(I)},use as bounding box] (-1.2*\R,-1.2*\R)rectangle (1.2*\R,1.2*\R);%this limits the image position

    %draws dot+label for coordinates T and I
    \foreach \v/\position in {T/above,I/below} { 
        \draw[fill=black] (\v) circle (0.7pt) node [\position=0.2mm] {$\v$}; 
    }  
  
    \begin{scope}[tdplot_screen_coords, on background layer]
        \fill[ball color=white] (I) circle[radius=\R]; 
        % determine the zeros of dicri 
        \path[overlay,name path=dicri] plot[variable=\x,domain=0:360,samples=73] 
        ({\x*1pt},{dicri(\x,\tdplotmaintheta,\tdplotmainphi,4)}); 
        \path[overlay,name path=zero] (0,0) -- (360pt,0); 
        \path[name intersections={of=dicri and zero,total=\t}] 
        let \p1=(intersection-1),\p2=(intersection-2) in 
        \pgfextra{\xdef\tmin{\x1}\xdef\tmax{\x2}}; 
    \end{scope} %this has to do with finding the equation of the intersection circle, I believe
    
    \pgfmathsetmacro{\SmallR}{4} %the radius of the intersection circle is supposed as known
  
    
    % Draw continuous part of intersecting circle
    \draw[thick,save path=\pathA] plot[variable=\t,domain=\tmax:\tmin+360,samples=50,smooth]
    ({\SmallR*cos(\t)},
    {\SmallR*sin(\t)},
    {3}); %same parametrization used in dicri. 
     
     %Plane construction
        %% some definitions
        \def\angEl{35} % elevation angle
        \def\angAz{-105} % azimuth angle
        \def\angPhi{-40} % longitude of point P
        \def\angBeta{19} % latitude of point P
        
        %% working planes
        \pgfmathsetmacro\H{\R*cos(\angEl)} % distance to north pole
        \tikzset{xyplane/.style={
          cm={cos(\angAz),sin(\angAz)*sin(\angEl),-sin(\angAz),cos(\angAz)*sin(\angEl),(0,-\H)}
          }, >=latex, % option for nice arrows
          inner sep=0pt,%
          outer sep=2pt,%
          mark coordinate/.style={inner sep=0pt,outer sep=0pt,minimum size=3pt,
          fill=black,circle}
        }
        \LatitudePlane[equator]{\angEl}{0}
                
        %% draw xy shifted plane
       \filldraw[xyplane,shift={(0,0,3)},fill=gray!20,opacity=0.4] 
          (-1.8*\R,-2.6*\R) rectangle (2.2*\R,2*\R);
% added
     \clip ({\SmallR*cos(\tmax)},{\SmallR*sin(\tmax)},{3})
    coordinate (auxA)
    plot[variable=\t,domain=\tmax:\tmin+360,samples=50,smooth]
    ({\SmallR*cos(\t)},{\SmallR*sin(\t)},{3})         
    coordinate (auxB) -- ($(auxB)!\R*1cm!-90:(auxA)$)
     -- ($(auxA)!\R*1cm!90:(auxB)$) -- cycle;
    \fill[ball color=white,tdplot_screen_coords] (I) circle[radius=\R]; 
% moved
% Draw dashed part of intersecting circle
    \draw[dashed] plot[variable=\t,domain=\tmin:\tmax,samples=50,smooth] 
    ({\SmallR*cos(\t)},
    {\SmallR*sin(\t)},
    {3}); 
\end{tikzpicture}
\end{figure}
\end{document}

enter image description here

So far so good. But your code is a combination of various codes, which may or may not have come with reasonable explanations. If you mix them together and do not really understand them, it might be nontrivial to add several of those in your LaTeX document while staying sane. (Sorry for the wording.) IMHO a much better approach would be come up with one streamlined code in which you only specify the normal and the elevation of the plane. There are various posts to which you link, maybe they allow you to piece things together in a more structured way.

1
  • thank you, that looks great! I totally agree with the staying sane problem and I tried to use an approach where there was only need to specify the normal, but I ran into troubles because there is no easy way of recovering 3D coordinates. I have it as a future plan to come up with a general code for this task, though.
    – B. Dors
    Nov 4, 2020 at 10:26

You must log in to answer this question.

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