MATLAB | 一文解决各类曲面交线绘制,包含三维隐函数曲面交线

本文很自然的分成了三部分两显函数交线显函数与隐函数交线两隐函数交线,对于解析解较难表示的函数,以下给出各种情况数值解求法及交线绘制方法:

两显函数交线

这是最简单的一部分,只需要两个显函数相减,使用三维等高线函数contour3获取两显函数相减的0等高线数据,再绘图即可:

% 显函数绘制曲面交线
[X,Y]=meshgrid(-2:.1:2);
Z1=5-2.*X-1.5.*Y;
Z2=X.^2+Y.^2;
% 绘制曲面
surf(X,Y,Z1,'EdgeColor','none','FaceAlpha',0.5,'FaceColor',[82,124,179]./255)
hold on
surf(X,Y,Z2,'EdgeColor','none','FaceAlpha',0.5,'FaceColor',[169,64,71]./255)
% 求出交线数值解并绘图
Z3=Z1-Z2;
CXY=contour3(X,Y,Z3,[0,0],'Visible','off');
CX=CXY(1,2:end);
CY=CXY(2,2:end);
% 以下两种方式均可,用任意显函数均可
plot3(CX,CY,5-2.*CX-1.5.*CY,'LineWidth',2,'Color',[0,0,0])
% plot3(CX,CY,CX.^2+CY.^2,'LineWidth',2,'Color',[0,0,0])
view(60,14) 


显函数与隐函数交线

这个也有很有意思的函数可以处理,contourslice函数可以在任意三维切片或者三维曲面上绘制等高线,我们使用三维隐函数提供三维体数据,获取该数据体在显函数曲面上的0等高线即可:

% 显函数与隐函数绘制曲面交线
f1=@(x,y,z)5-2.*x-1.5.*y-z;
f2=@(x,y,z)x.^2+y.^2+z.^2-12;

[X,Y]=meshgrid(-3:.1:4);
Z1=5-2.*X-1.5.*Y;

% 绘制曲面
surf(X,Y,Z1,'EdgeColor','none','FaceAlpha',0.5,'FaceColor',[82,124,179]./255)
hold on;
fimplicit3(f2,'EdgeColor','none','FaceAlpha',0.5,'FaceColor',[169,64,71]./255)

% 通过切片contour函数获取0等势面
[CX,CY,CZ]=meshgrid(-3:.1:4);
CV=f1(CX,CY,CZ)-f2(CX,CY,CZ);
S=contourslice(CX,CY,CZ,CV,X,Y,Z1,[0,0]);
S.EdgeColor=[0,0,0];
S.LineWidth=2; 


两隐函数交线

这个就贼麻烦了,因为没有显函数提供显式曲面,因此contourslice函数无法使用,而isosurface返回形式为三角面而不是折线段,绘制效果会非常粗糙:

因此需要提出新的方案,查阅了一些资料,发现大佬Mike Garrity在给出了一个对于:
{ y − x tan ⁡ ( z ) = 0 x 2 + y 2 = 1 \begin{cases} y-x\tan(z)=0\\ x^2+y^2=1 \end{cases} { yxtan(z)=0x2+y2=1
问题求隐函数交线有了非常详解的解答,详细内容请查看:https://blogs.mathworks.com/graphics/2015/07/22/implicit-surface-intersections/?from=cn

本人对其给出的代码进行了微调,制作工具函数使其能应对大部分隐函数:

function [nX,nY,nZ]=isocurve3(X,Y,Z,f1,f2)
% 获取f1隐函数的三角面和三角顶点
V1=f1(X,Y,Z);
hel=isosurface(X,Y,Z,V1,0);
% 将f1获取的三角顶点带入f2求得数值
V2=f2(hel.vertices(:,1),hel.vertices(:,2),hel.vertices(:,3));
% 检查三个顶点的数值是否同时含有有大于0数值及小于0数值
mask=V2>0;
outcount=sum(mask(hel.faces),2);
cross=(outcount==2)|(outcount==1);
crossing_tris=hel.faces(cross,:);
% 通过旋转交换三个顶点次序,将小于0的点放在第一列
out_vert=mask(crossing_tris);
flip=sum(out_vert,2)==1;
out_vert(flip,:)=1-out_vert(flip,:);
ntri=size(out_vert,1);
overt=zeros(ntri,3);
for i=1:ntri
    v1i=find(~out_vert(i,:));
    v2i=1+mod(v1i,3);
    v3i=1+mod(v1i+1,3);
    overt(i,:)=crossing_tris(i,[v1i v2i v3i]);
end
% 类似于求重心
u=(-V2(overt(:,1)))./(V2(overt(:,2))-V2(overt(:,1)));
v=(-V2(overt(:,1)))./(V2(overt(:,3))-V2(overt(:,1)));
uverts=repmat((1-u),[1 3]).*hel.vertices(overt(:,1),:)+repmat(u,[1 3]).*hel.vertices(overt(:,2),:);
vverts=repmat((1-v),[1 3]).*hel.vertices(overt(:,1),:)+repmat(v,[1 3]).*hel.vertices(overt(:,3),:);
% 因为可能含有多条曲线,因此逐段连线
nX=nan(3,ntri);
nX(1,:)=uverts(:,1)';
nX(2,:)=vverts(:,1)';
nY=nan(3,ntri);
nY(1,:)=uverts(:,2)'; 
nY(2,:)=vverts(:,2)';
nZ=nan(3,ntri);
nZ(1,:)=uverts(:,3)';
nZ(2,:)=vverts(:,3)';
end

调用函数求解交线示例(注意返回值应使用line函数而非plot函数绘制直线):

f1=@(x,y,z)x.^2+y.^2-z.^2;
f2=@(x,y,z)x.^2+1.5*y.^2+z.^2-12;

% 绘制曲面
fimplicit3(f1,'EdgeColor','none','FaceAlpha',0.5,'FaceColor',[82,124,179]./255)
hold on;
fimplicit3(f2,'EdgeColor','none','FaceAlpha',0.5,'FaceColor',[169,64,71]./255)

[X,Y,Z]=meshgrid(-5:.1:5);
[nX,nY,nZ]=isocurve3(X,Y,Z,f1,f2);
line(nX(:),nY(:),nZ(:),'LineWidth',2,'Color',[0,0,0]); 

猜你喜欢

转载自blog.csdn.net/slandarer/article/details/125710021