Commit 0ab6420c authored by Philip Trettner's avatar Philip Trettner
Browse files

debugging consistency failures

parent 4af91043
......@@ -2,6 +2,8 @@
#include <cassert>
#include "debug.hh"
using namespace polymesh;
void Mesh::assert_consistency() const
......@@ -60,21 +62,25 @@ void Mesh::assert_consistency() const
for (auto h : valid_vertices())
{
assert(h.is_valid());
assert(!h.is_removed());
++valid_vertex_cnt;
}
for (auto h : valid_faces())
{
assert(h.is_valid());
assert(!h.is_removed());
++valid_face_cnt;
}
for (auto h : valid_edges())
{
assert(h.is_valid());
assert(!h.is_removed());
++valid_edge_cnt;
}
for (auto h : valid_halfedges())
{
assert(h.is_valid());
assert(!h.is_removed());
++valid_halfedge_cnt;
}
......@@ -101,16 +107,210 @@ void Mesh::assert_consistency() const
assert(edge_cnt * 2 == halfedge_cnt);
}
// check prev-next heh
// check only non-removed can be accessed topologically
for (auto f : valid_faces())
{
assert(!f.any_halfedge().is_removed());
assert(!f.any_vertex().is_removed());
// check topology consistencies
for (auto v : f.vertices())
assert(!v.is_removed());
// check iterators
for (auto h : f.halfedges())
assert(!h.is_removed());
// check only non-removed can be accessed topologically
for (auto f : f.adjacent_faces())
assert(!f.is_removed());
for (auto f : f.edges())
assert(!f.is_removed());
}
for (auto v : valid_vertices())
{
assert(!v.any_face().is_removed());
assert(!v.any_edge().is_removed());
assert(!v.any_incoming_halfedge().is_removed());
assert(!v.any_outgoing_halfedge().is_removed());
for (auto v : v.adjacent_vertices())
assert(!v.is_removed());
for (auto h : v.incoming_halfedges())
assert(!h.is_removed());
for (auto h : v.outgoing_halfedges())
assert(!h.is_removed());
for (auto f : v.faces())
assert(!f.is_removed());
for (auto f : v.edges())
assert(!f.is_removed());
}
for (auto e : valid_edges())
{
assert(!e.faceA().is_removed());
assert(!e.faceB().is_removed());
assert(!e.vertexA().is_removed());
assert(!e.vertexB().is_removed());
assert(!e.halfedgeA().is_removed());
assert(!e.halfedgeB().is_removed());
}
for (auto h : valid_halfedges())
{
assert(!h.prev().is_removed());
assert(!h.next().is_removed());
assert(!h.edge().is_removed());
assert(!h.vertex_from().is_removed());
assert(!h.vertex_to().is_removed());
assert(!h.face().is_removed());
assert(!h.opposite().is_removed());
assert(!h.opposite_face().is_removed());
}
// check half-edge consistencies
for (auto h : valid_halfedges())
{
assert(h.next().is_valid());
assert(h.prev().is_valid());
assert(h.opposite().is_valid());
assert(h.vertex_to().is_valid());
assert(h.vertex_from().is_valid());
// face can be invalid
assert(h.next().prev() == h);
assert(h.prev().next() == h);
assert(h.opposite().opposite() == h);
if (!h.is_boundary())
assert(h.face().halfedges().contains(h));
assert(h.vertex_to().incoming_halfedges().contains(h));
assert(h.vertex_from().outgoing_halfedges().contains(h));
assert(h.edge().halfedgeA() == h || h.edge().halfedgeB() == h);
}
// check vertex consistencies
for (auto v : valid_vertices())
{
if (!v.is_isolated())
{
assert(v.any_incoming_halfedge().is_valid());
assert(v.any_outgoing_halfedge().is_valid());
assert(v.any_valid_face().is_valid());
assert(v.any_edge().is_valid());
assert(v.any_incoming_halfedge().vertex_to() == v);
assert(v.any_outgoing_halfedge().vertex_from() == v);
for (auto f : v.faces())
if (f.is_valid())
assert(f.vertices().contains(v));
for (auto h : v.outgoing_halfedges())
assert(h.vertex_from() == v);
for (auto h : v.incoming_halfedges())
assert(h.vertex_to() == v);
for (auto vv : v.adjacent_vertices())
assert(vv.adjacent_vertices().contains(v));
for (auto e : v.edges())
assert(e.vertexA() == v || e.vertexB() == v);
}
else
{
assert(v.any_face().is_invalid());
assert(v.any_valid_face().is_invalid());
assert(v.any_incoming_halfedge().is_invalid());
assert(v.any_outgoing_halfedge().is_invalid());
assert(v.any_edge().is_invalid());
assert(v.faces().size() == 0);
assert(v.edges().size() == 0);
assert(v.adjacent_vertices().size() == 0);
assert(v.outgoing_halfedges().size() == 0);
assert(v.incoming_halfedges().size() == 0);
}
}
// check face consistencies
for (auto f : valid_faces())
{
assert(f.any_halfedge().is_valid());
assert(f.any_vertex().is_valid());
assert(f.any_halfedge().face() == f);
assert(f.any_vertex().faces().contains(f));
for (auto h : f.halfedges())
assert(h.face() == f);
for (auto v : f.vertices())
assert(!v.is_removed());
assert(v.faces().contains(f));
for (auto ff : f.adjacent_faces())
if (ff.is_valid())
assert(ff.adjacent_faces().contains(f));
for (auto e : f.edges())
assert(e.faceA() == f || e.faceB() == f);
}
// check edge consistencies
for (auto e : valid_edges())
{
assert(e.vertexA().is_valid());
assert(e.vertexB().is_valid());
assert(e.halfedgeA().is_valid());
assert(e.halfedgeB().is_valid());
// faces can be invalid
assert(e.faceA().is_invalid() || e.faceA().edges().contains(e));
assert(e.faceB().is_invalid() || e.faceB().edges().contains(e));
assert(e.vertexA().edges().contains(e));
assert(e.vertexB().edges().contains(e));
assert(e.halfedgeA().edge() == e);
assert(e.halfedgeB().edge() == e);
}
// check boundaries
for (auto h : valid_halfedges())
if (h.is_boundary())
{
assert(h.face().is_invalid());
assert(h.edge().is_boundary());
if (h.opposite().is_boundary())
{
assert(h.edge().is_isolated());
assert(h.opposite_face().is_invalid());
}
else
{
assert(h.opposite_face().is_boundary());
}
assert(h.vertex_to().is_boundary());
assert(h.vertex_from().is_boundary());
}
// check derived counts
{
auto v_e_sum = 0;
for (auto v : valid_vertices())
{
v_e_sum += v.edges().size();
}
assert(v_e_sum == 2 * size_edges());
// TODO: more?
}
}
......@@ -179,6 +179,12 @@ private:
/// removes all adjacent edges, then the vertex
void remove_vertex(vertex_index v_idx);
/// choses a new outgoing half-edge for a given vertex, prefers boundary ones
/// assumes non-isolated vertex
void fix_boundary_state_of(vertex_index v_idx);
/// choses a new half-edge for a given face, prefers boundary ones
void fix_boundary_state_of(face_index f_idx);
// attributes
bool is_boundary(vertex_index idx) const;
bool is_boundary(halfedge_index idx) const;
......@@ -226,6 +232,7 @@ private:
bool is_valid() const { return halfedge.is_valid(); }
void set_removed() { halfedge = halfedge_index::invalid(); }
// is_boundary: check if half-edge is boundary
};
// 4 byte per vertex
......@@ -265,14 +272,46 @@ private:
std::vector<vertex_info> mVertices;
std::vector<halfedge_info> mHalfedges;
struct face_info &face(face_index i) { return mFaces[i.value]; }
struct face_info const &face(face_index i) const { return mFaces[i.value]; }
struct vertex_info &vertex(vertex_index i) { return mVertices[i.value]; }
struct vertex_info const &vertex(vertex_index i) const { return mVertices[i.value]; }
struct halfedge_info &halfedge(halfedge_index i) { return mHalfedges[i.value]; }
struct halfedge_info const &halfedge(halfedge_index i) const { return mHalfedges[i.value]; }
struct halfedge_info &halfedge(edge_index i, int h) { return mHalfedges[(i.value >> 1) + h]; }
struct halfedge_info const &halfedge(edge_index i, int h) const { return mHalfedges[(i.value >> 1) + h]; }
struct face_info &face(face_index i)
{
assert(i.is_valid());
return mFaces[i.value];
}
struct face_info const &face(face_index i) const
{
assert(i.is_valid());
return mFaces[i.value];
}
struct vertex_info &vertex(vertex_index i)
{
assert(i.is_valid());
return mVertices[i.value];
}
struct vertex_info const &vertex(vertex_index i) const
{
assert(i.is_valid());
return mVertices[i.value];
}
struct halfedge_info &halfedge(halfedge_index i)
{
assert(i.is_valid());
return mHalfedges[i.value];
}
struct halfedge_info const &halfedge(halfedge_index i) const
{
assert(i.is_valid());
return mHalfedges[i.value];
}
struct halfedge_info &halfedge(edge_index i, int h)
{
assert(i.is_valid());
return mHalfedges[(i.value >> 1) + h];
}
struct halfedge_info const &halfedge(edge_index i, int h) const
{
assert(i.is_valid());
return mHalfedges[(i.value >> 1) + h];
}
// internal state
private:
......@@ -354,7 +393,7 @@ inline face_index Mesh::add_face(const vertex_handle *v_handles, size_t vcnt)
{
mFaceInsertCache.resize(vcnt);
for (auto i = 0u; i < vcnt; ++i)
mFaceInsertCache[i] = find_halfedge(v_handles[i].idx, v_handles[(i + 1) % vcnt].idx);
mFaceInsertCache[i] = add_or_get_halfedge(v_handles[i].idx, v_handles[(i + 1) % vcnt].idx);
return add_face(mFaceInsertCache.data(), vcnt);
}
......@@ -362,7 +401,7 @@ inline face_index Mesh::add_face(const vertex_index *v_indices, size_t vcnt)
{
mFaceInsertCache.resize(vcnt);
for (auto i = 0u; i < vcnt; ++i)
mFaceInsertCache[i] = find_halfedge(v_indices[i], v_indices[(i + 1) % vcnt]);
mFaceInsertCache[i] = add_or_get_halfedge(v_indices[i], v_indices[(i + 1) % vcnt]);
return add_face(mFaceInsertCache.data(), vcnt);
}
......@@ -388,9 +427,9 @@ inline face_index Mesh::add_face(const halfedge_index *half_loop, size_t vcnt)
auto h1 = half_loop[(i + 1) % vcnt];
// half-edge must form a chain
assert(to_vertex_of(h0) == from_vertex_of(h1));
assert(to_vertex_of(h0) == from_vertex_of(h1) && "half-edges do not form a chain");
// half-edge must be free, i.e. allow a new polygon
assert(halfedge(h0).is_free());
assert(halfedge(h0).is_free() && "half-edge already contains a face");
// make them adjacent
make_adjacent(h0, h1);
......@@ -399,11 +438,29 @@ inline face_index Mesh::add_face(const halfedge_index *half_loop, size_t vcnt)
halfedge(h0).face = fidx;
}
// fix boundary states
for (auto i = 0u; i < vcnt; ++i)
{
auto h = half_loop[i];
auto v = halfedge(h).to_vertex;
auto f = halfedge(opposite(h)).face;
// fix vertex
fix_boundary_state_of(v);
// fix face
if (f.is_valid())
fix_boundary_state_of(f);
}
// set up face data
face_info f;
f.halfedge = half_loop[0];
mFaces.push_back(f);
// fix new face
fix_boundary_state_of(fidx);
// notify attributes
auto fCnt = mFaces.size();
for (auto p = mFaceAttrs; p; p = p->mNextAttribute)
......@@ -458,7 +515,7 @@ inline edge_index Mesh::add_or_get_edge(vertex_index v_from, vertex_index v_to)
// link to vertex
if (vd_to.is_isolated())
vd_to.outgoing_halfedge = h_from_to_idx;
vd_to.outgoing_halfedge = h_to_from_idx;
else
{
auto to_in_idx = find_free_incident(v_to);
......@@ -547,10 +604,16 @@ inline void Mesh::remove_face(face_index f_idx)
// set half-edge face to invalid
h.face = face_index::invalid();
// outgoing vertex half-edge
// fix outgoing vertex half-edge
// (vertex correctly reports is_boundary)
vertex(from_vertex_of(he)).outgoing_halfedge = he;
// fix opposite face half-edge
auto ohe = opposite(he);
auto of = halfedge(ohe).face;
if (of.is_valid())
face(of).halfedge = ohe;
// advance
he = h.next_halfedge;
} while (he != he_begin);
......@@ -624,6 +687,47 @@ inline void Mesh::remove_vertex(vertex_index v_idx)
v.set_removed();
}
inline void Mesh::fix_boundary_state_of(vertex_index v_idx)
{
auto &v = vertex(v_idx);
assert(!v.is_isolated());
auto he_begin = v.outgoing_halfedge;
auto he = he_begin;
do
{
// if half-edge is boundary, set it
if (halfedge(he).is_free())
{
v.outgoing_halfedge = he;
return;
}
// advance
he = halfedge(opposite(he)).next_halfedge;
} while (he != he_begin);
}
inline void Mesh::fix_boundary_state_of(face_index f_idx)
{
auto &f = face(f_idx);
auto he_begin = f.halfedge;
auto he = he_begin;
do
{
// if half-edge is boundary, set it
if (halfedge(opposite(he)).is_free())
{
f.halfedge = he;
return;
}
// advance
he = halfedge(he).next_halfedge;
} while (he != he_begin);
}
inline halfedge_index Mesh::find_free_incident(halfedge_index in_begin, halfedge_index in_end) const
{
assert(halfedge(in_begin).to_vertex == halfedge(in_end).to_vertex);
......@@ -1220,44 +1324,57 @@ inline void Mesh::compactify()
/// ======== HANDLES IMPLEMENTATION ========
inline bool vertex_handle::is_valid() const
inline bool vertex_handle::is_removed() const
{
return idx.is_valid() && mesh->vertex(idx).is_valid();
return idx.is_valid() && !mesh->vertex(idx).is_valid();
}
inline bool vertex_handle::is_removed() const
inline bool face_handle::is_removed() const
{
return !idx.is_valid() || !mesh->vertex(idx).is_valid();
return idx.is_valid() && !mesh->face(idx).is_valid();
}
inline bool face_handle::is_valid() const
inline bool edge_handle::is_removed() const
{
return idx.is_valid() && mesh->face(idx).is_valid();
return idx.is_valid() && !mesh->halfedge(idx, 0).is_valid();
}
inline bool face_handle::is_removed() const
inline bool halfedge_handle::is_removed() const
{
return !idx.is_valid() || !mesh->face(idx).is_valid();
return idx.is_valid() && !mesh->halfedge(idx).is_valid();
}
inline bool edge_handle::is_valid() const
inline bool vertex_handle::is_isolated() const
{
return idx.is_valid() && mesh->halfedge(idx, 0).is_valid();
return mesh->vertex(idx).is_isolated();
}
inline bool edge_handle::is_removed() const
inline bool vertex_handle::is_boundary() const
{
return !idx.is_valid() || !mesh->halfedge(idx, 0).is_valid();
auto const &v = mesh->vertex(idx);
if (v.is_isolated())
return true;
return mesh->halfedge(v.outgoing_halfedge).is_free();
}
inline bool halfedge_handle::is_valid() const
inline bool face_handle::is_boundary() const
{
return idx.is_valid() && mesh->halfedge(idx).is_valid();
return mesh->halfedge(mesh->opposite(mesh->face(idx).halfedge)).is_free();
}
inline bool halfedge_handle::is_removed() const
inline bool edge_handle::is_isolated() const
{
return mesh->halfedge(idx, 0).is_free() && mesh->halfedge(idx, 1).is_free();
}
inline bool edge_handle::is_boundary() const
{
return !idx.is_valid() || !mesh->halfedge(idx).is_valid();
return mesh->halfedge(idx, 0).is_free() || mesh->halfedge(idx, 1).is_free();
}
inline bool halfedge_handle::is_boundary() const
{
return mesh->halfedge(idx).is_free();
}
inline vertex_handle halfedge_handle::vertex_to() const
......@@ -1332,7 +1449,16 @@ inline face_handle edge_handle::faceB() const
inline face_handle vertex_handle::any_face() const
{
return mesh->handle_of(mesh->halfedge(mesh->vertex(idx).outgoing_halfedge).face);
auto h = mesh->vertex(idx).outgoing_halfedge;
return mesh->handle_of(h.is_valid() ? mesh->halfedge(h).face : face_index::invalid());
}
inline face_handle vertex_handle::any_valid_face() const
{
for (auto f : faces())
if (f.is_valid())
return f;
return mesh->handle_of(face_index::invalid());
}
inline halfedge_handle vertex_handle::any_outgoing_halfedge() const
......@@ -1342,7 +1468,14 @@ inline halfedge_handle vertex_handle::any_outgoing_halfedge() const
inline halfedge_handle vertex_handle::any_incoming_halfedge() const
{
return mesh->handle_of(mesh->opposite(mesh->vertex(idx).outgoing_halfedge));
auto h = mesh->vertex(idx).outgoing_halfedge;
return mesh->handle_of(h.is_valid() ? mesh->opposite(h) : halfedge_index::invalid());
}
inline edge_handle vertex_handle::any_edge() const
{
auto h = mesh->vertex(idx).outgoing_halfedge;
return mesh->handle_of(h.is_valid() ? mesh->edge_of(h) : edge_index::invalid());
}
inline vertex_handle face_handle::any_vertex() const
......@@ -1380,7 +1513,7 @@ inline vertex_halfedge_in_ring vertex_handle::incoming_halfedges() const
return {*this};
}
inline vertex_halfedge_out_ring vertex_handle::outcoming_halfedges() const
inline vertex_halfedge_out_ring vertex_handle::outgoing_halfedges() const
{
return {*this};
}
......
......@@ -39,6 +39,7 @@ struct face_index
explicit face_index(int idx) : value(idx) {}
bool is_valid() const { return value >= 0; }
bool is_invalid() const { return value < 0; }
static face_index invalid() { return {}; }
bool operator==(face_index const& rhs) const { return value == rhs.value; }
......@@ -58,6 +59,7 @@ struct vertex_index
explicit vertex_index(int idx) : value(idx) {}
bool is_valid() const { return value >= 0; }
bool is_invalid() const { return value < 0; }
static vertex_index invalid() { return {}; }
bool operator==(vertex_index const& rhs) const { return value == rhs.value; }
......@@ -77,6 +79,7 @@ struct edge_index
explicit edge_index(int idx) : value(idx) {}
bool is_valid() const { return value >= 0; }
bool is_invalid() const { return value < 0; }
static edge_index invalid() { return {}; }
bool operator==(edge_index const& rhs) const { return value == rhs.value; }
......@@ -96,6 +99,7 @@ struct halfedge_index
explicit halfedge_index(int idx) : value(idx) {}
bool is_valid() const { return value >= 0; }
bool is_invalid() const { return value < 0; }
static halfedge_index invalid() { return {}; }
bool operator==(halfedge_index const& rhs) const { return value == rhs.value; }
......@@ -126,8 +130,11 @@ struct face_handle
template <typename PropT>
PropT const& operator[](face_attribute<PropT> const& prop) const;
bool is_valid() const; ///< valid idx and not deleted
bool is_removed() const; ///< marked for deletion (or invalid idx)
bool is_valid() const { return idx.is_valid(); } ///< valid idx (but could be deleted in some iterators)
bool is_invalid() const { return !idx.is_valid(); }; ///< invalid idx