Merge branch 'CustomProperties' of https://www.graphics.rwth-aachen.de:9000/OpenMesh/OpenMesh into CustomProperties
# Conflicts: # src/OpenMesh/Core/IO/writer/OMWriter.cc
This commit is contained in:
58
Doc/Tutorial/12-predicates/predicates.cc
Normal file
58
Doc/Tutorial/12-predicates/predicates.cc
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
#include <OpenMesh/Core/IO/MeshIO.hh>
|
||||
#include <OpenMesh/Core/Mesh/DefaultTriMesh.hh>
|
||||
#include <OpenMesh/Core/Utils/PropertyManager.hh>
|
||||
#include <OpenMesh/Core/Utils/Predicates.hh>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using MyMesh = OpenMesh::TriMesh;
|
||||
|
||||
bool is_divisible_by_3(OpenMesh::FaceHandle vh) { return vh.idx() % 3 == 0; }
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace OpenMesh::Predicates; // for easier access to predicates
|
||||
|
||||
// Read command line options
|
||||
MyMesh mesh;
|
||||
if (argc != 4) {
|
||||
std::cerr << "Usage: " << argv[0] << " infile" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
const std::string infile = argv[1];
|
||||
|
||||
// Read mesh file
|
||||
if (!OpenMesh::IO::read_mesh(mesh, infile)) {
|
||||
std::cerr << "Error: Cannot read mesh from " << infile << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Count boundary vertices
|
||||
std::cout << "Mesh contains " << mesh.vertices().count_if(Boundary()) << " boundary vertices";
|
||||
|
||||
// Selected inner vertices
|
||||
std::cout << "These are the selected inner vertices: " << std::endl;
|
||||
for (auto vh : mesh.vertices().filtered(!Boundary() && Selected()))
|
||||
std::cout << vh.idx() << ", ";
|
||||
std::cout << std::endl;
|
||||
|
||||
// Faces whose id is divisible by 3
|
||||
auto vec = mesh.faces().filtered(is_divisible_by_3).to_vector();
|
||||
std::cout << "There are " << vec.size() << " faces whose id is divisible by 3" << std::endl;
|
||||
|
||||
// Faces which are tagged or whose id is not divisible by 3
|
||||
auto vec2 = mesh.faces().filtered(Tagged() || !make_predicate(is_divisible_by_3)).to_vector();
|
||||
std::cout << "There are " << vec2.size() << " faces which are tagged or whose id is not divisible by 3" << std::endl;
|
||||
|
||||
// Edges that are longer than 10 or shorter than 2
|
||||
OpenMesh::EProp<bool> longer_than_10(mesh);
|
||||
for (auto eh : mesh.edges())
|
||||
longer_than_10[eh] = mesh.calc_edge_length(eh) > 10;
|
||||
std::cout << "There are " <<
|
||||
mesh.edges().count_if(make_predicate(longer_than_10) || make_predicate([&](OpenMesh::EdgeHandle eh) { return mesh.calc_edge_length(eh) < 2; })) <<
|
||||
" edges which are shorter than 2 or longer than 10" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
<ul>
|
||||
<li>Add filtered range that stores reference instead of copy if the filter is not an rvalue reference</li>
|
||||
<li>Add halfedge loop range corresponding to hl_iter()</li>
|
||||
<li>Smart handles now give read access to status fields (feature(), selected(), deleted(), tagged(), tagged2(), hidden(), locked()</li>
|
||||
<li>Add predicates that can be used as smart range filters. There are default predicates to test the status fields and whether an elements is boundary. Predicates can be composed via operators ||, && and !</li>
|
||||
<li>Add make_predicate to create a predicate from anything containing a bool operator(T), e.g. a PropertyManager, a lambda, or a function pointer.</li>
|
||||
</ul>
|
||||
|
||||
<b>IO</b>
|
||||
<ul>
|
||||
<li>STL Reader: Change stl reader behaviour on extension .stl , Don't check for the solid keyword explicitly</li>
|
||||
</ul>
|
||||
|
||||
<b>Build System</b>
|
||||
|
||||
@@ -83,6 +83,7 @@ repeatedly replacing each vertex' position by the center of gravity
|
||||
\li \ref tutorial_03
|
||||
\li \ref tutorial_04
|
||||
\li \ref tutorial_11
|
||||
\li \ref tutorial_12
|
||||
\li \ref tutorial_05
|
||||
\li \ref tutorial_06
|
||||
\li \ref tutorial_07
|
||||
|
||||
@@ -71,7 +71,7 @@ Similarily we compute the update vector as the laplace of the freshly computed l
|
||||
|
||||
Finally, we apply the update after damping it by a factor of -0.5.
|
||||
|
||||
\skipline udpate points
|
||||
\skipline update points
|
||||
\until bi_laplace
|
||||
|
||||
Below is the complete source code:
|
||||
|
||||
38
Doc/tutorial_12.docu
Normal file
38
Doc/tutorial_12.docu
Normal file
@@ -0,0 +1,38 @@
|
||||
/** \page tutorial_12 Filtering ranges with predicates
|
||||
|
||||
This examples shows:
|
||||
- How to use predicates to filter which elements of a mesh you want iterate over
|
||||
|
||||
In the previous tutorial we discussed already that the ranges returned by functions like all_vertices(), voh_range() or outgoing_halfedges() provide a few helpful methods such as avg() or to_vector(). Another interesting method is filtered() which requires as argument something that can be called for an element of the range and returns a bool. The resulting range will then only iterate over elements for which the filter returs true.
|
||||
The filter can be a lambda, a function pointer, a property manager holding a bool property, or a functor object such as the predicates defined in <OpenMesh/Core/Utils/Predicates.hh>. The predefined predicates can check the status of a mesh element and test if they are boundary.
|
||||
With their help you can for example count all boundary vertices:
|
||||
|
||||
\dontinclude 12-predicates/predicates.cc
|
||||
\skipline Count boundary vertices
|
||||
\until boundary vertices
|
||||
|
||||
Predicates can be composed using the operators ||, &&, and !. This enables you to specify precisely which elements you want process in your loop, e.g. inner vertices that are selected:
|
||||
|
||||
\skipline Selected inner vertices
|
||||
\until std::cout << std::endl
|
||||
|
||||
As mentioned above, the filter argument can be anything returning a bool for a given input, e.g. a function pointer:
|
||||
|
||||
\skipline Faces whose id is divisible by 3
|
||||
\until faces whose id is divisible by 3
|
||||
|
||||
However, function pointers, lambdas etc are not composable. Fortunately, you can use make_predicate to turn them into composable predicates:
|
||||
|
||||
|
||||
\skipline Faces which are tagged or whose id is not divisible by 3
|
||||
\until faces which are tagged or whose id is not divisible by 3
|
||||
|
||||
Below is the complete source code:
|
||||
|
||||
\include 12-predicates/predicates.cc
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@@ -34,6 +34,7 @@ repeatedly replacing each vertex' position by the center of gravity
|
||||
<li> \subpage tutorial_03
|
||||
<li> \subpage tutorial_04
|
||||
<li> \subpage tutorial_11
|
||||
<li> \subpage tutorial_12
|
||||
<li> \subpage tutorial_05
|
||||
<li> \subpage tutorial_06
|
||||
<li> \subpage tutorial_07
|
||||
|
||||
@@ -63,7 +63,6 @@ int main(int argc, char **argv)
|
||||
#endif
|
||||
|
||||
// OpenGL check
|
||||
QApplication::setColorSpec( QApplication::CustomColor );
|
||||
QApplication app(argc,argv);
|
||||
|
||||
if ( !QGLFormat::hasOpenGL() ) {
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// OpenGL check
|
||||
QApplication::setColorSpec( QApplication::CustomColor );
|
||||
QApplication app(argc,argv);
|
||||
|
||||
if ( !QGLFormat::hasOpenGL() ) {
|
||||
@@ -67,7 +66,7 @@ int main(int argc, char **argv)
|
||||
|
||||
|
||||
// create widget
|
||||
ProgViewerWidget w(0);
|
||||
ProgViewerWidget w(nullptr);
|
||||
w.resize(400, 400);
|
||||
w.show();
|
||||
|
||||
|
||||
@@ -60,12 +60,11 @@ void usage_and_exit(int xcode);
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// OpenGL check
|
||||
QApplication::setColorSpec( QApplication::CustomColor );
|
||||
QApplication app(argc,argv);
|
||||
|
||||
if ( !QGLFormat::hasOpenGL() ) {
|
||||
QString msg = "System has no OpenGL support!";
|
||||
QMessageBox::critical( 0, QString("OpenGL"), msg + QString(argv[1]) );
|
||||
QMessageBox::critical( nullptr, QString("OpenGL"), msg + QString(argv[1]) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// OpenGL check
|
||||
QApplication::setColorSpec( QApplication::CustomColor );
|
||||
QApplication app(argc,argv);
|
||||
|
||||
if ( !QGLFormat::hasOpenGL() ) {
|
||||
@@ -66,7 +65,7 @@ int main(int argc, char **argv)
|
||||
|
||||
|
||||
// create widget
|
||||
SubdivideWidget* w = new SubdivideWidget(0, "Subdivider");
|
||||
SubdivideWidget* w = new SubdivideWidget(nullptr, "Subdivider");
|
||||
|
||||
w->resize(400, 400);
|
||||
w->show();
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// OpenGL check
|
||||
QApplication::setColorSpec( QApplication::CustomColor );
|
||||
QApplication app(argc,argv);
|
||||
|
||||
|
||||
@@ -66,7 +65,7 @@ int main(int argc, char **argv)
|
||||
|
||||
// create widget
|
||||
VDPMSynthesizerViewerWidget*
|
||||
w = new VDPMSynthesizerViewerWidget(0, "VDPMSynthesizerViewer");
|
||||
w = new VDPMSynthesizerViewerWidget(nullptr, "VDPMSynthesizerViewer");
|
||||
|
||||
w->resize(400, 400);
|
||||
w->show();
|
||||
|
||||
@@ -451,32 +451,7 @@ _STLReader_::
|
||||
check_stl_type(const std::string& _filename) const
|
||||
{
|
||||
|
||||
// open file
|
||||
std::ifstream ifs (_filename.c_str(), std::ifstream::binary);
|
||||
if(!ifs.good())
|
||||
{
|
||||
omerr() << "could not open file" << _filename << std::endl;
|
||||
return NONE;
|
||||
}
|
||||
|
||||
//find first non whitespace character
|
||||
std::string line = "";
|
||||
std::size_t firstChar;
|
||||
while(line.empty() && ifs.good())
|
||||
{
|
||||
std::getline(ifs,line);
|
||||
firstChar = line.find_first_not_of("\t ");
|
||||
}
|
||||
|
||||
//check for ascii keyword solid
|
||||
if(strnicmp("solid",&line[firstChar],5) == 0)
|
||||
{
|
||||
return STLA;
|
||||
}
|
||||
ifs.close();
|
||||
|
||||
//if the file does not start with solid it is probably STLB
|
||||
//check the file size to verify it.
|
||||
// Check the file size if it matches the binary value given after the header.
|
||||
|
||||
//open the file
|
||||
FILE* in = fopen(_filename.c_str(), "rb");
|
||||
@@ -493,7 +468,6 @@ check_stl_type(const std::string& _filename) const
|
||||
fread(dummy, 1, 80, in);
|
||||
size_t nT = read_int(in, swapFlag);
|
||||
|
||||
|
||||
// compute file size from nT
|
||||
size_t binary_size = 84 + nT*50;
|
||||
|
||||
@@ -504,8 +478,8 @@ check_stl_type(const std::string& _filename) const
|
||||
file_size += fread(dummy, 1, 100, in);
|
||||
fclose(in);
|
||||
|
||||
// if sizes match -> it's STLB
|
||||
return (binary_size == file_size ? STLB : NONE);
|
||||
// if sizes match -> it's STLB otherwise STLA is assumed
|
||||
return (binary_size == file_size ? STLB : STLA);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -623,7 +623,6 @@ size_t _OMWriter_::store_binary_custom_chunk(std::ostream& _os,
|
||||
// 3. data type
|
||||
OMFormat::Chunk::PropertyType type = OMFormat::Chunk::PropertyType::UnknownType;
|
||||
|
||||
|
||||
OpenMesh::PropertyT<bool>* bp_bool = dynamic_cast<OpenMesh::PropertyT<bool>*>(&_bp);
|
||||
OpenMesh::PropertyT<char>* bp_char = dynamic_cast<OpenMesh::PropertyT<char>* >(&_bp);
|
||||
OpenMesh::PropertyT<double>* bp_double = dynamic_cast<OpenMesh::PropertyT<double>* >(&_bp);
|
||||
@@ -636,7 +635,6 @@ size_t _OMWriter_::store_binary_custom_chunk(std::ostream& _os,
|
||||
OpenMesh::PropertyT<ulong>* bp_ulong = dynamic_cast<OpenMesh::PropertyT<ulong>* >(&_bp);
|
||||
OpenMesh::PropertyT<std::vector<double>>* bp_vecdouble = dynamic_cast<OpenMesh::PropertyT<std::vector<double>>* >(&_bp);
|
||||
|
||||
|
||||
//choose one of both tests
|
||||
if(_bp.internal_type_name() == get_type_name<bool>() || bp_bool != nullptr)
|
||||
type = OMFormat::Chunk::PropertyType::BoolType;
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#error Do not include this directly, include instead PolyConnectivity.hh
|
||||
#endif//OPENMESH_POLYCONNECTIVITY_INTERFACE_INCLUDE
|
||||
|
||||
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
|
||||
|
||||
//== NAMESPACES ===============================================================
|
||||
|
||||
namespace OpenMesh {
|
||||
@@ -73,8 +75,38 @@ private:
|
||||
|
||||
};
|
||||
|
||||
/// Base class for all smart handle types that contains status related methods
|
||||
template <typename HandleType>
|
||||
class SmartHandleStatusPredicates
|
||||
{
|
||||
public:
|
||||
/// Returns true iff the handle is marked as feature
|
||||
bool feature() const;
|
||||
/// Returns true iff the handle is marked as selected
|
||||
bool selected() const;
|
||||
/// Returns true iff the handle is marked as tagged
|
||||
bool tagged() const;
|
||||
/// Returns true iff the handle is marked as tagged2
|
||||
bool tagged2() const;
|
||||
/// Returns true iff the handle is marked as locked
|
||||
bool locked() const;
|
||||
/// Returns true iff the handle is marked as hidden
|
||||
bool hidden() const;
|
||||
/// Returns true iff the handle is marked as deleted
|
||||
bool deleted() const;
|
||||
};
|
||||
|
||||
/// Base class for all smart handle types that contains status related methods
|
||||
template <typename HandleType>
|
||||
class SmartHandleBoundaryPredicate
|
||||
{
|
||||
public:
|
||||
/// Returns true iff the handle is boundary
|
||||
bool is_boundary() const;
|
||||
};
|
||||
|
||||
/// Smart version of VertexHandle contains a pointer to the corresponding mesh and allows easier access to navigation methods
|
||||
struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandle
|
||||
struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandle, SmartHandleStatusPredicates<SmartVertexHandle>, SmartHandleBoundaryPredicate<SmartVertexHandle>
|
||||
{
|
||||
explicit SmartVertexHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), VertexHandle(_idx) {}
|
||||
|
||||
@@ -98,13 +130,11 @@ struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandl
|
||||
|
||||
/// 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
|
||||
struct OPENMESHDLLEXPORT SmartHalfedgeHandle : public SmartBaseHandle, HalfedgeHandle, SmartHandleStatusPredicates<SmartHalfedgeHandle>, SmartHandleBoundaryPredicate<SmartHalfedgeHandle>
|
||||
{
|
||||
explicit SmartHalfedgeHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), HalfedgeHandle(_idx) {}
|
||||
|
||||
@@ -125,12 +155,9 @@ struct OPENMESHDLLEXPORT SmartHalfedgeHandle : public SmartBaseHandle, HalfedgeH
|
||||
|
||||
/// Returns a range of halfedges in the face of the halfedge (or along the boundary) (PolyConnectivity::hl_range())
|
||||
PolyConnectivity::ConstHalfedgeLoopRange loop() 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
|
||||
struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle, SmartHandleStatusPredicates<SmartEdgeHandle>, SmartHandleBoundaryPredicate<SmartEdgeHandle>
|
||||
{
|
||||
explicit SmartEdgeHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), EdgeHandle(_idx) {}
|
||||
|
||||
@@ -150,12 +177,9 @@ struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle
|
||||
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
|
||||
struct OPENMESHDLLEXPORT SmartFaceHandle : public SmartBaseHandle, FaceHandle, SmartHandleStatusPredicates<SmartFaceHandle>, SmartHandleBoundaryPredicate<SmartFaceHandle>
|
||||
{
|
||||
explicit SmartFaceHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), FaceHandle(_idx) {}
|
||||
|
||||
@@ -173,8 +197,6 @@ struct OPENMESHDLLEXPORT SmartFaceHandle : public SmartBaseHandle, FaceHandle
|
||||
|
||||
/// 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -207,6 +229,70 @@ template <> struct SmartHandle<EdgeHandle> { using type = SmartEdgeHandle;
|
||||
template <> struct SmartHandle<FaceHandle> { using type = SmartFaceHandle; };
|
||||
|
||||
|
||||
template <typename HandleType>
|
||||
inline bool SmartHandleStatusPredicates<HandleType>::feature() const
|
||||
{
|
||||
const auto& handle = static_cast<const HandleType&>(*this);
|
||||
assert(handle.mesh() != nullptr);
|
||||
return handle.mesh()->status(handle).feature();
|
||||
}
|
||||
|
||||
template <typename HandleType>
|
||||
inline bool SmartHandleStatusPredicates<HandleType>::selected() const
|
||||
{
|
||||
const auto& handle = static_cast<const HandleType&>(*this);
|
||||
assert(handle.mesh() != nullptr);
|
||||
return handle.mesh()->status(handle).selected();
|
||||
}
|
||||
|
||||
template <typename HandleType>
|
||||
inline bool SmartHandleStatusPredicates<HandleType>::tagged() const
|
||||
{
|
||||
const auto& handle = static_cast<const HandleType&>(*this);
|
||||
assert(handle.mesh() != nullptr);
|
||||
return handle.mesh()->status(handle).tagged();
|
||||
}
|
||||
|
||||
template <typename HandleType>
|
||||
inline bool SmartHandleStatusPredicates<HandleType>::tagged2() const
|
||||
{
|
||||
const auto& handle = static_cast<const HandleType&>(*this);
|
||||
assert(handle.mesh() != nullptr);
|
||||
return handle.mesh()->status(handle).tagged2();
|
||||
}
|
||||
|
||||
template <typename HandleType>
|
||||
inline bool SmartHandleStatusPredicates<HandleType>::locked() const
|
||||
{
|
||||
const auto& handle = static_cast<const HandleType&>(*this);
|
||||
assert(handle.mesh() != nullptr);
|
||||
return handle.mesh()->status(handle).locked();
|
||||
}
|
||||
|
||||
template <typename HandleType>
|
||||
inline bool SmartHandleStatusPredicates<HandleType>::hidden() const
|
||||
{
|
||||
const auto& handle = static_cast<const HandleType&>(*this);
|
||||
assert(handle.mesh() != nullptr);
|
||||
return handle.mesh()->status(handle).hidden();
|
||||
}
|
||||
|
||||
template <typename HandleType>
|
||||
inline bool SmartHandleStatusPredicates<HandleType>::deleted() const
|
||||
{
|
||||
const auto& handle = static_cast<const HandleType&>(*this);
|
||||
assert(handle.mesh() != nullptr);
|
||||
return handle.mesh()->status(handle).deleted();
|
||||
}
|
||||
|
||||
template <typename HandleType>
|
||||
inline bool SmartHandleBoundaryPredicate<HandleType>::is_boundary() const
|
||||
{
|
||||
const auto& handle = static_cast<const HandleType&>(*this);
|
||||
assert(handle.mesh() != nullptr);
|
||||
return handle.mesh()->is_boundary(handle);
|
||||
}
|
||||
|
||||
inline SmartHalfedgeHandle SmartVertexHandle::out() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
@@ -229,12 +315,6 @@ inline uint SmartVertexHandle::valence() const
|
||||
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);
|
||||
@@ -283,12 +363,6 @@ inline SmartFaceHandle SmartHalfedgeHandle::face() const
|
||||
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);
|
||||
@@ -330,12 +404,6 @@ 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);
|
||||
@@ -348,11 +416,6 @@ inline uint SmartFaceHandle::valence() const
|
||||
return mesh()->valence(*this);
|
||||
}
|
||||
|
||||
inline bool SmartFaceHandle::is_boundary() const
|
||||
{
|
||||
assert(mesh() != nullptr);
|
||||
return mesh()->is_boundary(*this);
|
||||
}
|
||||
//=============================================================================
|
||||
} // namespace OpenMesh
|
||||
//=============================================================================
|
||||
|
||||
@@ -124,6 +124,34 @@ struct SmartRangeT
|
||||
return (1.0 / n_elements) * result;
|
||||
}
|
||||
|
||||
/** @brief Computes the weighted average of elements.
|
||||
*
|
||||
* Computes the weighted 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.
|
||||
* @param w Functor returning element weight.
|
||||
*/
|
||||
template <typename Functor, typename WeightFunctor>
|
||||
auto avg(Functor&& f, WeightFunctor&& w) -> typename std::decay<decltype ((1.0/(w(std::declval<HandleT>())+w(std::declval<HandleT>())))*f(std::declval<HandleT>()))>::type
|
||||
{
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
auto begin = range->begin();
|
||||
auto end = range->end();
|
||||
assert(begin != end);
|
||||
typename std::decay<decltype (w(*begin))>::type weight = w(*begin);
|
||||
typename std::decay<decltype (w(*begin)*f(*begin))>::type result = weight * f(*begin);
|
||||
typename std::decay<decltype (w(*begin)+w(*begin))>::type weight_sum = weight;
|
||||
auto it = begin;
|
||||
++it;
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
weight = w(*it);
|
||||
result += weight*f(*it);
|
||||
weight_sum += weight;
|
||||
}
|
||||
return (1.0 / weight_sum) * result;
|
||||
}
|
||||
|
||||
/** @brief Check if any element fulfils condition.
|
||||
*
|
||||
* Checks if functor \p f returns true for any of the elements in the range.
|
||||
@@ -400,27 +428,10 @@ struct SmartRangeT
|
||||
* @param f Functor that needs to be evaluated to true if the element should not be skipped.
|
||||
*/
|
||||
template <typename Functor>
|
||||
auto filtered(Functor&& f) -> FilteredSmartRangeT<SmartRange, Handle, typename std::decay<Functor>::type>
|
||||
auto filtered(Functor&& f) -> FilteredSmartRangeT<SmartRange, Handle, Functor>
|
||||
{
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
auto b = (*range).begin();
|
||||
auto e = (*range).end();
|
||||
return FilteredSmartRangeT<SmartRange, Handle, typename std::decay<Functor>::type>(f, b, e);
|
||||
}
|
||||
|
||||
/** @brief Only iterate over a subset of elements
|
||||
*
|
||||
* Returns a smart range which skips all elements that do not satisfy functor \p f
|
||||
*
|
||||
* @param f Functor that needs to be evaluated to true if the element should not be skipped.
|
||||
*/
|
||||
template <typename Functor>
|
||||
auto filtered(Functor& f) -> FilteredSmartRangeT<SmartRange, Handle, const typename std::decay<Functor>::type&>
|
||||
{
|
||||
auto range = static_cast<const RangeT*>(this);
|
||||
auto b = (*range).begin();
|
||||
auto e = (*range).end();
|
||||
return FilteredSmartRangeT<SmartRange, Handle, const typename std::decay<Functor>::type&>(f, b, e);
|
||||
return FilteredSmartRangeT<SmartRange, Handle, Functor>(std::forward<Functor>(f), (*range).begin(), (*range).end());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -460,11 +471,12 @@ struct FilteredSmartRangeT : public SmartRangeT<FilteredSmartRangeT<RangeT, Hand
|
||||
return *this;
|
||||
}
|
||||
|
||||
Functor f_;
|
||||
Functor f_; // Should iterators always get a reference to filter stored in range?
|
||||
// Should iterators stay valid after range goes out of scope?
|
||||
BaseIterator end_;
|
||||
};
|
||||
|
||||
FilteredSmartRangeT(Functor f, BaseIterator begin, BaseIterator end) : f_(f), begin_(begin), end_(end){}
|
||||
FilteredSmartRangeT(Functor&& f, BaseIterator begin, BaseIterator end) : f_(std::forward<Functor>(f)), begin_(std::move(begin)), end_(std::move(end)){}
|
||||
FilteredIterator begin() const { return FilteredIterator(f_, begin_, end_); }
|
||||
FilteredIterator end() const { return FilteredIterator(f_, end_, end_); }
|
||||
|
||||
|
||||
293
src/OpenMesh/Core/Utils/Predicates.hh
Normal file
293
src/OpenMesh/Core/Utils/Predicates.hh
Normal file
@@ -0,0 +1,293 @@
|
||||
/* ========================================================================= *
|
||||
* *
|
||||
* OpenMesh *
|
||||
* Copyright (c) 2001-2020, 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 <OpenMesh/Core/Mesh/PolyConnectivity.hh>
|
||||
#include <OpenMesh/Core/Utils/PropertyManager.hh>
|
||||
|
||||
#include <utility>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
|
||||
//== NAMESPACES ===============================================================
|
||||
|
||||
namespace OpenMesh {
|
||||
|
||||
namespace Predicates {
|
||||
|
||||
//== FORWARD DECLARATION ======================================================
|
||||
|
||||
//== CLASS DEFINITION =========================================================
|
||||
|
||||
template <typename PredicateT>
|
||||
struct PredicateBase
|
||||
{
|
||||
};
|
||||
|
||||
template <typename PredicateT>
|
||||
struct Predicate : public PredicateBase<Predicate<PredicateT>>
|
||||
{
|
||||
Predicate(PredicateT _p)
|
||||
:
|
||||
p_(_p)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T& _t) const { return p_(_t); }
|
||||
|
||||
PredicateT p_;
|
||||
};
|
||||
|
||||
template <typename PredicateT>
|
||||
Predicate<const PredicateT&> make_predicate(PredicateT& _p) { return { _p }; }
|
||||
|
||||
template <typename PredicateT>
|
||||
Predicate<PredicateT> make_predicate(PredicateT&& _p) { return { _p }; }
|
||||
|
||||
template <typename Predicate1T, typename Predicate2T>
|
||||
struct Disjunction : public PredicateBase<Disjunction<Predicate1T, Predicate2T>>
|
||||
{
|
||||
Disjunction(Predicate1T _p1, Predicate2T _p2)
|
||||
:
|
||||
p1_(_p1),
|
||||
p2_(_p2)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T& _t) const { return p1_( _t) || p2_( _t); }
|
||||
|
||||
Predicate1T p1_;
|
||||
Predicate2T p2_;
|
||||
};
|
||||
|
||||
template <typename Predicate1T, typename Predicate2T>
|
||||
struct Conjunction : public PredicateBase<Conjunction<Predicate1T, Predicate2T>>
|
||||
{
|
||||
Conjunction(Predicate1T _p1, Predicate2T _p2)
|
||||
:
|
||||
p1_(_p1),
|
||||
p2_(_p2)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T& _t) const { return p1_( _t) && p2_( _t); }
|
||||
|
||||
Predicate1T p1_;
|
||||
Predicate2T p2_;
|
||||
};
|
||||
|
||||
|
||||
template <typename PredicateT>
|
||||
struct Negation : public PredicateBase<Negation<PredicateT>>
|
||||
{
|
||||
Negation(const PredicateT& _p1)
|
||||
:
|
||||
p1_(_p1)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T& _t) const { return !p1_( _t); }
|
||||
|
||||
PredicateT p1_;
|
||||
};
|
||||
|
||||
template <typename P1, typename P2>
|
||||
Disjunction<const P1&, const P2&> operator||(PredicateBase<P1>& p1, PredicateBase<P2>& p2)
|
||||
{
|
||||
return Disjunction<const P1&, const P2&>(static_cast<const P1&>(p1), static_cast<const P2&>(p2));
|
||||
}
|
||||
|
||||
template <typename P1, typename P2>
|
||||
Disjunction<const P1&, P2> operator||(PredicateBase<P1>& p1, PredicateBase<P2>&& p2)
|
||||
{
|
||||
return Disjunction<const P1&, P2>(static_cast<const P1&>(p1), static_cast<P2&&>(p2));
|
||||
}
|
||||
|
||||
template <typename P1, typename P2>
|
||||
Disjunction<P1, const P2&> operator||(PredicateBase<P1>&& p1, PredicateBase<P2>& p2)
|
||||
{
|
||||
return Disjunction<P1, const P2&>(static_cast<P1&&>(p1), static_cast<const P2&>(p2));
|
||||
}
|
||||
|
||||
template <typename P1, typename P2>
|
||||
Disjunction<P1, P2> operator||(PredicateBase<P1>&& p1, PredicateBase<P2>&& p2)
|
||||
{
|
||||
return Disjunction<P1, P2>(static_cast<P1&&>(p1), static_cast<P2&&>(p2));
|
||||
}
|
||||
|
||||
template <typename P1, typename P2>
|
||||
Conjunction<const P1&, const P2&> operator&&(PredicateBase<P1>& p1, PredicateBase<P2>& p2)
|
||||
{
|
||||
return Conjunction<const P1&, const P2&>(static_cast<const P1&>(p1), static_cast<const P2&>(p2));
|
||||
}
|
||||
|
||||
template <typename P1, typename P2>
|
||||
Conjunction<const P1&, P2> operator&&(PredicateBase<P1>& p1, PredicateBase<P2>&& p2)
|
||||
{
|
||||
return Conjunction<const P1&, P2>(static_cast<const P1&>(p1), static_cast<P2&&>(p2));
|
||||
}
|
||||
|
||||
template <typename P1, typename P2>
|
||||
Conjunction<P1, const P2&> operator&&(PredicateBase<P1>&& p1, PredicateBase<P2>& p2)
|
||||
{
|
||||
return Conjunction<P1, const P2&>(static_cast<P1>(p1), static_cast<const P2&>(p2));
|
||||
}
|
||||
|
||||
template <typename P1, typename P2>
|
||||
Conjunction<P1, P2> operator&&(PredicateBase<P1>&& p1, PredicateBase<P2>&& p2)
|
||||
{
|
||||
return Conjunction<P1, P2>(static_cast<P1&&>(p1), static_cast<P2&&>(p2));
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
Negation<const P&> operator!(PredicateBase<P>& p)
|
||||
{
|
||||
return Negation<const P&>(static_cast<const P&>(p));
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
Negation<P> operator!(PredicateBase<P>&& p)
|
||||
{
|
||||
return Negation<P>(static_cast<P&&>(p));
|
||||
}
|
||||
|
||||
struct Feature : public PredicateBase<Feature>
|
||||
{
|
||||
template <typename HandleType>
|
||||
bool operator()(const SmartHandleStatusPredicates<HandleType>& _h) const { return _h.feature(); }
|
||||
};
|
||||
|
||||
struct Selected : public PredicateBase<Selected>
|
||||
{
|
||||
template <typename HandleType>
|
||||
bool operator()(const SmartHandleStatusPredicates<HandleType>& _h) const { return _h.selected(); }
|
||||
};
|
||||
|
||||
struct Tagged : public PredicateBase<Tagged>
|
||||
{
|
||||
template <typename HandleType>
|
||||
bool operator()(const SmartHandleStatusPredicates<HandleType>& _h) const { return _h.tagged(); }
|
||||
};
|
||||
|
||||
struct Tagged2 : public PredicateBase<Tagged2>
|
||||
{
|
||||
template <typename HandleType>
|
||||
bool operator()(const SmartHandleStatusPredicates<HandleType>& _h) const { return _h.tagged2(); }
|
||||
};
|
||||
|
||||
struct Locked : public PredicateBase<Locked>
|
||||
{
|
||||
template <typename HandleType>
|
||||
bool operator()(const SmartHandleStatusPredicates<HandleType>& _h) const { return _h.locked(); }
|
||||
};
|
||||
|
||||
struct Hidden : public PredicateBase<Hidden>
|
||||
{
|
||||
template <typename HandleType>
|
||||
bool operator()(const SmartHandleStatusPredicates<HandleType>& _h) const { return _h.hidden(); }
|
||||
};
|
||||
|
||||
struct Deleted : public PredicateBase<Deleted>
|
||||
{
|
||||
template <typename HandleType>
|
||||
bool operator()(const SmartHandleStatusPredicates<HandleType>& _h) const { return _h.deleted(); }
|
||||
};
|
||||
|
||||
struct Boundary : public PredicateBase<Boundary>
|
||||
{
|
||||
template <typename HandleType>
|
||||
bool operator()(const SmartHandleBoundaryPredicate<HandleType>& _h) const { return _h.is_boundary(); }
|
||||
};
|
||||
|
||||
template <int inner_reg, int boundary_reg>
|
||||
struct Regular: public PredicateBase<Regular<inner_reg, boundary_reg>>
|
||||
{
|
||||
bool operator()(const SmartVertexHandle& _vh) const { return _vh.valence() == (_vh.is_boundary() ? boundary_reg : inner_reg); }
|
||||
};
|
||||
|
||||
using RegularQuad = Regular<4,3>;
|
||||
using RegularTri = Regular<6,4>;
|
||||
|
||||
|
||||
/// Wrapper object to hold an object and a member function pointer,
|
||||
/// and provides operator() to call that member function for that object with one argument
|
||||
template <typename T, typename MF>
|
||||
struct MemberFunctionWrapper
|
||||
{
|
||||
T t_; // Objects whose member function we want to call
|
||||
MF mf_; // pointer to member function
|
||||
|
||||
MemberFunctionWrapper(T _t, MF _mf)
|
||||
:
|
||||
t_(_t),
|
||||
mf_(_mf)
|
||||
{}
|
||||
|
||||
template <typename O>
|
||||
auto operator()(const O& _o) -> decltype ((t_.*mf_)(_o))
|
||||
{
|
||||
return (t_.*mf_)(_o);
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper to create a MemberFunctionWrapper without explicitely naming the types
|
||||
template <typename T, typename MF>
|
||||
MemberFunctionWrapper<T,MF> make_member_function_wrapper(T&& _t, MF _mf)
|
||||
{
|
||||
return MemberFunctionWrapper<T,MF>(std::forward<T>(_t), _mf);
|
||||
}
|
||||
|
||||
/// Convenience macro to create a MemberFunctionWrapper for *this object
|
||||
#define OM_MFW(member_function) OpenMesh::Predicates::make_member_function_wrapper(*this, &std::decay<decltype(*this)>::type::member_function)
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
} // namespace Predicates
|
||||
|
||||
} // namespace OpenMesh
|
||||
//=============================================================================
|
||||
|
||||
//=============================================================================
|
||||
@@ -85,14 +85,17 @@ class PropertyManager {
|
||||
|
||||
private:
|
||||
// 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.
|
||||
// This class implements different behavior when initializing a property or when
|
||||
// copying or swapping data from one property manager to a another one.
|
||||
template <typename PropertyManager2, typename PropHandleT>
|
||||
struct StorageT;
|
||||
|
||||
// specialization for Mesh Properties
|
||||
template <typename PropertyManager2>
|
||||
struct StorageT<PropertyManager2, MPropHandleT<Value>> {
|
||||
static void initialize(PropertyManager<PROPTYPE, MeshT>& pm, const Value& initial_value ) {
|
||||
pm() = initial_value;
|
||||
}
|
||||
static void copy(const PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
|
||||
*to = *from;
|
||||
}
|
||||
@@ -110,6 +113,9 @@ class PropertyManager {
|
||||
// definition for other Mesh Properties
|
||||
template <typename PropertyManager2, typename PropHandleT>
|
||||
struct StorageT {
|
||||
static void initialize(PropertyManager<PROPTYPE, MeshT>& pm, const Value& initial_value ) {
|
||||
pm.set_range(pm.mesh_.template all_elements<Handle>(), initial_value);
|
||||
}
|
||||
static void copy(const PropertyManager& from, PropertyManager2& to) {
|
||||
from.copy_to(from.mesh_.template all_elements<Handle>(), to, to.mesh_.template all_elements<Handle>());
|
||||
}
|
||||
@@ -190,7 +196,7 @@ class PropertyManager {
|
||||
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);
|
||||
Storage::initialize(*this, initial_value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +221,7 @@ class PropertyManager {
|
||||
*/
|
||||
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);
|
||||
Storage::initialize(*this, initial_value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
BIN
src/Unittests/TestFiles/cube_tri_with_properties_2_1.om
Normal file
BIN
src/Unittests/TestFiles/cube_tri_with_properties_2_1.om
Normal file
Binary file not shown.
@@ -870,5 +870,12 @@ TEST_F(OpenMeshPropertyManager, return_property_from_function) {
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OpenMeshPropertyManager, mesh_property_initialization) {
|
||||
|
||||
OpenMesh::MProp<int> mesh_id(13, mesh_);
|
||||
ASSERT_EQ(mesh_id(), 13);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <Unittests/unittests_common.hh>
|
||||
|
||||
#include <OpenMesh/Core/Utils/PropertyManager.hh>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -1458,6 +1459,217 @@ TEST_F(OpenMeshReadWriteOM, LoadPolyMeshVersion_2_0) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string get_type_string(OpenMesh::FaceHandle) { return "Face"; }
|
||||
std::string get_type_string(OpenMesh::EdgeHandle) { return "Edge"; }
|
||||
std::string get_type_string(OpenMesh::HalfedgeHandle) { return "Halfedge"; }
|
||||
std::string get_type_string(OpenMesh::VertexHandle) { return "Vertex"; }
|
||||
|
||||
std::string get_type_string(char) { return "char"; }
|
||||
std::string get_type_string(double) { return "double"; }
|
||||
std::string get_type_string(float) { return "float"; }
|
||||
std::string get_type_string(int) { return "int"; }
|
||||
std::string get_type_string(short) { return "short"; }
|
||||
std::string get_type_string(unsigned char) { return "unsigned char"; }
|
||||
std::string get_type_string(unsigned int) { return "unsigned int"; }
|
||||
std::string get_type_string(unsigned short) { return "unsigned short"; }
|
||||
std::string get_type_string(bool) { return "bool"; }
|
||||
|
||||
template <typename T>
|
||||
std::string get_type_string(std::vector<T>) { return "std::vector of " + get_type_string(T()); }
|
||||
|
||||
template <typename T, int Dim>
|
||||
std::string get_type_string(OpenMesh::VectorT<T, Dim>) { return "OM vector of dimension " + std::to_string(Dim) + " of type " + get_type_string(T()); }
|
||||
|
||||
|
||||
template <typename T>
|
||||
T get_value(int seed, T, int seed2 = 0)
|
||||
{
|
||||
return (seed * 3 + seed2) % 20;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> get_value(int seed, const std::vector<T>&)
|
||||
{
|
||||
int size = get_value(seed, 3);
|
||||
std::vector<T> res(size);
|
||||
for (int i = 0; i < size; ++i)
|
||||
res[i] = get_value(seed, T(), i);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T, int Dim>
|
||||
OpenMesh::VectorT<T, Dim> get_value(int seed, const OpenMesh::VectorT<T, Dim>&)
|
||||
{
|
||||
OpenMesh::VectorT<T, Dim> res;
|
||||
for (int i = 0; i < Dim; ++i)
|
||||
res[i] = get_value(seed, T(), i);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename MeshT, typename HandleT, typename T>
|
||||
OpenMesh::Prop<HandleT, T> add_property(MeshT& _mesh)
|
||||
{
|
||||
std::string name = get_type_string(HandleT()) + ": " + get_type_string(T());
|
||||
OpenMesh::Prop<HandleT, T> prop(_mesh, name.c_str());
|
||||
_mesh.property(prop.getRawProperty()).set_persistent(true);
|
||||
for (auto e : _mesh.template elements<HandleT>())
|
||||
prop[e] = get_value(e.idx(), T());
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
template <typename MeshT, typename HandleT, typename T>
|
||||
void check_property(MeshT& _mesh)
|
||||
{
|
||||
std::string name = get_type_string(HandleT()) + ": " + get_type_string(T());
|
||||
bool has_prop = OpenMesh::hasProperty<HandleT, T>(_mesh, name.c_str());
|
||||
EXPECT_TRUE(has_prop) << "Property " << name << " is not available";
|
||||
if (!has_prop)
|
||||
return;
|
||||
OpenMesh::Prop<HandleT, T> prop(_mesh, name.c_str());
|
||||
for (auto e : _mesh.template elements<HandleT>())
|
||||
EXPECT_EQ(prop[e], get_value(e.idx(), T())) << "For property " << name;
|
||||
}
|
||||
|
||||
template <typename MeshT, typename HandleT, typename T>
|
||||
void request_property(MeshT& _mesh)
|
||||
{
|
||||
std::string name = get_type_string(HandleT()) + ": " + get_type_string(T());
|
||||
OpenMesh::Prop<HandleT, T> prop(_mesh, name.c_str());
|
||||
}
|
||||
|
||||
|
||||
enum class PropertyAction
|
||||
{
|
||||
Add, Check, Request
|
||||
};
|
||||
|
||||
|
||||
template <typename MeshT, typename HandleT, typename T>
|
||||
void do_property(MeshT& _mesh, PropertyAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case PropertyAction::Add:
|
||||
add_property<MeshT, HandleT, T>(_mesh);
|
||||
break;
|
||||
case PropertyAction::Check:
|
||||
check_property<MeshT, HandleT, T>(_mesh);
|
||||
break;
|
||||
case PropertyAction::Request:
|
||||
request_property<MeshT, HandleT, T>(_mesh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename MeshT, typename HandleT, int Dim>
|
||||
void do_all_property_types_vec(MeshT& _mesh, PropertyAction action)
|
||||
{
|
||||
do_property<MeshT, HandleT, OpenMesh::VectorT<signed char , Dim>>(_mesh, action);
|
||||
do_property<MeshT, HandleT, OpenMesh::VectorT<double , Dim>>(_mesh, action);
|
||||
do_property<MeshT, HandleT, OpenMesh::VectorT<float , Dim>>(_mesh, action);
|
||||
// do_property<MeshT, HandleT, OpenMesh::VectorT<int , Dim>>(_mesh, action);
|
||||
do_property<MeshT, HandleT, OpenMesh::VectorT<short , Dim>>(_mesh, action);
|
||||
do_property<MeshT, HandleT, OpenMesh::VectorT<unsigned char , Dim>>(_mesh, action);
|
||||
do_property<MeshT, HandleT, OpenMesh::VectorT<unsigned int , Dim>>(_mesh, action);
|
||||
do_property<MeshT, HandleT, OpenMesh::VectorT<unsigned short, Dim>>(_mesh, action);
|
||||
}
|
||||
|
||||
template <typename MeshT, typename HandleT>
|
||||
void do_all_property_types_vec_all_dim(MeshT& _mesh, PropertyAction action)
|
||||
{
|
||||
do_all_property_types_vec<MeshT, HandleT, 1>(_mesh, action);
|
||||
do_all_property_types_vec<MeshT, HandleT, 2>(_mesh, action);
|
||||
do_all_property_types_vec<MeshT, HandleT, 3>(_mesh, action);
|
||||
do_all_property_types_vec<MeshT, HandleT, 4>(_mesh, action);
|
||||
}
|
||||
|
||||
template <typename MeshT, typename HandleT>
|
||||
void do_all_property_types(MeshT& _mesh, PropertyAction action)
|
||||
{
|
||||
// TODO: add support for commented out types
|
||||
do_property<MeshT, HandleT, int> (_mesh, action);
|
||||
do_property<MeshT, HandleT, double> (_mesh, action);
|
||||
do_property<MeshT, HandleT, float> (_mesh, action);
|
||||
do_property<MeshT, HandleT, char> (_mesh, action);
|
||||
// do_property<MeshT, HandleT, bool> (_mesh, action);
|
||||
// do_property<MeshT, HandleT, std::vector<int>> (_mesh, action);
|
||||
// do_property<MeshT, HandleT, std::vector<double>> (_mesh, action);
|
||||
// do_property<MeshT, HandleT, std::vector<float>> (_mesh, action);
|
||||
// do_property<MeshT, HandleT, std::vector<char>> (_mesh, action);
|
||||
// do_property<MeshT, HandleT, std::vector<bool>> (_mesh, action);
|
||||
do_all_property_types_vec_all_dim<MeshT, HandleT>(_mesh, action);
|
||||
}
|
||||
|
||||
template <typename MeshT>
|
||||
void do_all_properties(MeshT& _mesh, PropertyAction action)
|
||||
{
|
||||
do_all_property_types<MeshT,OpenMesh::FaceHandle> (_mesh, action);
|
||||
do_all_property_types<MeshT,OpenMesh::EdgeHandle> (_mesh, action);
|
||||
do_all_property_types<MeshT,OpenMesh::HalfedgeHandle>(_mesh, action);
|
||||
do_all_property_types<MeshT,OpenMesh::VertexHandle> (_mesh, action);
|
||||
}
|
||||
|
||||
template <typename MeshT> void add_all_properties(MeshT& _mesh) { do_all_properties(_mesh, PropertyAction::Add ); }
|
||||
template <typename MeshT> void check_all_properties(MeshT& _mesh) { do_all_properties(_mesh, PropertyAction::Check ); }
|
||||
template <typename MeshT> void request_all_properties(MeshT& _mesh) { do_all_properties(_mesh, PropertyAction::Request); }
|
||||
|
||||
/*
|
||||
* Load a triangle mesh from an om file of version 2.1 with properties
|
||||
*/
|
||||
TEST_F(OpenMeshReadWriteOM, LoadTriangleMeshWithPropertiesVersion_2_1) {
|
||||
|
||||
mesh_.clear();
|
||||
|
||||
std::string file_name = "cube_tri_with_properties_2_1.om";
|
||||
|
||||
request_all_properties(mesh_);
|
||||
bool ok = OpenMesh::IO::read_mesh(mesh_, file_name);
|
||||
|
||||
ASSERT_TRUE(ok) << file_name;
|
||||
|
||||
ASSERT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
|
||||
ASSERT_EQ(18u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
|
||||
ASSERT_EQ(12u , mesh_.n_faces()) << "The number of loaded faces is not correct!";
|
||||
ASSERT_EQ(36u , mesh_.n_halfedges()) << "The number of loaded halfedges is not correct!";
|
||||
|
||||
check_all_properties(mesh_);
|
||||
}
|
||||
|
||||
/*
|
||||
* store and load a triangle mesh from an om file of the current version with properties
|
||||
*/
|
||||
TEST_F(OpenMeshReadWriteOM, LoadTriangleMeshWithPropertiesCurrentVersion) {
|
||||
|
||||
// TODO: create a LoadTriangleMeshWithPropertiesVersion_2_2 unittest from the file resulting from this test
|
||||
// so we will keep testing version 2.2 in the future.
|
||||
|
||||
mesh_.clear();
|
||||
|
||||
std::string file_name = "cube_tri_version_2_0.om";
|
||||
|
||||
bool ok = OpenMesh::IO::read_mesh(mesh_, file_name);
|
||||
|
||||
ASSERT_TRUE(ok) << file_name;
|
||||
ASSERT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
|
||||
ASSERT_EQ(18u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
|
||||
ASSERT_EQ(12u , mesh_.n_faces()) << "The number of loaded faces is not correct!";
|
||||
ASSERT_EQ(36u , mesh_.n_halfedges()) << "The number of loaded halfedges is not correct!";
|
||||
|
||||
add_all_properties(mesh_);
|
||||
|
||||
std::string file_name_2_2 = "cube_tri_with_properties_2_2.om";
|
||||
OpenMesh::IO::Options ops(OpenMesh::IO::Options::Custom);
|
||||
OpenMesh::IO::write_mesh(mesh_, file_name_2_2, ops);
|
||||
|
||||
Mesh new_mesh;
|
||||
|
||||
OpenMesh::IO::read_mesh(new_mesh, file_name_2_2, ops);
|
||||
|
||||
check_all_properties(new_mesh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to load mesh from om file with a version that is not yet supported
|
||||
*/
|
||||
|
||||
@@ -560,6 +560,60 @@ TEST(OpenMeshSmartHandlesNoFixture, SplitTriMesh)
|
||||
}
|
||||
|
||||
|
||||
template <typename MeshT, typename RangeT>
|
||||
void test_status_fields(MeshT& _mesh, const RangeT& _range)
|
||||
{
|
||||
for (auto el : _range)
|
||||
_mesh.status(el).set_selected(el.idx() % 3 == 0);
|
||||
for (auto el : _range)
|
||||
EXPECT_EQ(_mesh.status(el).selected(), el.selected());
|
||||
|
||||
for (auto el : _range)
|
||||
_mesh.status(el).set_feature(el.idx() % 3 == 0);
|
||||
for (auto el : _range)
|
||||
EXPECT_EQ(_mesh.status(el).feature(), el.feature());
|
||||
|
||||
for (auto el : _range)
|
||||
_mesh.status(el).set_tagged(el.idx() % 3 == 0);
|
||||
for (auto el : _range)
|
||||
EXPECT_EQ(_mesh.status(el).tagged(), el.tagged());
|
||||
|
||||
for (auto el : _range)
|
||||
_mesh.status(el).set_tagged2(el.idx() % 3 == 0);
|
||||
for (auto el : _range)
|
||||
EXPECT_EQ(_mesh.status(el).tagged2(), el.tagged2());
|
||||
|
||||
for (auto el : _range)
|
||||
_mesh.status(el).set_hidden(el.idx() % 3 == 0);
|
||||
for (auto el : _range)
|
||||
EXPECT_EQ(_mesh.status(el).hidden(), el.hidden());
|
||||
|
||||
for (auto el : _range)
|
||||
_mesh.status(el).set_locked(el.idx() % 3 == 0);
|
||||
for (auto el : _range)
|
||||
EXPECT_EQ(_mesh.status(el).locked(), el.locked());
|
||||
|
||||
for (auto el : _range)
|
||||
_mesh.status(el).set_deleted(el.idx() % 3 == 0);
|
||||
for (auto el : _range)
|
||||
EXPECT_EQ(_mesh.status(el).deleted(), el.deleted());
|
||||
}
|
||||
|
||||
TEST_F(OpenMeshSmartHandles, StatusAccess)
|
||||
{
|
||||
ASSERT_TRUE(mesh_.n_vertices() > 0) << "Mesh is empty";
|
||||
|
||||
mesh_.request_vertex_status();
|
||||
mesh_.request_halfedge_status();
|
||||
mesh_.request_edge_status();
|
||||
mesh_.request_face_status();
|
||||
|
||||
test_status_fields(mesh_, mesh_.all_vertices());
|
||||
test_status_fields(mesh_, mesh_.all_edges());
|
||||
test_status_fields(mesh_, mesh_.all_halfedges());
|
||||
test_status_fields(mesh_, mesh_.all_faces());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
|
||||
#include <OpenMesh/Core/Utils/PropertyManager.hh>
|
||||
#include <OpenMesh/Core/Utils/Predicates.hh>
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
@@ -258,7 +259,7 @@ TEST_F(OpenMeshSmartRanges, BoundingBox)
|
||||
// Thus we convert here.
|
||||
OpenMesh::VProp<OpenMesh::Vec3f> myPos(mesh_);
|
||||
for (auto vh : mesh_.vertices())
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
myPos(vh)[i] = mesh_.point(vh)[i];
|
||||
|
||||
auto bb_min = mesh_.vertices().min(myPos);
|
||||
@@ -386,6 +387,528 @@ TEST_F(OpenMeshSmartRanges, Filtered)
|
||||
|
||||
}
|
||||
|
||||
/* Test avg
|
||||
*/
|
||||
TEST_F(OpenMeshSmartRanges, Avg)
|
||||
{
|
||||
|
||||
Mesh::Point cog(0,0,0);
|
||||
for (auto vh : mesh_.vertices())
|
||||
cog += mesh_.point(vh);
|
||||
cog /= mesh_.n_vertices();
|
||||
|
||||
auto points = OpenMesh::getPointsProperty(mesh_);
|
||||
auto cog2 = mesh_.vertices().avg(points);
|
||||
|
||||
EXPECT_LT(norm(cog - cog2), 0.00001) << "Computed center of gravities are significantly different.";
|
||||
}
|
||||
|
||||
/* Test weighted avg
|
||||
*/
|
||||
TEST_F(OpenMeshSmartRanges, WeightedAvg)
|
||||
{
|
||||
Mesh::Point cog(0,0,0);
|
||||
for (auto fh : mesh_.faces())
|
||||
cog += mesh_.calc_face_centroid(fh);
|
||||
cog /= mesh_.n_faces();
|
||||
|
||||
OpenMesh::FProp<float> area(mesh_);
|
||||
for (auto fh : mesh_.faces())
|
||||
area[fh] = mesh_.calc_face_area(fh);
|
||||
|
||||
auto cog2 = mesh_.faces().avg([&](OpenMesh::FaceHandle fh) { return mesh_.calc_face_centroid(fh); }, area);
|
||||
|
||||
EXPECT_LT(norm(cog - cog2), 0.00001) << "Computed area weighted center of gravities are significantly different.";
|
||||
}
|
||||
|
||||
|
||||
template <typename HandleT>
|
||||
void test_range_predicates(Mesh& _mesh)
|
||||
{
|
||||
using namespace OpenMesh::Predicates;
|
||||
|
||||
auto get_random_set = [&](int n)
|
||||
{
|
||||
auto max = _mesh.n_elements<HandleT>();
|
||||
std::vector<HandleT> set;
|
||||
set.push_back(HandleT(0));
|
||||
for (int i = 0; i < n; ++i)
|
||||
set.push_back(HandleT(rand() % max));
|
||||
std::sort(set.begin(), set.end());
|
||||
set.erase(std::unique(set.begin(), set.end()), set.end());
|
||||
return set;
|
||||
};
|
||||
|
||||
// Feature
|
||||
{
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_feature(false);
|
||||
auto set = get_random_set(4);
|
||||
for (auto el : set)
|
||||
_mesh.status(el).set_feature(true);
|
||||
|
||||
auto set2 = _mesh.elements<HandleT>().filtered(Feature()).to_vector();
|
||||
|
||||
EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ";
|
||||
for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i)
|
||||
EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_feature(false);
|
||||
}
|
||||
|
||||
// Selected
|
||||
{
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
auto set = get_random_set(4);
|
||||
for (auto el : set)
|
||||
_mesh.status(el).set_selected(true);
|
||||
|
||||
auto set2 = _mesh.elements<HandleT>().filtered(Selected()).to_vector();
|
||||
|
||||
EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ";
|
||||
for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i)
|
||||
EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
}
|
||||
|
||||
// Tagged
|
||||
{
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
auto set = get_random_set(4);
|
||||
for (auto el : set)
|
||||
_mesh.status(el).set_tagged(true);
|
||||
|
||||
auto set2 = _mesh.elements<HandleT>().filtered(Tagged()).to_vector();
|
||||
|
||||
EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ";
|
||||
for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i)
|
||||
EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
}
|
||||
|
||||
// Tagged2
|
||||
{
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged2(false);
|
||||
auto set = get_random_set(4);
|
||||
for (auto el : set)
|
||||
_mesh.status(el).set_tagged2(true);
|
||||
|
||||
auto set2 = _mesh.elements<HandleT>().filtered(Tagged2()).to_vector();
|
||||
|
||||
EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ";
|
||||
for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i)
|
||||
EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged2(false);
|
||||
}
|
||||
|
||||
// Locked
|
||||
{
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_locked(false);
|
||||
auto set = get_random_set(4);
|
||||
for (auto el : set)
|
||||
_mesh.status(el).set_locked(true);
|
||||
|
||||
auto set2 = _mesh.elements<HandleT>().filtered(Locked()).to_vector();
|
||||
|
||||
EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ";
|
||||
for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i)
|
||||
EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_locked(false);
|
||||
}
|
||||
|
||||
// Hidden
|
||||
{
|
||||
for (auto el : _mesh.all_elements<HandleT>())
|
||||
_mesh.status(el).set_hidden(false);
|
||||
auto set = get_random_set(4);
|
||||
for (auto el : set)
|
||||
_mesh.status(el).set_hidden(true);
|
||||
|
||||
auto set2 = _mesh.all_elements<HandleT>().filtered(Hidden()).to_vector();
|
||||
|
||||
EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ";
|
||||
for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i)
|
||||
EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.all_elements<HandleT>())
|
||||
_mesh.status(el).set_hidden(false);
|
||||
}
|
||||
|
||||
// Deleted
|
||||
{
|
||||
for (auto el : _mesh.all_elements<HandleT>())
|
||||
_mesh.status(el).set_deleted(false);
|
||||
auto set = get_random_set(4);
|
||||
for (auto el : set)
|
||||
_mesh.status(el).set_deleted(true);
|
||||
|
||||
auto set2 = _mesh.all_elements<HandleT>().filtered(Deleted()).to_vector();
|
||||
|
||||
EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ";
|
||||
for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i)
|
||||
EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.all_elements<HandleT>())
|
||||
_mesh.status(el).set_deleted(false);
|
||||
}
|
||||
|
||||
// Custom property
|
||||
{
|
||||
OpenMesh::PropertyManager<typename OpenMesh::PropHandle<HandleT>::template type<bool>> prop(false, _mesh);
|
||||
auto set = get_random_set(4);
|
||||
for (auto el : set)
|
||||
prop[el] = true;
|
||||
|
||||
auto set2 = _mesh.elements<HandleT>().filtered(prop).to_vector();
|
||||
|
||||
EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ";
|
||||
for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i)
|
||||
EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i;
|
||||
}
|
||||
|
||||
// boundary
|
||||
{
|
||||
for (auto el : _mesh.elements<HandleT>().filtered(Boundary()))
|
||||
EXPECT_TRUE(el.is_boundary());
|
||||
int n_boundary1 = 0.0;
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
if (el.is_boundary())
|
||||
n_boundary1 += 1;
|
||||
int n_boundary2 = _mesh.elements<HandleT>().count_if(Boundary());
|
||||
EXPECT_EQ(n_boundary1, n_boundary2);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename HandleT>
|
||||
void test_range_predicate_combinations(Mesh& _mesh)
|
||||
{
|
||||
using namespace OpenMesh::Predicates;
|
||||
|
||||
auto n_elements = _mesh.n_elements<HandleT>();
|
||||
auto get_random_set = [&](int n)
|
||||
{
|
||||
std::vector<HandleT> set;
|
||||
for (int i = 0; i < n; ++i)
|
||||
set.push_back(HandleT(rand() % n_elements));
|
||||
std::sort(set.begin(), set.end());
|
||||
set.erase(std::unique(set.begin(), set.end()), set.end());
|
||||
return set;
|
||||
};
|
||||
|
||||
// negation
|
||||
{
|
||||
auto set = get_random_set(4);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : set)
|
||||
_mesh.status(el).set_selected(true);
|
||||
|
||||
auto true_set = _mesh.elements<HandleT>().filtered(Selected()).to_vector();
|
||||
auto false_set = _mesh.elements<HandleT>().filtered(!Selected()).to_vector();
|
||||
|
||||
std::vector<HandleT> intersection;
|
||||
std::set_intersection(true_set.begin(), true_set.end(), false_set.begin(), false_set.end(), std::back_inserter(intersection));
|
||||
|
||||
EXPECT_TRUE(intersection.empty());
|
||||
EXPECT_EQ(true_set.size() + false_set.size(), n_elements);
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
}
|
||||
|
||||
// conjunction
|
||||
{
|
||||
auto set1 = get_random_set(4);
|
||||
auto set2 = get_random_set(4);
|
||||
// make sure there is some overlap
|
||||
{
|
||||
auto set3 = get_random_set(3);
|
||||
set1.insert(set1.end(), set3.begin(), set3.end());
|
||||
set2.insert(set2.end(), set3.begin(), set3.end());
|
||||
std::sort(set1.begin(), set1.end());
|
||||
std::sort(set2.begin(), set2.end());
|
||||
set1.erase(std::unique(set1.begin(), set1.end()), set1.end());
|
||||
set2.erase(std::unique(set2.begin(), set2.end()), set2.end());
|
||||
}
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
for (auto el : set1)
|
||||
_mesh.status(el).set_selected(true);
|
||||
for (auto el : set2)
|
||||
_mesh.status(el).set_tagged(true);
|
||||
|
||||
auto set = _mesh.elements<HandleT>().filtered(Selected() && Tagged()).to_vector();
|
||||
|
||||
std::vector<HandleT> intersection;
|
||||
std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(intersection));
|
||||
|
||||
EXPECT_EQ(intersection.size(), set.size());
|
||||
for (size_t i = 0; i < std::min(intersection.size(), set.size()); ++i)
|
||||
EXPECT_EQ(intersection[i], set[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
}
|
||||
|
||||
// Disjunction
|
||||
{
|
||||
auto set1 = get_random_set(4);
|
||||
auto set2 = get_random_set(4);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
for (auto el : set1)
|
||||
_mesh.status(el).set_selected(true);
|
||||
for (auto el : set2)
|
||||
_mesh.status(el).set_tagged(true);
|
||||
|
||||
auto set = _mesh.elements<HandleT>().filtered(Selected() || Tagged()).to_vector();
|
||||
|
||||
std::vector<HandleT> union_set;
|
||||
std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set));
|
||||
|
||||
EXPECT_EQ(union_set.size(), set.size());
|
||||
for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i)
|
||||
EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename HandleT>
|
||||
bool test_func(HandleT _h)
|
||||
{
|
||||
return _h.idx() % 3 == 0;
|
||||
}
|
||||
|
||||
template <typename HandleT>
|
||||
void test_make_predicate(Mesh& _mesh)
|
||||
{
|
||||
using namespace OpenMesh::Predicates;
|
||||
|
||||
auto n_elements = _mesh.n_elements<HandleT>();
|
||||
auto get_random_set = [&](int n)
|
||||
{
|
||||
std::vector<HandleT> set;
|
||||
for (int i = 0; i < n; ++i)
|
||||
set.push_back(HandleT(rand() % n_elements));
|
||||
std::sort(set.begin(), set.end());
|
||||
set.erase(std::unique(set.begin(), set.end()), set.end());
|
||||
return set;
|
||||
};
|
||||
|
||||
// custom property
|
||||
{
|
||||
OpenMesh::PropertyManager<typename OpenMesh::PropHandle<HandleT>::template type<bool>> prop(false, _mesh);
|
||||
auto set1 = get_random_set(4);
|
||||
auto set2 = get_random_set(4);
|
||||
for (auto el : set1)
|
||||
prop[el] = true;
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : set2)
|
||||
_mesh.status(el).set_selected(true);
|
||||
|
||||
auto set = _mesh.elements<HandleT>().filtered(Selected() || make_predicate(prop)).to_vector();
|
||||
|
||||
std::vector<HandleT> union_set;
|
||||
std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set));
|
||||
|
||||
EXPECT_EQ(union_set.size(), set.size());
|
||||
for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i)
|
||||
EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
}
|
||||
|
||||
// lambda
|
||||
{
|
||||
OpenMesh::PropertyManager<typename OpenMesh::PropHandle<HandleT>::template type<bool>> prop(false, _mesh);
|
||||
auto set1 = get_random_set(4);
|
||||
auto set2 = get_random_set(4);
|
||||
for (auto el : set1)
|
||||
prop[el] = true;
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : set2)
|
||||
_mesh.status(el).set_selected(true);
|
||||
|
||||
auto test = [&](HandleT h) { return prop(h); };
|
||||
|
||||
auto set = _mesh.elements<HandleT>().filtered(Selected() || make_predicate(test)).to_vector();
|
||||
|
||||
std::vector<HandleT> union_set;
|
||||
std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set));
|
||||
|
||||
EXPECT_EQ(union_set.size(), set.size());
|
||||
for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i)
|
||||
EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
}
|
||||
|
||||
// r value lambda
|
||||
{
|
||||
OpenMesh::PropertyManager<typename OpenMesh::PropHandle<HandleT>::template type<bool>> prop(false, _mesh);
|
||||
auto set1 = get_random_set(4);
|
||||
auto set2 = get_random_set(4);
|
||||
for (auto el : set1)
|
||||
prop[el] = true;
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : set2)
|
||||
_mesh.status(el).set_selected(true);
|
||||
|
||||
auto set = _mesh.elements<HandleT>().filtered(Selected() || make_predicate([&](HandleT h) { return prop(h); })).to_vector();
|
||||
|
||||
std::vector<HandleT> union_set;
|
||||
std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set));
|
||||
|
||||
EXPECT_EQ(union_set.size(), set.size());
|
||||
for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i)
|
||||
EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
}
|
||||
|
||||
// function pointer
|
||||
{
|
||||
auto set1 = _mesh.elements<HandleT>().filtered([&](const HandleT& h) { return test_func(h); }).to_vector();
|
||||
auto set2 = get_random_set(4);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : set2)
|
||||
_mesh.status(el).set_selected(true);
|
||||
|
||||
auto set = _mesh.elements<HandleT>().filtered(Selected() || make_predicate(test_func<HandleT>)).to_vector();
|
||||
|
||||
std::vector<HandleT> union_set;
|
||||
std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set));
|
||||
|
||||
EXPECT_EQ(union_set.size(), set.size());
|
||||
for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i)
|
||||
EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i;
|
||||
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_selected(false);
|
||||
for (auto el : _mesh.elements<HandleT>())
|
||||
_mesh.status(el).set_tagged(false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenMeshSmartRanges, Predicate)
|
||||
{
|
||||
using namespace OpenMesh;
|
||||
|
||||
mesh_.request_vertex_status();
|
||||
mesh_.request_halfedge_status();
|
||||
mesh_.request_edge_status();
|
||||
mesh_.request_face_status();
|
||||
|
||||
mesh_.delete_face(FaceHandle(0)); // ensure there is a boundary
|
||||
mesh_.garbage_collection();
|
||||
|
||||
test_range_predicates<VertexHandle>(mesh_);
|
||||
test_range_predicates<HalfedgeHandle>(mesh_);
|
||||
test_range_predicates<EdgeHandle>(mesh_);
|
||||
test_range_predicates<FaceHandle>(mesh_);
|
||||
|
||||
test_range_predicate_combinations<VertexHandle>(mesh_);
|
||||
test_range_predicate_combinations<HalfedgeHandle>(mesh_);
|
||||
test_range_predicate_combinations<EdgeHandle>(mesh_);
|
||||
test_range_predicate_combinations<FaceHandle>(mesh_);
|
||||
|
||||
test_make_predicate<VertexHandle>(mesh_);
|
||||
test_make_predicate<HalfedgeHandle>(mesh_);
|
||||
test_make_predicate<EdgeHandle>(mesh_);
|
||||
test_make_predicate<FaceHandle>(mesh_);
|
||||
}
|
||||
|
||||
struct MemberFunctionWrapperTestStruct
|
||||
{
|
||||
MemberFunctionWrapperTestStruct(int _i)
|
||||
:
|
||||
i_(_i)
|
||||
{
|
||||
}
|
||||
|
||||
int get_i(const OpenMesh::SmartEdgeHandle& /*_eh*/) const
|
||||
{
|
||||
return i_;
|
||||
}
|
||||
|
||||
bool id_divisible_by_2(const OpenMesh::SmartEdgeHandle& _eh) const
|
||||
{
|
||||
return _eh.idx() % 2 == 0;
|
||||
}
|
||||
|
||||
int valence_times_i(const OpenMesh::SmartVertexHandle& vh)
|
||||
{
|
||||
return vh.edges().sum(OM_MFW(get_i));
|
||||
}
|
||||
|
||||
int i_;
|
||||
};
|
||||
|
||||
TEST_F(OpenMeshSmartRanges, MemberFunctionFunctor)
|
||||
{
|
||||
using namespace OpenMesh::Predicates;
|
||||
|
||||
EXPECT_TRUE(mesh_.n_vertices() > 0) << "Mesh has no vertices";
|
||||
EXPECT_TRUE(mesh_.n_edges() > 0) << "Mesh has no edges";
|
||||
|
||||
int factor = 3;
|
||||
MemberFunctionWrapperTestStruct test_object(factor);
|
||||
|
||||
// Test using a MemberFunctionWrapper as Functor
|
||||
EXPECT_EQ(mesh_.n_edges() / 2, mesh_.edges().count_if(make_member_function_wrapper(test_object, &MemberFunctionWrapperTestStruct::id_divisible_by_2)));
|
||||
|
||||
|
||||
// Test using a MemberFunctionWrapper as Functor that is created using the convenience macro from inside the struct
|
||||
for (auto vh : mesh_.vertices())
|
||||
EXPECT_EQ(test_object.valence_times_i(vh), vh.valence() * factor);
|
||||
|
||||
factor = 4;
|
||||
test_object.i_ = factor;
|
||||
for (auto vh : mesh_.vertices())
|
||||
{
|
||||
EXPECT_EQ(test_object.valence_times_i(vh), vh.valence() * factor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user