Search code examples
python-3.xmatplotlib3dplotly-python

How to visualize polyhedrons defined by their vertices in 3D with matplotlib or/and plotly offline?


I would like to visualize 3 polyhedrons defined by 3 numpy arrays of shape (8, 3).

I am looking for something like that:

enter image description here

My data are the following:

A = np.array([[0.92523719, 0.26843252, 0.77794309],
       [0.73156748, 0.27794309, 0.57476281],
       [0.62113842, 0.37886158, 0.87886158],
       [0.72205691, 0.07476281, 0.76843252],
       [0.57476281, 0.23156748, 0.72205691],
       [0.77794309, 0.42523719, 0.73156748],
       [0.87886158, 0.12113842, 0.62113842],
       [0.76843252, 0.22205691, 0.92523719]])

B = np.array([[0.23156748, 0.72205691, 0.57476281],
       [0.26843252, 0.77794309, 0.92523719],
       [0.12113842, 0.62113842, 0.87886158],
       [0.22205691, 0.92523719, 0.76843252],
       [0.27794309, 0.57476281, 0.73156748],
       [0.37886158, 0.87886158, 0.62113842],
       [0.07476281, 0.76843252, 0.72205691],
       [0.42523719, 0.73156748, 0.77794309]])

C = np.array([[0.73156748, 0.77794309, 0.42523719],
       [0.62113842, 0.87886158, 0.12113842],
       [0.77794309, 0.92523719, 0.26843252],
       [0.57476281, 0.73156748, 0.27794309],
       [0.87886158, 0.62113842, 0.37886158],
       [0.72205691, 0.57476281, 0.23156748],
       [0.76843252, 0.72205691, 0.07476281],
       [0.92523719, 0.76843252, 0.22205691]])

Solution

  • You seem to have points in 3D, but no edge nor polygon information. Supposing the polyhedra are convex, scipy.spatial's ConvexHull can find all the polygons on the convex hull. The convex hull exists of triangles that can be added to a 3D plot as a Poly3DCollection.

    from mpl_toolkits.mplot3d.art3d import Poly3DCollection
    from mpl_toolkits.mplot3d import Axes3D
    from matplotlib import pyplot as plt
    import numpy as np
    from scipy.spatial import ConvexHull
    
    A = np.array([[0.92523719, 0.26843252, 0.77794309], [0.73156748, 0.27794309, 0.57476281], [0.62113842, 0.37886158, 0.87886158], [0.72205691, 0.07476281, 0.76843252], [0.57476281, 0.23156748, 0.72205691], [0.77794309, 0.42523719, 0.73156748], [0.87886158, 0.12113842, 0.62113842], [0.76843252, 0.22205691, 0.92523719]])
    B = np.array([[0.23156748, 0.72205691, 0.57476281], [0.26843252, 0.77794309, 0.92523719], [0.12113842, 0.62113842, 0.87886158], [0.22205691, 0.92523719, 0.76843252], [0.27794309, 0.57476281, 0.73156748], [0.37886158, 0.87886158, 0.62113842], [0.07476281, 0.76843252, 0.72205691], [0.42523719, 0.73156748, 0.77794309]])
    C = np.array([[0.73156748, 0.77794309, 0.42523719], [0.62113842, 0.87886158, 0.12113842], [0.77794309, 0.92523719, 0.26843252], [0.57476281, 0.73156748, 0.27794309], [0.87886158, 0.62113842, 0.37886158], [0.72205691, 0.57476281, 0.23156748], [0.76843252, 0.72205691, 0.07476281], [0.92523719, 0.76843252, 0.22205691]])
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    for cube, color in zip([A, B, C], ['r', 'g', 'b']):
        hull = ConvexHull(cube)
        # draw the polygons of the convex hull
        for s in hull.simplices:
            tri = Poly3DCollection([cube[s]])
            tri.set_color(color)
            tri.set_alpha(0.5)
            ax.add_collection3d(tri)
        # draw the vertices
        ax.scatter(cube[:, 0], cube[:, 1], cube[:, 2], marker='o', color='purple')
    plt.show()
    

    3D plot

    Supposing the longest side of each triangle is a face diagonal of the cube, we could search for the two shortest sides and draw them in black:

    from mpl_toolkits.mplot3d.art3d import Poly3DCollection
    from mpl_toolkits.mplot3d import Axes3D
    from matplotlib import pyplot as plt
    import numpy as np
    from scipy.spatial import ConvexHull, distance
    
    A = np.array([[0.92523719, 0.26843252, 0.77794309], [0.73156748, 0.27794309, 0.57476281], [0.62113842, 0.37886158, 0.87886158], [0.72205691, 0.07476281, 0.76843252], [0.57476281, 0.23156748, 0.72205691], [0.77794309, 0.42523719, 0.73156748], [0.87886158, 0.12113842, 0.62113842], [0.76843252, 0.22205691, 0.92523719]])
    B = np.array([[0.23156748, 0.72205691, 0.57476281], [0.26843252, 0.77794309, 0.92523719], [0.12113842, 0.62113842, 0.87886158], [0.22205691, 0.92523719, 0.76843252], [0.27794309, 0.57476281, 0.73156748], [0.37886158, 0.87886158, 0.62113842], [0.07476281, 0.76843252, 0.72205691], [0.42523719, 0.73156748, 0.77794309]])
    C = np.array([[0.73156748, 0.77794309, 0.42523719], [0.62113842, 0.87886158, 0.12113842], [0.77794309, 0.92523719, 0.26843252], [0.57476281, 0.73156748, 0.27794309], [0.87886158, 0.62113842, 0.37886158], [0.72205691, 0.57476281, 0.23156748], [0.76843252, 0.72205691, 0.07476281], [0.92523719, 0.76843252, 0.22205691]])
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    for cube, color in zip([A, B, C], ['r', 'g', 'b']):
        hull = ConvexHull(cube)
        for s in hull.simplices:
            tri = Poly3DCollection([cube[s]])
            tri.set_color(color)
            tri.set_alpha(0.5)
            tri.set_edgecolor('none')
            ax.add_collection3d(tri)
            edges = []
            if distance.euclidean(cube[s[0]], cube[s[1]]) < distance.euclidean(cube[s[1]], cube[s[2]]):
                edges.append((s[0], s[1]))
                if distance.euclidean(cube[s[1]], cube[s[2]]) < distance.euclidean(cube[s[2]], cube[s[0]]):
                    edges.append((s[1], s[2]))
                else:
                    edges.append((s[2], s[0]))
            else:
                edges.append((s[1], s[2]))
                if distance.euclidean(cube[s[0]], cube[s[1]]) < distance.euclidean(cube[s[2]], cube[s[0]]):
                    edges.append((s[0], s[1]))
                else:
                    edges.append((s[2], s[0]))
            for v0, v1 in edges:
                ax.plot(xs=cube[[v0, v1], 0], ys=cube[[v0, v1], 1], zs=cube[[v0, v1], 2], color='black')
        ax.scatter(cube[:, 0], cube[:, 1], cube[:, 2], marker='o', color='purple')
    plt.show()
    

    cube edges