首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在曲面上画线

在曲面上画线
EN

Stack Overflow用户
提问于 2018-03-31 09:43:53
回答 3查看 9.1K关注 0票数 6

我希望能够看到线(和点)是在三维表面上的表面(第二个图像),而不是后面(第一个图像)。这是我的3D功能:

代码语言:javascript
复制
def f(x, y): 
    return np.sin(2*x) * np.cos(2*y)

三维曲面的X,Y,Z:

代码语言:javascript
复制
x = np.linspace(-2, 2, 100)
y = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

我生成了x点(xx)和y点(yy)的向量,其中zz = f(xx,yy)。

代码语言:javascript
复制
fig = plt.figure(figsize=(8,6))
ax = plt.axes(projection='3d')
ax.scatter(xx, yy, zz, c='r', marker='o')
#ax.plot(xx,yy,zz, c= 'r')

ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                cmap='viridis', edgecolor='none')

正如你所看到的,这些点就在情节的后面,图中包含了这些点。我想看看情节的要点。我该怎么办?

我希望能看到这样的点和线:

编辑:这就是我生成要点的方式:

代码语言:javascript
复制
for i in range(1,2000):
    [a, b] =  np.random.rand(2,1)*np.sign(np.random.randn(2,1))*2
    xx = np.append(xx, a)
    yy = np.append(yy, b)

我注意到,如果我写zz = f(xx,yy) + epsilon,我可以看到要点。如果是epsilon = 0,那么这些点在数学上是在表面,我看不清楚,就像在第一张图像中一样。如果是epsilon > 0.05,我可以看到点,但这意味着向上移动点。我不太喜欢这个解决方案。如果一个点是在一个表面上,表面有优先权,她表面似乎超过了点。我希望我的图形是相反的。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-04-01 19:35:29

我首先要说的是,你想要的东西有点模糊.您想要精确地在底层曲面上绘制点,其方式总是显示在图形中,即就在表面的上方,而不是显式地向上移动。这已经是个问题了,因为

  1. 浮点算法意味着你对点和曲面的精确坐标会随着机器精度的顺序而变化,所以试图依赖精确的等式是行不通的。
  2. 即使这些数字精确到无限精度,曲面也是用一组近似平面绘制的。这意味着精确的数据点将位于函数凸的近似曲面下。

然而,最大的问题是,matplotlib中的3d绘图在图形中绘制多个或复杂对象时是不可靠。特别是,渲染器本身就是2d,当试图找出物体的相对表观位置时,它经常会遇到问题。要克服这个问题,可以尝试绕开这个问题,也可以切换到像mayavi这样的3d渲染器。

不幸的是,zorder可选关键字参数通常被3d轴对象忽略。因此,我唯一能想到的就是你几乎拥有的东西:使用ax.plot而不是ax.scatter。虽然后者在第一个图形中显示了一个图(由于某种原因,每个散点点都隐藏了起来,而不管视角如何),但前者导致了在第二个图形中显示的一个图(在这里,这些点是可见的)。通过删除绘图样式中的线条,我们几乎可以得到您想要的:

代码语言:javascript
复制
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def f(x, y):                        
    return np.sin(2*x) * np.cos(2*y)

# data for the surface
x = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(x, x)
Z = f(X, Y)

# data for the scatter
xx = 4*np.random.rand(1000) - 2
yy = 4*np.random.rand(1000) - 2
zz = f(xx,yy)

fig = plt.figure(figsize=(8,6))
ax = plt.axes(projection='3d')
#ax.scatter(xx, yy, zz, c='r', marker='o')
ax.plot(xx, yy, zz, 'ro', alpha=0.5) # note the 'ro' (no '-') and the alpha

ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                cmap='viridis', edgecolor='none')

但不完全是这样:在这种情况下,这些点总是可见的,即使它们应该隐藏在表面的一部分后面,这一点很快就变得明显起来:

代码语言:javascript
复制
# note the change in points: generate only in the "back" quadrant
xx = 2*np.random.rand(1000) - 2
yy = 2*np.random.rand(1000)
zz = f(xx,yy)

