zoukankan      html  css  js  c++  java
  • 几个经典的数学库之一学习---VCGlib(1)

    1. VCG Libary是Visulization and Computer Graphics Libary(可视化与计算机图形学库)的缩写,是一个开源的C++模板库,用于三角网格和四面体网格的控制、处理和OpenGL显示。其中包含了超过100 000行的代码。基于该库,Visual Computing Lab开发了几个著名的工具,如metro和MeshLab。

    VCG Libary是专门为处理三角网格而设计的,库很大,且提供了许多最先进的处理网格的功能,如:

                *基于边坍塌(edge-collapse)二次误差的高质量网格简化(simplfication);
                *高效的空间检索数据结构(uniform grids, hashed grids, kdtree, ...);
                *先进的网格平滑和光顺算法;
                *曲率计算;
                *纹理坐标优化;
                *Hausdorff距离计算;
                *测地路径;
                *网格修复能力
                *等直面抽取和前沿的网格划分算法;
                *泊松圆盘采样和其他的网格点采样算法;
           *细分曲面。
    2. 可以从VCG的官网中获取:
      或者从github中下载:
     
      下载后的vcg的库结构如下。
     
         

          VCG Lib的大部分由头文件组成,下载后,将文件解压到一个vcglib文件夹中,然后将vcglib文件夹放到你的编译器的“include”目录中。然后使用时,只要包含其中需要的文件即可。
    同上图,vcglib文件夹中,总共包含5个子文件夹:

            *vcg:这是整个库的核心,其中定义了所有的算法和数据结构。该部分所有的C++代码都是STL支持的普通数据结构和算法,不包含任何其它标准库之外的库而且可以发现,该部分只包含头文件(.h)
            *wrap:这里包含一些针对特定需求/上下文/库的VCG概念的封装。例如,所有的用于计算机硬盘上很多格式的网格数据的导入和导出;用OpenGL渲染三角形网格的代码;普通GUI工具如跟踪球,等等;
            *apps:这个文件夹包含一些用VCG Lib开发的命令行程序应用。

      很多例子都能在MeshLab中找到apps/simple文件夹包含了这些程序的一个基础的子集,是一个初学者很好的入口点;
            *docs:文档(包括这个教程)
            *eigenLib:线性代数的eigen库最近的稳定版本的一个副本(相当于就是借用第三方库了),VCGLib中的高级矩阵操作都是基于这个库的。

     
     3 VCG中的邻接关系
     
      VCG库不通过硬编码的方式来位置三角形与面的邻接关系。VCG依赖于保存在各种“Simplices”(简素)中的属性来位置。如在每一个面中定义vcg::face::VertexRef,该属性保存了三个指针,指向Vertex。所有的算法都依赖于vcg::face::VertexRef。去过缺少次属性,则定义没有错误,但是算法将不会执行。
      VCG中有量大临街关系face-face和vertex-face邻接关系。通过这两个临街关系,可以遍历网格拓扑关系。
     
    1. FF Adjacent(Face-Face 邻接关系)
      面之间的邻接关系存储于vcg::face::FFAdj(正四面体为vcg::face::TTAdj),该属性通过边来记录面之间的邻接关系。
      

    图中顶点编号从0到2,以逆时针顺序编号,边i(i=0..2)的两个端点分别为i和(i+1)%3,因此图中面f0和面f1的公共边对f0而言是0边,对f1而言是1边。

    对于面f的每个边i,vcg::face::FFAdj存储以下信息:

    • FFp(i):指向共享边i的面的指针,若i是border,则该指针指向自己;
    • FFi(i):在指向的面中i的索引。在共享边的面中的索引。

    例如在上图中,有:

    • f1->FFp(1) == f0
    • f1->FFi(1) == 0
    • f0->FFp(0) == f1
    • f0->FFi(0) == 1
      需要说明的是, FFp(i)指向一个邻接面。如果是下图中的non-manifld 边的情况,(奇异边)则在一边上有多余两个面。这看起来在VCG中没有支持。
      VCG提供了一种方法来检查mesh是否是manifold(可组装的)在指定的边中。因为FFAdj的关系是相互的,如,f0指向面f1,则面f1也一定指向f0,则在对应的边上二者是manifold。
      【说明】mainfold是针对边来做区分的。一般指的是以下两种情况:
      (1)Non-Mainfold类型的边是多个面共边的情况。
      (2)计算一个vertex邻接的面的数量,通过Pos计算的结果比VFIterator计算的数量少。
     
      
    bool IsManifold(MyFace *f,int e) { return (f0 == f0->FFp(0)->FFp(f0->FFi(0)))}
      

    Pos关系

      三角网格中,Pos为一三元组,pos = {v,e,f},e是f的边,v是e的端点。
      
      从一个pos移动到该pos的一个邻居的操作称为Flip,把改变顶点的Flip操作分别记做FlipVFlipEFlipF
      

       

      下图以小三角形的形式显示了三角网格中的一些pos,在每个面中,每个小三角形指向一个顶点倚靠一条边,且在一个三角形面片中

      这样就能保证任意给定一个pos c,若只改变c的三元组的一个分量,能够唯一确定一个邻居pos。

      C2与C1,的Pos只有Vertex Component不同,即 C2 = FlipV(C1)

      

    c2 = FlipV(c1),定点分量不同,即换顶点则在同一个三角形中同一条边的另一个定点。
    c0 = FlipE(c1),边分量不同,即换边则在同一个三角形中同一个定点的另一个边。
    c3 = FlipF(c0), 面分量不同,即换面则通过边临街另一个面。
    
    CCW around v,(逆时针旋转V)
    c4 = FlipE(FlipF(c0)),即C0换面换边后为C4
    c5 = FlipE(FlipF(c4)),即C4换面换边后C5
    Bounce(弹跳,遇到边界情况)
    c6 = FlipE(FlipF(c5)),遇到边界后弹回
    CW around v (顺时针旋转V)
    c3 = FlipE(FlipF(c6))
    c1 = FlipE(FlipF(c3))
    Bounce(弹跳)
    c0 = FlipE(FlipF(c1)),遇到边界弹回

      当两个flip嵌套操作的时候,根据pos与面的关系,可以实现顺时针或逆时针的pos转换。并且由于面-面邻接关系的定义方式,当pos在border上的时候,FlipF操作会返回到pos本身所在的面。

      VCG库中广泛使用成对的flip,mannifold定点的相邻的ring环。

      下面的例子使用pos来迭代一个点的周围相关性。

    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 type
    
    class 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 vertex
      do
      {
        p.FlipF();
        p.FlipE();
      }while(p.f!=start);
    }

     注意:

      (1)使用属性vcg::vertex::VFAdj属性,可以关联的顶点指针指向面。(2)如果定点再边界上,则上述方法行不通。即从C4开始,将会找到C5,C6,C3,即C3余C4在同一个面中。但是,如果遇到边界,则会“弹”回来,即在这个图中,可能会得到C5,C6,C3, C1,C0, C4的序列,这或许不能够完成对一个定点的便利序列。

    VCG提供以下的方法解决

    Jumping Pos

      Jumping Pos类似Pos,但在遇到border的时候不会反弹回去,而是跨到下一个border-face上去。下面这个例子中,上图中的p会从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 vertex
          do
          {
            p.NextFE();
          }while(p.f!=start);
        }

     2. VF Adjacency(顶点-面, Vertex-Face 邻接关系)

       VCG lib中,顶点与面之间也实现了邻接关系,即给定一个顶点v,可以找出所有与其关联的面。

      设v_star = (f0,f1,f2,…,fk)为一个列表,该列表中的元素都是与顶点v关联的面。可以通过以下属性索引v_star:

    • vcg::vertex::VFAdj:这是一个顶点的属性,包含了指向f0的指针
    • vcg::face::VFAdj:这是面的属性,该面三个顶点中的每一个顶点都包含了指向v_star列表中下一个面的指针

      

      这两个属性不单是指针,他们同样包含在指向面中定点的索引,和vcg::face::FFAdj同样的效果。如上图展示了实际的例子,与Face-Face adjacent相似,需要使用vcg::tri::UpdateTopology::VertexFace(MeshType& m)来初始化。

    Vertex的VF邻接关系
    v.VFp() == f2  顶点的VFp是一个指针,指向此定点关联的面的列表的第一个面。 v.VFi() == 0  定点的VFi是一个索引值,是所指向的面中的定点所在的序号
    face的VF邻接关系,即,面中的指定索引值i顶点,其所对应的邻接面的序列的首个面的指针 f2
    ->VFp(0) == f3 ,即f2面中的0索引定点,其所对应的临界面的序列的首个面的指针,即面f3 f2->VFi(0) == 1 ,即f2面中的0索引点,其再所对应的邻接面的首个面中的相应的该(本身)定点所在的序号。 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是一个简单的迭代器,来遍历一个顶点的所有邻接三角形面,通过使用VFAdjacency属性。其用法如下图:

    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 face
      for(;!vfi.End();++vfi)
      {
        MyFace* f = vfi.F();
        // ...do something with face f
      }
    }

    Stars and Rings

      端点顶点的face和vertices集合,可以使用以下方式函数:

    面面邻接与点面邻接中的少量的面的处理

       以下陈述可以避免“冲突”,来帮助选择最适合的临街对象
      如果mesh是 manifold,则通过使用Pos计算出的顶点的临街ring(环)与使用VFIterator计算出的一致。
      如果使用Pos,所有的面的集合的访问顺序可以是顺时针,或者是逆时针。但是如果使用VIterator则顺序不能指定,如果mesh是non-manifold。
      Pos可能不会找到一个定点的所有邻接面,但是VFIterator可以。
    边界关系与邻接关系
      在许多的算法中,你应该简化三角面的边界。例如,如果一个给定的面F,有许多的邻接面在指定的边中。使用FF 邻接的过程中,可以使用face::isBorder(f,e)静态函数,可以简化合适面f中的点。
      *如果使用Pos来遍历网格,通过Pos的城管函数IsBorder()可以核实当前点的边界情况。
      *同样,通过mesh中指定区域,测试manifoldness,有一个函数face::IsManifold(f,e)和IsManifold(e) ,Pos类的函数。
     
       如果使用Face-Face邻接关系来处理“边界”条件,则效率不高,因此VCG库提供了将mesh的边界条件“标识flag”添加在顶点和面的标识中。使用类的静态方法UpdateFlags()来计算可以反映当前网格状态的标识,通过使用面的类的静态成员函数IsB(e)可以后去这些标识值。如果改变mesh网格的拓扑结构信息,则这些标识flag则无效。另一方面,对于许多non-mesh-modifying算法,不要求显示的Face-Face邻接关系,但是对于边界信息(典型的许多网格光滑和曲率计算算法)
      
      对于non manifold 条件,边界的标识被设置为true。
     
      http://vcg.isti.cnr.it/vcglib/adjacency.html

      


    endl;

     
     
     
     
     
     
     
     
     
     
     
    endl;
  • 相关阅读:
    【云速建站】表单应用
    【云速建站】页面产品维护简述
    Forrester:华为云容器是容器混合云最佳选择
    AI如何驱动软件开发?华为云DevCloud 权威专家邀你探讨
    完美数据迁移-MongoDB Stream的应用
    补习系列(3)-springboot中的几种scope
    补习系列(2)-springboot mime类型处理
    关于Python的随机数模块,你必须要掌握!
    【Java】实战Java虚拟机之五“开启JIT编译”
    Jetty实战之 嵌入式Jetty运行Servlet
  • 原文地址:https://www.cnblogs.com/icmzn/p/6640752.html
Copyright © 2011-2022 走看看