// +-------------------------------------------------------------------------
// | mesh.topoCache.tpp
// |
// | Author: Gilbert Bernstein
// +-------------------------------------------------------------------------
// | COPYRIGHT:
// | Copyright Gilbert Bernstein 2013
// | See the included COPYRIGHT file for further details.
// |
// | This file is part of the Cork library.
// |
// | Cork is free software: you can redistribute it and/or modify
// | it under the terms of the GNU Lesser General Public License as
// | published by the Free Software Foundation, either version 3 of
// | the License, or (at your option) any later version.
// |
// | Cork 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 Lesser General Public License for more details.
// |
// | You should have received a copy
// | of the GNU Lesser General Public License
// | along with Cork. If not, see .
// +-------------------------------------------------------------------------
#pragma once
#include "../util/iterPool.h"
/*
* Allows for topological algorithms to manipulate
* a more familiar pointer data structure based on a simplicial complex.
* This structure can be regenerated from the more basic
* vertex/triangle arrays using
* createTopoCache()
* Once manipulations have been satisfactorily performed,
* the underlying vertex/triangle arrays can be cleaned up for
* further use by topologically insensitive algorithms by
* commitTopoCache()
*/
#define INVALID_ID uint(-1)
struct TopoVert {
uint ref; // index to actual data
void* data; // algorithm specific handle
ShortVec tris; // triangles this vertex is incident on
ShortVec edges; // edges this vertex is incident on
};
struct TopoEdge {
void* data; // algorithm specific handle
Vptr verts[2]; // endpoint vertices
ShortVec tris; // incident triangles
};
struct TopoTri {
uint ref; // index to actual data
void* data; // algorithm specific handle
Vptr verts[3]; // vertices of this triangle
Eptr edges[3]; // edges of this triangle
// opposite to the given vertex
};
template
struct Mesh::TopoCache {
IterPool verts;
IterPool edges;
IterPool tris;
Mesh *mesh;
TopoCache(Mesh *owner);
virtual ~TopoCache() {}
// until commit() is called, the Mesh::verts and Mesh::tris
// arrays will still contain garbage entries
void commit();
bool isValid();
void print();
// helpers to create bits and pieces
inline Vptr newVert();
inline Eptr newEdge();
inline Tptr newTri();
// helpers to release bits and pieces
inline void freeVert(Vptr);
inline void freeEdge(Eptr);
inline void freeTri(Tptr);
// helper to delete geometry in a structured way
inline void deleteTri(Tptr);
// helper to flip triangle orientation
inline void flipTri(Tptr);
private:
void init();
};
template inline
Vptr Mesh::TopoCache::newVert()
{
uint ref = mesh->verts.size();
mesh->verts.push_back(VertData());
Vptr v = verts.alloc(); // cache.verts
v->ref = ref;
return v;
}
template inline
Eptr Mesh::TopoCache::newEdge()
{
Eptr e = edges.alloc(); // cache.edges
return e;
}
template inline
Tptr Mesh::TopoCache::newTri()
{
uint ref = mesh->tris.size();
mesh->tris.push_back(Tri());
Tptr t = tris.alloc(); // cache.tris
t->ref = ref;
return t;
}
template inline
void Mesh::TopoCache::freeVert(Vptr v)
{
verts.free(v);
}
template inline
void Mesh::TopoCache::freeEdge(Eptr e)
{
edges.free(e);
}
template inline
void Mesh::TopoCache::freeTri(Tptr t)
{
tris.free(t);
}
template inline
void Mesh::TopoCache::deleteTri(Tptr tri)
{
// first, unhook the triangle from its faces
for(uint k=0; k<3; k++) {
Vptr v = tri->verts[k];
v->tris.erase(tri);
Eptr e = tri->edges[k];
e->tris.erase(tri);
}
// now, let's check for any edges which no longer border triangles
for(uint k=0; k<3; k++) {
Eptr e = tri->edges[k];
if(e->tris.size() == 0) {
// delete edge
// unhook from vertices
Vptr v0 = e->verts[0];
v0->edges.erase(e);
Vptr v1 = e->verts[1];
v1->edges.erase(e);
freeEdge(e);
}
}
// now, let's check for any vertices which no longer border triangles
for(uint k=0; k<3; k++) {
Vptr v = tri->verts[k];
if(v->tris.size() == 0) {
freeVert(v);
}
}
// finally, release the triangle
freeTri(tri);
}
template inline
void Mesh::TopoCache::flipTri(Tptr t)
{
std::swap(t->verts[0], t->verts[1]);
std::swap(t->edges[0], t->edges[1]);
std::swap(mesh->tris[t->ref].v[0], mesh->tris[t->ref].v[1]);
}
template
Mesh::TopoCache::TopoCache(
Mesh *owner
) : mesh(owner) {
init();
}
// support structure for cache construction
struct TopoEdgePrototype {
uint vid;
ShortVec tris;
TopoEdgePrototype() {}
TopoEdgePrototype(uint v) : vid(v) {}
};
inline TopoEdgePrototype& getTopoEdgePrototype(
uint a, uint b,
std::vector< ShortVec > &prototypes
) {
uint N = prototypes[a].size();
for(uint i=0; i
void Mesh::TopoCache::init()
{
// first lay out vertices
std::vector temp_verts(mesh->verts.size()); // need temp. reference
for(uint i=0; iverts.size(); i++) {
Vptr vert = verts.alloc(); // cache.verts.alloc()
vert->ref = i;
temp_verts[i] = vert;
}
// We need to still do the following
// * Generate TopoTris
// * Generate TopoEdges
// ---- Hook up references between
// * Triangles and Vertices
// * Triangles and Edges
// * Vertices and Edges
// We handle two of these items in a pass over the triangles,
// * Generate TopoTris
// * Hook up Triangles and Vertices
// building a structure to handle the edges as we go:
std::vector< ShortVec > edgeacc(mesh->verts.size());
for(uint i=0; itris.size(); i++) {
Tptr tri = tris.alloc(); // cache.tris.alloc()
tri->ref = i;
const Tri &ref_tri = mesh->tris[i];
// triangles <--> verts
uint vids[3];
for(uint k=0; k<3; k++) {
uint vid = vids[k] = ref_tri.v[k];
tri->verts[k] = temp_verts[vid];
temp_verts[vid]->tris.push_back(tri);
}
// then, put these in arbitrary but globally consistent order
if(vids[0] > vids[1]) std::swap(vids[0], vids[1]);
if(vids[1] > vids[2]) std::swap(vids[1], vids[2]);
if(vids[0] > vids[1]) std::swap(vids[0], vids[1]);
// and accrue in structure
getTopoEdgePrototype(vids[0], vids[1], edgeacc).tris.push_back(tri);
getTopoEdgePrototype(vids[0], vids[2], edgeacc).tris.push_back(tri);
getTopoEdgePrototype(vids[1], vids[2], edgeacc).tris.push_back(tri);
}
// Now, we can unpack the edge accumulation to
// * Generate TopoEdges
// * Hook up Triangles and Edges
// * Hook up Vertices and Edges
for(uint vid0=0; vid0 < edgeacc.size(); vid0++) {
for(TopoEdgePrototype &proto : edgeacc[vid0]) {
uint vid1 = proto.vid;
Vptr v0 = temp_verts[vid0];
Vptr v1 = temp_verts[vid1];
Eptr edge = edges.alloc(); // cache.edges.alloc()
// edges <--> verts
edge->verts[0] = v0;
v0->edges.push_back(edge);
edge->verts[1] = v1;
v1->edges.push_back(edge);
// edges <--> tris
for(Tptr tri : proto.tris) {
edge->tris.push_back(tri);
for(uint k=0; k<3; k++) {
if(v0 != tri->verts[k] && v1 != tri->verts[k]) {
tri->edges[k] = edge;
break;
}
}
}
}}
//ENSURE(isValid());
//print();
}
template
void Mesh::TopoCache::commit()
{
//ENSURE(isValid());
// record which vertices are live
std::vector live_verts(mesh->verts.size(), false);
verts.for_each([&](Vptr vert) { // cache.verts
live_verts[vert->ref] = true;
});
// record which triangles are live, and record connectivity
std::vector live_tris(mesh->tris.size(), false);
tris.for_each([&](Tptr tri) { // cache.tris
live_tris[tri->ref] = true;
for(uint k=0; k<3; k++)
mesh->tris[tri->ref].v[k] = tri->verts[k]->ref;
});
// compact the vertices and build a remapping function
std::vector vmap(mesh->verts.size());
uint write = 0;
for(uint read = 0; read < mesh->verts.size(); read++) {
if(live_verts[read]) {
vmap[read] = write;
mesh->verts[write] = mesh->verts[read];
write++;
} else {
vmap[read] = INVALID_ID;
}
}
mesh->verts.resize(write);
// rewrite the vertex reference ids
verts.for_each([&](Vptr vert) { // cache.verts
vert->ref = vmap[vert->ref];
});
std::vector tmap(mesh->tris.size());
write = 0;
for(uint read = 0; read < mesh->tris.size(); read++) {
if(live_tris[read]) {
tmap[read] = write;
mesh->tris[write] = mesh->tris[read];
for(uint k=0; k<3; k++)
mesh->tris[write].v[k] = vmap[mesh->tris[write].v[k]];
write++;
} else {
tmap[read] = INVALID_ID;
}
}
mesh->tris.resize(write);
// rewrite the triangle reference ids
tris.for_each([&](Tptr tri) { // cache.tris
tri->ref = tmap[tri->ref];
});
}
// support functions for validity check
template inline
bool count(const Container &contain, const T &val) {
uint c=0;
for(const T &t : contain)
if(t == val) c++;
return c;
}
template inline
bool count2(const T arr[], const T &val) {
return ((arr[0] == val)? 1 : 0) + ((arr[1] == val)? 1 : 0);
}
template inline
bool count3(const T arr[], const T &val) {
return ((arr[0] == val)? 1 : 0) + ((arr[1] == val)? 1 : 0)
+ ((arr[2] == val)? 1 : 0);
}
template
bool Mesh::TopoCache::isValid()
{
//print();
std::set vaddr;
std::set eaddr;
std::set taddr;
verts.for_each([&vaddr](Vptr v) { vaddr.insert(v); });
edges.for_each([&eaddr](Eptr e) { eaddr.insert(e); });
tris.for_each( [&taddr](Tptr t) { taddr.insert(t); });
// check verts
verts.for_each([&](Vptr v) {
ENSURE(v->ref < mesh->verts.size());
// make sure each edge pointer goes somewhere and that
// the pointed-to site also points back correctly
for(Eptr e : v->edges) {
ENSURE(eaddr.count(e) > 0); // pointer is good
ENSURE(count2(e->verts, v) == 1); // back-pointer is good
}
for(Tptr t : v->tris) {
ENSURE(taddr.count(t) > 0);
ENSURE(count3(t->verts, v) == 1);
}
});
// check edges
edges.for_each([&](Eptr e) {
// check for non-degeneracy
ENSURE(e->verts[0] != e->verts[1]);
for(uint k=0; k<2; k++) {
Vptr v = e->verts[k];
ENSURE(vaddr.count(v) > 0);
ENSURE(count(v->edges, e) == 1);
}
for(Tptr t : e->tris) {
ENSURE(taddr.count(t) > 0);
ENSURE(count3(t->edges, e) == 1);
}
});
// check triangles
tris.for_each([&](Tptr t) {
// check for non-degeneracy
ENSURE(t->verts[0] != t->verts[1] && t->verts[1] != t->verts[2]
&& t->verts[0] != t->verts[2]);
for(uint k=0; k<3; k++) {
Vptr v = t->verts[k];
ENSURE(vaddr.count(v) > 0);
ENSURE(count(v->tris, t) == 1);
Eptr e = t->edges[k];
ENSURE(eaddr.count(e) == 1);
ENSURE(count(e->tris, t) == 1);
// also need to ensure that the edges are opposite the
// vertices as expected
Vptr v0 = e->verts[0];
Vptr v1 = e->verts[1];
ENSURE((v0 == t->verts[(k+1)%3] && v1 == t->verts[(k+2)%3])
|| (v0 == t->verts[(k+2)%3] && v1 == t->verts[(k+1)%3]));
}
});
return true;
}
std::ostream& operator<<(std::ostream &out, const TopoVert& vert)
{
out << "ref(" << vert.ref << ") "
<< "e(" << vert.edges.size() << "):";
for(Eptr e : vert.edges)
out << e << ";";
out << " "
<< "t(" << vert.tris.size() << "):";
for(Tptr t : vert.tris)
out << t << ";";
return out;
}
std::ostream& operator<<(std::ostream &out, const TopoEdge& edge)
{
out << "v(2):" << edge.verts[0] << "(" << edge.verts[0]->ref << ");"
<< edge.verts[1] << "(" << edge.verts[1]->ref << ");";
out << " "
<< "t(" << edge.tris.size() << "):";
for(Tptr t : edge.tris)
out << t << ";";
return out;
}
std::ostream& operator<<(std::ostream &out, const TopoTri& tri)
{
out << "ref(" << tri.ref << ") ";
out << "v(3):" << tri.verts[0] << "(" << tri.verts[0]->ref << ");"
<< tri.verts[1] << "(" << tri.verts[1]->ref << ");"
<< tri.verts[2] << "(" << tri.verts[2]->ref << ");";
out << " ";
out << "e(3):" << tri.edges[0] << ";"
<< tri.edges[1] << ";"
<< tri.edges[2] << ";";
return out;
}
template
void Mesh::TopoCache::print()
{
using std::cout;
using std::endl;
cout << "dumping remeshing cache for debug..." << endl;
cout << "TRIS" << endl;
int tri_count = 0;
tris.for_each([&](Tptr t) {
cout << " " << t << ": " << *t << endl;
tri_count++;
});
cout << "There were " << tri_count << " TRIS" << endl;
cout << "EDGES" << endl;
int edge_count = 0;
edges.for_each([&](Eptr e) {
cout << " " << e << ": " << endl;
cout << " v " << e->verts[0] << "; "
<< e->verts[1] << endl;
cout << " t (" << e->tris.size() << ")" << endl;
for(Tptr t : e->tris)
cout << " " << t << endl;
edge_count++;
});
cout << "There were " << edge_count << " EDGES" << endl;
cout << "VERTS" << endl;
int vert_count = 0;
verts.for_each([&](Vptr v) {
cout << " " << v << ": ref(" << v->ref << ")" << endl;
cout << " e (" << v->edges.size() << ")" << endl;
for(Eptr e : v->edges)
cout << " " << e << endl;
cout << " t (" << v->tris.size() << ")" << endl;
for(Tptr t : v->tris)
cout << " " << t << endl;
vert_count++;
});
cout << "There were " << vert_count << " VERTS" << endl;
}