我认为Guibas和Stolfi的“边缘代数”形式主义有点不必要。
真正需要做的就是记住原始图和对偶图之间的区别。原始图的每个面都有对应的对偶顶点;原始图的每个边具有对应的双重边;原始图的每个顶点都有一个对应的双面。原始边连接原始顶点和单独的原始面;双边缘连接双顶点和独立的双面。任何事物的对偶都是原始事物。参见Guibas和Stolfi的论文中的图4:f ∗ e e ∗ v v ∗ff∗ee∗vv∗
Guibas和Stolfi建议将每个边缘(原始边缘或双重边缘)考虑为四个定向的定向边缘的集合。为简单起见,我称这些飞镖。每个箭从一个端点指向另一端点,并在本地分隔两个面和。选择哪个端点调用是飞镖的方向,选择哪个端点调用就是它的方向尾巴( → e)头( → e)左( → e)右( → e)尾巴( → e)左( → e)e⃗ tail(e⃗ )head(e⃗ )left(e⃗ )right(e⃗ )tail(e⃗ )left(e⃗ )。(Guibas和Stolfi使用“ Org”和“ Dest”代替“ tail”和“ head”,但我更喜欢较短的标签,因为不必要的缩写是邪恶的。)
对于任何飞镖,Guibas和Stolfi会关联三个相关的飞镖:e⃗
- 尾部(→ e)→ etailNext(e⃗ ):飞镖在之后以逆时针顺序离开。tail(e⃗ )e⃗
- flip(e⃗ ):与相同的“飞镖” ,但带有和。e⃗ left(e⃗ )right(e⃗ )
- rotate(e⃗ ):通过将绕其中点逆时针旋转四分之一圈而获得的双重飞镖。e⃗
这三个函数满足各种奇妙的身份,如下所示:
- right(tailNext(e⃗ ))=left(e⃗ )
- right(flip(e⃗ ))=left(e⃗ )
- right(rotate(e⃗ ))=head(e⃗ )∗
- flip(flip(e⃗ ))=e⃗
- rotate(rotate(rotate(rotate(e⃗ ))))=e⃗
- tailNext(rotate(tailNext(rotate(e⃗ ))))=e⃗
有关完整列表,请参见论文的第83页(但请注意作者使用后缀表示法,大概是因为它更接近声明性代码)。Guibas和Stolfi将满足所有这些身份的任何三重函数称为边缘代数。e Flipe.Flip
而且,鉴于这三个功能,一个可以定义其他一些有用的功能,例如
- reverse(e⃗ )=rotate(flip(rotate(e⃗ ))) —交换头和尾顶点
- leftNext(e⃗ )=rotate(tailNext(rotate(rotate(rotate(e⃗ )))))左( → e)e⃗ left(e⃗ )
最后,了解这些功能可以绝对告诉您有关细分拓扑的所有信息,并且可以使用这三个功能对任何曲面(无论是否可定向)的任何多边形细分进行编码。
四边数据结构是表面图的一种特别方便的表示形式,它提供对所有这些功能的访问,以及一些其他的恒定时间操作,如插入,删除,收缩,扩展和翻转边缘。分割或合并顶点或面;以及添加或删除句柄或大写字母。
玩得开心!