简体   繁体   English

将 3D 表面(由 plot_trisurf 生成)投影到 xy 平面并在 2D 中绘制轮廓

[英]Project a 3D surface(generated by plot_trisurf) to xy plane and plot the outline in 2D

I have a set of point cloud with this format:我有一组这种格式的点云:

x = [1.2, 1.3, .....]
y = [2.1, 1.2, .....]
z = [0.5, 0.8, .....]

And I use plot_trisurf to plot(by Delauney triangulation) a 3D surface that "represents" the point cloud.我使用plot_trisurf来绘制(通过德洛尼三角剖分)一个“代表”点云的 3D 表面。

My question is: is there a quick/decent method to project the surface generated by plot_trisurf to xy plane and only plot the outline of the projection as a 2D plot?我的问题是:是否有一种快速/体面的方法可以将plot_trisurf生成的表面plot_trisurf到 xy 平面,并且只将投影的轮廓绘制为二维图?

For example, suppose all my points in the point cloud are on a sphere surface, then plot_trisurf will plot a (not-that-perfect) sphere for me.例如,假设我在点云中的所有点都在一个球面上,那么plot_trisurf将为我绘制一个(不那么完美的)球体。 Then my target is to "project" this sphere to xy plane and then draw its outline as 2D plot(which is a circle).然后我的目标是将这个球体“投影”到 xy 平面,然后将其轮廓绘制为2D图(这是一个圆圈)。

Edit:编辑:

Kindly note that this 2D plot is a 2D curve(possibly closed curve).请注意,此2D图是一条 2D 曲线(可能是闭合曲线)。

You could use trimesh or a similar module to quickly achieve your goal without reinventing the wheel, as such libraries already implement methods for dealing with meshes.您可以使用trimesh或类似的模块来快速实现您的目标,而无需重新发明轮子,因为此类库已经实现了处理网格的方法。

See below a quick implementation of a projection of a surface onto an arbitrary plane, defined by its normal vector and origin.请参阅下面将曲面投影到任意平面上的快速实现,该平面由其法向量和原点定义。

import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import trimesh

mesh = trimesh.load('./teapot.obj')
triangles = mesh.faces
nodes = mesh.vertices

x = nodes[:,0]
y = nodes[:,2]
z = nodes[:,1]

# Section mesh by an arbitrary plane defined by its normal vector and origin
section = mesh.section([1,0,1], [0,0,0])

fig = plt.figure()    
ax = fig.gca(projection='3d')
ax.plot_trisurf(x, y, z, triangles=triangles, color=(0,1,0,0.3), alpha=0.3)
ax.scatter(section.vertices[:,0], section.vertices[:,2], section.vertices[:,1])

plt.axis([-3, 3, -3, 3])
plt.show()

The result is:结果是: 在此处输入图片说明

Hope this can be of help!希望这能有所帮助!

For an arbitrary surface, the projection is trivial, simply set all the z values to 0 .对于任意表面,投影是微不足道的,只需将所有z值设置为0

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

fig = plt.figure()
ax1 = fig.add_subplot(121, projection='3d')
ax2 = fig.add_subplot(122, projection='3d')

n=100
x = np.linspace(0, np.pi*4, n)
y = np.sin(x)+np.cos(x)
z = y*y

ax1.plot_trisurf(x, y, z)
ax1.set_title(r"$z=y^2$")
ax2.plot_trisurf(x, y, np.zeros_like(x))
ax2.set_title(r"$z=0$")
plt.show()

在此处输入图片说明

For a known regular surface, like a sphere, you can simply take the maximum cross section with respect to a given direction.对于已知的规则表面,如球体,您可以简单地取相对于给定方向的最大横截面 Ie for a circle centered on the origin take only the x and y pairs for which z==0 or abs(z) < threshold , or for which the z along the vertical line perpendicular to the yx plane is minimized.即,对于以原点为中心的圆,仅取xy对,其中z==0abs(z) < threshold ,或者沿垂直于yx平面的垂直线的z最小化。 This approach only works if the sphere has not already been 'flattened'.这种方法仅在球体尚未“展平”时才有效。 As an example using the latter method (but using plot_surface since I already have code for it and using the same preamble from above),作为使用后一种方法的示例(但使用plot_surface因为我已经有了它的代码并使用了与上面相同的序言),

n = 100
r = 5
theta = np.linspace(0, np.pi*2, n)
phi = np.linspace(0, np.pi, n)
x = r * np.outer(np.cos(theta), np.sin(phi))
y = r * np.outer(np.sin(theta), np.sin(phi))
z = r * np.outer(np.ones_like(theta), np.cos(phi))

x_out = list()
y_out = list()
for t in theta:
    zm = r
    idx = 0
    for ii in range(len(phi)):
        if abs(r * np.cos(phi[ii])) < zm:
            zm = r * np.cos(phi[ii])
            idx = ii
    x_out.append(r * np.cos(t) * np.sin(phi[idx]))
    y_out.append(r * np.sin(t) * np.sin(phi[idx]))

ax1.plot_surface(x, y, z)
ax1.set_title("Sphere")
ax2.plot(x_out, y_out, np.zeros_like(x_out), linestyle='-')
ax2.set_title("Maximum Cross Section Outline")
plt.show()

在此处输入图片说明

There are irregular surfaces for which this will work as well, but may require interpolation if the polar distribution of points is not uniform.对于不规则的表面,这也适用,但如果点的极坐标分布不均匀,则可能需要插值。 A more robust (but computationally intensive way) to do this is to create a cascaded_union using shapely .更健壮的(但计算密集的方式)来做到这一点是创建一个cascaded_union使用shapely To generalize this approach, some filtering must be done to remove what shapely considers invalid polygons, ie those which have a self intersection.为了概括这种方法,必须进行一些过滤以去除shapely认为无效的多边形,即那些具有自相交的多边形。 You can do this with the following您可以使用以下方法执行此操作

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib import rcParams
from math import cos, sin
from shapely.ops import cascaded_union
from shapely import geometry
from matplotlib import patches

n=100
t = np.linspace(0, np.pi*2, n)
r = np.linspace(0, 1.0, n)

x = r * np.cos(t)
y = r * np.sin(t)

z = np.sin(-x*y)
fig = plt.figure()
ax1 = fig.add_subplot(121, projection='3d')
ax2 = fig.add_subplot(122, projection='3d')

polygons = list()
# Create a set of valid polygons spanning every combination of
# four xy pairs
for k in range(1, len(x)):
    for j in range(1, len(x)):
        try:
            polygons.append(geometry.Polygon([(x[k], y[k]), (x[k-1], y[k-1]), 
                                              (x[j], y[j]), (x[j-1], y[j-1])]))
        except (ValueError, Exception):
            pass

# Check for self intersection while building up the cascaded union
union = geometry.Polygon([])
for polygon in polygons:
    try:
        union = cascaded_union([polygon, union])
    except ValueError:
        pass

xp, yp = union.exterior.xy

ax1.plot_trisurf(x, y, z)
ax1.set_title(r"$z=sin(-x*y)$")
ax2.plot_trisurf(x, y, np.zeros_like(x))
ax2.set_title(r"$z=0$")
plt.show()   # Show surface and projection

fig, ax = plt.subplots(1, figsize=(8, 6))
ax.add_patch(patches.Polygon(np.stack([xp, yp], 1), alpha=0.6))
ax.plot(xp, yp, '-', linewidth=1.5)
plt.show()   # Show outline 

在此处输入图片说明

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM