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

相关文章

C#实现一键批量合并PDF文档

《C#实现一键批量合并PDF文档》这篇文章主要为大家详细介绍了如何使用C#实现一键批量合并PDF文档功能,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言效果展示功能实现1、添加文件2、文件分组(书签)3、定义页码范围4、自定义显示5、定义页面尺寸6、PDF批量合并7、其他方法

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

使用Python实现Word文档的自动化对比方案

《使用Python实现Word文档的自动化对比方案》我们经常需要比较两个Word文档的版本差异,无论是合同修订、论文修改还是代码文档更新,人工比对不仅效率低下,还容易遗漏关键改动,下面通过一个实际案例... 目录引言一、使用python-docx库解析文档结构二、使用difflib进行差异比对三、高级对比方

Python自动化处理PDF文档的操作完整指南

《Python自动化处理PDF文档的操作完整指南》在办公自动化中,PDF文档处理是一项常见需求,本文将介绍如何使用Python实现PDF文档的自动化处理,感兴趣的小伙伴可以跟随小编一起学习一下... 目录使用pymupdf读写PDF文件基本概念安装pymupdf提取文本内容提取图像添加水印使用pdfplum

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

C#高效实现Word文档内容查找与替换的6种方法

《C#高效实现Word文档内容查找与替换的6种方法》在日常文档处理工作中,尤其是面对大型Word文档时,手动查找、替换文本往往既耗时又容易出错,本文整理了C#查找与替换Word内容的6种方法,大家可以... 目录环境准备方法一:查找文本并替换为新文本方法二:使用正则表达式查找并替换文本方法三:将文本替换为图

Python批量替换多个Word文档的多个关键字的方法

《Python批量替换多个Word文档的多个关键字的方法》有时,我们手头上有多个Excel或者Word文件,但是领导突然要求对某几个术语进行批量的修改,你是不是有要崩溃的感觉,所以本文给大家介绍了Py... 目录工具准备先梳理一下思路神奇代码来啦!代码详解激动人心的测试结语嘿,各位小伙伴们,大家好!有没有想

Python调用LibreOffice处理自动化文档的完整指南

《Python调用LibreOffice处理自动化文档的完整指南》在数字化转型的浪潮中,文档处理自动化已成为提升效率的关键,LibreOffice作为开源办公软件的佼佼者,其命令行功能结合Python... 目录引言一、环境搭建:三步构建自动化基石1. 安装LibreOffice与python2. 验证安装

Python操作PDF文档的主流库使用指南

《Python操作PDF文档的主流库使用指南》PDF因其跨平台、格式固定的特性成为文档交换的标准,然而,由于其复杂的内部结构,程序化操作PDF一直是个挑战,本文主要为大家整理了Python操作PD... 目录一、 基础操作1.PyPDF2 (及其继任者 pypdf)2.PyMuPDF / fitz3.Fre

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (