Python 在matplot库中绘制三维凸闭合区域
我试图在3D中绘制一个由一组不等式定义的多面体。本质上,我试图在matplotlib中重现此matlab库的功能 我的方法是获取相交顶点,构造它们的凸包,然后获取并绘制生成的面(单纯形) 问题是许多单体是共面的,它们无缘无故地使绘图非常繁忙(请参见下面绘图中的所有对角线) 有没有简单的方法可以直接打印多面体的“外”边,而不必一个接一个地合并所有的共面单纯形 多谢各位Python 在matplot库中绘制三维凸闭合区域,python,numpy,matplotlib,scipy,mplot3d,Python,Numpy,Matplotlib,Scipy,Mplot3d,我试图在3D中绘制一个由一组不等式定义的多面体。本质上,我试图在matplotlib中重现此matlab库的功能 我的方法是获取相交顶点,构造它们的凸包,然后获取并绘制生成的面(单纯形) 问题是许多单体是共面的,它们无缘无故地使绘图非常繁忙(请参见下面绘图中的所有对角线) 有没有简单的方法可以直接打印多面体的“外”边,而不必一个接一个地合并所有的共面单纯形 多谢各位 from scipy.spatial import HalfspaceIntersection from scipy.spatia
from scipy.spatial import HalfspaceIntersection
from scipy.spatial import ConvexHull
import scipy as sp
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as a3
import matplotlib.colors as colors
w = np.array([1., 1., 1.])
# ∑ᵢ hᵢ wᵢ qᵢ - ∑ᵢ gᵢ wᵢ <= 0
# qᵢ - ubᵢ <= 0
# -qᵢ + lbᵢ <= 0
halfspaces = np.array([
[1.*w[0], 1.*w[1], 1.*w[2], -10 ],
[ 1., 0., 0., -4],
[ 0., 1., 0., -4],
[ 0., 0., 1., -4],
[-1., 0., 0., 0],
[ 0., -1., 0., 0],
[ 0., 0., -1., 0]
])
feasible_point = np.array([0.1, 0.1, 0.1])
hs = HalfspaceIntersection(halfspaces, feasible_point)
verts = hs.intersections
hull = ConvexHull(verts)
faces = hull.simplices
ax = a3.Axes3D(plt.figure())
ax.dist=10
ax.azim=30
ax.elev=10
ax.set_xlim([0,5])
ax.set_ylim([0,5])
ax.set_zlim([0,5])
for s in faces:
sq = [
[verts[s[0], 0], verts[s[0], 1], verts[s[0], 2]],
[verts[s[1], 0], verts[s[1], 1], verts[s[1], 2]],
[verts[s[2], 0], verts[s[2], 1], verts[s[2], 2]]
]
f = a3.art3d.Poly3DCollection([sq])
f.set_color(colors.rgb2hex(sp.rand(3)))
f.set_edgecolor('k')
f.set_alpha(0.1)
ax.add_collection3d(f)
plt.show()
从scipy.spatial导入半空间交点
从scipy.spatial导入convxhull
将scipy作为sp导入
将numpy作为np导入
将matplotlib.pyplot作为plt导入
将mpl_toolkits.mplot3d作为a3导入
将matplotlib.colors导入为颜色
w=np.数组([1,1,1.]))
# ∑ᵢ Hᵢ Wᵢ Qᵢ - ∑ᵢ Gᵢ Wᵢ 很确定matplotlib中没有任何本机内容。不过,找到属于一起的面孔并不特别困难。下面实现的基本思想是创建一个图,其中每个节点都是一个三角形。然后连接共面且相邻的三角形。最后,找到图形的连接组件以确定哪些三角形构成面
将numpy导入为np
从sympy导入平面,点3D
将networkx导入为nx
def简化(三角形):
"""
简化一组三角形,使相邻三角形和共面三角形形成一个面。
每个三角形是三维空间中3个点的集合。
"""
#创建一个节点表示三角形的图形;
#如果相应的三角形相邻且共面,则连接节点
G=nx.Graph()
G.从(范围(三角形))添加节点
对于ii,枚举中的a(三角形):
对于jj,枚举中的b(三角形):
if(ii return(角度-公差_in_弧度以下是我的解决方案版本。它类似于@Paul的解决方案,它采用三角形,按它们所属的面对它们进行分组,并将它们连接到一个面
区别主要在于此解决方案不使用nx
或simpy
。许多必要的操作都是通过重新索引、广泛使用unique
和一些线性代数来执行的。
最终面顶点的顺序由凸面决定。我认为这不应该是一个限制,因为(我认为)任何半空间相交都应该只产生凸面形状。但是,我还添加了另一种方法,如果形状不是凸面,可以使用它(基于来自的想法)
从scipy.spatial导入半空间交点
从scipy.spatial导入convxhull
将numpy作为np导入
将matplotlib.pyplot作为plt导入
将mpl_toolkits.mplot3d作为a3导入
w=np.数组([1,1,1.]))
# ∑ᵢ Hᵢ Wᵢ Qᵢ - ∑ᵢ Gᵢ Wᵢ 我猜“不太难”是一个委婉的说法?还有什么比最初的问题Qualified as更需要两个额外的包和更多的代码行吗?;-)我昨天尝试了类似的东西,但没有nx
,在第5个子循环中迷路后放弃了。哦,哇!非常感谢各位的努力!不是MaMattLIB的专家,但我认为这个主题中描述的功能非常有用(至少是足够有用的),以保证图书馆中的一个位置。也许我们应该打电话给开发商?再次感谢你!对我的生活圆满了!在@ImportanceOfBeingErnest被难倒的地方,我获胜了。我现在可以休息了但是,如果不是因为舍入误差,是共面的
会短得多,并且重新排列节点以获得凸面片几乎是剩余直线的一半。连接组件的整洁位非常短。@nikferrari:解决方案取决于networkx
和sympy
。我敢肯定,那些太大的依赖性,MatMattLIB团队考虑纳入MaMattLIB的主分支。替换是共面的
,因此清除sympy库可能非常简单(那天下午晚些时候我不相信我的数学);不过,实现一个图形结构并找到连接的组件至少需要一页代码。仔细检查,实际上可能没那么糟糕。scipy.sparse中已经有一个连接组件实现<代码>重新排序
仍然需要变得健壮,但是,我目前没有一个好主意。有什么建议吗,@ImportanceOfBeingErnest(当我们得到你的注意时)?
import numpy as np
from sympy import Plane, Point3D
import networkx as nx
def simplify(triangles):
"""
Simplify an iterable of triangles such that adjacent and coplanar triangles form a single face.
Each triangle is a set of 3 points in 3D space.
"""
# create a graph in which nodes represent triangles;
# nodes are connected if the corresponding triangles are adjacent and coplanar
G = nx.Graph()
G.add_nodes_from(range(len(triangles)))
for ii, a in enumerate(triangles):
for jj, b in enumerate(triangles):
if (ii < jj): # test relationships only in one way as adjacency and co-planarity are bijective
if is_adjacent(a, b):
if is_coplanar(a, b, np.pi / 180.):
G.add_edge(ii,jj)
# triangles that belong to a connected component can be combined
components = list(nx.connected_components(G))
simplified = [set(flatten(triangles[index] for index in component)) for component in components]
# need to reorder nodes so that patches are plotted correctly
reordered = [reorder(face) for face in simplified]
return reordered
def is_adjacent(a, b):
return len(set(a) & set(b)) == 2 # i.e. triangles share 2 points and hence a side
def is_coplanar(a, b, tolerance_in_radians=0):
a1, a2, a3 = a
b1, b2, b3 = b
plane_a = Plane(Point3D(a1), Point3D(a2), Point3D(a3))
plane_b = Plane(Point3D(b1), Point3D(b2), Point3D(b3))
if not tolerance_in_radians: # only accept exact results
return plane_a.is_coplanar(plane_b)
else:
angle = plane_a.angle_between(plane_b).evalf()
angle %= np.pi # make sure that angle is between 0 and np.pi
return (angle - tolerance_in_radians <= 0.) or \
((np.pi - angle) - tolerance_in_radians <= 0.)
flatten = lambda l: [item for sublist in l for item in sublist]
def reorder(vertices):
"""
Reorder nodes such that the resulting path corresponds to the "hull" of the set of points.
Note:
-----
Not tested on edge cases, and likely to break.
Probably only works for convex shapes.
"""
if len(vertices) <= 3: # just a triangle
return vertices
else:
# take random vertex (here simply the first)
reordered = [vertices.pop()]
# get next closest vertex that is not yet reordered
# repeat until only one vertex remains in original list
vertices = list(vertices)
while len(vertices) > 1:
idx = np.argmin(get_distance(reordered[-1], vertices))
v = vertices.pop(idx)
reordered.append(v)
# add remaining vertex to output
reordered += vertices
return reordered
def get_distance(v1, v2):
v2 = np.array(list(v2))
difference = v2 - v1
ssd = np.sum(difference**2, axis=1)
return np.sqrt(ssd)
from scipy.spatial import HalfspaceIntersection
from scipy.spatial import ConvexHull
import scipy as sp
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as a3
import matplotlib.colors as colors
w = np.array([1., 1., 1.])
# ∑ᵢ hᵢ wᵢ qᵢ - ∑ᵢ gᵢ wᵢ <= 0
# qᵢ - ubᵢ <= 0
# -qᵢ + lbᵢ <= 0
halfspaces = np.array([
[1.*w[0], 1.*w[1], 1.*w[2], -10 ],
[ 1., 0., 0., -4],
[ 0., 1., 0., -4],
[ 0., 0., 1., -4],
[-1., 0., 0., 0],
[ 0., -1., 0., 0],
[ 0., 0., -1., 0]
])
feasible_point = np.array([0.1, 0.1, 0.1])
hs = HalfspaceIntersection(halfspaces, feasible_point)
verts = hs.intersections
hull = ConvexHull(verts)
faces = hull.simplices
ax = a3.Axes3D(plt.figure())
ax.dist=10
ax.azim=30
ax.elev=10
ax.set_xlim([0,5])
ax.set_ylim([0,5])
ax.set_zlim([0,5])
triangles = []
for s in faces:
sq = [
(verts[s[0], 0], verts[s[0], 1], verts[s[0], 2]),
(verts[s[1], 0], verts[s[1], 1], verts[s[1], 2]),
(verts[s[2], 0], verts[s[2], 1], verts[s[2], 2])
]
triangles.append(sq)
new_faces = simplify(triangles)
for sq in new_faces:
f = a3.art3d.Poly3DCollection([sq])
f.set_color(colors.rgb2hex(sp.rand(3)))
f.set_edgecolor('k')
f.set_alpha(0.1)
ax.add_collection3d(f)
# # rotate the axes and update
# for angle in range(0, 360):
# ax.view_init(30, angle)
# plt.draw()
# plt.pause(.001)
from scipy.spatial import HalfspaceIntersection
from scipy.spatial import ConvexHull
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as a3
w = np.array([1., 1., 1.])
# ∑ᵢ hᵢ wᵢ qᵢ - ∑ᵢ gᵢ wᵢ <= 0
# qᵢ - ubᵢ <= 0
# -qᵢ + lbᵢ <= 0
halfspaces = np.array([
[1.*w[0], 1.*w[1], 1.*w[2], -10 ],
[ 1., 0., 0., -4],
[ 0., 1., 0., -4],
[ 0., 0., 1., -4],
[-1., 0., 0., 0],
[ 0., -1., 0., 0],
[ 0., 0., -1., 0]
])
feasible_point = np.array([0.1, 0.1, 0.1])
hs = HalfspaceIntersection(halfspaces, feasible_point)
verts = hs.intersections
hull = ConvexHull(verts)
simplices = hull.simplices
org_triangles = [verts[s] for s in simplices]
class Faces():
def __init__(self,tri, sig_dig=12, method="convexhull"):
self.method=method
self.tri = np.around(np.array(tri), sig_dig)
self.grpinx = list(range(len(tri)))
norms = np.around([self.norm(s) for s in self.tri], sig_dig)
_, self.inv = np.unique(norms,return_inverse=True, axis=0)
def norm(self,sq):
cr = np.cross(sq[2]-sq[0],sq[1]-sq[0])
return np.abs(cr/np.linalg.norm(cr))
def isneighbor(self, tr1,tr2):
a = np.concatenate((tr1,tr2), axis=0)
return len(a) == len(np.unique(a, axis=0))+2
def order(self, v):
if len(v) <= 3:
return v
v = np.unique(v, axis=0)
n = self.norm(v[:3])
y = np.cross(n,v[1]-v[0])
y = y/np.linalg.norm(y)
c = np.dot(v, np.c_[v[1]-v[0],y])
if self.method == "convexhull":
h = ConvexHull(c)
return v[h.vertices]
else:
mean = np.mean(c,axis=0)
d = c-mean
s = np.arctan2(d[:,0], d[:,1])
return v[np.argsort(s)]
def simplify(self):
for i, tri1 in enumerate(self.tri):
for j,tri2 in enumerate(self.tri):
if j > i:
if self.isneighbor(tri1,tri2) and \
self.inv[i]==self.inv[j]:
self.grpinx[j] = self.grpinx[i]
groups = []
for i in np.unique(self.grpinx):
u = self.tri[self.grpinx == i]
u = np.concatenate([d for d in u])
u = self.order(u)
groups.append(u)
return groups
f = Faces(org_triangles)
g = f.simplify()
ax = a3.Axes3D(plt.figure())
colors = list(map("C{}".format, range(len(g))))
pc = a3.art3d.Poly3DCollection(g, facecolor=colors,
edgecolor="k", alpha=0.9)
ax.add_collection3d(pc)
ax.dist=10
ax.azim=30
ax.elev=10
ax.set_xlim([0,5])
ax.set_ylim([0,5])
ax.set_zlim([0,5])
plt.show()