// Given an edge, the constructor for EdgeRecord finds the // optimal point associated with the edge's current quadric, // and assigns this edge a cost based on how much quadric // error is observed at this optimal point. EdgeRecord::EdgeRecord( EdgeIter& _edge ) : edge( _edge ) { // TODO Compute the combined quadric from the edge endpoints. Matrix4x4 q = _edge->halfedge()->vertex()->quadric + _edge->halfedge()->twin()->vertex()->quadric; // TODO Build the 3x3 linear system whose solution minimizes // the quadric error associated with these two endpoints. Matrix3x3 quadratic; quadratic(0,0) = q(0,0); quadratic(0,1) = q(0,1); quadratic(0,2) = q(0,2); quadratic(1,0) = q(1,0); quadratic(1,1) = q(1,1); quadratic(1,2) = q(1,2); quadratic(2,0) = q(2,0); quadratic(2,1) = q(2,1); quadratic(2,2) = q(2,2); Vector3D linear(q(3,0), q(3,1), q(3,2)); // TODO Use this system to solve for the optimal position, and // TODO store it in EdgeRecord::optimalPoint. optimalPoint = - quadratic.inv() * linear; // TODO Also store the cost associated with collapsing this edge // TODO in EdgeRecord::Cost. Vector4D optH(optimalPoint); optH.w = 1.0; score = dot(optH, q * optH); }
const HalfedgeMesh& HalfedgeMesh::operator=(const HalfedgeMesh& mesh) // The assignment operator does a "deep" copy of the halfedge mesh data // structure; in other words, it makes new instances of each mesh element, and // ensures that pointers in the copy point to the newly allocated elements // rather than elements in the original mesh. This behavior is especially // important for making assignments, since the mesh on the right-hand side of an // assignment may be temporary (hence any pointers to elements in this mesh will // become invalid as soon as it is released.) { // Clear any existing elements. halfedges.clear(); vertices.clear(); edges.clear(); faces.clear(); boundaries.clear(); // These maps will be used to identify elements of the old mesh // with elements of the new mesh. (Note that we can use a single // map for both interior and boundary faces, because the map // doesn't care which list of faces these iterators come from.) map<HalfedgeCIter, HalfedgeIter> halfedgeOldToNew; map<VertexCIter, VertexIter> vertexOldToNew; map<EdgeCIter, EdgeIter> edgeOldToNew; map<FaceCIter, FaceIter> faceOldToNew; // Copy geometry from the original mesh and create a map from // pointers in the original mesh to those in the new mesh. for (HalfedgeCIter h = mesh.halfedgesBegin(); h != mesh.halfedgesEnd(); h++) halfedgeOldToNew[h] = halfedges.insert(halfedges.end(), *h); for (VertexCIter v = mesh.verticesBegin(); v != mesh.verticesEnd(); v++) vertexOldToNew[v] = vertices.insert(vertices.end(), *v); for (EdgeCIter e = mesh.edgesBegin(); e != mesh.edgesEnd(); e++) edgeOldToNew[e] = edges.insert(edges.end(), *e); for (FaceCIter f = mesh.facesBegin(); f != mesh.facesEnd(); f++) faceOldToNew[f] = faces.insert(faces.end(), *f); for (FaceCIter b = mesh.boundariesBegin(); b != mesh.boundariesEnd(); b++) faceOldToNew[b] = boundaries.insert(boundaries.end(), *b); // "Search and replace" old pointers with new ones. for (HalfedgeIter he = halfedgesBegin(); he != halfedgesEnd(); he++) { he->next() = halfedgeOldToNew[he->next()]; he->twin() = halfedgeOldToNew[he->twin()]; he->vertex() = vertexOldToNew[he->vertex()]; he->edge() = edgeOldToNew[he->edge()]; he->face() = faceOldToNew[he->face()]; } for (VertexIter v = verticesBegin(); v != verticesEnd(); v++) v->halfedge() = halfedgeOldToNew[v->halfedge()]; for (EdgeIter e = edgesBegin(); e != edgesEnd(); e++) e->halfedge() = halfedgeOldToNew[e->halfedge()]; for (FaceIter f = facesBegin(); f != facesEnd(); f++) f->halfedge() = halfedgeOldToNew[f->halfedge()]; for (FaceIter b = boundariesBegin(); b != boundariesEnd(); b++) b->halfedge() = halfedgeOldToNew[b->halfedge()]; // Return a reference to the new mesh. return *this; }
bool willFlipEdgeImprove(EdgeIter e0) { //1. collect elements //Halfedges HalfedgeIter h0 = e0->halfedge(); HalfedgeIter h3 = h0->twin(); //Faces FaceIter f0 = h0->face(); FaceIter f1 = h3->face(); //Early Exit #1: Ignore requests to flip boundary edges if(f0->isBoundary() || f1->isBoundary()) return false; HalfedgeIter h1 = h0->next(); HalfedgeIter h2 = h1->next(); HalfedgeIter h4 = h3->next(); HalfedgeIter h5 = h4->next(); //Vertices VertexIter v0 = h0->vertex(); VertexIter v1 = h3->vertex(); VertexIter v2 = h2->vertex(); VertexIter v3 = h5->vertex(); //Early Exit #2: Does the flipped edge already exist? HalfedgeIter h = v2->halfedge(); do { h = h->twin(); if(h->vertex() == v3) return false; h = h->next(); } while(h != v2->halfedge()); int oldDev = std::abs((int)v0->degree() - 6) + std::abs((int)v1->degree() - 6) + std::abs((int)v2->degree() - 6) + std::abs((int)v3->degree() - 6); int newDev = std::abs((int)v0->degree() - 7) + std::abs((int)v1->degree() - 7) + std::abs((int)v2->degree() - 5) + std::abs((int)v3->degree() - 5); if( newDev < oldDev) return true; else return false; }
void MeshResampler::resample( HalfedgeMesh& mesh ) { // TODO Compute the mean edge length. double mean2 = 0; for(auto edge = mesh.edgesBegin(); edge != mesh.edgesEnd(); ++edge) { Vector3D v0 = edge->halfedge()->vertex()->position; Vector3D v1 = edge->halfedge()->twin()->vertex()->position; mean2 += (v0 - v1).norm(); } mean2 /= (double)mesh.nEdges(); mean2 *= mean2; // TODO Repeat the four main steps for 5 or 6 iterations const int ITER_TIMES = 5; const int SMOOTH_TIMES = 20; const double WEIGHT_FACTOR = 0.2; const double LONG_EDGE_2 = 1.7777777778; const double SHORT_EDGE_2 = 0.64; std::unordered_set<EdgeIter> edgeSet; edgeSet.reserve(mesh.nEdges()); for(int i = 0; i < ITER_TIMES; ++i) { // TODO Split edges much longer than the target length (being careful about how the loop is written!) EdgeIter old_end = mesh.edgesEnd(); --old_end; for(auto edge = mesh.edgesBegin(); edge != mesh.edgesEnd(); ++edge) { Vector3D v0 = edge->halfedge()->vertex()->position; Vector3D v1 = edge->halfedge()->twin()->vertex()->position; if((v0 - v1).norm2() > LONG_EDGE_2 * mean2) { mesh.splitEdge(edge); } if(edge == old_end) break; } // TODO Collapse edges much shorter than the target length. Here we need to be EXTRA careful about // TODO advancing the loop, because many edges may have been destroyed by a collapse (which ones?) edgeSet.clear(); for(auto edge = mesh.edgesBegin(); edge != mesh.edgesEnd(); ++edge) { edgeSet.insert(edge); } while(edgeSet.size() > 0) { EdgeIter curr = *(edgeSet.begin()); edgeSet.erase(edgeSet.begin()); VertexIter v0 = curr->halfedge()->vertex(); VertexIter v1 = curr->halfedge()->twin()->vertex(); if((v0->position - v1->position).norm2() >= SHORT_EDGE_2 * mean2) continue; HalfedgeIter h = v0->halfedge(); do { if(edgeSet.find(h->edge()) != edgeSet.end()) edgeSet.erase(h->edge()); h = h->twin()->next(); } while(h != v0->halfedge()); h = v1->halfedge(); do { if(edgeSet.find(h->edge()) != edgeSet.end()) edgeSet.erase(h->edge()); h = h->twin()->next(); } while(h != v1->halfedge()); VertexIter v_new = mesh.collapseEdge(curr); if(v_new != mesh.verticesEnd()) { h = v_new->halfedge(); do { edgeSet.insert(h->edge()); h = h->twin()->next(); } while(h != v_new->halfedge()); } } // TODO Now flip each edge if it improves vertex degree for(auto edge = mesh.edgesBegin(); edge != mesh.edgesEnd(); ++edge) { if(willFlipEdgeImprove(edge)) mesh.flipEdge(edge); } // TODO Finally, apply some tangential smoothing to the vertex positions for(int j = 0; j < SMOOTH_TIMES; ++j) { for(auto vertex = mesh.verticesBegin(); vertex != mesh.verticesEnd(); ++vertex) { if(vertex->isBoundary()) continue; vertex->computeCentroid(); } for(auto vertex = mesh.verticesBegin(); vertex != mesh.verticesEnd(); ++vertex) { if(vertex->isBoundary()) continue; Vector3D dir = vertex->centroid - vertex->position; Vector3D nrm = vertex->normal(); dir -= (dot(nrm, dir) * nrm); vertex->position = vertex->position + WEIGHT_FACTOR * dir; } } } }
void MeshResampler::upsample( HalfedgeMesh& mesh ) // This routine should increase the number of triangles in the mesh using Loop subdivision. { // Each vertex and edge of the original surface can be associated with a vertex in the new (subdivided) surface. // Therefore, our strategy for computing the subdivided vertex locations is to *first* compute the new positions // using the connectity of the original (coarse) mesh; navigating this mesh will be much easier than navigating // the new subdivided (fine) mesh, which has more elements to traverse. We will then assign vertex positions in // the new mesh based on the values we computed for the original mesh. // TODO Compute new positions for all the vertices in the input mesh, using the Loop subdivision rule, // TODO and store them in Vertex::newPosition. At this point, we also want to mark each vertex as being // TODO a vertex of the original mesh. for(VertexIter v = mesh.verticesBegin(); v != mesh.verticesEnd(); ++v ) { v->isNew = false; if(!v->isBoundary()) { //By convention, Vertex::degree() returns the face degree, //not the edge degree. The edge degree can be computed by finding the face //degree, and adding 1 if the vertex is a boundary vertex. int degree = v->degree(); double u = degree == 3 ? 0.1875 : (3.0 / (8.0 * degree)); //0.1875 = 3/16 v->newPosition = v->position * (1.0 - u * degree); HalfedgeIter h = v->halfedge(); do { h = h->twin(); v->newPosition += (h->vertex()->position * u); h = h->next(); } while(h != v->halfedge()); } else { HalfedgeIter h0_b = v->halfedge(); while(!h0_b->isBoundary()) h0_b = h0_b->twin()->next(); VertexIter v0_b = h0_b->twin()->vertex(); HalfedgeIter h1_b = v->halfedge(); do { h1_b = h1_b->twin(); if(h1_b->isBoundary()) break; h1_b = h1_b->next(); } while(h1_b != v->halfedge()); VertexIter v1_b = h1_b->vertex(); v->newPosition = 0.75 * v->position + 0.125 * (v0_b->position + v1_b->position); } } // TODO Next, compute the updated vertex positions associated with edges, and store it in Edge::newPosition. for(EdgeIter e = mesh.edgesBegin(); e != mesh.edgesEnd(); ++e) { e->isNew = false; FaceIter f0 = e->halfedge()->face(); FaceIter f1 = e->halfedge()->twin()->face(); if(f0->isBoundary() || f1->isBoundary()) { VertexIter v0_b = e->halfedge()->vertex(); VertexIter v1_b = e->halfedge()->twin()->vertex(); e->newPosition = 0.5 * (v0_b->position + v1_b->position); } else { Vector3D v0,v1; //this diagonal v0 = e->halfedge()->vertex()->position; v1 = e->halfedge()->twin()->vertex()->position; e->newPosition = 0.375 * (v0 + v1); Vector3D v2,v3; //opposite diagonal v2 = e->halfedge()->next()->next()->vertex()->position; v3 = e->halfedge()->twin()->next()->next()->vertex()->position; e->newPosition += 0.125 * (v2 + v3); } } // TODO Next, we're going to split every edge in the mesh, in any order. For future // TODO reference, we're also going to store some information about which subdivided // TODO edges come from splitting an edge in the original mesh, and which edges are new, // TODO by setting the flat Edge::isNew. Note that in this loop, we only want to iterate // TODO over edges of the original mesh---otherwise, we'll end up splitting edges that we // TODO just split (and the loop will never end!) EdgeIter e = mesh.edgesBegin(); while(e != mesh.edgesEnd()) { EdgeIter next = e; ++next; if(!e->isNew && !e->halfedge()->vertex()->isNew && !e->halfedge()->twin()->vertex()->isNew) { VertexIter e_v0 = e->halfedge()->vertex(); VertexIter e_v1 = e->halfedge()->twin()->vertex(); VertexIter v_new = mesh.splitEdge(e); v_new->isNew = true; v_new->position = e->newPosition; HalfedgeIter h = v_new->halfedge(); do { h = h->twin(); VertexIter v = h->vertex(); h->edge()->isNew = (v != e_v0 && v != e_v1); h = h->next(); } while(h != v_new->halfedge()); } e = next; } // TODO Now flip any new edge that connects an old and new vertex. for(EdgeIter e = mesh.edgesBegin(); e!= mesh.edgesEnd(); ++e) { VertexIter v0 = e->halfedge()->vertex(); VertexIter v1 = e->halfedge()->twin()->vertex(); bool v_old_new = (v0->isNew && !v1->isNew) || (!v0->isNew && v1->isNew); if(v_old_new && e->isNew) mesh.flipEdge(e); } // TODO Finally, copy the new vertex positions into final Vertex::position. for(VertexIter v = mesh.verticesBegin(); v != mesh.verticesEnd(); ++v) { if(!v->isNew) v->position = v->newPosition; } }
EdgeIter HalfedgeMesh::flipEdge( EdgeIter e0 ) { // TODO This method should flip the given edge and return an iterator to the flipped edge. //1. collect elements //Halfedges HalfedgeIter h0 = e0->halfedge(); HalfedgeIter h3 = h0->twin(); //Faces FaceIter f0 = h0->face(); FaceIter f1 = h3->face(); //Early Exit #1: Ignore requests to flip boundary edges if(f0->isBoundary() || f1->isBoundary()) return edgesEnd(); HalfedgeIter h1 = h0->next(); HalfedgeIter h2 = h1->next(); HalfedgeIter h4 = h3->next(); HalfedgeIter h5 = h4->next(); //Vertices VertexIter v0 = h0->vertex(); VertexIter v1 = h3->vertex(); VertexIter v2 = h2->vertex(); VertexIter v3 = h5->vertex(); //Early Exit #2: Does the flipped edge already exist? HalfedgeIter h = v2->halfedge(); do { h = h->twin(); if(h->vertex() == v3) return edgesEnd(); h = h->next(); } while(h != v2->halfedge()); //Edges //e0 is given //2. reassign elements //HalfEdges h0->next() = h5; h0->vertex() = v2; h5->next() = h1; h5->face() = f0; h1->next() = h0; h3->next() = h2; h3->vertex() = v3; h2->next() = h4; h2->face() = f1; h4->next() = h3; //Vertices v0->halfedge() = h4; v1->halfedge() = h1; v2->halfedge() = h0; v3->halfedge() = h3; //Faces f0->halfedge() = h0; f1->halfedge() = h3; return e0; }
VertexIter HalfedgeMesh::splitEdge( EdgeIter e0 ) { // TODO This method should split the given edge and return an iterator to the newly inserted vertex. // TODO The halfedge of this vertex should point along the edge that was split, rather than the new edges. //1. collect elements //Halfedges HalfedgeIter h0 = e0->halfedge(); HalfedgeIter h3 = h0->twin(); //Faces FaceIter f0 = h0->face(); FaceIter f1 = h3->face(); //Ignore requests to split boundary edges if(f0->isBoundary() && f1->isBoundary()) return verticesEnd(); else if(f0->isBoundary()) { //1. collent elements (continue) HalfedgeIter h1 = h0->next(); HalfedgeIter h4 = h3->next(); HalfedgeIter h5 = h4->next(); //Vertices VertexIter v0 = h0->vertex(); VertexIter v1 = h3->vertex(); VertexIter v3 = h5->vertex(); //Edges //e0 is given //2. allocate new elements HalfedgeIter h11 = newHalfedge(); HalfedgeIter h13 = newHalfedge(); HalfedgeIter h14 = newHalfedge(); HalfedgeIter h15 = newHalfedge(); FaceIter f3 = newFace(); EdgeIter e6 = newEdge(); EdgeIter e7 = newEdge(); VertexIter v4 = newVertex(); v4->position = 0.5f * (v0->position + v1->position); //3. reassign elements //Halfedges h0->next() = h11; h11->setNeighbors(h1, h14, v4, e7, f0); h3->vertex() = v4; h4->next() = h13; h13->setNeighbors(h3, h15, v3, e6, f1); h14->setNeighbors(h15, h11, v1, e7, f3); h15->setNeighbors(h5, h13, v4, e6, f3); h5->next() = h14; h5->face() = f3; //Vertices v0->halfedge() = h0; v1->halfedge() = h14; v3->halfedge() = h13; v4->halfedge() = h3; //Edges e6->halfedge() = h13; e7->halfedge() = h14; //Faces f1->halfedge() = h3; f3->halfedge() = h14; return v4; } else if(f1->isBoundary()) { //1. collent elements (continue) HalfedgeIter h1 = h0->next(); HalfedgeIter h2 = h1->next(); //HalfedgeIter h4 = h3->next(); HalfedgeIter h5 = h3; do h5 = h5->next(); while(h5->next() != h3); //Vertices VertexIter v0 = h0->vertex(); VertexIter v1 = h3->vertex(); VertexIter v2 = h2->vertex(); //Edges //e0 is given //2. allocate new elements HalfedgeIter h10 = newHalfedge(); HalfedgeIter h11 = newHalfedge(); HalfedgeIter h12 = newHalfedge(); HalfedgeIter h14 = newHalfedge(); FaceIter f2 = newFace(); EdgeIter e5 = newEdge(); EdgeIter e7 = newEdge(); VertexIter v4 = newVertex(); v4->position = 0.5f * (v0->position + v1->position); //3. reassign elements //Halfedges h0->next() = h10; h10->setNeighbors(h2, h12, v4, e5, f0); h11->setNeighbors(h1, h14, v4, e7, f2); h1->next() = h12; h1->face() = f2; h12->setNeighbors(h11, h10, v2, e5, f2); h3->vertex() = v4; h5->next() = h14; h14->setNeighbors(h3, h11, v1, e7, f1); //Vertices v0->halfedge() = h0; v1->halfedge() = h14; v2->halfedge() = h12; v4->halfedge() = h3; //Edges e7->halfedge() = h11; e5->halfedge() = h10; //Faces f0->halfedge() = h0; f1->halfedge() = h3; f2->halfedge() = h1; return v4; } else { //1. collent elements (continue) HalfedgeIter h1 = h0->next(); HalfedgeIter h2 = h1->next(); HalfedgeIter h4 = h3->next(); HalfedgeIter h5 = h4->next(); // HalfedgeIter h6 = h1->twin(); // HalfedgeIter h7 = h2->twin(); // HalfedgeIter h8 = h4->twin(); // HalfedgeIter h9 = h5->twin(); //Vertices VertexIter v0 = h0->vertex(); VertexIter v1 = h3->vertex(); VertexIter v2 = h2->vertex(); VertexIter v3 = h5->vertex(); //Edges //e0 is given EdgeIter e1 = h1->edge(); EdgeIter e2 = h2->edge(); EdgeIter e3 = h4->edge(); EdgeIter e4 = h5->edge(); //2. allocate new elements HalfedgeIter h10 = newHalfedge(); HalfedgeIter h11 = newHalfedge(); HalfedgeIter h12 = newHalfedge(); HalfedgeIter h13 = newHalfedge(); HalfedgeIter h14 = newHalfedge(); HalfedgeIter h15 = newHalfedge(); FaceIter f2 = newFace(); FaceIter f3 = newFace(); EdgeIter e5 = newEdge(); EdgeIter e6 = newEdge(); EdgeIter e7 = newEdge(); VertexIter v4 = newVertex(); v4->position = 0.5f * (v0->position + v1->position); //3. reassign elements //Halfedges h0->next() = h10; h0->twin() = h3; h0->vertex() = v0; h0->edge() = e0; h0->face() = f0; h1->next() = h12; /*h1->twin() = h6;*/ h1->vertex() = v1; h1->edge() = e1; h1->face() = f2; h2->next() = h0; /*h2->twin() = h7;*/ h2->vertex() = v2; h2->edge() = e2; h2->face() = f0; h3->next() = h4; h3->twin() = h0; h3->vertex() = v4; h3->edge() = e0; h3->face() = f1; h4->next() = h13; /*h4->twin() = h8;*/ h4->vertex() = v0; h4->edge() = e3; h4->face() = f1; h5->next() = h14; /*h5->twin() = h9;*/ h5->vertex() = v3; h5->edge() = e4; h5->face() = f3; // h6->twin() = h1; h6->vertex() = v2; h6->edge() = e1; // h7->twin() = h2; h7->vertex() = v0; h7->edge() = e2; // h8->twin() = h4; h8->vertex() = v3; h8->edge() = e3; // h9->twin() = h5; h9->vertex() = v1; h9->edge() = e4; h10->next() = h2; h10->twin() = h12; h10->vertex() = v4; h10->edge() = e5; h10->face() = f0; h11->next() = h1; h11->twin() = h14; h11->vertex() = v4; h11->edge() = e7; h11->face() = f2; h12->next() = h11; h12->twin() = h10; h12->vertex() = v2; h12->edge() = e5; h12->face() = f2; h13->next() = h3; h13->twin() = h15; h13->vertex() = v3; h13->edge() = e6; h13->face() = f1; h14->next() = h15; h14->twin() = h11; h14->vertex() = v1; h14->edge() = e7; h14->face() = f3; h15->next() = h5; h15->twin() = h13; h15->vertex() = v4; h15->edge() = e6; h15->face() = f3; //Vertices v0->halfedge() = h0; v1->halfedge() = h1; v2->halfedge() = h2; v3->halfedge() = h5; v4->halfedge() = h3; //Edges e0->halfedge() = h0; e1->halfedge() = h1; e2->halfedge() = h2; e3->halfedge() = h4; e4->halfedge() = h5; e5->halfedge() = h10; e6->halfedge() = h13; e7->halfedge() = h11; //Faces f0->halfedge() = h0; f1->halfedge() = h3; f2->halfedge() = h1; f3->halfedge() = h5; return v4; } }
void HalfedgeMesh::build(const vector<vector<Index> >& polygons, const vector<Vector3D>& vertexPositions) // This method initializes the halfedge data structure from a raw list of // polygons, where each input polygon is specified as a list of vertex indices. // The input must describe a manifold, oriented surface, where the orientation // of a polygon is determined by the order of vertices in the list. Polygons // must have at least three vertices. Note that there are no special conditions // on the vertex indices, i.e., they do not have to start at 0 or 1, nor does // the collection of indices have to be contiguous. Overall, this initializer // is designed to be robust but perhaps not incredibly fast (though of course // this does not affect the performance of the resulting data structure). One // could also implement faster initializers that handle important special cases // (e.g., all triangles, or data that is known to be manifold). Since there are // no strong conditions on the indices of polygons, we assume that the list of // vertex positions is given in lexicographic order (i.e., that the lowest index // appearing in any polygon corresponds to the first entry of the list of // positions and so on). { // define some types, to improve readability typedef vector<Index> IndexList; typedef IndexList::const_iterator IndexListCIter; typedef vector<IndexList> PolygonList; typedef PolygonList::const_iterator PolygonListCIter; typedef pair<Index, Index> IndexPair; // ordered pair of vertex indices, // corresponding to an edge of an // oriented polygon // Clear any existing elements. halfedges.clear(); vertices.clear(); edges.clear(); faces.clear(); boundaries.clear(); // Since the vertices in our halfedge mesh are stored in a linked list, // we will temporarily need to keep track of the correspondence between // indices of vertices in our input and pointers to vertices in the new // mesh (which otherwise can't be accessed by index). Note that since // we're using a general-purpose map (rather than, say, a vector), we can // be a bit more flexible about the indexing scheme: input vertex indices // aren't required to be 0-based or 1-based; in fact, the set of indices // doesn't even have to be contiguous. Taking advantage of this fact makes // our conversion a bit more robust to different types of input, including // data that comes from a subset of a full mesh. // maps a vertex index to the corresponding vertex map<Index, VertexIter> indexToVertex; // Also store the vertex degree, i.e., the number of polygons that use each // vertex; this information will be used to check that the mesh is manifold. map<VertexIter, Size> vertexDegree; // First, we do some basic sanity checks on the input. for (PolygonListCIter p = polygons.begin(); p != polygons.end(); p++) { if (p->size() < 3) { // Refuse to build the mesh if any of the polygons have fewer than three // vertices.(Note that if we omit this check the code will still // constructsomething fairlymeaningful for 1- and 2-point polygons, but // enforcing this stricterrequirementon the input will help simplify code // further downstream, since it canbe certainit doesn't have to check for // these rather degenerate cases.) cerr << "Error converting polygons to halfedge mesh: each polygon must " "have at least three vertices." << endl; exit(1); } // We want to count the number of distinct vertex indices in this // polygon, to make sure it's the same as the number of vertices // in the polygon---if they disagree, then the polygon is not valid // (or at least, for simplicity we don't handle polygons of this type!). set<Index> polygonIndices; // loop over polygon vertices for (IndexListCIter i = p->begin(); i != p->end(); i++) { polygonIndices.insert(*i); // allocate one vertex for each new index we encounter if (indexToVertex.find(*i) == indexToVertex.end()) { VertexIter v = newVertex(); v->halfedge() = halfedges.end(); // this vertex doesn't yet point to any halfedge indexToVertex[*i] = v; vertexDegree[v] = 1; // we've now seen this vertex only once } else { // keep track of the number of times we've seen this vertex vertexDegree[indexToVertex[*i]]++; } } // end loop over polygon vertices // check that all vertices of the current polygon are distinct Size degree = p->size(); // number of vertices in this polygon if (polygonIndices.size() < degree) { cerr << "Error converting polygons to halfedge mesh: one of the input " "polygons does not have distinct vertices!" << endl; cerr << "(vertex indices:"; for (IndexListCIter i = p->begin(); i != p->end(); i++) { cerr << " " << *i; } cerr << ")" << endl; exit(1); } // end check that polygon vertices are distinct } // end basic sanity checks on input // The number of vertices in the mesh is the // number of unique indices seen in the input. Size nVertices = indexToVertex.size(); // The number of faces is just the number of polygons in the input. Size nFaces = polygons.size(); faces.resize(nFaces); // allocate storage for faces in our new mesh // We will store a map from ordered pairs of vertex indices to // the corresponding halfedge object in our new (halfedge) mesh; // this map gets constructed during the next loop over polygons. map<IndexPair, HalfedgeIter> pairToHalfedge; // Next, we actually build the halfedge connectivity by again looping over // polygons PolygonListCIter p; FaceIter f; for (p = polygons.begin(), f = faces.begin(); p != polygons.end(); p++, f++) { vector<HalfedgeIter> faceHalfedges; // cyclically ordered list of the half // edges of this face Size degree = p->size(); // number of vertices in this polygon // loop over the halfedges of this face (equivalently, the ordered pairs of // consecutive vertices) for (Index i = 0; i < degree; i++) { Index a = (*p)[i]; // current index Index b = (*p)[(i + 1) % degree]; // next index, in cyclic order IndexPair ab(a, b); HalfedgeIter hab; // check if this halfedge already exists; if so, we have a problem! if (pairToHalfedge.find(ab) != pairToHalfedge.end()) { cerr << "Error converting polygons to halfedge mesh: found multiple " "oriented edges with indices (" << a << ", " << b << ")." << endl; cerr << "This means that either (i) more than two faces contain this " "edge (hence the surface is nonmanifold), or" << endl; cerr << "(ii) there are exactly two faces containing this edge, but " "they have the same orientation (hence the surface is" << endl; cerr << "not consistently oriented." << endl; exit(1); } else // otherwise, the halfedge hasn't been allocated yet { // so, we point this vertex pair to a new halfedge hab = newHalfedge(); pairToHalfedge[ab] = hab; // link the new halfedge to its face hab->face() = f; hab->face()->halfedge() = hab; // also link it to its starting vertex hab->vertex() = indexToVertex[a]; hab->vertex()->halfedge() = hab; // keep a list of halfedges in this face, so that we can later // link them together in a loop (via their "next" pointers) faceHalfedges.push_back(hab); } // Also, check if the twin of this halfedge has already been constructed // (during construction of a different face). If so, link the twins // together and allocate their shared halfedge. By the end of this pass // over polygons, the only halfedges that will not have a twin will hence // be those that sit along the domain boundary. IndexPair ba(b, a); map<IndexPair, HalfedgeIter>::iterator iba = pairToHalfedge.find(ba); if (iba != pairToHalfedge.end()) { HalfedgeIter hba = iba->second; // link the twins hab->twin() = hba; hba->twin() = hab; // allocate and link their edge EdgeIter e = newEdge(); hab->edge() = e; hba->edge() = e; e->halfedge() = hab; } else { // If we didn't find a twin... // ...mark this halfedge as being twinless by pointing // it to the end of the list of halfedges. If it remains // twinless by the end of the current loop over polygons, // it will be linked to a boundary face in the next pass. hab->twin() = halfedges.end(); } } // end loop over the current polygon's halfedges // Now that all the halfedges of this face have been allocated, // we can link them together via their "next" pointers. for (Index i = 0; i < degree; i++) { Index j = (i + 1) % degree; // index of the next halfedge, in cyclic order faceHalfedges[i]->next() = faceHalfedges[j]; } } // done building basic halfedge connectivity // For each vertex on the boundary, advance its halfedge pointer to one that // is also on the boundary. for (VertexIter v = verticesBegin(); v != verticesEnd(); v++) { // loop over halfedges around vertex HalfedgeIter h = v->halfedge(); do { if (h->twin() == halfedges.end()) { v->halfedge() = h; break; } h = h->twin()->next(); } while (h != v->halfedge()); // end loop over halfedges around vertex } // done advancing halfedge pointers for boundary vertices // Next we construct new faces for each boundary component. for (HalfedgeIter h = halfedgesBegin(); h != halfedgesEnd(); h++) // loop over all halfedges { // Any halfedge that does not yet have a twin is on the boundary of the // domain. If we follow the boundary around long enough we will of course // eventually make a closed loop; we can represent this boundary loop by a // new face. To make clear the distinction between faces and boundary loops, // the boundary face will (i) have a flag indicating that it is a boundary // loop, and (ii) be stored in a list of boundaries, rather than the usual // list of faces. The reason we need the both the flag *and* the separate // list is that faces are often accessed in two fundamentally different // ways: either by (i) local traversal of the neighborhood of some mesh // element using the halfedge structure, or (ii) global traversal of all // faces (or boundary loops). if (h->twin() == halfedges.end()) { FaceIter b = newBoundary(); vector<HalfedgeIter> boundaryHalfedges; // keep a list of halfedges along // the boundary, so we can link // them together // We now need to walk around the boundary, creating new // halfedges and edges along the boundary loop as we go. HalfedgeIter i = h; do { // create a twin, which becomes a halfedge of the boundary loop HalfedgeIter t = newHalfedge(); boundaryHalfedges.push_back( t); // keep a list of all boundary halfedges, in cyclic order i->twin() = t; t->twin() = i; t->face() = b; t->vertex() = i->next()->vertex(); // create the shared edge EdgeIter e = newEdge(); e->halfedge() = i; i->edge() = e; t->edge() = e; // Advance i to the next halfedge along the current boundary loop // by walking around its target vertex and stopping as soon as we // find a halfedge that does not yet have a twin defined. i = i->next(); while (i != h && // we're done if we end up back at the beginning of // the loop i->twin() != halfedges.end()) // otherwise, we're looking for // the next twinless halfedge // along the loop { i = i->twin(); i = i->next(); } } while (i != h); // The only pointers that still need to be set are the "next" pointers of // the twins; these we can set from the list of boundary halfedges, but we // must use the opposite order from the order in the list, since the // orientation of the boundary loop is opposite the orientation of the // halfedges "inside" the domain boundary. Size degree = boundaryHalfedges.size(); for (Index p = 0; p < degree; p++) { Index q = (p - 1 + degree) % degree; boundaryHalfedges[p]->next() = boundaryHalfedges[q]; } } // end construction of one of the boundary loops // Note that even though we are looping over all halfedges, we will still // construct the appropriate number of boundary loops (and not, say, one // loop per boundary halfedge). The reason is that as we continue to // iterate through halfedges, we check whether their twin has been assigned, // and since new twins may have been assigned earlier in this loop, we will // end up skipping many subsequent halfedges. } // done adding "virtual" faces corresponding to boundary loops // To make later traversal of the mesh easier, we will now advance the // halfedge // associated with each vertex such that it refers to the *first* non-boundary // halfedge, rather than the last one. for (VertexIter v = verticesBegin(); v != verticesEnd(); v++) { v->halfedge() = v->halfedge()->twin()->next(); } // Finally, we check that all vertices are manifold. for (VertexIter v = vertices.begin(); v != vertices.end(); v++) { // First check that this vertex is not a "floating" vertex; // if it is then we do not have a valid 2-manifold surface. if (v->halfedge() == halfedges.end()) { cerr << "Error converting polygons to halfedge mesh: some vertices are " "not referenced by any polygon." << endl; exit(1); } // Next, check that the number of halfedges emanating from this vertex in // our half edge data structure equals the number of polygons containing // this vertex, which we counted during our first pass over the mesh. If // not, then our vertex is not a "fan" of polygons, but instead has some // other (nonmanifold) structure. Size count = 0; HalfedgeIter h = v->halfedge(); do { if (!h->face()->isBoundary()) { count++; } h = h->twin()->next(); } while (h != v->halfedge()); if (count != vertexDegree[v]) { cerr << "Error converting polygons to halfedge mesh: at least one of the " "vertices is nonmanifold." << endl; exit(1); } } // end loop over vertices // Now that we have the connectivity, we copy the list of vertex // positions into member variables of the individual vertices. if (vertexPositions.size() != vertices.size()) { cerr << "Error converting polygons to halfedge mesh: number of vertex " "positions is different from the number of distinct vertices!" << endl; cerr << "(number of positions in input: " << vertexPositions.size() << ")" << endl; cerr << "( number of vertices in mesh: " << vertices.size() << ")" << endl; exit(1); } // Since an STL map internally sorts its keys, we can iterate over the map // from vertex indices to vertex iterators to visit our (input) vertices in // lexicographic order int i = 0; for (map<Index, VertexIter>::const_iterator e = indexToVertex.begin(); e != indexToVertex.end(); e++) { // grab a pointer to the vertex associated with the current key (i.e., the // current index) VertexIter v = e->second; // set the att of this vertex to the corresponding // position in the input v->position = vertexPositions[i]; i++; } // compute initial normals for (VertexIter v = verticesBegin(); v != verticesEnd(); v++) { v->computeNormal(); } } // end HalfedgeMesh::build()