Forked from
gmsh / gmsh
10817 commits behind the upstream repository.
-
Christophe Geuzaine authoredChristophe Geuzaine authored
meshGFaceQuadrilateralize.cpp 17.48 KiB
// Gmsh - Copyright (C) 1997-2013 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to the public mailing list <gmsh@geuz.org>.
#include "meshGFaceQuadrilateralize.h"
#include "GmshMessage.h"
#include "Numeric.h"
#include "GFace.h"
#include "meshGFaceDelaunayInsertion.h"
#include "meshGFaceOptimize.h"
#include "meshGFaceBDS.h"
#include "BDS.h"
#include "SVector3.h"
class edgeFront {
public:
typedef std::set<BDS_Edge *>::const_iterator eiter;
private:
BDS_Mesh *m;
GFace *gf;
void getFrontEdges(BDS_Point *p, eiter & it1, eiter & it2) const;
void getFrontEdges(BDS_Point *p, std::vector<eiter> & f) const;
public:
std::set<BDS_Edge *> edges;
std::set<BDS_Edge*> stat[5];
eiter begin() { return edges.begin(); }
eiter end() { return edges.end(); }
edgeFront(BDS_Mesh *_m, GFace *_gf) : m(_m), gf(_gf) {}
// initiate edges in the front i.e.
// take all edges that have one neighbor
// and all edges that have a quad and a triangle
// neighbor
void initiate();
// compute normal vector to an edge that points
// inside the domain
SVector3 normal(BDS_Edge *e) const;
// compute the state of a front edge
// 0 both vertices have edge angles < 60 deg
// 1 e->p1 have and edge angle > 60 deg
// 2 e->p2 have and edge angle > 60 deg
// 3 both vertices have edge angles > 60 deg
int computeStatus(BDS_Edge *e) const;
inline bool inFront(BDS_Edge *e) const{ return getStatus(e) != -1; }
inline int getStatus(BDS_Edge *e) const{
for(int i = 0; i < 5; i++){
if(stat[i].find(e) != stat[i].end()) return i;
}
return -1;
}
// get one edge of the front that is tagged "tag"
// and do whatever has to be done to remove it from
// the front/tag. return false if the front is not empty.
bool emptyFront(int tag);
// update the status of the edge
void updateStatus(BDS_Edge *e);
// form a quad now
bool formQuad(BDS_Edge *e, BDS_Edge *left, BDS_Edge *right);
// delete the cavity delimitated by 4 edges
void emptyCavity(BDS_Edge *bottom, BDS_Edge *top, BDS_Edge *left, BDS_Edge *right);
// delete an edge from the front
void deleteFromFront(BDS_Edge *e)
{
edges.erase(e);
for(int i = 0; i < 5; i++){
std::set<BDS_Edge*>::iterator it = stat[i].find(e);
if(it != stat[i].end()){
stat[i].erase(it);
return;
}
}
}
// taking a point from the front, find the optimal edge
BDS_Edge *findOptimalEdge(BDS_Point *p, BDS_Point *avoid);
};
void edgeFront::updateStatus(BDS_Edge *e)
{
for(int i = 0; i < 5; i++){
std::set<BDS_Edge*>::iterator it = stat[i].find(e);
if(it !=stat[i].end()){
stat[i].erase(it);
stat[computeStatus(e)].insert(e);
return;
}
}
Msg::Error("Something wrong in updateStatus");
}
SVector3 norm_edge(BDS_Point *p1, BDS_Point *p2)
{
SVector3 n(p2->X-p1->X,p2->Y-p1->Y,p2->Z-p1->Z);
n.normalize();
return n;
}
void recur_empty_cavity(BDS_Face *f,
BDS_Edge *be[4],
BDS_Point *bv[4],
std::set<BDS_Face*> & faces,
std::set<BDS_Edge*> & edges,
std::set<BDS_Point*> & vertices)
{
if(faces.find(f) != faces.end()) return;
faces.insert(f);
BDS_Edge *ee[3] = {f->e1,f->e2,f->e3};
for(int i=0;i<3;i++){
BDS_Edge *e = ee[i];
if(e != be[0] && e != be[1] && e != be[2] && e != be[3]){
edges.insert(e);
BDS_Face *of = e->otherFace(f);
recur_empty_cavity(of,be,bv,faces,edges,vertices);
}
}
}
void edgeFront::emptyCavity(BDS_Edge *bottom, BDS_Edge *top, BDS_Edge *left,
BDS_Edge *right)
{
// not optimal for now, will be improved further away
BDS_Face *f = 0;
if(bottom->faces(0) && bottom->faces(0)->numEdges() == 3) f=bottom->faces(0);
else if(bottom->faces(1))f = bottom->faces(1);
std::set<BDS_Face*> m_faces;
std::set<BDS_Edge*> m_edges;
std::set<BDS_Point*> m_vertices;
BDS_Edge *be[4] = {bottom,top,left,right};
BDS_Point *bv[4] = {bottom->commonvertex(left),
left->commonvertex(top),
top->commonvertex(right),
right->commonvertex(bottom)};
recur_empty_cavity(f, be, bv, m_faces, m_edges, m_vertices);
// printf("%d edges %d faces\n",m_edges.size(),m_faces.size());
for(std::set<BDS_Face*>::iterator it = m_faces.begin();
it != m_faces.end(); ++it) m->del_face(*it);
for(std::set<BDS_Edge*>::iterator it = m_edges.begin();
it != m_edges.end(); ++it) m->del_edge(*it);
}
SVector3 edgeFront::normal(BDS_Edge*e) const
{
BDS_Face *t = e->faces(0);
if(e->numfaces() == 2 && e->faces(1)->numEdges() == 3)t=e->faces(1);
/*
points p1, p2 and p3
p3 does NOT belong to the edge
p = p1 (1-u-v) + p2 u + p3 v;
J = [p2-p1 p3-p1 (p2-p1)x(p3-p1)]
N = - J^-1 * {0,1,0}
n = N/|N|
*/
BDS_Point *p1 = e->p1;
BDS_Point *p2 = e->p2;
BDS_Point *p3;
if(e == t->e1)
p3 = t->e2->commonvertex(t->e3);
else if(e == t->e2)
p3 = t->e1->commonvertex(t->e3);
else if(e == t->e3)
p3 = t->e2->commonvertex(t->e1);
else {
Msg::Error("Could not compute fron normal");
return SVector3();
}
SVector3 t1(p2->X-p1->X,p2->Y-p1->Y,p2->Z-p1->Z);
SVector3 t2(p3->X-p1->X,p3->Y-p1->Y,p3->Z-p1->Z);
SVector3 t3 = crossprod(t1,t2);
double m[3][3] = {{t1.x(),t2.x(),t3.x()},
{t1.y(),t2.y(),t3.y()},
{t1.z(),t2.z(),t3.z()}};
double im[3][3];
inv3x3(m,im);
SVector3 n(im[1][0],im[1][1],im[1][2]);
n.normalize();
return n;
}
void edgeFront::getFrontEdges(BDS_Point *p, std::vector<eiter> & fe) const
{
for(std::list<BDS_Edge*>::iterator itp = p->edges.begin();
itp != p->edges.end(); ++ itp){
eiter it = edges.find(*itp);
if(it != edges.end())
fe.push_back(it);
}
}
void edgeFront::getFrontEdges(BDS_Point *p, eiter & it1, eiter & it2) const
{
int count = 0;
for(std::list<BDS_Edge*>::iterator itp = p->edges.begin();
itp != p->edges.end(); ++ itp){
if(count == 0){
it1 = edges.find(*itp);
if(it1 != edges.end()) count++;
}
else if(count == 1){
it2 = edges.find(*itp);
if(it2 != edges.end()) return;
}
}
Msg::Error("point %d is in the front but has only %d edges\n",p->iD,count);
}
void edgeFront::initiate()
{
edges.clear();
for(int i = 0; i < 5; i++) stat[i].clear();
std::list<BDS_Edge*>::iterator it = m->edges.begin();
while(it != m->edges.end()){
if(((*it)->numfaces() == 1 && (*it)->faces(0)->e4 == 0) ||
((*it)->numfaces() == 2 && (*it)->numTriangles() == 1)) {
edges.insert(*it);
}
++it;
}
for(eiter it2 = begin(); it2 !=end(); ++it2){
int status = computeStatus(*it2);
stat[status].insert(*it2);
}
}
static double angle3Points(BDS_Point *p1, BDS_Point *p2, BDS_Point *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);
}
int edgeFront::computeStatus(BDS_Edge *e) const
{
eiter it11, it12, it21,it22;
getFrontEdges(e->p1, it11, it12);
getFrontEdges(e->p2, it21, it22);
BDS_Edge *e1 = (*it11) == e ? *it12 : *it11;
BDS_Edge *e2 = (*it21) == e ? *it22 : *it21;
double angle1 = angle3Points((*it11)->othervertex(e->p1), e->p1,
(*it12)->othervertex(e->p1));
double angle2 = angle3Points((*it21)->othervertex(e->p2), e->p2,
(*it22)->othervertex(e->p2));
SVector3 n1 = normal(e);
SVector3 n2 = norm_edge(e->p1, e1->othervertex(e->p1));
SVector3 n3 = norm_edge(e->p2, e2->othervertex(e->p2));
if(dot(n1,n2) < 0)angle1 = M_PI;
if(dot(n1,n3) < 0)angle2 = M_PI;
const double angleLimit = 3 * M_PI/4.;
if(angle1 > angleLimit && angle2 > angleLimit) return 0;
if(angle1 > angleLimit && angle2 < angleLimit) return 1;
if(angle1 < angleLimit && angle2 > angleLimit) return 2;
return 3;
}
bool edgeFront::formQuad(BDS_Edge *e, BDS_Edge *left, BDS_Edge *right)
{
printf("e (%d,%d), l(%d,%d), r(%d,%d)\n",
e->p1->iD,e->p2->iD,
left->p1->iD,left->p2->iD,
right->p1->iD,right->p2->iD);
// outputScalarField(m->triangles, "deb_before.pos", 0);
std::vector<BDS_Point*> toUpdate;
BDS_Point *pleft = left->othervertex(e->p1);
BDS_Point *pright = right->othervertex(e->p2);
// recover edge pleft->pright
BDS_Edge *top = m->find_edge(pleft,pright);
/*
We first have to ensure that, if left or right
are closing the front, then even number of edges should remain in
both left and right parts of the front
*/
if(!top) {
// printf("recover\n");
// top = m->recover_edge_fast(pleft,pright);
// if(!top)
bool _fatallyFailed;
top = m->recover_edge(pleft->iD, pright->iD,_fatallyFailed);
// printf("recover done %p\n",top);
if(!top) return false;
}
// delete internal triangles
emptyCavity(e, top, left, right);
// add the quad
m->add_quadrangle(e, left, top, right);
// update the edge status
// bottom edge leaves the front
deleteFromFront(e);
// top edge becomes part of the front
/* printf("(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",
e->p1->iD,e->p2->iD,
left->p1->iD,left->p2->iD,
top->p1->iD,top->p2->iD,
right->p1->iD,right->p2->iD);
*/
// outputScalarField(m->triangles, "deb.pos", 0);
// if left edge was in the front, then it leaves the front
// because it has either 2 neighboring quads or one quad
// and the void
// if the left edge was not in the front, then it becomes
// part of it.
// printf("coucou1\n");
if(inFront(left)) deleteFromFront(left);
else edges.insert(left);
// printf("coucou2\n");
if(inFront(right)) deleteFromFront(right);
else edges.insert(right);
// printf("coucou3\n");
if(inFront(top)) deleteFromFront(top);
else edges.insert(top);
toUpdate.push_back(e->p1);
toUpdate.push_back(e->p2);
toUpdate.push_back(pleft);
toUpdate.push_back(pright);
for(unsigned int i = 0; i < toUpdate.size(); i++){
toUpdate[i]->config_modified = true;
//bool done =
m->smooth_point_parametric(toUpdate[i], gf);
// printf("smooth done %d (g %d)\n",done,toUpdate[i]->g->classif_degree);
}
for(unsigned int i = 0; i < toUpdate.size(); i++){
BDS_Point *p = toUpdate[i];
for(std::list<BDS_Edge*>::iterator itp = p->edges.begin();
itp != p->edges.end(); ++ itp){
if(inFront(*itp)){
updateStatus(*itp);
}
}
}
return true;
}
BDS_Edge *edgeFront::findOptimalEdge(BDS_Point *p, BDS_Point *avoid)
{
eiter it1, it2;
getFrontEdges(p, it1, it2);
// compute bissector of front edges, this is the optimal direction
SVector3 n1 = normal(*it1);
SVector3 n2 = normal(*it2);
SVector3 n = n1 + n2;
n.normalize();
// printf("POINT %g %g %g N %g %g %g\n",p->X,p->Y,p->Z,n.x(),n.y(),n.z());
double lowerBound = cos(M_PI / 6.0);
BDS_Edge *found = 0;
for(std::list<BDS_Edge*>::iterator itp = p->edges.begin();
itp != p->edges.end(); ++ itp){
// the edge is not in the front and is not bounded by quads only
if(*it1 != *itp && *it2 != *itp && (*itp)->numTriangles()){
BDS_Point *q = (*itp)->othervertex(p);
SVector3 d(q->X - p->X, q->Y - p->Y, q->Z - p->Z);
d.normalize();
double COS = dot(n,d);
if(COS > lowerBound && q != avoid){
lowerBound = COS;
found = *itp;
}
}
}
if(found) return found;
else{
std::list<BDS_Face*> ts;
double x[2];
const double L = 0.5*sqrt(3.0)*((*it2)->length() * (*it1)->length());
p->getTriangles(ts);
std::list<BDS_Face*>::iterator it = ts.begin();
std::list<BDS_Face*>::iterator ite = ts.end();
while(it != ite) {
BDS_Face *t = *it;
if(!t->e4){
BDS_Edge *e = t->oppositeEdge(p);
if(e->numfaces() == 2){
BDS_Face *f = e->otherFace(t);
if(!f->e4){
BDS_Point *target = f->oppositeVertex(e);
// ONLY WORKS IN 2D for now !!!!!!!!!!!!!!!!!!!
Intersect_Edges_2d(e->p1->X, e->p1->Y,
e->p2->X, e->p2->Y,
p->X, p->Y,
p->X + n.x(), p->Y + n.y(), x);
if(x[0] >= 0 && x[0] <= 1){
SVector3 d(target->X-p->X,target->Y-p->Y,target->Z-p->Z);
d.normalize();
double COS = dot(n,d);
double L2 = sqrt((target->X - p->X) *(target->X - p->X) +
(target->X - p->Y) *(target->X - p->Y) +
(target->X - p->Z) *(target->X - p->Z) );
// swapping the edge alllow to find an edgge that has the right direction and
// right size
if(COS > cos(M_PI/6.0) && L2 < L){
m->swap_edge(e, BDS_SwapEdgeTestQuality(false,false));
BDS_Edge *newE = m->find_edge(p,target);
// printf("swapping -> %p\n",newE);
return newE;
}
// split the edge
else{
BDS_Point *mid;
mid = m->add_point(++m->MAXPOINTNUMBER,(1.-x[0])*e->p1->u + x[0]*e->p2->u,
(1.-x[0])*e->p1->v + x[0]*e->p2->v,gf);
mid->lc() = 0.5 * (p->lc() + target->lc());
mid->g = e->p1->g;
m->split_edge(e, mid);
BDS_Edge *newE = m->find_edge(p,mid);
// printf("splitting -> %p %p\n",newE,e->p1->g);
// m->cleanup();
return newE;
}
}
}
}
}
++it;
}
}
printf("zarbi\n");
return 0;
}
bool edgeFront::emptyFront(int tag)
{
// front edges tagged "tag" is empty
if(stat[tag].size() == 0) return true;
BDS_Edge *e = *(stat[tag].begin());
BDS_Edge *left, *right = 0;
eiter it1, it2;
std::vector<eiter> fe1, fe2;
printf("front edges %d %d tag %d\n", e->p1->iD, e->p2->iD, tag);
switch(tag){
// both left and right neighboring edges are
// sufficiently angled in order to allow to
// form the quad
case 3:
{
getFrontEdges(e->p1, it1, it2);
if(*it1 == e) left=*it2;
else left = *it1;
getFrontEdges(e->p2, it1, it2);
if(*it1 == e) right = *it2;
else right = *it1;
// printf("case 3\n");
}
break;
// right edge is angled with current edge
// we therefore find a new front edge in the
// mesh that allows to move to tag 3
case 2:
getFrontEdges(e->p1, it1, it2);
if(*it1 == e) left = *it2;
else left = *it1;
// printf("case 2 left edge %p\n",left);
right = findOptimalEdge(e->p2, left->othervertex(e->p1));
if(right) getFrontEdges(right->othervertex(e->p2), fe2);
break;
// left edge is angled with current edge
// we therefore find a new front edge in the
// mesh that allows to move to tag 3
case 1:
getFrontEdges(e->p2, it1, it2);
if(*it2 == e) right = *it1;
else right = *it2;
// printf("case 1 right edge %p %p\n",e,right);
left = findOptimalEdge(e->p1, right->othervertex(e->p2));
if(left) getFrontEdges(left->othervertex(e->p1), fe1);
break;
// no neighboring edge is angled with current edge
// we therefore find a new front edge in the
// mesh that allows to move to tag 3
case 0:
left = findOptimalEdge(e->p1, 0);
if(left) right= findOptimalEdge(e->p2, left->othervertex(e->p1));
if(right) getFrontEdges(right->othervertex(e->p2), fe2);
if(left) getFrontEdges(left->othervertex(e->p1), fe1);
// printf("strange\n");
break;
default:
Msg::Error("Unknown case in emptyFront");
return false;
}
if(fe1.size() || fe2.size() || !left || !right || !formQuad(e, left, right)){
// printf("front cloeses : algo has to be finished\n");
stat[tag].erase(stat[tag].begin());
stat[4].insert(e);
}
return false;
}
static int numQuads(BDS_Mesh *m)
{
int N = 0;
for(std::list<BDS_Face*>::iterator it = m->triangles.begin();
it != m->triangles.end(); ++it){
if((*it)->e4) N++;
}
return N;
}
int gmshQMorph(GFace *gf)
{
// assert first that there exist a triangulation of
// the face
if(!gf->triangles.size()){
Msg::Warning("Cannot Quadrilaterize a face that has not been triangulated");
return -1;
}
// create data structures for mesh manipulations
std::list<GFace*> l; l.push_back(gf);
BDS_Mesh *pm = gmsh2BDS(l);
// create the front
edgeFront front(pm,gf);
front.initiate();
int ITER = 1;
// empty the front for front edges tagged 3, 2, 1 then 0
int _numQuads = 0;
while(1){
if(front.emptyFront(3)){
if(front.emptyFront(2)){
if(front.emptyFront(1)){
if(front.emptyFront(0)){
int ns;
smoothVertexPass(gf,*pm,ns,false);
printf("nex row iter %6d->>>\n",ITER);
front.initiate();
int _numQuadsNew = numQuads(pm);
if(front.edges.size() == 0 || _numQuads == _numQuadsNew)
break;
_numQuads = _numQuadsNew;
}
}
}
}
ITER++;
if(1 || ITER%100 == 0){
char name[256];
sprintf(name,"qmorph-face%d-iter%d.pos",gf->tag(),ITER);
outputScalarField(pm->triangles, name, 0);
}
// if(ITER == 1123)break;
}
// delete the BDS
delete pm;
return 1;
}