VCG文档 - 邻接与拓扑(Adjacency and Topology)

2023-10-11 01:40

本文主要是介绍VCG文档 - 邻接与拓扑(Adjacency and Topology),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

邻接关系

VCG 库没有单一的,硬编码的方式来对三角形和边之间的关系进行编码. 这一切都取决于存储哪些属性以及如何使用它们. 在前面几节的例子中, 面的定义总是包含 vcg::face::VertexRef 属性, 这个属性存储了可以使用函数 V() 访问的指向MyVertex 的指针. 目前 VCG 中实现的几乎所有算法都假定 vcg::face::VertexRef 存在. 所以, 如果你的定义不包含 vcg::face::VertexRef 属性, 虽然这是正确的, 但几乎没有算法能正常运行.

还有其他的邻接关系可以用来遍历曲面, 例如遍历顶点的1- 邻域. VCG 库主要有两种邻接关系: 面-面(FF)和顶点-面(VF)邻接. 下面会对它们进行详细的介绍.

面-面邻接关系 (FF Adjacency)

vcg::face::FFAdj 属性声明了面-面邻接关系, 它通过面的公用边来编码这个邻接关系. 下图说明了用于索引两个三角面片的顶点和边的约定.

三角形的顶点按逆时针方向标记0,1,2. 边 i( 0,1,2) 就是 顶点 i和 i+1 之间的边.因此 ,在下图中, 面 f0,f1 之间的连接边就是 f0 的边 0 和 f1 的边 1.

Indexing of vertexes and edges

对于面 f 的第 i 个边, vcg::face::FFAdj 属性存储了

  • FFp(i): 指向公用面 f的第 i 个边的面的指针, 如果该边是边界, 那么就指向 f 自己.
  • FFi(i): f 的第 i 个边在指向的邻接面中的索引.

对于上面的例子, 假定 f0,f1 都是指针,那么就有下面的对应关系:

f1->FFp(1) == f0
f1->FFi(1) == 0f0->FFp(0) == f1
f0->FFi(0) == 1

请注意, 准确的说, 应当是 FFp(i) 指向一个邻接面, 而不是指向那个邻接面. (原文: FFp(i) points to a adjacent face, not to the adjacent face ). 原因就是在有非流形边的情况下, 有可能有多个面共用一条边, 这种情况 VCG 是无缝支持的. 下图展示了4 个面使用同一条边.

Non manifold edge

在这种情况下,FF邻接被构成了循环列表(不一定按照二面角进行排序). VCG 库是通过vcg::tri::UpdateTopology::FaceFace(MeshType &m) 函数来更新的. 这种情况下,VCG 提供了一种方法来检查一条边是否是流形边, 因为邻接关系是相互的. 例如, 如果是流形边的话, f0 指向 f1 , 同时 f1 又将指向 f0 .

bool IsManifold(MyFace *f,int e) { return (f0 == f0->FFp(0)->FFp(f0->FFi(0)))}
Pos

Pos 是 VCG Lib 对 Cell-Tuple{ref} 的实现. 简单起见, 这里给出一个尽可能简短的定义. 在三角网格中, Pos
vectex: pos = (v,e,f) 构成的三元组, e 是以 v 为起点并且属于面 f 的边. 下图使用指向一个顶点,靠在一条边并且属于一个面的小三角形展示 了几个 Pos . 例如 , c0 = (v, e0 ,f).

Pos Example

这里有一个非常好的性质, 给定一个 Pos c, 若仅改变 c 中的一个元素, 那就能唯一的得到 c 的一个邻域.

我们称从一个 Pos c 到它的一个邻域 Pos c' 的操作为 Flip. FlipV,FlipE, FlipF 表明操作的元素是Pos c 的顶点,边或者面.

例如, 考虑 c1 , 和 c2 只有顶点不同.可以写作 c2=FlipV(c1) .

下面有更多的例子对此进行解释.

c2 = FlipV(c1)
c0 = FlipE(c1)
c3 = FlipF(c0)CCW around v
c4 = FlipE(FlipF(c0))
c5 = FlipE(FlipF(c4))
Bounce
c6 = FlipE(FlipF(c5))
CW around v
c3 = FlipE(FlipF(c6))
c1 = FlipE(FlipF(c3))
Bounce
c0 = FlipE(FlipF(c1))

注意, 复合两个 Flip 操作, FlipF,FlipE, 我们得到了从一个Pos 到下一个(逆时针或者顺时针)Pos 的变换, 方向取决于开始Pos 相对于顶点是否位于面的逆时针边上. 也要注意, 由于 FF 邻接的定义, 当一个 Pos
在边界上时, 它将反转回来. 这一对 Flip 操作在VCG库中被广泛的使用, 例如遍历一个顶点的 1- 邻域.

下面的例子展示了这种使用:

sf/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp#include <vcg/simplex/face/pos.h> // include the definition of pos...includes to define your mesh typeclass MyVertex: ...
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef, vcg::face::FFAdj>{};void OneRingNeighborhood( MyFace * f)
{MyVertex * v = f->V(0);MyFace* start = f;vcg::face::Pos<MyFace> p(f,0,v);// constructor that takes face, edge and vertexdo{p.FlipF();p.FlipE();}while(p.f!=start);
}
  • 我们任意选择了一个作为旋转中心的顶点f->V(0). 通常来说是会选定一个已知的顶点. 可以通过使用vcg::vectex::VFAjd 属性来知道一个顶点和哪些面邻接. 更多介绍参考下面的 VF 邻接. 当顶点在边界上时这个操作就不会工作了. 看下面的例子, 从 c4 开始操作,可以一次找到 c5,c6,c3 , 这又回到了 c4 所在的面.当然,如果你使用Pos 本身作为一个警戒的条件, 这种情况就可以避免. 即使那样, 你将得到一个序列: c5,c6,c3,c1,c0,c4 对应着面 f2,f2,f1,f0,f0,f1 , 但这并不是你想要的. VCG 提供了另外一种变化的 Pos 来解决这个问题.
Jumping Pos

Jumping Pos 和 Pos 几乎完全相同, 只是它遇到边界时不会反转. 相反的, 它跳跃到共用顶点的边界面(图中的 f0,f2 ).

sf/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp#include <vcg/simplex/face/jumping_pos.h> // include the definition of jumping pos//...includes to define your mesh type//class MyVertex: ...class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef,vcg::face::FFAdj>{};void OneRingNeighborhoodJP( MyFace * f){MyVertex * v = f->V(0);MyFace* start = f;vcg::face::JumpingPos<MyFace> p(f,0,v);// constructor that takes face, edge and vertexdo{p.NextFE();}while(p.f!=start);}

VF Adjacency

VCG Lib 实现了顶点-面的邻接关系. 例如, 给定一个顶点 v, 可以检索到包含顶点v 的所有面. 令 v=(f0,f1,,fk) 是与顶点v 邻接的面的集合. VCG Lib 可以以最优的时间 (O( v )) 访问 v , 这个访问使用下面的属性:

  • vcg::vertex::VFAjd 顶点属性, 包含了指向面的指针.
  • vcg::face::VFAjd 面属性, 包含了指向集合 v 中下一个面的每个顶点的指针.

这两个属性不仅是一个指针, 它们也包含了类似 vcg::face::FFAdj 的索引. 下图给出了一个练习的示例: 类似 FF 邻接关系, 你需要使用 vcg::tri::UpdateTopology::VertexFace(MeshType &m).

Example of vertex-face adjacency

v.VFp() == f2
v.VFi() == 0f2->VFp(0) == f3
f2->VFi(0) == 1
f3->VFp(1) == f1
f3->VFi(1) == 2
f1->VFp(2) == f0
f1->VFi(2) == 2
f0->VFp(2) == NULL
f0->VFi(2) == -1
VFIterator

VFIterator 是一个用于遍历顶点的邻接面的迭代器(类似于 FF 邻接关系中的 Pos). 下面的代码展示了如何使用它:

sf/apps/sample/trimesh_pos_demo/trimesh_vfiter_demo.cpp
#include <vcg/simplex/face/pos.h> // include the definition of  VFIterator
//...includes to define your mesh type
class MyVertex: public vcg::VertexSimp2<MyVertex,MyEdge,MyFace, vcg::vertex::VFAdj /*,... other attributes*/ >{};
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef,vcg::face::VFAd>{};
void OneRingNeighborhoodVF( MyVertex * v)
{vcg::face::VFIterator<MyFace> vfi(v); //initialize the iterator tohe first facefor(;!vfi.End();++vfi){MyFace* f = vfi.F();// ...do something with face f}
}
Starts and Rings
vcg::face::VFOrderedStarFF
vcg::face::VVStarVF
vcg::face::VFStarVF
vcg::face::VFExtendedStarVF
vcg::face::EFStarFF
Few facts on FF adjacency and VF adjacency

在这里,我们提供一系列简单的描述来解除疑惑,并帮助选择最符合您需求的邻接关系.

如果曲面是流形的,那么使用 Pos 计算顶点的1-邻域和使用VFIterator 计算将是一致的.
如果使用 Pos,遍历面的顺序可能是顺时针或者逆时针的,使用 VFIterator 则未指明顺序. 如果曲面是非流形曲面, 那么无论使用哪种方式都有可能无法遍历所有面.

Boundary relations and adjacency

在许多算法中都需要面的边界条件. 例如, 知道给定的面的一条边是否有一个或多个邻接的面. FF邻接关系时, 使用 face::IsBorder(f,e) 静态函数可以简单判断 f 的边 e 存储的指针是否指向f自己.如果使用Pos , 那么 Pos的成员函数 IsBorder() 可以给出当前的边界状态. 类似的,可以使用 face::IsManifold(f,e)IsManifold(e) 来检测是否是流形边.

如果没有使用 FF 邻接关系, 那么判断边界条件将效率低下. VCG 提供了一种技术将边界状态提供给面和顶点的 Flags. 使用UpdateFlags 函数计算反应当前状态的 Flags , 使用 IsB(e) 函数来访问它. 谨记如果你改变了网格的拓扑关系将会导致边界信息不可用.另一方面, 许多不改变网格的算法仅要求边界信息而不要求FF邻接关系.

这篇关于VCG文档 - 邻接与拓扑(Adjacency and Topology)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/184596

相关文章

使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)

《使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)》PPT是一种高效的信息展示工具,广泛应用于教育、商务和设计等多个领域,PPT文档中常常包含丰富的图片内容,这些图片不仅提升了... 目录一、引言二、环境与工具三、python 提取PPT背景图片3.1 提取幻灯片背景图片3.2 提取

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Python实现word文档内容智能提取以及合成

《Python实现word文档内容智能提取以及合成》这篇文章主要为大家详细介绍了如何使用Python实现从10个左右的docx文档中抽取内容,再调整语言风格后生成新的文档,感兴趣的小伙伴可以了解一下... 目录核心思路技术路径实现步骤阶段一:准备工作阶段二:内容提取 (python 脚本)阶段三:语言风格调

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Java利用docx4j+Freemarker生成word文档

《Java利用docx4j+Freemarker生成word文档》这篇文章主要为大家详细介绍了Java如何利用docx4j+Freemarker生成word文档,文中的示例代码讲解详细,感兴趣的小伙伴... 目录技术方案maven依赖创建模板文件实现代码技术方案Java 1.8 + docx4j + Fr

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

详解C#如何提取PDF文档中的图片

《详解C#如何提取PDF文档中的图片》提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使用,下面我们就来看看如何使用C#通过代码从PDF文档中提取图片吧... 当 PDF 文件中包含有价值的图片,如艺术画作、设计素材、报告图表等,提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使

Python实现合并与拆分多个PDF文档中的指定页

《Python实现合并与拆分多个PDF文档中的指定页》这篇文章主要为大家详细介绍了如何使用Python实现将多个PDF文档中的指定页合并生成新的PDF以及拆分PDF,感兴趣的小伙伴可以参考一下... 安装所需要的库pip install PyPDF2 -i https://pypi.tuna.tsingh

Python批量调整Word文档中的字体、段落间距及格式

《Python批量调整Word文档中的字体、段落间距及格式》这篇文章主要为大家详细介绍了如何使用Python的docx库来批量处理Word文档,包括设置首行缩进、字体、字号、行间距、段落对齐方式等,需... 目录关键代码一级标题设置  正文设置完整代码运行结果最近关于批处理格式的问题我查了很多资料,但是都没

Python自动化Office文档处理全攻略

《Python自动化Office文档处理全攻略》在日常办公中,处理Word、Excel和PDF等Office文档是再常见不过的任务,手动操作这些文档不仅耗时耗力,还容易出错,幸运的是,Python提供... 目录一、自动化处理Word文档1. 安装python-docx库2. 读取Word文档内容3. 修改