MeshKit
1.0
|
00001 #include "meshkit/Tri2Quad.hpp" 00002 #include "meshkit/StopWatch.hpp" 00003 00004 using namespace Jaal; 00005 00007 00008 int Tri2Quads::verify(Mesh *mesh, const vector<FacePair> &matching) 00009 { 00010 int relexist2 = mesh->build_relations(0, 2); 00011 00012 // This is the Graph matching verification. It is useful to verify the 00013 // matching, if done by other algorithms. 00014 00015 size_t numfaces = mesh->getSize(2); 00016 for (size_t i = 0; i < numfaces; i++) { 00017 Face * f = mesh->getFaceAt(i); 00018 f->setVisitMark(0); 00019 } 00020 00021 FaceSequence faceneighs; 00022 size_t nsize = matching.size(); 00023 for (size_t i = 0; i < nsize; i++) { 00024 size_t f1 = matching[i].first; 00025 size_t f2 = matching[i].second; 00026 assert(f1 != f2); 00027 if (f1 < f2) { 00028 Face *f0 = mesh->getFaceAt(f1); 00029 Face *f1 = mesh->getFaceAt(f2); 00030 f0->getRelations12( faceneighs ); 00031 if (find(faceneighs.begin(), faceneighs.end(), f1) == faceneighs.end()) { 00032 cout << "Error: Face matching error: faces not neighbors " << endl; 00033 exit(0); 00034 return 1; 00035 } 00036 assert(!f0->isVisited()); 00037 assert(!f1->isVisited()); 00038 f0->setVisitMark(1); 00039 f1->setVisitMark(1); 00040 } 00041 } 00042 00043 for (size_t i = 0; i < numfaces; i++) { 00044 Face * f = mesh->getFaceAt(i); 00045 (void)f; 00046 assert(f->isVisited()); 00047 } 00048 00049 if( !relexist2 ) mesh->clear_relations(0,2 ); 00050 00051 return 0; 00052 00053 } 00055 00056 Mesh* Tri2Quads::collapse_matched_triangles(Mesh *trimesh, const vector<FacePair> &matching, 00057 int replace) 00058 { 00059 #ifdef DEBUG 00060 if(Tri2Quads::verify(trimesh, matching) != 0) return NULL; 00061 cout << " Verification Done " << endl; 00062 #endif 00063 00064 Mesh *quadmesh = new Mesh; 00065 00066 size_t nsize = matching.size(); 00067 00068 assert( nsize ); 00069 00070 Face *tri1, *tri2, *quad; 00071 00072 NodeSequence tnodes; 00073 trimesh->getNodes( tnodes ); 00074 quadmesh->addNodes(tnodes ); 00075 quadmesh->reserve( nsize, 2 ); 00076 00077 for (size_t i = 0; i < nsize; i++) { 00078 size_t f1 = matching[i].first; 00079 size_t f2 = matching[i].second; 00080 tri1 = trimesh->getFaceAt(f1); 00081 tri2 = trimesh->getFaceAt(f2); 00082 quad = Face::create_quad(tri1, tri2, replace); 00083 quadmesh->addFace(quad); 00084 if (replace) delete tri2; 00085 } 00086 00087 if (replace) trimesh->emptyAll(); 00088 00089 return quadmesh; 00090 } 00092 00093 int Tri2Quads::refine_boundary_triangle(Face *tri0) 00094 { 00095 if (tri0->getSize(0) != 3) 00096 return 1; 00097 00098 Vertex *bv0 = NULL; 00099 Vertex *bv1 = NULL; 00100 Vertex *bv2 = NULL; 00101 00102 for (int i = 0; i < 3; i++) { 00103 Vertex *ev1 = tri0->getNodeAt(i + 1); 00104 Vertex *ev2 = tri0->getNodeAt(i + 2); 00105 if (ev1->isBoundary() && ev2->isBoundary()) { 00106 bv0 = ev1; 00107 bv1 = ev2; 00108 bv2 = tri0->getNodeAt(i); 00109 break; 00110 } 00111 } 00112 00113 if (bv0 == NULL || bv1 == NULL) 00114 return 2; 00115 00116 Point3D p3d; 00117 Vertex::mid_point(bv0, bv1, p3d); 00118 00119 Vertex *bound = Vertex::newObject(); 00120 bound->setXYZCoords(p3d); 00121 00122 trimesh->addNode(bound); 00123 00124 NodeSequence tconnect(3); 00125 tconnect[0] = bv0; 00126 tconnect[1] = bound; 00127 tconnect[2] = bv2; 00128 tri0->setNodes(tconnect); 00129 00130 tconnect[0] = bound; 00131 tconnect[1] = bv1; 00132 tconnect[2] = bv2; 00133 maxfaceID++; 00134 Face *tri1 = Face::newObject(); 00135 tri1->setID(maxfaceID); 00136 tri1->setNodes(tconnect); 00137 trimesh->addFace(tri1); 00138 00139 steinerNodes.push_back(bound); 00140 00141 FacePair facepair; 00142 facepair.first = tri0->getID(); 00143 facepair.second = tri1->getID(); 00144 facematching.push_back(facepair); 00145 00146 return 0; 00147 } 00148 00150 00151 00152 void Tri2Quads::splitParent(BinaryNode *parent, BinaryNode *child1, 00153 BinaryNode *child2) 00154 { 00155 Vertex* dnode = NULL; 00156 dnode = parent->getDualNode(); 00157 00158 Face *parentface = NULL; 00159 dnode->getAttribute("PrimalFace", parentface); 00160 00161 dnode = child1->getDualNode(); 00162 Face *face1 = NULL; 00163 dnode->getAttribute("PrimalFace", face1); 00164 00165 dnode = child2->getDualNode(); 00166 Face *face2 = NULL; 00167 dnode->getAttribute("PrimalFace", face2); 00168 00169 NodeSequence connect(3); 00170 00171 // Remove all existing vertex-face relations; 00172 Vertex *vertex; 00173 for (int i = 0; i < 3; i++) { 00174 vertex = parentface->getNodeAt(i); 00175 vertex->clearRelations(2); 00176 00177 vertex = face1->getNodeAt(i); 00178 vertex->clearRelations(2); 00179 00180 vertex = face2->getNodeAt(i); 00181 vertex->clearRelations(2); 00182 } 00183 00184 // Rebuild vertex-face relations... 00185 for (int i = 0; i < 3; i++) { 00186 vertex = parentface->getNodeAt(i); 00187 vertex->addRelation(parentface); 00188 00189 vertex = face1->getNodeAt(i); 00190 vertex->addRelation(face1); 00191 00192 vertex = face2->getNodeAt(i); 00193 vertex->addRelation(face2); 00194 } 00195 00196 dnode = NULL; 00197 parentface->getAttribute("DualNode", dnode); 00198 Vertex *steiner = dnode->getClone(); 00199 steiner->setID(parentface->getID()); 00200 trimesh->addNode(steiner); 00201 steinerNodes.push_back(steiner); 00202 00203 int edge1, edge2, edge3; 00204 00205 edge1 = edge2 = edge3 = -1; 00206 FaceSequence neighs; 00207 for (int i = 0; i < 3; i++) { 00208 Vertex *v0 = parentface->getNodeAt(i + 1); 00209 Vertex *v1 = parentface->getNodeAt(i + 2); 00210 Mesh::getRelations112(v0, v1, neighs); 00211 00212 if (neighs.size() == 1) 00213 edge3 = i; 00214 00215 if (neighs.size() == 2) { 00216 if (find(neighs.begin(), neighs.end(), face1) != neighs.end()) 00217 edge1 = i; 00218 if (find(neighs.begin(), neighs.end(), face2) != neighs.end()) 00219 edge2 = i; 00220 } 00221 } 00222 00223 Face *qface; 00224 Vertex *ev0, *ev1; 00225 00226 // Match Child1 and One of the Split Triangle ... 00227 maxfaceID++; 00228 ev0 = parentface->getNodeAt(edge1 + 1); 00229 ev1 = parentface->getNodeAt(edge1 + 2); 00230 connect[0] = steiner; 00231 connect[1] = ev0; 00232 connect[2] = ev1; 00233 qface = Face::newObject(); 00234 qface->setNodes(connect); 00235 qface->setID(maxfaceID); 00236 Vertex *dc1 = DualGraph::getNewDualNode( qface ); 00237 dc1->setID(maxfaceID); 00238 trimesh->addFace(qface); 00239 steinerFaces.push_back(qface); 00240 dnode = NULL; 00241 face1->getAttribute("DualNode", dnode); 00242 matchnodes( dnode, dc1); 00243 BinaryNode *bnode1 = new BinaryNode(dc1); 00244 bnode1->setMatchMark(1); 00245 bnode1->setParent(parent); 00246 bnode1->addChild(child1); 00247 btree->addNode(bnode1); 00248 00249 // Match Child2 and One of the Split Triangle ... 00250 maxfaceID++; 00251 ev0 = parentface->getNodeAt(edge2 + 1); 00252 ev1 = parentface->getNodeAt(edge2 + 2); 00253 connect[0] = steiner; 00254 connect[1] = ev0; 00255 connect[2] = ev1; 00256 qface = Face::newObject(); 00257 qface->setID(maxfaceID); 00258 qface->setNodes(connect); 00259 Vertex *dc2 = DualGraph::getNewDualNode( qface ); 00260 dc2->setID(maxfaceID); 00261 trimesh->addFace(qface); 00262 steinerFaces.push_back(qface); 00263 dnode = NULL; 00264 face2->getAttribute( "DualNode", dnode); 00265 matchnodes( dnode, dc2); 00266 00267 BinaryNode *bnode2 = new BinaryNode(dc2); 00268 bnode2->setMatchMark(1); 00269 bnode2->setParent(parent); 00270 bnode2->addChild(child2); 00271 btree->addNode(bnode2); 00272 00273 // Now Parent have different connectivity ... 00274 ev0 = parentface->getNodeAt(edge3 + 1); 00275 ev1 = parentface->getNodeAt(edge3 + 2); 00276 connect[0] = steiner; 00277 connect[1] = ev0; 00278 connect[2] = ev1; 00279 parentface->setNodes(connect); 00280 Point3D p3d; 00281 parentface->getAvgPos( p3d ); 00282 00283 Vertex *dc3 = NULL; 00284 parentface->getAttribute("DualNode", dc3); 00285 dc3->setXYZCoords(p3d); 00286 parent->addChild(bnode1); 00287 parent->addChild(bnode2); 00288 modifiedFaces.push_back(parentface); 00289 00290 for (int i = 0; i < 3; i++) { 00291 vertex = parentface->getNodeAt(i); 00292 vertex->clearRelations(2); 00293 00294 vertex = face1->getNodeAt(i); 00295 vertex->clearRelations(2); 00296 00297 vertex = face2->getNodeAt(i); 00298 vertex->clearRelations(2); 00299 } 00300 00301 child1->setMatchMark(1); 00302 child2->setMatchMark(1); 00303 00304 btree->removeNode(child1); 00305 btree->removeNode(child2); 00306 } 00307 00309 00310 void Tri2Quads::matchnode(BinaryNode* v) 00311 { 00312 BinaryNode *parv = v->getParent(); 00313 00314 if (parv == NULL) 00315 return; 00316 00317 int degree = parv->getDegree(); 00318 00319 if (parv->isRoot() && degree == 2) { 00320 BinaryNode *vsib = v->getSibling(); 00321 splitParent(parv, v, vsib); 00322 return; 00323 } 00324 00325 if (degree == 1 || degree == 2) { 00326 matchnodes(v, parv); 00327 return; 00328 } 00329 00330 if (degree == 3) { 00331 00332 if (required_topology == ALL_QUADS) { 00333 BinaryNode *vsib = v->getSibling(); 00334 splitParent(parv, v, vsib); 00335 return; 00336 } 00337 00338 BinaryNode *vsib = v->getSibling(); 00339 Vertex *d0 = v->getDualNode(); 00340 Vertex *d1 = vsib->getDualNode(); 00341 if (d0->getNumRelations(0) < d1->getNumRelations(0)) { 00342 matchnodes(v, parv); 00343 btree->unlinkNode(vsib); 00344 } else { 00345 matchnodes(vsib, parv); 00346 btree->unlinkNode(v); 00347 } 00348 } 00349 } 00350 00352 00353 BinaryNode* Tri2Quads::getChildofDegreeNParent(BNodeList &levelnodes, 00354 int nd) 00355 { 00356 BinaryNode *currnode, *parent, *child; 00357 00358 int ncount; 00359 BNodeList::const_iterator it; 00360 00361 for (it = levelnodes.begin(); it != levelnodes.end(); ++it) { 00362 currnode = *it; 00363 parent = currnode->getParent(); 00364 if (parent) { 00365 if (!parent->isMatched()) { 00366 ncount = 0; 00367 if (parent->getParent()) 00368 ncount = 1; 00369 for (int i = 0; i < parent->getNumChildren(); i++) { 00370 child = parent->getChild(i); 00371 if (!child->isMatched()) 00372 ncount++; 00373 } 00374 if (ncount == nd) 00375 return currnode; 00376 } 00377 } 00378 } 00379 00380 return NULL; 00381 } 00382 00384 00385 BinaryNode *Tri2Quads::getNextNode(BNodeList &levelnodes) 00386 { 00387 BinaryNode *currnode = NULL; 00388 00389 if (levelnodes.empty()) 00390 return currnode; 00391 00392 BNodeList::iterator it; 00393 for (it = levelnodes.begin(); it != levelnodes.end(); ++it) { 00394 currnode = *it; 00395 if (currnode->isMatched()) 00396 btree->unlinkNode(currnode); 00397 } 00398 00399 it = remove_if(levelnodes.begin(), levelnodes.end(), already_matched); 00400 levelnodes.erase(it, levelnodes.end()); 00401 00402 BinaryNode *child = NULL; 00403 00404 // High Priority: parent having degree = 1; 00405 child = getChildofDegreeNParent(levelnodes, 1); 00406 00407 if (!child) 00408 child = getChildofDegreeNParent(levelnodes, 2); 00409 00410 // Low Priority: parent having degree = 3; 00411 if (!child) 00412 child = getChildofDegreeNParent(levelnodes, 3); 00413 00414 return child; 00415 } 00416 00418 00419 void Tri2Quads::prunelevel(BNodeList &levelnodes) 00420 { 00421 while (1) { 00422 BinaryNode *currnode = getNextNode(levelnodes); 00423 if (currnode == NULL) break; 00424 matchnode(currnode); 00425 } 00426 } 00427 00429 00430 void Tri2Quads::percolateup() 00431 { 00432 steinerNodes.clear(); 00433 steinerFaces.clear(); 00434 00435 int height = btree->getHeight(); 00436 BNodeList levelnodes; 00437 BNodeList::const_iterator it; 00438 00439 //Reset all the Matching marks to 0; 00440 for (int i = 0; i < height; i++) { 00441 levelnodes = btree->getLevelNodes(height - i - 1); 00442 BinaryNode *currnode; 00443 for (it = levelnodes.begin(); it != levelnodes.end(); ++it) { 00444 currnode = *it; 00445 currnode->setMatchMark(0); 00446 } 00447 } 00448 00449 // Start Prunning the level. At most the root will be unmatched. 00450 for (int i = 0; i < height; i++) { 00451 levelnodes = btree->getLevelNodes(height - i - 1); 00452 prunelevel(levelnodes); 00453 } 00454 00455 size_t numfaces = trimesh->getSize(2); 00456 facematching.reserve(numfaces); 00457 00458 for (size_t i = 0; i < numfaces; i++) { 00459 Face *face = trimesh->getFaceAt(i); 00460 Vertex *u = NULL; 00461 face->getAttribute("DualNode", u); 00462 00463 assert(u); 00464 Vertex *v = NULL; 00465 u->getAttribute("DualMate", v); 00466 if (v) { 00467 if (v->getID() > u->getID()) { 00468 FacePair facepair; 00469 facepair.first = u->getID(); 00470 facepair.second = v->getID(); 00471 facematching.push_back(facepair); 00472 } 00473 } 00474 } 00475 00476 // If the root is unmatched, bring it down to a leaf and then split the 00477 // leaf. Do this step after the triangles have been matched. 00478 BinaryNode *root = btree->getRoot(); 00479 if (!root->isMatched()) { 00480 #ifdef VERBOSE 00481 cout << "Warning: Boundary Triangle modified " << endl; 00482 #endif 00483 Vertex *dnode = root->getDualNode(); 00484 Face *rootface = NULL; 00485 dnode->getAttribute("PrimalFace", rootface); 00486 refine_boundary_triangle(rootface); 00487 } 00488 00489 } 00490 00492 00493 void Tri2Quads::maximum_tree_matching() 00494 { 00495 // In order to insert any steiner point on the boundary triangle (at the root) 00496 // We should know which triangles and nodes are on the boundary. Therefore, 00497 // call this function to set the boundary flags. Building the relationship 00498 // at this stage is good as even the DualGraph construction require it. 00499 00500 trimesh->build_relations(0, 2); 00501 trimesh->search_boundary(); 00502 00503 #ifdef VERBOSE 00504 cout << " Creating Dual Graph ... " << endl; 00505 #endif 00506 00507 DualGraph *dgraph = new DualGraph; 00508 dgraph->build(trimesh); 00509 00510 if (verbose) 00511 cout << " Building Binary Tree of Dual Graph ... " << endl; 00512 00513 btree = new BinaryTree(dgraph); 00514 btree->build(); 00515 btree->saveAs("btree"); 00516 00517 #ifdef VERBOSE 00518 cout << " Tree Matching ... " << endl; 00519 #endif 00520 00521 percolateup(); 00522 00523 // Percolateup will remove relations, so it is necessary to clear all the 00524 // relations. 00525 trimesh->clear_relations(0, 2); 00526 00527 btree->deleteAll(); 00528 dgraph->deleteAll(); 00529 00530 delete btree; 00531 delete dgraph; 00532 } 00533 00535 00536 Mesh* Tri2Quads::getQuadMesh(Mesh *inmesh, int replace, int topo) 00537 { 00538 00539 #ifdef DEBUG 00540 if (inmesh->isHomogeneous() != 3) { 00541 cout << "Warning: Input mesh is not triangular " << endl; 00542 return NULL; 00543 } 00544 00545 if (!inmesh->isSimple()) { 00546 cout << "Warning: Input mesh is not simple, use edmonds' algorithm " << endl; 00547 return NULL; 00548 } 00549 00550 if (inmesh->getNumOfComponents() > 1) { 00551 cout << "Warning: There are multiple components in the mesh" << endl; 00552 cout << " Algorithm works for single component " << endl; 00553 return NULL; 00554 } 00555 00556 int euler0 = trimesh->getEulerCharacteristic(); 00557 cout << " Input Euler # : " << euler0 << endl; 00558 cout << inmesh->saveAs( "Check.dat"); 00559 #endif 00560 00561 trimesh = inmesh; 00562 00563 required_topology = topo; 00564 00565 trimesh->enumerate(2); 00566 maxfaceID = trimesh->getSize(2) - 1; 00567 00569 // Generate Maximum Matching on a binary tree using Suneeta's Algorithm. 00570 // If the required topology is set to ALL_QUADS, steiner points( and new 00571 // faces) will be inserted in the input triangle mesh. Please note that 00572 // this implementation doesn't produces "The" optimal soluation as 00573 // described in the original papers, and doesn't even guarantee that 00574 // the resulting quadrilaterals will be convex. This along with other 00575 // topological and geometric optimization are anyhow essential and 00576 // are carried out during the "Post Processing" step. Therefore, we 00577 // have sacrifised performance over quality in this implementation. 00578 // Roughly we can expect that about 4-5% steiner points are inserted in 00579 // most of the general cases. 00581 StopWatch swatch; 00582 swatch.start(); 00583 00584 maximum_tree_matching(); 00585 Mesh *quadmesh = collapse_matched_triangles(trimesh, facematching, replace); 00586 swatch.stop(); 00587 cout << "Info: Tri->Quad Elapsed Time " << swatch.getSeconds() << endl; 00588 00589 if( quadmesh ) { 00590 if (!quadmesh->isSimple()) 00591 cout << "Warning: Quadrilateral Mesh is not simple " << endl; 00592 00593 quadmesh->enumerate(2); 00594 if (!quadmesh->is_consistently_oriented()) { 00595 cout << "Warning:Trying to make Quadrilateal Mesh consistently oriented " << endl; 00596 quadmesh->make_consistently_oriented(); 00597 if (quadmesh->is_consistently_oriented()) 00598 cout << "Info: Quadrilateral Mesh is now consistently oriented: Very good " << endl; 00599 else 00600 cout << "Alas ! Quadrilateral Mesh is still inconsistently oriented: Check manually " << endl; 00601 } 00602 00603 quadmesh->enumerate(0); 00604 quadmesh->enumerate(2); 00605 } 00606 00608 // Since Steiner points may be inserted in the mesh ( and new triangles). 00609 // Renumber all the nodes and faces for future processing. 00611 00612 trimesh->enumerate(0); 00613 trimesh->enumerate(2); 00614 00615 return quadmesh; 00616 } 00617 00619 00620 void Tri2Quads::match_tree_walk(BinaryTree *btree, BinaryNode *parent) 00621 { 00622 // 00623 // Brings all the internal unmatched nodes at the leaf. 00624 // 00625 if (parent == NULL) 00626 return; 00627 if (parent->isLeaf()) 00628 return; 00629 00630 int numChildren = parent->getNumChildren(); 00631 00632 for (int i = 0; i < numChildren; i++) { 00633 BinaryNode *child1 = parent->getChild(i); 00634 if (!btree->isMatched(parent, child1)) { 00635 int numGrandChildren = child1->getNumChildren(); 00636 for (int j = 0; j < numGrandChildren; j++) { 00637 BinaryNode *child2 = child1->getChild(j); 00638 if (btree->isMatched(child1, child2)) { 00639 Vertex *np = parent->getDualNode(); 00640 assert(np); 00641 Vertex *c1 = child1->getDualNode(); 00642 assert(c1); 00643 Vertex *c2 = child2->getDualNode(); 00644 assert(c2); 00645 matchnodes(np, c1); 00646 c2->setAttribute("DualMate", 0); 00647 c2->setStatus(MeshEntity::ACTIVE); 00648 match_tree_walk(btree, child2); 00649 return; 00650 } 00651 } 00652 } 00653 } 00654 00655 } 00656