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:
Alexandra Heuschling
2020-11-10 16:48:50 +01:00
22 changed files with 1347 additions and 104 deletions

View File

@@ -63,7 +63,6 @@ int main(int argc, char **argv)
#endif
// OpenGL check
QApplication::setColorSpec( QApplication::CustomColor );
QApplication app(argc,argv);
if ( !QGLFormat::hasOpenGL() ) {

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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
//=============================================================================

View File

@@ -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_); }

View 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
//=============================================================================
//=============================================================================

View File

@@ -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);
}
/**

View File

@@ -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);
}
}

View File

@@ -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
*/

View File

@@ -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());
}

View File

@@ -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);
}
}
}