本文介绍了Python Matplotlib - 绘制长方体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 matplotlib 绘制不同大小的长方体,这样:旋转后,长方体不会以非物理方式在视觉上重叠,立方体具有不同的颜色并在它们周围绘制一个框.

我已经阅读了几篇引用类似问题的博客文章和 stackoverflow 页面,但总是略有不同;没有一个对我有用.克服重叠问题的最简单方法是使用体素(如

解决方案

A.使用 Poly3DCollection

一个选项是创建一个长方体面的Poly3DCollection.由于同一收藏的艺术家不存在重叠问题,因此这可能最适合这里的目的.

from mpl_toolkits.mplot3d 导入 Axes3D从 mpl_toolkits.mplot3d.art3d 导入 Poly3DCollection将 numpy 导入为 np导入 matplotlib.pyplot 作为 pltdef cuboid_data2(o, size=(1,1,1)):X = [[[0, 1, 0], [0, 0, 0], [1, 0, 0], [1, 1, 0]],[[0, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 0]],[[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]],[[0, 0, 1], [0, 0, 0], [0, 1, 0], [0, 1, 1]],[[0, 1, 0], [0, 1, 1], [1, 1, 1], [1, 1, 0]],[[0, 1, 1], [0, 0, 1], [1, 0, 1], [1, 1, 1]]]X = np.array(X).astype(float)对于范围内的 i (3):X[:,:,i] *= 大小[i]X += np.array(o)返回 Xdef plotCubeAt2(positions,sizes=None,colors=None, **kwargs):如果不是 isinstance(colors,(list,np.ndarray)):colors=[C0"]*len(positions)如果不是 isinstance(sizes,(list,np.ndarray)):sizes=[(1,1,1)]*len(positions)克 = []对于 zip 中的 p、s、c(位置、大小、颜色):g.append( cuboid_data2(p, size=s) )返回 Poly3DCollection(np.concatenate(g),facecolors=np.repeat(colors,6), **kwargs)位置 = [(-3,5,-2),(1,7,1)]尺寸 = [(4,5,3), (3,3,7)]颜色 = [深红色",柠檬绿"]fig = plt.figure()ax = fig.gca(projection='3d')ax.set_aspect('相等')pc = plotCubeAt2(位置,大小,颜色=颜色,边缘颜色=k")ax.add_collection3d(pc)ax.set_xlim([-4,6])ax.set_ylim([4,13])ax.set_zlim([-3,9])plt.show()

B.使用 plot_surface

采用

I'm trying to plot cuboids of different sizes using matplotlib, such that: after rotation the cuboids do not overlap visually in a non-physical way, the cubes have different colors and a box drawn around them.

I've read several blog posts and stackoverflow pages referencing similar problems, but always with a slight difference; none which have worked for me. The easiest way to overcome the overlapping problem was to use voxels (as in https://matplotlib.org/api/_as_gen/mpl_toolkits.mplot3d.axes3d.Axes3D.html?highlight=voxel#mpl_toolkits.mplot3d.axes3d.Axes3D.voxels), but these do not allow me to draw boxes around them. What's the easiest way to do this in matplotlib?

The image below shows what I have on the left, and what I want on the right.

EDIT:I've looked into several approaches that can give the desired effect, of which the main ones are:

  • using voxels, but somehow scaling them such that a single voxel represents a single item.
  • using surface plots, but then adjusting the drawing order dynamically to avoid non-physical overlapping.

The former seemed easier to execute, but I'm still stumped.

解决方案

A. Using Poly3DCollection

An option is to create a Poly3DCollection of the faces of the cuboids. As the overlapping issue is not present for artists of the same collection, this might best serve the purpose here.

from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
import matplotlib.pyplot as plt

def cuboid_data2(o, size=(1,1,1)):
    X = [[[0, 1, 0], [0, 0, 0], [1, 0, 0], [1, 1, 0]],
         [[0, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 0]],
         [[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]],
         [[0, 0, 1], [0, 0, 0], [0, 1, 0], [0, 1, 1]],
         [[0, 1, 0], [0, 1, 1], [1, 1, 1], [1, 1, 0]],
         [[0, 1, 1], [0, 0, 1], [1, 0, 1], [1, 1, 1]]]
    X = np.array(X).astype(float)
    for i in range(3):
        X[:,:,i] *= size[i]
    X += np.array(o)
    return X

def plotCubeAt2(positions,sizes=None,colors=None, **kwargs):
    if not isinstance(colors,(list,np.ndarray)): colors=["C0"]*len(positions)
    if not isinstance(sizes,(list,np.ndarray)): sizes=[(1,1,1)]*len(positions)
    g = []
    for p,s,c in zip(positions,sizes,colors):
        g.append( cuboid_data2(p, size=s) )
    return Poly3DCollection(np.concatenate(g),
                            facecolors=np.repeat(colors,6), **kwargs)


positions = [(-3,5,-2),(1,7,1)]
sizes = [(4,5,3), (3,3,7)]
colors = ["crimson","limegreen"]

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal')

pc = plotCubeAt2(positions,sizes,colors=colors, edgecolor="k")
ax.add_collection3d(pc)

ax.set_xlim([-4,6])
ax.set_ylim([4,13])
ax.set_zlim([-3,9])

plt.show()

B. Using plot_surface

Adapting the solution from this question, which uses plot_surface, and allow for different sizes as desired here seems to work just fine for most cases:

from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt

def cuboid_data(o, size=(1,1,1)):
    # code taken from
    # https://stackoverflow.com/a/35978146/4124317
    # suppose axis direction: x: to left; y: to inside; z: to upper
    # get the length, width, and height
    l, w, h = size
    x = [[o[0], o[0] + l, o[0] + l, o[0], o[0]],
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],
         [o[0], o[0] + l, o[0] + l, o[0], o[0]]]
    y = [[o[1], o[1], o[1] + w, o[1] + w, o[1]],
         [o[1], o[1], o[1] + w, o[1] + w, o[1]],
         [o[1], o[1], o[1], o[1], o[1]],
         [o[1] + w, o[1] + w, o[1] + w, o[1] + w, o[1] + w]]
    z = [[o[2], o[2], o[2], o[2], o[2]],
         [o[2] + h, o[2] + h, o[2] + h, o[2] + h, o[2] + h],
         [o[2], o[2], o[2] + h, o[2] + h, o[2]],
         [o[2], o[2], o[2] + h, o[2] + h, o[2]]]
    return np.array(x), np.array(y), np.array(z)

def plotCubeAt(pos=(0,0,0), size=(1,1,1), ax=None,**kwargs):
    # Plotting a cube element at position pos
    if ax !=None:
        X, Y, Z = cuboid_data( pos, size )
        ax.plot_surface(X, Y, Z, rstride=1, cstride=1, **kwargs)

positions = [(-3,5,-2),(1,7,1)]
sizes = [(4,5,3), (3,3,7)]
colors = ["crimson","limegreen"]


fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal')

for p,s,c in zip(positions,sizes,colors):
    plotCubeAt(pos=p, size=s, ax=ax, color=c)

plt.show()

这篇关于Python Matplotlib - 绘制长方体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-01 23:28