我想画一个看起来像 15 度倾斜的半球形的形状,并在底面上放置一些三角形阵列中的圆圈。然后这些圆圈被投影到球体表面上。
我可以在基平面上画小圆。但是圆的投影形状不同。所以我不知道如何解决它。我认为,有圆柱形孔,我必须在球面上找到它们的交点。
谢谢大家的回复。我想知道是否有任何方法可以用笛卡尔坐标而不是极坐标来指定圆心或投影。因为很难以自动方式(换句话说,使用 \foreach 命令)指定如下所示的每个小圆的极坐标。
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{pgfplots}
\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/.estyle={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/.estyle={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}} %
}
\newcommand\DrawLongitudeCircle[2][2]{
\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,dotted] (\angVis:1) arc (\angVis:180:1);
\draw[current plane,dotted] (\angVis+90:1) arc (\angVis+90:0:1);
}
\newcommand\DrawLatitudeCircle[2][3]{
\LatitudePlane{\angEl}{#2}
\tikzset{current plane/.prefix style={scale=#1}}
\pgfmathsetmacro\sinVis{sin(#2)/cos(#2)*sin(\angEl)/cos(\angEl)}
\pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}
\draw[current plane,dotted] (\angVis:1) arc (\angVis:-\angVis-180:1);
\draw[current plane,dotted] (180-\angVis:1) arc (180-\angVis:\angVis:1);
}
\newcommand\DrawMidCircle[2][4]{
\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] (180-\angVis:1) arc (180-\angVis:\angVis:1);
}
\newcommand\DrawAxis[2][5]{
\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] (1,0) -- (-1,0);
}
\newcommand\DrawSmallCircle[2][6]{
\LatitudePlane{\angEl}{0}
\tikzset{current plane/.prefix style={scale=1}}
\pgfmathsetmacro\sinVis{sin(0)/cos(0)*sin(\angEl)/cos(\angEl)}
% angle of "visibility"
\pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}
\pgfmathsetmacro\x{(#1*2*\r)*cos(\angRot)-(#2*2*\r*cos(30))*sin(\angRot)}
\pgfmathsetmacro\y{(#1*2*\r)*sin(\angRot)+(#2*2*\r*cos(30))*cos(\angRot)}
\draw[current plane,green!85!black, fill=green!85!black, fill opacity=0.25] ($(\x,\y)+(0:\r)$) arc (\angVis:360+\angVis:\r);
}
\newcommand\DrawSmallCircleShift[2][7]{
\LatitudePlane{\angEl}{0}
\tikzset{current plane/.prefix style={scale=1}}
\pgfmathsetmacro\sinVis{sin(0)/cos(0)*sin(\angEl)/cos(\angEl)}
% angle of "visibility"
\pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}
\pgfmathsetmacro\x{(#1*2*\r+\r)*cos(\angRot)-(#2*2*\r*cos(30))*sin(\angRot)}
\pgfmathsetmacro\y{(#1*2*\r+\r)*sin(\angRot)+(#2*2*\r*cos(30))*cos(\angRot)}
\draw[current plane,green!85!black, fill=green!85!black, fill opacity=0.25] ($(\x,\y)+(0:\r)$) arc (\angVis:360+\angVis:\r);
}
\begin{document}
\begin{tikzpicture}
\def\R{4}
\def\r{0.35}
\def\angRot{30}
\node (C) at (0,0) {};
\draw (C) -- (0,1);
\def\angEl{20} % elevation angle
\LongitudePlane{\angEl}{\angRot}
\tikzset{current plane/.prefix style={scale=\R}}
% angle of "visibility"
\pgfmathsetmacro\angVis{atan(sin(\angRot)*cos(\angEl)/sin(\angEl))} %
\draw[current plane] (-1,0) node[below left]{$u$} -- (1,0);
\draw[current plane] (0,0,0) -- (0,1,0);
\LongitudePlane{\angEl}{90+\angRot}
\tikzset{current plane/.prefix style={scale=\R}}
% angle of "visibility"
\pgfmathsetmacro\angVis{atan(sin(90+\angRot)*cos(\angEl)/sin(\angEl))} %
\draw[current plane] (-1,0) node[below right]{$v$} -- (1,0);
\foreach \n in {0,-2}
{\foreach \m in {-4,-3,...,0}
{\DrawSmallCircle[\m]{\n}}}
\foreach \n in {-1,-3}
{\foreach \m in {-5,-4,...,-1}
{\DrawSmallCircleShift[\m]{\n}}}
\foreach \n in {-4}
{\foreach \m in {-3,...,0}
{\DrawSmallCircle[\m]{\n}}}
\foreach \n in {-5}
{\foreach \m in {-3,-2,-1}
{\DrawSmallCircleShift[\m]{\n}}}
\draw[current plane] (0,0,0) -- (0,1,0);
\foreach \t in {0,10,...,80} { \DrawLatitudeCircle[\R]{\t} }
\foreach \t in {0} { \DrawMidCircle[\R]{\t} }
\foreach \t in {0,\angRot,...,150} { \DrawLongitudeCircle[\R]{\t} }
\end{tikzpicture}
\end{document}
答案1
好吧,速度没有奖励(不仅仅是因为有一些重复的计算:TikZ是对于这种东西来说,速度很慢),但这展示了一种方法。我预计 asymptote/PSTricks 可以做得更快,但我没有看到 TikZ 中有任何其他方法可以做到这一点。
数学是简单的“粗略”三角学(在这种情况下是字面意思)。唯一的“洞察力”是手动绘制圆圈。
编辑1受到 Jake 缺失的(在撰写本文时) PGF Plots 答案的启发,我将大多数语句更改\foreach
为 TikZplot
命令,这使得事情变得更快一些。
编辑2将关键函数更改为,因为这比圆圈靠近边缘时sqrt(\R^2-(\cx+\r*cos(\t))^2-(\cy+\r*sin(\t))^2)
提供更好的准确性。veclen
编辑3已添加第二个版本,它使用图层同时添加两个圆圈。
\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[x=(-30:4cm),y=(30:4cm),z=(90:4cm)]
\def\R{1}
\draw (-\R,0,0) -- (\R,0,0);
\draw (0,-\R,0) -- (0,\R,0);
\draw (0,0,0) -- (0,0,\R);
\draw plot [domain=0:360, samples=60, variable=\i]
(\R*cos \i, \R*sin \i, 0) -- cycle;
\def\r{0.075}
\foreach \cr in {0.3, 0.5, 0.7, 0.9}
\foreach \ca [evaluate={\cx=\cr*cos \ca; \cy=\cr*sin \ca;}]in {-90,-60,-30,0,30, 60, 90}
\draw [green!85!black, fill=green!85!black, fill opacity=0.25]
plot [domain=0:360, samples=40, variable=\i]
(\cx+\r*cos \i, \cy+\r*sin \i, 0) -- cycle;
\foreach \i in {0, 30,...,150}
\draw [dotted] plot [domain=-90:90, samples=30, variable=\j]
(\R*cos \i*sin \j,\R*sin \i*sin \j, \R*cos \j);
\foreach \j in {0, 15,...,90}
\draw [dotted] plot [domain=0:360, samples=60, variable=\i]
(\R*cos \i*sin \j,\R*sin \i*sin \j, \R*cos \j);
\foreach \cr in {0.3, 0.5, 0.7, 0.9}
\foreach \ca [evaluate={\cx=\cr*cos \ca; \cy=\cr*sin \ca;}]in {-90,-60,-30,0,30, 60, 90}
\draw [red, fill=red, fill opacity=.25]
plot [domain=0:360, samples=40, variable=\t]
(\cx+\r*cos \t,\cy+\r*sin \t, {sqrt(\R^2-(\cx+\r*cos(\t))^2-(\cy+\r*sin(\t))^2)})
-- cycle;
\end{tikzpicture}
\end{document}
这是使用图层和笛卡尔坐标(而不是极坐标)来表示圆的版本:
\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\pgfdeclarelayer{dome floor}
\pgfdeclarelayer{dome}
\pgfdeclarelayer{dome surface}
\pgfsetlayers{dome floor,main,dome,dome surface}
\def\addcircle#1#2#3#4{%
\begingroup%
\pgfmathparse{#1}\let\R=\pgfmathresult
\pgfmathparse{#2}\let\cx=\pgfmathresult
\pgfmathparse{#3}\let\cy=\pgfmathresult
\pgfmathparse{#4}\let\r=\pgfmathresult
\begin{pgfonlayer}{dome floor}
\draw [blue!45!black, fill=blue!45, fill opacity=0.25]
plot [domain=0:360, samples=40, variable=\i]
(\cx+\r*cos \i, \cy+\r*sin \i, 0) -- cycle;
\end{pgfonlayer}
\begin{pgfonlayer}{dome surface}
\draw [red!75!black, fill=red!75, fill opacity=0.25]
plot [domain=0:360, samples=60, variable=\t]
(\cx+\r*cos \t,\cy+\r*sin \t, {sqrt(max(\R^2-(\cx+\r*cos(\t))^2-(\cy+\r*sin(\t))^2, 0))})
-- cycle;
\end{pgfonlayer}
\endgroup%
}
\begin{tikzpicture}[x=(-30:1cm),y=(30:1cm),z=(90:1cm)]
\def\R{6}
\begin{pgfonlayer}{dome floor}
\draw (-\R,0,0) -- (\R,0,0);
\draw (0,-\R,0) -- (0,\R,0);
\draw plot [domain=0:360, samples=90, variable=\i]
(\R*cos \i, \R*sin \i, 0) -- cycle;
\end{pgfonlayer}
\draw (0,0,0) -- (0,0,\R);
\begin{pgfonlayer}{dome surface}
\foreach \i in {0, 30,...,150}
\draw [dotted] plot [domain=-90:90, samples=60, variable=\j]
(\R*cos \i*sin \j,\R*sin \i*sin \j, \R*cos \j);
\foreach \j in {0, 15,...,90}
\draw [dotted] plot [domain=0:360, samples=60, variable=\i]
(\R*cos \i*sin \j,\R*sin \i*sin \j, \R*cos \j);
\end{pgfonlayer}
\def\r{0.5}
\foreach \m [evaluate={\N=max(-4, \m-7);}]in {0,...,5}{
\foreach \n in {0,-1,...,\N}
{\addcircle{\R}{\m*sin 60}{\n-mod(abs(\m),2)*\r}{\r}}}
\end{tikzpicture}
\end{document}
答案2
Asymptote
解决方案。它使用一个函数(来自维基百科)计算交点曲线。根据需要调整圆的位置。s.asy
:
size(300,300);
size3(300,300,300);
import graph3;
currentprojection=orthographic(-5,-4,2,center=true);
guide3 sphere_x_cyl(real a, real r, real R, int n=10){
// return a closed curve of the Sphere–cylinder intersection (top part)
// only for the case when a cylinder is completely inside
// except special case when the x-shift a=0
assert(a>0,"***** Positive a expected.");
real b=(R^2-r^2-a^2)/(2a);
guide3 g;
int n=30;
real phi, dphi;
phi=0;
dphi=360/n;
triple t;
for(int i=0;i<n;++i){
t=(r*Cos(phi),r*Sin(phi),sqrt(2a*(b+r*Cos(phi))));
g=g--t;
phi+=dphi;
}
g=g--cycle;
return g;
}
void Draw3(guide3 g,pen p=currentpen){
draw(project(g),p);
}
void FillDraw3(guide3 g,pen fillPen=currentpen, pen linePen=nullpen){
filldraw(project(g),fillPen,linePen);
}
int nr=11;
real r=1;
real R=nr*r;
real a; // distance between the centers
triple O=(0,0,0);
guide3 circ(triple sh=O, real r){
guide3 uc=(r,0,0)..(0,r,0)..(-r,0,0)..(0,-r,0)..cycle;
return shift(sh)*uc;
}
guide3 semicirc(triple sh=O, real r){
guide3 uc=(r,0,0)..(0,r,0)..(-r,0,0);
return shift(sh)*uc;
}
guide3 Parallel(real h, real R){
return circ((0,0,h),sqrt(R^2-h^2));
}
pen dashed=linetype(new real[] {4,3}); // set up dashed pattern
pen dashCircPen=gray(0.4)+dashed+opacity(1)+0.32pt;
pen boldLine=darkblue+1.2pt;
pen floorCircPen=olive+opacity(0.75);
pen sphereSpotPen=blue+opacity(0.382);
Draw3(O--(0,0,R),boldLine);
Draw3(O--(R,0,0),boldLine);
Draw3(O--(0,R,0),boldLine);
Draw3(O--(-R,0,0),boldLine);
Draw3(O--(0,-R,0),boldLine);
Draw3(circ(R),boldLine);
real h=0;
real dphi=10;
real phi=dphi;
for(int i=1;i<9;++i){
h=R*Sin(phi);
Draw3(Parallel(h,R),dashCircPen);
phi+=dphi;
}
for(int i=0;i<6;++i){
Draw3(rotate(30*i,(0,0,1))*rotate(90,(1,0,0))*semicirc(R),dashCircPen);
}
int nd=floor((nr-1)/2);
transform3 tr;
for(int j=0;j<3;++j){
a=2r;
for(int i=0;i<nd;++i){
tr=rotate(60*j,(0,0,1))*shift((-a,0,0));
FillDraw3(tr*circ(r),floorCircPen,red);
FillDraw3(tr*sphere_x_cyl(a,r,R),sphereSpotPen);
a+=2r;
}
}
FillDraw3(circ(r),floorCircPen,red);
FillDraw3(shift((0,0,sqrt(R^2-r^2)))*circ(r),sphereSpotPen);
为了独立s.pdf
运行asy -f pdf s.asy
。
答案3
以下是使用 PGFPlots 实现此目的的方法:
\documentclass{article}
\usepackage{pgfplots}
\begin{document}
\begin{tikzpicture}
\begin{axis}[hide axis, axis equal, samples=15]
\foreach \posx/\posy in {0/-0.1, 0.6/0.1, 0.6/-0.4, 0.1/-0.7}{
\addplot3 [red, thick, domain=0:360, samples=20] (
{cos(x)*0.2+\posx},
{sin(x)*0.2+\posy},
{0}
);
}
\addplot3 [surf, gray, faceted color=gray, opacity=0.5, samples=20, z buffer=sort, domain=0:360, y domain=0:1] ({cos(x)*y},{sin(x)*y},
{sqrt(1-(cos(x)*y)^2-(sin(x)*y)^2))});
\foreach \posx/\posy in {0/-0.1, 0.6/0.1, 0.6/-0.4, 0.1/-0.7}{
\addplot3 [red, thick, domain=0:360, samples=20] (
{cos(x)*0.2+\posx},
{sin(x)*0.2+\posy},
{sqrt(1-((cos(x)*0.2+\posx))^2-((sin(x)*0.2+\posy))^2))}
);
}
\end{axis}
\end{tikzpicture}
\end{document}