OpenMesh 教程:使用自定义属性

使用自定义属性

翻译,原文链接

本文包括:

  • 如何添加和删除自定义属性
  • 如何获取和设置自定义属性的值

上一篇文章中我们计算所有顶点相邻点的重心并将之存储在数组中,这里我们介绍一种更加方便也更不容易出错的方式,也就是利用 OpenMesh 来管理这些数据。动态的管理网格的属性会更加方便。

通过创建 OpenMesh::PropertyManager 对象,我们可以很方便地创建自定义属性 (custom properties)并将其与网格关联起来。PropertyManager 可以管理这些属性的生命周期并提供读取操作。

我们可以使用预定义的 VProp, HProp,EProp, FProp, MProp 来创建 PropertyManager将这些动态属性分别关联到顶点 (vertices)、半边 (halfedges)、边(edges) 、面 (faces) 和网格 (mesh)。创建这些属性的时候,需要给相应模板指定属性的数据类型。

值得注意的是,我们要明白具名属性 (Named properties) 和临时属性 (Temporary properties) 之间的差别。临时属性在创建的时候,其构造函数必须输入网格作为参数,相应的属性也立即创建,这些属性也会随着临时属性的 PropertyManager 脱离作用范围 (应该是被析构掉了),而立马被删除。如果一个属性在创建的时候,提供了属性名称,那么即使该具名属性的 PropertyManager 已经不存在了,该属性数据依旧存在。如果我们在创建一个具有相同名称的 PropertyManager,就可以读取和改写之前已经存在的属性。(译注:属性 property 实际存储在于网格相关的数据结构中,PropertyManager 只是提供了一个方便管理属性的工具)

最后,PropertyManager的第一个可选参数可以用来指定属性的初始值。下面的代码给出了创建网格属性的例子。

// Add a temporary mesh property that stores a double value for every vertex
auto temperature = OpenMesh::VProp<double>(mesh);
OpenMesh::VertexHandle vh = ...;
temperature[vh] = 1.0;
// The temperature property will be removed from the mesh when the handle reaches the end of the scope.
// Obtain an existing property that stores a 2D vector for every halfedge
// (or create that property if it does not exist already) and initilize it with the Vector(1,1))
auto uv = OpenMesh::HProp<OpenMesh::Vec2d>(mesh, "uv", OpenMesh::Vec2d(1,1));
OpenMesh::VertexHandle heh = ...;
std::cout << temperature[heh][0] << " " << temperature[heh][1] << std::endl;
// Obtain an existing mesh property (or create that property if it does not exist already)
// containing a description string
auto desc = OpenMesh::MProp<std::string>(mesh, "desc");
*desc = "This is a very nice mesh.";

下面我们将之前的重心平滑代码用顶点属性 (vertex property) 的方式重新写一遍。首先需要创建一个临时的顶点属性管理器,其属性数据类型为 MyMesh::Point

auto cog = OpenMesh::VProp<MyMesh::Point>(mesh);

这行代码会根据顶点数量分配足够的内存来存储这些顶点属性,接下来计算每个顶点相邻点的重心坐标

for (const auto& vh : mesh.vertices()) {
    cog[vh] = {0,0,0};
    int valence = 0;
    // Iterate over all 1-ring vertices around vh
    for (const auto& vvh : mesh.vv_range(vh)) {
        cog[vh] += mesh.point(vvh);
        ++valence;
    }
    cog[vh] /= valence;
}

最后,将顶点坐标设置为重心坐标

for (const auto& vh : mesh.vertices()) {
    mesh.point(vh) = cog[vh];
}

完整代码如下

#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/DefaultTriMesh.hh>
#include <OpenMesh/Core/Utils/PropertyManager.hh>
#include <iostream>
#include <vector>
using MyMesh = OpenMesh::TriMesh;
int main(int argc, char** argv)
{
    // Read command line options
    MyMesh mesh;
    if (argc != 4) {
        std::cerr << "Usage: " << argv[0] << " #iterations infile outfile" << std::endl;
        return 1;
    }
    const int iterations = argv[1];
    const std::string infile = argv[2];
    const std::string outfile = argv[3];
    
    // Read mesh file
    if (!OpenMesh::IO::read_mesh(mesh, infile)) {
        std::cerr << "Error: Cannot read mesh from " << infile << std::endl;
        return 1;
    }
    {
      // Add a vertex property storing the computed centers of gravity
      auto cog = OpenMesh::VProp<MyMesh::Point>(mesh);
      // Smooth the mesh several times
      for (int i = 0; i < iterations; ++i) {
          // Iterate over all vertices to compute centers of gravity
          for (const auto& vh : mesh.vertices()) {
              cog[vh] = {0,0,0};
              int valence = 0;
              // Iterate over all 1-ring vertices around vh
              for (const auto& vvh : mesh.vv_range(vh)) {
                  cog[vh] += mesh.point(vvh);
                  ++valence;
              }
              cog[vh] /= valence;
          }
          // Move all vertices to the previously computed positions
          for (const auto& vh : mesh.vertices()) {
              mesh.point(vh) = cog[vh];
          }
      }
    } // The cog vertex property is removed from the mesh at the end of this scope
    
    // Write mesh file
    if (!OpenMesh::IO::read_mesh(mesh, outfile)) {
        std::cerr << "Error: Cannot write mesh to " << outfile << std::endl;
        return 1;
    }
}

属性的生命周期

在上面的代码中,我们创建 VProp 的时候并没赋予名称,这就导致该属性在 其 handle 变量cog 离开其作用范围时自动被删除了。如果我们想让一个属性在其 handle 变量作用范围之外仍然继续存在,就需要在创建它的时候为之赋名,就像下面的代码

auto face_area = OpenMesh::FProp<double>(mesh, "face_area");

在之后的代码中,我们可以用过名称来获取该属性。如果想确定我们想要的属性究竟是否存在,可以用函数 hasProperty() 来查询一下,就像下面的代码

if (OpenMesh::hasProperty<OpenMesh::FaceHandle, double>(mesh, "face_area")) {
    // Property exists. Do something with it.
    auto valley = OpenMesh::FProp<bool>(mesh, "face_area");
}
else {
    // Property does not exist. Do something else.
}

Low-Level Property API

诸如 VProp, HProp, EProp, FProp, MProp等皆为高级的属性管理接口,除此之外,OpenMesh也提供了一些低级的接口来手动管理属性的生命周期。这些接口函数在 mesh 中函数中, add_property, get_propery, remove_perperty, property 以及其他一些 property handle classes (OpenMesh::VPropHandleT, OpenMesh::HPropHandleT, OpenMesh::EPropHandleT, OpenMesh::FPropHandleT, OpenMesh::MPropHandleT))

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页