如何在球体上绘制任意圆的不同可见部分和隐藏部分

如何在球体上绘制任意圆的不同可见部分和隐藏部分

我想画一个有多个圆的球体(不一定通过球体的中心)。我想给圆的可见部分赋予一种样式,给隐藏部分赋予另一种样式。

但是我找不到这两部分之间的点的坐标。如果圆通过球体的中心,那么就没那么难了,因为这两个部分的大小相等,但在其他情况下,我找不到解决方案。

为了绘制圆弧,我使用命令\draw[domain=\a:\b,smooth] plot ({\r*cos(\x)}, {\r*sin(\x)});,因此我必须找到角度\a\b在第二个基础的 xy 平面中(用欧拉角改变之后)。

你知道如何获得这些积分吗?

我不喜欢使用该intersections包,因为我想使用真实的 3D 坐标来进行一些计算。

下面是一个 MWE,显示了特定情况下的这种情况:

\documentclass{article}
\usepackage{tikz}
\usepackage{tikz-3dplot}
\begin{document}
\begin{tikzpicture}

% Distance between the center of the sphere and the center of the circle
\pgfmathsetmacro\h{0.8}

% Sphere radius
\pgfmathsetmacro\R{1}

% Circle radius
\pgfmathsetmacro\r{sqrt(\R*\R-\h*\h)}

% Euler angles
\pgfmathsetmacro\alpha{90}
\pgfmathsetmacro\beta{0}
\pgfmathsetmacro\gamma{0}

\tdplotsetmaincoords{70}{110}

\begin{scope}[tdplot_main_coords]

\draw[tdplot_screen_coords] (0,0) circle (\R);

% Draw first basis
\draw[blue,-latex] (0,0,0) -- (1,0,0) node[shift={(0.1,0,0)}] {$e_x$};
\draw[red,-latex] (0,0,0) -- (0,1,0) node[shift={(0,0.1,0)}] {$e_y$};
\draw[green,-latex] (0,0,0) -- (0,0,1) node[shift={(0,0,0.1)}] {$e_z$};

\tdplotsetrotatedcoords{\alpha}{\beta}{\gamma}
\begin{scope}[shift={(0,0,-\h)},tdplot_rotated_coords]

% Draw second basis
\draw[blue,-latex] (0,0,0) -- (1,0,0) node[shift={(0.1,0,0)}] {$e'_x$};
\draw[red,-latex] (0,0,0) -- (0,1,0) node[shift={(0,0.1,0)}] {$e'_y$};
\draw[green,-latex] (0,0,0) -- (0,0,1) node[shift={(0,0,0.1)}] {$e'_z$};

\draw[domain=0:360,smooth] plot ({\r*cos(\x)}, {\r*sin(\x)});   

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

答案1

编辑:发布这篇文章一段时间后,我了解到@Fritz 用 pgfplots 以更优雅的方式做了非常类似的事情这里。因此,任何打算做类似事情的人请在继续阅读之前查看他的回答(并点赞)。

更新:这更像是最终答案,但却是一个 hack 答案。我调整了情节流,您可以在球面坐标中在球面上绘制任意路径。目前,仅z spherical cs:实现了,但将其扩展到其他方面很简单。第一个例子:

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{tikz-3dplot}
\makeatletter

% from https://tex.stackexchange.com/a/375604/121799
%along x axis
\define@key{x sphericalkeys}{radius}{\def\myradius{#1}}
\define@key{x sphericalkeys}{theta}{\def\mytheta{#1}}
\define@key{x sphericalkeys}{phi}{\def\myphi{#1}}
\tikzdeclarecoordinatesystem{x spherical}{% 
    \setkeys{x sphericalkeys}{#1}%
    \pgfpointxyz{\myradius*cos(\mytheta)}{\myradius*sin(\mytheta)*cos(\myphi)}{\myradius*sin(\mytheta)*sin(\myphi)}}

%along y axis
\define@key{y sphericalkeys}{radius}{\def\myradius{#1}}
\define@key{y sphericalkeys}{theta}{\def\mytheta{#1}}
\define@key{y sphericalkeys}{phi}{\def\myphi{#1}}
\tikzdeclarecoordinatesystem{y spherical}{% 
    \setkeys{y sphericalkeys}{#1}%
    \pgfpointxyz{\myradius*sin(\mytheta)*cos(\myphi)}{\myradius*cos(\mytheta)}{\myradius*sin(\mytheta)*sin(\myphi)}}


%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}{sin(\tdplotmaintheta)*cos(\tdplotmainphi-90)*sin(\mytheta)*cos(\myphi)
    +sin(\tdplotmaintheta)*sin(\tdplotmainphi-90)*sin(\mytheta)*sin(\myphi)
    +cos(\tdplotmaintheta)*cos(\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{0.3}
    \else
    \xdef\MCheatOpa{1}
    \fi
    %\typeout{\mytheta,\tdplotmaintheta;\myphi,\tdplotmainphi:\ntest}
    \pgfpointxyz{\myradius*sin(\mytheta)*cos(\myphi)}{\myradius*sin(\mytheta)*sin(\myphi)}{\myradius*cos(\mytheta)}}

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

\pgfdeclareplothandler{\pgfplothandlercurveto}{}{%
  point macro=\pgf@plot@curveto@handler@initial,
  jump macro=\pgf@plot@smooth@next@moveto,
  end macro=\pgf@plot@curveto@handler@finish
}

\def\pgf@plot@smooth@next@moveto{%
  \pgf@plot@curveto@handler@finish%
  \global\pgf@plot@startedfalse%
  \global\let\pgf@plotstreampoint\pgf@plot@curveto@handler@initial%
}

\def\pgf@plot@curveto@handler@initial#1{%
  \pgf@process{#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@second%
}

\def\pgf@plot@curveto@handler@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@third%
  \global\pgf@plot@startedtrue%
}

\def\pgf@plot@curveto@handler@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@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[ball color = gray!40, opacity = 0.5] (0,0,0) circle (\RadiusSphere);

\tdplotsetmaincoords{72}{100}

\begin{scope}[tdplot_main_coords]
% \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$};

\begin{scope}[blue,samples=60]

\foreach \X in {0,20,...,180}
\draw[->] plot[smooth,variable=\x,domain=-180:180] 
(z spherical cs: radius = \RadiusSphere, phi = \X, theta= \x);

\foreach \X in {0,20,...,180}
\draw[->] plot[smooth,variable=\x,domain=-180:180] 
(z spherical cs: radius = \RadiusSphere, phi = \x, theta= \X);

\end{scope}
\end{scope}

\end{tikzpicture}
\end{document}

在此处输入图片描述

如果你添加

\begin{tikzpicture}
\shade[ball color = gray!40, opacity = 0.5] (0,0,0) circle (\RadiusSphere);

\tdplotsetmaincoords{101}{100}

\begin{scope}[tdplot_main_coords]
% \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$};

\begin{scope}[red,samples=180,thick] % note that you need to put options like
% thick or a color into a scope

\draw plot[smooth,variable=\x,domain=-180:180] 
(z spherical cs: radius = \RadiusSphere, phi = {4*cos(4*\x)}, theta= {\x});

\draw plot[smooth,variable=\x,domain=-180:180] 
(z spherical cs: radius = \RadiusSphere, phi = {\x-180}, theta=
{70+8*sin(\x*(\x/60))});

\end{scope}
\end{scope}

\end{tikzpicture}

你会得到

在此处输入图片描述

如果您想彻底消除隐藏线,只需替换\xdef\MCheatOpa{0.3}\xdef\MCheatOpa{0}

我还相信可以使可见线和隐藏线之间的区别更加明显:分离可见路径和隐藏路径的平面具有指向我们眼睛的法线矢量。

\begin{tikzpicture}
\draw[thick] (-9,0) -- ++(1,0.5) (-9,0) -- ++(1,-0.5)
(-9,0) +(0.8,0) arc(0:{atan2(1,2)}:0.8)
(-9,0) +(0.8,0) arc(0:{-atan2(1,2)}:0.8);

\shade[ball color = gray!40, opacity = 0.5] (0,0,0) circle (\RadiusSphere);

\tdplotsetmaincoords{101}{100}

\begin{scope}[tdplot_main_coords]
 \draw[-latex,ultra thick] (0,0,0) -- (z spherical cs: radius = \RadiusSphere, phi =
 {30+90}, theta= {-90}) 
 node[below]{$\vec n$};

 \draw[-latex,] (0,0,0) -- (z spherical cs: radius = \RadiusSphere, phi =
 {30+40}, theta= {-30}) 
 node[above]{$\vec x_\mathrm{vis}$};

 \draw[-latex,] (0,0,0) -- (z spherical cs: radius = \RadiusSphere, phi =
 {30+20}, theta= {+40}) 
 node[above]{$\vec x_\mathrm{hid}$};

\begin{scope}[red,samples=180,thick] % note that you need to put options like
% thick or a color into a scope

\draw[fill=yellow,opacity=0.3] plot[variable=\x,domain=-180:180] 
(z spherical cs: radius = \RadiusSphere, phi = {30}, theta= {\x});

\draw plot[smooth,variable=\x,domain=-180:180] 
(z spherical cs: radius = \RadiusSphere, phi = {30}, theta= {\x});


\end{scope}
\end{scope}

\end{tikzpicture}

在此处输入图片描述

如果给定向量在此法线上的投影为正,则该点可见,否则该点隐藏。

旧答案(如果我应该删除它,请告诉我):这当然不是最终答案,但事实证明,人们可以很容易地使用Alain Matthes 的酷炫宏在您的设置中。所有人要做的就是同步仰角\angEl,当然还有半径。曲线隐藏部分的样式由样式控制hidden lines,样式设置为,opacity=0.4但您可以将其更改为dashed,或者任何你喜欢的值。幸运的是,方位角,即中的第二个角度在\tdplotsetmaincoords这里并不重要,因为您说的是您想画圆。我将所需的圆画成圆弧,只是为了让您更容易地使用它。正如我所提到的,这不是最终答案。最终答案会(或将会)带来更多的自动化。但我添加了一个功能,即记住路径变为可见/隐藏的坐标。

\documentclass{article}
\usepackage{tikz}
\usepackage{tikz-3dplot}
\usetikzlibrary{calc,fadings,decorations.pathreplacing,shadings}
\tikzset{hidden lines/.style={opacity=0.4}}
\pgfkeys{/tikz/.cd,
    visible angle A/.store in=\VisibleAngleA,
    visible angle A=0,
    visible angle B/.store in=\VisibleAngleB,
    visible angle B=0,
}

\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{\RadiusSphere*\cosEl*\sint}
  \tikzset{#1/.style={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}} %
}
\newcommand\NewLatitudePlane[4][current plane]{%
  \pgfmathsinandcos\sinEl\cosEl{#3} % elevation
  \pgfmathsinandcos\sint\cost{#4} % latitude
  \pgfmathsetmacro\yshift{#2*\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,hidden lines] (\angVis-180:1) arc (\angVis-180:\angVis:1);
}
\newcommand\DrawLongitudeArc[4][black]{
  \LongitudePlane{\angEl}{#2}
  \tikzset{current plane/.prefix style={scale=1}}
  \pgfmathsetmacro\angVis{atan(sin(#2)*cos(\angEl)/sin(\angEl))} %
  \pgfmathsetmacro\angA{mod(max(\angVis,#3),360)} %
  \pgfmathsetmacro\angB{mod(min(\angVis+180,#4),360} %
  \draw[current plane,#1,hidden lines] (#3:\RadiusSphere) arc (#3:#4:\RadiusSphere);
  \draw[current plane,#1]  (\angA:\RadiusSphere) arc (\angA:\angB:\RadiusSphere);
}%
\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,hidden lines] (180-\angVis:1) arc (180-\angVis:\angVis:1);
}

\newcommand\DrawLatitudeArc[4][black]{
  \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)))}
  \pgfmathsetmacro\angA{max(min(\angVis,#3),-\angVis-180)} %
  \pgfmathsetmacro\angB{min(\angVis,#4)} %
  \tikzset{visible angle A=\angA,visible angle B=\angB}
  \draw[current plane,#1,hidden lines] (#3:\RadiusSphere) arc (#3:#4:\RadiusSphere);
  \draw[current plane,#1] (\angA:\RadiusSphere) arc (\angA:\angB:\RadiusSphere);
}

%% document-wide tikz options and styles

\tikzset{%
  >=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}%
}

\begin{document}

\begin{tikzpicture} % 
\def\RadiusSphere{3} % sphere radius
\def\angEl{20} % elevation angle 

\shade[ball color = gray!40, opacity = 0.5] (0,0) circle (\RadiusSphere);


\DrawLatitudeArc[blue]{-acos(0.6)}{-200}{160}
% the coordinates at which the curve becomes visible/invisible got stored 
\typeout{\VisibleAngleA,\VisibleAngleB}
\fill[current plane] (\VisibleAngleA:\RadiusSphere) circle(4pt) node[below]{A};
\fill[current plane] (\VisibleAngleB:\RadiusSphere) circle(4pt) node[below]{B};


\begin{scope}[scale=\RadiusSphere]

% Distance between the center of the sphere and the center of the circle
\pgfmathsetmacro\h{0.8}

% Sphere radius
\pgfmathsetmacro\R{1}

% Circle radius
\pgfmathsetmacro\r{sqrt(\R*\R-\h*\h)}

% Euler angles
\pgfmathsetmacro\alpha{90}
\pgfmathsetmacro\beta{0}
\pgfmathsetmacro\gamma{0}

\tdplotsetmaincoords{90-\angEl}{110} % \angEl was chosen so reproduce your value 70

\begin{scope}[tdplot_main_coords]

\draw[tdplot_screen_coords] (0,0) circle (\R);

% Draw first basis
\draw[blue,-latex] (0,0,0) -- (1,0,0) node[shift={(0.1,0,0)}] {$e_x$};
\draw[red,-latex] (0,0,0) -- (0,1,0) node[shift={(0,0.1,0)}] {$e_y$};
\draw[green,-latex] (0,0,0) -- (0,0,1) node[shift={(0,0,0.1)}] {$e_z$};

\tdplotsetrotatedcoords{\alpha}{\beta}{\gamma}
\begin{scope}[shift={(0,0,-\h)},tdplot_rotated_coords]

% Draw second basis
\draw[blue,-latex] (0,0,0) -- (1,0,0) node[shift={(0.1,0,0)}] {$e'_x$};
\draw[red,-latex] (0,0,0) -- (0,1,0) node[shift={(0,0.1,0)}] {$e'_y$};
\draw[green,-latex] (0,0,0) -- (0,0,1) node[shift={(0,0,0.1)}] {$e'_z$};

%\draw[domain=0:360,smooth] plot ({\r*cos(\x)}, {\r*sin(\x)});   

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

\end{document}

在此处输入图片描述

也一样\tikzset{hidden lines/.style={dashed}}

在此处输入图片描述

相关内容