Fixed a weird bug that caused wrong check on self-intersections and polychord's adjacency.

This commit is contained in:
giorgiomarcias 2014-12-18 17:07:08 +00:00
parent ee3177b1d9
commit b179459e62
1 changed files with 272 additions and 36 deletions

View File

@ -68,14 +68,15 @@ public:
* @brief The PC_ResultCode enum codifies the result type of a polychord collapse operation.
*/
enum PC_ResultCode {
PC_SUCCESS = 0,
PC_NOTMANIF = 1,
PC_NOTQUAD = 2,
PC_NOLINKCOND = 4,
PC_SINGBOTH = 8,
PC_SELFINTERSECT = 16,
PC_VOID = 32,
PC_OTHER = 64
PC_SUCCESS = 0x00,
PC_NOTMANIF = 0x01,
PC_NOTQUAD = 0x02,
PC_NOLINKCOND = 0x04,
PC_SINGBOTH = 0x08,
PC_SELFINTERSECT = 0x10,
PC_NOMOREMANIF = 0x20,
PC_VOID = 0x30,
PC_OTHER = 0x40
};
/**
@ -112,9 +113,8 @@ public:
* @brief ResetMarks
*/
void ResetMarks() {
typename std::vector<PC_Chord>::iterator it = _Chords.begin();
for (; it != _Chords.end(); it++)
(*it).mark = std::numeric_limits<unsigned long>::max();
for (size_t i = 0; i < _Chords.size(); ++i)
_Chords.at(i).mark = std::numeric_limits<unsigned long>::max();
}
/**
@ -124,13 +124,13 @@ public:
*/
void Reset(const PolyMeshType &mesh) {
_Chords.resize(2*mesh.face.size());
for (size_t j = 0; j < _Chords.size(); j++)
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++) {
for (size_t i = 0; i < _Chords.size(); ++i) {
// set the prev
chord = NULL;
if ((long long)i-1 >= 0) {
@ -138,7 +138,7 @@ public:
if (vcg::tri::HasPerFaceFlags(mesh)) {
j = i-1;
while (j >= 0 && mesh.face[j/2].IsD())
j--;
--j;
if (j >= 0)
chord = &_Chords[j];
else
@ -154,7 +154,7 @@ public:
if (vcg::tri::HasPerFaceFlags(mesh)) {
j = i+1;
while (j < (long long)_Chords.size() && mesh.face[j/2].IsD())
j++;
++j;
if (j < (long long)_Chords.size())
chord = &_Chords[j];
else
@ -213,8 +213,8 @@ public:
coord.prev->next = coord.next;
if (coord.next != NULL && &coord != _currentChord)
coord.next->prev = coord.prev;
coord.mark = mark;
}
coord.mark = mark;
coord.q = resultCode;
}
@ -372,7 +372,7 @@ public:
* @brief LC_ResetStars resets the stars on a polychord.
*/
void LC_ResetStars() {
for (size_t v = 0; v < _lcVertices.size(); v++)
for (size_t v = 0; v < _lcVertices.size(); ++v)
_lcVertices[v].reset();
}
@ -394,7 +394,7 @@ public:
// count how many edges
do {
nEdges++;
++nEdges;
// go on the next edge
runPos.FlipE();
runPos.FlipV();
@ -402,11 +402,11 @@ public:
runPos.FlipF();
} while (runPos != startPos && !runPos.IsBorder());
if (runPos.IsBorder())
nEdges++;
++nEdges;
// resize the vector of edges
lcEdges.resize(nEdges);
for (size_t e = 0; e < nEdges; e++)
for (size_t e = 0; e < nEdges; ++e)
lcEdges[e].reset();
/// compute the star of all the vertices and edges seen from the polychord
@ -524,7 +524,7 @@ public:
_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++) {
for (typename LCVertexStar::iterator vIt = _lcVertices[v2].star.begin(); vIt != _lcVertices[v2].star.end(); ++vIt) {
v = *vIt;
if (v == v2) // skip v2 itself
continue;
@ -534,9 +534,9 @@ public:
/// 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++)
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++) {
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);
}
@ -551,6 +551,58 @@ public:
// 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.
*
@ -595,6 +647,8 @@ public:
if (pos.IsNull())
return PC_VOID;
// vcg::tri::io::Exporter<PolyMeshType>::Save(mesh, "current_step.obj");
vcg::face::Pos<FaceType> tempPos, startPos;
// check if the sequence of facets is a polychord and find the starting coord
@ -623,24 +677,32 @@ public:
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
bool lc = linkConditions.CheckLinkConditions(mesh, startPos);
// if not satisfied, visit the sequence for marking it and return
if (!lc) {
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
bool si = IsPolychordSelfIntersecting(mesh, startPos, chords, mark);
// if it self-intersects, visit the polychord for marking it and return
if (si) {
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);
@ -669,12 +731,12 @@ public:
valenceB = runPos.NumberOfIncidentVertices();
tmpPos.Set(runPos.F(), runPos.E(), runPos.V());
if (tmpPos.FindBorder())
valenceB++;
++valenceB;
runPos.FlipV();
valenceA = runPos.NumberOfIncidentVertices();
tmpPos.Set(runPos.F(), runPos.E(), runPos.V());
if (tmpPos.FindBorder())
valenceA++;
++valenceA;
if (valenceA != 4)
onSideA = true;
if (valenceB != 4)
@ -816,6 +878,12 @@ public:
verticesToDeleteQueue.pop();
}
// if (!CheckConsistent(mesh)) {
// int mask = vcg::tri::io::Mask::IOM_FACECOLOR;
// vcg::tri::io::Exporter<PolyMeshType>::Save(mesh, "current_step_failed.obj", mask);
// assert(false);
// }
return PC_SUCCESS;
}
@ -843,6 +911,8 @@ public:
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
@ -851,7 +921,7 @@ public:
chords.Next();
// increment the mark
mark++;
++mark;
if (mark == std::numeric_limits<unsigned long>::max()) {
chords.ResetMarks();
mark = 0;
@ -1013,7 +1083,7 @@ public:
// 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++) {
for (size_t j = 0; j < 4; ++j) {
fIt->FFp(j) = &*fIt;
fIt->FFi(j) = j;
}
@ -1566,7 +1636,7 @@ public:
// 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++;
++valence;
if (valence != 4)
singSideB = true;
// a 2-valence internl vertex cause a polychord to touch itself, producing non-2manifoldness
@ -1579,7 +1649,7 @@ public:
// 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++;
++valence;
if (valence != 4)
singSideA = true;
// a 2-valence internal vertex cause a polychord to touch itself, producing non-2manifoldness
@ -1644,14 +1714,13 @@ public:
vcg::face::Pos<FaceType> tmpPos, runPos = startPos;
std::pair<size_t, unsigned char> face_edge(std::numeric_limits<size_t>::max(), 0);
if (runPos.F()->VN() != 4) // non-quads are not visited
return;
// 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());
@ -1694,6 +1763,7 @@ public:
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();
@ -1737,6 +1807,10 @@ public:
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();
@ -1747,6 +1821,10 @@ public:
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();
@ -1756,6 +1834,164 @@ public:
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;
}
};
}