You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib绘制3D凸多面体:消除共面单纯形冗余边的方法咨询

我之前也踩过这个坑!用凸包生成的3D多面体满是细碎的三角面,冗余线条看得人头疼,尤其是想复现Matlab plotregion那种干净的外表面效果时。这里有几个实用的解决方案,按省心程度排序:

1. 最省心:用专门的凸多面体库直接生成外表面

既然你的多面体是由不等式定义的,完全不用先求顶点再做凸包——直接用pycddlib(对应Matlab的cdd工具箱)从不等式组生成完整的外表面面,一步到位拿到干净的多边形面,没有冗余三角面。

示例代码:

import pycddlib as cdd
import numpy as np
from mpl_toolkits.mplot3d import Axes3D, Poly3DCollection
import matplotlib.pyplot as plt

# 把你的不等式转换为 Ax ≤ b 的形式
# 示例:x≥0, y≥0, z≥0, x+y+z ≤1
A = np.array([[-1, 0, 0], [0, -1, 0], [0, 0, -1], [1, 1, 1]])
b = np.array([0, 0, 0, 1])

# 转换为cdd要求的格式:每行是 [b_i, -A_i](齐次坐标形式)
mat = np.hstack((b.reshape(-1, 1), -A))
poly = cdd.Polyhedron(mat)

# 提取顶点和外表面面
vertices = np.array(poly.get_generators())[:, 1:]  # 去掉齐次坐标的首元素
faces = []
for face_idx in poly.get_faces():
    face_vertices = vertices[face_idx]
    faces.append(face_vertices)

# 绘制干净的多面体
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
poly3d = Poly3DCollection(faces, alpha=0.5, edgecolor='k')
ax.add_collection3d(poly3d)

# 适配坐标轴范围
ax.set_xlim(vertices[:,0].min(), vertices[:,0].max())
ax.set_ylim(vertices[:,1].min(), vertices[:,1].max())
ax.set_zlim(vertices[:,2].min(), vertices[:,2].max())

plt.show()

2. 次省心:用trimesh合并共面三角面

如果已经有了凸包的三角面数据,用trimesh库可以一键合并共面的细碎三角面,它封装了所有复杂的判断和合并逻辑,代码量极小。

示例代码:

import trimesh
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D, Poly3DCollection
from scipy.spatial import ConvexHull

# 替换成你的顶点数据
points = np.random.rand(10, 3)
hull = ConvexHull(points)

# 用trimesh加载凸包网格,合并共面
mesh = trimesh.Trimesh(vertices=points, faces=hull.simplices)
mesh = mesh.merge_coplanar(tol=1e-6)  # 合并共面,tol是浮点误差阈值

# 转换为Matplotlib可绘制的格式
faces = mesh.faces
vertices = mesh.vertices

# 绘图
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
poly3d = Poly3DCollection([vertices[face] for face in faces], alpha=0.5, edgecolor='k')
ax.add_collection3d(poly3d)

ax.set_xlim(vertices[:,0].min(), vertices[:,0].max())
ax.set_ylim(vertices[:,1].min(), vertices[:,1].max())
ax.set_zlim(vertices[:,2].min(), vertices[:,2].max())

plt.show()

3. 纯Matplotlib实现:手动合并共面三角面

如果不想引入第三方库,就手动识别共面的三角面,把它们合并成一个完整的多边形:

  • 计算每个三角面的归一化法向量,法向量几乎相同的面就是共面的
  • 提取共面组内的所有唯一顶点,投影到垂直于法向量的平面后按角度排序,得到有序的多边形顶点
  • 用Poly3DCollection绘制合并后的多边形

示例代码:

import numpy as np
from mpl_toolkits.mplot3d import Axes3D, Poly3DCollection
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull

# 替换成你的顶点数据
points = np.random.rand(10, 3)
hull = ConvexHull(points)

# 计算每个面的归一化法向量
normals = hull.equations[:, :3]
normals = normals / np.linalg.norm(normals, axis=1, keepdims=True)

# 按法向量分组共面的面(考虑浮点误差)
tol = 1e-6
groups = []
used = np.zeros(len(hull.simplices), dtype=bool)

for i in range(len(hull.simplices)):
    if used[i]:
        continue
    same_normal = np.all(np.abs(normals - normals[i]) < tol, axis=1)
    group = hull.simplices[same_normal]
    used[same_normal] = True
    groups.append(group)

# 合并每组共面的三角面为多边形
faces = []
for group in groups:
    # 提取组内唯一顶点
    vertex_indices = np.unique(group.flatten())
    face_points = points[vertex_indices]
    # 获取当前面的法向量
    normal = normals[np.where(same_normal)[0][0]]
    
    # 投影到垂直于法向量的平面,按角度排序顶点
    u = np.array([1,0,0]) if np.abs(np.dot(normal, [1,0,0])) < 0.9 else np.array([0,1,0])
    u = u - np.dot(u, normal)*normal
    u /= np.linalg.norm(u)
    v = np.cross(normal, u)
    proj = np.dot(face_points, np.vstack((u, v)).T)
    angles = np.arctan2(proj[:,1], proj[:,0])
    sorted_indices = np.argsort(angles)
    sorted_face = face_points[sorted_indices]
    
    faces.append(sorted_face)

# 绘图
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
poly3d = Poly3DCollection(faces, alpha=0.5, edgecolor='k')
ax.add_collection3d(poly3d)

ax.set_xlim(points[:,0].min(), points[:,0].max())
ax.set_ylim(points[:,1].min(), points[:,1].max())
ax.set_zlim(points[:,2].min(), points[:,2].max())

plt.show()

内容的提问来源于stack exchange,提问作者nikferrari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:14:05