OpenMesh 教程:使用 Smart Handles

使用 Smart Handles

本文包括:

  • 如何使用 Smart Handles 和 Smart Ranges 访问网格
  • 如何使用 Smart Ranges

到目前为止,我们都是使用诸如 halfedge_handle(), next_halfedge_handle(), prev_halfedge_handle(), oppopsite_halfedge_handle(), face_handle(), to_vertex_handle() 等 handle 来获取网格信息。这些函数都需要一个网格的 handle 作为输入参数才能够获取网格上的其他元素,handle 的类型可能是 VertexHandle 或者 HalfedgeHandle以及其他等等。下面的例子中,我们将遍历三角网格的所有顶点,并针对每一个顶点创建一个列表来保存该顶点之邻点的相反边。

// iterate over vertices of the mesh
for (auto vh : mesh.vertices())
{
  std::vector<OpenMesh::VertexHandle> opposite_vertices;
  // iterate over all outgoing halfedges
  for (auto heh : mesh.voh_range(vh))
  {
    // navigate to the opposite vertex and store it in the vector
    opposite_vertices.push_back(mesh.to_vertex_handle(mesh.next_halfedge_handle(mesh.opposite_halfedge_handle(mesh.next_halfedge_handle(heh)))));
  }
}

为了更加简洁地访问数据,OpenMesh 提供了 smart handles,如 OpenMesh::SmartVertexHandle, OpenMesh::SmartHalfedgeHandle, OpenMesh::SmartEdgeHandle, OpenMesh::SmartFaceHandle. Smart handles 之所以能称为智能,是因为所有 handle 都知其所属,这也使得我们在遍历网格时可以将代码写得更简洁

// iterate over vertices of the mesh
for (auto vh : mesh.vertices())
{
  // iterate over all outgoing halfedges
  std::vector<OpenMesh::VertexHandle> opposite_vertices;
  for (auto heh : vh.outgoing_halfedges())
  {
    // navigate to the opposite vertex and store it in the vector
    opposite_vertices.push_back(heh.next().opp().next().to());
  }
}

在 OpenMesh 中像 voh_range(), outgoing_halfedges() 等函数都会返回 OpenMesh::SmartRangeT 类型,这可以简化一些计算。举个例子,to_vector() 函数可以将range 内的元素转换到一个 vector 内。此类函数的输入参数均为仿函数类型 (有时也可以没有),range 内的每个元素都会经过这个仿函数的处理,借助于 range,上面的一段代码可以写成下面的形式

// iterate over vertices of the mesh
for (auto vh : mesh.vertices())
{
  // create lambda that returns opposite vertex
  auto opposite_vertex = [](OpenMesh::SmartHalfedgeHandle heh) { return heh.next().opp().next().to(); };
  // create vector containing all opposite vertices
  auto opposite_vertices = vh.outgoing_halfedges().to_vector(opposite_vertex);
}

在接下来的代码中,我们将展示 bi-laplacian 平滑算法。这里 laplace 向量的方向是从顶点之邻点的重心指向该顶点,我们利用一个 VProp 来保存 laplace 向量

// Add a vertex property storing the laplace vector
auto laplace = OpenMesh::VProp<MyMesh::Point>(mesh);

我们用 avg() 这个函数来计算顶点之顶点的重心坐标并将之存在 PropManager 中

// Get a propertymanager of the points property of the mesh to use as functor
auto points = OpenMesh::getPointsProperty(mesh);

// Smooth the mesh several times
for (int i = 0; i < iterations; ++i) {
    // Iterate over all vertices to compute laplace vector
    for (const auto& vh : mesh.vertices())
        laplace(vh) = vh.vertices().avg(points) - points(vh);

用相同的方式,可以计算二次 laplace 向量,

// Iterate over all vertices to compute the laplace vector of the laplace vectors
for (const auto& vh : mesh.vertices())
    bi_laplace(vh) =  (vh.vertices().avg(laplace) - laplace(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 laplace vector
    auto laplace = OpenMesh::VProp<MyMesh::Point>(mesh);
    
    // Add a vertex property storing the laplace of the laplace
    auto bi_laplace = OpenMesh::VProp<MyMesh::Point>(mesh);
    
    // Get a propertymanager of the points property of the mesh to use as functor
    auto points = OpenMesh::getPointsProperty(mesh);
    
    // Smooth the mesh several times
    for (int i = 0; i < iterations; ++i) {
      // Iterate over all vertices to compute laplace vector
      for (const auto& vh : mesh.vertices())
        laplace(vh) = vh.vertices().avg(points) - points(vh);
      
      // Iterate over all vertices to compute the laplace vector of the laplace vectors
      for (const auto& vh : mesh.vertices())
        bi_laplace(vh) =  (vh.vertices().avg(laplace) - laplace(vh));
      
      // update points by substracting the bi-laplacian damped by a factor of 0.5
      for (const auto& vh : mesh.vertices())
        points(vh) += -0.5 * bi_laplace(vh);
    }
  } // The laplace and update properties are 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;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值