使用 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;
}
}