fig = plt.figure(figsize=(8,6))
ax = plt.axes(projection='3d')
ax.plot(xx,yy,zz, 'ro', alpha=0.5)

ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                cmap='viridis', edgecolor='none')

这是很容易看到的,在前面的凸起应该隐藏了大量的点在背景中,但这些点是可见的。这正是pyplot在复杂的三维可视化中遇到的问题。因此,我的底线是,您无法使用matplotlib可靠地执行所需的操作。不管它的价值是什么,我不知道这样的阴谋到底有多容易理解。

最后是一个更积极的方面,下面是如何使用mayavi (您需要安装vtk,这是最好通过包管理器来完成的):

代码语言:javascript
复制
import numpy as np
from mayavi import mlab
from matplotlib.cm import get_cmap # for viridis

def f(x, y):
    return np.sin(2*x) * np.cos(2*y)

# data for the surface
x = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(x, x)
Z = f(X, Y)

# data for the scatter
xx = 4*np.random.rand(1000) - 2
yy = 4*np.random.rand(1000) - 2
zz = f(xx,yy)

fig = mlab.figure(bgcolor=(1,1,1))
# note the transpose in surf due to different conventions compared to meshgrid
su = mlab.surf(X.T, Y.T, Z.T)
sc = mlab.points3d(xx, yy, zz, scale_factor=0.1, scale_mode='none',
                   opacity=1.0, resolution=20, color=(1,0,0))

# manually set viridis for the surface
cmap_name = 'viridis'
cdat = np.array(get_cmap(cmap_name,256).colors)
cdat = (cdat*255).astype(int)
su.module_manager.scalar_lut_manager.lut.table = cdat

mlab.show()

正如你所看到的,结果是一个交互的三维图形,其中表面的数据点是适当的球体。一个人可以玩不透明度和球体的规模设置,以获得一个满意的可视化。由于适当的3d渲染,你可以看到一个适当的数量,每一点,无论视角。

票数 9
EN

Stack Overflow用户

发布于 2021-10-03 02:56:07

Andras给出了一个非常全面的回答,当涉及到手头的任务时,讨论了matplotlibs三维绘图能力的问题/局限性。在答案的结尾--以肯定的语气结束--他用另一个图书馆给出了一个解决方案。

我开始尝试在matplotlib中找到一个讨厌的/专门的解决方案。让我先说说原因。我想在二维曲面上绘制轨迹,然后我开始使用matplotlib。我将它用于我所有的2D绘图,并希望为这个特殊的3D绘图应用程序找到一个解决方案。关于matplotlibs 3D图的好处是,它们是矢量化的,因为它们基本上是通过将3D元素投影到摄像机平面并覆盖它们而生成的二维图形(根据它们与摄像机的距离来绘制它们的顺序)。在不影响轴、标签等的情况下,可以单独控制地块的每个元素。使用射线追踪的“真实”3D绘图库通常无法生成完全矢量化的图形。我认为mayavi是一个例子,我知道数学在这方面也是非常有限的。

接下来是我的解决方案:我查看了plot_surface的代码,它最终是基于Poly3DCollection的,以了解matplotlib如何决定首先绘制表面上的哪个多边形/元素。_do_3d_projection of Poly3DCollection将投影到二维摄像机平面上的多边形按(原始三维物体的距离)与摄像机的距离排序。远离摄像机的元素首先被绘制,元素接近相机之后才画出来。对于大多数绘图来说,这足够好地创建一个正确的透视图(但是这种方法有局限性,例如,参见mplot3d常见问题 )。然而,这种排序是我解决方案的关键。给定一组点和表面冲浪(必须用showsavefig绘制,才能设置相机/投影变量):

  1. 计算了surf中所有三维多边形在摄像机平面上的二维投影segments_2d,包括它们与摄像机的距离(存储在segments_idxs中)的排序。
  2. 所有点都与三维曲面上的一个元素/多边形相关联。
  3. 计算了三维点在摄像机平面上的二维投影。
  4. 要确定一个点是否可见,我们检查它是否被与之相关的多边形所覆盖(从步骤2开始)。要做到这一点,我们使用来自matplotlib.pathmatplotlib.path方法,也见相关的问题在python中,检查点是否在多边形内的最快方法是什么
  5. 我包括了一个动态更新(改编自如何在matplotlib中掩盖表面图后面的一条线?)。警告:有大量多边形的曲面的代码/绘图可能会变得非常慢。

