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 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