/** * \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); }
/** * \param mesh pointer toward the mesh structure. * \param met pointer toward the meric structure. * \param ptt pointer toward the triangle structure. * \return The computed area. * * Compute the area of the surface triangle \a ptt with respect to * the anisotropic metric \a met. * */ double _MMG5_surftri_ani(MMG5_pMesh mesh,MMG5_pSol met,MMG5_pTria ptt) { MMG5_pPoint p[3]; _MMG5_Bezier b; int np[3]; double surf,ux,uy,uz,dens,m[3][6],J[3][2],mJ[3][2],tJmJ[2][2]; char i,i1,i2; surf = 0.0; for (i=0; i<3; i++) { np[i] = ptt->v[i]; p[i] = &mesh->point[np[i]]; } if ( !_MMG5_bezierCP(mesh,ptt,&b,1) ) return(0.0); /* Set metric tensors at vertices of tria iel */ for(i=0; i<3; i++) { i1 = _MMG5_inxt2[i]; i2 = _MMG5_iprv2[i]; ux = 0.5*(p[i1]->c[0]+p[i2]->c[0]) - p[i]->c[0]; uy = 0.5*(p[i1]->c[1]+p[i2]->c[1]) - p[i]->c[1]; uz = 0.5*(p[i1]->c[2]+p[i2]->c[2]) - p[i]->c[2]; if ( MS_SIN(p[i]->tag) ) { memcpy(&m[i][0],&met->m[6*np[i]+1],6*sizeof(double)); } else if ( p[i]->tag & MG_GEO ) { if ( !_MMG5_buildridmet(mesh,met,np[i],ux,uy,uz,&m[i][0]) ) return(0.0); } else { memcpy(&m[i][0],&met->m[6*np[i]+1],6*sizeof(double)); } } /* Compute density integrand of volume at the 3 vertices of T */ for (i=0; i<3; i++) { if ( i == 0 ) { J[0][0] = 3.0*( b.b[7][0] - b.b[0][0] ) ; J[0][1] = 3.0*( b.b[6][0] - b.b[0][0] ); J[1][0] = 3.0*( b.b[7][1] - b.b[0][1] ) ; J[1][1] = 3.0*( b.b[6][1] - b.b[0][1] ); J[2][0] = 3.0*( b.b[7][2] - b.b[0][2] ) ; J[2][1] = 3.0*( b.b[6][2] - b.b[0][2] ); } else if ( i == 1 ) { J[0][0] = 3.0*( b.b[1][0] - b.b[8][0] ) ; J[0][1] = 3.0*( b.b[3][0] - b.b[8][0] ); J[1][0] = 3.0*( b.b[1][1] - b.b[8][1] ) ; J[1][1] = 3.0*( b.b[3][1] - b.b[8][1] ); J[2][0] = 3.0*( b.b[1][2] - b.b[8][2] ) ; J[2][1] = 3.0*( b.b[3][2] - b.b[8][2] ); } else { J[0][0] = 3.0*( b.b[4][0] - b.b[5][0] ) ; J[0][1] = 3.0*( b.b[2][0] - b.b[5][0] ); J[1][0] = 3.0*( b.b[4][1] - b.b[5][1] ) ; J[1][1] = 3.0*( b.b[2][1] - b.b[5][1] ); J[2][0] = 3.0*( b.b[4][2] - b.b[5][2] ) ; J[2][1] = 3.0*( b.b[2][2] - b.b[5][2] ); } mJ[0][0] = m[i][0]*J[0][0] + m[i][1]*J[1][0] + m[i][2]*J[2][0]; mJ[1][0] = m[i][1]*J[0][0] + m[i][3]*J[1][0] + m[i][4]*J[2][0]; mJ[2][0] = m[i][2]*J[0][0] + m[i][4]*J[1][0] + m[i][5]*J[2][0]; mJ[0][1] = m[i][0]*J[0][1] + m[i][1]*J[1][1] + m[i][2]*J[2][1]; mJ[1][1] = m[i][1]*J[0][1] + m[i][3]*J[1][1] + m[i][4]*J[2][1]; mJ[2][1] = m[i][2]*J[0][1] + m[i][4]*J[1][1] + m[i][5]*J[2][1]; /* dens = sqrt(tJacsigma * M * Jacsigma )*/ tJmJ[0][0] = J[0][0]*mJ[0][0] + J[1][0]*mJ[1][0] + J[2][0]*mJ[2][0]; tJmJ[0][1] = J[0][0]*mJ[0][1] + J[1][0]*mJ[1][1] + J[2][0]*mJ[2][1]; tJmJ[1][0] = J[0][1]*mJ[0][0] + J[1][1]*mJ[1][0] + J[2][1]*mJ[2][0]; tJmJ[1][1] = J[0][1]*mJ[0][1] + J[1][1]*mJ[1][1] + J[2][1]*mJ[2][1]; dens = tJmJ[0][0]*tJmJ[1][1] - tJmJ[1][0]*tJmJ[0][1]; if ( dens < 0.0 ) { //fprintf(stdout," ## Density should be positive : %E for elt %d %d %d \n",dens,ptt->v[0],ptt->v[1],ptt->v[2]); //return(0.0); } surf += sqrt(fabs(dens)); } surf *= _MMG5_ATHIRD; return(surf); }