在球体上绘制填充表面

在球体上绘制填充表面

我知道已经有关于这个主题的话题(例如这里这里)但是,由于我们提出的出色解决方案这里这里对于其他问题,我想知道是否有办法在这个框架中做到这一点。

答案1

原则使用 fillbetween 库可以做到这一点。技术挑战在于 TiZ 会发现太多交点,而交点的数量取决于绘制球体路径所用的样本数量。因此,原则上,您可以在球体上绘制任意形状(不仅仅是弧线!),并区分隐藏和可见的线和表面。虽然对于线条,它只需使用样式即可spherical smooth,但对于表面,不幸的是,必须手动摆弄。以下是一个例子:

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{intersections}
\usepackage{pgfplots} 
\pgfplotsset{compat=1.16}
\usepgfplotslibrary{fillbetween}
\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 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)}}

%%%%%%%%%%%%%%%%%
% define "new" plot handler
\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}


\begin{tikzpicture}
\shade[name path=sphere,ball color = gray!40, opacity = 0.5] 
plot[smooth,domain=-180:180] ({\RadiusSphere*cos(\x)},{\RadiusSphere*sin(\x)});


\tdplotsetmaincoords{42}{205}
\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$};

\draw plot[spherical smooth,variable=\x,domain=-180:180] 
(z spherical cs: radius=\RadiusSphere,phi={50*sin(\x)},theta={40*cos(\x)});
% I modified the plot handler in order to change the opacity along the paths
% therefore I need to redraw it if I want to use it in fills :-(
\path[name path=circle,fill=blue,opacity=0.2]  plot[smooth,variable=\x,domain=-180:180] 
(z spherical cs: radius=\RadiusSphere,phi={50*sin(\x)},theta={40*cos(\x)});

% main technical challenge: TikZ finds tons of intersections instead of 2
% \draw[red, name intersections={of=sphere and circle,name=i, total=\t}] 
% \foreach \s in {1,...,\t}{node[fill,circle,scale=0.3,label=above:\s] at (i-\s) {}} 
% \pgfextra{\typeout{\t}};

% 
\path [%draw,yellow,ultra thick,opacity=1,
    fill=blue,opacity=0.4,
    name path=visible surface,
    intersection segments={
        of=circle and sphere,
        sequence={A5--B0[reverse]--B7[reverse]}
    }];

\end{scope}
\end{tikzpicture}
\end{document}

在此处输入图片描述

你可以增加样本并改变视角,但这样做的代价是交叉点的数量会发生变化,你必须重新找到相关的路径。例如,对于

\shade[name path=sphere,ball color = gray!40, opacity = 0.5] 
plot[smooth,domain=-180:180,samples=120] ({\RadiusSphere*cos(\x)},{\RadiusSphere*sin(\x)}); % now 120 samples
\tdplotsetmaincoords{52}{200}

Ti 发现的交叉点数量Z 是 5,你需要做

  \path [%draw,yellow,ultra thick,opacity=1,
    fill=blue,opacity=0.4,
    name path=visible surface,
    intersection segments={
        of=circle and sphere,
        sequence={A4--B0[reverse]--B5[reverse]}
    }];

要得到

在此处输入图片描述

如果你厌倦了手动调整这些内容,请切换到渐近线;-)

小更新:第一个版本没有填充。

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{intersections,decorations.markings}
\usepackage{tikz-3dplot}
\tikzset{endmark/.style={postaction={decorate,decoration={markings,
     mark=at position 0 with {\coordinate (X0); },
     mark=at position 1 with {\coordinate (X1); }}}}}

\pgfkeys{/tikz/.cd,
    hidden opacity/.store in=\HiddenOpacity,
    hidden opacity=0.3,
}

\makeatletter

\xdef\prevhidden@toggle{0}
\xdef\spherical@plot@start{0}
% the spherical coordinates are from https://tex.stackexchange.com/a/375604/121799
% but the routine got modified to
% (i) decide whether a point is "visible" or "hidden"
% (ii) 

%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}
      %\typeout{point\space hidden}
      \ifnum\prevhidden@toggle=1 % previous point was also hidden
          \xdef\hid@path{\hid@path (z spherical cs: radius={\myradius},phi={\myphi},theta={\mytheta})}
        \else % finish visible path
         \ifnum\spherical@plot@start=1 % this is the first point of the plot
          %\typeout{start}
         \else
          \ifnum\thevis@paths=0
            \xdef\lst@vis@paths{{\vis@path}} 
           \else
            \xdef\lst@vis@paths{\lst@vis@paths,{\vis@path}} 
          \fi
          \stepcounter{vis@paths}
         \fi  % and start a new hidden path      
         \xdef\hid@path{(z spherical cs: radius={\myradius},phi={\myphi},theta={\mytheta})}
        \fi
        \xdef\prevhidden@toggle{1}
    \else
        \xdef\MCheatOpa{1}
        %\typeout{point\space visible}
        \ifnum\prevhidden@toggle=0 % previous point was also visible
           \xdef\vis@path{\vis@path (z spherical cs: radius={\myradius},phi={\myphi},theta={\mytheta})}
        \else % finish hidden path
          \ifnum\spherical@plot@start=1
           %\typeout{start}
          \else
           \ifnum\thehid@paths=0
             \xdef\lst@hid@paths{{\hid@path}} 
           \else
             \xdef\lst@hid@paths{\lst@hid@paths,{\hid@path}} 
           \fi 
           \stepcounter{hid@paths}
          \fi % and start a new visible path
          \xdef\vis@path{(z spherical cs: radius={\myradius},phi={\myphi},theta={\mytheta})}
        \fi
        \xdef\prevhidden@toggle{0}
    \fi
    %\typeout{\mytheta,\tdplotmaintheta;\myphi,\tdplotmainphi:\ntest}
    \pgfpointxyz{\myradius*cos(\mytheta)*cos(\myphi)}{%
    \myradius*cos(\mytheta)*sin(\myphi)}{\myradius*sin(\mytheta)}}

