Bmesh数据结构解析(1)
引子
网格结构编辑是3d/2d图形设计中绕不开的坎,网格结构编辑涉及到大量的UI互交,比如一个简单的边挤出(Extrude)操作涉及到边的几何选B择、边的拓扑修正、边和节点的缝合等等复杂操作、几何图形的渲染等多个步骤,比之直接绘图,难度提升比较大。
网格结构编辑涉及到图形学的知识比较系统,除了几何和绘图几何学,部分还涉及到部分微分内容和线代,所以在很多web应用中,能够直接处理网格的高质量的开源包的可怜,代码以c/c++为主,鲜有python和javascript写的
稳定性,在图形学中一个polygon subD几次后,几十万面或千万面是非常常见地,这里就牵涉到可视化调试,在此之前我也是初次尝试这种方法
行业软件如Max Maya SkeptchUP c4d的内部mesh数据结构是如何实现的,因为是软件闭源的关系,我们无从而知。但blender 的bmesh结构成为我们主要的参考材料
Bmesh数据结构解析
全节点和半节点机构
- 全边结构 储存每条边的两个节点 va-vb full-edge means (va,vb) to store edge inforamtion
- 半边结构 储存每条边的一个节点 va- 或 vb- 多边形的边按顺时针或逆时针排列half-edge means (va-) to store edge information while polygon’s edge-list should be clockwised or counter-clockwised
adject_edge 储存临边信息
除了储存位置信息和连接关系
为了减少在反复
adject_edge 和 outerface 分别存储相邻的边 和相邻的面(如果有),没有邻接则储存为None
ia--- --- la------oa
| | |
| | |
| | |
ib--- --- lb------ob
#面[ia,la,lb,ib] 中边[la,lb] 的adject_edge也是[la,lb]
#面[ia,la,lb,ib] 中边[la,lb] 的outerface 是[la,oa,ob,lb]
Loop cut 功能类似于Blender 中地 ctrl + R
边循环中loop 和 ring
多边形[ia la lb ib]
选中 [ia,ib],[la,lb]
#loop cut 后 产生两个多边形 [ia la ml mi] 和 [mi ml lb ib]
ia--- e4 ---la ia--- ---la
| | | |
e3 e1 cut mi--- en ---ml
| | | |
ib--- e2 ---lb ib--- ---lb
Bmesh->python
光读代码还是没有用,这里我尝试用PYQT来实现这个结构,以尽可能写的通俗易懂为目标(不一定最优)
我们这里选择全边结构储存数据
class Vertex(object):
def init(self, x:float = 0, y:float = 0 ):
self.x = x
self.y = y
def dist(self,p)->float:
return math.sqrt(
math.pow(self.x - p.x, 2) + math.pow(self.y - p.y, 2)
)
def len(self)->float:
return self.dist(self)
def inside(self,rect):
if self.x>rect[0] and self.x<rect[0]+rect[2] and self.y>rect[1] and self.y<rect[1]+rect[3] :
return True
else :
return False
在这里插入代码片
```python
class Edge(object):
def __init__(self,vertex_a:Vertex,vertex_b:Vertex):
self.va = vertex_a
self.vb = vertex_b
self.adject_edge = None
self.outerface = None
self.loop = None
def order_equal(self,edge)->int:
if _distance_to(self.va, edge.va) < POINT_TOL and _distance_to(self.vb, edge.vb) < POINT_TOL :
return 1
elif _distance_to(self.va, edge.vb) < POINT_TOL and _distance_to(self.vb, edge.va) < POINT_TOL :
return 2
else:
return 0
def equal(self,edge):
ev = self.order_equal(edge)
if ev == 1 or ev == 2 :
return True
else:
return False
def adject(self, edge)-> int:
# "based on POINT_TOL to testify whether two edge are the adjected and return adject status"
# "判定两条线是否连接,并返回检测结果"
# adject - 1 a-a点连接
if _distance_to(self.va,edge.va)<POINT_TOL:
return 1
# adject - 2 a-b点连接
if _distance_to(self.va,edge.vb)<POINT_TOL:
return 2
# adject - 3 b-a点连接
if _distance_to(self.vb,edge.va)<POINT_TOL:
return 3
# adject - 4 b-b点连接
if _distance_to(self.vb,edge.vb)<POINT_TOL:
return 4
return 0
def loop_edge_next(self):
index = self.loop.index(self)
if index == len(self.loop)-1:
return self.loop[0]
return self.loop[index+1]
def loop_edge_prev(self):
index = self.loop.index(self)
if index == 0:
return self.loop[len(self.loop)-1]
return self.loop[index-1]
def inside(self,rect):
if self.va.inside(rect) and self.vb.inside(rect):
return True
else:
return False
一个简单地编辑器,左键可以选择或拖动边进行移动,右键可以对页面平移,
出于实用性,必要加入了一些辅助类,比如框选,添加网格线
后一章 主要讲解ring和loop