Skip to content

Instantly share code, notes, and snippets.

@Lhy121125
Created December 30, 2022 02:41
Show Gist options
  • Select an option

  • Save Lhy121125/8d092eafd9e0583534c3f907b3a9b28f to your computer and use it in GitHub Desktop.

Select an option

Save Lhy121125/8d092eafd9e0583534c3f907b3a9b28f to your computer and use it in GitHub Desktop.
Mesh Simplification With Flux
#include "element.h"
#include "linear_algebra.h"
#include "mat.hpp"
#include "mesh.h"
#include "sphere.h"
#include "readwrite.h"
#include "simplifier.h"
#include "vec.hpp"
#include "webgl.h"
#include <map>
using namespace flux;
// set the following to 0 to test a mesh of your choice
#define TEST_SPHERE 1
bool
collapse_edge( HalfEdge* e, HalfEdgeMesh<Triangle>& halfedges, CollapsePriorityQueue& queue) {
// retrieve info arount this edge
HalfVertex* p = e->vertex;
HalfVertex* q = e->twin->vertex;
std::vector<HalfVertex*> onering_p;
halfedges.get_onering(p, onering_p);
std::vector<HalfVertex*> onering_q;
halfedges.get_onering(q, onering_q);
// check 1
int count = 0;
for (HalfVertex* i: onering_p){
for (HalfVertex* j: onering_q){
if (i == j){
count++;
}
}
}
// check 2
// Retrieve the onering of the faces around p and q
std::vector<HalfFace*> onering_face_p;
halfedges.get_onering(p, onering_face_p);
std::vector<HalfFace*> onering_face_q;
halfedges.get_onering(q, onering_face_q);
bool reject = false;
for(HalfFace* f : onering_face_p){
if(calculate_face_data(f)){
reject = true;
break;
}
}
for(HalfFace* f : onering_face_q){
if(calculate_face_data(f)){
reject = true;
break;
}
}
if (count == 2 || !reject){
/* perform contraction: */
// pick q as the receiving vertex
// Re-assign the coordinates of q to those stored in vbar of the edge
for (int d = 0; d < 3; d++) {
q->point[d] = e->vbar[d];
}
q->Q = e->Qbar;
// reassign the origin vertex of edges around p
std::vector<HalfEdge*> onering_p_edge;
halfedges.get_onering(p, onering_p_edge);
for (HalfEdge* half: onering_p_edge){
half->vertex = q;
}
HalfEdge* h4 = e->twin;
HalfEdge* h5 = h4->next;
HalfEdge* h6 = h4->next->next;
HalfEdge* h3 = e->next;
HalfEdge* h1 = e->next->next;
HalfEdge* e4 = h1->twin;
HalfEdge* e3 = h3->twin;
HalfEdge* e9 = h5->twin;
HalfEdge* e10 = h6->twin;
// reassign the twin of e4, e3, e9, and e10.
e4->twin = e3;
e3->twin = e4;
e9->twin = e10;
e10->twin = e9;
// reassign the edges of affected vertices with non-trash edges
e3->vertex->edge = e3;
e9->vertex->edge = e9;
e10->vertex->edge = e10;
/* Update all the pointers of the half-edge entities affected by the collapse */
// remove 1 vertex: p
halfedges.remove(p);
// remove 2 halffaces
halfedges.remove(e->face);
halfedges.remove(h4->face);
// remove 6 halfedges
queue.erase(h6);
queue.erase(h5);
queue.erase(h4);
queue.erase(h1);
queue.erase(h3);
queue.erase(e);
halfedges.remove(h6);
halfedges.remove(h5);
halfedges.remove(h4);
halfedges.remove(h1);
halfedges.remove(h3);
halfedges.remove(e);
/* recompute vbar, Qbar and cost of any edge (i.e. in the new one-ring
of the receiving vertex, including twins) affected by the collapse */
std::vector<HalfEdge*> edges_newOnering;
halfedges.get_onering(q, edges_newOnering);
for (int k = 0; k < edges_newOnering.size(); k++) {
HalfEdge* temp = edges_newOnering[k];
queue.erase(temp);
calculate_edge_data(temp);
queue.insert(temp);
queue.erase(temp->twin);
calculate_edge_data(temp->twin);
queue.insert(temp->twin);
}
} else{
queue.erase(e);
queue.erase(e->twin);
}
return reject;
}
int main( int argc , char** argv ) {
// setup the initial mesh
#if TEST_SPHERE
// #if 0
int N = 100;
Sphere<Triangle> mesh(N,N);
#else
// use read_obj or read_off to read a mesh from a file
// the working directory of this executable is "projects/project3"
std::unique_ptr<MeshBase> mesh_ptr = read_obj("../../data/spot.obj" , false );
Mesh<Triangle>& mesh = *static_cast< Mesh<Triangle>* >(mesh_ptr.get());
#endif
// Step 1: calculate the vertex, edge, and face data
// 1a: build up the half edges
HalfEdgeMesh<Triangle> halfmesh( mesh );
// 1b: compute the face data
for (auto& f_ptr : halfmesh.faces()) {
calculate_face_data( f_ptr.get() );
}
// 1c: compute the initial matrices for each vertex
for (auto& v_ptr : halfmesh.vertices()) {
calculate_vertex_data(v_ptr.get(),halfmesh);
}
// 1d: compute the optimal point location (vbar) and matrix Qbar, as
// well as the cost of every edge
for (auto& e_ptr : halfmesh.edges()) {
HalfEdge* e = e_ptr.get();
calculate_edge_data(e);
}
//step 2: insert each edge into a priority queue
CollapsePriorityQueue edges;
for (auto& e_ptr : halfmesh.edges()) {
edges.insert( e_ptr.get() );
}
int numFaces = halfmesh.faces().size();
int nf_target = 100;
int rejections = 0;
while (numFaces > nf_target){
// a) retrieve the edge with the highest prority (lowest cost)
HalfEdge* e = * edges.begin();
bool rejected = collapse_edge(e, halfmesh, edges);
if (rejected){
rejections++;
}
numFaces = halfmesh.faces().size();
}
printf("Check 2 - Number of Rejections: %d \n", rejections);
halfmesh.extract(mesh);
// add the initial mesh to the viewer
Viewer viewer;
viewer.add(mesh);
// simplify your mesh to some target number of faces (triangles)
// extract the simplified mesh and add it to the viewer
// run the visualizer
viewer.run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment