使用 tikz 和 tikz-3d 制作平面相交球体

使用 tikz 和 tikz-3d 制作平面相交球体

我真的很惊讶我找不到这个问题的答案,但问题是:如何使用 tikz 和 tikz-3dplots 绘制与球体相交的平面?

我设法做到了以下几点,使用这个答案这个答案(以下代码):

我拥有的

但我真正想要的是这样的(真遗憾,我完全不明白为什么 Geogebra 不将 3D 图形转换为 tikz):

目标

显然,我快到了。但阴影是错误的:平面被绘制成好像覆盖了球体,而不是与球体相交。我发现这里显然没有办法自动进行着色,但我可以手动进行。唯一的问题是,我不知道该怎么做。我花了一天多的时间试图理解前面提到的答案的代码中发生了什么,因为它们是针对特定情况制作的,我需要将它们转换为与随机平面相交的随机球体。我用收集到的信息在代码中添加了一些注释。但我仍然不确定所有这些命令是如何工作的,所以我无法正确识别应该在哪里结束和开始一个弧线,以便在平面绘图的顶部用一些深灰色填充。

我知道使用外部程序来生成图像是可行的(这里是一个使用 Asymptote 的好例子,但我必须向我的文件中添加许多与不同平面相交的不同球体,并且我希望能够使用 tikz 直接在 Latex 中生成它们,而不是使用多个图形。

所以我的问题简化为:如何在我的图中手动添加适当的阴影?(请注意,需要两种阴影:一种用于球体的盖子,一种较暗的用于球体盖子的后部。)当然,可以轻松修改不同球体和平面(特别是不平行于 xy 的平面)的替代代码也非常受欢迎。

我目前拥有的:

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

编辑:我设法使用以下命令找到连续圆变成虚线圆的点:

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

这些点是新阴影的开始位置,现在我只需要知道如何找到虚线圆和上球面的圆弧。

答案1

在这种情况下,很容易满足您的要求,但我不确定这是否会对您有长期帮助。以下是一些最小的损坏变化。

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

在此处输入图片描述

到目前为止一切顺利。但您的代码是各种代码的组合,这些代码可能有也可能没有合理的解释。如果您将它们混合在一起并且不真正理解它们,那么在保持理智的情况下在您的 LaTeX 文档中添加其中几个代码可能并不是一件容易的事。(抱歉措辞。)在我看来,一个更好的方法是想出一个简化的代码,您只在其中指定平面的法线和高度。您链接到了各种帖子,也许它们允许您以更结构化的方式将事情拼凑在一起。

相关内容