void display() { // Set up correct OpenGL projection glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(dmin[0], dmax[0], dmin[1], dmax[1]); glMatrixMode(GL_MODELVIEW); // Specify that we want to draw triangle outlines glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Black on white. glClearColor(1,1,1,0); glColor3f(0,0,0); // Clear the screen. glClear(GL_COLOR_BUFFER_BIT); for(FaceID f: m.faces()){ glBegin(GL_POLYGON); for(Walker w = m.walker(f); !w.full_circle(); w = w.next()) glVertex3dv(m.pos(w.vertex()).get()); glEnd(); } // Draw flipper. glColor3f(1,0,0); glBegin(GL_LINES); Walker hew = m.walker(*flipper); glVertex3dv(m.pos(hew.vertex()).get()); glVertex3dv(m.pos(hew.opp().vertex()).get()); glEnd(); glFinish(); }
bool obj_save(const string& filename, Manifold& m) { ofstream os(filename.data()); if(os.bad()) return false; VertexAttributeVector<int> vmap; int k = 0; for(VertexIDIterator v = m.vertices_begin(); v != m.vertices_end(); ++v){ Vec3d p = m.pos(*v); os << "v "<< p[0] << " " << p[1] << " " << p[2] << "\n"; vmap[*v] = k++; } for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f){ vector<int> verts; for(Walker w = m.walker(*f); !w.full_circle(); w = w.circulate_face_ccw()){ int idx = vmap[w.vertex()]; assert(static_cast<size_t>(idx) < m.no_vertices()); // move subscript range from 0..size-1 to 1..size according to OBJ standards verts.push_back(idx + 1); } os << "f "; for(size_t i = 0; i < verts.size() ; ++i){ os << verts[i] << " "; } os<<endl; } return true; }
AmbientOcclusionRenderer::AmbientOcclusionRenderer(const Manifold& m, bool smooth, VertexAttributeVector<double>& field, double max_val): SimpleShaderRenderer(vss,fss) { GLint old_prog; glGetIntegerv(GL_CURRENT_PROGRAM, &old_prog); glUseProgram(prog); GLuint scalar_attrib = glGetAttribLocation(prog, "scalar"); glUniform1fARB(glGetUniformLocationARB(prog, "scalar_max"), max_val); glNewList(display_list,GL_COMPILE); for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f) { if(!smooth) glNormal3dv(normal(m, *f).get()); if(no_edges(m, *f)== 3) glBegin(GL_TRIANGLES); else glBegin(GL_POLYGON); for(Walker w = m.walker(*f); !w.full_circle(); w = w.circulate_face_ccw()) { Vec3d n(normal(m, w.vertex())); if(smooth) glNormal3dv(n.get()); glVertexAttrib1d(scalar_attrib, field[w.vertex()]); glVertex3dv(m.pos(w.vertex()).get()); } glEnd(); } glEndList(); glUseProgram(old_prog); }
void mark_halfedges() { // Give all halfedges a mark of 0 for(HalfEdgeID h: m.halfedges()) { if(m.walker(h).opp().halfedge() < h) touched[h] = 0; else touched[h] = 1; } }
void dual(Manifold& m) { // Create new vertices. Each face becomes a vertex whose position // is the centre of the face int i = 0; FaceAttributeVector<int> ftouched; vector<Vec3d> vertices; vertices.resize(m.no_faces()); for(auto f : m.faces()) vertices[ftouched[f] = i++] = centre(m, f); // Create new faces. Each vertex is a new face with N=valency of vertex // edges. vector<int> faces; vector<int> indices; for(auto v : m.vertices()) if(valency(m, v) > 2 && !(boundary(m, v))) { // int N = circulate_vertex_ccw(m, v, (std::function<void(FaceID)>)[&](FaceID fid) { // indices.push_back(ftouched[fid]); // }); Walker w = m.walker(v); for(; !w.full_circle(); w = w.circulate_vertex_ccw()){ indices.push_back(ftouched[w.face()]); } int N = w.no_steps(); // Insert face valency in the face vector. faces.push_back(N); } // Clear the manifold before new geometry is inserted. m.clear(); // And build m.build( vertices.size(), reinterpret_cast<double*>(&vertices[0]), faces.size(), &faces[0], &indices[0]); }
LineFieldRenderer::LineFieldRenderer(const Manifold& m, bool smooth, VertexAttributeVector<Vec3d>& lines, float _r): SimpleShaderRenderer(vss,fss), r(_r) { float noise_scale = 10.0f/r; float line_scale = 0.003f; GLint old_prog; glGetIntegerv(GL_CURRENT_PROGRAM, &old_prog); glUseProgram(prog); glUniform1fARB(glGetUniformLocationARB(prog, "scale_line"),line_scale*noise_scale); glUniform1fARB(glGetUniformLocationARB(prog, "noise_scale"),noise_scale); glUniform1iARB(glGetUniformLocationARB(prog, "noise_tex"),0); GLuint direction = glGetAttribLocation(prog, "direction"); glNewList(display_list,GL_COMPILE); for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f) { if(!smooth) glNormal3dv(normal(m, *f).get()); if(no_edges(m, *f) == 3) glBegin(GL_TRIANGLES); else glBegin(GL_POLYGON); for(Walker w = m.walker(*f); !w.full_circle(); w = w.circulate_face_ccw()) { Vec3d n(normal(m, w.vertex())); if(smooth) glNormal3dv(n.get()); Vec3d d = lines[w.vertex()]; d = normalize(d-n*dot(n,d)); glVertexAttrib3dv(direction, d.get()); glVertex3dv(m.pos(w.vertex()).get()); } glEnd(); } glBindTexture(GL_TEXTURE_3D, 0); glEndList(); glUseProgram(old_prog); }
CircleFieldRenderer::CircleFieldRenderer(const Manifold& m, bool smooth, VertexAttributeVector<Vec2d>& field, float gamma): SimpleShaderRenderer(vss, fss) { GLint old_prog; glGetIntegerv(GL_CURRENT_PROGRAM, &old_prog); glUseProgram(prog); GLuint scalar_attrib = glGetAttribLocation(prog, "circlepos"); // static float& gamma = CreateCVar("display.scalar_field_renderer.gamma",2.2f); glUniform1fARB(glGetUniformLocationARB(prog, "gamma"), gamma); glNewList(display_list,GL_COMPILE); for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f) { if(!smooth) glNormal3dv(normal(m, *f).get()); if(no_edges(m, *f)== 3) glBegin(GL_TRIANGLES); else glBegin(GL_POLYGON); for(Walker w = m.walker(*f); !w.full_circle(); w = w.circulate_face_ccw()) { Vec3d n(normal(m, w.vertex())); if(smooth) glNormal3dv(n.get()); glVertexAttrib2dv(scalar_attrib, field[w.vertex()].get()); glVertex3dv(m.pos(w.vertex()).get()); } glEnd(); } glEndList(); glUseProgram(old_prog); }
void mean_curvature_smooth(Manifold& m, bool implicit, double lambda) { using EigMat = SparseMatrix<double>; using EigVec = VectorXd; int N = (int)m.no_vertices(); VertexAttributeVector<int> indices(m.allocated_vertices()); VertexAttributeVector<double> areas(m.allocated_vertices()); int i=0; for(auto v: m.vertices()) { indices[v] = i++; areas[v] = mixed_area(m, v); } EigMat K(N,N); // Sparse matrix initialized with 0 EigVec X(N),Y(N),Z(N); EigVec Xp(N), Yp(N), Zp(N); //----------------------------------------------------------- // Student implementation //----------------------------------------------------------- double epsilon = 1e-5; for (auto vkey : m.vertices()) { int i = indices[vkey]; for (auto w = m.walker(vkey); !w.full_circle(); w = w.circulate_vertex_ccw()) { int j = indices[w.vertex()]; assert(i != j); if (i > j or w.face() == HMesh::InvalidFaceID or w.opp().face() == HMesh::InvalidFaceID) { continue; // Avoid recomputation } auto pi = m.pos(w.opp().vertex()); auto pj = m.pos(w.vertex()); auto pl = m.pos(w.opp().next().vertex()); auto pk = m.pos(w.next().vertex()); double cot_alpha_ij = dot(pj - pk, pi - pk) / ( cross(pi - pk, pj - pk).length() + epsilon); double cot_beta_ij = dot(pj - pl, pi - pl) / ( cross(pi - pl, pj - pl).length() + epsilon); double Ai = areas[w.opp().vertex()]; double Aj = areas[w.vertex()]; double Lij = (cot_alpha_ij + cot_beta_ij) / sqrt(Ai*Aj + epsilon); K.coeffRef(i, j) = Lij; K.coeffRef(j, i) = Lij; K.coeffRef(i, i) -= Lij; K.coeffRef(j, j) -= Lij; } } EigMat I(N,N); for (int i = 0; i < N; i++) { I.coeffRef(i, i) = 1; } K = I - K*lambda; for (auto vkey : m.vertices()) { auto p = m.pos(vkey); int i = indices[vkey]; X.coeffRef(i) = p[0]; Y.coeffRef(i) = p[1]; Z.coeffRef(i) = p[2]; } // Solve SimplicialLLT<EigMat> solver(K); Xp = solver.solve(X); Yp = solver.solve(Y); Zp = solver.solve(Z); // End student implementation //----------------------------------------------------------- for(auto v: m.vertices()) { int i = indices[v]; m.pos(v) = Vec3d(Xp[i], Yp[i], Zp[i]); } }
int main(int argc, char** argv) { /* * Read and parse a point set. */ /* Open a data stream for reading. * We first open data.txt. There is also kote1.txt which contains height * values in addition to x,y positions. */ ifstream data("data.txt"); vector<Vec2d> pts; if(data.good()) while(!data.eof()) { double x,y; data >> x >> y; if(data.good()) { Vec2d p(x,y); pts.push_back(p); dmin = v_min(p,dmin); dmax = v_max(p,dmax); } } cout << "Loaded " << pts.size() << " points " << endl; Vec2d trans((dmax[0]+dmin[0])/2,(dmax[1]+dmin[1])/2); double skal = 2/max(dmax[0]-dmin[0],dmax[1]-dmin[1]); /* Træk trans fra alle punkter og gang med 'skal'*/ for (int i = 0; i < pts.size(); i++) { pts[i] -= trans; pts[i] *= skal; } /* * Build a triangle mesh with a single triangle consisting of the * first three vertices. */ create_single_triangle_manifold(Vec3f(0, 3, 0), Vec3f(4.5, -1.5, 0), Vec3f(-4.5, -1.5, 0), m); // Initially just split the triangle by inserting the first point VertexID v = m.split_face_by_vertex(*m.faces_begin()); m.pos(v) = Vec3d(pts[0][0], pts[0][1], 0); // Now insert all of the remaining points for (int i = 1; i < pts.size(); i++) { Vec3d insertionPoint = Vec3d(pts[i][0], pts[i][1], 0); VertexID insertionVertex; // Loop over all the faces and find the face that contains the point for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); f++) { Walker w = m.walker(*f); bool isLeftOf = true; while (!w.full_circle()) { // If the point to be inserted is not to the left of the halfedge, then break the while loop and continue to the next face if (!leftOf(m.pos(w.circulate_face_ccw().vertex()), m.pos(w.vertex()), insertionPoint)) { isLeftOf = false; break; } w = w.circulate_face_cw(); } // if we found the face the point belongs to then insert it and break the for loop if (isLeftOf == true) { insertionVertex = m.split_face_by_vertex(*f); m.pos(insertionVertex) = insertionPoint; break; } } // Now loop over all the edges affected by the inserted point. // Note that we are assuming that the point was inserted, if not then this will crash spectacularly. Walker w = m.walker(insertionVertex); // Keep track of the next halfedge pointing TO the inserted vertex HalfEdgeID next_edge = w.circulate_vertex_ccw().opp().halfedge(); HalfEdgeAttributeVector<int> touched; while (!w.full_circle()) { // Iterate over the face of the current halfedge until we reach the next edge pointing TO the inserted vertex if(w.halfedge() != next_edge) { // Check if the current halfedge is locally Delaunay using the inCircle function recursiveDelaunayFlip(m, w, false); // Update the walker to be the next halfedge in the current face. w = w.circulate_face_ccw(); } else { // If we are the next edge pointing to the inserted vertex then go to opposite halfedge. This means we are now looking at the halfedge pointing AWAY from the inserted vertex. w = w.opp(); // Remember to update the next_edge to be the next halfedge pointing to the inserted vertex. next_edge = w.circulate_vertex_ccw().opp().halfedge(); } } } /* * Initialize GLUT, the system used to show OpenGL windows. */ glutInit(&argc, argv); glutInitWindowSize(512,512); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow("Delaunay"); glutDisplayFunc(display); // This function is called from glut to draw glutKeyboardFunc(keyfun); // Parse keyboard input // Pass control to glut glutMainLoop(); return 0; }