这里是一个必要的代码/最小工作示例,其表面由OP给出,样本点位于单位圆处。

代码语言:javascript
复制
import matplotlib.pyplot as plt
import numpy as np
import copy
import matplotlib.path as mpltPath
from mpl_toolkits.mplot3d import proj3d
from matplotlib import cm


def clip_on_surface(surf,pts):
    ## Get projection of 3d surface onto 2d camera plane
    ## [Code form [mpl_toolkits/mplot3d/art3d.py -  Poly3DCollection._do_3d_projection(self, renderer=None)] to ]
    txs, tys, tzs = proj3d._proj_transform_vec(surf._vec, surf.axes.M)
    xyzlist = [(txs[sl], tys[sl], tzs[sl]) for sl in surf._segslices]
    cface = surf._facecolor3d
    cedge = surf._edgecolor3d
    if len(cface) != len(xyzlist):
        cface = cface.repeat(len(xyzlist), axis=0)
    if len(cedge) != len(xyzlist):
        if len(cedge) == 0:
            cedge = cface
        else:
            cedge = cedge.repeat(len(xyzlist), axis=0)
                    
    if xyzlist:
        # sort by depth (furthest drawn first)
        z_segments_2d = sorted(
            ((surf._zsortfunc(zs), np.column_stack([xs, ys]), fc, ec, idx)
              for idx, ((xs, ys, zs), fc, ec)
              in enumerate(zip(xyzlist, cface, cedge))),
            key=lambda x: x[0], reverse=True)
        
    # z_segments_2d = sorted(z_segments_2d,key=lambda x:x[4])    
    segments_zorder, segments_2d, facecolors2d, edgecolors2d, segments_idxs =  zip(*z_segments_2d)
    segments_paths = [mpltPath.Path(x) for x in segments_2d]
    
    ## Get polygons in 3d space
    xs, ys, zs = surf._vec[0:3,:]
    xyzlist = [(xs[sl], ys[sl], zs[sl]) for sl in surf._segslices]
    segments_3d=[]
    segments_3d_centroid=[]
    for q in xyzlist:    
        vertices = np.transpose(np.array([q[0],q[1],q[2]]))
        segments_3d.append( vertices )
        segments_3d_centroid.append( sum(list(vertices))/len(list(vertices)) ) # centroid of polygon (mean of vertices)
    
    ## Process points
    pts_info = [[0,0,True] for x in range(len(pts))] 
        # 0: index of closest 3d polygon
        # 1: index of closest 3d polygon in segments_idxs: drawing order
        # 2: True if visible (not overlapped by polygons drawn after associated polygon), False else
    
    pts_visible = copy.copy(pts) # visible points (invisible set to np.nan)
    pts_invisible = copy.copy(pts) # invisible points (visible set to np.nan)
    
  
    # compute pts_info[:,0] and  pts_info[:,1] -- index of closest 3d polygon and its position in segments_idxs
    for i in range(len(pts)):
        # Associate by distance
        dist = np.inf
        x=[pts[i][0],pts[i][1],pts[i][2]]
        for j in range(len(segments_3d_centroid)):
            yc=segments_3d_centroid[j]
            dist_tmp = np.sqrt( (x[0]-yc[0])**2 + (x[1]-yc[1])**2 + (x[2]-yc[2])**2 )
            if dist_tmp<dist:
                dist=dist_tmp
                pts_info[i][0]=j        
        pts_info[i][1] = segments_idxs.index( pts_info[i][0] )
    
    # compute projection of 3d points into 2d camera plane
    pts_2d_x, pts_2d_y, pts_2d_z = proj3d._proj_transform_vec(np.transpose(np.array([[x[0],x[1],x[2],1.0] for x in pts])), surf.axes.M) 
  
    # decide visibility    
    for i in range(len(pts_info)):
        for j in range(pts_info[i][1]+1,len(segments_paths)):
            b=segments_paths[j].contains_points( [[pts_2d_x[i],pts_2d_y[i]]] )
            if b==True:
                pts_info[i][2]=False
                break
        if pts_info[i][2]:
            pts_invisible[i][0]=np.nan
            pts_invisible[i][1]=np.nan
            pts_invisible[i][2]=np.nan
        else:
            pts_visible[i][0]=np.nan
            pts_visible[i][1]=np.nan
            pts_visible[i][2]=np.nan
            
            
    return { 'pts_visible': pts_visible, 'pts_invisible':pts_invisible, 'pts_info':pts_info }

