我想画一个光滑的表面https://mathematica.stackexchange.com/questions/20912/how-to-draw-a-higher-genus-surface但使用 Asymptote、TikZ、Metapost、PStricks 或其他 TeX 友好软件。以下结果相当粗糙。如果我增加nx它崩溃了(这是我使用 Asymptote 时经常遇到的问题)。
settings.outformat = "pdf";
settings.render = 16;
size(8cm);
real R=1.2;
real r=.5;
real a=sqrt(2);
real Rsqr=R^2;
real rsqr=r^2;
real asqr=a^2;
real diffsqr=Rsqr-rsqr;
real sqrdiffsqr=diffsqr^2;
real twsumsqr=2*(rsqr+Rsqr);
real twdiffsqr=2*diffsqr;
import contour3;
currentprojection=perspective(4,6,8);
real f(real x, real y, real z)
{
real ysqr=y^2;
real zsqr=z^2;
real p=(-r - R + x)^2;
real q=(r + R + x)^2;
return
-asqr
+
(
sqrdiffsqr
- twsumsqr*(p + ysqr)
+ twdiffsqr*zsqr
+ (p+ysqr+zsqr)^2
)
*
(
sqrdiffsqr
- twsumsqr*(q + ysqr)
+ twdiffsqr*zsqr
+ (q+ysqr+zsqr)^2
);
}
material opaq=material(diffusepen=gray(0.5), emissivepen=gray(0.4), specularpen=black);
draw(
surface(
contour3(
f,
(-2*(r + R), -(r + R),-r - a),
(2*(r + R), (r + R), r + a),
nx=60)
),
surfacepen=opaq
);
答案1
有趣的问题。只要您有一些 3D 例程,放置两个圆环就不是什么大问题,而且很多 3D 解决方案都可以做到这一点。Charles Staats 的评论很有意义。
我试图在两个圆环之间实现足够平滑的过渡。为此,我对(x,y,z)
可能x
属于的左侧部分进行了参数化[-R-a,-R]
,计算了 所在的区间y
,然后获得了 z 值。修改包括添加一个x
在 中的从属函数 null -R
,并在 中具有零导数-R-a
。从数学的角度来看,结果是C^1
(我认为)
第二种解决方案。我记得切比雪夫点与等距点的插值。因为对于x
固定的曲线,椭圆的某些部分是封闭的,y
所以我改变了参数化,使点更加平衡(这是我sqrt(y)
在第一次尝试时提出的原因)。所以解决方案是采取sin(pi/2*y)
,然后可以使用 Asymptote 的平滑样条函数。请找到(总是肮脏的和需要改进的)代码
size(200);
import graph3;
currentprojection=perspective(5,4,4);
real R=3;
real a=1;
triple f(pair t) {
triple z;
z= ((R+a*cos(t.y))*cos(t.x),(R+a*cos(t.y))*sin(t.x),a*sin(t.y));
return z;
}
surface s=surface(f,(0,0),(2pi,2pi),Spline);
surface ss=shift((2*R+2*a,0,0))*surface(f,(0,0),(2pi,2pi),Spline);
draw(s,green,render(compression=Low,merge=true));
draw(ss,green,render(compression=Low,merge=true));
triple f11 (pair z)
{
real y,ty,x,yy;
triple zz;
if ((z.y>=0) & (z.y<=1))
{
x=z.x+1/2*(z.x+R)^2/a;
y=sin(pi*(z.y)/2)*sqrt((R+a)^2-x*x);
zz=(-z.x,y,sqrt(a^2-(R-sqrt((x)^2+y^2))^2));
}
if ((z.y<0) & (z.y>=-1))
{
x=z.x+1/2*(z.x+R)^2/a;
y=-sin(pi*abs(z.y)/2)*sqrt((R+a)^2-x*x);
zz=(-z.x,y,sqrt(a^2-(R-sqrt((x)^2+y^2))^2));
}
if ((z.y>1) && (z.y<=2))
{
x=z.x+1/2*(z.x+R)^2/a;
yy=2-z.y;
y=sin(pi*(yy)/2)*sqrt((R+a)^2-x*x);
zz=(-z.x,y,-sqrt(a^2-(R-sqrt((x)^2+y^2))^2));
}
if ((z.y>1) && (z.y<=2))
{
zz=(-z.x,y,-sqrt(a^2-(R-sqrt((x)^2+y^2))^2));
}
if ((z.y>=-2) && (z.y<-1))
{
x=z.x+1/2*(z.x+R)^2/a;
yy=-2-z.y;
y=-sin(pi*(abs(yy))/2)*sqrt((R+a)^2-x*x);
zz=(-z.x,y,-sqrt(a^2-(R-sqrt((x)^2+y^2))^2));
}
return zz;
}
triple f21(pair z)
{
triple z=f11(z);
return (-z.x,z.y,z.z);
}
int N=20;
surface st=surface(f11,(-R-a,-2),(-R,2),N,2*N,Spline);
surface sstb=shift((2*R+2*a,0,0))*surface(f21,(-R-a,-2),(-R,2),N,2*N,Spline);
surface pont1=surface(st,sstb);
draw(pont1,green, render(compression=Low,merge=true));
您可以观察到绘制了两个圆环并添加了“桥”。代码更快,结果如下所示
第一个解决方案。但是我无法使用该surface(...,Spline)
程序获得真正光滑的表面。我认为这实际上是由于贝塞尔曲面片参数化的一些缺陷(De Boor 参考)。也许使用monotonic
Spline 的一些选项是解决方案,但我不得不承认我没有时间。在我看来,使用标准surface(....,100,100)
,结果是令人满意的。我相信可以改进它。
请找到(脏的和要清洁的)代码
size(200);
import graph3;
currentprojection=perspective(5,4,4);
real R=3;
real a=1;
triple f(pair t) {
triple z;
z= ((R+a*cos(t.y))*cos(t.x),(R+a*cos(t.y))*sin(t.x),a*sin(t.y));
return z;
}
bool active(pair pos) {return (R+a*cos(pos.y))*cos(pos.x)<=3.2;}
bool active2(pair pos) {return (R+a*cos(pos.y))*cos(pos.x)>=-3.2;}
surface s=surface(f,(0,0),(2pi,2pi),200,200, active);
surface ss=shift((2*R+2*a,0,0))*surface(f,(0,0),(2pi,2pi),200,200,active2);
draw(s,green,render(compression=Low,merge=true));
draw(ss,green,render(compression=Low,merge=true));
triple f11 (pair z)
{
real y,ty,x;
if (z.y>=0)
{
x=z.x+1/2*(z.x+R)^2/a;
y=sqrt(z.y)*sqrt((R+a)^2-x*x);
}
else
{
x=z.x+1/2*(z.x+R)^2/a;
y=-sqrt(abs(z.y))*sqrt((R+a)^2-x*x);
}
return (-z.x,y,sqrt(a^2-(R-sqrt((x)^2+y^2))^2));
}
triple f12(pair z)
{
triple z=f11(z);
return (z.x,z.y,-z.z);
}
triple f21(pair z)
{
triple z=f11(z);
return (-z.x,z.y,z.z);
}
triple f22(pair z)
{
triple z=f11(z);
return (-z.x,z.y,-z.z);
}
int N=100;
surface st=surface(f11,(-R-a,-1),(-R,1),N,N);
surface sst=surface(f12,(-R-a,-1),(-R,1),N,N);
surface sstb=shift((2*R+2*a,0,0))*surface(f21,(-R-a,-1),(-R,1),N,N);
surface sstbm=shift((2*R+2*a,0,0))*surface(f22,(-R-a,-1),(-R,1),N,N);
surface pont1=surface(st,sst,sstb,sstbm);
draw(pont1,green);//blue);
结果
答案2
更新该smoothcontour3
模块已纳入 Asymptote 版本 2.33(2015 年 5 月 11 日发布),因此如果您拥有该版本或更高版本,则无需下载该模块。
介绍 smoothcontour3.asy
我终于明白了用于绘制平滑隐式定义曲面的模块以一种我愿意与他人分享的形式。它并不完美,但我认为结果通常相当不错:
settings.outformat = "png";;
settings.render = 16;
size(8cm);
import smoothcontour3;
// Erdos lemniscate of order n:
real erdos(pair z, int n) { return abs(z^n-1)^2 - 1; }
real h = 0.12;
real lemn3(real x, real y) { return erdos((x,y), 3); }
real f(real x, real y, real z) {
return lemn3(x,y)^2 + (16*abs((x,y))^4 + 1) * (z^2 - h^2);
}
draw(
implicitsurface(f,
a=(-3,-3,-3),
b=(3,3,3),
overlapedges=true
),
surfacepen=material(diffusepen=gray(0.5),
emissivepen=gray(0.4),
specularpen=gray(0.1)
)
);
当你将它应用到 OP 的原始代码时会发生以下情况(略作修改;我通过 epsilon 扩展了边界并添加了一点点反射):
settings.outformat="png";
settings.render=16;
import smoothcontour3;
size(8cm);
real R=1.2;
real r=.5;
real a=sqrt(2);
real Rsqr=R^2;
real rsqr=r^2;
real asqr=a^2;
real diffsqr=Rsqr-rsqr;
real sqrdiffsqr=diffsqr^2;
real twsumsqr=2*(rsqr+Rsqr);
real twdiffsqr=2*diffsqr;
currentprojection=perspective(4,6,8);
real f(real x, real y, real z)
{
real ysqr=y^2;
real zsqr=z^2;
real p=(-r - R + x)^2;
real q=(r + R + x)^2;
return
-asqr
+
(
sqrdiffsqr
- twsumsqr*(p + ysqr)
+ twdiffsqr*zsqr
+ (p+ysqr+zsqr)^2
)
*
(
sqrdiffsqr
- twsumsqr*(q + ysqr)
+ twdiffsqr*zsqr
+ (q+ysqr+zsqr)^2
);
}
material softgray = material(diffusepen=gray(0.5),
emissivepen=gray(0.4),
specularpen=gray(0.1));
triple aa = (-2*(r + R), -(r + R),-r - a) - 1e-2*(1,1,1);
triple bb = (2*(r + R), (r + R), r + a) + 1e-2*(1,1,1);
draw(implicitsurface(f, aa, bb, overlapedges=true),
surfacepen=softgray);
有一个矢量图形版本。使用起来meshpen
有些不方便,但半透明变得可行,它让您了解算法的工作原理。(请注意,该算法是自适应的,与 Asymptote 的任何内置绘图算法不同。)
settings.outformat="pdf";
settings.render=0;
import smoothcontour3;
size(8cm);
real R=1.2;
real r=.5;
real a=sqrt(2);
real Rsqr=R^2;
real rsqr=r^2;
real asqr=a^2;
real diffsqr=Rsqr-rsqr;
real sqrdiffsqr=diffsqr^2;
real twsumsqr=2*(rsqr+Rsqr);
real twdiffsqr=2*diffsqr;
currentprojection=perspective(4,6,8);
real f(real x, real y, real z)
{
real ysqr=y^2;
real zsqr=z^2;
real p=(-r - R + x)^2;
real q=(r + R + x)^2;
return
-asqr
+
(
sqrdiffsqr
- twsumsqr*(p + ysqr)
+ twdiffsqr*zsqr
+ (p+ysqr+zsqr)^2
)
*
(
sqrdiffsqr
- twsumsqr*(q + ysqr)
+ twdiffsqr*zsqr
+ (q+ysqr+zsqr)^2
);
}
material softgray = material(diffusepen=gray(0.5) + opacity(0.9),
emissivepen=gray(0.4),
specularpen=gray(0.1));
triple aa = (-2*(r + R), -(r + R),-r - a) - 1e-2*(1,1,1);
triple bb = (2*(r + R), (r + R), r + a) + 1e-2*(1,1,1);
draw(implicitsurface(f, aa, bb, n=20, nx=30),
surfacepen=softgray,
meshpen=blue+0.2pt);
这是该函数的描述implicitsurface
,因为代码目前没有外部文档(我想除了这个答案):
隐式曲面有三个必需参数:一个函数f
(类型为 或real(triple)
)real(real, real, real)
和两个三元组a
和b
。它返回函数在对角为 a 和 b 的长方体中的零轨迹图。
[注意:如果使用两个签名进行重载,则将使用f
带有签名的版本。]real(triple)
可选参数:
int n
- x, y, z 方向上的初始段的数量。bool overlapedges
- 如果为真,则表面的斑块会略微放大,以补偿观察者可以透过斑块之间的边界看到的瑕疵。(其中一些可能实际上是由于边缘排列不完美造成的,但我相当肯定,很多都是纯粹的渲染瑕疵。)int nx
- 在 x 方向上覆盖 nint ny
- 在 y 方向上覆盖 nint nz
- 在 z 方向上覆盖 nint maxdepth
- 算法将细分的最大深度,以便找到最接近真实表面的面片。(该算法是自适应的)。
该算法旨在处理具有平滑零轨迹的可微分函数。它通常(但并非总是)能够处理零轨迹中的奇异点,但代价是计算速度会大大降低。有一次我看到它处理连续的分段可微分函数,尽管计算需要一段时间。无论你做什么,不要传递一个分段常数函数——它将永远耗费时间并且不会返回任何好的东西。
原始答案: 使用 Asymptote 绘制隐式定义的曲面总是会很粗糙。(我实际上已经启动了一个模块来做得更好,但不能保证它能正常工作。)据我所知,您列出的其他 TeX 友好软件都没有尝试绘制隐式定义的曲面。因此,对于 TeX 友好软件,您最好的选择可能是萨格特克斯包裹。
答案3
另一个 PSTricks 解决方案:
\documentclass[pstricks]{standalone}
\usepackage{pst-solides3d}
\begin{document}
\begin{pspicture}(-8,-4)(8,4)
\psset{viewpoint=50 10 40 rtp2xyz,Decran=50,lightsrc=viewpoint,solidmemory,
object=tore,r1=3,r0=1,ngrid=36 60,fillcolor=[rgb]{1 0.5 0}}
\psSolid[name=toreA,action=none](0,3.5,0)
\psSolid[name=toreB,action=none](0,-3.5,0)
\psSolid[object=fusion,base=toreA toreB,grid=false]
\end{pspicture}
\end{document}
使用latex
-> dvips
->ps2pdf
或使用xelatex
但这将花费很长时间
答案4
我不确定你说的 TeX 友好软件是什么意思。你可以使用软件包通过 LaTeX 使用 Sage,sagetex
但不幸的是,我得到的输出类似于你的 Asymptote 代码。如果我使用智者就像你用 Asymptote 做的那样,在 LaTeX 之外使用,那么结果会更好。转到Sage 单元服务器并复制/粘贴以下代码:
var('u,v');
a,b = 1.2,.5 # outer and inner radii
x = (a + b*cos(u))*cos(v)
y = (a + b*cos(u))*sin(v)
z = b*sin(u)
var('t')
T = parametric_plot3d([x,y,z], (u,0,2*pi),(v,0,2*pi), opacity=1,aspect_ratio=[1,1,1],color="yellow",plot_points=[100,100],frame=False)
M = T + T.translate(2,1.5,0)
M.show(frame=False)
然后按“评估”得到以下结果:
此处的双圆环是通过绘制圆环并将其附加到圆环的平移上而制作的,因此您可以随心所欲地摆弄它。在图像下方左侧,有一个链接可以将图像下载为 PNG 文件(可在使用 pdflatex 编译时使用)。图像更清晰,但您无法像在计算机上安装了 Sage 那样控制图片。直接使用 Sage 可以让您右键单击图像(见下文)并对其进行各种操作:旋转、放大、将其作为 3D 立体图查看等等。
通过旋转和缩放,您可以调整图像以满足您的满意程度。
这幅图像没有您在使用 Asymptote 时遇到的问题。如果您有 3D 红青眼镜,您也可以获得 3D 效果。您可以调整aspect_ratio 中的数字,使双圆环更平坦或更宽大。