Merge branch 'SmartRanges' into 'master'
Smart ranges and reworked property manager See merge request OpenMesh/OpenMesh!233
This commit is contained in:
@@ -32,7 +32,7 @@ echo "CPPCHECK Summary"
|
||||
echo "=============================================================================="
|
||||
echo -e "${NC}"
|
||||
|
||||
MAX_COUNT=26
|
||||
MAX_COUNT=27
|
||||
|
||||
if [ $COUNT -gt $MAX_COUNT ]; then
|
||||
echo -e ${WARNING}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <OpenMesh/Core/IO/MeshIO.hh>
|
||||
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
|
||||
#include <OpenMesh/Core/Mesh/DefaultTriMesh.hh>
|
||||
#include <OpenMesh/Core/Utils/PropertyManager.hh>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using MyMesh = OpenMesh::TriMesh_ArrayKernelT<>;
|
||||
using MyMesh = OpenMesh::TriMesh;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
@@ -27,7 +27,7 @@ int main(int argc, char** argv)
|
||||
|
||||
{
|
||||
// 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) {
|
||||
|
||||
62
Doc/Tutorial/11-smart_handles/smooth.cc
Normal file
62
Doc/Tutorial/11-smart_handles/smooth.cc
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,20 @@
|
||||
|
||||
<tr valign=top><td><b>8.1</b> (?/?/?)</td><td>
|
||||
|
||||
<b>Breaking Changes</b>
|
||||
<ul>
|
||||
<li>PropertyManager: PropertyManager only gives const access to the underlying mesh.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<b>Core</b>
|
||||
<ul>
|
||||
<li>Property System: Get rid of the OM_FORCE_STATIC_CAST defines. We use the type ids to check if the cast is valid or not. This will add more type safety. </li>
|
||||
<li>Default Traits: Added DefaultTraitsDouble as a version of the default traits that uses double precision for positions and normals as well as float for colors. </li>
|
||||
<li>Default Mesh Types: Added typdefs for a Triangle Mesh and a PolyMesh which use DefaultTraitsDouble and can be used as default mesh type be the user. </li>
|
||||
<li>Template Programming Convenience: Added n_elements which returns the number of elements corresponding to the handle type given as template argument. Also added elements and all_elements methods returning ranges of the elements corresponding to the handle type given as template argument.</li>
|
||||
<li>Smart Handles: Most userfacing functions returning handles should now return smart handles instead. Smart handles know their corresponding mesh and give convenient access to mesh navigation methods.
|
||||
<li>Smart Ranges: OpenMesh ranges now provide a few methods that simplify a few calculations. See documentation for more details.
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -27,6 +36,7 @@
|
||||
<ul>
|
||||
<li>Change PropertyManager::operator* to access the property value for mesh properties</li>
|
||||
<li>PropertyManager: add hasProperty function</li>
|
||||
<li>PropertyManager rework: The behavior of the PropertyManager has been changed, hopefully making it more usable. See tutoial.
|
||||
</ul>
|
||||
|
||||
<b>IO</b>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,34 +11,33 @@ let %OpenMesh manage the data.
|
||||
It would be even more helpful if we could attach such properties
|
||||
dynamically to the mesh.
|
||||
|
||||
Custom properties can be conveniently created and attached to meshes with the following functions:
|
||||
- makeTemporaryProperty() creates a property that is temporary to the current scope.
|
||||
- getOrMakeProperty() is used for creating and accessing permanent named properties on a mesh.
|
||||
- getProperty() is used for accessing an existing permanent named property on a mesh.
|
||||
Custom properties can be conveniently created and attached to meshes by creating an object of type OpenMesh::PropertyManager. A PropertyManager manages the lifetime of the property and provides read / write access to its values.
|
||||
|
||||
All three functions take two template arguments:
|
||||
- First, the type of the mesh element that the property is attached to (i.e. OpenMesh::VertexHandle, OpenMesh::HalfedgeHandle, OpenMesh::EdgeHandle, or OpenMesh::FaceHandle). <em>Mesh properties</em> (i.e. singleton properties that are attached to an entire mesh instead of individual elements) are accessed by passing \c void instead of a handle type.
|
||||
- Second, the type of the property value that is attached to each element (e.g., \p int, \p double, etc.).
|
||||
You can use the typedefs VProp, HProp, EProp, FProp, and MProp in order to create a PropertyManager attached to vertices, halfedge, edges, faces and the mesh respectively. Each of these takes as template argument the type of the property value that is attached to each element (e.g., \p int, \p double, etc.).
|
||||
|
||||
We differentiate between two kinds of properties. <em>Named</em> and <em>temporary</em> properties. Temporary properties are created by just providing the constructor with a mesh on which the property should be created. These properties will be removed as soon as the PropertyManager goes out of scope. If in addition to the mesh a property name is provided, a named property will be created which will stay alive even after the PropertyManager goes out of scope. If a PropertyManager is given a name of an already existing property, it will provide read and write access to the same property.
|
||||
|
||||
Finally, an optional first parameter can be given containing a value that will be used to initialize the property for all elements if the property is freshly created (i.e. always for temporary properties, and only the first time a specific name is used).
|
||||
|
||||
All three functions return a handle object (of type OpenMesh::PropertyManager) that manages the lifetime of the property and provides read / write access to its values.
|
||||
|
||||
Here are a few examples of how to create and access mesh properties:
|
||||
|
||||
\code
|
||||
// Add a temporary mesh property that stores a double value for every vertex
|
||||
auto temperature = OpenMesh::getOrMakeProperty<OpenMesh::VertexHandle, double>(mesh, "temperature");
|
||||
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 mesh property that stores a 2D vector for every halfedge
|
||||
// (Beware: the next line might throw if the expected property doesn't exist.)
|
||||
auto uv = OpenMesh::getProperty<OpenMesh::HalfedgeHandle, OpenMesh::Vec2d>(mesh, "uv");
|
||||
// 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;
|
||||
|
||||
// Add a permanent mesh property containing a description string
|
||||
auto desc = OpenMesh::getOrMakeProperty<void, std::string>(mesh, "desc");
|
||||
// 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.";
|
||||
\endcode
|
||||
|
||||
@@ -50,7 +49,7 @@ In this example, we will store the \c cog value (see previous example) in a vert
|
||||
To do so, we first add a (temporary) property of the desired element type (OpenMesh::VertexHandle) and value type (\c %MyMesh::Point) to the mesh:
|
||||
|
||||
\dontinclude 03-properties/smooth.cc
|
||||
\skipline makeTemporaryProperty
|
||||
\skipline VProp
|
||||
|
||||
Enough memory is allocated to hold as many values of \c %MyMesh::Point as there are vertices.
|
||||
All insert and delete operations on the mesh are synchronized with the attached properties.
|
||||
@@ -75,30 +74,19 @@ Below is the complete source code:
|
||||
|
||||
## Property Lifetime
|
||||
|
||||
In the above example, we chose to use makeTemporaryProperty(). This causes the created property to automatically be removed from the mesh as soon as we leave the scope of the associated handle variable \c cog.
|
||||
In the above example, we chose to use VProp without a name. This causes the created property to automatically be removed from the mesh as soon as we leave the scope of the associated handle variable \c cog.
|
||||
|
||||
If, instead, a property is desired to survive its local scope, it should be created with using getOrMakeProperty(). In that case, the property must be given a name that can later be used to retrieve the property. For example:
|
||||
If, instead, a property is desired to survive its local scope, it should be created with a name. For example:
|
||||
|
||||
\code
|
||||
auto face_area = OpenMesh::makeTemporaryProperty<OpenMesh::FaceHandle, double>(mesh, "face_area");
|
||||
auto face_area = OpenMesh::FProp<double>(mesh, "face_area");
|
||||
\endcode
|
||||
|
||||
At a later time, we can use the getProperty() function to obtain a handle to a property that was previously created by refering to its name:
|
||||
At a later time, we can access the same property by using the same name. If we want to make sure, that we access a property that has already been created earler, we can use hasProperty() to test whether a mesh has the desired property:
|
||||
\code
|
||||
try {
|
||||
auto face_area = OpenMesh::getProperty<OpenMesh::FaceHandle, double>(mesh, "face_area");
|
||||
// Use the face_area property.
|
||||
}
|
||||
catch (const std::runtime_error& e) {
|
||||
// Property not found. Handle the error here.
|
||||
}
|
||||
\endcode
|
||||
|
||||
Using hasProperty(), we can test whether a mesh has a certain property:
|
||||
\code
|
||||
if (OpenMesh::hasProperty<OpenMesh::EdgeHandle, bool>(mesh, "is_valley")) {
|
||||
if (OpenMesh::hasProperty<OpenMesh::FaceHandle, double>(mesh, "face_area")) {
|
||||
// Property exists. Do something with it.
|
||||
auto valley = OpenMesh::getProperty<OpenMesh::EdgeHandle, bool>(mesh, "is_valley");
|
||||
auto valley = OpenMesh::FProp<bool>(mesh, "face_area");
|
||||
}
|
||||
else {
|
||||
// Property does not exist. Do something else.
|
||||
@@ -109,9 +97,9 @@ Using hasProperty(), we can test whether a mesh has a certain property:
|
||||
|
||||
## Low-Level Property API
|
||||
|
||||
The functions makeTemporaryProperty(), getOrMakeProperty(), and getProperty() are the convenient high-level interface for creating and accessing mesh properties.
|
||||
The property managers VProp, HProp, EProp, FProp and MProp are the convenient high-level interface for creating and accessing mesh properties.
|
||||
|
||||
Beneath these convenience functions, there is also a low-level property interface where handle and property lifetime must be managed manually. This interface is accessed through a mesh's add_property(), remove_property(), and property() functions and several property handle classes (OpenMesh::VPropHandleT, OpenMesh::HPropHandleT, OpenMesh::EPropHandleT, OpenMesh::FPropHandleT, OpenMesh::MPropHandleT).
|
||||
Beneath these convenience functions, there is also a low-level property interface where handle and property lifetime must be managed manually. This interface is accessed through a mesh's add_property(), get_property(), remove_property(), and property() functions and several property handle classes (OpenMesh::VPropHandleT, OpenMesh::HPropHandleT, OpenMesh::EPropHandleT, OpenMesh::FPropHandleT, OpenMesh::MPropHandleT).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/** \page tutorial_09 Using custom properties
|
||||
/** \page tutorial_09 Using custom properties (old style)
|
||||
|
||||
This small code example shows how to attach and access additional properties on a mesh.
|
||||
|
||||
<em>Note that this is an old style of using properties. Nowadays you should use the OpenMesh::PropertyManager instead.</em>
|
||||
|
||||
When you want to add an additional properties you have to attach it to a primitive of the
|
||||
mesh. You can attach to verticies, halfedges, edges, faces or to the mesh itself. Use the
|
||||
add_property function:
|
||||
|
||||
85
Doc/tutorial_11.docu
Normal file
85
Doc/tutorial_11.docu
Normal file
@@ -0,0 +1,85 @@
|
||||
/** \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
|
||||
|
||||
@@ -169,6 +169,11 @@ class VectorT {
|
||||
std::copy_n(it, DIM, values_.begin());
|
||||
}
|
||||
|
||||
/// construct from an array
|
||||
explicit VectorT(container&& _array) {
|
||||
values_ = _array;
|
||||
}
|
||||
|
||||
/// copy & cast constructor (explicit)
|
||||
template<typename otherScalarType,
|
||||
typename = typename std::enable_if<
|
||||
@@ -759,6 +764,21 @@ VectorT<Scalar, DIM>& minimize(VectorT<Scalar, DIM>& _v1, VectorT<Scalar, DIM>&
|
||||
return _v1.minimize(_v2);
|
||||
}
|
||||
|
||||
/// \relates OpenMesh::VectorT
|
||||
/// non-member max
|
||||
template<typename Scalar, int DIM>
|
||||
VectorT<Scalar, DIM> max(const VectorT<Scalar, DIM>& _v1, const VectorT<Scalar, DIM>& _v2) {
|
||||
return _v1.max(_v2);
|
||||
}
|
||||
|
||||
/// \relates OpenMesh::VectorT
|
||||
/// non-member min
|
||||
template<typename Scalar, int DIM>
|
||||
VectorT<Scalar, DIM> min(const VectorT<Scalar, DIM>& _v1, const VectorT<Scalar, DIM>& _v2) {
|
||||
return _v1.min(_v2);
|
||||
}
|
||||
|
||||
|
||||
//== TYPEDEFS =================================================================
|
||||
|
||||
/** 1-byte signed vector */
|
||||
|
||||
@@ -317,6 +317,22 @@ VectorT<Scalar, DIM>& minimize(VectorT<Scalar, DIM>& _v1, VectorT<Scalar, DIM>&
|
||||
}
|
||||
|
||||
|
||||
/// \relates OpenMesh::VectorT
|
||||
/// non-member max
|
||||
template<typename Scalar, int DIM>
|
||||
VectorT<Scalar, DIM> max(VectorT<Scalar, DIM>& _v1, VectorT<Scalar, DIM>& _v2) {
|
||||
return VectorT<Scalar, DIM>(_v1).maximize(_v2);
|
||||
}
|
||||
|
||||
|
||||
/// \relates OpenMesh::VectorT
|
||||
/// non-member min
|
||||
template<typename Scalar, int DIM>
|
||||
VectorT<Scalar, DIM> min(VectorT<Scalar, DIM>& _v1, VectorT<Scalar, DIM>& _v2) {
|
||||
return VectorT<Scalar, DIM>(_v1).minimize(_v2);
|
||||
}
|
||||
|
||||
|
||||
//== TYPEDEFS =================================================================
|
||||
|
||||
/** 1-byte signed vector */
|
||||
|
||||
@@ -110,6 +110,22 @@ public:
|
||||
typedef EPropHandleT<EdgeData> DataEPropHandle;
|
||||
typedef FPropHandleT<FaceData> DataFPropHandle;
|
||||
|
||||
typedef VPropHandleT<Point> PointsPropertyHandle;
|
||||
typedef VPropHandleT<Normal> VertexNormalsPropertyHandle;
|
||||
typedef VPropHandleT<Color> VertexColorsPropertyHandle;
|
||||
typedef VPropHandleT<TexCoord1D> VertexTexCoords1DPropertyHandle;
|
||||
typedef VPropHandleT<TexCoord2D> VertexTexCoords2DPropertyHandle;
|
||||
typedef VPropHandleT<TexCoord3D> VertexTexCoords3DPropertyHandle;
|
||||
typedef HPropHandleT<TexCoord1D> HalfedgeTexCoords1DPropertyHandle;
|
||||
typedef HPropHandleT<TexCoord2D> HalfedgeTexCoords2DPropertyHandle;
|
||||
typedef HPropHandleT<TexCoord3D> HalfedgeTexCoords3DPropertyHandle;
|
||||
typedef EPropHandleT<Color> EdgeColorsPropertyHandle;
|
||||
typedef HPropHandleT<Normal> HalfedgeNormalsPropertyHandle;
|
||||
typedef HPropHandleT<Color> HalfedgeColorsPropertyHandle;
|
||||
typedef FPropHandleT<Normal> FaceNormalsPropertyHandle;
|
||||
typedef FPropHandleT<Color> FaceColorsPropertyHandle;
|
||||
typedef FPropHandleT<TextureIndex> FaceTextureIndexPropertyHandle;
|
||||
|
||||
public:
|
||||
|
||||
//-------------------------------------------------- constructor / destructor
|
||||
@@ -240,6 +256,9 @@ public:
|
||||
void set_point(VertexHandle _vh, const Point& _p)
|
||||
{ this->property(points_, _vh) = _p; }
|
||||
|
||||
const PointsPropertyHandle& points_property_handle() const
|
||||
{ return points_; }
|
||||
|
||||
|
||||
//------------------------------------------------------------ vertex normals
|
||||
|
||||
@@ -592,24 +611,6 @@ public:
|
||||
bool has_face_colors() const { return face_colors_.is_valid(); }
|
||||
bool has_face_texture_index() const { return face_texture_index_.is_valid(); }
|
||||
|
||||
public:
|
||||
|
||||
typedef VPropHandleT<Point> PointsPropertyHandle;
|
||||
typedef VPropHandleT<Normal> VertexNormalsPropertyHandle;
|
||||
typedef VPropHandleT<Color> VertexColorsPropertyHandle;
|
||||
typedef VPropHandleT<TexCoord1D> VertexTexCoords1DPropertyHandle;
|
||||
typedef VPropHandleT<TexCoord2D> VertexTexCoords2DPropertyHandle;
|
||||
typedef VPropHandleT<TexCoord3D> VertexTexCoords3DPropertyHandle;
|
||||
typedef HPropHandleT<TexCoord1D> HalfedgeTexCoords1DPropertyHandle;
|
||||
typedef HPropHandleT<TexCoord2D> HalfedgeTexCoords2DPropertyHandle;
|
||||
typedef HPropHandleT<TexCoord3D> HalfedgeTexCoords3DPropertyHandle;
|
||||
typedef EPropHandleT<Color> EdgeColorsPropertyHandle;
|
||||
typedef HPropHandleT<Normal> HalfedgeNormalsPropertyHandle;
|
||||
typedef HPropHandleT<Color> HalfedgeColorsPropertyHandle;
|
||||
typedef FPropHandleT<Normal> FaceNormalsPropertyHandle;
|
||||
typedef FPropHandleT<Color> FaceColorsPropertyHandle;
|
||||
typedef FPropHandleT<TextureIndex> FaceTextureIndexPropertyHandle;
|
||||
|
||||
public:
|
||||
//standard vertex properties
|
||||
PointsPropertyHandle points_pph() const
|
||||
|
||||
@@ -516,7 +516,7 @@ public:
|
||||
// Copy all properties, if build in is true
|
||||
// Otherwise, copy only properties without build in specifier
|
||||
if ( *p_it && ( _copyBuildIn || (*p_it)->name().substr(0,2) != "v:" ) )
|
||||
(*p_it)->copy(_vh_from.idx(), _vh_to.idx());
|
||||
(*p_it)->copy(static_cast<size_t>(_vh_from.idx()), static_cast<size_t>(_vh_to.idx()));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -690,6 +690,9 @@ public: //----------------------------------------------------- element numbers
|
||||
virtual size_t n_edges() const { return 0; }
|
||||
virtual size_t n_faces() const { return 0; }
|
||||
|
||||
template <typename HandleT>
|
||||
size_t n_elements() const;
|
||||
|
||||
|
||||
protected: //------------------------------------------- synchronize properties
|
||||
|
||||
@@ -814,6 +817,16 @@ private:
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
inline size_t BaseKernel::n_elements<VertexHandle>() const { return n_vertices(); }
|
||||
template <>
|
||||
inline size_t BaseKernel::n_elements<HalfedgeHandle>() const { return n_halfedges(); }
|
||||
template <>
|
||||
inline size_t BaseKernel::n_elements<EdgeHandle>() const { return n_edges(); }
|
||||
template <>
|
||||
inline size_t BaseKernel::n_elements<FaceHandle>() const { return n_faces(); }
|
||||
|
||||
|
||||
//=============================================================================
|
||||
} // namespace OpenMesh
|
||||
//=============================================================================
|
||||
|
||||
@@ -40,9 +40,8 @@
|
||||
* ========================================================================= */
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef OPENMESH_CIRCULATORS_HH
|
||||
#define OPENMESH_CIRCULATORS_HH
|
||||
//=============================================================================
|
||||
//
|
||||
// Vertex and Face circulators for PolyMesh/TriMesh
|
||||
@@ -54,6 +53,7 @@
|
||||
//== INCLUDES =================================================================
|
||||
|
||||
#include <OpenMesh/Core/System/config.h>
|
||||
#include <OpenMesh/Core/Mesh/SmartHandles.hh>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
@@ -248,19 +248,25 @@ class GenericCirculatorBaseT {
|
||||
int lap_counter_;
|
||||
};
|
||||
|
||||
template<class Mesh, class CenterEntityHandle, class ValueHandle,
|
||||
ValueHandle (GenericCirculatorBaseT<Mesh>::*Handle2Value)() const, bool CW = true >
|
||||
class GenericCirculatorT : protected GenericCirculatorBaseT<Mesh> {
|
||||
//template<class Mesh, class CenterEntityHandle, class ValueHandle,
|
||||
// ValueHandle (GenericCirculatorBaseT<Mesh>::*Handle2Value)() const, bool CW = true >
|
||||
template <typename GenericCirculatorT_TraitsT, bool CW = true>
|
||||
class GenericCirculatorT : protected GenericCirculatorBaseT<typename GenericCirculatorT_TraitsT::Mesh> {
|
||||
public:
|
||||
using Mesh = typename GenericCirculatorT_TraitsT::Mesh;
|
||||
using value_type = typename GenericCirculatorT_TraitsT::ValueHandle;
|
||||
using CenterEntityHandle = typename GenericCirculatorT_TraitsT::CenterEntityHandle;
|
||||
|
||||
using smart_value_type = decltype(make_smart(std::declval<value_type>(), std::declval<Mesh>()));
|
||||
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef ValueHandle value_type;
|
||||
typedef const value_type& reference;
|
||||
typedef const value_type* pointer;
|
||||
typedef const smart_value_type* pointer;
|
||||
typedef std::bidirectional_iterator_tag iterator_category;
|
||||
|
||||
typedef typename GenericCirculatorBaseT<Mesh>::mesh_ptr mesh_ptr;
|
||||
typedef typename GenericCirculatorBaseT<Mesh>::mesh_ref mesh_ref;
|
||||
typedef GenericCirculator_ValueHandleFnsT<Mesh, CenterEntityHandle, ValueHandle, CW> GenericCirculator_ValueHandleFns;
|
||||
typedef GenericCirculator_ValueHandleFnsT<Mesh, CenterEntityHandle, value_type, CW> GenericCirculator_ValueHandleFns;
|
||||
|
||||
public:
|
||||
GenericCirculatorT() {}
|
||||
@@ -276,8 +282,8 @@ class GenericCirculatorT : protected GenericCirculatorBaseT<Mesh> {
|
||||
}
|
||||
GenericCirculatorT(const GenericCirculatorT &rhs) : GenericCirculatorBaseT<Mesh>(rhs) {}
|
||||
|
||||
friend class GenericCirculatorT<Mesh,CenterEntityHandle,ValueHandle,Handle2Value,!CW>;
|
||||
explicit GenericCirculatorT( const GenericCirculatorT<Mesh,CenterEntityHandle,ValueHandle,Handle2Value,!CW>& rhs )
|
||||
friend class GenericCirculatorT<GenericCirculatorT_TraitsT,!CW>;
|
||||
explicit GenericCirculatorT( const GenericCirculatorT<GenericCirculatorT_TraitsT,!CW>& rhs )
|
||||
:GenericCirculatorBaseT<Mesh>(rhs){}
|
||||
|
||||
GenericCirculatorT& operator++() {
|
||||
@@ -308,16 +314,16 @@ class GenericCirculatorT : protected GenericCirculatorBaseT<Mesh> {
|
||||
}
|
||||
|
||||
/// Standard dereferencing operator.
|
||||
value_type operator*() const {
|
||||
smart_value_type operator*() const {
|
||||
// We can't use this due to a GCC6 compiler bug
|
||||
const GenericCirculatorBaseT<Mesh>* self = this;
|
||||
#ifndef NDEBUG
|
||||
assert(this->heh_.is_valid());
|
||||
value_type res = (self->*Handle2Value)();
|
||||
value_type res = GenericCirculatorT_TraitsT::toHandle(this->mesh_, this->heh_);
|
||||
assert(res.is_valid());
|
||||
return res;
|
||||
return make_smart(res, this->mesh_);
|
||||
#else
|
||||
return (self->*Handle2Value)();
|
||||
return make_smart(GenericCirculatorT_TraitsT::toHandle(this->mesh_, this->heh_), this->mesh_);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -357,7 +363,7 @@ class GenericCirculatorT : protected GenericCirculatorBaseT<Mesh> {
|
||||
}
|
||||
|
||||
private:
|
||||
mutable value_type pointer_deref_value;
|
||||
mutable smart_value_type pointer_deref_value;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -420,19 +426,22 @@ class GenericCirculator_ValueHandleFnsT_DEPRECATED<Mesh, CenterEntityHandle, typ
|
||||
}
|
||||
};
|
||||
|
||||
template<class Mesh, class CenterEntityHandle, class ValueHandle,
|
||||
ValueHandle (GenericCirculatorBaseT<Mesh>::*Handle2Value)() const>
|
||||
class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
|
||||
template <typename GenericCirculatorT_DEPRECATED_TraitsT>
|
||||
class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<typename GenericCirculatorT_DEPRECATED_TraitsT::Mesh> {
|
||||
public:
|
||||
using Mesh = typename GenericCirculatorT_DEPRECATED_TraitsT::Mesh;
|
||||
using CenterEntityHandle = typename GenericCirculatorT_DEPRECATED_TraitsT::CenterEntityHandle;
|
||||
using value_type = typename GenericCirculatorT_DEPRECATED_TraitsT::ValueHandle;
|
||||
using smart_value_type = decltype (make_smart(std::declval<value_type>(), std::declval<Mesh>()));
|
||||
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef ValueHandle value_type;
|
||||
typedef const value_type& reference;
|
||||
typedef const value_type* pointer;
|
||||
typedef const smart_value_type* pointer;
|
||||
typedef std::bidirectional_iterator_tag iterator_category;
|
||||
|
||||
typedef typename GenericCirculatorBaseT<Mesh>::mesh_ptr mesh_ptr;
|
||||
typedef typename GenericCirculatorBaseT<Mesh>::mesh_ref mesh_ref;
|
||||
typedef GenericCirculator_ValueHandleFnsT_DEPRECATED<Mesh, CenterEntityHandle, ValueHandle> GenericCirculator_ValueHandleFns;
|
||||
typedef GenericCirculator_ValueHandleFnsT_DEPRECATED<Mesh, CenterEntityHandle, value_type> GenericCirculator_ValueHandleFns;
|
||||
|
||||
public:
|
||||
GenericCirculatorT_DEPRECATED() {}
|
||||
@@ -494,16 +503,16 @@ class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
|
||||
}
|
||||
|
||||
/// Standard dereferencing operator.
|
||||
value_type operator*() const {
|
||||
smart_value_type operator*() const {
|
||||
// We can't use this due to a GCC6 compiler bug
|
||||
const GenericCirculatorBaseT<Mesh>* self = this;
|
||||
#ifndef NDEBUG
|
||||
assert(this->heh_.is_valid());
|
||||
value_type res = (self->*Handle2Value)();
|
||||
value_type res = (GenericCirculatorT_DEPRECATED_TraitsT::toHandle(this->mesh_, this->heh_));
|
||||
assert(res.is_valid());
|
||||
return res;
|
||||
return make_smart(res, this->mesh_);
|
||||
#else
|
||||
return (self->*Handle2Value)();
|
||||
return make_smart(GenericCirculatorT_DEPRECATED_TraitsT::toHandle(this->mesh_, this->heh_), this->mesh_);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -563,7 +572,7 @@ class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
|
||||
* This function clutters your code. Use dereferencing operators -> and * instead.
|
||||
*/
|
||||
OM_DEPRECATED("This function clutters your code. Use dereferencing operators -> and * instead.")
|
||||
value_type handle() const {
|
||||
smart_value_type handle() const {
|
||||
return **this;
|
||||
}
|
||||
|
||||
@@ -584,10 +593,9 @@ class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
|
||||
}
|
||||
|
||||
private:
|
||||
mutable value_type pointer_deref_value;
|
||||
mutable smart_value_type pointer_deref_value;
|
||||
};
|
||||
|
||||
} // namespace Iterators
|
||||
} // namespace OpenMesh
|
||||
|
||||
#endif
|
||||
|
||||
@@ -144,6 +144,15 @@ struct FaceHandle : public BaseHandle
|
||||
};
|
||||
|
||||
|
||||
/// Handle type for meshes to simplify some template programming
|
||||
struct MeshHandle : public BaseHandle
|
||||
{
|
||||
explicit MeshHandle(int _idx=-1) : BaseHandle(_idx) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
} // namespace OpenMesh
|
||||
//=============================================================================
|
||||
|
||||
@@ -40,9 +40,7 @@
|
||||
* ========================================================================= */
|
||||
|
||||
|
||||
|
||||
#ifndef OPENMESH_ITERATORS_HH
|
||||
#define OPENMESH_ITERATORS_HH
|
||||
#pragma once
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
@@ -56,6 +54,7 @@
|
||||
|
||||
#include <OpenMesh/Core/System/config.h>
|
||||
#include <OpenMesh/Core/Mesh/Status.hh>
|
||||
#include <OpenMesh/Core/Mesh/SmartHandles.hh>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
@@ -89,19 +88,20 @@ class GenericIteratorT {
|
||||
typedef value_handle value_type;
|
||||
typedef std::bidirectional_iterator_tag iterator_category;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef const value_type& reference;
|
||||
typedef const value_type* pointer;
|
||||
typedef const Mesh* mesh_ptr;
|
||||
typedef const Mesh& mesh_ref;
|
||||
typedef decltype(make_smart(std::declval<ValueHandle>(), std::declval<Mesh>())) SmartHandle;
|
||||
typedef const SmartHandle& reference;
|
||||
typedef const SmartHandle* pointer;
|
||||
|
||||
/// Default constructor.
|
||||
GenericIteratorT()
|
||||
: mesh_(0), skip_bits_(0)
|
||||
: hnd_(make_smart(ValueHandle(),nullptr)), skip_bits_(0)
|
||||
{}
|
||||
|
||||
/// Construct with mesh and a target handle.
|
||||
GenericIteratorT(mesh_ref _mesh, value_handle _hnd, bool _skip=false)
|
||||
: mesh_(&_mesh), hnd_(_hnd), skip_bits_(0)
|
||||
: hnd_(make_smart(_hnd, _mesh)), skip_bits_(0)
|
||||
{
|
||||
if (_skip) enable_skipping();
|
||||
}
|
||||
@@ -139,7 +139,7 @@ class GenericIteratorT {
|
||||
|
||||
/// Are two iterators equal? Only valid if they refer to the same mesh!
|
||||
bool operator==(const GenericIteratorT& _rhs) const {
|
||||
return ((mesh_ == _rhs.mesh_) && (hnd_ == _rhs.hnd_));
|
||||
return ((hnd_.mesh() == _rhs.hnd_.mesh()) && (hnd_ == _rhs.hnd_));
|
||||
}
|
||||
|
||||
/// Not equal?
|
||||
@@ -210,7 +210,7 @@ class GenericIteratorT {
|
||||
|
||||
/// Turn on skipping: automatically skip deleted/hidden elements
|
||||
void enable_skipping() {
|
||||
if (mesh_ && (mesh_->*PrimitiveStatusMember)()) {
|
||||
if (hnd_.mesh() && (hnd_.mesh()->*PrimitiveStatusMember)()) {
|
||||
Attributes::StatusInfo status;
|
||||
status.set_deleted(true);
|
||||
status.set_hidden(true);
|
||||
@@ -228,21 +228,20 @@ class GenericIteratorT {
|
||||
private:
|
||||
|
||||
void skip_fwd() {
|
||||
assert(mesh_ && skip_bits_);
|
||||
while ((hnd_.idx() < (signed) (mesh_->*PrimitiveCountMember)())
|
||||
&& (mesh_->status(hnd_).bits() & skip_bits_))
|
||||
assert(hnd_.mesh() && skip_bits_);
|
||||
while ((hnd_.idx() < (signed) (hnd_.mesh()->*PrimitiveCountMember)())
|
||||
&& (hnd_.mesh()->status(hnd_).bits() & skip_bits_))
|
||||
hnd_.__increment();
|
||||
}
|
||||
|
||||
void skip_bwd() {
|
||||
assert(mesh_ && skip_bits_);
|
||||
while ((hnd_.idx() >= 0) && (mesh_->status(hnd_).bits() & skip_bits_))
|
||||
assert(hnd_.mesh() && skip_bits_);
|
||||
while ((hnd_.idx() >= 0) && (hnd_.mesh()->status(hnd_).bits() & skip_bits_))
|
||||
hnd_.__decrement();
|
||||
}
|
||||
|
||||
protected:
|
||||
mesh_ptr mesh_;
|
||||
value_handle hnd_;
|
||||
SmartHandle hnd_;
|
||||
unsigned int skip_bits_;
|
||||
};
|
||||
|
||||
@@ -250,5 +249,3 @@ class GenericIteratorT {
|
||||
} // namespace Iterators
|
||||
} // namespace OpenMesh
|
||||
//=============================================================================
|
||||
#endif
|
||||
//=============================================================================
|
||||
|
||||
@@ -113,7 +113,7 @@ void PolyConnectivity::adjust_outgoing_halfedge(VertexHandle _vh)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
PolyConnectivity::FaceHandle
|
||||
SmartFaceHandle
|
||||
PolyConnectivity::add_face(const VertexHandle* _vertex_handles, size_t _vhs_size)
|
||||
{
|
||||
VertexHandle vh;
|
||||
@@ -142,7 +142,7 @@ PolyConnectivity::add_face(const VertexHandle* _vertex_handles, size_t _vhs_size
|
||||
if ( !is_boundary(_vertex_handles[i]) )
|
||||
{
|
||||
omerr() << "PolyMeshT::add_face: complex vertex\n";
|
||||
return InvalidFaceHandle;
|
||||
return make_smart(InvalidFaceHandle, this);
|
||||
}
|
||||
|
||||
// Initialise edge attributes
|
||||
@@ -154,7 +154,7 @@ PolyConnectivity::add_face(const VertexHandle* _vertex_handles, size_t _vhs_size
|
||||
if (!edgeData_[i].is_new && !is_boundary(edgeData_[i].halfedge_handle))
|
||||
{
|
||||
omerr() << "PolyMeshT::add_face: complex edge\n";
|
||||
return InvalidFaceHandle;
|
||||
return make_smart(InvalidFaceHandle, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ PolyConnectivity::add_face(const VertexHandle* _vertex_handles, size_t _vhs_size
|
||||
if (boundary_prev == inner_prev)
|
||||
{
|
||||
omerr() << "PolyMeshT::add_face: patch re-linking failed\n";
|
||||
return InvalidFaceHandle;
|
||||
return make_smart(InvalidFaceHandle, this);
|
||||
}
|
||||
|
||||
assert(is_boundary(boundary_prev));
|
||||
@@ -297,12 +297,12 @@ PolyConnectivity::add_face(const VertexHandle* _vertex_handles, size_t _vhs_size
|
||||
if (edgeData_[i].needs_adjust)
|
||||
adjust_outgoing_halfedge(_vertex_handles[i]);
|
||||
|
||||
return fh;
|
||||
return make_smart(fh, this);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FaceHandle PolyConnectivity::add_face(VertexHandle _vh0, VertexHandle _vh1, VertexHandle _vh2, VertexHandle _vh3)
|
||||
SmartFaceHandle PolyConnectivity::add_face(VertexHandle _vh0, VertexHandle _vh1, VertexHandle _vh2, VertexHandle _vh3)
|
||||
{
|
||||
VertexHandle vhs[4] = { _vh0, _vh1, _vh2, _vh3 };
|
||||
return add_face(vhs, 4);
|
||||
@@ -310,7 +310,7 @@ FaceHandle PolyConnectivity::add_face(VertexHandle _vh0, VertexHandle _vh1, Vert
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FaceHandle PolyConnectivity::add_face(VertexHandle _vh0, VertexHandle _vh1, VertexHandle _vh2)
|
||||
SmartFaceHandle PolyConnectivity::add_face(VertexHandle _vh0, VertexHandle _vh1, VertexHandle _vh2)
|
||||
{
|
||||
VertexHandle vhs[3] = { _vh0, _vh1, _vh2 };
|
||||
return add_face(vhs, 3);
|
||||
@@ -318,9 +318,17 @@ FaceHandle PolyConnectivity::add_face(VertexHandle _vh0, VertexHandle _vh1, Vert
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FaceHandle PolyConnectivity::add_face(const std::vector<VertexHandle>& _vhandles)
|
||||
SmartFaceHandle PolyConnectivity::add_face(const std::vector<VertexHandle>& _vhandles)
|
||||
{ return add_face(&_vhandles.front(), _vhandles.size()); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SmartFaceHandle PolyConnectivity::add_face(const std::vector<SmartVertexHandle>& _vhandles)
|
||||
{
|
||||
std::vector<VertexHandle> vhandles(_vhandles.begin(), _vhandles.end());
|
||||
return add_face(&vhandles.front(), vhandles.size());
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PolyConnectivity::is_collapse_ok(HalfedgeHandle v0v1)
|
||||
@@ -623,101 +631,6 @@ void PolyConnectivity::delete_face(FaceHandle _fh, bool _delete_isolated_vertice
|
||||
adjust_outgoing_halfedge(*v_it);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::VertexIter PolyConnectivity::vertices_begin()
|
||||
{
|
||||
return VertexIter(*this, VertexHandle(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::ConstVertexIter PolyConnectivity::vertices_begin() const
|
||||
{
|
||||
return ConstVertexIter(*this, VertexHandle(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::VertexIter PolyConnectivity::vertices_end()
|
||||
{
|
||||
return VertexIter(*this, VertexHandle( int(n_vertices() ) ));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::ConstVertexIter PolyConnectivity::vertices_end() const
|
||||
{
|
||||
return ConstVertexIter(*this, VertexHandle( int(n_vertices()) ));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::HalfedgeIter PolyConnectivity::halfedges_begin()
|
||||
{
|
||||
return HalfedgeIter(*this, HalfedgeHandle(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::ConstHalfedgeIter PolyConnectivity::halfedges_begin() const
|
||||
{
|
||||
return ConstHalfedgeIter(*this, HalfedgeHandle(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::HalfedgeIter PolyConnectivity::halfedges_end()
|
||||
{
|
||||
return HalfedgeIter(*this, HalfedgeHandle(int(n_halfedges())));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::ConstHalfedgeIter PolyConnectivity::halfedges_end() const
|
||||
{
|
||||
return ConstHalfedgeIter(*this, HalfedgeHandle(int(n_halfedges())));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::EdgeIter PolyConnectivity::edges_begin()
|
||||
{
|
||||
return EdgeIter(*this, EdgeHandle(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::ConstEdgeIter PolyConnectivity::edges_begin() const
|
||||
{
|
||||
return ConstEdgeIter(*this, EdgeHandle(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::EdgeIter PolyConnectivity::edges_end()
|
||||
{
|
||||
return EdgeIter(*this, EdgeHandle(int(n_edges())));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::ConstEdgeIter PolyConnectivity::edges_end() const
|
||||
{
|
||||
return ConstEdgeIter(*this, EdgeHandle(int(n_edges())));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::FaceIter PolyConnectivity::faces_begin()
|
||||
{
|
||||
return FaceIter(*this, FaceHandle(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::ConstFaceIter PolyConnectivity::faces_begin() const
|
||||
{
|
||||
return ConstFaceIter(*this, FaceHandle(0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::FaceIter PolyConnectivity::faces_end()
|
||||
{
|
||||
return FaceIter(*this, FaceHandle(int(n_faces())));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PolyConnectivity::ConstFaceIter PolyConnectivity::faces_end() const
|
||||
{
|
||||
return ConstFaceIter(*this, FaceHandle(int(n_faces())));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PolyConnectivity::collapse(HalfedgeHandle _hh)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
777
src/OpenMesh/Core/Mesh/PolyConnectivity_inline_impl.hh
Normal file
777
src/OpenMesh/Core/Mesh/PolyConnectivity_inline_impl.hh
Normal file
@@ -0,0 +1,777 @@
|
||||
/* ========================================================================= *
|
||||
* *
|
||||
* OpenMesh *
|
||||
* Copyright (c) 2001-2015, RWTH-Aachen University *
|
||||
* Department of Computer Graphics and Multimedia *
|
||||
* All rights reserved. *
|
||||
* www.openmesh.org *
|
||||
* *
|
||||
*---------------------------------------------------------------------------*
|
||||
* This file is part of OpenMesh. *
|
||||
*---------------------------------------------------------------------------*
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or without *
|
||||
* modification, are permitted provided that the following conditions *
|
||||
* are met: *
|
||||
* *
|
||||
* 1. Redistributions of source code must retain the above copyright notice, *
|
||||
* this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* 2. Redistributions in binary form must reproduce the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer in the *
|
||||
* documentation and/or other materials provided with the distribution. *
|
||||
* *
|
||||
* 3. Neither the name of the copyright holder nor the names of its *
|
||||
* contributors may be used to endorse or promote products derived from *
|
||||
* this software without specific prior written permission. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
|
||||
* *
|
||||
* ========================================================================= */
|
||||
|
||||
#ifndef OPENMESH_POLYCONNECTIVITY_INLINE_IMPL_HH
|
||||
#define OPENMESH_POLYCONNECTIVITY_INLINE_IMPL_HH
|
||||
|
||||
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
|
||||
#include <OpenMesh/Core/Mesh/IteratorsT.hh>
|
||||
#include <OpenMesh/Core/Mesh/CirculatorsT.hh>
|
||||
#include <OpenMesh/Core/Mesh/SmartHandles.hh>
|
||||
|
||||
namespace OpenMesh {
|
||||
|
||||
|
||||
inline SmartVertexHandle PolyConnectivity::add_vertex() { return make_smart(new_vertex(), *this); }
|
||||
|
||||
inline SmartHalfedgeHandle PolyConnectivity::next_halfedge_handle(SmartHalfedgeHandle _heh) const { return make_smart(next_halfedge_handle(HalfedgeHandle(_heh)), *this); }
|
||||
inline SmartHalfedgeHandle PolyConnectivity::prev_halfedge_handle(SmartHalfedgeHandle _heh) const { return make_smart(prev_halfedge_handle(HalfedgeHandle(_heh)), *this); }
|
||||
inline SmartHalfedgeHandle PolyConnectivity::opposite_halfedge_handle(SmartHalfedgeHandle _heh) const { return make_smart(opposite_halfedge_handle(HalfedgeHandle(_heh)), *this); }
|
||||
inline SmartHalfedgeHandle PolyConnectivity::ccw_rotated_halfedge_handle(SmartHalfedgeHandle _heh) const { return make_smart(ccw_rotated_halfedge_handle(HalfedgeHandle(_heh)), *this); }
|
||||
inline SmartHalfedgeHandle PolyConnectivity::cw_rotated_halfedge_handle(SmartHalfedgeHandle _heh) const { return make_smart(cw_rotated_halfedge_handle(HalfedgeHandle(_heh)), *this); }
|
||||
|
||||
inline SmartHalfedgeHandle PolyConnectivity::s_halfedge_handle(SmartEdgeHandle _eh, unsigned int _i) { return make_smart(ArrayKernel::s_halfedge_handle(EdgeHandle(_eh), _i), _eh.mesh()); }
|
||||
inline SmartEdgeHandle PolyConnectivity::s_edge_handle(SmartHalfedgeHandle _heh) { return make_smart(ArrayKernel::s_edge_handle(HalfedgeHandle(_heh)), _heh.mesh()); }
|
||||
|
||||
inline SmartHalfedgeHandle PolyConnectivity::halfedge_handle(SmartEdgeHandle _eh, unsigned int _i) const { return make_smart(halfedge_handle(EdgeHandle(_eh), _i), *this); }
|
||||
inline SmartEdgeHandle PolyConnectivity::edge_handle(SmartHalfedgeHandle _heh) const { return make_smart(edge_handle(HalfedgeHandle(_heh)), *this); }
|
||||
inline SmartHalfedgeHandle PolyConnectivity::halfedge_handle(SmartFaceHandle _fh) const { return make_smart(halfedge_handle(FaceHandle(_fh)), *this); }
|
||||
inline SmartHalfedgeHandle PolyConnectivity::halfedge_handle(SmartVertexHandle _vh) const { return make_smart(halfedge_handle(VertexHandle(_vh)), *this); }
|
||||
|
||||
inline SmartFaceHandle PolyConnectivity::face_handle(SmartHalfedgeHandle _heh) const { return make_smart(face_handle(HalfedgeHandle(_heh)), *this); }
|
||||
|
||||
|
||||
|
||||
/// Generic class for vertex/halfedge/edge/face ranges.
|
||||
template <typename RangeTraitT>
|
||||
class EntityRange : public SmartRangeT<EntityRange<RangeTraitT>, typename RangeTraitT::ITER_TYPE::SmartHandle> {
|
||||
public:
|
||||
typedef typename RangeTraitT::ITER_TYPE iterator;
|
||||
typedef typename RangeTraitT::ITER_TYPE const_iterator;
|
||||
|
||||
explicit EntityRange(typename RangeTraitT::CONTAINER_TYPE &container) : container_(container) {}
|
||||
typename RangeTraitT::ITER_TYPE begin() const { return RangeTraitT::begin(container_); }
|
||||
typename RangeTraitT::ITER_TYPE end() const { return RangeTraitT::end(container_); }
|
||||
|
||||
private:
|
||||
typename RangeTraitT::CONTAINER_TYPE &container_;
|
||||
};
|
||||
|
||||
/// Generic class for iterator ranges.
|
||||
template <typename CirculatorRangeTraitT>
|
||||
//class CirculatorRange : public SmartRangeT<CirculatorRange<CirculatorRangeTraitT>, decltype (make_smart(std::declval<typename CirculatorRangeTraitT::TO_ENTITYE_TYPE>(), std::declval<PolyConnectivity>()))>{
|
||||
class CirculatorRange : public SmartRangeT<CirculatorRange<CirculatorRangeTraitT>, typename SmartHandle<typename CirculatorRangeTraitT::TO_ENTITYE_TYPE>::type>{
|
||||
public:
|
||||
typedef typename CirculatorRangeTraitT::ITER_TYPE ITER_TYPE;
|
||||
typedef typename CirculatorRangeTraitT::CENTER_ENTITY_TYPE CENTER_ENTITY_TYPE;
|
||||
typedef typename CirculatorRangeTraitT::CONTAINER_TYPE CONTAINER_TYPE;
|
||||
typedef ITER_TYPE iterator;
|
||||
typedef ITER_TYPE const_iterator;
|
||||
|
||||
CirculatorRange(
|
||||
const CONTAINER_TYPE &container,
|
||||
CENTER_ENTITY_TYPE center) :
|
||||
container_(container), center_(center) {}
|
||||
ITER_TYPE begin() const { return CirculatorRangeTraitT::begin(container_, center_); }
|
||||
ITER_TYPE end() const { return CirculatorRangeTraitT::end(container_, center_); }
|
||||
|
||||
private:
|
||||
const CONTAINER_TYPE &container_;
|
||||
CENTER_ENTITY_TYPE center_;
|
||||
};
|
||||
|
||||
|
||||
inline PolyConnectivity::ConstVertexRangeSkipping PolyConnectivity::vertices() const { return ConstVertexRangeSkipping(*this); }
|
||||
inline PolyConnectivity::ConstVertexRange PolyConnectivity::all_vertices() const { return ConstVertexRange(*this); }
|
||||
inline PolyConnectivity::ConstHalfedgeRangeSkipping PolyConnectivity::halfedges() const { return ConstHalfedgeRangeSkipping(*this); }
|
||||
inline PolyConnectivity::ConstHalfedgeRange PolyConnectivity::all_halfedges() const { return ConstHalfedgeRange(*this); }
|
||||
inline PolyConnectivity::ConstEdgeRangeSkipping PolyConnectivity::edges() const { return ConstEdgeRangeSkipping(*this); }
|
||||
inline PolyConnectivity::ConstEdgeRange PolyConnectivity::all_edges() const { return ConstEdgeRange(*this); }
|
||||
inline PolyConnectivity::ConstFaceRangeSkipping PolyConnectivity::faces() const { return ConstFaceRangeSkipping(*this); }
|
||||
inline PolyConnectivity::ConstFaceRange PolyConnectivity::all_faces() const { return ConstFaceRange(*this); }
|
||||
|
||||
template <> inline PolyConnectivity::ConstVertexRangeSkipping PolyConnectivity::elements<VertexHandle>() const { return vertices(); }
|
||||
template <> inline PolyConnectivity::ConstVertexRange PolyConnectivity::all_elements<VertexHandle>() const { return all_vertices(); }
|
||||
template <> inline PolyConnectivity::ConstHalfedgeRangeSkipping PolyConnectivity::elements<HalfedgeHandle>() const { return halfedges(); }
|
||||
template <> inline PolyConnectivity::ConstHalfedgeRange PolyConnectivity::all_elements<HalfedgeHandle>() const { return all_halfedges(); }
|
||||
template <> inline PolyConnectivity::ConstEdgeRangeSkipping PolyConnectivity::elements<EdgeHandle>() const { return edges(); }
|
||||
template <> inline PolyConnectivity::ConstEdgeRange PolyConnectivity::all_elements<EdgeHandle>() const { return all_edges(); }
|
||||
template <> inline PolyConnectivity::ConstFaceRangeSkipping PolyConnectivity::elements<FaceHandle>() const { return faces(); }
|
||||
template <> inline PolyConnectivity::ConstFaceRange PolyConnectivity::all_elements<FaceHandle>() const { return all_faces(); }
|
||||
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexRange PolyConnectivity::vv_range(VertexHandle _vh) const {
|
||||
return ConstVertexVertexRange(*this, _vh);
|
||||
}
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeRange PolyConnectivity::vih_range(VertexHandle _vh) const {
|
||||
return ConstVertexIHalfedgeRange(*this, _vh);
|
||||
}
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeRange PolyConnectivity::voh_range(VertexHandle _vh) const {
|
||||
return ConstVertexOHalfedgeRange(*this, _vh);
|
||||
}
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeRange PolyConnectivity::ve_range(VertexHandle _vh) const {
|
||||
return ConstVertexEdgeRange(*this, _vh);
|
||||
}
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceRange PolyConnectivity::vf_range(VertexHandle _vh) const {
|
||||
return ConstVertexFaceRange(*this, _vh);
|
||||
}
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexRange PolyConnectivity::fv_range(FaceHandle _fh) const {
|
||||
return ConstFaceVertexRange(*this, _fh);
|
||||
}
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeRange PolyConnectivity::fh_range(FaceHandle _fh) const {
|
||||
return ConstFaceHalfedgeRange(*this, _fh);
|
||||
}
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeRange PolyConnectivity::fe_range(FaceHandle _fh) const {
|
||||
return ConstFaceEdgeRange(*this, _fh);
|
||||
}
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceRange PolyConnectivity::ff_range(FaceHandle _fh) const {
|
||||
return ConstFaceFaceRange(*this, _fh);
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline PolyConnectivity::VertexIter PolyConnectivity::vertices_begin()
|
||||
{ return VertexIter(*this, VertexHandle(0)); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIter PolyConnectivity::vertices_begin() const
|
||||
{ return ConstVertexIter(*this, VertexHandle(0)); }
|
||||
|
||||
inline PolyConnectivity::VertexIter PolyConnectivity::vertices_end()
|
||||
{ return VertexIter(*this, VertexHandle( int(n_vertices() ) )); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIter PolyConnectivity::vertices_end() const
|
||||
{ return ConstVertexIter(*this, VertexHandle( int(n_vertices()) )); }
|
||||
|
||||
inline PolyConnectivity::HalfedgeIter PolyConnectivity::halfedges_begin()
|
||||
{ return HalfedgeIter(*this, HalfedgeHandle(0)); }
|
||||
|
||||
inline PolyConnectivity::ConstHalfedgeIter PolyConnectivity::halfedges_begin() const
|
||||
{ return ConstHalfedgeIter(*this, HalfedgeHandle(0)); }
|
||||
|
||||
inline PolyConnectivity::HalfedgeIter PolyConnectivity::halfedges_end()
|
||||
{ return HalfedgeIter(*this, HalfedgeHandle(int(n_halfedges()))); }
|
||||
|
||||
inline PolyConnectivity::ConstHalfedgeIter PolyConnectivity::halfedges_end() const
|
||||
{ return ConstHalfedgeIter(*this, HalfedgeHandle(int(n_halfedges()))); }
|
||||
|
||||
inline PolyConnectivity::EdgeIter PolyConnectivity::edges_begin()
|
||||
{ return EdgeIter(*this, EdgeHandle(0)); }
|
||||
|
||||
inline PolyConnectivity::ConstEdgeIter PolyConnectivity::edges_begin() const
|
||||
{ return ConstEdgeIter(*this, EdgeHandle(0)); }
|
||||
|
||||
inline PolyConnectivity::EdgeIter PolyConnectivity::edges_end()
|
||||
{ return EdgeIter(*this, EdgeHandle(int(n_edges()))); }
|
||||
|
||||
inline PolyConnectivity::ConstEdgeIter PolyConnectivity::edges_end() const
|
||||
{ return ConstEdgeIter(*this, EdgeHandle(int(n_edges()))); }
|
||||
|
||||
inline PolyConnectivity::FaceIter PolyConnectivity::faces_begin()
|
||||
{ return FaceIter(*this, FaceHandle(0)); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceIter PolyConnectivity::faces_begin() const
|
||||
{ return ConstFaceIter(*this, FaceHandle(0)); }
|
||||
|
||||
inline PolyConnectivity::FaceIter PolyConnectivity::faces_end()
|
||||
{ return FaceIter(*this, FaceHandle(int(n_faces()))); }
|
||||
|
||||
|
||||
inline PolyConnectivity::ConstFaceIter PolyConnectivity::faces_end() const
|
||||
{ return ConstFaceIter(*this, FaceHandle(int(n_faces()))); }
|
||||
|
||||
inline PolyConnectivity::VertexIter PolyConnectivity::vertices_sbegin()
|
||||
{ return VertexIter(*this, VertexHandle(0), true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIter PolyConnectivity::vertices_sbegin() const
|
||||
{ return ConstVertexIter(*this, VertexHandle(0), true); }
|
||||
|
||||
inline PolyConnectivity::HalfedgeIter PolyConnectivity::halfedges_sbegin()
|
||||
{ return HalfedgeIter(*this, HalfedgeHandle(0), true); }
|
||||
|
||||
inline PolyConnectivity::ConstHalfedgeIter PolyConnectivity::halfedges_sbegin() const
|
||||
{ return ConstHalfedgeIter(*this, HalfedgeHandle(0), true); }
|
||||
|
||||
inline PolyConnectivity::EdgeIter PolyConnectivity::edges_sbegin()
|
||||
{ return EdgeIter(*this, EdgeHandle(0), true); }
|
||||
|
||||
inline PolyConnectivity::ConstEdgeIter PolyConnectivity::edges_sbegin() const
|
||||
{ return ConstEdgeIter(*this, EdgeHandle(0), true); }
|
||||
|
||||
inline PolyConnectivity::FaceIter PolyConnectivity::faces_sbegin()
|
||||
{ return FaceIter(*this, FaceHandle(0), true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceIter PolyConnectivity::faces_sbegin() const
|
||||
{ return ConstFaceIter(*this, FaceHandle(0), true); }
|
||||
|
||||
inline PolyConnectivity::VertexVertexIter PolyConnectivity::vv_iter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexVertexIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexVertexCWIter PolyConnectivity::vv_cwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexVertexCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexVertexCCWIter PolyConnectivity::vv_ccwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexVertexCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexIHalfedgeIter PolyConnectivity::vih_iter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexIHalfedgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexIHalfedgeCWIter PolyConnectivity::vih_cwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexIHalfedgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexIHalfedgeCCWIter PolyConnectivity::vih_ccwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexIHalfedgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexOHalfedgeIter PolyConnectivity::voh_iter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexOHalfedgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexOHalfedgeCWIter PolyConnectivity::voh_cwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexOHalfedgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexOHalfedgeCCWIter PolyConnectivity::voh_ccwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexOHalfedgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexEdgeIter PolyConnectivity::ve_iter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexEdgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexEdgeCWIter PolyConnectivity::ve_cwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexEdgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexEdgeCCWIter PolyConnectivity::ve_ccwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexEdgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexFaceIter PolyConnectivity::vf_iter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexFaceIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexFaceCWIter PolyConnectivity::vf_cwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexFaceCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexFaceCCWIter PolyConnectivity::vf_ccwiter(ArrayKernel::VertexHandle _vh)
|
||||
{ return VertexFaceCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexIter PolyConnectivity::cvv_iter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexVertexIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexCWIter PolyConnectivity::cvv_cwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexVertexCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexCCWIter PolyConnectivity::cvv_ccwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexVertexCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeIter PolyConnectivity::cvih_iter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexIHalfedgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeCWIter PolyConnectivity::cvih_cwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexIHalfedgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeCCWIter PolyConnectivity::cvih_ccwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexIHalfedgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeIter PolyConnectivity::cvoh_iter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexOHalfedgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeCWIter PolyConnectivity::cvoh_cwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexOHalfedgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeCCWIter PolyConnectivity::cvoh_ccwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexOHalfedgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeIter PolyConnectivity::cve_iter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexEdgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeCWIter PolyConnectivity::cve_cwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexEdgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeCCWIter PolyConnectivity::cve_ccwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexEdgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceIter PolyConnectivity::cvf_iter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexFaceIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceCWIter PolyConnectivity::cvf_cwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexFaceCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceCCWIter PolyConnectivity::cvf_ccwiter(ArrayKernel::VertexHandle _vh) const
|
||||
{ return ConstVertexFaceCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::FaceVertexIter PolyConnectivity::fv_iter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceVertexIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceVertexCWIter PolyConnectivity::fv_cwiter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceVertexCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceVertexCCWIter PolyConnectivity::fv_ccwiter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceVertexCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceHalfedgeIter PolyConnectivity::fh_iter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceHalfedgeIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceHalfedgeCWIter PolyConnectivity::fh_cwiter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceHalfedgeCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceHalfedgeCCWIter PolyConnectivity::fh_ccwiter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceHalfedgeCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceEdgeIter PolyConnectivity::fe_iter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceEdgeIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceEdgeCWIter PolyConnectivity::fe_cwiter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceEdgeCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceEdgeCCWIter PolyConnectivity::fe_ccwiter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceEdgeCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceFaceIter PolyConnectivity::ff_iter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceFaceIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceFaceCWIter PolyConnectivity::ff_cwiter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceFaceCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceFaceCCWIter PolyConnectivity::ff_ccwiter(ArrayKernel::FaceHandle _fh)
|
||||
{ return FaceFaceCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexIter PolyConnectivity::cfv_iter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceVertexIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexCWIter PolyConnectivity::cfv_cwiter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceVertexCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexCCWIter PolyConnectivity::cfv_ccwiter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceVertexCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeIter PolyConnectivity::cfh_iter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceHalfedgeIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeCWIter PolyConnectivity::cfh_cwiter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceHalfedgeCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeCCWIter PolyConnectivity::cfh_ccwiter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceHalfedgeCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeIter PolyConnectivity::cfe_iter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceEdgeIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeCWIter PolyConnectivity::cfe_cwiter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceEdgeCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeCCWIter PolyConnectivity::cfe_ccwiter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceEdgeCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceIter PolyConnectivity::cff_iter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceFaceIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceCWIter PolyConnectivity::cff_cwiter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceFaceCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceCCWIter PolyConnectivity::cff_ccwiter(ArrayKernel::FaceHandle _fh) const
|
||||
{ return ConstFaceFaceCCWIter(*this, _fh); }
|
||||
|
||||
|
||||
inline PolyConnectivity::VertexVertexIter PolyConnectivity::vv_begin(VertexHandle _vh)
|
||||
{ return VertexVertexIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexVertexCWIter PolyConnectivity::vv_cwbegin(VertexHandle _vh)
|
||||
{ return VertexVertexCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexVertexCCWIter PolyConnectivity::vv_ccwbegin(VertexHandle _vh)
|
||||
{ return VertexVertexCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexIHalfedgeIter PolyConnectivity::vih_begin(VertexHandle _vh)
|
||||
{ return VertexIHalfedgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexIHalfedgeCWIter PolyConnectivity::vih_cwbegin(VertexHandle _vh)
|
||||
{ return VertexIHalfedgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexIHalfedgeCCWIter PolyConnectivity::vih_ccwbegin(VertexHandle _vh)
|
||||
{ return VertexIHalfedgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexOHalfedgeIter PolyConnectivity::voh_begin(VertexHandle _vh)
|
||||
{ return VertexOHalfedgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexOHalfedgeCWIter PolyConnectivity::voh_cwbegin(VertexHandle _vh)
|
||||
{ return VertexOHalfedgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexOHalfedgeCCWIter PolyConnectivity::voh_ccwbegin(VertexHandle _vh)
|
||||
{ return VertexOHalfedgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexEdgeIter PolyConnectivity::ve_begin(VertexHandle _vh)
|
||||
{ return VertexEdgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexEdgeCWIter PolyConnectivity::ve_cwbegin(VertexHandle _vh)
|
||||
{ return VertexEdgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexEdgeCCWIter PolyConnectivity::ve_ccwbegin(VertexHandle _vh)
|
||||
{ return VertexEdgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexFaceIter PolyConnectivity::vf_begin(VertexHandle _vh)
|
||||
{ return VertexFaceIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexFaceCWIter PolyConnectivity::vf_cwbegin(VertexHandle _vh)
|
||||
{ return VertexFaceCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::VertexFaceCCWIter PolyConnectivity::vf_ccwbegin(VertexHandle _vh)
|
||||
{ return VertexFaceCCWIter(*this, _vh); }
|
||||
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexIter PolyConnectivity::cvv_begin(VertexHandle _vh) const
|
||||
{ return ConstVertexVertexIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexCWIter PolyConnectivity::cvv_cwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexVertexCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexCCWIter PolyConnectivity::cvv_ccwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexVertexCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeIter PolyConnectivity::cvih_begin(VertexHandle _vh) const
|
||||
{ return ConstVertexIHalfedgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeCWIter PolyConnectivity::cvih_cwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexIHalfedgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeCCWIter PolyConnectivity::cvih_ccwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexIHalfedgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeIter PolyConnectivity::cvoh_begin(VertexHandle _vh) const
|
||||
{ return ConstVertexOHalfedgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeCWIter PolyConnectivity::cvoh_cwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexOHalfedgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeCCWIter PolyConnectivity::cvoh_ccwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexOHalfedgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeIter PolyConnectivity::cve_begin(VertexHandle _vh) const
|
||||
{ return ConstVertexEdgeIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeCWIter PolyConnectivity::cve_cwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexEdgeCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeCCWIter PolyConnectivity::cve_ccwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexEdgeCCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceIter PolyConnectivity::cvf_begin(VertexHandle _vh) const
|
||||
{ return ConstVertexFaceIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceCWIter PolyConnectivity::cvf_cwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexFaceCWIter(*this, _vh); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceCCWIter PolyConnectivity::cvf_ccwbegin(VertexHandle _vh) const
|
||||
{ return ConstVertexFaceCCWIter(*this, _vh); }
|
||||
|
||||
|
||||
inline PolyConnectivity::FaceVertexIter PolyConnectivity::fv_begin(FaceHandle _fh)
|
||||
{ return FaceVertexIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceVertexCWIter PolyConnectivity::fv_cwbegin(FaceHandle _fh)
|
||||
{ return FaceVertexCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceVertexCCWIter PolyConnectivity::fv_ccwbegin(FaceHandle _fh)
|
||||
{ return FaceVertexCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceHalfedgeIter PolyConnectivity::fh_begin(FaceHandle _fh)
|
||||
{ return FaceHalfedgeIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceHalfedgeCWIter PolyConnectivity::fh_cwbegin(FaceHandle _fh)
|
||||
{ return FaceHalfedgeCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceHalfedgeCCWIter PolyConnectivity::fh_ccwbegin(FaceHandle _fh)
|
||||
{ return FaceHalfedgeCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceEdgeIter PolyConnectivity::fe_begin(FaceHandle _fh)
|
||||
{ return FaceEdgeIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceEdgeCWIter PolyConnectivity::fe_cwbegin(FaceHandle _fh)
|
||||
{ return FaceEdgeCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceEdgeCCWIter PolyConnectivity::fe_ccwbegin(FaceHandle _fh)
|
||||
{ return FaceEdgeCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceFaceIter PolyConnectivity::ff_begin(FaceHandle _fh)
|
||||
{ return FaceFaceIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceFaceCWIter PolyConnectivity::ff_cwbegin(FaceHandle _fh)
|
||||
{ return FaceFaceCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::FaceFaceCCWIter PolyConnectivity::ff_ccwbegin(FaceHandle _fh)
|
||||
{ return FaceFaceCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::HalfedgeLoopIter PolyConnectivity::hl_begin(HalfedgeHandle _heh)
|
||||
{ return HalfedgeLoopIter(*this, _heh); }
|
||||
|
||||
inline PolyConnectivity::HalfedgeLoopCWIter PolyConnectivity::hl_cwbegin(HalfedgeHandle _heh)
|
||||
{ return HalfedgeLoopCWIter(*this, _heh); }
|
||||
|
||||
inline PolyConnectivity::HalfedgeLoopCCWIter PolyConnectivity::hl_ccwbegin(HalfedgeHandle _heh)
|
||||
{ return HalfedgeLoopCCWIter(*this, _heh); }
|
||||
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexIter PolyConnectivity::cfv_begin(FaceHandle _fh) const
|
||||
{ return ConstFaceVertexIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexCWIter PolyConnectivity::cfv_cwbegin(FaceHandle _fh) const
|
||||
{ return ConstFaceVertexCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexCCWIter PolyConnectivity::cfv_ccwbegin(FaceHandle _fh) const
|
||||
{ return ConstFaceVertexCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeIter PolyConnectivity::cfh_begin(FaceHandle _fh) const
|
||||
{ return ConstFaceHalfedgeIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeCWIter PolyConnectivity::cfh_cwbegin(FaceHandle _fh) const
|
||||
{ return ConstFaceHalfedgeCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeCCWIter PolyConnectivity::cfh_ccwbegin(FaceHandle _fh) const
|
||||
{ return ConstFaceHalfedgeCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeIter PolyConnectivity::cfe_begin(FaceHandle _fh) const
|
||||
{ return ConstFaceEdgeIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeCWIter PolyConnectivity::cfe_cwbegin(FaceHandle _fh) const
|
||||
{ return ConstFaceEdgeCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeCCWIter PolyConnectivity::cfe_ccwbegin(FaceHandle _fh) const
|
||||
{ return ConstFaceEdgeCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceIter PolyConnectivity::cff_begin(FaceHandle _fh) const
|
||||
{ return ConstFaceFaceIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceCWIter PolyConnectivity::cff_cwbegin(FaceHandle _fh) const
|
||||
{ return ConstFaceFaceCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceCCWIter PolyConnectivity::cff_ccwbegin(FaceHandle _fh) const
|
||||
{ return ConstFaceFaceCCWIter(*this, _fh); }
|
||||
|
||||
inline PolyConnectivity::ConstHalfedgeLoopIter PolyConnectivity::chl_begin(HalfedgeHandle _heh) const
|
||||
{ return ConstHalfedgeLoopIter(*this, _heh); }
|
||||
|
||||
inline PolyConnectivity::ConstHalfedgeLoopCWIter PolyConnectivity::chl_cwbegin(HalfedgeHandle _heh) const
|
||||
{ return ConstHalfedgeLoopCWIter(*this, _heh); }
|
||||
|
||||
inline PolyConnectivity::ConstHalfedgeLoopCCWIter PolyConnectivity::chl_ccwbegin(HalfedgeHandle _heh) const
|
||||
{ return ConstHalfedgeLoopCCWIter(*this, _heh); }
|
||||
|
||||
// 'end' circulators
|
||||
|
||||
inline PolyConnectivity::VertexVertexIter PolyConnectivity::vv_end(VertexHandle _vh)
|
||||
{ return VertexVertexIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexVertexCWIter PolyConnectivity::vv_cwend(VertexHandle _vh)
|
||||
{ return VertexVertexCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexVertexCCWIter PolyConnectivity::vv_ccwend(VertexHandle _vh)
|
||||
{ return VertexVertexCCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexIHalfedgeIter PolyConnectivity::vih_end(VertexHandle _vh)
|
||||
{ return VertexIHalfedgeIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexIHalfedgeCWIter PolyConnectivity::vih_cwend(VertexHandle _vh)
|
||||
{ return VertexIHalfedgeCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexIHalfedgeCCWIter PolyConnectivity::vih_ccwend(VertexHandle _vh)
|
||||
{ return VertexIHalfedgeCCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexOHalfedgeIter PolyConnectivity::voh_end(VertexHandle _vh)
|
||||
{ return VertexOHalfedgeIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexOHalfedgeCWIter PolyConnectivity::voh_cwend(VertexHandle _vh)
|
||||
{ return VertexOHalfedgeCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexOHalfedgeCCWIter PolyConnectivity::voh_ccwend(VertexHandle _vh)
|
||||
{ return VertexOHalfedgeCCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexEdgeIter PolyConnectivity::ve_end(VertexHandle _vh)
|
||||
{ return VertexEdgeIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexEdgeCWIter PolyConnectivity::ve_cwend(VertexHandle _vh)
|
||||
{ return VertexEdgeCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexEdgeCCWIter PolyConnectivity::ve_ccwend(VertexHandle _vh)
|
||||
{ return VertexEdgeCCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexFaceIter PolyConnectivity::vf_end(VertexHandle _vh)
|
||||
{ return VertexFaceIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexFaceCWIter PolyConnectivity::vf_cwend(VertexHandle _vh)
|
||||
{ return VertexFaceCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::VertexFaceCCWIter PolyConnectivity::vf_ccwend(VertexHandle _vh)
|
||||
{ return VertexFaceCCWIter(*this, _vh, true); }
|
||||
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexIter PolyConnectivity::cvv_end(VertexHandle _vh) const
|
||||
{ return ConstVertexVertexIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexCWIter PolyConnectivity::cvv_cwend(VertexHandle _vh) const
|
||||
{ return ConstVertexVertexCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexVertexCCWIter PolyConnectivity::cvv_ccwend(VertexHandle _vh) const
|
||||
{ return ConstVertexVertexCCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeIter PolyConnectivity::cvih_end(VertexHandle _vh) const
|
||||
{ return ConstVertexIHalfedgeIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeCWIter PolyConnectivity::cvih_cwend(VertexHandle _vh) const
|
||||
{ return ConstVertexIHalfedgeCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexIHalfedgeCCWIter PolyConnectivity::cvih_ccwend(VertexHandle _vh) const
|
||||
{ return ConstVertexIHalfedgeCCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeIter PolyConnectivity::cvoh_end(VertexHandle _vh) const
|
||||
{ return ConstVertexOHalfedgeIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeCWIter PolyConnectivity::cvoh_cwend(VertexHandle _vh) const
|
||||
{ return ConstVertexOHalfedgeCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexOHalfedgeCCWIter PolyConnectivity::cvoh_ccwend(VertexHandle _vh) const
|
||||
{ return ConstVertexOHalfedgeCCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeIter PolyConnectivity::cve_end(VertexHandle _vh) const
|
||||
{ return ConstVertexEdgeIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeCWIter PolyConnectivity::cve_cwend(VertexHandle _vh) const
|
||||
{ return ConstVertexEdgeCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexEdgeCCWIter PolyConnectivity::cve_ccwend(VertexHandle _vh) const
|
||||
{ return ConstVertexEdgeCCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceIter PolyConnectivity::cvf_end(VertexHandle _vh) const
|
||||
{ return ConstVertexFaceIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceCWIter PolyConnectivity::cvf_cwend(VertexHandle _vh) const
|
||||
{ return ConstVertexFaceCWIter(*this, _vh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstVertexFaceCCWIter PolyConnectivity::cvf_ccwend(VertexHandle _vh) const
|
||||
{ return ConstVertexFaceCCWIter(*this, _vh, true); }
|
||||
|
||||
|
||||
inline PolyConnectivity::FaceVertexIter PolyConnectivity::fv_end(FaceHandle _fh)
|
||||
{ return FaceVertexIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceVertexCWIter PolyConnectivity::fv_cwend(FaceHandle _fh)
|
||||
{ return FaceVertexCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceVertexCCWIter PolyConnectivity::fv_ccwend(FaceHandle _fh)
|
||||
{ return FaceVertexCCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceHalfedgeIter PolyConnectivity::fh_end(FaceHandle _fh)
|
||||
{ return FaceHalfedgeIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceHalfedgeCWIter PolyConnectivity::fh_cwend(FaceHandle _fh)
|
||||
{ return FaceHalfedgeCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceHalfedgeCCWIter PolyConnectivity::fh_ccwend(FaceHandle _fh)
|
||||
{ return FaceHalfedgeCCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceEdgeIter PolyConnectivity::fe_end(FaceHandle _fh)
|
||||
{ return FaceEdgeIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceEdgeCWIter PolyConnectivity::fe_cwend(FaceHandle _fh)
|
||||
{ return FaceEdgeCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceEdgeCCWIter PolyConnectivity::fe_ccwend(FaceHandle _fh)
|
||||
{ return FaceEdgeCCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceFaceIter PolyConnectivity::ff_end(FaceHandle _fh)
|
||||
{ return FaceFaceIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceFaceCWIter PolyConnectivity::ff_cwend(FaceHandle _fh)
|
||||
{ return FaceFaceCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::FaceFaceCCWIter PolyConnectivity::ff_ccwend(FaceHandle _fh)
|
||||
{ return FaceFaceCCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::HalfedgeLoopIter PolyConnectivity::hl_end(HalfedgeHandle _heh)
|
||||
{ return HalfedgeLoopIter(*this, _heh, true); }
|
||||
|
||||
inline PolyConnectivity::HalfedgeLoopCWIter PolyConnectivity::hl_cwend(HalfedgeHandle _heh)
|
||||
{ return HalfedgeLoopCWIter(*this, _heh, true); }
|
||||
|
||||
inline PolyConnectivity::HalfedgeLoopCCWIter PolyConnectivity::hl_ccwend(HalfedgeHandle _heh)
|
||||
{ return HalfedgeLoopCCWIter(*this, _heh, true); }
|
||||
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexIter PolyConnectivity::cfv_end(FaceHandle _fh) const
|
||||
{ return ConstFaceVertexIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexCWIter PolyConnectivity::cfv_cwend(FaceHandle _fh) const
|
||||
{ return ConstFaceVertexCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceVertexCCWIter PolyConnectivity::cfv_ccwend(FaceHandle _fh) const
|
||||
{ return ConstFaceVertexCCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeIter PolyConnectivity::cfh_end(FaceHandle _fh) const
|
||||
{ return ConstFaceHalfedgeIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeCWIter PolyConnectivity::cfh_cwend(FaceHandle _fh) const
|
||||
{ return ConstFaceHalfedgeCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceHalfedgeCCWIter PolyConnectivity::cfh_ccwend(FaceHandle _fh) const
|
||||
{ return ConstFaceHalfedgeCCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeIter PolyConnectivity::cfe_end(FaceHandle _fh) const
|
||||
{ return ConstFaceEdgeIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeCWIter PolyConnectivity::cfe_cwend(FaceHandle _fh) const
|
||||
{ return ConstFaceEdgeCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceEdgeCCWIter PolyConnectivity::cfe_ccwend(FaceHandle _fh) const
|
||||
{ return ConstFaceEdgeCCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceIter PolyConnectivity::cff_end(FaceHandle _fh) const
|
||||
{ return ConstFaceFaceIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceCWIter PolyConnectivity::cff_cwend(FaceHandle _fh) const
|
||||
{ return ConstFaceFaceCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstFaceFaceCCWIter PolyConnectivity::cff_ccwend(FaceHandle _fh) const
|
||||
{ return ConstFaceFaceCCWIter(*this, _fh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstHalfedgeLoopIter PolyConnectivity::chl_end(HalfedgeHandle _heh) const
|
||||
{ return ConstHalfedgeLoopIter(*this, _heh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstHalfedgeLoopCWIter PolyConnectivity::chl_cwend(HalfedgeHandle _heh) const
|
||||
{ return ConstHalfedgeLoopCWIter(*this, _heh, true); }
|
||||
|
||||
inline PolyConnectivity::ConstHalfedgeLoopCCWIter PolyConnectivity::chl_ccwend(HalfedgeHandle _heh) const
|
||||
{ return ConstHalfedgeLoopCCWIter(*this, _heh, true); }
|
||||
|
||||
|
||||
}//namespace OpenMesh
|
||||
|
||||
#endif // OPENMESH_POLYCONNECTIVITY_INLINE_IMPL_HH
|
||||
|
||||
@@ -198,19 +198,19 @@ public:
|
||||
*
|
||||
* \sa new_vertex(const Point&), new_vertex_dirty()
|
||||
*/
|
||||
inline VertexHandle new_vertex()
|
||||
{ return Kernel::new_vertex(); }
|
||||
inline SmartVertexHandle new_vertex()
|
||||
{ return make_smart(Kernel::new_vertex(), this); }
|
||||
|
||||
/**
|
||||
* \brief Adds a new vertex initialized to a custom position.
|
||||
*
|
||||
* \sa new_vertex(), new_vertex_dirty()
|
||||
*/
|
||||
inline VertexHandle new_vertex(const Point& _p)
|
||||
inline SmartVertexHandle new_vertex(const Point& _p)
|
||||
{
|
||||
VertexHandle vh(Kernel::new_vertex());
|
||||
this->set_point(vh, _p);
|
||||
return vh;
|
||||
return make_smart(vh, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,20 +224,20 @@ public:
|
||||
*
|
||||
* \sa new_vertex(const Point &)
|
||||
*/
|
||||
inline VertexHandle new_vertex_dirty(const Point& _p)
|
||||
inline SmartVertexHandle new_vertex_dirty(const Point& _p)
|
||||
{
|
||||
VertexHandle vh(Kernel::new_vertex_dirty());
|
||||
this->set_point(vh, _p);
|
||||
return vh;
|
||||
return make_smart(vh, this);
|
||||
}
|
||||
|
||||
/// Alias for new_vertex(const Point&).
|
||||
inline VertexHandle add_vertex(const Point& _p)
|
||||
inline SmartVertexHandle add_vertex(const Point& _p)
|
||||
{ return new_vertex(_p); }
|
||||
|
||||
/// Alias for new_vertex_dirty().
|
||||
inline VertexHandle add_vertex_dirty(const Point& _p)
|
||||
{ return new_vertex_dirty(_p); }
|
||||
inline SmartVertexHandle add_vertex_dirty(const Point& _p)
|
||||
{ return make_smart(new_vertex_dirty(_p), this); }
|
||||
|
||||
// --- normal vectors ---
|
||||
|
||||
|
||||
115
src/OpenMesh/Core/Mesh/SmartHandles.cc
Normal file
115
src/OpenMesh/Core/Mesh/SmartHandles.cc
Normal file
@@ -0,0 +1,115 @@
|
||||
/* ========================================================================= *
|
||||
* *
|
||||
* OpenMesh *
|
||||
* Copyright (c) 2001-2019, RWTH-Aachen University *
|
||||
* Department of Computer Graphics and Multimedia *
|
||||
* All rights reserved. *
|
||||
* www.openmesh.org *
|
||||
* *
|
||||
*---------------------------------------------------------------------------*
|
||||
* This file is part of OpenMesh. *
|
||||
*---------------------------------------------------------------------------*
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or without *
|
||||
* modification, are permitted provided that the following conditions *
|
||||
* are met: *
|
||||
* *
|
||||
* 1. Redistributions of source code must retain the above copyright notice, *
|
||||
* this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* 2. Redistributions in binary form must reproduce the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer in the *
|
||||
* documentation and/or other materials provided with the distribution. *
|
||||
* *
|
||||
* 3. Neither the name of the copyright holder nor the names of its *
|
||||
* contributors may be used to endorse or promote products derived from *
|
||||
* this software without specific prior written permission. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
|
||||
* *
|
||||
* ========================================================================= */
|
||||
|
||||
|
||||
//== INCLUDES =================================================================
|
||||
|
||||
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
|
||||
#include "SmartHandles.hh"
|
||||
|
||||
|
||||
//== NAMESPACES ===============================================================
|
||||
|
||||
namespace OpenMesh {
|
||||
|
||||
//TODO: I was not able to leave those in the header. If you find a way to do that, please do.
|
||||
|
||||
PolyConnectivity::ConstVertexFaceRange SmartVertexHandle::faces() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->vf_range(*this);
|
||||
}
|
||||
|
||||
PolyConnectivity::ConstVertexEdgeRange SmartVertexHandle::edges() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->ve_range(*this);
|
||||
}
|
||||
|
||||
PolyConnectivity::ConstVertexVertexRange SmartVertexHandle::vertices() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->vv_range(*this);
|
||||
}
|
||||
|
||||
PolyConnectivity::ConstVertexIHalfedgeRange SmartVertexHandle::incoming_halfedges() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->vih_range(*this);
|
||||
}
|
||||
|
||||
PolyConnectivity::ConstVertexOHalfedgeRange SmartVertexHandle::outgoing_halfedges() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->voh_range(*this);
|
||||
}
|
||||
|
||||
|
||||
PolyConnectivity::ConstFaceVertexRange SmartFaceHandle::vertices() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->fv_range(*this);
|
||||
}
|
||||
|
||||
PolyConnectivity::ConstFaceHalfedgeRange SmartFaceHandle::halfedges() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->fh_range(*this);
|
||||
}
|
||||
|
||||
PolyConnectivity::ConstFaceEdgeRange SmartFaceHandle::edges() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->fe_range(*this);
|
||||
}
|
||||
|
||||
PolyConnectivity::ConstFaceFaceRange SmartFaceHandle::faces() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->ff_range(*this);
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
} // namespace OpenMesh
|
||||
//=============================================================================
|
||||
|
||||
//=============================================================================
|
||||
367
src/OpenMesh/Core/Mesh/SmartHandles.hh
Normal file
367
src/OpenMesh/Core/Mesh/SmartHandles.hh
Normal file
@@ -0,0 +1,367 @@
|
||||
/* ========================================================================= *
|
||||
* *
|
||||
* OpenMesh *
|
||||
* Copyright (c) 2001-2019, RWTH-Aachen University *
|
||||
* Department of Computer Graphics and Multimedia *
|
||||
* All rights reserved. *
|
||||
* www.openmesh.org *
|
||||
* *
|
||||
*---------------------------------------------------------------------------*
|
||||
* This file is part of OpenMesh. *
|
||||
*---------------------------------------------------------------------------*
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or without *
|
||||
* modification, are permitted provided that the following conditions *
|
||||
* are met: *
|
||||
* *
|
||||
* 1. Redistributions of source code must retain the above copyright notice, *
|
||||
* this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* 2. Redistributions in binary form must reproduce the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer in the *
|
||||
* documentation and/or other materials provided with the distribution. *
|
||||
* *
|
||||
* 3. Neither the name of the copyright holder nor the names of its *
|
||||
* contributors may be used to endorse or promote products derived from *
|
||||
* this software without specific prior written permission. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
|
||||
* *
|
||||
* ========================================================================= */
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//== INCLUDES =================================================================
|
||||
|
||||
#include "Handles.hh"
|
||||
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
|
||||
#include <OpenMesh/Core/System/OpenMeshDLLMacros.hh>
|
||||
|
||||
|
||||
//== NAMESPACES ===============================================================
|
||||
|
||||
namespace OpenMesh {
|
||||
|
||||
//== FORWARD DECLARATION ======================================================
|
||||
|
||||
struct SmartVertexHandle;
|
||||
struct SmartHalfedgeHandle;
|
||||
struct SmartEdgeHandle;
|
||||
struct SmartFaceHandle;
|
||||
|
||||
|
||||
//== CLASS DEFINITION =========================================================
|
||||
|
||||
/// Base class for all smart handle types
|
||||
class OPENMESHDLLEXPORT SmartBaseHandle
|
||||
{
|
||||
public:
|
||||
explicit SmartBaseHandle(const PolyConnectivity* _mesh = nullptr) : mesh_(_mesh) {}
|
||||
|
||||
/// Get the underlying mesh of this handle
|
||||
const PolyConnectivity* mesh() const { return mesh_; }
|
||||
|
||||
// TODO: should operators ==, !=, < look at mesh_?
|
||||
|
||||
private:
|
||||
const PolyConnectivity* mesh_;
|
||||
|
||||
};
|
||||
|
||||
/// Smart version of VertexHandle contains a pointer to the corresponding mesh and allows easier access to navigation methods
|
||||
struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandle
|
||||
{
|
||||
explicit SmartVertexHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), VertexHandle(_idx) {}
|
||||
|
||||
/// Returns an outgoing halfedge
|
||||
SmartHalfedgeHandle out() const;
|
||||
/// Returns an outgoing halfedge
|
||||
SmartHalfedgeHandle halfedge() const; // alias for out
|
||||
/// Returns an incoming halfedge
|
||||
SmartHalfedgeHandle in() const;
|
||||
|
||||
/// Returns a range of faces incident to the vertex (PolyConnectivity::vf_range())
|
||||
PolyConnectivity::ConstVertexFaceRange faces() const;
|
||||
/// Returns a range of edges incident to the vertex (PolyConnectivity::ve_range())
|
||||
PolyConnectivity::ConstVertexEdgeRange edges() const;
|
||||
/// Returns a range of vertices adjacent to the vertex (PolyConnectivity::vv_range())
|
||||
PolyConnectivity::ConstVertexVertexRange vertices() const;
|
||||
/// Returns a range of outgoing halfedges incident to the vertex (PolyConnectivity::voh_range())
|
||||
PolyConnectivity::ConstVertexIHalfedgeRange incoming_halfedges() const;
|
||||
/// Returns a range of incoming halfedges incident to the vertex (PolyConnectivity::vih_range())
|
||||
PolyConnectivity::ConstVertexOHalfedgeRange outgoing_halfedges() const;
|
||||
|
||||
/// Returns valence of the vertex
|
||||
uint valence() const;
|
||||
/// Returns true iff the vertex is incident to a boundary halfedge
|
||||
bool is_boundary() const;
|
||||
/// Returns true iff (the mesh at) the vertex is two-manifold ?
|
||||
bool is_manifold() const;
|
||||
};
|
||||
|
||||
struct OPENMESHDLLEXPORT SmartHalfedgeHandle : public SmartBaseHandle, HalfedgeHandle
|
||||
{
|
||||
explicit SmartHalfedgeHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), HalfedgeHandle(_idx) {}
|
||||
|
||||
/// Returns next halfedge handle
|
||||
SmartHalfedgeHandle next() const;
|
||||
/// Returns previous halfedge handle
|
||||
SmartHalfedgeHandle prev() const;
|
||||
/// Returns opposite halfedge handle
|
||||
SmartHalfedgeHandle opp() const;
|
||||
/// Returns vertex pointed to by halfedge
|
||||
SmartVertexHandle to() const;
|
||||
/// Returns vertex at start of halfedge
|
||||
SmartVertexHandle from() const;
|
||||
/// Returns incident edge of halfedge
|
||||
SmartEdgeHandle edge() const;
|
||||
/// Returns incident face of halfedge
|
||||
SmartFaceHandle face() const;
|
||||
|
||||
/// Returns true iff the halfedge is on the boundary (i.e. it has no corresponding face)
|
||||
bool is_boundary() const;
|
||||
};
|
||||
|
||||
struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle
|
||||
{
|
||||
explicit SmartEdgeHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), EdgeHandle(_idx) {}
|
||||
|
||||
/// Returns one of the two halfedges of the edge
|
||||
SmartHalfedgeHandle halfedge(unsigned int _i) const;
|
||||
/// Shorthand for halfedge()
|
||||
SmartHalfedgeHandle h(unsigned int _i) const;
|
||||
/// Shorthand for halfedge(0)
|
||||
SmartHalfedgeHandle h0() const;
|
||||
/// Shorthand for halfedge(1)
|
||||
SmartHalfedgeHandle h1() const;
|
||||
/// Returns one of the two incident vertices of the edge
|
||||
SmartVertexHandle vertex(unsigned int _i) const;
|
||||
/// Shorthand for vertex()
|
||||
SmartVertexHandle v(unsigned int _i) const;
|
||||
/// Shorthand for vertex(0)
|
||||
SmartVertexHandle v0() const;
|
||||
/// Shorthand for vertex(1)
|
||||
SmartVertexHandle v1() const;
|
||||
|
||||
/// Returns true iff the edge lies on the boundary (i.e. one of the halfedges is boundary)
|
||||
bool is_boundary() const;
|
||||
};
|
||||
|
||||
struct OPENMESHDLLEXPORT SmartFaceHandle : public SmartBaseHandle, FaceHandle
|
||||
{
|
||||
explicit SmartFaceHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), FaceHandle(_idx) {}
|
||||
|
||||
/// Returns one of the halfedges of the face
|
||||
SmartHalfedgeHandle halfedge() const;
|
||||
|
||||
/// Returns a range of vertices incident to the face (PolyConnectivity::fv_range())
|
||||
PolyConnectivity::ConstFaceVertexRange vertices() const;
|
||||
/// Returns a range of halfedges of the face (PolyConnectivity::fh_range())
|
||||
PolyConnectivity::ConstFaceHalfedgeRange halfedges() const;
|
||||
/// Returns a range of edges of the face (PolyConnectivity::fv_range())
|
||||
PolyConnectivity::ConstFaceEdgeRange edges() const;
|
||||
/// Returns a range adjacent faces of the face (PolyConnectivity::ff_range())
|
||||
PolyConnectivity::ConstFaceFaceRange faces() const;
|
||||
|
||||
/// Returns the valence of the face
|
||||
uint valence() const;
|
||||
/// Returns true iff the face lies at the boundary (i.e. one of the edges is boundary)
|
||||
bool is_boundary() const;
|
||||
};
|
||||
|
||||
|
||||
/// Creats a SmartVertexHandle from a VertexHandle and a Mesh
|
||||
inline SmartVertexHandle make_smart(VertexHandle _vh, const PolyConnectivity* _mesh) { return SmartVertexHandle (_vh.idx(), _mesh); }
|
||||
/// Creats a SmartHalfedgeHandle from a HalfedgeHandle and a Mesh
|
||||
inline SmartHalfedgeHandle make_smart(HalfedgeHandle _hh, const PolyConnectivity* _mesh) { return SmartHalfedgeHandle(_hh.idx(), _mesh); }
|
||||
/// Creats a SmartEdgeHandle from an EdgeHandle and a Mesh
|
||||
inline SmartEdgeHandle make_smart(EdgeHandle _eh, const PolyConnectivity* _mesh) { return SmartEdgeHandle (_eh.idx(), _mesh); }
|
||||
/// Creats a SmartFaceHandle from a FaceHandle and a Mesh
|
||||
inline SmartFaceHandle make_smart(FaceHandle _fh, const PolyConnectivity* _mesh) { return SmartFaceHandle (_fh.idx(), _mesh); }
|
||||
|
||||
/// Creats a SmartVertexHandle from a VertexHandle and a Mesh
|
||||
inline SmartVertexHandle make_smart(VertexHandle _vh, const PolyConnectivity& _mesh) { return SmartVertexHandle (_vh.idx(), &_mesh); }
|
||||
/// Creats a SmartHalfedgeHandle from a HalfedgeHandle and a Mesh
|
||||
inline SmartHalfedgeHandle make_smart(HalfedgeHandle _hh, const PolyConnectivity& _mesh) { return SmartHalfedgeHandle(_hh.idx(), &_mesh); }
|
||||
/// Creats a SmartEdgeHandle from an EdgeHandle and a Mesh
|
||||
inline SmartEdgeHandle make_smart(EdgeHandle _eh, const PolyConnectivity& _mesh) { return SmartEdgeHandle (_eh.idx(), &_mesh); }
|
||||
/// Creats a SmartFaceHandle from a FaceHandle and a Mesh
|
||||
inline SmartFaceHandle make_smart(FaceHandle _fh, const PolyConnectivity& _mesh) { return SmartFaceHandle (_fh.idx(), &_mesh); }
|
||||
|
||||
|
||||
// helper to convert Handle Types to Smarthandle Types
|
||||
template <typename HandleT>
|
||||
struct SmartHandle;
|
||||
|
||||
template <> struct SmartHandle<VertexHandle> { using type = SmartVertexHandle; };
|
||||
template <> struct SmartHandle<HalfedgeHandle> { using type = SmartHalfedgeHandle; };
|
||||
template <> struct SmartHandle<EdgeHandle> { using type = SmartEdgeHandle; };
|
||||
template <> struct SmartHandle<FaceHandle> { using type = SmartFaceHandle; };
|
||||
|
||||
|
||||
inline SmartHalfedgeHandle SmartVertexHandle::out() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->halfedge_handle(*this), mesh());
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartVertexHandle::halfedge() const
|
||||
{
|
||||
return out();
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartVertexHandle::in() const
|
||||
{
|
||||
return out().opp();
|
||||
}
|
||||
|
||||
inline uint SmartVertexHandle::valence() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->valence(*this);
|
||||
}
|
||||
|
||||
inline bool SmartVertexHandle::is_boundary() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->is_boundary(*this);
|
||||
}
|
||||
|
||||
inline bool SmartVertexHandle::is_manifold() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->is_manifold(*this);
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartHalfedgeHandle::next() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->next_halfedge_handle(*this), mesh());
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartHalfedgeHandle::prev() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->prev_halfedge_handle(*this), mesh());
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartHalfedgeHandle::opp() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->opposite_halfedge_handle(*this), mesh());
|
||||
}
|
||||
|
||||
inline SmartVertexHandle SmartHalfedgeHandle::to() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->to_vertex_handle(*this), mesh());
|
||||
}
|
||||
|
||||
inline SmartVertexHandle SmartHalfedgeHandle::from() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->from_vertex_handle(*this), mesh());
|
||||
}
|
||||
|
||||
inline SmartEdgeHandle SmartHalfedgeHandle::edge() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->edge_handle(*this), mesh());
|
||||
}
|
||||
|
||||
inline SmartFaceHandle SmartHalfedgeHandle::face() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->face_handle(*this), mesh());
|
||||
}
|
||||
|
||||
inline bool SmartHalfedgeHandle::is_boundary() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->is_boundary(*this);
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartEdgeHandle::halfedge(unsigned int _i) const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->halfedge_handle(*this, _i), mesh());
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartEdgeHandle::h(unsigned int _i) const
|
||||
{
|
||||
return halfedge(_i);
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartEdgeHandle::h0() const
|
||||
{
|
||||
return h(0);
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartEdgeHandle::h1() const
|
||||
{
|
||||
return h(1);
|
||||
}
|
||||
|
||||
inline SmartVertexHandle SmartEdgeHandle::vertex(unsigned int _i) const
|
||||
{
|
||||
return halfedge(_i).from();
|
||||
}
|
||||
|
||||
inline SmartVertexHandle SmartEdgeHandle::v(unsigned int _i) const
|
||||
{
|
||||
return vertex(_i);
|
||||
}
|
||||
|
||||
inline SmartVertexHandle SmartEdgeHandle::v0() const
|
||||
{
|
||||
return v(0);
|
||||
}
|
||||
|
||||
inline SmartVertexHandle SmartEdgeHandle::v1() const
|
||||
{
|
||||
return v(1);
|
||||
}
|
||||
|
||||
inline bool SmartEdgeHandle::is_boundary() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->is_boundary(*this);
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartFaceHandle::halfedge() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return make_smart(mesh()->halfedge_handle(*this), mesh());
|
||||
}
|
||||
|
||||
inline uint SmartFaceHandle::valence() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->valence(*this);
|
||||
}
|
||||
|
||||
inline bool SmartFaceHandle::is_boundary() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->is_boundary(*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
} // namespace OpenMesh
|
||||
//=============================================================================
|
||||
|
||||
//=============================================================================
|
||||
268
src/OpenMesh/Core/Mesh/SmartRange.hh
Normal file
268
src/OpenMesh/Core/Mesh/SmartRange.hh
Normal file
@@ -0,0 +1,268 @@
|
||||
/* ========================================================================= *
|
||||
* *
|
||||
* OpenMesh *
|
||||
* Copyright (c) 2001-2019, RWTH-Aachen University *
|
||||
* Department of Computer Graphics and Multimedia *
|
||||
* All rights reserved. *
|
||||
* www.openmesh.org *
|
||||
* *
|
||||
*---------------------------------------------------------------------------*
|
||||
* This file is part of OpenMesh. *
|
||||
*---------------------------------------------------------------------------*
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or without *
|
||||
* modification, are permitted provided that the following conditions *
|
||||
* are met: *
|
||||
* *
|
||||
* 1. Redistributions of source code must retain the above copyright notice, *
|
||||
* this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* 2. Redistributions in binary form must reproduce the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer in the *
|
||||
* documentation and/or other materials provided with the distribution. *
|
||||
* *
|
||||
* 3. Neither the name of the copyright holder nor the names of its *
|
||||
* contributors may be used to endorse or promote products derived from *
|
||||
* this software without specific prior written permission. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
|
||||
* *
|
||||
* ========================================================================= */
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
//== NAMESPACES ===============================================================
|
||||
|
||||
namespace OpenMesh {
|
||||
|
||||
//== FORWARD DECLARATION ======================================================
|
||||
|
||||
//== CLASS DEFINITION =========================================================
|
||||
|
||||
namespace {
|
||||
|
||||
struct Identity
|
||||
{
|
||||
template <typename T>
|
||||
T operator()(const T& _t) const { return _t; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// Base class for all smart range types
|
||||
template <typename RangeT, typename HandleT>
|
||||
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>()))
|
||||
{
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
auto begin = range->begin();
|
||||
auto end = range->end();
|
||||
assert(begin != end);
|
||||
decltype (f(*begin) + f(*begin)) sum = f(*begin);
|
||||
auto it = begin;
|
||||
++it;
|
||||
for (; it != end; ++it)
|
||||
sum += f(*it);
|
||||
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>())))
|
||||
{
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
auto begin = range->begin();
|
||||
auto end = range->end();
|
||||
assert(begin != end);
|
||||
decltype (f(*begin) + f(*begin)) sum = f(*begin);
|
||||
auto it = begin;
|
||||
++it;
|
||||
int n_elements = 1;
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
sum += f(*it);
|
||||
++n_elements;
|
||||
}
|
||||
return (1.0 / n_elements) * sum;
|
||||
}
|
||||
|
||||
/** @brief Check if any element fulfils condition.
|
||||
*
|
||||
* Checks if functor \p f returns true for any of the elements in the range.
|
||||
* Returns true if that is the case, false otherwise.
|
||||
*
|
||||
* @param f Functor that is evaluated for all elements.
|
||||
*/
|
||||
template <typename Functor>
|
||||
auto any_of(Functor&& f) -> bool
|
||||
{
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
for (auto e : *range)
|
||||
if (f(e))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @brief Check if all elements fulfil condition.
|
||||
*
|
||||
* Checks if functor \p f returns true for all of the elements in the range.
|
||||
* Returns true if that is the case, false otherwise.
|
||||
*
|
||||
* @param f Functor that is evaluated for all elements.
|
||||
*/
|
||||
template <typename Functor>
|
||||
auto all_of(Functor&& f) -> bool
|
||||
{
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
for (auto e : *range)
|
||||
if (!f(e))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @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>
|
||||
{
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
std::array<typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type, n> res;
|
||||
auto it = range->begin();
|
||||
auto end = range->end();
|
||||
int i = 0;
|
||||
while (i < n && it != end)
|
||||
res[i++] = f(*(it++));
|
||||
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>
|
||||
{
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
std::vector<typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type> res;
|
||||
for (const auto& e : *range)
|
||||
res.push_back(f(e));
|
||||
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
|
||||
{
|
||||
using std::min;
|
||||
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
auto it = range->begin();
|
||||
auto end = range->end();
|
||||
assert(it != end);
|
||||
|
||||
typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type res = f(*it);
|
||||
++it;
|
||||
|
||||
for (; it != end; ++it)
|
||||
res = min(res, f(*it));
|
||||
|
||||
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
|
||||
{
|
||||
using std::max;
|
||||
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
auto it = range->begin();
|
||||
auto end = range->end();
|
||||
assert(it != end);
|
||||
|
||||
typename std::remove_reference<decltype (f(std::declval<HandleT>()))>::type res = f(*it);
|
||||
++it;
|
||||
|
||||
for (; it != end; ++it)
|
||||
res = max(res, f(*it));
|
||||
|
||||
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>
|
||||
{
|
||||
return std::make_pair(this->min(f), this->max(f));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
} // namespace OpenMesh
|
||||
//=============================================================================
|
||||
|
||||
//=============================================================================
|
||||
@@ -49,11 +49,11 @@
|
||||
namespace OpenMesh
|
||||
{
|
||||
|
||||
TriConnectivity::FaceHandle
|
||||
SmartFaceHandle
|
||||
TriConnectivity::add_face(const VertexHandle* _vertex_handles, size_t _vhs_size)
|
||||
{
|
||||
// need at least 3 vertices
|
||||
if (_vhs_size < 3) return InvalidFaceHandle;
|
||||
if (_vhs_size < 3) return make_smart(InvalidFaceHandle, this);
|
||||
|
||||
/// face is triangle -> ok
|
||||
if (_vhs_size == 3)
|
||||
@@ -78,21 +78,29 @@ TriConnectivity::add_face(const VertexHandle* _vertex_handles, size_t _vhs_size)
|
||||
fh = PolyConnectivity::add_face(vhandles, 3);
|
||||
}
|
||||
|
||||
return fh;
|
||||
return make_smart(fh, this);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FaceHandle TriConnectivity::add_face(const std::vector<VertexHandle>& _vhandles)
|
||||
SmartFaceHandle TriConnectivity::add_face(const std::vector<VertexHandle>& _vhandles)
|
||||
{
|
||||
return add_face(&_vhandles.front(), _vhandles.size());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SmartFaceHandle TriConnectivity::add_face(const std::vector<SmartVertexHandle>& _vhandles)
|
||||
{
|
||||
std::vector<VertexHandle> vhandles(_vhandles.begin(), _vhandles.end());
|
||||
return add_face(&vhandles.front(), vhandles.size());
|
||||
}
|
||||
|
||||
FaceHandle TriConnectivity::add_face(VertexHandle _vh0, VertexHandle _vh1, VertexHandle _vh2)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
SmartFaceHandle TriConnectivity::add_face(VertexHandle _vh0, VertexHandle _vh1, VertexHandle _vh2)
|
||||
{
|
||||
VertexHandle vhs[3] = { _vh0, _vh1, _vh2 };
|
||||
return PolyConnectivity::add_face(vhs, 3);
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
*
|
||||
*
|
||||
* */
|
||||
FaceHandle add_face(const VertexHandle* _vhandles, size_t _vhs_size);
|
||||
SmartFaceHandle add_face(const VertexHandle* _vhandles, size_t _vhs_size);
|
||||
|
||||
/** \brief Add a face with arbitrary valence to the triangle mesh
|
||||
*
|
||||
@@ -95,7 +95,17 @@ public:
|
||||
*
|
||||
*
|
||||
* */
|
||||
FaceHandle add_face(const std::vector<VertexHandle>& _vhandles);
|
||||
SmartFaceHandle add_face(const std::vector<VertexHandle>& _vhandles);
|
||||
|
||||
/** \brief Add a face with arbitrary valence to the triangle mesh
|
||||
*
|
||||
* Override OpenMesh::Mesh::PolyMeshT::add_face(). Faces that aren't
|
||||
* triangles will be triangulated and added. In this case an
|
||||
* invalid face handle will be returned.
|
||||
*
|
||||
*
|
||||
* */
|
||||
SmartFaceHandle add_face(const std::vector<SmartVertexHandle>& _vhandles);
|
||||
|
||||
/** \brief Add a face to the mesh (triangle)
|
||||
*
|
||||
@@ -107,7 +117,7 @@ public:
|
||||
* @param _vh2 VertexHandle 3
|
||||
* @return FaceHandle of the added face (invalid, if the operation failed)
|
||||
*/
|
||||
FaceHandle add_face(VertexHandle _vh0, VertexHandle _vh1, VertexHandle _vh2);
|
||||
SmartFaceHandle add_face(VertexHandle _vh0, VertexHandle _vh1, VertexHandle _vh2);
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
@@ -264,10 +264,10 @@ public:
|
||||
* @param _p New point position that will be inserted at the edge
|
||||
* @return Vertex handle of the newly added vertex
|
||||
*/
|
||||
inline VertexHandle split(EdgeHandle _eh, const Point& _p)
|
||||
inline SmartVertexHandle split(EdgeHandle _eh, const Point& _p)
|
||||
{
|
||||
//Do not call PolyMeshT function below as this does the wrong operation
|
||||
const VertexHandle vh = this->add_vertex(_p); Kernel::split(_eh, vh); return vh;
|
||||
const SmartVertexHandle vh = this->add_vertex(_p); Kernel::split(_eh, vh); return vh;
|
||||
}
|
||||
|
||||
/** \brief Edge split (= 2-to-4 split)
|
||||
@@ -278,10 +278,10 @@ public:
|
||||
* @param _p New point position that will be inserted at the edge
|
||||
* @return Vertex handle of the newly added vertex
|
||||
*/
|
||||
inline VertexHandle split_copy(EdgeHandle _eh, const Point& _p)
|
||||
inline SmartVertexHandle split_copy(EdgeHandle _eh, const Point& _p)
|
||||
{
|
||||
//Do not call PolyMeshT function below as this does the wrong operation
|
||||
const VertexHandle vh = this->add_vertex(_p); Kernel::split_copy(_eh, vh); return vh;
|
||||
const SmartVertexHandle vh = this->add_vertex(_p); Kernel::split_copy(_eh, vh); return vh;
|
||||
}
|
||||
|
||||
/** \brief Edge split (= 2-to-4 split)
|
||||
@@ -319,8 +319,8 @@ public:
|
||||
*
|
||||
* @return Vertex handle of the new vertex
|
||||
*/
|
||||
inline VertexHandle split(FaceHandle _fh, const Point& _p)
|
||||
{ const VertexHandle vh = this->add_vertex(_p); PolyMesh::split(_fh, vh); return vh; }
|
||||
inline SmartVertexHandle split(FaceHandle _fh, const Point& _p)
|
||||
{ const SmartVertexHandle vh = this->add_vertex(_p); PolyMesh::split(_fh, vh); return vh; }
|
||||
|
||||
/** \brief Face split (= 1-to-3 split, calls corresponding PolyMeshT function).
|
||||
*
|
||||
@@ -331,8 +331,8 @@ public:
|
||||
*
|
||||
* @return Vertex handle of the new vertex
|
||||
*/
|
||||
inline VertexHandle split_copy(FaceHandle _fh, const Point& _p)
|
||||
{ const VertexHandle vh = this->add_vertex(_p); PolyMesh::split_copy(_fh, vh); return vh; }
|
||||
inline SmartVertexHandle split_copy(FaceHandle _fh, const Point& _p)
|
||||
{ const SmartVertexHandle vh = this->add_vertex(_p); PolyMesh::split_copy(_fh, vh); return vh; }
|
||||
|
||||
|
||||
/** \brief Face split (= 1-to-4) split, splits edges at midpoints and adds 4 new faces in the interior).
|
||||
|
||||
@@ -485,6 +485,7 @@ struct VPropHandleT : public BasePropHandleT<T>
|
||||
{
|
||||
typedef T Value;
|
||||
typedef T value_type;
|
||||
typedef VertexHandle Handle;
|
||||
|
||||
explicit VPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
|
||||
explicit VPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
|
||||
@@ -499,6 +500,7 @@ struct HPropHandleT : public BasePropHandleT<T>
|
||||
{
|
||||
typedef T Value;
|
||||
typedef T value_type;
|
||||
typedef HalfedgeHandle Handle;
|
||||
|
||||
explicit HPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
|
||||
explicit HPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
|
||||
@@ -513,6 +515,7 @@ struct EPropHandleT : public BasePropHandleT<T>
|
||||
{
|
||||
typedef T Value;
|
||||
typedef T value_type;
|
||||
typedef EdgeHandle Handle;
|
||||
|
||||
explicit EPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
|
||||
explicit EPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
|
||||
@@ -527,6 +530,7 @@ struct FPropHandleT : public BasePropHandleT<T>
|
||||
{
|
||||
typedef T Value;
|
||||
typedef T value_type;
|
||||
typedef FaceHandle Handle;
|
||||
|
||||
explicit FPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
|
||||
explicit FPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
|
||||
@@ -541,11 +545,39 @@ struct MPropHandleT : public BasePropHandleT<T>
|
||||
{
|
||||
typedef T Value;
|
||||
typedef T value_type;
|
||||
typedef MeshHandle Handle;
|
||||
|
||||
explicit MPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
|
||||
explicit MPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
|
||||
};
|
||||
|
||||
template <typename HandleT>
|
||||
struct PropHandle;
|
||||
|
||||
template <>
|
||||
struct PropHandle<VertexHandle> {
|
||||
template <typename T>
|
||||
using type = VPropHandleT<T>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PropHandle<HalfedgeHandle> {
|
||||
template <typename T>
|
||||
using type = HPropHandleT<T>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PropHandle<EdgeHandle> {
|
||||
template <typename T>
|
||||
using type = EPropHandleT<T>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PropHandle<FaceHandle> {
|
||||
template <typename T>
|
||||
using type = FPropHandleT<T>;
|
||||
};
|
||||
|
||||
} // namespace OpenMesh
|
||||
//=============================================================================
|
||||
#endif // OPENMESH_PROPERTY_HH defined
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
|
||||
#include <OpenMesh/Core/System/config.h>
|
||||
#include <OpenMesh/Core/Utils/HandleToPropHandle.hh>
|
||||
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -59,6 +60,8 @@ namespace OpenMesh {
|
||||
* makeTemporaryProperty(), getProperty(), and getOrMakeProperty()
|
||||
* to construct a PropertyManager, e.g.
|
||||
*
|
||||
* Note that the second template parameter is depcretated.
|
||||
*
|
||||
* \code
|
||||
* {
|
||||
* TriMesh mesh;
|
||||
@@ -73,27 +76,66 @@ namespace OpenMesh {
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
template<typename PROPTYPE, typename MeshT>
|
||||
template<typename PROPTYPE, typename MeshT = int>
|
||||
class PropertyManager {
|
||||
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
|
||||
public:
|
||||
PropertyManager(const PropertyManager&) = delete;
|
||||
PropertyManager& operator=(const PropertyManager&) = delete;
|
||||
#else
|
||||
using Value = typename PROPTYPE::Value;
|
||||
using value_type = typename PROPTYPE::value_type;
|
||||
using Handle = typename PROPTYPE::Handle;
|
||||
using Self = PropertyManager<PROPTYPE, MeshT>;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Noncopyable because there aren't no straightforward copy semantics.
|
||||
*/
|
||||
PropertyManager(const PropertyManager&);
|
||||
// Mesh properties (MPropHandleT<...>) are stored differently than the other properties.
|
||||
// This class implements different behavior when copying or swapping data from one
|
||||
// property manager to a another one.
|
||||
template <typename PropertyManager2, typename PropHandleT>
|
||||
struct StorageT;
|
||||
|
||||
/**
|
||||
* Noncopyable because there aren't no straightforward copy semantics.
|
||||
*/
|
||||
PropertyManager& operator=(const PropertyManager&);
|
||||
#endif
|
||||
// specialization for Mesh Properties
|
||||
template <typename PropertyManager2>
|
||||
struct StorageT<PropertyManager2, MPropHandleT<Value>> {
|
||||
static void copy(const PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
|
||||
*to = *from;
|
||||
}
|
||||
static void swap(PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
|
||||
std::swap(*to, *from);
|
||||
}
|
||||
static const Value& access_property_const(PolyConnectivity& mesh, const PROPTYPE& prop_handle, const Handle&) {
|
||||
return mesh.property(prop_handle);
|
||||
}
|
||||
static Value& access_property(PolyConnectivity& mesh, const PROPTYPE& prop_handle, const Handle&) {
|
||||
return mesh.property(prop_handle);
|
||||
}
|
||||
};
|
||||
|
||||
// definition for other Mesh Properties
|
||||
template <typename PropertyManager2, typename PropHandleT>
|
||||
struct StorageT {
|
||||
static void copy(const PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
|
||||
from.copy_to(from.mesh_.template all_elements<Handle>(), to, to.mesh_.template all_elements<Handle>());
|
||||
}
|
||||
static void swap(PropertyManager<PROPTYPE, MeshT>& lhs, PropertyManager2& rhs) {
|
||||
std::swap(lhs.mesh().property(lhs.prop_).data_vector(), rhs.mesh().property(rhs.prop_).data_vector());
|
||||
// resize the property to the correct size
|
||||
lhs.mesh().property(lhs.prop_).resize(lhs.mesh().template n_elements<Handle>());
|
||||
rhs.mesh().property(rhs.prop_).resize(rhs.mesh().template n_elements<Handle>());
|
||||
}
|
||||
static const Value& access_property_const(PolyConnectivity& mesh, const PROPTYPE& prop_handle, const Handle& handle) {
|
||||
return mesh.property(prop_handle, handle);
|
||||
}
|
||||
static Value& access_property(PolyConnectivity& mesh, const PROPTYPE& prop_handle, const Handle& handle) {
|
||||
return mesh.property(prop_handle, handle);
|
||||
}
|
||||
};
|
||||
|
||||
using Storage = StorageT<Self, PROPTYPE>;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @deprecated Use a constructor without \p existing and check existance with hasProperty() instead.
|
||||
*
|
||||
* Constructor.
|
||||
*
|
||||
* Throws an \p std::runtime_error if \p existing is true and
|
||||
@@ -107,22 +149,115 @@ class PropertyManager {
|
||||
* the instance merely acts as a convenience wrapper around an existing property with no
|
||||
* lifecycle management whatsoever.
|
||||
*
|
||||
* @see PropertyManager::createIfNotExists, makePropertyManagerFromNew,
|
||||
* makePropertyManagerFromExisting, makePropertyManagerFromExistingOrNew
|
||||
* @see PropertyManager::getOrMakeProperty, PropertyManager::getProperty,
|
||||
* PropertyManager::makeTemporaryProperty
|
||||
*/
|
||||
PropertyManager(MeshT &mesh, const char *propname, bool existing = false) : mesh_(&mesh), retain_(existing), name_(propname) {
|
||||
OM_DEPRECATED("Use the constructor without parameter 'existing' instead. Check for existance with hasProperty") // As long as this overload exists, initial value must be first parameter due to ambiguity for properties of type bool
|
||||
PropertyManager(PolyConnectivity& mesh, const char *propname, bool existing) : mesh_(mesh), retain_(existing), name_(propname) {
|
||||
if (existing) {
|
||||
if (!mesh_->get_property_handle(prop_, propname)) {
|
||||
if (!PropertyManager::mesh().get_property_handle(prop_, propname)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Requested property handle \"" << propname << "\" does not exist.";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
} else {
|
||||
mesh_->add_property(prop_, propname);
|
||||
PropertyManager::mesh().add_property(prop_, propname);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyManager() : mesh_(0), retain_(false) {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Asks for a property with name propname and creates one if none exists. Lifetime is not managed.
|
||||
*
|
||||
* @param mesh The mesh on which to create the property.
|
||||
* @param propname The name of the property.
|
||||
*/
|
||||
PropertyManager(PolyConnectivity& mesh, const char *propname) : mesh_(mesh), retain_(true), name_(propname) {
|
||||
if (!PropertyManager::mesh().get_property_handle(prop_, propname)) {
|
||||
PropertyManager::mesh().add_property(prop_, propname);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Asks for a property with name propname and creates one if none exists. Lifetime is not managed.
|
||||
*
|
||||
* @param initial_value If the proeprty is newly created, it will be initialized with initial_value.
|
||||
* If the property already existed, nothing is changes.
|
||||
* @param mesh The mesh on which to create the property.
|
||||
* @param propname The name of the property.
|
||||
*/
|
||||
PropertyManager(const Value& initial_value, PolyConnectivity& mesh, const char *propname) : mesh_(mesh), retain_(true), name_(propname) {
|
||||
if (!mesh_.get_property_handle(prop_, propname)) {
|
||||
PropertyManager::mesh().add_property(prop_, propname);
|
||||
set_range(mesh_.all_elements<Handle>(), initial_value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Create an anonymous property. Lifetime is managed.
|
||||
*
|
||||
* @param mesh The mesh on which to create the property.
|
||||
*/
|
||||
PropertyManager(const PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") {
|
||||
PropertyManager::mesh().add_property(prop_, name_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Create an anonymous property. Lifetime is managed.
|
||||
*
|
||||
* @param initial_value The property will be initialized with initial_value.
|
||||
* @param mesh The mesh on which to create the property.
|
||||
*/
|
||||
PropertyManager(const Value& initial_value, const PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") {
|
||||
PropertyManager::mesh().add_property(prop_, name_);
|
||||
set_range(mesh_.all_elements<Handle>(), initial_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* 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_() {
|
||||
}
|
||||
|
||||
PropertyManager() = delete;
|
||||
|
||||
PropertyManager(const PropertyManager& rhs)
|
||||
:
|
||||
mesh_(rhs.mesh_),
|
||||
prop_(),
|
||||
retain_(rhs.retain_),
|
||||
name_(rhs.name_)
|
||||
{
|
||||
if (rhs.retain_) // named property -> create a property manager referring to the same
|
||||
{
|
||||
prop_ = rhs.prop_;
|
||||
}
|
||||
else // unnamed property -> create a property manager refering to a new property and copy the contents
|
||||
{
|
||||
PropertyManager::mesh().add_property(prop_, name_);
|
||||
Storage::copy(rhs, *this);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyManager& operator=(const PropertyManager& rhs)
|
||||
{
|
||||
if (&mesh_ == &rhs.mesh_ && prop_ == rhs.prop_)
|
||||
; // nothing to do
|
||||
else
|
||||
Storage::copy(rhs, *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~PropertyManager() {
|
||||
@@ -130,49 +265,84 @@ class PropertyManager {
|
||||
}
|
||||
|
||||
void swap(PropertyManager &rhs) {
|
||||
std::swap(mesh_, rhs.mesh_);
|
||||
std::swap(prop_, rhs.prop_);
|
||||
std::swap(retain_, rhs.retain_);
|
||||
std::swap(name_, rhs.name_);
|
||||
// swap the data stored in the properties
|
||||
Storage::swap(rhs, *this);
|
||||
}
|
||||
|
||||
static bool propertyExists(MeshT &mesh, const char *propname) {
|
||||
static bool propertyExists(PolyConnectivity &mesh, const char *propname) {
|
||||
PROPTYPE dummy;
|
||||
return mesh.get_property_handle(dummy, propname);
|
||||
}
|
||||
|
||||
bool isValid() const { return mesh_ != 0; }
|
||||
bool isValid() const { return prop_.is_valid(); }
|
||||
operator bool() const { return isValid(); }
|
||||
|
||||
const PROPTYPE &getRawProperty() const { return prop_; }
|
||||
|
||||
const std::string &getName() const { return name_; }
|
||||
|
||||
MeshT &getMesh() const { return *mesh_; }
|
||||
/**
|
||||
* Get the mesh corresponding to the property.
|
||||
*
|
||||
* If you use PropertyManager without second template parameter (recommended)
|
||||
* you need to specify the actual mesh type when using this function, e.g.:
|
||||
* \code
|
||||
* {
|
||||
* TriMesh mesh;
|
||||
* auto visited = VProp<bool>(mesh);
|
||||
* TriMesh& mesh_ref = visited.getMesh<TriMesh>();
|
||||
* }
|
||||
*
|
||||
*/
|
||||
template <typename MeshType >
|
||||
const MeshType& getMesh() const { return dynamic_cast<MeshType&>(mesh_); }
|
||||
|
||||
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
/// Only for pre C++11 compatibility.
|
||||
const MeshT& getMesh() const { return dynamic_cast<MeshT&>(mesh_); }
|
||||
|
||||
typedef PropertyManager<PROPTYPE, MeshT> Proxy;
|
||||
|
||||
/**
|
||||
* @deprecated This method no longer has any effect. Instead, named properties are always retained, while unnamed ones are not
|
||||
*
|
||||
* Tells the PropertyManager whether lifetime should be managed or not.
|
||||
*/
|
||||
OM_DEPRECATED("retain no longer has any effect. Instead, named properties are always retained, while unnamed ones are not.")
|
||||
void retain(bool = true) {}
|
||||
|
||||
/**
|
||||
* Move constructor. Transfers ownership (delete responsibility).
|
||||
*/
|
||||
PropertyManager(PropertyManager &&rhs) : mesh_(rhs.mesh_), prop_(rhs.prop_), retain_(rhs.retain_), name_(rhs.name_) {
|
||||
rhs.retain_ = true;
|
||||
PropertyManager(PropertyManager &&rhs)
|
||||
:
|
||||
mesh_(rhs.mesh_),
|
||||
prop_(rhs.prop_),
|
||||
retain_(rhs.retain_),
|
||||
name_(rhs.name_)
|
||||
{
|
||||
if (!rhs.retain_)
|
||||
rhs.prop_.invalidate(); // only invalidate unnamed properties
|
||||
}
|
||||
|
||||
/**
|
||||
* Move assignment. Transfers ownership (delete responsibility).
|
||||
*/
|
||||
PropertyManager &operator=(PropertyManager &&rhs) {
|
||||
if (&rhs != this) {
|
||||
deleteProperty();
|
||||
mesh_ = rhs.mesh_;
|
||||
prop_ = rhs.prop_;
|
||||
retain_ = rhs.retain_;
|
||||
name_ = rhs.name_;
|
||||
rhs.retain_ = true;
|
||||
PropertyManager& operator=(PropertyManager&& rhs)
|
||||
{
|
||||
if ((&mesh_ != &rhs.mesh_) || (prop_ != rhs.prop_))
|
||||
{
|
||||
if (rhs.retain_)
|
||||
{
|
||||
// retained properties cannot be invalidated. Copy instead
|
||||
Storage::copy(rhs, *this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// swap the data stored in the properties
|
||||
Storage::swap(rhs, *this);
|
||||
// remove the property from rhs
|
||||
rhs.mesh().remove_property(rhs.prop_);
|
||||
// invalidate prop_
|
||||
rhs.prop_.invalidate();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -184,11 +354,8 @@ class PropertyManager {
|
||||
*
|
||||
* @see makePropertyManagerFromExistingOrNew
|
||||
*/
|
||||
static PropertyManager createIfNotExists(MeshT &mesh, const char *propname) {
|
||||
PROPTYPE dummy_prop;
|
||||
PropertyManager pm(mesh, propname, mesh.get_property_handle(dummy_prop, propname));
|
||||
pm.retain();
|
||||
return std::move(pm);
|
||||
static PropertyManager createIfNotExists(PolyConnectivity &mesh, const char *propname) {
|
||||
return PropertyManager(mesh, propname);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,7 +368,7 @@ class PropertyManager {
|
||||
* @see makePropertyManagerFromExistingOrNew
|
||||
*/
|
||||
template<typename PROP_VALUE, typename ITERATOR_TYPE>
|
||||
static PropertyManager createIfNotExists(MeshT &mesh, const char *propname,
|
||||
static PropertyManager createIfNotExists(PolyConnectivity &mesh, const char *propname,
|
||||
const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end,
|
||||
const PROP_VALUE &init_value) {
|
||||
const bool exists = propertyExists(mesh, propname);
|
||||
@@ -222,107 +389,12 @@ class PropertyManager {
|
||||
* @see makePropertyManagerFromExistingOrNew
|
||||
*/
|
||||
template<typename PROP_VALUE, typename ITERATOR_RANGE>
|
||||
static PropertyManager createIfNotExists(MeshT &mesh, const char *propname,
|
||||
static PropertyManager createIfNotExists(PolyConnectivity &mesh, const char *propname,
|
||||
const ITERATOR_RANGE &range, const PROP_VALUE &init_value) {
|
||||
return createIfNotExists(
|
||||
mesh, propname, range.begin(), range.end(), init_value);
|
||||
}
|
||||
|
||||
PropertyManager duplicate(const char *clone_name) {
|
||||
PropertyManager pm(*mesh_, clone_name, false);
|
||||
pm.mesh_->property(pm.prop_) = mesh_->property(prop_);
|
||||
return pm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Included for backwards compatibility with non-C++11 version.
|
||||
*/
|
||||
PropertyManager move() {
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
#else
|
||||
class Proxy {
|
||||
private:
|
||||
Proxy(MeshT *mesh_, PROPTYPE prop_, bool retain_, const std::string &name_) :
|
||||
mesh_(mesh_), prop_(prop_), retain_(retain_), name_(name_) {}
|
||||
MeshT *mesh_;
|
||||
PROPTYPE prop_;
|
||||
bool retain_;
|
||||
std::string name_;
|
||||
|
||||
friend class PropertyManager;
|
||||
};
|
||||
|
||||
operator Proxy() {
|
||||
Proxy p(mesh_, prop_, retain_, name_);
|
||||
mesh_ = 0;
|
||||
retain_ = true;
|
||||
return p;
|
||||
}
|
||||
|
||||
Proxy move() {
|
||||
return (Proxy)*this;
|
||||
}
|
||||
|
||||
PropertyManager(Proxy p) : mesh_(p.mesh_), prop_(p.prop_), retain_(p.retain_), name_(p.name_) {}
|
||||
|
||||
PropertyManager &operator=(Proxy p) {
|
||||
PropertyManager(p).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a property manager for the supplied property and mesh.
|
||||
* If the property doesn't exist, it is created. In any case,
|
||||
* lifecycle management is disabled.
|
||||
*
|
||||
* @see makePropertyManagerFromExistingOrNew
|
||||
*/
|
||||
static Proxy createIfNotExists(MeshT &mesh, const char *propname) {
|
||||
PROPTYPE dummy_prop;
|
||||
PropertyManager pm(mesh, propname, mesh.get_property_handle(dummy_prop, propname));
|
||||
pm.retain();
|
||||
return (Proxy)pm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like createIfNotExists() with two parameters except, if the property
|
||||
* doesn't exist, it is initialized with the supplied value over
|
||||
* the supplied range after creation. If the property already exists,
|
||||
* this method has the exact same effect as the two parameter version.
|
||||
* Lifecycle management is disabled in any case.
|
||||
*
|
||||
* @see makePropertyManagerFromExistingOrNew
|
||||
*/
|
||||
template<typename PROP_VALUE, typename ITERATOR_TYPE>
|
||||
static Proxy createIfNotExists(MeshT &mesh, const char *propname,
|
||||
const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end,
|
||||
const PROP_VALUE &init_value) {
|
||||
const bool exists = propertyExists(mesh, propname);
|
||||
PropertyManager pm(mesh, propname, exists);
|
||||
pm.retain();
|
||||
if (!exists)
|
||||
pm.set_range(begin, end, init_value);
|
||||
return (Proxy)pm;
|
||||
}
|
||||
|
||||
Proxy duplicate(const char *clone_name) {
|
||||
PropertyManager pm(*mesh_, clone_name, false);
|
||||
pm.mesh_->property(pm.prop_) = mesh_->property(prop_);
|
||||
return (Proxy)pm;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Disable lifecycle management for this property.
|
||||
*
|
||||
* If this method is called, the encapsulated property will not be deleted
|
||||
* upon destruction of the PropertyManager instance.
|
||||
*/
|
||||
inline void retain(bool doRetain = true) {
|
||||
retain_ = doRetain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the value of the encapsulated mesh property.
|
||||
@@ -337,7 +409,7 @@ class PropertyManager {
|
||||
* @note This method is only used for mesh properties.
|
||||
*/
|
||||
typename PROPTYPE::reference& operator*() {
|
||||
return mesh_->mproperty(prop_)[0];
|
||||
return mesh().mproperty(prop_)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,7 +425,7 @@ class PropertyManager {
|
||||
* @note This method is only used for mesh properties.
|
||||
*/
|
||||
typename PROPTYPE::const_reference& operator*() const {
|
||||
return mesh_->mproperty(prop_)[0];
|
||||
return mesh().mproperty(prop_)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -363,9 +435,8 @@ class PropertyManager {
|
||||
*
|
||||
* @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.)
|
||||
*/
|
||||
template<typename HandleType>
|
||||
inline typename PROPTYPE::reference operator[] (const HandleType &handle) {
|
||||
return mesh_->property(prop_, handle);
|
||||
inline typename PROPTYPE::reference operator[] (Handle handle) {
|
||||
return mesh().property(prop_, handle);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,9 +446,32 @@ class PropertyManager {
|
||||
*
|
||||
* @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.)
|
||||
*/
|
||||
template<typename HandleType>
|
||||
inline typename PROPTYPE::const_reference operator[] (const HandleType &handle) const {
|
||||
return mesh_->property(prop_, handle);
|
||||
inline typename PROPTYPE::const_reference operator[] (const Handle& handle) const {
|
||||
return mesh().property(prop_, handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables convenient access to the encapsulated property.
|
||||
*
|
||||
* For a usage example see this class' documentation.
|
||||
*
|
||||
* @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.)
|
||||
*/
|
||||
inline typename PROPTYPE::reference operator() (const Handle& handle = Handle()) {
|
||||
// return mesh().property(prop_, handle);
|
||||
return Storage::access_property(mesh(), prop_, handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables convenient access to the encapsulated property.
|
||||
*
|
||||
* For a usage example see this class' documentation.
|
||||
*
|
||||
* @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.)
|
||||
*/
|
||||
inline typename PROPTYPE::const_reference operator() (const Handle& handle = Handle()) const {
|
||||
// return mesh().property(prop_, handle);
|
||||
return Storage::access_property_const(mesh(), prop_, handle);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,7 +480,7 @@ class PropertyManager {
|
||||
* Examples:
|
||||
* \code
|
||||
* MeshT mesh;
|
||||
* PropertyManager<VPropHandleT<double>, MeshT> distance(
|
||||
* PropertyManager<VPropHandleT<double>> distance(
|
||||
* mesh, "distance.plugin-example.i8.informatik.rwth-aachen.de");
|
||||
* distance.set_range(
|
||||
* mesh.vertices_begin(), mesh.vertices_end(),
|
||||
@@ -434,9 +528,9 @@ class PropertyManager {
|
||||
* Will be used with dst_propmanager. Used to double check the bounds.
|
||||
*/
|
||||
template<typename HandleTypeIterator, typename PROPTYPE_2,
|
||||
typename MeshT_2, typename HandleTypeIterator_2>
|
||||
typename HandleTypeIterator_2>
|
||||
void copy_to(HandleTypeIterator begin, HandleTypeIterator end,
|
||||
PropertyManager<PROPTYPE_2, MeshT_2> &dst_propmanager,
|
||||
PropertyManager<PROPTYPE_2> &dst_propmanager,
|
||||
HandleTypeIterator_2 dst_begin, HandleTypeIterator_2 dst_end) const {
|
||||
|
||||
for (; begin != end && dst_begin != dst_end; ++begin, ++dst_begin) {
|
||||
@@ -445,14 +539,15 @@ class PropertyManager {
|
||||
}
|
||||
|
||||
template<typename RangeType, typename PROPTYPE_2,
|
||||
typename MeshT_2, typename RangeType_2>
|
||||
typename RangeType_2>
|
||||
void copy_to(const RangeType &range,
|
||||
PropertyManager<PROPTYPE_2, MeshT_2> &dst_propmanager,
|
||||
PropertyManager<PROPTYPE_2> &dst_propmanager,
|
||||
const RangeType_2 &dst_range) const {
|
||||
copy_to(range.begin(), range.end(), dst_propmanager,
|
||||
dst_range.begin(), dst_range.end());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy the values of a property from a source range to
|
||||
* a target range. The source range must not be smaller than the
|
||||
@@ -467,15 +562,15 @@ class PropertyManager {
|
||||
* @param dst_mesh Destination mesh on which to copy.
|
||||
* @param dst_range Destination range.
|
||||
*/
|
||||
template<typename RangeType, typename MeshT_2, typename RangeType_2>
|
||||
template<typename RangeType, typename RangeType_2>
|
||||
static void copy(const char *prop_name,
|
||||
MeshT &src_mesh, const RangeType &src_range,
|
||||
MeshT_2 &dst_mesh, const RangeType_2 &dst_range) {
|
||||
PolyConnectivity &src_mesh, const RangeType &src_range,
|
||||
PolyConnectivity &dst_mesh, const RangeType_2 &dst_range) {
|
||||
|
||||
typedef OpenMesh::PropertyManager<PROPTYPE, MeshT> DstPM;
|
||||
typedef OpenMesh::PropertyManager<PROPTYPE> DstPM;
|
||||
DstPM dst(DstPM::createIfNotExists(dst_mesh, prop_name));
|
||||
|
||||
typedef OpenMesh::PropertyManager<PROPTYPE, MeshT_2> SrcPM;
|
||||
typedef OpenMesh::PropertyManager<PROPTYPE> SrcPM;
|
||||
SrcPM src(src_mesh, prop_name, true);
|
||||
|
||||
src.copy_to(src_range, dst, dst_range);
|
||||
@@ -483,18 +578,54 @@ class PropertyManager {
|
||||
|
||||
private:
|
||||
void deleteProperty() {
|
||||
if (!retain_)
|
||||
mesh_->remove_property(prop_);
|
||||
if (!retain_ && prop_.is_valid())
|
||||
mesh().remove_property(prop_);
|
||||
}
|
||||
|
||||
PolyConnectivity& mesh() const
|
||||
{
|
||||
return const_cast<PolyConnectivity&>(mesh_);
|
||||
}
|
||||
|
||||
private:
|
||||
MeshT *mesh_;
|
||||
const PolyConnectivity& mesh_;
|
||||
PROPTYPE prop_;
|
||||
bool retain_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
template <typename PropertyT>
|
||||
class ConstPropertyViewer
|
||||
{
|
||||
public:
|
||||
using Value = typename PropertyT::Value;
|
||||
using value_type = typename PropertyT::value_type;
|
||||
using Handle = typename PropertyT::Handle;
|
||||
|
||||
ConstPropertyViewer(const PolyConnectivity& mesh, PropertyT property_handle)
|
||||
:
|
||||
mesh_(mesh),
|
||||
prop_(property_handle)
|
||||
{}
|
||||
|
||||
inline const typename PropertyT::const_reference operator() (const Handle& handle)
|
||||
{
|
||||
return mesh_.property(prop_, handle);
|
||||
}
|
||||
|
||||
inline const typename PropertyT::const_reference operator[] (const Handle& handle)
|
||||
{
|
||||
return mesh_.property(prop_, handle);
|
||||
}
|
||||
|
||||
private:
|
||||
const PolyConnectivity& mesh_;
|
||||
PropertyT prop_;
|
||||
};
|
||||
|
||||
/** @relates PropertyManager
|
||||
*
|
||||
* @deprecated Temporary properties should not have a name.
|
||||
*
|
||||
* Creates a new property whose lifetime is limited to the current scope.
|
||||
*
|
||||
@@ -517,13 +648,72 @@ class PropertyManager {
|
||||
* @param propname (optional) The name of the created property
|
||||
* @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc.
|
||||
* @tparam T Value type of the created property, e.g., \p double, \p int, etc.
|
||||
* @tparam MeshT Type of the mesh. Can often be inferred from \p mesh
|
||||
* @returns A PropertyManager handling the lifecycle of the property
|
||||
*/
|
||||
template<typename ElementT, typename T, typename MeshT>
|
||||
PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>
|
||||
makeTemporaryProperty(MeshT &mesh, const char *propname = "") {
|
||||
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>(mesh, propname, false);
|
||||
template<typename ElementT, typename T>
|
||||
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
|
||||
OM_DEPRECATED("Named temporary properties are deprecated. Either create a temporary without name or a non-temporary with name")
|
||||
makeTemporaryProperty(PolyConnectivity &mesh, const char *propname) {
|
||||
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh, propname, false);
|
||||
}
|
||||
|
||||
/** @relates PropertyManager
|
||||
*
|
||||
* Creates a new property whose lifetime is limited to the current scope.
|
||||
*
|
||||
* Used for temporary properties. Shadows any existing properties of
|
||||
* matching name and type.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* PolyMesh m;
|
||||
* {
|
||||
* auto is_quad = makeTemporaryProperty<FaceHandle, bool>(m);
|
||||
* for (auto& fh : m.faces()) {
|
||||
* is_quad[fh] = (m.valence(fh) == 4);
|
||||
* }
|
||||
* // The property is automatically removed from the mesh at the end of the scope.
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @param mesh The mesh on which the property is created
|
||||
* @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc.
|
||||
* @tparam T Value type of the created property, e.g., \p double, \p int, etc.
|
||||
* @returns A PropertyManager handling the lifecycle of the property
|
||||
*/
|
||||
template<typename ElementT, typename T>
|
||||
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
|
||||
makeTemporaryProperty(PolyConnectivity &mesh) {
|
||||
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh);
|
||||
}
|
||||
|
||||
|
||||
/** @relates PropertyManager
|
||||
*
|
||||
* Tests whether a property with the given element type, value type, and name is
|
||||
* present on the given mesh.
|
||||
*
|
||||
* * Example:
|
||||
* @code
|
||||
* PolyMesh m;
|
||||
* if (hasProperty<FaceHandle, bool>(m, "is_quad")) {
|
||||
* // We now know the property exists: getProperty won't throw.
|
||||
* auto is_quad = getProperty<FaceHandle, bool>(m, "is_quad");
|
||||
* // Use is_quad here.
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @param mesh The mesh in question
|
||||
* @param propname The property name of the expected property
|
||||
* @tparam ElementT Element type of the expected property, e.g. VertexHandle, HalfedgeHandle, etc.
|
||||
* @tparam T Value type of the expected property, e.g., \p double, \p int, etc.
|
||||
* @tparam MeshT Type of the mesh. Can often be inferred from \p mesh
|
||||
*/
|
||||
template<typename ElementT, typename T>
|
||||
bool
|
||||
hasProperty(const PolyConnectivity &mesh, const char *propname) {
|
||||
typename HandleToPropHandle<ElementT, T>::type ph;
|
||||
return mesh.get_property_handle(ph, propname);
|
||||
}
|
||||
|
||||
/** @relates PropertyManager
|
||||
@@ -551,13 +741,18 @@ makeTemporaryProperty(MeshT &mesh, const char *propname = "") {
|
||||
* @param propname The name of the created property
|
||||
* @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc.
|
||||
* @tparam T Value type of the created property, e.g., \p double, \p int, etc.
|
||||
* @tparam MeshT Type of the mesh. Can often be inferred from \p mesh
|
||||
* @returns A PropertyManager wrapping the property
|
||||
*/
|
||||
template<typename ElementT, typename T, typename MeshT>
|
||||
PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>
|
||||
getProperty(MeshT &mesh, const char *propname) {
|
||||
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>(mesh, propname, true);
|
||||
template<typename ElementT, typename T>
|
||||
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
|
||||
getProperty(PolyConnectivity &mesh, const char *propname) {
|
||||
if (!hasProperty<ElementT, T>(mesh, propname))
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Requested property handle \"" << propname << "\" does not exist.";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh, propname);
|
||||
}
|
||||
|
||||
/** @relates PropertyManager
|
||||
@@ -587,41 +782,12 @@ getProperty(MeshT &mesh, const char *propname) {
|
||||
* @param propname The name of the created property
|
||||
* @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc.
|
||||
* @tparam T Value type of the created property, e.g., \p double, \p int, etc.
|
||||
* @tparam MeshT Type of the mesh. Can often be inferred from \p mesh
|
||||
* @returns A PropertyManager wrapping the property
|
||||
*/
|
||||
template<typename ElementT, typename T, typename MeshT>
|
||||
PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>
|
||||
getOrMakeProperty(MeshT &mesh, const char *propname) {
|
||||
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>::createIfNotExists(mesh, propname);
|
||||
}
|
||||
|
||||
/** @relates PropertyManager
|
||||
*
|
||||
* Tests whether a property with the given element type, value type, and name is
|
||||
* present on the given mesh.
|
||||
*
|
||||
* * Example:
|
||||
* @code
|
||||
* PolyMesh m;
|
||||
* if (hasProperty<FaceHandle, bool>(m, "is_quad")) {
|
||||
* // We now know the property exists: getProperty won't throw.
|
||||
* auto is_quad = getProperty<FaceHandle, bool>(m, "is_quad");
|
||||
* // Use is_quad here.
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @param mesh The mesh in question
|
||||
* @param propname The property name of the expected property
|
||||
* @tparam ElementT Element type of the expected property, e.g. VertexHandle, HalfedgeHandle, etc.
|
||||
* @tparam T Value type of the expected property, e.g., \p double, \p int, etc.
|
||||
* @tparam MeshT Type of the mesh. Can often be inferred from \p mesh
|
||||
*/
|
||||
template<typename ElementT, typename T, typename MeshT>
|
||||
bool
|
||||
hasProperty(const MeshT &mesh, const char *propname) {
|
||||
typename HandleToPropHandle<ElementT, T>::type ph;
|
||||
return mesh.get_property_handle(ph, propname);
|
||||
template<typename ElementT, typename T>
|
||||
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
|
||||
getOrMakeProperty(PolyConnectivity &mesh, const char *propname) {
|
||||
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>::createIfNotExists(mesh, propname);
|
||||
}
|
||||
|
||||
/** @relates PropertyManager
|
||||
@@ -633,11 +799,11 @@ hasProperty(const MeshT &mesh, const char *propname) {
|
||||
* Intended for temporary properties. Shadows any existing properties of
|
||||
* matching name and type.
|
||||
*/
|
||||
template<typename PROPTYPE, typename MeshT>
|
||||
template<typename PROPTYPE>
|
||||
OM_DEPRECATED("Use makeTemporaryProperty instead.")
|
||||
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromNew(MeshT &mesh, const char *propname)
|
||||
PropertyManager<PROPTYPE> makePropertyManagerFromNew(PolyConnectivity &mesh, const char *propname)
|
||||
{
|
||||
return PropertyManager<PROPTYPE, MeshT>(mesh, propname, false);
|
||||
return PropertyManager<PROPTYPE>(mesh, propname, false);
|
||||
}
|
||||
|
||||
/** \relates PropertyManager
|
||||
@@ -652,9 +818,9 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromNew(MeshT &mesh, const c
|
||||
* @throws std::runtime_error if no property with the name \p propname of
|
||||
* matching type exists.
|
||||
*/
|
||||
template<typename PROPTYPE, typename MeshT>
|
||||
template<typename PROPTYPE, typename MeshT = int>
|
||||
OM_DEPRECATED("Use getProperty instead.")
|
||||
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExisting(MeshT &mesh, const char *propname)
|
||||
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExisting(PolyConnectivity &mesh, const char *propname)
|
||||
{
|
||||
return PropertyManager<PROPTYPE, MeshT>(mesh, propname, true);
|
||||
}
|
||||
@@ -667,9 +833,9 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExisting(MeshT &mesh, co
|
||||
*
|
||||
* Intended for creating or accessing persistent properties.
|
||||
*/
|
||||
template<typename PROPTYPE, typename MeshT>
|
||||
template<typename PROPTYPE, typename MeshT = int>
|
||||
OM_DEPRECATED("Use getOrMakeProperty instead.")
|
||||
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(MeshT &mesh, const char *propname)
|
||||
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(PolyConnectivity &mesh, const char *propname)
|
||||
{
|
||||
return PropertyManager<PROPTYPE, MeshT>::createIfNotExists(mesh, propname);
|
||||
}
|
||||
@@ -685,14 +851,14 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(MeshT &mes
|
||||
*
|
||||
* Intended for creating or accessing persistent properties.
|
||||
*/
|
||||
template<typename PROPTYPE, typename MeshT,
|
||||
template<typename PROPTYPE,
|
||||
typename ITERATOR_TYPE, typename PROP_VALUE>
|
||||
OM_DEPRECATED("Use getOrMakeProperty instead.")
|
||||
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(
|
||||
MeshT &mesh, const char *propname,
|
||||
PropertyManager<PROPTYPE> makePropertyManagerFromExistingOrNew(
|
||||
PolyConnectivity &mesh, const char *propname,
|
||||
const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end,
|
||||
const PROP_VALUE &init_value) {
|
||||
return PropertyManager<PROPTYPE, MeshT>::createIfNotExists(
|
||||
return PropertyManager<PROPTYPE>::createIfNotExists(
|
||||
mesh, propname, begin, end, init_value);
|
||||
}
|
||||
|
||||
@@ -707,16 +873,55 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(
|
||||
*
|
||||
* Intended for creating or accessing persistent properties.
|
||||
*/
|
||||
template<typename PROPTYPE, typename MeshT,
|
||||
template<typename PROPTYPE,
|
||||
typename ITERATOR_RANGE, typename PROP_VALUE>
|
||||
OM_DEPRECATED("Use getOrMakeProperty instead.")
|
||||
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(
|
||||
MeshT &mesh, const char *propname,
|
||||
PropertyManager<PROPTYPE> makePropertyManagerFromExistingOrNew(
|
||||
PolyConnectivity &mesh, const char *propname,
|
||||
const ITERATOR_RANGE &range,
|
||||
const PROP_VALUE &init_value) {
|
||||
return makePropertyManagerFromExistingOrNew<PROPTYPE, MeshT>(
|
||||
return makePropertyManagerFromExistingOrNew<PROPTYPE>(
|
||||
mesh, propname, range.begin(), range.end(), init_value);
|
||||
}
|
||||
|
||||
|
||||
/** @relates PropertyManager
|
||||
* Returns a convenience wrapper around the points property of a mesh.
|
||||
*/
|
||||
template<typename MeshT>
|
||||
PropertyManager<OpenMesh::VPropHandleT<typename MeshT::Point>>
|
||||
getPointsProperty(MeshT &mesh) {
|
||||
return PropertyManager<OpenMesh::VPropHandleT<typename MeshT::Point>>(mesh, mesh.points_property_handle());
|
||||
}
|
||||
|
||||
/** @relates PropertyManager
|
||||
* Returns a convenience wrapper around the points property of a mesh that only allows const access.
|
||||
*/
|
||||
template<typename MeshT>
|
||||
ConstPropertyViewer<OpenMesh::VPropHandleT<typename MeshT::Point>>
|
||||
getPointsProperty(const MeshT &mesh) {
|
||||
using PropType = OpenMesh::VPropHandleT<typename MeshT::Point>;
|
||||
return ConstPropertyViewer<PropType>(mesh, mesh.points_property_handle());
|
||||
}
|
||||
|
||||
template <typename HandleT, typename T>
|
||||
using Prop = PropertyManager<typename PropHandle<HandleT>::template type<T>>;
|
||||
|
||||
template <typename T>
|
||||
using VProp = PropertyManager<OpenMesh::VPropHandleT<T>>;
|
||||
|
||||
template <typename T>
|
||||
using HProp = PropertyManager<OpenMesh::HPropHandleT<T>>;
|
||||
|
||||
template <typename T>
|
||||
using EProp = PropertyManager<OpenMesh::EPropHandleT<T>>;
|
||||
|
||||
template <typename T>
|
||||
using FProp = PropertyManager<OpenMesh::FPropHandleT<T>>;
|
||||
|
||||
template <typename T>
|
||||
using MProp = PropertyManager<OpenMesh::MPropHandleT<T>>;
|
||||
|
||||
|
||||
} /* namespace OpenMesh */
|
||||
#endif /* PROPERTYMANAGER_HH_ */
|
||||
|
||||
@@ -51,8 +51,8 @@ protected: // SubdividerT interface
|
||||
_m.request_edge_status();
|
||||
_m.request_vertex_status();
|
||||
_m.request_face_status();
|
||||
PropertyManager<EPropHandleT<typename mesh_t::VertexHandle>, mesh_t> edge_midpoint(_m, "edge_midpoint");
|
||||
PropertyManager<VPropHandleT<bool>, mesh_t> is_original_vertex(_m, "is_original_vertex");
|
||||
PropertyManager<EPropHandleT<typename mesh_t::VertexHandle>> edge_midpoint(_m, "edge_midpoint");
|
||||
PropertyManager<VPropHandleT<bool>> is_original_vertex(_m, "is_original_vertex");
|
||||
|
||||
for (size_t iteration = 0; iteration < _n; ++iteration) {
|
||||
is_original_vertex.set_range(_m.vertices_begin(), _m.vertices_end(), true);
|
||||
|
||||
22576
src/Unittests/TestFiles/cube_noisy.off
Normal file
22576
src/Unittests/TestFiles/cube_noisy.off
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,15 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
//#define ENABLE_PROPERTY_TIMING_OUTPUT
|
||||
#ifdef ENABLE_PROPERTY_TIMING_OUTPUT
|
||||
#define N_VERTICES_TIMING 1000000
|
||||
#define TIMING_OUTPUT(X) X
|
||||
#else
|
||||
#define N_VERTICES_TIMING 10
|
||||
#define TIMING_OUTPUT(X)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
class OpenMeshPropertyManager : public OpenMeshBase {
|
||||
@@ -62,7 +71,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
|
||||
|
||||
{
|
||||
OpenMesh::PropertyManager<
|
||||
OpenMesh::VPropHandleT<bool>, Mesh> pm_v_bool(mesh_, "pm_v_bool");
|
||||
OpenMesh::VPropHandleT<bool>> pm_v_bool(mesh_, "pm_v_bool");
|
||||
pm_v_bool.set_range(mesh_.vertices_begin(), mesh_.vertices_end(), false);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
ASSERT_FALSE(pm_v_bool[vhandle[i]]);
|
||||
@@ -71,7 +80,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
|
||||
ASSERT_TRUE(pm_v_bool[vhandle[i]]);
|
||||
|
||||
OpenMesh::PropertyManager<
|
||||
OpenMesh::EPropHandleT<bool>, Mesh> pm_e_bool(mesh_, "pm_e_bool");
|
||||
OpenMesh::EPropHandleT<bool>> pm_e_bool(mesh_, "pm_e_bool");
|
||||
pm_e_bool.set_range(mesh_.edges_begin(), mesh_.edges_end(), false);
|
||||
for (Mesh::EdgeIter e_it = mesh_.edges_begin(), f_end = mesh_.edges_end();
|
||||
e_it != f_end; ++e_it)
|
||||
@@ -82,7 +91,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
|
||||
ASSERT_TRUE(pm_e_bool[*e_it]);
|
||||
|
||||
OpenMesh::PropertyManager<
|
||||
OpenMesh::FPropHandleT<bool>, Mesh> pm_f_bool(mesh_, "pm_f_bool");
|
||||
OpenMesh::FPropHandleT<bool>> pm_f_bool(mesh_, "pm_f_bool");
|
||||
pm_f_bool.set_range(mesh_.faces_begin(), mesh_.faces_end(), false);
|
||||
for (Mesh::FaceIter f_it = mesh_.faces_begin(), f_end = mesh_.faces_end();
|
||||
f_it != f_end; ++f_it)
|
||||
@@ -98,7 +107,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
|
||||
*/
|
||||
{
|
||||
OpenMesh::PropertyManager<
|
||||
OpenMesh::VPropHandleT<bool>, Mesh> pm_v_bool(mesh_, "pm_v_bool2");
|
||||
OpenMesh::VPropHandleT<bool>> pm_v_bool(mesh_, "pm_v_bool2");
|
||||
pm_v_bool.set_range(mesh_.vertices(), false);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
ASSERT_FALSE(pm_v_bool[vhandle[i]]);
|
||||
@@ -107,7 +116,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
|
||||
ASSERT_TRUE(pm_v_bool[vhandle[i]]);
|
||||
|
||||
OpenMesh::PropertyManager<
|
||||
OpenMesh::EPropHandleT<bool>, Mesh> pm_e_bool(mesh_, "pm_e_bool2");
|
||||
OpenMesh::EPropHandleT<bool>> pm_e_bool(mesh_, "pm_e_bool2");
|
||||
pm_e_bool.set_range(mesh_.edges(), false);
|
||||
for (Mesh::EdgeIter e_it = mesh_.edges_begin(), f_end = mesh_.edges_end();
|
||||
e_it != f_end; ++e_it)
|
||||
@@ -118,7 +127,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
|
||||
ASSERT_TRUE(pm_e_bool[*e_it]);
|
||||
|
||||
OpenMesh::PropertyManager<
|
||||
OpenMesh::FPropHandleT<bool>, Mesh> pm_f_bool(mesh_, "pm_f_bool2");
|
||||
OpenMesh::FPropHandleT<bool>> pm_f_bool(mesh_, "pm_f_bool2");
|
||||
pm_f_bool.set_range(mesh_.faces(), false);
|
||||
for (Mesh::FaceIter f_it = mesh_.faces_begin(), f_end = mesh_.faces_end();
|
||||
f_it != f_end; ++f_it)
|
||||
@@ -130,62 +139,6 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ====================================================================
|
||||
* Factory Functions
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
template<typename PropHandle, typename Mesh>
|
||||
bool has_property(const Mesh& _mesh, const std::string& _name) {
|
||||
auto dummy_handle = PropHandle{};
|
||||
return _mesh.get_property_handle(dummy_handle, _name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Temporary property
|
||||
*/
|
||||
TEST_F(OpenMeshPropertyManager, cpp11_temp_property) {
|
||||
using handle_type = OpenMesh::VPropHandleT<int>;
|
||||
const auto prop_name = "pm_v_test_property";
|
||||
ASSERT_FALSE(has_property<handle_type>(mesh_, prop_name));
|
||||
|
||||
{
|
||||
auto vprop = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
|
||||
static_cast<void>(vprop); // Unused variable
|
||||
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
|
||||
}
|
||||
|
||||
ASSERT_FALSE(has_property<handle_type>(mesh_, prop_name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Two temporary properties on a mesh using the same name and type. The second
|
||||
* (inner) one shadows the first (outer) one instead of aliasing.
|
||||
*/
|
||||
TEST_F(OpenMeshPropertyManager, cpp11_temp_property_shadowing) {
|
||||
auto vh = mesh_.add_vertex({0,0,0}); // Dummy vertex to attach properties to
|
||||
|
||||
using handle_type = OpenMesh::VPropHandleT<int>;
|
||||
const auto prop_name = "pm_v_test_property";
|
||||
|
||||
auto outer_prop = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
|
||||
outer_prop[vh] = 100;
|
||||
ASSERT_EQ(100, outer_prop[vh]);
|
||||
|
||||
{
|
||||
// inner_prop uses same type and name as outer_prop
|
||||
auto inner_prop = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
|
||||
inner_prop[vh] = 200;
|
||||
ASSERT_EQ(200, inner_prop[vh]);
|
||||
// End of scope: inner_prop is removed from mesh_
|
||||
}
|
||||
|
||||
// Ensure outer_prop still exists and its data has not been overwritten by inner_prop
|
||||
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
|
||||
ASSERT_EQ(100, outer_prop[vh]);
|
||||
}
|
||||
|
||||
/*
|
||||
* In sequence:
|
||||
* - add a persistent property to a mesh
|
||||
@@ -196,10 +149,9 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property_shadowing) {
|
||||
TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
|
||||
auto vh = mesh_.add_vertex({0,0,0}); // Dummy vertex to attach properties to
|
||||
|
||||
using handle_type = OpenMesh::VPropHandleT<int>;
|
||||
const auto prop_name = "pm_v_test_property";
|
||||
|
||||
ASSERT_FALSE(has_property<handle_type>(mesh_, prop_name));
|
||||
ASSERT_FALSE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
|
||||
|
||||
{
|
||||
auto prop = OpenMesh::getOrMakeProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
|
||||
@@ -207,7 +159,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
|
||||
// End of scope, property persists
|
||||
}
|
||||
|
||||
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
|
||||
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
|
||||
|
||||
{
|
||||
// Since a property of the same name and type already exists, this refers to the existing property.
|
||||
@@ -217,7 +169,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
|
||||
// End of scope, property persists
|
||||
}
|
||||
|
||||
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
|
||||
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
|
||||
|
||||
{
|
||||
// Acquire non-owning handle to the property, knowing it exists
|
||||
@@ -225,7 +177,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
|
||||
ASSERT_EQ(200, prop[vh]);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
|
||||
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
|
||||
|
||||
{
|
||||
// Attempt to acquire non-owning handle for a non-existing property
|
||||
@@ -235,7 +187,688 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
|
||||
ASSERT_THROW(code_that_throws(), std::runtime_error);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
|
||||
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OpenMeshPropertyManager, property_copy_construction) {
|
||||
for (int i = 0; i < N_VERTICES_TIMING; ++i)
|
||||
mesh_.add_vertex(Mesh::Point());
|
||||
|
||||
// unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_);
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
auto prop2 = prop1; // prop1 and prop2 should be two different properties with the same content
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13);
|
||||
}
|
||||
|
||||
// named
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids");
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
auto prop2 = prop1; // prop1 and prop2 should refere to the same property
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13);
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenMeshPropertyManager, property_move_construction) {
|
||||
for (int i = 0; i < N_VERTICES_TIMING; ++i)
|
||||
mesh_.add_vertex(Mesh::Point());
|
||||
|
||||
// unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_);
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
auto prop2 = std::move(prop1);
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "move constructing property from temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_FALSE(prop1.isValid()) << "prop1 should have been invalidated";
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13);
|
||||
}
|
||||
|
||||
// named
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids");
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
auto prop2 = std::move(prop1); // prop1 and prop2 should refere to the same property
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "move constructing from named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_TRUE(prop1.isValid()) << "named properties cannot be invalidated";
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "property is not valid anymore";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "did not copy property correctly";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OpenMeshPropertyManager, property_copying_same_mesh) {
|
||||
|
||||
for (int i = 0; i < N_VERTICES_TIMING; ++i)
|
||||
mesh_.add_vertex(Mesh::Point());
|
||||
|
||||
// unnamed to unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(3, mesh_);
|
||||
auto prop2 = OpenMesh::VProp<int>(0, mesh_);
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1;
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
}
|
||||
|
||||
// unnamed to named
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_);
|
||||
auto prop2 = OpenMesh::VProp<int>(0, mesh_, "ids");
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1;
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids");
|
||||
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
|
||||
}
|
||||
|
||||
// named to unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
|
||||
auto prop2 = OpenMesh::VProp<int>(mesh_);
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1;
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
}
|
||||
|
||||
// named to named (different names)
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
|
||||
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids4");
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1;
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
}
|
||||
|
||||
// named to named (same names)
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1; // this should be a no op
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 42);
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
|
||||
|
||||
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
|
||||
}
|
||||
|
||||
{
|
||||
auto prop1 = OpenMesh::MProp<int>(mesh_);
|
||||
*prop1 = 43;
|
||||
auto prop2 = prop1;
|
||||
|
||||
prop2 = prop1;
|
||||
|
||||
prop2 = std::move(prop1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OpenMeshPropertyManager, property_moving_same_mesh) {
|
||||
|
||||
for (int i = 0; i < N_VERTICES_TIMING; ++i)
|
||||
mesh_.add_vertex(Mesh::Point());
|
||||
|
||||
// unnamed to unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_);
|
||||
auto prop2 = OpenMesh::VProp<int>(mesh_);
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1); // this should be cheap
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
}
|
||||
|
||||
// unnamed to named
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_);
|
||||
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids");
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1);
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids");
|
||||
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
|
||||
}
|
||||
|
||||
// named to unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
|
||||
auto prop2 = OpenMesh::VProp<int>(mesh_);
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
}
|
||||
|
||||
// named to named (different names)
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
|
||||
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids4");
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
}
|
||||
|
||||
// named to named (same names)
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1); // this should be a no op
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
|
||||
|
||||
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_F(OpenMeshPropertyManager, property_copying_different_mesh) {
|
||||
|
||||
for (int i = 0; i < N_VERTICES_TIMING; ++i)
|
||||
mesh_.add_vertex(Mesh::Point());
|
||||
|
||||
auto copy = mesh_;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
copy.add_vertex(Mesh::Point());
|
||||
|
||||
// unnamed to unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(3, mesh_);
|
||||
auto prop2 = OpenMesh::VProp<int>(0, copy);
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1;
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(static_cast<int>(copy.n_vertices())-1)]) << "Property not correctly resized";
|
||||
}
|
||||
|
||||
// unnamed to named
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_);
|
||||
auto prop2 = OpenMesh::VProp<int>(0, copy, "ids");
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1;
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto prop3 = OpenMesh::VProp<int>(copy, "ids");
|
||||
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
|
||||
}
|
||||
|
||||
// named to unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
|
||||
auto prop2 = OpenMesh::VProp<int>(copy);
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1;
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
}
|
||||
|
||||
// named to named (different names)
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
|
||||
auto prop2 = OpenMesh::VProp<int>(copy, "ids4");
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1;
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
}
|
||||
|
||||
// named to named (same names)
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
auto prop2 = OpenMesh::VProp<int>(copy, "ids5");
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = prop1; // this should be a no op
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "copying property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 42);
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
|
||||
auto prop4 = OpenMesh::VProp<int>(copy, "ids5");
|
||||
EXPECT_EQ(prop4[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OpenMeshPropertyManager, property_moving_different_mesh) {
|
||||
|
||||
for (int i = 0; i < N_VERTICES_TIMING; ++i)
|
||||
mesh_.add_vertex(Mesh::Point());
|
||||
|
||||
auto copy = mesh_;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
copy.add_vertex(Mesh::Point());
|
||||
|
||||
// unnamed to unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_);
|
||||
auto prop2 = OpenMesh::VProp<int>(copy);
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1); // this should be cheap
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(static_cast<int>(copy.n_vertices())-1)]) << "Property not correctly resized";
|
||||
}
|
||||
|
||||
// unnamed to named
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_);
|
||||
auto prop2 = OpenMesh::VProp<int>(copy, "ids");
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1);
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto prop3 = OpenMesh::VProp<int>(copy, "ids");
|
||||
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
|
||||
}
|
||||
|
||||
// named to unnamed
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
|
||||
auto prop2 = OpenMesh::VProp<int>(copy);
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
}
|
||||
|
||||
// named to named (different names)
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
|
||||
auto prop2 = OpenMesh::VProp<int>(copy, "ids4");
|
||||
prop2.set_range(mesh_.vertices(), 0);
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 0);
|
||||
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
}
|
||||
|
||||
// named to named (same names)
|
||||
{
|
||||
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
auto prop2 = OpenMesh::VProp<int>(copy, "ids5");
|
||||
|
||||
auto prop6 = OpenMesh::Prop<OpenMesh::VertexHandle, int>(mesh_);
|
||||
prop6 = prop1;
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
prop1[vh] = vh.idx()*2-13;
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
prop2 = std::move(prop1); // should copy
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "moving property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
prop1.set_range(mesh_.vertices(), 42);
|
||||
|
||||
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
|
||||
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
|
||||
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
|
||||
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
|
||||
auto prop4 = OpenMesh::VProp<int>(copy, "ids5");
|
||||
EXPECT_EQ(prop4[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OpenMeshPropertyManager, temporary_property_on_const_mesh) {
|
||||
|
||||
const auto& const_ref = mesh_;
|
||||
|
||||
auto cog = OpenMesh::FProp<Mesh::Point>(const_ref);
|
||||
auto points = OpenMesh::getPointsProperty(const_ref);
|
||||
|
||||
for (auto fh : const_ref.faces())
|
||||
cog(fh) = fh.vertices().avg(points);
|
||||
|
||||
auto cog_copy = cog;
|
||||
|
||||
for (auto fh : const_ref.faces())
|
||||
{
|
||||
EXPECT_NE(&cog(fh), &cog_copy(fh)) << "Both properties point to the same memory";
|
||||
EXPECT_EQ(cog(fh), cog_copy(fh)) << "Property not copied correctly";
|
||||
}
|
||||
|
||||
auto description = OpenMesh::MProp<std::string>(const_ref);
|
||||
description() = "Cool Const Mesh";
|
||||
|
||||
std::cout << description(OpenMesh::MeshHandle(33)) << std::endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
OpenMesh::VProp<int> get_id_prop(const OpenMesh::PolyConnectivity& mesh)
|
||||
{
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
auto id_prop = OpenMesh::VProp<int>(mesh);
|
||||
for (auto vh : mesh.vertices())
|
||||
id_prop(vh) = vh.idx();
|
||||
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "Time spend in function: " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
return id_prop;
|
||||
}
|
||||
|
||||
TEST_F(OpenMeshPropertyManager, return_property_from_function) {
|
||||
|
||||
for (int i = 0; i < N_VERTICES_TIMING; ++i)
|
||||
mesh_.add_vertex(Mesh::Point());
|
||||
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
auto id_p = get_id_prop(mesh_);
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
TIMING_OUTPUT(std::cout << "Time spend around function " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
{
|
||||
EXPECT_EQ(id_p(vh), vh.idx()) << "Property not returned correctly" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
565
src/Unittests/unittests_smart_handles.cc
Normal file
565
src/Unittests/unittests_smart_handles.cc
Normal file
@@ -0,0 +1,565 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <Unittests/unittests_common.hh>
|
||||
|
||||
#include <OpenMesh/Core/Mesh/SmartHandles.hh>
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
namespace {
|
||||
|
||||
class OpenMeshSmartHandles : public OpenMeshBase {
|
||||
|
||||
protected:
|
||||
|
||||
// This function is called before each test is run
|
||||
virtual void SetUp() {
|
||||
|
||||
mesh_.clear();
|
||||
|
||||
// Add some vertices
|
||||
Mesh::VertexHandle vhandle[8];
|
||||
vhandle[0] = mesh_.add_vertex(Mesh::Point(-1, -1, 1));
|
||||
vhandle[1] = mesh_.add_vertex(Mesh::Point( 1, -1, 1));
|
||||
vhandle[2] = mesh_.add_vertex(Mesh::Point( 1, 1, 1));
|
||||
vhandle[3] = mesh_.add_vertex(Mesh::Point(-1, 1, 1));
|
||||
vhandle[4] = mesh_.add_vertex(Mesh::Point(-1, -1, -1));
|
||||
vhandle[5] = mesh_.add_vertex(Mesh::Point( 1, -1, -1));
|
||||
vhandle[6] = mesh_.add_vertex(Mesh::Point( 1, 1, -1));
|
||||
vhandle[7] = mesh_.add_vertex(Mesh::Point(-1, 1, -1));
|
||||
|
||||
// Add six faces to form a cube
|
||||
std::vector<Mesh::VertexHandle> face_vhandles;
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[0]);
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[2]);
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
face_vhandles.push_back(vhandle[6]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
face_vhandles.push_back(vhandle[4]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[0]);
|
||||
face_vhandles.push_back(vhandle[4]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[4]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[2]);
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[2]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
face_vhandles.push_back(vhandle[6]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
face_vhandles.push_back(vhandle[2]);
|
||||
face_vhandles.push_back(vhandle[6]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
face_vhandles.push_back(vhandle[6]);
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[0]);
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[0]);
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
face_vhandles.push_back(vhandle[4]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
|
||||
// Test setup:
|
||||
//
|
||||
//
|
||||
// 3 ======== 2
|
||||
// / /|
|
||||
// / / | z
|
||||
// 0 ======== 1 | |
|
||||
// | | | | y
|
||||
// | 7 | 6 | /
|
||||
// | | / | /
|
||||
// | |/ |/
|
||||
// 4 ======== 5 -------> x
|
||||
//
|
||||
|
||||
// Check setup
|
||||
EXPECT_EQ(18u, mesh_.n_edges() ) << "Wrong number of Edges";
|
||||
EXPECT_EQ(36u, mesh_.n_halfedges() ) << "Wrong number of HalfEdges";
|
||||
EXPECT_EQ(8u, mesh_.n_vertices() ) << "Wrong number of vertices";
|
||||
EXPECT_EQ(12u, mesh_.n_faces() ) << "Wrong number of faces";
|
||||
}
|
||||
|
||||
// This function is called after all tests are through
|
||||
virtual void TearDown() {
|
||||
|
||||
// Do some final stuff with the member data here...
|
||||
|
||||
mesh_.clear();
|
||||
}
|
||||
|
||||
// Member already defined in OpenMeshBase
|
||||
//Mesh mesh_;
|
||||
};
|
||||
|
||||
/*
|
||||
* ====================================================================
|
||||
* Define tests below
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* Test if navigation operations on smart handles yield the expected element
|
||||
*/
|
||||
TEST_F(OpenMeshSmartHandles, SimpleNavigation)
|
||||
{
|
||||
for (auto vh : mesh_.vertices())
|
||||
{
|
||||
EXPECT_EQ(mesh_.halfedge_handle(vh), vh.halfedge()) << "outgoing halfedge of vertex does not match";
|
||||
}
|
||||
|
||||
for (auto heh : mesh_.halfedges())
|
||||
{
|
||||
EXPECT_EQ(mesh_.next_halfedge_handle(heh), heh.next()) << "next halfedge of halfedge does not match";
|
||||
EXPECT_EQ(mesh_.prev_halfedge_handle(heh), heh.prev()) << "prevt halfedge of halfedge does not match";
|
||||
EXPECT_EQ(mesh_.opposite_halfedge_handle(heh), heh.opp()) << "opposite halfedge of halfedge does not match";
|
||||
EXPECT_EQ(mesh_.to_vertex_handle(heh), heh.to()) << "to vertex handle of halfedge does not match";
|
||||
EXPECT_EQ(mesh_.from_vertex_handle(heh), heh.from()) << "from vertex handle of halfedge does not match";
|
||||
EXPECT_EQ(mesh_.face_handle(heh), heh.face()) << "face handle of halfedge does not match";
|
||||
}
|
||||
|
||||
for (auto eh : mesh_.edges())
|
||||
{
|
||||
EXPECT_EQ(mesh_.halfedge_handle(eh, 0), eh.h0()) << "halfedge 0 of edge does not match";
|
||||
EXPECT_EQ(mesh_.halfedge_handle(eh, 1), eh.h1()) << "halfedge 1 of edge does not match";
|
||||
EXPECT_EQ(mesh_.from_vertex_handle(mesh_.halfedge_handle(eh, 0)), eh.v0()) << "first vertex of edge does not match";
|
||||
EXPECT_EQ(mesh_.to_vertex_handle (mesh_.halfedge_handle(eh, 0)), eh.v1()) << "second vertex of edge does not match";
|
||||
}
|
||||
|
||||
for (auto fh : mesh_.faces())
|
||||
{
|
||||
EXPECT_EQ(mesh_.halfedge_handle(fh), fh.halfedge()) << "halfedge of face does not match";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Test if ranges yield the same elements when using smart handles
|
||||
*/
|
||||
TEST_F(OpenMeshSmartHandles, SimpleRanges)
|
||||
{
|
||||
for (auto vh : mesh_.vertices())
|
||||
{
|
||||
{
|
||||
std::vector<OpenMesh::VertexHandle> handles0;
|
||||
std::vector<OpenMesh::VertexHandle> handles1;
|
||||
for (auto h : mesh_.vv_range(vh))
|
||||
handles0.push_back(h);
|
||||
for (auto h : vh.vertices())
|
||||
handles1.push_back(h);
|
||||
EXPECT_EQ(handles0, handles1) << "vertex range of vertex does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles0;
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles1;
|
||||
for (auto h : mesh_.voh_range(vh))
|
||||
handles0.push_back(h);
|
||||
for (auto h : vh.outgoing_halfedges())
|
||||
handles1.push_back(h);
|
||||
EXPECT_EQ(handles0, handles1) << "outgoing halfedge range of vertex does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles0;
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles1;
|
||||
for (auto h : mesh_.vih_range(vh))
|
||||
handles0.push_back(h);
|
||||
for (auto h : vh.incoming_halfedges())
|
||||
handles1.push_back(h);
|
||||
EXPECT_EQ(handles0, handles1) << "incoming halfedge range of vertex does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::EdgeHandle> handles0;
|
||||
std::vector<OpenMesh::EdgeHandle> handles1;
|
||||
for (auto h : mesh_.ve_range(vh))
|
||||
handles0.push_back(h);
|
||||
for (auto h : vh.edges())
|
||||
handles1.push_back(h);
|
||||
EXPECT_EQ(handles0, handles1) << "edge range of vertex does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::FaceHandle> handles0;
|
||||
std::vector<OpenMesh::FaceHandle> handles1;
|
||||
for (auto h : mesh_.vf_range(vh))
|
||||
handles0.push_back(h);
|
||||
for (auto h : vh.faces())
|
||||
handles1.push_back(h);
|
||||
EXPECT_EQ(handles0, handles1) << "face range of vertex does not match";
|
||||
}
|
||||
}
|
||||
|
||||
for (auto fh : mesh_.faces())
|
||||
{
|
||||
{
|
||||
std::vector<OpenMesh::VertexHandle> handles0;
|
||||
std::vector<OpenMesh::VertexHandle> handles1;
|
||||
for (auto h : mesh_.fv_range(fh))
|
||||
handles0.push_back(h);
|
||||
for (auto h : fh.vertices())
|
||||
handles1.push_back(h);
|
||||
EXPECT_EQ(handles0, handles1) << "vertex range of face does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles0;
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles1;
|
||||
for (auto h : mesh_.fh_range(fh))
|
||||
handles0.push_back(h);
|
||||
for (auto h : fh.halfedges())
|
||||
handles1.push_back(h);
|
||||
EXPECT_EQ(handles0, handles1) << "halfedge range of face does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::EdgeHandle> handles0;
|
||||
std::vector<OpenMesh::EdgeHandle> handles1;
|
||||
for (auto h : mesh_.fe_range(fh))
|
||||
handles0.push_back(h);
|
||||
for (auto h : fh.edges())
|
||||
handles1.push_back(h);
|
||||
EXPECT_EQ(handles0, handles1) << "edge range of face does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::FaceHandle> handles0;
|
||||
std::vector<OpenMesh::FaceHandle> handles1;
|
||||
for (auto h : mesh_.ff_range(fh))
|
||||
handles0.push_back(h);
|
||||
for (auto h : fh.faces())
|
||||
handles1.push_back(h);
|
||||
EXPECT_EQ(handles0, handles1) << "face range of face does not match";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test if ranges yield the same elements when using smart handles
|
||||
*/
|
||||
TEST_F(OpenMeshSmartHandles, RangesOfRanges)
|
||||
{
|
||||
for (auto vh : mesh_.vertices())
|
||||
{
|
||||
{
|
||||
std::vector<OpenMesh::VertexHandle> handles0;
|
||||
std::vector<OpenMesh::VertexHandle> handles1;
|
||||
for (auto h : mesh_.vv_range(vh))
|
||||
for (auto h2 : mesh_.vv_range(h))
|
||||
handles0.push_back(h2);
|
||||
for (auto h : vh.vertices())
|
||||
for (auto h2 : h.vertices())
|
||||
handles1.push_back(h2);
|
||||
EXPECT_EQ(handles0, handles1) << "vertex range of vertex range does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles0;
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles1;
|
||||
for (auto h : mesh_.vv_range(vh))
|
||||
for (auto h2 : mesh_.voh_range(h))
|
||||
handles0.push_back(h2);
|
||||
for (auto h : vh.vertices())
|
||||
for (auto h2 : h.outgoing_halfedges())
|
||||
handles1.push_back(h2);
|
||||
EXPECT_EQ(handles0, handles1) << "outgoing halfedge range of vertex range does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles0;
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles1;
|
||||
for (auto h : mesh_.vv_range(vh))
|
||||
for (auto h2 : mesh_.vih_range(h))
|
||||
handles0.push_back(h2);
|
||||
for (auto h : vh.vertices())
|
||||
for (auto h2 : h.incoming_halfedges())
|
||||
handles1.push_back(h2);
|
||||
EXPECT_EQ(handles0, handles1) << "incoming halfedge range of vertex range does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::EdgeHandle> handles0;
|
||||
std::vector<OpenMesh::EdgeHandle> handles1;
|
||||
for (auto h : mesh_.vv_range(vh))
|
||||
for (auto h2 : mesh_.ve_range(h))
|
||||
handles0.push_back(h2);
|
||||
for (auto h : vh.vertices())
|
||||
for (auto h2 : h.edges())
|
||||
handles1.push_back(h2);
|
||||
EXPECT_EQ(handles0, handles1) << "edge range of vertex range does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::FaceHandle> handles0;
|
||||
std::vector<OpenMesh::FaceHandle> handles1;
|
||||
for (auto h : mesh_.vv_range(vh))
|
||||
for (auto h2 : mesh_.vf_range(h))
|
||||
handles0.push_back(h2);
|
||||
for (auto h : vh.vertices())
|
||||
for (auto h2 : h.faces())
|
||||
handles1.push_back(h2);
|
||||
EXPECT_EQ(handles0, handles1) << "face range of vertex range does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::VertexHandle> handles0;
|
||||
std::vector<OpenMesh::VertexHandle> handles1;
|
||||
for (auto h : mesh_.vf_range(vh))
|
||||
for (auto h2 : mesh_.fv_range(h))
|
||||
handles0.push_back(h2);
|
||||
for (auto h : vh.faces())
|
||||
for (auto h2 : h.vertices())
|
||||
handles1.push_back(h2);
|
||||
EXPECT_EQ(handles0, handles1) << "vertex range of face range does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles0;
|
||||
std::vector<OpenMesh::HalfedgeHandle> handles1;
|
||||
for (auto h : mesh_.vf_range(vh))
|
||||
for (auto h2 : mesh_.fh_range(h))
|
||||
handles0.push_back(h2);
|
||||
for (auto h : vh.faces())
|
||||
for (auto h2 : h.halfedges())
|
||||
handles1.push_back(h2);
|
||||
EXPECT_EQ(handles0, handles1) << "vertex range of face range does not match";
|
||||
}
|
||||
{
|
||||
std::vector<OpenMesh::FaceHandle> handles0;
|
||||
std::vector<OpenMesh::FaceHandle> handles1;
|
||||
for (auto h : mesh_.vf_range(vh))
|
||||
for (auto h2 : mesh_.ff_range(h))
|
||||
handles0.push_back(h2);
|
||||
for (auto h : vh.faces())
|
||||
for (auto h2 : h.faces())
|
||||
handles1.push_back(h2);
|
||||
EXPECT_EQ(handles0, handles1) << "vertex range of face range does not match";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Test a chain of navigation on a cube
|
||||
*/
|
||||
TEST_F(OpenMeshSmartHandles, ComplicatedNavigtaion)
|
||||
{
|
||||
for (auto vh : mesh_.vertices())
|
||||
{
|
||||
EXPECT_EQ(mesh_.next_halfedge_handle(
|
||||
mesh_.opposite_halfedge_handle(
|
||||
mesh_.halfedge_handle(vh))),
|
||||
vh.out().opp().next());
|
||||
EXPECT_EQ(mesh_.prev_halfedge_handle(
|
||||
mesh_.prev_halfedge_handle(
|
||||
mesh_.opposite_halfedge_handle(
|
||||
mesh_.next_halfedge_handle(
|
||||
mesh_.next_halfedge_handle(
|
||||
mesh_.halfedge_handle(vh)))))),
|
||||
vh.out().next().next().opp().prev().prev());
|
||||
EXPECT_EQ(mesh_.face_handle(
|
||||
mesh_.opposite_halfedge_handle(
|
||||
mesh_.halfedge_handle(
|
||||
mesh_.face_handle(
|
||||
mesh_.opposite_halfedge_handle(
|
||||
mesh_.next_halfedge_handle(
|
||||
mesh_.halfedge_handle(vh))))))),
|
||||
vh.out().next().opp().face().halfedge().opp().face());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Test performance of smart handles
|
||||
*/
|
||||
TEST_F(OpenMeshSmartHandles, Performance)
|
||||
{
|
||||
#if DEBUG
|
||||
int n_tests = 300000;
|
||||
#else
|
||||
int n_tests = 10000000;
|
||||
#endif
|
||||
|
||||
auto t_before_old = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::vector<OpenMesh::HalfedgeHandle> halfedges0;
|
||||
for (int i = 0; i < n_tests; ++i)
|
||||
{
|
||||
for (auto vh : mesh_.vertices())
|
||||
{
|
||||
auto heh = mesh_.prev_halfedge_handle(
|
||||
mesh_.prev_halfedge_handle(
|
||||
mesh_.opposite_halfedge_handle(
|
||||
mesh_.next_halfedge_handle(
|
||||
mesh_.next_halfedge_handle(
|
||||
mesh_.halfedge_handle(vh))))));
|
||||
if (i == 0)
|
||||
halfedges0.push_back(heh);
|
||||
}
|
||||
}
|
||||
|
||||
auto t_after_old = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::vector<OpenMesh::HalfedgeHandle> halfedges1;
|
||||
for (int i = 0; i < n_tests; ++i)
|
||||
{
|
||||
for (auto vh : mesh_.vertices())
|
||||
{
|
||||
auto heh = vh.out().next().next().opp().prev().prev();
|
||||
if (i == 0)
|
||||
halfedges1.push_back(heh);
|
||||
}
|
||||
}
|
||||
|
||||
auto t_after_new = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::cout << "Conventional navigation took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_after_old-t_before_old).count() << "ms" << std::endl;
|
||||
std::cout << "SmartHandle navigation took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_after_new-t_after_old ).count() << "ms" << std::endl;
|
||||
|
||||
EXPECT_EQ(halfedges0, halfedges1) << "halfedges do not match";
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Mix old and new api
|
||||
*/
|
||||
TEST_F(OpenMeshSmartHandles, MixOldAndNew)
|
||||
{
|
||||
for (OpenMesh::SmartHalfedgeHandle heh : mesh_.halfedges())
|
||||
{
|
||||
heh = mesh_.opposite_halfedge_handle(heh);
|
||||
OpenMesh::SmartEdgeHandle eh = OpenMesh::PolyConnectivity::s_edge_handle(heh);
|
||||
OpenMesh::SmartEdgeHandle eh2 = mesh_.edge_handle(heh);
|
||||
OpenMesh::SmartFaceHandle fh = mesh_.face_handle(heh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* comparability
|
||||
*/
|
||||
TEST_F(OpenMeshSmartHandles, ComparisionBetweenSmartHandleAndNormalHandles)
|
||||
{
|
||||
OpenMesh::VertexHandle vh(0);
|
||||
OpenMesh::SmartVertexHandle svh(0, &mesh_);
|
||||
EXPECT_EQ(vh, svh) << "Vertex handle and smart vertex handle are different";
|
||||
|
||||
std::vector<OpenMesh::VertexHandle> vertices = mesh_.vertices().to_vector([](OpenMesh::SmartVertexHandle _svh) { return OpenMesh::VertexHandle(_svh); });
|
||||
|
||||
std::replace(vertices.begin(), vertices.end(), OpenMesh::VertexHandle(0), OpenMesh::VertexHandle(1));
|
||||
EXPECT_EQ(vertices[0], OpenMesh::VertexHandle(1));
|
||||
|
||||
std::vector<OpenMesh::SmartVertexHandle> smart_vertices = mesh_.vertices().to_vector();
|
||||
|
||||
std::replace(smart_vertices.begin(), smart_vertices.end(), OpenMesh::SmartVertexHandle(0, &mesh_), OpenMesh::SmartVertexHandle(1, &mesh_));
|
||||
EXPECT_EQ(smart_vertices[0], OpenMesh::VertexHandle(1));
|
||||
EXPECT_EQ(smart_vertices[0], OpenMesh::SmartVertexHandle(1, &mesh_));
|
||||
|
||||
std::replace(vertices.begin(), vertices.end(), OpenMesh::SmartVertexHandle(1, &mesh_), OpenMesh::SmartVertexHandle(2, &mesh_));
|
||||
EXPECT_EQ(vertices[0], OpenMesh::VertexHandle(2));
|
||||
|
||||
}
|
||||
|
||||
TEST(OpenMeshSmartHandlesNoFixture, AddingFacesPolyMesh)
|
||||
{
|
||||
using MyMesh = OpenMesh::PolyMesh_ArrayKernelT<>;
|
||||
|
||||
MyMesh mesh;
|
||||
|
||||
std::vector<OpenMesh::SmartVertexHandle> vertices;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
vertices.push_back(mesh.add_vertex(MyMesh::Point()));
|
||||
|
||||
auto fh = mesh.add_face(vertices);
|
||||
|
||||
for (auto heh : fh.halfedges())
|
||||
{
|
||||
heh = heh.next();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(OpenMeshSmartHandlesNoFixture, AddingFacesTriMesh)
|
||||
{
|
||||
using MyMesh = OpenMesh::TriMesh_ArrayKernelT<>;
|
||||
|
||||
MyMesh mesh;
|
||||
|
||||
std::vector<OpenMesh::SmartVertexHandle> vertices;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
vertices.push_back(mesh.add_vertex(MyMesh::Point()));
|
||||
|
||||
auto fh = mesh.add_face(vertices);
|
||||
|
||||
for (auto heh : fh.halfedges())
|
||||
{
|
||||
heh = heh.next();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(OpenMeshSmartHandlesNoFixture, SplitTriMesh)
|
||||
{
|
||||
using MyMesh = OpenMesh::TriMesh_ArrayKernelT<>;
|
||||
|
||||
MyMesh mesh;
|
||||
|
||||
std::vector<OpenMesh::SmartVertexHandle> vertices;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
vertices.push_back(mesh.add_vertex(MyMesh::Point()));
|
||||
|
||||
auto fh = mesh.add_face(vertices);
|
||||
|
||||
auto p = (MyMesh::Point());
|
||||
|
||||
OpenMesh::SmartVertexHandle vh = mesh.split(fh, p);
|
||||
|
||||
OpenMesh::SmartEdgeHandle eh = fh.halfedge().edge();
|
||||
OpenMesh::SmartVertexHandle vh2 = mesh.split(eh, p);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
285
src/Unittests/unittests_smart_ranges.cc
Normal file
285
src/Unittests/unittests_smart_ranges.cc
Normal file
@@ -0,0 +1,285 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <Unittests/unittests_common.hh>
|
||||
|
||||
#include <OpenMesh/Core/Mesh/SmartHandles.hh>
|
||||
#include <OpenMesh/Core/Utils/PropertyManager.hh>
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
namespace {
|
||||
|
||||
class OpenMeshSmartRanges : public OpenMeshBase {
|
||||
|
||||
protected:
|
||||
|
||||
// This function is called before each test is run
|
||||
virtual void SetUp() {
|
||||
|
||||
mesh_.clear();
|
||||
|
||||
// Add some vertices
|
||||
Mesh::VertexHandle vhandle[8];
|
||||
vhandle[0] = mesh_.add_vertex(Mesh::Point(-1, -1, 1));
|
||||
vhandle[1] = mesh_.add_vertex(Mesh::Point( 1, -1, 1));
|
||||
vhandle[2] = mesh_.add_vertex(Mesh::Point( 1, 1, 1));
|
||||
vhandle[3] = mesh_.add_vertex(Mesh::Point(-1, 1, 1));
|
||||
vhandle[4] = mesh_.add_vertex(Mesh::Point(-1, -1, -1));
|
||||
vhandle[5] = mesh_.add_vertex(Mesh::Point( 1, -1, -1));
|
||||
vhandle[6] = mesh_.add_vertex(Mesh::Point( 1, 1, -1));
|
||||
vhandle[7] = mesh_.add_vertex(Mesh::Point(-1, 1, -1));
|
||||
|
||||
// Add six faces to form a cube
|
||||
std::vector<Mesh::VertexHandle> face_vhandles;
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[0]);
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[2]);
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
face_vhandles.push_back(vhandle[6]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
face_vhandles.push_back(vhandle[4]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[0]);
|
||||
face_vhandles.push_back(vhandle[4]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[4]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[2]);
|
||||
face_vhandles.push_back(vhandle[1]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[2]);
|
||||
face_vhandles.push_back(vhandle[5]);
|
||||
face_vhandles.push_back(vhandle[6]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
face_vhandles.push_back(vhandle[2]);
|
||||
face_vhandles.push_back(vhandle[6]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
face_vhandles.push_back(vhandle[6]);
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
//=======================
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[0]);
|
||||
face_vhandles.push_back(vhandle[3]);
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
face_vhandles.clear();
|
||||
face_vhandles.push_back(vhandle[0]);
|
||||
face_vhandles.push_back(vhandle[7]);
|
||||
face_vhandles.push_back(vhandle[4]);
|
||||
mesh_.add_face(face_vhandles);
|
||||
|
||||
|
||||
// Test setup:
|
||||
//
|
||||
//
|
||||
// 3 ======== 2
|
||||
// / /|
|
||||
// / / | z
|
||||
// 0 ======== 1 | |
|
||||
// | | | | y
|
||||
// | 7 | 6 | /
|
||||
// | | / | /
|
||||
// | |/ |/
|
||||
// 4 ======== 5 -------> x
|
||||
//
|
||||
|
||||
// Check setup
|
||||
EXPECT_EQ(18u, mesh_.n_edges() ) << "Wrong number of Edges";
|
||||
EXPECT_EQ(36u, mesh_.n_halfedges() ) << "Wrong number of HalfEdges";
|
||||
EXPECT_EQ(8u, mesh_.n_vertices() ) << "Wrong number of vertices";
|
||||
EXPECT_EQ(12u, mesh_.n_faces() ) << "Wrong number of faces";
|
||||
}
|
||||
|
||||
// This function is called after all tests are through
|
||||
virtual void TearDown() {
|
||||
|
||||
// Do some final stuff with the member data here...
|
||||
|
||||
mesh_.clear();
|
||||
}
|
||||
|
||||
// Member already defined in OpenMeshBase
|
||||
//Mesh mesh_;
|
||||
};
|
||||
|
||||
/*
|
||||
* ====================================================================
|
||||
* Define tests below
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
|
||||
template <typename HandleT>
|
||||
struct F
|
||||
{
|
||||
int operator()(HandleT ) { return 1; }
|
||||
};
|
||||
|
||||
/* Test if smart ranges work
|
||||
*/
|
||||
TEST_F(OpenMeshSmartRanges, Sum)
|
||||
{
|
||||
auto one = [](OpenMesh::VertexHandle ) { return 1; };
|
||||
EXPECT_EQ(mesh_.vertices().sum(one), mesh_.n_vertices());
|
||||
EXPECT_EQ(mesh_.vertices().sum(F<OpenMesh::VertexHandle>()), mesh_.n_vertices());
|
||||
EXPECT_EQ(mesh_.halfedges().sum(F<OpenMesh::HalfedgeHandle>()), mesh_.n_halfedges());
|
||||
EXPECT_EQ(mesh_.edges().sum(F<OpenMesh::EdgeHandle>()), mesh_.n_edges());
|
||||
EXPECT_EQ(mesh_.faces().sum(F<OpenMesh::FaceHandle>()), mesh_.n_faces());
|
||||
|
||||
for (auto vh : mesh_.vertices())
|
||||
EXPECT_EQ(vh.vertices().sum(F<OpenMesh::VertexHandle>()), mesh_.valence(vh));
|
||||
for (auto vh : mesh_.vertices())
|
||||
EXPECT_EQ(vh.faces().sum(F<OpenMesh::FaceHandle>()), mesh_.valence(vh));
|
||||
for (auto vh : mesh_.vertices())
|
||||
EXPECT_EQ(vh.outgoing_halfedges().sum(F<OpenMesh::HalfedgeHandle>()), mesh_.valence(vh));
|
||||
for (auto vh : mesh_.vertices())
|
||||
EXPECT_EQ(vh.incoming_halfedges().sum(F<OpenMesh::HalfedgeHandle>()), mesh_.valence(vh));
|
||||
|
||||
for (auto fh : mesh_.faces())
|
||||
EXPECT_EQ(fh.vertices().sum(F<OpenMesh::VertexHandle>()), mesh_.valence(fh));
|
||||
for (auto fh : mesh_.faces())
|
||||
EXPECT_EQ(fh.halfedges().sum(F<OpenMesh::HalfedgeHandle>()), mesh_.valence(fh));
|
||||
for (auto fh : mesh_.faces())
|
||||
EXPECT_EQ(fh.edges().sum(F<OpenMesh::EdgeHandle>()), mesh_.valence(fh));
|
||||
for (auto fh : mesh_.faces())
|
||||
EXPECT_EQ(fh.faces().sum(F<OpenMesh::FaceHandle>()), 3);
|
||||
}
|
||||
|
||||
|
||||
/* Test if Property Manager can be used in smart ranges
|
||||
*/
|
||||
TEST_F(OpenMeshSmartRanges, PropertyManagerAsFunctor)
|
||||
{
|
||||
auto myPos = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, Mesh::Point>(mesh_);
|
||||
for (auto vh : mesh_.vertices())
|
||||
myPos(vh) = mesh_.point(vh);
|
||||
|
||||
Mesh::Point cog(0,0,0);
|
||||
for (auto vh : mesh_.vertices())
|
||||
cog += mesh_.point(vh);
|
||||
cog /= mesh_.n_vertices();
|
||||
|
||||
auto cog2 = mesh_.vertices().avg(myPos);
|
||||
|
||||
EXPECT_LT(norm(cog - cog2), 0.00001) << "Computed center of gravities are significantly different.";
|
||||
}
|
||||
|
||||
/* Test to vector
|
||||
*/
|
||||
TEST_F(OpenMeshSmartRanges, ToVector)
|
||||
{
|
||||
auto uvs = OpenMesh::makeTemporaryProperty<OpenMesh::HalfedgeHandle, OpenMesh::Vec2d>(mesh_);
|
||||
|
||||
for (auto heh : mesh_.halfedges())
|
||||
uvs(heh) = OpenMesh::Vec2d(heh.idx(), (heh.idx() * 13)%7);
|
||||
|
||||
for (auto fh : mesh_.faces())
|
||||
{
|
||||
auto tri_uvs = fh.halfedges().to_vector(uvs);
|
||||
auto heh_handles = fh.halfedges().to_vector();
|
||||
for (auto heh : heh_handles)
|
||||
auto n = heh.next();
|
||||
}
|
||||
|
||||
auto vertex_vec = mesh_.vertices().to_vector();
|
||||
for (auto vh : vertex_vec)
|
||||
auto heh = vh.out();
|
||||
}
|
||||
|
||||
/* Test to array
|
||||
*/
|
||||
TEST_F(OpenMeshSmartRanges, ToArray)
|
||||
{
|
||||
auto uvs = OpenMesh::makeTemporaryProperty<OpenMesh::HalfedgeHandle, OpenMesh::Vec2d>(mesh_);
|
||||
|
||||
for (auto heh : mesh_.halfedges())
|
||||
uvs(heh) = OpenMesh::Vec2d(heh.idx(), (heh.idx() * 13)%7);
|
||||
|
||||
for (auto fh : mesh_.faces())
|
||||
{
|
||||
auto tri_uvs = fh.halfedges().to_array<3>(uvs);
|
||||
auto heh_handles = fh.halfedges().to_array<3>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Test bounding box
|
||||
*/
|
||||
TEST_F(OpenMeshSmartRanges, BoundingBox)
|
||||
{
|
||||
// The custom vecs OpenMesh are tested with here do not implement a min or max function.
|
||||
// Thus we convert here.
|
||||
auto myPos = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, OpenMesh::Vec3f>(mesh_);
|
||||
for (auto vh : mesh_.vertices())
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
myPos(vh)[i] = mesh_.point(vh)[i];
|
||||
|
||||
auto bb_min = mesh_.vertices().min(myPos);
|
||||
auto bb_max = mesh_.vertices().max(myPos);
|
||||
auto bb = mesh_.vertices().minmax(myPos);
|
||||
|
||||
EXPECT_LT(norm(bb_min - OpenMesh::Vec3f(-1,-1,-1)), 0.000001) << "Bounding box minimum seems off";
|
||||
EXPECT_LT(norm(bb_max - OpenMesh::Vec3f( 1, 1, 1)), 0.000001) << "Bounding box maximum seems off";
|
||||
|
||||
|
||||
auto uvs = OpenMesh::makeTemporaryProperty<OpenMesh::HalfedgeHandle, OpenMesh::Vec2d>(mesh_);
|
||||
for (auto heh : mesh_.halfedges())
|
||||
uvs(heh) = OpenMesh::Vec2d(heh.idx(), (heh.idx() * 13)%7);
|
||||
|
||||
for (auto fh : mesh_.faces())
|
||||
{
|
||||
auto uv_bb_min = fh.halfedges().min(uvs);
|
||||
auto uv_bb_max = fh.halfedges().max(uvs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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'";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user