我有一组来自 4-6 个向量的坐标 x,y,z 。我想绘制相应的棱镜。但是我的线是交叉的,根本看起来不像是棱镜。
我认为我必须对数据集进行排序,但是我不确定这是正确的答案还是答案。
这显然是错误的:
这应该是这样的:
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollection
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
p = np.array([[-43.11150999, -118.14365791, -1100.99389988],
[-27.97693445,-124.54828379, -1089.54038197],
[-55.99892873, -120.42384095, -1084.32576297],
[-40.75143664, -133.41566716, -1077.33745869],
[-43.2165748, -34.770722, -1030.85272686],
[-27.89568594, -43.06953117, -1021.03437003],
[-56.072327, -44.66085799, -1019.15166512],
[-40.75143814, -52.95966716, -1009.3333083]])
ax.scatter3D(p[:, 0], p[:, 1], p[:, 2])
x = np.array([[-43.11150999], [-27.97693445], [-55.99892873], [-40.75143664], [-43.2165748],[-27.89568594],[-56.072327],[-40.75143814]])
y = np.array([[-118.14365791], [-124.54828379], [-120.42384095], [-133.41566716], [-34.770722],[-43.06953117],[-44.66085799],[-52.95966716]])
z = np.array([[-1100.99389988], [-1089.54038197], [-1084.32576297], [-1077.33745869], [-1030.85272686],[-1021.03437003],[-1019.15166512],[-1009.3333083]])
labels = ['PT-EP-1n', 'PT-EP-2n', 'PT-EP-3n', 'PT-EP-4n', 'PT-TP-1n','PT-TP-2n','PT-TP-3n','PT-TP-4n']
x = x.flatten()
y = y.flatten()
z = z.flatten()
ax.scatter(x, y, z)
#give the labels to each point
for x, y, z, label in zip(x, y,z, labels):
ax.text(x, y, z, label)
verts = [[p[0],p[1],p[2],p[3]],
[p[1],p[2],p[6],p[5]],
[p[2],p[3],p[7],p[6]],
[p[3],p[0],p[4],p[7]],
[p[0],p[1],p[5],p[4]],
[p[4],p[5],p[6],p[7]]]
collection = Poly3DCollection(verts, linewidths=1, edgecolors='black', alpha=0.2, zsort='min')
face_color = "salmon"
collection.set_facecolor(face_color)
ax.add_collection3d(collection)
plt.show()
答案 0 :(得分:2)
第一个很好问的问题!
我认为您正在寻找的是数据点的凸包,可以使用scipy.spatial.ConvexHull
进行计算。但是,这种方法的问题在于,此函数返回一组三角形,这些三角形将不会以一对一的方式对应于棱镜的一组面。相反,多个共面三角形通常会形成一个面。
因此,在第二步中,您需要将相邻的共面三角形简化为单个面。如果您的顶点来自实验数据,那么在此步骤中您可能会遇到麻烦,因为三角形实际上可能不是共面的,因此下面给出的幼稚simplify
过程将失败。
借用@ImportanceOfBeingErnest的答案here(比接受的答案更简单,更快),您将得到:
#!/usr/bin/env python
# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as a3
from scipy.spatial import ConvexHull
class Faces():
def __init__(self,tri, sig_dig=12, method="convexhull"):
self.method=method
self.tri = np.around(np.array(tri), sig_dig)
self.grpinx = list(range(len(tri)))
norms = np.around([self.norm(s) for s in self.tri], sig_dig)
_, self.inv = np.unique(norms,return_inverse=True, axis=0)
def norm(self,sq):
cr = np.cross(sq[2]-sq[0],sq[1]-sq[0])
return np.abs(cr/np.linalg.norm(cr))
def isneighbor(self, tr1,tr2):
a = np.concatenate((tr1,tr2), axis=0)
return len(a) == len(np.unique(a, axis=0))+2
def order(self, v):
if len(v) <= 3:
return v
v = np.unique(v, axis=0)
n = self.norm(v[:3])
y = np.cross(n,v[1]-v[0])
y = y/np.linalg.norm(y)
c = np.dot(v, np.c_[v[1]-v[0],y])
if self.method == "convexhull":
h = ConvexHull(c)
return v[h.vertices]
else:
mean = np.mean(c,axis=0)
d = c-mean
s = np.arctan2(d[:,0], d[:,1])
return v[np.argsort(s)]
def simplify(self):
for i, tri1 in enumerate(self.tri):
for j,tri2 in enumerate(self.tri):
if j > i:
if self.isneighbor(tri1,tri2) and \
self.inv[i]==self.inv[j]:
self.grpinx[j] = self.grpinx[i]
groups = []
for i in np.unique(self.grpinx):
u = self.tri[self.grpinx == i]
u = np.concatenate([d for d in u])
u = self.order(u)
groups.append(u)
return groups
if __name__ == '__main__':
x = np.array([[-43.11150999], [-27.97693445], [-55.99892873], [-40.75143664], [-43.2165748],[-27.89568594],[-56.072327],[-40.75143814]])
y = np.array([[-118.14365791], [-124.54828379], [-120.42384095], [-133.41566716], [-34.770722],[-43.06953117],[-44.66085799],[-52.95966716]])
z = np.array([[-1100.99389988], [-1089.54038197], [-1084.32576297], [-1077.33745869], [-1030.85272686],[-1021.03437003],[-1019.15166512],[-1009.3333083]])
verts = np.c_[x, y, z]
# compute the triangles that make up the convex hull of the data points
hull = ConvexHull(verts)
triangles = [verts[s] for s in hull.simplices]
# combine co-planar triangles into a single face
faces = Faces(triangles, sig_dig=1).simplify()
# plot
ax = a3.Axes3D(plt.figure())
pc = a3.art3d.Poly3DCollection(faces,
facecolor="salmon",
edgecolor="k", alpha=0.9)
ax.add_collection3d(pc)
# define view
ax.set_xlim(np.min(x), np.max(x))
ax.set_ylim(np.min(y), np.max(y))
ax.set_zlim(np.min(z), np.max(z))
ax.dist=10
ax.azim=30
ax.elev=10
plt.show()