// +------------------------------------------------------------------------- // | 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; }