MeshKit  1.0
QslimDecimation.cpp
Go to the documentation of this file.
00001 /*
00002  * QslimDecimation.cpp
00003  *
00004  *  Created on: Mar 10, 2010
00005  *      Author: iulian
00006  */
00007 
00008 #include <assert.h>
00009 #include "QslimDecimation.hpp"
00010 
00011 // for proximity searches
00012 #include "moab/AdaptiveKDTree.hpp"
00013 #include "moab/ReadUtilIface.hpp"
00014 
00015 #include "Mat4.h"
00016 #include "defs.h"
00017 #include "quadrics.h"
00018 #include <time.h>
00019 #include <map>
00020 
00021 // those are used in model navigation/simplification
00022 #include "primitives.h"
00023 
00024 // this is the global thing, that everybody will use
00025 moab::Interface * mb;
00026 moab::Tag uniqIDtag; // this will be used to mark vertices moab::EntityHandles
00027 moab::Tag validTag;
00028 
00029 moab::Tag costTag; // simplification induces an error cost at each vertex
00030 // try to keep adding the cost, to see if it is spreading nicely
00031 
00032 // this will be used to store plane data for each triangle, as 4 doubles
00033 // will be updated only if needed ( -m option == opts.will_preserve_mesh_quality)
00034 moab::Tag planeDataTag;
00035 
00036 moab::Range verts; // original list of vertices, that are part of the original triangles
00037 moab::Range triangles;
00038 moab::Range edgs;
00039 QslimOptions opts; // external
00040 moab::EntityHandle iniSet;
00041 
00042 int uniqID(moab::EntityHandle v) {
00043   int val;
00044   moab::ErrorCode rval = mb->tag_get_data(uniqIDtag, &v, 1, &val);
00045   assert(rval==moab::MB_SUCCESS);
00046   return val;
00047 }
00048 // the vertices are not deleted anymore, just invalidated
00049 // the edges are deleted, though, and triangles
00050 int ehIsValid(moab::EntityHandle v) {
00051   unsigned char val;
00052   moab::ErrorCode rval = mb->tag_get_data(validTag, &v, 1, &val);
00053   assert(rval==moab::MB_SUCCESS);
00054   return (int) val;
00055 }
00056 
00057 // include here the main classes used for decimation
00058 
00059 #include "Heap.hpp"
00060 // prox grid is used for proximity grid only
00061 //#include "ProxGrid.h"
00062 
00063 class pair_info: public Heapable {
00064 public:
00065   moab::EntityHandle v0, v1; // Vertex *v0, *v1;
00066 
00067   Vec3 candidate;
00068   double cost;
00069 
00070   //pair_info ( Vertex *a,Vertex *b ) { v0=a; v1=b; cost=HUGE; }
00071   pair_info(moab::EntityHandle a, moab::EntityHandle b) {
00072     v0 = a;
00073     v1 = b;
00074     cost = HUGE;
00075   }
00076 
00077   bool isValid() {
00078     return ehIsValid(v0) && ehIsValid(v1);
00079   }//  :v0->isValid() && v1->isValid(); }
00080 };
00081 
00082 typedef buffer<pair_info *> pair_buffer;
00083 
00084 class vert_info {
00085 public:
00086 
00087   pair_buffer pairs;
00088 
00089   Mat4 Q;
00090   double norm;
00091 
00092   vert_info() :
00093     Q(Mat4::zero) {
00094     pairs.init(2);
00095     norm = 0.0;
00096   }
00097 };
00098 
00099 // these are
00100 static Heap *heap;
00101 static array<vert_info> vinfo;
00102 static double proximity_limit; // distance threshold squared
00103 
00104 int validFaceCount;
00105 int validVertCount;
00106 
00108 //
00109 // Low-level routines for manipulating pairs
00110 //
00111 
00112 static inline vert_info& vertex_info(moab::EntityHandle v)//Vertex *v )
00113 {
00114   //  moab::EntityHandle should return an integer tag with an
00115   //  index in the big array of vert_info
00116   //   something like: return tag
00117   //   for the time being, we can return the simple id...
00118   return vinfo(uniqID(v));
00119 }
00120 
00121 static
00122 bool check_for_pair(moab::EntityHandle v0, moab::EntityHandle v1)//Vertex *v0, Vertex *v1 )
00123 {
00124   const pair_buffer& pairs = vertex_info(v0).pairs;
00125 
00126   for (int i = 0; i < pairs.length(); i++) {
00127     if (pairs(i)->v0 == v1 || pairs(i)->v1 == v1)
00128       return true;
00129   }
00130 
00131   return false;
00132 }
00133 
00134 static pair_info *new_pair(moab::EntityHandle v0, moab::EntityHandle v1)//  Vertex *v0, Vertex *v1 )
00135 {
00136   vert_info& v0_info = vertex_info(v0);
00137   vert_info& v1_info = vertex_info(v1);
00138 
00139   pair_info *pair = new pair_info(v0, v1);
00140   v0_info.pairs.add(pair);
00141   v1_info.pairs.add(pair);
00142 
00143   return pair;
00144 }
00145 
00146 static
00147 void delete_pair(pair_info *pair) {
00148   vert_info& v0_info = vertex_info(pair->v0);
00149   vert_info& v1_info = vertex_info(pair->v1);
00150 
00151   v0_info.pairs.remove(v0_info.pairs.find(pair));
00152   v1_info.pairs.remove(v1_info.pairs.find(pair));
00153 
00154   if (pair->isInHeap())
00155     heap->kill(pair->getHeapPos());
00156 
00157   delete pair;
00158 }
00159 
00161 //
00162 // The actual guts of the algorithm:
00163 //
00164 //     - pair_is_valid
00165 //     - compute_pair_info
00166 //     - do_contract
00167 //
00168 /*
00169 static
00170 bool pair_is_valid(moab::EntityHandle u, moab::EntityHandle v)// Vertex *u, Vertex *v )
00171 {
00172   //
00173   Vec3 vu = getVec3FromMBVertex(mb, u);
00174   Vec3 vv = getVec3FromMBVertex(mb, v);
00175   return norm2(vu - vv) < proximity_limit;
00176   //return  norm2 ( *u - *v ) < proximity_limit;
00177 }*/
00178 
00179 static
00180 int predict_face(moab::EntityHandle tria, moab::EntityHandle v1,
00181     moab::EntityHandle v2, /*Face& F, Vertex *v1, Vertex *v2,*/
00182     Vec3& vnew, Vec3& f1, Vec3& f2, Vec3& f3) {
00183   int nmapped = 0;
00184   const moab::EntityHandle * conn;
00185   int num_nodes;
00186   moab::ErrorCode rval = mb->get_connectivity(tria, conn, num_nodes);
00187   assert(3==num_nodes && rval == moab::MB_SUCCESS);
00188   if (conn[0] == v1 || conn[0] == v2) {
00189     f1 = vnew;
00190     nmapped++;
00191   } else
00192     f1 = getVec3FromMBVertex(mb, conn[0]);
00193 
00194   if (conn[1] == v1 || conn[1] == v2) {
00195     f2 = vnew;
00196     nmapped++;
00197   } else
00198     f2 = getVec3FromMBVertex(mb, conn[1]);
00199 
00200   if (conn[2] == v1 || conn[2] == v2) {
00201     f3 = vnew;
00202     nmapped++;
00203   } else
00204     f3 = getVec3FromMBVertex(mb, conn[2]);
00205 
00206   // find vertices in face tria
00207   /*
00208    if ( F.vertex ( 0 ) == v1 || F.vertex ( 0 ) == v2 )
00209    { f1 = vnew;  nmapped++; }
00210    else f1 = *F.vertex ( 0 );
00211 
00212    if ( F.vertex ( 1 ) == v1 || F.vertex ( 1 ) == v2 )
00213    { f2 = vnew;  nmapped++; }
00214    else f2 = *F.vertex ( 1 );
00215 
00216    if ( F.vertex ( 2 ) == v1 || F.vertex ( 2 ) == v2 )
00217    { f3 = vnew;  nmapped++; }
00218    else f3 = *F.vertex ( 2 );
00219    */
00220   return nmapped;
00221 }
00222 
00223 #define MESH_INVERSION_PENALTY 1e9
00224 
00225 static
00226 double pair_mesh_positivity(/* Model& M,*/moab::EntityHandle v1,
00227     moab::EntityHandle v2, /*Vertex *v1, Vertex *v2,*/
00228     Vec3& vnew) {
00229   std::vector<moab::EntityHandle> changed;
00230 
00231   // :  construct the list of faces influenced by the
00232   //   moving of vertices v1 and v2 into vnew
00233   //M.contractionRegion ( v1, v2, changed );
00234   moab::ErrorCode rval = contractionRegion(mb, v1, v2, changed);
00235   if (rval != moab::MB_SUCCESS) {
00236     std::cout << "error in getting adjacency information vs: "
00237         << mb->id_from_handle(v1) << " " << mb->id_from_handle(v2) << "\n";
00238   }
00239 
00240   // double Nsum = 0;
00241   if (opts.logfile && opts.selected_output & OUTPUT_CONTRACTIONS)
00242     *opts.logfile << " positivity for v1, v2: " << mb->id_from_handle(v1)
00243         << " " << mb->id_from_handle(v2) << std::endl;
00244 
00245   for (unsigned int i = 0; i < changed.size(); i++) {
00246     //Face& F = *changed ( i );
00247     moab::EntityHandle F = changed[i];
00248     Vec3 f1, f2, f3;
00249 
00250     int nmapped = predict_face(F, v1, v2, vnew, f1, f2, f3);
00251 
00252     //
00253     // Only consider non-degenerate faces
00254     if (nmapped < 2) {
00255       //Plane Pnew ( f1, f2, f3 );
00256 #if 0
00257       Vec3 normalNew = Pnew.normal();
00258       if ( normalNew[Z] < positivityMin )
00259       positivityMin=normalNew[Z]; // Z direction!!!
00260       if (opts.logfile && opts.selected_output&OUTPUT_CONTRACTIONS )
00261       *opts.logfile << "Triangle " << mb->id_from_handle(F)
00262       << " nmapped " << nmapped << std::endl;
00263       if (opts.logfile && positivityMin<=0 && opts.selected_output&OUTPUT_CONTRACTIONS )
00264       *opts.logfile << "Triangle " << mb->id_from_handle(F)
00265       << " normal Z:" << normalNew[Z] << std::endl;
00266 #endif
00267       double positiv = (f2[0] - f1[0]) * (f3[1] - f1[1]) - (f2[1] - f1[1])
00268           * (f3[0] - f1[0]);
00269       if (positiv <= 0) {
00270         if (opts.logfile && opts.selected_output & OUTPUT_CONTRACTIONS)
00271           *opts.logfile << "Triangle " << mb->id_from_handle(F) << " nmapped "
00272               << nmapped << " orient: " << positiv << std::endl;
00273         return MESH_INVERSION_PENALTY * 10;
00274       }
00275     }
00276   }
00277 
00278   //return (-Nmin) * MESH_INVERSION_PENALTY;
00279 
00280   return 0.0;
00281 }
00282 
00283 // will make sure that no hole would be closed
00284 /*
00285  * will accomplish that by looking at the edges connected to both vertices
00286  * if there would be edges that would merge without a connecting triangle,
00287  * it would increase the cost (penalize it harshly)
00288  */
00289 static double pair_mesh_topology(moab::EntityHandle v1, moab::EntityHandle v2)
00290 {
00291   // first, find nodes v3 that are connected to both v1 and v2 by edges
00292   // if there is no triangle that is formed with v1, v2, v3, it means it would
00293   //  collapse a hole
00294   std::vector<moab::EntityHandle> edges1, edges2;
00295   moab::Range nodes1, nodes2;
00296   moab::ErrorCode rval = mb->get_adjacencies(&v1, 1, 1, false, edges1);
00297   assert (moab::MB_SUCCESS == rval);
00298   filterValid(mb, edges1);
00299   rval = mb->get_adjacencies(&v2, 1, 1, false, edges2);
00300   assert (moab::MB_SUCCESS == rval);
00301   filterValid(mb, edges2);
00302   rval = mb->get_connectivity(&edges1[0], edges1.size(), nodes1);
00303   assert (moab::MB_SUCCESS == rval);
00304   rval = mb->get_connectivity(&edges2[0], edges2.size(), nodes2);
00305   assert (moab::MB_SUCCESS == rval);
00306   moab::Range commonV=intersect (nodes1, nodes2);
00307   moab::Range v12;
00308   v12.insert(v1); v12.insert(v2);
00309   moab::Range leftOver = subtract(commonV, v12);
00310   for (moab::Range::iterator it = leftOver.begin(); it!=leftOver.end(); it++)
00311   {
00312     moab::EntityHandle thirdNode = *it;
00313     if (!ehIsValid(thirdNode))
00314       continue;
00315     // the moment we find a triple v1, v2, thirdNode that is not a triangle, return penalty
00316     moab::Range triple=v12;
00317     triple.insert(thirdNode);
00318     moab::Range connTris;
00319     rval = mb->get_adjacencies(triple, 2, false, connTris );
00320     if (moab::MB_SUCCESS == rval)
00321     {
00322       if (connTris.empty())
00323         return MESH_INVERSION_PENALTY; // this means that there are no
00325                                        // so it would close a hole/tunnel
00326 
00327       int noValidTris = 0;
00328       for (moab::Range::iterator it2 = connTris.begin(); it2!=connTris.end(); it2++)
00329       {
00330         if (ehIsValid(*it2))
00331         {
00332           noValidTris++;
00333           break;
00334         }
00335       }
00336       if (0==noValidTris)
00337         return MESH_INVERSION_PENALTY; // this means that there are no
00338                                        // triangles connected to the 3 nodes
00339                                        // so it would close a hole/tunnel
00340 
00341     }
00342 
00343   }
00344   return 0.; // no penalty
00345 }
00346 static
00347 double pair_mesh_penalty( /*Model& M, Vertex *v1, Vertex *v2,*/
00348 moab::EntityHandle v1, moab::EntityHandle v2, Vec3& vnew) {
00349   std::vector<moab::EntityHandle> changed;
00350 
00351   //   construct the list of faces influenced by the
00352   //   moving of vertices v1 and v2 into vnew
00353   //M.contractionRegion ( v1, v2, changed );
00354   moab::ErrorCode rval = contractionRegion(mb, v1, v2, changed);
00355   if (rval != moab::MB_SUCCESS) {
00356     std::cout << "error in getting adjacency information vs: "
00357         << mb->id_from_handle(v1) << " " << mb->id_from_handle(v2) << "\n";
00358   }
00359 
00360   // double Nsum = 0;
00361   double Nmin = 0;
00362 
00363   for (unsigned int i = 0; i < changed.size(); i++) {
00364     //Face& F = *changed ( i );
00365     moab::EntityHandle F = changed[i];
00366     Vec3 f1, f2, f3;
00367 
00368     int nmapped = predict_face(F, v1, v2, vnew, f1, f2, f3);
00369     //
00370     // Only consider non-degenerate faces
00371     if (nmapped < 2) {
00372       Plane Pnew(f1, f2, f3);
00373       Plane p = trianglePlane(mb, F);
00374       double delta = Pnew.normal() * p.normal(); //  Pnew.normal() * F.plane().normal();
00375 
00376       if (Nmin > delta)
00377         Nmin = delta;
00378     }
00379   }
00380 
00381   //return (-Nmin) * MESH_INVERSION_PENALTY;
00382   if (Nmin < 0.0)
00383     return MESH_INVERSION_PENALTY;
00384   else
00385     return 0.0;
00386 }
00387 
00388 static
00389 void compute_pair_info(pair_info *pair /* Model * pM0,*/) {
00390   moab::EntityHandle v0 = pair->v0;
00391   moab::EntityHandle v1 = pair->v1;
00392 
00393   // Vertex *v0 = pair->v0;
00394   // Vertex *v1 = pair->v1;
00395 
00396   vert_info& v0_info = vertex_info(v0);
00397   vert_info& v1_info = vertex_info(v1);
00398 
00399   Mat4 Q = v0_info.Q + v1_info.Q;
00400   double norm = v0_info.norm + v1_info.norm;
00401 
00402   pair->cost = quadrix_pair_target(Q, v0, v1, pair->candidate);
00403 
00404   if (opts.will_weight_by_area)
00405     pair->cost /= norm;
00406 
00407   if (opts.will_preserve_mesh_quality)
00408     pair->cost += pair_mesh_penalty(/* *pM0,*/v0, v1, pair->candidate);
00409 
00410   if (opts.height_fields)
00411     pair->cost += pair_mesh_positivity(/* *pM0, */v0, v1, pair->candidate);
00412 
00413   if (opts.topology)
00414     pair->cost += pair_mesh_topology(v0, v1);
00415 
00416   if (opts.logfile && opts.selected_output & OUTPUT_CONTRACTIONS)
00417 
00418     *opts.logfile << "pair ( v" << mb->id_from_handle(v0) << " v"
00419         << mb->id_from_handle(v1) << " ) cost: " << -pair->cost << std::endl;
00420   //
00421   // NOTICE:  In the heap we use the negative cost.  That's because
00422   //          the heap is implemented as a MAX heap.
00423   //
00424   if (pair->isInHeap()) {
00425     heap->update(pair, -pair->cost);
00426   } else {
00427     heap->insert(pair, -pair->cost);
00428   }
00429   if (opts.logfile && opts.selected_output & OUTPUT_COST) {
00430     heap_node *top = heap->top();
00431     pair_info *pairTop = (pair_info *) top->obj;
00432     *opts.logfile << " i/u pair (" << uniqID(pair->v0) << "," << uniqID(
00433         pair->v1) << ")=" << pair->cost << "  min : (" << uniqID(pairTop->v0)
00434         << "," << uniqID(pairTop->v1) << ") " << pairTop->cost << std::endl;
00435   }
00436 }
00437 void recomputeChangedPairsCost(std::vector<moab::EntityHandle> & changed, moab::EntityHandle v0) {
00438   //
00439   for (unsigned int i = 0; i < changed.size(); i++) {
00440 
00441     moab::EntityHandle F = changed[i];
00442     const moab::EntityHandle * conn;
00443     int num_nodes;
00444     mb->get_connectivity(F, conn, num_nodes);
00445     //if (!F->isValid())
00446     //   continue;
00447     // recompute the pair that is not connected to vertex v0
00448     // loop over all the vertices of F that are not v0, and recompute the costs
00449     // of all the pairs associated  that do not contain v0
00450     // we do not have to recreate or delete any pair, we just recompute what we have
00451     // some will be recomputed 2 times, but it is OK
00452     for (int k = 0; k < 3; k++) {
00453       moab::EntityHandle v = conn[k];
00454       if (v == v0)
00455         continue;
00456       vert_info & v_info = vertex_info(v);
00457       for (int j = 0; j < v_info.pairs.length(); j++) {
00458         pair_info *p = v_info.pairs(j);
00459         if (p->v0 == v0 || p->v1 == v0)
00460           continue; // do not recompute cost of pairs already computed
00461         if (opts.logfile && (opts.selected_output & OUTPUT_COST))
00462           *opts.logfile << "recompute cost of pair (v" << uniqID(p->v0) + 1
00463               << " v" << uniqID(p->v1) + 1 << ")" << std::endl;
00464         compute_pair_info(p);
00465       }
00466 
00467     }
00468 
00469   }
00470 }
00471 
00472 static
00473 void do_contract(pair_info *pair) {
00474 
00475   moab::EntityHandle v0 = pair->v0;
00476   moab::EntityHandle v1 = pair->v1;
00477   // cost of contraction is accumulated at v0
00478   double costToContract = pair->cost;
00479   vert_info& v0_info = vertex_info(v0);
00480   vert_info& v1_info = vertex_info(v1);
00481   Vec3 vnew = pair->candidate;
00482   if (opts.logfile && (opts.selected_output & OUTPUT_COST)) {
00483     *opts.logfile << "---- do contract v0:" << uniqID(v0) + 1 << " v1:"
00484         << uniqID(v1) + 1 << std::endl;
00485   }
00486   int i;
00487 
00488   //
00489   // Make v0 be the new vertex
00490   v0_info.Q += v1_info.Q;
00491   v0_info.norm += v1_info.norm;
00492 
00493   //
00494   // Perform the actual contraction
00495   std::vector<moab::EntityHandle> changed;
00496   moab::ErrorCode rval1 = contract(mb, v0, v1, vnew, changed);
00497   // this is a list of triangles still connected to v0 (are they valid? probably)
00498   if (opts.will_preserve_mesh_quality) {
00499     // recompute normals only in this case, because they are not needed otherwise
00500     int size = changed.size();
00501     for (int i = 0; i < size; i++) {
00502       computeTrianglePlane(mb, changed[i]);
00503     }
00504   }
00505   assert (moab::MB_SUCCESS == rval1 || moab::MB_MULTIPLE_ENTITIES_FOUND == rval1);
00506 
00507 #ifdef SUPPORT_VCOLOR
00508   //
00509   // If the vertices are colored, color the new vertex
00510   // using the average of the old colors.
00511   v0->props->color += v1->props->color;
00512   v0->props->color /= 2;
00513 #endif
00514 
00515   //
00516   // Remove the pair that we just contracted
00517   delete_pair(pair);
00518   //
00519   // Recalculate pairs associated with v0
00520   for (i = 0; i < v0_info.pairs.length(); i++) {
00521     pair_info *p = v0_info.pairs(i);
00522     compute_pair_info(p);
00523   }
00524 
00525   //
00526   // Process pairs associated with now dead vertex
00527 
00528   static pair_buffer condemned(6); // collect condemned pairs for execution
00529   condemned.reset();
00530 
00531   for (i = 0; i < v1_info.pairs.length(); i++) {
00532     pair_info *p = v1_info.pairs(i);
00533 
00534     moab::EntityHandle u = 0L;
00535     if (p->v0 == v1)
00536       u = p->v1;
00537     else if (p->v1 == v1)
00538       u = p->v0;
00539     else
00540       std::cerr << "YOW!  This is a bogus pair." << std::endl;
00541 
00542     if (!check_for_pair(u, v0)) {
00543       p->v0 = v0;
00544       p->v1 = u;
00545       v0_info.pairs.add(p);
00546       compute_pair_info(p);
00547     } else
00548       condemned.add(p);
00549   }
00550 
00551   for (i = 0; i < condemned.length(); i++)
00552     // Do you have any last requests?
00553     delete_pair(condemned(i));
00554   // only now you can delete the vertex v1 from database
00555   // moab::ErrorCode rval = mb->delete_entities(&v1, 1);
00556   // no, it is better to invalidate the vertex, do not delete it
00557   // maybe we will delete at the end all that are invalid ??
00558   int invalid = 0;
00559   moab::ErrorCode rval = mb->tag_set_data(validTag, &v1, 1, &invalid);
00560   assert (moab::MB_SUCCESS == rval);
00561 
00562   if (opts.plotCost) {
00563     double cost_at_v0 = 0; // maybe it is already set before
00564     rval = mb->tag_get_data(costTag, &v0, 1, &cost_at_v0);
00565     cost_at_v0 += costToContract;
00566     rval = mb->tag_set_data(costTag, &v0, 1, &cost_at_v0);
00567   }
00568 
00569   v1_info.pairs.reset(); // safety precaution
00570   recomputeChangedPairsCost(changed, v0);
00571 
00572 }
00573 
00575 //
00576 // External interface: setup and single step iteration
00577 //
00578 
00579 bool decimate_quadric(moab::EntityHandle v, Mat4& Q) {
00580   if (vinfo.length() > 0) {
00581     vert_info & vinf = vertex_info(v);
00582     Q = vinf.Q;
00583     return true;
00584   } else
00585     return false;
00586 }
00587 
00588 // it is assumed it is mb, moab interface
00589 void decimate_contract() {
00590   heap_node *top;
00591   pair_info *pair;
00592 
00593   for (;;) {
00594     top = heap->extract();
00595     if (!top)
00596       return;
00597     pair = (pair_info *) top->obj;
00598 
00599     //
00600     // This may or may not be necessary.  I'm just not quite
00601     // willing to assume that all the junk has been removed from the
00602     // heap.
00603     if (pair->isValid())
00604       break;
00605 
00606     delete_pair(pair);
00607   }
00608 
00609   if (opts.logfile && (opts.selected_output & OUTPUT_COST))
00610     *opts.logfile << "#$cost " << validFaceCount << " before contract: "
00611         << pair->cost << std::endl;
00612 
00613   do_contract(pair);
00614 
00615   if (opts.logfile && (opts.selected_output & OUTPUT_COST))
00616     *opts.logfile << "#$cost " << validFaceCount << std::endl;
00617 
00618   validVertCount--; // Attempt to maintain valid vertex information
00619 }
00620 
00621 double decimate_error(moab::EntityHandle v) {
00622   vert_info& info = vertex_info(v);
00623 
00624   double err = quadrix_evaluate_vertex(v, info.Q);
00625 
00626   if (opts.will_weight_by_area)
00627     err /= info.norm;
00628 
00629   return err;
00630 }
00631 
00632 double decimate_min_error() {
00633   heap_node *top;
00634   pair_info *pair;
00635 
00636   for (;;) {
00637     top = heap->top();
00638     if (!top)
00639       return -1.0;
00640     pair = (pair_info *) top->obj;
00641 
00642     if (pair->isValid())
00643       break;
00644 
00645     top = heap->extract();
00646     delete_pair(pair);
00647   }
00648 
00649   return pair->cost;
00650 }
00651 #if 0
00652 double decimate_max_error ( )
00653 {
00654   real max_err = 0;
00655 
00656   for ( int i=0; i<m.vertCount(); i++ )
00657   if ( m.vertex ( i )->isValid() )
00658   {
00659     max_err = MAX ( max_err, decimate_error ( m.vertex ( i ) ) );
00660   }
00661 
00662   return max_err;
00663 }
00664 #endif
00665 namespace MeshKit {
00666 
00667 QslimDecimation::QslimDecimation(moab::Interface * mbi,
00668     moab::EntityHandle root_set) {
00669   _mb = mbi;
00670   iniSet = root_set;// it is not necessarily the root set; this is external; bad design
00671 }
00672 
00673 QslimDecimation::~QslimDecimation() {
00674 }
00675 int QslimDecimation::decimate(QslimOptions & iOpts, moab::Range & oRange) {
00676   // opts is external
00677   opts = iOpts;
00678 
00679   mb = _mb; // (reinterpret_cast<MBiMesh *> (m_mesh))->mbImpl;
00680   // need to get all the triangles from the set
00681   // also all the edges, and all vertices
00682   // not  a good design here; mb is extern !
00683   //
00684   if (NULL == mb)
00685     return 1;// error
00686   //moab::EntityHandle mbSet = reinterpret_cast<moab::EntityHandle>(m_InitialSet);
00687 
00688   clock_t start_time = clock();
00689   int err = Init();
00690   if (err) {
00691     std::cerr << "Error in initialization of decimation. Abort\n";
00692     return 1;
00693   }
00694   clock_t init_time = clock();
00695   std::cout << "   Initialization  " << (double) (init_time - start_time)
00696       / CLOCKS_PER_SEC << " s.\n";
00697 
00698   int faces_reduction = validFaceCount - opts.face_target;
00699   int counter = 0, interval = 0;
00700   clock_t currentTime = init_time;
00701   while (validFaceCount > opts.face_target && decimate_min_error()
00702       < opts.error_tolerance) {
00703     int initf = validFaceCount;
00704     // big routine
00705     decimate_contract();
00706     counter += (initf - validFaceCount);
00707     if (counter > faces_reduction / opts.timingIntervals) {
00708       // print some time stats
00709       clock_t p10_time = clock();
00710       std::cout << "     " << ++interval << "/" << opts.timingIntervals
00711           << " reduce to " << validFaceCount << " faces in "
00712           << (double) (p10_time - currentTime) / CLOCKS_PER_SEC << " s, total:"
00713           << (double) (p10_time - init_time) / CLOCKS_PER_SEC << " s.\n";
00714       counter = 0;
00715       currentTime = p10_time;
00716     }
00717   }
00718 
00719   clock_t finish_time = clock();
00720   std::cout << "   Decimation: " << (double) (finish_time - init_time)
00721       / CLOCKS_PER_SEC << " s.\n";
00722 
00723   if (opts.create_range) {
00724     // the remaining nodes and triangles are copied in the range
00725     // they are put in the old set, too
00726     // maybe this has to change
00727     // count first valid vertices and triangles
00728     moab::Range::iterator it;
00729     std::vector<moab::EntityHandle> validVerts;
00730     std::vector<moab::EntityHandle> validTris;
00731     for (it = triangles.begin(); it != triangles.end(); it++) {
00732       if (ehIsValid(*it))
00733         validTris.push_back(*it);
00734     }
00735     for (it = verts.begin(); it != verts.end(); it++) {
00736       if (ehIsValid(*it))
00737         validVerts.push_back(*it);
00738     }
00739     //
00740     std::vector<double> coords;
00741     int numNodes = (int) validVerts.size();
00742     int numTriangles = (int) validTris.size();
00743     coords.resize(3 * numNodes);
00744 
00745     moab::ErrorCode rval = mb->get_coords(&(validVerts[0]), numNodes,
00746         &(coords[0]));
00747     assert(moab::MB_SUCCESS==rval);
00748 
00749     // create the new vertices, at the same  coordinates
00750     // put those verts in the range that is output
00751 
00752     // to make sure, the range is cleared
00753     oRange.clear();
00754     rval = mb->create_vertices(&coords[0], numNodes, oRange);
00755     assert(moab::MB_SUCCESS==rval);
00756     // the first element in the range must be the start of the new vertex sequence
00757     std::map<moab::EntityHandle, moab::EntityHandle> mapping; // this will be from old
00758     //  to new vertices, for connectivity
00759     for (int i = 0; i < numNodes; i++) {
00760       mapping[validVerts[i]] = oRange[i];
00761     }
00762 
00763     //get the query interface, which we will use to create the triangles directly
00764     moab::ReadUtilIface *iface;
00765     rval = mb -> query_interface(iface);// use the new query interface
00766     assert(moab::MB_SUCCESS==rval);
00767 
00768     //create the triangles, get a direct ptr to connectivity
00769     moab::EntityHandle starth, *connect;
00770     rval = iface -> get_element_connect(numTriangles, 3, moab::MBTRI, 1,
00771         starth, connect);
00772     assert(moab::MB_SUCCESS==rval);
00773     // get first the connectivity of the old triangles
00774     const moab::EntityHandle * conn3;
00775     for (int i = 0; i < numTriangles; i++) {
00776       int num_nodes;
00777       // get each valid triangle one by one, and set the new connectivity directly
00778       rval = mb->get_connectivity(validTris[i], conn3, num_nodes);
00779       assert( (moab::MB_SUCCESS==rval) && (num_nodes==3));
00780       // directly modify the connect array in database
00781       for (int j = 0; j < 3; j++)
00782         connect[j] = mapping[conn3[j]];
00783 
00784       // update adjacencies
00785       // not very smart...; we would like to update once and for all triangles
00786       // not in a loop
00787       rval = iface -> update_adjacencies(starth+i, 1, 3, connect);
00788       assert(moab::MB_SUCCESS==rval);
00789 
00790       connect += 3; // advance
00791     }
00792 
00793 
00794 
00795     // clear completely the initial set, after deleting all elements from it...
00796     //   ok, we are done, commit to ME ?
00797     rval = mb->delete_entities(triangles);
00798     assert(moab::MB_SUCCESS==rval);
00799     rval = mb->delete_entities(edgs);
00800     assert(moab::MB_SUCCESS==rval);
00801     rval = mb->delete_entities(verts);
00802     assert(moab::MB_SUCCESS==rval);
00803     // remove everything from the initial set, because we will add the new triangles
00804     mb->remove_entities(iniSet, triangles);
00805     mb->remove_entities(iniSet, verts);
00806     mb->remove_entities(iniSet, edgs);
00807 
00808     //add triangles to output range (for the MESelection)
00809     oRange.insert(starth, starth + numTriangles - 1);
00810     // add all entities from the range to the initial set, now
00811     rval = mb->add_entities(iniSet, oRange);
00812     assert(moab::MB_SUCCESS==rval);
00813     //me->commit_mesh(mit->second, COMPLETE_MESH);
00814     // end copy
00815   } else {
00816     moab::Range::const_reverse_iterator rit;
00817     if (opts.useDelayedDeletion) {
00818 
00819       // put in a range triangles to delete
00820       moab::Range delRange;
00821       // delete triangles and edges that are invalid
00822       for (rit = triangles.rbegin(); rit != triangles.rend(); rit++) {
00823         moab::EntityHandle tr = *rit;
00824         // check the validity
00825         if (ehIsValid(tr))
00826           continue;
00827         mb->delete_entities(&tr, 1);
00828         delRange.insert(tr);
00829       }
00830       mb->remove_entities(iniSet, delRange);
00831       // maybe we should delete all edges, but for now, we will keep them
00832       for (rit = edgs.rbegin(); rit != edgs.rend(); rit++) {
00833         moab::EntityHandle v = *rit;
00834         // check the validity
00835         if (ehIsValid(v))
00836           continue;
00837         mb->delete_entities(&v, 1);
00838       }
00839 
00840     }
00841     // delete them one by one
00842     for (rit = verts.rbegin(); rit != verts.rend(); rit++) {
00843       moab::EntityHandle v = *rit;
00844       // check the validity
00845       if (ehIsValid(v))
00846         continue;
00847       mb->delete_entities(&v, 1);
00848     }
00849   }
00850   clock_t delete_vTime = clock();
00851   std::cout << "   Delete Vertices: " << (double) (delete_vTime - finish_time)
00852       / CLOCKS_PER_SEC << " s.\n";
00853   // we need to delete the tags we created; they are artificial
00854   // list of tags to delete:
00855   // moab::Tag uniqIDtag; // this will be used to mark vertices moab::EntityHandles
00856   // moab::Tag validTag;
00857   // moab::Tag planeDataTag;
00858 
00859   // moab::Tag costTag; // simplification induces an error cost at each vertex
00860   // try to keep adding the cost, to see if it is spreading nicely
00861 
00862   // keep only the cost, the rest are artificial
00863   mb->tag_delete(uniqIDtag);
00864   mb->tag_delete(validTag);
00865   mb->tag_delete(planeDataTag);
00866   //
00867 
00868   return 0;
00869 }
00870 
00871 int QslimDecimation::Init() {
00872   int i;
00873   unsigned int j;
00874 
00875   //moab::EntityHandle * set = reinterpret_cast<moab::EntityHandle *> (&_InitialSet);
00876   moab::ErrorCode rval = mb->get_entities_by_type(iniSet, moab::MBTRI,
00877       triangles);
00878   validFaceCount = triangles.size();// this gets reduced every time we simplify the model
00879 
00880   // store the normals/planes computed at each triangle
00881   // we may need just the normals, but compute planes, it is about the same job
00882   double defPlane[] = { 0., 0., 1., 0. };
00883   rval = mb->tag_get_handle("PlaneTriangleData", 4, moab::MB_TYPE_DOUBLE,
00884       planeDataTag, moab::MB_TAG_DENSE | moab::MB_TAG_CREAT, &defPlane);
00885 
00886   // compute the triangle plane and store it, for each triangle
00887   for (moab::Range::iterator itr = triangles.begin(); itr != triangles.end(); itr++) {
00888     // this index i will be the index in the vinfo array
00889     moab::EntityHandle tri = *itr;
00890     computeTrianglePlane(mb, tri);
00891     // setting the data for the tag/triangle is done in the compute
00892     //rval = mb->tag_set_data(planeDataTag, &tri, 1, &plane);
00893   }
00894 
00895   // create all the edges if not existing
00896   mb->get_adjacencies(triangles, 1, true, edgs, moab::Interface::UNION);
00897 
00898   // moab::Range verts;// the vertices are always there, they do not need to be created
00899   mb->get_adjacencies(triangles, 0, true, verts, moab::Interface::UNION);
00900   int numNodes = verts.size();
00901   validVertCount = numNodes; // this will be kept
00902   vinfo.init(numNodes);
00903   // set a unique integer tag with the position in vinfo array
00904   //  this will be used instead of v->uniqID in the vinfo array
00905   int def_data = -1;
00906 
00907   rval = mb->tag_get_handle("uniqID", 1, moab::MB_TYPE_INTEGER, uniqIDtag,
00908       moab::MB_TAG_DENSE | moab::MB_TAG_EXCL, &def_data);
00909   if (moab::MB_SUCCESS != rval)
00910     return 1;
00911 
00912   unsigned char def_data_bit = 1;// valid by default
00913 
00914   rval = mb->tag_get_handle("valid", 1, moab::MB_TYPE_BIT, validTag,
00915       moab::MB_TAG_EXCL | moab::MB_TAG_BIT, &def_data_bit);
00916   if (moab::MB_SUCCESS != rval)
00917     return 1;
00918 
00919   if (opts.plotCost) {
00920     double cost_default = 0.;
00921 
00922     rval = mb->tag_get_handle("costTAG", 1, moab::MB_TYPE_DOUBLE, costTag,
00923         moab::MB_TAG_DENSE | moab::MB_TAG_CREAT, &cost_default);
00924     if (moab::MB_SUCCESS != rval)
00925       return 1;
00926   }
00927 
00928   // set tag for each vertex; this will not be changed during simplification
00929   i = 0; // for index
00930   for (moab::Range::iterator it = verts.begin(); it != verts.end(); it++, i++) {
00931     // this index i will be the index in the vinfo array
00932     moab::EntityHandle v = *it;
00933     rval = mb->tag_set_data(uniqIDtag, &v, 1, &i);
00934     // the default valid data is 1; set it to 0 only to "mark" the vertex invalid for future deletion
00935     // is it really necessary if we put the default value as 1 anyway
00936 
00937     //rval = mb->tag_set_data(validTag, &v, 1, &def_data_bit);
00938   }
00939   moab::Range::iterator it;
00940   if (opts.logfile && opts.selected_output & OUTPUT_MODEL_DEFN) {
00941     for (it = verts.begin(); it != verts.end(); it++) {
00942       moab::EntityHandle v = *it;
00943       double coords[3];
00944       rval = mb->get_coords(&v, 1, coords);
00945       *opts.logfile << "v: " << uniqID(v) << " " << mb->id_from_handle(v)
00946           << " " << coords[0] << " " << coords[1] << " " << coords[2]
00947           << std::endl;
00948     }
00949   }
00950   std::cout << "  Decimate:  Distributing shape constraints." << std::endl;
00951 
00952   if (opts.will_use_vertex_constraint)
00953     for (it = verts.begin(); it != verts.end(); it++) {
00954       moab::EntityHandle v = *it;
00955       vertex_info(v).Q = quadrix_vertex_constraint(v);
00956     }
00957 
00958   if (opts.will_use_plane_constraint) {
00959     for (it = triangles.begin(); it != triangles.end(); it++) {
00960 
00961       moab::EntityHandle tr = *it;
00962       Mat4 Q = quadrix_plane_constraint(tr);
00963       double norm = 0.0;
00964 
00965       if (opts.will_weight_by_area) {
00966         norm = 1; // triangle area : m_model->face ( i )->area();
00967         Q *= norm;
00968       }
00969       const moab::EntityHandle * conn;
00970       int num_nodes;
00971       rval = mb->get_connectivity(tr, conn, num_nodes);
00972       for (j = 0; j < 3; j++) {
00973         vert_info& vj_info = vertex_info(conn[j]);
00974         vj_info.Q += Q;
00975         vertex_info(conn[j]).norm += norm;
00976 
00977       }
00978     }
00979   }
00980   // just define (one uniqTag for a triangle, see what is happening)
00981   moab::EntityHandle tr1 = triangles[0];
00982   rval = mb->tag_set_data(uniqIDtag, &tr1, 1, &i);// just some value
00983 
00984   if (opts.will_constrain_boundaries) {
00985     std::cout << "  Decimate:  Accumulating discontinuity constraints."
00986         << std::endl;
00987     for (it = edgs.begin(); it != edgs.end(); it++) {
00988       moab::EntityHandle edg = *it;
00989       if (is_border(edg)) {
00990         const moab::EntityHandle * conn;
00991         int num_nodes;
00992         rval = mb->get_connectivity(edg, conn, num_nodes);
00993         if (moab::MB_SUCCESS != rval)
00994           return 1;// fail
00995         Mat4 B = quadrix_discontinuity_constraint(edg);
00996         double norm = 0.0;
00997 
00998         if (opts.will_weight_by_area) {
00999           Vec3 ve1 = getVec3FromMBVertex(mb, conn[0]);
01000           Vec3 ve2 = getVec3FromMBVertex(mb, conn[1]);
01001           norm = norm2(ve1 - ve2);
01002           B *= norm;
01003         }
01004 
01005         B *= opts.boundary_constraint_weight;
01006         vert_info& v0_info = vertex_info(conn[0]);
01007         vert_info& v1_info = vertex_info(conn[1]);
01008         v0_info.Q += B;
01009         v0_info.norm += norm;
01010         v1_info.Q += B;
01011         v1_info.norm += norm;
01012       }
01013     }
01014   }
01015 
01016   std::cout << "  Decimate:  Allocating heap." << std::endl;
01017   heap = new Heap(edgs.size());
01018 
01019   int pair_count = 0;
01020 
01021   std::cout << "  Decimate:  Collecting pairs [edges]." << std::endl;
01022   for (it = edgs.begin(); it != edgs.end(); it++) {
01023     moab::EntityHandle edg = *it;
01024     const moab::EntityHandle * conn;
01025     int num_nodes;
01026     rval = mb->get_connectivity(edg, conn, num_nodes);
01027     if (moab::MB_SUCCESS != rval)
01028       return 1;// fail
01029     pair_info *pair = new_pair(conn[0], conn[1]);
01030     compute_pair_info(pair);
01031     pair_count++;
01032   }
01033 
01034   if (opts.pair_selection_tolerance < 0) {
01035     opts.pair_selection_tolerance = 1;//m_model->bounds.radius * 0.05;
01036     std::cout << "  Decimate:  Auto-limiting at 5% of model radius."
01037         << std::endl;
01038   }
01039   proximity_limit = opts.pair_selection_tolerance
01040       * opts.pair_selection_tolerance;
01041   if (proximity_limit > 0) {
01042     std::cout << "  Decimate:  Collecting pairs [limit="
01043         << opts.pair_selection_tolerance << "]." << std::endl;
01044     // use adaptive kd tree to find proximity vertices
01045     moab::EntityHandle tree_root = 0;
01046     moab::AdaptiveKDTree kd(mb);
01047     rval = kd.build_tree(verts, &tree_root);
01048     if (rval != moab::MB_SUCCESS) {
01049       std::cout << "Can't build tree for vertices" << std::endl;
01050       return 1;
01051     }
01052 
01053     for (it = verts.begin(); it != verts.end(); it++) {
01054       moab::Range closeVertices;
01055       closeVertices.clear();
01056       moab::EntityHandle v = *it;
01057       double coords[3];
01058       mb->get_coords(&v, 1, coords);
01059       //moab::CartVect v1(coords);
01060       std::vector<moab::EntityHandle> leaves; // sets of vertices close by
01061       kd.distance_search( coords,
01062           opts.pair_selection_tolerance, leaves);
01063       // add to the list of close vertices
01064       for (j = 0; j < leaves.size(); j++) {
01065         rval = mb->get_entities_by_type(leaves[j], moab::MBVERTEX,
01066             closeVertices);// add to the list
01067       }
01068 
01069       for (moab::Range::iterator it2 = closeVertices.begin(); it2
01070           != closeVertices.end(); it2++) {
01071         moab::EntityHandle vclose = *it2;
01072         if (v == vclose)
01073           continue;
01074         double coords2[3];
01075         mb->get_coords(&vclose, 1, coords2);
01076 
01077         //moab::CartVect v2(coords2);
01078         double dd = (coords[0] - coords2[0]) * (coords[0] - coords2[0])
01079             + (coords[1] - coords2[1]) * (coords[1] - coords2[1]) + (coords[2]
01080             - coords2[2]) * (coords[2] - coords2[2]);
01081 
01082         if (dd > proximity_limit)
01083           continue;
01084 
01085 /*
01086 #ifdef SAFETY
01087         assert ( pair_is_valid ( v1,v2 ) );
01088 #endif
01089 */
01090         if (!check_for_pair(v, vclose)) {
01091           pair_info *pair = new_pair(v, vclose);
01092           compute_pair_info(pair);
01093           pair_count++;
01094         }
01095       }
01096 
01097     }
01098   } else
01099     std::cout << "  Decimate:  Ignoring non-edge pairs [limit=0]." << std::endl;
01100 
01101   std::cout << "  Decimate:  Designated " << pair_count << " pairs."
01102       << std::endl;
01103 
01104   return 0;// no error
01105 }
01106 
01107 } // namespace MeshKit
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines