Mmg
Simplicial remeshers (mesh adaptation, isovalue discretization, lagrangian movement)
colver_3d.c
Go to the documentation of this file.
1/* =============================================================================
2** This file is part of the mmg software package for the tetrahedral
3** mesh modification.
4** Copyright (c) Bx INP/CNRS/Inria/UBordeaux/UPMC, 2004-
5**
6** mmg is free software: you can redistribute it and/or modify it
7** under the terms of the GNU Lesser General Public License as published
8** by the Free Software Foundation, either version 3 of the License, or
9** (at your option) any later version.
10**
11** mmg is distributed in the hope that it will be useful, but WITHOUT
12** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13** FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14** License for more details.
15**
16** You should have received a copy of the GNU Lesser General Public
17** License and of the GNU General Public License along with mmg (in
18** files COPYING.LESSER and COPYING). If not, see
19** <http://www.gnu.org/licenses/>. Please read their terms carefully and
20** use this copy of the mmg distribution only if you accept them.
21** =============================================================================
22*/
23
38
39extern int8_t ddb;
40
43int MMG5_chkcol_int(MMG5_pMesh mesh,MMG5_pSol met,MMG5_int k,int8_t iface,
44 int8_t iedg,int64_t *list,int ilist,int8_t typchk) {
45 MMG5_pTetra pt,pt0;
46 MMG5_pPoint p0;
47 double calold,calnew,caltmp,ll,lon;
48 MMG5_int j,iel,nq,nr;
49 int8_t i,jj,ip,iq;
50
51 iq = MMG5_idir[iface][MMG5_iprv2[iedg]];
52 pt = &mesh->tetra[k];
53 pt0 = &mesh->tetra[0];
54 nq = pt->v[iq];
55
56 lon = 1.6;
57 if ( typchk == 2 && met->m ) {
58 lon = MMG5_lenedg(mesh,met,MMG5_iarf[iface][iedg],pt);
59
60 if ( !lon ) return 0;
61 /*on cherche a se rapprocher de 1*/
62 //lon = MG_MAX(0.8/lon,1.6);// test ok but less good than the next one
63 lon = MG_MAX(2.-lon,1.6);
64 }
65
66 calold = calnew = DBL_MAX;
67 for (j=0; j<ilist; j++) {
68 iel = list[j] / 4;
69 ip = list[j] % 4;
70 pt = &mesh->tetra[iel];
71
72 /* exclude elements from shell */
73 for (jj=0; jj<4; jj++) if ( pt->v[jj] == nq ) break;
74 if ( jj < 4 ) continue;
75 memcpy(pt0,pt,sizeof(MMG5_Tetra));
76 /* Update edges tag for pt0 (needed by lenedg). */
77
78 /* prevent from recreating internal edge between boundaries */
79 if ( mesh->info.fem==typchk ) {
80 p0 = &mesh->point[nq];
81 if ( (p0->tag & MG_BDY) && !(p0->tag & MG_PARBDY) ) {
82 i = ip;
83 for (jj=0; jj<3; jj++) {
84 i = MMG5_inxt3[i];
85 p0 = &mesh->point[pt->v[i]];
86 if ( (p0->tag & MG_BDY) && !(p0->tag & MG_PARBDY) ){
87 return 0;
88 }
89 }
90
91 /* Prevent from creating a tetra with 4 bdy vertices */
92 // Algiane (2022) this test is useless I think (because we forbid the
93 // creation of internal edges between boundary points)
94#ifndef NDEBUG
95 i = ip;
96 nr = 0;
97 for (jj=0; jj<3; jj++) {
98 i = MMG5_inxt3[i];
99 p0 = &mesh->point[pt->v[i]];
100 if ( (p0->tag & MG_BDY) && !(p0->tag & MG_PARBDY) ) ++nr;
101 }
102 if ( nr==3 ) {
103 assert ( 0 && "Uncomment this test, it is not useless");
104 return 0;
105 }
106#endif
107 }
108 }
109 else {
110 /* In aniso : prevent from creating a tetra with 4 ridges vertices or
111 * internal edges between two ridges (unable to split it after because of
112 * the ridge metric) */
113 if ( met->size==6 ) {
114 p0 = &mesh->point[nq];
115
116 if ( (p0->tag & MG_GEO) && !MG_SIN_OR_NOM(p0->tag) ) {
117 i = ip;
118 for (jj=0; jj<3; jj++) {
119 i = MMG5_inxt3[i];
120 p0 = &mesh->point[pt->v[i]];
121 if ( p0->tag & MG_GEO && !MG_SIN_OR_NOM(p0->tag) ) {
122 return 0;
123 }
124 }
125
126 // Algiane (2022) this test is useless because we forbid the creation of
127 // internal edges between boundary points but we can keep it in case we
128 // comment the previous test
129#ifndef NDEBUG
130 i = ip;
131 nr = 0;
132 for (jj=0; jj<3; jj++) {
133 i = MMG5_inxt3[i];
134 p0 = &mesh->point[pt->v[i]];
135 if ( p0->tag & MG_GEO && !MG_SIN_OR_NOM(p0->tag) ) {
136 ++nr;
137 }
138 }
139 if ( nr==3 ) {
140 assert ( 0 && "Uncomment this test, it is not useless");
141 return 0;
142 }
143#endif
144 }
145 }
146 }
147
148 pt0->v[ip] = nq;
149
150 calold = MG_MIN(calold,pt->qual);
151 if ( typchk==1 && met->m && met->size > 1 )
152 caltmp = MMG5_caltet33_ani(mesh,met,pt0);
153 else
154 caltmp = MMG5_orcal(mesh,met,0);
155
156 if ( caltmp < MMG5_NULKAL ) return 0;
157 calnew = MG_MIN(calnew,caltmp);
158 /* check length */
159 if ( typchk == 2 && met->m ) {
160 for (jj=0; jj<6; jj++) {
161 /* Rough evaluation of edge length (doesn't take into account if some of
162 * the modified edges of pt0 are boundaries): for a more precise
163 * computation, we need to update the edge tags of pt0. */
164
165 // Algiane 06/24: to check and fix: If the edge is marked as MG_BDY
166 // (keeping in mind that this tag is not always consistent), the curve
167 // length is computed, I don't know if it is wanted.
168 ll = MMG5_lenedgspl(mesh,met,jj,pt0);
169 if ( (!ll) || (ll > lon) )//LOPTL too small, we need to put greater than 1.41
170 return 0;
171 }
172 }
173 }
174 if ( calold < MMG5_EPSOK && calnew <= calold ) return 0;
175 else if ( calnew < MMG5_EPSOK || calnew < 0.3*calold ) return 0;
176
177 return ilist;
178}
179
196static inline
197MMG5_int MMG3D_unfold_shell(MMG5_pMesh mesh,MMG5_int start,MMG5_int end, MMG5_int na, MMG5_int nb,MMG5_int piv,
198 MMG5_int *iel,int8_t *iopp) {
199 MMG5_pTetra pt;
200 MMG5_int adj,*adja;
201 int8_t i,ipiv,isface;
202
203 adj = start;
204 do {
205 *iel = adj;
206 pt = &mesh->tetra[*iel];
207 adja = &mesh->adja[4*(*iel-1)+1];
208
209 /* Identification of edge number in tetra iel */
210 if ( !MMG3D_findEdge(mesh,pt,*iel,na,nb,1,NULL,&i) ) return -1;
211
212 /* set sense of travel */
213 if ( pt->v[ MMG5_ifar[i][0] ] == piv ) {
214 adj = adja[ MMG5_ifar[i][0] ] / 4;
215 ipiv = MMG5_ifar[i][1];
216 *iopp = MMG5_ifar[i][0];
217 piv = pt->v[ipiv];
218 }
219 else {
220 adj = adja[ MMG5_ifar[i][1] ] / 4;
221 ipiv = MMG5_ifar[i][0];
222 *iopp = MMG5_ifar[i][1];
223 piv = pt->v[ipiv];
224 }
225
226 isface = 0;
227 if ( pt->xt ) {
228 isface = (MG_BDY & mesh->xtetra[pt->xt].ftag[*iopp]);
229 }
230 }
231 while ( adj && ( adj != end ) && !isface );
232
233 return piv;
234}
235
254static int
255MMG5_topchkcol_bdy(MMG5_pMesh mesh,MMG5_int k,int iface,int8_t iedg,MMG5_int *lists,
256 int ilists) {
257 MMG5_pTetra pt;
258 MMG5_pxTetra pxt;
259 double n0[3],n1[3],devnew;
260 MMG5_int nump,numq,iel,jel,jel1,nap,nbp,naq,nbq,nro;
261 int8_t ip,iq,iopp,i,j,j1,jface,jface1;
262
263 pt = &mesh->tetra[k];
264 ip = MMG5_idir[iface][MMG5_inxt2[iedg]];
265 iq = MMG5_idir[iface][MMG5_iprv2[iedg]];
266 nump = pt->v[ip];
267 numq = pt->v[iq];
268
269 /* Surface ball has been enumerated as f1,...,f2 - f1,f2 = both triangles of
270 * surface shell, point nap, facing the first vanishing face in surface ball
271 * of p */
272 nro = pt->v[MMG5_idir[iface][iedg]];
273
274 jel = lists[1] / 4;
275 jface = lists[1] % 4;
276
277 pt = &mesh->tetra[jel];
278 for (j=0; j<3; j++) {
279 i = MMG5_idir[jface][j];
280 if ( pt->v[i] != nump && pt->v[i] != nro ) break;
281 }
282 assert(j<3);
283
284 nap = pt->v[i];
285
286 /* Unfold shell of (numq,nro), starting from (jel,jface), with pivot nump */
287 naq = MMG3D_unfold_shell(mesh,k,k,numq,nro,nump,&iel,&iopp);
288
289 if ( naq < 0 ) {
290 return -1;
291 }
292 else if ( nap == naq ) {
293 return 0;
294 }
295
296 assert ( mesh->tetra[k].xt && "initial tetra is not boundary");
297 pxt = &mesh->xtetra[mesh->tetra[k].xt];
298
299 if ( !( MG_GEO_OR_NOM(pxt->tag[MMG5_iarf[iface][MMG5_inxt2[iedg]]]) ||
300 MG_GEO_OR_NOM(pxt->tag[MMG5_iarf[iface][MMG5_iprv2[iedg]]]) ) ) {
301
302 /* Check the normal deviation between the boundary faces sharing the edge
303 * numq (or nump)-nro */
304 if ( !MMG5_norpts (mesh,numq,nro,nap,n0) ) return 0;
305 if ( !MMG5_norface(mesh,iel,iopp ,n1) ) return 0;
306
307 devnew = n0[0]*n1[0] + n0[1]*n1[1] + n0[2]*n1[2];
308 if ( devnew < mesh->info.dhd ) return 0;
309 }
310
311 /* Point nbp, facing the second vanishing face in surface ball of p */
312 jel1 = lists[ilists-1] / 4;
313 jface1 = lists[ilists-1] % 4;
314 pt = &mesh->tetra[jel1];
315 for (j1=0; j1<3; j1++) {
316 i = MMG5_idir[jface1][j1];
317 if ( pt->v[i] != nump && pt->v[i] != numq ) break;
318 }
319 assert(j1<3);
320
321 nro = pt->v[i];
322 jel = lists[ilists-2] / 4;
323 jface = lists[ilists-2] % 4;
324 pt = &mesh->tetra[jel];
325 for (j=0; j<3; j++) {
326 i = MMG5_idir[jface][j];
327 if ( pt->v[i] != nump && pt->v[i] != nro ) break;
328 }
329 assert(j<3);
330
331 nbp = pt->v[i];
332
333 /* Unfold shell of (numq,nro), starting from (jel,jface), with pivot nump */
334 nbq = MMG3D_unfold_shell(mesh,lists[ilists-1]/4,k,numq,nro,nump,&iel,&iopp);
335
336 if ( nbq < 0 ) {
337 return -1;
338 }
339 else if ( nbp == nbq ) {
340 return 0;
341 }
342
343 pxt = &mesh->xtetra[mesh->tetra[jel1].xt];
344
345 if ( !( MG_GEO_OR_NOM(pxt->tag[MMG5_iarf[jface1][MMG5_iprv2[j1]]]) ||
346 MG_GEO_OR_NOM(pxt->tag[MMG5_iarf[jface1][MMG5_inxt2[j1]]]) ) ) {
347
348 /* Check the normal deviation between the boundary faces sharing the edge
349 * numq (or nump)-nro */
350 if ( !MMG5_norpts (mesh,nro,numq,nbp,n0) ) return 0;
351 if ( !MMG5_norface(mesh,iel,iopp ,n1) ) return 0;
352
353 devnew = n0[0]*n1[0] + n0[1]*n1[1] + n0[2]*n1[2];
354 if ( devnew < mesh->info.dhd ) return 0;
355 }
356
357 return 1;
358}
359
380static inline
381int MMG3D_get_shellEdgeTag_oneDir(MMG5_pMesh mesh,MMG5_int start, MMG5_int na, MMG5_int nb,
382 uint16_t *tag,MMG5_int *ref, MMG5_int piv,MMG5_int adj,
383 int8_t *filled) {
384 MMG5_pTetra pt;
385 MMG5_pxTetra pxt;
386 MMG5_int *adja;
387 int8_t i;
388
389 *filled = 0;
390 while ( adj && (adj != start) ) {
391 pt = &mesh->tetra[adj];
392
393 /* identification of edge number in tetra adj */
394 if ( !MMG3D_findEdge(mesh,pt,adj,na,nb,1,NULL,&i) ) return -1;
395
396 /* update edge ref and tag */
397 if ( pt->xt ) {
398 pxt = &mesh->xtetra[pt->xt];
399 *ref = pxt->edg[i];
400 if ( pxt->tag[i] & MG_BDY ) {
401 *tag |= pxt->tag[i];
402 *filled = 1;
403 return adj;
404 } else if ((pxt->ftag[MMG5_ifar[i][0]] & MG_BDY) || (pxt->ftag[MMG5_ifar[i][1]] & MG_BDY)) {
405 *tag |= (pxt->tag[i] | MG_BDY);
406 *filled = 1;
407 return adj;
408 }
409 }
410
411 /* set new triangle for travel */
412 adja = &mesh->adja[4*(adj-1)+1];
413 if ( pt->v[ MMG5_ifar[i][0] ] == piv ) {
414 adj = adja[ MMG5_ifar[i][0] ] / 4;
415 piv = pt->v[ MMG5_ifar[i][1] ];
416 }
417 else {
418 adj = adja[ MMG5_ifar[i][1] ] /4;
419 piv = pt->v[ MMG5_ifar[i][0] ];
420 }
421 }
422
423 return adj;
424}
425
439int MMG3D_get_shellEdgeTag(MMG5_pMesh mesh,MMG5_int start, int8_t ia,uint16_t *tag,MMG5_int *ref) {
440 MMG5_pTetra pt;
441 MMG5_pxTetra pxt;
442 MMG5_int piv,na,nb,adj,*adja;
443 int8_t filled;
444
445 pt = &mesh->tetra[start];
446
447 assert( start >= 1 && MG_EOK(pt) );
448
449 pxt = NULL;
450 na = pt->v[MMG5_iare[ia][0]];
451 nb = pt->v[MMG5_iare[ia][1]];
452
453 if ( pt->xt ) {
454 pxt = &mesh->xtetra[pt->xt];
455 *ref = pxt->edg[ia];
456 if ( pxt->tag[ia] & MG_BDY ) {
457 *tag |= pxt->tag[ia];
458 *ref = pxt->edg[ia];
459 return 1;
460 } else if ((pxt->ftag[MMG5_ifar[ia][0]] & MG_BDY) || (pxt->ftag[MMG5_ifar[ia][1]] & MG_BDY)) {
461 *tag |= (pxt->tag[ia] | MG_BDY);
462 *ref = pxt->edg[ia];
463 return 1;
464 }
465 }
466
467 /* Travel in one direction */
468 adja = &mesh->adja[4*(start-1)+1];
469 adj = adja[MMG5_ifar[ia][0]] / 4;
470 piv = pt->v[MMG5_ifar[ia][1]];
471
472 adj = MMG3D_get_shellEdgeTag_oneDir(mesh,start,na,nb,tag,ref,piv,adj,&filled);
473
474 /* If an xtetra has been found or if all shell has been travelled, stop, else,
475 * travel it the other sense */
476 if ( adj > 0 ) {
477 assert ( (adj == start) || filled );
478 return 1;
479 }
480 else if ( adj < 0 ) return 0;
481
482 assert(!adj);
483
484 pt = &mesh->tetra[start];
485 adja = &mesh->adja[4*(start-1)+1];
486 adj = adja[MMG5_ifar[ia][1]] / 4;
487 piv = pt->v[MMG5_ifar[ia][0]];
488
489 adj = MMG3D_get_shellEdgeTag_oneDir(mesh,start,na,nb,tag,ref,piv,adj,&filled);
490
491 if ( adj < 0 ) return 0;
492
493 return 1;
494}
495
527int MMG5_chkcol_bdy(MMG5_pMesh mesh,MMG5_pSol met,MMG5_int k,int8_t iface,
528 int8_t iedg,int64_t *listv,int ilistv,MMG5_int *lists,int ilists,
529 MMG5_int refmin,MMG5_int refplus, int8_t typchk,int isnm,int8_t isnmint) {
530 MMG5_pTetra pt,pt0,pt1;
531 MMG5_pxTetra pxt;
532 MMG5_Tria tt;
533 MMG5_pPar par;
534 double calold,calnew,caltmp,nadja[3],nprvold[3],nprvnew[3],ncurold[3],ncurnew[3];
535 double ps,devold,devnew,hmax,hausd;
536 MMG5_int nump,numq,ndepmin,ndepplus,l,kk,iel;
537 int nr,nbbdy,isloc,iedgeOpp,ipp;
538 uint16_t tag;
539 int8_t iopp,iopp2,ia,ip,i,iq,i0,i1,ier,isminp,isplp;
540#ifndef NDEBUG
541 MMG5_pPoint p0;
542#endif
543
544 pt = &mesh->tetra[k];
545 pxt = 0;
546 pt0 = &mesh->tetra[0];
547 ip = MMG5_idir[iface][MMG5_inxt2[iedg]];
548 nump = pt->v[ip];
549 numq = pt->v[MMG5_idir[iface][MMG5_iprv2[iedg]]];
550
551#ifndef NDEBUG
552 p0 = &mesh->point[nump];
553 assert(p0->tag & MG_BDY);
554 assert(p0->xp);
555
556 /* Remove maybe-uninitialized value warning */
557 nprvold[0] = nprvold[1] = nprvold[2] = 0.;
558 ncurold[0] = ncurold[1] = ncurold[2] = 0.;
559 nprvnew[0] = nprvnew[1] = nprvnew[2] = 0.;
560 ncurnew[0] = ncurnew[1] = ncurnew[2] = 0.;
561#endif
562
563 ndepmin = ndepplus = 0;
564 isminp = isplp = 0;
565
566 if ( !isnm ) {
567 refmin = MG_MINUS;
568 refplus = MG_PLUS;
569 }
570
571 if ( !isnmint ) {
572 /* prevent collapse in case surface ball has 3 triangles */
573 if ( ilists <= 2 ) return 0; // ATTENTION, Normalement, avec 2 c est bon !
574
575 /* Surfacic ball is enumerated with first tet having (pq) as edge n° MMG5_iprv2[ip] on face iopp */
576 MMG5_startedgsurfball(mesh,nump,numq,lists,ilists);
577 }
578
579 /* check tetra quality */
580 calold = calnew = DBL_MAX;
581 for (l=0; l<ilistv; l++) {
582 iel = listv[l] / 4;
583 ipp = listv[l] % 4;
584 assert(iel && ipp >=0 && ipp < 4 && "unexpected tetra or local vertex idx");
585
586 pt = &mesh->tetra[iel];
587
588 if ( !isnmint ) {
589 if ( pt->ref == refmin ) isminp = 1;
590 else if ( pt->ref == refplus ) isplp = 1;
591 }
592
593 /* Topological test for tetras of the shell */
594 for (iq=0; iq<4; iq++)
595 if ( pt->v[iq] == numq ) break;
596
597 if ( iq < 4 ) {
598 nbbdy = 0;
599 if ( pt->xt ) {
600 pxt = &mesh->xtetra[pt->xt];
601 }
602
603 for (i=0; i<4; i++) {
604 if ( pt->xt && (pxt->ftag[i] & MG_BDY) ) nbbdy++;
605 }
606
607 if ( nbbdy == 4 ) {
608 /* Only one element in the domain: we don't want to delete it */
609 return 0;
610 }
611 else if ( nbbdy == 3 ) {
612 /* Topological problem triggered when one of the two faces of collapsed
613 edge is the only internal one : closing a part of the domain */
614
615 /* Identification of edge number in tetra iel */
616 if ( !MMG3D_findEdge(mesh,pt,iel,numq,nump,1,NULL,&ia) ) return -1;
617
618 i0 = MMG5_ifar[ia][0];
619 i1 = MMG5_ifar[ia][1];
620 if ( pt->xt && (!(pxt->ftag[i0] & MG_BDY) || !(pxt->ftag[i1] & MG_BDY)) )
621 return 0;
622 }
623
624 /* Now check that the 2 faces identified by collapse are not boundary */
625 if ( pt->xt && (pxt->ftag[ipp] & MG_BDY) && (pxt->ftag[iq] & MG_BDY) )
626 return 0;
627
628 for (i=0; i<4; i++) {
629 if ( i==ipp || i==iq ) {
630 continue;
631 }
632
633 /* Avoid surface crimping: check that the collapse doesn't merge 3
634 * bdy edge along a non bdy face: we have to check the edge of each
635 * shell because some MG_BDY tags may be missings due to the creation
636 * of an xtetra during a previous collapse */
637 if ( (!pt->xt) || !(pxt->ftag[i] & MG_BDY) ) {
638 uint16_t tag0,tag1,tag2;
639 MMG5_int ref0,ref1,ref2;
640
641 tag0 = tag1 = tag2 = 0;
642 ref0 = ref1 = ref2 = 0;
643
644 if ( !MMG3D_get_shellEdgeTag(mesh,iel,MMG5_iarf[i][0],&tag0,&ref0) ) {
645 fprintf(stderr,"\n ## Error: %s: 0. unable to get edge info.\n",__func__);
646 return 0;
647 }
648 if ( !MMG3D_get_shellEdgeTag(mesh,iel,MMG5_iarf[i][1],&tag1,&ref1) ) {
649 fprintf(stderr,"\n ## Error: %s: 1. unable to get edge info.\n",__func__);
650 return 0;
651 }
652 if ( !MMG3D_get_shellEdgeTag(mesh,iel,MMG5_iarf[i][2],&tag2,&ref2) ) {
653 fprintf(stderr,"\n ## Error: %s: 2. unable to get edge info.\n",__func__);
654 return 0;
655 }
656 if ( (tag0 & MG_BDY) && (tag1 & MG_BDY) && (tag2 & MG_BDY ) ) {
657 return 0;
658 }
659 }
660 }
661
662 continue;
663 }
664
665 if ( !isnmint ) {
666 if ( isnm || mesh->info.iso ) {
667 /* Volume test for tetras outside the shell */
668 if ( (!ndepmin) && (pt->ref == refmin) ) {
669 ndepmin = iel;
670 }
671 else if ( (!ndepplus) && (pt->ref == refplus) ) {
672 ndepplus = iel;
673 }
674 }
675 }
676
677 /* Prevent from creating a tetra with 4 ridges metrics in aniso mode */
678 if ( met && met->m && met->size == 6 ) {
679 if ( (mesh->point[numq].tag & MG_GEO) && !MG_SIN_OR_NOM(mesh->point[numq].tag) ) {
680 i = ipp;
681 nr = 0;
682 for (iq=0; iq<3; iq++) {
683 i = MMG5_inxt3[i];
684 if ( (mesh->point[pt->v[i]].tag & MG_GEO) &&
685 !MG_SIN_OR_NOM(mesh->point[pt->v[i]].tag) ) {
686 ++nr;
687 }
688 }
689 if ( nr==3 ) return 0;
690 }
691 }
692
693 memcpy(pt0,pt,sizeof(MMG5_Tetra));
694 pt0->v[ipp] = numq;
695
696 calold = MG_MIN(calold, pt->qual);
697 if ( typchk==1 && met->m && met->size > 1 )
698 caltmp = MMG5_caltet33_ani(mesh,met,pt0);
699 else
700 caltmp = MMG5_orcal(mesh,met,0);
701
702 if ( caltmp < MMG5_NULKAL ) return 0;
703 calnew = MG_MIN(calnew,caltmp);
704 }
705 if ( calold < MMG5_EPSOK && calnew <= calold ) return 0;
706 else if ( calnew < MMG5_EPSOK || calnew < 0.3*calold ) return 0;
707
708 if ( isnmint ) {
709 return ilistv;
710 }
711
712 /* analyze surfacic ball of p */
713 for (l=1; l<ilists-1; l++) {
714 iel = lists[l] / 4;
715 iopp = lists[l] % 4;
716 assert(iel && iopp >=0 && iopp < 4 && "unexpected tetra or local vertex idx");
717
718 pt = &mesh->tetra[iel];
719 pxt = &mesh->xtetra[pt->xt];
720 assert(pt->xt);
721
722 /* retrieve vertex in tetra */
723 for (ip=0; ip<4; ip++)
724 if ( pt->v[ip] == nump ) break;
725
726 assert(ip<4);
727 if ( ip==4 ) return 0;
728
729 memcpy(pt0,pt,sizeof(MMG5_Tetra));
730 pt0->v[ip] = numq;
731
732 if ( !MMG5_norface(mesh,iel,iopp,ncurold) ) return 0;
733 if ( !MMG5_norface(mesh,0,iopp,ncurnew) ) return 0;
734
735 /* check normal flipping */
736 ps = ncurold[0]*ncurnew[0] + ncurold[1]*ncurnew[1] + ncurold[2]*ncurnew[2];
737 if ( ps < 0.0 ) return 0;
738
739 /* check normal deviation between l and its neighbour through the edge ia */
740 ia = MMG5_idirinv[iopp][ip]; /* index of p in tria iopp */
741
742 iedgeOpp = MMG5_iarf[iopp][ia];
743
744 if ( ! ( MG_GEO_OR_NOM(mesh->xtetra[pt->xt].tag[iedgeOpp])) ) {
745
746 ier = MMG3D_normalAdjaTri(mesh,iel,iopp,ia,nadja);
747 if ( ier < 0 ) return -1;
748 else if (!ier ) return 0;
749
750 devnew = nadja[0]*ncurnew[0] + nadja[1]*ncurnew[1] + nadja[2]*ncurnew[2];
751
752 if ( devnew < mesh->info.dhd ) return 0;
753 }
754
755 if ( l == 1 ) {
756 /* check normal deviation between the new first tri of the surfacic ball
757 * and the surfacic triangle facing ip in the old first tri of the
758 * surfacic ball (that vanishes due to the collapse)
759 */
760 kk = lists[0] / 4;
761 iopp2 = lists[0] % 4;
762 pt1 = &mesh->tetra[kk];
763 assert(pt1->xt);
764
765 /* retrieve vertex ip in tria iopp2 */
766 for (ipp=0; ipp<3; ipp++)
767 if ( pt1->v[MMG5_idir[iopp2][ipp]] == nump ) break;
768 assert(ipp<3);
769
770 iedgeOpp = MMG5_iarf[iopp2][ipp];
771
772 if ( ! ( MG_GEO_OR_NOM(mesh->xtetra[pt1->xt].tag[iedgeOpp] ) ) ) {
773 ier = MMG3D_normalAdjaTri(mesh,kk,iopp2,ipp,nadja);
774
775 if ( ier < 0 ) return -1;
776 else if ( !ier ) return 0;
777
778 devnew = nadja[0]*ncurnew[0] + nadja[1]*ncurnew[1] + nadja[2]*ncurnew[2];
779 if ( devnew < mesh->info.dhd ) {
780 return 0;
781 }
782 }
783 }
784 else {
785 /* check normal deviation between l and l-1 */
786 ia = MMG5_iprv2[ia]; /* edge between l-1 and l, in local num of tria */
787 ia = MMG5_iarf[iopp][ia]; /* edge between l-1 and l in local num of tetra */
788
789 if ( !MG_GEO_OR_NOM(mesh->xtetra[pt->xt].tag[ia]) ) {
790 devold = nprvold[0]*ncurold[0] + nprvold[1]*ncurold[1] + nprvold[2]*ncurold[2];
791 devnew = nprvnew[0]*ncurnew[0] + nprvnew[1]*ncurnew[1] + nprvnew[2]*ncurnew[2];
792
793 if ( devold < MMG5_ANGEDG ) {
794 if ( devnew < devold ) {
795 return 0;
796 }
797 }
798 else if ( devnew < MMG5_ANGEDG ) {
799 return 0;
800 }
801 }
802 }
803
804 /* check Hausdorff distance to geometric support */
805 MMG5_tet2tri(mesh,iel,iopp,&tt);
806 if ( l == 1 ) {
807 for (i=0; i<3; i++) {
808 if ( tt.v[i] == nump ) break;
809 }
810
811 assert(i<3);
812 if ( i==3 ) return 0;
813
814 /* Index of the third point of the first collapsed triangle */
815 i = MMG5_inxt2[i];
816 ia = MMG5_inxt2[i];
817 tag = pxt->tag[MMG5_iarf[iopp][ia]];
818 tt.tag[ia] = MG_MAX(tt.tag[ia],tag);
819 }
820 else if ( l == ilists-2 ) {
821 for (i=0; i<3; i++) {
822 if ( tt.v[i] == nump ) break;
823 }
824 assert(i<3);
825 /* Index of the third point of the second collapsed triangle */
826 i = MMG5_iprv2[i];
827 ia = MMG5_iprv2[i];
828 tag = pxt->tag[MMG5_iarf[iopp][ia]];
829 tt.tag[ia] = MG_MAX(tt.tag[ia],tag);
830 }
831
832 for (i=0; i<3; i++) {
833 if ( tt.v[i] == nump ) break;
834 }
835 assert(i<3);
836 if ( i==3 ) return 0;
837
838 tt.v[i] = numq;
839
840 /* Local parameters for tt and iel */
841 hmax = mesh->info.hmax;
842 hausd = mesh->info.hausd;
843 isloc = 0;
844 if ( mesh->info.parTyp & MG_Tetra ) {
845 for ( kk=0; kk<mesh->info.npar; ++kk ) {
846 par = &mesh->info.par[kk];
847
848 if ( par->elt != MMG5_Tetrahedron ) continue;
849 if ( par->ref != pt->ref ) continue;
850
851 hmax = par->hmax;
852 hausd = par->hausd;
853 isloc = 1;
854 break;
855 }
856 }
857 if ( mesh->info.parTyp & MG_Tria ) {
858 if ( isloc ) {
859 for ( kk=0; kk<mesh->info.npar; ++kk ) {
860 par = &mesh->info.par[kk];
861
862 if ( par->elt != MMG5_Triangle ) continue;
863 if ( par->ref != tt.ref ) continue;
864
865 hmax = MG_MIN(hmax,par->hmax);
866 hausd = MG_MIN(hausd,par->hausd);
867 break;
868 }
869 }
870 else {
871 for ( kk=0; kk<mesh->info.npar; ++kk ) {
872 par = &mesh->info.par[kk];
873
874 if ( par->elt != MMG5_Triangle ) continue;
875 if ( par->ref != tt.ref ) continue;
876
877 hmax = par->hmax;
878 hausd = par->hausd;
879 isloc = 1;
880 break;
881 }
882 }
883 }
884
885 /* If some edges must be splitted, refuse the collapse */
886 if ( MMG5_chkedg(mesh,&tt,MG_GET(pxt->ori,iopp),hmax,hausd,isloc) > 0 ) return 0;
887
888 memcpy(nprvold,ncurold,3*sizeof(double));
889 memcpy(nprvnew,ncurnew,3*sizeof(double));
890 }
891
892 /* check normal deviation between the new last tri of the surfacic ball
893 * and the surfacic triangle facing ip in the old last tri of the
894 * surfacic ball (that vanishes due to the collapse)
895 */
896 kk = lists[ilists-1] / 4;
897 iopp2 = lists[ilists-1] % 4;
898 pt1 = &mesh->tetra[kk];
899 assert(pt1->xt);
900
901 /* retrieve vertex ip in tria iopp2 */
902 for (ipp=0; ipp<3; ipp++)
903 if ( pt1->v[MMG5_idir[iopp2][ipp]] == nump ) break;
904 assert(ipp<3);
905
906 iedgeOpp = MMG5_iarf[iopp2][ipp];
907
908 if ( ! ( MG_GEO_OR_NOM(mesh->xtetra[pt1->xt].tag[iedgeOpp]) ) ) {
909
910 ier = MMG3D_normalAdjaTri(mesh,kk,iopp2,ipp,nadja);
911 if ( ier < 0 ) return -1;
912 else if ( !ier ) return 0;
913
914 devnew = nadja[0]*ncurnew[0] + nadja[1]*ncurnew[1] + nadja[2]*ncurnew[2];
915
916 if ( devnew < mesh->info.dhd ) {
917 return 0;
918 }
919 }
920
921 if ( isnm || mesh->info.iso ) {
922 ier = MMG3D_chkmanicoll(mesh,k,iface,iedg,ndepmin,ndepplus,refmin,refplus,isminp,isplp);
923 if ( !ier ) return 0;
924 }
925 else {
926 /* Topological check for surface ball */
927 ier = MMG5_topchkcol_bdy(mesh,k,iface,iedg,lists,ilists);
928 if ( ier<0 ) return -1;
929 else if ( !ier ) return 0;
930 }
931
932 return ilistv;
933}
934
949static inline
950void MMG3D_update_edgeTag(MMG5_pTetra pt,MMG5_pxTetra pxt,MMG5_int np, MMG5_int nq,
951 uint8_t ip, MMG5_pTetra pt1,MMG5_pxTetra pxt1,
952 uint8_t voyp) {
953
954 int i,j;
955 MMG5_int p0,p1;
956 uint8_t ia,iav;
957 uint16_t tag,tag1;
958
959 /* update tags for edges */
960 for ( j=0; j<3; j++ ) {
961 ia = MMG5_iarf[ip][j];
962 p0 = pt->v[MMG5_iare[ia][0]];
963 p1 = pt->v[MMG5_iare[ia][1]];
964 if ( pxt->tag[ia] ) {
965 for ( i=0; i<3; i++ ) {
966 iav=MMG5_iarf[voyp][i];
967 if ( p0==nq ) {
968 if ( ((pt1->v[MMG5_iare[iav][0]]==np) && (pt1->v[MMG5_iare[iav][1]]==p1)) ||
969 ((pt1->v[MMG5_iare[iav][0]]==p1) && (pt1->v[MMG5_iare[iav][1]]==np)) )
970 break;
971 }
972 else if ( p1==nq ) {
973 if ( ((pt1->v[MMG5_iare[iav][0]]==np ) && (pt1->v[MMG5_iare[iav][1]]==p0)) ||
974 ((pt1->v[MMG5_iare[iav][0]]==p0) && (pt1->v[MMG5_iare[iav][1]]==np )) )
975 break;
976 }
977 else {
978 if ( ((pt1->v[MMG5_iare[iav][0]]==p0) && (pt1->v[MMG5_iare[iav][1]]==p1)) ||
979 ((pt1->v[MMG5_iare[iav][0]]==p1) && (pt1->v[MMG5_iare[iav][1]]==p0)) )
980 break;
981 }
982 }
983 assert(i!=3);
984 tag1 = pxt1->tag[iav];
985 tag = pxt->tag[ia];
986 pxt1->tag[iav] |= pxt->tag[ia];
987 if( ((tag1 & MG_REQ) && !(tag1 & MG_NOSURF)) ||
988 (( tag & MG_REQ) && !( tag & MG_NOSURF)) )
989 pxt1->tag[iav] &= ~MG_NOSURF;
990 pxt1->edg[iav] = MG_MAX ( pxt1->edg[iav],pxt->edg[ia] );
991 }
992 }
993}
994
1011static inline
1012MMG5_int MMG3D_update_shellEdgeTag_oneDir(MMG5_pMesh mesh,MMG5_int start, MMG5_int na, MMG5_int nb,
1013 uint16_t tag,MMG5_int ref, MMG5_int piv,MMG5_int adj) {
1014 MMG5_pTetra pt;
1015 MMG5_pxTetra pxt;
1016 MMG5_int *adja;
1017 uint16_t xtag;
1018 int8_t i;
1019
1020 assert ( tag & MG_BDY && "Unexpected non boundary tag");
1021
1022 while ( adj && (adj != start) ) {
1023 pt = &mesh->tetra[adj];
1024
1025 /* identification of edge number in tetra adj */
1026 if ( !MMG3D_findEdge(mesh,pt,adj,na,nb,1,NULL,&i) ) return -1;
1027
1028 /* update edge ref and tag */
1029 if ( pt->xt ) {
1030 pxt = &mesh->xtetra[pt->xt];
1031
1032 /* if tag and edge are already consistent, no need to update the shell (do
1033 * not consider edges with tag 0 as they com from the creation of a xtetra
1034 * during a previous collapse and are not updated) */
1035 xtag = pxt->tag[i] | MG_BDY;
1036
1037 if ( pxt->tag[i] & MG_BDY ) {
1038 if ( xtag == tag && pxt->edg[i] == ref ) {
1039 return start;
1040 }
1041 }
1042
1043 pxt->edg[i] = ref;
1044 pxt->tag[i] |= tag;
1045 if( ((xtag & MG_REQ) && !(xtag & MG_NOSURF)) ||
1046 (( tag & MG_REQ) && !( tag & MG_NOSURF)))
1047 pxt->tag[i] &= ~MG_NOSURF;
1048 }
1049
1050 /* set new triangle for travel */
1051 adja = &mesh->adja[4*(adj-1)+1];
1052 if ( pt->v[ MMG5_ifar[i][0] ] == piv ) {
1053 adj = adja[ MMG5_ifar[i][0] ] / 4;
1054 piv = pt->v[ MMG5_ifar[i][1] ];
1055 }
1056 else {
1057 adj = adja[ MMG5_ifar[i][1] ] /4;
1058 piv = pt->v[ MMG5_ifar[i][0] ];
1059 }
1060 }
1061
1062 return adj;
1063}
1064
1076static inline
1077int MMG3D_update_shellEdgeTag(MMG5_pMesh mesh,MMG5_int start, int8_t ia,uint16_t tag,MMG5_int ref) {
1078 MMG5_pTetra pt;
1079 MMG5_pxTetra pxt;
1080 MMG5_int piv,na,nb,adj,*adja;
1081 uint16_t xtag;
1082
1083 pt = &mesh->tetra[start];
1084
1085 assert( start >= 1 && MG_EOK(pt) && "invalid tetra" );
1086 assert ( tag & MG_BDY && "Unexpected non boundary tag");
1087
1088 pxt = NULL;
1089 na = pt->v[MMG5_iare[ia][0]];
1090 nb = pt->v[MMG5_iare[ia][1]];
1091
1092 if ( pt->xt ) {
1093 pxt = &mesh->xtetra[pt->xt];
1094
1095 /* if tag and edge are already consistent, no need to update the shell (do
1096 * not consider edges with tag 0 as they com from the creation of a xtetra
1097 * during a previous collapse and are not updated) */
1098 xtag = pxt->tag[ia] | MG_BDY;
1099
1100 if ( pxt->tag[ia] & MG_BDY ) {
1101 if ( xtag == tag && pxt->edg[ia] == ref ) {
1102 return 1;
1103 }
1104 }
1105
1106 pxt->tag[ia] |= tag;
1107 if( ((xtag & MG_REQ) && !(xtag & MG_NOSURF)) ||
1108 (( tag & MG_REQ) && !( tag & MG_NOSURF)))
1109 pxt->tag[ia] &= ~MG_NOSURF;
1110 pxt->edg[ia] = ref;
1111 }
1112
1113 /* Travel in one direction */
1114 adja = &mesh->adja[4*(start-1)+1];
1115 adj = adja[MMG5_ifar[ia][0]] / 4;
1116 piv = pt->v[MMG5_ifar[ia][1]];
1117
1118 adj = MMG3D_update_shellEdgeTag_oneDir(mesh,start,na,nb,tag,ref,piv,adj);
1119
1120 /* If all shell has been travelled, stop, else, travel it the other sense */
1121 if ( adj == start ) return 1;
1122 else if ( adj < 0 ) return 0;
1123
1124 assert(!adj);
1125
1126 pt = &mesh->tetra[start];
1127 adja = &mesh->adja[4*(start-1)+1];
1128 adj = adja[MMG5_ifar[ia][1]] / 4;
1129 piv = pt->v[MMG5_ifar[ia][0]];
1130
1131 adj = MMG3D_update_shellEdgeTag_oneDir(mesh,start,na,nb,tag,ref,piv,adj);
1132
1133 if ( adj < 0 ) return 0;
1134
1135 return 1;
1136}
1137
1154MMG5_int MMG5_colver(MMG5_pMesh mesh,MMG5_pSol met,int64_t *list,int ilist,int8_t indq,int8_t typchk) {
1155 MMG5_pTetra pt,pt1;
1156 MMG5_pxTetra pxt,pxt1;
1157 MMG5_xTetra xt,xts;
1158 MMG5_int iel,jel,pel,qel,k,np,nq,*adja;
1159 uint8_t ip,iq,j,voy,voyp,voyq;
1160
1161 /* coledge[i] contains the local indices of edges that will be merged by the
1162 * collapse corresponding with the configuration i. The edge coledge[i][0] is
1163 * merged with the edge coledge[i][1] a,d the edge coledge[i][2] is merged
1164 * with edge coledge[i][3].
1165 * Config 0: merge of vertices 0 and 1
1166 * config 1: merge of vertices 0 and 2
1167 * config 2: merge of vertices 0 and 3
1168 * config 3: merge of vertices 1 and 2
1169 * config 4: merge of vertices 1 and 3
1170 * config 5: merge of vertices 2 and 3
1171 */
1172 const int8_t MMG5_coledge[6][4] = {
1173 {1,3,2,4}, {0,3,2,5}, {0,4,1,5},{0,1,4,5}, {0,2,3,5}, {4,3,2,1} };
1174
1175 iel = list[0] / 4;
1176 ip = list[0] % 4;
1177 pt = &mesh->tetra[iel];
1178 np = pt->v[ip];
1179 nq = pt->v[indq];
1180
1181 /* Mark elements of the shell of edge (pq) */
1182 for (k=0; k<ilist; k++) {
1183 iel = list[k] / 4;
1184 ip = list[k] % 4;
1185 pt = &mesh->tetra[iel];
1186
1187 for (j=0; j<3; j++) {
1188 iq = MMG5_idir[ip][j];
1189 if ( pt->v[iq] == nq ) {
1190
1191 list[k] *= -1;
1192 break;
1193 }
1194 }
1195 }
1196
1197 /* Avoid recreating existing elt */
1198 for (k=0; k<ilist; k++) {
1199
1200 if ( list[k] < 0 ) continue;
1201
1202 iel = list[k] / 4;
1203 ip = list[k] % 4;
1204
1205 adja = &mesh->adja[4*(iel-1)+1];
1206 jel = adja[ip];
1207 if ( !jel ) continue;
1208
1209 jel /= 4;
1210 voy = adja[ip] % 4;
1211 pt = &mesh->tetra[jel];
1212 if (pt->v[voy] == nq) {
1213 return 0;
1214 }
1215 }
1216
1222 for (k=0; k<ilist; k++) {
1223
1224 if ( list[k] > 0 ) continue;
1225
1226 iel = -list[k] / 4;
1227 ip = -list[k] % 4;
1228 pt = &mesh->tetra[iel];
1229
1230 for (j=0; j<3; j++) {
1231 iq = MMG5_idir[ip][j];
1232 if ( pt->v[iq] == nq ) {
1233 break;
1234 }
1235 }
1236
1237 /* The configuration is computed by setting the ip and iq bits to 1 */
1238 int8_t cfg = 0;
1239 MG_SET(cfg,ip);
1240 MG_SET(cfg,iq);
1241
1242 const int8_t *coled;
1243 switch(cfg) {
1244 case 3:
1245 /* collapse of vertices 0 and 1 */
1246 coled = MMG5_coledge[0];
1247 break;
1248 case 5:
1249 /* collapse of vertices 0 and 2 */
1250 coled = MMG5_coledge[1];
1251 break;
1252 case 9:
1253 /* collapse of vertices 0 and 3 */
1254 coled = MMG5_coledge[2];
1255 break;
1256 case 6:
1257 /* collapse of vertices 1 and 2 */
1258 coled = MMG5_coledge[3];
1259 break;
1260 case 10:
1261 /* collapse of vertices 1 and 3 */
1262 coled = MMG5_coledge[4];
1263 break;
1264 case 12:
1265 /* collapse of vertices 2 and 3 */
1266 coled = MMG5_coledge[5];
1267 break;
1268 default:
1269 assert ( 0 && "Unexpected collapse configuration");
1270 }
1271
1272 int l = 0;
1273 for ( l=0; l<3; l+=2 ) {
1274 /* when l=0 we update 2 edges that will be merged together, when l=2 we
1275 * update the two others. In practice we don't care which edge comes
1276 * from ip and which one from iq, it only matters that iped and iqed
1277 * will be merged at the end of the collapse. */
1278 int iped = coled[l+0];
1279 int iqed = coled[l+1];
1280
1281 uint16_t tagip = 0;
1282 MMG5_int refip = 0;
1283 uint16_t tagiq = 0;
1284 MMG5_int refiq = 0;
1285
1286 if ( !MMG3D_get_shellEdgeTag(mesh,iel,iped,&tagip,&refip) ) {
1287 fprintf(stderr,"\n ## Error: %s: 1. unable to get edge info.\n",__func__);
1288 return 0;
1289 }
1290 if ( !MMG3D_get_shellEdgeTag(mesh,iel,iqed,&tagiq,&refiq) ) {
1291 fprintf(stderr,"\n ## Error: %s: 2. unable to get edge info.\n",__func__);
1292 return 0;
1293 }
1294
1295 if ( (tagip != tagiq) || (refip != refiq) ) {
1296 /* Update edge infos */
1297 tagip |= tagiq;
1298 refip = (refip > refiq )? refip : refiq;
1299
1300 /* If the xtetra info are consistent when entering in colver taged and
1301 * refed contains suitable values and we can travel the shell of
1302 * iped and iqed to update all the needed info on edges */
1303 if ( !MMG3D_update_shellEdgeTag(mesh,iel,iped,tagip,refip) ) {
1304 fprintf(stderr,"\n ## Error: %s: 1. unable to update edge info.\n",__func__);
1305 return 0;
1306 }
1307 if ( !MMG3D_update_shellEdgeTag(mesh,iel,iqed,tagip,refip) ) {
1308 fprintf(stderr,"\n ## Error: %s: 1. unable to update edge info.\n",__func__);
1309 return 0;
1310 }
1311 }
1312#ifndef NDEBUG
1313 else {
1314 assert ( tagip == tagiq && refip == refiq );
1315 if ( mesh->info.ddebug && (tagip & MG_BDY) ) {
1316 MMG3D_chk_shellEdgeTag(mesh,iel,iped,tagip,refip);
1317 MMG3D_chk_shellEdgeTag(mesh,iel,iqed,tagip,refip);
1318 }
1319 }
1320#endif
1321 }
1322 }
1323
1324 /* deal with the shell of edge (pq) and the implied updates */
1325 for (k=0; k<ilist; k++) {
1326 if ( list[k] > 0 ) continue;
1327 iel = (-list[k]) / 4;
1328 ip = (-list[k]) % 4;
1329 pt = &mesh->tetra[iel];
1330
1331 iq = MMG5_inxt3[ip];
1332 for (j=0; j<3; j++) {
1333 if ( pt->v[iq] == nq ) break;
1334 iq = MMG5_inxt3[iq];
1335 }
1336 assert(j<3);
1337
1338
1339 adja = &mesh->adja[4*(iel-1)+1];
1340
1341 /* pel = neighbour of iel that belongs to ball of p \setminus shell, same for qel */
1342 pel = adja[iq] / 4;
1343 voyp = adja[iq] % 4;
1344 qel = adja[ip] / 4;
1345 voyq = adja[ip] % 4;
1346
1347 /* Update adjacency relations */
1348 if ( pel ) {
1349 adja = &mesh->adja[4*(pel-1)+1];
1350 adja[voyp] = 4*qel+voyq;
1351 }
1352 if ( qel ) {
1353 adja = &mesh->adja[4*(qel-1)+1];
1354 adja[voyq] = 4*pel+voyp;
1355 }
1356
1357 /* Update references for faces (one in pel) ;
1358 possibly, creation of a new field pxt for pel must be carried out */
1359 if ( pel ) {
1360 pt1 = &mesh->tetra[pel];
1361 if ( pt->xt ) {
1362 pxt = &mesh->xtetra[pt->xt];
1363 memcpy(&xts,pxt,sizeof(MMG5_xTetra));
1364 if ( pt1->xt > 0 ) {
1365 pxt1 = &mesh->xtetra[pt1->xt];
1366 pxt1->ref[voyp] = MG_MAX(pxt1->ref[voyp],pxt->ref[ip]);
1367 pxt1->ftag[voyp] = pxt1->ftag[voyp] | pxt->ftag[ip];
1368
1369 /* the face voyp of pxt1 is projected into the face ip of pxt so it
1370 * takes its orientation */
1371 if ( pxt->ftag[ip] & MG_BDY ) {
1372 if ( MG_GET(pxt->ori,ip) )
1373 MG_SET( pxt1->ori,voyp );
1374 else
1375 MG_CLR( pxt1->ori,voyp );
1376 }
1377 /* correct the orientation if the new adjacent has a different
1378 * reference */
1379 if( qel ) {
1380 if( pt1->ref > mesh->tetra[qel].ref )
1381 MG_SET( pxt1->ori,voyp );
1382 else if( pt1->ref < mesh->tetra[qel].ref )
1383 MG_CLR( pxt1->ori,voyp );
1384 }
1385
1386#ifndef NDEBUG
1387 else {
1388 /* Check that a non parallel external boundary face has always a good
1389 * orientation */
1390 if ( (pxt1->ftag[voyp] & MG_BDY) && !(pxt1->ftag[voyp] & MG_PARBDY) ) {
1391 assert ( MG_GET( pxt1->ori,voyp ) );
1392 }
1393 }
1394#endif
1395
1396 /* update tags for edges */
1397 MMG3D_update_edgeTag(pt,pxt,np,nq,ip,pt1,pxt1,voyp);
1398 }
1399 else {
1400 /* shell tet has a xtetra and pel exists but pel don't have a xtetra */
1401 pxt1 = &xt;
1402 memset(pxt1,0,sizeof(MMG5_xTetra));
1403 pxt1->ref[voyp] = pxt->ref[ip];
1404 pxt1->ftag[voyp] = pxt->ftag[ip];
1405 pxt1->ori = 15;
1406 if ( !MG_GET(pxt->ori,ip) ) MG_CLR(pxt1->ori,voyp);
1407
1408 /* update tags for edges */
1409 MMG3D_update_edgeTag(pt,pxt,np,nq,ip,pt1,pxt1,voyp);
1410
1411 /* Recover the already used place by pxt: now pel has a xtetra but the
1412 * edges coming from voyp (not directly involved in the collapse) will
1413 * have the tag 0 that may be non consistent with other tags in their
1414 * respective shells. */
1415 pt1->xt = pt->xt;
1416 memcpy(pxt,pxt1,sizeof(MMG5_xTetra));
1417 }
1418 }
1419 else {
1420 /* Shell tet don't have a xtetra: only the values of pel corresponding
1421 * to pt become 0 */
1422 if ( pt1->xt > 0 ) {
1423 pxt1 = &mesh->xtetra[pt1->xt];
1424 pxt1->ref[voyp] = 0;
1425 pxt1->ftag[voyp] = 0;
1426 MG_SET(pxt1->ori,voyp);
1427 }
1428 }
1429
1430 if ( qel ) {
1431 pt1 = &mesh->tetra[qel];
1432 if ( pt->xt ) {
1433 pxt = &xts;
1434 if ( pt1->xt > 0 ) {
1435 pxt1 = &mesh->xtetra[pt1->xt];
1436 pxt1->ref[voyq] = MG_MAX(pxt1->ref[voyq],pxt->ref[iq]);
1437 pxt1->ftag[voyq] = (pxt1->ftag[voyq] | pxt->ftag[iq]);
1438
1439 /* the face voyq of pxt1 is projected into the face iq of pxt so it
1440 * takes its orientation */
1441 if ( pxt->ftag[iq] & MG_BDY ) {
1442 if ( MG_GET(pxt->ori,iq) )
1443 MG_SET( pxt1->ori,voyq );
1444 else
1445 MG_CLR( pxt1->ori,voyq );
1446 }
1447 /* correct the orientation if the new adjacent has a different
1448 * reference */
1449 if( pel ) {
1450 if( pt1->ref > mesh->tetra[pel].ref )
1451 MG_SET( pxt1->ori,voyq );
1452 else if( pt1->ref < mesh->tetra[pel].ref )
1453 MG_CLR( pxt1->ori,voyq );
1454 }
1455
1456#ifndef NDEBUG
1457 else {
1458 /* Check that a non parallel external boundary face has always a good
1459 * orientation */
1460 if ( (pxt1->ftag[voyq] & MG_BDY) && !(pxt1->ftag[voyq] & MG_PARBDY) ) {
1461 assert ( MG_GET( pxt1->ori,voyq ) );
1462 }
1463 }
1464#endif
1465
1466 /* update tags for edges */
1467 MMG3D_update_edgeTag(pt,pxt,nq,np,iq,pt1,pxt1,voyq);
1468 }
1469 else {
1470 /* pel exists, shell tet has a xtetra but qel doesn't have boundary
1471 * tetra: create it */
1472 pxt1 = &xt;
1473 memset(pxt1,0,sizeof(MMG5_xTetra));
1474 pxt1->ref[voyq] = pxt->ref[iq];
1475 pxt1->ftag[voyq] = pxt->ftag[iq];
1476 pxt1->ori = 15;
1477 if ( !MG_GET(pxt->ori,iq) ) MG_CLR(pxt1->ori,voyq);
1478
1479 /* update tags for edges */
1480 MMG3D_update_edgeTag(pt,pxt,nq,np,iq,pt1,pxt1,voyq);
1481
1482 /* Create new field xt */
1483 mesh->xt++;
1484 if ( mesh->xt > mesh->xtmax ) {
1486 "larger xtetra table",
1487 mesh->xt--;return -1;);
1488 }
1489 /* Now qel has a xtetra but the edges coming from voyq (not directly
1490 * involved in the collapse) will have the tag 0 that may be non
1491 * consistent with other tags in their respective shells. */
1492 pt1->xt = mesh->xt;
1493 pxt = &mesh->xtetra[pt1->xt];
1494 memcpy(pxt,pxt1,sizeof(MMG5_xTetra));
1495 }
1496 }
1497 else {
1498 /* pel exist but shell tet doesn't have a boundary tetra: only the
1499 * values of qel corresponding to pt become 0 */
1500 if ( pt1->xt > 0 ) {
1501 pxt1 = &mesh->xtetra[pt1->xt];
1502 pxt1->ref[voyq] = 0;
1503 pxt1->ftag[voyq] = 0;
1504 MG_SET(pxt1->ori,voyq);
1505 }
1506 }
1507 }
1508 }
1509 else {
1510 /* pel==0: No adjacent through face iq */
1511 assert(pt->xt);
1512 pxt = &mesh->xtetra[pt->xt];
1513 if ( qel ) {
1514 pt1 = &mesh->tetra[qel];
1515 if ( pt1->xt > 0 ) {
1516 pxt1 = &mesh->xtetra[pt1->xt];
1517 pxt1->ref[voyq] = pxt->ref[iq];
1518 pxt1->ftag[voyq] = pxt->ftag[iq];
1519
1520 MG_SET(pxt1->ori,voyq);
1521
1522 /* update tags for edges */
1523 MMG3D_update_edgeTag(pt,pxt,nq,np,iq,pt1,pxt1,voyq);
1524 }
1525 else {
1526 pxt1 = &xt;
1527 memset(pxt1,0,sizeof(MMG5_xTetra));
1528 pxt1->ref[voyq] = pxt->ref[iq];
1529 pxt1->ftag[voyq] = pxt->ftag[iq];
1530 pxt1->ori = 15;
1531
1532 /* update tags for edges */
1533 MMG3D_update_edgeTag(pt,pxt,nq,np,iq,pt1,pxt1,voyq);
1534
1535 /* Recover the already used place by pxt */
1536 pt1->xt = pt->xt;
1537 memcpy(pxt,pxt1,sizeof(MMG5_xTetra));
1538 }
1539 }
1540 }
1541 if ( !MMG3D_delElt(mesh,iel) ) {
1542 return -1;
1543 }
1544 }
1545
1546 /* Update vertices coordinates for elements that do not belong to the shell of (pq) */
1547 for (k=0; k<ilist; k++) {
1548 if ( list[k] < 0 ) continue;
1549 iel = list[k] / 4;
1550 ip = list[k] % 4;
1551 pt = &mesh->tetra[iel];
1552 pt->v[ip] = nq;
1553 if ( typchk==1 && met->m && met->size > 1 )
1554 pt->qual=MMG5_caltet33_ani(mesh,met,pt);
1555 else
1556 pt->qual=MMG5_orcal(mesh,met,iel);
1557 pt->mark=mesh->mark;
1558 }
1559
1560 return np;
1561}
int ier
MMG5_pMesh * mesh
MMG5_Info info
int MMG3D_findEdge(MMG5_pMesh mesh, MMG5_pTetra pt, MMG5_int k, MMG5_int na, MMG5_int nb, int error, int8_t *mmgWarn, int8_t *ia)
Definition: boulep_3d.c:116
int MMG3D_chk_shellEdgeTag(MMG5_pMesh mesh, MMG5_int start, int8_t ia, uint16_t tag, MMG5_int ref)
Definition: chkmsh_3d.c:142
static void MMG3D_update_edgeTag(MMG5_pTetra pt, MMG5_pxTetra pxt, MMG5_int np, MMG5_int nq, uint8_t ip, MMG5_pTetra pt1, MMG5_pxTetra pxt1, uint8_t voyp)
Definition: colver_3d.c:950
static MMG5_int MMG3D_update_shellEdgeTag_oneDir(MMG5_pMesh mesh, MMG5_int start, MMG5_int na, MMG5_int nb, uint16_t tag, MMG5_int ref, MMG5_int piv, MMG5_int adj)
Definition: colver_3d.c:1012
static int MMG5_topchkcol_bdy(MMG5_pMesh mesh, MMG5_int k, int iface, int8_t iedg, MMG5_int *lists, int ilists)
Definition: colver_3d.c:255
static int MMG3D_update_shellEdgeTag(MMG5_pMesh mesh, MMG5_int start, int8_t ia, uint16_t tag, MMG5_int ref)
Definition: colver_3d.c:1077
int MMG3D_get_shellEdgeTag(MMG5_pMesh mesh, MMG5_int start, int8_t ia, uint16_t *tag, MMG5_int *ref)
Definition: colver_3d.c:439
static int MMG3D_get_shellEdgeTag_oneDir(MMG5_pMesh mesh, MMG5_int start, MMG5_int na, MMG5_int nb, uint16_t *tag, MMG5_int *ref, MMG5_int piv, MMG5_int adj, int8_t *filled)
Definition: colver_3d.c:381
MMG5_int MMG5_colver(MMG5_pMesh mesh, MMG5_pSol met, int64_t *list, int ilist, int8_t indq, int8_t typchk)
Definition: colver_3d.c:1154
int8_t ddb
Definition: mmg3d1_delone.c:42
int MMG5_chkcol_int(MMG5_pMesh mesh, MMG5_pSol met, MMG5_int k, int8_t iface, int8_t iedg, int64_t *list, int ilist, int8_t typchk)
Definition: colver_3d.c:43
int MMG5_chkcol_bdy(MMG5_pMesh mesh, MMG5_pSol met, MMG5_int k, int8_t iface, int8_t iedg, int64_t *listv, int ilistv, MMG5_int *lists, int ilists, MMG5_int refmin, MMG5_int refplus, int8_t typchk, int isnm, int8_t isnmint)
Definition: colver_3d.c:527
static MMG5_int MMG3D_unfold_shell(MMG5_pMesh mesh, MMG5_int start, MMG5_int end, MMG5_int na, MMG5_int nb, MMG5_int piv, MMG5_int *iel, int8_t *iopp)
Definition: colver_3d.c:197
static double MMG5_orcal(MMG5_pMesh mesh, MMG5_pSol met, MMG5_int iel)
static const int8_t MMG5_idirinv[4][4]
int MMG3D_normalAdjaTri(MMG5_pMesh, MMG5_int, int8_t, int, double n[3])
Definition: split_3d.c:472
int MMG5_startedgsurfball(MMG5_pMesh mesh, MMG5_int nump, MMG5_int numq, MMG5_int *list, int ilist)
Definition: tools_3d.c:91
int MMG3D_chkmanicoll(MMG5_pMesh, MMG5_int, int, int, MMG5_int, MMG5_int, MMG5_int, MMG5_int, int8_t, int8_t)
Definition: mmg3d2.c:1683
int MMG3D_delElt(MMG5_pMesh mesh, MMG5_int iel)
Definition: zaldy_3d.c:122
int8_t MMG5_chkedg(MMG5_pMesh mesh, MMG5_Tria *pt, int8_t ori, double, double, int)
Definition: mmg3d1.c:363
static const int8_t MMG5_iarf[4][3]
iarf[i]: edges of face opposite to vertex i
double MMG5_caltet33_ani(MMG5_pMesh mesh, MMG5_pSol met, MMG5_pTetra pt)
Definition: quality_3d.c:109
static const uint8_t MMG5_iare[6][2]
vertices of extremities of the edges of the tetra
static const uint8_t MMG5_ifar[6][2]
ifar[i][]: faces sharing the ith edge of the tetra
void MMG5_tet2tri(MMG5_pMesh mesh, MMG5_int k, int8_t ie, MMG5_Tria *ptt)
Definition: mmg3d1.c:102
static const uint8_t MMG5_inxt3[7]
next vertex of tetra: {1,2,3,0,1,2,3}
int MMG5_norface(MMG5_pMesh mesh, MMG5_int k, int iface, double v[3])
Definition: tools_3d.c:52
static const uint8_t MMG5_idir[4][3]
idir[i]: vertices of face opposite to vertex i
#define MG_PLUS
Definition: libmmgtypes.h:77
@ MMG5_Tetrahedron
Definition: libmmgtypes.h:233
@ MMG5_Triangle
Definition: libmmgtypes.h:232
#define MG_MINUS
Definition: libmmgtypes.h:82
#define MG_Tria
#define MG_REQ
#define MG_GEO
#define MMG5_EPSOK
#define MG_EOK(pt)
#define MG_CLR(flag, bit)
#define MG_PARBDY
#define MG_MIN(a, b)
#define MG_MAX(a, b)
#define MMG5_GAP
static const uint8_t MMG5_iprv2[3]
#define MMG5_TAB_RECALLOC(mesh, ptr, initSize, wantedGap, type, message, law)
#define MMG5_NULKAL
#define MG_GEO_OR_NOM(tag)
#define MG_Tetra
#define MG_GET(flag, bit)
static const uint8_t MMG5_inxt2[6]
#define MG_BDY
#define MMG5_ANGEDG
#define MG_NOSURF
#define MG_SIN_OR_NOM(tag)
int MMG5_norpts(MMG5_pMesh, MMG5_int, MMG5_int, MMG5_int, double *)
Definition: tools.c:183
#define MG_SET(flag, bit)
int8_t iso
Definition: libmmgtypes.h:541
int8_t parTyp
Definition: libmmgtypes.h:548
int8_t ddebug
Definition: libmmgtypes.h:539
double hmax
Definition: libmmgtypes.h:525
MMG5_pPar par
Definition: libmmgtypes.h:524
double dhd
Definition: libmmgtypes.h:525
double hausd
Definition: libmmgtypes.h:525
int8_t fem
Definition: libmmgtypes.h:546
MMG mesh structure.
Definition: libmmgtypes.h:613
MMG5_Info info
Definition: libmmgtypes.h:659
MMG5_int xt
Definition: libmmgtypes.h:628
MMG5_pPoint point
Definition: libmmgtypes.h:649
MMG5_int mark
Definition: libmmgtypes.h:626
MMG5_int * adja
Definition: libmmgtypes.h:632
MMG5_int xtmax
Definition: libmmgtypes.h:620
MMG5_pTetra tetra
Definition: libmmgtypes.h:651
MMG5_pxTetra xtetra
Definition: libmmgtypes.h:652
Local parameters for a specific entity and reference.
Definition: libmmgtypes.h:263
double hmax
Definition: libmmgtypes.h:265
double hausd
Definition: libmmgtypes.h:266
MMG5_int ref
Definition: libmmgtypes.h:267
int8_t elt
Definition: libmmgtypes.h:268
Structure to store vertices of an MMG mesh.
Definition: libmmgtypes.h:276
uint16_t tag
Definition: libmmgtypes.h:290
MMG5_int xp
Definition: libmmgtypes.h:285
double * m
Definition: libmmgtypes.h:680
Structure to store tetrahedra of an MMG mesh.
Definition: libmmgtypes.h:407
MMG5_int v[4]
Definition: libmmgtypes.h:409
MMG5_int xt
Definition: libmmgtypes.h:413
MMG5_int ref
Definition: libmmgtypes.h:410
MMG5_int mark
Definition: libmmgtypes.h:412
double qual
Definition: libmmgtypes.h:408
Structure to store triangles of a MMG mesh.
Definition: libmmgtypes.h:338
MMG5_int ref
Definition: libmmgtypes.h:341
uint16_t tag[3]
Definition: libmmgtypes.h:348
MMG5_int v[3]
Definition: libmmgtypes.h:340
Structure to store additional information for the surface tetrahedra of an MMG mesh.
Definition: libmmgtypes.h:425
uint16_t tag[6]
Definition: libmmgtypes.h:432
int8_t ori
Definition: libmmgtypes.h:434
MMG5_int edg[6]
Definition: libmmgtypes.h:428
MMG5_int ref[4]
Definition: libmmgtypes.h:426
uint16_t ftag[4]
Definition: libmmgtypes.h:430