From 80d39234fa741e531de7b89c14ae8c6d808ed3c7 Mon Sep 17 00:00:00 2001 From: Janis Born Date: Tue, 14 Nov 2023 07:57:17 +0000 Subject: [PATCH] Add Edge circulators --- Doc/Examples/circulator_functions.cc | 13 ++ Doc/changelog.docu | 2 + Doc/mesh.docu | 6 + src/OpenMesh/Core/Mesh/ArrayKernel.hh | 4 +- src/OpenMesh/Core/Mesh/CirculatorsT.hh | 23 ++- src/OpenMesh/Core/Mesh/PolyConnectivity.hh | 133 ++++++++++++- .../Core/Mesh/PolyConnectivity_inline_impl.hh | 85 ++++++++ src/OpenMesh/Core/Mesh/SmartHandles.hh | 13 +- src/Unittests/CMakeLists.txt | 6 +- src/Unittests/unittests_smart_handles.cc | 4 + .../unittests_trimesh_circulator_edge.hh | 62 ++++++ .../unittests_trimesh_circulator_edge_face.cc | 142 ++++++++++++++ ...ttests_trimesh_circulator_edge_halfedge.cc | 184 ++++++++++++++++++ ...nittests_trimesh_circulator_edge_vertex.cc | 119 +++++++++++ src/Unittests/unittests_trimesh_navigation.cc | 42 ++++ 15 files changed, 828 insertions(+), 10 deletions(-) create mode 100644 src/Unittests/unittests_trimesh_circulator_edge.hh create mode 100644 src/Unittests/unittests_trimesh_circulator_edge_face.cc create mode 100644 src/Unittests/unittests_trimesh_circulator_edge_halfedge.cc create mode 100644 src/Unittests/unittests_trimesh_circulator_edge_vertex.cc create mode 100644 src/Unittests/unittests_trimesh_navigation.cc diff --git a/Doc/Examples/circulator_functions.cc b/Doc/Examples/circulator_functions.cc index d43dc4c7..289cc6ef 100644 --- a/Doc/Examples/circulator_functions.cc +++ b/Doc/Examples/circulator_functions.cc @@ -32,3 +32,16 @@ FaceEdgeIter OpenMesh::PolyConnectivity::fe_iter (FaceHandle _fh); // Get the face-face circulator of face _fh FaceFaceIter OpenMesh::PolyConnectivity::ff_iter (FaceHandle _fh); + +/************************************************** + * Edge circulators + **************************************************/ + +// Get the edge-vertex circulator of edge _eh +EdgeVertexIter OpenMesh::PolyConnectivity::ev_iter (EdgeHandle _eh); + +// Get the edge-halfedge circulator of edge _eh +EdgeHalfedgeIter OpenMesh::PolyConnectivity::eh_iter (EdgeHandle _eh); + +// Get the edge-face circulator of of edge _eh +EdgeFaceIter OpenMesh::PolyConnectivity::ef_iter (EdgeHandle _eh); diff --git a/Doc/changelog.docu b/Doc/changelog.docu index cb70e049..bc73f013 100644 --- a/Doc/changelog.docu +++ b/Doc/changelog.docu @@ -19,6 +19,8 @@
  • legacy vector min max now take const args to avoid matching std implementations
  • Fixed several warnings
  • Make Previous Halfedge Attribute optional again
  • +
  • Added edge-halfedge, edge-vertex, and edge-face circulators
  • +
  • Added default argument: mesh.halfedge_handle(eh) is now equivalent to mesh.halfedge_handle(eh, 0)
  • Tools diff --git a/Doc/mesh.docu b/Doc/mesh.docu index 22984d89..2a75419e 100644 --- a/Doc/mesh.docu +++ b/Doc/mesh.docu @@ -899,6 +899,12 @@ The circulators around a face are: \arg \c FaceEdgeIter: iterate over the face's edges. \arg \c FaceFaceIter: iterate over all edge-neighboring faces. +The circulators around an edge are: + +\arg \c EdgeVertexIter: iterate over the edge's incident vertices. +\arg \c EdgeHalfedgeIter: iterate over the edge's halfedges. +\arg \c EdgeFaceIter: iterate over the edge's incident faces. + Other circulators: \arg \c HalfedgeLoopIter: iterate over a sequence of Halfedges. (all Halfedges over a face or a hole) diff --git a/src/OpenMesh/Core/Mesh/ArrayKernel.hh b/src/OpenMesh/Core/Mesh/ArrayKernel.hh index b9c97fa1..921907dc 100644 --- a/src/OpenMesh/Core/Mesh/ArrayKernel.hh +++ b/src/OpenMesh/Core/Mesh/ArrayKernel.hh @@ -471,7 +471,7 @@ public: { return next_halfedge_handle(opposite_halfedge_handle(_heh)); } // --- edge connectivity --- - static HalfedgeHandle s_halfedge_handle(EdgeHandle _eh, unsigned int _i) + static HalfedgeHandle s_halfedge_handle(EdgeHandle _eh, unsigned int _i = 0) { assert(_i<=1); return HalfedgeHandle((_eh.idx() << 1) + _i); @@ -480,7 +480,7 @@ public: static EdgeHandle s_edge_handle(HalfedgeHandle _heh) { return EdgeHandle(_heh.idx() >> 1); } - HalfedgeHandle halfedge_handle(EdgeHandle _eh, unsigned int _i) const + HalfedgeHandle halfedge_handle(EdgeHandle _eh, unsigned int _i = 0) const { return s_halfedge_handle(_eh, _i); } diff --git a/src/OpenMesh/Core/Mesh/CirculatorsT.hh b/src/OpenMesh/Core/Mesh/CirculatorsT.hh index d937908b..a869ceb8 100644 --- a/src/OpenMesh/Core/Mesh/CirculatorsT.hh +++ b/src/OpenMesh/Core/Mesh/CirculatorsT.hh @@ -44,7 +44,7 @@ //============================================================================= // -// Vertex and Face circulators for PolyMesh/TriMesh +// Vertex, Face, and Edge circulators for PolyMesh/TriMesh // //============================================================================= @@ -98,6 +98,19 @@ class GenericCirculator_CenterEntityFnsT } }; +template +class GenericCirculator_CenterEntityFnsT { + public: + inline static void increment(const Mesh *mesh, typename Mesh::HalfedgeHandle &heh, const typename Mesh::HalfedgeHandle &start, int &lap_counter) { + heh = mesh->opposite_halfedge_handle(heh); + if (heh == start) ++lap_counter; + } + inline static void decrement(const Mesh *mesh, typename Mesh::HalfedgeHandle &heh, const typename Mesh::HalfedgeHandle &start, int &lap_counter) { + if (heh == start) --lap_counter; + heh = mesh->opposite_halfedge_handle(heh); + } +}; + ///////////////////////////////////////////////////////////// // CCW @@ -150,6 +163,14 @@ class GenericCirculator_DereferenciabilityCheckT +class GenericCirculator_DereferenciabilityCheckT { + public: + inline static bool isDereferenciable(const Mesh *mesh, const typename Mesh::HalfedgeHandle &heh) { + return mesh->face_handle(heh).is_valid(); + } +}; + template class GenericCirculator_ValueHandleFnsT { public: diff --git a/src/OpenMesh/Core/Mesh/PolyConnectivity.hh b/src/OpenMesh/Core/Mesh/PolyConnectivity.hh index b34d2a19..005cd0f6 100644 --- a/src/OpenMesh/Core/Mesh/PolyConnectivity.hh +++ b/src/OpenMesh/Core/Mesh/PolyConnectivity.hh @@ -379,6 +379,53 @@ public: typedef FaceFaceCWIter ConstFaceFaceCWIter; typedef FaceFaceCCWIter ConstFaceFaceCCWIter; + /* + * Edge-centered circulators + */ + + struct EdgeVertexTraits + { + using Mesh = This; + using CenterEntityHandle = This::EdgeHandle; + using ValueHandle = This::VertexHandle; + static ValueHandle toHandle(const Mesh* const _mesh, This::HalfedgeHandle _heh) { return static_cast(_mesh)->from_vertex_handle(_heh); } + }; + + /** + * Enumerate vertices incident to an edge. + */ + typedef Iterators::GenericCirculatorT_DEPRECATED EdgeVertexIter; + + struct EdgeHalfedgeTraits + { + using Mesh = This; + using CenterEntityHandle = This::EdgeHandle; + using ValueHandle = This::HalfedgeHandle; + static ValueHandle toHandle(const Mesh* const /* _mesh */, This::HalfedgeHandle _heh) { return _heh; } + }; + + /** + * Enumerate the halfedges of an edge. + */ + typedef Iterators::GenericCirculatorT_DEPRECATED EdgeHalfedgeIter; + + struct EdgeFaceTraits + { + using Mesh = This; + using CenterEntityHandle = This::EdgeHandle; + using ValueHandle = This::FaceHandle; + static ValueHandle toHandle(const Mesh* const _mesh, This::HalfedgeHandle _heh) { return static_cast(_mesh)->face_handle(_heh); } + }; + + /** + * Enumerate faces incident to an edge. + */ + typedef Iterators::GenericCirculatorT_DEPRECATED EdgeFaceIter; + + typedef EdgeVertexIter ConstEdgeVertexIter; + typedef EdgeHalfedgeIter ConstEdgeHalfedgeIter; + typedef EdgeFaceIter ConstEdgeFaceIter; + /* * Halfedge circulator */ @@ -435,6 +482,9 @@ public: typedef FaceEdgeCWIter FECWIter; typedef FaceEdgeCCWIter FECWWIter; typedef FaceFaceIter FFIter; + typedef EdgeVertexIter EVIter; + typedef EdgeHalfedgeIter EHIter; + typedef EdgeFaceIter EFIter; typedef ConstVertexVertexIter CVVIter; typedef ConstVertexVertexCWIter CVVCWIter; @@ -463,6 +513,9 @@ public: typedef ConstFaceFaceIter CFFIter; typedef ConstFaceFaceCWIter CFFCWIter; typedef ConstFaceFaceCCWIter CFFCCWIter; + typedef ConstEdgeVertexIter CEVIter; + typedef ConstEdgeHalfedgeIter CEHIter; + typedef ConstEdgeFaceIter CEFIter; //@} public: @@ -600,14 +653,14 @@ public: using ArrayKernel::s_halfedge_handle; using ArrayKernel::s_edge_handle; - static SmartHalfedgeHandle s_halfedge_handle(SmartEdgeHandle _eh, unsigned int _i); + static SmartHalfedgeHandle s_halfedge_handle(SmartEdgeHandle _eh, unsigned int _i = 0); static SmartEdgeHandle s_edge_handle(SmartHalfedgeHandle _heh); using ArrayKernel::halfedge_handle; using ArrayKernel::edge_handle; using ArrayKernel::face_handle; - inline SmartHalfedgeHandle halfedge_handle(SmartEdgeHandle _eh, unsigned int _i) const; + inline SmartHalfedgeHandle halfedge_handle(SmartEdgeHandle _eh, unsigned int _i = 0) const; inline SmartHalfedgeHandle halfedge_handle(SmartFaceHandle _fh) const; inline SmartHalfedgeHandle halfedge_handle(SmartVertexHandle _vh) const; inline SmartEdgeHandle edge_handle(SmartHalfedgeHandle _heh) const; @@ -688,7 +741,7 @@ public: //--- circulators --- - /** \name Vertex and Face circulators + /** \name Vertex, Face, and Edge circulators */ //@{ @@ -803,6 +856,20 @@ public: ConstFaceFaceCWIter cff_cwiter(FaceHandle _fh) const; /// const face - face circulator ConstFaceFaceCCWIter cff_ccwiter(FaceHandle _fh) const; + + /// edge - vertex circulator + EdgeVertexIter ev_iter(EdgeHandle _eh); + /// edge - halfedge circulator + EdgeHalfedgeIter eh_iter(EdgeHandle _eh); + /// edge - face circulator + EdgeFaceIter ef_iter(EdgeHandle _eh); + + /// const edge - vertex circulator + ConstEdgeVertexIter cev_iter(EdgeHandle _eh) const; + /// const edge - halfedge circulator + ConstEdgeHalfedgeIter ceh_iter(EdgeHandle _eh) const; + /// const edge - face circulator + ConstEdgeFaceIter cef_iter(EdgeHandle _eh) const; // 'begin' circulators @@ -930,6 +997,20 @@ public: ConstHalfedgeLoopCWIter chl_cwbegin(HalfedgeHandle _heh) const; /// const halfedge circulator ccw ConstHalfedgeLoopCCWIter chl_ccwbegin(HalfedgeHandle _heh) const; + + /// edge - vertex circulator + EdgeVertexIter ev_begin(EdgeHandle _eh); + /// edge - halfedge circulator + EdgeHalfedgeIter eh_begin(EdgeHandle _eh); + /// edge - face circulator + EdgeFaceIter ef_begin(EdgeHandle _eh); + + /// const edge - vertex circulator + ConstEdgeVertexIter cev_begin(EdgeHandle _eh) const; + /// const edge - halfedge circulator + ConstEdgeHalfedgeIter ceh_begin(EdgeHandle _eh) const; + /// const edge - face circulator + ConstEdgeFaceIter cef_begin(EdgeHandle _eh) const; // 'end' circulators @@ -1056,6 +1137,21 @@ public: ConstHalfedgeLoopCWIter chl_cwend(HalfedgeHandle _heh) const; /// const face - face circulator ccw ConstHalfedgeLoopCCWIter chl_ccwend(HalfedgeHandle _heh) const; + + /// edge - vertex circulator + EdgeVertexIter ev_end(EdgeHandle _eh); + /// edge - halfedge circulator + EdgeHalfedgeIter eh_end(EdgeHandle _eh); + /// edge - face circulator + EdgeFaceIter ef_end(EdgeHandle _eh); + + /// const edge - vertex circulator + ConstEdgeVertexIter cev_end(EdgeHandle _eh) const; + /// const edge - halfedge circulator + ConstEdgeHalfedgeIter ceh_end(EdgeHandle _eh) const; + /// const edge - face circulator + ConstEdgeFaceIter cef_end(EdgeHandle _eh) const; + //@} /** @name Range based iterators and circulators */ @@ -1178,6 +1274,9 @@ public: typedef CirculatorRange> ConstFaceHalfedgeRange; typedef CirculatorRange> ConstFaceEdgeRange; typedef CirculatorRange> ConstFaceFaceRange; + typedef CirculatorRange> ConstEdgeVertexRange; + typedef CirculatorRange> ConstEdgeHalfedgeRange; + typedef CirculatorRange> ConstEdgeFaceRange; typedef CirculatorRange> ConstHalfedgeLoopRange; typedef CirculatorRange> ConstVertexVertexCWRange; @@ -1270,6 +1369,31 @@ public: */ ConstFaceFaceRange ff_range(FaceHandle _fh) const; + /** + * @return The vertices incident to the specified edge + * as a range object suitable for C++11 range based for loops. + */ + ConstEdgeVertexRange ev_range(EdgeHandle _eh) const; + + /** + * @return The halfedges of the specified edge + * as a range object suitable for C++11 range based for loops. + */ + ConstEdgeHalfedgeRange eh_range(EdgeHandle _eh) const; + + /** + * @return The halfedges of the specified edge + * as a range object suitable for C++11 range based for loops. + * Like eh_range(_heh.edge()) but starts iteration at _heh + */ + ConstEdgeHalfedgeRange eh_range(HalfedgeHandle _heh) const; + + /** + * @return The faces incident to the specified edge + * as a range object suitable for C++11 range based for loops. + */ + ConstEdgeFaceRange ef_range(EdgeHandle _eh) const; + /** * @return The halfedges in the face * as a range object suitable for C++11 range based for loops. @@ -1313,7 +1437,7 @@ public: * @return The edges incident to the specified vertex * as a range object suitable for C++11 range based for loops. */ - ConstVertexEdgeCWRange ve_cw_range(VertexHandle _vh) const ; + ConstVertexEdgeCWRange ve_cw_range(VertexHandle _vh) const; /** * @return The faces incident to the specified vertex @@ -1420,6 +1544,7 @@ public: */ ConstFaceFaceCCWRange ff_ccw_range(FaceHandle _fh) const; + /** * @return The halfedges in the face * as a range object suitable for C++11 range based for loops. diff --git a/src/OpenMesh/Core/Mesh/PolyConnectivity_inline_impl.hh b/src/OpenMesh/Core/Mesh/PolyConnectivity_inline_impl.hh index eab1e52d..a191dbbf 100644 --- a/src/OpenMesh/Core/Mesh/PolyConnectivity_inline_impl.hh +++ b/src/OpenMesh/Core/Mesh/PolyConnectivity_inline_impl.hh @@ -183,6 +183,22 @@ inline PolyConnectivity::ConstFaceFaceRange PolyConnectivity::ff_range(FaceHandl return ConstFaceFaceRange(*this, _fh); } +inline PolyConnectivity::ConstEdgeVertexRange PolyConnectivity::ev_range(EdgeHandle _eh) const { + return ConstEdgeVertexRange(*this, _eh); +} + +inline PolyConnectivity::ConstEdgeHalfedgeRange PolyConnectivity::eh_range(EdgeHandle _eh) const { + return ConstEdgeHalfedgeRange(*this, _eh); +} + +inline PolyConnectivity::ConstEdgeHalfedgeRange PolyConnectivity::eh_range(HalfedgeHandle _heh) const { + return ConstEdgeHalfedgeRange(*this, _heh, 1); +} + +inline PolyConnectivity::ConstEdgeFaceRange PolyConnectivity::ef_range(EdgeHandle _eh) const { + return ConstEdgeFaceRange(*this, _eh); +} + inline PolyConnectivity::ConstHalfedgeLoopRange PolyConnectivity::hl_range(HalfedgeHandle _heh) const { return ConstHalfedgeLoopRange(*this, _heh); } @@ -282,6 +298,7 @@ inline PolyConnectivity::ConstFaceFaceCCWRange PolyConnectivity::ff_ccw_range(Fa return ConstFaceFaceCCWRange(*this, _fh); } + inline PolyConnectivity::ConstHalfedgeLoopCCWRange PolyConnectivity::hl_ccw_range(HalfedgeHandle _heh) const { return ConstHalfedgeLoopCCWRange(*this, _heh); } @@ -523,6 +540,24 @@ inline PolyConnectivity::ConstFaceFaceCWIter PolyConnectivity::cff_cwiter(ArrayK inline PolyConnectivity::ConstFaceFaceCCWIter PolyConnectivity::cff_ccwiter(ArrayKernel::FaceHandle _fh) const { return ConstFaceFaceCCWIter(*this, _fh); } +inline PolyConnectivity::EdgeVertexIter PolyConnectivity::ev_iter(ArrayKernel::EdgeHandle _eh) +{ return EdgeVertexIter(*this, _eh); } + +inline PolyConnectivity::EdgeHalfedgeIter PolyConnectivity::eh_iter(ArrayKernel::EdgeHandle _eh) +{ return EdgeHalfedgeIter(*this, _eh); } + +inline PolyConnectivity::EdgeFaceIter PolyConnectivity::ef_iter(ArrayKernel::EdgeHandle _eh) +{ return EdgeFaceIter(*this, _eh); } + +inline PolyConnectivity::ConstEdgeVertexIter PolyConnectivity::cev_iter(ArrayKernel::EdgeHandle _eh) const +{ return ConstEdgeVertexIter(*this, _eh); } + +inline PolyConnectivity::ConstEdgeHalfedgeIter PolyConnectivity::ceh_iter(ArrayKernel::EdgeHandle _eh) const +{ return ConstEdgeHalfedgeIter(*this, _eh); } + +inline PolyConnectivity::ConstEdgeFaceIter PolyConnectivity::cef_iter(ArrayKernel::EdgeHandle _eh) const +{ return ConstEdgeFaceIter(*this, _eh); } + inline PolyConnectivity::VertexVertexIter PolyConnectivity::vv_begin(VertexHandle _vh) { return VertexVertexIter(*this, _vh); } @@ -707,6 +742,27 @@ inline PolyConnectivity::ConstHalfedgeLoopCWIter PolyConnectivity::chl_cwbegin(H inline PolyConnectivity::ConstHalfedgeLoopCCWIter PolyConnectivity::chl_ccwbegin(HalfedgeHandle _heh) const { return ConstHalfedgeLoopCCWIter(*this, _heh); } + +inline PolyConnectivity::EdgeVertexIter PolyConnectivity::ev_begin(EdgeHandle _eh) +{ return EdgeVertexIter(*this, _eh); } + +inline PolyConnectivity::EdgeHalfedgeIter PolyConnectivity::eh_begin(EdgeHandle _eh) +{ return EdgeHalfedgeIter(*this, _eh); } + +inline PolyConnectivity::EdgeFaceIter PolyConnectivity::ef_begin(EdgeHandle _eh) +{ return EdgeFaceIter(*this, _eh); } + + +inline PolyConnectivity::ConstEdgeVertexIter PolyConnectivity::cev_begin(EdgeHandle _eh) const +{ return ConstEdgeVertexIter(*this, _eh); } + +inline PolyConnectivity::ConstEdgeHalfedgeIter PolyConnectivity::ceh_begin(EdgeHandle _eh) const +{ return ConstEdgeHalfedgeIter(*this, _eh); } + +inline PolyConnectivity::ConstEdgeFaceIter PolyConnectivity::cef_begin(EdgeHandle _eh) const +{ return ConstEdgeFaceIter(*this, _eh); } + + // 'end' circulators inline PolyConnectivity::VertexVertexIter PolyConnectivity::vv_end(VertexHandle _vh) @@ -893,6 +949,26 @@ inline PolyConnectivity::ConstHalfedgeLoopCCWIter PolyConnectivity::chl_ccwend(H { return ConstHalfedgeLoopCCWIter(*this, _heh, true); } +inline PolyConnectivity::EdgeVertexIter PolyConnectivity::ev_end(EdgeHandle _eh) +{ return EdgeVertexIter(*this, _eh, true); } + +inline PolyConnectivity::EdgeHalfedgeIter PolyConnectivity::eh_end(EdgeHandle _eh) +{ return EdgeHalfedgeIter(*this, _eh, true); } + +inline PolyConnectivity::EdgeFaceIter PolyConnectivity::ef_end(EdgeHandle _eh) +{ return EdgeFaceIter(*this, _eh, true); } + + +inline PolyConnectivity::ConstEdgeVertexIter PolyConnectivity::cev_end(EdgeHandle _eh) const +{ return ConstEdgeVertexIter(*this, _eh, true); } + +inline PolyConnectivity::ConstEdgeHalfedgeIter PolyConnectivity::ceh_end(EdgeHandle _eh) const +{ return ConstEdgeHalfedgeIter(*this, _eh, true); } + +inline PolyConnectivity::ConstEdgeFaceIter PolyConnectivity::cef_end(EdgeHandle _eh) const +{ return ConstEdgeFaceIter(*this, _eh, true); } + + inline PolyConnectivity::ConstVertexFaceRange SmartVertexHandle::faces() const { assert(mesh() != nullptr); return mesh()->vf_range (*this); } inline PolyConnectivity::ConstVertexFaceCWRange SmartVertexHandle::faces_cw() const { assert(mesh() != nullptr); return mesh()->vf_cw_range (*this); } inline PolyConnectivity::ConstVertexFaceCCWRange SmartVertexHandle::faces_ccw() const { assert(mesh() != nullptr); return mesh()->vf_ccw_range(*this); } @@ -943,4 +1019,13 @@ inline PolyConnectivity::ConstFaceFaceRange SmartFaceHandle::faces() inline PolyConnectivity::ConstFaceFaceCWRange SmartFaceHandle::faces_cw() const { assert(mesh() != nullptr); return mesh()->ff_cw_range (*this); } inline PolyConnectivity::ConstFaceFaceCCWRange SmartFaceHandle::faces_ccw() const { assert(mesh() != nullptr); return mesh()->ff_ccw_range(*this); } + +inline PolyConnectivity::ConstEdgeVertexRange SmartEdgeHandle::vertices() const { assert(mesh() != nullptr); return mesh()->ev_range (*this); } + +inline PolyConnectivity::ConstEdgeHalfedgeRange SmartEdgeHandle::halfedges() const { assert(mesh() != nullptr); return mesh()->eh_range (*this); } + +inline PolyConnectivity::ConstEdgeHalfedgeRange SmartEdgeHandle::halfedges(HalfedgeHandle _heh) const { assert(mesh() != nullptr); return mesh()->eh_range (_heh); } + +inline PolyConnectivity::ConstEdgeFaceRange SmartEdgeHandle::faces() const { assert(mesh() != nullptr); return mesh()->ef_range (*this); } + }//namespace OpenMesh diff --git a/src/OpenMesh/Core/Mesh/SmartHandles.hh b/src/OpenMesh/Core/Mesh/SmartHandles.hh index 812fbd79..67e21c88 100644 --- a/src/OpenMesh/Core/Mesh/SmartHandles.hh +++ b/src/OpenMesh/Core/Mesh/SmartHandles.hh @@ -213,6 +213,15 @@ struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle, S SmartVertexHandle v0() const; /// Shorthand for vertex(1) SmartVertexHandle v1() const; + + /// Returns a range of vertices incident to the edge (PolyConnectivity::ev_range()) + PolyConnectivity::ConstEdgeVertexRange vertices() const; + /// Returns a range of halfedges of the edge (PolyConnectivity::eh_range()) + PolyConnectivity::ConstEdgeHalfedgeRange halfedges() const; + /// Returns a range of halfedges of the edge (PolyConnectivity::eh_range()) + PolyConnectivity::ConstEdgeHalfedgeRange halfedges(HalfedgeHandle _heh) const; + /// Returns a range of faces incident to the edge (PolyConnectivity::ef_range()) + PolyConnectivity::ConstEdgeFaceRange faces() const; }; struct OPENMESHDLLEXPORT SmartFaceHandle : public SmartBaseHandle, FaceHandle, SmartHandleStatusPredicates, SmartHandleBoundaryPredicate @@ -415,13 +424,13 @@ inline SmartFaceHandle SmartHalfedgeHandle::face() const return make_smart(mesh()->face_handle(*this), mesh()); } -inline SmartHalfedgeHandle SmartEdgeHandle::halfedge(unsigned int _i) const +inline SmartHalfedgeHandle SmartEdgeHandle::halfedge(unsigned int _i = 0) const { assert(mesh() != nullptr); return make_smart(mesh()->halfedge_handle(*this, _i), mesh()); } -inline SmartHalfedgeHandle SmartEdgeHandle::h(unsigned int _i) const +inline SmartHalfedgeHandle SmartEdgeHandle::h(unsigned int _i = 0) const { return halfedge(_i); } diff --git a/src/Unittests/CMakeLists.txt b/src/Unittests/CMakeLists.txt index 8b82a407..fe8cfbeb 100644 --- a/src/Unittests/CMakeLists.txt +++ b/src/Unittests/CMakeLists.txt @@ -38,8 +38,11 @@ unittests_sr_binary.cc unittests_stripifier.cc unittests_subdivider_adaptive.cc unittests_subdivider_uniform.cc -unittests_trimesh_circulator_current_halfedge_handle_replacement.cc unittests_traits.cc +unittests_trimesh_circulator_current_halfedge_handle_replacement.cc +unittests_trimesh_circulator_edge_face.cc +unittests_trimesh_circulator_edge_halfedge.cc +unittests_trimesh_circulator_edge_vertex.cc unittests_trimesh_circulator_face_edge.cc unittests_trimesh_circulator_face_face.cc unittests_trimesh_circulator_face_halfedge.cc @@ -53,6 +56,7 @@ unittests_trimesh_circulator_vertex_vertex.cc unittests_trimesh_collapse.cc unittests_trimesh_garbage_collection.cc unittests_trimesh_iterators.cc +unittests_trimesh_navigation.cc unittests_trimesh_others.cc unittests_trimesh_ranges.cc unittests_trimesh_split.cc diff --git a/src/Unittests/unittests_smart_handles.cc b/src/Unittests/unittests_smart_handles.cc index eec50a1a..39afebf6 100644 --- a/src/Unittests/unittests_smart_handles.cc +++ b/src/Unittests/unittests_smart_handles.cc @@ -177,6 +177,10 @@ TEST_F(OpenMeshSmartHandles, SimpleNavigation) for (auto eh : mesh_.edges()) { + EXPECT_EQ(mesh_.halfedge_handle(eh), eh.halfedge()) << "halfedge of edge does not match"; + EXPECT_EQ(mesh_.halfedge_handle(eh, 0), eh.halfedge(0)) << "halfedge 0 of edge does not match"; + EXPECT_EQ(mesh_.halfedge_handle(eh, 1), eh.halfedge(1)) << "halfedge 1 of edge does not match"; + EXPECT_EQ(mesh_.halfedge_handle(eh), eh.h()) << "halfedge of edge does not match"; EXPECT_EQ(mesh_.halfedge_handle(eh, 0), eh.h0()) << "halfedge 0 of edge does not match"; EXPECT_EQ(mesh_.halfedge_handle(eh, 1), eh.h1()) << "halfedge 1 of edge does not match"; EXPECT_EQ(mesh_.from_vertex_handle(mesh_.halfedge_handle(eh, 0)), eh.v0()) << "first vertex of edge does not match"; diff --git a/src/Unittests/unittests_trimesh_circulator_edge.hh b/src/Unittests/unittests_trimesh_circulator_edge.hh new file mode 100644 index 00000000..cbdd9ac2 --- /dev/null +++ b/src/Unittests/unittests_trimesh_circulator_edge.hh @@ -0,0 +1,62 @@ +#include + +#include + +namespace { + +class OpenMeshTrimeshCirculatorEdge : public OpenMeshBase { +public: + using VH = OpenMesh::VertexHandle; + using FH = OpenMesh::FaceHandle; + using EH = OpenMesh::EdgeHandle; + using HEH = OpenMesh::HalfedgeHandle; + + // This function is called before each test is run. + void SetUp() override { + std::vector vh; + + // 3----2 5 + // | /| | + // | / | | + // | / | | + // |/ | | + // 0----1 4 + + // A quad consisting of two triangles. + vh.push_back(mesh_.add_vertex({0.0, 0.0, 0.0})); // vh[0] + vh.push_back(mesh_.add_vertex({1.0, 0.0, 0.0})); // vh[1] + vh.push_back(mesh_.add_vertex({1.0, 1.0, 0.0})); // vh[2] + vh.push_back(mesh_.add_vertex({0.0, 1.0, 0.0})); // vh[3] + mesh_.add_face(vh[0], vh[1], vh[2]); + mesh_.add_face(vh[0], vh[2], vh[3]); + + // An isolated edge. + vh.push_back(mesh_.add_vertex({2.0, 0.0, 0.0})); // vh[4] + vh.push_back(mesh_.add_vertex({2.0, 1.0, 0.0})); // vh[5] + auto heh = mesh_.new_edge(vh[4], vh[5]); + auto heh_opp = mesh_.opposite_halfedge_handle(heh); + mesh_.set_halfedge_handle(vh[4], heh); + mesh_.set_halfedge_handle(vh[5], heh_opp); + + InteriorEdge = Edge(0, 2); + BoundaryEdge = Edge(0, 1); + IsolatedEdge = Edge(4, 5); + } + + // Helper function to quickly retrieve edges from their endpoint vertex IDs. + EH Edge(int _vh0_idx, int _vh1_idx) { + VH vh0(_vh0_idx); + VH vh1(_vh1_idx); + HEH heh = mesh_.find_halfedge(vh0, vh1); + if (!heh.is_valid()) + return EH(); + return mesh_.edge_handle(heh); + } + + // Handles of some interesting edges. + EH InteriorEdge; + EH BoundaryEdge; + EH IsolatedEdge; +}; + +} diff --git a/src/Unittests/unittests_trimesh_circulator_edge_face.cc b/src/Unittests/unittests_trimesh_circulator_edge_face.cc new file mode 100644 index 00000000..cc55bbfe --- /dev/null +++ b/src/Unittests/unittests_trimesh_circulator_edge_face.cc @@ -0,0 +1,142 @@ +#include "unittests_trimesh_circulator_edge.hh" + +#include +#include + +#include + +namespace { + +class OpenMeshTrimeshCirculatorEdgeFace : public OpenMeshTrimeshCirculatorEdge { +public: + // Check that the _visited faces comply with the expected iteration order on _eh. + void CheckIteration(EH _eh, const std::vector& _visited) { + // Up to 2 elements, depending on whether incident faces exist. + ASSERT_TRUE(mesh_.is_valid_handle(_eh)); + const auto eh = make_smart(_eh, mesh_); + std::vector expected; + for (int i = 0; i < 2; ++i) { + const auto fh = eh.halfedge(i).face(); + if (fh.is_valid()) { + expected.push_back(fh); + } + } + + // Elements must have been visited in the expected order. + ASSERT_EQ(_visited.size(), expected.size()); + for (size_t i = 0; i < _visited.size(); ++i) { + EXPECT_EQ(_visited[i], expected[i]); + } + } +}; + +// Mutable mesh. + +TEST_F(OpenMeshTrimeshCirculatorEdgeFace, Iter) { + for (auto eh : mesh_.edges()) { + std::vector visited; + for (auto it = mesh_.ef_iter(eh); it.is_valid(); ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeFace, BeginEnd) { + for (auto eh : mesh_.edges()) { + std::vector visited; + for (auto it = mesh_.ef_begin(eh), end = mesh_.ef_end(eh); it != end; ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeFace, Range) { + for (auto eh : mesh_.edges()) { + std::vector visited; + for (auto fh : mesh_.ef_range(eh)) { + visited.push_back(fh); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeFace, SmartHandleRange) { + for (auto eh : mesh_.edges()) { + std::vector visited; + auto smart_eh = make_smart(eh, mesh_); + for (auto fh : smart_eh.faces()) { + visited.push_back(fh); + } + CheckIteration(eh, visited); + } +} + +// const mesh. + +TEST_F(OpenMeshTrimeshCirculatorEdgeFace, ConstIter) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + for (auto it = const_mesh.cef_iter(eh); it.is_valid(); ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeFace, ConstBeginEnd) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + for (auto it = const_mesh.cef_begin(eh), end = const_mesh.cef_end(eh); it != end; ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeFace, ConstRange) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + for (auto fh : const_mesh.ef_range(eh)) { + visited.push_back(fh); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeFace, ConstSmartHandleRange) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + auto smart_eh = make_smart(eh, const_mesh); + for (auto fh : smart_eh.faces()) { + visited.push_back(fh); + } + CheckIteration(eh, visited); + } +} + +// Expected number of faces for interior, boundary, isolated edges. +TEST_F(OpenMeshTrimeshCirculatorEdgeFace, ExpectedNumber) { + std::vector> pairs = { + { InteriorEdge, 2 }, + { BoundaryEdge, 1 }, + { IsolatedEdge, 0 }, + }; + for (const auto& pair : pairs) { + const auto& eh = pair.first; + const auto& expected_faces = pair.second; + int visited_faces = 0; + for (auto fh : mesh_.ef_range(eh)) { + (void)fh; // Unused + ++visited_faces; + } + EXPECT_EQ(visited_faces, expected_faces); + } +} + +} diff --git a/src/Unittests/unittests_trimesh_circulator_edge_halfedge.cc b/src/Unittests/unittests_trimesh_circulator_edge_halfedge.cc new file mode 100644 index 00000000..ec4869f5 --- /dev/null +++ b/src/Unittests/unittests_trimesh_circulator_edge_halfedge.cc @@ -0,0 +1,184 @@ +#include "unittests_trimesh_circulator_edge.hh" + +#include +#include + +#include + +namespace { + +class OpenMeshTrimeshCirculatorEdgeHalfedge : public OpenMeshTrimeshCirculatorEdge { +public: + // Check that the _visited halfedges comply with the expected iteration order on _eh. + void CheckIteration(EH _eh, const std::vector& _visited) { + ASSERT_TRUE(mesh_.is_valid_handle(_eh)); + const auto eh = make_smart(_eh, mesh_); + std::vector expected; + for (int i = 0; i < 2; ++i) { + expected.push_back(eh.halfedge(i)); + } + + // Elements must have been visited in the expected order. + ASSERT_EQ(_visited.size(), expected.size()); + for (size_t i = 0; i < _visited.size(); ++i) { + EXPECT_EQ(_visited[i], expected[i]); + } + } + + // Check that the _visited halfedges comply with the expected iteration order on _heh.edge(), starting at _heh. + void CheckIteration(HEH _heh, const std::vector& _visited) { + // Always exactly 2 elements. + ASSERT_TRUE(mesh_.is_valid_handle(_heh)); + std::vector expected; + expected.push_back(_heh); + expected.push_back(mesh_.opposite_halfedge_handle(_heh)); + + // Elements must have been visited in the expected order. + ASSERT_EQ(_visited.size(), expected.size()); + for (size_t i = 0; i < _visited.size(); ++i) { + EXPECT_EQ(_visited[i], expected[i]); + } + } +}; + +// Mutable mesh. + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, Iter) { + for (auto eh : mesh_.edges()) { + std::vector visited; + for (auto it = mesh_.eh_iter(eh); it.is_valid(); ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, BeginEnd) { + for (auto eh : mesh_.edges()) { + std::vector visited; + for (auto it = mesh_.eh_begin(eh), end = mesh_.eh_end(eh); it != end; ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, Range) { + for (auto eh : mesh_.edges()) { + std::vector visited; + for (auto vh : mesh_.eh_range(eh)) { + visited.push_back(vh); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, SmartHandleRange) { + for (auto eh : mesh_.edges()) { + std::vector visited; + auto smart_eh = make_smart(eh, mesh_); + for (auto vh : smart_eh.halfedges()) { + visited.push_back(vh); + } + CheckIteration(eh, visited); + } +} + +// const mesh. + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, ConstIter) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + for (auto it = const_mesh.ceh_iter(eh); it.is_valid(); ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, ConstBeginEnd) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + for (auto it = const_mesh.ceh_begin(eh), end = const_mesh.ceh_end(eh); it != end; ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, ConstRange) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + for (auto vh : const_mesh.eh_range(eh)) { + visited.push_back(vh); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, ConstSmartHandleRange) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + auto smart_eh = make_smart(eh, const_mesh); + for (auto vh : smart_eh.halfedges()) { + visited.push_back(vh); + } + CheckIteration(eh, visited); + } +} + +// Mutable mesh, with given start halfedge. + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, InitializedRange) { + for (auto start_heh : mesh_.halfedges()) { + std::vector visited; + for (auto vh : mesh_.eh_range(start_heh)) { + visited.push_back(vh); + } + CheckIteration(start_heh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, InitializedSmartHandleRange) { + for (auto start_heh : mesh_.halfedges()) { + std::vector visited; + auto smart_start_heh = make_smart(start_heh, mesh_); + auto smart_start_eh = smart_start_heh.edge(); + for (auto vh : smart_start_eh.halfedges(smart_start_heh)) { + visited.push_back(vh); + } + CheckIteration(start_heh, visited); + } +} + +// const mesh, with given start halfedge. + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, ConstInitializedRange) { + const auto& const_mesh = mesh_; + for (auto start_heh : const_mesh.halfedges()) { + std::vector visited; + for (auto vh : mesh_.eh_range(start_heh)) { + visited.push_back(vh); + } + CheckIteration(start_heh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeHalfedge, ConstInitializedSmartHandleRange) { + const auto& const_mesh = mesh_; + for (auto start_heh : const_mesh.halfedges()) { + std::vector visited; + auto smart_start_heh = make_smart(start_heh, const_mesh); + auto smart_start_eh = smart_start_heh.edge(); + for (auto vh : smart_start_eh.halfedges(smart_start_heh)) { + visited.push_back(vh); + } + CheckIteration(start_heh, visited); + } +} + +} diff --git a/src/Unittests/unittests_trimesh_circulator_edge_vertex.cc b/src/Unittests/unittests_trimesh_circulator_edge_vertex.cc new file mode 100644 index 00000000..6157f99a --- /dev/null +++ b/src/Unittests/unittests_trimesh_circulator_edge_vertex.cc @@ -0,0 +1,119 @@ +#include "unittests_trimesh_circulator_edge.hh" + +#include +#include + +#include + +namespace { + +class OpenMeshTrimeshCirculatorEdgeVertex : public OpenMeshTrimeshCirculatorEdge { +public: + // Check that the _visited vertices comply with the expected iteration order on _eh. + void CheckIteration(EH _eh, const std::vector& _visited) { + ASSERT_TRUE(mesh_.is_valid_handle(_eh)); + const auto eh = make_smart(_eh, mesh_); + std::vector expected; + for (int i = 0; i < 2; ++i) { + expected.push_back(eh.vertex(i)); + } + + // Elements must have been visited in the expected order. + ASSERT_EQ(_visited.size(), expected.size()); + for (size_t i = 0; i < _visited.size(); ++i) { + EXPECT_EQ(_visited[i], expected[i]); + } + } +}; + +// Mutable mesh. + +TEST_F(OpenMeshTrimeshCirculatorEdgeVertex, Iter) { + for (auto eh : mesh_.edges()) { + std::vector visited; + for (auto it = mesh_.ev_iter(eh); it.is_valid(); ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeVertex, BeginEnd) { + for (auto eh : mesh_.edges()) { + std::vector visited; + for (auto it = mesh_.ev_begin(eh), end = mesh_.ev_end(eh); it != end; ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeVertex, Range) { + for (auto eh : mesh_.edges()) { + std::vector visited; + for (auto vh : mesh_.ev_range(eh)) { + visited.push_back(vh); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeVertex, SmartHandleRange) { + for (auto eh : mesh_.edges()) { + std::vector visited; + auto smart_eh = make_smart(eh, mesh_); + for (auto vh : smart_eh.vertices()) { + visited.push_back(vh); + } + CheckIteration(eh, visited); + } +} + +// const mesh. + +TEST_F(OpenMeshTrimeshCirculatorEdgeVertex, ConstIter) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + for (auto it = const_mesh.cev_iter(eh); it.is_valid(); ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeVertex, ConstBeginEnd) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + for (auto it = const_mesh.cev_begin(eh), end = const_mesh.cev_end(eh); it != end; ++it) { + visited.push_back(*it); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeVertex, ConstRange) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + for (auto vh : const_mesh.ev_range(eh)) { + visited.push_back(vh); + } + CheckIteration(eh, visited); + } +} + +TEST_F(OpenMeshTrimeshCirculatorEdgeVertex, ConstSmartHandleRange) { + const auto& const_mesh = mesh_; + for (auto eh : const_mesh.edges()) { + std::vector visited; + auto smart_eh = make_smart(eh, const_mesh); + for (auto vh : smart_eh.vertices()) { + visited.push_back(vh); + } + CheckIteration(eh, visited); + } +} + +} diff --git a/src/Unittests/unittests_trimesh_navigation.cc b/src/Unittests/unittests_trimesh_navigation.cc new file mode 100644 index 00000000..e03d928e --- /dev/null +++ b/src/Unittests/unittests_trimesh_navigation.cc @@ -0,0 +1,42 @@ +#include + +#include + +#include + +namespace { + +class OpenMeshTrimeshNavigation : public OpenMeshBase { +public: + using VH = OpenMesh::VertexHandle; + using FH = OpenMesh::FaceHandle; + using EH = OpenMesh::EdgeHandle; + using HEH = OpenMesh::HalfedgeHandle; + + // This function is called before each test is run. + void SetUp() override { + std::vector vh; + + // 3----2 + // | /| + // | / | + // | / | + // |/ | + // 0----1 + + vh.push_back(mesh_.add_vertex({0.0, 0.0, 0.0})); // vh[0] + vh.push_back(mesh_.add_vertex({1.0, 0.0, 0.0})); // vh[1] + vh.push_back(mesh_.add_vertex({1.0, 1.0, 0.0})); // vh[2] + vh.push_back(mesh_.add_vertex({0.0, 1.0, 0.0})); // vh[3] + mesh_.add_face(vh[0], vh[1], vh[2]); + mesh_.add_face(vh[0], vh[2], vh[3]); + } +}; + +TEST_F(OpenMeshTrimeshNavigation, EdgeHalfedgeDefault) { + for (EH eh : mesh_.edges()) { + EXPECT_EQ(mesh_.halfedge_handle(eh), mesh_.halfedge_handle(eh, 0)); + } +} + +}