Forked from
gmsh / gmsh
13371 commits behind the upstream repository.
-
Christophe Geuzaine authored
- fixed MED node ordering - polished 64 bit Cocoa Mac OS X FLTK version - updated copyright
Christophe Geuzaine authored- fixed MED node ordering - polished 64 bit Cocoa Mac OS X FLTK version - updated copyright
meshGFaceTransfinite.cpp 16.27 KiB
// Gmsh - Copyright (C) 1997-2011 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to <gmsh@geuz.org>.
#include <map>
#include "meshGFace.h"
#include "GVertex.h"
#include "GEdge.h"
#include "GFace.h"
#include "MVertex.h"
#include "MTriangle.h"
#include "MQuadrangle.h"
#include "Context.h"
#include "GmshMessage.h"
#include "Numeric.h"
#define SQU(a) ((a)*(a))
/*
s4 +-----c3-----+ s3
| |
| |
c4 c2
| |
| |
s1 +-----c1-----+ s2
*/
// f(u,v) = (1-u) c4(v) + u c2(v) + (1-v) c1(u) + v c3(u)
// - [ (1-u)(1-v) s1 + u(1-v) s2 + uv s3 + (1-u)v s4 ]
#define TRAN_QUA(c1,c2,c3,c4,s1,s2,s3,s4,u,v) \
(1.-u)*c4+u*c2+(1.-v)*c1+v*c3-((1.-u)*(1.-v)*s1+u*(1.-v)*s2+u*v*s3+(1.-u)*v*s4)
// s1=s4=c4
// f(u,v) = u c2 (v) + (1-v) c1(u) + v c3(u) - u(1-v) s2 - uv s3
#define TRAN_TRI(c1,c2,c3,s1,s2,s3,u,v) u*c2+(1.-v)*c1+v*c3-(u*(1.-v)*s2+u*v*s3)
void findTransfiniteCorners(GFace *gf, std::vector<MVertex*> &corners)
{
if(gf->meshAttributes.corners.size()){
// corners have been specified explicitly
for(unsigned int i = 0; i < gf->meshAttributes.corners.size(); i++)
corners.push_back(gf->meshAttributes.corners[i]->mesh_vertices[0]);
}
else{
// try to find the corners automatically
std::list<GEdge*> fedges = gf->edges();
GEdgeLoop el(fedges);
for(GEdgeLoop::iter it = el.begin(); it != el.end(); it++)
corners.push_back(it->getBeginVertex()->mesh_vertices[0]);
// try reaaally hard for 3-sided faces
if(corners.size() == 3){
GEdge *first = 0, *last = 0;
for(std::list<GEdge*>::iterator it = fedges.begin(); it != fedges.end(); it++){
if(((*it)->getBeginVertex()->mesh_vertices[0] == corners[0] &&
(*it)->getEndVertex()->mesh_vertices[0] == corners[1]) ||
((*it)->getBeginVertex()->mesh_vertices[0] == corners[1] &&
(*it)->getEndVertex()->mesh_vertices[0] == corners[0])){
first = *it;
}
if(((*it)->getBeginVertex()->mesh_vertices[0] == corners[2] &&
(*it)->getEndVertex()->mesh_vertices[0] == corners[0]) ||
((*it)->getBeginVertex()->mesh_vertices[0] == corners[0] &&
(*it)->getEndVertex()->mesh_vertices[0] == corners[2])){
last = *it;
}
}
if(first && last){
if(first->mesh_vertices.size() != last->mesh_vertices.size()){
std::vector<MVertex*> corners2(3);
corners2[0] = corners[1];
corners2[1] = corners[2];
corners2[2] = corners[0];
corners = corners2;
}
}
}
}
}
static void computeEdgeLoops(const GFace *gf, std::vector<MVertex*> &all_mvertices,
std::vector<int> &indices)
{
std::list<GEdge*> edges = gf->edges();
std::list<int> ori = gf->orientations();
std::list<GEdge*>::iterator it = edges.begin();
std::list<int>::iterator ito = ori.begin();
indices.push_back(0);
GVertex *start = ((*ito) == 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
GVertex *v_end = ((*ito) != 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
all_mvertices.push_back(start->mesh_vertices[0]);
if(*ito == 1)
for(unsigned int i = 0; i < (*it)->mesh_vertices.size(); i++)
all_mvertices.push_back((*it)->mesh_vertices[i]);
else
for(int i = (*it)->mesh_vertices.size() - 1; i >= 0; i--)
all_mvertices.push_back((*it)->mesh_vertices[i]);
GVertex *v_start = start;
while(1){
++it;
++ito;
if(v_end == start){
indices.push_back(all_mvertices.size());
if(it == edges.end ()) break;
start = ((*ito) == 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
v_end = ((*ito) != 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
v_start = start;
}
else{
if(it == edges.end ()){
Msg::Error("Something wrong in edge loop computation");
return;
}
v_start = ((*ito) == 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
if(v_start != v_end){
Msg::Error("Something wrong in edge loop computation");
return;
}
v_end = ((*ito) != 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
}
all_mvertices.push_back(v_start->mesh_vertices[0]);
if(*ito == 1)
for(unsigned int i = 0; i < (*it)->mesh_vertices.size(); i++)
all_mvertices.push_back((*it)->mesh_vertices[i]);
else
for(int i = (*it)->mesh_vertices.size()-1; i >= 0; i--)
all_mvertices.push_back((*it)->mesh_vertices[i]);
}
}
int MeshTransfiniteSurface(GFace *gf)
{
if(gf->meshAttributes.Method != MESH_TRANSFINITE) return 0;
Msg::Info("Meshing surface %d (transfinite)", gf->tag());
std::vector<MVertex*> corners;
findTransfiniteCorners(gf, corners);
if(corners.size () != 3 && corners.size () != 4){
Msg::Error("Surface %d is transfinite but has %d corners",
gf->tag(), corners.size());
return 0;
}
std::vector<MVertex*> d_vertices;
std::vector<int> indices;
computeEdgeLoops(gf, d_vertices, indices);
if(indices.size () != 2){
Msg::Error("Surface %d is transfinite but has %d holes",
gf->tag(), indices.size() - 2);
return 0;
}
// create a list of all boundary vertices, starting at the first
// transfinite corner
std::vector <MVertex *> m_vertices;
unsigned int I;
for(I = 0; I < d_vertices.size(); I++)
if(d_vertices[I] == corners[0]) break;
for(unsigned int j = 0; j < d_vertices.size(); j++)
m_vertices.push_back(d_vertices[(I + j) % d_vertices.size()]);
// make the ordering of the list consistent with the ordering of the
// first two corners (if the second found corner is not the second
// corner, just revert the list)
bool revert = false;
for(unsigned int i = 1; i < m_vertices.size(); i++){
MVertex *v = m_vertices[i];
if(v == corners[1] || v == corners[2] ||
(corners.size() == 4 && v == corners[3])){
if(v != corners[1]) revert = true;
break;
}
}
if(revert){
std::vector <MVertex *> tmp;
tmp.push_back(m_vertices[0]);
for(int i = m_vertices.size() - 1; i > 0; i--)
tmp.push_back(m_vertices[i]);
m_vertices = tmp;
}
// get the indices of the interpolation corners as well as the u,v
// coordinates of all the boundary vertices
int iCorner = 0, N[4] = {0, 0, 0, 0};
std::vector<double> U, V;
for(unsigned int i = 0; i < m_vertices.size(); i++){
MVertex *v = m_vertices[i];
if(v == corners[0] || v == corners[1] || v == corners[2] ||
(corners.size() == 4 && v == corners[3])){
N[iCorner++] = i;
if(iCorner > 4){
Msg::Error("Surface %d transfinite parameters are incoherent", gf->tag());
return 0;
}
}
SPoint2 param;
reparamMeshVertexOnFace(v, gf, param);
U.push_back(param[0]);
V.push_back(param[1]);
}
int N1 = N[0], N2 = N[1], N3 = N[2], N4 = N[3];
int L = N2 - N1, H = N3 - N2;
if(corners.size () == 4){
int Lb = N4 - N3, Hb = m_vertices.size() - N4;
if(Lb != L || Hb != H){
Msg::Error("Surface %d cannot be meshed using the transfinite algo",
gf->tag());
return 0;
}
}
else{
int Lb = m_vertices.size() - N3;
if(Lb != L){
Msg::Error("Surface %d cannot be meshed using the transfinite algo %d != %d",
gf->tag(), L, Lb);
return 0;
}
}
std::vector<double> lengths_i, lengths_j;
double L_i = 0, L_j = 0;
lengths_i.push_back(0.);
lengths_j.push_back(0.);
for(int i = 0; i < L; i++){
MVertex *v1 = m_vertices[i];
MVertex *v2 = m_vertices[i + 1];
L_i += v1->distance(v2);
lengths_i.push_back(L_i);
}
for(int i = L; i < L + H; i++){
MVertex *v1 = m_vertices[i];
MVertex *v2 = m_vertices[i + 1];
L_j += v1->distance(v2);
lengths_j.push_back(L_j);
}
/*
2L+H +------------+ L+H
| |
| |
| |
| |
2L+2H+2 +------------+
0 L
*/
std::vector<std::vector<MVertex*> > &tab(gf->transfinite_vertices);
tab.resize(L + 1);
for(int i = 0; i <= L; i++) tab[i].resize(H + 1);
if(corners.size () == 4){
tab[0][0] = m_vertices[0];
tab[L][0] = m_vertices[L];
tab[L][H] = m_vertices[L+H];
tab[0][H] = m_vertices[2*L+H];
for (int i = 1; i < L; i++){
tab[i][0] = m_vertices[i];
tab[i][H] = m_vertices[2*L+H-i];
}
for(int i = 1; i < H; i++){
tab[L][i] = m_vertices[L+i];
tab[0][i] = m_vertices[2*L+2*H-i];
}
}
else{
tab[0][0] = m_vertices[0];
tab[L][0] = m_vertices[L];
tab[L][H] = m_vertices[L+H];
// degenerated, only necessary for transfinite volume algo
tab[0][H] = m_vertices[0];
for (int i = 1; i < L; i++){
tab[i][0] = m_vertices[i];
tab[i][H] = m_vertices[2*L+H-i];
}
for(int i = 1; i < H;i++){
tab[L][i] = m_vertices[L+i];
// degenerated, only necessary for transfinite volume algo
tab[0][i] = m_vertices[0];
}
}
double UC1 = U[N1], UC2 = U[N2], UC3 = U[N3];
double VC1 = V[N1], VC2 = V[N2], VC3 = V[N3];
//create points using transfinite interpolation
if(corners.size() == 4){
double UC4 = U[N4];
double VC4 = V[N4];
for(int i = 1; i < L; i++){
double u = lengths_i[i] / L_i;
for(int j = 1; j < H; j++){
double v = lengths_j[j] / L_j;
int iP1 = N1 + i;
int iP2 = N2 + j;
int iP3 = N4 - i;
int iP4 = (N4 + (N3 - N2) - j) % m_vertices.size();
double Up = TRAN_QUA(U[iP1], U[iP2], U[iP3], U[iP4], UC1, UC2, UC3, UC4, u, v);
double Vp = TRAN_QUA(V[iP1], V[iP2], V[iP3], V[iP4], VC1, VC2, VC3, VC4, u, v);
GPoint gp = gf->point(SPoint2(Up, Vp));
MFaceVertex *newv = new MFaceVertex(gp.x(), gp.y(), gp.z(), gf, Up, Vp);
gf->mesh_vertices.push_back(newv);
tab[i][j] = newv;
}
}
}
else{
for(int i = 1; i < L; i++){
double u = lengths_i[i] / L_i;
for(int j = 1; j < H; j++){
double v = lengths_j[j] / L_j;
int iP1 = N1 + i;
int iP2 = N2 + j;
int iP3 = ((N3 + N2) - i) % m_vertices.size();
double Up, Vp;
if(gf->geomType() != GEntity::RuledSurface){
Up = TRAN_TRI(U[iP1], U[iP2], U[iP3], UC1, UC2, UC3, u, v);
Vp = TRAN_TRI(V[iP1], V[iP2], V[iP3], VC1, VC2, VC3, u, v);
}
else{
// FIXME: to get nice meshes we would need to make the u,v
// coords match with the (degenerate) coordinates of the
// underlying ruled surface; so instead we just interpolate
// in real space
double xp = TRAN_TRI(m_vertices[iP1]->x(), m_vertices[iP2]->x(),
m_vertices[iP3]->x(), m_vertices[N1]->x(),
m_vertices[N2]->x(), m_vertices[N3]->x(), u, v);
double yp = TRAN_TRI(m_vertices[iP1]->y(), m_vertices[iP2]->y(),
m_vertices[iP3]->y(), m_vertices[N1]->y(),
m_vertices[N2]->y(), m_vertices[N3]->y(), u, v);
double zp = TRAN_TRI(m_vertices[iP1]->z(), m_vertices[iP2]->z(),
m_vertices[iP3]->z(), m_vertices[N1]->z(),
m_vertices[N2]->z(), m_vertices[N3]->z(), u, v);
// xp,yp,zp can be off the surface so we cannot use parFromPoint
gf->XYZtoUV(xp, yp, zp, Up, Vp, 1.0, false);
}
GPoint gp = gf->point(SPoint2(Up, Vp));
MFaceVertex *newv = new MFaceVertex(gp.x(), gp.y(), gp.z(), gf, Up, Vp);
gf->mesh_vertices.push_back(newv);
tab[i][j] = newv;
}
}
}
// should we apply the elliptic smoother?
int numSmooth = 0;
if(gf->meshAttributes.transfiniteSmoothing < 0 && CTX::instance()->mesh.nbSmoothing > 1)
numSmooth = CTX::instance()->mesh.nbSmoothing;
else if(gf->meshAttributes.transfiniteSmoothing > 0)
numSmooth = gf->meshAttributes.transfiniteSmoothing;
if(corners.size() == 4 && numSmooth){
std::vector<std::vector<double> > u(L + 1), v(L + 1);
for(int i = 0; i <= L; i++){
u[i].resize(H + 1);
v[i].resize(H + 1);
}
for(int i = 0; i <= L; i++){
for(int j = 0; j <= H; j++){
int iP1 = N1 + i;
int iP2 = N2 + j;
int iP3 = N4 - i;
int iP4 = (N4 + (N3 - N2) - j) % m_vertices.size();
if(j == 0) { u[i][j] = U[iP1]; v[i][j] = V[iP1]; }
else if(i == L){ u[i][j] = U[iP2]; v[i][j] = V[iP2]; }
else if(j == H){ u[i][j] = U[iP3]; v[i][j] = V[iP3]; }
else if(i == 0){ u[i][j] = U[iP4]; v[i][j] = V[iP4]; }
else{
tab[i][j]->getParameter(0, u[i][j]);
tab[i][j]->getParameter(1, v[i][j]);
}
}
}
for(int IT = 0; IT < numSmooth; IT++){
for(int i = 1; i < L; i++){
for(int j = 1; j < H; j++){
double alpha = 0.25 * (SQU(u[i][j + 1] - u[i][j - 1]) +
SQU(v[i][j + 1] - v[i][j - 1])) ;
double gamma = 0.25 * (SQU(u[i + 1][j] - u[i - 1][j]) +
SQU(v[i + 1][j] - v[i - 1][j]));
double beta = 0.0625 *
((u[i + 1][j] - u[i - 1][j]) * (u[i][j + 1] - u[i][j - 1]) +
(v[i + 1][j] - v[i - 1][j]) * (v[i][j + 1] - v[i][j - 1]));
u[i][j] = 0.5 *
(alpha * (u[i + 1][j] + u[i - 1][j]) +
gamma * (u[i][j + 1] + u[i][j - 1]) -
2. * beta * (u[i + 1][j + 1] - u[i - 1][j + 1] -
u[i + 1][j - 1] + u[i - 1][j - 1])) / (alpha + gamma);
v[i][j] = 0.5 *
(alpha * (v[i + 1][j] + v[i - 1][j]) +
gamma * (v[i][j + 1] + v[i][j - 1]) -
2. * beta * (v[i + 1][j + 1] - v[i - 1][j + 1] -
v[i + 1][j - 1] + v[i - 1][j - 1])) / (alpha + gamma);
}
}
}
for(int i = 1; i < L; i++){
for(int j = 1; j < H; j++){
GPoint p = gf->point(SPoint2(u[i][j], v[i][j]));
tab[i][j]->x() = p.x();
tab[i][j]->y() = p.y();
tab[i][j]->z() = p.z();
tab[i][j]->setParameter(0, u[i][j]);
tab[i][j]->setParameter(1, v[i][j]);
}
}
}
// create elements
if(corners.size() == 4){
for(int i = 0; i < L ; i++){
for(int j = 0; j < H; j++){
MVertex *v1 = tab[i][j];
MVertex *v2 = tab[i + 1][j];
MVertex *v3 = tab[i + 1][j + 1];
MVertex *v4 = tab[i][j + 1];
if(CTX::instance()->mesh.recombineAll || gf->meshAttributes.recombine)
gf->quadrangles.push_back(new MQuadrangle(v1, v2, v3, v4));
else if(gf->meshAttributes.transfiniteArrangement == 1 ||
(gf->meshAttributes.transfiniteArrangement == 0 &&
((i % 2 == 0 && j % 2 == 1) ||
(i % 2 == 1 && j % 2 == 0)))){
gf->triangles.push_back(new MTriangle(v1, v2, v3));
gf->triangles.push_back(new MTriangle(v3, v4, v1));
}
else{
gf->triangles.push_back(new MTriangle(v1, v2, v4));
gf->triangles.push_back(new MTriangle(v4, v2, v3));
}
}
}
}
else{
for(int j = 0; j < H; j++){
MVertex *v1 = tab[0][0];
MVertex *v2 = tab[1][j];
MVertex *v3 = tab[1][j + 1];
gf->triangles.push_back(new MTriangle(v1, v2, v3));
}
for(int i = 1; i < L ; i++){
for(int j = 0; j < H; j++){
MVertex *v1 = tab[i][j];
MVertex *v2 = tab[i + 1][j];
MVertex *v3 = tab[i + 1][j + 1];
MVertex *v4 = tab[i][j + 1];
if(CTX::instance()->mesh.recombineAll || gf->meshAttributes.recombine)
gf->quadrangles.push_back(new MQuadrangle(v1, v2, v3, v4));
else if(gf->meshAttributes.transfiniteArrangement == 1 ||
(gf->meshAttributes.transfiniteArrangement == 0 &&
((i % 2 == 0 && j % 2 == 1) ||
(i % 2 == 1 && j % 2 == 0)))){
gf->triangles.push_back(new MTriangle(v1, v2, v3));
gf->triangles.push_back(new MTriangle(v3, v4, v1));
}
else{
gf->triangles.push_back(new MTriangle(v1, v2, v4));
gf->triangles.push_back(new MTriangle(v4, v2, v3));
}
}
}
}
gf->meshStatistics.status = GFace::DONE;
return 1;
}