diff --git a/Doc/Tutorial/12-predicates/predicates.cc b/Doc/Tutorial/12-predicates/predicates.cc new file mode 100644 index 00000000..790a0fbb --- /dev/null +++ b/Doc/Tutorial/12-predicates/predicates.cc @@ -0,0 +1,58 @@ + +#include +#include +#include +#include + +#include +#include + +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 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; + +} + diff --git a/Doc/changelog.docu b/Doc/changelog.docu index 4d890368..24f358f1 100644 --- a/Doc/changelog.docu +++ b/Doc/changelog.docu @@ -18,6 +18,14 @@
  • Add filtered range that stores reference instead of copy if the filter is not an rvalue reference
  • Add halfedge loop range corresponding to hl_iter()
  • +
  • Smart handles now give read access to status fields (feature(), selected(), deleted(), tagged(), tagged2(), hidden(), locked()
  • +
  • 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 !
  • +
  • Add make_predicate to create a predicate from anything containing a bool operator(T), e.g. a PropertyManager, a lambda, or a function pointer.
  • +
+ +IO +
    +
  • STL Reader: Change stl reader behaviour on extension .stl , Don't check for the solid keyword explicitly
Build System diff --git a/Doc/mainpage.docu b/Doc/mainpage.docu index 63bfee8c..d31b189c 100644 --- a/Doc/mainpage.docu +++ b/Doc/mainpage.docu @@ -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 diff --git a/Doc/tutorial_11.docu b/Doc/tutorial_11.docu index f99724cf..50b9b787 100644 --- a/Doc/tutorial_11.docu +++ b/Doc/tutorial_11.docu @@ -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: diff --git a/Doc/tutorial_12.docu b/Doc/tutorial_12.docu new file mode 100644 index 00000000..80ad2a8a --- /dev/null +++ b/Doc/tutorial_12.docu @@ -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 . 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 + +--- + + + +*/ diff --git a/Doc/tutorial_main.docu b/Doc/tutorial_main.docu index 908099dc..3bb81314 100644 --- a/Doc/tutorial_main.docu +++ b/Doc/tutorial_main.docu @@ -34,6 +34,7 @@ repeatedly replacing each vertex' position by the center of gravity
  • \subpage tutorial_03
  • \subpage tutorial_04
  • \subpage tutorial_11 +
  • \subpage tutorial_12
  • \subpage tutorial_05
  • \subpage tutorial_06
  • \subpage tutorial_07 diff --git a/src/OpenMesh/Apps/Decimating/decimaterviewer.cc b/src/OpenMesh/Apps/Decimating/decimaterviewer.cc index 5edaf767..da5a22db 100644 --- a/src/OpenMesh/Apps/Decimating/decimaterviewer.cc +++ b/src/OpenMesh/Apps/Decimating/decimaterviewer.cc @@ -63,7 +63,6 @@ int main(int argc, char **argv) #endif // OpenGL check - QApplication::setColorSpec( QApplication::CustomColor ); QApplication app(argc,argv); if ( !QGLFormat::hasOpenGL() ) { diff --git a/src/OpenMesh/Apps/ProgViewer/progviewer.cc b/src/OpenMesh/Apps/ProgViewer/progviewer.cc index 6f8eebc5..4ce5de6b 100644 --- a/src/OpenMesh/Apps/ProgViewer/progviewer.cc +++ b/src/OpenMesh/Apps/ProgViewer/progviewer.cc @@ -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(); diff --git a/src/OpenMesh/Apps/QtViewer/meshviewer.cc b/src/OpenMesh/Apps/QtViewer/meshviewer.cc index a3649154..fdb5c144 100644 --- a/src/OpenMesh/Apps/QtViewer/meshviewer.cc +++ b/src/OpenMesh/Apps/QtViewer/meshviewer.cc @@ -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; } diff --git a/src/OpenMesh/Apps/Subdivider/qtsubdivider.cc b/src/OpenMesh/Apps/Subdivider/qtsubdivider.cc index 665274dc..9421653b 100644 --- a/src/OpenMesh/Apps/Subdivider/qtsubdivider.cc +++ b/src/OpenMesh/Apps/Subdivider/qtsubdivider.cc @@ -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(); diff --git a/src/OpenMesh/Apps/VDProgMesh/Synthesizer/vdpmsynthesizer.cc b/src/OpenMesh/Apps/VDProgMesh/Synthesizer/vdpmsynthesizer.cc index d4c11fcd..a46e8a19 100644 --- a/src/OpenMesh/Apps/VDProgMesh/Synthesizer/vdpmsynthesizer.cc +++ b/src/OpenMesh/Apps/VDProgMesh/Synthesizer/vdpmsynthesizer.cc @@ -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(); diff --git a/src/OpenMesh/Core/IO/reader/STLReader.cc b/src/OpenMesh/Core/IO/reader/STLReader.cc index c5a12917..1964d194 100644 --- a/src/OpenMesh/Core/IO/reader/STLReader.cc +++ b/src/OpenMesh/Core/IO/reader/STLReader.cc @@ -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); } diff --git a/src/OpenMesh/Core/Mesh/SmartHandles.hh b/src/OpenMesh/Core/Mesh/SmartHandles.hh index 2fd0af68..fbe4a94e 100644 --- a/src/OpenMesh/Core/Mesh/SmartHandles.hh +++ b/src/OpenMesh/Core/Mesh/SmartHandles.hh @@ -43,6 +43,8 @@ #error Do not include this directly, include instead PolyConnectivity.hh #endif//OPENMESH_POLYCONNECTIVITY_INTERFACE_INCLUDE +#include + //== NAMESPACES =============================================================== namespace OpenMesh { @@ -73,8 +75,38 @@ private: }; +/// Base class for all smart handle types that contains status related methods +template +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 +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, SmartHandleBoundaryPredicate { 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, SmartHandleBoundaryPredicate { 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, SmartHandleBoundaryPredicate { 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, SmartHandleBoundaryPredicate { 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 { using type = SmartEdgeHandle; template <> struct SmartHandle { using type = SmartFaceHandle; }; +template +inline bool SmartHandleStatusPredicates::feature() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).feature(); +} + +template +inline bool SmartHandleStatusPredicates::selected() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).selected(); +} + +template +inline bool SmartHandleStatusPredicates::tagged() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).tagged(); +} + +template +inline bool SmartHandleStatusPredicates::tagged2() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).tagged2(); +} + +template +inline bool SmartHandleStatusPredicates::locked() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).locked(); +} + +template +inline bool SmartHandleStatusPredicates::hidden() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).hidden(); +} + +template +inline bool SmartHandleStatusPredicates::deleted() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).deleted(); +} + +template +inline bool SmartHandleBoundaryPredicate::is_boundary() const +{ + const auto& handle = static_cast(*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 //============================================================================= diff --git a/src/OpenMesh/Core/Mesh/SmartRange.hh b/src/OpenMesh/Core/Mesh/SmartRange.hh index 0277bd59..6524042c 100644 --- a/src/OpenMesh/Core/Mesh/SmartRange.hh +++ b/src/OpenMesh/Core/Mesh/SmartRange.hh @@ -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 + auto avg(Functor&& f, WeightFunctor&& w) -> typename std::decay())+w(std::declval())))*f(std::declval()))>::type + { + auto range = static_cast(this); + auto begin = range->begin(); + auto end = range->end(); + assert(begin != end); + typename std::decay::type weight = w(*begin); + typename std::decay::type result = weight * f(*begin); + typename std::decay::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 - auto filtered(Functor&& f) -> FilteredSmartRangeT::type> + auto filtered(Functor&& f) -> FilteredSmartRangeT { auto range = static_cast(this); - auto b = (*range).begin(); - auto e = (*range).end(); - return FilteredSmartRangeT::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 - auto filtered(Functor& f) -> FilteredSmartRangeT::type&> - { - auto range = static_cast(this); - auto b = (*range).begin(); - auto e = (*range).end(); - return FilteredSmartRangeT::type&>(f, b, e); + return FilteredSmartRangeT(std::forward(f), (*range).begin(), (*range).end()); } }; @@ -460,11 +471,12 @@ struct FilteredSmartRangeT : public SmartRangeT(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_); } diff --git a/src/OpenMesh/Core/Utils/Predicates.hh b/src/OpenMesh/Core/Utils/Predicates.hh new file mode 100644 index 00000000..2880eb5a --- /dev/null +++ b/src/OpenMesh/Core/Utils/Predicates.hh @@ -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 +#include + +#include +#include +#include +#include +#include + +//== NAMESPACES =============================================================== + +namespace OpenMesh { + +namespace Predicates { + +//== FORWARD DECLARATION ====================================================== + +//== CLASS DEFINITION ========================================================= + +template +struct PredicateBase +{ +}; + +template +struct Predicate : public PredicateBase> +{ + Predicate(PredicateT _p) + : + p_(_p) + {} + + template + bool operator()(const T& _t) const { return p_(_t); } + + PredicateT p_; +}; + +template +Predicate make_predicate(PredicateT& _p) { return { _p }; } + +template +Predicate make_predicate(PredicateT&& _p) { return { _p }; } + +template +struct Disjunction : public PredicateBase> +{ + Disjunction(Predicate1T _p1, Predicate2T _p2) + : + p1_(_p1), + p2_(_p2) + {} + + template + bool operator()(const T& _t) const { return p1_( _t) || p2_( _t); } + + Predicate1T p1_; + Predicate2T p2_; +}; + +template +struct Conjunction : public PredicateBase> +{ + Conjunction(Predicate1T _p1, Predicate2T _p2) + : + p1_(_p1), + p2_(_p2) + {} + + template + bool operator()(const T& _t) const { return p1_( _t) && p2_( _t); } + + Predicate1T p1_; + Predicate2T p2_; +}; + + +template +struct Negation : public PredicateBase> +{ + Negation(const PredicateT& _p1) + : + p1_(_p1) + {} + + template + bool operator()(const T& _t) const { return !p1_( _t); } + + PredicateT p1_; +}; + +template +Disjunction operator||(PredicateBase& p1, PredicateBase& p2) +{ + return Disjunction(static_cast(p1), static_cast(p2)); +} + +template +Disjunction operator||(PredicateBase& p1, PredicateBase&& p2) +{ + return Disjunction(static_cast(p1), static_cast(p2)); +} + +template +Disjunction operator||(PredicateBase&& p1, PredicateBase& p2) +{ + return Disjunction(static_cast(p1), static_cast(p2)); +} + +template +Disjunction operator||(PredicateBase&& p1, PredicateBase&& p2) +{ + return Disjunction(static_cast(p1), static_cast(p2)); +} + +template +Conjunction operator&&(PredicateBase& p1, PredicateBase& p2) +{ + return Conjunction(static_cast(p1), static_cast(p2)); +} + +template +Conjunction operator&&(PredicateBase& p1, PredicateBase&& p2) +{ + return Conjunction(static_cast(p1), static_cast(p2)); +} + +template +Conjunction operator&&(PredicateBase&& p1, PredicateBase& p2) +{ + return Conjunction(static_cast(p1), static_cast(p2)); +} + +template +Conjunction operator&&(PredicateBase&& p1, PredicateBase&& p2) +{ + return Conjunction(static_cast(p1), static_cast(p2)); +} + +template +Negation operator!(PredicateBase

    & p) +{ + return Negation(static_cast(p)); +} + +template +Negation

    operator!(PredicateBase

    && p) +{ + return Negation

    (static_cast(p)); +} + +struct Feature : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.feature(); } +}; + +struct Selected : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.selected(); } +}; + +struct Tagged : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.tagged(); } +}; + +struct Tagged2 : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.tagged2(); } +}; + +struct Locked : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.locked(); } +}; + +struct Hidden : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.hidden(); } +}; + +struct Deleted : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.deleted(); } +}; + +struct Boundary : public PredicateBase +{ + template + bool operator()(const SmartHandleBoundaryPredicate& _h) const { return _h.is_boundary(); } +}; + +template +struct Regular: public PredicateBase> +{ + 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 +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 + auto operator()(const O& _o) -> decltype ((t_.*mf_)(_o)) + { + return (t_.*mf_)(_o); + } +}; + +/// Helper to create a MemberFunctionWrapper without explicitely naming the types +template +MemberFunctionWrapper make_member_function_wrapper(T&& _t, MF _mf) +{ + return MemberFunctionWrapper(std::forward(_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::type::member_function) + + + +//============================================================================= +} // namespace Predicates + +} // namespace OpenMesh +//============================================================================= + +//============================================================================= diff --git a/src/OpenMesh/Core/Utils/PropertyManager.hh b/src/OpenMesh/Core/Utils/PropertyManager.hh index b5d07764..62d87b76 100644 --- a/src/OpenMesh/Core/Utils/PropertyManager.hh +++ b/src/OpenMesh/Core/Utils/PropertyManager.hh @@ -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 struct StorageT; // specialization for Mesh Properties template struct StorageT> { + static void initialize(PropertyManager& pm, const Value& initial_value ) { + pm() = initial_value; + } static void copy(const PropertyManager& from, PropertyManager2& to) { *to = *from; } @@ -110,6 +113,9 @@ class PropertyManager { // definition for other Mesh Properties template struct StorageT { + static void initialize(PropertyManager& pm, const Value& initial_value ) { + pm.set_range(pm.mesh_.template all_elements(), initial_value); + } static void copy(const PropertyManager& from, PropertyManager2& to) { from.copy_to(from.mesh_.template all_elements(), to, to.mesh_.template all_elements()); } @@ -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(), 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(), initial_value); + Storage::initialize(*this, initial_value); } /** diff --git a/src/Unittests/unittests_propertymanager.cc b/src/Unittests/unittests_propertymanager.cc index 87c92b95..d620f3ec 100644 --- a/src/Unittests/unittests_propertymanager.cc +++ b/src/Unittests/unittests_propertymanager.cc @@ -870,5 +870,12 @@ TEST_F(OpenMeshPropertyManager, return_property_from_function) { } +TEST_F(OpenMeshPropertyManager, mesh_property_initialization) { + + OpenMesh::MProp mesh_id(13, mesh_); + ASSERT_EQ(mesh_id(), 13); +} + + } diff --git a/src/Unittests/unittests_smart_handles.cc b/src/Unittests/unittests_smart_handles.cc index 6b42b45a..eec50a1a 100644 --- a/src/Unittests/unittests_smart_handles.cc +++ b/src/Unittests/unittests_smart_handles.cc @@ -560,6 +560,60 @@ TEST(OpenMeshSmartHandlesNoFixture, SplitTriMesh) } +template +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()); +} + diff --git a/src/Unittests/unittests_smart_ranges.cc b/src/Unittests/unittests_smart_ranges.cc index 353e528c..a547686d 100644 --- a/src/Unittests/unittests_smart_ranges.cc +++ b/src/Unittests/unittests_smart_ranges.cc @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -258,7 +259,7 @@ TEST_F(OpenMeshSmartRanges, BoundingBox) // Thus we convert here. OpenMesh::VProp 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 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 +void test_range_predicates(Mesh& _mesh) +{ + using namespace OpenMesh::Predicates; + + auto get_random_set = [&](int n) + { + auto max = _mesh.n_elements(); + std::vector 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()) + _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().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()) + _mesh.status(el).set_feature(false); + } + + // Selected + { + for (auto el : _mesh.elements()) + _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().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()) + _mesh.status(el).set_selected(false); + } + + // Tagged + { + for (auto el : _mesh.elements()) + _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().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()) + _mesh.status(el).set_tagged(false); + } + + // Tagged2 + { + for (auto el : _mesh.elements()) + _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().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()) + _mesh.status(el).set_tagged2(false); + } + + // Locked + { + for (auto el : _mesh.elements()) + _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().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()) + _mesh.status(el).set_locked(false); + } + + // Hidden + { + for (auto el : _mesh.all_elements()) + _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().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()) + _mesh.status(el).set_hidden(false); + } + + // Deleted + { + for (auto el : _mesh.all_elements()) + _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().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()) + _mesh.status(el).set_deleted(false); + } + + // Custom property + { + OpenMesh::PropertyManager::template type> prop(false, _mesh); + auto set = get_random_set(4); + for (auto el : set) + prop[el] = true; + + auto set2 = _mesh.elements().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().filtered(Boundary())) + EXPECT_TRUE(el.is_boundary()); + int n_boundary1 = 0.0; + for (auto el : _mesh.elements()) + if (el.is_boundary()) + n_boundary1 += 1; + int n_boundary2 = _mesh.elements().count_if(Boundary()); + EXPECT_EQ(n_boundary1, n_boundary2); + } +} + +template +void test_range_predicate_combinations(Mesh& _mesh) +{ + using namespace OpenMesh::Predicates; + + auto n_elements = _mesh.n_elements(); + auto get_random_set = [&](int n) + { + std::vector 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()) + _mesh.status(el).set_selected(false); + for (auto el : set) + _mesh.status(el).set_selected(true); + + auto true_set = _mesh.elements().filtered(Selected()).to_vector(); + auto false_set = _mesh.elements().filtered(!Selected()).to_vector(); + + std::vector 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()) + _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()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _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().filtered(Selected() && Tagged()).to_vector(); + + std::vector 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()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + + // Disjunction + { + auto set1 = get_random_set(4); + auto set2 = get_random_set(4); + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _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().filtered(Selected() || Tagged()).to_vector(); + + std::vector 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()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + +} + +template +bool test_func(HandleT _h) +{ + return _h.idx() % 3 == 0; +} + +template +void test_make_predicate(Mesh& _mesh) +{ + using namespace OpenMesh::Predicates; + + auto n_elements = _mesh.n_elements(); + auto get_random_set = [&](int n) + { + std::vector 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::template type> 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()) + _mesh.status(el).set_selected(false); + for (auto el : set2) + _mesh.status(el).set_selected(true); + + auto set = _mesh.elements().filtered(Selected() || make_predicate(prop)).to_vector(); + + std::vector 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()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + + // lambda + { + OpenMesh::PropertyManager::template type> 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()) + _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().filtered(Selected() || make_predicate(test)).to_vector(); + + std::vector 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()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + + // r value lambda + { + OpenMesh::PropertyManager::template type> 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()) + _mesh.status(el).set_selected(false); + for (auto el : set2) + _mesh.status(el).set_selected(true); + + auto set = _mesh.elements().filtered(Selected() || make_predicate([&](HandleT h) { return prop(h); })).to_vector(); + + std::vector 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()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + + // function pointer + { + auto set1 = _mesh.elements().filtered([&](const HandleT& h) { return test_func(h); }).to_vector(); + auto set2 = get_random_set(4); + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : set2) + _mesh.status(el).set_selected(true); + + auto set = _mesh.elements().filtered(Selected() || make_predicate(test_func)).to_vector(); + + std::vector 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()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _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(mesh_); + test_range_predicates(mesh_); + test_range_predicates(mesh_); + test_range_predicates(mesh_); + + test_range_predicate_combinations(mesh_); + test_range_predicate_combinations(mesh_); + test_range_predicate_combinations(mesh_); + test_range_predicate_combinations(mesh_); + + test_make_predicate(mesh_); + test_make_predicate(mesh_); + test_make_predicate(mesh_); + test_make_predicate(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); + } + +} + }