绘制 3D 多边形

发布于 2024-10-10 23:01:39 字数 903 浏览 9 评论 0原文

我未能成功浏览网页寻找以下简单问题的解决方案:

如何使用顶点值绘制 3D 多边形(例如填充矩形或三角形)? 我尝试了很多想法,但都失败了,请参阅:

from mpl_toolkits.mplot3d import Axes3D
from matplotlib.collections import PolyCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [zip(x, y,z)]
ax.add_collection3d(PolyCollection(verts),zs=z)
plt.show()

我提前感谢任何想法/评论。

根据接受的答案进行更新:

import mpl_toolkits.mplot3d as a3
import matplotlib.colors as colors
import pylab as pl
import numpy as np

ax = a3.Axes3D(pl.figure())
for i in range(10000):
    vtx = np.random.rand(3,3)
    tri = a3.art3d.Poly3DCollection([vtx])
    tri.set_color(colors.rgb2hex(np.random.rand(3)))
    tri.set_edgecolor('k')
    ax.add_collection3d(tri)
pl.show()

结果如下: 在此处输入图像描述

I was unsuccessful browsing web for a solution for the following simple question:

How to draw 3D polygon (say a filled rectangle or triangle) using vertices values?
I have tried many ideas but all failed, see:

from mpl_toolkits.mplot3d import Axes3D
from matplotlib.collections import PolyCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [zip(x, y,z)]
ax.add_collection3d(PolyCollection(verts),zs=z)
plt.show()

I appreciate in advance any idea/comment.

Updates based on the accepted answer:

import mpl_toolkits.mplot3d as a3
import matplotlib.colors as colors
import pylab as pl
import numpy as np

ax = a3.Axes3D(pl.figure())
for i in range(10000):
    vtx = np.random.rand(3,3)
    tri = a3.art3d.Poly3DCollection([vtx])
    tri.set_color(colors.rgb2hex(np.random.rand(3)))
    tri.set_edgecolor('k')
    ax.add_collection3d(tri)
pl.show()

Here is the result:
enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

旧竹 2024-10-17 23:01:39

我想你已经差不多明白了。这是你想要的吗?

from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig, auto_add_to_figure=False)
fig.add_axes(ax)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [list(zip(x,y,z))]
ax.add_collection3d(Poly3DCollection(verts))
plt.show()

替代文本
您可能还对 art3d.pathpatch_2d_to_3d 感兴趣。

I think you've almost got it. Is this what you want?

from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig, auto_add_to_figure=False)
fig.add_axes(ax)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [list(zip(x,y,z))]
ax.add_collection3d(Poly3DCollection(verts))
plt.show()

alt text
You might also be interested in art3d.pathpatch_2d_to_3d.

栀梦 2024-10-17 23:01:39

不幸的是,答案和更新代码都会导致可视化不正确,但原因不同。下面提供了解释。

解决方案

首先考虑一个“正确”答案。 Matplotlib 将正确绘制填充多边形。多边形是平面。问题中的四个示例顶点不是平面的。但这些顶点可以用来定义两个三角形。 art3d.Poly3DCollection 是 (N, 3) 个类似数组的多边形序列的列表。使用四个顶点 v 的列表和两个三角形面 f 的顶点索引列表,一个简单的解决方案是:

import pylab as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection

v = [ [0,0,0], [1,0,1], [1,1,0], [0,1,1] ]   # 4 vertices
f = [ [0,1,3], [1,2,3] ]                     # 2 faces
verts =  [ [ v[i] for i in p] for p in f]    # list comprehension
c = ['C0','C3']                              # 2 colors

fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set(xlim=(0,1), ylim=(0,1), zlim=(0,1))
ax.add_collection3d(Poly3DCollection(verts,color=c))
plt.show()

生成具有不同颜色的两个多边形的图形,如下所示:

两个表面解决方案

这甚至适用于具有混合边数的多边形列表。例如,正方形和三角形面的集合:

闭合表面

生成此图的代码是:

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection

z = 2**(1/2)
v =  [ [1,1, 1], [-1,1, 1], [-1,-1, 1], [1,-1, 1], 
       [z,0,-1], [ 0,z,-1], [-z, 0,-1], [0,-z,-1] ]      # 8 vertices
f4 = [ [0,1,2,3], [7,6,5,4] ]
fa = [ [0,3,4], [1,0,5], [2,1,6], [3,2,7] ]
fb = [ [4,5,0], [5,6,1], [6,7,2], [7,4,3] ]
f = f4 + fa + fb                                         # 10 faces
verts =  [ [ v[i] for i in p] for p in f]                # list comprehension
colors = ['teal']*2 + ['indianred']*4 + ['olivedrab']*4  # 10 colors

fig = plt.figure(figsize=plt.figaspect(1))
ax = plt.axes(projection='3d')
ax.set(xlim=(-1.5,1.5), ylim=(-1.5,1.5), zlim=(-1.5,1.5))
ax.add_collection3d(Poly3DCollection(verts,color=colors))

fig.tight_layout()
plt.show()

请注意,colors 列表中的颜色与中的面列表的顺序相同。 f 列表。此外,如果所有多边形都属于同一类型,例如所有三角形或所有四边形,则可以方便地使用 Numpy 数组来表示顶点 v 和面 f。那么verts可以简单地定义为:

verts = v[f]

而不是使用Python列表理解。此外,如果使用着色,则必须使用与右手或左手顶点顺序一致的每个面的顶点索引列表来定义所有多边形。

问题 1. 非平面

通过以下方式定义顶点列表:

x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [list(zip(x,y,z))]

导致定义一个四侧面。对于 azim=-60 的 Matplotlib 默认轴视图,可视化“显示”正确。然而,不同观点的视觉解释是不一致的。下面通过比较三个视图来显示这一点。这不是 Matplotlib 问题,而是使用不在同一平面上的四个顶点错误定义 4 边多边形的问题。

不正确的视图

使用两个三角形面解决方案,此 3D 集合的可视化在任何观看方向上都是一致的:

正确视图

可以从四个顶点构造扭曲曲面,但这需要将曲面细分为多个多边形。使用Matplotlib第三方包S3Dlib,由512个三角形构建了以下直纹曲面。

直纹曲面

在本例中,表面的顶部和底部分别为蓝色和红色。然后应用阴影以增强表面的可视化。不再简单而是正确。

Matplotlib 3D 可视化可以通过沿视图方向正确堆叠多边形来处理多个多边形的集合。但是,Matplotlib 无法直接处理多边形相交时的可视化。可视化将不正确并且取决于视图。

例如,考虑三个三角形,定义为:

v = [ [0,0,0], [1,0,0], [1,1,0], [0,1,0],
      [0,0,1], [1,0,1], [1,1,1], [0,1,1] ]    # 8 vertices
f = [ [1,6,4], [1,3,5], [0,2,7] ]             # 3 triangles
c = [ 'C0', 'C1', 'C2' ]

这是三个相交的三角形,但从不同方向查看时,可视化效果不一致。

不正确的相交三角形

通过将三个三角形中的每一个细分为更小的三角形,三个三角形之间的交点可以独立于视图而一致地可视化:

正确相交三角形

上面的相交三角形图是使用 S3Dlib 通过 Matplotlib 创建的。

问题更新产生随机三角形多边形的集合。然而,正如上面的简单示例所示,如此大量的多边形将会相交。因此,如果不考虑交叉点,实际集合将无法正确显示。

通过仔细检查来自不同方向的相似三角形集合,可以检测到可视化异常。如下图所示。

随机三角形

一个可能的解决方案是将大量三角形细分为更小的多边形以考虑相交。然而,这种方法会导致大量的计算成本。问题的解决不再“简单”。

总结

通过从不同的观察方向查看 3D 集合,这两个问题都可以被识别。这样,在开发过程中,解决方法可能会被识别为有缺陷的。

附加说明:种子 Numpy 随机生成器在采样之前,例如使用:

np.random.seed(seed)

这样做允许在代码开发和验证期间使用同一组随机数。

Unfortunately, both the answer and update code result in visualizations that are incorrect, but for different reasons. Explanations are provided below.

A Solution

First consider a 'correct' answer. Matplotlib will correctly plot filled polygons. Polygons are planar. The four example vertices in the question are not planar. But these vertices can be used to define two triangles. The verts parameter in the art3d.Poly3DCollection is a list of (N, 3) array-like sequence of polygons. Using a list of the four vertices, v, and a list of vertex indices for two triangular faces, f, a simple solution is:

import pylab as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection

v = [ [0,0,0], [1,0,1], [1,1,0], [0,1,1] ]   # 4 vertices
f = [ [0,1,3], [1,2,3] ]                     # 2 faces
verts =  [ [ v[i] for i in p] for p in f]    # list comprehension
c = ['C0','C3']                              # 2 colors

fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set(xlim=(0,1), ylim=(0,1), zlim=(0,1))
ax.add_collection3d(Poly3DCollection(verts,color=c))
plt.show()

which produces a figure of two polygons with different colors as:

two surface solution

This even works for a list of polygons with a mixed number of sides. For example, a collection of square and triangular faces:

closed surface

The code to produce this figure is:

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection

z = 2**(1/2)
v =  [ [1,1, 1], [-1,1, 1], [-1,-1, 1], [1,-1, 1], 
       [z,0,-1], [ 0,z,-1], [-z, 0,-1], [0,-z,-1] ]      # 8 vertices
f4 = [ [0,1,2,3], [7,6,5,4] ]
fa = [ [0,3,4], [1,0,5], [2,1,6], [3,2,7] ]
fb = [ [4,5,0], [5,6,1], [6,7,2], [7,4,3] ]
f = f4 + fa + fb                                         # 10 faces
verts =  [ [ v[i] for i in p] for p in f]                # list comprehension
colors = ['teal']*2 + ['indianred']*4 + ['olivedrab']*4  # 10 colors

fig = plt.figure(figsize=plt.figaspect(1))
ax = plt.axes(projection='3d')
ax.set(xlim=(-1.5,1.5), ylim=(-1.5,1.5), zlim=(-1.5,1.5))
ax.add_collection3d(Poly3DCollection(verts,color=colors))

fig.tight_layout()
plt.show()

Notice that the colors in the colors list are in the same order as the list of faces in the f list. Also, if all the polygons are of the same type, for example all triangles or all quadrilaterals, it is convenient to use Numpy arrays for the vertices, v, and faces, f. Then the verts can be simply defined as:

verts = v[f]

instead of using Python list comprehension. In addition, if shading is used, all polygons must be defined with the list of vertex indices for each face consistent with a right-handed or left-handed vertex order.

Problem 1. Non-planar faces

Defining the verts list by:

x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [list(zip(x,y,z))]

results in defining one four-sided face. The visualization 'appears' correct for the Matplotlib default axes view with azim=-60. However, the visual interpretation is inconsistent for different views. This is shown below by comparing three views. This is not a Matplotlib problem but a problem of incorrectly defining a 4-sided polygon using four vertices which are not in the same plane.

incorrect views

Using the two triangular face solution, the visualization of this 3D collection is consistent for any viewing direction:

correct views

A warped surface can be constructed from the four vertices, but this requires subdividing the surface into multiple polygons. Using the Matplotlib third-party package S3Dlib, the following ruled surface was constructed from 512 triangles.

ruled surface

In this case, the top and bottom of the surface was colored blue and red, respectively. Then shading was applied to enhance the visualization of the surface. No longer simple but correct.

Matplotlib 3D visualizations can handle a polycollection of multiple polygons by correctly stacking the polygons along the view direction. However, Matplotlib cannot directly handle the visualization when polygons intersect. Visualizations will be incorrect and dependent on the view.

For example, consider three triangles defined as:

v = [ [0,0,0], [1,0,0], [1,1,0], [0,1,0],
      [0,0,1], [1,0,1], [1,1,1], [0,1,1] ]    # 8 vertices
f = [ [1,6,4], [1,3,5], [0,2,7] ]             # 3 triangles
c = [ 'C0', 'C1', 'C2' ]

These are three intersecting triangles, but when viewed from different orientations, the visualizations are inconsistent.

incorrect intersecting triangles

By subdividing each of the three triangles into smaller triangles, the intersections among the three triangles can be consistently visualized independent of the view:

correct intersecting triangles

The above figure of intersecting triangles was created with Matplotlib using S3Dlib.

The question update produces a collection of random triangular polygons. However, as demonstrated in the above simple example, such a large number of polygons will be intersecting. As a result, the actual collection will not be correctly displayed without accounting for the intersections.

By closely examining a similar collection of triangles from different orientations, the visualization anomalies can be detected. This is shown in the following figure.

random triangles

A possible solution could be to subdivide the numerous triangles into smaller polygons to account for the intersections. However this method would result in a significant computational cost. The problem solution is no longer 'simple'.

Summary

Both problems could have been identified by viewing the 3D collection from various viewing directions. That way, during development, the solution method may have been identified as flawed.

As additional note: seed the Numpy random generator prior to sampling, for example using :

np.random.seed(seed)

Doing this allows using the same set of random numbers during code development and verification.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文