python - 基于顶点绘制 3D 连接棱镜 matplotlib

标签 python matplotlib

我有一组来自 4-6 向量的坐标 x, y, z。我想绘制相应的棱镜。但是我的线是交叉的,看起来根本不像棱镜。

我假设我必须对我的数据集进行排序,但我不确定这是正确的答案还是正确的答案。

我的情节

这显然是错误的:

wrong crossed prism plot

理想棱镜

它应该是这样的:

rectangular prism plot

三坐标数据集的真实棱镜

3D prism for 3c-dataset

脚本

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()

最佳答案

第一个问题问得很好!

我认为您正在寻找的是数据点的凸包,可以使用 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()

enter image description here

关于python - 基于顶点绘制 3D 连接棱镜 matplotlib,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53816211/

相关文章:

python - 如何删除在 Matplotlib 中使用鼠标悬停事件创建的绘图线?

python - 我如何 json_normalize() df 中的特定字段并保留其他列?

python - 有没有更快的方法可以通过 python 的 nltk 从单词列表中进行检查?

python - 多维排列

python - 如何有效地生成具有多个(可能重复的)字母的所有唯一单词

python - 如何在Python中正确使用多进程

python - ubuntu:用 pip 卸载 matplotlib 不起作用

python - Matplotlib - 更改自动轴范围

python - 有没有办法在 matplotlib.pyplot.show() 中选择默认鼠标 "mode"作为缩放指针?

python - 属性错误 : 'Figure' object has no attribute 'plot'