diff --git a/Geo/fourierModel.cpp b/Geo/fourierModel.cpp index 0a4c958a2d306161c450aca862c35667731a4790..420a363abb13be6e1867ad31004f54daf6902b79 100644 --- a/Geo/fourierModel.cpp +++ b/Geo/fourierModel.cpp @@ -3,6 +3,23 @@ #include "Context.h" #include "Views.h" +/* + Investigate the following approach: + + compute and store the pou of each overlapping patch in the nodes of + all the patches + + for each pair of overlapping patches, find the line pou1=pou2 by + interpolation on the overlapping grids + + compute the intersections of these lines + + this should define a non-overlapping partitioning of the grid, which + could be used as the boundary constrain for the unstructured algo + + Would that work?? +*/ + extern Context_T CTX; #if defined(HAVE_FOURIER_MODEL) @@ -12,8 +29,55 @@ extern Context_T CTX; static model *FM = 0; +void debugVertices(std::vector<MVertex*> &vertices, std::string file, + bool parametric, int num=0) +{ + char name[256]; + sprintf(name, "%s_%d.pos", file.c_str(), num); + FILE *fp = fopen(name, "w"); + fprintf(fp, "View \"debug\"{\n"); + for(unsigned int i = 0; i < vertices.size(); i++){ + double x, y, z; + if(parametric){ + vertices[i]->getParameter(0, x); + vertices[i]->getParameter(1, y); + z = 0; + } + else{ + x = vertices[i]->x(); + y = vertices[i]->y(); + z = vertices[i]->z(); + } + fprintf(fp, "SP(%g,%g,%g){%d};\n", x, y, z, i); + } + fprintf(fp, "};\n"); + fclose(fp); +} + +void debugElements(std::vector<MElement*> &elements, std::string file, + bool parametric, int num=0) +{ + char name[256]; + sprintf(name, "%s_%d.pos", file.c_str(), num); + FILE *fp = fopen(name, "w"); + fprintf(fp, "View \"debug\"{\n"); + for(unsigned int i = 0; i < elements.size(); i++) + elements[i]->writePOS(fp); + fprintf(fp, "};\n"); + fclose(fp); +} + class meshCartesian{ public: + typedef MDataFaceVertex<double> DVertex; + static std::set<DVertex*, MVertexLessThanLexicographic> vPosition; + static std::set<MVertex*> vDelete; + static void deleteUnusedVertices() + { + for(std::set<MVertex*>::iterator it = vDelete.begin(); it != vDelete.end(); it++) + delete *it; + vDelete.clear(); + } void operator() (GFace *gf) { int M = (int)(30. / CTX.mesh.lc_factor), N = (int)(30. / CTX.mesh.lc_factor); @@ -24,8 +88,17 @@ public: GPoint p = gf->point(u, v); double pou; FM->GetPou(gf->tag(), u, v, pou); - gf->mesh_vertices.push_back - (new MDataFaceVertex<double>(p.x(), p.y(), p.z(), gf, u, v, pou)); + DVertex *w = new DVertex(p.x(), p.y(), p.z(), gf, u, v, pou); + // eliminate duplicate vertices on hard edges + std::set<DVertex*, MVertexLessThanLexicographic>::iterator it = vPosition.find(w); + if(it != vPosition.end()){ + delete w; + gf->mesh_vertices.push_back(*it); + } + else{ + vPosition.insert(w); + gf->mesh_vertices.push_back(w); + } } } for(int i = 0; i < M - 1; i++){ @@ -41,6 +114,9 @@ public: } }; +std::set<meshCartesian::DVertex*, MVertexLessThanLexicographic> meshCartesian::vPosition; +std::set<MVertex*> meshCartesian::vDelete; + class computePartitionOfUnity{ public: void operator() (GFace *gf) @@ -57,7 +133,7 @@ public: SPoint2 uv2 = gf2->parFromPoint(SPoint3(v->x(), v->y(), v->z())); if(gf2->containsParam(uv2)){ GPoint xyz2 = gf2->point(uv2); - const double tol = 1.e-2; // need to adapt this w.r.t size of model + const double tol = 1.e-2; // Warning: tolerance if(fabs(xyz2.x() - v->x()) < tol && fabs(xyz2.y() - v->y()) < tol && fabs(xyz2.z() - v->z()) < tol) @@ -87,10 +163,11 @@ public: } }; -class createGrooves{ +class createGroove{ public: void operator() (GFace *gf) { + // mark elements in the groove with '-1' visibility tag for(unsigned int i = 0; i < gf->quadrangles.size(); i++){ MElement *e = gf->quadrangles[i]; for(int j = 0; j < e->getNumVertices(); j++){ @@ -98,23 +175,39 @@ public: if(data){ double pou = *(double*)data; if(pou < 0.51) - e->setVisibility(0); + e->setVisibility(-1); } } } - std::vector<MQuadrangle*> remaining; + + // remove elements in the groove + std::vector<MQuadrangle*> newq; for(unsigned int i = 0; i < gf->quadrangles.size(); i++) - if(gf->quadrangles[i]->getVisibility()) - remaining.push_back(gf->quadrangles[i]); - else + if(gf->quadrangles[i]->getVisibility() < 0) delete gf->quadrangles[i]; - gf->quadrangles.clear(); - gf->quadrangles = remaining; + else + newq.push_back(gf->quadrangles[i]); + gf->quadrangles = newq; + + // remove vertices in the groove (we cannot delete them right away + // since some can be shared between faces on hard edges) + std::vector<MVertex*> newv; + for(unsigned int i = 0; i < gf->mesh_vertices.size(); i++) + gf->mesh_vertices[i]->setVisibility(-1); + for(unsigned int i = 0; i < gf->quadrangles.size(); i++) + for(int j = 0; j < gf->quadrangles[i]->getNumVertices(); j++) + gf->quadrangles[i]->getVertex(j)->setVisibility(1); + for(unsigned int i = 0; i < gf->mesh_vertices.size(); i++) + if(gf->mesh_vertices[i]->getVisibility() < 0) + meshCartesian::vDelete.insert(gf->mesh_vertices[i]); + else + newv.push_back(gf->mesh_vertices[i]); + gf->mesh_vertices = newv; } }; -void getOrderedBoundaryVertices(std::vector<MElement*> &elements, - std::vector<MVertex*> &boundary) +void getOrderedBoundaryLoops(std::vector<MElement*> &elements, + std::vector<std::vector<MVertex*> > &loops) { typedef std::pair<MVertex*, MVertex*> vpair; std::map<vpair, vpair> edges; @@ -126,12 +219,27 @@ void getOrderedBoundaryVertices(std::vector<MElement*> &elements, else edges[p] = vpair(e.getVertex(0), e.getVertex(1)); } } + std::map<MVertex*, MVertex*> connect; for(std::map<vpair, vpair>::iterator it = edges.begin(); it != edges.end(); it++) connect[it->second.first] = it->second.second; - boundary.push_back(connect.begin()->first); - for(unsigned int i = 0; i < connect.size(); i++) - boundary.push_back(connect[boundary[boundary.size() - 1]]); + + loops.resize(1); + while(connect.size()){ + if(loops[loops.size() - 1].empty()) + loops[loops.size() - 1].push_back(connect.begin()->first); + MVertex *prev = loops[loops.size() - 1][loops[loops.size() - 1].size() - 1]; + MVertex *next = connect[prev]; + connect.erase(prev); + loops[loops.size() - 1].push_back(next); + if(next == loops[loops.size() - 1][0]) + loops.resize(loops.size() + 1); + } + + if(loops[loops.size() - 1].empty()) + loops.pop_back(); + + Msg(INFO, "Found %d loop(s) in boundary", loops.size()); } void addElements(GFace *gf, std::vector<MElement*> &elements) @@ -142,55 +250,78 @@ void addElements(GFace *gf, std::vector<MElement*> &elements) elements.push_back(gf->quadrangles[i]); } -bool isConnected(GFace *gf1, GFace *gf2) +void classifyFaces(GFace *gf, std::set<GFace*> &connected, std::set<GFace*> &other) { - std::set<MVertex*> set; - std::vector<MElement*> elements1, elements2; - std::vector<MVertex*> boundary1, boundary2; - addElements(gf1, elements1); - addElements(gf2, elements2); - getOrderedBoundaryVertices(elements1, boundary1); - getOrderedBoundaryVertices(elements2, boundary2); - - for(unsigned int i = 0; i < boundary1.size(); i++) - set.insert(boundary1[i]); - for(unsigned int i = 0; i < boundary2.size(); i++){ - std::set<MVertex*>::iterator it = set.find(boundary2[i]); - if(it != set.end()) return true; + connected.insert(gf); + std::set<MVertex*> verts; + for(unsigned int i = 0; i < gf->mesh_vertices.size(); i++) + verts.insert(gf->mesh_vertices[i]); + + for(int i = 0; i < gf->model()->numFace() - 1; i++){ // worst case + for(GModel::fiter it = gf->model()->firstFace(); it != gf->model()->lastFace(); it++){ + if(connected.find(*it) == connected.end()){ + for(unsigned int j = 0; j < (*it)->mesh_vertices.size(); j++){ + if(std::find(verts.begin(), verts.end(), (*it)->mesh_vertices[j]) != verts.end()){ + connected.insert(*it); + for(unsigned int k = 0; k < (*it)->mesh_vertices.size(); k++) + verts.insert((*it)->mesh_vertices[k]); + break; + } + } + } + } } - return false; + + for(GModel::fiter it = gf->model()->firstFace(); it != gf->model()->lastFace(); it++) + if(connected.find(*it) == connected.end()) + other.insert(*it); + + Msg(INFO, "Found %d face(s) connected to face %d", (int)connected.size(), gf->tag()); + for(std::set<GFace*>::iterator it = connected.begin(); it != connected.end(); it++) + Msg(INFO, " %d", (*it)->tag()); } -void getIntersectingBoundaryParts(GFace *gf, std::vector<MElement*> &elements, int &start, - std::map<int, std::vector<MVertex*> > &parts) +void getIntersectingBoundaryParts(GFace *gf, std::vector<MElement*> &elements, + std::vector<std::vector<std::vector<MVertex*> > > &parts) { - bool newpart = true; - std::vector<MVertex*> vertices; - getOrderedBoundaryVertices(elements, vertices); - for(unsigned int i = 0; i < vertices.size() - 1; i++){ - MVertex *v = vertices[i]; - SPoint2 uv = gf->parFromPoint(SPoint3(v->x(), v->y(), v->z())); - if(gf->containsParam(uv)){ - GPoint xyz = gf->point(uv); - const double tol = 1.e-2; // need to adapt this w.r.t size of model - if(fabs(xyz.x() - v->x()) < tol && - fabs(xyz.y() - v->y()) < tol && - fabs(xyz.z() - v->z()) < tol){ - if(newpart){ - start++; - newpart = false; + std::vector<std::vector<MVertex*> > loops; + getOrderedBoundaryLoops(elements, loops); + parts.resize(loops.size()); + + if(gf->tag() == 0){ + debugElements(elements, "elements", false); + for(unsigned int i = 0; i < loops.size(); i++) + debugVertices(loops[i], "boundary", false, i); + } + + for(unsigned int i = 0; i < loops.size(); i++){ + // last vertex in loop is equal to the firt vertex + bool newpart = true; + for(unsigned int j = 0; j < loops[i].size() - 1; j++){ + MVertex *v = loops[i][j]; + SPoint2 uv = gf->parFromPoint(SPoint3(v->x(), v->y(), v->z())); + if(gf->containsParam(uv)){ + GPoint xyz = gf->point(uv); + const double tol = 1.e-2; // Warning: tolerance + if(fabs(xyz.x() - v->x()) < tol && + fabs(xyz.y() - v->y()) < tol && + fabs(xyz.z() - v->z()) < tol){ + if(newpart){ + parts[i].resize(parts[i].size() + 1); + newpart = false; + } + v->setParameter(0, uv[0]); + v->setParameter(1, uv[1]); + parts[i][parts[i].size() - 1].push_back(v); + } + else{ + newpart = true; } - v->setParameter(0, uv[0]); - v->setParameter(1, uv[1]); - parts[start].push_back(v); } else{ newpart = true; } } - else{ - newpart = true; - } } } @@ -199,169 +330,122 @@ public: void operator() (GFace *gf) { if(gf->tag() > 1) return; - printf("processing grout for face %d\n", gf->tag()); - - GModel *m = gf->model(); - - // need to find the disconnected parts in inside too!!! then - // we'll pair them with disconnected parts in outside (by looking - // at the value of the POU) - // Distinguish 2 cases: - // - patch with no hard edges and and no connections: we have a hole - // THIS IS WHAT WE ASSUME NOW - // - patch with hard edges or connections: we don't have a hole, but - // we might have non-connected parts - std::vector<MElement*> elements; - - for(GModel::fiter it = m->firstFace(); it != m->lastFace(); it++){ - if(isConnected(gf, *it)){ - printf("face %d: mesh connected to %d\n", gf->tag(), (*it)->tag()); - addElements(*it, elements); - } - } - int numinside = 0; - std::map<int, std::vector<MVertex*> > inside; - getIntersectingBoundaryParts(gf, elements, numinside, inside); - - printf("numinside = %d\n", numinside); - - int numoutside = 0; - std::map<int, std::vector<MVertex*> > outside; - for(GModel::fiter it = m->firstFace(); it != m->lastFace(); it++){ - if(!isConnected(gf, *it)){ - elements.clear(); - addElements(*it, elements); - getIntersectingBoundaryParts(gf, elements, numoutside, outside); - } - } - printf("numoutside = %d\n", numoutside); + Msg(INFO, "Processing grout for face %d", gf->tag()); + + std::set<GFace*> connected, other; + classifyFaces(gf, connected, other); + + std::vector<MElement*> connectedElements, otherElements; + for(std::set<GFace*>::iterator it = connected.begin(); it != connected.end(); it++) + addElements(*it, connectedElements); + for(std::set<GFace*>::iterator it = other.begin(); it != other.end(); it++) + addElements(*it, otherElements); + + std::vector<std::vector<std::vector<MVertex*> > > inside; + getIntersectingBoundaryParts(gf, connectedElements, inside); + Msg(INFO, "%d inside loop(s)", (int)inside.size()); + for(unsigned int i = 0; i < inside.size(); i++) + Msg(INFO, " inside loop %d intersect has %d part(s)", i, (int)inside[i].size()); + + std::vector<std::vector<std::vector<MVertex*> > > outside; + getIntersectingBoundaryParts(gf, otherElements, outside); + Msg(INFO, "%d outside loop(s)", (int)outside.size()); + for(unsigned int i = 0; i < outside.size(); i++) + Msg(INFO, " outside loop %d intersect has %d part(s)", i, (int)outside[i].size()); std::vector<MVertex*> hole, loop; - if(numinside == 1){ + if(inside.size() == 1 && inside[0].size() == 1){ // create hole SPoint2 ic(0., 0.); - for(unsigned int i = 0; i < inside[1].size(); i++){ - hole.push_back(inside[1][i]); - double u, v; - inside[1][i]->getParameter(0, u); - inside[1][i]->getParameter(1, v); - ic += SPoint2(u, v); - } - ic *= 1. / (double) inside[1].size(); - hole.push_back(hole[0]); - - // order exterior parts - std::vector<std::pair<double, int> > angle; - std::map<int, std::vector<MVertex*> >::iterator it = outside.begin(); - for(; it != outside.end(); it++){ - SPoint2 oc(0., 0.); - for(unsigned int i = 0; i < it->second.size(); i++){ + { + for(unsigned int i = 0; i < inside[0][0].size(); i++){ + hole.push_back(inside[0][0][i]); double u, v; - it->second[i]->getParameter(0, u); - it->second[i]->getParameter(1, v); - oc += SPoint2(u, v); + inside[0][0][i]->getParameter(0, u); + inside[0][0][i]->getParameter(1, v); + ic += SPoint2(u, v); } - oc *= 1. / (double)it->second.size(); - double a = atan2(oc[1] - ic[1], oc[0] - ic[0]); - angle.push_back(std::make_pair(a, it->first)); + ic *= 1. / (double)inside[0][0].size(); + hole.push_back(hole[0]); } - std::sort(angle.begin(), angle.end()); - - // create exterior loop - for(unsigned int i = 0; i < angle.size(); i++){ - for(unsigned int j = 0; j < outside[angle[i].second].size(); j++) - loop.push_back(outside[angle[i].second][j]); + // order exterior parts and create exterior loop + { + std::vector<std::pair<double, int> > angle; + std::map<int, std::vector<MVertex*> > outside_flat; + int part = 0; + for(unsigned int i = 0; i < outside.size(); i++){ + for(unsigned int j = 0; j < outside[i].size(); j++){ + for(unsigned int k = 0; k < outside[i][j].size(); k++){ + outside_flat[part].push_back(outside[i][j][k]); + } + part++; + } + } + std::map<int, std::vector<MVertex*> >::iterator it = outside_flat.begin(); + for(; it != outside_flat.end(); it++){ + SPoint2 oc(0., 0.); + for(unsigned int i = 0; i < it->second.size(); i++){ + double u, v; + it->second[i]->getParameter(0, u); + it->second[i]->getParameter(1, v); + oc += SPoint2(u, v); + } + oc *= 1. / (double)it->second.size(); + double a = atan2(oc[1] - ic[1], oc[0] - ic[0]); + angle.push_back(std::make_pair(a, it->first)); + } + std::sort(angle.begin(), angle.end()); + for(unsigned int i = 0; i < angle.size(); i++){ + for(unsigned int j = 0; j < outside_flat[angle[i].second].size(); j++) + loop.push_back(outside_flat[angle[i].second][j]); + } + loop.push_back(hole[0]); } - loop.push_back(hole[0]); - // mesh the grout fourierFace *grout = new fourierFace(gf, loop, hole); meshGFace mesh; mesh(grout); for(unsigned int i = 0; i < grout->triangles.size(); i++) gf->triangles.push_back(grout->triangles[i]); + for(unsigned int i = 0; i < loop.size(); i++) + gf->mesh_vertices.push_back(loop[i]); + for(unsigned int i = 0; i < hole.size(); i++) + gf->mesh_vertices.push_back(hole[i]); for(unsigned int i = 0; i < grout->mesh_vertices.size(); i++) gf->mesh_vertices.push_back(grout->mesh_vertices[i]); delete grout; } else{ - Msg(WARNING, "Faces with no holes not implemented yet!"); - } + // num individual meshes = num parts with onWhat() == gf! - // debug - char name[256]; - sprintf(name, "aa%d.pos", gf->tag()); - FILE *fp = fopen(name, "w"); - fprintf(fp, "View \"aa\"{\n"); - for(unsigned int i = 0; i < hole.size(); i++){ - double x = hole[i]->x(), y = hole[i]->y(), z = hole[i]->z(); - // plot in parametric space: - hole[i]->getParameter(0, x); hole[i]->getParameter(1, y); z = 0; - fprintf(fp, "SP(%g,%g,%g){0};\n", x, y, z); - } - for(unsigned int i = 0; i < loop.size(); i++){ - double x = loop[i]->x(), y = loop[i]->y(), z = loop[i]->z(); - // plot in parametric space: - loop[i]->getParameter(0, x); loop[i]->getParameter(1, y); z = 0; - fprintf(fp, "SP(%g,%g,%g){%d};\n", x, y, z, i); - } - fprintf(fp, "};\n"); - fclose(fp); - } -}; + // for each one + // - find other parts that are "close" (using POUs?) + // - sort parts w.r.t barycenter of each group -class exportFourierFace{ -public: - void operator() (GFace *gf) - { - Post_View *view = BeginView(1); - for(unsigned int i = 0; i < gf->quadrangles.size(); i++){ - MElement *e = gf->quadrangles[i]; - double x[4], y[4], z[4], val[4]; - for(int j = 0; j < 4; j++){ - MVertex *v = e->getVertex(j); - x[j] = v->x(); - y[j] = v->y(); - z[j] = v->z(); - val[j] = *(double*)v->getData(); + for(unsigned int i = 0; i < inside.size(); i++){ + for(unsigned int j = 0; j < inside[i].size(); j++){ + for(unsigned int k = 0; k < inside[i][j].size(); k++){ + loop.push_back(inside[i][j][k]); + } + } } - for(int j = 0; j < 4; j++) List_Add(view->SQ, &x[j]); - for(int j = 0; j < 4; j++) List_Add(view->SQ, &y[j]); - for(int j = 0; j < 4; j++) List_Add(view->SQ, &z[j]); - for(int j = 0; j < 4; j++) List_Add(view->SQ, &val[j]); - view->NbSQ++; } - std::vector<MElement*> elements; - std::vector<MVertex*> vertices; - addElements(gf, elements); - getOrderedBoundaryVertices(elements, vertices); - for(unsigned int i = 0; i < vertices.size() - 1; i++){ - double x[2] = {vertices[i]->x(), vertices[i + 1]->x()}; - double y[2] = {vertices[i]->y(), vertices[i + 1]->y()}; - double z[2] = {vertices[i]->z(), vertices[i + 1]->z()}; - double val[2] = {10, 10}; - for(int j = 0; j < 2; j++) List_Add(view->SL, &x[j]); - for(int j = 0; j < 2; j++) List_Add(view->SL, &y[j]); - for(int j = 0; j < 2; j++) List_Add(view->SL, &z[j]); - for(int j = 0; j < 2; j++) List_Add(view->SL, &val[j]); - view->NbSL++; - } - char name[1024], filename[1024]; - sprintf(name, "patch%d", gf->tag()); - sprintf(filename, "patch%d", gf->tag()); - EndView(view, 1, filename, name); + + debugVertices(hole, "hole", false, gf->tag()); + debugVertices(loop, "loop", false, gf->tag()); } }; - fourierModel::fourierModel(const std::string &name) : GModel(name) { FM = new model(name); + + CTX.terminal = 1; Msg(INFO, "Fourier model created: %d patches", FM->GetNumPatches()); @@ -369,6 +453,9 @@ fourierModel::fourierModel(const std::string &name) for(int i = 0; i < FM->GetNumPatches(); i++) add(new fourierFace(this, i)); + meshCartesian::vPosition.clear(); + MVertexLessThanLexicographic::tolerance = 1.e-12; // Warning: tolerance + // mesh each face with quads std::for_each(firstFace(), lastFace(), meshCartesian()); @@ -376,14 +463,13 @@ fourierModel::fourierModel(const std::string &name) std::for_each(firstFace(), lastFace(), computePartitionOfUnity()); // create grooves - std::for_each(firstFace(), lastFace(), createGrooves()); + meshCartesian::vDelete.clear(); + std::for_each(firstFace(), lastFace(), createGroove()); + meshCartesian::deleteUnusedVertices(); // create grout std::for_each(firstFace(), lastFace(), createGrout()); - // visualize as a post-pro view - //std::for_each(firstFace(), lastFace(), exportFourierFace()); - CTX.mesh.changed = ENT_ALL; }