def f(x, y):                        
    return np.sin(2*x) * np.cos(2*y)

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.view_init(elev=30., azim=55.)

# Generate surface plot (surf)
xs = np.linspace(-2, 2, 25)
ys = np.linspace(-2, 2, 25)
Xs, Ys = np.meshgrid(xs, ys)
zs = np.array(f(np.ravel(Xs), np.ravel(Ys)))
Zs = zs.reshape(Xs.shape)

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')

surf = ax.plot_surface(Xs, Ys, Zs, rstride=1, cstride=1, 
                        cmap=cm.get_cmap('viridis'),linewidth=0.0,edgecolor='black',
                        antialiased=True,rasterized=False)

# Generate pts on surf
t = np.linspace(0, 1, 200)
xp = np.sin(t*2*np.pi)
yp = np.cos(t*2*np.pi)
zp = f(xp,yp)
pts=np.transpose(np.array([xp,yp,zp]))


def rotate(event):
    if event.inaxes == ax:
        surf_pts=clip_on_surface(surf,pts)
        ax.plot(surf_pts['pts_visible'][:,0],surf_pts['pts_visible'][:,1],surf_pts['pts_visible'][:,2],'.', zorder=10,c='red',markersize=2)
        ax.plot(surf_pts['pts_invisible'][:,0],surf_pts['pts_invisible'][:,1],surf_pts['pts_invisible'][:,2],'.', zorder=10,c='green',markersize=2)
        
        fig.canvas.draw_idle()
        
c1 = fig.canvas.mpl_connect('motion_notify_event', rotate)
plt.show()

该代码仍然有点混乱,它仍然不能完美地工作,但这里的一些结果与25*25=625四元在表面和200个点的单位圈。

红色的点是可见的点,绿色的点是看不见的点(这里是为了说明的目的而绘制的,但为了最初的问题,你会诅咒它们从情节中省略)。有些点应该是明显的,但被检测到是无形的。我还不知道哪里出了什么问题,但对我来说,这种有限的缺失探测并不太成问题,因为我最终想要绘制许多(任意密集的)点的线/轨迹。如果错过了,我可以和几个失踪的人住在一起。

另一个固有的问题/限制是,目前的方法没有真正的概念,无论是在一个表面之上或之下,意味着当从地面下面看时,表面上的点是可见的。这里是此行为的一个示例:

这与安德拉斯·德克( Andras )已经提出的观点相联系,即目前的问题有些不明确,或者至少是模棱两可,没有额外的限制。例如,我们可以要求,所有的点都放在朝向摄像机的表面上。在目前的办法中执行这一点是困难的。在几何术语中,当前的实现将有限大小的球放置在无穷小多边形上,使它们从两边可见(在某些用例中这可能是可行的/理想的)。

代码仍在进行中,如果我发现有重大改进,我可能会更新这个答案。关于一般办法和(或)执行情况的评论是非常受欢迎的。我绝不是python专家(我几乎只用于绘图和相关的非常轻的数据处理),因此它们在代码性能和范围方面可能有很大的改进空间。

票数 2
EN

Stack Overflow用户

发布于 2020-02-20 17:31:25

从您的图表来看,您似乎愿意显示通向非线性优化器局部解决方案的路径,因此,我认为您应该考虑在等高线图上绘制一个线框:

..。

ax.scatter(xx,yy,zz,c='r',marker='o') ###这将只绘制点,而不是线

ax.plot_surface(X,Y,Z,rstride=1,cstride=1,cmap='viridis',edgecolor='none')

ax.plot_wireframe(xx,yy,zz) ###这将绘制线和可能的点。

..。

as (xx,yy,zz)包含从非线性回归方法得到的到达局部最大值的路径,正如我所假设的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49586376

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档