/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2016 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #ifndef POLYGON_POLYCHORD_COLLAPSE_H #define POLYGON_POLYCHORD_COLLAPSE_H #include #include #if __cplusplus >= 201103L #include #else #include #endif #include #include #include #include #include namespace vcg { namespace tri { /** \addtogroup trimesh */ /** * @brief The PolychordCollapse class provides methods to semplify a quad mesh, by collapsing the polychords. * * This class is an implementation of a method very similar to that for mesh semplification proposed * by Daniels et al. in "Quadrilateral mesh simplification", see http://www.cs.utah.edu/~jdaniels/research/asia2008_qms.htm * The main function is PolychordCollapse::CollapsePolychord() which deletes all the quadrilateral faces in a polychord. * The polychords that can be collapsed in this case are those forming a closed loop (a ring) or that start and end to * mesh borders. A way to preserve the structure of the singularities is also provided. * The convenient method PolychordCollapse::CollapseAllPolychords() finds and collapses all the polychords on a mesh. * The input mesh should be polygonal, i.e. it should have the vcg::face::PolyInfo component. Even though a generic * triangle mesh can be given, actually the class does not perform any collapsing operation since it sees only triangles, * in fact it does not consider faux edges. */ template < typename PolyMeshType > class PolychordCollapse { public: typedef typename PolyMeshType::CoordType CoordType; typedef typename PolyMeshType::VertexType VertexType; typedef typename PolyMeshType::VertexPointer VertexPointer; typedef typename PolyMeshType::VertexIterator VertexIterator; typedef typename PolyMeshType::FaceType FaceType; typedef typename PolyMeshType::FacePointer FacePointer; typedef typename PolyMeshType::FaceIterator FaceIterator; /** * @brief The PC_ResultCode enum codifies the result type of a polychord collapse operation. */ enum PC_ResultCode { PC_SUCCESS = 0x000, PC_NOTMANIF = 0x001, PC_NOTQUAD = 0x002, PC_NOLINKCOND = 0x004, PC_SINGSIDEA = 0x008, PC_SINGSIDEB = 0x010, PC_SINGBOTH = 0x020, PC_SELFINTERSECT = 0x040, PC_NOMOREMANIF = 0x080, PC_VOID = 0x100, PC_OTHER = 0x100 }; /** * @brief The PC_Chord struct identifies a coord of a polychord passing through a quad. */ struct PC_Chord { unsigned long mark; PC_ResultCode q; PC_Chord * prev; PC_Chord * next; PC_Chord() : mark(std::numeric_limits::max()), q(PC_VOID), prev(NULL), next(NULL) { } inline void Reset() { mark = std::numeric_limits::max(); q = PC_VOID; prev = next = NULL; } }; /** * @brief The PC_Chords class gives efficient access to each coord (relative to a face). */ class PC_Chords { public: /** * @brief PC_Chords constructor. * @note Since each face corresponds to two chords, the actual size of the vector of chords is 2*mesh.face.size(). * @param mesh */ PC_Chords (const PolyMeshType &mesh) : _Chords(2*mesh.face.size()), _currentChord(NULL) { Reset(mesh); } /** * @brief ResetMarks */ void ResetMarks() { for (size_t i = 0; i < _Chords.size(); ++i) _Chords.at(i).mark = std::numeric_limits::max(); } /** * @brief Reset rearrages the container. * @note Since each face corresponds to two chords, the actual size of the vector of chords is 2*mesh.face.size(). * @param mesh */ void Reset(const PolyMeshType &mesh) { _Chords.resize(2*mesh.face.size()); for (size_t j = 0; j < _Chords.size(); ++j) _Chords[j].Reset(); _currentChord = NULL; PC_Chord *chord = NULL; long long j = 0; for (size_t i = 0; i < _Chords.size(); ++i) { // set the prev chord = NULL; if ((long long)i-1 >= 0) { chord = &_Chords[i-1]; if (vcg::tri::HasPerFaceFlags(mesh)) { j = i-1; while (j >= 0 && mesh.face[j/2].IsD()) --j; if (j >= 0) chord = &_Chords[j]; else chord = NULL; } } _Chords[i].prev = chord; // set the next chord = NULL; if (i+1 < _Chords.size()) { chord = &_Chords[i+1]; if (vcg::tri::HasPerFaceFlags(mesh)) { j = i+1; while (j < (long long)_Chords.size() && mesh.face[j/2].IsD()) ++j; if (j < (long long)_Chords.size()) chord = &_Chords[j]; else chord = NULL; } } _Chords[i].next = chord; } if (mesh.face.size() > 0) { // set the current coord (first - not deleted - face) _currentChord = &_Chords[0]; if (vcg::tri::HasPerFaceFlags(mesh) && mesh.face[0].IsD()) _currentChord = _currentChord->next; } } /** * @brief operator [], given a face index and an offset, it returns (a reference to) its corresponding PC_Chord. * @param face_edge A std::pair(face_index, offset). The offset should be 0 or 1. * @return A reference to the corresponding PC_Chord. */ inline PC_Chord & operator[] (const std::pair &face_edge) { assert(face_edge.first >= 0 && 2*face_edge.first+face_edge.second < _Chords.size()); return _Chords[2*face_edge.first + face_edge.second]; } /** * @brief operator [], given a face index and an offset, it returns (a const reference to) its corresponding PC_Chord. * @param face_edge A std::pair(face_index, offset). The offset should be 0 or 1. * @return A reference to the corresponding PC_Chord. */ inline const PC_Chord & operator[] (const std::pair &face_edge) const { assert(face_edge.first >= 0 && 2*face_edge.first+face_edge.second < _Chords.size()); return _Chords[2*face_edge.first + face_edge.second]; } /** * @brief operator [], given a coord, it returns its corresponding face index and edge. * @param coord The coord pointer. * @return A std::pair (face_index, offset) with offset being 0 or 1. */ inline std::pair operator[] (PC_Chord const * const coord) { assert(coord >= &_Chords[0] && coord < &_Chords[0]+_Chords.size()); return std::pair((coord - &_Chords[0])/2, (coord - &_Chords[0])%2); } /** * @brief UpdateCoord updates the coord information and links. * @param coord The coord to update. * @param mark The mark of the polychord. * @param resultCode The code for the type of the polychord. */ inline void UpdateCoord (PC_Chord &coord, const unsigned long mark, const PC_ResultCode resultCode) { // update prev and next if (coord.q == PC_VOID) { if (coord.prev != NULL && &coord != _currentChord) coord.prev->next = coord.next; if (coord.next != NULL && &coord != _currentChord) coord.next->prev = coord.prev; } coord.mark = mark; coord.q = resultCode; } /** * @brief Next, if it's not at the end, it goes to the next coord. */ inline void Next () { if (_currentChord != NULL) _currentChord = _currentChord->next; } /** * @brief GetCurrent returns the current FaceType pointer and edge. * @param face_edge A std::pair where to store the FaceType pointer and the edge index. */ inline void GetCurrent (std::pair &face_edge) { if (_currentChord != NULL) { face_edge.first = (_currentChord - &_Chords[0])/2; face_edge.second = (_currentChord - &_Chords[0])%2; } else { face_edge.first = std::numeric_limits::max(); face_edge.second = 0; } } /** * @brief End says if an end has been reached. * @return true if an end has been reached, false otherwise. */ inline bool End () { return _currentChord == NULL; } private: std::vector _Chords; PC_Chord *_currentChord; }; /** * @brief The LinkCondition class provides a tool to check if a polychord satisfies the link conditions. */ class LinkConditions { private: typedef long int LCVertexIndex; typedef std::set LCVertexStar; ///< define the star of a vertex typedef long int LCEdgeIndex; typedef std::set LCEdgeStar; ///< define the set of edges whose star involves a vertex /** * @brief The LCVertex struct represents a vertex for the Link Conditions. */ struct LCVertex { LCVertexStar star; // vertex star LCEdgeStar edges; // list of edges whose star involves this vertex LCVertex(){} // default constructor LCVertex(const LCVertex &lcVertex) { // copy constructor star = lcVertex.star; edges = lcVertex.edges; } LCVertex & operator=(const LCVertex &lcVertex) { // assignment operator star = lcVertex.star; edges = lcVertex.edges; return *this; } void reset() { star.clear(); edges.clear(); } // reset }; /** * @brief The LCEdge struct represents an edge for the Link Conditions. */ struct LCEdge { LCVertexIndex v1, v2; // endpoints LCVertexStar star; // edge star LCEdge() {v1 = v2 = -1;} // default contructor LCEdge(const LCEdge &lcEdge) { // copy constructor v1 = lcEdge.v1; v2 = lcEdge.v2; star = lcEdge.star; } LCEdge & operator=(const LCEdge &lcEdge) { // assignment operator v1 = lcEdge.v1; v2 = lcEdge.v2; star = lcEdge.star; return *this; } void reset() { // reset v1 = -1; v2 = -1; star.clear(); } }; public: /** * @brief LinkCondition constructor. * @param size The number of vertices of the mesh. */ LinkConditions (const size_t size) : _lcVertices(size) { } /** * @brief Resize just resets the size of the container. * @param size */ inline void Resize(const size_t size) { _lcVertices.resize(size); LC_ResetStars(); } /** * @brief CheckLinkConditions checks if collapsing the polychord starting from startPos * satisfies the link conditions. * @warning The polychord starts from startPos and ends to itself (if it's a loop) or to a border. In the latter case, * call this method starting from the opposite border of the strip of quads. * @param mesh The mesh for getting the vertex index. * @param startPos The starting position of the polychord. * @return true if satisfied, false otherwise. */ bool CheckLinkConditions (const PolyMeshType &mesh, const vcg::face::Pos &startPos) { assert(!startPos.IsNull()); assert(mesh.vert.size() == _lcVertices.size()); std::vector lcEdges; LCVertexStar intersection; // reset the stars LC_ResetStars(); // compute the stars LC_computeStars(mesh, startPos, lcEdges); // for each edge e = (v1,v2) // if intersection( star(v1) , star(v2) ) == star(e) // then collapse e // else // return false (i.e. link conditions not satisfied) for (size_t e = 0; e < lcEdges.size(); e++) { // compute the intersetion intersection.clear(); std::set_intersection(_lcVertices[lcEdges[e].v1].star.begin(), _lcVertices[lcEdges[e].v1].star.end(), _lcVertices[lcEdges[e].v2].star.begin(), _lcVertices[lcEdges[e].v2].star.end(), std::inserter(intersection, intersection.end())); // if intersection( star(v1) , star(v2) ) != star(e) then return false if (intersection != lcEdges[e].star) return false; // else simulate the collapse LC_SimulateEdgeCollapse(lcEdges, e); } // at this point all collapses are possible, thus return true return true; } private: /** * @brief LC_ResetStars resets the stars on a polychord. */ void LC_ResetStars() { for (size_t v = 0; v < _lcVertices.size(); ++v) _lcVertices[v].reset(); } /** * @brief LC_computeStars computes the stars of edges and vertices of the polychord from the starting pos * either to itself (if it's a loop) or to the border edge. * @param mesh The mesh for getting the vertex index. * @param startPos Starting position. * @param lcEdges Vector of edge stars. */ void LC_computeStars (const PolyMeshType &mesh, const vcg::face::Pos &startPos, std::vector &lcEdges) { assert(!startPos.IsNull()); assert(mesh.vert.size() == _lcVertices.size()); vcg::face::Pos runPos = startPos; vcg::face::JumpingPos vStarPos; vcg::face::Pos eStarPos; LCEdgeIndex edgeInd = -1; size_t nEdges = 0; // count how many edges do { ++nEdges; // go on the next edge runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (runPos != startPos && !runPos.IsBorder()); if (runPos.IsBorder()) ++nEdges; // resize the vector of edges lcEdges.resize(nEdges); for (size_t e = 0; e < nEdges; ++e) lcEdges[e].reset(); /// compute the star of all the vertices and edges seen from the polychord runPos = startPos; do { // access the next lcedge edgeInd++; // set lcvertices references lcEdges[edgeInd].v1 = vcg::tri::Index(mesh, runPos.V()); lcEdges[edgeInd].v2 = vcg::tri::Index(mesh, runPos.VFlip()); // add this edge to its vertices edge-stars _lcVertices[lcEdges[edgeInd].v1].edges.insert(edgeInd); _lcVertices[lcEdges[edgeInd].v2].edges.insert(edgeInd); // compute the star of this edge lcEdges[edgeInd].star.insert(lcEdges[edgeInd].v1); // its endpoints, clearly lcEdges[edgeInd].star.insert(lcEdges[edgeInd].v2); // its endpoints, clearly // navigate over the other vertices of this facet eStarPos = runPos; eStarPos.FlipE(); eStarPos.FlipV(); while (eStarPos.V() != runPos.VFlip()) { // add current vertex to the star of this edge lcEdges[edgeInd].star.insert(vcg::tri::Index(mesh, eStarPos.V())); // add this edge to the edge-star of the current vertex _lcVertices[vcg::tri::Index(mesh, eStarPos.V())].edges.insert(edgeInd); // go on eStarPos.FlipE(); eStarPos.FlipV(); } // go on the opposite facet if (!runPos.IsBorder()) { eStarPos = runPos; eStarPos.FlipF(); eStarPos.FlipE(); eStarPos.FlipV(); while (eStarPos.V() != runPos.VFlip()) { // add current vertex to the star of this edge lcEdges[edgeInd].star.insert(vcg::tri::Index(mesh, eStarPos.V())); // add this edge to the edge-star of the current vertex _lcVertices[vcg::tri::Index(mesh, eStarPos.V())].edges.insert(edgeInd); // go on eStarPos.FlipE(); eStarPos.FlipV(); } } // compute the star of vertex v2 runPos.FlipV(); vStarPos.Set(runPos.F(), runPos.E(), runPos.V()); // v2 is in its star _lcVertices[vcg::tri::Index(mesh, vStarPos.V())].star.insert(vcg::tri::Index(mesh, vStarPos.V())); do { vStarPos.FlipV(); vStarPos.FlipE(); while (vStarPos.V() != runPos.V()) { // add the current vertex to the v2 star _lcVertices[vcg::tri::Index(mesh, runPos.V())].star.insert(vcg::tri::Index(mesh, vStarPos.V())); // add v2 to the star of the current vertex _lcVertices[vcg::tri::Index(mesh, vStarPos.V())].star.insert(vcg::tri::Index(mesh, runPos.V())); vStarPos.FlipV(); vStarPos.FlipE(); } vStarPos.NextFE(); } while (vStarPos != runPos); // compute the star of vertex v1 runPos.FlipV(); vStarPos.Set(runPos.F(), runPos.E(), runPos.V()); // v1 is in its star _lcVertices[vcg::tri::Index(mesh, vStarPos.V())].star.insert(vcg::tri::Index(mesh, vStarPos.V())); do { vStarPos.FlipV(); vStarPos.FlipE(); while (vStarPos.V() != runPos.V()) { // add the current vertex to the v2 star _lcVertices[vcg::tri::Index(mesh, runPos.V())].star.insert(vcg::tri::Index(mesh, vStarPos.V())); // add v2 to the star of the current vertex _lcVertices[vcg::tri::Index(mesh, vStarPos.V())].star.insert(vcg::tri::Index(mesh, runPos.V())); vStarPos.FlipV(); vStarPos.FlipE(); } vStarPos.NextFE(); } while (vStarPos != runPos); // when arrive to a border, stop if (runPos != startPos && runPos.IsBorder()) break; // go on the next edge runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (runPos != startPos); // check if the starting pos or the border has been reached assert(runPos == startPos || runPos.IsBorder()); } /** * @brief LC_SimulateEdgeCollapse simulates an edge collapse by updating the stars involved. * @param lcEdges The vector of edges. * @param edgeInd The in dex of the edge to collapse. */ void LC_SimulateEdgeCollapse (std::vector &lcEdges, const LCEdgeIndex edgeInd) { // let v1 and v2 be the two end points LCVertexIndex v1 = lcEdges[edgeInd].v1; LCVertexIndex v2 = lcEdges[edgeInd].v2; LCVertexIndex v = -1; /// v2 merges into v1: // star(v1) = star(v1) U star(v2) _lcVertices[v1].star.insert(_lcVertices[v2].star.begin(), _lcVertices[v2].star.end()); _lcVertices[v1].star.erase(v2); // remove v2 from v1-star _lcVertices[v2].star.erase(v1); // remove v1 from v2-star // foreach v | v2 \in star(v) [i.e. v \in star(v2)] // star(v) = star(v) U {v1} \ {v2} for (typename LCVertexStar::iterator vIt = _lcVertices[v2].star.begin(); vIt != _lcVertices[v2].star.end(); ++vIt) { v = *vIt; if (v == v2) // skip v2 itself continue; _lcVertices[v].star.insert(v1); _lcVertices[v].star.erase(v2); } /// update the star of the edges which include v1 and v2 in their star // foreach e | v1 \in star(e) ^ v2 \in star(e) // star(e) = star(e) \ {v1,v2} U {v1} for (typename LCEdgeStar::iterator eIt = _lcVertices[v1].edges.begin(); eIt != _lcVertices[v1].edges.end(); ++eIt) lcEdges[*eIt].star.erase(v2); for (typename LCEdgeStar::iterator eIt = _lcVertices[v2].edges.begin(); eIt != _lcVertices[v2].edges.end(); ++eIt) { lcEdges[*eIt].star.erase(v2); lcEdges[*eIt].star.insert(v1); } } /** * @brief _lcVertices is a vector of vertex stars for the link conditions. */ std::vector _lcVertices; }; // PolychordCollapse's methods begin here:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: // /** // * @brief CheckConsistent checks for consistency. ONLY FOR DEBUG. // * @param mesh // * @return // */ // static bool CheckConsistent(PolyMeshType &mesh) { // vcg::tri::RequirePerFaceFlags(mesh); // vcg::tri::RequirePerFaceColor(mesh); // for (size_t f = 0; f < mesh.face.size(); ++f) { // if (!mesh.face[f].IsD()) { // for (int v = 0; v < mesh.face[f].VN(); ++v) { // if (!vcg::face::IsBorder(mesh.face[f], v)) { // if (mesh.face[f].FFp(v)->IsD()) { // mesh.face[f].C() = vcg::Color4b(vcg::Color4b::Magenta); // return false; // } // if (mesh.face[f].FFp(v)->FFp(mesh.face[f].FFi(v)) != &mesh.face[f]) { // mesh.face[f].C() = vcg::Color4b(vcg::Color4b::Yellow); // return false; // } // } // } // } // } // return true; // } /** * @brief MarkPolychords marks the chords of the polychord starting at startPos. * @param mesh The input mesh. * @param startPos The starting position. * @param chords The vector of chords. * @param mark The current mark, used to identify quads already visited. */ static void MarkPolychords(const PolyMeshType &mesh, const vcg::face::Pos &startPos, PC_Chords &chords, const unsigned long mark) { vcg::face::Pos runPos = startPos; std::pair face_edge(std::numeric_limits::max(), 0); do { assert(runPos.F()->VN() == 4); face_edge.first = vcg::tri::Index(mesh, runPos.F()); face_edge.second = runPos.E() % 2; chords[face_edge].mark = mark; runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (runPos != startPos && !runPos.IsBorder()); } /** * @brief CollapsePolychord performs all checks and then collapses the polychord. * * @warning This function deletes faces and vertices by calling * vcg::tri::Allocator::DeleteFace() and * vcg::tri::Allocator::DeleteVertex(). * The object PC_Chords chords is used to track the polychords, and it has got * a size proportional to that of the mesh face container. If you actually * delete faces and vertices by calling vcg::tri::Allocator::CompactFaceVector() * and vcg::tri::Allocator::CompactVertexVector() after this function, * object PC_Chords chords then is not valid any more, so you MUST rearrange it * by calling PC_Chords.Reset(). For the same reason, you MUST rearrange LinkConditions linkConditions * by calling LinkConditions.Resize(). * However, for efficiency, you SHOULD compact vertex and face containers at the end of all your * polychord collapsing operations, without having to rearrange chords and linkConditions. * The function CollapseAllPolychords() does this for you. * * @note Vertex flags, face flags, FF adjacency and FV adjacency are required. Not anything else. * Such components are automatically updated here. If the mesh has other components that may be * affected by this editing, you should update them later by yourself. * * @param mesh The polygonal mesh used for getting the face index and deleting the faces * (it SHOULD have the vcg::face::PolyInfo component). * @param pos Position of the polychord. * @param mark Mark for the current polychord. * @param chords Vector of chords. * @param linkConditions Link conditions checker. * @param checkSing true if singularities on both sides are not allowed. * @return A PC_ResultCode resulting from checks or PC_SUCCESS if the collapse has been performed. */ static PC_ResultCode CollapsePolychord (PolyMeshType &mesh, const vcg::face::Pos &pos, const unsigned long mark, PC_Chords &chords, LinkConditions &linkConditions, const bool checkSing = true) { vcg::tri::RequireFFAdjacency(mesh); if (mesh.IsEmpty()) return PC_VOID; if (pos.IsNull()) return PC_VOID; vcg::face::Pos tempPos, startPos; // check if the sequence of facets is a polychord and find the starting coord PC_ResultCode resultCode = CheckPolychordFindStartPosition(pos, startPos, checkSing); // if not successful, visit the sequence for marking it and return if (resultCode != PC_SUCCESS && resultCode != PC_SINGSIDEA && resultCode != PC_SINGSIDEB) { // if not manifold, visit the entire polychord ending on the non-manifold edge if (resultCode == PC_NOTMANIF) { tempPos = pos; VisitPolychord(mesh, tempPos, chords, mark, resultCode); if (tempPos.IsManifold() && !tempPos.IsBorder()) { tempPos.FlipF(); VisitPolychord(mesh, tempPos, chords, mark, resultCode); } return resultCode; } // if not quad, visit all the polychords passing through this coord if (resultCode == PC_NOTQUAD) { tempPos = startPos; do { if (!tempPos.IsBorder()) { tempPos.FlipF(); VisitPolychord(mesh, tempPos, chords, mark, resultCode); tempPos.FlipF(); } tempPos.FlipV(); tempPos.FlipE(); } while (tempPos != startPos); VisitPolychord(mesh, startPos, chords, mark, resultCode); return resultCode; } VisitPolychord(mesh, startPos, chords, mark, resultCode); return resultCode; } // check if the link conditions are satisfied // if not satisfied, visit the sequence for marking it and return if (!linkConditions.CheckLinkConditions(mesh, startPos)) { VisitPolychord(mesh, startPos, chords, mark, PC_NOLINKCOND); return PC_NOLINKCOND; } // mark the polychord's chords MarkPolychords(mesh, startPos, chords, mark); // check if the polychord does not intersect itself // if it self-intersects, visit the polychord for marking it and return if (IsPolychordSelfIntersecting(mesh, startPos, chords, mark)) { VisitPolychord(mesh, startPos, chords, mark, PC_SELFINTERSECT); return PC_SELFINTERSECT; } // check if manifoldness remains // if it will loose manifoldness, visit the sequence for marking it and return if (!WillPolychordBeManifold(mesh, startPos, chords, mark)) { VisitPolychord(mesh, startPos, chords, mark, PC_NOMOREMANIF); return PC_NOMOREMANIF; } // at this point the polychord is collapsable, visit it for marking VisitPolychord(mesh, startPos, chords, mark, PC_SUCCESS); // now collapse CoordType point; // int valenceA = 0, valenceB = 0; vcg::face::Pos runPos = startPos; vcg::face::JumpingPos tmpPos; // bool onSideA = false, onSideB = false; vcg::face::Pos sideA, sideB; typedef std::queue FacesVertex; typedef std::pair FacesVertexPair; typedef std::queue FacesVertexPairQueue; FacesVertexPairQueue vQueue; typedef std::pair FFpPair; typedef std::pair FFiPair; typedef std::pair FFPair; typedef std::queue FFQueue; FFQueue ffQueue; std::queue verticesToDeleteQueue; std::queue facesToDeleteQueue; runPos = startPos; do { // compute new vertex point = (runPos.V()->P() + runPos.VFlip()->P()) / 2.f; if (checkSing) { if (resultCode == PC_SINGSIDEA) point = runPos.V()->P(); else if (resultCode == PC_SINGSIDEB) point = runPos.VFlip()->P(); } runPos.V()->P() = point; // list the vertex pointer of the faces on the other side to be updated vQueue.push(FacesVertexPair()); vQueue.back().first = runPos.V(); tmpPos.Set(runPos.F(), runPos.E(), runPos.V()); tmpPos.FlipV(); tmpPos.NextFE(); // go to next face while (tmpPos.F() != runPos.F()) { if (tmpPos.F() != runPos.FFlip()) vQueue.back().second.push(&tmpPos.F()->V(tmpPos.VInd())); tmpPos.NextFE(); // go to next face } // enqueue to delete the other vertex verticesToDeleteQueue.push(runPos.VFlip()); // list the adjacencies sideA = runPos; sideA.FlipE(); sideA.FlipF(); sideB = runPos; sideB.FlipV(); sideB.FlipE(); sideB.FlipF(); // first side if (!sideA.IsBorder()) { ffQueue.push(FFPair(FFpPair(),FFiPair())); ffQueue.back().first.first = &sideA.F()->FFp(sideA.E()); ffQueue.back().second.first = &sideA.F()->FFi(sideA.E()); if (!sideB.IsBorder()) { ffQueue.back().first.second = sideB.F(); ffQueue.back().second.second = sideB.E(); } else { ffQueue.back().first.second = sideA.F(); ffQueue.back().second.second = sideA.E(); } } // second side if (!sideB.IsBorder()) { ffQueue.push(FFPair(FFpPair(),FFiPair())); ffQueue.back().first.first = &sideB.F()->FFp(sideB.E()); ffQueue.back().second.first = &sideB.F()->FFi(sideB.E()); if (!sideA.IsBorder()) { ffQueue.back().first.second = sideA.F(); ffQueue.back().second.second = sideA.E(); } else { ffQueue.back().first.second = sideB.F(); ffQueue.back().second.second = sideB.E(); } } // enqueue to delete the face facesToDeleteQueue.push(runPos.F()); // go on next edge/face runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (runPos != startPos && !runPos.IsBorder()); assert(runPos == startPos || vcg::face::IsBorder(*startPos.F(),startPos.E())); if (runPos.IsBorder()) { // compute new vertex on the last (border) edge point = (runPos.V()->P() + runPos.VFlip()->P()) / 2.f; if (checkSing) { if (resultCode == PC_SINGSIDEA) point = runPos.V()->P(); else if (resultCode == PC_SINGSIDEB) point = runPos.VFlip()->P(); } runPos.V()->P() = point; // list the vertex pointer of the faces on the other side to be updated vQueue.push(FacesVertexPair()); vQueue.back().first = runPos.V(); tmpPos.Set(runPos.F(), runPos.E(), runPos.V()); tmpPos.FlipV(); tmpPos.NextFE(); // go to next face while (tmpPos.F() != runPos.F()) { vQueue.back().second.push(&tmpPos.F()->V(tmpPos.VInd())); tmpPos.NextFE(); } // enqueue to delete the other vertex verticesToDeleteQueue.push(runPos.VFlip()); } // update vertices while (!vQueue.empty()) { while (!vQueue.front().second.empty()) { *vQueue.front().second.front() = vQueue.front().first; vQueue.front().second.pop(); } vQueue.pop(); } // update adjacencies while (!ffQueue.empty()) { *ffQueue.front().first.first = ffQueue.front().first.second; *ffQueue.front().second.first = ffQueue.front().second.second; ffQueue.pop(); } // delete faces while (!facesToDeleteQueue.empty()) { vcg::tri::Allocator::DeleteFace(mesh, *facesToDeleteQueue.front()); facesToDeleteQueue.pop(); } // delete vertices while (!verticesToDeleteQueue.empty()) { vcg::tri::Allocator::DeleteVertex(mesh, *verticesToDeleteQueue.front()); verticesToDeleteQueue.pop(); } return PC_SUCCESS; } /** * @brief CollapseAllPolychords finds and collapses all the polychords. * @param mesh The input polygonal mesh (it SHOULD have the vcg::face::PolyInfo component). * @param checkSing true if singularities on both sides of a polychord are not allowed. */ static void CollapseAllPolychords (PolyMeshType &mesh, const bool checkSing = true) { vcg::tri::RequireFFAdjacency(mesh); if (mesh.IsEmpty()) return; vcg::face::Pos pos; PC_ResultCode resultCode; std::pair face_edge; // construct the link conditions checker LinkConditions linkConditions(mesh.vert.size()); // construct the vector of chords PC_Chords chords(mesh); unsigned long mark = 0; // iterate over all the chords while (!chords.End()) { // get the current coord chords.GetCurrent(face_edge); resultCode = chords[face_edge].q; assert(resultCode == PC_VOID); // construct a pos on the face and edge of the current coord pos.Set(&mesh.face[face_edge.first], face_edge.second, mesh.face[face_edge.first].V(face_edge.second)); // (try to) collapse the polychord resultCode = CollapsePolychord(mesh, pos, mark, chords, linkConditions, checkSing); // go to the next coord chords.Next(); // increment the mark ++mark; if (mark == std::numeric_limits::max()) { chords.ResetMarks(); mark = 0; } } } /** * @brief FindPolychords lists all the valid polychords starting position of a mesh. * @param mesh The input mesh. * @param polychords The container of results. * @param loopsOnly true if closed polychords only must be listed, false for all polychords. */ static void FindPolychords (PolyMeshType &mesh, std::deque< vcg::face::Pos > &polychords, const bool loopsOnly = false) { vcg::tri::RequireFFAdjacency(mesh); polychords.clear(); if (mesh.IsEmpty()) return; vcg::face::Pos pos, startPos; PC_ResultCode resultCode; std::pair face_edge; // construct the vector of chords PC_Chords chords(mesh); unsigned long mark = 0; // iterate over all the chords while (!chords.End()) { // get the current coord chords.GetCurrent(face_edge); // construct a pos on the face and edge of the current coord pos.Set(&mesh.face[face_edge.first], face_edge.second, mesh.face[face_edge.first].V(face_edge.second)); // check and find start pos resultCode = CheckPolychordFindStartPosition(pos, startPos, false); // visit the polychord if (resultCode == PC_SUCCESS || resultCode == PC_SINGBOTH || resultCode == PC_SINGSIDEA || resultCode == PC_SINGSIDEB) { VisitPolychord(mesh, startPos, chords, mark, PC_OTHER); // store a new polychord if (!loopsOnly) polychords.push_back(startPos); else if (!startPos.IsBorder()) polychords.push_back(startPos); } else { if (resultCode == PC_NOTMANIF) { pos = startPos; VisitPolychord(mesh, pos, chords, mark, resultCode); if (pos.IsManifold() && !pos.IsBorder()) { pos.FlipF(); VisitPolychord(mesh, pos, chords, mark, resultCode); } } else if (resultCode == PC_NOTQUAD) { // if not quad, visit all the polychords passing through this coord pos = startPos; do { if (!pos.IsBorder()) { pos.FlipF(); VisitPolychord(mesh, pos, chords, mark, resultCode); pos.FlipF(); } pos.FlipV(); pos.FlipE(); } while (pos != startPos); VisitPolychord(mesh, startPos, chords, mark, resultCode); } VisitPolychord(mesh, startPos, chords, mark, resultCode); } // go to the next coord chords.Next(); // increment the mark ++mark; if (mark == std::numeric_limits::max()) { chords.ResetMarks(); mark = 0; } } } /** * @brief SplitPolychord splits a polychord into n polychords by inserting all the needed faces. * @param mesh is the input polygonal mesh. * @param pos is a position into the polychord (not necessarily the starting border). It will be updated with changes. * @param n is the number of polychords to replace the input one. * @param facesToUpdate is a vector of face pointers to be updated after re-allocation. * @param verticesToUpdate is a vector of vertex pointers to be updated after re-allocation. */ static void SplitPolychord (PolyMeshType &mesh, vcg::face::Pos &pos, const size_t n, std::vector &facesToUpdate, std::vector &verticesToUpdate) { vcg::tri::RequireFFAdjacency(mesh); vcg::tri::RequirePerFaceFlags(mesh); if (mesh.IsEmpty()) return; if (pos.IsNull()) return; if (n <= 1) return; // remember which face vertex has pos, for later updating int posVInd = pos.VInd(); vcg::face::Pos startPos, runPos; PC_ResultCode result = CheckPolychordFindStartPosition(pos, startPos, false); if (result != PC_SUCCESS && result != PC_SINGBOTH && result != PC_SINGSIDEA && result != PC_SINGSIDEB) return; // since every face has an orientation, ensure that the new polychords are inserted on the right of the starting pos startPos.FlipE(); int e = startPos.E(); startPos.FlipE(); if (startPos.F()->Next(startPos.E()) == e) startPos.FlipV(); // clear flags vcg::tri::UpdateFlags::FaceClearV(mesh); vcg::tri::UpdateFlags::FaceClearS(mesh); // count how many faces there are size_t fn1 = 0, fn2 = 0; runPos = startPos; do { // increase the number of faces if (runPos.F()->IsV()) { ++fn2; --fn1; runPos.F()->SetS(); } else { ++fn1; runPos.F()->SetV(); } // go onto the next face runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (!runPos.IsBorder() && runPos != startPos); // clear flags runPos = startPos; do { // clear visited runPos.F()->ClearV(); // go onto the next face runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (!runPos.IsBorder() && runPos != startPos); // compute the number of faces and vertices that must be added to the mesh in order to insert the new polychords size_t FN = fn1 * (n - 1) + fn2 * (n * n - 1); size_t VN = fn1 * (n - 1) + fn2 * (n + 1) * (n - 1); if (startPos.IsBorder()) VN += n - 1; // add the pos to update face and vertex pointer to the list of things to update after re-allocation facesToUpdate.push_back(&pos.F()); verticesToUpdate.push_back(&pos.V()); // add the starting position's face and vertex pointers to the list of things to update after re-allocation facesToUpdate.push_back(&startPos.F()); verticesToUpdate.push_back(&startPos.V()); runPos.SetNull(); // add faces to the mesh FaceIterator firstAddedFaceIt = vcg::tri::Allocator::AddFaces(mesh, FN, facesToUpdate); // add vertices to the mesh VertexIterator firstAddedVertexIt = vcg::tri::Allocator::AddVertices(mesh, VN, verticesToUpdate); // delete the added starting position's face and vertex pointers facesToUpdate.pop_back(); verticesToUpdate.pop_back(); // delete the added pos to update face and vertex pointers facesToUpdate.pop_back(); verticesToUpdate.pop_back(); // allocate and initialize 4 vertices and ffAdj for each new face for (FaceIterator fIt = firstAddedFaceIt; fIt != mesh.face.end(); ++fIt) { fIt->Alloc(4); for (size_t j = 0; j < 4; ++j) { fIt->FFp(j) = &*fIt; fIt->FFi(j) = j; } } // two structures to store temporary face data and splitting information struct FaceData { FacePointer faceP; std::vector ffpAdj; std::vector ffiAdj; std::vector fvpAdj; FaceData() : faceP(0), ffpAdj(4, 0), ffiAdj(4, 0), fvpAdj(4, 0) { } }; struct FaceSubdivision { int firstEdge; int firstVertex; std::vector< std::vector > subfaces; }; #if __cplusplus >= 201103L std::unordered_map faceSubdivisions; typename std::unordered_map::iterator faceSubdivisionsIt; typename std::unordered_map::iterator faceSubdivisionsPrevIt; typename std::unordered_map::iterator faceSubdivisionsNeighbourIt; #else std::map::iterator faceSubdivisionsIt; typename std::map::iterator faceSubdivisionsPrevIt; typename std::map::iterator faceSubdivisionsNeighbourIt; #endif // structure to store temporary data to assign at external faces (close to polychord) struct ExternalFaceData { FacePointer faceTo; FacePointer faceFrom; int edgeTo; int edgeFrom; ExternalFaceData() : faceTo(0), faceFrom(0), edgeTo(0), edgeFrom(0) { } ExternalFaceData(const FacePointer &ft, const FacePointer &ff, const int et, const int ef) : faceTo(ft), faceFrom(ff), edgeTo(et), edgeFrom(ef) { } }; std::list externalFaces; typename std::list::iterator externalFacesIt; int leftEdge, rightEdge, topEdge, bottomEdge, blVInd, brVInd, tlVInd, trVInd; int pleftEdge, prightEdge, ptopEdge, pbottomEdge, pblVInd, pbrVInd, ptlVInd, ptrVInd; CoordType fromPoint, toPoint; FacePointer faceP; // first pass: make subdivisions runPos = startPos; do { // create temporary data if (!runPos.F()->IsV()) { runPos.F()->SetV(); faceSubdivisionsIt = faceSubdivisions.insert(std::make_pair(runPos.F(), FaceSubdivision())).first; faceSubdivisionsIt->second.firstEdge = runPos.E(); faceSubdivisionsIt->second.firstVertex = runPos.VInd(); if (runPos.F()->IsS()) faceSubdivisionsIt->second.subfaces.resize(n, std::vector(n)); else faceSubdivisionsIt->second.subfaces.resize(1, std::vector(n)); // assign face pointers faceSubdivisionsIt->second.subfaces.at(0).at(0).faceP = runPos.F(); for (size_t j = 1; j < n; ++j, ++firstAddedFaceIt) faceSubdivisionsIt->second.subfaces.at(0).at(j).faceP = &*firstAddedFaceIt; for (size_t i = 1; i < faceSubdivisionsIt->second.subfaces.size(); ++i) for (size_t j = 0; j < n; ++j, ++firstAddedFaceIt) faceSubdivisionsIt->second.subfaces.at(i).at(j).faceP = &*firstAddedFaceIt; // internal face pointers adj rightEdge = runPos.F()->Next(runPos.E()); leftEdge = runPos.F()->Prev(runPos.E()); for (size_t i = 0; i < faceSubdivisionsIt->second.subfaces.size(); ++i) for (size_t j = 0; j < n - 1; ++j) { faceSubdivisionsIt->second.subfaces.at(i).at(j).ffpAdj[rightEdge] = faceSubdivisionsIt->second.subfaces.at(i).at(j+1).faceP; faceSubdivisionsIt->second.subfaces.at(i).at(j).ffiAdj[rightEdge] = leftEdge; faceSubdivisionsIt->second.subfaces.at(i).at(j+1).ffpAdj[leftEdge] = faceSubdivisionsIt->second.subfaces.at(i).at(j).faceP; faceSubdivisionsIt->second.subfaces.at(i).at(j+1).ffiAdj[leftEdge] = rightEdge; } topEdge = runPos.F()->Next(rightEdge); bottomEdge = runPos.E(); for (size_t i = 0; i < faceSubdivisionsIt->second.subfaces.size() - 1; ++i) for (size_t j = 0; j < n; ++j) { faceSubdivisionsIt->second.subfaces.at(i).at(j).ffpAdj[topEdge] = faceSubdivisionsIt->second.subfaces.at(i+1).at(j).faceP; faceSubdivisionsIt->second.subfaces.at(i).at(j).ffiAdj[topEdge] = bottomEdge; faceSubdivisionsIt->second.subfaces.at(i+1).at(j).ffpAdj[bottomEdge] = faceSubdivisionsIt->second.subfaces.at(i).at(j).faceP; faceSubdivisionsIt->second.subfaces.at(i+1).at(j).ffiAdj[bottomEdge] = topEdge; } // assign old vertex pointers blVInd = runPos.VInd(); brVInd = runPos.F()->Next(blVInd); trVInd = runPos.F()->Next(brVInd); tlVInd = runPos.F()->Next(trVInd); faceSubdivisionsIt->second.subfaces.front().front().fvpAdj.at(blVInd) = runPos.F()->V(blVInd); faceSubdivisionsIt->second.subfaces.front().back().fvpAdj.at(brVInd) = runPos.F()->V(brVInd); faceSubdivisionsIt->second.subfaces.back().back().fvpAdj.at(trVInd) = runPos.F()->V(trVInd); faceSubdivisionsIt->second.subfaces.back().front().fvpAdj.at(tlVInd) = runPos.F()->V(tlVInd); // assign new internal vertex pointers for (size_t i = 0; i < faceSubdivisionsIt->second.subfaces.size() - 1; ++i) for (size_t j = 0; j < n - 1; ++j, ++firstAddedVertexIt) { faceSubdivisionsIt->second.subfaces.at(i).at(j).fvpAdj.at(trVInd) = &*firstAddedVertexIt; faceSubdivisionsIt->second.subfaces.at(i).at(j+1).fvpAdj.at(tlVInd) = &*firstAddedVertexIt; faceSubdivisionsIt->second.subfaces.at(i+1).at(j).fvpAdj.at(brVInd) = &*firstAddedVertexIt; faceSubdivisionsIt->second.subfaces.at(i+1).at(j+1).fvpAdj.at(blVInd) = &*firstAddedVertexIt; } } runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (!runPos.IsBorder() && runPos != startPos); // update subdivision iterator if (runPos.IsBorder()) faceSubdivisionsIt = faceSubdivisions.end(); // second pass: assign edge vertices and subdivisions-to-subdivisions face-face adjacency runPos = startPos; do { // get current and previous subdivision faceSubdivisionsPrevIt = faceSubdivisionsIt; faceSubdivisionsIt = faceSubdivisions.find(runPos.F()); // get original indices bottomEdge = faceSubdivisionsIt->second.firstEdge; rightEdge = runPos.F()->Next(bottomEdge); topEdge = runPos.F()->Next(rightEdge); leftEdge = runPos.F()->Next(topEdge); blVInd = faceSubdivisionsIt->second.firstVertex; brVInd = runPos.F()->Next(blVInd); trVInd = runPos.F()->Next(brVInd); tlVInd = runPos.F()->Next(trVInd); if (faceSubdivisionsPrevIt != faceSubdivisions.end()) { pbottomEdge = faceSubdivisionsPrevIt->second.firstEdge; prightEdge = runPos.FFlip()->Next(pbottomEdge); ptopEdge = runPos.FFlip()->Next(prightEdge); pleftEdge = runPos.FFlip()->Next(ptopEdge); pblVInd = faceSubdivisionsPrevIt->second.firstVertex; pbrVInd = runPos.F()->Next(pblVInd); ptrVInd = runPos.F()->Next(pbrVInd); ptlVInd = runPos.F()->Next(ptrVInd); } // assign bottom edge vertices (and vertex adjacency with the previous subdivision) and face-to-face adjacency if (runPos.E() == bottomEdge) { // get pre-existing coords fromPoint = faceSubdivisionsIt->second.subfaces.front().front().fvpAdj.at(blVInd)->P(); toPoint = faceSubdivisionsIt->second.subfaces.front().back().fvpAdj.at(brVInd)->P(); // assign new vertices for (size_t j = 0; j < n - 1; ++j, ++firstAddedVertexIt) { firstAddedVertexIt->P() = fromPoint + (toPoint - fromPoint) * (j + 1) / n; faceSubdivisionsIt->second.subfaces.front().at(j).fvpAdj.at(brVInd) = &*firstAddedVertexIt; faceSubdivisionsIt->second.subfaces.front().at(j+1).fvpAdj.at(blVInd) = &*firstAddedVertexIt; } if (faceSubdivisionsPrevIt != faceSubdivisions.end()) { if (runPos.F()->FFi(bottomEdge) == ptopEdge) { // update face-to-vertex adjacency for (size_t j = 0; j < n - 1; ++j) { faceSubdivisionsPrevIt->second.subfaces.back().at(j).fvpAdj.at(ptrVInd) = faceSubdivisionsIt->second.subfaces.front().at(j).fvpAdj.at(brVInd); faceSubdivisionsPrevIt->second.subfaces.back().at(j+1).fvpAdj.at(ptlVInd) = faceSubdivisionsIt->second.subfaces.front().at(j+1).fvpAdj.at(blVInd); } // update face-to-face adjacency for (size_t j = 0; j < n; ++j) { faceSubdivisionsIt->second.subfaces.front().at(j).ffpAdj.at(bottomEdge) = faceSubdivisionsPrevIt->second.subfaces.back().at(j).faceP; faceSubdivisionsIt->second.subfaces.front().at(j).ffiAdj.at(bottomEdge) = ptopEdge; faceSubdivisionsPrevIt->second.subfaces.back().at(j).ffpAdj.at(ptopEdge) = faceSubdivisionsIt->second.subfaces.front().at(j).faceP; faceSubdivisionsPrevIt->second.subfaces.back().at(j).ffiAdj.at(ptopEdge) = bottomEdge; } } else if (runPos.F()->FFi(bottomEdge) == prightEdge) { // update face-to-vertex adjacency for (size_t i = 0; i < n - 1; ++i) { faceSubdivisionsPrevIt->second.subfaces.at(i).back().fvpAdj.at(ptrVInd) = faceSubdivisionsIt->second.subfaces.front().at(n-i-1).fvpAdj.at(blVInd); faceSubdivisionsPrevIt->second.subfaces.at(i+1).back().fvpAdj.at(pbrVInd) = faceSubdivisionsIt->second.subfaces.front().at(n-i-2).fvpAdj.at(brVInd); } // update face-to-face adjacency for (size_t j = 0; j < n; ++j) { faceSubdivisionsIt->second.subfaces.front().at(j).ffpAdj.at(bottomEdge) = faceSubdivisionsPrevIt->second.subfaces.at(n-j-1).back().faceP; faceSubdivisionsIt->second.subfaces.front().at(j).ffiAdj.at(bottomEdge) = prightEdge; faceSubdivisionsPrevIt->second.subfaces.at(n-j-1).back().ffpAdj.at(prightEdge) = faceSubdivisionsIt->second.subfaces.front().at(j).faceP; faceSubdivisionsPrevIt->second.subfaces.at(n-j-1).back().ffiAdj.at(prightEdge) = bottomEdge; } } else { // must be pleftEdge // update face-to-vertex adjacency for (size_t i = 0; i < n - 1; ++i) { faceSubdivisionsPrevIt->second.subfaces.at(i).front().fvpAdj.at(ptlVInd) = faceSubdivisionsIt->second.subfaces.front().at(i).fvpAdj.at(brVInd); faceSubdivisionsPrevIt->second.subfaces.at(i+1).front().fvpAdj.at(pblVInd) = faceSubdivisionsIt->second.subfaces.front().at(i+1).fvpAdj.at(blVInd); } // update face-to-face adjacency for (size_t j = 0; j < n; ++j) { faceSubdivisionsIt->second.subfaces.front().at(j).ffpAdj.at(bottomEdge) = faceSubdivisionsPrevIt->second.subfaces.at(j).front().faceP; faceSubdivisionsIt->second.subfaces.front().at(j).ffiAdj.at(bottomEdge) = pleftEdge; faceSubdivisionsPrevIt->second.subfaces.at(j).front().ffpAdj.at(pleftEdge) = faceSubdivisionsIt->second.subfaces.front().at(j).faceP; faceSubdivisionsPrevIt->second.subfaces.at(j).front().ffiAdj.at(pleftEdge) = bottomEdge; } } } else { // must be on border // update face-to-face adjacency for (size_t j = 0; j < n; ++j) { faceSubdivisionsIt->second.subfaces.front().at(j).ffpAdj.at(bottomEdge) = faceSubdivisionsIt->second.subfaces.front().at(j).faceP; faceSubdivisionsIt->second.subfaces.front().at(j).ffiAdj.at(bottomEdge) = bottomEdge; } } } else if (runPos.E() == leftEdge) { // get pre-existing coords fromPoint = faceSubdivisionsIt->second.subfaces.front().front().fvpAdj.at(blVInd)->P(); toPoint = faceSubdivisionsIt->second.subfaces.back().front().fvpAdj.at(tlVInd)->P(); // assign new vertices for (size_t i = 0; i < n - 1; ++i, ++firstAddedVertexIt) { firstAddedVertexIt->P() = fromPoint + (toPoint - fromPoint) * (i + 1) / n; faceSubdivisionsIt->second.subfaces.at(i).front().fvpAdj.at(tlVInd) = &*firstAddedVertexIt; faceSubdivisionsIt->second.subfaces.at(i+1).front().fvpAdj.at(blVInd) = &*firstAddedVertexIt; } // can't be on border if (runPos.F()->FFi(leftEdge) == ptopEdge) { // update face-to-vertex adjacency for (size_t j = 0; j < n - 1; ++j) { faceSubdivisionsPrevIt->second.subfaces.back().at(j).fvpAdj.at(ptrVInd) = faceSubdivisionsIt->second.subfaces.at(n-j-1).front().fvpAdj.at(blVInd); faceSubdivisionsPrevIt->second.subfaces.back().at(j+1).fvpAdj.at(ptlVInd) = faceSubdivisionsIt->second.subfaces.at(n-j-2).front().fvpAdj.at(tlVInd); } // update face-to-face adjacency for (size_t i = 0; i < n; ++i) { faceSubdivisionsIt->second.subfaces.at(i).front().ffpAdj.at(leftEdge) = faceSubdivisionsPrevIt->second.subfaces.back().at(n-i-1).faceP; faceSubdivisionsIt->second.subfaces.at(i).front().ffiAdj.at(leftEdge) = ptopEdge; faceSubdivisionsPrevIt->second.subfaces.back().at(n-i-1).ffpAdj.at(ptopEdge) = faceSubdivisionsIt->second.subfaces.at(i).front().faceP; faceSubdivisionsPrevIt->second.subfaces.back().at(n-i-1).ffiAdj.at(ptopEdge) = leftEdge; } } else if (runPos.F()->FFi(leftEdge) == prightEdge) { // update face-to-vertex adjacency for (size_t i = 0; i < n - 1; ++i) { faceSubdivisionsPrevIt->second.subfaces.at(i).back().fvpAdj.at(ptrVInd) = faceSubdivisionsIt->second.subfaces.at(i).front().fvpAdj.at(tlVInd); faceSubdivisionsPrevIt->second.subfaces.at(i+1).back().fvpAdj.at(pbrVInd) = faceSubdivisionsIt->second.subfaces.at(i+1).front().fvpAdj.at(blVInd); } // update face-to-face adjacency for (size_t i = 0; i < n; ++i) { faceSubdivisionsIt->second.subfaces.at(i).front().ffpAdj.at(leftEdge) = faceSubdivisionsPrevIt->second.subfaces.at(i).back().faceP; faceSubdivisionsIt->second.subfaces.at(i).front().ffiAdj.at(leftEdge) = prightEdge; faceSubdivisionsPrevIt->second.subfaces.at(i).back().ffpAdj.at(prightEdge) = faceSubdivisionsIt->second.subfaces.at(i).front().faceP; faceSubdivisionsPrevIt->second.subfaces.at(i).back().ffiAdj.at(prightEdge) = leftEdge; } } else { // must be runPos.F()->FFi(leftEdge) == pleftEdge // update face-to-vertex adjacency for (size_t i = 0; i < n - 1; ++i) { faceSubdivisionsPrevIt->second.subfaces.at(i).front().fvpAdj.at(ptlVInd) = faceSubdivisionsIt->second.subfaces.front().at(n-i-1).fvpAdj.at(blVInd); faceSubdivisionsPrevIt->second.subfaces.at(i+1).front().fvpAdj.at(pblVInd) = faceSubdivisionsIt->second.subfaces.front().at(n-i-2).fvpAdj.at(tlVInd); } // update face-to-face adjacency for (size_t i = 0; i < n; ++i) { faceSubdivisionsIt->second.subfaces.at(i).front().ffpAdj.at(leftEdge) = faceSubdivisionsPrevIt->second.subfaces.at(n-i-1).front().faceP; faceSubdivisionsIt->second.subfaces.at(i).front().ffiAdj.at(leftEdge) = pleftEdge; faceSubdivisionsPrevIt->second.subfaces.at(n-i-1).front().ffpAdj.at(pleftEdge) = faceSubdivisionsIt->second.subfaces.at(i).front().faceP; faceSubdivisionsPrevIt->second.subfaces.at(n-i-1).front().ffiAdj.at(pleftEdge) = leftEdge; } } } else { // must be runPos.E() == rightEdge // get pre-existing coords fromPoint = faceSubdivisionsIt->second.subfaces.front().back().fvpAdj.at(brVInd)->P(); toPoint = faceSubdivisionsIt->second.subfaces.back().back().fvpAdj.at(trVInd)->P(); // assign new vertices for (size_t i = 0; i < n - 1; ++i, ++firstAddedVertexIt) { firstAddedVertexIt->P() = fromPoint + (toPoint - fromPoint) * (i + 1) / n; faceSubdivisionsIt->second.subfaces.at(i).back().fvpAdj.at(trVInd) = &*firstAddedVertexIt; faceSubdivisionsIt->second.subfaces.at(i+1).back().fvpAdj.at(brVInd) = &*firstAddedVertexIt; } // can't be on border if (runPos.F()->FFi(rightEdge) == ptopEdge) { // update face-to-vertex adjacency for (size_t j = 0; j < n - 1; ++j) { faceSubdivisionsPrevIt->second.subfaces.back().at(j).fvpAdj.at(ptrVInd) = faceSubdivisionsIt->second.subfaces.at(j).back().fvpAdj.at(trVInd); faceSubdivisionsPrevIt->second.subfaces.back().at(j+1).fvpAdj.at(ptlVInd) = faceSubdivisionsIt->second.subfaces.at(j+1).back().fvpAdj.at(brVInd); } // update face-to-face adjacency for (size_t i = 0; i < n; ++i) { faceSubdivisionsIt->second.subfaces.at(i).back().ffpAdj.at(rightEdge) = faceSubdivisionsPrevIt->second.subfaces.back().at(i).faceP; faceSubdivisionsIt->second.subfaces.at(i).back().ffiAdj.at(rightEdge) = ptopEdge; faceSubdivisionsPrevIt->second.subfaces.back().at(i).ffpAdj.at(ptopEdge) = faceSubdivisionsIt->second.subfaces.at(i).back().faceP; faceSubdivisionsPrevIt->second.subfaces.back().at(i).ffiAdj.at(ptopEdge) = rightEdge; } } else if (runPos.F()->FFi(rightEdge) == prightEdge) { // update face-to-vertex adjacency for (size_t i = 0; i < n - 1; ++i) { faceSubdivisionsPrevIt->second.subfaces.at(i).back().fvpAdj.at(ptrVInd) = faceSubdivisionsIt->second.subfaces.at(n-i-1).back().fvpAdj.at(brVInd); faceSubdivisionsPrevIt->second.subfaces.at(i+1).back().fvpAdj.at(pbrVInd) = faceSubdivisionsIt->second.subfaces.at(n-i-2).back().fvpAdj.at(trVInd); } // update face-to-face adjacency for (size_t i = 0; i < n; ++i) { faceSubdivisionsIt->second.subfaces.at(i).back().ffpAdj.at(rightEdge) = faceSubdivisionsPrevIt->second.subfaces.at(n-i-1).back().faceP; faceSubdivisionsIt->second.subfaces.at(i).back().ffiAdj.at(rightEdge) = prightEdge; faceSubdivisionsPrevIt->second.subfaces.at(n-i-1).back().ffpAdj.at(prightEdge) = faceSubdivisionsIt->second.subfaces.at(i).back().faceP; faceSubdivisionsPrevIt->second.subfaces.at(n-i-1).back().ffiAdj.at(prightEdge) = rightEdge; } } else { // must be runPos.F()->FFi(rightEdge) == pleftEdge // update face-to-vertex adjacency for (size_t i = 0; i < n - 1; ++i) { faceSubdivisionsPrevIt->second.subfaces.at(i).front().fvpAdj.at(ptlVInd) = faceSubdivisionsIt->second.subfaces.at(i).back().fvpAdj.at(trVInd); faceSubdivisionsPrevIt->second.subfaces.at(i+1).front().fvpAdj.at(pblVInd) = faceSubdivisionsIt->second.subfaces.at(i+1).back().fvpAdj.at(brVInd); } // update face-to-face adjacency for (size_t i = 0; i < n; ++i) { faceSubdivisionsIt->second.subfaces.at(i).back().ffpAdj.at(rightEdge) = faceSubdivisionsPrevIt->second.subfaces.at(i).front().faceP; faceSubdivisionsIt->second.subfaces.at(i).back().ffiAdj.at(rightEdge) = pleftEdge; faceSubdivisionsPrevIt->second.subfaces.at(i).front().ffpAdj.at(pleftEdge) = faceSubdivisionsIt->second.subfaces.at(i).back().faceP; faceSubdivisionsPrevIt->second.subfaces.at(i).front().ffiAdj.at(pleftEdge) = rightEdge; } } } // update subdivision's left and right sides face-to-face adjacency // go on left edge runPos.FlipE(); runPos.FlipV(); if (runPos.IsBorder()) { if (!runPos.F()->IsS()) { // must be runPos.E() == leftEdge and faceSubdivisionsIt->second.subfaces.size() == 1 faceSubdivisionsIt->second.subfaces.front().front().ffpAdj.at(leftEdge) = faceSubdivisionsIt->second.subfaces.front().front().faceP; faceSubdivisionsIt->second.subfaces.front().front().ffiAdj.at(leftEdge) = leftEdge; } } else if (!runPos.FFlip()->IsV()) { // must be runPos.E() == leftEdge and faceSubdivisionsIt->second.subfaces.size() == 1 assert(faceSubdivisionsIt->second.subfaces.size() == 1); faceSubdivisionsIt->second.subfaces.front().front().ffpAdj.at(leftEdge) = runPos.FFlip(); faceSubdivisionsIt->second.subfaces.front().front().ffiAdj.at(leftEdge) = runPos.F()->FFi(leftEdge); externalFaces.push_back(ExternalFaceData(runPos.FFlip(), faceSubdivisionsIt->second.subfaces.front().front().faceP, runPos.F()->FFi(leftEdge), leftEdge)); } else if (!runPos.FFlip()->IsS() && !runPos.F()->IsS()) { // must be runPos.E() == leftEdge and faceSubdivisionsIt->second.subfaces.size() == 1 faceSubdivisionsNeighbourIt = faceSubdivisions.find(runPos.FFlip()); // must be faceSubdivisionsNeighbourIt != faceSubdivisions.end() and faceSubdivisionsNeighbourIt->second.subfaces.size() == 1 pbottomEdge = faceSubdivisionsNeighbourIt->second.firstEdge; prightEdge = runPos.FFlip()->Next(pbottomEdge); ptopEdge = runPos.FFlip()->Next(prightEdge); pleftEdge = runPos.FFlip()->Next(ptopEdge); if (runPos.F()->FFi(leftEdge) == pleftEdge) { faceSubdivisionsIt->second.subfaces.front().front().ffpAdj.at(leftEdge) = faceSubdivisionsNeighbourIt->second.subfaces.front().front().faceP; faceSubdivisionsIt->second.subfaces.front().front().ffiAdj.at(leftEdge) = pleftEdge; faceSubdivisionsNeighbourIt->second.subfaces.front().front().ffpAdj.at(pleftEdge) = faceSubdivisionsIt->second.subfaces.front().front().faceP; faceSubdivisionsNeighbourIt->second.subfaces.front().front().ffiAdj.at(pleftEdge) = leftEdge; } else { // must be runPos.F()->FFi(leftEdge) == prightEdge faceSubdivisionsIt->second.subfaces.front().front().ffpAdj.at(leftEdge) = faceSubdivisionsNeighbourIt->second.subfaces.front().back().faceP; faceSubdivisionsIt->second.subfaces.front().front().ffiAdj.at(leftEdge) = prightEdge; faceSubdivisionsNeighbourIt->second.subfaces.front().back().ffpAdj.at(prightEdge) = faceSubdivisionsIt->second.subfaces.front().front().faceP; faceSubdivisionsNeighbourIt->second.subfaces.front().back().ffiAdj.at(prightEdge) = leftEdge; } } // go on right edge runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); if (runPos.IsBorder()) { if (!runPos.F()->IsS()) { // must be runPos.E() == rightEdge and faceSubdivisionsIt->second.subfaces.size() == 1 faceSubdivisionsIt->second.subfaces.front().back().ffpAdj.at(rightEdge) = faceSubdivisionsIt->second.subfaces.front().back().faceP; faceSubdivisionsIt->second.subfaces.front().back().ffiAdj.at(rightEdge) = rightEdge; } } else if (!runPos.FFlip()->IsV()) { // must be runPos.E() == rightEdge and faceSubdivisionsIt->second.subfaces.size() == 1 faceSubdivisionsIt->second.subfaces.front().back().ffpAdj.at(rightEdge) = runPos.FFlip(); faceSubdivisionsIt->second.subfaces.front().back().ffiAdj.at(rightEdge) = runPos.F()->FFi(rightEdge); externalFaces.push_back(ExternalFaceData(runPos.FFlip(), faceSubdivisionsIt->second.subfaces.front().back().faceP, runPos.F()->FFi(rightEdge), rightEdge)); } else if (!runPos.FFlip()->IsS() && !runPos.F()->IsS()) { // must be runPos.E() == rightEdge and faceSubdivisionsIt->second.subfaces.size() == 1 faceSubdivisionsNeighbourIt = faceSubdivisions.find(runPos.FFlip()); // must be faceSubdivisionsNeighbourIt != faceSubdivisions.end() and faceSubdivisionsNeighbourIt->second.subfaces.size() == 1 pbottomEdge = faceSubdivisionsNeighbourIt->second.firstEdge; prightEdge = runPos.FFlip()->Next(pbottomEdge); ptopEdge = runPos.FFlip()->Next(prightEdge); pleftEdge = runPos.FFlip()->Next(ptopEdge); if (runPos.F()->FFi(rightEdge) == pleftEdge) { faceSubdivisionsIt->second.subfaces.front().back().ffpAdj.at(rightEdge) = faceSubdivisionsNeighbourIt->second.subfaces.front().front().faceP; faceSubdivisionsIt->second.subfaces.front().back().ffiAdj.at(rightEdge) = pleftEdge; faceSubdivisionsNeighbourIt->second.subfaces.front().front().ffpAdj.at(pleftEdge) = faceSubdivisionsIt->second.subfaces.front().back().faceP; faceSubdivisionsNeighbourIt->second.subfaces.front().front().ffiAdj.at(pleftEdge) = rightEdge; } else { // must be runPos.F()->FFi(rightEdge) == prightEdge faceSubdivisionsIt->second.subfaces.front().back().ffpAdj.at(rightEdge) = faceSubdivisionsNeighbourIt->second.subfaces.front().back().faceP; faceSubdivisionsIt->second.subfaces.front().back().ffiAdj.at(rightEdge) = prightEdge; faceSubdivisionsNeighbourIt->second.subfaces.front().back().ffpAdj.at(prightEdge) = faceSubdivisionsIt->second.subfaces.front().back().faceP; faceSubdivisionsNeighbourIt->second.subfaces.front().back().ffiAdj.at(prightEdge) = rightEdge; } } // go on top edge runPos.FlipE(); runPos.FlipV(); if (runPos.IsBorder()) { // assign top edge vertices and face-to-border adjacency if (runPos.E() == topEdge) { // get pre-existing coords fromPoint = faceSubdivisionsIt->second.subfaces.back().front().fvpAdj.at(tlVInd)->P(); toPoint = faceSubdivisionsIt->second.subfaces.back().back().fvpAdj.at(trVInd)->P(); // assign new vertices for (size_t j = 0; j < n - 1; ++j, ++firstAddedVertexIt) { firstAddedVertexIt->P() = fromPoint + (toPoint - fromPoint) * (j + 1) / n; faceSubdivisionsIt->second.subfaces.back().at(j).fvpAdj.at(trVInd) = &*firstAddedVertexIt; faceSubdivisionsIt->second.subfaces.back().at(j+1).fvpAdj.at(tlVInd) = &*firstAddedVertexIt; } // update face-to-face adjacency for (size_t j = 0; j < n; ++j) { faceSubdivisionsIt->second.subfaces.back().at(j).ffpAdj.at(topEdge) = faceSubdivisionsIt->second.subfaces.back().at(j).faceP; faceSubdivisionsIt->second.subfaces.back().at(j).ffiAdj.at(topEdge) = topEdge; } } else if (runPos.E() == leftEdge) { // get pre-existing coords fromPoint = faceSubdivisionsIt->second.subfaces.front().front().fvpAdj.at(blVInd)->P(); toPoint = faceSubdivisionsIt->second.subfaces.back().front().fvpAdj.at(tlVInd)->P(); // assign new vertices for (size_t i = 0; i < n - 1; ++i, ++firstAddedVertexIt) { firstAddedVertexIt->P() = fromPoint + (toPoint - fromPoint) * (i + 1) / n; faceSubdivisionsIt->second.subfaces.at(i).front().fvpAdj.at(tlVInd) = &*firstAddedVertexIt; faceSubdivisionsIt->second.subfaces.at(i+1).front().fvpAdj.at(blVInd) = &*firstAddedVertexIt; } // update face-to-face adjacency for (size_t i = 0; i < n; ++i) { faceSubdivisionsIt->second.subfaces.at(i).front().ffpAdj.at(leftEdge) = faceSubdivisionsIt->second.subfaces.at(i).front().faceP; faceSubdivisionsIt->second.subfaces.at(i).front().ffiAdj.at(leftEdge) = leftEdge; } } else { // must be runPos.E() == rightEdge // get pre-existing coords fromPoint = faceSubdivisionsIt->second.subfaces.front().back().fvpAdj.at(brVInd)->P(); toPoint = faceSubdivisionsIt->second.subfaces.back().back().fvpAdj.at(trVInd)->P(); // assign new vertices for (size_t i = 0; i < n - 1; ++i, ++firstAddedVertexIt) { firstAddedVertexIt->P() = fromPoint + (toPoint - fromPoint) * (i + 1) / n; faceSubdivisionsIt->second.subfaces.at(i).back().fvpAdj.at(trVInd) = &*firstAddedVertexIt; faceSubdivisionsIt->second.subfaces.at(i+1).back().fvpAdj.at(brVInd) = &*firstAddedVertexIt; } // update face-to-face adjacency for (size_t i = 0; i < n; ++i) { faceSubdivisionsIt->second.subfaces.at(i).back().ffpAdj.at(rightEdge) = faceSubdivisionsIt->second.subfaces.at(i).back().faceP; faceSubdivisionsIt->second.subfaces.at(i).back().ffiAdj.at(rightEdge) = rightEdge; } } } else { // go onto the next face runPos.FlipF(); } } while (!runPos.IsBorder() && runPos != startPos); // final pass: compute coords of new internal vertices, copy all data into mesh and clear flags for (faceSubdivisionsIt = faceSubdivisions.begin(); faceSubdivisionsIt != faceSubdivisions.end(); ++faceSubdivisionsIt) { // clear flags faceSubdivisionsIt->first->ClearV(); if (faceSubdivisionsIt->first->IsS()) faceSubdivisionsIt->first->ClearS(); // get vertex indices blVInd = faceSubdivisionsIt->second.firstVertex; brVInd = faceSubdivisionsIt->first->Next(blVInd); // compute coords on bottom side and internal, horizontally for (size_t i = 1; i < faceSubdivisionsIt->second.subfaces.size(); ++i) { fromPoint = faceSubdivisionsIt->second.subfaces.at(i).front().fvpAdj.at(blVInd)->P(); toPoint = faceSubdivisionsIt->second.subfaces.at(i).back().fvpAdj.at(brVInd)->P(); for (size_t j = 1; j < n; ++j) faceSubdivisionsIt->second.subfaces.at(i).at(j).fvpAdj.at(blVInd)->P() = fromPoint + (toPoint - fromPoint) * j / n; } // finally, copy data into mesh for (size_t i = 0; i < faceSubdivisionsIt->second.subfaces.size(); ++i) for (size_t j = 0; j < n; ++j) { faceP = faceSubdivisionsIt->second.subfaces.at(i).at(j).faceP; for (size_t k = 0; k < faceSubdivisionsIt->second.subfaces.at(i).at(j).ffpAdj.size(); ++k) faceP->FFp(k) = faceSubdivisionsIt->second.subfaces.at(i).at(j).ffpAdj.at(k); for (size_t k = 0; k < faceSubdivisionsIt->second.subfaces.at(i).at(j).ffiAdj.size(); ++k) faceP->FFi(k) = faceSubdivisionsIt->second.subfaces.at(i).at(j).ffiAdj.at(k); for (size_t k = 0; k < faceSubdivisionsIt->second.subfaces.at(i).at(j).fvpAdj.size(); ++k) faceP->V(k) = faceSubdivisionsIt->second.subfaces.at(i).at(j).fvpAdj.at(k); } } // very last step: update external faces adjacency for (externalFacesIt = externalFaces.begin(); externalFacesIt != externalFaces.end(); ++externalFacesIt) { externalFacesIt->faceTo->FFp(externalFacesIt->edgeTo) = externalFacesIt->faceFrom; externalFacesIt->faceTo->FFi(externalFacesIt->edgeTo) = externalFacesIt->edgeFrom; } // very very last step: update pos pos.V() = pos.F()->V(posVInd); } /** * @brief SplitPolychord splits a polychord into n polychords by inserting all the needed faces. * @param mesh is the input polygonal mesh. * @param pos is a position into the polychord (not necessarily the starting border). It will be updated with changes. * @param n is the number of polychords to replace the input one. */ static void SplitPolychord (PolyMeshType &mesh, vcg::face::Pos &pos, const size_t n) { std::vector facesToUpdate; std::vector verticesToUpdate; SplitPolychord(mesh, pos, n, facesToUpdate, verticesToUpdate); } /** * @brief CheckPolychordFindStartPosition checks if it's a collapsable polychord. * @param pos Input The starting position. * @param startPos Output the new starting position (in case of borders). * @param checkSing true if singularities on both sides are not allowed. * @return PC_SUCCESS if it's a collapsable polychord, otherwise the code for the cause (startPos is on it). */ static PC_ResultCode CheckPolychordFindStartPosition (const vcg::face::Pos &pos, vcg::face::Pos &startPos, const bool checkSing = true) { assert(!pos.IsNull()); int valence = 0; bool singSideA = false, singSideB = false; bool borderSideA = false, borderSideB = false; bool polyBorderFound = false; vcg::face::JumpingPos jmpPos; startPos = pos; do { // check if it is a quad if (startPos.F()->VN() != 4) return PC_NOTQUAD; // check manifoldness if (IsVertexAdjacentToAnyNonManifoldEdge(startPos)) return PC_NOTMANIF; startPos.FlipV(); if (IsVertexAdjacentToAnyNonManifoldEdge(startPos)) return PC_NOTMANIF; startPos.FlipV(); // check if side A is on border startPos.FlipE(); if (startPos.IsBorder()) borderSideA = true; startPos.FlipE(); // check if side B is on border startPos.FlipV(); startPos.FlipE(); if (startPos.IsBorder()) borderSideB = true; startPos.FlipE(); startPos.FlipV(); // check if singularities are not in both sides if (checkSing) { // compute the valence of the vertex on side B startPos.FlipV(); valence = startPos.NumberOfIncidentVertices(); // if the vertex is on border increment its valence by 1 (virtually connect it to a dummy vertex) jmpPos.Set(startPos.F(), startPos.E(), startPos.V()); if (jmpPos.FindBorder()) ++valence; if (valence != 4) singSideB = true; // a 2-valence internl vertex cause a polychord to touch itself, producing non-2manifoldness // in that case, a 2-valence vertex is dealt as 2 singularities in both sides if (valence == 2 && !borderSideB) singSideA = true; // compute the valence of the vertex on side A startPos.FlipV(); valence = startPos.NumberOfIncidentVertices(); // if the vertex is on border increment its valence by 1 (virtually connect it to a dummy vertex) jmpPos.Set(startPos.F(), startPos.E(), startPos.V()); if (jmpPos.FindBorder()) ++valence; if (valence != 4) singSideA = true; // a 2-valence internal vertex cause a polychord to touch itself, producing non-2manifoldness // in that case, a 2-valence vertex is dealt as 2 singularities in both sides if (valence == 2 && !borderSideA) singSideB = true; } // if the first border has been reached, go on the other direction to find the other border if (startPos != pos && startPos.IsBorder() && !polyBorderFound) { startPos = pos; startPos.FlipF(); polyBorderFound = true; } // if the other border has been reached, return if (polyBorderFound && startPos.IsBorder()) break; // go to the next edge startPos.FlipE(); startPos.FlipV(); startPos.FlipE(); // check manifoldness if (IsVertexAdjacentToAnyNonManifoldEdge(startPos)) return PC_NOTMANIF; startPos.FlipV(); if (IsVertexAdjacentToAnyNonManifoldEdge(startPos)) return PC_NOTMANIF; startPos.FlipV(); // go to the next face startPos.FlipF(); } while (startPos != pos); // polychord with singularities on both sides can not collapse if ((singSideA && singSideB) || (singSideA && borderSideB) || (singSideB && borderSideA)) return PC_SINGBOTH; // polychords that are rings and have borders on both sides can not collapse if (!polyBorderFound && borderSideA && borderSideB) return PC_SINGBOTH; // if there are singularities or borders on the side A, remember to keep coordinates on it if (singSideA || borderSideA) return PC_SINGSIDEA; // if there are singularities or borders on the side B, remember to keep coordinates on it if (singSideB || borderSideB) return PC_SINGSIDEB; return PC_SUCCESS; } /** * @brief VisitPolychord updates the information of a polychord. * @param mesh The mesh used for getting the face index. * @param startPos The starting position. * @param chords The vector of chords. * @param mark The mark. * @param q The visiting type. */ static void VisitPolychord (const PolyMeshType &mesh, const vcg::face::Pos &startPos, PC_Chords &chords, const unsigned long mark, const PC_ResultCode q) { assert(!startPos.IsNull()); vcg::face::Pos tmpPos, runPos = startPos; std::pair face_edge(std::numeric_limits::max(), 0); // follow the sequence of quads do { // check manifoldness tmpPos = runPos; do { if (runPos.F()->VN() != 4) // non-quads are not visited return; if (!tmpPos.IsManifold()) { // update current coord face_edge.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge.second = tmpPos.E()%2; chords.UpdateCoord(chords[face_edge], mark, q); face_edge.second = (tmpPos.E()+1)%2; chords.UpdateCoord(chords[face_edge], mark, q); return; } tmpPos.FlipV(); tmpPos.FlipE(); } while (tmpPos != runPos); // update current coord face_edge.first = vcg::tri::Index(mesh, runPos.F()); face_edge.second = runPos.E()%2; chords.UpdateCoord(chords[face_edge], mark, q); // if the polychord has to collapse, i.e. q == PC_SUCCESS, also visit the orthogonal coord if (q == PC_SUCCESS) { face_edge.second = (runPos.E()+1)%2; chords.UpdateCoord(chords[face_edge], mark, q); } runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (runPos != startPos && !runPos.IsBorder() && runPos.F()->VN() == 4); assert(runPos == startPos || vcg::face::IsBorder(*startPos.F(),startPos.E()) || runPos.F()->VN() != 4 || startPos.FFlip()->VN() != 4); } /** * @brief IsVertexAdjacentToAnyNonManifoldEdge checks if a vertex is adjacent to any non-manifold edge. * @param pos The starting position. * @return true if adjacent to non-manifold edges, false otherwise. */ static bool IsVertexAdjacentToAnyNonManifoldEdge (const vcg::face::Pos &pos) { assert(!pos.IsNull()); vcg::face::JumpingPos jmpPos; jmpPos.Set(pos.F(), pos.E(), pos.V()); do { assert(!jmpPos.FFlip()->IsD()); if (!jmpPos.IsManifold()) return true; jmpPos.NextFE(); } while (jmpPos != pos); return false; } /** * @brief IsPolychordSelfIntersecting checks if the input polychord intersects itself. * @warning Don't call this function without being sure that it's a polychord * (i.e. call CheckPolychordFindStartPoint() before calling IsPolychordSelfIntersecting(). * @param mesh The mesh used for getting the face index. * @param startPos The starting position. * @param chords The vector of chords. * @param mark The current mark, used to identify quads already visited. * @return true if it intersects itself, false otherwise. */ static bool IsPolychordSelfIntersecting (const PolyMeshType &mesh, const vcg::face::Pos &startPos, const PC_Chords &chords, const unsigned long mark) { assert(!startPos.IsNull()); vcg::face::Pos runPos = startPos; vcg::face::Pos tmpPos; std::pair face_edge(std::numeric_limits::max(), 0); do { assert(runPos.F()->VN() == 4); // check if we've already crossed this face face_edge.first = vcg::tri::Index(mesh, runPos.F()); face_edge.second = (runPos.E()+1)%2; if (chords[face_edge].mark == mark) return true; // if this coord is adjacent to another coord of the same polychord // i.e., this polychord touches itself without intersecting // it might cause a wrong collapse, producing holes and non-2manifoldness tmpPos = runPos; tmpPos.FlipE(); if (!tmpPos.IsBorder()) { tmpPos.FlipF(); face_edge.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge.second = (tmpPos.E()+1)%2; if (chords[face_edge].mark == mark) return true; // this should never hapen: face_edge.second = tmpPos.E()%2; if (chords[face_edge].mark == mark) return true; } tmpPos = runPos; tmpPos.FlipV(); tmpPos.FlipE(); if (!tmpPos.IsBorder()) { tmpPos.FlipF(); face_edge.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge.second = (tmpPos.E()+1)%2; if (chords[face_edge].mark == mark) return true; // this should never hapen: face_edge.second = tmpPos.E()%2; if (chords[face_edge].mark == mark) return true; } runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (runPos != startPos && !runPos.IsBorder()); return false; } /** * @brief WillPolychordBeManifold checks whether a polychord starting at startPos would cause non-manifoldness * if it was collapsed. * @note VisitPolychord() should be called before this method. * @param mesh The input mesh. * @param startPos The starting Pos. * @param chords The vector of chords. * @param mark The current mark, used to identify quads already visited. * @return true if manifoldness remains, false otherwise. */ static bool WillPolychordBeManifold(const PolyMeshType &mesh, const vcg::face::Pos &startPos, PC_Chords &chords, const unsigned long mark) { assert(!startPos.IsNull()); vcg::face::Pos runPos = startPos; vcg::face::JumpingPos tmpPos; std::pair face_edge1(std::numeric_limits::max(), 0); std::pair face_edge2(std::numeric_limits::max(), 0); bool in = true; unsigned int nTraversal = 0; // second step: check runPos = startPos; do { face_edge1.first = vcg::tri::Index(mesh, runPos.F()); face_edge1.second = runPos.E() % 2; assert(chords[face_edge1].mark == mark); // check one vertex runPos.FlipV(); in = true; nTraversal = 0; tmpPos.Set(runPos.F(), runPos.E(), runPos.V()); do { if (tmpPos.IsBorder() && in) { in = false; nTraversal++; } // go to next edge tmpPos.NextFE(); // check if this face is already visited face_edge1.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge1.second = tmpPos.E() % 2; face_edge2.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge2.second = (tmpPos.E() + 1) % 2; if (in && chords[face_edge1].mark != mark && chords[face_edge2].mark != mark) { in = false; ++nTraversal; } else if (!in && (chords[face_edge1].mark == mark || chords[face_edge2].mark == mark)) { in = true; ++nTraversal; } } while (tmpPos != runPos); assert(in); assert(nTraversal % 2 == 0); if (nTraversal > 2) return false; // check other vertex runPos.FlipV(); in = true; nTraversal = 0; tmpPos.Set(runPos.F(), runPos.E(), runPos.V()); do { if (tmpPos.IsBorder() && in) { in = false; ++nTraversal; } // go to next edge tmpPos.NextFE(); // check if this face is already visited face_edge1.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge1.second = tmpPos.E() % 2; face_edge2.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge2.second = (tmpPos.E() + 1) % 2; if (in && chords[face_edge1].mark != mark && chords[face_edge2].mark != mark) { in = false; nTraversal++; } else if (!in && (chords[face_edge1].mark == mark || chords[face_edge2].mark == mark)) { in = true; ++nTraversal; } } while (tmpPos != runPos); assert(in); assert(nTraversal % 2 == 0); if (nTraversal > 2) return false; runPos.FlipE(); runPos.FlipV(); runPos.FlipE(); runPos.FlipF(); } while (runPos != startPos && !runPos.IsBorder()); if (runPos.IsBorder()) { // check one vertex runPos.FlipV(); in = true; nTraversal = 0; tmpPos.Set(runPos.F(), runPos.E(), runPos.V()); do { if (tmpPos.IsBorder() && in) { in = false; ++nTraversal; } // go to next edge tmpPos.NextFE(); // check if this face is already visited face_edge1.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge1.second = tmpPos.E() % 2; face_edge2.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge2.second = (tmpPos.E() + 1) % 2; if (in && chords[face_edge1].mark != mark && chords[face_edge2].mark != mark) { in = false; ++nTraversal; } else if (!in && (chords[face_edge1].mark == mark || chords[face_edge2].mark == mark)) { in = true; ++nTraversal; } } while (tmpPos != runPos); assert(in); assert(nTraversal % 2 == 0); if (nTraversal > 2) return false; // check other vertex runPos.FlipV(); in = true; nTraversal = 0; tmpPos.Set(runPos.F(), runPos.E(), runPos.V()); do { if (tmpPos.IsBorder() && in) { in = false; ++nTraversal; } // go to next edge tmpPos.NextFE(); // check if this face is already visited face_edge1.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge1.second = tmpPos.E() % 2; face_edge2.first = vcg::tri::Index(mesh, tmpPos.F()); face_edge2.second = (tmpPos.E() + 1) % 2; if (in && chords[face_edge1].mark != mark && chords[face_edge2].mark != mark) { in = false; ++nTraversal; } else if (!in && (chords[face_edge1].mark == mark || chords[face_edge2].mark == mark)) { in = true; ++nTraversal; } } while (tmpPos != runPos); assert(in); assert(nTraversal % 2 == 0); if (nTraversal > 2) return false; } return true; } }; } } #endif // POLYGON_Polychord_COLLAPSE_H