我有一套这种格式的点云:
fil <- 'https://raw.githubusercontent.com/djouallah/keplergl/master/test.rds'
cony <- gzcon(url(fil))
XXX <- readRDS(cony,refhook = NULL)
plotRGB(XXX)
然后我用x = [1.2, 1.3, .....]
y = [2.1, 1.2, .....]
z = [0.5, 0.8, .....]
绘制(通过Delauney三角剖分)“代表”点云的3D曲面。
我的问题是:是否有一种快速/体面的方法将plot_trisurf
生成的表面投影到xy平面上,并且仅将投影的轮廓绘制为2D图?
例如,假设我在点云中的所有点都在一个球体表面上,那么plot_trisurf
将为我绘制一个(不是那么完美的)球体。然后,我的目标是将该球体“投影”到xy平面,然后将其轮廓绘制为 2D 图(是一个圆)。
编辑:
请注意,此 2D 图是2D曲线(可能是闭合曲线)。
答案 0 :(得分:2)
您可以使用trimesh
或类似的模块来快速实现目标,而无需重新发明轮子,因为此类库已经实现了处理网格的方法。
快速查看将表面投影到任意平面的快速实现,该平面由其法向矢量和原点定义。
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()
希望这会有所帮助!
答案 1 :(得分:1)
对于任意曲面,投影都是微不足道的,只需将所有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()
对于已知的规则表面(例如球体),您可以相对于给定方向简单地使用maximum cross section。即对于以原点为中心的圆,仅采用x
或y
或z==0
沿垂直线垂直的abs(z) < threshold
和z
对至yx
平面的距离最小。仅当球体尚未“展平”时,此方法才有效。作为使用后一种方法的示例(但使用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()
在某些不规则表面上也可以使用,但是如果点的极性分布不均匀,则可能需要插值。一种更健壮(但计算量大的方式)是使用cascaded_union
创建一个shapely
。为了推广这种方法,必须进行一些过滤以删除shapely
认为无效的多边形,即具有自交点的多边形。您可以执行以下操作
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