Created
December 30, 2022 02:41
-
-
Save Lhy121125/8d092eafd9e0583534c3f907b3a9b28f to your computer and use it in GitHub Desktop.
Mesh Simplification With Flux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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