matplotlib Matploilib 3D如何在多边形之间填充颜色

wlwcrazw  于 2023-03-03  发布在  其他
关注(0)|答案(1)|浏览(134)

我有一个带空心的三维梁截面(从外部多边形和空心多边形的2D多边形代码开始),我需要在Matplotlib 3D空间中绘制它。我根据梁的长度添加z坐标,首先过滤所有涉及的多边形,然后使用以下代码在Matplotlib 3D空间中绘制它们。此时,我能够填充空隙(见附图)但这不是我想要的。我如何改变顶点之间的空白和外部多边形(我的意思是固体部分)

import numpy as np
from sympy import Line, Polygon as Polyg
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt

shell=[(-500,0), (500,0), (400,500), (-400,500), (-500,0)]
hole1= [(-200,100), (200,100), (300,400), (-300,400), (-200,100)]
length =20000 

ex = np.array(shell)
int_=np.array(hole1)
#hole2=[(-400,50), (-200,50), (-200,100), (-400,100), (-400,50)]
#holes=(hole1,hole2)

# insert Z cordinate for external polygon
polye1 = np.insert(ex, 2, 0, axis=1)
polye2 = np.insert(ex, 2, length, axis=1)

polyi1 = np.insert(int_, 2, 0, axis=1)
polyi2 = np.insert(int_, 2, length, axis=1)

vertices_e = np.dstack((polye1, polye2))
vertices_i = np.dstack((polyi1, polyi2))
polygons = []
polygonsi = []

for i in np.arange(vertices_e.shape[0]) - 1:
    polygons.append(np.array([vertices_e[i, :, 1],
                              vertices_e[i + 1, :, 1],
                              vertices_e[i + 1, :, 0],
                              vertices_e[i, :, 0]]))
    polygons.append(polye1)
    polygons.append(polye2)

for i in np.arange(vertices_i.shape[0]) - 1:
    polygonsi.append(np.array([vertices_i[i, :, 1],
                              vertices_i[i + 1, :, 1],
                              vertices_i[i + 1, :, 0],
                              vertices_i[i, :, 0]]))
    polygonsi.append(polyi1)
    polygonsi.append(polyi2)
    
A=polygons
B=polygonsi
    
fig = plt.figure()
ax = Axes3D(fig, auto_add_to_figure=False)
ax.set_box_aspect([1, 1, 1])
fig.add_axes(ax)
for i in range(0, len(A)):
    x, y, z = zip(*A[i])
    x1 = [*(x)]
    y1 = [*(y)]
    z1 = [*(z)]
    ax.plot(x1, z1, y1, 'b')  # interchange the axis to get correct oriantation

for i in range(0, len(B)):
    xi, yi, zi = zip(*B[i])
    x2 = [*(xi)]
    y2 = [*(yi)]
    z2 = [*(zi)]
    ax.plot(x2, z2, y2, 'r')  # interchange the axis to get correct oriantation
    verts = [list(zip(x2, z2, y2))]  # this is important variable to fill in between later
    ax.add_collection3d(Poly3DCollection(verts, facecolors='cyan', linewidths=1, edgecolors='b', alpha=.25))

    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    ax.view_init(30, 120)
plt.show()
new9mtju

new9mtju1#

下面是我如何克服这个问题的完整代码(从2D多边形开始),以及如何使用Matplotlib 3D绘图使纵横比相同。希望这对某些人有帮助。

import numpy as np
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
from shapely.geometry import Polygon
from shapely.geometry.polygon import orient
from matplotlib.path import Path
from matplotlib.patches import PathPatch
import mpl_toolkits.mplot3d.art3d as art3d

def set_axes_equal(ax):
    x_limits = ax.get_xlim3d()
    y_limits = ax.get_ylim3d()
    z_limits = ax.get_zlim3d()

    x_range = abs(x_limits[1] - x_limits[0])
    x_middle = np.mean(x_limits)
    y_range = abs(y_limits[1] - y_limits[0])
    y_middle = np.mean(y_limits)
    z_range = abs(z_limits[1] - z_limits[0])
    z_middle = np.mean(z_limits)

    # The plot bounding box is a sphere in the sense of the infinity
    # norm, hence I call half the max range the plot radius.
    plot_radius = 0.5*max([x_range, y_range, z_range])

    ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])
    ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])
    ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])

def plot_polygon_3D(ax, poly, z, **kwargs):
    polygon = orient(poly)
    path = Path.make_compound_path(
        Path(np.asarray(polygon.exterior.coords)[:, :2]),
        *[Path(np.asarray(ring.coords)[:, :2]) for ring in polygon.interiors])

    patch = PathPatch(path, **kwargs)
    ax.add_patch(patch)
    art3d.pathpatch_2d_to_3d(patch, z=z, zdir="y")



