MeshKit  1.0
Tri2Quad.cpp
Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines