diff --git a/CMakeLists.txt b/CMakeLists.txt index d3be9d1a18d77da079083c2c4fb1d18bd3be7317..58386f088ce55ddc8907232a3877af90457fc1b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ option(ENABLE_GMM "Enable GMM linear algebra solvers" ON) option(ENABLE_GRAPHICS "Compile-in OpenGL graphics even if there is no GUI" OFF) option(ENABLE_KBIPACK "Enable Kbipack for homology solver" ON) option(ENABLE_LUA "Enable the Programming Language Lua " ON) +option(ENABLE_MATCH "Enable Minimum cost perfect matching algo" ON) option(ENABLE_MATHEX "Enable MathEx expression parser" ON) option(ENABLE_MED "Enable MED mesh and post-processing file formats" ON) option(ENABLE_MESH "Build the mesh module" ON) @@ -700,6 +701,22 @@ if(ENABLE_ACIS) endif(ACIS_LIB) endif(ENABLE_ACIS) +if(ENABLE_MATCH) + find_library(MATCH_LIB match PATH_SUFFIXES) + find_library(CONCORDE_LIB concorde PATH_SUFFIXES) + if(MATCH_LIB AND CONCORDE_LIB) + find_path(CONCORDE_INC "concorde.h" PATH_SUFFIXES) + find_path(MATCH_INC "match.h" PATH_SUFFIXES) + if(MATCH_INC AND CONCORDE_INC) + set_config_option(HAVE_MATCH "Match") + list(APPEND EXTERNAL_LIBRARIES ${MATCH_LIB}) + list(APPEND EXTERNAL_LIBRARIES ${CONCORDE_LIB}) + list(APPEND EXTERNAL_INCLUDES ${MATCH_INC}) + list(APPEND EXTERNAL_INCLUDES ${CONCORDE_INC}) + endif(MATCH_INC AND CONCORDE_INC) + endif(MATCH_LIB AND CONCORDE_LIB) +endif(ENABLE_MATCH) + if(ENABLE_OSMESA) find_library(OSMESA_LIB OSMesa) if(OSMESA_LIB) diff --git a/Common/GmshConfig.h.in b/Common/GmshConfig.h.in index 5728ace40ee1da111c5e373de05b24829670a7a6..48ff64e93e7b8b9fc58bd942cda662f414a92e64 100644 --- a/Common/GmshConfig.h.in +++ b/Common/GmshConfig.h.in @@ -26,6 +26,7 @@ #cmakedefine HAVE_LIBPNG #cmakedefine HAVE_LIBZ #cmakedefine HAVE_LUA +#cmakedefine HAVE_MATCH #cmakedefine HAVE_MATHEX #cmakedefine HAVE_MED #cmakedefine HAVE_MESH diff --git a/Common/GmshDefines.h b/Common/GmshDefines.h index 6baf3c1854d4bd34e878c1c68a11b68236a9e46b..64f8d3233c2086061d657cc9252887e2e2a601dc 100644 --- a/Common/GmshDefines.h +++ b/Common/GmshDefines.h @@ -133,6 +133,9 @@ #define MSH_TET_165 73 #define MSH_TET_220 74 #define MSH_TET_286 75 +#define MSH_HEX_64 76 +#define MSH_HEX_125 77 +#define MSH_HEX_196 78 #define MSH_NUM_TYPE 75 diff --git a/Common/Options.cpp b/Common/Options.cpp index 5667a5eac06b4523f65d60ca6e4414e056ebff43..f7373a8978a2c447af73ee41fb215748401dd7d1 100644 --- a/Common/Options.cpp +++ b/Common/Options.cpp @@ -5609,7 +5609,7 @@ double opt_mesh_algo_subdivide(OPT_ARGS_NUM) if(action & GMSH_SET){ CTX::instance()->mesh.algoSubdivide = (int)val; if(CTX::instance()->mesh.algoSubdivide < 0 && - CTX::instance()->mesh.algoSubdivide > 2) + CTX::instance()->mesh.algoSubdivide > 3) CTX::instance()->mesh.algoSubdivide = 0; } #if defined(HAVE_FLTK) diff --git a/Fltk/optionWindow.cpp b/Fltk/optionWindow.cpp index a9be8070596ef5552313279e778d0fdea455894e..18e2f006ee8e5054c593a3cb2e9e8f403674f222 100644 --- a/Fltk/optionWindow.cpp +++ b/Fltk/optionWindow.cpp @@ -1971,6 +1971,7 @@ optionWindow::optionWindow(int deltaFontSize) {"None", 0, 0, 0}, {"All Quads", 0, 0, 0}, {"All Hexas", 0, 0, 0}, + {"All Quads (Blossom)", 0, 0, 0}, {0} }; diff --git a/Geo/GFace.h b/Geo/GFace.h index e1c39e80bace0fdb7adac42663026903a14eb0d0..71e76f2cce065b6f61d8b4a041030d130c23ce10 100644 --- a/Geo/GFace.h +++ b/Geo/GFace.h @@ -316,6 +316,9 @@ class GFace : public GEntity // periodic counterparts of edges std::map<int,int> edgeCounterparts; + + // tells if it's a sphere, and if it is, returns parameters + virtual bool isSphere (double &radius, SPoint3 ¢er) const {return false;} }; #endif diff --git a/Geo/GFaceCompound.cpp b/Geo/GFaceCompound.cpp index 2dcaabc5d0551d3b1dfe11e08c909a1c17518f29..21313830190177a37eec3c0b7ce45da6610346fd 100644 --- a/Geo/GFaceCompound.cpp +++ b/Geo/GFaceCompound.cpp @@ -2022,7 +2022,7 @@ double GFaceCompound::checkAspectRatio() const void GFaceCompound::coherenceNormals() { - + return; Msg::Info("Re-orient all triangles (face normals) coherently"); std::map<MEdge, std::set<MTriangle*>, Less_Edge > edge2tris; diff --git a/Geo/MElement.h b/Geo/MElement.h index 4b0bcb558599130b7cb7037c559309a3483e1373..ee26dfce960c6121cb0f42ae4e45de6305f058d0 100644 --- a/Geo/MElement.h +++ b/Geo/MElement.h @@ -72,9 +72,10 @@ class MElement virtual char getVisibility() const; virtual void setVisibility(char val){ _visible = val; } - // get the vertices + // get & set the vertices virtual int getNumVertices() const = 0; virtual MVertex *getVertex(int num) = 0; + virtual void setVertex(int num, MVertex *v) {throw;} // give an MVertex as input and get its local number virtual void getVertexInfo(const MVertex * vertex, int &ithVertex) const diff --git a/Geo/MQuadrangle.cpp b/Geo/MQuadrangle.cpp index bc3b8fd609304a72834f1062fa56df12911ee65a..733d765819254c213dd8436c9856927339ca7089 100644 --- a/Geo/MQuadrangle.cpp +++ b/Geo/MQuadrangle.cpp @@ -215,6 +215,32 @@ void MQuadrangle::getIntegrationPoints(int pOrder, int *npts, IntPt **pts) *pts = getGQQPts(pOrder); } +double angle3Points(MVertex *p1, MVertex *p2, MVertex *p3); + +double MQuadrangle::etaShapeMeasure(){ + double a1 = 180 * angle3Points(_v[0], _v[1], _v[2]) / M_PI; + double a2 = 180 * angle3Points(_v[1], _v[2], _v[3]) / M_PI; + double a3 = 180 * angle3Points(_v[2], _v[3], _v[0]) / M_PI; + double a4 = 180 * angle3Points(_v[3], _v[0], _v[1]) / M_PI; + +// if (fabs(a1+a2+a3+a4 - 360) > 1) { +// return -1.0; +// } + + a1 = std::min(180.,a1); + a2 = std::min(180.,a2); + a3 = std::min(180.,a3); + a4 = std::min(180.,a4); + double angle = fabs(90. - a1); + angle = std::max(fabs(90. - a2),angle); + angle = std::max(fabs(90. - a3),angle); + angle = std::max(fabs(90. - a4),angle); + + return 1.-angle/90; + +} + + double MQuadrangle::distoShapeMeasure() { #if defined(HAVE_MESH) diff --git a/Geo/MQuadrangle.h b/Geo/MQuadrangle.h index 4684e2b0c7a1b9012d1596517de83fc1026af2ab..76d3afcfafba93c842c52ff771bef7a5f1736bad 100644 --- a/Geo/MQuadrangle.h +++ b/Geo/MQuadrangle.h @@ -51,9 +51,11 @@ class MQuadrangle : public MElement { for(int i = 0; i < 4; i++) _v[i] = v[i]; } ~MQuadrangle(){} + virtual double etaShapeMeasure(); virtual int getDim() const { return 2; } virtual int getNumVertices() const { return 4; } virtual MVertex *getVertex(int num){ return _v[num]; } + virtual void setVertex(int num, MVertex *v){_v[num]=v;} virtual MVertex *getVertexMED(int num) { static const int map[4] = {0, 3, 2, 1}; diff --git a/Geo/MTriangle.cpp b/Geo/MTriangle.cpp index 90493793f5e8eab95cbe7d0b55785af81060026d..faa771b077489e80612b92cc90fcd4e16907c089 100644 --- a/Geo/MTriangle.cpp +++ b/Geo/MTriangle.cpp @@ -56,6 +56,19 @@ double MTriangle::angleShapeMeasure() #endif } +double angle3Points(MVertex *p1, MVertex *p2, MVertex *p3); + +double MTriangle::etaShapeMeasure() +{ + double a1 = 180 * angle3Points(_v[0], _v[1], _v[2]) / M_PI; + double a2 = 180 * angle3Points(_v[1], _v[2], _v[0]) / M_PI; + double a3 = 180 * angle3Points(_v[2], _v[0], _v[1]) / M_PI; + double angle = fabs(60. - a1); + angle = std::max(fabs(60. - a2),angle); + angle = std::max(fabs(60. - a3),angle); + return 1.-angle/60; +} + double MTriangle::gammaShapeMeasure() { #if defined(HAVE_MESH) diff --git a/Geo/MTriangle.h b/Geo/MTriangle.h index 53faaf9e320aecbe03c30eb812cf99df4490ca83..60be6b993589d8ea37711b51b893a94a8b71bcd9 100644 --- a/Geo/MTriangle.h +++ b/Geo/MTriangle.h @@ -50,6 +50,7 @@ class MTriangle : public MElement { } ~MTriangle(){} virtual int getDim() const { return 2; } + virtual double etaShapeMeasure(); virtual double gammaShapeMeasure(); virtual double distoShapeMeasure(); virtual double getInnerRadius(); diff --git a/Geo/OCCFace.cpp b/Geo/OCCFace.cpp index b72cdb024148ff82d82662010ab15456f19f9a5a..55dbff43057d25df637b4b8952b5310f79de74f3 100644 --- a/Geo/OCCFace.cpp +++ b/Geo/OCCFace.cpp @@ -94,6 +94,8 @@ void OCCFace::setup() umax += fabs(du) / 100.0; vmax += fabs(dv) / 100.0; occface = BRep_Tool::Surface(s); + // specific parametrization for a sphere. + _isSphere = isSphere(_radius,_center); } Range<double> OCCFace::parBounds(int i) const @@ -476,4 +478,22 @@ void OCCFace::replaceEdgesInternal(std::list<GEdge*> &new_edges) setup(); } +bool OCCFace::isSphere (double &radius, SPoint3 ¢er) const{ + switch(geomType()){ + case GEntity::Sphere: + { + radius = Handle(Geom_SphericalSurface)::DownCast(occface)->Radius(); + gp_Ax3 pos = Handle(Geom_SphericalSurface)::DownCast(occface)->Position(); + gp_Ax1 axis = pos.Axis(); + gp_Pnt loc = axis.Location(); + center = SPoint3(loc.X(),loc.Y(),loc.Z()); + } + return true; + default: + return false; + } + +} + + #endif diff --git a/Geo/OCCFace.h b/Geo/OCCFace.h index 453602116fbcef84603093705065f3156d55b9e2..7eed8c4ae299d11c429dd43e6e3ce3e89a8b47d7 100644 --- a/Geo/OCCFace.h +++ b/Geo/OCCFace.h @@ -25,6 +25,9 @@ class OCCFace : public GFace { bool buildSTLTriangulation(bool force=false); void replaceEdgesInternal (std::list<GEdge*> &); void setup(); + bool _isSphere; + double _radius; + SPoint3 _center; public: OCCFace(GModel *m, TopoDS_Face s, int num); virtual ~OCCFace(){} @@ -46,6 +49,8 @@ class OCCFace : public GFace { surface_params getSurfaceParams() const; TopoDS_Face getTopoDS_Face () {return s;} TopoDS_Face getTopoDS_FaceOld () {return _replaced;} + // tells if it's a sphere, and if it is, returns parameters + virtual bool isSphere (double &radius, SPoint3 ¢er) const; }; GFace *getOCCFaceByNativePtr(GModel *model, TopoDS_Face toFind); diff --git a/Mesh/meshGEdge.cpp b/Mesh/meshGEdge.cpp index c3345d1eefc12ca59716add9d64fa17801969ea9..655e1e6f40ac1e9a63c00c175c65ee4782f78c63 100644 --- a/Mesh/meshGEdge.cpp +++ b/Mesh/meshGEdge.cpp @@ -383,6 +383,8 @@ void meshGEdge::operator() (GEdge *ge) } N = std::max(ge->minimumMeshSegments() + 1, (int)(a + 1.)); } + + if(CTX::instance()->mesh.algoSubdivide == 3 && N%2 == 0)N++; // printFandPrimitive(ge->tag(),Points); diff --git a/Mesh/meshGFace.cpp b/Mesh/meshGFace.cpp index 40eb85be8e9d85b6a05321b04fc8090feba8c1da..44c8a4ffed264be34fca1fab11a7fccad46bc92a 100644 --- a/Mesh/meshGFace.cpp +++ b/Mesh/meshGFace.cpp @@ -296,6 +296,9 @@ static void remeshUnrecoveredEdges(std::map<MVertex*, BDS_Point*> &recoverMapInv static bool algoDelaunay2D(GFace *gf) { + // HACK REMOVE ME + if (gf->geomType() == GEntity::CompoundSurface)return true; + if(noSeam(gf) && (CTX::instance()->mesh.algo2d == ALGO_2D_DELAUNAY || CTX::instance()->mesh.algo2d == ALGO_2D_BAMG || CTX::instance()->mesh.algo2d == ALGO_2D_FRONTAL)) @@ -870,7 +873,8 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER, // BDS mesh is passed in order not to recompute local coordinates of // vertices if(algoDelaunay2D(gf)){ - if(CTX::instance()->mesh.algo2d == ALGO_2D_FRONTAL) + if(CTX::instance()->mesh.algo2d == ALGO_2D_FRONTAL || + gf->geomType() == GEntity::CompoundSurface) bowyerWatsonFrontal(gf); else if(CTX::instance()->mesh.algo2d == ALGO_2D_DELAUNAY) bowyerWatson(gf); @@ -1450,7 +1454,8 @@ static bool meshGeneratorPeriodic(GFace *gf, bool debug = true) } if(algoDelaunay2D(gf)){ - if(CTX::instance()->mesh.algo2d == ALGO_2D_FRONTAL) + if(CTX::instance()->mesh.algo2d == ALGO_2D_FRONTAL || + gf->geomType() == GEntity::CompoundSurface) bowyerWatsonFrontal(gf); else if(CTX::instance()->mesh.algo2d == ALGO_2D_DELAUNAY) bowyerWatson(gf); @@ -1712,6 +1717,15 @@ void partitionAndRemesh(GFaceCompound *gf) } gf->triangles.push_back(new MTriangle(v[0], v[1], v[2])); } + for(unsigned int j = 0; j < gfc->quadrangles.size(); ++j){ + MQuadrangle *q = gfc->quadrangles[j]; + std::vector<MVertex *> v(4); + for(int k = 0; k < 4; k++){ + v[k] = q->getVertex(k); + allNod.insert(v[k]); + } + gf->quadrangles.push_back(new MQuadrangle(v[0], v[1], v[2], v[3])); + } } diff --git a/Mesh/meshGFaceOptimize.cpp b/Mesh/meshGFaceOptimize.cpp index 0d6279373241e387e66c0a162aa0af26494b8042..e4b602c2acd51f201de93d9328016af747ec5248 100644 --- a/Mesh/meshGFaceOptimize.cpp +++ b/Mesh/meshGFaceOptimize.cpp @@ -8,6 +8,7 @@ #include "GFace.h" #include "GEdge.h" #include "GVertex.h" +#include "GModel.h" #include "MVertex.h" #include "MTriangle.h" #include "MQuadrangle.h" @@ -15,6 +16,29 @@ #include "Numeric.h" #include "GmshMessage.h" #include "Generator.h" +#include "Context.h" + +#ifdef HAVE_MATCH +extern "C" int FAILED_NODE; +extern "C" struct CCdatagroup; +extern "C" int perfect_match +(int ncount, CCdatagroup *dat, int ecount, + int **elist, int **elen, char *blo_filename, + char *mat_filename, int just_fractional, int no_fractional, + int use_all_trees, int partialprice, + double *totalzeit) ; +#endif + +double angle3Points(MVertex *p1, MVertex *p2, MVertex *p3) +{ + SVector3 a(p1->x() - p2->x(), p1->y() - p2->y(), p1->z() - p2->z()); + SVector3 b(p3->x() - p2->x(), p3->y() - p2->y(), p3->z() - p2->z()); + SVector3 c = crossprod(a, b); + double sinA = c.norm(); + double cosA = dot(a, b); + // printf("%d %d %d -> %g %g\n",p1->iD,p2->iD,p3->iD,cosA,sinA); + return atan2 (sinA, cosA); +} edge_angle::edge_angle(MVertex *_v1, MVertex *_v2, MElement *t1, MElement *t2) : v1(_v1), v2(_v2) @@ -248,21 +272,26 @@ void buildListOfEdgeAngle(e2t_cont adj, std::vector<edge_angle> &edges_detected, std::sort(edges_detected.begin(), edges_detected.end()); } -void parametricCoordinates(MElement *t, GFace *gf, double u[4], double v[4]) +void parametricCoordinates(MElement *t, GFace *gf, double u[4], double v[4], MVertex *close = 0) { for(int j = 0; j < t->getNumVertices(); j++){ MVertex *ver = t->getVertex(j); - SPoint2 param; - reparamMeshVertexOnFace(ver, gf, param); + SPoint2 param, dummy; + if (!close)reparamMeshVertexOnFace(ver, gf, param); + else reparamMeshEdgeOnFace(ver, close, gf, param, dummy); u[j] = param[0]; v[j] = param[1]; } } -double surfaceTriangleUV(MElement *t,GFace *gf){ +double surfaceFaceUV(MElement *t,GFace *gf){ double u[4],v[4]; parametricCoordinates(t,gf,u,v); - return 0.5*fabs((u[1]-u[0])*(v[2]-v[0])-(u[2]-u[0])*(v[1]-v[0])); + if (t->getNumVertices() == 3) + return 0.5*fabs((u[1]-u[0])*(v[2]-v[0])-(u[2]-u[0])*(v[1]-v[0])); + else + return 0.5*fabs((u[1]-u[0])*(v[2]-v[0])-(u[2]-u[0])*(v[1]-v[0])) + + 0.5*fabs((u[3]-u[2])*(v[0]-v[2])-(u[0]-u[2])*(v[3]-v[2])) ; } double surfaceTriangleUV(MVertex *v1, MVertex *v2, MVertex *v3, @@ -297,7 +326,7 @@ int _removeFourTrianglesNodes(GFace *gf,bool replace_by_quads) } int j; - surfaceRef+=surfaceTriangleUV(lt[i],gf); + surfaceRef+=surfaceFaceUV(lt[i],gf); for(j=0;j<3;j++) { if(lt[i]->getVertex(j)==it->first) { edges[i][0] = lt[i]->getVertex((j+1)%3); @@ -333,7 +362,7 @@ int _removeFourTrianglesNodes(GFace *gf,bool replace_by_quads) double surf[4],qual[4]; for(int i=0;i<4;i++){ newt[i] = new MTriangle(edges[i][0],edges[(i+1)%4][0],edges[(i+2)%4][0]); - surf[i]=surfaceTriangleUV(newt[i],gf); + surf[i]=surfaceFaceUV(newt[i],gf); qual[i]=qmTriangle(newt[i],QMTRI_RHO); } double q02=(fabs((surf[0]+surf[2]-surfaceRef)/surfaceRef)<1e-8) ? std::min(qual[0],qual[2]) : -1; @@ -380,6 +409,378 @@ void removeFourTrianglesNodes(GFace *gf,bool replace_by_quads) while(_removeFourTrianglesNodes(gf,replace_by_quads)); } +/* + + +--------+ + | /| + | / | + | + | + | / | + |/ | + +--------+ + +*/ + +static int _removeTwoQuadsNodes(GFace *gf) +{ + v2t_cont adj; + buildVertexToElement(gf->triangles,adj); + buildVertexToElement(gf->quadrangles,adj); + v2t_cont :: iterator it = adj.begin(); + std::set<MElement*> touched; + std::set<MVertex*> vtouched; + while (it != adj.end()) { + bool skip=false; + double surfaceRef=0; + if(it->second.size()==2 && it->first->onWhat()->dim() == 2) { + const std::vector<MElement*> < = it->second; + MElement *q1 = it->second[0]; + MElement *q2 = it->second[1]; + if (q1->getNumVertices() == 4 && + q2->getNumVertices() == 4 && + touched.find(q1) == touched.end() && touched.find(q2) == touched.end()){ + touched.insert(q1); + touched.insert(q2); + int comm = 0; + for (int i=0;i<4;i++){ + if (q1->getVertex(i) == it->first){ + comm = i; + break; + } + } + MVertex *v1 = q1->getVertex((comm+1)%4); + MVertex *v2 = q1->getVertex((comm+2)%4); + MVertex *v3 = q1->getVertex((comm+3)%4); + MVertex *v4 = 0; + for (int i=0;i<4;i++){ + if (q2->getVertex(i) != v1 && q2->getVertex(i) != v2 && + q2->getVertex(i) != v3 && q2->getVertex(i) != it->first){ + v4 = q2->getVertex(i); + break; + } + } + gf->quadrangles.push_back(new MQuadrangle(v1,v2,v3,v4)); + vtouched.insert(it->first); + } + } + it++; + } + std::vector<MQuadrangle*> quadrangles2; + for(unsigned int i = 0; i < gf->quadrangles.size(); i++){ + if(touched.find(gf->quadrangles[i]) == touched.end()){ + quadrangles2.push_back(gf->quadrangles[i]); + } + else { + delete gf->quadrangles[i]; + } + } + gf->quadrangles = quadrangles2; + + std::vector<MVertex*> mesh_vertices2; + for(unsigned int i = 0; i < gf->mesh_vertices.size(); i++){ + if(vtouched.find(gf->mesh_vertices[i]) == vtouched.end()){ + mesh_vertices2.push_back(gf->mesh_vertices[i]); + } + else { + delete gf->mesh_vertices[i]; + } + } + gf->mesh_vertices = mesh_vertices2; + + return vtouched.size(); +} + +int removeTwoQuadsNodes(GFace *gf){ + int nbRemove = 0; + while(1){ + int x = _removeTwoQuadsNodes(gf); + if (!x)break; + nbRemove += x; + } + Msg::Debug("%i two-quadrangles vertices removed",nbRemove); + return nbRemove; +} + + +static bool _isItAGoodIdeaToCollapseThatVertex (GFace *gf, + std::vector<MElement*> &e1, + std::vector<MElement*> &e2, + MElement *q, + MVertex *v1, + MVertex *v2, + MVertex *v, + double FACT){ + + double surface_old = surfaceFaceUV(q,gf); + double surface_new = 0; + // double worst_quality_old = q->etaShapeMeasure(); + // double worst_quality_new = 1.0; + + double x = v->x(); + double y = v->y(); + double z = v->z(); + double uu,vv; + v->getParameter(0,uu); + v->getParameter(1,vv); + SPoint2 p1,p2; + reparamMeshEdgeOnFace(v1,v2,gf,p1,p2); + SPoint2 p =(p1+p2)*0.5; + GPoint gp = gf->point(p); + // v->setXYZ(gp.x(),gp.y(),gp.z()); + // v->setParameter(0,p.x()); + // v->setParameter(1,p.y()); + + for (int j=0;j<e1.size();++j){ + surface_old += surfaceFaceUV(e1[j],gf); + // worst_quality_old = std::min(worst_quality_old,e1[j]-> etaShapeMeasure()); + for (int k=0;k<e1[j]->getNumVertices();k++){ + if (e1[j]->getVertex(k) == v1 && e1[j] != q) + e1[j]->setVertex(k,v); + } + surface_new += surfaceFaceUV(e1[j],gf); + // worst_quality_new = std::min(worst_quality_new,e1[j]-> etaShapeMeasure()); + for (int k=0;k<e1[j]->getNumVertices();k++){ + if (e1[j]->getVertex(k) == v && e1[j] != q) + e1[j]->setVertex(k,v1); + } + } + + for (int j=0;j<e2.size();++j){ + surface_old += surfaceFaceUV(e2[j],gf); + // worst_quality_old = std::min(worst_quality_old,e2[j]-> etaShapeMeasure()); + for (int k=0;k<e2[j]->getNumVertices();k++){ + if (e2[j]->getVertex(k) == v2 && e2[j] != q) + e2[j]->setVertex(k,v); + } + surface_new += surfaceFaceUV(e2[j],gf); + // worst_quality_new = std::min(worst_quality_new,e2[j]-> etaShapeMeasure()); + for (int k=0;k<e2[j]->getNumVertices();k++){ + if (e2[j]->getVertex(k) == v && e2[j] != q) + e2[j]->setVertex(k,v2); + } + } + + if ( fabs (surface_old - surface_new ) > 1.e-10 * surface_old) { + // || FACT*worst_quality_new < worst_quality_old ) { + v->setXYZ(x,y,z); + v->setParameter(0,uu); + v->setParameter(1,vv); + return false; + } + + return true; + +} + + +static bool _isItAGoodIdeaToMoveThatVertex (GFace *gf, + std::vector<MElement*> &e1, + MVertex *v1, + const SPoint2 &before, + const SPoint2 &after){ + + double surface_old = 0; + double surface_new = 0; + + for (int j=0;j<e1.size();++j) + surface_old += surfaceFaceUV(e1[j],gf); + + v1->setParameter(0,after.x()); + v1->setParameter(1,after.y()); + + for (int j=0;j<e1.size();++j) + surface_new += surfaceFaceUV(e1[j],gf); + + v1->setParameter(0,before.x()); + v1->setParameter(1,before.y()); + + if ( surface_new - surface_old > 1.e-10 * surface_old) { + return false; + } + return true; + +} + + +static int _quadWithOneVertexOnBoundary (GFace *gf, + std::set<MVertex*> &touched, + std::set<MElement*> &diamonds, + std::set<MVertex*> &deleted, + std::vector<MElement*> &e2, + std::vector<MElement*> &e4, + std::vector<MElement*> &e1, + MQuadrangle *q, + MVertex *v1, + MVertex *v2, + MVertex *v3, + MVertex *v4){ + // return 0; + if (v1->onWhat()->dim() < 2 && + v2->onWhat()->dim() == 2 && + v3->onWhat()->dim() == 2 && + v4->onWhat()->dim() == 2 && + e2.size() < 5 && + e4.size() < 5 && + ( _isItAGoodIdeaToCollapseThatVertex (gf, e2, e4, q, v2, v4, v4,12.0) || + _isItAGoodIdeaToCollapseThatVertex (gf, e2, e4, q, v2, v4, v2,12.0))){ + touched.insert(v1); + touched.insert(v2); + touched.insert(v3); + touched.insert(v4); + /* + std::vector<MVertex*> line; + for (int j=0;j<e1.size();j++){ + for (int k=0;k<e1[j]->getNumVertices();k++){ + if (e1[j]->getVertex(k) != v1 && e1[j] != q && + e1[j]->getVertex(k)->onWhat()->dim() < 2){ + line.push_back(e1[j]->getVertex(k)); + } + } + } + // do not collapse if it's a corner + if (line.size() == 2){ + if (fabs(angle3Points(line[0],v1,line[1]) - M_PI) > 3*M_PI/8.){ + // printf("coucou %g\n",angle3Points(line[0],v1,line[1])*180./M_PI); + return 0; + } + } + */ + // if (line.size() == 2)printf("caca\n"); + // else printf("hohcozbucof\n"); + for (int j=0;j<e2.size();++j){ + for (int k=0;k<e2[j]->getNumVertices();k++){ + if (e2[j]->getVertex(k) == v2 && e2[j] != q) + e2[j]->setVertex(k,v4); + } + } + diamonds.insert(q); + deleted.insert(v2); + return 1; + } + return 0; +} + +static int _countCommon(std::vector<MElement*> &a, std::vector<MElement*> &b) { + int count = 0; + for (int i=0;i<a.size();i++){ + for (int j=0;j<b.size();j++){ + if (a[i]==b[j])count++; + } + } + return count; +} + +static int _removeDiamonds(GFace *gf) +{ + v2t_cont adj; + // buildVertexToElement(gf->triangles,adj); + buildVertexToElement(gf->quadrangles,adj); + std::set<MElement*> diamonds; + std::set<MVertex*> touched; + std::set<MVertex*> deleted; + std::vector<MVertex*> mesh_vertices2; + std::vector<MQuadrangle*> quadrangles2; + + for(unsigned int i = 0; i < gf->triangles.size(); i++){ + touched.insert(gf->triangles[i]->getVertex(0)); + touched.insert(gf->triangles[i]->getVertex(1)); + touched.insert(gf->triangles[i]->getVertex(2)); + } + + + for(unsigned int i = 0; i < gf->quadrangles.size(); i++){ + MQuadrangle *q = gf->quadrangles[i]; + MVertex *v1 = q->getVertex(0); + MVertex *v2 = q->getVertex(1); + MVertex *v3 = q->getVertex(2); + MVertex *v4 = q->getVertex(3); + v2t_cont::iterator it1 = adj.find(v1); + v2t_cont::iterator it2 = adj.find(v2); + v2t_cont::iterator it3 = adj.find(v3); + v2t_cont::iterator it4 = adj.find(v4); + if (touched.find(v1) == touched.end() && + touched.find(v2) == touched.end() && + touched.find(v3) == touched.end() && + touched.find(v4) == touched.end() ) { + if (_quadWithOneVertexOnBoundary (gf,touched,diamonds,deleted,it2->second,it4->second,it1->second,q,v1,v2,v3,v4)){} + else if (_quadWithOneVertexOnBoundary (gf,touched,diamonds,deleted,it3->second,it1->second,it2->second,q,v2,v3,v4,v1)){} + else if (_quadWithOneVertexOnBoundary (gf,touched,diamonds,deleted,it4->second,it2->second,it3->second,q,v3,v4,v1,v2)){} + else if (_quadWithOneVertexOnBoundary (gf,touched,diamonds,deleted,it1->second,it3->second,it4->second,q,v4,v1,v2,v3)){} + else if (v2->onWhat()->dim() == 2 && + v4->onWhat()->dim() == 2 && + v1->onWhat()->dim() == 2 && + v3->onWhat()->dim() == 2 && + it1->second.size() ==3 && it3->second.size() == 3 && + (_isItAGoodIdeaToCollapseThatVertex (gf, it1->second, it3->second, q, v1, v3, v3,10.) || + _isItAGoodIdeaToCollapseThatVertex (gf, it1->second, it3->second, q, v1, v3, v1,10.) ) ){ + touched.insert(v1); + touched.insert(v2); + touched.insert(v3); + touched.insert(v4); + for (int j=0;j<it1->second.size();++j){ + for (int k=0;k<it1->second[j]->getNumVertices();k++){ + if (it1->second[j]->getVertex(k) == v1 && it1->second[j] != q) + it1->second[j]->setVertex(k,v3); + } + } + deleted.insert(v1); + diamonds.insert(q); + } + else if (v1->onWhat()->dim() == 2 && + v3->onWhat()->dim() == 2 && + v2->onWhat()->dim() == 2 && + v4->onWhat()->dim() == 2 && + it2->second.size() == 3 && it4->second.size() == 3 && + (_isItAGoodIdeaToCollapseThatVertex (gf, it2->second, it4->second, q, v2, v4, v4,10.) || + _isItAGoodIdeaToCollapseThatVertex (gf, it2->second, it4->second, q, v2, v4, v2,10.) ) ){ + touched.insert(v1); + touched.insert(v2); + touched.insert(v3); + touched.insert(v4); + for (int j=0;j<it2->second.size();++j){ + for (int k=0;k<it2->second[j]->getNumVertices();k++){ + if (it2->second[j]->getVertex(k) == v2 && it2->second[j] != q) + it2->second[j]->setVertex(k,v4); + } + } + deleted.insert(v2); + diamonds.insert(q); + } + else { + quadrangles2.push_back(q); + } + } + else{ + quadrangles2.push_back(q); + } + } + gf->quadrangles = quadrangles2; + + for(unsigned int i = 0; i < gf->mesh_vertices.size(); i++){ + if(deleted.find(gf->mesh_vertices[i]) == deleted.end()){ + mesh_vertices2.push_back(gf->mesh_vertices[i]); + } + else { + delete gf->mesh_vertices[i]; + } + } + gf->mesh_vertices = mesh_vertices2; + + return diamonds.size(); +} + +int removeDiamonds(GFace *gf){ + int nbRemove = 0; + while(1){ + int x = _removeDiamonds(gf); + if (!x)break; + nbRemove += x; + } + Msg::Debug("%i diamond quads removed",nbRemove); + return nbRemove; +} + + void laplaceSmoothing(GFace *gf) { v2t_cont adj; @@ -401,7 +802,7 @@ void laplaceSmoothing(GFace *gf) double pu[4], pv[4]; double fact = 0.0; for(unsigned int i = 0; i < lt.size(); i++){ - parametricCoordinates(lt[i], gf, pu, pv); + parametricCoordinates(lt[i], gf, pu, pv, ver); cu += (pu[0] + pu[1] + pu[2]); cv += (pv[0] + pv[1] + pv[2]); if(lt[i]->getNumVertices() == 4){ @@ -409,17 +810,22 @@ void laplaceSmoothing(GFace *gf) cv += pv[3]; } fact += lt[i]->getNumVertices(); - // have to test validity ! + } if(fact != 0.0){ - ver->setParameter(0, cu / fact); - ver->setParameter(1, cv / fact); - GPoint pt = gf->point(SPoint2(cu / fact, cv / fact)); - if(pt.succeeded()){ - ver->x() = pt.x(); - ver->y() = pt.y(); - ver->z() = pt.z(); - } + SPoint2 before(initu,initv); + SPoint2 after(cu / fact,cv / fact); + bool success = _isItAGoodIdeaToMoveThatVertex (gf, it->second, ver,before,after); + if (success){ + ver->setParameter(0, cu / fact); + ver->setParameter(1, cv / fact); + GPoint pt = gf->point(SPoint2(cu / fact, cv / fact)); + if(pt.succeeded()){ + ver->x() = pt.x(); + ver->y() = pt.y(); + ver->z() = pt.z(); + } + } } } ++it; @@ -428,6 +834,80 @@ void laplaceSmoothing(GFace *gf) } +int _edgeSwapQuadsForBetterQuality ( GFace *gf ) { + return 0; + e2t_cont adj; + // buildEdgeToElement(gf->triangles, adj); + buildEdgeToElement(gf->quadrangles, adj); + + std::set<MElement*>touched; + std::vector<MQuadrangle*>created; + std::set<MElement*>deleted; + + int COUNT = 0; + + for(e2t_cont::iterator it = adj.begin(); it!= adj.end(); ++it){ + if(it->second.second && + it->second.first->getNumVertices() == 4 && + it->second.second->getNumVertices() == 4){ + MVertex *v1 = it->first.getVertex(0); + MVertex *v2 = it->first.getVertex(1); + MElement *e1 = it->second.first; + MElement *e2 = it->second.second; + if (deleted.find(e1) == deleted.end() || + deleted.find(e2) == deleted.end()){ + MVertex *v12,*v11,*v22,*v21; + for (int i=0;i<4;i++){ + MEdge ed = e1->getEdge(i); + if (ed.getVertex(0) == v1 && ed.getVertex(1) != v2)v11 = ed.getVertex(1); + if (ed.getVertex(1) == v1 && ed.getVertex(0) != v2)v11 = ed.getVertex(0); + if (ed.getVertex(0) == v2 && ed.getVertex(1) != v1)v12 = ed.getVertex(1); + if (ed.getVertex(1) == v2 && ed.getVertex(0) != v1)v12 = ed.getVertex(0); + ed = e2->getEdge(i); + if (ed.getVertex(0) == v1 && ed.getVertex(1) != v2)v21 = ed.getVertex(1); + if (ed.getVertex(1) == v1 && ed.getVertex(0) != v2)v21 = ed.getVertex(0); + if (ed.getVertex(0) == v2 && ed.getVertex(1) != v1)v22 = ed.getVertex(1); + if (ed.getVertex(1) == v2 && ed.getVertex(0) != v1)v22 = ed.getVertex(0); + } + MQuadrangle *q1 = new MQuadrangle (v11,v22,v2,v12); + MQuadrangle *q2 = new MQuadrangle (v22,v11,v1,v21); + + double sold = surfaceFaceUV(e1,gf) + surfaceFaceUV(e2,gf); + double snew = surfaceFaceUV(q1,gf) + surfaceFaceUV(q2,gf); + + double min_quality_old = std::min(e1->etaShapeMeasure(),e2->etaShapeMeasure()); + double min_quality_new = std::min(q1->etaShapeMeasure(),q2->etaShapeMeasure()); + + // if (min_quality_old < min_quality_new){ + if (sold > 1.00001*snew){ + printf("%g %g\n",sold, snew); + deleted.insert(e1); + deleted.insert(e2); + created.push_back(q1); + created.push_back(q2); + COUNT++; + } + else{ + delete q1; + delete q2; + } + } + } + } + + for(unsigned int i = 0; i < gf->quadrangles.size(); i++){ + if(deleted.find(gf->quadrangles[i]) == deleted.end()){ + created.push_back(gf->quadrangles[i]); + } + else { + delete gf->quadrangles[i]; + } + } + gf->quadrangles = created; + Msg::Debug("%i swap quads performed",COUNT); + return COUNT; +} + bool edgeSwap(std::set<swapquad> &configs, MTri3 *t1, GFace *gf, int iLocalEdge, std::vector<MTri3*> &newTris, const swapCriterion &cr, const std::vector<double> &Us, const std::vector<double> &Vs, @@ -568,6 +1048,44 @@ bool edgeSwap(std::set<swapquad> &configs, MTri3 *t1, GFace *gf, int iLocalEdge, return true; } +int edgeSwapPass(GFace *gf, std::set<MTri3*, compareTri3Ptr> &allTris, + const swapCriterion &cr, + const std::vector<double> &Us, const std::vector<double> &Vs, + const std::vector<double> &vSizes, const std::vector<double> &vSizesBGM) +{ + typedef std::set<MTri3*, compareTri3Ptr> CONTAINER; + + int nbSwapTot = 0; + std::set<swapquad> configs; + for(int iter = 0; iter < 1200; iter++){ + int nbSwap = 0; + std::vector<MTri3*> newTris; + for(CONTAINER::iterator it = allTris.begin(); it != allTris.end(); ++it){ + if(!(*it)->isDeleted()){ + for(int i = 0; i < 3; i++){ + if(edgeSwap(configs, *it, gf, i, newTris, cr, Us, Vs, vSizes, vSizesBGM)){ + nbSwap++; + break; + } + } + } + else{ + delete *it; + CONTAINER::iterator itb = it; + ++it; + allTris.erase(itb); + if(it == allTris.end()) break; + } + } + allTris.insert(newTris.begin(), newTris.end()); + nbSwapTot += nbSwap; + if(nbSwap == 0) break; + } + return nbSwapTot; +} + + + inline double computeEdgeAdimLength(MVertex *v1, MVertex *v2, GFace *f, const std::vector<double> &Us, const std::vector<double> &Vs, @@ -592,136 +1110,6 @@ inline double computeEdgeAdimLength(MVertex *v1, MVertex *v2, GFace *f, return 2 * (l1 + l2) / (vSizesBGM[v1->getIndex()] + vSizesBGM[v2->getIndex()]); } -bool edgeSplit(const double lMax, MTri3 *t1, GFace *gf, int iLocalEdge, - std::vector<MTri3*> &newTris, const splitCriterion &cr, - std::vector<double> &Us, std::vector<double> &Vs, - std::vector<double> &vSizes, std::vector<double> &vSizesBGM) -{ - MTri3 *t2 = t1->getNeigh(iLocalEdge); - if(!t2) return false; - - MVertex *v1 = t1->tri()->getVertex(iLocalEdge == 0 ? 2 : iLocalEdge - 1); - MVertex *v2 = t1->tri()->getVertex(iLocalEdge % 3); - double edgeCenter[2] ={(Us[v1->getIndex()] + Us[v2->getIndex()]) * .5, - (Vs[v1->getIndex()] + Vs[v2->getIndex()]) * .5}; - - double al = computeEdgeAdimLength(v1, v2, gf,Us, Vs, vSizes, vSizesBGM); - if(al <= lMax) return false; - - MVertex *v3 = t1->tri()->getVertex((iLocalEdge + 1) % 3); - MVertex *v4 = 0; - for(int i = 0; i < 3; i++) - if(t2->tri()->getVertex(i) != v1 && t2->tri()->getVertex(i) != v2) - v4 = t2->tri()->getVertex(i); - - GPoint p = gf->point(edgeCenter[0], edgeCenter[1]); - MVertex *vnew = new MFaceVertex(p.x(), p.y(), p.z(), gf, - edgeCenter[0], edgeCenter[1]); - - MTriangle *t1b = new MTriangle(v1, vnew, v3); - MTriangle *t2b = new MTriangle(vnew, v2, v3); - MTriangle *t3b = new MTriangle(v1, v4, vnew); - MTriangle *t4b = new MTriangle(v4, v2, vnew); - - switch(cr){ - case SPCR_QUAL: - { - const double triQualityRef = std::min(qmTriangle(t1->tri(), QMTRI_RHO), - qmTriangle(t2->tri(), QMTRI_RHO)); - const double triQuality = - std::min(std::min(std::min(qmTriangle(t1b, QMTRI_RHO), - qmTriangle(t2b, QMTRI_RHO)), - qmTriangle(t3b, QMTRI_RHO)), - qmTriangle(t4b, QMTRI_RHO)); - if(triQuality < triQualityRef){ - delete t1b; - delete t2b; - delete t3b; - delete t4b; - delete vnew; - return false; - } - break; - } - case SPCR_ALLWAYS: - break; - default : - Msg::Error("Unknown swapping criterion"); - return false; - } - - gf->mesh_vertices.push_back(vnew); - vnew->setIndex(Us.size()); - double lcN = 0.5 * (vSizes[v1->getIndex()] + vSizes[v2->getIndex()] ); - double lcBGM = BGM_MeshSize(gf, edgeCenter[0], edgeCenter[1], p.x(), p.y(), p.z()); - - vSizesBGM.push_back(lcBGM); - vSizes.push_back(lcN); - Us.push_back(edgeCenter[0]); - Vs.push_back(edgeCenter[1]); - - std::list<MTri3*> cavity; - for(int i = 0; i < 3; i++){ - if(t1->getNeigh(i) && t1->getNeigh(i) != t2){ - bool found = false; - for(std::list<MTri3*>::iterator it = cavity.begin();it != cavity.end(); it++){ - if(*it == t1->getNeigh(i)) found = true; - } - if(!found) cavity.push_back(t1->getNeigh(i)); - } - } - for(int i = 0; i < 3; i++){ - if(t2->getNeigh(i) && t2->getNeigh(i) != t1){ - bool found = false; - for(std::list<MTri3*>::iterator it = cavity.begin(); it != cavity.end(); it++){ - if(*it == t2->getNeigh(i))found = true; - } - if(!found) cavity.push_back(t2->getNeigh(i)); - } - } - double lc1 = 0.3333333333 * (vSizes[t1b->getVertex(0)->getIndex()] + - vSizes[t1b->getVertex(1)->getIndex()] + - vSizes[t1b->getVertex(2)->getIndex()]); - double lcBGM1 = 0.3333333333 * (vSizesBGM[t1b->getVertex(0)->getIndex()] + - vSizesBGM[t1b->getVertex(1)->getIndex()] + - vSizesBGM[t1b->getVertex(2)->getIndex()]); - double lc2 = 0.3333333333 * (vSizes[t2b->getVertex(0)->getIndex()] + - vSizes[t2b->getVertex(1)->getIndex()] + - vSizes[t2b->getVertex(2)->getIndex()]); - double lcBGM2 = 0.3333333333 * (vSizesBGM[t2b->getVertex(0)->getIndex()] + - vSizesBGM[t2b->getVertex(1)->getIndex()] + - vSizesBGM[t2b->getVertex(2)->getIndex()]); - double lc3 = 0.3333333333 * (vSizes[t3b->getVertex(0)->getIndex()] + - vSizes[t3b->getVertex(1)->getIndex()] + - vSizes[t3b->getVertex(2)->getIndex()]); - double lcBGM3 = 0.3333333333 * (vSizesBGM[t3b->getVertex(0)->getIndex()] + - vSizesBGM[t3b->getVertex(1)->getIndex()] + - vSizesBGM[t3b->getVertex(2)->getIndex()]); - double lc4 = 0.3333333333 * (vSizes[t4b->getVertex(0)->getIndex()] + - vSizes[t4b->getVertex(1)->getIndex()] + - vSizes[t4b->getVertex(2)->getIndex()]); - double lcBGM4 = 0.3333333333 * (vSizesBGM[t4b->getVertex(0)->getIndex()] + - vSizesBGM[t4b->getVertex(1)->getIndex()] + - vSizesBGM[t4b->getVertex(2)->getIndex()]); - MTri3 *t1b3 = new MTri3(t1b, Extend1dMeshIn2dSurfaces() ? std::min(lc1, lcBGM1) : lcBGM1); - MTri3 *t2b3 = new MTri3(t2b, Extend1dMeshIn2dSurfaces() ? std::min(lc2, lcBGM2) : lcBGM2); - MTri3 *t3b3 = new MTri3(t3b, Extend1dMeshIn2dSurfaces() ? std::min(lc3, lcBGM3) : lcBGM3); - MTri3 *t4b3 = new MTri3(t4b, Extend1dMeshIn2dSurfaces() ? std::min(lc4, lcBGM4) : lcBGM4); - - cavity.push_back(t1b3); - cavity.push_back(t2b3); - cavity.push_back(t3b3); - cavity.push_back(t4b3); - t1->setDeleted(true); - t2->setDeleted(true); - connectTriangles(cavity); - newTris.push_back(t1b3); - newTris.push_back(t2b3); - newTris.push_back(t3b3); - newTris.push_back(t4b3); - - return true; -} void computeNeighboringTrisOfACavity(const std::vector<MTri3*> &cavity, std::vector<MTri3*> &outside) @@ -800,181 +1188,42 @@ bool buildVertexCavity(MTri3 *t, int iLocalVertex, MVertex **v1, } } -bool vertexCollapse(const double lMin, MTri3 *t1, GFace *gf, - int iLocalVertex, std::vector<MTri3*> &newTris, - std::vector<double> &Us, std::vector<double> &Vs, - std::vector<double> &vSizes, std::vector<double> &vSizesBGM) -{ - MVertex *v; - std::vector<MTri3*> cavity; - std::vector<MTri3*> outside; - std::vector<MVertex*> ring; - if(!buildVertexCavity(t1, iLocalVertex, &v, cavity, outside, ring)) return false; - - double l_min = lMin; - int iMin = -1; - for(unsigned int i = 0; i < ring.size(); i++){ - double l = computeEdgeAdimLength(v, ring[i], gf, Us, Vs, vSizes, vSizesBGM); - if(l < l_min){ - iMin = i; - } - } - if(iMin == -1) return false; - double surfBefore = 0.0; - for(unsigned int i = 0; i < ring.size(); i++){ - MVertex *v1 = ring[i]; - MVertex *v2 = ring[(i + 1) % ring.size()]; - surfBefore += surfaceTriangleUV(v1, v2, v, Us, Vs); - } - - double surfAfter = 0.0; - for(unsigned int i = 0; i < ring.size() - 2; i++){ - MVertex *v1 = ring[(iMin + 1 + i) % ring.size()]; - MVertex *v2 = ring[(iMin + 1 + i + 1) % ring.size()]; - double sAfter = surfaceTriangleUV(v1, v2, ring[iMin], Us, Vs); - double sBefore = surfaceTriangleUV(v1, v2, v, Us, Vs); - if(sAfter < 0.1 * sBefore) return false; - surfAfter += sAfter; - } +// split one triangle into 3 triangles then apply edge swop (or not) +// will do better (faster) soon, just to test +void _triangleSplit (GFace *gf, MElement *t, bool swop = false) { + MVertex *v1 = t->getVertex(0); + MVertex *v2 = t->getVertex(1); + MVertex *v3 = t->getVertex(2); + SPoint2 p1,p2,p3; - // printf("%12.5E %12.5E %d\n",surfBefore,surfAfter,iMin); - if(fabs(surfBefore - surfAfter) > 1.e-10*(surfBefore + surfAfter)) return false; - - for(unsigned int i = 0; i < ring.size() - 2; i++){ - MVertex *v1 = ring[(iMin + 1 + i) % ring.size()]; - MVertex *v2 = ring[(iMin + 1 + i + 1) % ring.size()]; - MTriangle *t = new MTriangle(v1,v2,ring[iMin]); - double lc = 0.3333333333 * (vSizes[t->getVertex(0)->getIndex()] + - vSizes[t->getVertex(1)->getIndex()] + - vSizes[t->getVertex(2)->getIndex()]); - double lcBGM = 0.3333333333 * (vSizesBGM[t->getVertex(0)->getIndex()] + - vSizesBGM[t->getVertex(1)->getIndex()] + - vSizesBGM[t->getVertex(2)->getIndex()]); - MTri3 *t3 = new MTri3(t, Extend1dMeshIn2dSurfaces() ? std::min(lc, lcBGM) : lcBGM); - // printf("Creation %p = %d %d %d\n",t3,v1->getIndex(),v2->getIndex(),ring[iMin]->getIndex()); - outside.push_back(t3); - newTris.push_back(t3); - } - for(unsigned int i = 0; i < cavity.size(); i++) - cavity[i]->setDeleted(true); - connectTriangles(outside); - return true; -} - -int edgeSwapPass(GFace *gf, std::set<MTri3*, compareTri3Ptr> &allTris, - const swapCriterion &cr, - const std::vector<double> &Us, const std::vector<double> &Vs, - const std::vector<double> &vSizes, const std::vector<double> &vSizesBGM) -{ - typedef std::set<MTri3*, compareTri3Ptr> CONTAINER; - - int nbSwapTot = 0; - std::set<swapquad> configs; - for(int iter = 0; iter < 1200; iter++){ - int nbSwap = 0; - std::vector<MTri3*> newTris; - for(CONTAINER::iterator it = allTris.begin(); it != allTris.end(); ++it){ - if(!(*it)->isDeleted()){ - for(int i = 0; i < 3; i++){ - if(edgeSwap(configs, *it, gf, i, newTris, cr, Us, Vs, vSizes, vSizesBGM)){ - nbSwap++; - break; - } - } - } - else{ - delete *it; - CONTAINER::iterator itb = it; - ++it; - allTris.erase(itb); - if(it == allTris.end()) break; - } - } - allTris.insert(newTris.begin(), newTris.end()); - nbSwapTot += nbSwap; - if(nbSwap == 0) break; - } - return nbSwapTot; -} - -int edgeSplitPass(double maxLC, GFace *gf, std::set<MTri3*,compareTri3Ptr> &allTris, - const splitCriterion &cr, - std::vector<double> &Us, std::vector<double> &Vs, - std::vector<double> &vSizes, std::vector<double> &vSizesBGM) -{ - typedef std::set<MTri3*, compareTri3Ptr> CONTAINER; - std::vector<MTri3*> newTris; - - int nbSplit = 0; + reparamMeshEdgeOnFace(v1, v2, gf, p1,p2); + reparamMeshEdgeOnFace(v1, v3, gf, p1,p3); - for(CONTAINER::iterator it = allTris.begin(); it != allTris.end(); ++it){ - if(!(*it)->isDeleted()){ - for(int i = 0; i < 3; i++){ - if(edgeSplit(maxLC, *it, gf, i, newTris, cr, Us, Vs, vSizes, vSizesBGM)) { - nbSplit++; - break; - } - } - } - else{ - CONTAINER::iterator itb = it; - delete *it; - ++it; - allTris.erase(itb); - if(it == allTris.end()) break; - } - } - printf("B %d %d tris ", (int)allTris.size(), (int)newTris.size()); - allTris.insert(newTris.begin(),newTris.end()); - printf("A %d %d tris\n", (int)allTris.size(), (int)newTris.size()); - return nbSplit; -} + SPoint2 np = (p1+p2+p3)*(1./3.0); -int edgeCollapsePass(double minLC, GFace *gf, std::set<MTri3*,compareTri3Ptr> &allTris, - std::vector<double> &Us, std::vector<double> &Vs, - std::vector<double> &vSizes, std::vector<double> &vSizesBGM) -{ - typedef std::set<MTri3*,compareTri3Ptr> CONTAINER; - std::vector<MTri3*> newTris; + GPoint gp = gf->point(np); - int nbCollapse = 0; - - for(CONTAINER::reverse_iterator it = allTris.rbegin(); it != allTris.rend(); ++it){ - if(!(*it)->isDeleted()){ - for(int i = 0; i < 3; i++){ - if(vertexCollapse(minLC, *it, gf, i, newTris, Us, Vs, vSizes, vSizesBGM)) { - nbCollapse++; - break; - } - } + MFaceVertex *fv = new MFaceVertex(gp.x(),gp.y(),gp.z(), + gf,np.x(),np.y()); + std::vector<MTriangle*> triangles2; + for(unsigned int i = 0; i < gf->triangles.size(); i++){ + if(gf->triangles[i] != t){ + triangles2.push_back(gf->triangles[i]); } -// else{ -// CONTAINER::reverse_iterator itb = it; -// delete *it; -// ++it; -// allTris.rerase(itb); -// if(it == allTris.rend())break; -// } -// if(nbCollapse == 114)break; - } - printf("B %d %d tris ", (int)allTris.size(), (int)newTris.size()); - allTris.insert(newTris.begin(),newTris.end()); - printf("A %d %d tris\n", (int)allTris.size(), (int)newTris.size()); - return nbCollapse; + } + delete t; + MTriangle *t1 = new MTriangle(v1,v2,fv); + MTriangle *t2 = new MTriangle(v2,v3,fv); + MTriangle *t3 = new MTriangle(v3,v1,fv); + gf->triangles = triangles2; + gf->triangles.push_back(t1); + gf->triangles.push_back(t2); + gf->triangles.push_back(t3); + gf->mesh_vertices.push_back(fv); } -double angle3Points(MVertex *p1, MVertex *p2, MVertex *p3) -{ - SVector3 a(p1->x() - p2->x(), p1->y() - p2->y(), p1->z() - p2->z()); - SVector3 b(p3->x() - p2->x(), p3->y() - p2->y(), p3->z() - p2->z()); - SVector3 c = crossprod(a, b); - double sinA = c.norm(); - double cosA = dot(a, b); - // printf("%d %d %d -> %g %g\n",p1->iD,p2->iD,p3->iD,cosA,sinA); - return atan2 (sinA, cosA); -} struct RecombineTriangle { @@ -1009,8 +1258,9 @@ struct RecombineTriangle } }; -static void _recombineIntoQuads(GFace *gf) +static int _recombineIntoQuads(GFace *gf, int recur_level, bool cubicGraph = 1) { + int success = 1; std::set<MVertex*> emb_edgeverts; { @@ -1029,23 +1279,226 @@ static void _recombineIntoQuads(GFace *gf) } } + { + std::list<GEdge*> _edges = gf->edges(); + std::list<GEdge*>::iterator ite = _edges.begin(); + while(ite != _edges.end()){ + if(!(*ite)->isMeshDegenerated()){ + if ((*ite)->isSeam(gf)){ + emb_edgeverts.insert((*ite)->mesh_vertices.begin(), + (*ite)->mesh_vertices.end() ); + emb_edgeverts.insert((*ite)->getBeginVertex()->mesh_vertices.begin(), + (*ite)->getBeginVertex()->mesh_vertices.end()); + emb_edgeverts.insert((*ite)->getEndVertex()->mesh_vertices.begin(), + (*ite)->getEndVertex()->mesh_vertices.end()); + } + } + ++ite; + } + } + e2t_cont adj; buildEdgeToElement(gf->triangles, adj); - std::set<RecombineTriangle> pairs; + std::map<MVertex*,std::pair<MElement*,MElement*> > makeGraphPeriodic; + + std::vector<RecombineTriangle> pairs; + int nbValidPairs = 0; for(e2t_cont::iterator it = adj.begin(); it!= adj.end(); ++it){ if(it->second.second && it->second.first->getNumVertices() == 3 && it->second.second->getNumVertices() == 3 && (emb_edgeverts.find(it->first.getVertex(0)) == emb_edgeverts.end() || - emb_edgeverts.find(it->first.getVertex(1)) == emb_edgeverts.end())) - pairs.insert(RecombineTriangle(it->first, + emb_edgeverts.find(it->first.getVertex(1)) == emb_edgeverts.end())){ + + pairs.push_back(RecombineTriangle(it->first, it->second.first, it->second.second)); + } + else if (!it->second.second && + it->second.first->getNumVertices() == 3){ + for (int i=0;i<2;i++){ + MVertex *v = it->first.getVertex(i); + std::map<MVertex*,std::pair<MElement*,MElement*> > :: iterator itv = makeGraphPeriodic.find(v); + if (itv == makeGraphPeriodic.end()){ + makeGraphPeriodic[v] = std::make_pair(it->second.first,(MElement*)0); + } + else{ + if ( itv->second.first != it->second.first) + itv->second.second = it->second.first; + else + makeGraphPeriodic.erase(itv); + } + } + } } + std::sort(pairs.begin(),pairs.end()); std::set<MElement*> touched; - std::set<RecombineTriangle>::iterator itp = pairs.begin(); + + + if(CTX::instance()->mesh.algoSubdivide == 3){ +#ifdef HAVE_MATCH + int ncount = gf->triangles.size(); + // printf("%d %d ----------------\n",CTX::instance()->mesh.algoSubdivide, ncount); + if (ncount % 2 == 0) { + int ecount = cubicGraph ? pairs.size() + makeGraphPeriodic.size() : pairs.size(); + printf("%d internal %d closed\n",pairs.size(), makeGraphPeriodic.size()); + // Msg::Info("Cubic Graph should have ne (%d) = 3 x nv (%d) ",ecount,ncount); + Msg::Debug("Perfect Match Starts %d edges %d nodes",ecount,ncount); + std::map<MElement*,int> t2n; + std::map<int,MElement*> n2t; + for (int i=0;i<gf->triangles.size();++i){ + t2n[gf->triangles[i]] = i; + n2t[i] = gf->triangles[i]; + } + + int *elist = new int [2*ecount]; + int *elen = new int [ecount]; + for (int i=0;i<pairs.size();++i){ + elist[2*i] = t2n[pairs[i].t1]; + elist[2*i+1] = t2n[pairs[i].t2]; + elen [i] = (int) pairs[i].angle; + int NB = 0; + if (pairs[i].n1->onWhat()->dim() < 2)NB++; + if (pairs[i].n2->onWhat()->dim() < 2)NB++; + if (pairs[i].n3->onWhat()->dim() < 2)NB++; + if (pairs[i].n4->onWhat()->dim() < 2)NB++; + if (elen[i] > 60 && NB > 2) {elen[i] = 1000;} + } + + if (cubicGraph){ + std::map<MVertex*,std::pair<MElement*,MElement*> > :: iterator itv = makeGraphPeriodic.begin(); + int CC = pairs.size(); + for ( ; itv != makeGraphPeriodic.end(); ++itv){ + elist[2*CC] = t2n[itv->second.first]; + elist[2*CC+1] = t2n[itv->second.second]; + elen [CC++] = 100000; + } + } + + double matzeit = 0.0; + char MATCHFILE[256]; + sprintf(MATCHFILE,".face.match"); + if (perfect_match (ncount, + NULL, + ecount, + &elist, + &elen, + NULL, + MATCHFILE, + 0, + 0, + 0, + 0, + &matzeit)){ + if (recur_level < 20){ + Msg::Warning("Perfect Match Failed in Quadrangulation, Applying Graph Splits"); + std::set<MElement*> removed; + std::vector<MTriangle*> triangles2; + for (int i=0;i<pairs.size();++i){ + RecombineTriangle &rt = pairs[i]; + if ((rt.n1->onWhat()->dim() < 2 && + rt.n2->onWhat()->dim() < 2) || + (recur_level > 10 && i < 10) + ){ + if (removed.find(rt.t1) == removed.end() && + removed.find(rt.t2) == removed.end() ){ + removed.insert(rt.t1); + removed.insert(rt.t2); + SPoint2 p1,p2; + reparamMeshEdgeOnFace(rt.n1,rt.n2, gf, p1,p2); + SPoint2 np = (p1+p2)*(1./2.0); + GPoint gp = gf->point(np); + MFaceVertex *newv = new MFaceVertex(gp.x(),gp.y(),gp.z(), + gf,np.x(),np.y()); + MTriangle *t1 = new MTriangle(rt.n1,rt.n4,newv); + MTriangle *t2 = new MTriangle(rt.n4,rt.n2,newv); + MTriangle *t3 = new MTriangle(rt.n2,rt.n3,newv); + MTriangle *t4 = new MTriangle(rt.n3,rt.n1,newv); + //MTriangle *t1 = new MTriangle(rt.n1,rt.n4,rt.n3); + // MTriangle *t2 = new MTriangle(rt.n4,rt.n2,rt.n3); + gf->mesh_vertices.push_back(newv); + triangles2.push_back(t1); + triangles2.push_back(t2); + triangles2.push_back(t3); + triangles2.push_back(t4); + } + } + } + if (removed.size() == 0) return _recombineIntoQuads(gf, 11); + for (int i=0;i<gf->triangles.size();++i){ + if (removed.find(gf->triangles[i]) == removed.end())triangles2.push_back(gf->triangles[i]); + else delete gf->triangles[i]; + } + gf->triangles=triangles2; + // for (int i=0;i<tos.size();i++)_triangleSplit (gf,tos[i]); + // gf->model()->writeMSH("chplit.msh"); + free(elist); + return _recombineIntoQuads(gf, recur_level+1); + } + else { + Msg::Error("Perfect Match Finally Failed in Quadrangulation, Try Something Else"); + free(elist); + pairs.clear(); + } + } + else{ + // TEST + // FILE *f = fopen (MATCHFILE,"r"); + // int nn,ne; + // fscanf(f,"%d %d",&nn,&ne); + for (int k=0;k<elist[0];k++){ + int i1 = elist[1+3*k],i2 = elist[1+3*k+1],an=elist[1+3*k+2]; + if (an == 100000){ + Msg::Debug("Forbidden quad found in blossom quadrangulation, optimization will be required"); + } + else{ + MElement *t1 = n2t[i1]; + MElement *t2 = n2t[i2]; + touched.insert(t1); + touched.insert(t2); + int orientation = 0; + MVertex *other = 0; + for(int i = 0; i < 3; i++) { + if (t1->getVertex(0) != t2->getVertex(i) && + t1->getVertex(1) != t2->getVertex(i) && + t1->getVertex(2) != t2->getVertex(i)){ + other = t2->getVertex(i); + break; + } + } + int start; + for(int i = 0; i < 3; i++) { + if (t2->getVertex(0) != t1->getVertex(i) && + t2->getVertex(1) != t1->getVertex(i) && + t2->getVertex(2) != t1->getVertex(i)){ + start=i; + break; + } + } + MQuadrangle *q = new MQuadrangle(t1->getVertex(start), + t1->getVertex((start+1)%3), + other, + t1->getVertex((start+2)%3)); + gf->quadrangles.push_back(q); + } + } + // fclose(f); + free(elist); + pairs.clear(); + if (recur_level == 0) + Msg::Debug("Perfect Match Succeeded in Quadrangulation (%g sec)",matzeit); + else + Msg::Info(" :-) Perfect Match Succeeded in Quadrangulation after Splits (%g sec)",matzeit); + } + } +#else + Msg::Warning("Gmsh should be compiled with the Blossom IV code and CONCORDE in order to allow the Blossom optimization"); +#endif + } + + std::vector<RecombineTriangle>::iterator itp = pairs.begin(); while(itp != pairs.end()){ // recombine if difference between max quad angle and right // angle is smaller than tol @@ -1088,15 +1541,43 @@ static void _recombineIntoQuads(GFace *gf) } } gf->triangles = triangles2; + return success; } void recombineIntoQuads(GFace *gf) { - - _recombineIntoQuads(gf); - laplaceSmoothing(gf); - _recombineIntoQuads(gf); - laplaceSmoothing(gf); - _recombineIntoQuads(gf); - laplaceSmoothing(gf); + // gf->model()->writeMSH("before.msh"); + removeFourTrianglesNodes(gf, false); + int success = _recombineIntoQuads(gf,0); + gf->model()->writeMSH("raw.msh"); + for(int i = 0; i < CTX::instance()->mesh.nbSmoothing; i++) + laplaceSmoothing(gf); + if(success && CTX::instance()->mesh.algoSubdivide == 3){ + gf->model()->writeMSH("smoothed.msh"); + int COUNT = 0; + char NAME[256]; + while(1){ + int x = removeTwoQuadsNodes(gf); + if (x)sprintf(NAME,"iter%dTQ.msh",COUNT++); + if (x)gf->model()->writeMSH(NAME); + int y= removeDiamonds(gf); + if (y)sprintf(NAME,"iter%dD.msh",COUNT++); + if (y)gf->model()->writeMSH(NAME); + int z = _edgeSwapQuadsForBetterQuality ( gf ); + if (z)sprintf(NAME,"iter%dS.msh",COUNT++); + if (z)gf->model()->writeMSH(NAME); + if (!(x+y+z))break; + } + for(int i = 0; i < CTX::instance()->mesh.nbSmoothing; i++){ + laplaceSmoothing(gf); + } + return; + } + _recombineIntoQuads(gf,0); + for(int i = 0; i < CTX::instance()->mesh.nbSmoothing; i++) + laplaceSmoothing(gf); + _recombineIntoQuads(gf,0); + for(int i = 0; i < CTX::instance()->mesh.nbSmoothing; i++) + laplaceSmoothing(gf); + // gf->model()->writeMSH("after.msh"); } diff --git a/Numeric/polynomialBasis.cpp b/Numeric/polynomialBasis.cpp index 5b91118476fb0ad756f42035780a8145a3d3ab80..85fbc1adb91be6a2e6cac542ddd46ad501a6ee26 100644 --- a/Numeric/polynomialBasis.cpp +++ b/Numeric/polynomialBasis.cpp @@ -102,6 +102,31 @@ static fullMatrix<double> generatePascalQuad(int order) 04 14 â‹® â‹® */ + +// generate all monomials xi^m * eta^n with n and m <= order +static fullMatrix<double> generatePascalHex(int order) +{ + + fullMatrix<double> monomials( (order+1)*(order+1)*(order+1), 3); + int index = 0; + for (int p = 0; p <= order; p++) { + for(int i = 0; i < p; i++) { + for(int j = 0; j < i; j++, index++) { + monomials(index, 0) = p; + monomials(index, 1) = i; + monomials(index, 2) = j; + } + } + for(int i = 0; i <= p; i++, index++) { + for(int j = 0; j <= p; i++, index++) { + monomials(index, 0) = p-i; + monomials(index, 1) = p; + } + } + } + return monomials; +} + static fullMatrix<double> generatePascalQuadSerendip(int order) { fullMatrix<double> monomials( (order)*4, 2); diff --git a/benchmarks/2d/conge.geo b/benchmarks/2d/conge.geo index 8d59bd00fab50acf1160bee46f4cb0d8f58364dc..41bfe3d0777d0a3d1db10176b6ff1fee6818f18c 100644 --- a/benchmarks/2d/conge.geo +++ b/benchmarks/2d/conge.geo @@ -78,3 +78,4 @@ Physical Line(25) = {12,13,14,15,16,17,18,19,20,10}; Physical Surface(26) = {22,24}; Physical Line(27) = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, 11}; Physical Line(28) = {17, 16, 20, 19, 18, 15}; +Recombine Surface {24, 22}; diff --git a/benchmarks/2d/recombine.geo b/benchmarks/2d/recombine.geo index 15bc57a7f9c67bff202a01aeaf07ea6e6f32b5c3..b0cddc8b8e74ac264d4ff1cbeb35f0429e1037cc 100644 --- a/benchmarks/2d/recombine.geo +++ b/benchmarks/2d/recombine.geo @@ -10,4 +10,4 @@ Line(4) = {4,1} ; Line Loop(5) = {4,1,-2,3} ; Plane Surface(6) = {5} ; Recombine Surface{6}; -Mesh.SubdivisionAlgorithm = 1; +Mesh.SubdivisionAlgorithm = 3; diff --git a/benchmarks/2d/slot.geo b/benchmarks/2d/slot.geo index c090a2fd5d34cef3243e8783854e5ab94a954362..7e4d787585589146abc42c007463f196681a9232 100644 --- a/benchmarks/2d/slot.geo +++ b/benchmarks/2d/slot.geo @@ -1,3 +1,4 @@ +Mesh.SubdivisionAlgorithm = 3; Point(1) = {0, 0, 0, 9.999999999999999e-05}; Point(2) = {0.00125, 0.045983013167908, 0, 0.0002}; Point(3) = {-0.00125, 0.045983013167908, 0, 0.0002}; @@ -1265,3 +1266,4 @@ Physical Surface(4098) = {607}; Physical Surface(4099) = {613}; Physical Surface(4100) = {619}; Physical Surface(4101) = {625}; +Recombine Surface {1 ... 635};