@staticmethod
def by_two_polygons(shaplypoly,shaplypoly2=[],start = 0, length=10000):
    if type(shaplypoly) == Polygon:
        xe, ye = shaplypoly.exterior.xy
        epoly_cord = list(zip(xe, ye))
        e = np.array(epoly_cord)
        ipoly_cord = []
        if shaplypoly.interiors:
            for i, interior in enumerate(shaplypoly.interiors):
                ppx, ppy = zip(*interior.coords)
                ipoly = Polygon(list(zip(ppx, ppy)))
                ipoly_cord.append(list(zip(ppx, ppy)))

        # add extra z variable and assign two polygons beam start location and beam end location
        poly1_points = np.insert(e, 2, start, axis=1)
        # starting point = strat
        poly1 = poly1_points

        if not shaplypoly2:
            poly2_points = np.insert(e, 2, start + length, axis=1)
            # end point of beam start+length of beam
            poly2 = poly2_points
        else:
            xe2, ye2 = shaplypoly2.exterior.xy
            epoly_cord2 = list(zip(xe2, ye2))
            # make np array
            e2 = np.array(epoly_cord2)
            ipoly_cord2 = []
            if shaplypoly2.interiors:
                for i, interior in enumerate(shaplypoly2.interiors):
                    ppx2, ppy2 = zip(*interior.coords)
                    ipoly2 = Polygon(list(zip(ppx2, ppy2)))
                    ipoly_cord2.append(list(zip(ppx2, ppy2)))

            # add extra z variable and assign two polygons beam start location and beam end location
            poly2_points = np.insert(e2, 2, start + length, axis=1)
            poly2 = poly2_points

    vertices = np.dstack((poly1, poly2))
    polygons = []
    polygonsi = []
    for i in np.arange(vertices.shape[0]) - 1:
        polygons.append(np.array([vertices[i, :, 1],
                                  vertices[i + 1, :, 1],
                                  vertices[i + 1, :, 0],
                                  vertices[i, :, 0]]))

    plot_polygon_3D(ax, shaplypoly, -1+start ,facecolor = "gray", alpha = 1)
    plot_polygon_3D(ax, shaplypoly2, start+length+1 ,facecolor = "gray", alpha = 1)

    if shaplypoly.interiors and not shaplypoly2:
        for iploy in ipoly_cord:
            i=iploy
            polyi1_points = np.insert(i, 2, 0, axis=1)
            polyi2_points = np.insert(i, 2, length, axis=1)
            verticesi = np.dstack((polyi1_points, polyi2_points))

            for i in np.arange(verticesi.shape[0]) - 1:
                polygonsi.append(np.array([verticesi[i, :, 1],
                                          verticesi[i + 1, :, 1],
                                          verticesi[i + 1, :, 0],
                                          verticesi[i, :, 0]]))
            polygonsi.append(polyi1_points)
            polygonsi.append(polyi2_points)

    elif shaplypoly.interiors and shaplypoly2.interiors:
        for iploy in ipoly_cord:
            i=iploy
            polyi1_points = np.insert(i, 2, start, axis=1)

            for iploy2 in ipoly_cord2:
                i2 = iploy2
                polyi2_points = np.insert(i2, 2, start+length, axis=1)

            verticesi = np.dstack((polyi1_points, polyi2_points))

            for i in np.arange(verticesi.shape[0]) - 1:
                polygonsi.append(np.array([verticesi[i, :, 1],
                                          verticesi[i + 1, :, 1],
                                          verticesi[i + 1, :, 0],
                                          verticesi[i, :, 0]]))
            polygonsi.append(polyi1_points)
            polygonsi.append(polyi2_points)

    return polygons,polygonsi

class plot3Dbeam(object):
    def __init__(self, shapelypoly,shapelypoly2=[],start=0, length=20000):
        self.shapelypoly = shapelypoly
        self.shapelypoly2 = shapelypoly2
        self.length = length
        self.start=start # start point

    def plot_exbeam(self):
        A = by_two_polygons(self.shapelypoly,self.shapelypoly2,start=self.start, length=self.length)[0]
        B = by_two_polygons(self.shapelypoly, self.shapelypoly2,start=self.start, length=self.length)[1]

        for i in range(0, len(A)):
            x, y, z = zip(*A[i])
            x1 = [*(x)]
            y1 = [*(y)]
            z1 = [*(z)]
            ax.plot(x1, z1, y1, 'black')  # interchange the axis to get correct oriantation

            exterior_verts = [list(zip(x1, z1, y1))]
            ax.add_collection3d(Poly3DCollection(exterior_verts, facecolors='grey', linewidths=0.2, edgecolors='black', alpha=0.8))

        for i in range(0, len(B)):
            xi, yi, zi = zip(*B[i])
            x2 = [*(xi)]
            y2 = [*(yi)]
            z2 = [*(zi)]
            ax.plot(x2, z2, y2, 'black')  # interchange the axis to get correct oriantation

            interior_verts = [list(zip(x2, z2, y2))]  # this is important variable to fill in between later
            ax.add_collection3d(Poly3DCollection(interior_verts, facecolors='w', linewidths=0.2, edgecolors='black', alpha=0))

        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_zlabel('z')

下面给出了如何使用上述函数和类绘制实心空心3D多面体的示例代码:

shell1 = [(-500, 0), (500, 0), (400, 500), (-400, 500), (-500, 0)] # i.e section 1 exterior
shell2 = [(-500, 0), (500, 0), (400, 900), (-400, 900), (-500, 0)] # i.e section 2 exterior
hole1 = [(-200, 100), (200, 100), (300, 400), (-300, 400), (-200, 100)] # section 1 void1
hole2 = [(-200, 100), (200, 100), (300, 700), (-300, 700), (-200, 100)] # section 2 void 1
holes1=(hole1,)
holes2=(hole2,)
poly1 = orient(Polygon(shell1,holes1))
poly2 = orient(Polygon(shell2,holes2))


fig = plt.figure(figsize = (10, 6))
ax = fig.add_subplot(111, projection = '3d')
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
plot3Dbeam(poly1,poly2,start=0,length=5000).plot_exbeam()


ax.axis('off')
ax.grid(False)
set_axes_equal(ax)
ax.set_box_aspect([1, 1, 1], zoom = 2)


plt.show()

相关问题