/** compute iso size map */ int _MMG5_DoSol(MMG5_pMesh mesh,MMG5_pSol met) { MMG5_pTetra pt; MMG5_pPoint p1,p2; double ux,uy,uz,dd; int i,k,ia,ib,ipa,ipb; int *mark; _MMG5_SAFE_CALLOC(mark,mesh->np+1,int); /* Memory alloc */ met->np = mesh->np; met->npmax = mesh->npmax; met->size = 1; met->dim = mesh->dim; _MMG5_ADD_MEM(mesh,(met->npmax*met->size+1)*sizeof(double),"solution",return(0)); _MMG5_SAFE_CALLOC(met->m,met->npmax*met->size+1,double); /* internal edges */ for (k=1; k<=mesh->ne; k++) { pt = &mesh->tetra[k]; if ( !pt->v[0] ) continue; /* internal edges */ for (i=0; i<6; i++) { ia = _MMG5_iare[i][0]; ib = _MMG5_iare[i][1]; ipa = pt->v[ia]; ipb = pt->v[ib]; p1 = &mesh->point[ipa]; p2 = &mesh->point[ipb]; ux = p1->c[0] - p2->c[0]; uy = p1->c[1] - p2->c[1]; uz = p1->c[2] - p2->c[2]; dd = sqrt(ux*ux + uy*uy + uz*uz); met->m[ipa] += dd; mark[ipa]++; met->m[ipb] += dd; mark[ipb]++; } } /* vertex size */ for (k=1; k<=mesh->np; k++) { p1 = &mesh->point[k]; if ( !mark[k] ) { met->m[k] = mesh->info.hmax; continue; } else met->m[k] = MG_MIN(mesh->info.hmax,MG_MAX(mesh->info.hmin,met->m[k] / (double)mark[k])); } _MMG5_SAFE_FREE(mark); return(1); }
/* Compute the intersected (2 x 2) metric between metrics m and n, PRESERVING the directions of m. Result is stored in mr*/ int intmetsavedir(MMG5_pMesh mesh, double *m,double *n,double *mr) { int i; double lambda[2],vp[2][2],siz,isqhmin; isqhmin = 1.0 / (mesh->info.hmin * mesh->info.hmin); _MMG5_eigensym(m,lambda,vp); for (i=0; i<2; i++) { siz = n[0]*vp[i][0]*vp[i][0] + 2.0*n[1]*vp[i][0]*vp[i][1] + n[2]*vp[i][1]*vp[i][1]; lambda[i] = MG_MAX(lambda[i],siz); lambda[i] = MG_MIN(lambda[i],isqhmin); } mr[0] = lambda[0]*vp[0][0]*vp[0][0] + lambda[1]*vp[1][0]*vp[1][0]; mr[1] = lambda[0]*vp[0][0]*vp[0][1] + lambda[1]*vp[1][0]*vp[1][1]; mr[2] = lambda[0]*vp[0][1]*vp[0][1] + lambda[1]*vp[1][1]*vp[1][1]; return(1); }
/** * \param n pointer toward the vector that we want to send on the third vector * of canonical basis. * \param r computed rotation matrix. * * Compute rotation matrix that sends vector \a n to the third vector of * canonical basis. * */ inline int _MMG5_rotmatrix(double n[3],double r[3][3]) { double aa,bb,ab,ll,l,cosalpha,sinalpha; aa = n[0]*n[0]; bb = n[1]*n[1]; ab = n[0]*n[1]; ll = aa+bb; cosalpha = n[2]; sinalpha = sqrt(1.0- MG_MIN(1.0,cosalpha*cosalpha)); /* No rotation needed in this case */ if ( ll < _MMG5_EPS ) { if ( n[2] > 0.0 ) { r[0][0] = 1.0 ; r[0][1] = 0.0 ; r[0][2] = 0.0; r[1][0] = 0.0 ; r[1][1] = 1.0 ; r[1][2] = 0.0; r[2][0] = 0.0 ; r[2][1] = 0.0 ; r[2][2] = 1.0; } else { r[0][0] = -1.0 ; r[0][1] = 0.0 ; r[0][2] = 0.0; r[1][0] = 0.0 ; r[1][1] = 1.0 ; r[1][2] = 0.0; r[2][0] = 0.0 ; r[2][1] = 0.0 ; r[2][2] = -1.0; } } else { l = sqrt(ll); r[0][0] = (aa*cosalpha + bb)/ll; r[0][1] = ab*(cosalpha-1)/ll; r[0][2] = -n[0]*sinalpha/l; r[1][0] = r[0][1]; r[1][1] = (bb*cosalpha + aa)/ll; r[1][2] = -n[1]*sinalpha/l; r[2][0] = n[0]*sinalpha/l; r[2][1] = n[1]*sinalpha/l; r[2][2] = cosalpha; } return(1); }
/** * \param mesh pointer toward the mesh structure. * \param met pointer toward the metric structure. * \param nump index of point in which the size must be computed. * \param lists pointer toward the surfacic ball of \a nump. * \param ilists size of surfacic ball of \a nump. * \param hmin minimal edge size. * \param hmax maximal edge size. * \param hausd hausdorff value. * \return the isotropic size at the point. * * Define isotropic size at regular point nump, whose surfacic ball is provided. * */ static double _MMG5_defsizreg(MMG5_pMesh mesh,MMG5_pSol met,int nump,int *lists, int ilists, double hmin,double hmax,double hausd) { MMG5_pTetra pt; MMG5_pxTetra pxt; MMG5_pPoint p0,p1; MMG5_Tria tt; _MMG5_Bezier b; double ux,uy,uz,det2d,h,isqhmin,isqhmax,ll,lmin,lmax,hnm,s; double *n,*t,r[3][3],lispoi[3*MMG3D_LMAX+1],intm[3],b0[3],b1[3],c[3],tAA[6],tAb[3],d[3]; double kappa[2],vp[2][2]; int k,na,nb,ntempa,ntempb,iel,ip0; char iface,i,j,i0; p0 = &mesh->point[nump]; if ( !p0->xp || MG_EDG(p0->tag) || (p0->tag & MG_NOM) || (p0->tag & MG_REQ)) { fprintf(stdout," ## Func. _MMG5_defsizreg : wrong point qualification : xp ? %d\n",p0->xp); return(0); } isqhmin = 1.0 / (hmin*hmin); isqhmax = 1.0 / (hmax*hmax); n = &mesh->xpoint[p0->xp].n1[0]; /* Step 1 : rotation matrix that sends normal n to the third coordinate vector of R^3 */ if ( !_MMG5_rotmatrix(n,r) ) { fprintf(stdout,"%s:%d: Error: function _MMG5_rotmatrix return 0\n", __FILE__,__LINE__); exit(EXIT_FAILURE); } /* Step 2 : rotation of the oriented surfacic ball with r : lispoi[k] is the common edge between faces lists[k-1] and lists[k] */ iel = lists[0] / 4; iface = lists[0] % 4; pt = &mesh->tetra[iel]; lmin = MAXLEN; lmax = 0.0; na = nb = 0; for (i=0; i<3; i++) { if ( pt->v[_MMG5_idir[iface][i]] != nump ) { if ( !na ) na = pt->v[_MMG5_idir[iface][i]]; else nb = pt->v[_MMG5_idir[iface][i]]; } } for (k=1; k<ilists; k++) { iel = lists[k] / 4; iface = lists[k] % 4; pt = &mesh->tetra[iel]; ntempa = ntempb = 0; for (i=0; i<3; i++) { if ( pt->v[_MMG5_idir[iface][i]] != nump ) { if ( !ntempa ) ntempa = pt->v[_MMG5_idir[iface][i]]; else ntempb = pt->v[_MMG5_idir[iface][i]]; } } if ( ntempa == na ) p1 = &mesh->point[na]; else if ( ntempa == nb ) p1 = &mesh->point[nb]; else if ( ntempb == na ) p1 = &mesh->point[na]; else { assert(ntempb == nb); p1 = &mesh->point[nb]; } ux = p1->c[0] - p0->c[0]; uy = p1->c[1] - p0->c[1]; uz = p1->c[2] - p0->c[2]; lispoi[3*k+1] = r[0][0]*ux + r[0][1]*uy + r[0][2]*uz; lispoi[3*k+2] = r[1][0]*ux + r[1][1]*uy + r[1][2]*uz; lispoi[3*k+3] = r[2][0]*ux + r[2][1]*uy + r[2][2]*uz; ll = lispoi[3*k+1]*lispoi[3*k+1] + lispoi[3*k+2]*lispoi[3*k+2] + lispoi[3*k+3]*lispoi[3*k+3]; lmin = MG_MIN(lmin,ll); lmax = MG_MAX(lmax,ll); na = ntempa; nb = ntempb; } /* Finish with point 0 */ iel = lists[0] / 4; iface = lists[0] % 4; pt = &mesh->tetra[iel]; ntempa = ntempb = 0; for (i=0; i<3; i++) { if ( pt->v[_MMG5_idir[iface][i]] != nump ) { if ( !ntempa ) ntempa = pt->v[_MMG5_idir[iface][i]]; else ntempb = pt->v[_MMG5_idir[iface][i]]; } } if ( ntempa == na ) p1 = &mesh->point[na]; else if ( ntempa == nb ) p1 = &mesh->point[nb]; else if ( ntempb == na ) p1 = &mesh->point[na]; else { assert(ntempb == nb); p1 = &mesh->point[nb]; } ux = p1->c[0] - p0->c[0]; uy = p1->c[1] - p0->c[1]; uz = p1->c[2] - p0->c[2]; lispoi[1] = r[0][0]*ux + r[0][1]*uy + r[0][2]*uz; lispoi[2] = r[1][0]*ux + r[1][1]*uy + r[1][2]*uz; lispoi[3] = r[2][0]*ux + r[2][1]*uy + r[2][2]*uz; ll = lispoi[1]*lispoi[1] + lispoi[2]*lispoi[2] + lispoi[3]*lispoi[3]; lmin = MG_MIN(lmin,ll); lmax = MG_MAX(lmax,ll); /* list goes modulo ilist */ lispoi[3*ilists+1] = lispoi[1]; lispoi[3*ilists+2] = lispoi[2]; lispoi[3*ilists+3] = lispoi[3]; /* At this point, lispoi contains the oriented surface ball of point p0, that has been rotated through r, with the convention that triangle l has edges lispoi[l]; lispoi[l+1] */ if ( lmax/lmin > 4.0*hmax*hmax/ (hmin*hmin) ) return(hmax); /* Check all projections over tangent plane. */ for (k=0; k<ilists-1; k++) { det2d = lispoi[3*k+1]*lispoi[3*(k+1)+2] - lispoi[3*k+2]*lispoi[3*(k+1)+1]; if ( det2d < 0.0 ) return(hmax); } det2d = lispoi[3*(ilists-1)+1]*lispoi[3*0+2] - lispoi[3*(ilists-1)+2]*lispoi[3*0+1]; if ( det2d < 0.0 ) return(hmax); /* Reconstitution of the curvature tensor at p0 in the tangent plane, with a quadric fitting approach */ memset(intm,0.0,3*sizeof(double)); memset(tAA,0.0,6*sizeof(double)); memset(tAb,0.0,3*sizeof(double)); for (k=0; k<ilists; k++) { iel = lists[k] / 4; iface = lists[k] % 4; _MMG5_tet2tri(mesh,iel,iface,&tt); pxt = &mesh->xtetra[mesh->tetra[iel].xt]; if ( !_MMG5_bezierCP(mesh,&tt,&b,MG_GET(pxt->ori,iface)) ) { fprintf(stdout,"%s:%d: Error: function _MMG5_bezierCP return 0\n", __FILE__,__LINE__); exit(EXIT_FAILURE); } for (i0=0; i0<3; i0++) { if ( tt.v[i0] == nump ) break; } assert(i0 < 3); for (j=0; j<10; j++) { c[0] = b.b[j][0] - p0->c[0]; c[1] = b.b[j][1] - p0->c[1]; c[2] = b.b[j][2] - p0->c[2]; b.b[j][0] = r[0][0]*c[0] + r[0][1]*c[1] + r[0][2]*c[2]; b.b[j][1] = r[1][0]*c[0] + r[1][1]*c[1] + r[1][2]*c[2]; b.b[j][2] = r[2][0]*c[0] + r[2][1]*c[1] + r[2][2]*c[2]; } /* Mid-point along left edge and endpoint in the rotated frame */ if ( i0 == 0 ) { memcpy(b0,&(b.b[7][0]),3*sizeof(double)); memcpy(b1,&(b.b[8][0]),3*sizeof(double)); } else if ( i0 == 1 ) { memcpy(b0,&(b.b[3][0]),3*sizeof(double)); memcpy(b1,&(b.b[4][0]),3*sizeof(double)); } else { memcpy(b0,&(b.b[5][0]),3*sizeof(double)); memcpy(b1,&(b.b[6][0]),3*sizeof(double)); } s = 0.5; /* At this point, the two control points are expressed in the rotated frame */ c[0] = 3.0*s*(1.0-s)*(1.0-s)*b0[0] + 3.0*s*s*(1.0-s)*b1[0] + s*s*s*lispoi[3*k+1]; c[1] = 3.0*s*(1.0-s)*(1.0-s)*b0[1] + 3.0*s*s*(1.0-s)*b1[1] + s*s*s*lispoi[3*k+2]; c[2] = 3.0*s*(1.0-s)*(1.0-s)*b0[2] + 3.0*s*s*(1.0-s)*b1[2] + s*s*s*lispoi[3*k+3]; /* Fill matric tAA and second member tAb*/ tAA[0] += c[0]*c[0]*c[0]*c[0]; tAA[1] += c[0]*c[0]*c[1]*c[1]; tAA[2] += c[0]*c[0]*c[0]*c[1]; tAA[3] += c[1]*c[1]*c[1]*c[1]; tAA[4] += c[0]*c[1]*c[1]*c[1]; tAA[5] += c[0]*c[0]*c[1]*c[1]; tAb[0] += c[0]*c[0]*c[2]; tAb[1] += c[1]*c[1]*c[2]; tAb[2] += c[0]*c[1]*c[2]; s = 1.0; /* At this point, the two control points are expressed in the rotated frame */ c[0] = 3.0*s*(1.0-s)*(1.0-s)*b0[0] + 3.0*s*s*(1.0-s)*b1[0] + s*s*s*lispoi[3*k+1]; c[1] = 3.0*s*(1.0-s)*(1.0-s)*b0[1] + 3.0*s*s*(1.0-s)*b1[1] + s*s*s*lispoi[3*k+2]; c[2] = 3.0*s*(1.0-s)*(1.0-s)*b0[2] + 3.0*s*s*(1.0-s)*b1[2] + s*s*s*lispoi[3*k+3]; /* Fill matric tAA and second member tAb*/ tAA[0] += c[0]*c[0]*c[0]*c[0]; tAA[1] += c[0]*c[0]*c[1]*c[1]; tAA[2] += c[0]*c[0]*c[0]*c[1]; tAA[3] += c[1]*c[1]*c[1]*c[1]; tAA[4] += c[0]*c[1]*c[1]*c[1]; tAA[5] += c[0]*c[0]*c[1]*c[1]; tAb[0] += c[0]*c[0]*c[2]; tAb[1] += c[1]*c[1]*c[2]; tAb[2] += c[0]*c[1]*c[2]; /* Mid-point along median edge and endpoint in the rotated frame */ if ( i0 == 0 ) { c[0] = A64TH*(b.b[1][0] + b.b[2][0] + 3.0*(b.b[3][0] + b.b[4][0])) \ + 3.0*A16TH*(b.b[6][0] + b.b[7][0] + b.b[9][0]) + A32TH*(b.b[5][0] + b.b[8][0]); c[1] = A64TH*(b.b[1][1] + b.b[2][1] + 3.0*(b.b[3][1] + b.b[4][1])) \ + 3.0*A16TH*(b.b[6][1] + b.b[7][1] + b.b[9][1]) + A32TH*(b.b[5][1] + b.b[8][1]); c[2] = A64TH*(b.b[1][2] + b.b[2][2] + 3.0*(b.b[3][2] + b.b[4][2])) \ + 3.0*A16TH*(b.b[6][2] + b.b[7][2] + b.b[9][2]) + A32TH*(b.b[5][2] + b.b[8][2]); d[0] = 0.125*b.b[1][0] + 0.375*(b.b[3][0] + b.b[4][0]) + 0.125*b.b[2][0]; d[1] = 0.125*b.b[1][1] + 0.375*(b.b[3][1] + b.b[4][1]) + 0.125*b.b[2][1]; d[2] = 0.125*b.b[1][2] + 0.375*(b.b[3][2] + b.b[4][2]) + 0.125*b.b[2][2]; } else if (i0 == 1) { c[0] = A64TH*(b.b[0][0] + b.b[2][0] + 3.0*(b.b[5][0] + b.b[6][0])) \ + 3.0*A16TH*(b.b[3][0] + b.b[8][0] + b.b[9][0]) + A32TH*(b.b[4][0] + b.b[7][0]); c[1] = A64TH*(b.b[0][1] + b.b[2][1] + 3.0*(b.b[5][1] + b.b[6][1])) \ + 3.0*A16TH*(b.b[3][1] + b.b[8][1] + b.b[9][1]) + A32TH*(b.b[4][1] + b.b[7][1]); c[2] = A64TH*(b.b[0][2] + b.b[2][2] + 3.0*(b.b[5][2] + b.b[6][2])) \ + 3.0*A16TH*(b.b[3][2] + b.b[8][2] + b.b[9][2]) + A32TH*(b.b[4][2] + b.b[7][2]); d[0] = 0.125*b.b[2][0] + 0.375*(b.b[5][0] + b.b[6][0]) + 0.125*b.b[0][0]; d[1] = 0.125*b.b[2][1] + 0.375*(b.b[5][1] + b.b[6][1]) + 0.125*b.b[0][1]; d[2] = 0.125*b.b[2][2] + 0.375*(b.b[5][2] + b.b[6][2]) + 0.125*b.b[0][2]; } else { c[0] = A64TH*(b.b[0][0] + b.b[1][0] + 3.0*(b.b[7][0] + b.b[8][0])) \ + 3.0*A16TH*(b.b[4][0] + b.b[5][0] + b.b[9][0]) + A32TH*(b.b[3][0] + b.b[6][0]); c[1] = A64TH*(b.b[0][1] + b.b[1][1] + 3.0*(b.b[7][1] + b.b[8][1])) \ + 3.0*A16TH*(b.b[4][1] + b.b[5][1] + b.b[9][1]) + A32TH*(b.b[3][1] + b.b[6][1]); c[2] = A64TH*(b.b[0][2] + b.b[1][2] + 3.0*(b.b[7][2] + b.b[8][2])) \ + 3.0*A16TH*(b.b[4][2] + b.b[5][2] + b.b[9][2]) + A32TH*(b.b[3][2] + b.b[6][2]); d[0] = 0.125*b.b[0][0] + 0.375*(b.b[7][0] + b.b[8][0]) + 0.125*b.b[1][0]; d[1] = 0.125*b.b[0][1] + 0.375*(b.b[7][1] + b.b[8][1]) + 0.125*b.b[1][1]; d[2] = 0.125*b.b[0][2] + 0.375*(b.b[7][2] + b.b[8][2]) + 0.125*b.b[1][2]; } /* Fill matric tAA and second member tAb*/ tAA[0] += c[0]*c[0]*c[0]*c[0]; tAA[1] += c[0]*c[0]*c[1]*c[1]; tAA[2] += c[0]*c[0]*c[0]*c[1]; tAA[3] += c[1]*c[1]*c[1]*c[1]; tAA[4] += c[0]*c[1]*c[1]*c[1]; tAA[5] += c[0]*c[0]*c[1]*c[1]; tAb[0] += c[0]*c[0]*c[2]; tAb[1] += c[1]*c[1]*c[2]; tAb[2] += c[0]*c[1]*c[2]; tAA[0] += d[0]*d[0]*d[0]*d[0]; tAA[1] += d[0]*d[0]*d[1]*d[1]; tAA[2] += d[0]*d[0]*d[0]*d[1]; tAA[3] += d[1]*d[1]*d[1]*d[1]; tAA[4] += d[0]*d[1]*d[1]*d[1]; tAA[5] += d[0]*d[0]*d[1]*d[1]; tAb[0] += d[0]*d[0]*d[2]; tAb[1] += d[1]*d[1]*d[2]; tAb[2] += d[0]*d[1]*d[2]; } /* solve now (a b c) = tAA^{-1} * tAb */ if ( !_MMG5_sys33sym(tAA,tAb,c) ) return(hmax); intm[0] = 2.0*c[0]; intm[1] = c[2]; intm[2] = 2.0*c[1]; /* At this point, intm stands for the integral matrix of Taubin's approach : vp[0] and vp[1] are the two pr. directions of curvature, and the two curvatures can be inferred from lambdas*/ if( !_MMG5_eigensym(intm,kappa,vp) ){ fprintf(stdout,"%s:%d: Error: function _MMG5_eigensym return 0\n", __FILE__,__LINE__); exit(EXIT_FAILURE); } /* h computation : h(x) = sqrt( 9*hausd / (2 * max(kappa1(x),kappa2(x)) ) */ kappa[0] = 2.0/9.0 * fabs(kappa[0]) / hausd; kappa[0] = MG_MIN(kappa[0],isqhmin); kappa[0] = MG_MAX(kappa[0],isqhmax); kappa[1] = 2.0/9.0 * fabs(kappa[1]) / hausd; kappa[1] = MG_MIN(kappa[1],isqhmin); kappa[1] = MG_MAX(kappa[1],isqhmax); kappa[0] = 1.0 / sqrt(kappa[0]); kappa[1] = 1.0 / sqrt(kappa[1]); h = MG_MIN(kappa[0],kappa[1]); /* Travel surfacic ball one last time and update non manifold point metric */ for (k=0; k<ilists; k++) { iel = lists[k] / 4; iface = lists[k] % 4; for (j=0; j<3; j++) { i0 = _MMG5_idir[iface][j]; ip0 = pt->v[i0]; p1 = &mesh->point[ip0]; if( !(p1->tag & MG_NOM) || MG_SIN(p1->tag) ) continue; assert(p1->xp); t = &p1->n[0]; memcpy(c,t,3*sizeof(double)); d[0] = r[0][0]*c[0] + r[0][1]*c[1] + r[0][2]*c[2]; d[1] = r[1][0]*c[0] + r[1][1]*c[1] + r[1][2]*c[2]; hnm = intm[0]*d[0]*d[0] + 2.0*intm[1]*d[0]*d[1] + intm[2]*d[1]*d[1]; hnm = 2.0/9.0 * fabs(hnm) / hausd; hnm = MG_MIN(hnm,isqhmin); hnm = MG_MAX(hnm,isqhmax); hnm = 1.0 / sqrt(hnm); met->m[ip0] = MG_MIN(met->m[ip0],hnm); } } return(h); }
/* collapse edge i of k, i1->i2 */ int litcol(MMG5_pMesh mesh,int k,char i,double kali) { MMG5_pTria pt,pt0,pt1; MMG5_pPoint p1,p2; double kal,ps,cosnold,cosnnew,n0old[3],n0new[3],n1old[3],n1new[3],n00old[3],n00new[3]; int *adja,list[_MMG5_LMAX+2],jel,ip2,l,ilist; char i1,i2,j,jj,j2,open; pt0 = &mesh->tria[0]; pt = &mesh->tria[k]; i1 = _MMG5_inxt2[i]; i2 = _MMG5_iprv2[i]; ip2 = pt->v[i2]; /* collect all triangles around vertex i1 */ ilist = boulet(mesh,k,i1,list); /* check for open ball */ adja = &mesh->adja[3*(k-1)+1]; open = adja[i] == 0; if ( ilist > 3 ) { /* check references */ jel = list[1] / 3; pt1 = &mesh->tria[jel]; if ( abs(pt->ref) != abs(pt1->ref) ) return(0); /* analyze ball */ for (l=1; l<ilist-1+open; l++) { jel = list[l] / 3; j = list[l] % 3; jj = _MMG5_inxt2[j]; j2 = _MMG5_iprv2[j]; pt1 = &mesh->tria[jel]; /* check normal flipping */ if ( !_MMG5_nortri(mesh,pt1,n1old) ) return(0); memcpy(pt0,pt1,sizeof(MMG5_Tria)); pt0->v[j] = ip2; if ( !_MMG5_nortri(mesh,pt0,n1new) ) return(0); ps = n1new[0]*n1old[0] + n1new[1]*n1old[1] + n1new[2]*n1old[2]; if ( ps < 0.0 ) return(0); /* keep normals at 1st triangles */ if ( l == 1 && !open ) { memcpy(n00old,n1old,3*sizeof(double)); memcpy(n00new,n1new,3*sizeof(double)); } /* check normals deviation */ if ( !(pt1->tag[j2] & MG_GEO) ) { if ( l > 1 ) { cosnold = n0old[0]*n1old[0] + n0old[1]*n1old[1] + n0old[2]*n1old[2]; cosnnew = n0new[0]*n1new[0] + n0new[1]*n1new[1] + n0new[2]*n1new[2]; if ( cosnold < _MMG5_ANGEDG ) { if ( cosnnew < MG_MIN(0.0,cosnold) ) return(0); } else if ( cosnnew < _MMG5_ANGEDG ) return(0); } memcpy(n0old,n1old,3*sizeof(double)); memcpy(n0new,n1new,3*sizeof(double)); } /* check quality */ kal = ALPHAD*_MMG5_caltri_iso(mesh,NULL,pt0); if ( kal < NULKAL ) return(0); } /* check angle between 1st and last triangles */ if ( !open ) { cosnold = n00old[0]*n1old[0] + n00old[1]*n1old[1] + n00old[2]*n1old[2]; cosnnew = n00new[0]*n1new[0] + n00new[1]*n1new[1] + n00new[2]*n1new[2]; if ( cosnold < _MMG5_ANGEDG ) { if ( cosnnew < MG_MIN(0.0,cosnold) ) return(0); } else if ( cosnnew < _MMG5_ANGEDG ) return(0); /* other reference checks */ jel = list[ilist-1] / 3; pt = &mesh->tria[jel]; jel = list[ilist-2] / 3; pt1 = &mesh->tria[jel]; if ( abs(pt->ref) != abs(pt1->ref) ) return(0); } return(colver(mesh,list,ilist)); } /* specific test: no collapse if any interior edge is EDG */ else if ( ilist == 3 ) { p1 = &mesh->point[pt->v[i1]]; if ( MS_SIN(p1->tag) ) return(0); else if ( MG_EDG(pt->tag[i2]) && !MG_EDG(pt->tag[i]) ) return(0); else if ( !MG_EDG(pt->tag[i2]) && MG_EDG(pt->tag[i]) ) return(0); else if ( MG_EDG(pt->tag[i2]) && MG_EDG(pt->tag[i]) && MG_EDG(pt->tag[i1]) ) return(0); return(colver3(mesh,list)); } /* for specific configurations along open ridge */ else if ( ilist == 2 ) { if ( !open ) return(0); jel = list[1] / 3; j = list[1] % 3; jj = _MMG5_inxt2[j]; pt1 = &mesh->tria[jel]; if ( abs(pt->ref) != abs(pt1->ref) ) return(0); else if ( !(pt1->tag[jj] & MG_GEO) ) return(0); p1 = &mesh->point[pt->v[i1]]; p2 = &mesh->point[pt1->v[jj]]; if ( p2->tag > p1->tag || p2->ref != p1->ref ) return(0); return(colver2(mesh,list)); } return(0); }
/* check if geometry preserved by collapsing edge i */ int chkcol(MMG5_pMesh mesh,MMG5_pSol met,int k,char i,int *list,char typchk) { MMG5_pTria pt,pt0,pt1,pt2; MMG5_pPoint p1,p2; double len,lon,ps,cosnold,cosnnew,kal,n0old[3],n1old[3],n00old[3]; double n0new[3],n1new[3],n00new[3]; int *adja,jel,kel,ip1,ip2,l,ll,ilist; char i1,i2,j,jj,j2,lj,open,voy; pt0 = &mesh->tria[0]; pt = &mesh->tria[k]; i1 = _MMG5_inxt2[i]; i2 = _MMG5_iprv2[i]; ip1 = pt->v[i1]; ip2 = pt->v[i2]; if ( typchk == 2 && met->m ) { lon = _MMG5_lenedg(mesh,met,ip1,ip2,0); lon = MG_MIN(lon,LSHRT); lon = MG_MAX(1.0/lon,LLONG); } /* collect all triangles around vertex i1 */ ilist = boulechknm(mesh,k,i1,list); if ( ilist <= 0 ) return(0); /* check for open ball */ adja = &mesh->adja[3*(k-1)+1]; open = adja[i] == 0; if ( ilist > 3 ) { /* check references */ if ( MG_EDG(pt->tag[i2]) ) { jel = list[1] / 3; pt1 = &mesh->tria[jel]; if ( abs(pt->ref) != abs(pt1->ref) ) return(0); } /* analyze ball */ for (l=1; l<ilist-1+open; l++) { jel = list[l] / 3; j = list[l] % 3; jj = _MMG5_inxt2[j]; j2 = _MMG5_iprv2[j]; pt1 = &mesh->tria[jel]; /* check length */ if ( typchk == 2 && met->m && !MG_EDG(mesh->point[ip2].tag) ) { ip1 = pt1->v[j2]; len = _MMG5_lenedg(mesh,met,ip1,ip2,0); if ( len > lon ) return(0); } /* check normal flipping */ if ( !_MMG5_nortri(mesh,pt1,n1old) ) return(0); memcpy(pt0,pt1,sizeof(MMG5_Tria)); pt0->v[j] = ip2; if ( !_MMG5_nortri(mesh,pt0,n1new) ) return(0); ps = n1new[0]*n1old[0] + n1new[1]*n1old[1] + n1new[2]*n1old[2]; if ( ps < 0.0 ) return(0); /* keep normals at 1st triangles */ if ( l == 1 && !open ) { memcpy(n00old,n1old,3*sizeof(double)); memcpy(n00new,n1new,3*sizeof(double)); } /* check normals deviation */ if ( !(pt1->tag[j2] & MG_GEO) ) { if ( l > 1 ) { cosnold = n0old[0]*n1old[0] + n0old[1]*n1old[1] + n0old[2]*n1old[2]; cosnnew = n0new[0]*n1new[0] + n0new[1]*n1new[1] + n0new[2]*n1new[2]; if ( cosnold < _MMG5_ANGEDG ) { if ( cosnnew < cosnold ) return(0); } else if ( cosnnew < _MMG5_ANGEDG ) return(0); } } /* check geometric support */ if ( l == 1 ) { pt0->tag[j2] = MG_MAX(pt0->tag[j2],pt->tag[i1]); } else if ( l == ilist-2+open ) { ll = list[ilist-1+open] / 3; if ( ll > mesh->nt ) return(0); lj = list[ilist-1+open] % 3; pt0->tag[jj] = MG_MAX(pt0->tag[jj],mesh->tria[ll].tag[lj]); } if ( chkedg(mesh,0) ) return(0); /* check quality */ if ( typchk == 2 && met->m ) kal = ALPHAD*_MMG5_calelt(mesh,met,pt0); else kal = ALPHAD*_MMG5_caltri_iso(mesh,NULL,pt0); if ( kal < NULKAL ) return(0); memcpy(n0old,n1old,3*sizeof(double)); memcpy(n0new,n1new,3*sizeof(double)); } /* check angle between 1st and last triangles */ if ( !open && !(pt->tag[i] & MG_GEO) ) { cosnold = n00old[0]*n1old[0] + n00old[1]*n1old[1] + n00old[2]*n1old[2]; cosnnew = n00new[0]*n1new[0] + n00new[1]*n1new[1] + n00new[2]*n1new[2]; if ( cosnold < _MMG5_ANGEDG ) { if ( cosnnew < cosnold ) return(0); } else if ( cosnnew < _MMG5_ANGEDG ) return(0); /* other checks for reference collapse */ jel = list[ilist-1] / 3; j = list[ilist-1] % 3; j = _MMG5_iprv2[j]; pt = &mesh->tria[jel]; if ( MG_EDG(pt->tag[j]) ) { jel = list[ilist-2] / 3; pt1 = &mesh->tria[jel]; if ( abs(pt->ref) != abs(pt1->ref) ) return(0); } } } /* specific test: no collapse if any interior edge is EDG */ else if ( ilist == 3 ) { p1 = &mesh->point[pt->v[i1]]; if ( MS_SIN(p1->tag) ) return(0); else if ( MG_EDG(pt->tag[i2]) && !MG_EDG(pt->tag[i]) ) return(0); else if ( !MG_EDG(pt->tag[i2]) && MG_EDG(pt->tag[i]) ) return(0); else if ( MG_EDG(pt->tag[i2]) && MG_EDG(pt->tag[i]) && MG_EDG(pt->tag[i1]) ) return(0); /* Check geometric approximation */ jel = list[1] / 3; j = list[1] % 3; jj = _MMG5_inxt2[j]; j2 = _MMG5_iprv2[j]; pt0 = &mesh->tria[0]; pt1 = &mesh->tria[jel]; memcpy(pt0,pt1,sizeof(MMG5_Tria)); pt0->v[j] = ip2; jel = list[2] / 3; j = list[2] % 3; pt1 = &mesh->tria[jel]; pt0->tag[jj] |= pt1->tag[j]; pt0->tag[j2] |= pt1->tag[_MMG5_inxt2[j]]; if ( chkedg(mesh,0) ) return(0); } /* for specific configurations along open ridge */ else if ( ilist == 2 ) { if ( !open ) return(0); jel = list[1] / 3; j = list[1] % 3; /* Topological test */ adja = &mesh->adja[3*(jel-1)+1]; kel = adja[j] / 3; voy = adja[j] % 3; pt2 = &mesh->tria[kel]; if ( pt2->v[voy] == ip2) return(0); jj = _MMG5_inxt2[j]; pt1 = &mesh->tria[jel]; if ( abs(pt->ref) != abs(pt1->ref) ) return(0); else if ( !(pt1->tag[jj] & MG_GEO) ) return(0); p1 = &mesh->point[pt->v[i1]]; p2 = &mesh->point[pt1->v[jj]]; if ( p2->tag > p1->tag || p2->ref != p1->ref ) return(0); /* Check geometric approximation */ j2 = _MMG5_iprv2[j]; pt0 = &mesh->tria[0]; memcpy(pt0,pt,sizeof(MMG5_Tria)); pt0->v[i1] = pt1->v[j2]; if ( chkedg(mesh,0) ) return(0); } return(ilist); }
/** * \param mesh pointer toward the mesh structure * \param start tetrahedra in which the swap should be performed * \param ia edge that we want to swap * \param ilist pointer to store the size of the shell of the edge * \param list pointer to store the shell of the edge * \param crit improvment coefficient * \return 0 if fail, the index of point corresponding to the swapped * configuration otherwise (\f$4*k+i\f$). * * Check whether swap of edge \a ia in \a start should be performed, and * return \f$4*k+i\f$ the index of point corresponding to the swapped * configuration. The shell of edge is built during the process. * */ int _MMG5_chkswpgen(MMG5_pMesh mesh,int start,int ia,int *ilist,int *list,double crit) { MMG5_pTetra pt,pt0; MMG5_pPoint p0; double calold,calnew,caltmp; int na,nb,np,adj,piv,npol,refdom,k,l,iel; int *adja,pol[_MMG5_LMAX+2]; char i,ipa,ipb,ip,ier; pt = &mesh->tetra[start]; refdom = pt->ref; pt0 = &mesh->tetra[0]; na = pt->v[_MMG5_iare[ia][0]]; nb = pt->v[_MMG5_iare[ia][1]]; calold = pt->qual; /* Store shell of ia in list, and associated pseudo polygon in pol */ (*ilist) = 0; npol = 0; list[(*ilist)] = 6*start+ia; (*ilist)++; adja = &mesh->adja[4*(start-1)+1]; adj = adja[_MMG5_ifar[ia][0]] / 4; // start travelling by face (ia,0) piv = pt->v[_MMG5_ifar[ia][1]]; pol[npol] = 4*start + _MMG5_ifar[ia][1]; npol++; while ( adj && adj != start ) { pt = &mesh->tetra[adj]; if ( pt->tag & MG_REQ ) return(0); /* Edge is on a boundary between two different domains */ if ( pt->ref != refdom ) return(0); calold = MG_MIN(calold, pt->qual); /* identification of edge number in tetra adj */ for (i=0; i<6; i++) { ipa = _MMG5_iare[i][0]; ipb = _MMG5_iare[i][1]; if ( (pt->v[ipa] == na && pt->v[ipb] == nb) || (pt->v[ipa] == nb && pt->v[ipb] == na)) break; } assert(i<6); list[(*ilist)] = 6*adj +i; (*ilist)++; /* overflow */ if ( (*ilist) > _MMG5_LMAX-3 ) return(0); /* set new triangle for travel */ adja = &mesh->adja[4*(adj-1)+1]; if ( pt->v[ _MMG5_ifar[i][0] ] == piv ) { pol[npol] = 4*adj + _MMG5_ifar[i][1]; npol++; adj = adja[ _MMG5_ifar[i][0] ] / 4; piv = pt->v[ _MMG5_ifar[i][1] ]; } else { assert(pt->v[ _MMG5_ifar[i][1] ] == piv); pol[npol] = 4*adj + _MMG5_ifar[i][0]; npol++; adj = adja[ _MMG5_ifar[i][1] ] /4; piv = pt->v[ _MMG5_ifar[i][0] ]; } } //CECILE : je vois pas pourquoi ca ameliore de faire ce test //plus rapide mais du coup on elimine des swap... //4/01/14 commentaire //if ( calold*_MMG5_ALPHAD > 0.5 ) return(0); /* Prevent swap of an external boundary edge */ if ( !adj ) return(0); assert(npol == (*ilist)); // du coup, apres on pourra virer npol /* Find a configuration that enhances the worst quality within the shell */ for (k=0; k<npol; k++) { iel = pol[k] / 4; ip = pol[k] % 4; np = mesh->tetra[iel].v[ip]; calnew = 1.0; ier = 1; if ( mesh->info.fem ) { p0 = &mesh->point[np]; if ( p0->tag & MG_BDY ) { for (l=0; l<npol;l++) { if ( k < npol-1 ) { if ( l == k || l == k+1 ) continue; } else { if ( l == npol-1 || l == 0 ) continue; } iel = pol[l] / 4; ip = pol[l] % 4; pt = &mesh->tetra[iel]; p0 = &mesh->point[pt->v[ip]]; if ( p0->tag & MG_BDY ) { ier = 0; break; } } } if ( !ier ) continue; ier = 1; } for (l=0; l<(*ilist); l++) { /* Do not consider tets of the shell of collapsed edge */ if ( k < npol-1 ) { if ( l == k || l == k+1 ) continue; } else { if ( l == npol-1 || l == 0 ) continue; } iel = list[l] / 6; i = list[l] % 6; pt = &mesh->tetra[iel]; /* First tetra obtained from iel */ memcpy(pt0,pt,sizeof(MMG5_Tetra)); pt0->v[_MMG5_iare[i][0]] = np; caltmp = _MMG5_orcal(mesh,0); calnew = MG_MIN(calnew,caltmp); /* Second tetra obtained from iel */ memcpy(pt0,pt,sizeof(MMG5_Tetra)); pt0->v[_MMG5_iare[i][1]] = np; caltmp = _MMG5_orcal(mesh,0); calnew = MG_MIN(calnew,caltmp); ier = (calnew > crit*calold); if ( !ier ) break; } if ( ier ) return(pol[k]); } return(0); }