%%%%%%%%%%%%%%%%%
% define "new" plot handler
\newcounter{vis@paths}
\newcounter{hid@paths}
\tikzoption{spherical smooth}[]{\xdef\lst@vis@paths{}
\xdef\lst@hid@paths{}
\xdef\hid@path{}
\xdef\vis@path{}
\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
  \setcounter{vis@paths}{0}
  \setcounter{hid@paths}{0}
  \xdef\spherical@plot@start{1}
  \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{%
  \xdef\spherical@plot@start{0}
  \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%
  \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%
  \ifnum\prevhidden@toggle=1
    \xdef\lstvispaths{\lst@vis@paths}
    \ifx\hid@path\empty
    \else
      %\typeout{closing\space hidden}
      \ifx\lst@hid@paths\empty
       \xdef\lst@hid@paths{{\hid@path}}
      \else
       \xdef\lst@hid@paths{\lst@hid@paths,{\hid@path}}
      \fi 
      \foreach \X [count=\Y] in \lst@hid@paths
      {\xdef\my@len{\Y}}
      %\typeout{\my@len\space hidden\space patches}
      \xdef\lsthidpaths{}     
      \foreach \X [count=\Y] in \lst@hid@paths
      {\ifnum\Y=1 % save the first stretch
        \xdef\tmppath{\X}
        \ifnum\my@len=1
          \xdef\lsthidpaths{{\X}}
        \fi  
        \else
        \ifnum\Y=\my@len       
          \ifnum\Y=2
           \xdef\lsthidpaths{{\X\tmppath}}
          \else
           \xdef\lsthidpaths{\lsthidpaths,{\X\tmppath}}
          \fi
           %\typeout{adding:{\tmppath\X}}
           %\typeout{result:\lsthidpaths}
        \else
         \xdef\lsthidpaths{\lsthidpaths,{\X}}
        \fi
       \fi}
       %\typeout{hidden\space paths:\lst@hid@paths}
    \fi
  \else
    \xdef\lsthidpaths{\lst@hid@paths}
    \ifx\vis@path\empty
    \else
      %\typeout{closing\space visible}
      \xdef\lst@vis@paths{\lst@vis@paths,{\vis@path}}
      \foreach \X [count=\Y] in \lst@vis@paths
      {\xdef\my@len{\Y}}
      %\typeout{\my@len\space hidden\space coordinates:\vis@path}
      \xdef\lstvispaths{}     
      \foreach \X [count=\Y] in \lst@vis@paths
      {\ifnum\Y=1 % save the first stretch
        \xdef\tmppath{\X}
        \ifnum\my@len=1
          \xdef\lstvispaths{{\X}}
        \fi  
        \else
        \ifnum\Y=\my@len       
         \ifnum\my@len=1
          \xdef\lstvispaths{{\X}}
         \else
          \ifnum\Y=2
           \xdef\lstvispaths{{\X\tmppath}}
          \else
           \xdef\lstvispaths{\lstvispaths,{\X\tmppath}}
          \fi
           %\typeout{adding:{\tmppath\X}}
           %\typeout{result:\lstvispaths}
         \fi     
        \else
         \xdef\lstvispaths{\lstvispaths,{\X}}
        \fi
       \fi}
    \fi
  \fi 
}
\makeatother

\newcommand{\FillVisibleSurfaces}[1][]{\xdef\numvis{0}
\foreach \X [count=\Y] in \lstvispaths
{\ifx\X\empty
\else
\xdef\numvis{\Y}
\fi}
\ifnum\numvis=0%
\else
\foreach \X [count=\Y] in \lstvispaths
{%\typeout{processing\space \X}
\path[endmark] plot[tdplot_main_coords,samples=60] coordinates {\X};
\fill[#1] let
\p1=(X0),\p2=(X1),\n1={mod(atan2(\y1,\x1)+720,360)},\n2={mod(atan2(\y2,\x2)+720,360)} 
in % \pgfextra{\typeout{visible:\space start:\n1,end:\n2}}
plot[tdplot_main_coords,samples=60] coordinates {\X} -- (X1) arc(\n2:\n1:\RadiusSphere);
}
\fi
}
\newcommand{\FillHiddenSurfaces}[1][]{\xdef\numhid{0}
\foreach \X [count=\Y] in \lsthidpaths
{\ifx\X\empty
\else
\xdef\numhid{\Y}
\fi}
\ifnum\numhid=0%
\else
\foreach \X [count=\Y] in \lsthidpaths
{
\path[endmark] plot[tdplot_main_coords,samples=60] coordinates {\X};
\fill[#1] let
\p1=(X0),\p2=(X1),\n1={mod(atan2(\y1,\x1)+720,360)},\n2={mod(atan2(\y2,\x2)+720,360)} 
in %\pgfextra{\typeout{hidden\space start:\n1,end:\n2}}
plot[tdplot_main_coords,samples=60] coordinates {\X} -- (X1) arc(\n2:\n1:\RadiusSphere)
--cycle;
}
\fi
}


\begin{document}
\pgfmathsetmacro{\RadiusSphere}{3}

\foreach \X in {0,10,...,350}
{\begin{tikzpicture}
\shade[name path=sphere,ball color = gray!40, opacity = 0.5] 
plot[smooth,domain=-180:180,samples=120] ({\RadiusSphere*cos(\x)},{\RadiusSphere*sin(\x)});


\tdplotsetmaincoords{110}{\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$};

\draw plot[spherical smooth,variable=\x,domain=-180:180] 
(z spherical cs: radius=\RadiusSphere,phi={50*sin(\x)},theta={40*cos(\x)});
\end{scope}
% note that these commands need to be placed *outside* the tdplot_main_coords scope
\FillHiddenSurfaces[blue,opacity=0.2]
\FillVisibleSurfaces[blue,opacity=0.5]
\end{tikzpicture}
}
\end{document}

在此处输入图片描述

我相信已经用大量的\if语句解决了几个问题。但是,上面的动画显然看起来不对。(请注意,底部和右侧出现的额外位是由于转换为 gif 造成的,它们在 pdf 中不存在。)但这是因为在球体上,封闭曲面的“内部”定义不明确,而是有两个选项。而且总是填充较小的区域似乎也不对,因为人们可能想要填充较大的区域。读过这篇文章的人知道如何在数学上甚至在 Ti 中定义它吗?Z?

最后,我想提出一个“作弊”解决方案,它几乎与上面的解决方案一样强大,但不需要修改绘图处理程序,并且可以与任何\draw命令一起使用,而不仅仅是plot。只需绘制两次,让坐标系决定该点是否可见。如果不可见,则返回定义球体的圆边界上的一个点。

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{intersections,decorations.markings}
\usepackage{tikz-3dplot}

\makeatletter

%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 visible}{% 
    \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
      \pgfmathsetmacro{\myx}{\myradius*cos(\mytheta)*cos(\myphi)*\raarot
      +\myradius*cos(\mytheta)*sin(\myphi)*\rabrot+\myradius*sin(\mytheta*\racrot}
      \pgfmathsetmacro{\myy}{\myradius*cos(\mytheta)*cos(\myphi)*\rbarot
      +\myradius*cos(\mytheta)*sin(\myphi)*\rbbrot+\myradius*sin(\mytheta*\rbcrot}  
      \pgfpoint{\RadiusSphere*cos(atan2(\myy,\myx))*1cm}{\RadiusSphere*sin(atan2(\myy,\myx))*1cm}
    \else
      \pgfpointxyz{\myradius*cos(\mytheta)*cos(\myphi)}{%
      \myradius*cos(\mytheta)*sin(\myphi)}{\myradius*sin(\mytheta)}
    \fi
}

\tikzdeclarecoordinatesystem{z spherical invisible}{% 
    \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
    %\typeout{\raarot,\rbarot,\rabrot,\rbbrot,\racrot, \rbcrot}
    \pgfmathsetmacro{\ntest}{ifthenelse(\Xtest<0,0,1)}
    \ifnum\ntest=1
      \pgfmathsetmacro{\myx}{\myradius*cos(\mytheta)*cos(\myphi)*\raarot
      +\myradius*cos(\mytheta)*sin(\myphi)*\rabrot+\myradius*sin(\mytheta*\racrot}
      \pgfmathsetmacro{\myy}{\myradius*cos(\mytheta)*cos(\myphi)*\rbarot
      +\myradius*cos(\mytheta)*sin(\myphi)*\rbbrot+\myradius*sin(\mytheta*\rbcrot}  
      \pgfpoint{\RadiusSphere*cos(atan2(\myy,\myx))*1cm}{\RadiusSphere*sin(atan2(\myy,\myx))*1cm}
    \else
      \pgfpointxyz{\myradius*cos(\mytheta)*cos(\myphi)}{%
      \myradius*cos(\mytheta)*sin(\myphi)}{\myradius*sin(\mytheta)}
    \fi
}

%%%%%%%%%%%%%%%%%

\makeatother
% decoration
\begin{document}
\pgfmathsetmacro{\RadiusSphere}{3}

\foreach \X in {0,10,...,350}
{\begin{tikzpicture}
\path[use as bounding box] ({-1.2*\RadiusSphere},{-1.2*\RadiusSphere}) rectangle
({1.2*\RadiusSphere},{1.2*\RadiusSphere});
\shade[ball color = gray!40, opacity = 0.5]  (0,0) circle (\RadiusSphere);

\tdplotsetmaincoords{110}{\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$};
\pgfmathtruncatemacro{\Dis}{ifthenelse(\X<50,1,0)+ifthenelse(\X>130,1,0)}
\ifnum\Dis=0
\else
\draw[opacity=0.3,fill opacity=0.2,fill=blue] plot[smooth,variable=\x,domain=-180:180] 
(z spherical invisible cs: radius=\RadiusSphere,phi={50*sin(\x)},theta={40*cos(\x)});
\fi

\pgfmathtruncatemacro{\Dis}{ifthenelse(\X<230,1,0)+ifthenelse(\X>320,1,0)}
\ifnum\Dis=0
\else
\draw[fill opacity=0.5,fill=blue] plot[smooth,variable=\x,domain=-180:180] 
(z spherical visible cs: radius=\RadiusSphere,phi={50*sin(\x)},theta={40*cos(\x)});
\fi

%(z spherical cs: {\RadiusSphere},{50*sin(\x)},{40*cos(\x)});
\end{scope}

\end{tikzpicture}
}
\end{document}

在此处输入图片描述

这很好用除非隐藏的表面包含圆心。在这种情况下,应该不绘制该表面。也许有一种方法可以避免手动删除这些表面。

相关内容