From 6a4cb61a6a55566f8276b3f3a64d54239d9575ca Mon Sep 17 00:00:00 2001 From: Jean-Francois Remacle <jean-francois.remacle@uclouvain.be> Date: Fri, 29 Jan 2016 09:33:19 +0000 Subject: [PATCH] new 3D mesher online --- Mesh/delaunay3d.cpp | 251 +++++++++++++++++++++++++-- Mesh/delaunay3d_private.h | 2 +- Mesh/delaunay_refinement.cpp | 25 ++- Mesh/meshGRegionBoundaryRecovery.cpp | 28 +-- Mesh/meshGRegionBoundaryRecovery.h | 4 +- benchmarks/3d/Cube-01.geo | 2 +- 6 files changed, 280 insertions(+), 32 deletions(-) diff --git a/Mesh/delaunay3d.cpp b/Mesh/delaunay3d.cpp index 7ba803bdd4..f075922b24 100644 --- a/Mesh/delaunay3d.cpp +++ b/Mesh/delaunay3d.cpp @@ -319,6 +319,231 @@ void computeAdjacencies (Tet *t, int iFace, connContainer &faceToTet){ faceToTet.erase(it); } } +/* + Fixing a non star shaped cavity (non delaunay triangulations) + See P.L. George's paper + Improvements on Delaunay-based three-dimensional automatic mesh generator + Finite Elements in Analysis and Design 25 (1997) 297-317 +*/ + +static void starShapeness (Vertex *v, connContainer &bndK, + std::vector<unsigned int> &_negatives, + std::vector<unsigned int> &_nulls, + double threshold){ + _negatives.clear(); + _nulls.clear(); + for (unsigned int i=0; i< bndK.size(); i++) { + // no symbolic perturbation + const double val = robustPredicates::orient3d((double*)bndK[i].f.V[0], + (double*)bndK[i].f.V[1], + (double*)bndK[i].f.V[2], + (double*)v); + if (val <= threshold ) { + if(val >= 0)_nulls.push_back(i); + else _negatives.push_back(i); + } + } +} + +static Tet* tetContainsV (Vertex *v, cavityContainer &cavity){ + + for (unsigned int i=0; i< cavity.size(); i++) { + unsigned int count = 0; + for (unsigned int j=0;j<4;j++){ + Face f = cavity[i]->getFace(j); + const double val = robustPredicates::orient3d((double*)f.V[0], + (double*)f.V[1], + (double*)f.V[2], + (double*)v); + if (val >= 0 ) { + count++; + } + } + if (count == 4)return cavity[i]; + } + // printf("AIE\n"); + return NULL; +} + +static void buildDelaunayBall (cavityContainer &cavity, connContainer &faceToTet){ + faceToTet.clear(); + for (unsigned int i=0; i< cavity.size(); i++) { + Tet *t = cavity[i]; + for (unsigned int iFace=0; iFace< 4; iFace++) { + Tet *neigh = t->T[iFace]; + conn c (t->getFace(iFace), iFace, neigh); + connContainer::iterator it = std::find(faceToTet.begin(),faceToTet.end(),c); + if (it == faceToTet.end()){ + faceToTet.push_back(c); + } + else { + faceToTet.erase(it); + } + } + } +} + +static void findPossibleTets (cavityContainer &cavity, + connContainer &bndK, + std::vector<Tet*> &_possible){ + _possible.clear(); + std::vector<Vertex*> _all; + for (unsigned int i=0; i< bndK.size(); i++) { + for (unsigned int j=0; j< 3; j++) { + _all.push_back(bndK[i].f.V[j]); + } + } + // std::vector<Vertex*>::iterator last = std::unique(_all.begin(), _all.end()); + for (unsigned int i=0; i< cavity.size(); i++) { + Tet *t = cavity[i]; + for (unsigned int j=0; j< 4; j++) { + Vertex *v = t->V[j]; + if (std::find (_all.begin(),_all.end(), v) == _all.end()){ + _possible.push_back(t); + break; + } + } + } +} + +static bool removeIsolatedTets(Tet *containsV, + cavityContainer &cavity, + connContainer &bndK, + int myThread, int K){ + cavityContainer cc; + cc.push_back(containsV); + std::stack<Tet*> _stack; + _stack.push(containsV); + + while (!_stack.empty()){ + Tet *t = _stack.top(); + _stack.pop(); + for (int i=0;i<4;i++){ + Tet *neigh = t->T[i]; + if (neigh && + (std::find(cc.begin(),cc.end(),neigh) == cc.end()) && + (std::find(cavity.begin(),cavity.end(),neigh) != cavity.end())){ + cc.push_back(neigh); + _stack.push(neigh); + } + } + } + if (cc.size() == cavity.size())return false; + // // Msg::Info(" cavity updated (%3ld elements) %3ld isolated tet removed",cavity.size(),cavity.size()-cc.size()); + cavity = cc; + return true; +} + +static Tet *tetInsideCavityWithFAce (Face &f, cavityContainer &cavity){ + // printf("size of cavity %ld\n",cavity.size()); + for (unsigned int i=0; i< cavity.size(); i++) { + Tet *t = cavity[i]; + for (unsigned int iFace=0; iFace< 4; iFace++) { + if (t->getFace(iFace) == f) return t; + } + } + return NULL; +} + + +void __print (const char *name, connContainer &conn, Vertex *v); +static bool fixDelaunayCavity (double threshold, + Vertex *v, + cavityContainer &cavity, + connContainer &bndK, + int myThread, int K, + std::vector<unsigned int> & _negatives, + std::vector<unsigned int> & _nulls){ + + static int XXX = 1; + starShapeness (v, bndK, _negatives, _nulls, threshold); + +#if 0 + // test coherence ... + connContainer bndK2; + buildDelaunayBall (cavity,bndK2); + if (bndK.size() != bndK2.size())printf("BUG1\n"); + for (unsigned int i=0;i<bndK.size();i++){ + connContainer::iterator it = std::find(bndK.begin(),bndK.end(),bndK2[i]); + if( it == bndK.end())printf("%d BUG2\n",XXX); + if(it->t != bndK2[i].t)printf("%d BUG3\n",XXX); + if(it->i != bndK2[i].i)printf("%d BUG4\n",XXX); + } +#endif + + + if (_negatives.empty() && _nulls.empty())return false; + + // unset all tets of the cavity + for (unsigned int i=0; i< cavity.size(); i++)cavity[i]->unset(myThread,K); + for (unsigned int i=0; i<bndK.size(); i++)if(bndK[i].t)bndK[i].t->unset(myThread,K); + + Msg::Debug("Fixing cavity %3d (%3ld,%3ld) : %ld negatives and %ld Nulls",XXX, + cavity.size(),bndK.size(), _negatives.size(),_nulls.size()); + + Tet *containsV = tetContainsV (v,cavity); + + if (! containsV) return true; + + while (1) { + if (removeIsolatedTets(containsV, cavity,bndK,myThread,K)){ + buildDelaunayBall (cavity,bndK); + starShapeness (v, bndK, _negatives, _nulls, threshold); + } + + for (unsigned int i=0;i<_negatives.size();i++){ + // FIXME + int index = _negatives[i]; + conn &c = bndK[index]; + Tet *toRemove = tetInsideCavityWithFAce (c.f, cavity); + if (toRemove){ + std::vector<Tet*>::iterator it = std::find(cavity.begin(), cavity.end(), toRemove); + if (it != cavity.end()) + cavity.erase(it); + else + Msg::Fatal("Datastructure Broken in %s line %5d",__FILE__,__LINE__); + } + } + for (unsigned int i=0;i<_nulls.size();i++){ + return true; + int index = _nulls[i]; + conn &c = bndK[index]; + Tet *toAdd = c.t; + if (toAdd && std::find(cavity.begin(), cavity.end(), toAdd) == cavity.end())cavity.push_back(toAdd); + else return true; + } + // rebuild the boundary of the cavity + // this step could be done faster by directly + // updating bndK in the steps above + buildDelaunayBall (cavity,bndK); + // if isolated points are present in the cavity + // find all tets that contain thos vertices + std::vector<Tet*> _possible; + if (_nulls.size())findPossibleTets (cavity,bndK,_possible); + if (!_possible.empty()){ + printf("cavity is dead\n"); + throw; + // FIXME + return true; + std::random_shuffle(_possible.begin(), _possible.end()); + Tet *toRemove = _possible[0]; + std::remove(cavity.begin(), cavity.end(), toRemove); + buildDelaunayBall (cavity,bndK); + starShapeness (v, bndK, _negatives, _nulls, threshold); + } + else { + starShapeness (v, bndK, _negatives, _nulls, threshold); + // Msg::Info(" cavity updated (%3ld elements) %3ld negatives",cavity.size(),_negatives.size()); + if (_negatives.empty() && _nulls.empty()){ + // sprintf(name,"final%d.pos",XXX++); + // __print (name, bndK, v); + //Msg::Info(" cavity is fixed"); + // getchar(); + return false; + } + } + } +} static void delaunayCavity2 (Tet *t, Tet *prev, @@ -375,20 +600,23 @@ Tet* walk (Tet *t, Vertex *v, int maxx, double &totSearch, int thread){ if (t == NULL) { return NULL; // we should NEVER return here } - if (t->inSphere(v,thread)) {return t;} + // if (t->inSphere(v,thread)) {return t;} double _min = 0.0; int NEIGH = -1; + int count = 0; for (int iNeigh=0; iNeigh<4; iNeigh++){ Face f = t->getFace (iNeigh); double val = robustPredicates::orient3d((double*)f.V[0], (double*)f.V[1], (double*)f.V[2], (double*)v); + if (val >=-1.e-12) count++; if (val < _min){ NEIGH = iNeigh; _min = val; } } + if (count == 4 && t->inSphere(v,thread))return t; if (NEIGH >= 0){ t = t->T[NEIGH]; } @@ -400,10 +628,12 @@ Tet* walk (Tet *t, Vertex *v, int maxx, double &totSearch, int thread){ } -void __print (const char *name, connContainer &conn){ +void __print (const char *name, connContainer &conn, Vertex *v){ FILE *f = fopen(name,"w"); fprintf(f,"View \"\"{\n"); + if (v)fprintf(f,"SP(%g,%g,%g){%d};\n",v->x(),v->y(),v->z(),v->getNum()); + for (unsigned int i=0;i<conn.size();i++){ fprintf(f,"ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n", conn[i].f.V[0]->x(),conn[i].f.V[0]->y(),conn[i].f.V[0]->z(), @@ -522,7 +752,8 @@ void delaunayTrgl (const unsigned int numThreads, const unsigned int NPTS_AT_ONCE, unsigned int Npts, std::vector<Vertex*> assignTo[], - tetContainer &allocator){ + tetContainer &allocator, + double threshold){ #ifdef _VERBOSE double totSearchGlob=0; double totCavityGlob=0; @@ -552,6 +783,7 @@ void delaunayTrgl (const unsigned int numThreads, double totSearch=0; double totCavity=0; + std::vector<unsigned int> _negatives, _nulls; std::vector<cavityContainer> cavity(NPTS_AT_ONCE); std::vector<connContainer> bnd(NPTS_AT_ONCE); std::vector<bool> ok(NPTS_AT_ONCE); @@ -620,16 +852,9 @@ void delaunayTrgl (const unsigned int numThreads, delaunayCavity2(t[K], NULL, vToAdd[K], cavityK, bndK, myThread, K); //delaunayCavity(t[K], vToAdd[K], cavityK, bndK, myThread, K); // verify the cavity - for (unsigned int i=0; i< bndK.size(); i++) { - const double val = robustPredicates::orient3d((double*)bndK[i].f.V[0], - (double*)bndK[i].f.V[1], - (double*)bndK[i].f.V[2], - (double*)vToAdd[K]); - if (val <= 0 ) { - vToAdd[K] = NULL; - invalidCavities [myThread]++; - break; - } + if (fixDelaunayCavity (threshold, vToAdd[K], cavityK, bndK, myThread, K, _negatives, _nulls)){ + vToAdd[K] = NULL; + invalidCavities [myThread]++; } } } diff --git a/Mesh/delaunay3d_private.h b/Mesh/delaunay3d_private.h index 36af19a9bf..a71990cb00 100644 --- a/Mesh/delaunay3d_private.h +++ b/Mesh/delaunay3d_private.h @@ -338,6 +338,6 @@ void delaunayTrgl (const unsigned int numThreads, const unsigned int NPTS_AT_ONCE, unsigned int Npts, std::vector<Vertex*> assignTo[], - tetContainer &allocator); + tetContainer &allocator, double threshold = 0.0); #endif diff --git a/Mesh/delaunay_refinement.cpp b/Mesh/delaunay_refinement.cpp index 5bd391a679..803ddc8b39 100644 --- a/Mesh/delaunay_refinement.cpp +++ b/Mesh/delaunay_refinement.cpp @@ -263,8 +263,25 @@ void filterVertices (const int numThreads, std::vector<int> indices; SortHilbert(add, indices); + std::vector<Vertex*> _add=add; + + // std::vector<Vertex*> _add; + // Vertex *current = add[0]; + // printf("before %d\n",add.size()); + // for (unsigned int i=1;i<add.size();i++){ + // const double d = sqrt (SQR(add[i]->x()-current->x()) + + // SQR(add[i]->y()-current->y()) + + // SQR(add[i]->z()-current->z()) ); + // if (0.8*current->lc() > d){ + // delete add[i]; + // } + // else { + // current = add[i]; + // _add.push_back(add[i]); + // } + // } + // printf("after %d\n",_add.size()); - std::vector<Vertex*> _add = add; add.clear(); for (unsigned int i=0;i<_add.size();i++){ SPoint3 p (_add[i]->x(),_add[i]->y(),_add[i]->z()); @@ -415,7 +432,7 @@ void edgeBasedRefinement (const int numThreads, int iter = 1; - Msg::Info("----------------------------------- SATUR FILTR SORTH DELNY TIME TETS"); + Msg::Info("------------------------------------- SATUR FILTR SORTH DELNY TIME TETS"); double __t__ = Cpu(); // Tet::in_sphere_counter = 0; @@ -433,10 +450,10 @@ void edgeBasedRefinement (const int numThreads, std::vector<int> indices; SortHilbert(add, indices); double t4 = Cpu(); - delaunayTrgl (1,1,add.size(), &add,allocator); + delaunayTrgl (1,1,add.size(), &add,allocator,1.e-24); double t5 = Cpu(); add_all.insert (add_all.end(), add.begin(), add.end()); - Msg::Info("IT %3d %6d points added, timings %5.2f %5.2f %5.2f %5.2f %5.2f %5d",iter,add.size(), + Msg::Info("IT %3d %8d points added, timings %5.2f %5.2f %5.2f %5.2f %5.2f %5d",iter,add.size(), (t2-t1), (t3-t2), (t4-t3), diff --git a/Mesh/meshGRegionBoundaryRecovery.cpp b/Mesh/meshGRegionBoundaryRecovery.cpp index 58db969066..91f82a9f23 100644 --- a/Mesh/meshGRegionBoundaryRecovery.cpp +++ b/Mesh/meshGRegionBoundaryRecovery.cpp @@ -12894,7 +12894,7 @@ long meshGRegionBoundaryRecovery::lawsonflip3d(flipconstraints *fc) return totalcount + sliver_peels; } -void meshGRegionBoundaryRecovery::recoverdelaunay() +bool meshGRegionBoundaryRecovery::recoverdelaunay() { arraypool *flipqueue, *nextflipqueue, *swapqueue; triface tetloop, neightet, *parytet; @@ -12941,13 +12941,13 @@ void meshGRegionBoundaryRecovery::recoverdelaunay() fc.enqflag = 2; lawsonflip3d(&fc); - + if (b->verbose > 1) { printf(" obj (after Lawson) = %.17g\n", tetprism_vol_sum); } - + if (unflipqueue->objects == 0l) { - return; // The mesh is Delaunay. + return true; // The mesh is Delaunay. } fc.unflip = 1; // Unflip if the edge is not flipped. @@ -13042,7 +13042,9 @@ void meshGRegionBoundaryRecovery::recoverdelaunay() } } // while (flipqueue->objects > 0l) + bool returnValue = true; if (flipqueue->objects > 0l) { + returnValue = false; if (b->verbose > 1) { printf(" %ld non-Delaunay edges remained.\n", flipqueue->objects); } @@ -13055,6 +13057,7 @@ void meshGRegionBoundaryRecovery::recoverdelaunay() b->flipstarsize = bakmaxflipstarsize; delete flipqueue; delete nextflipqueue; + return returnValue; } int meshGRegionBoundaryRecovery::gettetrahedron(point pa, point pb, point pc, @@ -14450,11 +14453,11 @@ void meshGRegionBoundaryRecovery::jettisonnodes() /////////////////////////////////////////////////////////////////////////////// -void meshGRegionBoundaryRecovery::reconstructmesh(GRegion *_gr) +bool meshGRegionBoundaryRecovery::reconstructmesh(GRegion *_gr) { + bool returnValue (true); double t_start = Cpu(); - std::vector<MVertex*> _vertices; // Get the set of vertices from GRegion. @@ -14512,7 +14515,7 @@ void meshGRegionBoundaryRecovery::reconstructmesh(GRegion *_gr) longest = sqrt(x * x + y * y + z * z); if (longest == 0.0) { Msg::Warning("Error: The point set is trivial.\n"); - return; + return true; } // Two identical points are distinguished by 'lengthlimit'. @@ -14854,9 +14857,10 @@ void meshGRegionBoundaryRecovery::reconstructmesh(GRegion *_gr) suppresssteinerpoints(); } - recoverdelaunay(); + returnValue = recoverdelaunay(); - // optimizemesh(); + // let's trry + optimizemesh(); if ((dupverts > 0l) || (unuverts > 0l)) { // Remove hanging nodes. @@ -15159,8 +15163,10 @@ void meshGRegionBoundaryRecovery::reconstructmesh(GRegion *_gr) _gr->tetrahedra.push_back(t); tetloop.tet = tetrahedrontraverse(); } -} // mesh output - Msg::Info("Reconstruct time : %g sec",Cpu()-t_start); + } // mesh output + if (returnValue)Msg::Info("Reconstruct time : %g sec (mesh is Delaunay)",Cpu()-t_start); + else Msg::Info("Reconstruct time : %g sec (mesh is not Delaunay)",Cpu()-t_start); + return returnValue; } void terminateBoundaryRecovery(void *, int exitcode) diff --git a/Mesh/meshGRegionBoundaryRecovery.h b/Mesh/meshGRegionBoundaryRecovery.h index 0023617e71..0bfc189d40 100644 --- a/Mesh/meshGRegionBoundaryRecovery.h +++ b/Mesh/meshGRegionBoundaryRecovery.h @@ -743,7 +743,7 @@ class meshGRegionBoundaryRecovery { // Mesh optimize long lawsonflip3d(flipconstraints *fc); - void recoverdelaunay(); + bool recoverdelaunay(); int gettetrahedron(point, point, point, point, triface *); long improvequalitybyflips(); int smoothpoint(point smtpt, arraypool*, int ccw, optparameters *opm); @@ -874,7 +874,7 @@ class meshGRegionBoundaryRecovery { void unifysubfaces(face *f1, face *f2); void unifysegments(); void jettisonnodes(); - void reconstructmesh(GRegion *_gr); + bool reconstructmesh(GRegion *_gr); }; void terminateBoundaryRecovery(void *, int exitcode); diff --git a/benchmarks/3d/Cube-01.geo b/benchmarks/3d/Cube-01.geo index 239b5af5e4..da9b61afa8 100644 --- a/benchmarks/3d/Cube-01.geo +++ b/benchmarks/3d/Cube-01.geo @@ -18,4 +18,4 @@ Line Loop(5) = {2,3,4,1}; Plane Surface(6) = {5}; Extrude Surface { 6, {0,0.0,1} }; //Characteristic Length {10} = lc/100; -Physical Point(1)={1 }; +//Physical Point(1)={1 }; -- GitLab