You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1985 lines
87 KiB
1985 lines
87 KiB
/**************************************************************************** |
|
* 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 <list> |
|
#include <unordered_map> |
|
#include <vcg/complex/complex.h> |
|
#include <vcg/simplex/face/jumping_pos.h> |
|
|
|
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<unsigned long>::max()), q(PC_VOID), prev(NULL), next(NULL) { } |
|
inline void Reset() { |
|
mark = std::numeric_limits<unsigned long>::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<unsigned long>::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<size_t, unsigned char>(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<size_t, unsigned char> &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<size_t, unsigned char>(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<size_t, unsigned char> &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 <size_t, unsigned char>(face_index, offset) with offset being 0 or 1. |
|
*/ |
|
inline std::pair<size_t, unsigned char> operator[] (PC_Chord const * const coord) { |
|
assert(coord >= &_Chords[0] && coord < &_Chords[0]+_Chords.size()); |
|
return std::pair<size_t, unsigned char>((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<size_t, unsigned char> &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<size_t>::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<PC_Chord> _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<LCVertexIndex> LCVertexStar; ///< define the star of a vertex |
|
typedef long int LCEdgeIndex; |
|
typedef std::set<LCEdgeIndex> 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<FaceType> &startPos) { |
|
assert(!startPos.IsNull()); |
|
assert(mesh.vert.size() == _lcVertices.size()); |
|
std::vector<LCEdge> 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<FaceType> &startPos, std::vector<LCEdge> &lcEdges) { |
|
assert(!startPos.IsNull()); |
|
assert(mesh.vert.size() == _lcVertices.size()); |
|
vcg::face::Pos<FaceType> runPos = startPos; |
|
vcg::face::JumpingPos<FaceType> vStarPos; |
|
vcg::face::Pos<FaceType> 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<LCEdge> &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<LCVertex> _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<FaceType> &startPos, |
|
PC_Chords &chords, |
|
const unsigned long mark) { |
|
vcg::face::Pos<FaceType> runPos = startPos; |
|
std::pair<size_t, unsigned char> face_edge(std::numeric_limits<size_t>::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<PolyMeshType>::DeleteFace() and |
|
* vcg::tri::Allocator<PolyMeshType>::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<PolyMeshType>::CompactFaceVector() |
|
* and vcg::tri::Allocator<PolyMeshType>::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<FaceType> &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<FaceType> 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<FaceType> runPos = startPos; |
|
vcg::face::JumpingPos<FaceType> tmpPos; |
|
// bool onSideA = false, onSideB = false; |
|
vcg::face::Pos<FaceType> sideA, sideB; |
|
typedef std::queue<VertexPointer *> FacesVertex; |
|
typedef std::pair<VertexPointer, FacesVertex> FacesVertexPair; |
|
typedef std::queue<FacesVertexPair> FacesVertexPairQueue; |
|
FacesVertexPairQueue vQueue; |
|
typedef std::pair<FacePointer *, FacePointer> FFpPair; |
|
typedef std::pair<char *, char> FFiPair; |
|
typedef std::pair<FFpPair, FFiPair> FFPair; |
|
typedef std::queue<FFPair> FFQueue; |
|
FFQueue ffQueue; |
|
std::queue<VertexPointer> verticesToDeleteQueue; |
|
std::queue<FacePointer> 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<PolyMeshType>::DeleteFace(mesh, *facesToDeleteQueue.front()); |
|
facesToDeleteQueue.pop(); |
|
} |
|
|
|
// delete vertices |
|
while (!verticesToDeleteQueue.empty()) { |
|
vcg::tri::Allocator<PolyMeshType>::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<FaceType> pos; |
|
PC_ResultCode resultCode; |
|
std::pair<size_t, unsigned char> 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<unsigned long>::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<FaceType> > &polychords, const bool loopsOnly = false) { |
|
vcg::tri::RequireFFAdjacency(mesh); |
|
|
|
polychords.clear(); |
|
|
|
if (mesh.IsEmpty()) |
|
return; |
|
|
|
vcg::face::Pos<FaceType> pos, startPos; |
|
PC_ResultCode resultCode; |
|
std::pair<size_t, unsigned char> 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<unsigned long>::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<FaceType> &pos, const size_t n, |
|
std::vector<FacePointer *> &facesToUpdate, std::vector<VertexPointer *> &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<FaceType> 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<PolyMeshType>::FaceClearV(mesh); |
|
vcg::tri::UpdateFlags<PolyMeshType>::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<PolyMeshType>::AddFaces(mesh, FN, facesToUpdate); |
|
// add vertices to the mesh |
|
VertexIterator firstAddedVertexIt = vcg::tri::Allocator<PolyMeshType>::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<FacePointer> ffpAdj; |
|
std::vector<int> ffiAdj; |
|
std::vector<VertexPointer> fvpAdj; |
|
FaceData() : faceP(0), ffpAdj(4, 0), ffiAdj(4, 0), fvpAdj(4, 0) { } |
|
}; |
|
struct FaceSubdivision { |
|
int firstEdge; |
|
int firstVertex; |
|
std::vector< std::vector<FaceData> > subfaces; |
|
}; |
|
#if __cplusplus >= 201103L |
|
std::unordered_map<FacePointer,FaceSubdivision> faceSubdivisions; |
|
typename std::unordered_map<FacePointer,FaceSubdivision>::iterator faceSubdivisionsIt; |
|
typename std::unordered_map<FacePointer,FaceSubdivision>::iterator faceSubdivisionsPrevIt; |
|
typename std::unordered_map<FacePointer,FaceSubdivision>::iterator faceSubdivisionsNeighbourIt; |
|
#else |
|
std::map<FacePointer,FaceSubdivision faceSubdivisions; |
|
typename std::map<FacePointer,FaceSubdivision>::iterator faceSubdivisionsIt; |
|
typename std::map<FacePointer,FaceSubdivision>::iterator faceSubdivisionsPrevIt; |
|
typename std::map<FacePointer,FaceSubdivision>::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<ExternalFaceData> externalFaces; |
|
typename std::list<ExternalFaceData>::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<FaceData>(n)); |
|
else |
|
faceSubdivisionsIt->second.subfaces.resize(1, std::vector<FaceData>(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<FaceType> &pos, const size_t n) { |
|
std::vector<FacePointer *> facesToUpdate; |
|
std::vector<VertexPointer *> 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<FaceType> &pos, |
|
vcg::face::Pos<FaceType> &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<FaceType> 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<FaceType> &startPos, |
|
PC_Chords &chords, |
|
const unsigned long mark, |
|
const PC_ResultCode q) { |
|
assert(!startPos.IsNull()); |
|
vcg::face::Pos<FaceType> tmpPos, runPos = startPos; |
|
std::pair<size_t, unsigned char> face_edge(std::numeric_limits<size_t>::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<FaceType> &pos) { |
|
assert(!pos.IsNull()); |
|
vcg::face::JumpingPos<FaceType> 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<FaceType> &startPos, |
|
const PC_Chords &chords, |
|
const unsigned long mark) { |
|
assert(!startPos.IsNull()); |
|
vcg::face::Pos<FaceType> runPos = startPos; |
|
vcg::face::Pos<FaceType> tmpPos; |
|
std::pair<size_t, unsigned char> face_edge(std::numeric_limits<size_t>::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<FaceType> &startPos, |
|
PC_Chords &chords, |
|
const unsigned long mark) { |
|
assert(!startPos.IsNull()); |
|
vcg::face::Pos<FaceType> runPos = startPos; |
|
vcg::face::JumpingPos<FaceType> tmpPos; |
|
std::pair<size_t, unsigned char> face_edge1(std::numeric_limits<size_t>::max(), 0); |
|
std::pair<size_t, unsigned char> face_edge2(std::numeric_limits<size_t>::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
|
|
|