Commit 3a2791c4 authored by Max Lyon's avatar Max Lyon

update documentation

parent f4722800
Pipeline #12702 failed with stages
in 167 minutes and 31 seconds
#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 compte update vectors as the negative of the laplace of the laplace damped by 0.5
for (const auto& vh : mesh.vertices())
bi_laplace(vh) = (vh.vertices().avg(laplace) - laplace(vh));
// update points
for (const auto& vh : mesh.vertices())
points(vh) += -0.5 * bi_laplace(vh);
}
} // The laplace and update properties are removed 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;
}
}
......@@ -82,6 +82,7 @@ repeatedly replacing each vertex' position by the center of gravity
\li \ref tutorial_02
\li \ref tutorial_03
\li \ref tutorial_04
\li \ref tutorial_11
\li \ref tutorial_05
\li \ref tutorial_06
\li \ref tutorial_07
......
/** \page tutorial_11 Using Smart Handles
This examples shows:
- How to use Smart Handles and ranges to navigate on the mesh
- How to use Smart Ranges
So far we have used methods such as halfedge_handle(), next_halfedge_handle(), prev_halfedge_handle(), oppopsite_halfedge_handle(), face_handle(), to_vertex_handle(), and some others, to navigate on that mesh. These functions are defined on a mesh and require as input a handle to an element of the mesh, such as VertexHandle or HalfedgeHandle. In the following example we iterate over all vertices of a triangle mesh and for each vertex we create a list of the vertices that lie opposite of the edges in the ring around the vertex:
\code
// 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)))));
}
}
\endcode
For a more concise way of navigating OpenMesh provides smart handles, OpenMesh::SmartVertexHandle, OpenMesh::SmartHalfedgeHandle, OpenMesh::SmartEdgeHandle, and OpenMesh::SmartFaceHandle. Smart handles are smart, because they know to which mesh they belong. This allows them to provide functions for navigating the mesh allowing us to write the above code much simpler:
\code
// 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());
}
}
\endcode
The ranges of OpenMesh that are returned by functions like voh_range() or outgoing_halfedges() all provide a few methods than can simplify some calculations (see OpenMesh::SmartRangeT). One example is the to_vector() method which convertes the range of elements into a vector containing the elements. All of these methods take a functor as argument (sometimes optional) which is called for each element of the range. With this, the above code can also be implemented like this:
\code
// 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);
}
\endcode
---
## Code Example
In this example, we will use bi-laplacian smoothing on a mesh. We store the \c laplace vector which is the vector pointing from a vertex to the center of gravity of its neighboring vertices in a vertex property.
\dontinclude 11-smart_handles/smooth.cc
\skipline laplace
\skipline laplace
To compute the center of gravity, i.e. the average position, we use the avg() method of the range of 1-ring vertices and pass in a PropertyManager acting as functor returning the corresponding point of a vertex.
\skipline points
\until avg(points)
Similarily we compute the update vector as the laplace of the freshly computed laplace vectors by simply exchanging the points property manager with the laplace property manager.
\skipline Iterate
\until bi_laplace
Finally, we apply the update after damping it by a factor of -0.5.
\skipline udpate points
\until bi_laplace
Below is the complete source code:
\include 11-smart_handles/smooth.cc
---
*/
......@@ -33,6 +33,7 @@ repeatedly replacing each vertex' position by the center of gravity
<li> \subpage tutorial_02
<li> \subpage tutorial_03
<li> \subpage tutorial_04
<li> \subpage tutorial_11
<li> \subpage tutorial_05
<li> \subpage tutorial_06
<li> \subpage tutorial_07
......
......@@ -70,7 +70,12 @@ struct SmartRangeT
{
// TODO: Someone with better c++ knowledge may improve the code below.
/** @brief Computes the sum of elements.
*
* Computes the sum of all elements in the range after applying the functor \p f.
*
* @param f Functor that is applied to all elements before computing the sum
*/
template <typename Functor>
auto sum(Functor&& f) -> decltype (f(std::declval<HandleT>())+f(std::declval<HandleT>()))
{
......@@ -86,6 +91,12 @@ struct SmartRangeT
return sum;
}
/** @brief Computes the average of elements.
*
* Computes the average of all elements in the range after applying the functor \p f.
*
* @param f Functor that is applied to all elements before computing the average.
*/
template <typename Functor>
auto avg(Functor&& f) -> decltype (1.0 * (f(std::declval<HandleT>())+f(std::declval<HandleT>())))
{
......@@ -105,6 +116,15 @@ struct SmartRangeT
return (1.0 / n_elements) * sum;
}
/** @brief Convert range to array.
*
* Converts the range of elements into an array of objects returned by functor \p f.
* The size of the array needs to be provided by the user. If the size is larger than the number of
* elements in the range, the remaining entries of the array will be uninitialized.
*
* @param f Functor that is applied to all elements before putting them into the array. If no functor is provided
* the array will contain the handles.
*/
template <int n, typename Functor = Identity>
auto to_array(Functor&& f = {}) -> std::array<typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type, n>
{
......@@ -118,6 +138,13 @@ struct SmartRangeT
return res;
}
/** @brief Convert range to vector.
*
* Converts the range of elements into a vector of objects returned by functor \p f.
*
* @param f Functor that is applied to all elements before putting them into the vector. If no functor is provided
* the vector will contain the handles.
*/
template <typename Functor = Identity>
auto to_vector(Functor&& f = {}) -> std::vector<typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type>
{
......@@ -128,7 +155,12 @@ struct SmartRangeT
return res;
}
/** @brief Compute minimum.
*
* Computes the minimum of all objects returned by functor \p f.
*
* @param f Functor that is applied to all elements before computing minimum.
*/
template <typename Functor>
auto min(Functor&& f) -> typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type
{
......@@ -148,6 +180,12 @@ struct SmartRangeT
return res;
}
/** @brief Compute maximum.
*
* Computes the maximum of all objects returned by functor \p f.
*
* @param f Functor that is applied to all elements before computing maximum.
*/
template <typename Functor>
auto max(Functor&& f) -> typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type
{
......@@ -167,6 +205,13 @@ struct SmartRangeT
return res;
}
/** @brief Computes minimum and maximum.
*
* Computes the minimum and maximum of all objects returned by functor \p f. Result is returned as std::pair
* containing minimum as first and maximum as second element.
*
* @param f Functor that is applied to all elements before computing maximum.
*/
template <typename Functor>
auto minmax(Functor&& f) -> std::pair<typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type,
typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type>
......
......@@ -226,6 +226,7 @@ class PropertyManager {
* Create a wrapper around an existing property. Lifetime is not managed.
*
* @param mesh The mesh on which to create the property.
* @param property_handle Handle to an existing property that should be wrapped.
*/
PropertyManager(PolyConnectivity& mesh, PROPTYPE property_handle) : mesh_(mesh), prop_(property_handle), retain_(true), name_() {
}
......
This diff is collapsed.
......@@ -446,14 +446,14 @@ TEST_F(OpenMeshTutorials, using_iterators_and_circulators) {
TEST_F(OpenMeshTutorials, using_custom_properties) {
MyMesh mesh;
bool ok = OpenMesh::IO::read_mesh(mesh, "output.off");
EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'";
bool ok = OpenMesh::IO::read_mesh(mesh, "cube_noisy.off");
EXPECT_TRUE(ok) << "Cannot read mesh from file 'cube_noisy.off'";
const int iterations = 100;
{
// Add a vertex property storing the computed centers of gravity
auto cog = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, MyMesh::Point>(mesh);
auto cog = OpenMesh::VProp<MyMesh::Point>(mesh);
// Smooth the mesh several times
for (int i = 0; i < iterations; ++i) {
......@@ -484,8 +484,8 @@ TEST_F(OpenMeshTutorials, using_custom_properties) {
TEST_F(OpenMeshTutorials, using_STL_algorithms) {
MyMeshWithTraits mesh;
bool ok = OpenMesh::IO::read_mesh(mesh, "output.off");
EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'";
bool ok = OpenMesh::IO::read_mesh(mesh, "cube_noisy.off");
EXPECT_TRUE(ok) << "Cannot read mesh from file 'cube_noisy.off'";
SmootherT<MyMeshWithTraits> smoother(mesh);
smoother.smooth(100);
......@@ -882,4 +882,44 @@ TEST_F(OpenMeshTutorials, collapsing_edges) {
// Our mesh now looks like in the illustration above after the collapsing.
}
TEST_F(OpenMeshTutorials, using_smart_handles_and_smart_ranges) {
MyMesh mesh;
bool ok = OpenMesh::IO::read_mesh(mesh, "cube_noisy.off");
EXPECT_TRUE(ok) << "Cannot read mesh from file 'cube_noisy.off'";
const int iterations = 100;
{
// 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 compte update vectors as the negative of the laplace of the laplace damped by 0.5
for (const auto& vh : mesh.vertices())
bi_laplace(vh) = (vh.vertices().avg(laplace) - laplace(vh));
// update points
for (const auto& vh : mesh.vertices())
points(vh) += -0.5 * bi_laplace(vh);
}
} // The laplace and update properties are removed is removed from the mesh at the end of this scope.
// write mesh
ok = OpenMesh::IO::write_mesh(mesh, "smoothed_smart_output.off");
EXPECT_TRUE(ok) << "Cannot write mesh to file 'smoothed_smart_output.off'";
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment