00001
00006 #ifdef HAVE_CONFIG_H
00007 #include <tumble-conf.h>
00008 #endif
00009
00010 #include "conformalmesher.h"
00011
00012 #include "beziermesh.h"
00013 #include "boundarymesh.h"
00014 #include "cell.h"
00015 #include "globals.h"
00016 #include "iterator_wrapper.h"
00017 #include "spline.h"
00018
00019 #include <algorithm>
00020 #include <stack>
00021
00022
00023
00024
00025
00026 #ifdef DEBUG_CONF_MESHER
00027 #include "meshio.h"
00028 #endif
00029
00030 using namespace std;
00031 using namespace hashers;
00032
00033 class ConformalMesher::Subspline {
00034 public:
00035 Subspline(BoundaryEdge *bdrye, BezierVertex *v0, BezierVertex *v1) :
00036 bdrye_(bdrye) {
00037 v_[0] = v0;
00038 v_[1] = v1;
00039 }
00040
00041
00042 void set_delete_iterator(std::list<Subspline*>::iterator it) {
00043 delete_iterator = it;
00044 }
00045 list<Subspline*>::iterator get_delete_iterator() {
00046 return delete_iterator;
00047 }
00048
00049 BezierVertex *get_vertex(int i) {
00050 assert(i >= 0); assert(i < 2);
00051 return v_[i];
00052 }
00053 const BezierVertex *get_vertex(int i) const {
00054 return const_cast<Subspline*>(this)->get_vertex(i);
00055 }
00056
00057 const BezierVertex *get_other_vertex(BezierVertex *v) const {
00058 if(get_vertex(0) == v) {
00059 return get_vertex(1);
00060 } else {
00061 assert(get_vertex(1) == v);
00062 return get_vertex(0);
00063 }
00064 }
00065
00066 const QBSpline *get_spline() const { return bdrye_->get_spline(); }
00067 QBSpline *get_spline() { return bdrye_->get_spline(); }
00068
00069 const Point2D& get_deboor() const {
00070 int idx = get_segment_index();
00071 return *get_spline()->get_deboor(idx + 1);
00072 }
00073
00074 BezierVertex *split(BezierMesh *mesh, vector<Subspline*>& subs) {
00075
00076
00077 int idx = get_segment_index();
00078 ControlPoint cp[3];
00079 ControlPoint newpt;
00080
00081 get_spline()->split(idx, 0.5, cp[0], cp[1], cp[2]);
00082 newpt = cp[1];
00083
00084 BezierVertex *splitvertex = mesh->clean_insert_point(*newpt);
00085 splitvertex->set_bdry(bdrye_, idx + 1);
00086 mesh->replace_control_point(splitvertex, newpt);
00087
00088
00089 subs.push_back(new Subspline(bdrye_, get_vertex(0), splitvertex));
00090 subs.push_back(new Subspline(bdrye_, splitvertex, get_vertex(1)));
00091
00092 return splitvertex;
00093 }
00094
00095
00096 bool is_encroached(BezierVertex *v) const {
00097 int idx = get_segment_index();
00098
00099
00100 Tumble::iterators::dereferencer<typeof(get_spline()->b.begin()), Point2D,
00101 const Point2D&>
00102 first(get_spline()->b.begin() + 2 * idx),
00103 last (get_spline()->b.begin() + 2 * (idx + 1));
00104 return BezierEdge::encroaches(v->get_cp(), first, ++last);
00105 }
00106
00111 int get_segment_index() const {
00112 if(get_vertex(0)->containment_dimension() == 0) {
00113
00114 return 0;
00115 }
00116 else {
00117 int idx = get_spline()->get_bezier_vertex_idx(get_vertex(0));
00118 assert(idx > 0); assert(idx < get_spline()->get_num_segments());
00119 return idx;
00120 }
00121 }
00122
00123
00124 private:
00125 BoundaryEdge *bdrye_;
00126 BezierVertex *v_[2];
00127 list<Subspline*>::iterator delete_iterator;
00128 };
00129
00130 #ifdef DEBUG_CONF_MESHER
00131 static ostream& operator<<(ostream& os, ConformalMesher::Subspline *s) {
00132 return os << (void*)s << " [spline: " << (s->get_spline()) << " => "
00133 << s->get_vertex(0) << " to " << s->get_vertex(1)
00134 << " via " << s->get_deboor() << ']';
00135 }
00136 #endif
00137
00138
00139
00140
00141 ConformalMesher::ConformalMesher(BezierMesh* _bezier_mesh,
00142 BoundaryMesh* _bdry_mesh)
00143 {
00144 bezier = _bezier_mesh;
00145 bdry = _bdry_mesh;
00146 }
00147
00148
00149
00150
00151 ConformalMesher::~ConformalMesher()
00152 {
00153 for(list<Subspline*>::iterator it = subsplines.begin();
00154 it != subsplines.end(); ++it) {
00155 delete (*it);
00156 }
00157 }
00158
00159
00160
00161
00162
00163
00164 void ConformalMesher::mesh()
00165 {
00166 #ifdef DEBUG_CONF_MESHER
00167 cout << "ConformalMesher: starting.\n";
00168 #endif
00169
00170 bezier->set_linear_flips(true);
00171
00172 create_bbox();
00173 insert_d0_vertices();
00174 create_subsplines();
00175 conform();
00176 reticulate_splines();
00177
00178
00179 #ifdef DEBUG_CONF_MESHER
00180 cout << "ConformalMesher: done building; mesh size "
00181 << bezier->get_num_faces() << endl;
00182 cout<<"BezierMesh:"<<endl;
00183 bezier->print_statistics();
00184 #endif
00185 bezier->set_linear_flips(false);
00186
00187 set_face_pointers();
00188 excavate_holes();
00189 cout << "ConformalMesher: done.\n";
00190 cout<<"************ Statistics (make sure these add up) ****************"<<endl;
00191 cout<<"BoundaryMesh:"<<endl;
00192 bdry->print_statistics();
00193 cout<<"BezierMesh:"<<endl;
00194 bezier->print_statistics();
00195 }
00196
00197
00198
00199
00200 void ConformalMesher::create_bbox()
00201 {
00202 #ifdef DEBUG_CONF_MESHER
00203 cout << "ConformalMesher: building bbox" << flush;
00204 #endif
00205
00206 Point2D p[2][2];
00207 BezierVertex *v[2][2];
00208 BezierEdge *e[5];
00209 double diameter;
00210
00211 bdry->bbox(p[1][1], p[0][0]);
00212 p[1][0] = Point2D(p[1][1].x(), p[0][0].y());
00213 p[0][1] = Point2D(p[0][0].x(), p[1][1].y());
00214
00215
00216
00217 diameter = (p[0][0]-p[1][1]).mag();
00218 p[0][0] += Point2D(-diameter, -diameter);
00219 p[0][1] += Point2D(-diameter, diameter);
00220 p[1][0] += Point2D( diameter, -diameter);
00221 p[1][1] += Point2D( diameter, diameter);
00222 cout << " bottom-left: " << p[0][0] << " top-right " << p[1][1] << endl;
00223
00224
00225 for(int i = 0; i < 2; i++) {
00226 for(int j = 0; j < 2; j++) {
00227 v[i][j] = bezier->add_bezier_vertex(p[i][j]);
00228 }
00229 }
00230
00231
00232 e[0] = bezier->add_straight_bezier_edge(v[0][0], v[0][1]);
00233 e[1] = bezier->add_straight_bezier_edge(v[0][0], v[1][0]);
00234 e[2] = bezier->add_straight_bezier_edge(v[1][1], v[0][1]);
00235 e[3] = bezier->add_straight_bezier_edge(v[1][1], v[1][0]);
00236 e[4] = bezier->add_straight_bezier_edge(v[0][0], v[1][1]);
00237
00238
00239 bezier->add_bezier_triangle(NULL, e[4], e[2], e[0]);
00240 bezier->add_bezier_triangle(NULL, e[4], e[1], e[3], true);
00241
00242
00243
00244 remove_points.push_back(p[0][0]);
00245
00246 #ifdef DEBUG_CONF_MESHER
00247 bezier->check_consistency();
00248 #endif
00249 }
00250
00251
00252
00253
00254
00255
00256 void ConformalMesher::insert_d0_vertices()
00257 {
00258 #ifdef DEBUG_CONF_MESHER
00259 cout << "ConformalMesher: inserting D0 vertices\n";
00260 #endif
00261 for(BoundaryMesh::Boundary_Vertex_Hash_T::iterator it = bdry->get_vertexs_begin();
00262 it != bdry->get_vertexs_end(); ++it) {
00263 BoundaryVertex *bdryv = (*it);
00264 BezierVertex *bezv = bezier->clean_insert_point(bdryv->get_cp());
00265 bezv->set_bdry(bdryv);
00266 bezier->replace_control_point(bezv, bdryv->get_control_point());
00267
00268 #ifdef DEBUG_CONF_MESHER
00269 cout << "ConformalMesher: - inserted " << (void*)bezv
00270 << " at " << bezv->get_cp() << endl;
00271 #endif
00272 }
00273
00274 #ifdef DEBUG_CONF_MESHER
00275 bezier->check_consistency();
00276 #endif
00277 }
00278
00279
00280
00281
00282
00283
00284 void ConformalMesher::create_subsplines()
00285 {
00286 #ifdef DEBUG_CONF_MESHER
00287 cout << "ConformalMesher: inserting D1 vertices, storing splines\n";
00288 #endif
00289 for(BoundaryMesh::Boundary_Edge_Hash_T::iterator it = bdry->get_edges_begin();
00290 it != bdry->get_edges_end(); ++it) {
00291 BoundaryEdge *bdrye = (*it);
00292 QBSpline *spline = bdrye->get_spline();
00293 vector<BezierVertex*> knots;
00294
00295 #ifdef DEBUG_CONF_MESHER
00296 vector<BezierTriangle*> inverts;
00297 #endif
00298
00299
00300
00301
00302 knots.push_back(bdrye->get_vertex(0)->get_bezier());
00303 for (int i = 1; i <= spline->get_num_segments() - 1; i++) {
00304 ControlPoint cp = spline->get_control_point_at_idx(i);
00305 BezierVertex *bezv = bezier->clean_insert_point(*cp);
00306
00307 #ifdef DEBUG_CONF_MESHER
00308 cout << "ConformalMesher: - inserting " << cp << "... " << flush;
00309 bezier->find_inverted_triangles(inverts);
00310 if (!inverts.empty())
00311 {
00312 cout<< " INVERTED TRIANGLES FOUND IN CONF_MESHER "<<endl;
00313 cout<< " Just inserted : "<<cp<<endl;
00314 for (vector<BezierTriangle*>::iterator i = inverts.begin();
00315 i != inverts.end(); ++i)
00316 cout << "Inverted Triangle: "<<(**i)<<endl;
00317 MeshBinaryOutput mo(bezier,bdry);
00318 mo.write("created_inverted_during_create_subsplines.geo");
00319 assert(0);
00320 } else {
00321 cout << "Conformal mesher inversion check passed."<<endl;
00322 }
00323 #endif
00324
00325 bezier->replace_control_point(bezv, cp);
00326 bezv->set_bdry(bdrye, i);
00327 knots.push_back(bezv);
00328
00329 spline->set_bezier_vertex(i, knots.back());
00330
00331 }
00332 knots.push_back(bdrye->get_vertex(1)->get_bezier());
00333 assert((unsigned)knots.size() == (unsigned)spline->get_num_segments() + 1);
00334
00335
00336 for (int i = 0; i < spline->get_num_segments(); i++) {
00337 Subspline *s = new Subspline(bdrye, knots[i], knots[i+1]);
00338 link_edge(s);
00339 }
00340 }
00341 #ifdef DEBUG_CONF_MESHER
00342 bezier->check_consistency();
00343 #endif
00344 }
00345
00346
00347
00348
00349
00350
00351
00352 void ConformalMesher::conform()
00353 {
00354 #ifdef DEBUG_CONF_MESHER
00355 cout << "ConformalMesher: enforcing conformity\n";
00356 #endif
00357 while(!spline_stack.empty()) {
00358 Subspline *s = spline_stack.back();
00359 spline_stack.pop_back();
00360 in_stack.erase(s);
00361 #ifdef DEBUG_CONF_MESHER
00362 cout << "ConformalMesher: - considering " << s << endl;
00363 cout << "ConformalMesher: ";
00364 #endif
00365 if ( (is_encroached.count(s) == 0) && segment_appears(s)) {
00366 #ifdef DEBUG_CONF_MESHER
00367 cout << "+ happy\n";
00368 #endif
00369 continue;
00370 }
00371 #ifdef DEBUG_CONF_MESHER
00372 cout << " => splitting!\n";
00373 #endif
00374
00375 vector<Subspline*> newsegs;
00376 BezierVertex *splitvertex = split_destroy_segment(s, newsegs);
00377 assert(newsegs.size() == 2);
00378 s = (Subspline*)0x7;
00379 #ifdef DEBUG_CONF_MESHER
00380 cout << "ConformalMesher: produced:\n"
00381 << " " << newsegs[0] << endl
00382 << " " << newsegs[1] << endl;
00383 #endif
00384
00385
00386 deque<BezierEdge*> link_edges;
00387 bezier->enqueue_edges(splitvertex, link_edges);
00388 for(deque<BezierEdge*>::iterator link_it = link_edges.begin();
00389 link_it != link_edges.end(); ++link_it) {
00390 BezierEdge *link_e = (*link_it);
00391 BezierVertex *link_v = bezier->get_opposite_vertex(link_e, splitvertex);
00392
00393 vector<Subspline*>::iterator seg_it = incident_edges[link_v].begin();
00394 vector<Subspline*>::iterator seg_end = incident_edges[link_v].end();
00395 for ( ; seg_it != seg_end ; ++seg_it) {
00396 Subspline *link_s = *seg_it;
00397 if (link_s == newsegs[0] || link_s == newsegs[1]) {
00398
00399
00400 continue;
00401 }
00402 if (is_encroached.count(link_s) != 0) {
00403
00404
00405
00406 assert(in_stack.count(link_s) != 0);
00407 continue;
00408 }
00409
00410
00411
00412 if (link_s->is_encroached(splitvertex)) {
00413 is_encroached.insert(link_s);
00414 if (in_stack.count(link_s) == 0) {
00415 spline_stack.push_back(link_s);
00416 in_stack.insert(link_s);
00417 }
00418 }
00419 }
00420 }
00421 }
00422
00423 #ifdef DEBUG_CONF_MESHER
00424 bezier->check_consistency();
00425 #endif
00426 }
00427
00428
00429
00430
00431
00432 void ConformalMesher::reticulate_splines()
00433 {
00434 #ifdef DEBUG_CONF_MESHER
00435 cout << "ConformalMesher: adding splines to BezierMesh\n";
00436 #endif
00437
00438 for (BoundaryMesh::Boundary_Edge_Hash_T::iterator it = bdry->get_edges_begin();
00439 it != bdry->get_edges_end(); ++it) {
00440 BoundaryEdge *bdrye = (*it);
00441 QBSpline *spline = bdrye->get_spline();
00442
00443 int n = spline->get_num_segments();
00444 for (int i = 0 ; i < n; i++) {
00445 BezierVertex *v1 = (i == 0) ? bdrye->get_vertex(0)->get_bezier()
00446 : spline->get_bezier_vertex(i);
00447 assert(v1);
00448 BezierVertex *v2 = (i + 1 == n) ? bdrye->get_vertex(1)->get_bezier()
00449 : spline->get_bezier_vertex(i + 1);
00450 assert(v2);
00451 BezierEdge *edge = bezier->find_common_edge(v1, v2);
00452 assert(edge);
00453
00454 edge->set_bdry(bdrye, spline->k[i], spline->k[i+1]);
00455 bezier->replace_control_point(edge, spline->get_deboor(i+1));
00456 }
00457 }
00458 #ifdef DEBUG_CONF_MESHER
00459 bezier->check_consistency();
00460 #endif
00461 }
00462
00463
00464
00465
00466
00467 void ConformalMesher::set_face_pointers()
00468 {
00469 #ifdef DEBUG_CONF_MESHER
00470 cout << "ConformalMesher: setting face pointers\n";
00471 #endif
00472 for(vector<pair<BoundaryFace*,Point2D> >::iterator it = face_points.begin();
00473 it != face_points.end(); ++it) {
00474 BoundaryFace *face = (*it).first;
00475 Point2D p = (*it).second;
00476 hash_set<BezierTriangle*> triangles;
00477 int triangles_set = 0;
00478
00479 collect_faces(p, triangles);
00480 for(hash_set<BezierTriangle*>::iterator it = triangles.begin();
00481 it != triangles.end(); ++it, triangles_set++) {
00482 BezierTriangle *t = *it;
00483 t->set_boundary_face(face);
00484 }
00485 #ifdef DEBUG_CONF_MESHER
00486 cout << "ConformalMesher: - face " << (void*)face << " set "
00487 << triangles_set << " triangles\n";
00488 #endif
00489 }
00490 }
00491
00492
00493
00494
00495 void ConformalMesher::excavate_holes()
00496 {
00497 #ifdef DEBUG_CONF_MESHER
00498 cout << "ConformalMesher: excavating holes\n";
00499 #endif
00500
00501
00502
00503 hash_set<BezierTriangle*> triangles;
00504 hash_set<BezierEdge*> edges;
00505 hash_set<BezierVertex*> vertices;
00506
00507 #ifdef DEBUG_CONF_MESHER
00508 cout << "ConformalMesher: - considering " << remove_points.size()
00509 << " holes\n";
00510 #endif
00511
00512 for(vector<Point2D>::iterator it = remove_points.begin();
00513 it != remove_points.end(); ++it) {
00514 collect_faces(*it, triangles);
00515 }
00516
00517 for(hash_set<BezierTriangle*>::iterator it = triangles.begin();
00518 it != triangles.end(); ++it) {
00519 BezierTriangle *t = (*it);
00520
00521
00522 BezierTuple final_tup = bezier->get_tuple(t);
00523 BezierTuple tup = final_tup;
00524 do {
00525
00526 if (!tup.e->is_boundary()) {
00527 edges.insert(tup.e);
00528 }
00529 if (!tup.v->is_boundary()) {
00530 vertices.insert(tup.v);
00531 }
00532 tup = bezier->Switch(0, bezier->Switch(1, tup));
00533 } while (tup != final_tup);
00534 }
00535
00536
00537
00538 #ifdef DEBUG_CONF_MESHER
00539 cout << "ConformalMesher: - removing " << triangles.size()
00540 << " triangles\n";
00541 #endif
00542 for(hash_set<BezierTriangle*>::iterator it = triangles.begin();
00543 it != triangles.end(); ++it) {
00544 bezier->delete_face(*it);
00545 }
00546
00547
00548
00549 #ifdef DEBUG_CONF_MESHER
00550 cout << "ConformalMesher: - removing " << edges.size() << " edges\n";
00551 #endif
00552 for(hash_set<BezierEdge*>::iterator it = edges.begin();
00553 it != edges.end(); ++it) {
00554 bezier->delete_edge(*it);
00555 }
00556
00557
00558
00559 #ifdef DEBUG_CONF_MESHER
00560 cout << "ConformalMesher: - removing " << vertices.size() << " vertices\n";
00561 #endif
00562 for(hash_set<BezierVertex*>::iterator
00563 it = vertices.begin(); it != vertices.end(); ++it) {
00564 bezier->delete_vertex(*it);
00565 }
00566
00567 #ifdef DEBUG_CONF_MESHER
00568 bezier->check_consistency();
00569 #endif
00570 }
00571
00572
00573
00574
00575
00576
00577
00578 bool ConformalMesher::segment_appears(Subspline *seg)
00579 {
00580 BezierEdge *edge = bezier->find_common_edge(seg->get_vertex(0),
00581 seg->get_vertex(1));
00582 if (!edge) {
00583
00584 cout << "(no such edge)" << flush;
00585 return false;
00586 }
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596 BezierTuple tuple;
00597 BezierEdge *e[4];
00598 BezierVertex *v[4];
00599
00600
00601 tuple = bezier->get_tuple(edge);
00602 v[0] = v[1] = tuple.v;
00603 assert(v[0] == seg->get_vertex(0) || v[0] == seg->get_vertex(1));
00604 e[0] = bezier->Switch(1, tuple).e;
00605 e[1] = bezier->Switch(1, bezier->Switch(2, tuple)).e;
00606
00607
00608 tuple = bezier->Switch(0, tuple);
00609 v[2] = v[3] = tuple.v;
00610 assert(v[2] == seg->get_vertex(0) || v[2] == seg->get_vertex(1));
00611 assert(v[2] != v[0]);
00612 e[2] = bezier->Switch(1, tuple).e;
00613 e[3] = bezier->Switch(1, bezier->Switch(2, tuple)).e;
00614
00615 for (int i = 0; i < 4; i++) {
00616 assert(e[i] != edge);
00617 if (!angle_is_positive(seg, v[i], e[i])) {
00618 #ifdef DEBUG_CONF_MESHER
00619 cout << "(angle " << i << " is negative)" << flush;
00620 #endif
00621 return false;
00622 }
00623 }
00624
00625
00626 return true;
00627 }
00628
00629
00630
00631
00632
00633
00634
00635 bool ConformalMesher::angle_is_positive(Subspline *s,
00636 BezierVertex *shared,
00637 BezierEdge *e)
00638 {
00639 Point2D pshared = shared->get_cp();
00640 Point2D pedge = bezier->get_opposite_vertex(e, shared)->get_cp();
00641 Point2D pdeboor = s->get_deboor();
00642 Point2D pspline = s->get_other_vertex(shared)->get_cp();
00643
00644
00645 double sp_sh_d = pdeboor.line_side_test(pspline, pshared);
00646 if (sp_sh_d == 0.0) {
00647 return true;
00648 }
00649
00650
00651
00652 double sp_sh_e = pedge.line_side_test(pspline, pshared);
00653 assert(sp_sh_e != 0.0);
00654 if (sp_sh_d * sp_sh_e < 0.0) {
00655 return true;
00656 }
00657
00658
00659
00660 double e_sh_d = pdeboor.line_side_test(pedge, pshared);
00661 double e_sh_sp = pspline.line_side_test(pedge, pshared);
00662 assert(e_sh_sp != 0.0);
00663 if (e_sh_d * e_sh_sp > 0.0) {
00664 return true;
00665 }
00666
00667
00668 return false;
00669 }
00670
00671
00672
00673
00674 void ConformalMesher::link_edge(Subspline *s)
00675 {
00676 spline_stack.push_back(s);
00677 in_stack.insert(s);
00678 incident_edges[s->get_vertex(0)].push_back(s);
00679 incident_edges[s->get_vertex(1)].push_back(s);
00680 subsplines.push_front(s);
00681 s->set_delete_iterator(subsplines.begin());
00682 }
00683
00684
00685
00686
00687 void ConformalMesher::unlink_edge(Subspline *s)
00688 {
00689 assert(in_stack.count(s) == 0);
00690 is_encroached.erase(s);
00691 for (int vi = 0; vi < 2; ++vi) {
00692 vector<Subspline*>& vect = incident_edges[s->get_vertex(vi)];
00693 bool did_find = false;
00694 for (unsigned i = 0; i < vect.size(); ++i) {
00695 if(vect[i] == s) {
00696 vect[i] = vect.back();
00697 vect.pop_back();
00698 did_find = true;
00699 break;
00700 }
00701 }
00702 assert(did_find);
00703 }
00704 subsplines.erase(s->get_delete_iterator());
00705 delete s;
00706 }
00707
00708
00709
00710
00711
00712 BezierVertex *ConformalMesher::split_destroy_segment(Subspline *s,
00713 vector<Subspline*>& newsegs)
00714 {
00715
00716 BezierVertex *splitvertex = s->split(bezier, newsegs);
00717 assert(newsegs.size() == 2);
00718
00719
00720 unlink_edge(s);
00721
00722
00723 link_edge(newsegs[0]);
00724 link_edge(newsegs[1]);
00725
00726 return splitvertex;
00727 }
00728
00729
00730
00731
00732
00733 void ConformalMesher::collect_faces(Point2D start,
00734 hash_set<BezierTriangle*>& faces)
00735 {
00736 int cd;
00737 BezierTuple searchtup;
00738 double u, v;
00739
00740
00741 cd = bezier->blind_locate_point(start, searchtup, u, v);
00742
00743
00744 deque<BezierTriangle*> todo;
00745
00746 switch(cd) {
00747 case 0:
00748
00749 bezier->enqueue_faces(searchtup.v, todo);
00750 break;
00751
00752 case 1:
00753
00754 todo.push_back(searchtup.f);
00755 todo.push_back(bezier->Switch(2, searchtup).f);
00756 break;
00757
00758 case 2:
00759 todo.push_back(searchtup.f);
00760 break;
00761
00762 default:
00763 assert(0);
00764 break;
00765 }
00766
00767 while(!todo.empty()){
00768 BezierTriangle *tri = todo.back();
00769 todo.pop_back();
00770
00771 faces.insert(tri);
00772
00773
00774
00775 BezierTuple start = bezier->get_tuple(tri);
00776 BezierTuple tup = start;
00777 do {
00778 if(!tup.e->is_boundary()) {
00779
00780 BezierTriangle *t = bezier->Switch(2, tup).f;
00781 if(faces.count(t) == 0) {
00782 todo.push_back(t);
00783 }
00784 }
00785
00786 tup = bezier->Switch(0, bezier->Switch(1, tup));
00787 } while(tup != start);
00788 }
00789 }