double qmQuadrangleAngles (MQuadrangle *e) { double a = 100; double worst_quality = std::numeric_limits<double>::max(); double mat[3][3]; double mat2[3][3]; double den = atan(a*(M_PI/4)) + atan(a*(2*M_PI/4 - (M_PI/4))); // This matrix is used to "rotate" the triangle to get each vertex // as the "origin" of the mapping in turn //double rot[3][3]; //rot[0][0]=-1; rot[0][1]=1; rot[0][2]=0; //rot[1][0]=-1; rot[1][1]=0; rot[1][2]=0; //rot[2][0]= 0; rot[2][1]=0; rot[2][2]=1; //double tmp[3][3]; const double u[9] = {-1,-1, 1, 1, 0,0,1,-1,0}; const double v[9] = {-1, 1, 1,-1, -1,1,0,0,0}; for (int i = 0; i < 9; i++) { e->getJacobian(u[i], v[i], 0, mat); e->getPrimaryJacobian(u[i],v[i],0,mat2); //for (int j = 0; j < i; j++) { // matmat(rot,mat,tmp); // memcpy(mat, tmp, sizeof(mat)); //} //get angle double v1[3] = {mat[0][0], mat[0][1], mat[0][2] }; double v2[3] = {mat[1][0], mat[1][1], mat[1][2] }; double v3[3] = {mat2[0][0], mat2[0][1], mat2[0][2] }; double v4[3] = {mat2[1][0], mat2[1][1], mat2[1][2] }; norme(v1); norme(v2); norme(v3); norme(v4); double v12[3], v34[3]; prodve(v1,v2,v12); prodve(v3,v4,v34); norme(v12); norme(v34); double orientation; prosca(v12,v34,&orientation); // If the if the triangle is "flipped" it's no good // if (orientation < 0) // return -std::numeric_limits<double>::max(); double c; prosca(v1,v2,&c); double x = fabs(acos(c))-M_PI/2; //double angle = fabs(acos(c))*180/M_PI; double quality = (atan(a*(x+M_PI/4)) + atan(a*(2*M_PI/4 - (x+M_PI/4))))/den; worst_quality = std::min(worst_quality, quality); } return worst_quality; }
bool operator()(const AlphaElement &e1, const AlphaElement &e2) const { double cg1[3] = { 0., 0., 0. }, cg2[3] = { 0., 0., 0.}; for(int i = 0; i < numVertices; i++) { cg1[0] += e1.v[3 * i]; cg1[1] += e1.v[3 * i + 1]; cg1[2] += e1.v[3 * i + 2]; cg2[0] += e2.v[3 * i]; cg2[1] += e2.v[3 * i + 1]; cg2[2] += e2.v[3 * i + 2]; } return prosca(eye, cg1) < prosca(eye, cg2); }
SPoint2 gmshFace::parFromPoint(const SPoint3 &qp, bool onSurface) const { if(s->Typ == MSH_SURF_PLAN){ double x, y, z, VX[3], VY[3]; getMeanPlaneData(VX, VY, x, y, z); double u, v, vec[3] = {qp.x() - x, qp.y() - y, qp.z() - z}; prosca(vec, VX, &u); prosca(vec, VY, &v); return SPoint2(u, v); } else{ return GFace::parFromPoint(qp, onSurface); } }
bool edgeSwapTestAngle(BDS_Edge *e, double min_cos) { BDS_Face *f1 = e->faces(0); BDS_Face *f2 = e->faces(1); BDS_Point *n1[4]; BDS_Point *n2[4]; f1->getNodes(n1); f2->getNodes(n2); double norm1[3]; double norm2[3]; normal_triangle(n1[0], n1[1], n1[2], norm1); normal_triangle(n2[0], n2[1], n2[2], norm2); double cosa;prosca (norm1, norm2, &cosa); return cosa > min_cos; }
int IsoSimplex(double *X, double *Y, double *Z, double *Val, double V, double *Xp, double *Yp, double *Zp, double n[3]) { if(Val[0] == Val[1] && Val[0] == Val[2] && Val[0] == Val[3]) return 0; int nb = 0; if((Val[0] >= V && Val[1] <= V) || (Val[1] >= V && Val[0] <= V)) { InterpolateIso(X, Y, Z, Val, V, 0, 1, &Xp[nb], &Yp[nb], &Zp[nb]); nb++; } if((Val[0] >= V && Val[2] <= V) || (Val[2] >= V && Val[0] <= V)) { InterpolateIso(X, Y, Z, Val, V, 0, 2, &Xp[nb], &Yp[nb], &Zp[nb]); nb++; } if((Val[0] >= V && Val[3] <= V) || (Val[3] >= V && Val[0] <= V)) { InterpolateIso(X, Y, Z, Val, V, 0, 3, &Xp[nb], &Yp[nb], &Zp[nb]); nb++; } if((Val[1] >= V && Val[2] <= V) || (Val[2] >= V && Val[1] <= V)) { InterpolateIso(X, Y, Z, Val, V, 1, 2, &Xp[nb], &Yp[nb], &Zp[nb]); nb++; } if((Val[1] >= V && Val[3] <= V) || (Val[3] >= V && Val[1] <= V)) { InterpolateIso(X, Y, Z, Val, V, 1, 3, &Xp[nb], &Yp[nb], &Zp[nb]); nb++; } if((Val[2] >= V && Val[3] <= V) || (Val[3] >= V && Val[2] <= V)) { InterpolateIso(X, Y, Z, Val, V, 2, 3, &Xp[nb], &Yp[nb], &Zp[nb]); nb++; } // Remove identical nodes (this can happen if an edge belongs to the // zero levelset). We should be doing this even for nb < 4, but it // would slow us down even more (and we don't really care if some // nodes in a postprocessing element are identical) if(nb > 4) { double xi[6], yi[6], zi[6]; affect(xi, yi, zi, 0, Xp, Yp, Zp, 0); int ni = 1; for(int j = 1; j < nb; j++) { for(int i = 0; i < ni; i++) { if(fabs(Xp[j] - xi[i]) < 1.e-12 && fabs(Yp[j] - yi[i]) < 1.e-12 && fabs(Zp[j] - zi[i]) < 1.e-12) { break; } if(i == ni - 1) { affect(xi, yi, zi, i + 1, Xp, Yp, Zp, j); ni++; } } } for(int i = 0; i < ni; i++) affect(Xp, Yp, Zp, i, xi, yi, zi, i); nb = ni; } if(nb < 3 || nb > 4) return 0; // 3 possible quads at this point: (0,2,5,3), (0,1,5,4) or // (1,2,4,3), so simply invert the 2 last vertices for having the // quad ordered if(nb == 4) { double x = Xp[3], y = Yp[3], z = Zp[3]; Xp[3] = Xp[2]; Yp[3] = Yp[2]; Zp[3] = Zp[2]; Xp[2] = x; Yp[2] = y; Zp[2] = z; } // to get a nice isosurface, we should have n . grad v > 0, where n // is the normal to the polygon and v is the unknown field we want // to draw double v1[3] = {Xp[2] - Xp[0], Yp[2] - Yp[0], Zp[2] - Zp[0]}; double v2[3] = {Xp[1] - Xp[0], Yp[1] - Yp[0], Zp[1] - Zp[0]}; prodve(v1, v2, n); norme(n); double g[3]; gradSimplex(X, Y, Z, Val, g); double gdotn; prosca(g, n, &gdotn); if(gdotn > 0.) { double Xpi[6], Ypi[6], Zpi[6]; for(int i = 0; i < nb; i++) { Xpi[i] = Xp[i]; Ypi[i] = Yp[i]; Zpi[i] = Zp[i]; } for(int i = 0; i < nb; i++) { Xp[i] = Xpi[nb - i - 1]; Yp[i] = Ypi[nb - i - 1]; Zp[i] = Zpi[nb - i - 1]; } } else { n[0] = -n[0]; n[1] = -n[1]; n[2] = -n[2]; } return nb; }
int CutTriangle(double *X, double *Y, double *Z, double *Val, double V1, double V2, double *Xp2, double *Yp2, double *Zp2, double *Vp2) { // fill io so that it contains an indexing of the nodes such that // Val[io[i]] > Val[io[j]] if i > j int io[3] = {0, 1, 2}; for(int i = 0; i < 2; i++) { for(int j = i + 1; j < 3; j++) { if(Val[io[i]] > Val[io[j]]) { int iot = io[i]; io[i] = io[j]; io[j] = iot; } } } if(Val[io[0]] > V2 || Val[io[2]] < V1) return 0; if(V1 <= Val[io[0]] && Val[io[2]] <= V2) { for(int i = 0; i < 3; i++) { Vp2[i] = Val[i]; Xp2[i] = X[i]; Yp2[i] = Y[i]; Zp2[i] = Z[i]; } return 3; } int Np = 0, Fl = 0; double Xp[10], Yp[10], Zp[10], Vp[10]; if(V1 <= Val[io[0]]) { Vp[Np] = Val[io[0]]; Xp[Np] = X[io[0]]; Yp[Np] = Y[io[0]]; Zp[Np] = Z[io[0]]; Np++; Fl = 1; } else if(Val[io[0]] < V1 && V1 <= Val[io[1]]) { Vp[Np] = V1; InterpolateIso(X, Y, Z, Val, V1, io[0], io[2], &Xp[Np], &Yp[Np], &Zp[Np]); Np++; Vp[Np] = V1; InterpolateIso(X, Y, Z, Val, V1, io[0], io[1], &Xp[Np], &Yp[Np], &Zp[Np]); Np++; Fl = 1; } else { Vp[Np] = V1; InterpolateIso(X, Y, Z, Val, V1, io[0], io[2], &Xp[Np], &Yp[Np], &Zp[Np]); Np++; Vp[Np] = V1; InterpolateIso(X, Y, Z, Val, V1, io[1], io[2], &Xp[Np], &Yp[Np], &Zp[Np]); Np++; Fl = 0; } if(V2 == Val[io[0]]) { return 0; } else if((Val[io[0]] < V2) && (V2 < Val[io[1]])) { Vp[Np] = V2; InterpolateIso(X, Y, Z, Val, V2, io[0], io[1], &Xp[Np], &Yp[Np], &Zp[Np]); Np++; Vp[Np] = V2; InterpolateIso(X, Y, Z, Val, V2, io[0], io[2], &Xp[Np], &Yp[Np], &Zp[Np]); Np++; } else if(V2 < Val[io[2]]) { if(Fl) { Vp[Np] = Val[io[1]]; Xp[Np] = X[io[1]]; Yp[Np] = Y[io[1]]; Zp[Np] = Z[io[1]]; Np++; } Vp[Np] = V2; InterpolateIso(X, Y, Z, Val, V2, io[1], io[2], &Xp[Np], &Yp[Np], &Zp[Np]); Np++; Vp[Np] = V2; InterpolateIso(X, Y, Z, Val, V2, io[0], io[2], &Xp[Np], &Yp[Np], &Zp[Np]); Np++; } else { if(Fl) { Vp[Np] = Val[io[1]]; Xp[Np] = X[io[1]]; Yp[Np] = Y[io[1]]; Zp[Np] = Z[io[1]]; Np++; } Vp[Np] = Val[io[2]]; Xp[Np] = X[io[2]]; Yp[Np] = Y[io[2]]; Zp[Np] = Z[io[2]]; Np++; } Vp2[0] = Vp[0]; Xp2[0] = Xp[0]; Yp2[0] = Yp[0]; Zp2[0] = Zp[0]; int Np2 = 1; for(int i = 1; i < Np; i++) { if((Xp[i] != Xp2[Np2 - 1]) || (Yp[i] != Yp2[Np2 - 1]) || (Zp[i] != Zp2[Np2 - 1])){ Vp2[Np2] = Vp[i]; Xp2[Np2] = Xp[i]; Yp2[Np2] = Yp[i]; Zp2[Np2] = Zp[i]; Np2++; } } if(Xp2[0] == Xp2[Np2 - 1] && Yp2[0] == Yp2[Np2 - 1] && Zp2[0] == Zp2[Np2 - 1]) { Np2--; } // check and fix orientation double in1[3] = {X[1] - X[0], Y[1] - Y[0], Z[1] - Z[0]}; double in2[3] = {X[2] - X[0], Y[2] - Y[0], Z[2] - Z[0]}; double inn[3]; prodve(in1, in2, inn); double out1[3] = {Xp2[1] - Xp2[0], Yp2[1] - Yp2[0], Zp2[1] - Zp2[0]}; double out2[3] = {Xp2[2] - Xp2[0], Yp2[2] - Yp2[0], Zp2[2] - Zp2[0]}; double outn[3]; prodve(out1, out2, outn); double res; prosca(inn, outn, &res); if(res < 0){ for(int i = 0; i < Np2; i++){ Vp[i] = Vp2[Np2 - i - 1]; Xp[i] = Xp2[Np2 - i - 1]; Yp[i] = Yp2[Np2 - i - 1]; Zp[i] = Zp2[Np2 - i - 1]; } for(int i = 0; i < Np2; i++){ Vp2[i] = Vp[i]; Xp2[i] = Xp[i]; Yp2[i] = Yp[i]; Zp2[i] = Zp[i]; } } return Np2; }
bool evalSwapForOptimize(BDS_Edge *e, GFace *gf, BDS_Mesh &m) { if(e->numfaces() != 2) return false; BDS_Point *p11, *p12, *p13; BDS_Point *p21, *p22, *p23; BDS_Point *p31, *p32, *p33; BDS_Point *p41, *p42, *p43; swap_config(e, &p11, &p12, &p13, &p21, &p22, &p23, &p31, &p32, &p33, &p41, &p42, &p43); // First, evaluate what we gain in element quality if the // swap is performed double qa1 = qmTriangle(p11, p12, p13, QMTRI_RHO); double qa2 = qmTriangle(p21, p22, p23, QMTRI_RHO); double qb1 = qmTriangle(p31, p32, p33, QMTRI_RHO); double qb2 = qmTriangle(p41, p42, p43, QMTRI_RHO); double qa = std::min(qa1, qa2); double qb = std::min(qb1, qb2); double qualIndicator = qb - qa; bool qualShouldSwap = qb > 2*qa; bool qualCouldSwap = !(qb < qa * .5) && qb > .05; // then evaluate if swap produces smoother surfaces double norm11[3]; double norm12[3]; double norm21[3]; double norm22[3]; normal_triangle(p11, p12, p13, norm11); normal_triangle(p21, p22, p23, norm12); normal_triangle(p31, p32, p33, norm21); normal_triangle(p41, p42, p43, norm22); double cosa; prosca(norm11, norm12, &cosa); double cosb; prosca(norm21, norm22, &cosb); // double smoothIndicator = cosb - cosa; // bool smoothShouldSwap = (cosa < 0.1 && cosb > 0.3); // bool smoothCouldSwap = !(cosb < cosa * .5); double la = computeEdgeLinearLength(p11, p12); double la_ = computeEdgeLinearLength(p11, p12, gf, m.scalingU, m.scalingV); double lb = computeEdgeLinearLength(p13, p23); double lb_ = computeEdgeLinearLength(p13, p23, gf, m.scalingU, m.scalingV); double LA = (la_ -la) / la_; double LB = (lb_ -lb) / lb_; double distanceIndicator = LA - LB; bool distanceShouldSwap = (LB < .5 * LA) && LA > 1.e-2; bool distanceCouldSwap = !(LB > 2 * LA) || LB < 1.e-2; if (20 * qa < qb) return true; // if (LB > .025 && distanceIndicator > 0 && qb > .25) return true; // if (LB > .05 && distanceIndicator > 0 && qb > .15) return true; // if (LB > .1 && distanceIndicator > 0 && qb > .1) return true; // if (LB > .2 && distanceIndicator > 0 && qb > .05) return true; // if (LB > .3 && distanceIndicator > 0 && qb > .025) return true; // if swap enhances both criterion, the do it! if (distanceIndicator > 0 && qualIndicator > 0) return true; if (distanceShouldSwap && qualCouldSwap) return true; if (distanceCouldSwap && qualShouldSwap) return true; // if (smoothIndicator > 0 && qualIndicator > 0) return true; // if (smoothShouldSwap && qualCouldSwap) return true; // if (smoothCouldSwap && qualShouldSwap) return true; // if (distanceShouldSwap && qualCouldSwap) return true; // if (distanceCouldSwap && qualShouldSwap) return true; if (cosa < 0 && cosb > 0 && qb > 0.0) return true; return false; }
double qmTriangleAngles (MTriangle *e) { double a = 500; double worst_quality = std::numeric_limits<double>::max(); double mat[3][3]; double mat2[3][3]; double den = atan(a*(M_PI/9)) + atan(a*(M_PI/9)); // This matrix is used to "rotate" the triangle to get each vertex // as the "origin" of the mapping in turn double rot[3][3]; rot[0][0]=-1; rot[0][1]=1; rot[0][2]=0; rot[1][0]=-1; rot[1][1]=0; rot[1][2]=0; rot[2][0]= 0; rot[2][1]=0; rot[2][2]=1; double tmp[3][3]; //double minAngle = 120.0; for (int i = 0; i < e->getNumPrimaryVertices(); i++) { const double u = i == 1 ? 1 : 0; const double v = i == 2 ? 1 : 0; const double w = 0; e->getJacobian(u, v, w, mat); e->getPrimaryJacobian(u,v,w,mat2); for (int j = 0; j < i; j++) { matmat(rot,mat,tmp); memcpy(mat, tmp, sizeof(mat)); } //get angle double v1[3] = {mat[0][0], mat[0][1], mat[0][2] }; double v2[3] = {mat[1][0], mat[1][1], mat[1][2] }; double v3[3] = {mat2[0][0], mat2[0][1], mat2[0][2] }; double v4[3] = {mat2[1][0], mat2[1][1], mat2[1][2] }; norme(v1); norme(v2); norme(v3); norme(v4); double v12[3], v34[3]; prodve(v1,v2,v12); prodve(v3,v4,v34); norme(v12); norme(v34); double orientation; prosca(v12,v34,&orientation); // If the triangle is "flipped" it's no good if (orientation < 0) return -std::numeric_limits<double>::max(); double c; prosca(v1,v2,&c); double x = acos(c)-M_PI/3; //double angle = (x+M_PI/3)/M_PI*180; double quality = (atan(a*(x+M_PI/9)) + atan(a*(M_PI/9-x)))/den; worst_quality = std::min(worst_quality, quality); //minAngle = std::min(angle, minAngle); //printf("Angle %g ", angle); // printf("Quality %g\n",quality); } //printf("MinAngle %g \n", minAngle); //return minAngle; return worst_quality; }
void GMSH_LevelsetPlugin::_cutAndAddElements(PViewData *vdata, PViewData *wdata, int ent, int ele, int vstep, int wstep, double x[8], double y[8], double z[8], double levels[8], double scalarValues[8], PViewDataList* out) { int stepmin = vstep, stepmax = vstep + 1, otherstep = wstep; if(stepmin < 0){ stepmin = vdata->getFirstNonEmptyTimeStep(); stepmax = vdata->getNumTimeSteps(); } if(wstep < 0) otherstep = wdata->getFirstNonEmptyTimeStep(); int numNodes = vdata->getNumNodes(stepmin, ent, ele); int numEdges = vdata->getNumEdges(stepmin, ent, ele); int numComp = wdata->getNumComponents(otherstep, ent, ele); int type = vdata->getType(stepmin, ent, ele); // decompose the element into simplices for(int simplex = 0; simplex < numSimplexDec(type); simplex++){ int n[4], ep[12], nsn, nse; getSimplexDec(numNodes, numEdges, type, simplex, n[0], n[1], n[2], n[3], nsn, nse); // loop over time steps for(int step = stepmin; step < stepmax; step++){ // check which edges cut the iso and interpolate the value if(wstep < 0) otherstep = step; if(!wdata->hasTimeStep(otherstep)) continue; int np = 0; double xp[12], yp[12], zp[12], valp[12][9]; for(int i = 0; i < nse; i++){ int n0 = exn[nse][i][0], n1 = exn[nse][i][1]; if(levels[n[n0]] * levels[n[n1]] <= 0.) { double c = InterpolateIso(x, y, z, levels, 0., n[n0], n[n1], &xp[np], &yp[np], &zp[np]); for(int comp = 0; comp < numComp; comp++){ double v0, v1; wdata->getValue(otherstep, ent, ele, n[n0], comp, v0); wdata->getValue(otherstep, ent, ele, n[n1], comp, v1); valp[np][comp] = v0 + c * (v1 - v0); } ep[np++] = i + 1; } } // remove identical nodes (this can happen if an edge actually // belongs to the zero levelset, i.e., if levels[] * levels[] == 0) if(np > 1) removeIdenticalNodes(&np, numComp, xp, yp, zp, valp, ep); // if there are no cuts and we extract the volume, save the full // element if it is on the correct side of the levelset if(np <= 1 && _extractVolume){ bool add = true; for(int nod = 0; nod < nsn; nod++){ if((_extractVolume < 0. && levels[n[nod]] > 0.) || (_extractVolume > 0. && levels[n[nod]] < 0.)){ add = false; break; } } if(add){ for(int nod = 0; nod < nsn; nod++){ xp[nod] = x[n[nod]]; yp[nod] = y[n[nod]]; zp[nod] = z[n[nod]]; for(int comp = 0; comp < numComp; comp++) wdata->getValue(otherstep, ent, ele, n[nod], comp, valp[nod][comp]); } _addElement(nsn, nse, numComp, xp, yp, zp, valp, out, step == stepmin); } continue; } // discard invalid cases if(numEdges > 4 && np < 3) // 3D input should only lead to 2D output continue; else if(numEdges > 1 && np < 2) // 2D input should only lead to 1D output continue; else if(np < 1 || np > 4) // can't deal with this continue; // avoid "butterflies" if(np == 4) reorderQuad(numComp, xp, yp, zp, valp, ep); // orient the triangles and the quads to get the normals right if(!_extractVolume && (np == 3 || np == 4)) { // compute invertion test only once for spatially-fixed views if(step == stepmin || !_valueIndependent) { double v1[3] = {xp[2] - xp[0], yp[2] - yp[0], zp[2] - zp[0]}; double v2[3] = {xp[1] - xp[0], yp[1] - yp[0], zp[1] - zp[0]}; double gr[3], normal[3]; prodve(v1, v2, normal); switch (_orientation) { case MAP: gradSimplex(x, y, z, scalarValues, gr); prosca(gr, normal, &_invert); break; case PLANE: prosca(normal, _ref, &_invert); break; case SPHERE: gr[0] = xp[0] - _ref[0]; gr[1] = yp[0] - _ref[1]; gr[2] = zp[0] - _ref[2]; prosca(gr, normal, &_invert); case NONE: default: break; } } if(_invert > 0.) { double xpi[12], ypi[12], zpi[12], valpi[12][9]; int epi[12]; for(int k = 0; k < np; k++) affect(xpi, ypi, zpi, valpi, epi, k, xp, yp, zp, valp, ep, k, numComp); for(int k = 0; k < np; k++) affect(xp, yp, zp, valp, ep, k, xpi, ypi, zpi, valpi, epi, np - k - 1, numComp); } } // if we extract volumes, add the nodes on the chosen side // (FIXME: when cutting 2D views, the elts can have the wrong // orientation) if(_extractVolume){ int nbCut = np; for(int nod = 0; nod < nsn; nod++){ if((_extractVolume < 0. && levels[n[nod]] < 0.) || (_extractVolume > 0. && levels[n[nod]] > 0.)){ xp[np] = x[n[nod]]; yp[np] = y[n[nod]]; zp[np] = z[n[nod]]; for(int comp = 0; comp < numComp; comp++) wdata->getValue(otherstep, ent, ele, n[nod], comp, valp[np][comp]); ep[np] = -(nod + 1); // store node num! np++; } } removeIdenticalNodes(&np, numComp, xp, yp, zp, valp, ep); if(np == 4 && numEdges <= 4) reorderQuad(numComp, xp, yp, zp, valp, ep); if(np == 6) reorderPrism(numComp, xp, yp, zp, valp, ep, nbCut); if(np > 8) // can't deal with this continue; } // finally, add the new element _addElement(np, numEdges, numComp, xp, yp, zp, valp, out, step == stepmin); } if(vstep < 0){ for(int i = stepmin; i < stepmax; i++) { out->Time.push_back(vdata->getTime(i)); } } } }