我想画一个有多个圆的球体(不一定通过球体的中心)。我想给圆的可见部分赋予一种样式,给隐藏部分赋予另一种样式。
但是我找不到这两部分之间的点的坐标。如果圆通过球体的中心,那么就没那么难了,因为这两个部分的大小相等,但在其他情况下,我找不到解决方案。
为了绘制圆弧,我使用命令\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}}
。