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