Commit a64121b6 authored by Jan Möbius's avatar Jan Möbius

Merge branch 'smart_range_predicates' into 'master'

Smart range predicates

See merge request !285
parents 2378a7d3 e257be3e
Pipeline #15387 passed with stages
in 116 minutes and 53 seconds
#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,9 @@
<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>
......
......@@ -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:
......
/** \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
......
......@@ -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
//=============================================================================
......
/* ========================================================================= *
* *
* 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>
//== 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>;
//=============================================================================
} // namespace Predicates
} // namespace OpenMesh
//=============================================================================
//=============================================================================
......@@ -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)