diff --git a/contrib/Tetgen1.5/predicates.cxx b/contrib/Tetgen1.5/predicates.cxx index 5fe852e211fd4ad5675cfb815aa00485c81799e1..4120704c3521a80f27aa144feb1387ccf78c66a2 100644 --- a/contrib/Tetgen1.5/predicates.cxx +++ b/contrib/Tetgen1.5/predicates.cxx @@ -125,13 +125,6 @@ #include "tetgen.h" // Defines the symbol REAL (float or double). -#ifdef USE_CGAL_PREDICATES - #include <CGAL/Exact_predicates_inexact_constructions_kernel.h> - typedef CGAL::Exact_predicates_inexact_constructions_kernel cgalEpick; - typedef cgalEpick::Point_3 Point; - cgalEpick cgal_pred_obj; -#endif // #ifdef USE_CGAL_PREDICATES - /* On some machines, the exact arithmetic routines might be defeated by the */ /* use of internal extended precision floating-point registers. Sometimes */ /* this problem can be fixed by defining certain values to be volatile, */ @@ -156,8 +149,8 @@ /* which is disastrously slow. A faster way on IEEE machines might be to */ /* mask the appropriate bit, but that's difficult to do in C. */ -//#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) -#define Absolute(a) fabs(a) +#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +/* #define Absolute(a) fabs(a) */ /* Many of the operations are broken up into two pieces, a main part that */ /* performs an approximate operation, and a "tail" that computes the */ @@ -382,144 +375,271 @@ static REAL o3derrboundA, o3derrboundB, o3derrboundC; static REAL iccerrboundA, iccerrboundB, iccerrboundC; static REAL isperrboundA, isperrboundB, isperrboundC; -// Options to choose types of geometric computtaions. -// Added by H. Si, 2012-08-23. -static int _use_inexact_arith; // -X option. -static int _use_static_filter; // Default option, disable it by -X1 - -// Static filters for orient3d() and insphere(). -// They are pre-calcualted and set in exactinit(). -// Added by H. Si, 2012-08-23. -static REAL o3dstaticfilter; -static REAL ispstaticfilter; - - - -// The following codes were part of "IEEE 754 floating-point test software" -// http://www.math.utah.edu/~beebe/software/ieee/ -// The original program was "fpinfo2.c". +/*****************************************************************************/ +/* */ +/* doubleprint() Print the bit representation of a double. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ -double fppow2(int n) +/* +void doubleprint(number) +double number; { - double x, power; - x = (n < 0) ? ((double)1.0/(double)2.0) : (double)2.0; - n = (n < 0) ? -n : n; - power = (double)1.0; - while (n-- > 0) - power *= x; - return (power); + unsigned long long no; + unsigned long long sign, expo; + int exponent; + int i, bottomi; + + no = *(unsigned long long *) &number; + sign = no & 0x8000000000000000ll; + expo = (no >> 52) & 0x7ffll; + exponent = (int) expo; + exponent = exponent - 1023; + if (sign) { + printf("-"); + } else { + printf(" "); + } + if (exponent == -1023) { + printf( + "0.0000000000000000000000000000000000000000000000000000_ ( )"); + } else { + printf("1."); + bottomi = -1; + for (i = 0; i < 52; i++) { + if (no & 0x0008000000000000ll) { + printf("1"); + bottomi = i; + } else { + printf("0"); + } + no <<= 1; + } + printf("_%d (%d)", exponent, exponent - 1 - bottomi); + } } +*/ -#ifdef SINGLE +/*****************************************************************************/ +/* */ +/* floatprint() Print the bit representation of a float. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ -float fstore(float x) +/* +void floatprint(number) +float number; { - return (x); + unsigned no; + unsigned sign, expo; + int exponent; + int i, bottomi; + + no = *(unsigned *) &number; + sign = no & 0x80000000; + expo = (no >> 23) & 0xff; + exponent = (int) expo; + exponent = exponent - 127; + if (sign) { + printf("-"); + } else { + printf(" "); + } + if (exponent == -127) { + printf("0.00000000000000000000000_ ( )"); + } else { + printf("1."); + bottomi = -1; + for (i = 0; i < 23; i++) { + if (no & 0x00400000) { + printf("1"); + bottomi = i; + } else { + printf("0"); + } + no <<= 1; + } + printf("_%3d (%3d)", exponent, exponent - 1 - bottomi); + } } +*/ -int test_float(int verbose) -{ - float x; - int pass = 1; +/*****************************************************************************/ +/* */ +/* expansion_print() Print the bit representation of an expansion. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ - //(void)printf("float:\n"); +/* +void expansion_print(elen, e) +int elen; +REAL *e; +{ + int i; - if (verbose) { - (void)printf(" sizeof(float) = %2u\n", (unsigned int)sizeof(float)); -#ifdef CPU86 // <float.h> - (void)printf(" FLT_MANT_DIG = %2d\n", FLT_MANT_DIG); -#endif + for (i = elen - 1; i >= 0; i--) { + REALPRINT(e[i]); + if (i > 0) { + printf(" +\n"); + } else { + printf("\n"); + } } +} +*/ - x = (float)1.0; - while (fstore((float)1.0 + x/(float)2.0) != (float)1.0) - x /= (float)2.0; - if (verbose) - (void)printf(" machine epsilon = %13.5e ", x); +/*****************************************************************************/ +/* */ +/* doublerand() Generate a double with random 53-bit significand and a */ +/* random exponent in [0, 511]. */ +/* */ +/*****************************************************************************/ - if (x == (float)fppow2(-23)) { - if (verbose) - (void)printf("[IEEE 754 32-bit macheps]\n"); - } else { - (void)printf("[not IEEE 754 conformant] !!\n"); - pass = 0; +/* +double doublerand() +{ + double result; + double expo; + long a, b, c; + long i; + + a = random(); + b = random(); + c = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } } + return result; +} +*/ - x = (float)1.0; - while (fstore(x / (float)2.0) != (float)0.0) - x /= (float)2.0; - if (verbose) - (void)printf(" smallest positive number = %13.5e ", x); - - if (x == (float)fppow2(-149)) { - if (verbose) - (void)printf("[smallest 32-bit subnormal]\n"); - } else if (x == (float)fppow2(-126)) { - if (verbose) - (void)printf("[smallest 32-bit normal]\n"); - } else { - (void)printf("[not IEEE 754 conformant] !!\n"); - pass = 0; - } +/*****************************************************************************/ +/* */ +/* narrowdoublerand() Generate a double with random 53-bit significand */ +/* and a random exponent in [0, 7]. */ +/* */ +/*****************************************************************************/ - return pass; +/* +double narrowdoublerand() +{ + double result; + double expo; + long a, b, c; + long i; + + a = random(); + b = random(); + c = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; } +*/ -# else +/*****************************************************************************/ +/* */ +/* uniformdoublerand() Generate a double with random 53-bit significand. */ +/* */ +/*****************************************************************************/ -double dstore(double x) +/* +double uniformdoublerand() { - return (x); + double result; + long a, b; + + a = random(); + b = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + return result; } +*/ + +/*****************************************************************************/ +/* */ +/* floatrand() Generate a float with random 24-bit significand and a */ +/* random exponent in [0, 63]. */ +/* */ +/*****************************************************************************/ -int test_double(int verbose) +/* +float floatrand() { - double x; - int pass = 1; - - // (void)printf("double:\n"); - if (verbose) { - (void)printf(" sizeof(double) = %2u\n", (unsigned int)sizeof(double)); -#ifdef CPU86 // <float.h> - (void)printf(" DBL_MANT_DIG = %2d\n", DBL_MANT_DIG); -#endif + float result; + float expo; + long a, c; + long i; + + a = random(); + c = random(); + result = (float) ((a - 1073741824) >> 6); + for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } } + return result; +} +*/ - x = 1.0; - while (dstore(1.0 + x/2.0) != 1.0) - x /= 2.0; - if (verbose) - (void)printf(" machine epsilon = %13.5le ", x); +/*****************************************************************************/ +/* */ +/* narrowfloatrand() Generate a float with random 24-bit significand and */ +/* a random exponent in [0, 7]. */ +/* */ +/*****************************************************************************/ - if (x == (double)fppow2(-52)) { - if (verbose) - (void)printf("[IEEE 754 64-bit macheps]\n"); - } else { - (void)printf("[not IEEE 754 conformant] !!\n"); - pass = 0; +/* +float narrowfloatrand() +{ + float result; + float expo; + long a, c; + long i; + + a = random(); + c = random(); + result = (float) ((a - 1073741824) >> 6); + for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } } + return result; +} +*/ - x = 1.0; - while (dstore(x / 2.0) != 0.0) - x /= 2.0; - if (verbose) - (void)printf(" smallest positive number = %13.5le ", x); - - if (x == (double)fppow2(-1074)) { - if (verbose) - (void)printf("[smallest 64-bit subnormal]\n"); - } else if (x == (double)fppow2(-1022)) { - if (verbose) - (void)printf("[smallest 64-bit normal]\n"); - } else { - (void)printf("[not IEEE 754 conformant] !!\n"); - pass = 0; - } +/*****************************************************************************/ +/* */ +/* uniformfloatrand() Generate a float with random 24-bit significand. */ +/* */ +/*****************************************************************************/ - return pass; -} +/* +float uniformfloatrand() +{ + float result; + long a; -#endif + a = random(); + result = (float) ((a - 1073741824) >> 6); + return result; +} +*/ /*****************************************************************************/ /* */ @@ -540,8 +660,7 @@ int test_double(int verbose) /* */ /*****************************************************************************/ -void exactinit(int verbose, int noexact, int nofilter, REAL maxx, REAL maxy, - REAL maxz) +REAL exactinit() { REAL half; REAL check, lastcheck; @@ -568,24 +687,6 @@ void exactinit(int verbose, int noexact, int nofilter, REAL maxx, REAL maxy, _FPU_SETCW(cword); #endif /* LINUX */ - if (verbose) { - printf(" Initializing robust predicates.\n"); - } - -#ifdef USE_CGAL_PREDICATES - if (cgal_pred_obj.Has_static_filters) { - printf(" Use static filter.\n"); - } else { - printf(" No static filter.\n"); - } -#endif // USE_CGAL_PREDICATES - -#ifdef SINGLE - test_float(verbose); -#else - test_double(verbose); -#endif - every_other = 1; half = 0.5; epsilon = 1.0; @@ -621,31 +722,7 @@ void exactinit(int verbose, int noexact, int nofilter, REAL maxx, REAL maxy, isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; - // Set TetGen options. Added by H. Si, 2012-08-23. - _use_inexact_arith = noexact; - _use_static_filter = !nofilter; - - // Calculate the two static filters for orient3d() and insphere() tests. - // Added by H. Si, 2012-08-23. - - // Sort maxx < maxy < maxz. Re-use 'half' for swapping. - assert(maxx > 0); - assert(maxy > 0); - assert(maxz > 0); - - if (maxx > maxz) { - half = maxx; maxx = maxz; maxz = half; - } - if (maxy > maxz) { - half = maxy; maxy = maxz; maxz = half; - } - else if (maxy < maxx) { - half = maxy; maxy = maxx; maxx = half; - } - - o3dstaticfilter = 5.1107127829973299e-15 * maxx * maxy * maxz; - ispstaticfilter = 1.2466136531027298e-13 * maxx * maxy * maxz * (maxz * maxz); - + return epsilon; /* Added by H. Si 30 Juli, 2004. */ } /*****************************************************************************/ @@ -1792,6 +1869,16 @@ REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent) REAL fin1[192], fin2[192]; int finlength; + //////////////////////////////////////////////////////// + // To avoid uninitialized warnings reported by valgrind. + int i; + for (i = 0; i < 8; i++) { + adet[i] = bdet[i] = cdet[i] = 0.0; + } + for (i = 0; i < 16; i++) { + abdet[i] = 0.0; + } + //////////////////////////////////////////////////////// REAL adxtail, bdxtail, cdxtail; REAL adytail, bdytail, cdytail; @@ -1829,7 +1916,6 @@ REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent) INEXACT REAL _i, _j, _k; REAL _0; - adx = (REAL) (pa[0] - pd[0]); bdx = (REAL) (pb[0] - pd[0]); cdx = (REAL) (pc[0] - pd[0]); @@ -2177,16 +2263,27 @@ REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent) return finnow[finlength - 1]; } -#ifdef USE_CGAL_PREDICATES +#ifdef INEXACT_GEOM_PRED REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd) { - return (REAL) - - cgal_pred_obj.orientation_3_object() - (Point(pa[0], pa[1], pa[2]), - Point(pb[0], pb[1], pb[2]), - Point(pc[0], pc[1], pc[2]), - Point(pd[0], pd[1], pd[2])); + REAL adx, bdx, cdx; + REAL ady, bdy, cdy; + REAL adz, bdz, cdz; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + return adx * (bdy * cdz - bdz * cdy) + + bdx * (cdy * adz - cdz * ady) + + cdx * (ady * bdz - adz * bdy); } #else @@ -2196,16 +2293,16 @@ REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd) REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; REAL det; - + REAL permanent, errbound; adx = pa[0] - pd[0]; - ady = pa[1] - pd[1]; - adz = pa[2] - pd[2]; bdx = pb[0] - pd[0]; - bdy = pb[1] - pd[1]; - bdz = pb[2] - pd[2]; cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; cdz = pc[2] - pd[2]; bdxcdy = bdx * cdy; @@ -2221,19 +2318,6 @@ REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady); - if (_use_inexact_arith) { - return det; - } - - if (_use_static_filter) { - //if (fabs(det) > o3dstaticfilter) return det; - if (det > o3dstaticfilter) return det; - if (det < -o3dstaticfilter) return det; - } - - - REAL permanent, errbound; - permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz) + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz) + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz); @@ -2245,7 +2329,7 @@ REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd) return orient3dadapt(pa, pb, pc, pd, permanent); } -#endif // #ifdef USE_CGAL_PREDICATES +#endif // ifdef INEXACT_GEOM_PRED /*****************************************************************************/ /* */ @@ -3278,7 +3362,6 @@ REAL insphereexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) INEXACT REAL _i, _j; REAL _0; - Two_Product(pa[0], pb[1], axby1, axby0); Two_Product(pb[0], pa[1], bxay1, bxay0); Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); @@ -3855,7 +3938,6 @@ REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, INEXACT REAL _i, _j; REAL _0; - aex = (REAL) (pa[0] - pe[0]); bex = (REAL) (pb[0] - pe[0]); cex = (REAL) (pc[0] - pe[0]); @@ -4032,19 +4114,50 @@ REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, return insphereexact(pa, pb, pc, pd, pe); } -#ifdef USE_CGAL_PREDICATES +#ifdef INEXACT_GEOM_PRED REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) { - return (REAL) - - cgal_pred_obj.side_of_oriented_sphere_3_object() - (Point(pa[0], pa[1], pa[2]), - Point(pb[0], pb[1], pb[2]), - Point(pc[0], pc[1], pc[2]), - Point(pd[0], pd[1], pd[2]), - Point(pe[0], pe[1], pe[2])); -} + REAL aex, bex, cex, dex; + REAL aey, bey, cey, dey; + REAL aez, bez, cez, dez; + REAL alift, blift, clift, dlift; + REAL ab, bc, cd, da, ac, bd; + REAL abc, bcd, cda, dab; + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + + ab = aex * bey - bex * aey; + bc = bex * cey - cex * bey; + cd = cex * dey - dex * cey; + da = dex * aey - aex * dey; + + ac = aex * cey - cex * aey; + bd = bex * dey - dex * bey; + + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + + alift = aex * aex + aey * aey + aez * aez; + blift = bex * bex + bey * bey + bez * bez; + clift = cex * cex + cey * cey + cez * cez; + dlift = dex * dex + dey * dey + dez * dez; + + return (dlift * abc - clift * dab) + (blift * cda - alift * bcd); +} #else REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) @@ -4057,8 +4170,12 @@ REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) REAL alift, blift, clift, dlift; REAL ab, bc, cd, da, ac, bd; REAL abc, bcd, cda, dab; + REAL aezplus, bezplus, cezplus, dezplus; + REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; + REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; + REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; REAL det; - + REAL permanent, errbound; aex = pa[0] - pe[0]; bex = pb[0] - pe[0]; @@ -4105,23 +4222,6 @@ REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd); - if (_use_inexact_arith) { - return det; - } - - if (_use_static_filter) { - if (fabs(det) > ispstaticfilter) return det; - //if (det > ispstaticfilter) return det; - //if (det < minus_ispstaticfilter) return det; - - } - - REAL aezplus, bezplus, cezplus, dezplus; - REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; - REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; - REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; - REAL permanent, errbound; - aezplus = Absolute(aez); bezplus = Absolute(bez); cezplus = Absolute(cez); @@ -4162,7 +4262,7 @@ REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) return insphereadapt(pa, pb, pc, pd, pe, permanent); } -#endif // #ifdef USE_CGAL_PREDICATES +#endif // #ifdef INEXACT_GEOM_PRED /*****************************************************************************/ /* */ @@ -4227,7 +4327,6 @@ REAL orient4dexact(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, INEXACT REAL _i, _j; REAL _0; - Two_Product(pa[0], pb[1], axby1, axby0); Two_Product(pb[0], pa[1], bxay1, bxay0); Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); @@ -4441,7 +4540,6 @@ REAL orient4dadapt(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, INEXACT REAL _i, _j; REAL _0; - aex = (REAL) (pa[0] - pe[0]); bex = (REAL) (pb[0] - pe[0]); cex = (REAL) (pc[0] - pe[0]); @@ -4601,106 +4699,108 @@ REAL orient4d(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, REAL aheight, REAL bheight, REAL cheight, REAL dheight, REAL eheight) { - REAL aex, bex, cex, dex; - REAL aey, bey, cey, dey; - REAL aez, bez, cez, dez; - REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey; - REAL aexcey, cexaey, bexdey, dexbey; - REAL aeheight, beheight, ceheight, deheight; - REAL ab, bc, cd, da, ac, bd; - REAL abc, bcd, cda, dab; - REAL aezplus, bezplus, cezplus, dezplus; - REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; - REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; - REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; - REAL det; - REAL permanent, errbound; - - - aex = pa[0] - pe[0]; - bex = pb[0] - pe[0]; - cex = pc[0] - pe[0]; - dex = pd[0] - pe[0]; - aey = pa[1] - pe[1]; - bey = pb[1] - pe[1]; - cey = pc[1] - pe[1]; - dey = pd[1] - pe[1]; - aez = pa[2] - pe[2]; - bez = pb[2] - pe[2]; - cez = pc[2] - pe[2]; - dez = pd[2] - pe[2]; - aeheight = aheight - eheight; - beheight = bheight - eheight; - ceheight = cheight - eheight; - deheight = dheight - eheight; - - aexbey = aex * bey; - bexaey = bex * aey; - ab = aexbey - bexaey; - bexcey = bex * cey; - cexbey = cex * bey; - bc = bexcey - cexbey; - cexdey = cex * dey; - dexcey = dex * cey; - cd = cexdey - dexcey; - dexaey = dex * aey; - aexdey = aex * dey; - da = dexaey - aexdey; - - aexcey = aex * cey; - cexaey = cex * aey; - ac = aexcey - cexaey; - bexdey = bex * dey; - dexbey = dex * bey; - bd = bexdey - dexbey; - - abc = aez * bc - bez * ac + cez * ab; - bcd = bez * cd - cez * bd + dez * bc; - cda = cez * da + dez * ac + aez * cd; - dab = dez * ab + aez * bd + bez * da; - - det = (deheight * abc - ceheight * dab) + (beheight * cda - aeheight * bcd); - - aezplus = Absolute(aez); - bezplus = Absolute(bez); - cezplus = Absolute(cez); - dezplus = Absolute(dez); - aexbeyplus = Absolute(aexbey); - bexaeyplus = Absolute(bexaey); - bexceyplus = Absolute(bexcey); - cexbeyplus = Absolute(cexbey); - cexdeyplus = Absolute(cexdey); - dexceyplus = Absolute(dexcey); - dexaeyplus = Absolute(dexaey); - aexdeyplus = Absolute(aexdey); - aexceyplus = Absolute(aexcey); - cexaeyplus = Absolute(cexaey); - bexdeyplus = Absolute(bexdey); - dexbeyplus = Absolute(dexbey); - permanent = ((cexdeyplus + dexceyplus) * bezplus - + (dexbeyplus + bexdeyplus) * cezplus - + (bexceyplus + cexbeyplus) * dezplus) - * Absolute(aeheight) - + ((dexaeyplus + aexdeyplus) * cezplus - + (aexceyplus + cexaeyplus) * dezplus - + (cexdeyplus + dexceyplus) * aezplus) - * Absolute(beheight) - + ((aexbeyplus + bexaeyplus) * dezplus - + (bexdeyplus + dexbeyplus) * aezplus - + (dexaeyplus + aexdeyplus) * bezplus) - * Absolute(ceheight) - + ((bexceyplus + cexbeyplus) * aezplus - + (cexaeyplus + aexceyplus) * bezplus - + (aexbeyplus + bexaeyplus) * cezplus) - * Absolute(deheight); - errbound = isperrboundA * permanent; - if ((det > errbound) || (-det > errbound)) { - return det; - } - - return orient4dadapt(pa, pb, pc, pd, pe, - aheight, bheight, cheight, dheight, eheight, permanent); -} + REAL aex, bex, cex, dex; + REAL aey, bey, cey, dey; + REAL aez, bez, cez, dez; + REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey; + REAL aexcey, cexaey, bexdey, dexbey; + REAL aeheight, beheight, ceheight, deheight; + REAL ab, bc, cd, da, ac, bd; + REAL abc, bcd, cda, dab; + REAL aezplus, bezplus, cezplus, dezplus; + REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; + REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; + REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; + REAL det; + REAL permanent, errbound; + + //orient4dcount++; + + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + aeheight = aheight - eheight; + beheight = bheight - eheight; + ceheight = cheight - eheight; + deheight = dheight - eheight; + aexbey = aex * bey; + bexaey = bex * aey; + ab = aexbey - bexaey; + bexcey = bex * cey; + cexbey = cex * bey; + bc = bexcey - cexbey; + cexdey = cex * dey; + dexcey = dex * cey; + cd = cexdey - dexcey; + dexaey = dex * aey; + aexdey = aex * dey; + da = dexaey - aexdey; + + aexcey = aex * cey; + cexaey = cex * aey; + ac = aexcey - cexaey; + bexdey = bex * dey; + dexbey = dex * bey; + bd = bexdey - dexbey; + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + det = (deheight * abc - ceheight * dab) + (beheight * cda - aeheight * bcd); + + if (0) { //if (noexact) { + return det; + } + + aezplus = Absolute(aez); + bezplus = Absolute(bez); + cezplus = Absolute(cez); + dezplus = Absolute(dez); + aexbeyplus = Absolute(aexbey); + bexaeyplus = Absolute(bexaey); + bexceyplus = Absolute(bexcey); + cexbeyplus = Absolute(cexbey); + cexdeyplus = Absolute(cexdey); + dexceyplus = Absolute(dexcey); + dexaeyplus = Absolute(dexaey); + aexdeyplus = Absolute(aexdey); + aexceyplus = Absolute(aexcey); + cexaeyplus = Absolute(cexaey); + bexdeyplus = Absolute(bexdey); + dexbeyplus = Absolute(dexbey); + permanent = ((cexdeyplus + dexceyplus) * bezplus + + (dexbeyplus + bexdeyplus) * cezplus + + (bexceyplus + cexbeyplus) * dezplus) + * aeheight + + ((dexaeyplus + aexdeyplus) * cezplus + + (aexceyplus + cexaeyplus) * dezplus + + (cexdeyplus + dexceyplus) * aezplus) + * beheight + + ((aexbeyplus + bexaeyplus) * dezplus + + (bexdeyplus + dexbeyplus) * aezplus + + (dexaeyplus + aexdeyplus) * bezplus) + * ceheight + + ((bexceyplus + cexbeyplus) * aezplus + + (cexaeyplus + aexceyplus) * bezplus + + (aexbeyplus + bexaeyplus) * cezplus) + * deheight; + errbound = isperrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return orient4dadapt(pa, pb, pc, pd, pe, + aheight, bheight, cheight, dheight, eheight, permanent); +} diff --git a/contrib/Tetgen1.5/tetgen.cxx b/contrib/Tetgen1.5/tetgen.cxx index df7fb43790fc2aa8a3bfde8a9d711c8c53a74f79..0a66de153c7e2825e01c02a2f1fdb01be35381ed 100644 --- a/contrib/Tetgen1.5/tetgen.cxx +++ b/contrib/Tetgen1.5/tetgen.cxx @@ -2,17 +2,21 @@ // // // TetGen // // // -// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // +// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // // // // Version 1.5 // -// (March 27, 2013) // +// February 21, 2012 // // // -// Copyright (C) 2002--2013 // +// PRE-RELEASE TEST CODE. // +// PLEASE DO NOT DISTRIBUTE !! // +// PLEASE HELP ME TO IMPROVE IT !! // +// // +// Copyright (C) 2002--2012 // // Hang Si // // Research Group: Numerical Mathematics and Scientific Computing // // Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // // Mohrenstr. 39, 10117 Berlin, Germany // -// si@wias-berlin.de // +// Hang.Si@wias-berlin.de // // // // TetGen is freely available through the website: http://www.tetgen.org. // // It may be copied, modified, and redistributed for non-commercial use. // @@ -331,10 +335,6 @@ bool tetgenio::load_edge(char* filebasename) } edgelist[index++] = corner; } - if (numberofcorners == 10) { - // Skip an extra vertex (generated by a previous -o2 option). - stringptr = findnextnumber(stringptr); - } // Read the edge marker if it has. if (markers) { stringptr = findnextnumber(stringptr); @@ -419,12 +419,6 @@ bool tetgenio::load_face(char* filebasename) } trifacelist[index++] = corner; } - if (numberofcorners == 10) { - // Skip 3 extra vertices (generated by a previous -o2 option). - for (j = 0; j < 3; j++) { - stringptr = findnextnumber(stringptr); - } - } // Read the boundary marker if it exists. if (markers) { stringptr = findnextnumber(stringptr); @@ -633,6 +627,7 @@ bool tetgenio::load_var(char* filebasename) if (infile != (FILE *) NULL) { printf("Opening %s.\n", varfilename); } else { + // No such file. Ignore it without a message. return false; } @@ -745,6 +740,7 @@ bool tetgenio::load_mtr(char* filebasename) if (infile != (FILE *) NULL) { printf("Opening %s.\n", mtrfilename); } else { + // No such file. Return. return false; } @@ -1385,11 +1381,14 @@ bool tetgenio::load_off(char* filebasename) return false; } + // Check whether read all points if (iverts != nverts) { printf("Expected %d vertices, but read only %d vertices in file %s\n", nverts, iverts, infilename); return false; } + + // Check whether read all faces if (ifaces != nfaces) { printf("Expected %d faces, but read only %d faces in file %s\n", nfaces, ifaces, infilename); @@ -1604,11 +1603,14 @@ bool tetgenio::load_ply(char* filebasename) return false; } + // Check whether read all points if (iverts != nverts) { printf("Expected %d vertices, but read only %d vertices in file %s\n", nverts, iverts, infilename); return false; } + + // Check whether read all faces if (ifaces != nfaces) { printf("Expected %d faces, but read only %d faces in file %s\n", nfaces, ifaces, infilename); @@ -2387,7 +2389,6 @@ bool tetgenio::load_plc(char* filebasename, int object) } if (success) { - // Try to load the following files (.edge, .var, .mtr). load_edge(filebasename); load_var(filebasename); load_mtr(filebasename); @@ -2414,7 +2415,6 @@ bool tetgenio::load_tetmesh(char* filebasename, int object) success = load_tet(filebasename); } if (success) { - // Try to load the following files (.face, .edge, .vol). load_face(filebasename); load_edge(filebasename); load_vol(filebasename); @@ -2422,7 +2422,6 @@ bool tetgenio::load_tetmesh(char* filebasename, int object) } if (success) { - // Try to load the following files (.var, .mtr). load_var(filebasename); load_mtr(filebasename); } @@ -2903,22 +2902,16 @@ char* tetgenio::findnextnumber(char *string) void tetgenbehavior::syntax() { - printf(" tetgen [-pYq_Aa_mriO_S_T_XMwcdzfenvgkJBNEFICQVh] input_file\n"); + printf(" tetgen [-pYrq_a_AiS_T_dzfenvgKJBNEFICQVh] input_file\n"); printf(" -p Tetrahedralizes a piecewise linear complex (PLC).\n"); - printf(" -Y Preserves the input surface mesh (does not modify it).\n"); + printf(" -Y No splitting of input boundaries (facets and segments).\n"); + printf(" -r Reconstructs a previously generated mesh.\n"); printf(" -q Refines mesh (to improve mesh quality).\n"); - printf(" -A Assigns attributes to tetrahedra in different regions.\n"); printf(" -a Applies a maximum tetrahedron volume constraint.\n"); - printf(" -m Applies a mesh sizing function.\n"); - printf(" -r Reconstructs a previously generated mesh.\n"); - printf(" -i Inserts a list of additional points.\n"); - printf(" -O Specifies the level of mesh optimization.\n"); + printf(" -A Assigns attributes to tetrahedra in different regions.\n"); + printf(" -i Inserts a list of additional points into mesh.\n"); printf(" -S Specifies maximum number of added points.\n"); printf(" -T Sets a tolerance for coplanar test (default 1e-8).\n"); - printf(" -X Suppresses use of exact arithmetic.\n"); - printf(" -M No merge of coplanar facets or very close vertices.\n"); - printf(" -w Generates weighted Delaunay (regular) triangulation.\n"); - printf(" -c Retains the convex hull of the PLC.\n"); printf(" -d Detects self-intersections of facets of the PLC.\n"); printf(" -z Numbers all output items starting from zero.\n"); printf(" -f Outputs all faces to .face file.\n"); @@ -2926,12 +2919,12 @@ void tetgenbehavior::syntax() printf(" -n Outputs tetrahedra neighbors to .neigh file.\n"); printf(" -v Outputs Voronoi diagram to files.\n"); printf(" -g Outputs mesh to .mesh file for viewing by Medit.\n"); - printf(" -k Outputs mesh to .vtk file for viewing by Paraview.\n"); + printf(" -K Outputs mesh to .vtk file for viewing by Paraview.\n"); printf(" -J No jettison of unused vertices from output .node file.\n"); printf(" -B Suppresses output of boundary information.\n"); printf(" -N Suppresses output of .node file.\n"); printf(" -E Suppresses output of .ele file.\n"); - printf(" -F Suppresses output of .face and .edge file.\n"); + printf(" -F Suppresses output of .face file.\n"); printf(" -I Suppresses mesh iteration numbers.\n"); printf(" -C Checks the consistency of the final mesh.\n"); printf(" -Q Quiet: No terminal output except errors.\n"); @@ -2950,18 +2943,20 @@ void tetgenbehavior::usage() printf("TetGen\n"); printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay "); printf("Triangulator\n"); - printf("Version 1.5\n"); - printf("(March 27, 2013)\n"); + printf("Version 1.5 (February 21, 2012).\n"); printf("\n"); - printf("Copyright (C) 2002 - 2013\n"); + printf("Copyright (C) 2002 - 2012\n"); printf("Hang Si\n"); printf("Mohrenstr. 39, 10117 Berlin, Germany\n"); - printf("si@wias-berlin.de\n"); + printf("Hang.Si@wias-berlin.de\n"); printf("\n"); printf("What Can TetGen Do?\n"); printf("\n"); - printf(" TetGen generates Delaunay tetrahedralizations, constrained\n"); - printf(" Delaunay tetrahedralizations, and quality tetrahedral meshes.\n"); + printf(" TetGen generates exact Delaunay tetrahedralizations, exact\n"); + printf(" constrained Delaunay tetrahedralizations, and quality "); + printf("tetrahedral\n meshes. The latter are nicely graded and whose "); + printf("tetrahedra have\n radius-edge ratio bounded, thus are suitable "); + printf("for finite element and\n finite volume analysis.\n"); printf("\n"); printf("Command Line Syntax:\n"); printf("\n"); @@ -2982,21 +2977,19 @@ void tetgenbehavior::usage() printf("Examples of How to Use TetGen:\n"); printf("\n"); printf(" \'tetgen object\' reads vertices from object.node, and writes "); - printf("their\n Delaunay tetrahedralization to object.1.node, "); - printf("object.1.ele\n (tetrahedra), and object.1.face"); - printf(" (convex hull faces).\n"); + printf("their\n Delaunay tetrahedralization to object.1.node and "); + printf("object.1.ele.\n"); printf("\n"); printf(" \'tetgen -p object\' reads a PLC from object.poly or object."); printf("smesh (and\n possibly object.node) and writes its constrained "); - printf("Delaunay\n tetrahedralization to object.1.node, object.1.ele, "); - printf("object.1.face,\n"); - printf(" (boundary faces) and object.1.edge (boundary edges).\n"); + printf("Delaunay\n tetrahedralization to object.1.node, object.1.ele and "); + printf("object.1.face.\n"); printf("\n"); printf(" \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n"); printf(" object.smesh (and possibly object.node), generates a mesh "); printf("whose\n tetrahedra have radius-edge ratio smaller than 1.414 and "); printf("have volume\n of 0.1 or less, and writes the mesh to "); - printf("object.1.node, object.1.ele,\n object.1.face, and object.1.edge\n"); + printf("object.1.node, object.1.ele\n and object.1.face.\n"); printf("\n"); printf("Please send bugs/comments to Hang Si <si@wias-berlin.de>\n"); terminatetetgen(0); @@ -3011,6 +3004,19 @@ void tetgenbehavior::usage() // of a C/C++ program. They together represent the command line user invoked // // from an environment in which TetGen is running. // // // +// When TetGen is invoked from an environment. 'argc' is nonzero, switches // +// and input filename should be supplied as zero-terminated strings in // +// argv[0] through argv[argc - 1] and argv[0] shall be the name used to // +// invoke TetGen, i.e. "tetgen". Switches are previously started with a // +// dash '-' to identify them from the input filename. // +// // +// When TetGen is called from within another program. 'argc' is set to zero. // +// switches are given in one zero-terminated string (no previous dash is // +// required.), and 'argv' is a pointer points to this string. No input // +// filename is required (usually the input data has been directly created by // +// user in the 'tetgenio' structure). A default filename 'tetgen-tmpfile' // +// will be created for debugging output purpose. // +// // /////////////////////////////////////////////////////////////////////////////// bool tetgenbehavior::parse_commandline(int argc, char **argv) @@ -3018,6 +3024,7 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) int startindex; int increment; int meshnumber; + int scount, ocount; int i, j, k; char workstring[1024]; @@ -3031,9 +3038,12 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) strcpy(commandline, argv[0]); strcat(commandline, " "); } + + // Count the number of '-O' and '-o' be used. + scount = ocount = 0; for (i = startindex; i < argc; i++) { - // Remember the command line for output. + // Remember the command line switches. strcat(commandline, argv[i]); strcat(commandline, " "); if (startindex == 1) { @@ -3041,6 +3051,7 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) if (argv[i][0] != '-') { strncpy(infilename, argv[i], 1024 - 1); infilename[1024 - 1] = '\0'; + // Go to the next string directly. continue; } } @@ -3061,17 +3072,11 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) facet_ang_tol = (REAL) strtod(workstring, (char **) NULL); } } else if (argv[i][j] == 's') { - psc = 1; - } else if (argv[i][j] == 'Y') { - nobisect = 1; - if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { - nobisect_param = (argv[i][j + 1] - '0'); - j++; - } + psc = 1; } else if (argv[i][j] == 'r') { - refine = 1; + refine++; } else if (argv[i][j] == 'q') { - quality = 1; + quality++; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3082,47 +3087,30 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - minratio = (REAL) strtod(workstring, (char **) NULL); - } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { - j++; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; + if (quality == 1) { // -q# + minratio = (REAL) strtod(workstring, (char **) NULL); + } else if (quality == 2) { // -qq# mindihedral = (REAL) strtod(workstring, (char **) NULL); } } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { - j++; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - optmaxdihedral = (REAL) strtod(workstring, (char **) NULL); + } else if (argv[i][j] == 'm') { + metric++; + } else if (argv[i][j] == 'Y') { + nobisect = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e')) { + j++; + workstring[k] = argv[i][j]; + k++; } + workstring[k] = '\0'; + nobisect_param = (int) strtol(workstring, (char **) NULL, 0); } } else if (argv[i][j] == 'w') { weighted = 1; - if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { - weighted_param = (argv[i][j + 1] - '0'); - j++; - } - } else if (argv[i][j] == 'b') { - // -b(brio_threshold/brio_ratio/hilbert_limit/hilbert_order) - brio_hilbert = 1; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3133,66 +3121,8 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - brio_threshold = (int) strtol(workstring, (char **) &workstring, 0); - } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { - j++; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - brio_ratio = (REAL) strtod(workstring, (char **) NULL); - } - } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { - j++; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - hilbert_limit = (int) strtol(workstring, (char **) &workstring, 0); - } - } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { - j++; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - hilbert_order = (REAL) strtod(workstring, (char **) NULL); - } - } - if (brio_threshold == 0) { // -b0 - brio_hilbert = 0; // Turn off BRIO-Hilbert sorting. + weighted_param = (int) strtol(workstring, (char **) NULL, 0); } - if (brio_ratio >= 1.0) { // -b/1 - no_sort = 1; - brio_hilbert = 0; // Turn off BRIO-Hilbert sorting. - } - } else if (argv[i][j] == 'l') { - incrflip = 1; - } else if (argv[i][j] == 'L') { - flipinsert = 1; - } else if (argv[i][j] == 'm') { - metric = 1; } else if (argv[i][j] == 'a') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { @@ -3211,44 +3141,71 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) varvolume = 1; } } else if (argv[i][j] == 'A') { - regionattrib = 1; - } else if (argv[i][j] == 'D') { - conforming = 1; - if ((argv[i][j + 1] >= '1') && (argv[i][j + 1] <= '3')) { - reflevel = (argv[i][j + 1] - '1') + 1; - j++; + regionattrib++; + } else if (argv[i][j] == 'l') { + incrflip = 1; + // Check if a smallest edge length is given. + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + minedgelength = (REAL) strtod(workstring, (char **) NULL); + } + } else if (argv[i][j] == 'L') { + flipinsert++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + if (flipinsert == 1) { // -L + fliplinklevel = (int) strtol(workstring, (char **) NULL, 0); + } else if (flipinsert == 2) { // -LL + flipstarsize = (int) strtol(workstring, (char **) NULL, 0); + } + } + } else if (argv[i][j] == 'u') { + // Set the maximum btree node size, -u0 means do not use btree. + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + max_btreenode_size = (int) strtol(workstring, (char **) NULL, 0); + } + if (max_btreenode_size == 0) { + btree = 0; } + } else if (argv[i][j] == 'U') { + hilbertcurve = 1; + btree = 0; } else if (argv[i][j] == 'i') { insertaddpoints = 1; } else if (argv[i][j] == 'd') { diagnose = 1; } else if (argv[i][j] == 'c') { convex = 1; - } else if (argv[i][j] == 'M') { - nomergefacet = 1; - nomergevertex = 1; - if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) { - nomergefacet = (argv[i][j + 1] - '0'); - j++; - } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { - j++; - if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) { - nomergevertex = (argv[i][j + 1] - '0'); - j++; - } - } - } else if (argv[i][j] == 'X') { - if (argv[i][j + 1] == '1') { - nostaticfilter = 1; - j++; - } else { - noexact = 1; - } } else if (argv[i][j] == 'z') { zeroindex = 1; } else if (argv[i][j] == 'f') { - facesout++; + facesout = 1; } else if (argv[i][j] == 'e') { edgesout++; } else if (argv[i][j] == 'n') { @@ -3257,8 +3214,10 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) voroout = 1; } else if (argv[i][j] == 'g') { meditview = 1; - } else if (argv[i][j] == 'k') { + } else if (argv[i][j] == 'K') { vtkview = 1; + } else if (argv[i][j] == 'M') { + nomerge = 1; } else if (argv[i][j] == 'J') { nojettison = 1; } else if (argv[i][j] == 'B') { @@ -3267,6 +3226,10 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) nonodewritten = 1; } else if (argv[i][j] == 'E') { noelewritten = 1; + if (argv[i][j + 1] == '2') { + j++; + noelewritten = 2; + } } else if (argv[i][j] == 'F') { nofacewritten = 1; } else if (argv[i][j] == 'I') { @@ -3286,38 +3249,53 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) steinerleft = (int) strtol(workstring, (char **) NULL, 0); } } else if (argv[i][j] == 'o') { - if (argv[i][j + 1] == '2') { - order = 2; - j++; - } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { - j++; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - j++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - optmaxdihedral = (REAL) strtod(workstring, (char **) NULL); + ocount++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; } + workstring[k] = '\0'; + if (ocount == 1) { // -o# + optmaxdihedral = (REAL) strtod(workstring, (char **) NULL); + } else if (ocount == 2) { // -oo# + optminsmtdihed = (REAL) strtod(workstring, (char **) NULL); + } else if (ocount == 3) { // -ooo# + optminslidihed = (REAL) strtod(workstring, (char **) NULL); + } } } else if (argv[i][j] == 'O') { - if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { - optlevel = (argv[i][j + 1] - '0'); - j++; - } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { - j++; - if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '7')) { - optscheme = (argv[i][j + 1] - '0'); + scount++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + if (scount == 1) { // -O + optlevel = (int) strtol(workstring, (char **) NULL, 0); + } else if (scount == 2) { // -OO + optpasses = (int) strtol(workstring, (char **) NULL, 0); + } else if (scount == 3) { // -OOO + optmaxfliplevel = (int) strtol(workstring, (char **) NULL, 0); + } else if (scount == 4) { // -OOOO + delmaxfliplevel = (int) strtol(workstring, (char **) NULL, 0); + } else if (scount == 5) { // -OOOOO (5 Os) + optmaxflipstarsize = (int) strtol(workstring, (char **) NULL, 0); } } - } else if (argv[i][j] == 'T') { + } else if (argv[i][j] == 'D') { + conforming = 1; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3329,17 +3307,9 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - epsilon = (REAL) strtod(workstring, (char **) NULL); + reflevel = (int) strtol(workstring, (char **) NULL, 0); } - } else if (argv[i][j] == 'R') { - reversetetori = 1; - } else if (argv[i][j] == 'C') { - docheck++; - } else if (argv[i][j] == 'Q') { - quiet = 1; - } else if (argv[i][j] == 'V') { - verbose++; - } else if (argv[i][j] == 'x') { + } else if (argv[i][j] == 'T') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3351,14 +3321,14 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - tetrahedraperblock = (int) strtol(workstring, (char **) NULL, 0); - if (tetrahedraperblock > 8188) { - vertexperblock = tetrahedraperblock / 2; - shellfaceperblock = vertexperblock / 2; - } else { - tetrahedraperblock = 8188; - } + epsilon = (REAL) strtod(workstring, (char **) NULL); } + } else if (argv[i][j] == 'C') { + docheck++; + } else if (argv[i][j] == 'Q') { + quiet = 1; + } else if (argv[i][j] == 'V') { + verbose++; } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || (argv[i][j] == '?')) { usage(); @@ -3425,10 +3395,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) if (diagnose && !plc) { // -d plc = 1; } - if (plc && !quality && !nobisect) { // -p only - // Create a CDT, do not do mesh optimization. - optlevel = 0; - } // Detect improper combinations of switches. if (plc && refine) { @@ -3464,16 +3430,18 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) } } } - // No user-specified dihedral angle bound. Use default ones. - if (!quality) { - if (optmaxdihedral < 179.0) { - optmaxdihedral = 179.0; - } - if (optminsmtdihed < 179.999) { - optminsmtdihed = 179.999; - } - if (optminslidihed < 179.999) { - optminslidihed = 179.999; + if (ocount == 0) { + // No user-specified dihedral angle bound. Use default ones. + if (!quality) { + if (optmaxdihedral < 179.0) { + optmaxdihedral = 179.0; + } + if (optminsmtdihed < 179.999) { + optminsmtdihed = 179.999; + } + if (optminslidihed < 179.999) { + optminslidihed = 179.99; + } } } @@ -3529,25 +3497,18 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) // Initialize fast lookup tables for mesh maniplulation primitives. -int tetgenmesh::bondtbl[12][12] = {{0,},}; -int tetgenmesh::enexttbl[12] = {0,}; -int tetgenmesh::eprevtbl[12] = {0,}; -int tetgenmesh::enextesymtbl[12] = {0,}; -int tetgenmesh::eprevesymtbl[12] = {0,}; -int tetgenmesh::eorgoppotbl[12] = {0,}; -int tetgenmesh::edestoppotbl[12] = {0,}; -int tetgenmesh::fsymtbl[12][12] = {{0,},}; -int tetgenmesh::facepivot1[12] = {0,}; -int tetgenmesh::facepivot2[12][12] = {{0,},}; -int tetgenmesh::tsbondtbl[12][6] = {{0,},}; -int tetgenmesh::stbondtbl[12][6] = {{0,},}; -int tetgenmesh::tspivottbl[12][6] = {{0,},}; -int tetgenmesh::stpivottbl[12][6] = {{0,},}; - -// Table 'esymtbl' takes an directed edge (version) as input, returns the +int tetgenmesh::mod12[36] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + +int tetgenmesh::mod6[18] = {0, 1, 2, 3, 4, 5, + 0, 1, 2, 3, 4, 5, + 0, 1, 2, 3, 4, 5}; + +// Table 'edgepivot' takes an directed edge (version) as input, returns the // inversed edge (version) of it. -int tetgenmesh::esymtbl[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2}; +int tetgenmesh::edgepivot[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2}; // The following four tables give the 12 permutations of the set {0,1,2,3}. // An offset 4 is added to each element for a direct access of the points @@ -3564,11 +3525,6 @@ int tetgenmesh::oppopivot[12] = {4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}; int tetgenmesh::ver2edge[12] = {0, 1, 2, 3, 3, 5, 1, 5, 4, 0, 4, 2}; int tetgenmesh::edge2ver[ 6] = {0, 1, 2, 3, 8, 5}; -// Edge versions whose apex or opposite may be dummypoint. - -int tetgenmesh::epivot[12] = {4, 5, 2, 11, 4, 5, 2, 11, 4, 5, 2, 11}; - - // Table 'snextpivot' takes an edge version as input, returns the next edge // version in the same edge ring. @@ -3582,91 +3538,11 @@ int tetgenmesh::sorgpivot [6] = {3, 4, 4, 5, 5, 3}; int tetgenmesh::sdestpivot[6] = {4, 3, 5, 4, 3, 5}; int tetgenmesh::sapexpivot[6] = {5, 5, 3, 3, 4, 4}; -/////////////////////////////////////////////////////////////////////////////// -// // -// inittable() Initialize the look-up tables. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::inittables() -{ - int i, j; - - - // i = t1.ver; j = t2.ver; - for (i = 0; i < 12; i++) { - for (j = 0; j < 12; j++) { - bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12); - } - } - - - // i = t1.ver; j = t2.ver - for (i = 0; i < 12; i++) { - for (j = 0; j < 12; j++) { - fsymtbl[i][j] = (j + 12 - (i & 12)) % 12; - } - } - - - for (i = 0; i < 12; i++) { - facepivot1[i] = (esymtbl[i] & 3); - } - - for (i = 0; i < 12; i++) { - for (j = 0; j < 12; j++) { - facepivot2[i][j] = fsymtbl[esymtbl[i]][j]; - } - } - - for (i = 0; i < 12; i++) { - enexttbl[i] = (i + 4) % 12; - eprevtbl[i] = (i + 8) % 12; - } - - for (i = 0; i < 12; i++) { - enextesymtbl[i] = esymtbl[enexttbl[i]]; - eprevesymtbl[i] = esymtbl[eprevtbl[i]]; - } - - for (i = 0; i < 12; i++) { - eorgoppotbl [i] = eprevtbl[esymtbl[enexttbl[i]]]; - edestoppotbl[i] = enexttbl[esymtbl[eprevtbl[i]]]; - } - - int soffset, toffset; +// Edge versions whose apex or opposite may be dummypoint. - // i = t.ver, j = s.shver - for (i = 0; i < 12; i++) { - for (j = 0; j < 6; j++) { - if ((j & 1) == 0) { - soffset = (6 - ((i & 12) >> 1)) % 6; - toffset = (12 - ((j & 6) << 1)) % 12; - } else { - soffset = (i & 12) >> 1; - toffset = (j & 6) << 1; - } - tsbondtbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6); - stbondtbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12); - } - } +int tetgenmesh::epivot[4] = {4, 5, 2, 11}; - // i = t.ver, j = s.shver - for (i = 0; i < 12; i++) { - for (j = 0; j < 6; j++) { - if ((j & 1) == 0) { - soffset = (i & 12) >> 1; - toffset = (j & 6) << 1; - } else { - soffset = (6 - ((i & 12) >> 1)) % 6; - toffset = (12 - ((j & 6) << 1)) % 12; - } - tspivottbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6); - stpivottbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12); - } - } -} /////////////////////////////////////////////////////////////////////////////// // // @@ -3700,7 +3576,6 @@ void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) log2objectsperblock = log2objperblk; // Compute the number of objects in each block. objectsperblock = ((int) 1) << log2objectsperblock; - objectsperblockmark = objectsperblock - 1; // No memory has been allocated. totalmemory = 0l; @@ -3878,7 +3753,6 @@ int tetgenmesh::arraypool::newindex(void **newptr) return newindex; } - /////////////////////////////////////////////////////////////////////////////// // // // memorypool() The constructors of memorypool. // @@ -3892,6 +3766,7 @@ tetgenmesh::memorypool::memorypool() deaditemstack = (void *) NULL; pathblock = (void **) NULL; pathitem = (void *) NULL; + itemwordtype = POINTER; alignbytes = 0; itembytes = itemwords = 0; itemsperblock = 0; @@ -3900,10 +3775,10 @@ tetgenmesh::memorypool::memorypool() pathitemsleft = 0; } -tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, - int alignment) +tetgenmesh::memorypool:: +memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment) { - poolinit(bytecount, itemcount, wsize, alignment); + poolinit(bytecount, itemcount, wtype, alignment); } /////////////////////////////////////////////////////////////////////////////// @@ -3937,9 +3812,14 @@ tetgenmesh::memorypool::~memorypool() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize, - int alignment) +void tetgenmesh::memorypool:: +poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) { + int wordsize; + + // Initialize values in the pool. + itemwordtype = wtype; + wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL); // Find the proper alignment, which must be at least as large as: // - The parameter `alignment'. // - The primary word type, to avoid unaligned accesses. @@ -4049,7 +3929,11 @@ void* tetgenmesh::memorypool::alloc() // Allocate a new item. newitem = nextitem; // Advance `nextitem' pointer to next free item in block. - nextitem = (void *) ((uintptr_t) nextitem + itembytes); + if (itemwordtype == POINTER) { + nextitem = (void *) ((void **) nextitem + itemwords); + } else { + nextitem = (void *) ((REAL *) nextitem + itemwords); + } unallocateditems--; maxitems++; } @@ -4133,18 +4017,25 @@ void* tetgenmesh::memorypool::traverse() } newitem = pathitem; // Find the next item in the block. - pathitem = (void *) ((uintptr_t) pathitem + itembytes); + if (itemwordtype == POINTER) { + pathitem = (void *) ((void **) pathitem + itemwords); + } else { + pathitem = (void *) ((REAL *) pathitem + itemwords); + } pathitemsleft--; return newitem; } + + /////////////////////////////////////////////////////////////////////////////// // // // makeindex2pointmap() Create a map from index to vertices. // // // // 'idx2verlist' returns the created map. Traverse all vertices, a pointer // // to each vertex is set into the array. The pointer to the first vertex is // -// saved in 'idx2verlist[in->firstnumber]'. // +// saved in 'idx2verlist[0]'. Don't forget to minus 'in->firstnumber' when // +// to get the vertex form its index. // // // /////////////////////////////////////////////////////////////////////////////// @@ -4161,13 +4052,14 @@ void tetgenmesh::makeindex2pointmap(point*& idx2verlist) points->traversalinit(); pointloop = pointtraverse(); - idx = in->firstnumber; + idx = in->firstnumber;; while (pointloop != (point) NULL) { idx2verlist[idx++] = pointloop; pointloop = pointtraverse(); } } + /////////////////////////////////////////////////////////////////////////////// // // // makesubfacemap() Create a map from vertex to subfaces incident at it. // @@ -4271,13 +4163,15 @@ void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) // dead tetrahedra when traversing the list of all tetrahedra. dyingtetrahedron[4] = (tetrahedron) NULL; - // Dealloc the space to subfaces/subsegments. - if (dyingtetrahedron[8] != NULL) { - tet2segpool->dealloc((shellface *) dyingtetrahedron[8]); - } - if (dyingtetrahedron[9] != NULL) { - tet2subpool->dealloc((shellface *) dyingtetrahedron[9]); - } + //if (b->plc || b->refine) { //if (b->useshelles) { + // Dealloc the space to subfaces/subsegments. + if (dyingtetrahedron[8] != NULL) { + tet2segpool->dealloc((shellface *) dyingtetrahedron[8]); + } + if (dyingtetrahedron[9] != NULL) { + tet2subpool->dealloc((shellface *) dyingtetrahedron[9]); + } + //} tetrahedrons->dealloc((void *) dyingtetrahedron); } @@ -4350,6 +4244,38 @@ tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) return newshellface; } +/////////////////////////////////////////////////////////////////////////////// +// // +// badfacedealloc() Deallocate space for a badface, marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::badfacedealloc(memorypool *pool, badface *dying) +{ + // Set badface's forg to NULL. This makes it possible to detect dead + // ones when traversing the list of all items. + dying->forg = (point) NULL; + pool->dealloc((void *) dying); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// badfacetraverse() Traverse the pools, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool) +{ + badface *newsh; + + do { + newsh = (badface *) pool->traverse(); + if (newsh == (badface *) NULL) { + return (badface *) NULL; + } + } while (newsh->forg == (point) NULL); // Skip dead ones. + return newsh; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -4393,7 +4319,6 @@ tetgenmesh::point tetgenmesh::pointtraverse() void tetgenmesh::maketetrahedron(triface *newtet) { newtet->tet = (tetrahedron *) tetrahedrons->alloc(); - // Initialize the four adjoining tetrahedra to be "outer space". newtet->tet[0] = NULL; newtet->tet[1] = NULL; @@ -4407,9 +4332,9 @@ void tetgenmesh::maketetrahedron(triface *newtet) // No attached segments and sbfaces yet. newtet->tet[8] = NULL; newtet->tet[9] = NULL; - // Initialize the marker (clear all flags). + // Initialize the marker (for flags). setelemmarker(newtet->tet, 0); - for (int i = 0; i < numelemattrib; i++) { + for (int i = 0; i < in->numberoftetrahedronattributes; i++) { setelemattribute(newtet->tet, i, 0.0); } if (b->varvolume) { @@ -4430,7 +4355,6 @@ void tetgenmesh::maketetrahedron(triface *newtet) void tetgenmesh::makeshellface(memorypool *pool, face *newface) { newface->sh = (shellface *) pool->alloc(); - // No adjointing subfaces. newface->sh[0] = NULL; newface->sh[1] = NULL; @@ -4458,7 +4382,7 @@ void tetgenmesh::makeshellface(memorypool *pool, face *newface) setshellmark(*newface, 0); // Set the default face type. setshelltype(*newface, NSHARP); - + // Initialize the version to be Zero. newface->shver = 0; } @@ -4470,14 +4394,9 @@ void tetgenmesh::makeshellface(memorypool *pool, face *newface) void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) { - int i; + int ptmark, i; *pnewpoint = (point) points->alloc(); - - // Initialize the point attributes. - for (i = 0; i < numpointattrib; i++) { - (*pnewpoint)[3 + i] = 0.0; - } // Initialize the metric tensor. for (i = 0; i < sizeoftensor; i++) { (*pnewpoint)[pointmtrindex + i] = 0.0; @@ -4492,11 +4411,20 @@ void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) } } // Initialize the point marker (starting from in->firstnumber). - setpointmark(*pnewpoint, (int) (points->items) - (!in->firstnumber)); - // Clear all flags. - ((int *) (*pnewpoint))[pointmarkindex + 1] = 0; - // Initialize (set) the point type. + ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1); + setpointmark(*pnewpoint, ptmark); + // Initialize the point type. setpointtype(*pnewpoint, vtype); + // Clear the point flags. + puninfect(*pnewpoint); + punmarktest(*pnewpoint); + if (b->psc) { + // Initialize the u,v coordinates. + setpointgeomuv(*pnewpoint, 0, 0); + setpointgeomuv(*pnewpoint, 1, 0); + // Initialize the geometry tag. + setpointgeomtag(*pnewpoint, 0); + } } /////////////////////////////////////////////////////////////////////////////// @@ -4512,36 +4440,11 @@ void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) void tetgenmesh::initializepools() { - int pointsize = 0, elesize = 0, shsize = 0; - int i; + enum memorypool::wordtype wtype; + int pointsize, elesize, shsize; if (b->verbose) { printf(" Initializing memorypools.\n"); - printf(" tetrahedron per block: %d.\n", b->tetrahedraperblock); - } - - inittables(); - - // There are three input point lists available, which are in, addin, - // and bgm->in. These point lists may have different number of - // attributes. Decide the maximum number. - numpointattrib = in->numberofpointattributes; - if (bgm != NULL) { - if (bgm->in->numberofpointattributes > numpointattrib) { - numpointattrib = bgm->in->numberofpointattributes; - } - } - if (addin != NULL) { - if (addin->numberofpointattributes > numpointattrib) { - numpointattrib = addin->numberofpointattributes; - } - } - if (b->weighted || b->flipinsert) { // -w or -L. - // The internal number of point attribute needs to be at least 1 - // (for storing point weights). - if (numpointattrib == 0) { - numpointattrib = 1; - } } // Default varconstraint = 0; @@ -4549,16 +4452,17 @@ void tetgenmesh::initializepools() checkconstraints = 1; } + // Each vertex has three coordinates plus a weight, hence 4 REALs. // The index within each point at which its metric tensor is found. - // Each vertex has three coordinates. if (b->psc) { - // '-s' option (PSC), the u,v coordinates are provided. - pointmtrindex = 5 + numpointattrib; + // For '-s' option (PSC), the u,v coordinates are provided. It is + // saved directly after the list of point attributes. + pointmtrindex = 6 + in->numberofpointattributes; } else { - pointmtrindex = 3 + numpointattrib; + pointmtrindex = 4 + in->numberofpointattributes; } // The index within each point at which its u, v coordinates are found. - pointparamindex = 3 + (numpointattrib > 0); + pointparamindex = pointmtrindex - 2; // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file). if (b->metric) { // Decide the size (1, 3, or 6) of the metric tensor. @@ -4587,11 +4491,19 @@ void tetgenmesh::initializepools() // - a pointer to a parent point, read by point2ppt()). // - a pointer to a subface or segment, read by point2sh(); if (b->metric && (bgm != (tetgenmesh *) NULL)) { - // Increase one pointer into the background mesh, point2bgmtet(). + // Increase one pointer to into the background mesh, point2bgmtet(). pointsize = (point2simindex + 4) * sizeof(tetrahedron); } else { pointsize = (point2simindex + 3) * sizeof(tetrahedron); } + // The index within each point at which a pbc point is found. + point2pbcptindex = (pointsize + sizeof(tetrahedron) - 1) + / sizeof(tetrahedron); + if (checkpbcs) { + // Increase the size by one pointer to a corresponding pbc point, + // read by point2pbcpt(). + pointsize = (point2pbcptindex + 1) * sizeof(tetrahedron); + } } else { // Increase the point size by two pointer, which are: // - a pointer to a tet, read by point2tet(); @@ -4605,10 +4517,14 @@ void tetgenmesh::initializepools() // - an integer for boundary marker; // - an integer for vertex type; // - an integer for geometry tag (optional, -s option). + //pointsize = (pointmarkindex + 2)*sizeof(int); // Wrong for 64 bit system. pointsize = (pointmarkindex + 2 + (b->psc ? 1 : 0)) * sizeof(tetrahedron); + // Decide the wordtype used in vertex pool. + wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ? + memorypool::FLOATINGPOINT : memorypool::POINTER; // Initialize the pool of vertices. - points = new memorypool(pointsize, b->vertexperblock, sizeof(REAL), 0); + points = new memorypool(pointsize, b->vertexperblock, wtype, 0); if (b->verbose) { printf(" Size of a point: %d bytes.\n", points->itembytes); @@ -4616,32 +4532,7 @@ void tetgenmesh::initializepools() // Initialize the infinite vertex. dummypoint = (point) new char[pointsize]; - // Initialize all fields of this point. - dummypoint[0] = 0.0; - dummypoint[1] = 0.0; - dummypoint[2] = 0.0; - for (i = 0; i < numpointattrib; i++) { - dummypoint[3 + i] = 0.0; - } - // Initialize the metric tensor. - for (i = 0; i < sizeoftensor; i++) { - dummypoint[pointmtrindex + i] = 0.0; - } - setpoint2tet(dummypoint, NULL); - setpoint2ppt(dummypoint, NULL); - if (b->plc || b->psc || b->refine) { - // Initialize the point-to-simplex field. - setpoint2sh(dummypoint, NULL); - if (b->metric && (bgm != NULL)) { - setpoint2bgmtet(dummypoint, NULL); - } - } - // Initialize the point marker (starting from in->firstnumber). - setpointmark(dummypoint, -1); // The unique marker for dummypoint. - // Clear all flags. - ((int *) (dummypoint))[pointmarkindex + 1] = 0; - // Initialize (set) the point type. - setpointtype(dummypoint, UNUSEDVERTEX); // Does not matter. + setpointmark(dummypoint, -1); // The number of bytes occupied by a tetrahedron is varying by the user- // specified options. The contents of the first 12 pointers are listed @@ -4667,28 +4558,26 @@ void tetgenmesh::initializepools() assert((sizeof(tetrahedron) % sizeof(int)) == 0); elemmarkerindex = (elesize - sizeof(tetrahedron)) / sizeof(int); - // The actual number of element attributes. Note that if the - // `b->regionattrib' flag is set, an additional attribute will be added. - numelemattrib = in->numberoftetrahedronattributes + (b->regionattrib > 0); - // The index within each element at which its attributes are found, where // the index is measured in REALs. elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL); // The index within each element at which the maximum voulme bound is - // found, where the index is measured in REALs. - volumeboundindex = elemattribindex + numelemattrib; + // found, where the index is measured in REALs. Note that if the + // `b->regionattrib' flag is set, an additional attribute will be added. + volumeboundindex = elemattribindex + in->numberoftetrahedronattributes + + (b->regionattrib > 0); // If element attributes or an constraint are needed, increase the number // of bytes occupied by an element. if (b->varvolume) { elesize = (volumeboundindex + 1) * sizeof(REAL); - } else if (numelemattrib > 0) { + } else if (in->numberoftetrahedronattributes + b->regionattrib > 0) { elesize = volumeboundindex * sizeof(REAL); } // Having determined the memory size of an element, initialize the pool. - tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void *), - 16); + tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, + memorypool::POINTER, 16); if (b->verbose) { printf(" Size of a tetrahedron: %d (%d) bytes.\n", elesize, @@ -4715,12 +4604,13 @@ void tetgenmesh::initializepools() shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int); // Increase the number of bytes by two or three integers, one for facet // marker, one for shellface type, and optionally one for pbc group. - shsize = (shmarkindex + 2) * sizeof(shellface); + shsize = (shmarkindex + 2 + checkpbcs) * sizeof(shellface); // Initialize the pool of subfaces. Each subface record is eight-byte // aligned so it has room to store an edge version (from 0 to 5) in // the least three bits. - subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); + subfaces = new memorypool(shsize, b->shellfaceperblock, + memorypool::POINTER, 8); if (b->verbose) { printf(" Size of a shellface: %d (%d) bytes.\n", shsize, @@ -4729,37 +4619,40 @@ void tetgenmesh::initializepools() // Initialize the pool of subsegments. The subsegment's record is same // with subface. - subsegs = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); + subsegs = new memorypool(shsize, b->shellfaceperblock, + memorypool::POINTER, 8); // Initialize the pool for tet-subseg connections. tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock, - sizeof(void *), 0); + memorypool::POINTER, 0); // Initialize the pool for tet-subface connections. tet2subpool = new memorypool(4 * sizeof(shellface), b->shellfaceperblock, - sizeof(void *), 0); + memorypool::POINTER, 0); // Initialize arraypools for segment & facet recovery. subsegstack = new arraypool(sizeof(face), 10); subfacstack = new arraypool(sizeof(face), 10); subvertstack = new arraypool(sizeof(point), 8); + suppsteinerptlist = new arraypool(sizeof(point), 8); - // Initialize arraypools for surface point insertion/deletion. + // Initialize arraypools for surface Bowyer-Watson algorithm. caveshlist = new arraypool(sizeof(face), 8); caveshbdlist = new arraypool(sizeof(face), 8); cavesegshlist = new arraypool(sizeof(face), 4); cavetetshlist = new arraypool(sizeof(face), 8); cavetetseglist = new arraypool(sizeof(face), 8); + caveencshlist = new arraypool(sizeof(face), 8); caveencseglist = new arraypool(sizeof(face), 8); } - // Initialize the pools for flips. - flippool = new memorypool(sizeof(badface), 1024, sizeof(void *), 0); + // Initialize the pool for flips. + flippool = new memorypool(sizeof(badface), 1024, memorypool::POINTER, 0); unflipqueue = new arraypool(sizeof(badface), 10); - // Initialize the arraypools for point insertion. + // Initialize the arraypools for Bowyer-Watson algorithm. cavetetlist = new arraypool(sizeof(triface), 10); cavebdrylist = new arraypool(sizeof(triface), 10); caveoldtetlist = new arraypool(sizeof(triface), 10); @@ -4775,147 +4668,8 @@ void tetgenmesh::initializepools() //// //// // PI is the ratio of a circle's circumference to its diameter. -REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; - -/////////////////////////////////////////////////////////////////////////////// -// // -// insphere_s() Insphere test with symbolic perturbation. // -// // -// Given four points pa, pb, pc, and pd, test if the point pe lies inside or // -// outside the circumscirbed sphere of the four points. // -// // -// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // -// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // -// points pa, pb, and pc. Otherwise, the returned sign is flipped. // -// // -// Return a positive value (> 0) if pe lies inside, a negative value (< 0) // -// if pe lies outside the sphere, the returned value will not be zero. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) -{ - REAL sign; - - sign = insphere(pa, pb, pc, pd, pe); - if (sign != 0.0) { - return sign; - } - - // Symbolic perturbation. - point pt[5], swappt; - REAL oriA, oriB; - int swaps, count; - int n, i; - - pt[0] = pa; - pt[1] = pb; - pt[2] = pc; - pt[3] = pd; - pt[4] = pe; - - // Sort the five points such that their indices are in the increasing - // order. An optimized bubble sort algorithm is used, i.e., it has - // the worst case O(n^2) runtime, but it is usually much faster. - swaps = 0; // Record the total number of swaps. - n = 5; - do { - count = 0; - n = n - 1; - for (i = 0; i < n; i++) { - if (pointmark(pt[i]) > pointmark(pt[i+1])) { - swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt; - count++; - } - } - swaps += count; - } while (count > 0); // Continue if some points are swapped. - - oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); - if (oriA != 0.0) { - // Flip the sign if there are odd number of swaps. - if ((swaps % 2) != 0) oriA = -oriA; - return oriA; - } - - oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); - assert(oriB != 0.0); // SELF_CHECK - // Flip the sign if there are odd number of swaps. - if ((swaps % 2) != 0) oriB = -oriB; - return oriB; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// orient4d_s() 4d orientation test with symbolic perturbation. // -// // -// Given four lifted points pa', pb', pc', and pd' in R^4,test if the lifted // -// point pe' in R^4 lies below or above the hyperplance passing through the // -// four points pa', pb', pc', and pd'. // -// // -// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // -// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // -// the points pa, pb, and pc. Otherwise, the returned sign is flipped. // -// // -// Return a positive value (> 0) if pe' lies below, a negative value (< 0) // -// if pe' lies above the hyperplane, the returned value should not be zero. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, - REAL aheight, REAL bheight, REAL cheight, - REAL dheight, REAL eheight) -{ - REAL sign; - - sign = orient4d(pa, pb, pc, pd, pe, - aheight, bheight, cheight, dheight, eheight); - if (sign != 0.0) { - return sign; - } - - // Symbolic perturbation. - point pt[5], swappt; - REAL oriA, oriB; - int swaps, count; - int n, i; - - pt[0] = pa; - pt[1] = pb; - pt[2] = pc; - pt[3] = pd; - pt[4] = pe; - - // Sort the five points such that their indices are in the increasing - // order. An optimized bubble sort algorithm is used, i.e., it has - // the worst case O(n^2) runtime, but it is usually much faster. - swaps = 0; // Record the total number of swaps. - n = 5; - do { - count = 0; - n = n - 1; - for (i = 0; i < n; i++) { - if (pointmark(pt[i]) > pointmark(pt[i+1])) { - swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt; - count++; - } - } - swaps += count; - } while (count > 0); // Continue if some points are swapped. - oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); - if (oriA != 0.0) { - // Flip the sign if there are odd number of swaps. - if ((swaps % 2) != 0) oriA = -oriA; - return oriA; - } - - oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); - assert(oriB != 0.0); // SELF_CHECK - // Flip the sign if there are odd number of swaps. - if ((swaps % 2) != 0) oriB = -oriB; - return oriB; -} +REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; /////////////////////////////////////////////////////////////////////////////// // // @@ -4939,10 +4693,6 @@ REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, // // /////////////////////////////////////////////////////////////////////////////// -#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) - -#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) - int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, point R, int level, int *types, int *pos) { @@ -4959,14 +4709,14 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, REAL n[3], len; // Calculate a lift point, saved in dummypoint. facenormal(A, B, C, n, 1, NULL); - len = sqrt(dot(n, n)); + len = sqrt(DOT(n, n)); if (len != 0) { n[0] /= len; n[1] /= len; n[2] /= len; - len = distance(A, B); - len += distance(B, C); - len += distance(C, A); + len = DIST(A, B); + len += DIST(B, C); + len += DIST(C, A); len /= 3.0; R = abovept; //dummypoint; R[0] = A[0] + len * n[0]; @@ -4987,6 +4737,7 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, sB = orient3d(P, Q, R, B); sC = orient3d(P, Q, R, C); + triedgcopcount++; if (sA < 0) { if (sB < 0) { @@ -5570,6 +5321,7 @@ int tetgenmesh::tri_edge_tail(point A,point B,point C,point P,point Q,point R, REAL s1, s2, s3; int z1; + triedgcount++; if (sP < 0) { if (sQ < 0) { // (--) disjoint @@ -6064,9 +5816,6 @@ void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) // Return a negative value if pd is inside the circumcircle of the triangle // // pa, pb, and pc. // // // -// IMPORTANT: It assumes that [a,b] is the common edge, i.e., the two input // -// triangles are [a,b,c] and [b,a,d]. // -// // /////////////////////////////////////////////////////////////////////////////// REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) @@ -6076,19 +5825,19 @@ REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) // Calculate the areas of the two triangles [a, b, c] and [b, a, d]. facenormal(pa, pb, pc, n1, 1, NULL); - area2[0] = dot(n1, n1); + area2[0] = DOT(n1, n1); facenormal(pb, pa, pd, n2, 1, NULL); - area2[1] = dot(n2, n2); + area2[1] = DOT(n2, n2); if (area2[0] > area2[1]) { // Choose [a, b, c] as the base triangle. circumsphere(pa, pb, pc, NULL, c, &r); - d = distance(c, pd); + d = DIST(c, pd); } else { // Choose [b, a, d] as the base triangle. if (area2[1] > 0) { circumsphere(pb, pa, pd, NULL, c, &r); - d = distance(c, pc); + d = DIST(c, pc); } else { // The four points are collinear. This case only happens on the boundary. return 0; // Return "not inside". @@ -6105,90 +5854,239 @@ REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) /////////////////////////////////////////////////////////////////////////////// // // -// facenormal() Calculate the normal of the face. // +// insphere_s() Insphere test with symbolic perturbation. // // // -// The normal of the face abc can be calculated by the cross product of 2 of // -// its 3 edge vectors. A better choice of two edge vectors will reduce the // -// numerical error during the calculation. Burdakov proved that the optimal // -// basis problem is equivalent to the minimum spanning tree problem with the // -// edge length be the functional, see Burdakov, "A greedy algorithm for the // -// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two // -// short edges in abc are chosen for the calculation. // +// Given four points pa, pb, pc, and pd, test if the point pe lies inside or // +// outside the circumscirbed sphere of the four points. // // // -// If 'lav' is not NULL and if 'pivot' is set, the average edge length of // -// the edges of the face [a,b,c] is returned. // +// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // +// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // +// points pa, pb, and pc. Otherwise, the returned sign is flipped. // +// // +// Return a positive value (> 0) if pe lies inside, a negative value (< 0) // +// if pe lies outside the sphere, the returned value will not be zero. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, - REAL* lav) +REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) { - REAL v1[3], v2[3], v3[3], *pv1, *pv2; - REAL L1, L2, L3; + REAL sign; - v1[0] = pb[0] - pa[0]; // edge vector v1: a->b - v1[1] = pb[1] - pa[1]; - v1[2] = pb[2] - pa[2]; - v2[0] = pa[0] - pc[0]; // edge vector v2: c->a - v2[1] = pa[1] - pc[1]; - v2[2] = pa[2] - pc[2]; + inspherecount++; - // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal). - if (pivot > 0) { - // Choose edge vectors by Burdakov's algorithm. - v3[0] = pc[0] - pb[0]; // edge vector v3: b->c - v3[1] = pc[1] - pb[1]; - v3[2] = pc[2] - pb[2]; - L1 = dot(v1, v1); - L2 = dot(v2, v2); - L3 = dot(v3, v3); - // Sort the three edge lengths. - if (L1 < L2) { - if (L2 < L3) { - pv1 = v1; pv2 = v2; // n = v1 x (-v2). - } else { - pv1 = v3; pv2 = v1; // n = v3 x (-v1). - } - } else { - if (L1 < L3) { - pv1 = v1; pv2 = v2; // n = v1 x (-v2). - } else { - pv1 = v2; pv2 = v3; // n = v2 x (-v3). - } - } - if (lav) { - // return the average edge length. - *lav = (sqrt(L1) + sqrt(L2) + sqrt(L3)) / 3.0; - } - } else { - pv1 = v1; pv2 = v2; // n = v1 x (-v2). + sign = insphere(pa, pb, pc, pd, pe); + if (sign != 0.0) { + return sign; } - // Calculate the face normal. - cross(pv1, pv2, n); - // Inverse the direction; - n[0] = -n[0]; - n[1] = -n[1]; - n[2] = -n[2]; -} + insphere_sos_count++; -/////////////////////////////////////////////////////////////////////////////// -// // -// shortdistance() Returns the shortest distance from point p to a line // -// defined by two points e1 and e2. // -// // -// First compute the projection length l_p of the vector v1 = p - e1 along // -// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the // -// shortest distance. // -// // -// This routine allows that p is collinear with the line. In this case, the // -// return value is zero. The two points e1 and e2 should not be identical. // -// // -/////////////////////////////////////////////////////////////////////////////// + // Symbolic perturbation. + point pt[5], swappt; + REAL oriA, oriB; + int swaps, count; + int n, i; -REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) -{ - REAL v1[3], v2[3]; + pt[0] = pa; + pt[1] = pb; + pt[2] = pc; + pt[3] = pd; + pt[4] = pe; + + // Sort the five points such that their indices are in the increasing + // order. An optimized bubble sort algorithm is used, i.e., it has + // the worst case O(n^2) runtime, but it is usually much faster. + swaps = 0; // Record the total number of swaps. + n = 5; + do { + count = 0; + n = n - 1; + for (i = 0; i < n; i++) { + if (pointmark(pt[i]) > pointmark(pt[i+1])) { + swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt; + count++; + } + } + swaps += count; + } while (count > 0); // Continue if some points are swapped. + + oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); + if (oriA != 0.0) { + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriA = -oriA; + return oriA; + } + + oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); + assert(oriB != 0.0); // SELF_CHECK + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// orient4d_s() 4d orientation test with symbolic perturbation. // +// // +// Given four lifted points pa', pb', pc', and pd' in R^4,test if the lifted // +// point pe' in R^4 lies below or above the hyperplance passing through the // +// four points pa', pb', pc', and pd'. // +// // +// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // +// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // +// the points pa, pb, and pc. Otherwise, the returned sign is flipped. // +// // +// Return a positive value (> 0) if pe' lies below, a negative value (< 0) // +// if pe' lies above the hyperplane, the returned value should not be zero. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, + REAL aheight, REAL bheight, REAL cheight, + REAL dheight, REAL eheight) +{ + REAL sign; + + inspherecount++; + + sign = orient4d(pa, pb, pc, pd, pe, + aheight, bheight, cheight, dheight, eheight); + if (sign != 0.0) { + return sign; + } + + insphere_sos_count++; + + // Symbolic perturbation. + point pt[5], swappt; + REAL oriA, oriB; + int swaps, count; + int n, i; + + pt[0] = pa; + pt[1] = pb; + pt[2] = pc; + pt[3] = pd; + pt[4] = pe; + + // Sort the five points such that their indices are in the increasing + // order. An optimized bubble sort algorithm is used, i.e., it has + // the worst case O(n^2) runtime, but it is usually much faster. + swaps = 0; // Record the total number of swaps. + n = 5; + do { + count = 0; + n = n - 1; + for (i = 0; i < n; i++) { + if (pointmark(pt[i]) > pointmark(pt[i+1])) { + swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt; + count++; + } + } + swaps += count; + } while (count > 0); // Continue if some points are swapped. + + oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); + if (oriA != 0.0) { + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriA = -oriA; + return oriA; + } + + oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); + assert(oriB != 0.0); // SELF_CHECK + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// facenormal() Calculate the normal of the face. // +// // +// The normal of the face abc can be calculated by the cross product of 2 of // +// its 3 edge vectors. A better choice of two edge vectors will reduce the // +// numerical error during the calculation. Burdakov proved that the optimal // +// basis problem is equivalent to the minimum spanning tree problem with the // +// edge length be the functional, see Burdakov, "A greedy algorithm for the // +// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two // +// short edges in abc are chosen for the calculation. // +// // +// If 'lav' is not NULL and if 'pivot' is set, the average edge length of // +// the edges of the face [a,b,c] is returned. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, + REAL* lav) +{ + REAL v1[3], v2[3], v3[3], *pv1, *pv2; + REAL L1, L2, L3; + + v1[0] = pb[0] - pa[0]; // edge vector v1: a->b + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + v2[0] = pa[0] - pc[0]; // edge vector v2: c->a + v2[1] = pa[1] - pc[1]; + v2[2] = pa[2] - pc[2]; + + // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal). + if (pivot > 0) { + // Choose edge vectors by Burdakov's algorithm. + v3[0] = pc[0] - pb[0]; // edge vector v3: b->c + v3[1] = pc[1] - pb[1]; + v3[2] = pc[2] - pb[2]; + L1 = DOT(v1, v1); + L2 = DOT(v2, v2); + L3 = DOT(v3, v3); + // Sort the three edge lengths. + if (L1 < L2) { + if (L2 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v3; pv2 = v1; // n = v3 x (-v1). + } + } else { + if (L1 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v2; pv2 = v3; // n = v2 x (-v3). + } + } + if (lav) { + // return the average edge length. + *lav = (sqrt(L1) + sqrt(L2) + sqrt(L3)) / 3.0; + } + } else { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } + + // Calculate the face normal. + CROSS(pv1, pv2, n); + // Inverse the direction; + n[0] = -n[0]; + n[1] = -n[1]; + n[2] = -n[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shortdistance() Returns the shortest distance from point p to a line // +// defined by two points e1 and e2. // +// // +// First compute the projection length l_p of the vector v1 = p - e1 along // +// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the // +// shortest distance. // +// // +// This routine allows that p is collinear with the line. In this case, the // +// return value is zero. The two points e1 and e2 should not be identical. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) +{ + REAL v1[3], v2[3]; REAL len, l_p; v1[0] = e2[0] - e1[0]; @@ -6199,8 +6097,9 @@ REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) v2[2] = p[2] - e1[2]; len = sqrt(dot(v1, v1)); +#ifdef SELF_CHECK assert(len != 0.0); - +#endif v1[0] /= len; v1[1] /= len; v1[2] /= len; @@ -6209,6 +6108,7 @@ REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) return sqrt(dot(v2, v2) - l_p * l_p); } + /////////////////////////////////////////////////////////////////////////////// // // // triarea() Return the area of a triangle. // @@ -6232,27 +6132,6 @@ REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc) return 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c]. } -REAL tetgenmesh::orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd) -{ - REAL adx, bdx, cdx; - REAL ady, bdy, cdy; - REAL adz, bdz, cdz; - - adx = pa[0] - pd[0]; - bdx = pb[0] - pd[0]; - cdx = pc[0] - pd[0]; - ady = pa[1] - pd[1]; - bdy = pb[1] - pd[1]; - cdy = pc[1] - pd[1]; - adz = pa[2] - pd[2]; - bdz = pb[2] - pd[2]; - cdz = pc[2] - pd[2]; - - return adx * (bdy * cdz - bdz * cdy) - + bdx * (cdy * adz - cdz * ady) - + cdx * (ady * bdz - adz * bdy); -} - /////////////////////////////////////////////////////////////////////////////// // // // interiorangle() Return the interior angle (0 - 2 * PI) between vectors // @@ -6282,8 +6161,9 @@ REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) len1 = sqrt(dot(v1, v1)); len2 = sqrt(dot(v2, v2)); lenlen = len1 * len2; +#ifdef SELF_CHECK assert(lenlen != 0.0); - +#endif costheta = dot(v1, v2) / lenlen; if (costheta > 1.0) { costheta = 1.0; // Roundoff. @@ -6325,7 +6205,9 @@ void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) v2[2] = p[2] - e1[2]; len = sqrt(dot(v1, v1)); +#ifdef SELF_CHECK assert(len != 0.0); +#endif v1[0] /= len; v1[1] /= len; v1[2] /= len; @@ -6367,6 +6249,7 @@ void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) prj[2] = p[2] - dist * fnormal[2]; } + /////////////////////////////////////////////////////////////////////////////// // // // facedihedral() Return the dihedral angle (in radian) between two // @@ -6488,7 +6371,6 @@ bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, } cosd = -dot(N[f1], N[f2]); if (cosd < -1.0) cosd = -1.0; // Rounding. - if (cosd > 1.0) cosd = 1.0; // Rounding. if (cosdd) cosdd[i] = cosd; if (cosmaxd || cosmind) { if (i == 0) { @@ -6677,59 +6559,7 @@ bool tetgenmesh::circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// orthosphere() Calulcate the orthosphere of four weighted points. // -// // -// A weighted point (p, P^2) can be interpreted as a sphere centered at the // -// point 'p' with a radius 'P'. The 'height' of 'p' is pheight = p[0]^2 + // -// p[1]^2 + p[2]^2 - P^2. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, - REAL aheight, REAL bheight, REAL cheight, - REAL dheight, REAL* orthocent, REAL* radius) -{ - REAL A[4][4], rhs[4], D; - int indx[4]; - - // Set the coefficient matrix A (4 x 4). - A[0][0] = 1.0; A[0][1] = pa[0]; A[0][2] = pa[1]; A[0][3] = pa[2]; - A[1][0] = 1.0; A[1][1] = pb[0]; A[1][2] = pb[1]; A[1][3] = pb[2]; - A[2][0] = 1.0; A[2][1] = pc[0]; A[2][2] = pc[1]; A[2][3] = pc[2]; - A[3][0] = 1.0; A[3][1] = pd[0]; A[3][2] = pd[1]; A[3][3] = pd[2]; - - // Set the right hand side vector (4 x 1). - rhs[0] = 0.5 * aheight; - rhs[1] = 0.5 * bheight; - rhs[2] = 0.5 * cheight; - rhs[3] = 0.5 * dheight; - - // Solve the 4 by 4 equations use LU decomposition with partial pivoting - // and backward and forward substitute.. - if (!lu_decmp(A, 4, indx, &D, 0)) { - if (radius != (REAL *) NULL) *radius = 0.0; - return false; - } - lu_solve(A, 4, indx, rhs, 0); - if (orthocent != (REAL *) NULL) { - orthocent[0] = rhs[1]; - orthocent[1] = rhs[2]; - orthocent[2] = rhs[3]; - } - if (radius != (REAL *) NULL) { - // rhs[0] = - rheight / 2; - // rheight = - 2 * rhs[0]; - // = r[0]^2 + r[1]^2 + r[2]^2 - radius^2 - // radius^2 = r[0]^2 + r[1]^2 + r[2]^2 -rheight - // = r[0]^2 + r[1]^2 + r[2]^2 + 2 * rhs[0] - *radius = sqrt(rhs[1] * rhs[1] + rhs[2] * rhs[2] + rhs[3] * rhs[3] - + 2.0 * rhs[0]); - } - return true; -} /////////////////////////////////////////////////////////////////////////////// // // @@ -6777,6 +6607,8 @@ void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, } } + + /////////////////////////////////////////////////////////////////////////////// // // // tetprismvol() Calculate the volume of a tetrahedral prism in 4D. // @@ -6824,145 +6656,44 @@ REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3) return fabs(vol[0]) + fabs(vol[1]) + fabs(vol[2]) + fabs(vol[3]); } +//// //// +//// //// +//// geom_cxx ///////////////////////////////////////////////////////////////// + +//// flip_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // -// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. // +// flippush() Push a face (possibly will be flipped) into flipstack. // +// // +// The face is marked. The flag is used to check the validity of the face on // +// its popup. Some other flips may change it already. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, - point *ppb, point *ppc) +void tetgenmesh::flippush(badface*& fstack, triface* flipface) { - point *ppt, pa, pb, pc; - REAL v1[3], v2[3], n[3]; - REAL lab, len, A, area; - REAL x, y, z; - int i; - - ppt = (point *) fastlookup(facpoints, 0); - pa = *ppt; // a is the first point. - pb = pc = NULL; // Avoid compiler warnings. + badface *newflipface; - // Get a point b s.t. the length of [a, b] is maximal. - lab = 0; - for (i = 1; i < facpoints->objects; i++) { - ppt = (point *) fastlookup(facpoints, i); - x = (*ppt)[0] - pa[0]; - y = (*ppt)[1] - pa[1]; - z = (*ppt)[2] - pa[2]; - len = x * x + y * y + z * z; - if (len > lab) { - lab = len; - pb = *ppt; - } - } - lab = sqrt(lab); - if (lab == 0) { - if (!b->quiet) { - printf("Warning: All points of a facet are coincident with %d.\n", - pointmark(pa)); - } - return false; - } - - // Get a point c s.t. the area of [a, b, c] is maximal. - v1[0] = pb[0] - pa[0]; - v1[1] = pb[1] - pa[1]; - v1[2] = pb[2] - pa[2]; - A = 0; - for (i = 1; i < facpoints->objects; i++) { - ppt = (point *) fastlookup(facpoints, i); - v2[0] = (*ppt)[0] - pa[0]; - v2[1] = (*ppt)[1] - pa[1]; - v2[2] = (*ppt)[2] - pa[2]; - cross(v1, v2, n); - area = dot(n, n); - if (area > A) { - A = area; - pc = *ppt; - } - } - if (A == 0) { - // All points are collinear. No above point. - if (!b->quiet) { - printf("Warning: All points of a facet are collinaer with [%d, %d].\n", - pointmark(pa), pointmark(pb)); - } - return false; - } - - // Calculate an above point of this facet. - facenormal(pa, pb, pc, n, 1, NULL); - len = sqrt(dot(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; - lab /= 2.0; // Half the maximal length. - dummypoint[0] = pa[0] + lab * n[0]; - dummypoint[1] = pa[1] + lab * n[1]; - dummypoint[2] = pa[2] + lab * n[2]; - - if (ppa != NULL) { - // Return the three points. - *ppa = pa; - *ppb = pb; - *ppc = pc; - } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// Calculate an above point. It lies above the plane containing the subface // -// [a,b,c], and save it in dummypoint. Moreover, the vector pa->dummypoint // -// is the normal of the plane. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) -{ - REAL n1[3], n2[3], *norm; - REAL len, len1, len2; - - // Select a base. - facenormal(pa, pb, pc, n1, 1, NULL); - len1 = sqrt(dot(n1, n1)); - facenormal(pa, pb, pd, n2, 1, NULL); - len2 = sqrt(dot(n2, n2)); - if (len1 > len2) { - norm = n1; - len = len1; - } else { - norm = n2; - len = len2; + if (!facemarked(*flipface)) { + newflipface = (badface *) flippool->alloc(); + newflipface->tt = *flipface; + markface(newflipface->tt); + // Push this face into stack. + newflipface->nextitem = fstack; + fstack = newflipface; } - assert(len > 0); - norm[0] /= len; - norm[1] /= len; - norm[2] /= len; - len = distance(pa, pb); - dummypoint[0] = pa[0] + len * norm[0]; - dummypoint[1] = pa[1] + len * norm[1]; - dummypoint[2] = pa[2] + len * norm[2]; } -//// //// -//// //// -//// geom_cxx ///////////////////////////////////////////////////////////////// - -//// flip_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // // flip23() Perform a 2-to-3 flip (face-to-edge flip). // // // -// 'fliptets' is an array of three tets (handles), where the [0] and [1] are // -// [a,b,c,d] and [b,a,c,e]. The three new tets: [e,d,a,b], [e,d,b,c], and // -// [e,d,c,a] are returned in [0], [1], and [2] of 'fliptets'. As a result, // -// The face [a,b,c] is removed, and the edge [d,e] is created. // +// 'fliptets' is an array of tetrahedra. On input it contains two tets // +// [a,b,c,d] and [b,a,c,e]. It returns three new tets: [e,d,a,b], [e,d,b,c], // +// [e,d,c,a]. The face [a,b,c] is removed, and the edge [d,e] is created. // // // // If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // // the five vertices may be 'dummypoint'. There are two canonical cases: // @@ -6973,19 +6704,27 @@ void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) // rotate the three input tets counterclockwisely (right-hand rule) // // until a or b is in c's position. // // // -// If 'fc->enqflag' is set, convex hull faces will be queued for flipping. // -// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip() // -// after the insertion of a new point. It is assumed that 'd' is the new // -// point. IN this case, only link faces of 'd' are queued. // +// If 'flipflag > 0', faces on the convex hull of the five vertices might // +// need to be flipped, e.g., for incremental DT construction or mesh quality // +// improvement. They will be queued in 'flipstack'. // +// // +// If 'flipflag = 1', it is in the process of incrmental flip DT algorithm, // +// and we assume that 'd' must be the newly inserted vertex. In such case, // +// only the link faces at 'd', i.e., three faces [a,b,e], [b,c,e], and [c,a, // +// e] needs to be queued, see [Edelsbrunner & Shah'1996] and [M\"ucke'1998]. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) +void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, + int chkencflag) { triface topcastets[3], botcastets[3]; triface newface, casface; + face checksh; + face checkseg; + badface *bface; // used by chkencflag point pa, pb, pc, pd, pe; - REAL attrib, volume; + REAL volneg[2], volpos[3], vol_diff; // volumes of involved tet-prisms. int dummyflag = 0; // range = {-1, 0, 1, 2}. int i; @@ -7013,12 +6752,16 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) } } - pa = org(fliptets[0]); + pa = org(fliptets[0]); pb = dest(fliptets[0]); pc = apex(fliptets[0]); pd = oppo(fliptets[0]); pe = oppo(fliptets[1]); + if (b->verbose > 3) { + printf(" flip 2-to-3: (%d, %d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); + } flip23count++; // Get the outer boundary faces. @@ -7036,7 +6779,6 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) fliptets[1].ver = 11; setelemmarker(fliptets[0].tet, 0); // Clear all flags. setelemmarker(fliptets[1].tet, 0); - // NOTE: the element attributes and volume constraint remain unchanged. if (checksubsegflag) { // Dealloc the space to subsegments. if (fliptets[0].tet[8] != NULL) { @@ -7061,15 +6803,6 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) } // Create a new tet. maketetrahedron(&(fliptets[2])); - // The new tet have the same attributes from the old tet. - for (i = 0; i < numelemattrib; i++) { - attrib = elemattribute(fliptets[0].tet, i); - setelemattribute(fliptets[2].tet, i, attrib); - } - if (b->varvolume) { - volume = volumebound(fliptets[0].tet); - setvolumebound(fliptets[2].tet, volume); - } if (hullflag > 0) { // Check if d is dummytet. @@ -7103,8 +6836,7 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] * } - if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol - REAL volneg[2], volpos[3], vol_diff; + if (calc_tetprism_vol) { if (pd != dummypoint) { if (pc != dummypoint) { volpos[0] = tetprismvol(pe, pd, pa, pb); @@ -7127,8 +6859,8 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) volneg[1] = tetprismvol(pb, pa, pc, pe); } vol_diff = volpos[0] + volpos[1] + volpos[2] - volneg[0] - volneg[1]; - fc->tetprism_vol_sum += vol_diff; // Update the total sum. - } + tetprism_vol_sum += vol_diff; // Update the total sum. + } // if (check_tetprism_vol_diff) // Bond three new tets together. for (i = 0; i < 3; i++) { @@ -7137,36 +6869,43 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) } // Bond to top outer boundary faces (at [a,b,c,d]). for (i = 0; i < 3; i++) { - eorgoppo(fliptets[i], newface); // At edges [b,a], [c,b], [a,c]. + enextesym(fliptets[i], newface); + eprevself(newface); // At edges [b,a], [c,b], [a,c]. bond(newface, topcastets[i]); } // Bond bottom outer boundary faces (at [b,a,c,e]). for (i = 0; i < 3; i++) { - edestoppo(fliptets[i], newface); // At edges [a,b], [b,c], [c,a]. + eprevesym(fliptets[i], newface); + enextself(newface); // At edges [a,b], [b,c], [c,a]. bond(newface, botcastets[i]); } + // Bond 15 subsegments if there are. if (checksubsegflag) { - // Bond subsegments if there are. - // Each new tet has 5 edges to be checked (except the edge [e,d]). - face checkseg; // The middle three: [a,b], [b,c], [c,a]. - for (i = 0; i < 3; i++) { - if (issubseg(topcastets[i])) { - tsspivot1(topcastets[i], checkseg); - eorgoppo(fliptets[i], newface); + for (i = 0; i < 3; i++) { + tsspivot1(topcastets[i], checkseg); + if (checkseg.sh != NULL) { + enextesym(fliptets[i], newface); + eprevself(newface); // At edges [b,a], [c,b], [a,c]. tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (fc->chkencflag & 1) { - enqueuesubface(badsubsegs, &checkseg); + if (chkencflag & 1) { + // Skip it if it has already queued. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } } } } // The top three: [d,a], [d,b], [d,c]. Two tets per edge. for (i = 0; i < 3; i++) { - eprev(topcastets[i], casface); - if (issubseg(casface)) { - tsspivot1(casface, checkseg); + eprev(topcastets[i], casface); + tsspivot1(casface, checkseg); + if (checkseg.sh != NULL) { enext(fliptets[i], newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); @@ -7174,16 +6913,22 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) eprevself(newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (fc->chkencflag & 1) { - enqueuesubface(badsubsegs, &checkseg); + if (chkencflag & 1) { + // Skip it if it has already queued. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } } } } // The bot three: [a,e], [b,e], [c,e]. Two tets per edge. for (i = 0; i < 3; i++) { enext(botcastets[i], casface); - if (issubseg(casface)) { - tsspivot1(casface, checkseg); + tsspivot1(casface, checkseg); + if (checkseg.sh != NULL) { eprev(fliptets[i], newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); @@ -7191,53 +6936,75 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) enextself(newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (fc->chkencflag & 1) { - enqueuesubface(badsubsegs, &checkseg); + if (chkencflag & 1) { + // Skip it if it has already queued. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } } } } - } // if (checksubsegflag) + } + // Bond 6 subfaces if there are. if (checksubfaceflag) { - // Bond 6 subfaces if there are. - face checksh; - for (i = 0; i < 3; i++) { - if (issubface(topcastets[i])) { - tspivot(topcastets[i], checksh); - eorgoppo(fliptets[i], newface); + for (i = 0; i < 3; i++) { + tspivot(topcastets[i], checksh); + if (checksh.sh != NULL) { + enextesym(fliptets[i], newface); + eprevself(newface); // At edge [b,a], [c,b], [a,c]. sesymself(checksh); tsbond(newface, checksh); - if (fc->chkencflag & 2) { - enqueuesubface(badsubfacs, &checksh); + if (chkencflag & 2) { + if (!smarktest2ed(checksh)) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = checksh; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checksh); // An alive badface + } } } } for (i = 0; i < 3; i++) { - if (issubface(botcastets[i])) { - tspivot(botcastets[i], checksh); - edestoppo(fliptets[i], newface); + tspivot(botcastets[i], checksh); + if (checksh.sh != NULL) { + eprevesym(fliptets[i], newface); + enextself(newface); // At edge [a,b], [b,c], [c,a] sesymself(checksh); tsbond(newface, checksh); - if (fc->chkencflag & 2) { - enqueuesubface(badsubfacs, &checksh); + if (chkencflag & 2) { + if (!smarktest2ed(checksh)) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = checksh; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checksh); // An alive badface + } } } } - } // if (checksubfaceflag) + } - if (fc->chkencflag & 4) { + if (chkencflag & 4) { // Put three new tets into check list. for (i = 0; i < 3; i++) { - enqueuetetrahedron(&(fliptets[i])); + if (!marktest2ed(fliptets[i])) { + bface = (badface *) badtetrahedrons->alloc(); + bface->tt = fliptets[i]; + marktest2(bface->tt); + bface->forg = org(fliptets[i]); + } } } // Update the point-to-tet map. - setpoint2tet(pa, (tetrahedron) fliptets[0].tet); - setpoint2tet(pb, (tetrahedron) fliptets[0].tet); - setpoint2tet(pc, (tetrahedron) fliptets[1].tet); - setpoint2tet(pd, (tetrahedron) fliptets[0].tet); - setpoint2tet(pe, (tetrahedron) fliptets[0].tet); + setpoint2tet(pa, encode(fliptets[0])); + setpoint2tet(pb, encode(fliptets[0])); + setpoint2tet(pc, encode(fliptets[1])); + setpoint2tet(pd, encode(fliptets[0])); + setpoint2tet(pe, encode(fliptets[0])); if (hullflag > 0) { if (dummyflag != 0) { @@ -7270,15 +7037,19 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) } } - if (fc->enqflag > 0) { + if (flipflag > 0) { // Queue faces which may be locally non-Delaunay. + //pd = dest(fliptets[0]); // 'd' may be a new vertex. for (i = 0; i < 3; i++) { eprevesym(fliptets[i], newface); + //flippush(flipstack, &newface, pd); flippush(flipstack, &newface); } - if (fc->enqflag > 1) { + if (flipflag > 1) { + //pe = org(fliptets[0]); for (i = 0; i < 3; i++) { enextesym(fliptets[i], newface); + //flippush(flipstack, &newface, pe); flippush(flipstack, &newface); } } @@ -7291,10 +7062,9 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) // // // flip32() Perform a 3-to-2 flip (edge-to-face flip). // // // -// 'fliptets' is an array of three tets (handles), which are [e,d,a,b], // -// [e,d,b,c], and [e,d,c,a]. The two new tets: [a,b,c,d] and [b,a,c,e] are // -// returned in [0] and [1] of 'fliptets'. As a result, the edge [e,d] is // -// replaced by the face [a,b,c]. // +// 'fliptets' is an array of three tetrahedra. On input, it contains three // +// tets: [e,d,a,b], [e,d,b,c], and [e,d,c,a]. It returns tw tets: [a,b,c,d], // +// and [b,a,c,e]. The edge [e,d] is replaced by the face [a,b,c]. // // // // If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // // the five vertices may be 'dummypoint'. There are two canonical cases: // @@ -7305,36 +7075,35 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) // three old tets counterclockwisely (right-hand rule) until a or b // // is in c's position. // // // -// If 'fc->enqflag' is set, convex hull faces will be queued for flipping. // -// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip() // -// after the insertion of a new point. It is assumed that 'a' is the new // -// point. In this case, only link faces of 'a' are queued. // +// If 'flipflag > 0', faces on the convex hull of the five vertices might // +// need to be flipped, e.g., for incremental DT construction or mesh quality // +// improvement. They will be queued in 'flipstack'. // +// // +// If 'flipflag = 1', it is in the process of incrmental flip DT algorithm, // +// and we assume that 'a' must be the newly inserted vertex. In such case, // +// only the link faces at 'a', i.e., two faces [c,b,d] and [b,c,e] needs to // +// be queued, refer to [Edelsbrunner & Shah'1996] and [M\"ucke'1998]. // // // -// If 'checksubfaceflag' is on (global variable), and assume [e,d] is not a // -// segment. There may be two (interior) subfaces sharing at [e,d], which are // -// [e,d,p] and [e,d,q], where the pair (p,q) may be either (a,b), or (b,c), // -// or (c,a) In such case, a 2-to-2 flip is performed on these two subfaces // -// and two new subfaces [p,q,e] and [p,q,d] are created. They are inserted // -// back into the tetrahedralization. However, it is possible that the new // -// subface ([p,q,e] or [p,q,d] already exists. In such case, we just delete // -// the conflict subface. As a result, either 'd' or 'e' is removed from the // -// surface mesh. An alternative solution would be to detect this case in // -// advance and perform a 3-to-1 flip to remove 'd' or 'e' (see 2011-11-15). // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) +void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, + int chkencflag) { triface topcastets[3], botcastets[3]; triface newface, casface; - face flipshs[3]; + face checksh; face checkseg; + badface *bface; // used by chkencflag point pa, pb, pc, pd, pe; - REAL attrib, volume; + REAL volneg[3], volpos[2], vol_diff; // volumes of involved tet-prisms. int dummyflag = 0; // Rangle = {-1, 0, 1, 2} - int spivot = -1, scount = 0; // for flip22() - int t1ver; - int i, j; + int i; + + // For 2-to-2 flip (subfaces). + face flipshs[3], flipfaces[2]; + point rempt; + int spivot = -1, scount = 0; if (hullflag > 0) { // Check if e is 'dummypoint'. @@ -7374,24 +7143,36 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) pd = dest(fliptets[0]); pe = org(fliptets[0]); + if (b->verbose > 3) { + printf(" flip 3-to-2: (%d, %d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); + } flip32count++; // Get the outer boundary faces. for (i = 0; i < 3; i++) { - eorgoppo(fliptets[i], casface); + enextesym(fliptets[i], casface); + eprevself(casface); fsym(casface, topcastets[i]); } for (i = 0; i < 3; i++) { - edestoppo(fliptets[i], casface); + eprevesym(fliptets[i], casface); + enextself(casface); fsym(casface, botcastets[i]); } if (checksubfaceflag) { // Check if there are interior subfaces at the edge [e,d]. + spivot = -1; + scount = 0; for (i = 0; i < 3; i++) { tspivot(fliptets[i], flipshs[i]); if (flipshs[i].sh != NULL) { - // Found an interior subface. + if (b->verbose > 3) { + printf(" Found an interior subface (%d, %d, %d).\n", + pointmark(sorg(flipshs[i])), pointmark(sdest(flipshs[i])), + pointmark(sapex(flipshs[i]))); + } stdissolve(flipshs[i]); // Disconnect the sub-tet bond. scount++; } else { @@ -7427,28 +7208,7 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) fliptets[1].tet[9] = NULL; } } - if (checksubfaceflag) { - if (scount > 0) { - // The element attributes and volume constraint must be set correctly. - // There are two subfaces involved in this flip. The three tets are - // separated into two different regions, one may be exterior. The - // first region has two tets, and the second region has only one. - // The two created tets must be in the same region as the first region. - // The element attributes and volume constraint must be set correctly. - //assert(spivot != -1); - // The tet fliptets[spivot] is in the first region. - for (j = 0; j < 2; j++) { - for (i = 0; i < numelemattrib; i++) { - attrib = elemattribute(fliptets[spivot].tet, i); - setelemattribute(fliptets[j].tet, i, attrib); - } - if (b->varvolume) { - volume = volumebound(fliptets[spivot].tet); - setvolumebound(fliptets[j].tet, volume); - } - } - } - } + // Delete an old tet. tetrahedrondealloc(fliptets[2].tet); @@ -7472,15 +7232,14 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) esymself(fliptets[0]); // Adjust abec -> bace. esymself(fliptets[1]); - // The hullsize does not change. + // The hullsize does not changle. } } else { setvertices(fliptets[0], pa, pb, pc, pd); setvertices(fliptets[1], pb, pa, pc, pe); } - if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol - REAL volneg[3], volpos[2], vol_diff; + if (calc_tetprism_vol) { if (pc != dummypoint) { if (pd != dummypoint) { volneg[0] = tetprismvol(pe, pd, pa, pb); @@ -7503,7 +7262,7 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) volpos[1] = 0.; } vol_diff = volpos[0] + volpos[1] - volneg[0] - volneg[1] - volneg[2]; - fc->tetprism_vol_sum += vol_diff; // Update the total sum. + tetprism_vol_sum += vol_diff; // Update the total sum. } // Bond abcd <==> bace. @@ -7522,91 +7281,133 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) } if (checksubsegflag) { - // Bond 9 segments to new (flipped) tets. - for (i = 0; i < 3; i++) { // edges a->b, b->c, c->a. - if (issubseg(topcastets[i])) { - tsspivot1(topcastets[i], checkseg); + // Bond segments to new (flipped) tets. + for (i = 0; i < 3; i++) { + tsspivot1(topcastets[i], checkseg); + if (checkseg.sh != NULL) { tssbond1(fliptets[0], checkseg); sstbond1(checkseg, fliptets[0]); - tssbond1(fliptets[1], checkseg); - sstbond1(checkseg, fliptets[1]); - if (fc->chkencflag & 1) { - enqueuesubface(badsubsegs, &checkseg); + if (chkencflag & 1) { + // Skip it if it has already queued. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } } } enextself(fliptets[0]); - eprevself(fliptets[1]); } // The three top edges. - for (i = 0; i < 3; i++) { // edges b->d, c->d, a->d. + for (i = 0; i < 3; i++) { esym(fliptets[0], newface); - eprevself(newface); - enext(topcastets[i], casface); - if (issubseg(casface)) { - tsspivot1(casface, checkseg); + eprevself(newface); // edge b->d, c->d, a->d. + enext(topcastets[i], casface); + tsspivot1(casface, checkseg); + if (checkseg.sh != NULL) { tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (fc->chkencflag & 1) { - enqueuesubface(badsubsegs, &checkseg); + if (chkencflag & 1) { + // Skip it if it has already queued. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } } } enextself(fliptets[0]); } + // Process the bottom tet bace. + for (i = 0; i < 3; i++) { + tsspivot1(botcastets[i], checkseg); + if (checkseg.sh != NULL) { + tssbond1(fliptets[1], checkseg); + sstbond1(checkseg, fliptets[1]); + if (chkencflag & 1) { + // Skip it if it has already queued. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } + } + } + eprevself(fliptets[1]); + } // The three bot edges. - for (i = 0; i < 3; i++) { // edges b<-e, c<-e, a<-e. + for (i = 0; i < 3; i++) { esym(fliptets[1], newface); - enextself(newface); + enextself(newface); // edge b<-e, c<-e, a<-e. eprev(botcastets[i], casface); - if (issubseg(casface)) { - tsspivot1(casface, checkseg); + tsspivot1(casface, checkseg); + if (checkseg.sh != NULL) { tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (fc->chkencflag & 1) { - enqueuesubface(badsubsegs, &checkseg); + if (chkencflag & 1) { + // Skip it if it has already queued. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } } } eprevself(fliptets[1]); } - } // if (checksubsegflag) + } if (checksubfaceflag) { - face checksh; // Bond the top three casing subfaces. - for (i = 0; i < 3; i++) { // At edges [b,a], [c,b], [a,c] - if (issubface(topcastets[i])) { - tspivot(topcastets[i], checksh); - esym(fliptets[0], newface); + for (i = 0; i < 3; i++) { + tspivot(topcastets[i], checksh); + if (checksh.sh != NULL) { + esym(fliptets[0], newface); // At edge [b,a], [c,b], [a,c] sesymself(checksh); tsbond(newface, checksh); - if (fc->chkencflag & 2) { - enqueuesubface(badsubfacs, &checksh); + if (chkencflag & 2) { + if (!smarktest2ed(checksh)) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = checksh; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checksh); // An alive badface + } } } enextself(fliptets[0]); } // Bond the bottom three casing subfaces. - for (i = 0; i < 3; i++) { // At edges [a,b], [b,c], [c,a] - if (issubface(botcastets[i])) { - tspivot(botcastets[i], checksh); - esym(fliptets[1], newface); + for (i = 0; i < 3; i++) { + tspivot(botcastets[i], checksh); + if (checksh.sh != NULL) { + esym(fliptets[1], newface); // // At edge [a,b], [b,c], [c,a] sesymself(checksh); tsbond(newface, checksh); - if (fc->chkencflag & 2) { - enqueuesubface(badsubfacs, &checksh); + if (chkencflag & 2) { + if (!smarktest2ed(checksh)) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = checksh; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checksh); // An alive badface + } } } eprevself(fliptets[1]); } + } + if (checksubfaceflag) { if (scount > 0) { assert(spivot != -1); // spivot = i, in {0,1,2} - face flipfaces[2]; - point rempt; // Perform a 2-to-2 flip in subfaces. flipfaces[0] = flipshs[(spivot + 1) % 3]; flipfaces[1] = flipshs[(spivot + 2) % 3]; sesymself(flipfaces[1]); - flip22(flipfaces, 0, fc->chkencflag); + flip22(flipfaces, 0, chkencflag); // Connect the flipped subfaces to flipped tets. // First go to the corresponding flipping edge. // Re-use top- and botcastets[0]. @@ -7634,6 +7435,9 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) assert(checkseg.sh == NULL); // Delete the two duplicated subfaces. rempt = sapex(checksh); + if (b->verbose > 2) { + printf(" Remove vertex %d from surface.\n", pointmark(rempt)); + } // Make sure we do not delete a Steiner points in segment. assert(pointtype(rempt) == FREEFACETVERTEX); setpointtype(rempt, FREEVOLVERTEX); @@ -7690,6 +7494,9 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) // the surface triangulation. // Delete the two duplicated subfaces. rempt = sapex(checksh); + if (b->verbose > 2) { + printf(" Remove vertex %d from surface.\n", pointmark(rempt)); + } // Make sure we do not delete a Steiner points in segment. assert(pointtype(rempt) == FREEFACETVERTEX); setpointtype(rempt, FREEVOLVERTEX); @@ -7723,21 +7530,26 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) // // Push botcastets[0] into queue for checking new sliver. // assert(oppo(botcastets[0]) != dummypoint); // flippush(&(botcastets[0]), oppo(botcastets[0])); - } // if (scount > 0) - } // if (checksubfaceflag) + } + } - if (fc->chkencflag & 4) { + if (chkencflag & 4) { // Put two new tets into check list. for (i = 0; i < 2; i++) { - enqueuetetrahedron(&(fliptets[i])); + if (!marktest2ed(fliptets[i])) { + bface = (badface *) badtetrahedrons->alloc(); + bface->tt = fliptets[i]; + marktest2(bface->tt); + bface->forg = org(fliptets[i]); + } } } - setpoint2tet(pa, (tetrahedron) fliptets[0].tet); - setpoint2tet(pb, (tetrahedron) fliptets[0].tet); - setpoint2tet(pc, (tetrahedron) fliptets[0].tet); - setpoint2tet(pd, (tetrahedron) fliptets[0].tet); - setpoint2tet(pe, (tetrahedron) fliptets[1].tet); + setpoint2tet(pa, encode(fliptets[0])); + setpoint2tet(pb, encode(fliptets[0])); + setpoint2tet(pc, encode(fliptets[0])); + setpoint2tet(pd, encode(fliptets[0])); + setpoint2tet(pe, encode(fliptets[1])); if (hullflag > 0) { if (dummyflag != 0) { @@ -7760,14 +7572,14 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) } } - if (fc->enqflag > 0) { + if (flipflag > 0) { // Queue faces which may be locally non-Delaunay. // pa = org(fliptets[0]); // 'a' may be a new vertex. enextesym(fliptets[0], newface); flippush(flipstack, &newface); eprevesym(fliptets[1], newface); flippush(flipstack, &newface); - if (fc->enqflag > 1) { + if (flipflag > 1) { //pb = dest(fliptets[0]); eprevesym(fliptets[0], newface); flippush(flipstack, &newface); @@ -7793,28 +7605,28 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) // four tets in 'fliptets' are: [p,d,a,b], [p,d,b,c], [p,d,c,a], and [a,b,c, // // p]. On return, 'fliptets[0]' is the new tet [a,b,c,d]. // // // -// If 'hullflag' is set (> 0), one of the five vertices may be 'dummypoint'. // -// The 'hullsize' may be changed. Note that p may be dummypoint. In this // -// case, four hull tets are replaced by one real tet. // +// If 'hullflag' is set (> 0), one of the four vertices may be 'duumypoint'. // +// The 'hullsize' may be changed. // // // // If 'checksubface' flag is set (>0), it is possible that there are three // // interior subfaces connecting at p. If so, a 3-to-1 flip is performed to // // to remove p from the surface triangulation. // // // -// If it is called by the routine incrementalflip(), we assume that d is the // -// newly inserted vertex. // -// // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) +void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, + int chkencflag) { triface topcastets[3], botcastet; triface newface, neightet; face flipshs[4]; + face checksh; + face checkseg; point pa, pb, pc, pd, pp; + badface *bface; // used by chkencflag + REAL volneg[4], volpos[1], vol_diff; // volumes of involved tet-prisms. int dummyflag = 0; // in {0, 1, 2, 3, 4} int spivot = -1, scount = 0; - int t1ver; int i; pa = org(fliptets[3]); @@ -7823,7 +7635,11 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) pd = dest(fliptets[0]); pp = org(fliptets[0]); // The removing vertex. - flip41count++; + if (b->verbose > 3) { + printf(" flip 4-to-1: (%d, %d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pp)); + } + // flip41count++; // Get the outer boundary faces. for (i = 0; i < 3; i++) { @@ -7836,6 +7652,8 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) if (checksubfaceflag) { // Check if there are three subfaces at 'p'. // Re-use 'newface'. + spivot = -1; + scount = 0; for (i = 0; i < 3; i++) { fnext(fliptets[3], newface); // [a,b,p,d],[b,c,p,d],[c,a,p,d]. tspivot(newface, flipshs[i]); @@ -7865,11 +7683,9 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) } } // if (checksubfaceflag) - // Re-use fliptets[0] for [a,b,c,d]. fliptets[0].ver = 11; setelemmarker(fliptets[0].tet, 0); // Clean all flags. - // NOTE: the element attributes and volume constraint remain unchanged. if (checksubsegflag) { // Dealloc the space to subsegments. if (fliptets[0].tet[8] != NULL) { @@ -7884,20 +7700,19 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) fliptets[0].tet[9] = NULL; } } + // Delete the other three tets. for (i = 1; i < 4; i++) { tetrahedrondealloc(fliptets[i].tet); } - if (pp != dummypoint) { - // Mark the point pp as unused. - setpointtype(pp, UNUSEDVERTEX); - unuverts++; - } + // Mark the point pp as unused. + setpointtype(pp, UNUSEDVERTEX); + unuverts++; // Create the new tet [a,b,c,d]. if (hullflag > 0) { - // One of the five vertices may be 'dummypoint'. + // One of the four vertices may be 'dummypoint'. if (pa == dummypoint) { // pa is dummypoint. setvertices(fliptets[0], pc, pb, pd, pa); @@ -7918,26 +7733,17 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) dummyflag = 4; } else { setvertices(fliptets[0], pa, pb, pc, pd); - if (pp == dummypoint) { - dummyflag = -1; - } else { - dummyflag = 0; - } + dummyflag = 0; } if (dummyflag > 0) { - // We deleted 3 hull tets, and create 1 hull tet. + // We delete 3 hull tets, and create 1 hull tet. hullsize -= 2; - } else if (dummyflag < 0) { - // We deleted 4 hull tets. - hullsize -= 4; - // meshedges does not change. } } else { setvertices(fliptets[0], pa, pb, pc, pd); } - if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol - REAL volneg[4], volpos[1], vol_diff; + if (calc_tetprism_vol) { if (dummyflag > 0) { if (pa == dummypoint) { volneg[0] = 0.; @@ -7961,12 +7767,6 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) volneg[3] = tetprismvol(pa, pb, pc, pp); } volpos[0] = 0.; - } else if (dummyflag < 0) { - volneg[0] = 0.; - volneg[1] = 0.; - volneg[2] = 0.; - volneg[3] = 0.; - volpos[0] = tetprismvol(pa, pb, pc, pd); } else { volneg[0] = tetprismvol(pp, pd, pa, pb); volneg[1] = tetprismvol(pp, pd, pb, pc); @@ -7975,7 +7775,7 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) volpos[0] = tetprismvol(pa, pb, pc, pd); } vol_diff = volpos[0] - volneg[0] - volneg[1] - volneg[2] - volneg[3]; - fc->tetprism_vol_sum += vol_diff; // Update the total sum. + tetprism_vol_sum += vol_diff; // Update the total sum. } // Bond the new tet to adjacent tets. @@ -7987,29 +7787,40 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) bond(fliptets[0], botcastet); if (checksubsegflag) { - face checkseg; // Bond 6 segments (at edges of [a,b,c,d]) if there there are. for (i = 0; i < 3; i++) { eprev(topcastets[i], newface); // At edges [d,a],[d,b],[d,c]. - if (issubseg(newface)) { - tsspivot1(newface, checkseg); + tsspivot1(newface, checkseg); + if (checkseg.sh != NULL) { esym(fliptets[0], newface); enextself(newface); // At edges [a,d], [b,d], [c,d]. tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (fc->chkencflag & 1) { - enqueuesubface(badsubsegs, &checkseg); + if (chkencflag & 1) { + // Skip it if it has already queued. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } } } enextself(fliptets[0]); } for (i = 0; i < 3; i++) { - if (issubseg(topcastets[i])) { - tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a]. + tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a]. + if (checkseg.sh != NULL) { tssbond1(fliptets[0], checkseg); sstbond1(checkseg, fliptets[0]); - if (fc->chkencflag & 1) { - enqueuesubface(badsubsegs, &checkseg); + if (chkencflag & 1) { + // Skip it if it has already queued. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } } } enextself(fliptets[0]); @@ -8017,29 +7828,40 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) } if (checksubfaceflag) { - face checksh; // Bond 4 subfaces (at faces of [a,b,c,d]) if there are. for (i = 0; i < 3; i++) { - if (issubface(topcastets[i])) { - tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d] + tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d] + if (checksh.sh != NULL) { esym(fliptets[0], newface); // At faces [b,a,d],[c,b,d],[a,c,d] sesymself(checksh); tsbond(newface, checksh); - if (fc->chkencflag & 2) { - enqueuesubface(badsubfacs, &checksh); + if (chkencflag & 2) { + if (!smarktest2ed(checksh)) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = checksh; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checksh); // An alive badface + } } } enextself(fliptets[0]); } - if (issubface(botcastet)) { - tspivot(botcastet, checksh); // At face [b,a,c] + tspivot(botcastet, checksh); // At face [b,a,c] + if (checksh.sh != NULL) { sesymself(checksh); tsbond(fliptets[0], checksh); - if (fc->chkencflag & 2) { - enqueuesubface(badsubfacs, &checksh); + if (chkencflag & 2) { + if (!smarktest2ed(checksh)) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = checksh; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checksh); // An alive badface + } } } + } + if (checksubfaceflag) { if (spivot >= 0) { // Perform a 3-to-1 flip in surface triangulation. // Depending on the value of 'spivot', the three subfaces are: @@ -8073,26 +7895,30 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) } // if (spivot > 0) } // if (checksubfaceflag) - if (fc->chkencflag & 4) { - enqueuetetrahedron(&(fliptets[0])); + if (chkencflag & 4) { + // Put the new tet into check list. + if (!marktest2ed(fliptets[0])) { + bface = (badface *) badtetrahedrons->alloc(); + bface->tt = fliptets[0]; + marktest2(bface->tt); + bface->forg = org(fliptets[0]); + } } // Update the point-to-tet map. - setpoint2tet(pa, (tetrahedron) fliptets[0].tet); - setpoint2tet(pb, (tetrahedron) fliptets[0].tet); - setpoint2tet(pc, (tetrahedron) fliptets[0].tet); - setpoint2tet(pd, (tetrahedron) fliptets[0].tet); + setpoint2tet(pa, encode(fliptets[0])); + setpoint2tet(pb, encode(fliptets[0])); + setpoint2tet(pc, encode(fliptets[0])); + setpoint2tet(pd, encode(fliptets[0])); - if (fc->enqflag > 0) { + if (flipflag > 0) { // Queue faces which may be locally non-Delaunay. - flippush(flipstack, &(fliptets[0])); // [a,b,c] (opposite to new point). - if (fc->enqflag > 1) { - for (i = 0; i < 3; i++) { - esym(fliptets[0], newface); - flippush(flipstack, &newface); - enextself(fliptets[0]); - } + for (i = 0; i < 3; i++) { + esym(fliptets[0], newface); + flippush(flipstack, &newface); + enextself(fliptets[0]); } + flippush(flipstack, &(fliptets[0])); } recenttet = fliptets[0]; @@ -8100,7 +7926,7 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) /////////////////////////////////////////////////////////////////////////////// // // -// flipnm() Flip an edge through a sequence of elementary flips. // +// flipnm() Try to flip an edge through a sequence of elementary flips. // // // // 'abtets' is an array of 'n' tets in the star of edge [a,b].These tets are // // ordered in a counterclockwise cycle with respect to the vector a->b, i.e.,// @@ -8125,6 +7951,7 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) // - Neither a nor b is 'dummypoint'. // // - [a,b] must not be a segment. // // // +// // /////////////////////////////////////////////////////////////////////////////// int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, @@ -8132,19 +7959,27 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, { triface fliptets[3], spintet, flipedge; triface *tmpabtets, *parytet; + face checksh; + face checkseg, *paryseg; point pa, pb, pc, pd, pe, pf; - REAL ori; - int hullflag; + point tmppts[3]; + REAL abovept[3]; + REAL ori, ori1, ori2; int reducflag, rejflag; + int hullflag; int reflexlinkedgecount; int edgepivot; int n1, nn; - int t1ver; int i, j; pa = org(abtets[0]); pb = dest(abtets[0]); + if (b->verbose > 2) { + printf(" flipnm(%d): (%d, %d) - n(%d), e(%d).\n", level, pointmark(pa), + pointmark(pb), n, abedgepivot); + } + if (n > 3) { // Try to reduce the size of the Star(ab) by flipping a face in it. reflexlinkedgecount = 0; @@ -8152,7 +7987,9 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, for (i = 0; i < n; i++) { // Let the face of 'abtets[i]' be [a,b,c]. if (checksubfaceflag) { - if (issubface(abtets[i])) { + // Do not flip this face if it is a constraining face. + tspivot(abtets[i], checksh); + if (checksh.sh != NULL) { continue; // Skip a subface. } } @@ -8161,28 +7998,30 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, (elemcounter(abtets[(i - 1 + n) % n]) > 1)) { continue; } - pc = apex(abtets[i]); pd = apex(abtets[(i + 1) % n]); pe = apex(abtets[(i - 1 + n) % n]); if ((pd == dummypoint) || (pe == dummypoint)) { - continue; // [a,b,c] is a hull face. + // [a,b,c] is a hull face, it is not flipable. + continue; } - if (checkinverttetflag) { // The mesh contains inverted (or degenerated) elements. // Only do check if both elements are valid. - ori = orient3d(pa, pb, pc, pd); - if (ori < 0) { - ori = orient3d(pb, pa, pc, pe); - } - if (ori >= 0) { - continue; // An invalid tet. + if (pc != dummypoint) { + ori = orient3d(pa, pb, pc, pd); + if (ori < 0) { + ori = orient3d(pb, pa, pc, pe); + } + if (ori >= 0) { + continue; // An invalid tet. + } + } else { + continue; } } // if (checkinverttetflag) - // Decide whether [a,b,c] is flippable or not. - reducflag = 0; + reducflag = 0; // Not reducible. hullflag = (pc == dummypoint); // pc may be dummypoint. if (hullflag == 0) { @@ -8233,7 +8072,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, if (reducflag) { // [a,b,c] could be removed by a 2-to-3 flip. rejflag = 0; - if (fc->checkflipeligibility) { + if (fc != NULL) { // Check if the flip can be performed. rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, level, abedgepivot, fc); @@ -8242,7 +8081,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Do flip: [a,b,c] => [e,d]. fliptets[0] = abtets[i]; fsym(fliptets[0], fliptets[1]); // abtets[i-1]. - flip23(fliptets, hullflag, fc); + flip23(fliptets, hullflag, 0, 0); // Shrink the array 'abtets', maintain the original order. // Two tets 'abtets[i-1] ([a,b,e,c])' and 'abtets[i] ([a,b,c,d])' @@ -8260,9 +8099,11 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // [n-2] |___________| [n-2] |___________| // [n-1] |___________| [n-1] |_[i]_2-t-3_| // - edestoppoself(fliptets[0]); // [a,b,e,d] + eprevself(fliptets[0]); + esymself(fliptets[0]); + enextself(fliptets[0]); // [a,b,e,d] // Increase the counter of this new tet (it is in Star(ab)). - increaseelemcounter(fliptets[0]); + increaseelemcounter(fliptets[0]); //marktest(fliptets[0]); abtets[(i - 1 + n) % n] = fliptets[0]; for (j = i; j < n - 1; j++) { abtets[j] = abtets[j + 1]; // Upshift @@ -8293,10 +8134,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Star(ab) is reduced. Try to flip the edge [a,b]. nn = flipnm(abtets, n - 1, level, abedgepivot, fc); - if (nn == 2) { - // The edge has been flipped. - return nn; - } else { // if (nn > 2) + if (nn > 2) { // The edge is not flipped. if (fc->unflip || (ori == 0)) { // Undo the previous 2-to-3 flip, i.e., do a 3-to-2 flip to @@ -8306,15 +8144,17 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Remeber that 'abtets[i-1]' is [a,b,e,d]. We can use it to // find another two tets [e,d,b,c] and [e,d,c,a]. fliptets[0] = abtets[(i-1 + (n-1)) % (n-1)]; // [a,b,e,d] - edestoppoself(fliptets[0]); // [e,d,a,b] + eprevself(fliptets[0]); + esymself(fliptets[0]); + enextself(fliptets[0]); // [e,d,a,b] fnext(fliptets[0], fliptets[1]); // [1] is [e,d,b,c] fnext(fliptets[1], fliptets[2]); // [2] is [e,d,c,a] assert(apex(fliptets[0]) == oppo(fliptets[2])); // SELF_CHECK // Restore the two original tets in Star(ab). - flip32(fliptets, hullflag, fc); + flip32(fliptets, hullflag, 0, 0); // Marktest the two restored tets in Star(ab). for (j = 0; j < 2; j++) { - increaseelemcounter(fliptets[j]); + increaseelemcounter(fliptets[j]); //marktest(fliptets[j]); } // Expand the array 'abtets', maintain the original order. for (j = n - 2; j>= i; j--) { @@ -8333,14 +8173,27 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } // if (upflip || (ori == 0)) } // if (nn > 2) + if (nn == 2) { //if ((nn == 2) || !fullsearch) { + // The edge has been flipped. + return nn; + } if (!fc->unflip) { // The flips are not reversed. The current Star(ab) can not be - // further reduced. Return its current size (# of tets). + // further reduced. Return its size (# of tets). return nn; } // unflip is set. // Continue the search for flips. - } + } else { + if (b->verbose > 2) { + printf(" -- Reject a 2-to-3 flip at star face (%d, %d, %d)", + pointmark(pa), pointmark(pb), pointmark(pc)); + printf(", link (%d)\n", level); + } + if (fc != NULL) { + fc->rejf23count++; + } + } // if (rejflag) } // if (reducflag) } // i @@ -8349,6 +8202,16 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // There are reflex edges in the Link(ab). if (((b->fliplinklevel < 0) && (level < autofliplinklevel)) || ((b->fliplinklevel >= 0) && (level < b->fliplinklevel))) { + // Record the largest level. + if ((level + 1) > maxfliplinklevel) { + maxfliplinklevel = level + 1; + } + if (fc != NULL) { + // Increase the link level counter. + if ((level + 1) > fc->maxflippedlinklevelcount) { + fc->maxflippedlinklevelcount = level + 1; + } + } // Try to reduce the Star(ab) by flipping a reflex edge in Link(ab). for (i = 0; i < n; i++) { // Do not flip this face [a,b,c] if there are two Stars involved. @@ -8358,14 +8221,13 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } pc = apex(abtets[i]); if (pc == dummypoint) { - continue; // [a,b] is a hull edge. + continue; // [a,b,dummypoint] is a hull edge. } pd = apex(abtets[(i + 1) % n]); pe = apex(abtets[(i - 1 + n) % n]); if ((pd == dummypoint) || (pe == dummypoint)) { continue; // [a,b,c] is a hull face. } - if (checkinverttetflag) { // The mesh contains inverted (or degenerated) elements. // Only do check if both elements are valid. @@ -8403,15 +8265,21 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // An edge is selected. if (checksubsegflag) { // Do not flip it if it is a segment. - if (issubseg(flipedge)) { - if (fc->collectencsegflag) { - face checkseg, *paryseg; - tsspivot1(flipedge, checkseg); - if (!sinfected(checkseg)) { - // Queue this segment in list. - sinfect(checkseg); - caveencseglist->newindex((void **) &paryseg); - *paryseg = checkseg; + tsspivot1(flipedge, checkseg); + if (checkseg.sh != NULL) { + if (b->verbose > 2) { + printf(" -- Can't flip a link(%d) segment (%d, %d).\n", + level, pointmark(org(flipedge)), pointmark(dest(flipedge))); + } + if (fc != NULL) { + fc->encsegcount++; + if (fc->collectencsegflag) { + if (!sinfected(checkseg)) { + // Queue this segment in list. + sinfect(checkseg); + caveencseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } } } continue; @@ -8426,7 +8294,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, spintet = flipedge; while (1) { n1++; - j += (elemcounter(spintet)); + j += (elemcounter(spintet)); //if (marktested(spintet)) j++; fnextself(spintet); if (spintet.tet == flipedge.tet) break; } @@ -8438,8 +8306,14 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Only two tets can be marktested. assert(j == 2); + flipstarcount++; + // Record the maximum star size. + if (n1 > maxflipstarsize) { + maxflipstarsize = n1; + } if ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) { - // The star size exceeds the given limit. + // The star size exceeds the given limit (-LL__). + skpflipstarcount++; continue; // Do not flip it. } @@ -8456,6 +8330,15 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, fnextself(spintet); if (spintet.tet == flipedge.tet) break; } + // SELF_CHECK BEGIN + // These two tets are inside both of the Stars. + assert(elemcounter(tmpabtets[0]) == 2); + assert(elemcounter(tmpabtets[1]) == 2); + // Marktest the tets in Star(flipedge) but not in Star(ab). + for (j = 2; j < n1; j++) { + assert(elemcounter(tmpabtets[j]) == 1); + //marktest(tmpabtets[j]); + } // Try to flip the selected edge away. nn = flipnm(tmpabtets, n1, level + 1, edgepivot, fc); @@ -8476,8 +8359,10 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, esymself(spintet); eprevself(spintet); // [a,b,e,d] } // edgepivot == 2 - assert(elemcounter(spintet) == 0); // It's a new tet. - increaseelemcounter(spintet); // It is in Star(ab). + //assert(!marktested(spintet)); // It's a new tet. + assert(elemcounter(spintet) == 0); + //marktest(spintet); // It is in Star(ab). + increaseelemcounter(spintet); // Put the new tet at [i-1]-th entry. abtets[(i - 1 + n) % n] = spintet; for (j = i; j < n - 1; j++) { @@ -8504,10 +8389,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Continue to flip the edge [a,b]. nn = flipnm(abtets, n - 1, level, abedgepivot, fc); - if (nn == 2) { - // The edge has been flipped. - return nn; - } else { // if (nn > 2) { + if (nn > 2) { // The edge is not flipped. if (fc->unflip) { // Recover the flipped edge ([c,b] or [a,c]). @@ -8573,6 +8455,10 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } // if (unflip) } // if (nn > 2) + if (nn == 2) { //if ((nn == 2) || !fullsearch) { + // The edge has been flipped. + return nn; + } if (!fc->unflip) { // The flips are not reversed. The current Star(ab) can not be // further reduced. Return its size (# of tets). @@ -8598,56 +8484,70 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, delete [] tmpabtets; } } // i + } else { + if (b->verbose > 2) { + printf(" -- Maximal link level (%d) reached at edge (%d, %d).\n", + level, pointmark(org(abtets[0])), pointmark(dest(abtets[0]))); + } + if (fc != NULL) { + fc->misfliplinklevelcount++; + } } // if (level...) } // if (reflexlinkedgecount > 0) } else { // Check if a 3-to-2 flip is possible. - // Let the three apexes be c, d,and e. Hull tets may be involved. If so, - // we rearrange them such that the vertex e is dummypoint. - hullflag = 0; + pc = apex(abtets[0]); + pd = apex(abtets[1]); + pe = apex(abtets[2]); - if (apex(abtets[0]) == dummypoint) { - pc = apex(abtets[1]); - pd = apex(abtets[2]); - pe = apex(abtets[0]); + // Check if one of them is dummypoint. If so, we rearrange the vertices + // c, d, and e into p0, p1, and p2, such that p2 is the dummypoint. + hullflag = 0; + if (pc == dummypoint) { + hullflag = 1; + tmppts[0] = pd; + tmppts[1] = pe; + tmppts[2] = pc; + } else if (pd == dummypoint) { hullflag = 1; - } else if (apex(abtets[1]) == dummypoint) { - pc = apex(abtets[2]); - pd = apex(abtets[0]); - pe = apex(abtets[1]); + tmppts[0] = pe; + tmppts[1] = pc; + tmppts[2] = pd; + } else if (pe == dummypoint) { hullflag = 1; + tmppts[0] = pc; + tmppts[1] = pd; + tmppts[2] = pe; } else { - pc = apex(abtets[0]); - pd = apex(abtets[1]); - pe = apex(abtets[2]); - hullflag = (pe == dummypoint); - } + tmppts[0] = pc; + tmppts[1] = pd; + tmppts[2] = pe; + } reducflag = 0; rejflag = 0; if (checkinverttetflag) { - // Only do flip if no tet is inverted. - // NOTE: [a,b,c,d] may be degenerated (ori == 0). For instance, - // it is in the middle of a 4-to-4 flip. + // Only do flip if no tet is inverted (or degenerated). if (hullflag == 0) { - ori = orient3d(pa, pb, pc, pd); - if (ori <= 0) { + ori = orient3d(pa, pb, pc, pd); + if (ori < 0) { ori = orient3d(pa, pb, pd, pe); if (ori < 0) { ori = orient3d(pa, pb, pe, pc); } - if (ori >= 0) { - return 3; // Either [a,b,d,e] or [a,b,e,c] is inverted. - } - } else { - return 3; // [a,b,c,d] is inverted. } } else { - ori = orient3d(pa, pb, pc, pd); - if (ori > 0) { - return 3; // [a,b,c,d] is inverted. + ori = orient3d(pa, pb, tmppts[0], tmppts[1]); + } + if (ori >= 0) { + if (b->verbose > 2) { + printf(" -- Hit a non-valid tet (%d, %d) - (%d, %d, %d)", + pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd), + pointmark(pe)); + printf(" at link(%d)\n", level); } + return 3; } } // if (checkinverttetflag) @@ -8660,29 +8560,97 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, if (ori < 0) { reducflag = 1; } + } else { + if (b->verbose > 2) { + printf(" -- Hit a chrismastree (%d, %d) - (%d, %d, %d)", + pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd), + pointmark(pe)); + printf(" at link(%d)\n", level); + } + if (fc != NULL) { + fc->chrismastreecount++; + } } } else { - // [a,b] is a hull edge. + // [a,b] is a hull edge. Moreover, the tet [a,b,p0,p1] is a hull tet + // ([a,b,p0] and [a,b,p1] are two hull faces). // This can happen when it is in the middle of a 4-to-4 flip. // Note that [a,b] may even be a non-convex hull edge. if (!nonconvex) { - // [a,b], [a,b,c] and [a,b,d] are on the convex hull. - // They four vertices are coplanar. A 2-to-2 flip is possible if - // [a,b] and [c,d] are intersecting each other. - // NOTE: The following test is not robust, should be replaced in - // the future. 2011-12-01. - REAL abovept[3], ori1, ori2; - calculateabovepoint4(pa, pb, pc, pd); - for (j = 0; j < 3; j++) abovept[j] = dummypoint[j]; - ori1 = orient3d(pc, pd, abovept, pa); - ori2 = orient3d(pc, pd, abovept, pb); - if (ori1 * ori2 < 0) { - reducflag = 1; // Flipable. - } - } - else { // if (nonconvex) - // A hull edge. The case is not fully handled. We do not flip it. - reducflag = 0; + // [a,b], [a,b,p0] and [a,b,p1] are on the convex hull. + ori = orient3d(pa, pb, tmppts[0], tmppts[1]); + if (ori == 0) { + // They four vertices are coplanar. A 2-to-2 flip is possible if + // [a,b] and [p0,p1] are intersecting each other. + // NOTE: The following test is not robust, should be replaced in + // the future. 2011-12-01. + calculateabovepoint4(pa, pb, tmppts[0], tmppts[1]); + for (j = 0; j < 3; j++) { + abovept[j] = dummypoint[j]; + } + // Make sure that no inverted face will be created, i.e., [p1,p0, + // abvpt,pa] and [p0,p1,abvpt,pb] must be valid tets. + ori1 = orient3d(tmppts[0], tmppts[1], abovept, pa); + ori2 = orient3d(tmppts[0], tmppts[1], abovept, pb); + if (ori1 * ori2 < 0) { + reducflag = 1; // Flipable. + } + if (!reducflag) { + if (b->verbose > 2) { + printf(" -- Hit a degenerate chrismastree (%d, %d)", + pointmark(pa), pointmark(pb)); + printf(" - (%d, %d, -1) at link(%d)\n", + pointmark(tmppts[0]), pointmark(tmppts[1]), level); + } + if (fc != NULL) { + fc->chrismastreecount++; + } + } + } else { + if (b->verbose > 2) { + printf(" -- Hit a convex hull edge (%d, %d) at link(%d).\n", + pointmark(pa), pointmark(pb), level); + } + if (fc != NULL) { + fc->convexhulledgecount++; + } + } + } else { // if (nonconvex) + // [a,b,p0] and [a,b,p1] must be two subfaces. + // Since [a,b] is not a segment. A 3-to-2 flip (including a 2-to-2 + // flip) is possible. + // Here we only do flip if there are exactly three tets containing + // the edge [p0,p1]. In this case, the other two tets at [p0,p1] + // (not [a,b,p0,p1]) must be valid. Since they already exist. + for (j = 0; j < 3; j++) { + if (apex(abtets[j]) == dummypoint) { + flipedge = abtets[(j + 1) % 3]; // [a,b,p0,p1]. + break; + } + } + // assert(j < 3); + eprevself(flipedge); + esymself(flipedge); + enextself(flipedge); // [p0,p1,a,b]. + assert(apex(flipedge) == pa); + spintet = flipedge; + j = 0; + while (1) { + j++; + fnextself(spintet); + if (spintet.tet == flipedge.tet) break; + } + if (j == 3) { + reducflag = 1; + } else { + if (b->verbose > 2) { + printf(" -- Hit a hull edge (%d, %d) at link(%d).\n", + pointmark(pa), pointmark(pb), level); + } + //if (fc != NULL) { + // fc->convexhulledgecount++; + //} + } } } // if (hullflag == 1) @@ -8695,7 +8663,8 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // 3-to-2 flip. nn = 0; for (j = 0; j < 3; j++) { - if (issubface(abtets[j])) { + tspivot(abtets[j], checksh); + if (checksh.sh != NULL) { nn++; // Found a subface. } } @@ -8705,38 +8674,34 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // the boundary recovery phase. The neighbor subface is not yet // recovered. This edge should not be flipped at this moment. rejflag = 1; - } else if (nn == 2) { - // Found two subfaces. A 2-to-2 flip is possible. - if (nonconvex) { - // However, do not flip if one of the endpoints of this edge is a - // Steiner point on segment. This case may cause the vertex to be - // removed. It is not handled yet. - if ((pointtype(pa) == FREESEGVERTEX) || - (pointtype(pb) == FREESEGVERTEX)) { - rejflag = 1; - } - } } } - if (!rejflag && fc->checkflipeligibility) { - // Here we must exchange 'a' and 'b'. Since in the check... function, + if (!rejflag && (fc != NULL)) { + //rejflag = checkflipeligibility(2, tmppts[0], tmppts[1], tmppts[2], + // pa, pb, level, abedgepivot, fc); + // Here we must permute 'a' and 'b'. Since in the check... function, // we assume the following point sequence, 'a,b,c,d,e', where // the face [a,b,c] will be flipped and the edge [e,d] will be // created. The two new tets are [a,b,c,d] and [b,a,c,e]. - rejflag = checkflipeligibility(2, pc, pd, pe, pb, pa, level, - abedgepivot, fc); + rejflag = checkflipeligibility(2, tmppts[0], tmppts[1], tmppts[2], + pb, pa, level, abedgepivot, fc); } if (!rejflag) { // Do flip: [a,b] => [c,d,e] - flip32(abtets, hullflag, fc); + flip32(abtets, hullflag, 0, 0); + sucflipstarcount++; if (fc->remove_ndelaunay_edge) { if (level == 0) { - // It is the desired removing edge. Check if we have improved - // the objective function. - if ((fc->tetprism_vol_sum >= 0.0) || - (fabs(fc->tetprism_vol_sum) < fc->bak_tetprism_vol)) { - // No improvement! flip back: [c,d,e] => [a,b]. - flip23(abtets, hullflag, fc); + // It is the desired removing edge. + if (tetprism_vol_sum >= fc->bak_tetprism_vol) { + if (b->verbose > 2) { + printf(" -- Reject to flip (%d, %d) at link(%d)\n", + pointmark(pa), pointmark(pb), level); + printf(" due to an increased volume (%.17g).\n", + tetprism_vol_sum - fc->bak_tetprism_vol); + } + // flip back: [c,d,e] => [a,b]. + flip23(abtets, hullflag, 0, 0); // Increase the element counter -- They are in cavity. for (j = 0; j < 3; j++) { increaseelemcounter(abtets[j]); @@ -8766,7 +8731,15 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } } // if (fc->collectnewtets) return 2; - } + } else { + if (b->verbose > 2) { + printf(" -- Reject a 3-to-2 flip (%d, %d) at link(%d).\n", + pointmark(pa), pointmark(pb), level); + } + if (fc != NULL) { + fc->rejf32count++; + } + } // if (rejflag) } // if (reducflag) } // if (n == 3) @@ -8821,7 +8794,7 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, // 'abtets[1]' is [d,c,e,a] or [#,#,#,a]. if (fc->unflip) { // Do a 2-to-3 flip to recover the edge [a,b]. There may be hull tets. - flip23(abtets, 1, fc); + flip23(abtets, 1, 0, 0); if (fc->collectnewtets) { // Pop up new (flipped) tets from the stack. if (abedgepivot == 0) { @@ -8835,7 +8808,9 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, } // The initial size of Star(ab) is 3. nn++; - } + } else { // nn > 2. + // The edge [a,b] exists. + } // Walk through the performed flips. for (i = nn; i < n; i++) { @@ -8861,7 +8836,7 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, fnext(fliptets[1], fliptets[2]); // [e,d,c,a] // Do a 3-to-2 flip: [e,d] => [a,b,c]. // NOTE: hull tets may be invloved. - flip32(fliptets, 1, fc); + flip32(fliptets, 1, 0, 0); // Expand the array 'abtets', maintain the original order. // The new array length is (i+1). for (j = i - 1; j >= t; j--) { @@ -8948,6 +8923,9 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, printf(" Release %d spaces at f[%d].\n", n1, i); } delete [] tmpabtets; + } else { + assert(fliptype == 0); // Not a saved flip. + assert(0); // Should be not possible. } } // i @@ -8956,196 +8934,937 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, /////////////////////////////////////////////////////////////////////////////// // // -// insertpoint() Insert a point into current tetrahedralization. // +// lawsonflip3d() A three-dimensional Lawson's flip algorithm. // +// // +// The basic idea of Lawson's algorithm is to flip every face of the triang- // +// ulation which is not locally Delaunay until no such face exists, then the // +// triangulation is a DT. However, in 3D, it is common that a face which is // +// not locally Delaunay and is not flippable. Hence, Laowson's algorithm may // +// get stuck. It is still an open problem, whether there exists a flip algo- // +// rithm which has a guarantee to create a DT in 3D. // +// // +// If only one vertex is added into a DT, then Lawson's flip algorithm is // +// guaranteed to transform it into a new DT [Joe'91]. Moreover, an arbitrary // +// order of flips is sufficient [Edelsbrunner & Shah'96]. // +// // +// In practice, it is desired to remove not locally Delaunay faces by flips // +// as many as possible. For this purpose, a second queue is used to store // +// the not locally Delaunay faces which are not flippable, and try them at a // +// later time. // // // -// The Bowyer-Watson (B-W) algorithm is used to add a new point p into the // -// tetrahedralization T. It first finds a "cavity", denoted as C, in T, C // -// consists of tetrahedra in T that "conflict" with p. If T is a Delaunay // -// tetrahedralization, then all boundary faces (triangles) of C are visible // -// by p, i.e.,C is star-shaped. We can insert p into T by first deleting all // -// tetrahedra in C, then creating new tetrahedra formed by boundary faces of // -// C and p. If T is not a DT, then C may be not star-shaped. It must be // -// modified so that it becomes star-shaped. // +// If 'newpt' (p) is not NULL, it is a new vertex just inserted into the // +// tetrahedralization T. // +// // +// 'flipflag' indicates the property of the tetrahedralization 'T' which // +// does not include 'p' yet. // +// // +// If 'peelsliverflag' is set, the purpose of calling Lawson's flip is to // +// remove "hull slivers". This flag only works with a non-convex mesh, i.e., // +// the mesh must contains boundaries (segments and subfaces). // +// // +// 'chkencflag' indicates whether segments, subfaces, and tets should be // +// checked (for encroaching and quality) after flips. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, - face *splitseg, insertvertexflags *ivf) +long tetgenmesh::lawsonflip3d(point newpt, int flipflag, int peelsliverflag, + int chkencflag, int flipedgeflag) { - arraypool *swaplist; - triface *cavetet, spintet, neightet, neineitet, *parytet; - triface oldtet, newtet, newneitet; - face checksh, neighsh, *parysh; + badface *popface, *bface; + triface fliptets[5], baktets[2]; + triface fliptet, neightet, *parytet; + face checksh, *parysh; face checkseg, *paryseg; - point *pts, pa, pb, pc, *parypt; - enum locateresult loc = OUTSIDE; + point *ppt, pd, pe, pf; + long flipcount; REAL sign, ori; - REAL attrib, volume; - bool enqflag; - int t1ver; - int i, j, k, s; + int convflag; + int n, i; + + // For removing hull slivers. + face neighsh; + point p1, p2; + point pa, pb, pc, rempt; + REAL ang; + long tetpeelcount; + int remflag; + + flipconstraints fc; if (b->verbose > 2) { - printf(" Insert point %d\n", pointmark(insertpt)); + printf(" Lawson flip %ld faces.\n", flippool->items); } - // Locate the point. - if (searchtet->tet != NULL) { - loc = (enum locateresult) ivf->iloc; - } + flipcount = flip23count + flip32count + flip44count; + tetpeelcount = opt_sliver_peels; - if (loc == OUTSIDE) { - if (searchtet->tet == NULL) { - if (!b->weighted) { - randomsample(insertpt, searchtet); - } else { - // Weighted DT. There may exist dangling vertex. - *searchtet = recenttet; - } - } - // Locate the point. - loc = locate(insertpt, searchtet, ivf->chkencflag); + if (flipedgeflag) { + fc.remove_ndelaunay_edge = 1; + fc.unflip = 1; // Unflip if the edge is not flipped. + fc.collectnewtets = 1; + assert(cavetetlist->objects == 0l); + assert(calc_tetprism_vol == 1); // Swith on. + } else { + assert(unflipqueue->objects == 0); // The second queue must be empty. } - ivf->iloc = (int) loc; // The return value. + while (1) { - if (b->weighted) { - if (loc != OUTSIDE) { - // Check if this vertex is regular. - pts = (point *) searchtet->tet; - assert(pts[7] != dummypoint); - sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt, - pts[4][3], pts[5][3], pts[6][3], pts[7][3], - insertpt[3]); - if (sign > 0) { - // This new vertex does not lie below the lower hull. Skip it. - setpointtype(insertpt, NREGULARVERTEX); - nonregularcount++; - ivf->iloc = (int) NONREGULAR; - return 0; - } - } - } + while (flipstack != (badface *) NULL) { - // Create the initial cavity C(p) which contains all tetrahedra that - // intersect p. It may include 1, 2, or n tetrahedra. - // If p lies on a segment or subface, also create the initial sub-cavity - // sC(p) which contains all subfaces (and segment) which intersect p. + // Pop a face from the stack. + popface = flipstack; + flipstack = flipstack->nextitem; // The next top item in stack. + fliptet = popface->tt; + flippool->dealloc((void *) popface); - if (loc == OUTSIDE) { - flip14count++; - // The current hull will be enlarged. - // Add four adjacent boundary tets into list. - for (i = 0; i < 4; i++) { - decode(searchtet->tet[i], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - } - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; - } else if (loc == INTETRAHEDRON) { - flip14count++; - // Add four adjacent boundary tets into list. - for (i = 0; i < 4; i++) { - decode(searchtet->tet[i], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - } - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; - } else if (loc == ONFACE) { - flip26count++; - // Add six adjacent boundary tets into list. - j = (searchtet->ver & 3); // The current face number. - for (i = 1; i < 4; i++) { - decode(searchtet->tet[(j + i) % 4], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - } - decode(searchtet->tet[j], spintet); - j = (spintet.ver & 3); // The current face number. - for (i = 1; i < 4; i++) { - decode(spintet.tet[(j + i) % 4], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - } - infect(spintet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = spintet; - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; + // Skip it if it is a dead tet (destroyed by previous flips). + if (isdeadtet(fliptet)) continue; + // Skip it if it is not the same tet as we saved. + if (!facemarked(fliptet)) continue; - if (ivf->splitbdflag) { - if (splitsh != NULL) { - // Create the initial sub-cavity sC(p). - smarktest(*splitsh); - caveshlist->newindex((void **) &parysh); - *parysh = *splitsh; + unmarkface(fliptet); + + // FOR DEBUG + if (flipflag == 1) { + assert(oppo(fliptet) == newpt); } - } // if (splitbdflag) - } else if (loc == ONEDGE) { - flipn2ncount++; - // Add all adjacent boundary tets into list. - spintet = *searchtet; - while (1) { - eorgoppo(spintet, neightet); - decode(neightet.tet[neightet.ver & 3], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - edestoppo(spintet, neightet); - decode(neightet.tet[neightet.ver & 3], neightet); - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; - infect(spintet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = spintet; - fnextself(spintet); - if (spintet.tet == searchtet->tet) break; - } // while (1) - if (ivf->splitbdflag) { - // Create the initial sub-cavity sC(p). - if ((splitseg != NULL) && (splitseg->sh != NULL)) { - smarktest(*splitseg); - splitseg->shver = 0; - spivot(*splitseg, *splitsh); + if (ishulltet(fliptet)) { + // It is a hull tet. + if (((flipflag == 4) || peelsliverflag) && !b->convex) { + fliptet.ver = epivot[fliptet.ver & 3]; + if (oppo(fliptet) == dummypoint) { + // It's a hull face (oppo(fliptet) == dummypoint). + // Check if there exists a "hull sliver". + fsymself(fliptet); + tspivot(fliptet, checksh); + assert(checksh.sh != NULL); + for (i = 0; i < 3; i++) { + sspivot(checksh, checkseg); + if (checkseg.sh == NULL) { + spivot(checksh, neighsh); + assert(neighsh.sh != NULL); + if (sorg(checksh) != sdest(neighsh)) { + sesymself(neighsh); + } + stpivot(neighsh, neightet); + if (neightet.tet == fliptet.tet) { + // Found a hull sliver 'neightet' [d,e,a,b], where [d,e,a] + // and [e,d,b] are two hull faces. Normally, a 3-to-2 flip + // (including a 2-to-2 flip on hull subfaces) can remove + // this hull sliver. + // A special case is the existence of a hull tet [b,a,d,-1] + // or [a,b,e,-1]. It was creared by a previous hull tet + // removal. Moreover, 'd' or 'e' might be Steiner points + // on segments [a,b]. In this case, eithe [a,d],[b,d] or + // [a,e],[b,e] are subsegments. If so, a 4-to-1 flip + // (including a 3-to-1, and maybe a 2-to-1 flip) should be + // applied to remove an exterior vertex. + // See figures (2011-11-13 and 15) for illustraions. + + // First check if the face [b,a,d] is a hull face. + eprev(neightet, fliptets[0]); + esymself(fliptets[0]); // [d,a,b,e] + enextself(fliptets[0]); // [a,b,d,e] + fsymself(fliptets[0]); // [b,a,d,#] + if (oppo(fliptets[0]) != dummypoint) { + // Second check if the face [a,b,e] is a hull face. + enext(neightet, fliptets[0]); + esymself(fliptets[0]); // [a,e,b,d] + eprevself(fliptets[0]); // [b,a,e,d] + fsymself(fliptets[0]); // [b,a,e,#] + } + + if (oppo(fliptets[0]) != dummypoint) { + // Make sure we do not create an "inverted triangle" in the + // boundary, i.e., in exactly planar case, d and e must + // lie in the different sides of the edge [a,b]. + // If the dihedral angle formed by [a,b,e] and [a,b,d] is + // larger than 90 degree, we can remove [a,b,e,d]. + fliptets[0] = neightet; // [e,d,a,b] + eprevself(fliptets[0]); + esymself(fliptets[0]); + enextself(fliptets[0]); // [a,b,e,d]. + pa = org(fliptets[0]); + pb = dest(fliptets[0]); + p1 = apex(fliptets[0]); // pe + p2 = oppo(fliptets[0]); // pd + ang = facedihedral(pa, pb, p1, p2); + ang *= 2.0; + if (ang > PI) { + if (b->verbose > 2) { + printf(" Remove a hull sliver (%d, %d, %d, %d).\n", + pointmark(org(fliptet)), pointmark(dest(fliptet)), + pointmark(apex(fliptet)), pointmark(oppo(fliptet))); + } + // Remove the ill tet from bounday. + fliptets[0] = neightet; // [e,d,a,b] + fnext(fliptets[0], fliptets[1]); // [e,d,b,c] + fnext(fliptets[1], fliptets[2]); // [e,d,c,a] + // FOR DEBUG + fnext(fliptets[2], fliptets[3]); + assert(fliptets[3].tet == neightet.tet); + assert(oppo(fliptets[1]) == dummypoint); + // Do a 3-to-2 flip to remove the ill tet. Two hull tets + // are removed toether. Two hull subfaces are flipped. + flip32(fliptets, 1, flipflag, 0); + // Update counters. + flip32count--; + flip22count--; + opt_sliver_peels++; + } + } else { + // There exists a thrid hull tet at vertex. + rempt = apex(fliptets[0]); + if (pointmark(rempt) > + (in->numberofpoints - (in->firstnumber ? 0 : 1))) { + if (pointtype(rempt) == FREESEGVERTEX) { + st_segref_count--; + } else if (pointtype(rempt) == FREEFACETVERTEX) { + st_facref_count--; + } else { + assert(0); // Impossible. + } + if (b->verbose > 2) { + printf(" Remove an exterior Steiner vertex %d.\n", + pointmark(rempt)); + } + if (removevertexbyflips(rempt)) { + // exsteinercount++; + } else { + assert(0); // Not possible. + } + } else { + //if (b->verbose > 2) { + // printf(" Remove an exterior input vertex %d.\n", + // pointmark(rempt)); + //} + // Comment: We do not remove an input point. + } + } + break; + } + } // if (checkseg.sh == NULL) + senextself(checksh); + } // i + } else { + // It's a hull edge. + assert(apex(fliptet) == dummypoint); + if (!peelsliverflag) { + // The hull edge may be not locally Delaunay. Put interior + // faces at this edge into 'flipstack' for flipping. + neightet = fliptet; // [a,b,c,d] ('c' is dummypoint). + fnextself(neightet); // [a,b,d,#1] ([a,b,d] is a hull face). + while (1) { + fnextself(neightet); // [a,b,#1,#2] + if (oppo(neightet) != dummypoint) { + // It is an interior face. + flippush(flipstack, &neightet); + } else { + // We assume the interior of the domain is connected. + // Hence we can hit hull faces only twice. + break; + } + } // while (1) + } // if (!peelsliverflag) + } + } // if ((flipflag == 4) || peelsliverflag) + + // Do not flip a hull face/edge UNLESS it is in the process of + // incrementally creating a DT in which the convex hull may be + // enlarged by the flips (when p lies outside of it). + if (flipflag != 1) { + continue; + } + } // if (ishulltet(fliptet)) + + if (peelsliverflag) { + continue; // Only check hull tets. } - if (splitsh != NULL) { - if (splitsh->sh != NULL) { - // Collect all subfaces share at this edge. - pa = sorg(*splitsh); - neighsh = *splitsh; - while (1) { - // Adjust the origin of its edge to be 'pa'. - if (sorg(neighsh) != pa) { - sesymself(neighsh); + + // Let 'fliptet' be [a,b,c,d], the face [a,b,c] is the flip face. + // Get its opposite tet [b,a,c,e]. + fsym(fliptet, neightet); + + if (ishulltet(neightet)) { + // It is a hull tet. + if (flipflag == 1) { + // Check if the new point is visible by the hull face. + ppt = (point *) neightet.tet; + ori = orient3d(ppt[4], ppt[5], ppt[6], newpt); orient3dcount++; + if (ori < 0) { + // Visible. Perform a 2-to-3 flip on the flip face. + fliptets[0] = fliptet; // [a,b,c,d], d = newpt. + fliptets[1] = neightet; // [b,a,c,e], c = dummypoint. + flip23(fliptets, 1, flipflag, chkencflag); // flip a hull tet. + //recenttet = fliptets[0]; + } else if (ori == 0) { + // Handle degenerate case ori == 0. + if (oppo(neightet) == newpt) { + // Two hull tets have the same base face. + if (b->verbose > 2) { + printf(" Close an open face (%d, %d, %d)\n", + pointmark(org(fliptet)), pointmark(dest(fliptet)), + pointmark(apex(fliptet))); + } + // The following code connect adjacent tets at corresponding + // sides of the two hull tets. It is hard to understand. + // See an example in 2011-11-11. + // First infect the two hull tets (they will be deleted). + infect(fliptet); + infect(neightet); + // Connect the actual adjacent tets. + for (i = 0; i < 3; i++) { + fnext(fliptet, fliptets[0]); + fnext(neightet, fliptets[1]); + if (!infected(fliptets[0])) { + assert(!infected(fliptets[1])); + bond(fliptets[0], fliptets[1]); + // Update the point-to-tet map. + pa = org(fliptet); + pb = dest(fliptet); + setpoint2tet(pa, encode(fliptets[0])); + setpoint2tet(pb, encode(fliptets[0])); + // Remeber a recent tet for point location. + recenttet = fliptets[0]; + // apex(fliptets[0]) is the new point. The opposite face may + // be not locally Delaunay. Put it in flip stack. + assert(apex(fliptets[0]) == newpt); // SELF_CHECK + esymself(fliptets[0]); + flippush(flipstack, &(fliptets[0])); + assert(apex(fliptets[1]) == newpt); // SELF_CHECK + esymself(fliptets[1]); + flippush(flipstack, &(fliptets[1])); + } + enextself(fliptet); + eprevself(neightet); + } + // Delete the two tets. + tetrahedrondealloc(fliptet.tet); + tetrahedrondealloc(neightet.tet); + // Update the hull size. + hullsize -= 2; } - // Add this face into list (in B-W cavity). - smarktest(neighsh); - caveshlist->newindex((void **) &parysh); - *parysh = neighsh; - // Add this face into face-at-splitedge list. - cavesegshlist->newindex((void **) &parysh); - *parysh = neighsh; - // Go to the next face at the edge. - spivotself(neighsh); - // Stop if all faces at the edge have been visited. - if (neighsh.sh == splitsh->sh) break; - if (neighsh.sh == NULL) break; - } // while (1) - } // if (not a dangling segment) + } + } // if (flipflag == 1) + + continue; // Do not flip a hull face. + } // if (ishulltet(neightet)) + + if (ishulltet(fliptet)) { + continue; // Do not flip a hull tet. } - } // if (splitbdflag) - } else if (loc == INSTAR) { + + if ((flipflag == 3) || (flipflag == 4)) { + if (checksubfaceflag) { + // Do not flip a subface. + tspivot(fliptet, checksh); + if (checksh.sh != NULL) { + if (chkencflag & 2) { + // Mesh refinement. + // Put this subface into list. + if (!smarktest2ed(checksh)) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = checksh; + smarktest2(checksh); // Only queue it once. + bface->forg = sorg(checksh); // An alive badface. + } + } + continue; + } + } + } // if ((flipflag == 3) || (flipflag == 4)) + + ppt = (point *) fliptet.tet; + pe = oppo(neightet); + + sign = insphere_s(ppt[4], ppt[5], ppt[6], ppt[7], pe); + + if (sign < 0) { + if (b->verbose > 3) { + printf(" A non-Delaunay face (%d, %d, %d) - %d, %d\n", + pointmark(org(fliptet)), pointmark(dest(fliptet)), + pointmark(apex(fliptet)), pointmark(oppo(fliptet)), + pointmark(pe)); + } + + // Try to flip this face. + pd = oppo(fliptet); + // Check the convexity of its three edges. + convflag = 1; + for (i = 0; i < 3; i++) { + p1 = org(fliptet); + p2 = dest(fliptet); + ori = orient3d(p1, p2, pd, pe); orient3dcount++; + if (ori < 0) { + // A locally non-convex edge. + convflag = -1; + break; + } else if (ori == 0) { + // A locally flat edge. + convflag = 0; + break; + } + enextself(fliptet); + } + + if (convflag > 0) { + // A 2-to-3 flip is found. + fliptets[0] = fliptet; // abcd, d may be the new vertex. + fliptets[1] = neightet; // bace. + if ((flipflag == 1) || (flipflag == 2)) { // CDT boundary recovery. + if (checksubfaceflag) { + // Check if a subface will be flipped. + tspivot(fliptets[0], checksh); + if (checksh.sh != NULL) { + assert(flipflag < 3); // 1 or 2. + // It is updateing a conforming DT or a CDT. + if (b->verbose > 3) { + printf(" Queue a flipped subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + for (i = 0; i < 2; i++) { + tsdissolve(fliptets[i]); // Disconnect the tet->sub bond. + } + stdissolve(checksh); // Disconnect the sub->tet bond. + // Add the missing subface into list. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } // if (checksh.sh != NULL) + } + } // if ((flipflag == 1) || (flipflag == 2)) + flip23(fliptets, 0, flipflag, chkencflag); + //recenttet = fliptets[0]; // for point location. + } else { + // The edge ('fliptet') is non-convex or flat. + if ((flipflag == 3) || (flipflag == 4)) { + // Do not flip a subsegment. + tsspivot1(fliptet, checkseg); + if (checkseg.sh != NULL) { + if (b->verbose > 3) { + printf(" Found a non-Delaunay segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + // Comment: this should be only possible when a new Steiner + // point is inserted on a segment nearby. + if (chkencflag & 1) { + // Put this segment into list. + if (!smarktest2ed(checkseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(checkseg); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. + } + } + continue; + } + } + + // A 3-to-2 or 4-to-4 may be possible. + esym(fliptet, fliptets[0]); // [b,a,d,c] + // assert(apex(fliptets[0]) == pd); + n = 0; + do { + fnext(fliptets[n], fliptets[n + 1]); + n++; + } while ((fliptets[n].tet != fliptet.tet) && (n < 5)); + + if (n == 3) { + // Found a 3-to-2 flip. + if ((flipflag == 1) || (flipflag == 2)) { // CDT boundary recovery. + if (checksubsegflag) { + // Check if the flip edge is subsegment. + tsspivot1(fliptets[0], checkseg); + if (checkseg.sh != NULL) { + if (!sinfected(checkseg)) { + // This subsegment will be flipped. Queue it. + if (b->verbose > 3) { + printf(" Queue a flipped segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + sinfect(checkseg); // Only save it once. + subsegstack->newindex((void **) &paryseg); + *paryseg = checkseg; + } + // Clean tet-to-seg pointers. + for (i = 0; i < 3; i++) { + tssdissolve1(fliptets[i]); + } + // Clean the seg-to-tet pointer. + sstdissolve1(checkseg); + } + } + if (checksubfaceflag) { + // Check if there are subfaces to be flipped. + for (i = 0; i < 3; i++) { + tspivot(fliptets[i], checksh); + if (checksh.sh != NULL) {//if (flipshs[i].sh != NULL) { + if (b->verbose > 2) { + printf(" Queue a flipped subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + tsdissolve(fliptets[i]); // Disconnect the tet->sub bond. + stdissolve(checksh); // Disconnect the sub->tet bond. + // Add the missing subface into list. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + } + } // if ((flipflag == 1) || (flipflag == 2)) + + // Now flip the edge. + flip32(fliptets, 0, flipflag, chkencflag); + //recenttet = fliptets[0]; // for point location. + } else { + // There are more than 3 tets shared at this edge. + if ((n == 4) && (convflag < 0)) { + // Check if a 4-to-4 flip is possible. + pf = apex(fliptets[3]); + if (pf == dummypoint) { + // It is a non-convex hull edge shared by four tets (two hull + // tets and two interior tets). + // Let the two interior tets be [a,b,c,d] and [b,a,c,e] where + // [a,b] be the hull edge, [a,b,c] be the interior face. + // [a,b,d] and [a,b,e] are two hull faces. + // A 4-to-4 flip is possible if the two new tets [e,d,b,c] + // and [e,d,c,a] are valid tets. + // Current status: + // 'fliptets[0]' is [a,b,e,c] + // 'fliptets[1]' is [a,b,c,d] + // 'fliptets[2]' is [a,b,d,f] (hull tet) + // 'fliptets[3]' is [a,b,f,e] (hull tet) + pa = org(fliptets[1]); + pb = dest(fliptets[1]); + pc = apex(fliptets[1]); + p1 = oppo(fliptets[1]); // pd + p2 = apex(fliptets[0]); // pe + ori = orient3d(p2, p1, pb, pc); + if (ori < 0) { + ori = orient3d(p2, p1, pc, pa); + if (ori < 0) { + convflag = -2; // A 4-to-4 flip is possible. + } + } + } + } // if ((n == 4) && (convflag < 0)) + if ((n == 4) && ((convflag == 0) || (convflag == -2))) { + // Found a 4-to-4 flip. + if (b->verbose > 3) { + printf(" A 4-to-4 flip (%d, %d) - (%d, %d).\n", + pointmark(org(fliptet)), pointmark(dest(fliptet)), + pointmark(pd), pointmark(pe)); + } + if ((flipflag == 1) || (flipflag == 2)) { // CDT boundary recovery + if (checksubsegflag) { + // Check if the flip edge is subsegment. + tsspivot1(fliptets[0], checkseg); + if (checkseg.sh != NULL) { + if (!sinfected(checkseg)) { + // This subsegment will be flipped. Queue it. + if (b->verbose > 3) { + printf(" Queue a flipped segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + sinfect(checkseg); // Only save it once. + subsegstack->newindex((void **) &paryseg); + *paryseg = checkseg; + } + // Clean the tet-to-seg pointers. + for (i = 0; i < 4; i++) { + tssdissolve1(fliptets[i]); + } + // Clean the seg-to-tet pointer. + sstdissolve1(checkseg); + } + } + if (checksubfaceflag) { + // Check if there are subfaces to be flipped. + for (i = 0; i < 4; i++) { + tspivot(fliptets[i], checksh); + if (checksh.sh != NULL) { + if (b->verbose > 3) { + printf(" Queue a flipped subface (%d,%d,%d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + tsdissolve(fliptets[i]); // Disconnect the tet->sub bond. + stdissolve(checksh); // Disconnect the sub->tet bond. + // Add the missing subface into list. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + } + } // if ((flipflag == 1) || (flipflag == 2)) + + // First do a 2-to-3 flip. + // Comment: This flip temporarily creates either a degenerated + // tet (convflag == 0) or an inverted tet (convflag < 0). + // It is removed by the followed 3-to-2 flip. + fliptets[0] = fliptet; // tet abcd, d is the new vertex. + baktets[0] = fliptets[2]; + baktets[1] = fliptets[3]; + // The flip may involve hull tets. + flip23(fliptets, 1, flipflag, chkencflag); + // Then do a 3-to-2 flip. + enextesymself(fliptets[0]); // fliptets[0] is edab. + eprevself(fliptets[0]); // tet badc, d is the new vertex. + fliptets[1] = baktets[0]; + fliptets[2] = baktets[1]; + flip32(fliptets, 1, flipflag, chkencflag); + flip23count--; + flip32count--; + flip44count++; + //recenttet = fliptets[0]; // for point location. + } else { + // This edge is shared by more than 4 tets. + if (b->verbose > 2) { + printf(" An unflippable non-Delaunay edge (%d,%d).\n", + pointmark(org(fliptet)), pointmark(dest(fliptet))); + } + remflag = 0; + if (flipedgeflag == 2) { + // Try to flip this edge by my edge flip algorithm. + // Remember the the objective value (volume of all tetprisms). + fc.bak_tetprism_vol = tetprism_vol_sum; + if (removeedgebyflips(&fliptet, &fc) == 2) { + if (b->verbose > 2) { + printf(" Decreased quantity: %.17g.\n", + fc.bak_tetprism_vol - tetprism_vol_sum); + } + // Queue new faces in flipstack. + for (i = 0; i < cavetetlist->objects; i++) { + parytet = (triface *) fastlookup(cavetetlist, i); + if (!isdeadtet(*parytet)) { // Skip a dead tet. + for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { + // Avoid queue a face twice. + fsym(*parytet, neightet); + if (!facemarked(neightet)) { + //flippush(flipstack, parytet); + bface = (badface *) flippool->alloc(); + bface->tt = *parytet; + markface(bface->tt); + bface->forg = org(bface->tt); // An alive badface. + bface->fdest = dest(bface->tt); + bface->fapex = apex(bface->tt); + // bface->foppo = oppo(bface->tt); + // Push this face into stack. + bface->nextitem = flipstack; + flipstack = bface; + } + } // parytet->ver + } + } // i + cavetetlist->restart(); + remflag = 1; + } + } + if (!remflag) { + // Found an unflippable non-Delaunay edge. + if (flipedgeflag > 0) { // if (flipflag > 1) { + // Save this face (of the edge) in a second queue. + unflipqueue->newindex((void **) &bface); + bface->tt = fliptet; + bface->forg = org(fliptet); + bface->fdest = dest(fliptet); + bface->fapex = apex(fliptet); // FOR DEBUG. + } + } + } + } // if (n > 3) + } // if (convflag <= 0) + } // if (sign < 0) + + } // while (flipstack != NULL) + + + break; + + } // while (1) + + + if (b->verbose > 2) { + printf(" Total %ld flips", flip23count + flip32count + flip44count + - flipcount); + if ((flipflag == 4) || peelsliverflag) { + printf(", %ld sliver peels", opt_sliver_peels - tetpeelcount); + } + printf("\n"); + } + + + return flip23count + flip32count + flip44count - flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertvertex() Insert a point into current tetrahedralization. // +// // +// This routine implements the famous Bowyer-Watson (B-W) algorithm to add a // +// new point p into current tetrahedralization, denoted as T. The baisc idea // +// of B-W algorithm is: first finds a "cavity", denoted as C inside T, where // +// C is a simplicial polyhedron formed by a union of tetrahedra in T. If all // +// boundary faces (triangles) of C are visible by p, i.e., C is star-shaped, // +// then T can be re-tetrahedralized by first deleting all old tetrahedra in // +// C, then replacing new tetrahedra formed by boundary faces of C and p. The // +// result is that p becomesis a vertex of T. // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, + face *splitseg, insertvertexflags *ivf) +{ + arraypool *swaplist; // for updating cavity. + triface *cavetet, spintet, neightet, neineitet, *parytet; + triface oldtet, newtet, newneitet; + face checksh, *parysh, neighsh, spinsh; + face checkseg, *paryseg; + point *pts, pa, pb, pc, *parypt; + badface *bface; + enum locateresult loc; + REAL sign, ori; + REAL rd, cent[3]; + REAL attrib, volume; + long cutcount, cutshcount, tetcount = 0; + long bakhullsize; + bool enqflag; + int i, j, k, s; + + int rejptflag, encptflag; // for protecting balls. + int bgmloc; + +#ifdef WITH_RUNTIME_COUNTERS + clock_t tstart, tend; +#endif + + if (b->verbose > 2) { + printf(" Insert point %d\n", pointmark(insertpt)); + } + + + // Locate the point. + loc = OUTSIDE; // Set a default value. + + if (searchtet->tet != NULL) { + loc = (enum locateresult) ivf->iloc; + } + + if (loc == OUTSIDE) { +#ifdef WITH_RUNTIME_COUNTERS + tstart = clock(); +#endif + tetcount = ptloc_count; // Count the number of walked tets. + if (searchtet->tet == NULL) { + if (!b->weighted) { + if (b->btree) { + btree_search(insertpt, searchtet); + } else if (b->hilbertcurve) { // -U + *searchtet = recenttet; + } else { // -u0 + randomsample(insertpt, searchtet); + } + } else { + // There may exist dangling vertex. + *searchtet = recenttet; + } + } + // Locate the point. Use 'randflag' if the mesh is non-convex. + loc = locate(insertpt, searchtet, ivf->chkencflag, checksubfaceflag); + if (b->verbose > 3) { + printf(" Walk distance (# tets): %ld\n", ptloc_count-tetcount); + } + if (ptloc_max_count < (ptloc_count - tetcount)) { + ptloc_max_count = (ptloc_count - tetcount); + } +#ifdef WITH_RUNTIME_COUNTERS + tend = clock(); + t_ptloc += (tend - tstart); +#endif + } + + if (b->verbose > 3) { + printf(" Located tet (%d, %d, %d, %d).\n", + pointmark(org(*searchtet)), pointmark(dest(*searchtet)), + pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); + } + +#ifdef WITH_RUNTIME_COUNTERS + tstart = clock(); +#endif + + if (b->weighted) { + if (loc != OUTSIDE) { + // Check if this vertex is regular. + pts = (point *) searchtet->tet; + assert(pts[7] != dummypoint); + sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], pts[7][3], + insertpt[3]); + if (sign > 0) { + // This new vertex does not lie below the lower hull. Skip it. + if (b->verbose > 2) { + printf(" The point is above the lower hull, skipped.\n"); + } + return OUTSIDE; + } + } + } + + // Create the initial cavity C(p) which contains all tetrahedra directly + // intersect with p. + // If 'bowywat > 2' and p lies on a segment or subface, also create the + // initial sub-cavity sC(p) which contains all subfaces (and segment) + // which directly intersect with p. + // If 'bowywat > 2', the initial C(p) is validated, i.e., all boundary + // faces of C(p) should be visible by p. + + // Remember the current hullsize. It is used to restore the hullsize + // if the new point is rejected for insertion. + bakhullsize = hullsize; + + if (loc == OUTSIDE) { + if (b->verbose > 3) { + printf(" Outside hull.\n"); + } + // The current hull will be enlarged. + // Add four adjacent boundary tets into list. + for (i = 0; i < 4; i++) { + decode(searchtet->tet[i], neightet); + neightet.ver = epivot[neightet.ver & 3]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + if ((point) searchtet->tet[7] == dummypoint) hullsize--; + // tetrahedrondealloc(searchtet->tet); + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + flip14count++; + } else if (loc == INTETRAHEDRON) { + if (b->verbose > 3) { + printf(" Inside tet.\n"); + } + // Add four adjacent boundary tets into list. + for (i = 0; i < 4; i++) { + decode(searchtet->tet[i], neightet); + neightet.ver = epivot[neightet.ver & 3]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + // tetrahedrondealloc(searchtet->tet); + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + flip14count++; + } else if (loc == ONFACE) { + if (b->verbose > 3) { + printf(" On face.\n"); + } + // Add six adjacent boundary tets into list. + j = (searchtet->ver & 3); // The current face number. + for (i = 1; i < 4; i++) { + decode(searchtet->tet[(j + i) % 4], neightet); + neightet.ver = epivot[neightet.ver & 3]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + decode(searchtet->tet[j], spintet); + j = (spintet.ver & 3); // The current face number. + for (i = 1; i < 4; i++) { + decode(spintet.tet[(j + i) % 4], neightet); + neightet.ver = epivot[neightet.ver & 3]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + if ((point) spintet.tet[7] == dummypoint) hullsize--; + if ((point) searchtet->tet[7] == dummypoint) hullsize--; + // tetrahedrondealloc(spintet.tet); + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + // tetrahedrondealloc(searchtet->tet); + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + flip26count++; + + if (ivf->splitbdflag) { //if (bowywat > 2) { + if (splitsh != NULL) { + // Create the initial sub-cavity sC(p). + smarktest(*splitsh); + caveshlist->newindex((void **) &parysh); + *parysh = *splitsh; + } + } // if (splitbdflag) + } else if (loc == ONEDGE) { + if (b->verbose > 3) { + printf(" On edge.\n"); + } + // Add all adjacent boundary tets into list. + spintet = *searchtet; + while (1) { + enextesym(spintet, neightet); + fsymself(neightet); + neightet.ver = epivot[neightet.ver & 3]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + eprevesym(spintet, neightet); + fsymself(neightet); + neightet.ver = epivot[neightet.ver & 3]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + if ((point) spintet.tet[7] == dummypoint) hullsize--; + // tetrahedrondealloc(spintet.tet); + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) + flipn2ncount++; + + if (ivf->splitbdflag) { //if (bowywat > 2) { + // Create the initial sub-cavity sC(p). + if (splitseg != NULL) { + smarktest(*splitseg); + splitseg->shver = 0; + spivot(*splitseg, *splitsh); + } + if (splitsh != NULL) { + if (splitsh->sh != NULL) { + // Collect all subfaces share at this edge. + pa = sorg(*splitsh); + neighsh = *splitsh; + while (1) { + // Adjust the origin of its edge to be 'pa'. + if (sorg(neighsh) != pa) { + sesymself(neighsh); + } + // Add this face into list (in B-W cavity). + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Add this face into face-at-splitedge list. + cavesegshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Go to the next face at the edge. + spivotself(neighsh); + // Stop if all faces at the edge have been visited. + if (neighsh.sh == splitsh->sh) break; + if (neighsh.sh == NULL) break; + } // while (1) + } // if (not a dangling segment) + } + } // if (splitbdflag) + } else if (loc == INSTAR) { + if (b->verbose > 3) { + printf(" Inside star.\n"); + } // We assume that all tets in the star are given in 'caveoldtetlist', // and they are all infected. assert(caveoldtetlist->objects > 0); @@ -9157,19 +9876,35 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, decode(cavetet->tet[j], neightet); if (!infected(neightet)) { // It's a boundary face. - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); + neightet.ver = epivot[neightet.ver & 3]; + cavetetlist->newindex((void **) &parytet); *parytet = neightet; } } } } else if (loc == ONVERTEX) { + pa = org(*searchtet); + if (b->verbose > 3) { + printf(" On vertex %d.\n", pointmark(pa)); + } + if (insertpt != pa) { + // Remember it is a duplicated point. + setpointtype(insertpt, DUPLICATEDVERTEX); + // Set a pointer to the point it duplicates. + setpoint2ppt(insertpt, pa); + } // The point already exist. Do nothing and return. - return 0; + return (int) loc; } else if (loc == ENCSUBFACE) { - // Treat it as outside. - ivf->iloc = (int) OUTSIDE; - return 0; + if (b->verbose > 3) { + printf(" Encroached.\n"); + } + // The vertex lies outside of the region boundary. + // Treated it as outside + loc = OUTSIDE; + return (int) loc; + } else { + assert(0); // Unknown type. } @@ -9177,33 +9912,104 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // Assign mesh size for the new point. if (bgm != NULL) { // Interpolate the mesh size from the background mesh. - bgm->decode(point2bgmtet(org(*searchtet)), neightet); - int bgmloc = (int) bgm->scoutpoint(insertpt, &neightet, 0); + pa = org(*searchtet); + bgm->decode(point2bgmtet(pa), neightet); // neightet is in 'bgm'! + bgmloc = bgm->scoutpoint(insertpt, &neightet, 0); // randflag = 0 if (bgmloc != (int) OUTSIDE) { - insertpt[pointmtrindex] = - bgm->getpointmeshsize(insertpt, &neightet, bgmloc); + insertpt[pointmtrindex] = // posflag = 1 + bgm->getpointmeshsize(insertpt, &neightet, bgmloc, 1); setpoint2bgmtet(insertpt, bgm->encode(neightet)); } } else { - insertpt[pointmtrindex] = getpointmeshsize(insertpt,searchtet,(int)loc); + insertpt[pointmtrindex] = // posflag = 1 + getpointmeshsize(insertpt, searchtet, (int) loc, 1); } } // if (assignmeshsize) - if (ivf->bowywat) { - // Update the cavity C(p) using the Bowyer-Watson algorithm. - swaplist = cavetetlist; - cavetetlist = cavebdrylist; - cavebdrylist = swaplist; + if (ivf->validflag) { //if (bowywat > 2) { + // Validate the initial C(p). Enlarge it at a face which is not visible + // by p. This removes (interior) slivers. Re-use 'cavebdrylist'. + tetcount = 0l; + for (i = 0; i < cavetetlist->objects; i++) { - // 'cavetet' is an adjacent tet at outside of the cavity. cavetet = (triface *) fastlookup(cavetetlist, i); - // The tet may be tested and included in the (enlarged) cavity. + // Other expansions may make this face inside C(p). if (!infected(*cavetet)) { - // Check for two possible cases for this tet: - // (1) It is a cavity tet, or - // (2) it is a cavity boundary face. - enqflag = false; - if (!marktested(*cavetet)) { + pc = apex(*cavetet); + // Do valid if it is a face (not a hull edge). + if (pc != dummypoint) { + pa = org(*cavetet); + pb = dest(*cavetet); + ori = orient3d(pa, pb, pc, insertpt); + if (ori <= 0) { + // An invalid face. Enlarge the cavity. + //if (oppo(*cavetet) != dummypoint) { + if (b->verbose > 3) { + printf(" Enlarge cavity at (%d, %d, %d)\n", + pointmark(pa), pointmark(pb), pointmark(pc)); + } + // Add the other three faces into list. + j = (cavetet->ver & 3); // The current face number. + for (k = 1; k < 4; k++) { + decode(cavetet->tet[(j + k) % 4], neightet); + neightet.ver = epivot[neightet.ver & 3]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + if ((point) cavetet->tet[7] == dummypoint) hullsize--; + infect(*cavetet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *cavetet; + tetcount++; + //} else { + // printf("Error in insertvertex %d: ", pointmark(insertpt)); + // printf("Invalid initial cavity at face %d.\n", i + 1); + // assert(0); + //} + } else { + // A valid face. + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } else { + // A hull edge is valid. + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } // if (!infected(*cavetet)) + } // i + + if (tetcount > 0l) { + // The cavity has been enlarged. Update it. + cavetetlist->restart(); + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + if (!infected(*cavetet)) { + cavetetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } // i + } // if (tetcount) + + cavebdrylist->restart(); + tetcount = 0l; + } // if (bowywat > 2) + + // Update the cavity C(p) using the Bowyer-Watson approach (bowywat > 0). + + for (i = 0; i < cavetetlist->objects; i++) { + // 'cavetet' is an adjacent tet at outside of the cavity. + cavetet = (triface *) fastlookup(cavetetlist, i); + // The tet may be tested and included in the (enlarged) cavity. + if (!infected(*cavetet)) { + // Check for two possible cases for this tet: + // (1) It is a cavity tet, or + // (2) it is a cavity boundary face. + // In case (1), this tet is grabbed in the cavity and three adjacent + // tets on other faces of this tet are added into 'cavetetlist'. + enqflag = false; + if (!marktested(*cavetet)) { + if (ivf->bowywat) { // Do Delaunay (in-sphere) test. pts = (point *) cavetet->tet; if (pts[7] != dummypoint) { @@ -9220,17 +10026,20 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (!nonconvex) { // Test if this hull face is visible by the new point. ori = orient3d(pts[4], pts[5], pts[6], insertpt); + orient3dcount++; if (ori < 0) { // A visible hull face. //if (!nonconvex) { // Include it in the cavity. The convex hull will be enlarged. enqflag = true; // (ori < 0.0); - //} + //} } else if (ori == 0.0) { // A coplanar hull face. We need to test if this hull face is // Delaunay or not. We test if the adjacent tet (not faked) // of this hull face is Delaunay or not. - decode(cavetet->tet[3], neineitet); + neightet = *cavetet; + neightet.ver = 3; // The face opposite to dummypoint. + fsym(neightet, neineitet); if (!infected(neineitet)) { if (!marktested(neineitet)) { // Do Delaunay test on this tet. @@ -9244,7 +10053,12 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt); } enqflag = (sign < 0.0); - } + } else { + // The adjacent tet has been tested (marktested), and it + // is Delaunay (not get infected). Hence the the hull + // face is Delaunay as well. + // enqflag = false; + } } else { // The adjacent tet is non-Delaunay. The hull face is non- // Delaunay as well. Include it in the cavity. @@ -9253,11 +10067,15 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } // if (ori == 0.0) } else { // A hull face (must be a subface). + assert(checksubfaceflag); + assert(ivf->validflag); // We FIRST include it in the initial cavity if the adjacent tet // (not faked) of this hull face is not Delaunay wrt p. // Whether it belongs to the final cavity will be determined // during the validation process. 'validflag'. - decode(cavetet->tet[3], neineitet); + neightet = *cavetet; + neightet.ver = 3; // The face opposite to dummypoint. + fsym(neightet, neineitet); if (!infected(neineitet)) { if (!marktested(neineitet)) { // Do Delaunay test on this tet. @@ -9271,7 +10089,12 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt); } enqflag = (sign < 0.0); - } + } else { + // The adjacent tet has been tested (marktested), and it + // is Delaunay (not get infected). Hence the the hull + // face is Delaunay as well. + // enqflag = false; + } // if (marktested(neineitet)) } else { // The adjacent tet is non-Delaunay. The hull face is non- // Delaunay as well. Include it in the cavity. @@ -9279,124 +10102,200 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } // if (infected(neineitet)) } // if (nonconvex) } // if (pts[7] != dummypoint) - marktest(*cavetet); // Only test it once. - } // if (!marktested(*cavetet)) - - if (enqflag) { - // Found a tet in the cavity. Put other three faces in check list. - k = (cavetet->ver & 3); // The current face number - for (j = 1; j < 4; j++) { - decode(cavetet->tet[(j + k) % 4], neightet); - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - infect(*cavetet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *cavetet; - } else { - // Found a boundary face of the cavity. - cavetet->ver = epivot[cavetet->ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = *cavetet; + } // if (bowywat) + marktest(*cavetet); // Only test it once. + } // if (!marktested(*cavetet)) + + if (enqflag) { + // Found a tet in the cavity. Put other three faces in check list. + k = (cavetet->ver & 3); // The current face number + for (j = 1; j < 4; j++) { + decode(cavetet->tet[(j + k) % 4], neightet); + neightet.ver = epivot[neightet.ver & 3]; + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; } - } // if (!infected(*cavetet)) - } // i + if ((point) cavetet->tet[7] == dummypoint) hullsize--; + // tetrahedrondealloc(cavetet->tet); + infect(*cavetet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + // Found a boundary face of the cavity. It may be a face of a hull + // tet which contains 'dummypoint'. Choose the edge in the face + // such that its endpoints are not 'dummypoint', while its apex + // may be 'dummypoint'. + //j = (cavetet->ver & 3); // j is the face number. + //cavetet->ver = epivot[j]; // [4,5,2,11] + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } // if (!infected(*cavetet)) + } // i + + if (b->verbose > 3) { + printf(" Initial cavity size: %ld tets, %ld faces.\n", + caveoldtetlist->objects, cavebdrylist->objects); + } - cavetetlist->restart(); // Clear the working list. - } // if (ivf->bowywat) if (checksubsegflag) { // Collect all segments of C(p). - shellface *ssptr; for (i = 0; i < caveoldtetlist->objects; i++) { cavetet = (triface *) fastlookup(caveoldtetlist, i); - if ((ssptr = (shellface*) cavetet->tet[8]) != NULL) { - for (j = 0; j < 6; j++) { - if (ssptr[j]) { - sdecode(ssptr[j], checkseg); - if (!sinfected(checkseg)) { - sinfect(checkseg); - cavetetseglist->newindex((void **) &paryseg); - *paryseg = checkseg; - } + for (j = 0; j < 6; j++) { + cavetet->ver = edge2ver[j]; + tsspivot1(*cavetet, checkseg); + if (checkseg.sh != NULL) { + if (!sinfected(checkseg)) { + sinfect(checkseg); + cavetetseglist->newindex((void **) &paryseg); + *paryseg = checkseg; } - } // j + } } - } // i + } // Uninfect collected segments. for (i = 0; i < cavetetseglist->objects; i++) { - paryseg = (face *) fastlookup(cavetetseglist, i); - suninfect(*paryseg); - } - - if (ivf->rejflag & 1) { - // Reject this point if it encroaches upon any segment. - face *paryseg1; - for (i = 0; i < cavetetseglist->objects; i++) { - paryseg1 = (face *) fastlookup(cavetetseglist, i); - if (checkseg4encroach((point) paryseg1->sh[3], (point) paryseg1->sh[4], - insertpt)) { - encseglist->newindex((void **) &paryseg); - *paryseg = *paryseg1; - } - } // i - if (encseglist->objects > 0) { - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) ENCSEGMENT; - return 0; - } + checkseg = * (face *) fastlookup(cavetetseglist, i); + suninfect(checkseg); } } // if (checksubsegflag) if (checksubfaceflag) { // Collect all subfaces of C(p). - shellface *sptr; for (i = 0; i < caveoldtetlist->objects; i++) { cavetet = (triface *) fastlookup(caveoldtetlist, i); - if ((sptr = (shellface*) cavetet->tet[9]) != NULL) { - for (j = 0; j < 4; j++) { - if (sptr[j]) { - sdecode(sptr[j], checksh); - if (!sinfected(checksh)) { - sinfect(checksh); - cavetetshlist->newindex((void **) &parysh); - *parysh = checksh; - } + oldtet = *cavetet; + for (oldtet.ver = 0; oldtet.ver < 4; oldtet.ver++) { + tspivot(oldtet, checksh); + if (checksh.sh != NULL) { + if (!sinfected(checksh)) { + sinfect(checksh); + cavetetshlist->newindex((void **) &parysh); + *parysh = checksh; } - } // j + } } - } // i + } // Uninfect collected subfaces. for (i = 0; i < cavetetshlist->objects; i++) { - parysh = (face *) fastlookup(cavetetshlist, i); - suninfect(*parysh); + checksh = * (face *) fastlookup(cavetetshlist, i); + suninfect(checksh); } + } // if (checksubfaceflag) - if (ivf->rejflag & 2) { - REAL rd, cent[3]; - badface *bface; - // Reject this point if it encroaches upon any subface. - for (i = 0; i < cavetetshlist->objects; i++) { - parysh = (face *) fastlookup(cavetetshlist, i); - if (checkfac4encroach((point) parysh->sh[3], (point) parysh->sh[4], - (point) parysh->sh[5], insertpt, cent, &rd)) { - encshlist->newindex((void **) &bface); - bface->ss = *parysh; - bface->forg = (point) parysh->sh[3]; // Not a dad one. - for (j = 0; j < 3; j++) bface->cent[j] = cent[j]; - bface->key = rd; + if (ivf->rejflag & 1) { + // Reject insertion of this point if it encroaches upon any segment. + for (i = 0; i < cavetetseglist->objects; i++) { + checkseg = * (face *) fastlookup(cavetetseglist, i); + pa = sorg(checkseg); + pb = sdest(checkseg); + if (checkseg4encroach(pa, pb, insertpt)) { + if (b->verbose > 3) { + printf(" Found an encroached seg (%d, %d).\n", + pointmark(pa), pointmark(pb)); + } + encseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } + } // i + if (encseglist->objects > 0) { + if (b->verbose > 3) { + printf(" Found %ld encroached segments. Reject it.\n", + encseglist->objects); + } + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); // Unmark it. + } + // Clear working lists. + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + cavetetseglist->restart(); + cavetetshlist->restart(); + if (ivf->splitbdflag) { //if (bowywat > 2) { + if (splitseg != NULL) { + sunmarktest(*splitseg); + } + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + assert(smarktested(*parysh)); + sunmarktest(*parysh); } + caveshlist->restart(); + cavesegshlist->restart(); + } + // Restore the hullsize. + hullsize = bakhullsize; + return (int) ENCSEGMENT; + } + } // if (reject & 1) + + if (ivf->rejflag & 2) { + // Reject insertion of this point if it encroaches upon any subface. + for (i = 0; i < cavetetshlist->objects; i++) { + checksh = * (face *) fastlookup(cavetetshlist, i); + pa = sorg(checksh); + pb = sdest(checksh); + pc = sapex(checksh); + if (checkfac4encroach(pa, pb, pc, insertpt, cent, &rd)) { + if (b->verbose > 3) { + printf(" Found an encroached subface (%d, %d, %d).\n", + pointmark(pa), pointmark(pb), pointmark(pc)); + } + encshlist->newindex((void **) &bface); + bface->ss = checksh; + bface->forg = pa; // Not a dad one. + for (j = 0; j < 3; j++) bface->cent[j] = cent[j]; + bface->key = rd; + } + } // i + if (encshlist->objects > 0) { + if (b->verbose > 3) { + printf(" Found %ld encroached subfaces. Reject it.\n", + caveencshlist->objects); + } + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); // Unmark it. } - if (encshlist->objects > 0) { - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) ENCSUBFACE; - return 0; + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + cavetetseglist->restart(); + cavetetshlist->restart(); + if (ivf->splitbdflag) { //if (bowywat > 2) { + if (splitseg != NULL) { + sunmarktest(*splitseg); + } + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + assert(smarktested(*parysh)); + sunmarktest(*parysh); + } + caveshlist->restart(); + cavesegshlist->restart(); } + // Restore the hullsize. + hullsize = bakhullsize; + return (int) ENCSUBFACE; } - } // if (checksubfaceflag) + } // if (reject & 2) - if (ivf->splitbdflag) { - // The new point locates in surface mesh. Update the sC(p). + if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { + // Update the sC(p). // We have already 'smarktested' the subfaces which directly intersect // with p in 'caveshlist'. From them, we 'smarktest' their neighboring // subfaces which are included in C(p). Do not across a segment. @@ -9405,15 +10304,16 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, assert(smarktested(*parysh)); checksh = *parysh; for (j = 0; j < 3; j++) { - if (!isshsubseg(checksh)) { + sspivot(checksh, checkseg); + if (checkseg.sh == NULL) { spivot(checksh, neighsh); assert(neighsh.sh != NULL); if (!smarktested(neighsh)) { + // Add this subface if it is inside C(p). stpivot(neighsh, neightet); if (infected(neightet)) { fsymself(neightet); if (infected(neightet)) { - // This subface is inside C(p). smarktest(neighsh); caveshlist->newindex((void **) &parysh); *parysh = neighsh; @@ -9424,34 +10324,48 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, senextself(checksh); } // j } // i - } // if (ivf->splitbdflag) + if (b->verbose > 3) { + printf(" Initial subcavity size: %ld subfacess.\n", + caveshlist->objects); + } + } + + cutcount = 0l; - if (ivf->validflag) { - // Validate C(p) and update it if it is not star-shaped. - int cutcount = 0; + if (ivf->validflag) { + //if (bowywat > 1) { // if (bowywat == 2 || bowywat == 3) { + // T is a CT. Validation is needed (fig/dump-cavity-case8). + cavetetlist->restart(); // Re-use it. + //if (splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { if (ivf->respectbdflag) { // The initial cavity may include subfaces which are not on the facets - // being splitting. Find them and make them as boundary of C(p). - // Comment: We have already 'smarktested' the subfaces in sC(p). They - // are completely inside C(p). + // being splitting. Find them and make them as boundary of C(p). + // Comment: We have already 'smarktested' the subfaces in sC(p). + // It is needed by 'splitbdflag'. for (i = 0; i < cavetetshlist->objects; i++) { parysh = (face *) fastlookup(cavetetshlist, i); stpivot(*parysh, neightet); if (infected(neightet)) { fsymself(neightet); if (infected(neightet)) { - // Found a subface inside C(p). if (!smarktested(*parysh)) { + if (b->verbose > 3) { + printf(" Found a subface (%d, %d, %d) inside cavity.\n", + pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh))); + } // It is possible that this face is a boundary subface. // Check if it is a hull face. - //assert(apex(neightet) != dummypoint); + assert(apex(neightet) != dummypoint); if (oppo(neightet) != dummypoint) { fsymself(neightet); } if (oppo(neightet) != dummypoint) { - ori = orient3d(org(neightet), dest(neightet), apex(neightet), - insertpt); + pa = org(neightet); + pb = dest(neightet); + pc = apex(neightet); + ori = orient3d(pa, pb, pc, insertpt); if (ori < 0) { // A visible face, get its neighbor face. fsymself(neightet); @@ -9463,20 +10377,27 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } // Cut this tet if it is either invisible by or coplanar with p. if (ori >= 0) { + if (b->verbose > 3) { + printf(" Cut tet (%d, %d, %d, %d)\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet)), pointmark(oppo(neightet))); + } uninfect(neightet); unmarktest(neightet); cutcount++; - neightet.ver = epivot[neightet.ver]; + neightet.ver = epivot[neightet.ver & 3]; cavebdrylist->newindex((void **) &parytet); *parytet = neightet; // Add three new faces to find new boundaries. for (j = 0; j < 3; j++) { esym(neightet, neineitet); - neineitet.ver = epivot[neineitet.ver]; + neineitet.ver = epivot[neineitet.ver & 3]; cavebdrylist->newindex((void **) &parytet); *parytet = neineitet; enextself(neightet); } + // Update hullsize. + if (oppo(neightet) == dummypoint) hullsize++; } // if (ori >= 0) } } @@ -9498,6 +10419,10 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (spintet.tet == neightet.tet) break; } if (infected(spintet)) { + if (b->verbose > 3) { + printf(" Found an interior segment (%d, %d).\n", + pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg))); + } // Find an adjacent tet at this segment such that both faces // at this segment are not visible by p. pa = org(neightet); @@ -9522,6 +10447,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } } } + } else { } fnextself(spintet); if (spintet.tet == neightet.tet) break; @@ -9539,34 +10465,42 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, uninfect(neightet); unmarktest(neightet); cutcount++; - neightet.ver = epivot[neightet.ver]; + neightet.ver = epivot[neightet.ver & 3]; cavebdrylist->newindex((void **) &parytet); *parytet = neightet; // Add three new faces to find new boundaries. for (j = 0; j < 3; j++) { esym(neightet, neineitet); - neineitet.ver = epivot[neineitet.ver]; + neineitet.ver = epivot[neineitet.ver & 3]; cavebdrylist->newindex((void **) &parytet); *parytet = neineitet; enextself(neightet); } + // Update hullsize. + //if (oppo(neightet) == dummypoint) hullsize++; + if ((point) (neightet.tet[7]) == dummypoint) hullsize++; } } } // i - } // if (ivf->respectbdflag) + } // if (bowywat > 2) // Update the cavity by removing invisible faces until it is star-shaped. for (i = 0; i < cavebdrylist->objects; i++) { cavetet = (triface *) fastlookup(cavebdrylist, i); - // 'cavetet' is an exterior tet adjacent to the cavity. + // 'cavetet' is an exterior tet adjacent to the cavity. + assert(cavetet->ver == epivot[cavetet->ver & 3]); // SELF_CHECK + // It must be not inside the cavity (since we only cut tets). + assert(!infected(*cavetet)); // Check if its neighbor is inside C(p). fsym(*cavetet, neightet); if (infected(neightet)) { if (apex(*cavetet) != dummypoint) { // It is a cavity boundary face. Check its visibility. if (oppo(neightet) != dummypoint) { - ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet), - insertpt); + pa = org(*cavetet); + pb = dest(*cavetet); + pc = apex(*cavetet); + ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; enqflag = (ori > 0); // Comment: if ori == 0 (coplanar case), we also cut the tet. } else { @@ -9583,17 +10517,24 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, cavetetlist->newindex((void **) &parytet); *parytet = *cavetet; } else { + if (b->verbose > 3) { + printf(" Cut tet (%d, %d, %d, %d)\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet)), pointmark(oppo(neightet))); + } uninfect(neightet); unmarktest(neightet); cutcount++; // Add three new faces to find new boundaries. for (j = 0; j < 3; j++) { esym(neightet, neineitet); - neineitet.ver = epivot[neineitet.ver]; + neineitet.ver = epivot[neineitet.ver & 3]; cavebdrylist->newindex((void **) &parytet); *parytet = neineitet; enextself(neightet); } + // Update the hullsize. + if (oppo(neightet) == dummypoint) hullsize++; // 'cavetet' is not on the cavity boundary anymore. unmarktest(*cavetet); } @@ -9605,11 +10546,14 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (cutcount > 0) { // The cavity has been updated. + // Update the cavity boundary faces. cavebdrylist->restart(); for (i = 0; i < cavetetlist->objects; i++) { cavetet = (triface *) fastlookup(cavetetlist, i); // 'cavetet' was an exterior tet adjacent to the cavity. + assert(cavetet->ver == epivot[cavetet->ver & 3]); // SELF_CHECK + assert(!infected(*cavetet)); fsym(*cavetet, neightet); if (infected(neightet)) { // It is a cavity boundary face. @@ -9637,13 +10581,12 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // The cavity should contain at least one tet. if (caveoldtetlist->objects == 0l) { - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) BADELEMENT; - return 0; + printf("Invalid cavity of Steiner point %d.\n", pointmark(insertpt)); + assert(0); } - if (ivf->splitbdflag) { - int cutshcount = 0; + if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { + cutshcount = 0; // Update the sub-cavity sC(p). for (i = 0; i < caveshlist->objects; i++) { parysh = (face *) fastlookup(caveshlist, i); @@ -9657,6 +10600,11 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } } if (!enqflag) { + if (b->verbose > 3) { + printf(" Cut subface (%d, %d, %d).\n", + pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh))); + } sunmarktest(*parysh); // Use the last entry of this array to fill this entry. j = caveshlist->objects - 1; @@ -9704,140 +10652,127 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (i > 0) { // The updated sC(p) is invalid. Do not insert this vertex. - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) BADELEMENT; - return 0; + if (b->verbose > 3) { + printf(" Found %d invalid items. Reject it.\n", i); + } + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); // Unmark it. + } + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + cavetetseglist->restart(); + cavetetshlist->restart(); + if (ivf->splitbdflag) { //if (bowywat > 2) { + if (splitseg != NULL) { + sunmarktest(*splitseg); + } + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + assert(smarktested(*parysh)); + sunmarktest(*parysh); + } + caveshlist->restart(); + cavesegshlist->restart(); + } + // Restore the hullsize. + hullsize = bakhullsize; + return (int) BADELEMENT; } } // if (cutshcount > 0) - } // if (ivf->splitbdflag) + } // if (bowywat > 2) + } // if (cutcount > 0) - } // if (ivf->validflag) + } // if (validflag) // if (bowywat > 1) + + if (b->verbose > 3) { + printf(" Final cavity: %ld tets, %ld faces.", + caveoldtetlist->objects, cavebdrylist->objects); + if (cutcount > 0l) { + printf(" Updated %ld times.", cutcount); + } + printf("\n"); + } + if (ivf->refineflag) { // The new point is inserted by Delaunay refinement, i.e., it is the // circumcenter of a tetrahedron, or a subface, or a segment. // Do not insert this point if the tetrahedron, or subface, or segment // is not inside the final cavity. - if (((ivf->refineflag == 1) && !infected(ivf->refinetet)) || - ((ivf->refineflag == 2) && !smarktested(ivf->refinesh))) { - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) BADELEMENT; - return 0; - } - } // if (ivf->refineflag) - - if (b->plc && (loc != INSTAR)) { - // Reject the new point if it lies too close to an existing point (b->plc), - // or it lies inside a protecting ball of near vertex (ivf->rejflag & 4). - // Collect the list of vertices of the initial cavity. - if (loc == OUTSIDE) { - pts = (point *) &(searchtet->tet[4]); - for (i = 0; i < 3; i++) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; - } - } else if (loc == INTETRAHEDRON) { - pts = (point *) &(searchtet->tet[4]); - for (i = 0; i < 4; i++) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; - } - } else if (loc == ONFACE) { - pts = (point *) &(searchtet->tet[4]); - for (i = 0; i < 3; i++) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; + rejptflag = 0; + if (ivf->refineflag == 1) { + // The new point is the circumcenter of a tetrahedron. + assert(!isdeadtet(ivf->refinetet)); + if (!infected(ivf->refinetet)) { + rejrefinetetcount++; + rejptflag = 1; + } + } else if (ivf->refineflag == 2) { + // The new point is the circumcenter of a subface. + assert(ivf->refinesh.sh != NULL); + if (!smarktested(ivf->refinesh)) { + rejrefineshcount++; + rejptflag = 1; + } + } + if (rejptflag) { + if (b->verbose > 2) { + printf(" Point %d does not refine its element. Rejected.\n", + pointmark(insertpt)); } - if (pts[3] != dummypoint) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[3]; + // Restore the original status. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); } - fsym(*searchtet, spintet); - if (oppo(spintet) != dummypoint) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = oppo(spintet); + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); // Unmark it. } - } else if (loc == ONEDGE) { - spintet = *searchtet; - cavetetvertlist->newindex((void **) &parypt); - *parypt = org(spintet); - cavetetvertlist->newindex((void **) &parypt); - *parypt = dest(spintet); - while (1) { - if (apex(spintet) != dummypoint) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = apex(spintet); + // Clear working lists. + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + cavetetshlist->restart(); + cavetetseglist->restart(); + cavetetvertlist->restart(); + if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { + if (splitseg != NULL) { + sunmarktest(*splitseg); } - fnextself(spintet); - if (spintet.tet == searchtet->tet) break; - } - } - - int rejptflag = (ivf->rejflag & 4); - REAL rd; - pts = NULL; - - for (i = 0; i < cavetetvertlist->objects; i++) { - parypt = (point *) fastlookup(cavetetvertlist, i); - rd = distance(*parypt, insertpt); - // Is the point very close to an existing point? - if (rd < b->minedgelength) { - pts = parypt; - loc = NEARVERTEX; - break; - } - if (rejptflag) { - // Is the point encroaches upon an existing point? - if (rd < (*parypt)[pointmtrindex]) { - pts = parypt; - loc = ENCVERTEX; - break; + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + assert(smarktested(*parysh)); + sunmarktest(*parysh); } + caveshlist->restart(); + cavesegshlist->restart(); } - } - cavetetvertlist->restart(); // Clear the work list. - if (pts != NULL) { - // The point is either too close to an existing vertex (NEARVERTEX) - // or encroaches upon (isndie the protecting ball) of that vertex. - if (loc == NEARVERTEX) { - if (b->nomergevertex) { // -M0/1 option. - // In this case, we still insert this vertex. Although it is very - // close to an existing vertex. Give a warning, anyway. - if (!b->quiet) { - printf("Warning: Two points, %d and %d, are very close.\n", - pointmark(insertpt), pointmark(*pts)); - printf(" Creating a very short edge (len = %g) (< %g).\n", - rd, b->minedgelength); - printf(" You may try a smaller tolerance (-T) (current is %g)\n", - b->epsilon); - printf(" to avoid this warning.\n"); - } - } else { - insertpt[3] = rd; // Only for reporting. - setpoint2ppt(insertpt, *pts); - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) loc; - return 0; - } - } else { // loc == ENCVERTEX - // The point lies inside the protection ball. - setpoint2ppt(insertpt, *pts); - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) loc; - return 0; - } - } - } // if (b->plc && (loc != INSTAR)) + // Restore the hullsize. + hullsize = bakhullsize; + loc = BADELEMENT; + return (int) loc; + } // if (rejptflag) + } // if (ivf->refineflag) + + rejptflag = (ivf->rejflag & 4); + encptflag = 0; - if (b->weighted || ivf->cdtflag - ) { - // There may be other vertices inside C(p). We need to find them. + if (b->weighted || b->plc || rejptflag) { // Collect all vertices of C(p). for (i = 0; i < caveoldtetlist->objects; i++) { cavetet = (triface *) fastlookup(caveoldtetlist, i); - //assert(infected(*cavetet)); + assert(infected(*cavetet)); pts = (point *) &(cavetet->tet[4]); for (j = 0; j < 4; j++) { if (pts[j] != dummypoint) { @@ -9849,33 +10784,118 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } } // j } // i + if (b->verbose > 3) { + printf(" %ld cavity vertices.\n", cavetetvertlist->objects); + } // Uninfect all collected (cavity) vertices. for (i = 0; i < cavetetvertlist->objects; i++) { parypt = (point *) fastlookup(cavetetvertlist, i); puninfect(*parypt); } - } + if (b->plc || rejptflag) { + // Check if p is too close to an existing vertex. + pts = NULL; + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + rd = distance(*parypt, insertpt); + // Is the point very close to an existing point? + if (rd < b->minedgelength) { + pts = parypt; + break; + } + if (rejptflag) { + // Is the point encroaches upon an existing point? + if (rd < (*parypt)[pointmtrindex]) { + // The point lies inside the protection ball. + if (b->verbose > 2) { + printf(" Point %d lies in protball of %d. Rejected.\n", + pointmark(insertpt), pointmark(*parypt)); + } + pts = parypt; + encptflag = 1; + break; + } + } + } // i + if (pts != NULL) { + // p is too close to *pts. + if (ivf->iloc != (int) INSTAR) { + if (pointmark(insertpt) <= in->numberofpoints) { + // It's an input point. + if (!b->quiet) { + printf("Warning: Point %d is replaced by point %d.\n", + pointmark(insertpt), pointmark(*pts)); + } + // Count the number of duplicated points. + dupverts++; + } else { // It's a Steiner point. + if (b->verbose) { + if (!rejptflag) { + printf("Warning: Reject a Steiner point %d (close to %d).\n", + pointmark(insertpt), pointmark(*pts)); + } + } + } + // Remember it is a duplicated point. + setpointtype(insertpt, DUPLICATEDVERTEX); + // Set a pointer to the point it duplicates. + setpoint2ppt(insertpt, *pts); + // Restore the original status. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); // Unmark it. + } + // Clear working lists. + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + cavetetshlist->restart(); + cavetetseglist->restart(); + cavetetvertlist->restart(); + if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { + if (splitseg != NULL) { + sunmarktest(*splitseg); + } + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + assert(smarktested(*parysh)); + sunmarktest(*parysh); + } + caveshlist->restart(); + cavesegshlist->restart(); + } - if (ivf->cdtflag) { - // Unmark tets. - for (i = 0; i < caveoldtetlist->objects; i++) { - cavetet = (triface *) fastlookup(caveoldtetlist, i); - unmarktest(*cavetet); - } - for (i = 0; i < cavebdrylist->objects; i++) { - cavetet = (triface *) fastlookup(cavebdrylist, i); - unmarktest(*cavetet); - } - // Clean up arrays which are not needed. - cavetetlist->restart(); - if (checksubsegflag) { - cavetetseglist->restart(); - } - if (checksubfaceflag) { - cavetetshlist->restart(); - } - return 1; + // Restore the hullsize. + hullsize = bakhullsize; + if (!encptflag) { + loc = NEARVERTEX; + } else { + loc = ENCVERTEX; + } + return (int) loc; + } else { // (iloc == (int) INSTAR) + // The cavity is guaranteed to be valid by the caller of this + // function. We still insert this vertex. + if (b->verbose) { + printf("Warning: The Steiner point %d is very close to %d.\n", + pointmark(insertpt), pointmark(*pts)); + } + } + } // if (pts != NULL) + } + } + + // The new point will be inserted. + totaldeadtets += caveoldtetlist->objects; + totalbowatcavsize += cavebdrylist->objects; + if (maxbowatcavsize < cavebdrylist->objects) { + maxbowatcavsize = cavebdrylist->objects; } // Before re-mesh C(p). Process the segments and subfaces which are on the @@ -9897,7 +10917,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, while (1) { j++; if (!infected(spintet)) { - neineitet = spintet; // An outer tet. Remember it. + neineitet = spintet; // An outer tet. Remember it. } else { k++; // An in tet. } @@ -9918,8 +10938,12 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, sstbond1(*paryseg, neineitet); } else { // k == j // The segment is inside C(p). - if (!ivf->splitbdflag) { + if (!ivf->splitbdflag) {//if (bowywat < 3) { // if (bowywat == 2) { checkseg = *paryseg; + if (b->verbose > 3) { + printf(" Queueing a missing seg (%d, %d)\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } sinfect(checkseg); // Flag it as an interior segment. caveencseglist->newindex((void **) &paryseg); *paryseg = checkseg; @@ -9934,6 +10958,10 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, sinfect(*paryseg); } } // i + if (b->verbose > 3) { + printf(" %ld (%ld) cavity (interior) segments.\n", + cavetetseglist->objects, caveencseglist->objects); + } } // if (checksubsegflag) if (checksubfaceflag) { @@ -9963,8 +10991,13 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // This side is the outer boundary of C(p). *parysh = checksh; } else { // k == 2 - if (!ivf->splitbdflag) { + if (!ivf->splitbdflag) { //if (bowywat < 3) { // if (bowywat == 2) { checksh = *parysh; + if (b->verbose > 3) { + printf(" Queueing a missing subface (%d, %d, %d)\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } sinfect(checksh); // Flag it. caveencshlist->newindex((void **) &parysh); *parysh = checksh; @@ -9979,26 +11012,40 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, sinfect(*parysh); } } // i - } // if (checksubfaceflag) + if (b->verbose > 3) { + printf(" %ld (%ld) cavity (interior) subfaces.\n", + cavetetshlist->objects, caveencshlist->objects); + } + } // if (checksubfaceflag) { // Create new tetrahedra to fill the cavity. for (i = 0; i < cavebdrylist->objects; i++) { cavetet = (triface *) fastlookup(cavebdrylist, i); neightet = *cavetet; + assert(!infected(neightet)); unmarktest(neightet); // Unmark it. // Get the oldtet (inside the cavity). fsym(neightet, oldtet); if (apex(neightet) != dummypoint) { - // Create a new tet in the cavity. + // Create a new tet in the cavity (see Fig. bowyerwatson 1 or 3). maketetrahedron(&newtet); setorg(newtet, dest(neightet)); setdest(newtet, org(neightet)); setapex(newtet, apex(neightet)); setoppo(newtet, insertpt); + // The new tet inherits attribtes from the old tet. + for (j = 0; j < in->numberoftetrahedronattributes; j++) { + attrib = elemattribute(oldtet.tet, j); + setelemattribute(newtet.tet, j, attrib); + } + if (b->varvolume) { + volume = volumebound(oldtet.tet); + setvolumebound(newtet.tet, volume); + } } else { - // Create a new hull tet. - hullsize++; + // Create a new hull tet (see Fig. bowyerwatson 2). + hullsize++; maketetrahedron(&newtet); setorg(newtet, org(neightet)); setdest(newtet, dest(neightet)); @@ -10007,15 +11054,6 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // Adjust back to the cavity bounday face. esymself(newtet); } - // The new tet inherits attribtes from the old tet. - for (j = 0; j < numelemattrib; j++) { - attrib = elemattribute(oldtet.tet, j); - setelemattribute(newtet.tet, j, attrib); - } - if (b->varvolume) { - volume = volumebound(oldtet.tet); - setvolumebound(newtet.tet, volume); - } // Connect newtet <==> neightet, this also disconnect the old bond. bond(newtet, neightet); // oldtet still connects to neightet. @@ -10024,11 +11062,12 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // Set a handle for speeding point location. recenttet = newtet; - //setpoint2tet(insertpt, encode(newtet)); - setpoint2tet(insertpt, (tetrahedron) (newtet.tet)); + setpoint2tet(insertpt, encode(newtet)); - // Re-use this list to save new interior cavity faces. - cavetetlist->restart(); + if (ivf->lawson > 1) { // if (lawson == 2 || lawson == 3) { + // Re-use this list to save new interior cavity faces. + cavetetlist->restart(); + } // Connect adjacent new tetrahedra together. for (i = 0; i < cavebdrylist->objects; i++) { @@ -10050,15 +11089,16 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } fsym(spintet, newneitet); esymself(newneitet); - assert(newneitet.tet[newneitet.ver & 3] == NULL); + assert(newneitet.tet[newneitet.ver & 3] == NULL); // FOR DEBUG bond(neightet, newneitet); - if (ivf->lawson > 1) { + if (ivf->lawson > 1) { + // We are updateing a CDT. Queue the internal face. + // See also fig/dump-cavity-case13, -case21. cavetetlist->newindex((void **) &parytet); *parytet = neightet; } } - //setpoint2tet(org(newtet), encode(newtet)); - setpoint2tet(org(newtet), (tetrahedron) (newtet.tet)); + setpoint2tet(org(newtet), encode(newtet)); enextself(newtet); enextself(oldtet); } @@ -10102,7 +11142,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } if (checksubfaceflag) { - if (ivf->splitbdflag) { + if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { // Recover new subfaces in C(p). for (i = 0; i < caveshbdlist->objects; i++) { // Get an old subface at edge [a, b]. @@ -10147,6 +11187,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // There should be no missing interior subfaces in C(p). assert(caveencshlist->objects == 0l); } else { + // bowywat = 1 or bowywat = 2. // The Boundary reocvery phase. // Put all new subfaces into stack for recovery. for (i = 0; i < caveshbdlist->objects; i++) { @@ -10155,6 +11196,12 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, spivot(*parysh, checksh); // The new subface [a, b, p]. // Do not recover a deleted new face (degenerated). if (checksh.sh[3] != NULL) { + if (b->verbose > 3) { + printf(" Queue new subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + //sdissolve(checksh); // It has not been connected yet. subfacstack->newindex((void **) &parysh); *parysh = checksh; } @@ -10166,6 +11213,11 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // Some subfaces inside C(p) might be split in sinsertvertex(). // Only queue those faces which are not split. if (!smarktested(*parysh)) { + if (b->verbose > 3) { + printf(" Queue a missing subface (%d, %d, %d) x%lx.\n", + pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh)), (uintptr_t) parysh->sh); + } checksh = *parysh; suninfect(checksh); stdissolve(checksh); // Detach connections to old tets. @@ -10177,7 +11229,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } // if (checksubfaceflag) if (checksubsegflag) { - if (ivf->splitbdflag) { + if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { if (splitseg != NULL) { // Recover the two new subsegments in C(p). for (i = 0; i < cavesegshlist->objects; i++) { @@ -10192,9 +11244,11 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, stpivot(checksh, neightet); } else { // It's a dangling segment. - point2tetorg(sorg(checkseg), neightet); - finddirection(&neightet, sdest(checkseg)); - assert(dest(neightet) == sdest(checkseg)); + pa = sorg(checkseg); + pb = sdest(checkseg); + point2tetorg(pa, neightet); + finddirection(&neightet, pb, 1); + assert(dest(neightet) == pb); } assert(!infected(neightet)); sstbond1(checkseg, neightet); @@ -10209,12 +11263,17 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // There should be no interior segment in C(p). assert(caveencseglist->objects == 0l); } else { + // bowywat == 1 or bowywat == 2; // The Boundary Recovery Phase. // Queue missing segments in C(p) for recovery. if (splitseg != NULL) { // Queue two new subsegments in C(p) for recovery. for (i = 0; i < cavesegshlist->objects; i++) { paryseg = (face *) fastlookup(cavesegshlist, i); + if (b->verbose > 3) { + printf(" Queue new subseg (%d, %d)\n", + pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg))); + } checkseg = *paryseg; //sstdissolve1(checkseg); // It has not been connected yet. s = randomnation(subsegstack->objects + 1); @@ -10228,6 +11287,10 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, paryseg = (face *) fastlookup(caveencseglist, i); assert(sinfected(*paryseg)); if (!smarktested(*paryseg)) { // It may be split. + if (b->verbose > 3) { + printf(" Queue a missing segment (%d, %d).\n", + pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg))); + } checkseg = *paryseg; suninfect(checkseg); sstdissolve1(checkseg); // Detach connections to old tets. @@ -10241,10 +11304,12 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } } // if (checksubsegflag) - if (b->weighted - ) { + if (b->plc || b->weighted) { // Some vertices may be completed inside the cavity. They must be // detected and added to recovering list. + if (b->plc) { + tetcount = subvertstack->objects; // Re-use tetcount; + } // Since every "live" vertex must contain a pointer to a non-dead // tetrahedron, we can check for each vertex this pointer. for (i = 0; i < cavetetvertlist->objects; i++) { @@ -10253,15 +11318,28 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, assert(searchtet->tet != NULL); // No tet has been deleted yet. if (infected(*searchtet)) { if (b->weighted) { - if (b->verbose > 1) { - printf(" Point #%d is non-regular after the insertion of #%d.\n", - pointmark(*pts), pointmark(insertpt)); + if (b->verbose > 2) { + printf(" Point #%d is removed from the hull.\n", + pointmark(*pts)); } - setpointtype(*pts, NREGULARVERTEX); - nonregularcount++; + setpointtype(*pts, UNUSEDVERTEX); + } else { + if (b->verbose > 3) { + printf(" Queue a dangling vertex %d.\n", pointmark(*pts)); + } + subvertstack->newindex((void **) &parypt); + *parypt = *pts; } } } + if (b->plc) { + if (subvertstack->objects > tetcount) { + // There are missing vertices after inserting the new point. + printf("DBG: Insert %d. Found %ld interior vertices.\n", + pointmark(insertpt), subvertstack->objects); + assert(0); // NEED TO DEBUG. + } + } } if (ivf->chkencflag & 1) { @@ -10270,14 +11348,23 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, paryseg = (face *) fastlookup(cavetetseglist, i); // Skip if it is the split segment. if (!sinfected(*paryseg)) { - enqueuesubface(badsubsegs, paryseg); + // Skip it if it has already queued. + if (!smarktest2ed(*paryseg)) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = *paryseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(*paryseg); // An alive badface. + } } } if (splitseg != NULL) { // Queue the two new subsegments inside C(p). for (i = 0; i < cavesegshlist->objects; i++) { paryseg = (face *) fastlookup(cavesegshlist, i); - enqueuesubface(badsubsegs, paryseg); + bface = (badface *) badsubsegs->alloc(); + bface->ss = *paryseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(*paryseg); // An alive badface. } } } // if (chkencflag & 1) @@ -10288,7 +11375,15 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, parysh = (face *) fastlookup(cavetetshlist, i); // Skip if it is a split subface. if (!sinfected(*parysh)) { - enqueuesubface(badsubfacs, parysh); + // Skip it if it has already queued. + if (!smarktest2ed(*parysh)) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = *parysh; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(*parysh); // An alive badface. + //bface->fdest = sdest(*parysh); + //bface->fapex = sapex(*parysh); + } } } // Queue all new subfaces inside C(p). @@ -10298,7 +11393,11 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, spivot(*parysh, checksh); // checksh is a new subface [a, b, p]. // Do not recover a deleted new face (degenerated). if (checksh.sh[3] != NULL) { - enqueuesubface(badsubfacs, &checksh); + //assert(!smarktest2ed(checksh)); + bface = (badface *) badsubfacs->alloc(); + bface->ss = checksh; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checksh); // An alive badface. } } } // if (chkencflag & 2) @@ -10307,18 +11406,19 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // Queue all new tetrahedra in C(p). for (i = 0; i < cavebdrylist->objects; i++) { cavetet = (triface *) fastlookup(cavebdrylist, i); - enqueuetetrahedron(cavetet); + //assert(!marktest2ed(*cavetet)); + bface = (badface *) badtetrahedrons->alloc(); + bface->tt = *cavetet; + marktest2(bface->tt); + bface->forg = org(*cavetet); } } - // C(p) is re-meshed successfully. + // C(p) is re-meshed successfully. - // Delete the old tets in C(p). + // Deleted the old tets in C(p). for (i = 0; i < caveoldtetlist->objects; i++) { searchtet = (triface *) fastlookup(caveoldtetlist, i); - if (ishulltet(*searchtet)) { - hullsize--; - } tetrahedrondealloc(searchtet->tet); } @@ -10344,7 +11444,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } shellfacedealloc(subfaces, parysh->sh); } - if ((splitseg != NULL) && (splitseg->sh != NULL)) { + if (splitseg != NULL) { // Delete the old segment in sC(p). shellfacedealloc(subsegs, splitseg->sh); } @@ -10353,25 +11453,28 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (ivf->lawson) { for (i = 0; i < cavebdrylist->objects; i++) { searchtet = (triface *) fastlookup(cavebdrylist, i); + //flippush(flipstack, searchtet, insertpt); flippush(flipstack, searchtet); } if (ivf->lawson > 1) { for (i = 0; i < cavetetlist->objects; i++) { searchtet = (triface *) fastlookup(cavetetlist, i); + //flippush(flipstack, searchtet, oppo(*searchtet)); flippush(flipstack, searchtet); } } } - // Set the point type if it is not set yet. - if (pointtype(insertpt) == UNUSEDVERTEX) { - if ((splitseg != NULL) && (splitseg->sh != NULL)) { - setpointtype(insertpt, FREESEGVERTEX); - } else if (splitsh != NULL) { - setpointtype(insertpt, FREEFACETVERTEX); - } else { - setpointtype(insertpt, FREEVOLVERTEX); - } + // The vertex should already have a type. + assert(pointtype(insertpt) != UNUSEDVERTEX); + +#ifdef WITH_RUNTIME_COUNTERS + tend = clock(); + t_ptinsert += (tend - tstart); +#endif + + if (b->btree) { + btree_insert(insertpt); } // Clean the working lists. @@ -10385,64 +11488,22 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, caveencseglist->restart(); } - if (checksubfaceflag) { - cavetetshlist->restart(); - caveencshlist->restart(); - } - - if (b->weighted || ivf->validflag) { - cavetetvertlist->restart(); - } - - if (splitsh != NULL) { - caveshlist->restart(); - caveshbdlist->restart(); - cavesegshlist->restart(); - } - - return 1; // Point is inserted. -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertpoint_abort() Abort the insertion of a new vertex. // -// // -// The cavity will be restored. All working lists are cleared. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf) -{ - triface *cavetet; - face *parysh; - int i; - - for (i = 0; i < caveoldtetlist->objects; i++) { - cavetet = (triface *) fastlookup(caveoldtetlist, i); - uninfect(*cavetet); - unmarktest(*cavetet); - } - for (i = 0; i < cavebdrylist->objects; i++) { - cavetet = (triface *) fastlookup(cavebdrylist, i); - unmarktest(*cavetet); - } - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - cavetetseglist->restart(); - cavetetshlist->restart(); - if (ivf->splitbdflag) { - if ((splitseg != NULL) && (splitseg->sh != NULL)) { - sunmarktest(*splitseg); - } - for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - assert(smarktested(*parysh)); - sunmarktest(*parysh); - } + if (checksubfaceflag) { + cavetetshlist->restart(); + caveencshlist->restart(); + } + + if (b->plc || b->weighted) { + cavetetvertlist->restart(); + } + + if (splitsh != NULL) { caveshlist->restart(); + caveshbdlist->restart(); cavesegshlist->restart(); } + + return (int) loc; } //// //// @@ -10457,10 +11518,10 @@ void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf) // // // transfernodes() Read the vertices from the input (tetgenio). // // // -// Transferring all points from input ('in->pointlist') to TetGen's 'points'.// -// All points are indexed (the first point index is 'in->firstnumber'). Each // -// point's type is initialized as UNUSEDVERTEX. The bounding box (xmax, xmin,// -// ...) and the diameter (longest) of the point set are calculated. // +// Initializing 'this->points'. Transferring all points from 'in->pointlist'// +// into it. All points are indexed (start from in->firstnumber). Each point // +// is initialized be UNUSEDVERTEX. The bounding box (xmin, xmax, ymin, ymax,// +// zmin, zmax) and the diameter (longest) of the point set are calculated. // // // /////////////////////////////////////////////////////////////////////////////// @@ -10473,6 +11534,9 @@ void tetgenmesh::transfernodes() int mtrindex; int i, j; + if (b->psc) { + assert(in->pointparamlist != NULL); + } // Read the points. coordindex = 0; @@ -10483,32 +11547,30 @@ void tetgenmesh::transfernodes() // Read the point coordinates. x = pointloop[0] = in->pointlist[coordindex++]; y = pointloop[1] = in->pointlist[coordindex++]; - z = pointloop[2] = in->pointlist[coordindex++]; - // Read the point attributes. (Including point weights.) - for (j = 0; j < in->numberofpointattributes; j++) { - pointloop[3 + j] = in->pointattributelist[attribindex++]; - } - // Read the point metric tensor. - for (j = 0; j < in->numberofpointmtrs; j++) { - pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++]; - } + z = pointloop[2] = in->pointlist[coordindex++]; if (b->weighted) { // -w option if (in->numberofpointattributes > 0) { - // The first point attribute is its weight. - //w = in->pointattributelist[in->numberofpointattributes * i]; - w = pointloop[3]; + // The first point attribute is weight. + w = in->pointattributelist[in->numberofpointattributes * i]; } else { - // No given weight available. Default choose the maximum - // absolute value among its coordinates. - w = fabs(x); - if (w < fabs(y)) w = fabs(y); - if (w < fabs(z)) w = fabs(z); + // No given weight available. + w = 0; } if (b->weighted_param == 0) { pointloop[3] = x * x + y * y + z * z - w; // Weighted DT. } else { // -w1 option pointloop[3] = w; // Regular tetrahedralization. } + } else { + pointloop[3] = 0; + } + // Read the point attributes. + for (j = 0; j < in->numberofpointattributes; j++) { + pointloop[4 + j] = in->pointattributelist[attribindex++]; + } + // Read the point metric tensor. + for (j = 0; j < in->numberofpointmtrs; j++) { + pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++]; } // Determine the smallest and largests x, y and z coordinates. if (i == 0) { @@ -10558,256 +11620,293 @@ void tetgenmesh::transfernodes() /////////////////////////////////////////////////////////////////////////////// // // -// hilbert_init() Initialize the Gray code permutation table. // -// // -// The table 'transgc' has 8 x 3 x 8 entries. It contains all possible Gray // -// code sequences traveled by the 1st order Hilbert curve in 3 dimensions. // -// The first column is the Gray code of the entry point of the curve, and // -// the second column is the direction (0, 1, or 2, 0 means the x-axis) where // -// the exit point of curve lies. // -// // -// The table 'tsb1mod3' contains the numbers of trailing set '1' bits of the // -// indices from 0 to 7, modulo by '3'. The code for generating this table is // -// from: http://graphics.stanford.edu/~seander/bithacks.html. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::hilbert_init(int n) -{ - int gc[8], N, mask, travel_bit; - int e, d, f, k, g; - int v, c; - int i; - - N = (n == 2) ? 4 : 8; - mask = (n == 2) ? 3 : 7; - - // Generate the Gray code sequence. - for (i = 0; i < N; i++) { - gc[i] = i ^ (i >> 1); - } - - for (e = 0; e < N; e++) { - for (d = 0; d < n; d++) { - // Calculate the end point (f). - f = e ^ (1 << d); // Toggle the d-th bit of 'e'. - // travel_bit = 2**p, the bit we want to travel. - travel_bit = e ^ f; - for (i = 0; i < N; i++) { - // // Rotate gc[i] left by (p + 1) % n bits. - k = gc[i] * (travel_bit * 2); - g = ((k | (k / N)) & mask); - // Calculate the permuted Gray code by xor with the start point (e). - transgc[e][d][i] = (g ^ e); - } - assert(transgc[e][d][0] == e); - assert(transgc[e][d][N - 1] == f); - } // d - } // e - - // Count the consecutive '1' bits (trailing) on the right. - tsb1mod3[0] = 0; - for (i = 1; i < N; i++) { - v = ~i; // Count the 0s. - v = (v ^ (v - 1)) >> 1; // Set v's trailing 0s to 1s and zero rest - for (c = 0; v; c++) { - v >>= 1; - } - tsb1mod3[i] = c % n; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// hilbert_sort3() Sort points using the 3d Hilbert curve. // +// btree_sort() Sort vertices using a binary space partition (bsp) tree. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::hilbert_split(point* vertexarray,int arraysize,int gc0,int gc1, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, - REAL bzmin, REAL bzmax) +void tetgenmesh::btree_sort(point* vertexarray, int arraysize, int axis, + REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, + REAL bzmin, REAL bzmax, int depth) { - point swapvert; - int axis, d; + point *leftarray, *rightarray; + point **pptary, swapvert; REAL split; - int i, j; + bool lflag, rflag; + int i, j, k; // *iptr, + if (b->verbose > 3) { + printf(" Depth %d, %d verts. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n", + depth, arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax, + axis == 0 ? "x" : (axis == 1 ? "y" : "z")); + } - // Find the current splitting axis. 'axis' is a value 0, or 1, or 2, which - // correspoding to x-, or y- or z-axis. - axis = (gc0 ^ gc1) >> 1; + if (depth > max_btree_depth) { + max_btree_depth = depth; + } - // Calulate the split position along the axis. if (axis == 0) { + // Split along x-axis. split = 0.5 * (bxmin + bxmax); } else if (axis == 1) { + // Split along y-axis. split = 0.5 * (bymin + bymax); - } else { // == 2 + } else { + // Split along z-axis. split = 0.5 * (bzmin + bzmax); } - // Find the direction (+1 or -1) of the axis. If 'd' is +1, the direction - // of the axis is to the positive of the axis, otherwise, it is -1. - d = ((gc0 & (1<<axis)) == 0) ? 1 : -1; - - // Partition the vertices into left- and right-arraies such that left points - // have Hilbert indices lower than the right points. i = 0; j = arraysize - 1; // Partition the vertices into left- and right-arraies. - if (d > 0) { - do { - for (; i < arraysize; i++) { - if (vertexarray[i][axis] >= split) break; - } - for (; j >= 0; j--) { - if (vertexarray[j][axis] < split) break; - } - // Is the partition finished? - if (i == (j + 1)) break; - // Swap i-th and j-th vertices. - swapvert = vertexarray[i]; - vertexarray[i] = vertexarray[j]; - vertexarray[j] = swapvert; - // Continue patitioning the array; - } while (true); - } else { - do { - for (; i < arraysize; i++) { - if (vertexarray[i][axis] <= split) break; + do { + for (; i < arraysize; i++) { + if (vertexarray[i][axis] >= split) { + break; + } + } + for (; j >= 0; j--) { + if (vertexarray[j][axis] < split) { + break; + } + } + // Is the partition finished? + if (i == (j + 1)) { + break; + } + // Swap i-th and j-th vertices. + swapvert = vertexarray[i]; + vertexarray[i] = vertexarray[j]; + vertexarray[j] = swapvert; + // Continue patitioning the array; + } while (true); + + if (b->verbose > 3) { + printf(" leftsize = %d, rightsize = %d\n", i, arraysize - i); + } + lflag = rflag = false; + + // Do not continue the partition if one of the array sizes is 0. + // if (depth < max_tree_depth) { + if (!((i == 0) || (i == arraysize))) { + if (i > b->max_btreenode_size) { + // Recursively partition the left array (length = i). + if (axis == 0) { // x + btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, split, bymin, + bymax, bzmin, bzmax, depth + 1); + } else if (axis == 1) { // y + btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin, + split, bzmin, bzmax, depth + 1); + } else { // z + btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin, + bymax, bzmin, split, depth + 1); } - for (; j >= 0; j--) { - if (vertexarray[j][axis] > split) break; + } else { + lflag = true; + } + if ((arraysize - i) > b->max_btreenode_size) { + // Recursively partition the right array (length = arraysize - i). + if (axis == 0) { // x + btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, split, + bxmax, bymin, bymax, bzmin, bzmax, depth + 1); + } else if (axis == 1) { // y + btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin, + bxmax, split, bymax, bzmin, bzmax, depth + 1); + } else { // z + btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin, + bxmax, bymin, bymax, split, bzmax, depth + 1); } - // Is the partition finished? - if (i == (j + 1)) break; - // Swap i-th and j-th vertices. - swapvert = vertexarray[i]; - vertexarray[i] = vertexarray[j]; - vertexarray[j] = swapvert; - // Continue patitioning the array; - } while (true); + } else { + rflag = true; + } + } else { + // Both left and right are done. + lflag = rflag = true; + } + + if (lflag && (i > 0)) { + // Remember the maximal length of the partitions. + if (i > max_btreenode_size) { + max_btreenode_size = i; + } + // Allocate space for the left array (use the first entry to save + // the length of this array). + leftarray = new point[i + 1]; + leftarray[0] = (point) i; // The array lenth. + //iptr = (int *) &(leftarray[0]); + //*iptr = i; // Save the array lenth in the first entry. + // Put all points in this array. + for (k = 0; k < i; k++) { + leftarray[k + 1] = vertexarray[k]; + setpoint2ppt(leftarray[k + 1], (point) leftarray); + } + // Save this array in list. + btreenode_list->newindex((void **) &pptary); + *pptary = leftarray; + } + + // Get the length of the right array. + j = arraysize - i; + if (rflag && (j > 0)) { + if (j > max_btreenode_size) { + max_btreenode_size = j; + } + // Allocate space for the right array (use the first entry to save + // the length of this array). + rightarray = new point[j + 1]; + rightarray[0] = (point) j; // The array lenth. + //iptr = (int *) &(rightarray[0]); + //*iptr = j; // Save the array length in the first entry. + // Put all points in this array. + for (k = 0; k < j; k++) { + rightarray[k + 1] = vertexarray[i + k]; + setpoint2ppt(rightarray[k + 1], (point) rightarray); + } + // Save this array in list. + btreenode_list->newindex((void **) &pptary); + *pptary = rightarray; } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// btree_insert() Add a vertex into a tree node. // +// // +/////////////////////////////////////////////////////////////////////////////// - return i; +void tetgenmesh::btree_insert(point insertpt) +{ + point *ptary; + long arylen; // The array lenhgth is saved in ptary[0]. + + // Get the tree node (save in this point). + ptary = (point *) point2ppt(insertpt); + // Get the current array length. + arylen = (long long) ptary[0]; + // Insert the point into the node. + ptary[arylen + 1] = insertpt; + // Increase the array length by 1. + ptary[0] = (point) (arylen + 1); } -void tetgenmesh::hilbert_sort3(point* vertexarray, int arraysize, int e, int d, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, - REAL bzmin, REAL bzmax, int depth) +/////////////////////////////////////////////////////////////////////////////// +// // +// btree_search() Search a near point for an inserting point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::btree_search(point insertpt, triface* searchtet) { - REAL x1, x2, y1, y2, z1, z2; - int p[9], w, e_w, d_w, k, ei, di; - int n = 3, mask = 7; - - p[0] = 0; - p[8] = arraysize; - - // Sort the points according to the 1st order Hilbert curve in 3d. - p[4] = hilbert_split(vertexarray, p[8], transgc[e][d][3], transgc[e][d][4], - bxmin, bxmax, bymin, bymax, bzmin, bzmax); - p[2] = hilbert_split(vertexarray, p[4], transgc[e][d][1], transgc[e][d][2], - bxmin, bxmax, bymin, bymax, bzmin, bzmax); - p[1] = hilbert_split(vertexarray, p[2], transgc[e][d][0], transgc[e][d][1], - bxmin, bxmax, bymin, bymax, bzmin, bzmax); - p[3] = hilbert_split(&(vertexarray[p[2]]), p[4] - p[2], - transgc[e][d][2], transgc[e][d][3], - bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[2]; - p[6] = hilbert_split(&(vertexarray[p[4]]), p[8] - p[4], - transgc[e][d][5], transgc[e][d][6], - bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4]; - p[5] = hilbert_split(&(vertexarray[p[4]]), p[6] - p[4], - transgc[e][d][4], transgc[e][d][5], - bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4]; - p[7] = hilbert_split(&(vertexarray[p[6]]), p[8] - p[6], - transgc[e][d][6], transgc[e][d][7], - bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[6]; - - if (b->hilbert_order > 0) { - // A maximum order is prescribed. - if ((depth + 1) == b->hilbert_order) { - // The maximum prescribed order is reached. - return; - } - } - - // Recursivly sort the points in sub-boxes. - for (w = 0; w < 8; w++) { - // w is the local Hilbert index (NOT Gray code). - // Sort into the sub-box either there are more than 2 points in it, or - // the prescribed order of the curve is not reached yet. - //if ((p[w+1] - p[w] > b->hilbert_limit) || (b->hilbert_order > 0)) { - if ((p[w+1] - p[w]) > b->hilbert_limit) { - // Calulcate the start point (ei) of the curve in this sub-box. - // update e = e ^ (e(w) left_rotate (d+1)). - if (w == 0) { - e_w = 0; - } else { - // calculate e(w) = gc(2 * floor((w - 1) / 2)). - k = 2 * ((w - 1) / 2); - e_w = k ^ (k >> 1); // = gc(k). - } - k = e_w; - e_w = ((k << (d+1)) & mask) | ((k >> (n-d-1)) & mask); - ei = e ^ e_w; - // Calulcate the direction (di) of the curve in this sub-box. - // update d = (d + d(w) + 1) % n - if (w == 0) { - d_w = 0; - } else { - d_w = ((w % 2) == 0) ? tsb1mod3[w - 1] : tsb1mod3[w]; - } - di = (d + d_w + 1) % n; - // Calculate the bounding box of the sub-box. - if (transgc[e][d][w] & 1) { // x-axis - x1 = 0.5 * (bxmin + bxmax); - x2 = bxmax; - } else { - x1 = bxmin; - x2 = 0.5 * (bxmin + bxmax); - } - if (transgc[e][d][w] & 2) { // y-axis - y1 = 0.5 * (bymin + bymax); - y2 = bymax; - } else { - y1 = bymin; - y2 = 0.5 * (bymin + bymax); + point *ptary; + point nearpt, candpt; + REAL dist2, mindist2; + int ptsamples, ptidx; + long arylen; + int i; + + // Get the tree node (save in this point). + ptary = (point *) point2ppt(insertpt); + // Get the current array length. + arylen = (long long) ptary[0]; + + if (arylen == 0) { + searchtet->tet = NULL; + return; + } + + if (1) { + + if (arylen < 5) { //if (arylen < 10) { + ptsamples = arylen; + } else { + ptsamples = 5; // Take at least 10 samples. + // The number of random samples taken is proportional to the third root + // of the number of points in the cell. + while (ptsamples * ptsamples * ptsamples < arylen) { + ptsamples++; } - if (transgc[e][d][w] & 4) { // z-axis - z1 = 0.5 * (bzmin + bzmax); - z2 = bzmax; - } else { - z1 = bzmin; - z2 = 0.5 * (bzmin + bzmax); + } + + // Select "good" candidate using k random samples, taking the closest one. + mindist2 = 1.79769E+308; // The largest double value (8 byte). + nearpt = NULL; + + for (i = 0; i < ptsamples; i++) { + ptidx = randomnation((unsigned long) arylen); + candpt = ptary[ptidx + 1]; + dist2 = (candpt[0] - insertpt[0]) * (candpt[0] - insertpt[0]) + + (candpt[1] - insertpt[1]) * (candpt[1] - insertpt[1]) + + (candpt[2] - insertpt[2]) * (candpt[2] - insertpt[2]); + if (dist2 < mindist2) { + mindist2 = dist2; + nearpt = candpt; } - hilbert_sort3(&(vertexarray[p[w]]), p[w+1] - p[w], ei, di, - x1, x2, y1, y2, z1, z2, depth+1); - } // if (p[w+1] - p[w] > 1) - } // w + } + + } else { + + // TEST, randomly select a point. + ptidx = randomnation((unsigned long) arylen); + nearpt = ptary[ptidx + 1]; + + } + + if (b->verbose > 2) { + printf(" Get point %d (cell size %ld).\n", pointmark(nearpt), arylen); + } + + decode(point2tet(nearpt), *searchtet); } /////////////////////////////////////////////////////////////////////////////// // // -// brio_multiscale_sort() Sort the points using BRIO and Hilbert curve. // +// ordervertices() Order the vertices for incremental inserting. // +// // +// We assume the vertices have been sorted by a binary tree. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, - int threshold, REAL ratio, int *depth) +void tetgenmesh::ordervertices(point* vertexarray, int arraysize) { - int middle; - - middle = 0; - if (arraysize >= threshold) { - (*depth)++; - middle = arraysize * ratio; - brio_multiscale_sort(vertexarray, middle, threshold, ratio, depth); - } - // Sort the right-array (rnd-th round) using the Hilbert curve. - hilbert_sort3(&(vertexarray[middle]), arraysize - middle, 0, 0, // e, d - xmin, xmax, ymin, ymax, zmin, zmax, 0); // depth. + point **ipptary, **jpptary, *swappptary; + point *ptary; + long arylen; + int index, i, j; + + // First pick one vertex from each tree node. + for (i = 0; i < (int) btreenode_list->objects; i++) { + ipptary = (point **) fastlookup(btreenode_list, i); + ptary = *ipptary; + vertexarray[i] = ptary[1]; // Skip the first entry. + } + + index = i; + // Then put all other points in the array node by node. + for (i = (int) btreenode_list->objects - 1; i >= 0; i--) { + // Randomly pick a tree node. + j = randomnation(i + 1); + // Save the i-th node. + ipptary = (point **) fastlookup(btreenode_list, i); + // Get the j-th node. + jpptary = (point **) fastlookup(btreenode_list, j); + // Order the points in the node. + ptary = *jpptary; + arylen = (long long) ptary[0]; + for (j = 2; j <= arylen; j++) { // Skip the first point. + vertexarray[index] = ptary[j]; + index++; + } + // Clear this tree node. + ptary[0] = (point) 0; + // Swap i-th node to j-th node. + swappptary = *ipptary; + *ipptary = *jpptary; // [i] <= [j] + *jpptary = swappptary; // [j] <= [i] + } + + // Make sure we've done correctly. + assert(index == arraysize); } /////////////////////////////////////////////////////////////////////////////// @@ -10839,14 +11938,17 @@ unsigned long tetgenmesh::randomnation(unsigned int choices) // // // randomsample() Randomly sample the tetrahedra for point loation. // // // -// Searching begins from one of handles: the input 'searchtet', a recently // -// encountered tetrahedron 'recenttet', or from one chosen from a random // -// sample. The choice is made by determining which one's origin is closest // -// to the point we are searcing for. // +// This routine implements Muecke's Jump-and-walk point location algorithm. // +// It improves the simple walk-through by "jumping" to a good starting point // +// via random sampling. Searching begins from one of handles: the input // +// 'searchtet', a recently encountered tetrahedron 'recenttet', or from one // +// chosen from a random sample. The choice is made by determining which one // +// 's origin is closest to the point we are searcing for. Having chosen the // +// starting tetrahedron, the simple Walk-through algorithm is executed. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::randomsample(point searchpt,triface *searchtet) +void tetgenmesh::randomsample(point searchpt, triface *searchtet) { tetrahedron *firsttet, *tetptr; point torg; @@ -10861,50 +11963,44 @@ void tetgenmesh::randomsample(point searchpt,triface *searchtet) pointmark(searchpt)); } - if (!nonconvex) { - if (searchtet->tet == NULL) { - // A null tet. Choose the recenttet as the starting tet. - *searchtet = recenttet; - // Recenttet should not be dead. - assert(recenttet.tet[4] != NULL); - } + if (searchtet->tet == NULL) { + // A null tet. Choose the recenttet as the starting tet. + *searchtet = recenttet; + // Recenttet should not be dead. + assert(recenttet.tet[4] != NULL); + } - // 'searchtet' should be a valid tetrahedron. Choose the base face - // whose vertices must not be 'dummypoint'. - searchtet->ver = 3; - // Record the distance from its origin to the searching point. - torg = org(*searchtet); - searchdist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) + - (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) + - (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]); - if (b->verbose > 3) { - printf(" Dist %g from tet (%d, %d, %d, %d).\n", searchdist, - pointmark(torg), pointmark(dest(*searchtet)), - pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); - } - - // If a recently encountered tetrahedron has been recorded and has not - // been deallocated, test it as a good starting point. - if (recenttet.tet != searchtet->tet) { - recenttet.ver = 3; - torg = org(recenttet); - dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) + - (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) + - (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]); - if (dist < searchdist) { - *searchtet = recenttet; - searchdist = dist; - if (b->verbose > 3) { - printf(" Dist %g from recent tet (%d, %d, %d, %d).\n", - searchdist, pointmark(torg), pointmark(dest(*searchtet)), - pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); - } + // 'searchtet' should be a valid tetrahedron. Choose the base face + // whose vertices must not be 'dummypoint'. + searchtet->ver = 3; + // Record the distance from its origin to the searching point. + torg = org(*searchtet); + searchdist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) + + (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) + + (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]); + if (b->verbose > 3) { + printf(" Dist %g from tet (%d, %d, %d, %d).\n", searchdist, + pointmark(torg), pointmark(dest(*searchtet)), + pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); + } + + // If a recently encountered tetrahedron has been recorded and has not + // been deallocated, test it as a good starting point. + if (recenttet.tet != searchtet->tet) { + recenttet.ver = 3; + torg = org(recenttet); + dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) + + (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) + + (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]); + if (dist < searchdist) { + *searchtet = recenttet; + searchdist = dist; + if (b->verbose > 3) { + printf(" Dist %g from recent tet (%d, %d, %d, %d).\n", + searchdist, pointmark(torg), pointmark(dest(*searchtet)), + pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); } } - } else { - // The mesh is non-convex. Do not use 'recenttet'. - assert(samples >= 1l); // Make sure at least 1 sample. - searchdist = longest; } // Select "good" candidate using k random samples, taking the closest one. @@ -10944,6 +12040,11 @@ void tetgenmesh::randomsample(point searchpt,triface *searchtet) searchtet->tet = tetptr; searchtet->ver = 11; // torg = org(t); searchdist = dist; + if (b->verbose > 3) { + printf(" Dist %g from tet (%d, %d, %d, %d).\n", searchdist, + pointmark(torg), pointmark(dest(*searchtet)), + pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); + } } } else { // A dead tet. Re-sample it. @@ -10958,6 +12059,7 @@ void tetgenmesh::randomsample(point searchpt,triface *searchtet) // // // locate() Find a tetrahedron containing a given point. // // // +// This routine implements the simple Walk-through point location algorithm. // // Begins its search from 'searchtet', assume there is a line segment L from // // a vertex of 'searchtet' to the query point 'searchpt', and simply walk // // towards 'searchpt' by traversing all faces intersected by L. // @@ -10969,26 +12071,34 @@ void tetgenmesh::randomsample(point searchpt,triface *searchtet) // - ONFACE, the search point lies on a face of 'searchtet'. // // - INTET, the search point lies in the interior of 'searchtet'. // // - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a // -// hull face which is visible by the search point. // +// hull tetrahedron whose base face is visible by the search point. // // // // WARNING: This routine is designed for convex triangulations, and will not // // generally work after the holes and concavities have been carved. // // // +// If 'randflag' is set (> 0). Randomly choose a tetrahedron when there are // +// multiple choices in the path. // +// // /////////////////////////////////////////////////////////////////////////////// enum tetgenmesh::locateresult - tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag) + tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag, + int randflag) { + triface neightet; + face checksh; point torg, tdest, tapex, toppo; enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; REAL ori, oriorg, oridest, oriapex; - enum locateresult loc = OUTSIDE; - int t1ver; - int s; + enum locateresult loc; + int s; // i; + if (searchtet->tet == NULL) { // A null tet. Choose the recenttet as the starting tet. - searchtet->tet = recenttet.tet; + *searchtet = recenttet; + // Recenttet should not be dead. + assert(recenttet.tet[4] != NULL); } // Check if we are in the outside of the convex hull. @@ -10996,6 +12106,7 @@ enum tetgenmesh::locateresult // Get its adjacent tet (inside the hull). searchtet->ver = 3; fsymself(*searchtet); + assert(!ishulltet(*searchtet)); } // Let searchtet be the face such that 'searchpt' lies above to it. @@ -11003,14 +12114,22 @@ enum tetgenmesh::locateresult torg = org(*searchtet); tdest = dest(*searchtet); tapex = apex(*searchtet); - ori = orient3d(torg, tdest, tapex, searchpt); + ori = orient3d(torg, tdest, tapex, searchpt); orient3dcount++; if (ori < 0.0) break; } - assert(searchtet->ver != 4); + if (searchtet->ver == 4) { + // Either 'searchtet' is a very flat tet, or the 'searchpt' lies in + // infinity, or both of them. Return OUTSIDE. + assert(0); // return OUTSIDE; + } + + loc = OUTSIDE; // Set a default return value. // Walk through tetrahedra to locate the point. while (true) { + ptloc_count++; // Algorithimic count. + toppo = oppo(*searchtet); // Check if the vertex is we seek. @@ -11022,43 +12141,58 @@ enum tetgenmesh::locateresult break; } - // We enter from one of serarchtet's faces, which face do we exit? + // We enter from serarchtet's base face. There are three other faces in + // searchtet (all connecting to toppo), which one is the exit? oriorg = orient3d(tdest, tapex, toppo, searchpt); oridest = orient3d(tapex, torg, toppo, searchpt); oriapex = orient3d(torg, tdest, toppo, searchpt); + orient3dcount+=3; // Now decide which face to move. It is possible there are more than one - // faces are viable moves. If so, randomly choose one. + // faces are viable moves. Use the opposite points of thier neighbors + // to discriminate, i.e., we choose the face whose opposite point has + // the shortest distance to searchpt. if (oriorg < 0) { if (oridest < 0) { if (oriapex < 0) { - // All three faces are possible. - s = randomnation(3); // 's' is in {0,1,2}. - if (s == 0) { - nextmove = ORGMOVE; - } else if (s == 1) { - nextmove = DESTMOVE; + if (0) { //if (!randflag) { } else { - nextmove = APEXMOVE; - } + // Randomly choose a direction. + s = randomnation(3); // 's' is in {0,1,2}. + if (s == 0) { + nextmove = ORGMOVE; + } else if (s == 1) { + nextmove = DESTMOVE; + } else { + nextmove = APEXMOVE; + } + } // if (randflag) } else { // Two faces, opposite to origin and destination, are viable. - //s = randomnation(2); // 's' is in {0,1}. - if (randomnation(2)) { - nextmove = ORGMOVE; + if (0) { // if (!randflag) { } else { - nextmove = DESTMOVE; - } + // Randomly choose a direction. + s = randomnation(2); // 's' is in {0,1}. + if (s == 0) { + nextmove = ORGMOVE; + } else { + nextmove = DESTMOVE; + } + } // if (randflag) } } else { if (oriapex < 0) { // Two faces, opposite to origin and apex, are viable. - //s = randomnation(2); // 's' is in {0,1}. - if (randomnation(2)) { - nextmove = ORGMOVE; + if (0) { // if (!randflag) { } else { - nextmove = APEXMOVE; - } + // Randomly choose a direction. + s = randomnation(2); // 's' is in {0,1}. + if (s == 0) { + nextmove = ORGMOVE; + } else { + nextmove = APEXMOVE; + } + } // if (randflag) } else { // Only the face opposite to origin is viable. nextmove = ORGMOVE; @@ -11068,12 +12202,16 @@ enum tetgenmesh::locateresult if (oridest < 0) { if (oriapex < 0) { // Two faces, opposite to destination and apex, are viable. - //s = randomnation(2); // 's' is in {0,1}. - if (randomnation(2)) { - nextmove = DESTMOVE; + if (0) { // if (!randflag) { } else { - nextmove = APEXMOVE; - } + // Randomly choose a direction. + s = randomnation(2); // 's' is in {0,1}. + if (s == 0) { + nextmove = DESTMOVE; + } else { + nextmove = APEXMOVE; + } + } // if (randflag) } else { // Only the face opposite to destination is viable. nextmove = DESTMOVE; @@ -11087,8 +12225,10 @@ enum tetgenmesh::locateresult // tetrahedron. Check for boundary cases. if (oriorg == 0) { // Go to the face opposite to origin. + //enextfnextself(*searchtet); enextesymself(*searchtet); if (oridest == 0) { + //enextself(*searchtet); // edge apex->oppo eprevself(*searchtet); // edge oppo->apex if (oriapex == 0) { // oppo is duplicated with p. @@ -11099,294 +12239,74 @@ enum tetgenmesh::locateresult break; } if (oriapex == 0) { + //enext2self(*searchtet); enextself(*searchtet); // edge dest->oppo loc = ONEDGE; // return ONEDGE; - break; - } - loc = ONFACE; // return ONFACE; - break; - } - if (oridest == 0) { - // Go to the face opposite to destination. - eprevesymself(*searchtet); - if (oriapex == 0) { - eprevself(*searchtet); // edge oppo->org - loc = ONEDGE; // return ONEDGE; - break; - } - loc = ONFACE; // return ONFACE; - break; - } - if (oriapex == 0) { - // Go to the face opposite to apex - esymself(*searchtet); - loc = ONFACE; // return ONFACE; - break; - } - loc = INTETRAHEDRON; // return INTETRAHEDRON; - break; - } - } - } - - // Move to the selected face. - if (nextmove == ORGMOVE) { - enextesymself(*searchtet); - } else if (nextmove == DESTMOVE) { - eprevesymself(*searchtet); - } else { - esymself(*searchtet); - } - if (chkencflag) { - // Check if we are walking across a subface. - if (issubface(*searchtet)) { - loc = ENCSUBFACE; - break; - } - } - // Move to the adjacent tetrahedron (maybe a hull tetrahedron). - fsymself(*searchtet); - if (oppo(*searchtet) == dummypoint) { - loc = OUTSIDE; // return OUTSIDE; - break; - } - - // Retreat the three vertices of the base face. - torg = org(*searchtet); - tdest = dest(*searchtet); - tapex = apex(*searchtet); - - } // while (true) - - return loc; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// flippush() Push a face (possibly will be flipped) into flipstack. // -// // -// The face is marked. The flag is used to check the validity of the face on // -// its popup. Some other flips may change it already. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::flippush(badface*& fstack, triface* flipface) -{ - badface *newflipface; - - if (!facemarked(*flipface)) { - newflipface = (badface *) flippool->alloc(); - newflipface->tt = *flipface; - markface(newflipface->tt); - // Push this face into stack. - newflipface->nextitem = fstack; - fstack = newflipface; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// incrementalflip() Incrementally flipping to construct DT. // -// // -// Faces need to be checked for flipping are already queued in 'flipstack'. // -// Return the total number of performed flips. // -// // -// Comment: This routine should be only used in the incremental Delaunay // -// construction. In other cases, lawsonflip3d() should be used. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::incrementalflip(point newpt, flipconstraints *fc) -{ - badface *popface; - triface fliptets[5], *parytet; - point *pts; - REAL sign, ori; - int flipcount = 0; - int i; - - if (b->verbose > 2) { - printf(" Lawson flip (%ld faces).\n", flippool->items); - } - - // Loop until the queue is empty. - while (flipstack != NULL) { - - // Pop a face from the stack. - popface = flipstack; - fliptets[0] = popface->tt; - flipstack = flipstack->nextitem; // The next top item in stack. - flippool->dealloc((void *) popface); - - // Skip it if it is a dead tet (destroyed by previous flips). - if (isdeadtet(fliptets[0])) continue; - // Skip it if it is not the same tet as we saved. - if (!facemarked(fliptets[0])) continue; - - unmarkface(fliptets[0]); - - if ((point) fliptets[0].tet[7] == dummypoint) { - // It must be a hull edge. - fliptets[0].ver = epivot[fliptets[0].ver]; - // A hull edge. The current convex hull may be enlarged. - fsym(fliptets[0], fliptets[1]); - pts = (point *) fliptets[1].tet; - ori = orient3d(pts[4], pts[5], pts[6], newpt); - if (ori < 0) { - // Perform a 2-to-3 flip. Replace face [a,b,c] by edge [e,d]. - // [0] [a,b,c,d], d = newpt. - // [1] [b,a,c,e], c = dummypoint. - flip23(fliptets, 1, fc); - flipcount++; - } else if (ori == 0) { - if (oppo(fliptets[1]) != newpt) { - // A coplanar convex hull face! - // Treat it as unflippable. - } else { - // Two identical hull faces were bonded together! - triface fliptet = fliptets[0]; - triface neightet = fliptets[1]; - // First infect the two hull tets (they will be deleted). - infect(fliptet); - infect(neightet); - // Connect the actual adjacent tets. - for (i = 0; i < 3; i++) { - fnext(fliptet, fliptets[0]); - fnext(neightet, fliptets[1]); - if (!infected(fliptets[0])) { - assert(!infected(fliptets[1])); - bond(fliptets[0], fliptets[1]); - // Update the point-to-tet map. - setpoint2tet(org(fliptet), encode(fliptets[0])); - setpoint2tet(dest(fliptet), encode(fliptets[0])); - // Remeber a recent tet for point location. - recenttet = fliptets[0]; - // apex(fliptets[0]) is the new point. The opposite face may - // be not locally Delaunay. Put it in flip stack. - assert(apex(fliptets[0]) == newpt); // SELF_CHECK - esymself(fliptets[0]); - flippush(flipstack, &(fliptets[0])); - assert(apex(fliptets[1]) == newpt); // SELF_CHECK - esymself(fliptets[1]); - flippush(flipstack, &(fliptets[1])); + break; } - enextself(fliptet); - eprevself(neightet); + loc = ONFACE; // return ONFACE; + break; + } + if (oridest == 0) { + // Go to the face opposite to destination. + //enext2fnextself(*searchtet); + eprevesymself(*searchtet); + if (oriapex == 0) { + //enextself(*searchtet); + eprevself(*searchtet); // edge oppo->org + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oriapex == 0) { + // Go to the face opposite to apex + //fnextself(*searchtet); + esymself(*searchtet); + loc = ONFACE; // return ONFACE; + break; } - // Delete the two tets. - tetrahedrondealloc(fliptet.tet); - tetrahedrondealloc(neightet.tet); - // Update the hull size. - hullsize -= 2; - flipcount++; + loc = INTETRAHEDRON; // return INTETRAHEDRON; + break; } - } // ori - continue; - } // if (dummypoint) - - fsym(fliptets[0], fliptets[1]); - if ((point) fliptets[1].tet[7] == dummypoint) { - // A hull face is locally Delaunay. - continue; - } - // Check if the adjacent tet has already been tested. - if (marktested(fliptets[1])) { - // It has been tested and it is Delaunay. - continue; + } } - - // Test whether the face is locally Delaunay or not. - pts = (point *) fliptets[1].tet; - if (b->weighted) { - sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], newpt, - pts[4][3], pts[5][3], pts[6][3], pts[7][3], - newpt[3]); + + // Move to the selected face. + if (nextmove == ORGMOVE) { + enextesymself(*searchtet); + } else if (nextmove == DESTMOVE) { + eprevesymself(*searchtet); } else { - sign = insphere_s(pts[4], pts[5], pts[6], pts[7], newpt); + esymself(*searchtet); } - - - if (sign < 0) { - point pd = newpt; - point pe = oppo(fliptets[1]); - // Check the convexity of its three edges. Stop checking either a - // locally non-convex edge (ori < 0) or a flat edge (ori = 0) is - // encountered, and 'fliptet' represents that edge. - for (i = 0; i < 3; i++) { - ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe); - if (ori <= 0) break; - enextself(fliptets[0]); + if (chkencflag) { + // Check if we are walking across a subface. + tspivot(*searchtet, checksh); + if (checksh.sh != NULL) { + loc = ENCSUBFACE; + break; } - if (ori > 0) { - // A 2-to-3 flip is found. - // [0] [a,b,c,d], - // [1] [b,a,c,e]. no dummypoint. - flip23(fliptets, 0, fc); - flipcount++; - } else { // ori <= 0 - // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat, - // where the edge [a',b'] is one of [a,b], [b,c], and [c,a]. - // Check if there are three or four tets sharing at this edge. - esymself(fliptets[0]); // [b,a,d,c] - for (i = 0; i < 3; i++) { - fnext(fliptets[i], fliptets[i+1]); - } - if (fliptets[3].tet == fliptets[0].tet) { - // A 3-to-2 flip is found. (No hull tet.) - flip32(fliptets, 0, fc); - flipcount++; - } else { - // There are more than 3 tets at this edge. - fnext(fliptets[3], fliptets[4]); - if (fliptets[4].tet == fliptets[0].tet) { - if (ori == 0) { - // A 4-to-4 flip is found. (Two hull tets may be involved.) - // Current tets in 'fliptets': - // [0] [b,a,d,c] (d may be newpt) - // [1] [b,a,c,e] - // [2] [b,a,e,f] (f may be dummypoint) - // [3] [b,a,f,d] - esymself(fliptets[0]); // [a,b,c,d] - // A 2-to-3 flip replaces face [a,b,c] by edge [e,d]. - // This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]). - // It will be removed by the followed 3-to-2 flip. - flip23(fliptets, 0, fc); // No hull tet. - fnext(fliptets[3], fliptets[1]); - fnext(fliptets[1], fliptets[2]); - // Current tets in 'fliptets': - // [0] [...] - // [1] [b,a,d,e] (degenerated, d may be new point). - // [2] [b,a,e,f] (f may be dummypoint) - // [3] [b,a,f,d] - // A 3-to-2 flip replaces edge [b,a] by face [d,e,f]. - // Hull tets may be involved (f may be dummypoint). - flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc); - flipcount++; - } - } - } - } // ori - } else { - // The adjacent tet is Delaunay. Mark it to avoid testing it again. - marktest(fliptets[1]); - // Save it for unmarking it later. - cavebdrylist->newindex((void **) &parytet); - *parytet = fliptets[1]; + } + // Move to the adjacent tetrahedron (maybe a hull tetrahedron). + fsymself(*searchtet); + if (oppo(*searchtet) == dummypoint) { + loc = OUTSIDE; // return OUTSIDE; + break; } - } // while (flipstack) - - // Unmark saved tetrahedra. - for (i = 0; i < cavebdrylist->objects; i++) { - parytet = (triface *) fastlookup(cavebdrylist, i); - unmarktest(*parytet); - } - cavebdrylist->restart(); + // Retreat the three vertices of the base face. + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + } // while (true) - return flipcount; + return loc; } + /////////////////////////////////////////////////////////////////////////////// // // // initialdelaunay() Create an initial Delaunay tetrahedralization. // @@ -11463,6 +12383,13 @@ void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) setpointtype(pd, VOLVERTEX); } + if (b->btree) { + btree_insert(pa); + btree_insert(pb); + btree_insert(pc); + btree_insert(pd); + } + setpoint2tet(pa, encode(firsttet)); setpoint2tet(pb, encode(firsttet)); setpoint2tet(pc, encode(firsttet)); @@ -11479,29 +12406,53 @@ void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) // // /////////////////////////////////////////////////////////////////////////////// - void tetgenmesh::incrementaldelaunay(clock_t& tv) { triface searchtet; point *permutarray, swapvertex; + insertvertexflags ivf; REAL v1[3], v2[3], n[3]; REAL bboxsize, bboxsize2, bboxsize3, ori; - int randindex; - int ngroup = 0; + int randindex, loc; int i, j; if (!b->quiet) { printf("Delaunizing vertices...\n"); } + if (b->max_btreenode_size > 0) { // not -u0. + b->btree = 1; + btreenode_list = new arraypool(sizeof(point*), 10); + max_btreenode_size = 0; + max_btree_depth = 0; + } + + // Form a random permuation (uniformly at random) of the set of vertices. permutarray = new point[in->numberofpoints]; points->traversalinit(); - - if (b->no_sort) { + if (b->btree) { // -u option + for (i = 0; i < in->numberofpoints; i++) { + permutarray[i] = (point) points->traverse(); + } + if (b->verbose) { + printf(" Sorting vertices by a bsp-tree.\n"); + } + // Sort the points using a binary tree recursively. + btree_sort(permutarray, in->numberofpoints, 0, xmin, xmax, ymin, ymax, + zmin, zmax, 0); + if (b->verbose) { + printf(" Number of tree nodes: %ld.\n", btreenode_list->objects); + printf(" Maximum tree node size: %d.\n", max_btreenode_size); + printf(" Maximum tree depth: %d.\n", max_btree_depth); + } + // Order the sorted points. + ordervertices(permutarray, in->numberofpoints); + } else if (b->hilbertcurve) { if (b->verbose) { - printf(" Using the input order.\n"); + printf(" Sorting vertices by hilbert curve.\n"); } + // To be done... for (i = 0; i < in->numberofpoints; i++) { permutarray[i] = (point) points->traverse(); } @@ -11509,32 +12460,23 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) if (b->verbose) { printf(" Permuting vertices.\n"); } - srand(in->numberofpoints); for (i = 0; i < in->numberofpoints; i++) { - randindex = rand() % (i + 1); // randomnation(i + 1); + randindex = randomnation(i + 1); permutarray[i] = permutarray[randindex]; permutarray[randindex] = (point) points->traverse(); } - if (b->brio_hilbert) { // -b option - if (b->verbose) { - printf(" Sort vertices using BRIO and Hilbert curve.\n"); - } - hilbert_init(in->mesh_dim); - brio_multiscale_sort(permutarray, in->numberofpoints, b->brio_threshold, - b->brio_ratio, &ngroup); - } } tv = clock(); // Remember the time for sorting points. // Calculate the diagonal size of its bounding box. - bboxsize = sqrt(norm2(xmax - xmin, ymax - ymin, zmax - zmin)); + bboxsize = sqrt(NORM2(xmax - xmin, ymax - ymin, zmax - zmin)); bboxsize2 = bboxsize * bboxsize; bboxsize3 = bboxsize2 * bboxsize; // Make sure the second vertex is not identical with the first one. i = 1; - while ((distance(permutarray[0],permutarray[i])/bboxsize)<b->epsilon) { + while ((DIST(permutarray[0], permutarray[i]) / bboxsize) < b->epsilon) { i++; if (i == in->numberofpoints - 1) { printf("Exception: All vertices are (nearly) identical (Tol = %g).\n", @@ -11555,8 +12497,8 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) v1[j] = permutarray[1][j] - permutarray[0][j]; v2[j] = permutarray[i][j] - permutarray[0][j]; } - cross(v1, v2, n); - while ((sqrt(norm2(n[0], n[1], n[2])) / bboxsize2) < b->epsilon) { + CROSS(v1, v2, n); + while ((sqrt(NORM2(n[0], n[1], n[2])) / bboxsize2) < b->epsilon) { i++; if (i == in->numberofpoints - 1) { printf("Exception: All vertices are (nearly) collinear (Tol = %g).\n", @@ -11566,7 +12508,7 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) for (j = 0; j < 3; j++) { v2[j] = permutarray[i][j] - permutarray[0][j]; } - cross(v1, v2, n); + CROSS(v1, v2, n); } if (i > 2) { // Swap to move the non-indetical vertex from index i to index 1. @@ -11577,8 +12519,8 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) // Make sure the fourth vertex is not coplanar with the first three. i = 3; - ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], - permutarray[i]); + ori = orient3d(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); while ((fabs(ori) / bboxsize3) < b->epsilon) { i++; if (i == in->numberofpoints) { @@ -11586,8 +12528,8 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) b->epsilon); terminatetetgen(10); } - ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], - permutarray[i]); + ori = orient3d(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); } if (i > 3) { // Swap to move the non-indetical vertex from index i to index 1. @@ -11612,73 +12554,57 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) if (b->verbose) { printf(" Incrementally inserting vertices.\n"); } - insertvertexflags ivf; - flipconstraints fc; - // int loc; - // Choose algorithm: Bowyer-Watson (default) or Incremental Flip + // Choose algorithm: Bowyer-Watson (default) or Incremental Flip (-l). if (b->incrflip) { ivf.bowywat = 0; ivf.lawson = 1; - fc.enqflag = 1; } else { ivf.bowywat = 1; ivf.lawson = 0; } - for (i = 4; i < in->numberofpoints; i++) { + if (b->verbose > 2) printf(" #%d", i); if (pointtype(permutarray[i]) == UNUSEDVERTEX) { setpointtype(permutarray[i], VOLVERTEX); } - if (b->brio_hilbert || b->no_sort) { // -b or -b/1 - // Start the last updated tet. - searchtet.tet = recenttet.tet; - } else { // -b0 - // Randomly choose the starting tet for point location. - searchtet.tet = NULL; - } + // Auto choose the starting tet for point location. + searchtet.tet = NULL; ivf.iloc = (int) OUTSIDE; // Insert the vertex. - if (insertpoint(permutarray[i], &searchtet, NULL, NULL, &ivf)) { - if (flipstack != NULL) { - // Perform flip to recover Delaunayness. - incrementalflip(permutarray[i], &fc); - } - } else { - if (ivf.iloc == (int) ONVERTEX) { - // The point already exists. Mark it and do nothing on it. - swapvertex = org(searchtet); - assert(swapvertex != permutarray[i]); // SELF_CHECK - if (b->object != tetgenbehavior::STL) { - if (!b->quiet) { - printf("Warning: Point #%d is coincident with #%d. Ignored!\n", - pointmark(permutarray[i]), pointmark(swapvertex)); - } - } - setpoint2ppt(permutarray[i], swapvertex); - setpointtype(permutarray[i], DUPLICATEDVERTEX); - dupverts++; - } else if (ivf.iloc == (int) NEARVERTEX) { - swapvertex = point2ppt(permutarray[i]); + loc = insertvertex(permutarray[i], &searchtet, NULL, NULL, &ivf); + if (loc == (int) ONVERTEX) { + // The point already exists. Mark it and do nothing on it. + swapvertex = org(searchtet); + assert(swapvertex != permutarray[i]); // SELF_CHECK + if (b->object != tetgenbehavior::STL) { if (!b->quiet) { - printf("Warning: Point %d is replaced by point %d.\n", + printf("Warning: Point #%d is coincident with #%d. Ignored!\n", pointmark(permutarray[i]), pointmark(swapvertex)); - printf(" Avoid creating a very short edge (len = %g) (< %g).\n", - permutarray[i][3], b->minedgelength); - printf(" You may try a smaller tolerance (-T) (current is %g)\n", - b->epsilon); - printf(" or use the option -M0/1 to avoid such replacement.\n"); } - // Remember it is a duplicated point. - setpointtype(permutarray[i], DUPLICATEDVERTEX); - // Count the number of duplicated points. - dupverts++; } + setpoint2ppt(permutarray[i], swapvertex); + setpointtype(permutarray[i], DUPLICATEDVERTEX); + dupverts++; + continue; + } + if (ivf.lawson) { + // If -l option. Perform flip to recover Delaunayness. + lawsonflip3d(permutarray[i], ivf.lawson, 0, 0, 0); } } - + if (b->btree) { + // Bsp-tree is used only in DT construction. + point **pptary; + for (i = 0; i < (int) btreenode_list->objects; i++) { + pptary = (point **) fastlookup(btreenode_list, i); + delete [] *pptary; + } + delete btreenode_list; + b->btree = 0; // Disable it. + } delete [] permutarray; } @@ -11691,6 +12617,123 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) //// //// //// //// +/////////////////////////////////////////////////////////////////////////////// +// // +// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, + point *ppb, point *ppc) +{ + point *ppt, pa, pb, pc; + REAL v1[3], v2[3], n[3]; + REAL lab, len, A, area; + REAL x, y, z; + int i; + + ppt = (point *) fastlookup(facpoints, 0); + pa = *ppt; // a is the first point. + pb = pc = NULL; // Avoid compiler warnings. + + // Get a point b s.t. the length of [a, b] is maximal. + lab = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + x = (*ppt)[0] - pa[0]; + y = (*ppt)[1] - pa[1]; + z = (*ppt)[2] - pa[2]; + len = x * x + y * y + z * z; + if (len > lab) { + lab = len; + pb = *ppt; + } + } + lab = sqrt(lab); + if (lab == 0) { + if (!b->quiet) { + printf("Warning: All points of a facet are coincident with %d.\n", + pointmark(pa)); + } + return false; + } + + // Get a point c s.t. the area of [a, b, c] is maximal. + v1[0] = pb[0] - pa[0]; + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + A = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + v2[0] = (*ppt)[0] - pa[0]; + v2[1] = (*ppt)[1] - pa[1]; + v2[2] = (*ppt)[2] - pa[2]; + CROSS(v1, v2, n); + area = DOT(n, n); + if (area > A) { + A = area; + pc = *ppt; + } + } + if (A == 0) { + // All points are collinear. No above point. + if (!b->quiet) { + printf("Warning: All points of a facet are collinaer with [%d, %d].\n", + pointmark(pa), pointmark(pb)); + } + return false; + } + + // Calculate an above point of this facet. + facenormal(pa, pb, pc, n, 1, NULL); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + lab /= 2.0; // Half the maximal length. + dummypoint[0] = pa[0] + lab * n[0]; + dummypoint[1] = pa[1] + lab * n[1]; + dummypoint[2] = pa[2] + lab * n[2]; + + if (ppa != NULL) { + // Return the three points. + *ppa = pa; + *ppb = pb; + *ppc = pc; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Calculate an above point. It lies above the plane containing the subface // +// [a,b,c], and save it in dummypoint. Moreover, the vector pa->dummypoint // +// is the normal of the plane. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) +{ + arraypool *ptarray; + point *parypt; + + ptarray = new arraypool(sizeof(point), 4); + + ptarray->newindex((void **) &parypt); + *parypt = pa; + ptarray->newindex((void **) &parypt); + *parypt = pb; + ptarray->newindex((void **) &parypt); + *parypt = pc; + ptarray->newindex((void **) &parypt); + *parypt = pd; + + calculateabovepoint(ptarray, NULL, NULL, NULL); + + delete ptarray; +} + /////////////////////////////////////////////////////////////////////////////// // // // flipshpush() Push a facet edge into flip stack. // @@ -11711,20 +12754,19 @@ void tetgenmesh::flipshpush(face* flipedge) /////////////////////////////////////////////////////////////////////////////// // // -// flip22() Perform a 2-to-2 flip in surface mesh. // +// flip22() Remove an edge by transforming 2-to-2 subfaces. // // // -// 'flipfaces' is an array of two subfaces. On input, they are [a,b,c] and // -// [b,a,d]. On output, they are [c,d,b] and [d,c,a]. As a result, edge [a,b] // -// is replaced by edge [c,d]. // +// 'flipfaces' contains two faces: abc and bad. This routine removes these 2 // +// faces and replaces them by two new faces: cdb and dca. // // // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) { - face bdedges[4], outfaces[4], infaces[4]; - face bdsegs[4]; - face checkface; + face bdedges[4], outfaces[4], infaces[4], bdsegs[4]; + face checkface, checkseg; point pa, pb, pc, pd; + badface *bface; int i; pa = sorg(flipfaces[0]); @@ -11736,6 +12778,10 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) sesymself(flipfaces[1]); } + if (b->verbose > 3) { + printf(" flip 2-to-2: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } flip22count++; // Collect the four boundary edges. @@ -11750,7 +12796,8 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) infaces[i] = outfaces[i]; sspivot(bdedges[i], bdsegs[i]); if (outfaces[i].sh != NULL) { - if (isshsubseg(bdedges[i])) { + sspivot(bdedges[i], checkseg); + if (checkseg.sh != NULL) { spivot(infaces[i], checkface); while (checkface.sh != bdedges[i].sh) { infaces[i] = checkface; @@ -11764,9 +12811,9 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) // Shellmark does not change. // area constraint does not change. - // Transform [a,b,c] -> [c,d,b]. + // Transform abc -> cdb. setshvertices(flipfaces[0], pc, pd, pb); - // Transform [b,a,d] -> [d,c,a]. + // Transform bad -> dca. setshvertices(flipfaces[1], pd, pc, pa); // Update the point-to-subface map. @@ -11802,7 +12849,12 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) ssbond(bdedges[i], bdsegs[(3 + i) % 4]); if (chkencflag & 1) { // Queue this segment for encroaching check. - enqueuesubface(badsubsegs, &(bdsegs[(3 + i) % 4])); + if (!smarktest2ed(bdsegs[(3 + i) % 4])) { + bface = (badface *) badsubsegs->alloc(); + bface->ss = bdsegs[(3 + i) % 4]; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(bface->ss); // An alive badface. + } } } else { ssdissolve(bdedges[i]); @@ -11812,7 +12864,12 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) if (chkencflag & 2) { // Queue the flipped subfaces for quality/encroaching checks. for (i = 0; i < 2; i++) { - enqueuesubface(badsubfacs, &(flipfaces[i])); + if (!smarktest2ed(flipfaces[i])) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = flipfaces[i]; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(bface->ss); // An alive badface. + } } } @@ -11843,17 +12900,22 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) void tetgenmesh::flip31(face* flipfaces, int flipflag) { - face bdedges[3], outfaces[3], infaces[3]; - face bdsegs[3]; - face checkface; - point pa, pb, pc; + face bdedges[3], outfaces[3], infaces[3], bdsegs[3]; + face checkface, checkseg; + point pa, pb, pc, delpt; + REAL area; int i; + delpt = sorg(flipfaces[0]); pa = sdest(flipfaces[0]); pb = sdest(flipfaces[1]); pc = sdest(flipfaces[2]); - flip31count++; + if (b->verbose > 3) { + printf(" flip 3-to-1: (%d, %d, %d) - %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(delpt)); + } + // flip31count++; // Collect all infos at the three boundary edges. for (i = 0; i < 3; i++) { @@ -11862,7 +12924,8 @@ void tetgenmesh::flip31(face* flipfaces, int flipflag) infaces[i] = outfaces[i]; sspivot(bdedges[i], bdsegs[i]); if (outfaces[i].sh != NULL) { - if (isshsubseg(bdedges[i])) { + sspivot(bdedges[i], checkseg); + if (checkseg.sh != NULL) { spivot(infaces[i], checkface); while (checkface.sh != bdedges[i].sh) { infaces[i] = checkface; @@ -11877,8 +12940,8 @@ void tetgenmesh::flip31(face* flipfaces, int flipflag) setshvertices(flipfaces[3], pa, pb,pc); setshellmark(flipfaces[3], shellmark(flipfaces[0])); if (checkconstraints) { - //area = areabound(flipfaces[0]); - setareabound(flipfaces[3], areabound(flipfaces[0])); + area = areabound(flipfaces[0]); + setareabound(flipfaces[3], area); } // Update the point-to-subface map. @@ -11935,13 +12998,15 @@ long tetgenmesh::lawsonflip() { badface *popface; face flipfaces[2]; + face checkseg; point pa, pb, pc, pd; REAL sign; - long flipcount = 0; + long flipcount; if (b->verbose > 2) { printf(" Lawson flip %ld edges.\n", flippool->items); } + flipcount = flip22count; while (flipstack != (badface *) NULL) { @@ -11958,7 +13023,8 @@ long tetgenmesh::lawsonflip() // Skip it if it is not the same edge as we saved. if ((sorg(flipfaces[0]) != pa) || (sdest(flipfaces[0]) != pb)) continue; // Skip it if it is a subsegment. - if (isshsubseg(flipfaces[0])) continue; + sspivot(flipfaces[0], checkseg); + if (checkseg.sh != NULL) continue; // Get the adjacent face. spivot(flipfaces[0], flipfaces[1]); @@ -11971,15 +13037,16 @@ long tetgenmesh::lawsonflip() if (sign < 0) { // It is non-locally Delaunay. Flip it. flip22(flipfaces, 1, 0); - flipcount++; } } if (b->verbose > 2) { - printf(" Performed %ld flips.\n", flipcount); + printf(" %ld edges stacked, %ld flips.\n", flippool->items, + flip22count - flipcount); } + assert(flippool->items == 0l); // SELF_CHECK - return flipcount; + return flip22count - flipcount; } /////////////////////////////////////////////////////////////////////////////// @@ -11991,19 +13058,6 @@ long tetgenmesh::lawsonflip() // 'caveshbdlist' contains new subfaces in C(p). If the new point lies on a // // segment, 'cavesegshlist' returns the two new subsegments. // // // -// 'iloc' suggests the location of the point. If it is OUTSIDE, this routine // -// will first locate the point. It starts searching from 'searchsh' or 'rec- // -// entsh' if 'searchsh' is NULL. // -// // -// If 'bowywat' is set (1), the Bowyer-Watson algorithm is used to insert // -// the vertex. Otherwise, only insert the vertex in the initial cavity. // -// // -// If 'iloc' is 'INSTAR', this means the cavity of this vertex was already // -// provided in the list 'caveshlist'. // -// // -// If 'splitseg' is not NULL, the new vertex lies on the segment and it will // -// be split. 'iloc' must be either 'ONEDGE' or 'INSTAR'. -// // // NOTE: the old subfaces in C(p) are not deleted. Theyare needed in case we // // want to remove the new point immedately. // // // @@ -12012,47 +13066,67 @@ long tetgenmesh::lawsonflip() int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, int iloc, int bowywat) { + triface adjtet; face cavesh, neighsh, *parysh; face newsh, casout, casin; + face aseg, bseg, aoutseg, boutseg; face checkseg; - point pa, pb; - enum locateresult loc = OUTSIDE; - REAL sign, ori; + point pa, pb, pc; + enum locateresult loc; + REAL sign, ori, area; int i, j; if (b->verbose > 2) { printf(" Insert facet point %d.\n", pointmark(insertpt)); } - if (bowywat == 3) { - loc = INSTAR; - } - - if ((splitseg != NULL) && (splitseg->sh != NULL)) { + if (splitseg != NULL) { // A segment is going to be split, no point location. spivot(*splitseg, *searchsh); - if (loc != INSTAR) loc = ONEDGE; + loc = ONEDGE; } else { - if (loc != INSTAR) loc = (enum locateresult) iloc; + loc = (enum locateresult) iloc; if (loc == OUTSIDE) { // Do point location in surface mesh. if (searchsh->sh == NULL) { *searchsh = recentsh; } - // Search the vertex. An above point must be provided ('aflag' = 1). + // Start searching from 'searchsh'. loc = slocate(insertpt, searchsh, 1, 1, 0); } } + if (b->verbose > 2) { + if (searchsh->sh != NULL) { + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); + printf(" Located subface (%d, %d, %d).\n", pointmark(pa), + pointmark(pb), pointmark(pc)); + } else { + assert(splitseg != NULL); + pa = sorg(*splitseg); + pb = sdest(*splitseg); + printf(" Located segment (%d, %d).\n", pointmark(pa),pointmark(pb)); + } + } + +if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { // Form the initial sC(p). if (loc == ONFACE) { + if (b->verbose > 2) { + printf(" Inside face.\n"); + } // Add the face into list (in B-W cavity). smarktest(*searchsh); caveshlist->newindex((void **) &parysh); *parysh = *searchsh; } else if (loc == ONEDGE) { - if ((splitseg != NULL) && (splitseg->sh != NULL)) { + if (b->verbose > 2) { + printf(" On edge.\n"); + } + if (splitseg != NULL) { splitseg->shver = 0; pa = sorg(*splitseg); } else { @@ -12063,7 +13137,9 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, neighsh = *searchsh; while (1) { // Adjust the origin of its edge to be 'pa'. - if (sorg(neighsh) != pa) sesymself(neighsh); + if (sorg(neighsh) != pa) { + sesymself(neighsh); + } // Add this face into list (in B-W cavity). smarktest(neighsh); caveshlist->newindex((void **) &parysh); @@ -12079,12 +13155,18 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, } } // If (not a non-dangling segment). } else if (loc == ONVERTEX) { + if (b->verbose > 2) { + printf(" On vertex.\n"); + } return (int) loc; } else if (loc == OUTSIDE) { // Comment: This should only happen during the surface meshing step. // Enlarge the convex hull of the triangulation by including p. // An above point of the facet is set in 'dummypoint' to replace // orient2d tests by orient3d tests. + if (b->verbose > 2) { + printf(" Outside face.\n"); + } // Imagine that the current edge a->b (in 'searchsh') is horizontal in a // plane, and a->b is directed from left to right, p lies above a->b. // Find the right-most edge of the triangulation which is visible by p. @@ -12094,7 +13176,9 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, spivot(neighsh, casout); if (casout.sh == NULL) { // A convex hull edge. Is it visible by p. - ori = orient3d(sorg(neighsh), sdest(neighsh), dummypoint, insertpt); + pa = sorg(neighsh); + pb = sdest(neighsh); + ori = orient3d(pa, pb, dummypoint, insertpt); if (ori < 0) { *searchsh = neighsh; // Visible, update 'searchsh'. } else { @@ -12115,8 +13199,8 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, setshvertices(newsh, pb, pa, insertpt); setshellmark(newsh, shellmark(*searchsh)); if (checkconstraints) { - //area = areabound(*searchsh); - setareabound(newsh, areabound(*searchsh)); + area = areabound(*searchsh); + setareabound(newsh, area); } // Connect the new subface to the bottom subfaces. sbond1(newsh, *searchsh); @@ -12152,30 +13236,47 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, // Finish the process if p is not visible by the hull edge. if (ori >= 0) break; } - } else if (loc == INSTAR) { - // Under this case, the sub-cavity sC(p) has already been formed in - // insertvertex(). } +} else { + + // Under this case, the sub-cavity sC(p) has already been formed in + // insertvertex(). Check it. + // FOR DEBUG ONLY. + for (i = 0; i < caveshlist->objects; i++) { + cavesh = * (face *) fastlookup(caveshlist, i); + assert(smarktested(cavesh)); + } + if (splitseg != NULL) { + assert(smarktested(*splitseg)); + } + + +}// if (bowywat < 3) + // Form the Bowyer-Watson cavity sC(p). for (i = 0; i < caveshlist->objects; i++) { cavesh = * (face *) fastlookup(caveshlist, i); for (j = 0; j < 3; j++) { - if (!isshsubseg(cavesh)) { + sspivot(cavesh, checkseg); + if (checkseg.sh == NULL) { spivot(cavesh, neighsh); if (neighsh.sh != NULL) { // The adjacent face exists. if (!smarktested(neighsh)) { if (bowywat) { - if (loc == INSTAR) { // if (bowywat > 2) { + if (bowywat > 2) { // It must be a boundary edge. sign = 1; } else { // Check if this subface is connected to adjacent tet(s). - if (!isshtet(neighsh)) { + stpivot(neighsh, adjtet); + if (adjtet.tet == NULL) { // Check if the subface is non-Delaunay wrt. the new pt. - sign = incircle3d(sorg(neighsh), sdest(neighsh), - sapex(neighsh), insertpt); + pa = sorg(neighsh); + pb = sdest(neighsh); + pc = sapex(neighsh); + sign = incircle3d(pa, pb, pc, insertpt); } else { // It is connected to an adjacent tet. A boundary edge. sign = 1; @@ -12219,6 +13320,10 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, } // j } // i + if (b->verbose > 3) { + printf(" Size of cavity: %ld faces, %ld bdry edges.\n", + caveshlist->objects, caveshbdlist->objects); + } // Creating new subfaces. for (i = 0; i < caveshbdlist->objects; i++) { @@ -12233,8 +13338,8 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, setshellmark(newsh, shellmark(*parysh)); setshelltype(newsh, shelltype(*parysh)); if (checkconstraints) { - //area = areabound(*parysh); - setareabound(newsh, areabound(*parysh)); + area = areabound(*parysh); + setareabound(newsh, area); } // Update the point-to-subface map. if (pointtype(pa) == FREEFACETVERTEX) { @@ -12271,10 +13376,8 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, sbond1(*parysh, newsh); } - if (newsh.sh != NULL) { - // Set a handle for searching. - recentsh = newsh; - } + // Set a handle for searching. + recentsh = newsh; // Update the point-to-subface map. if (pointtype(insertpt) == FREEFACETVERTEX) { @@ -12302,6 +13405,8 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, if (neighsh.sh != NULL) { // Now 'neighsh' is a new subface at edge [b, #]. if (sorg(neighsh) != pb) sesymself(neighsh); + assert(sorg(neighsh) == pb); // SELF_CHECK + assert(sapex(neighsh) == insertpt); // SELF_CHECK senext2self(neighsh); // Go to the open edge [p, b]. sbond(newsh, neighsh); } else { @@ -12326,6 +13431,8 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, if (neighsh.sh != NULL) { // Now 'neighsh' is a new subface at edge [#, a]. if (sdest(neighsh) != pa) sesymself(neighsh); + assert(sdest(neighsh) == pa); // SELF_CHECK + assert(sapex(neighsh) == insertpt); // SELF_CHECK senextself(neighsh); // Go to the open edge [a, p]. sbond(newsh, neighsh); } else { @@ -12335,15 +13442,13 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, } } - if ((loc == ONEDGE) || ((splitseg != NULL) && (splitseg->sh != NULL)) - || (cavesegshlist->objects > 0l)) { + if (loc == ONEDGE) { + // An edge is being split. We distinguish two cases: // (1) the edge is not on the boundary of the cavity; // (2) the edge is on the boundary of the cavity. // In case (2), the edge is either a segment or a hull edge. There are // degenerated new faces in the cavity. They must be removed. - face aseg, bseg, aoutseg, boutseg; - for (i = 0; i < cavesegshlist->objects; i++) { // Get the saved old subface. parysh = (face *) fastlookup(cavesegshlist, i); @@ -12385,7 +13490,7 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, sdissolve(newsh); } } - //recentsh = newsh; + recentsh = newsh; // Update the point-to-subface map. if (pointtype(insertpt) == FREEFACETVERTEX) { setpoint2sh(insertpt, sencode(newsh)); @@ -12393,14 +13498,18 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, } } - if ((splitseg != NULL) && (splitseg->sh != NULL)) { - if (loc != INSTAR) { // if (bowywat < 3) { + if (splitseg != NULL) { + if (bowywat < 3) { smarktest(*splitseg); // Mark it as being processed. } aseg = *splitseg; pa = sorg(*splitseg); pb = sdest(*splitseg); + if (b->verbose > 2) { + printf(" Split seg (%d, %d) by %d.\n", pointmark(pa), + pointmark(pb), pointmark(insertpt)); + } // Insert the new point p. makeshellface(subsegs, &aseg); @@ -12413,7 +13522,7 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, setshelltype(aseg, shelltype(*splitseg)); setshelltype(bseg, shelltype(*splitseg)); if (checkconstraints) { - setareabound(aseg, areabound(*splitseg)); + setareabound(bseg, areabound(*splitseg)); setareabound(bseg, areabound(*splitseg)); } @@ -12456,17 +13565,11 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, // Let the point remember the segment it lies on. - if (pointtype(insertpt) == FREESEGVERTEX) { - setpoint2sh(insertpt, sencode(aseg)); - } + setpoint2sh(insertpt, sencode(aseg)); // Update the point-to-seg map. - if (pointtype(pa) == FREESEGVERTEX) { - setpoint2sh(pa, sencode(aseg)); - } - if (pointtype(pb) == FREESEGVERTEX) { - setpoint2sh(pb, sencode(bseg)); - } - } // if ((splitseg != NULL) && (splitseg->sh != NULL)) + setpoint2sh(pa, sencode(aseg)); + setpoint2sh(pb, sencode(bseg)); + } // if (splitseg != NULL) // Delete all degenerated new faces. for (i = 0; i < cavesegshlist->objects; i++) { @@ -12478,7 +13581,7 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, } cavesegshlist->restart(); - if ((splitseg != NULL) && (splitseg->sh != NULL)) { + if (splitseg != NULL) { // Return the two new subsegments (for further process). // Re-use 'cavesegshlist'. cavesegshlist->newindex((void **) &parysh); @@ -12486,6 +13589,7 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, cavesegshlist->newindex((void **) &parysh); *parysh = bseg; } + } // if (loc == ONEDGE) @@ -12500,34 +13604,39 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, // a segment vertex, and the origin of 'parentseg' is p. Otherwise, p is a // // facet vertex, and the origin of 'parentsh' is p. // // // +// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay- // +// ness after p is removed. // +// // // Within each facet, we first use a sequence of 2-to-2 flips to flip any // // edge at p, finally use a 3-to-1 flip to remove p. // // // // All new created subfaces are returned in the global array 'caveshbdlist'. // // The new segment (when p is on segment) is returned in 'parentseg'. // // // -// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay- // -// ness after p is removed. // -// // /////////////////////////////////////////////////////////////////////////////// int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, int lawson) { - face flipfaces[4], spinsh, *parysh; + face flipfaces[4], *parysh; + face spinsh, startsh, neighsh, nextsh, fakesh; + face abseg, prevseg, checkseg; + face adjseg1, adjseg2; point pa, pb, pc, pd; - REAL ori1, ori2; int it, i, j; + REAL *norm, n1[3], n2[3]; + REAL len, len1, len2; + REAL ori1, ori2; + if (parentseg != NULL) { + assert(sorg(*parentseg) == delpt); + assert(parentseg->shver == 0); // 'delpt' (p) should be a Steiner point inserted in a segment [a,b], // where 'parentseg' should be [p,b]. Find the segment [a,p]. - face startsh, neighsh, nextsh; - face abseg, prevseg, checkseg; - face adjseg1, adjseg2; - face fakesh; senext2(*parentseg, prevseg); spivotself(prevseg); + assert(prevseg.sh != NULL); prevseg.shver = 0; assert(sdest(prevseg) == delpt); // Restore the original segment [a,b]. @@ -12632,7 +13741,12 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, // Since we will re-connect the face ring using the faked subfaces. // We put the adjacent face of [a,b,p] to the list. spivot(neighsh, startsh); // The original adjacent subface. - if (sorg(startsh) != pa) sesymself(startsh); + if (sorg(startsh) != pa) { + sesymself(startsh); + } + assert(sorg(startsh) == pa); + assert(sdest(startsh) == pb); + assert(sapex(startsh) != delpt); sdissolve(startsh); // Connect fakesh to the segment [a,b]. ssbond(startsh, abseg); @@ -12683,7 +13797,9 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, parentsh = (face *) fastlookup(cavesegshlist, it); // [a,b,p] senextself(*parentsh); // [b,p,a]. spivotself(*parentsh); - if (sorg(*parentsh) != delpt) sesymself(*parentsh); + if (sorg(*parentsh) != delpt) { + sesymself(*parentsh); + } // now parentsh is [p,b,#]. if (sorg(*parentsh) != delpt) { // The vertex has already been removed in above special case. @@ -12701,8 +13817,10 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, spivotself(spinsh); assert(spinsh.sh != NULL); if (spinsh.sh == parentsh->sh) break; - if (sorg(spinsh) != delpt) sesymself(spinsh); - assert(sorg(spinsh) == delpt); + if (sorg(spinsh) != delpt) { + sesymself(spinsh); + assert(sorg(spinsh) == delpt); + } } // while (1) if (caveshlist->objects == 3) { @@ -12721,6 +13839,9 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, *parysh = flipfaces[3]; // The vertex is removed. break; + } else { + // There should be more than 3 subfaces in list. + assert(caveshlist->objects > 3); } // Search an edge to flip. @@ -12728,15 +13849,35 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, parysh = (face *) fastlookup(caveshlist, i); flipfaces[0] = *parysh; spivot(flipfaces[0], flipfaces[1]); - if (sorg(flipfaces[0]) != sdest(flipfaces[1])) + if (sorg(flipfaces[0]) != sdest(flipfaces[1])) { sesymself(flipfaces[1]); + } // Skip this edge if it belongs to a faked subface. if (!smarktested(flipfaces[0]) && !smarktested(flipfaces[1])) { pa = sorg(flipfaces[0]); pb = sdest(flipfaces[0]); pc = sapex(flipfaces[0]); pd = sapex(flipfaces[1]); - calculateabovepoint4(pa, pb, pc, pd); + // Select a base. + facenormal(pa, pb, pc, n1, 1, NULL); + len1 = sqrt(DOT(n1, n1)); + facenormal(pa, pb, pd, n2, 1, NULL); + len2 = sqrt(DOT(n2, n2)); + if (len1 > len2) { + norm = n1; + len = len1; + } else { + norm = n2; + len = len2; + } + assert(len > 0); + norm[0] /= len; + norm[1] /= len; + norm[2] /= len; + len = DIST(pa, pb); + dummypoint[0] = pa[0] + len * norm[0]; + dummypoint[1] = pa[1] + len * norm[1]; + dummypoint[2] = pa[2] + len * norm[2]; // Check if a 2-to-2 flip is possible. ori1 = orient3d(pc, pd, dummypoint, pa); ori2 = orient3d(pc, pd, dummypoint, pb); @@ -12753,7 +13894,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, } } // } // i - if (i == caveshlist->objects) { // This can happen only if there are 4 edges at p, and they are // orthogonal to each other, see Fig. 2010-11-01. @@ -12771,7 +13911,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, caveshbdlist->newindex((void **) &parysh); *parysh = flipfaces[0]; } - // The edge list at p are changed. caveshlist->restart(); } // while (1) @@ -12823,19 +13962,35 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, face* searchsh, int aflag, int cflag, int rflag) { face neighsh; - point pa, pb, pc; + face checkseg; + point pa, pb, pc, pd, *parypt; enum locateresult loc; enum {MOVE_BC, MOVE_CA} nextmove; REAL ori, ori_bc, ori_ca; + REAL dist_bc, dist_ca; int i; + // For finding an approximate location. + //REAL n[3], len, len3; + REAL n[3], area_abc, area_abp, area_bcp, area_cap; + pa = sorg(*searchsh); pb = sdest(*searchsh); pc = sapex(*searchsh); if (!aflag) { // No above point is given. Calculate an above point for this facet. - calculateabovepoint4(pa, pb, pc, searchpt); + // Re-use the 'cavetetvertlist'. + cavetetvertlist->newindex((void **) &parypt); + *parypt = pa; + cavetetvertlist->newindex((void **) &parypt); + *parypt = pb; + cavetetvertlist->newindex((void **) &parypt); + *parypt = pc; + cavetetvertlist->newindex((void **) &parypt); + *parypt = searchpt; + calculateabovepoint(cavetetvertlist, NULL, NULL, NULL); + cavetetvertlist->restart(); } // 'dummypoint' is given. Make sure it is above [a,b,c] @@ -12870,7 +14025,25 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, if (ori_bc < 0) { if (ori_ca < 0) { // (--) // Any of the edges is a viable move. - if (randomnation(2)) { + senext(*searchsh, neighsh); // At edge [b, c]. + spivotself(neighsh); + if (neighsh.sh != NULL) { + pd = sapex(neighsh); + dist_bc = NORM2(searchpt[0] - pd[0], searchpt[1] - pd[1], + searchpt[2] - pd[2]); + } else { + dist_bc = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); + } + senext2(*searchsh, neighsh); // At edge [c, a]. + spivotself(neighsh); + if (neighsh.sh != NULL) { + pd = sapex(neighsh); + dist_ca = NORM2(searchpt[0] - pd[0], searchpt[1] - pd[1], + searchpt[2] - pd[2]); + } else { + dist_ca = dist_bc; + } + if (dist_ca < dist_bc) { nextmove = MOVE_CA; } else { nextmove = MOVE_BC; @@ -12915,7 +14088,8 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, } if (!cflag) { // NON-convex case. Check if we will cross a boundary. - if (isshsubseg(*searchsh)) { + sspivot(*searchsh, checkseg); + if (checkseg.sh != NULL) { return ENCSEGMENT; } } @@ -12947,8 +14121,6 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, if (rflag) { // Round the locate result before return. - REAL n[3], area_abc, area_abp, area_bcp, area_cap; - pa = sorg(*searchsh); pb = sdest(*searchsh); pc = sapex(*searchsh); @@ -13022,15 +14194,17 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, - point endpt) +enum tetgenmesh::interresult + tetgenmesh::sscoutsegment(face *searchsh, point endpt) { face flipshs[2], neighsh; - face newseg; + face newseg, checkseg; point startpt, pa, pb, pc, pd; enum interresult dir; enum {MOVE_AB, MOVE_CA} nextmove; REAL ori_ab, ori_ca; + REAL dist_b, dist_c; + int shmark = 0; // The origin of 'searchsh' is fixed. startpt = sorg(*searchsh); // pa = startpt; @@ -13064,7 +14238,16 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, if (ori_ab < 0) { if (ori_ca < 0) { // (--) // Both sides are viable moves. - if (randomnation(2)) { + spivot(*searchsh, neighsh); // At edge [a, b]. + assert(neighsh.sh != NULL); // SELF_CHECK + pd = sapex(neighsh); + dist_b = NORM2(endpt[0] - pd[0], endpt[1] - pd[1], endpt[2] - pd[2]); + senext2(*searchsh, neighsh); // At edge [c, a]. + spivotself(neighsh); + assert(neighsh.sh != NULL); // SELF_CHECK + pd = sapex(neighsh); + dist_c = NORM2(endpt[0] - pd[0], endpt[1] - pd[1], endpt[2] - pd[2]); + if (dist_c < dist_b) { nextmove = MOVE_CA; } else { nextmove = MOVE_AB; @@ -13120,8 +14303,11 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, // Insert the segment into the triangulation. makeshellface(subsegs, &newseg); setshvertices(newseg, startpt, endpt, NULL); - // Set the default segment marker. - setshellmark(newseg, 1); + // Set the actual segment marker. + if (in->facetmarkerlist != NULL) { + shmark = shellmark(*searchsh); + setshellmark(newseg, in->facetmarkerlist[shmark - 1]); + } ssbond(*searchsh, newseg); spivot(*searchsh, neighsh); if (neighsh.sh != NULL) { @@ -13138,7 +14324,8 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, if (dir == ACROSSEDGE) { // Edge [b, c] intersects with the segment. senext(*searchsh, flipshs[0]); - if (isshsubseg(flipshs[0])) { + sspivot(flipshs[0], checkseg); + if (checkseg.sh != NULL) { printf("Error: Invalid PLC.\n"); pb = sorg(flipshs[0]); pc = sdest(flipshs[0]); @@ -13162,8 +14349,16 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, ori_ca = orient3d(pd, pc, dummypoint, pa); //assert(ori_ab * ori_ca != 0); // SELF_CHECK if (ori_ab < 0) { + if (b->verbose > 2) { + printf(" Queue an inversed triangle (%d, %d, %d) %d\n", + pointmark(pc), pointmark(pd), pointmark(pb), pointmark(pa)); + } flipshpush(&(flipshs[0])); // push it to 'flipstack' } else if (ori_ca < 0) { + if (b->verbose > 2) { + printf(" Queue an inversed triangle (%d, %d, %d) %d\n", + pointmark(pd), pointmark(pc), pointmark(pa), pointmark(pb)); + } flipshpush(&(flipshs[1])); // // push it to 'flipstack' } // Set 'searchsh' s.t. its origin is 'startpt'. @@ -13185,6 +14380,7 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, void tetgenmesh::scarveholes(int holes, REAL* holelist) { face *parysh, searchsh, neighsh; + face checkseg; enum locateresult loc; int i, j; @@ -13207,7 +14403,8 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) } } else { // A hull side. Check if it is protected by a segment. - if (!isshsubseg(searchsh)) { + sspivot(searchsh, checkseg); + if (checkseg.sh == NULL) { // Not protected. Save this face. if (!sinfected(searchsh)) { sinfect(searchsh); @@ -13239,7 +14436,8 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) for (j = 0; j < 3; j++) { spivot(searchsh, neighsh); if (neighsh.sh != NULL) { - if (!isshsubseg(searchsh)) { + sspivot(searchsh, checkseg); + if (checkseg.sh == NULL) { if (!sinfected(neighsh)) { sinfect(neighsh); caveshbdlist->newindex((void **) &parysh); @@ -13271,9 +14469,6 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) // // // triangulate() Create a CDT for the facet. // // // -// All vertices of the triangulation have type FACETVERTEX. The actual type // -// of boundary vertices are set by the routine unifysements(). // -// // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, @@ -13282,6 +14477,7 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, face searchsh, newsh, *parysh; face newseg; point pa, pb, pc, *ppt, *cons; + enum locateresult loc; int iloc; int i, j; @@ -13297,27 +14493,57 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, if (ptlist->objects < 2l) { // Not a segment or a facet. return; - } - - if (ptlist->objects == 2l) { + } if (ptlist->objects == 2l) { pa = * (point *) fastlookup(ptlist, 0); pb = * (point *) fastlookup(ptlist, 1); if (distance(pa, pb) > 0) { // It is a single segment. makeshellface(subsegs, &newseg); setshvertices(newseg, pa, pb, NULL); - // Set the default segment marker '1'. - setshellmark(newseg, 1); + // Set the actual segment marker. + if (in->facetmarkerlist != NULL) { + setshellmark(newseg, in->facetmarkerlist[shmark - 1]); + } } if (pointtype(pa) == VOLVERTEX) { - setpointtype(pa, FACETVERTEX); + setpointtype(pa, RIDGEVERTEX); } if (pointtype(pb) == VOLVERTEX) { - setpointtype(pb, FACETVERTEX); + setpointtype(pb, RIDGEVERTEX); } return; - } - + } if (ptlist->objects == 3l) { + // The facet has only one triangle. + pa = * (point *) fastlookup(ptlist, 0); + pb = * (point *) fastlookup(ptlist, 1); + pc = * (point *) fastlookup(ptlist, 2); + if (triarea(pa, pb, pc) > 0) { + makeshellface(subfaces, &newsh); + setshvertices(newsh, pa, pb, pc); + setshellmark(newsh, shmark); + // Create three new segments. + for (i = 0; i < 3; i++) { + makeshellface(subsegs, &newseg); + setshvertices(newseg, sorg(newsh), sdest(newsh), NULL); + // Set the actual segment marker. + if (in->facetmarkerlist != NULL) { + setshellmark(newseg, in->facetmarkerlist[shmark - 1]); + } + ssbond(newsh, newseg); + senextself(newsh); + } + if (pointtype(pa) == VOLVERTEX) { + setpointtype(pa, FACETVERTEX); + } + if (pointtype(pb) == VOLVERTEX) { + setpointtype(pb, FACETVERTEX); + } + if (pointtype(pc) == VOLVERTEX) { + setpointtype(pc, FACETVERTEX); + } + } + return; + } // Calulcate an above point of this facet. if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) { @@ -13340,34 +14566,6 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, setpointtype(pc, FACETVERTEX); } - // Are there area constraints? - if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) { - int idx, fmarker; - REAL area; - idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker. - for (i = 0; i < in->numberoffacetconstraints; i++) { - fmarker = (int) in->facetconstraintlist[i * 2]; - if (fmarker == idx) { - area = in->facetconstraintlist[i * 2 + 1]; - setareabound(newsh, area); - break; - } - } - } - - if (ptlist->objects == 3) { - // The triangulation only has one element. - for (i = 0; i < 3; i++) { - makeshellface(subsegs, &newseg); - setshvertices(newseg, sorg(newsh), sdest(newsh), NULL); - // Set the default segment marker '1'. - setshellmark(newseg, 1); - ssbond(newsh, newseg); - senextself(newsh); - } - return; - } - // Incrementally build the triangulation. pinfect(pa); pinfect(pb); @@ -13378,8 +14576,8 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, searchsh = recentsh; // Start from 'recentsh'. iloc = (int) OUTSIDE; if (b->verbose > 2) printf(" # %d", i); - iloc = sinsertvertex(*ppt, &searchsh, NULL, iloc, 1); - assert(iloc != (enum locateresult) ONVERTEX); // SELF_CHECK + loc = (enum locateresult) sinsertvertex(*ppt, &searchsh, NULL, iloc, 1); + assert(loc != ONVERTEX); // SELF_CHECK if (pointtype(*ppt) == VOLVERTEX) { setpointtype(*ppt, FACETVERTEX); } @@ -13401,8 +14599,8 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, for (i = 0; i < conlist->objects; i++) { cons = (point *) fastlookup(conlist, i); searchsh = recentsh; - iloc = (int) slocate(cons[0], &searchsh, 1, 1, 0); - assert(iloc == (enum locateresult) ONVERTEX); // SELF_CHECK + loc = slocate(cons[0], &searchsh, 1, 1, 0); + assert(loc == ONVERTEX); // SELF_CHECK // Recover the segment. Some edges may be flipped. sscoutsegment(&searchsh, cons[1]); if (flipstack != NULL) { @@ -13431,13 +14629,18 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, void tetgenmesh::unifysubfaces(face *f1, face *f2) { face casout, casin, neighsh; - face sseg; + face sseg, checkseg; point pa, pb, pc, pd; int i; + assert(f1->sh != f2->sh); // SELF_CHECK + pa = sorg(*f1); pb = sdest(*f1); pc = sapex(*f1); + + assert(sorg(*f2) == pa); // SELF_CHECK + assert(sdest(*f2) == pb); // SELF_CHECK pd = sapex(*f2); if (pc != pd) { @@ -13484,6 +14687,10 @@ void tetgenmesh::unifysubfaces(face *f1, face *f2) sspivot(*f2, sseg); if (sseg.sh != NULL) { // f2 has a segment. It must be different to f1's. + sspivot(*f1, checkseg); // SELF_CHECK + if (checkseg.sh != NULL) { // SELF_CHECK + assert(checkseg.sh != sseg.sh); // SELF_CHECK + } // Disconnect bonds of subfaces to this segment. spivot(*f2, casout); if (casout.sh != NULL) { @@ -13523,9 +14730,6 @@ void tetgenmesh::unifysubfaces(face *f1, face *f2) // // // unifysegments() Remove redundant segments and create face links. // // // -// After this routine, although segments are unique, but some of them may be // -// removed later by mergefacet(). All vertices still have type FACETVERTEX. // -// // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::unifysegments() @@ -13648,7 +14852,7 @@ void tetgenmesh::unifysegments() // f is either codirection with f1 or is codirection with f2. facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); facenormal(torg, tdest, sapex(sface), n2, 1, NULL); - if (dot(n1, n2) > 0) { + if (DOT(n1, n2) > 0) { unifysubfaces(&(f1->ss), &sface); } else { unifysubfaces(&(f2->ss), &sface); @@ -13674,7 +14878,7 @@ void tetgenmesh::unifysegments() // f is coplanar with f1 (see Fig. 8). facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); facenormal(torg, tdest, sapex(sface), n2, 1, NULL); - if (dot(n1, n2) > 0) { + if (DOT(n1, n2) > 0) { // The two faces are codirectional as well. unifysubfaces(&(f1->ss), &sface); } @@ -13696,10 +14900,16 @@ void tetgenmesh::unifysegments() } } // for (k = idx2faclist[idx]; ...) + if (b->verbose > 2) { + printf(" Found %ld segments at (%d %d).\n", flippool->items, + pointmark(torg), pointmark(tdest)); + } - // Set the vertex types of the endpoints of the segment. - //setpointtype(torg, RIDGEVERTEX); - //setpointtype(tdest, RIDGEVERTEX); + //if (b->nobisect || b->nomerge) { // -Y or -M + // Set the vertex types of the endpoints of the segment. + setpointtype(torg, RIDGEVERTEX); + setpointtype(tdest, RIDGEVERTEX); + //} // Set the connection between this segment and faces containing it, // at the same time, remove redundant segments. @@ -13720,30 +14930,21 @@ void tetgenmesh::unifysegments() f1 = facelink; for (k = 1; k <= flippool->items; k++) { k < flippool->items ? f2 = f1->nextitem : f2 = facelink; + if (b->verbose > 3) { + printf(" Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n", + pointmark(torg), pointmark(tdest), pointmark(sapex(f1->ss)), + pointmark(torg), pointmark(tdest), pointmark(sapex(f2->ss))); + } sbond1(f1->ss, f2->ss); f1 = f2; } } + // // All identified segments has a marker "-1". + //setshellmark(subsegloop, -1); // All identified segments has an init marker "0". flippool->restart(); - // Are there length constraints? - if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) { - int e1, e2; - REAL len; - for (k = 0; k < in->numberofsegmentconstraints; k++) { - e1 = (int) in->segmentconstraintlist[k * 3]; - e2 = (int) in->segmentconstraintlist[k * 3 + 1]; - if (((pointmark(torg) == e1) && (pointmark(tdest) == e2)) || - ((pointmark(torg) == e2) && (pointmark(tdest) == e1))) { - len = in->segmentconstraintlist[k * 3 + 2]; - setareabound(subsegloop, len); - break; - } - } - } - subsegloop.sh = shellfacetraverse(subsegs); } @@ -13808,6 +15009,11 @@ void tetgenmesh::mergefacets() ang = facedihedral(pa, pb, pc, pd); if (ang > PI) ang = (2 * PI - ang); if (ang > ang_tol) { + if (b->verbose > 2) { + printf(" Merge at segment (%d, %d)-(%d, %d) ang = %g\n", + pointmark(pa), pointmark(pb), pointmark(pc), + pointmark(pd), ang / PI * 180.0); + } remsegcount++; ssdissolve(parentsh); ssdissolve(neighsh); @@ -13827,6 +15033,7 @@ void tetgenmesh::mergefacets() lawsonflip(); // Recover Delaunayness. } + if (b->verbose > 1) { printf(" %d segments are removed.\n", remsegcount); } @@ -13856,13 +15063,12 @@ void tetgenmesh::identifypscedges(point *idx2verlist) int edgemarker; int idx, i, j; - int e1, e2; - REAL len; - if (!b->quiet) { printf("Inserting edges ...\n"); } + //assert(in->numberofedges > 0); // SELF_CHECK + //assert(in->edgemarkerlist != NULL); // SELF_CHECK // All identified segments have the initial marker '0'. // All segments inserted here should have a non-zero marker. @@ -13873,7 +15079,6 @@ void tetgenmesh::identifypscedges(point *idx2verlist) for (i = 0; i < in->numberofedges; i++) { endpts = &(in->edgelist[(i << 1)]); // Find a face contains the edge. - newseg.sh = NULL; searchsh.sh = NULL; idx = endpts[0] - in->firstnumber; for (j = idx2shlist[idx]; j < idx2shlist[idx + 1]; j++) { @@ -13897,7 +15102,7 @@ void tetgenmesh::identifypscedges(point *idx2verlist) if (edgemarker == 0) { edgemarker = 1; } - + // We should find a subface having this edge. if (searchsh.sh != NULL) { // Check if this edge is already a segment of the mesh. sspivot(searchsh, checkseg); @@ -13909,6 +15114,10 @@ void tetgenmesh::identifypscedges(point *idx2verlist) // Create a new segment at this edge. pa = sorg(searchsh); pb = sdest(searchsh); + if (b->verbose > 2) { + printf(" Create a new segment (%d, %d).\n", + pointmark(pa), pointmark(pb)); + } makeshellface(subsegs, &newseg); setshvertices(newseg, pa, pb, NULL); setshellmark(newseg, edgemarker); @@ -13920,30 +15129,27 @@ void tetgenmesh::identifypscedges(point *idx2verlist) spivotself(neighsh); // SELF_CHECK assert(neighsh.sh == searchsh.sh); } + if (!b->psc) { + setpointtype(pa, RIDGEVERTEX); + setpointtype(pb, RIDGEVERTEX); + } } } else { // It is a dangling segment (not belong to any facets). // Get the two endpoints of this segment. pa = idx2verlist[endpts[0]]; pb = idx2verlist[endpts[1]]; + if (b->verbose > 2) { + printf(" Create a new segment (%d, %d) - dangling.\n", + pointmark(pa), pointmark(pb)); + } makeshellface(subsegs, &newseg); setshvertices(newseg, pa, pb, NULL); setshellmark(newseg, edgemarker); - } - - if (newseg.sh != NULL) { - if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) { - for (i = 0; i < in->numberofsegmentconstraints; i++) { - e1 = (int) in->segmentconstraintlist[i * 3]; - e2 = (int) in->segmentconstraintlist[i * 3 + 1]; - if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) || - ((pointmark(pa) == e2) && (pointmark(pb) == e1))) { - len = in->segmentconstraintlist[i * 3 + 2]; - setareabound(newseg, len); - break; - } - } - } + //if (!b->psc) { + setpointtype(pa, RIDGEVERTEX); + setpointtype(pb, RIDGEVERTEX); + //} } } // i @@ -13953,6 +15159,10 @@ void tetgenmesh::identifypscedges(point *idx2verlist) segloop.sh = shellfacetraverse(subsegs); while (segloop.sh != NULL) { if (shellmark(segloop) == 0) { + if (b->verbose > 2) { + printf(" Remove a segment (%d, %d).\n", + pointmark(sorg(segloop)), pointmark(sdest(segloop))); + } spivot(segloop, searchsh); if (searchsh.sh != NULL) { ssdissolve(searchsh); @@ -14116,7 +15326,7 @@ void tetgenmesh::meshsurface() unifysegments(); } - if (!b->nomergefacet && !b->nobisect && !b->diagnose) { + if (!b->nomerge && !b->nobisect && !b->diagnose) { // Merge adjacent coplanar facets. mergefacets(); } @@ -14144,7 +15354,6 @@ void tetgenmesh::meshsurface() delete conlist; } - /////////////////////////////////////////////////////////////////////////////// // // // interecursive() Recursively do intersection test on a set of triangles.// @@ -14233,7 +15442,9 @@ void tetgenmesh::interecursive(shellface** subfacearray, int arraysize, toright = true; } // At least one is true; +#ifdef SELF_CHECK assert(!(toleft == false && toright == false)); +#endif if (toleft) { leftarray[leftsize] = sface1.sh; leftsize++; @@ -14408,7 +15619,7 @@ void tetgenmesh::detectinterfaces() // // // markacutevertices() Classify vertices as ACUTEVERTEXs or RIDGEVERTEXs. // // // -// Initialize all segment vertices's type be RIDGEVERTEX. A segment is acute // +// Initially all segment vertices have type RIDGEVERTEX. A segment is acute // // if there are at least two segments incident at it form an angle less than // // theta (= 60 degree). // // // @@ -14421,16 +15632,19 @@ void tetgenmesh::markacutevertices() face* segperverlist; int* idx2seglist; point pa, pb, pc; - REAL anglimit, sharpanglimit, ang; + REAL anglimit, ang; bool acuteflag; - int acutecount, sharpsegcount; + int acutecount; int idx, i, j; + REAL sharpanglimit; + int sharpsegcount; + if (b->verbose) { printf(" Marking acute vertices.\n"); } anglimit = PI / 3.0; // 60 degree. - sharpanglimit = 5.0 / 180.0 * PI; // 5 degree. + sharpanglimit = 10.0 / 180.0 * PI; // 10 degree. minfaceang = PI; // 180 degree. acutecount = sharpsegcount = 0; @@ -14444,15 +15658,22 @@ void tetgenmesh::markacutevertices() idx = pointmark(pa) - in->firstnumber; // Mark it if it is an endpoint of some segments. if (idx2seglist[idx + 1] > idx2seglist[idx]) { - // It is an endpoint of some segments. - setpointtype(pa, RIDGEVERTEX); + if (b->psc) { + // Only test it if it is an input vertex. + if (pointtype(pa) == FREESEGVERTEX) { + pa = pointtraverse(); + continue; + } + } acuteflag = false; // Do a brute-force pair-pair check. for (i=idx2seglist[idx]; i<idx2seglist[idx + 1]; i++) { pb = sdest(segperverlist[i]); + //for (j = i + 1; j < idx2seglist[idx + 1] && !acuteflag; j++) { for (j = i + 1; j < idx2seglist[idx + 1]; j++) { pc = sdest(segperverlist[j]); ang = interiorangle(pa, pb, pc, NULL); + //acuteflag = ang < anglimit; if (!acuteflag) { acuteflag = ang < anglimit; } @@ -14467,11 +15688,20 @@ void tetgenmesh::markacutevertices() if (shelltype(segperverlist[j]) != SHARP) { setshelltype(segperverlist[j], SHARP); sharpsegcount++; - } + } } } // j } // i + if (!acuteflag) { + if ((idx2seglist[idx + 1] - idx2seglist[idx]) > 4) { + // There are at least 5 segments shared at this vertices. + acuteflag = true; + } + } if (acuteflag) { + if (b->verbose > 2) { + printf(" Mark %d as ACUTEVERTEX.\n", pointmark(pa)); + } setpointtype(pa, ACUTEVERTEX); acutecount++; } @@ -14493,6 +15723,34 @@ void tetgenmesh::markacutevertices() delete [] segperverlist; } +/////////////////////////////////////////////////////////////////////////////// +// // +// reportselfintersect() Report a self-intersection. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::reportselfintersect(face *checkseg, face *checksh) +{ + face parentsh; + point pa, pb, pc, pd, pe; + point fa, fb; + + pa = sorg(*checkseg); + pb = sdest(*checkseg); + fa = farsorg(*checkseg); + fb = farsdest(*checkseg); + + pc = sorg(*checksh); + pd = sdest(*checksh); + pe = sapex(*checksh); + + printf(" !! Detected a self-intersection between:\n"); + printf(" A segment [%d,%d] < [%d,%d], \n", pointmark(pa), pointmark(pb), + pointmark(fa), pointmark(fb)); + printf(" a subface [%d,%d,%d] in facet #%d.\n", pointmark(pc), + pointmark(pd), pointmark(pe), shellmark(*checksh)); + +} /////////////////////////////////////////////////////////////////////////////// // // @@ -14513,20 +15771,21 @@ void tetgenmesh::markacutevertices() /////////////////////////////////////////////////////////////////////////////// enum tetgenmesh::interresult - tetgenmesh::finddirection(triface* searchtet, point endpt) + tetgenmesh::finddirection(triface* searchtet, point endpt, int randflag) { triface neightet; point pa, pb, pc, pd; enum {HMOVE, RMOVE, LMOVE} nextmove; REAL hori, rori, lori; - int t1ver; int s; + // The origin is fixed. pa = org(*searchtet); if ((point) searchtet->tet[7] == dummypoint) { // A hull tet. Choose the neighbor of its base face. - decode(searchtet->tet[3], *searchtet); + searchtet->ver = 11; + fsymself(*searchtet); // Reset the origin to be pa. if ((point) searchtet->tet[4] == pa) { searchtet->ver = 11; @@ -14535,7 +15794,7 @@ enum tetgenmesh::interresult } else if ((point) searchtet->tet[6] == pa) { searchtet->ver = 7; } else { - assert((point) searchtet->tet[7] == pa); + assert((point) searchtet->tet[7] == pa); // SELF_CHECK searchtet->ver = 0; } } @@ -14550,7 +15809,8 @@ enum tetgenmesh::interresult pc = apex(*searchtet); if (pc == endpt) { // pa->pc is the search edge. - eprevesymself(*searchtet); + eprevself(*searchtet); + esymself(*searchtet); return ACROSSVERT; } @@ -14558,6 +15818,12 @@ enum tetgenmesh::interresult while (1) { pd = oppo(*searchtet); + + if (b->verbose > 3) { + printf(" From tet (%d, %d, %d, %d) to %d.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(endpt)); + } + // Check whether the opposite vertex is 'endpt'. if (pd == endpt) { // pa->pd is the search edge. @@ -14580,39 +15846,54 @@ enum tetgenmesh::interresult hori = orient3d(pa, pb, pc, endpt); rori = orient3d(pb, pa, pd, endpt); lori = orient3d(pa, pc, pd, endpt); + orient3dcount += 3; // Now decide the tet to move. It is possible there are more than one - // tets are viable moves. Is so, randomly choose one. + // tet are viable moves. Use the opposite points of thier neighbors + // to discriminate, i.e., we choose the tet whose opposite point has + // the shortest distance to 'endpt'. if (hori > 0) { if (rori > 0) { if (lori > 0) { // Any of the three neighbors is a viable move. - s = randomnation(3); - if (s == 0) { - nextmove = HMOVE; - } else if (s == 1) { - nextmove = RMOVE; + if (0) { // if (!randflag) { } else { - nextmove = LMOVE; - } + // Randomly choose a direction. + s = randomnation(3); // 's' is in {0,1,2}. + if (s == 0) { + nextmove = HMOVE; + } else if (s == 1) { + nextmove = RMOVE; + } else { + nextmove = LMOVE; + } + } // if (randflag) } else { // Two tets, below horizon and below right, are viable. - //s = randomnation(2); - if (randomnation(2)) { - nextmove = HMOVE; + if (0) { // if (!randflag) { } else { - nextmove = RMOVE; - } + // Randomly choose a direction. + s = randomnation(2); // 's' is in {0,1}. + if (s == 0) { + nextmove = HMOVE; + } else { + nextmove = RMOVE; + } + } // if (randflag) } } else { if (lori > 0) { // Two tets, below horizon and below left, are viable. - //s = randomnation(2); - if (randomnation(2)) { - nextmove = HMOVE; + if (0) { // if (!randflag) { } else { - nextmove = LMOVE; - } + // Randomly choose a direction. + s = randomnation(2); // 's' is in {0,1}. + if (s == 0) { + nextmove = HMOVE; + } else { + nextmove = LMOVE; + } + } // if (randflag) } else { // The tet below horizon is chosen. nextmove = HMOVE; @@ -14622,12 +15903,16 @@ enum tetgenmesh::interresult if (rori > 0) { if (lori > 0) { // Two tets, below right and below left, are viable. - //s = randomnation(2); - if (randomnation(2)) { - nextmove = RMOVE; + if (0) { // if (!randflag) { } else { - nextmove = LMOVE; - } + // Randomly choose a direction. + s = randomnation(2); // 's' is in {0,1}. + if (s == 0) { + nextmove = RMOVE; + } else { + nextmove = LMOVE; + } + } // if (randflag) } else { // The tet below right is chosen. nextmove = RMOVE; @@ -14645,7 +15930,8 @@ enum tetgenmesh::interresult } if (lori == 0) { // pa->'endpt' is COLLINEAR with pa->pc. - eprevesymself(*searchtet); // // [a,c,d] + eprevself(*searchtet); + esymself(*searchtet); // [a,c,d] return ACROSSVERT; } // pa->'endpt' crosses the edge pb->pc. @@ -14665,7 +15951,8 @@ enum tetgenmesh::interresult } if (lori == 0) { // pa->'endpt' crosses the edge pc->pd. - eprevesymself(*searchtet); // [a,c,d] + eprevself(*searchtet); + esymself(*searchtet); // face acd return ACROSSEDGE; } // pa->'endpt' crosses the face bcd. @@ -14685,7 +15972,7 @@ enum tetgenmesh::interresult fsymself(*searchtet); enextself(*searchtet); } - assert(org(*searchtet) == pa); + assert(org(*searchtet) == pa); // SELF_CHECK pb = dest(*searchtet); pc = apex(*searchtet); @@ -14695,22 +15982,25 @@ enum tetgenmesh::interresult /////////////////////////////////////////////////////////////////////////////// // // -// scoutsegment() Search an edge in the tetrahedralization. // +// scoutsegment() Look for a given segment in the tetrahedralization T. // // // -// If the edge is found, it returns SHAREEDGE, and 'searchtet' returns the // -// edge from startpt to endpt. // +// Search an edge in the tetrahedralization that matches the given segmment. // +// If such an edge exists, the segment is 'locked' at the edge. 'searchtet' // +// returns this (constrained) edge. Otherwise, the segment is missing. // // // -// If the edge is missing, it returns either ACROSSEDGE or ACROSSFACE, which // -// indicates that the edge intersects an edge or a face. If 'refpt' is NULL,// -// 'searchtet' returns the edge or face. If 'refpt' is not NULL, it returns // -// a vertex which encroaches upon this edge, and 'searchtet' returns a tet // -// which containing 'refpt'. // +// The returned value indicates one of the following cases: // +// - SHAREEDGE, the segment exists and is inserted in T; // +// - ACROSSEDGE, the segment intersects an edge (in 'searchtet'). // +// - ACROSSFACE, the segment crosses a face (in 'searchtet'). // // // // The following cases can happen when the input PLC is not valid. // -// - ACROSSVERT, the edge intersects a vertex return by the origin of // -// 'searchtet'. // -// - ACROSSSEG, the edge intersects a segment returned by 'searchtet'. // -// - ACROSSSUB, the edge intersects a subface returned by 'searchtet'. // +// - ACROSSVERT, the segment intersects a vertex ('refpt'). // +// - ACROSSSEG, the segment intersects a segment(returned by 'searchtet'). // +// - ACROSSSUB, the segment intersects a subface(returned by 'searchtet'). // +// // +// If the returned value is ACROSSEDGE or ACROSSFACE, i.e., the segment is // +// missing, 'refpt' returns the reference point for splitting thus segment, // +// 'searchtet' returns a tet containing the 'refpt'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -14718,16 +16008,22 @@ enum tetgenmesh::interresult tetgenmesh::scoutsegment(point startpt, point endpt, triface* searchtet, point* refpt, arraypool* intfacelist) { - point pd; + triface neightet, reftet; + face checkseg, checksh; + point pa, pb, pc, pd; + badface *bface; enum interresult dir; - int t1ver; + REAL angmax, ang; + long facecount; + int types[2], poss[4]; + int pos, i, j; if (b->verbose > 2) { - printf(" Scout seg (%d, %d).\n",pointmark(startpt),pointmark(endpt)); + printf(" Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt)); } point2tetorg(startpt, *searchtet); - dir = finddirection(searchtet, endpt); + dir = finddirection(searchtet, endpt, 0); if (dir == ACROSSVERT) { pd = dest(*searchtet); @@ -14736,13 +16032,14 @@ enum tetgenmesh::interresult return SHAREEDGE; } else { // A point is on the path. - // Let the origin of the searchtet be the vertex. - enextself(*searchtet); - if (refpt) *refpt = pd; + *refpt = pd; return ACROSSVERT; } } // if (dir == ACROSSVERT) + if (b->verbose > 2) { + printf(" Seg is missing.\n"); + } // dir is either ACROSSEDGE or ACROSSFACE. enextesymself(*searchtet); // Go to the opposite face. @@ -14750,28 +16047,29 @@ enum tetgenmesh::interresult if (dir == ACROSSEDGE) { // Check whether two segments are intersecting. - if (issubseg(*searchtet)) { + tsspivot1(*searchtet, checkseg); + if (checkseg.sh != NULL) { return ACROSSSEG; } + across_edge_count++; } else if (dir == ACROSSFACE) { if (checksubfaceflag) { // Check whether a segment and a subface are intersecting. - if (issubface(*searchtet)) { + tspivot(*searchtet, checksh); + if (checksh.sh != NULL) { return ACROSSSUB; } } } if (refpt == NULL) { - // Do not need a reference point. Return. return dir; } - triface neightet, reftet; - point pa, pb, pc; - REAL angmax, ang; - int types[2], poss[4]; - int pos, i, j; + if (b->verbose > 2) { + printf(" Scout a ref-point for it.\n"); + } + facecount = across_face_count; pa = org(*searchtet); angmax = interiorangle(pa, startpt, endpt, NULL); @@ -14793,10 +16091,53 @@ enum tetgenmesh::interresult // Search intersecting faces along the segment. while (1) { + if (intfacelist != NULL) { + if (dir == ACROSSFACE) { + // Save the intersecting face. + intfacelist->newindex((void **) &bface); + bface->tt = *searchtet; + bface->forg = org(*searchtet); + bface->fdest = dest(*searchtet); + bface->fapex = apex(*searchtet); + // Save the intersection type (ACROSSFACE or ACROSSEDGE). + bface->key = (REAL) dir; + } else { // dir == ACROSSEDGE + i = 0; + if (intfacelist->objects > 0l) { + // Get the last saved one. + bface = (badface *) fastlookup(intfacelist, intfacelist->objects - 1); + if (((enum interresult) (int) bface->key) == ACROSSEDGE) { + // Skip this edge if it is the same as the last saved one. + if (((bface->forg == org(*searchtet)) && + (bface->fdest == dest(*searchtet))) || + ((bface->forg == dest(*searchtet)) && + (bface->fdest == org(*searchtet)))) { + i = 1; // Skip this edge. + } + } + } + if (i == 0) { + // Save this crossing edge. + intfacelist->newindex((void **) &bface); + bface->tt = *searchtet; + bface->forg = org(*searchtet); + bface->fdest = dest(*searchtet); + // bface->fapex = apex(*searchtet); + // Save the intersection type (ACROSSFACE or ACROSSEDGE). + bface->key = (REAL) dir; + } + } + } pd = oppo(*searchtet); assert(pd != dummypoint); // SELF_CHECK + if (b->verbose > 3) { + printf(" Passing face (%d, %d, %d, %d), dir(%d).\n", + pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd), + (int) dir); + } + across_face_count++; // Stop if we meet 'endpt'. if (pd == endpt) break; @@ -14864,6 +16205,9 @@ enum tetgenmesh::interresult enextself(neightet); } pd = org(neightet); + if (b->verbose > 2) { + angmax = interiorangle(pd, startpt, endpt, NULL); + } *refpt = pd; // break; return ACROSSVERT; @@ -14878,13 +16222,16 @@ enum tetgenmesh::interresult if (dir == ACROSSEDGE) { // Check whether two segments are intersecting. - if (issubseg(*searchtet)) { + tsspivot1(*searchtet, checkseg); + if (checkseg.sh != NULL) { return ACROSSSEG; } + across_edge_count++; } else if (dir == ACROSSFACE) { if (checksubfaceflag) { // Check whether a segment and a subface are intersecting. - if (issubface(*searchtet)) { + tspivot(*searchtet, checksh); + if (checksh.sh != NULL) { return ACROSSSUB; } } @@ -14898,6 +16245,19 @@ enum tetgenmesh::interresult *refpt = NULL; } + // dir is either ACROSSVERT, or ACROSSEDGE, or ACROSSFACE. + if (b->verbose > 2) { + if (*refpt != NULL) { + printf(" Refpt %d (%g), visited %ld faces.\n", pointmark(*refpt), + angmax / PI * 180.0, across_face_count - facecount); + } else { + printf(" No refpt (%g) is found, visited %ld faces.\n", + angmax / PI * 180.0, across_face_count - facecount); + } + } + if (across_face_count - facecount > across_max_count) { + across_max_count = across_face_count - facecount; + } *searchtet = reftet; return dir; @@ -14912,69 +16272,173 @@ enum tetgenmesh::interresult void tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) { point ei, ej; - REAL Li, Lj, L; - REAL t; - int i; + REAL Li, Lj, L, dj, dr; + REAL ti = 0.0, tj = 0.0, t; + int type, eid = 0, i; + + REAL diff, stept = 0.0, L1; + int iter; ei = sorg(*seg); ej = sdest(*seg); - if (refpt != NULL) { - // Let ei be the closer one to refpt. - Li = distance(ei, refpt); - Lj = distance(ej, refpt); - if (Li > Lj) { - // Swap ei and ej; - sesymself(*seg); - ei = sorg(*seg); - ej = sdest(*seg); - L = Li; - Li = Lj; - Lj = L; + if (b->verbose > 2) { + printf(" Get Steiner point on seg [%d (%c), %d (%c)].\n", + pointmark(ei), pointtype(ei) == ACUTEVERTEX ? 'A' : 'N', + pointmark(ej), pointtype(ej) == ACUTEVERTEX ? 'A' : 'N'); + } + + if (b->psc) { + eid = shellmark(*seg); + if (pointtype(ei) != FREESEGVERTEX) { + ti = in->getvertexparamonedge(in->geomhandle, pointmark(ei), eid); + } else { + ti = pointgeomuv(ei, 0); + } + if (pointtype(ej) != FREESEGVERTEX) { + tj = in->getvertexparamonedge(in->geomhandle, pointmark(ej), eid); + } else { + tj = pointgeomuv(ej, 0); } + } + + if (refpt != NULL) { if (pointtype(ei) == ACUTEVERTEX) { - // Cut the segment by a sphere centered at ei with radius Li. - L = distance(ei, ej); - t = Li / L; // t \in (0, 1). + if (pointtype(ej) == ACUTEVERTEX) { + // Choose the vertex which is closer to refpt. + Li = distance(ei, refpt); + Lj = distance(ej, refpt); + if (Li > Lj) { + // Swap ei and ej; + sesymself(*seg); + ei = sorg(*seg); + ej = sdest(*seg); + t = ti; + ti = tj; + tj = t; + } + type = 1; + } else { + type = 1; + } + } else { + if (pointtype(ej) == ACUTEVERTEX) { + type = 1; + // Swap ei and ej; + sesymself(*seg); + ei = sorg(*seg); + ej = sdest(*seg); + t = ti; + ti = tj; + tj = t; + } else { + type = 0; + } + } + } else { + type = 0; + } + + if (type == 1) { + L = distance(ei, ej); + Li = distance(ei, refpt); + // Cut the segment by a sphere centered at ei with radius Li. + if (b->psc) { + stept = (tj - ti) / 100.0; + iter = 0; + t = ti + (Li / L) * (tj - ti); + while (1) { + in->getsteineronedge(in->geomhandle, eid, t, steinpt); + L1 = distance(steinpt, ei); + diff = L1 - Li; + if ((fabs(diff) / L) < 1e-3) { + break; + } + if (diff > 0) { + t -= stept; // Move it towards ei. + } else { + t += stept; // Move it towards ej. + } + iter++; + if (iter > 10) { + printf("Warning: Get the right Steiner point failed.\n"); + break; + } + } // while (1) + } else { + t = Li / L; for (i = 0; i < 3; i++) { steinpt[i] = ei[i] + t * (ej[i] - ei[i]); } - // Re-use Li and Lj; - Li = distance(steinpt, refpt); - Lj = distance(steinpt, ej); - if (Li > Lj) { - // Avoid to create a very short edge at ej. - t = 0.5; + } + // Avoid creating a too short edge. + dj = distance(steinpt, ej); + dr = distance(steinpt, refpt); + if (dj < dr) { + // Cut the segment by the radius equal to Li / 2. + if (b->psc) { + iter = 0; + t = ti + ((Li / 2.0) / L) * (tj - ti); + while (1) { + in->getsteineronedge(in->geomhandle, eid, t, steinpt); + L1 = distance(steinpt, ei); + diff = L1 - (Li / 2.0); + if ((fabs(diff) / L) < 1e-3) { + break; + } + if (diff > 0) { + t -= stept; // Move it towards ei. + } else { + t += stept; // Move it towards ej. + } + iter++; + if (iter > 10) { + printf("Warning: Get the right Steiner point failed.\n"); + break; + } + } // while (1) + } else { + t = (Li / 2.0) / L; for (i = 0; i < 3; i++) { steinpt[i] = ei[i] + t * (ej[i] - ei[i]); } } + r3count++; } else { - // Cut the segment by the projection point of refpt. - projpt2edge(refpt, ei, ej, steinpt); - L = distance(steinpt, refpt); - if ((L > distance(steinpt, ei)) || (L > distance(steinpt, ej))) { - // Avoid creating a very short edge. - t = 0.5; - for (i = 0; i < 3; i++) { - steinpt[i] = ei[i] + t * (ej[i] - ei[i]); - } - } + r2count++; } } else { // Split the point at the middle. - t = 0.5; - for (i = 0; i < 3; i++) { - steinpt[i] = ei[i] + t * (ej[i] - ei[i]); + if (b->psc) { + t = 0.5 * (ti + tj); + in->getsteineronedge(in->geomhandle, eid, t, steinpt); + } else { + t = 0.5; + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + t * (ej[i] - ei[i]); + } } - } // if (refpt == NULL) + r1count++; + } + + if (b->psc) { + setpointgeomuv(steinpt, 0, t); + setpointgeomtag(steinpt, eid); + } if (pointtype(steinpt) == UNUSEDVERTEX) { setpointtype(steinpt, FREESEGVERTEX); } -} + if (b->verbose > 2) { + printf(" Split at t(%g)", t); + if (b->psc) { + printf(", ti(%g), tj(%g)", ti, tj); + } + printf(".\n"); + } +} /////////////////////////////////////////////////////////////////////////////// @@ -14994,18 +16458,29 @@ void tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) void tetgenmesh::delaunizesegments() { triface searchtet, spintet; - face searchsh; - face sseg, *psseg; + face searchsh, checksh; + face sseg, checkseg, *psseg; point refpt, newpt; enum interresult dir; insertvertexflags ivf; - int t1ver; + int loc; + // For reporting PLC problems. + point forg1, fdest1; // The 1st segment. + point forg2, fdest2, fapex2; // The 2nd segment. + + // Does this mesh containing subfaces? + if (checksubfaceflag) { + ivf.bowywat = 2; // The mesh is a CDT. + ivf.lawson = 2; // Do flip to recover Delaunayness. + ivf.validflag = 1; // Validation is needed. + } else { + ivf.bowywat = 1; // The mesh is a DT. + ivf.lawson = 0; // No need to do flip. + ivf.validflag = 0; // No need to valid the B-W cavity. + } - ivf.bowywat = 1; // Use Bowyer-Watson insertion. - ivf.assignmeshsize = b->metric; - ivf.sloc = (int) ONEDGE; // on 'sseg'. - ivf.sbowywat = 1; // Use Bowyer-Watson insertion. + searchsh.sh = NULL; // Loop until 'subsegstack' is empty. while (subsegstack->objects > 0l) { @@ -15014,9 +16489,14 @@ void tetgenmesh::delaunizesegments() psseg = (face *) fastlookup(subsegstack, subsegstack->objects); sseg = *psseg; + assert(!sinfected(sseg)); // FOR DEBUG // Check if this segment has been recovered. sstpivot1(sseg, searchtet); if (searchtet.tet != NULL) { + // FOR DEBUG + // Check if the tet contains the same segment. + tsspivot1(searchtet, checkseg); + assert(checkseg.sh == sseg.sh); continue; // Not a missing segment. } @@ -15025,7 +16505,8 @@ void tetgenmesh::delaunizesegments() if (dir == SHAREEDGE) { // Found this segment, insert it. - if (!issubseg(searchtet)) { + tsspivot1(searchtet, checkseg); // SELF_CHECK + if (checkseg.sh == NULL) { // Let the segment remember an adjacent tet. sstbond1(sseg, searchtet); // Bond the segment to all tets containing it. @@ -15035,7 +16516,7 @@ void tetgenmesh::delaunizesegments() fnextself(spintet); } while (spintet.tet != searchtet.tet); } else { - // Collision! Maybe a bug. + // Collision! Should not happen. assert(0); } } else { @@ -15046,30 +16527,145 @@ void tetgenmesh::delaunizesegments() //setpointtype(newpt, FREESEGVERTEX); getsteinerptonsegment(&sseg, refpt, newpt); - // Start searching from 'searchtet'. + // Start searching from the 'searchtet'. ivf.iloc = (int) OUTSIDE; + //ivf.bowywat; + //ivf.lawson; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = ivf.iloc; + ivf.sbowywat = ivf.bowywat; + ivf.splitbdflag = 0; + // ivf.validflag + ivf.respectbdflag = 0; + ivf.assignmeshsize = 0; // Insert the new point into the tetrahedralization T. // Missing segments and subfaces are queued for recovery. // Note that T is convex (nonconvex = 0). - if (insertpoint(newpt, &searchtet, &searchsh, &sseg, &ivf)) { + loc = insertvertex(newpt, &searchtet, &searchsh, &sseg, &ivf); + + assert(loc != (int) ONVERTEX); + if (loc != (int) NEARVERTEX) { // The new point has been inserted. - st_segref_count++; + if (ivf.lawson > 0) { + // For CDT, use flips to reocver Delaunayness. + lawsonflip3d(newpt, ivf.lawson, 0, 0, 0); + } + st_segref_count++; //st_segpro_count++; if (steinerleft > 0) steinerleft--; } else { - assert (ivf.iloc == (enum locateresult) NEARVERTEX); + // The new point is either ON or VERY CLOSE to an existing point. + refpt = point2ppt(newpt); + printf(" !! Avoid to create a short edge (length = %g)\n", + distance(newpt, refpt)); + + // It is probably an input problem. Two possible cases are: + // (1) An input vertex is very close an input segment; or + // (2) Two input segments are nearly intersect each other. + forg1 = farsorg(sseg); + fdest1 = farsdest(sseg); + + if ((pointtype(refpt) == RIDGEVERTEX) || + (pointtype(refpt) == ACUTEVERTEX) || + (pointtype(refpt) == VOLVERTEX)) { + // Case (1) + printf(" !! Point %d is very close to segment (%d, %d).\n", + pointmark(refpt), pointmark(forg1), pointmark(fdest1)); + } else if (pointtype(refpt) == FREESEGVERTEX) { + // Case (2). Find a subsegment contain 'refpt'. + subsegs->traversalinit(); + checkseg.sh = shellfacetraverse(subsegs); + while (checkseg.sh != NULL) { + if (((point) checkseg.sh[3] == refpt) || + ((point) checkseg.sh[4] == refpt)) break; + checkseg.sh = shellfacetraverse(subsegs); + } + assert(checkseg.sh != NULL); + checkseg.shver = 0; + forg2 = farsorg(checkseg); + fdest2 = farsdest(checkseg); + printf(" !! Two segments are very close to each other.\n"); + printf(" 1st: (%d, %d), 2nd: (%d, %d)\n", pointmark(forg1), + pointmark(fdest1), pointmark(forg2), pointmark(fdest2)); + } else { + // Unknown case + assert(0); + } + // Indicate it may be an input problem. + printf(" Short edge length bound is: %g. Tolerance is %g.\n", + b->minedgelength, b->epsilon); terminatetetgen(4); } } else { + // The input PLC contains self-intersections. + if (dir == ACROSSVERT) { + // refpt is the vertex intersecting the segment. + forg1 = farsorg(sseg); + fdest1 = farsdest(sseg); + if ((pointtype(refpt) == RIDGEVERTEX) || + (pointtype(refpt) == ACUTEVERTEX) || + (pointtype(refpt) == FACETVERTEX) || + (pointtype(refpt) == VOLVERTEX)) { + printf("Point %d is on segment (%d, %d).\n", + pointmark(refpt), pointmark(forg1), pointmark(fdest1)); + } else if (pointtype(refpt) == FREESEGVERTEX) { + // Case (2). Find a subsegment contain 'refpt'. + subsegs->traversalinit(); + checkseg.sh = shellfacetraverse(subsegs); + while (checkseg.sh != NULL) { + if (((point) checkseg.sh[3] == refpt) || + ((point) checkseg.sh[4] == refpt)) break; + checkseg.sh = shellfacetraverse(subsegs); + } + assert(checkseg.sh != NULL); + checkseg.shver = 0; + forg2 = farsorg(checkseg); + fdest2 = farsdest(checkseg); + printf("Two segments intersect.\n"); + printf(" 1st: (%d, %d), 2nd: (%d, %d)", pointmark(forg1), + pointmark(fdest1), pointmark(forg2), pointmark(fdest2)); + } else if (pointtype(refpt) == FREEFACETVERTEX) { + assert(0); // Report this case. + } + } else if (dir == ACROSSSEG) { + tsspivot1(searchtet, checkseg); + if (!b->quiet) { + printf("Two segments intersect.\n"); + forg1 = farsorg(sseg); + fdest1 = farsdest(sseg); + forg2 = farsorg(checkseg); + fdest2 = farsdest(checkseg); + printf(" 1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(forg1), + pointmark(fdest1), pointmark(forg2), pointmark(fdest2)); + } + } else if (dir == ACROSSSUB) { + tspivot(searchtet, checksh); + if (!b->quiet) { + printf("A segment and a subface intersect.\n"); + forg1 = farsorg(sseg); + fdest1 = farsdest(sseg); + forg2 = sorg(checksh); + fdest2 = sdest(checksh); + fapex2 = sapex(checksh); + printf(" Seg: (%d, %d), Sub: (%d, %d, %d).\n", + pointmark(forg1), pointmark(fdest1), + pointmark(forg2), pointmark(fdest2), pointmark(fapex2)); + } + } else { + // Unknown cases. + assert(0); + } // Indicate it is an input problem. terminatetetgen(3); } } } // while + } /////////////////////////////////////////////////////////////////////////////// // // -// scoutsubface() Search subface in the tetrahedralization. // +// scoutsubface() Look for a given subface in the tetrahedralization T. // // // // 'searchsh' is searched in T. If it exists, it is 'locked' at the face in // // T. 'searchtet' refers to the face. Otherwise, it is missing. // @@ -15085,25 +16681,27 @@ enum tetgenmesh::interresult tetgenmesh::scoutsubface(face* searchsh, triface* searchtet) { triface spintet; + face checksh; point pa, pb, pc; enum interresult dir; - int t1ver; pa = sorg(*searchsh); pb = sdest(*searchsh); + if (b->verbose > 2) { + printf(" Scout subface (%d, %d, %d).\n", pointmark(pa), pointmark(pb), + pointmark(sapex(*searchsh))); + } // Get a tet whose origin is a. point2tetorg(pa, *searchtet); // Search the edge [a,b]. - dir = finddirection(searchtet, pb); + dir = finddirection(searchtet, pb, 0); if (dir == ACROSSVERT) { // Check validity of a PLC. if (dest(*searchtet) != pb) { - // A vertex lies on the search edge. + // A vertex lies on the search edge. Return it. enextself(*searchtet); - // It is possible a PLC self-intersection problem. - terminatetetgen(3); return TOUCHEDGE; } // The edge exists. Check if the face exists. @@ -15113,7 +16711,8 @@ enum tetgenmesh::interresult while (1) { if (apex(spintet) == pc) { // Found a face matching to 'searchsh'! - if (!issubface(spintet)) { + tspivot(spintet, checksh); + if (checksh.sh == NULL) { // Insert 'searchsh'. tsbond(spintet, *searchsh); fsymself(spintet); @@ -15123,8 +16722,6 @@ enum tetgenmesh::interresult return SHAREFACE; } else { // Another subface is already inserted. - face checksh; - tspivot(spintet, checksh); assert(checksh.sh != searchsh->sh); // SELF_CHECK // This is possibly an input problem, i.e., two facets overlap. // Report this problem and exit. @@ -15141,39 +16738,47 @@ enum tetgenmesh::interresult } // dir is either ACROSSEDGE or ACROSSFACE. - return dir; + return dir; //ACROSSTET; } /////////////////////////////////////////////////////////////////////////////// // // -// formregion() Form the missing region of a missing subface. // +// formmissingregion() Form the missing region of a missing subface. // // // // 'missh' is a missing subface. From it we form a missing region R which is // -// a connected region formed by a set of missing subfaces of a facet. // -// Comment: There should be no segment inside R. // +// a collection of missing subfaces connected through adjacent edges. // +// // +// The missing region R is returned in the array 'missingshs'. All subfaces // +// in R are oriented as 'missh'. The array 'missingshverts' returns all ver- // +// tices of R. All subfaces and vertices of R are marktested. // // // -// 'missingshs' returns the list of subfaces in R. All subfaces in this list // -// are oriented as the 'missh'. 'missingshbds' returns the list of boundary // -// edges (tetrahedral handles) of R. 'missingshverts' returns all vertices // -// of R. They are all pmarktested. // +// 'adjtets' returns a list of tetrahedra adjacent to R. They are used to // +// search a crossing tetrahedron of R. // +// // +// Many ways are possible to form the missing region. The method used here // +// is to search missing edges in R. Starting from 'missh', its three edges // +// are checked. If one of the edges is missing, then the adjacent subface at // +// this edge is also missing. It is added to the array. By an incrementally // +// broad-first searching, we can find all subfaces of R. // // // -// Except the first one (which is 'missh') in 'missingshs', each subface in // -// this list represents an internal edge of R, i.e., it is missing in the // -// tetrahedralization. Since R may contain interior vertices, not all miss- // -// ing edges can be found by this way. // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::formregion(face* missh, arraypool* missingshs, - arraypool* missingshbds, arraypool* missingshverts) +void tetgenmesh::formmissingregion(face* missh, arraypool* missingshs, + arraypool* missingshbds, + arraypool* missingshverts, + arraypool* adjtets) { - triface searchtet, spintet; + triface searchtet, *parytet; face neighsh, *parysh; - face neighseg, fakeseg; point pa, pb, *parypt; enum interresult dir; - int t1ver; int i, j; + if (b->verbose > 2) { + printf(" Form missing region from subface (%d, %d, %d)\n", + pointmark(sorg(*missh)), pointmark(sdest(*missh)), + pointmark(sapex(*missh))); + } smarktest(*missh); missingshs->newindex((void **) &parysh); *parysh = *missh; @@ -15184,22 +16789,43 @@ void tetgenmesh::formregion(face* missh, arraypool* missingshs, for (j = 0; j < 3; j++) { pa = sorg(*missh); pb = sdest(*missh); + // Get a tet whose origin is a. point2tetorg(pa, searchtet); - dir = finddirection(&searchtet, pb); + // Search the edge [a,b]. + dir = finddirection(&searchtet, pb, 0); if (dir != ACROSSVERT) { // This edge is missing. Its neighbor is a missing subface. spivot(*missh, neighsh); + assert(neighsh.sh != NULL); if (!smarktested(neighsh)) { // Adjust the face orientation. - if (sorg(neighsh) != pb) sesymself(neighsh); + if (sorg(neighsh) != pb) { + sesymself(neighsh); + } + if (b->verbose > 3) { + printf(" Add a missing subface (%d, %d, %d)\n", + pointmark(pb), pointmark(pa), pointmark(sapex(neighsh))); + } smarktest(neighsh); missingshs->newindex((void **) &parysh); *parysh = neighsh; } } else { - if (dest(searchtet) != pb) { - // This might be a self-intersection problem. - terminatetetgen(3); + if (dest(searchtet) == pb) { + // Remember an existing edge for searching the first crossing tet. + adjtets->newindex((void **) &parytet); + *parytet = searchtet; + // Found an existing edge, it must be a boundary edge of R. + if (b->verbose > 3) { + printf(" -- A boundary edge (%d, %d)\n", pointmark(pa), + pointmark(pb)); + } + missingshbds->newindex((void **) &parysh); + *parysh = *missh; // It is only queued once. + } else { + // The input PLC has problem. + //assert(0); + terminatetetgen(3); } } // Collect the vertices of R. @@ -15212,86 +16838,57 @@ void tetgenmesh::formregion(face* missh, arraypool* missingshs, } // j } // i - // Get the boundary edges of R. - for (i = 0; i < missingshs->objects; i++) { - missh = (face *) fastlookup(missingshs, i); - for (j = 0; j < 3; j++) { - spivot(*missh, neighsh); - if ((neighsh.sh == NULL) || !smarktested(neighsh)) { - // A boundary edge of R. - // Let the segment point to the adjacent tet. - point2tetorg(sorg(*missh), searchtet); - finddirection(&searchtet, sdest(*missh)); - missingshbds->newindex((void **) &parysh); - *parysh = *missh; - // Check if this edge is a segment. - sspivot(*missh, neighseg); - if (neighseg.sh == NULL) { - // Temporarily create a segment at this edge. - makeshellface(subsegs, &fakeseg); - setsorg(fakeseg, sorg(*missh)); - setsdest(fakeseg, sdest(*missh)); - sinfect(fakeseg); // Mark it as faked. - // Connect it to all tets at this edge. - spintet = searchtet; - while (1) { - tssbond1(spintet, fakeseg); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - neighseg = fakeseg; - } - // Let the segment and the boundary edge point to each other. - ssbond(*missh, neighseg); - sstbond1(neighseg, searchtet); - } - senextself(*missh); - } // j - } // i + if (b->verbose > 2) { + printf(" Region has: %ld subfaces, %ld vertices\n", + missingshs->objects, missingshverts->objects); + } + if (missingshs->objects > maxregionsize) { + maxregionsize = missingshs->objects; + } // Unmarktest collected missing subfaces. for (i = 0; i < missingshs->objects; i++) { - parysh = (face *) fastlookup(missingshs, i); - sunmarktest(*parysh); + missh = (face *) fastlookup(missingshs, i); + sunmarktest(*missh); } + + // Comment: All vertices in R are pmarktested. } + + /////////////////////////////////////////////////////////////////////////////// // // // scoutcrossedge() Search an edge that crosses the missing region. // // // -// Return 1 if a crossing edge is found. It is returned by 'crosstet'. More- // -// over, the edge is oriented such that its origin lies below R. Return 0 // -// if no such edge is found. // -// // // Assumption: All vertices of the missing region are marktested. // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, +int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, arraypool* missingshs) { - triface searchtet, spintet; + triface *searchtet, spintet; face *parysh; - face neighseg; + face checkseg; point pa, pb, pc, pd, pe; enum interresult dir; REAL ori; int types[2], poss[4]; int searchflag, interflag; - int t1ver; int i, j; + if (b->verbose > 2) { + printf(" Search a crossing edge.\n"); + } searchflag = 0; - for (j = 0; j < missingshbds->objects && !searchflag; j++) { - parysh = (face *) fastlookup(missingshbds, j); - sspivot(*parysh, neighseg); - sstpivot1(neighseg, searchtet); + for (j = 0; j < adjtets->objects && !searchflag; j++) { + searchtet = (triface *) fastlookup(adjtets, j); interflag = 0; // Let 'spintet' be [#,#,d,e] where [#,#] is the boundary edge of R. - spintet = searchtet; + spintet = *searchtet; while (1) { pd = apex(spintet); pe = oppo(spintet); @@ -15305,7 +16902,7 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, pa = sorg(*parysh); pb = sdest(*parysh); pc = sapex(*parysh); - interflag=tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss); + interflag = tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss); if (interflag > 0) { if (interflag == 2) { // They intersect at a single point. @@ -15313,12 +16910,13 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { //pos = poss[0]; // Go to the crossing edge [d,e,#,#]. - edestoppo(spintet, crosstet); // // [d,e,#,#]. + eprev(spintet, crosstet); + esymself(crosstet); + enextself(crosstet); // [d,e,#,#]. // Check if it is a segment. - if (issubseg(crosstet)) { - //face checkseg; - //tsspivot1(crosstet, checkseg); - //reportselfintersect(&checkseg, parysh); + tsspivot1(crosstet, checkseg); + if (checkseg.sh != NULL) { + reportselfintersect(&checkseg, parysh); terminatetetgen(3); } // Adjust the edge such that d lies below [a,b,c]. @@ -15327,7 +16925,21 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, if (ori < 0) { esymself(crosstet); } + if (b->verbose > 2) { + printf(" Found edge (%d, %d) intersect", pointmark(pd), + pointmark(pe)); + printf(" face (%d, %d, %d)\n", pointmark(pa), pointmark(pb), + pointmark(pc)); + } + // Save the corners of this subface. + plane_pa = pa; + plane_pb = pb; + plane_pc = pc; searchflag = 1; + } else { + // An improper intersection type. + // Maybe it is a PLC problem. + // At the moment, just ignore it. } } break; @@ -15339,10 +16951,11 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, if (interflag > 0) break; // Go to the next tetrahedron. fnextself(spintet); - if (spintet.tet == searchtet.tet) break; + if (spintet.tet == searchtet->tet) break; } // while (1) } // j + adjtets->restart(); return searchflag; } @@ -15356,6 +16969,23 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, // #] which intersects R in its interior, where the edge [d,e] intersects R, // // and d lies below R. // // // +// By knowing a crossing edge [d,e], all tetrahedra sharing at [d,e] must // +// cross R. They are added into the list 'crosstets'. From this set of tets, // +// new crossing edges (if there exist) can be detected. The key is how to // +// detect them correctly. The approach used here is described as follows: // +// Suppose [d,e,a,b] is a crossing tet, where [d,e] intersects R and d lies // +// below R. We look at the face [d,e,a]. If the apex a is not pmarktested, // +// i.e., it is not a vertex of R, then either edge [e,a] or [a,d] intersects // +// R. A simple way is to perform above/below test between [a] and R. But it // +// is not clear which subface of R is the best for this test. Also, this is // +// not safe when R is not strictly planar. A second way is to test if [e,a] // +// intersects one of the subfaces of R. If an intersection is found, then [e,// +// a] is a crossing edge. Otherwise, the edge [a,d] must be a crossing edge // +// of R. NOTE: This statement is correct if the input PLC is correct. We can // +// also detect the incorrectness of the input, e.g., if [a] only touches a // +// subface of R. The second approach is implemented here. It is slow, but is // +// more robust. // +// // // 'crosstets' returns the set of crossing tets. Every tet in it has the // // form [d,e,#,#] where [d,e] is a crossing edge, and d lies below R. The // // set of tets form the cavity C, which is divided into two parts by R, one // @@ -15364,6 +16994,13 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, // in the top part of C, and so does 'botpoints'. Both 'toppoints' and // // 'botpoints' contain vertices of R. // // // +// NOTE: 'toppoints' may contain points which are not vertices of any top // +// faces, and so may 'botpoints'. Such points may belong to other facets and // +// need to be present after the recovery of this cavity (P1029.poly). // +// // +// A pair of boundary faces: 'firsttopface' and 'firstbotface', are saved. // +// They share the same edge in the boundary of the missing region. // +// // // Important: This routine assumes all vertices of the facet containing this // // subface are marked, i.e., pmarktested(p) returns true. // // // @@ -15374,22 +17011,27 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, arraypool* botfaces, arraypool* toppoints, arraypool* botpoints) { - arraypool *crossedges; + arraypool *crossedges, *testededges; triface spintet, neightet, *parytet; - face *parysh = NULL; + face checksh, *parysh = NULL; + face checkseg; // *paryseg; point pa, pd, pe, *parypt; enum interresult dir; + //REAL ori; + //REAL elen[3]; bool testflag, invalidflag; int types[2], poss[4]; - int t1ver; int i, j, k; // Temporarily re-use 'topfaces' for all crossing edges. crossedges = topfaces; + // Temporarily re-use 'botfaces' for all tested edges. + testededges = botfaces; // Only used by 'b->psc'. if (b->verbose > 2) { - printf(" Form the cavity of a missing region.\n"); + printf(" Form the cavity of missing region.\n"); } + missingsubfacecount += missingshs->objects; // Mark this edge to avoid testing it later. markedge(*searchtet); crossedges->newindex((void **) &parytet); @@ -15400,18 +17042,22 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, // Collect all crossing tets. Each cross tet is saved in the standard // form [d,e,#,#], where [d,e] is a corossing edge, d lies below R. // NEITHER d NOR e is a vertex of R (!pmarktested). + // NOTE: hull tets may be collected. See fig/dump-cavity-case2a(b).lua. + // Make sure that neither d nor e is dummypoint. for (i = 0; i < crossedges->objects; i++) { // Get a crossing edge [d,e,#,#]. searchtet = (triface *) fastlookup(crossedges, i); // Sort vertices into the bottom and top arrays. pd = org(*searchtet); + assert(!pmarktested(pd)); // pd is not on R. if (!pinfected(pd)) { pinfect(pd); botpoints->newindex((void **) &parypt); *parypt = pd; } pe = dest(*searchtet); + assert(!pmarktested(pe)); // pe is not on R. if (!pinfected(pe)) { pinfect(pe); toppoints->newindex((void **) &parypt); @@ -15422,6 +17068,11 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, spintet = *searchtet; while (1) { if (!infected(spintet)) { + if (b->verbose > 3) { + printf(" Add a crossing tet (%d, %d, %d, %d)\n", + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(apex(spintet)), pointmark(oppo(spintet))); + } infect(spintet); crosstets->newindex((void **) &parytet); *parytet = spintet; @@ -15437,10 +17088,10 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, // spintet is [d,e,a,#], where d lies below R, and e lies above R. pa = apex(spintet); if (pa != dummypoint) { - if (!pmarktested(pa)) { - // There exists a crossing edge, either [e,a] or [a,d]. First check - // if the crossing edge has already be added, i.e., check if a - // tetrahedron at this edge is marked. + if (!pmarktested(pa) || b->psc) { + // There exists a crossing edge, either [e,a] or [a,d]. First check + // if the crossing edge has already be added. This is to check if + // a tetrahedron at this edge is marked. testflag = true; for (j = 0; j < 2 && testflag; j++) { if (j == 0) { @@ -15466,8 +17117,12 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, pe = dest(spintet); for (k = 0; k < missingshs->objects; k++) { parysh = (face *) fastlookup(missingshs, k); - if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), - pe, pa, NULL, 1, types, poss)) { + plane_pa = sorg(*parysh); + plane_pb = sdest(*parysh); + plane_pc = sapex(*parysh); + // Test if this face intersects [e,a]. + if (tri_edge_test(plane_pa, plane_pb, plane_pc, pe, pa, + NULL, 1, types, poss)) { // Found intersection. 'a' lies below R. enext(spintet, neightet); dir = (enum interresult) types[0]; @@ -15479,8 +17134,9 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, } break; } - if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), - pa, pd, NULL, 1, types, poss)) { + // Test if this face intersects [a,d]. + if (tri_edge_test(plane_pa, plane_pb, plane_pc, pa, pd, + NULL, 1, types, poss)) { // Found intersection. 'a' lies above R. eprev(spintet, neightet); dir = (enum interresult) types[0]; @@ -15496,56 +17152,78 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, if (k < missingshs->objects) { // Found a pair of triangle - edge interseciton. if (invalidflag) { - if (!b->quiet) { - printf("Warning: A non-valid facet - edge intersection\n"); + if (b->verbose > 2) { + printf(" A non-valid subface - edge intersection\n"); printf(" subface: (%d, %d, %d) edge: (%d, %d)\n", - pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), - pointmark(sapex(*parysh)), pointmark(org(neightet)), + pointmark(plane_pa), pointmark(plane_pb), + pointmark(plane_pc), pointmark(org(neightet)), pointmark(dest(neightet))); } // It may be a PLC problem. terminatetetgen(3); - } + } else if (b->psc) { + if (pmarktested(pa)) { + // The intersection is invalid. + if (b->verbose > 2) { + printf(" A non-valid subface - edge intersection\n"); + printf(" subface: (%d, %d, %d) edge: (%d, %d)\n", + pointmark(plane_pa), pointmark(plane_pb), + pointmark(plane_pc), pointmark(org(neightet)), + pointmark(dest(neightet))); + } + // Split the subface intersecting this edge. + recentsh = *parysh; + recenttet = neightet; // For point location. + invalidflag = 1; + break; + } // if (pmarktested(pa)) + } // if (b->psc) // Adjust the edge direction, so that its origin lies below R, // and its destination lies above R. esymself(neightet); // Check if this edge is a segment. - if (issubseg(neightet)) { + tsspivot1(neightet, checkseg); + if (checkseg.sh != NULL) { // Invalid PLC! - //face checkseg; - //tsspivot1(neightet, checkseg); - //reportselfintersect(&checkseg, parysh); + reportselfintersect(&checkseg, parysh); terminatetetgen(3); } + if (b->verbose > 3) { + printf(" Add a crossing edge (%d, %d)\n", + pointmark(org(neightet)), pointmark(dest(neightet))); + } // Mark this edge to avoid testing it again. markedge(neightet); crossedges->newindex((void **) &parytet); *parytet = neightet; } else { // No intersection is found. It may be a PLC problem. + //assert(b->psc); + // Mark this edge to avoid testing it again. + //markedge(neightet); + //testededges->newindex((void **) &parytet); + //*parytet = neightet; invalidflag = 1; // Split the subface intersecting [d,e]. for (k = 0; k < missingshs->objects; k++) { parysh = (face *) fastlookup(missingshs, k); + plane_pa = sorg(*parysh); + plane_pb = sdest(*parysh); + plane_pc = sapex(*parysh); // Test if this face intersects [e,a]. - if (tri_edge_test(sorg(*parysh),sdest(*parysh),sapex(*parysh), - pd, pe, NULL, 1, types, poss)) { + if (tri_edge_test(plane_pa, plane_pb, plane_pc, pd, pe, + NULL, 1, types, poss)) { break; } } // k - if (k == missingshs->objects) { - // Not found such an edge. - // Arbitrarily choose an edge (except the first) to split. - k = randomnation(missingshs->objects - 1); - parysh = (face *) fastlookup(missingshs, k + 1); - } + assert(k < missingshs->objects); recentsh = *parysh; recenttet = spintet; // For point location. break; // the while (1) loop } // if (k == missingshs->objects) } // if (testflag) - } // if (!pmarktested(pa) || b->psc) - } // if (pa != dummypoint) + } // if (!pmarktested(pa) || b->psc) + } // Go to the next crossing tet. fnextself(spintet); if (spintet.tet == searchtet->tet) break; @@ -15560,6 +17238,7 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", crosstets->objects, crossedges->objects); } + crossingtetcount += crosstets->objects; // Unmark all marked edges. for (i = 0; i < crossedges->objects; i++) { @@ -15569,6 +17248,17 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, } crossedges->restart(); + if (b->psc) { + // Unmark all marked edges. + for (i = 0; i < testededges->objects; i++) { + searchtet = (triface *) fastlookup(testededges, i); + assert(edgemarked(*searchtet)); // SELF_CHECK + unmarkedge(*searchtet); + } + testededges->restart(); + } else { // only p->plc + assert(testededges->objects == 0l); + } if (invalidflag) { // Unmark all collected tets. @@ -15588,13 +17278,37 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, crosstets->restart(); botpoints->restart(); toppoints->restart(); - - // Randomly split an interior edge of R. - i = randomnation(missingshs->objects - 1); - recentsh = * (face *) fastlookup(missingshs, i); return false; } + // Find a pair of cavity boundary faces from the top and bottom sides of + // the facet each, and they share the same edge. Save them in the + // global variables: firsttopface, firstbotface. They will be used in + // fillcavity() for gluing top and bottom new tets. + for (i = 0; i < crosstets->objects; i++) { + searchtet = (triface *) fastlookup(crosstets, i); + // Crosstet is [d,e,a,b]. + enextesym(*searchtet, spintet); + eprevself(spintet); // spintet is [b,a,e,d] + fsym(spintet, neightet); // neightet is [a,b,e,#] + if (!infected(neightet)) { + // A top face. + firsttopface = neightet; + } else { + continue; // Go to the next cross tet. + } + eprevesym(*searchtet, spintet); + enextself(spintet); // spintet is [a,b,d,e] + fsym(spintet, neightet); // neightet is [b,a,d,#] + if (!infected(neightet)) { + // A bottom face. + firstbotface = neightet; + } else { + continue; + } + break; + } // i + assert(i < crosstets->objects); // SELF_CHECK // Collect the top and bottom faces and the middle vertices. Since all top // and bottom vertices have been infected. Uninfected vertices must be @@ -15611,14 +17325,16 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, for (i = 0; i < crosstets->objects; i++) { searchtet = (triface *) fastlookup(crosstets, i); // searchtet is [d,e,a,b]. - eorgoppo(*searchtet, spintet); + enextesym(*searchtet, spintet); + eprevself(spintet); // spintet is [b,a,e,d] fsym(spintet, neightet); // neightet is [a,b,e,#] if (!infected(neightet)) { // A top face. topfaces->newindex((void **) &parytet); *parytet = neightet; - } - edestoppo(*searchtet, spintet); + } + eprevesym(*searchtet, spintet); + enextself(spintet); // spintet is [a,b,d,e] fsym(spintet, neightet); // neightet is [b,a,d,#] if (!infected(neightet)) { // A bottom face. @@ -15683,17 +17399,19 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, arraypool *cavshells, arraypool *newtets, arraypool *crosstets, arraypool *misfaces) { - triface searchtet, neightet, *parytet, *parytet1; - face tmpsh, *parysh; + triface searchtet, neightet, spintet, *parytet, *parytet1; + face checksh, tmpsh, *parysh; + face checkseg; point pa, pb, pc, pd, pt[3], *parypt; enum interresult dir; insertvertexflags ivf; - REAL ori; + REAL ori; //, ang, len; long baknum, bakhullsize; int bakchecksubsegflag, bakchecksubfaceflag; - int t1ver; + //int iloc; int i, j; + if (b->verbose > 2) { printf(" Delaunizing cavity: %ld points, %ld faces.\n", cavpoints->objects, cavfaces->objects); @@ -15707,38 +17425,31 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, checksubsegflag = 0; checksubfaceflag = 0; b->verbose--; // Suppress informations for creating Delaunay tetra. - b->plc = 0; // Do not check near vertices. - - ivf.bowywat = 1; // Use Bowyer-Watson algorithm. + b->plc = 0; // Do not do unifypoint(); // Get four non-coplanar points (no dummypoint). - pa = pb = pc = NULL; - for (i = 0; i < cavfaces->objects; i++) { - parytet = (triface *) fastlookup(cavfaces, i); - parytet->ver = epivot[parytet->ver]; - if (apex(*parytet) != dummypoint) { - pa = org(*parytet); - pb = dest(*parytet); - pc = apex(*parytet); - break; - } - } + parytet = (triface *) fastlookup(cavfaces, 0); + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); pd = NULL; - for (; i < cavfaces->objects; i++) { + for (i = 1; i < cavfaces->objects; i++) { parytet = (triface *) fastlookup(cavfaces, i); pt[0] = org(*parytet); pt[1] = dest(*parytet); pt[2] = apex(*parytet); for (j = 0; j < 3; j++) { if (pt[j] != dummypoint) { // Do not include a hull point. - ori = orient3d(pa, pb, pc, pt[j]); - if (ori != 0) { - pd = pt[j]; - if (ori > 0) { // Swap pa and pb. - pt[j] = pa; pa = pb; pb = pt[j]; + // if (!pinfected(pt[j])) { + ori = orient3d(pa, pb, pc, pt[j]); + if (ori != 0) { + pd = pt[j]; + if (ori > 0) { // Swap pa and pb. + pt[j] = pa; pa = pb; pb = pt[j]; + } + break; } - break; - } + // } } } if (pd != NULL) break; @@ -15751,9 +17462,11 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, // Incrementally insert the vertices (duplicated vertices are ignored). for (i = 0; i < cavpoints->objects; i++) { pt[0] = * (point *) fastlookup(cavpoints, i); + assert(pt[0] != dummypoint); // SELF_CHECK searchtet = recenttet; ivf.iloc = (int) OUTSIDE; - insertpoint(pt[0], &searchtet, NULL, NULL, &ivf); + ivf.bowywat = 1; + insertvertex(pt[0], &searchtet, NULL, NULL, &ivf); } if (b->verbose > 2) { @@ -15768,7 +17481,10 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, parytet = (triface *) fastlookup(cavfaces, i); // Skip an interior face (due to the enlargement of the cavity). if (infected(*parytet)) continue; - parytet->ver = epivot[parytet->ver]; + // This face may contain dummypoint (See fig/dum-cavity-case2). + // If so, dummypoint must be its apex. + j = (parytet->ver & 3); // j is the face number. + parytet->ver = epivot[j]; // [4,5,2,11] pt[0] = org(*parytet); pt[1] = dest(*parytet); pt[2] = apex(*parytet); @@ -15779,21 +17495,38 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, searchtet.tet = NULL; dir = scoutsubface(&tmpsh, &searchtet); if (dir == SHAREFACE) { - // Inserted! 'tmpsh' must face toward the inside of the cavity. - // Remember the boundary tet (outside the cavity) in tmpsh - // (use the adjacent tet slot). - tmpsh.sh[0] = (shellface) encode(*parytet); - // Save this subface. - cavshells->newindex((void **) &parysh); - *parysh = tmpsh; - } - else { - // This boundary face is missing. + // Inserted. Make sure that tmpsh connects an interior tet of C. + stpivot(tmpsh, neightet); + // neightet and tmpsh refer to the same edge [pt[0], pt[1]]. + // If the origin of neightet is pt[1], it is inside. + if (org(neightet) != pt[1]) { + fsymself(neightet); + assert(org(neightet) == pt[1]); // SELF_CHECK + // Make sure that tmpsh is connected with an interior tet. + sesymself(tmpsh); + tsbond(neightet, tmpsh); + } + assert(dest(neightet) == pt[0]); // SELF_CHECK + } else if (dir == COLLISIONFACE) { + // This case is not possible anymore. 2010-02-01 + assert(0); + } else { + if (b->verbose > 2) { + printf(" bdry face (%d, %d, %d) -- %d is missing\n", + pointmark(pt[0]), pointmark(pt[1]), pointmark(pt[2]), i); + } shellfacedealloc(subfaces, tmpsh.sh); // Save this face in list. misfaces->newindex((void **) &parytet1); *parytet1 = *parytet; + continue; } + // Remember the boundary tet (outside the cavity) in tmpsh + // (use the adjacent tet slot). + tmpsh.sh[0] = (shellface) encode(*parytet); + // Save this subface. + cavshells->newindex((void **) &parysh); + *parysh = tmpsh; } // i if (misfaces->objects > 0) { @@ -15833,7 +17566,11 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, if (!pinfected(pd)) { searchtet = recenttet; ivf.iloc = (int) OUTSIDE; - insertpoint(pd, &searchtet, NULL, NULL, &ivf); + ivf.bowywat = 1; + insertvertex(pd, &searchtet, NULL, NULL, &ivf); + if (b->verbose > 2) { + printf(" Add point %d into list.\n", pointmark(pd)); + } pinfect(pd); cavpoints->newindex((void **) &parypt); *parypt = pd; @@ -15843,9 +17580,15 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, esym(*parytet, neightet); fsymself(neightet); if (!infected(neightet)) { + if (b->verbose > 2) { + printf(" Add a cavface (%d, %d, %d).\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet))); + } cavfaces->newindex((void **) &parytet1); *parytet1 = neightet; - } + } else { + } enextself(*parytet); } // j } // if (!infected(parytet)) @@ -15871,8 +17614,8 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, *parytet = recenttet; for (i = 0; i < newtets->objects; i++) { searchtet = * (triface *) fastlookup(newtets, i); - for (j = 0; j < 4; j++) { - decode(searchtet.tet[j], neightet); + for (searchtet.ver = 0; searchtet.ver < 4; searchtet.ver++) { + fsym(searchtet, neightet); if (!marktested(neightet)) { marktest(neightet); newtets->newindex((void **) &parytet); @@ -15884,6 +17627,9 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, cavpoints->restart(); cavfaces->restart(); + if (cavshells->objects > maxcavsize) { + maxcavsize = cavshells->objects; + } if (crosstets->objects > baknum) { // The cavity has been enlarged. cavityexpcount++; @@ -15911,131 +17657,112 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, /////////////////////////////////////////////////////////////////////////////// bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, - arraypool* midfaces, arraypool* missingshs, - arraypool* topnewtets, arraypool* botnewtets, - triface* crossedge) + arraypool* midfaces, arraypool* missingshs) { arraypool *cavshells; - triface bdrytet, neightet, *parytet; - triface searchtet, spintet; - face *parysh; + triface *parytet, bdrytet, toptet, bottet, midface; + triface neightet, spintet; + face checksh, *parysh; face checkseg; - point pa, pb, pc; - bool mflag; - int t1ver; - int i, j; + point pa, pb, pc, pf, pg; //, *pts; + int types[2], poss[4]; + //REAL elen[3]; //ori, len, n[3]; + bool mflag, bflag; + int i, j, k; // Connect newtets to tets outside the cavity. These connections are needed // for identifying the middle faces (which belong to R). - for (j = 0; j < 2; j++) { - cavshells = (j == 0 ? topshells : botshells); + for (k = 0; k < 2; k++) { + cavshells = (k == 0 ? topshells : botshells); if (cavshells != NULL) { for (i = 0; i < cavshells->objects; i++) { // Get a temp subface. parysh = (face *) fastlookup(cavshells, i); - // Get the boundary tet outside the cavity (saved in sh[0]). + // Get the boundary tet outside the cavity. decode(parysh->sh[0], bdrytet); pa = org(bdrytet); pb = dest(bdrytet); pc = apex(bdrytet); - // Get the adjacent new tet inside the cavity. + // Get the adjacent new tet. stpivot(*parysh, neightet); - // Mark neightet as an interior tet of this cavity. - infect(neightet); - // Connect the two tets (the old connections are replaced). - bond(bdrytet, neightet); + assert(org(neightet) == pb); // SELF_CHECK + assert(dest(neightet) == pa); // SELF_CHECK + // Mark neightet as an interior tet of this cavity, 2009-04-24. + // Comment: We know neightet is an interior tet. + if (!infected(neightet)) { + infect(neightet); + } + assert(oppo(bdrytet) != NULL); // No faked tet. + // if (oppo(bdrytet) != NULL) { + // Bond the two tets. + bond(bdrytet, neightet); // Also cleared the pointer to tmpsh. + // } tsdissolve(neightet); // Clear the pointer to tmpsh. // Update the point-to-tets map. - setpoint2tet(pa, (tetrahedron) neightet.tet); - setpoint2tet(pb, (tetrahedron) neightet.tet); - setpoint2tet(pc, (tetrahedron) neightet.tet); + setpoint2tet(pa, encode(neightet)); + setpoint2tet(pb, encode(neightet)); + setpoint2tet(pc, encode(neightet)); + // Delete the temp subface. + // shellfacedealloc(subfacepool, parysh->sh); } // i } // if (cavshells != NULL) - } // j + } // k - if (crossedge != NULL) { - // Glue top and bottom tets at their common facet. - triface toptet, bottet, spintet, *midface; - point pd, pe; - REAL ori; - int types[2], poss[4]; - int interflag; - int bflag; - - mflag = false; - pd = org(*crossedge); - pe = dest(*crossedge); - - // Search the first (middle) face in R. - // Since R may be non-convex, we must make sure that the face is in the - // interior of R. We search a face in 'topnewtets' whose three vertices - // are on R and it intersects 'crossedge' in its interior. Then search - // a matching face in 'botnewtets'. - for (i = 0; i < topnewtets->objects && !mflag; i++) { - searchtet = * (triface *) fastlookup(topnewtets, i); - for (searchtet.ver = 0; searchtet.ver < 4 && !mflag; searchtet.ver++) { - pa = org(searchtet); - if (pmarktested(pa)) { - pb = dest(searchtet); - if (pmarktested(pb)) { - pc = apex(searchtet); - if (pmarktested(pc)) { - // Check if this face intersects [d,e]. - interflag = tri_edge_test(pa,pb,pc,pd,pe,NULL,1,types,poss); - if (interflag == 2) { - // They intersect at a single point. Found. - toptet = searchtet; - // The face lies in the interior of R. - // Get the tet (in topnewtets) which lies above R. - ori = orient3d(pa, pb, pc, pd); - assert(ori != 0); - if (ori < 0) { - fsymself(toptet); - pa = org(toptet); - pb = dest(toptet); - } - // Search the face [b,a,c] in 'botnewtets'. - for (j = 0; j < botnewtets->objects; j++) { - neightet = * (triface *) fastlookup(botnewtets, j); - // Is neightet contains 'b'. - if ((point) neightet.tet[4] == pb) { - neightet.ver = 11; - } else if ((point) neightet.tet[5] == pb) { - neightet.ver = 3; - } else if ((point) neightet.tet[6] == pb) { - neightet.ver = 7; - } else if ((point) neightet.tet[7] == pb) { - neightet.ver = 0; - } else { - continue; - } - // Is the 'neightet' contains edge [b,a]. - if (dest(neightet) == pa) { - // 'neightet' is just the edge. - } else if (apex(neightet) == pa) { - eprevesymself(neightet); - } else if (oppo(neightet) == pa) { - esymself(neightet); - enextself(neightet); - } else { - continue; - } - // Is 'neightet' the face [b,a,c]. - if (apex(neightet) == pc) { - bottet = neightet; - mflag = true; - break; - } - } // j - } // if (interflag == 2) - } // pc - } // pb - } // pa - } // toptet.ver - } // i + mflag = true; // Initialize it. + if (midfaces != NULL) { + + // The first pair of top and bottom tets share the same edge [a, b]. + // toptet = * (triface *) fastlookup(topfaces, 0); + if (infected(firsttopface)) { + // This is due to he enlargement of the cavity. Find the updated top + // boundary face at edge [a,b]. + // Comment: An uninfected tet at [a,b] should be found since [a,b] is a + // boundary edge of the missing region R. It should not be enclosed + // by the enlarged cavity. + pa = apex(firsttopface); // SELF_CHECK + while (1) { + fnextself(firsttopface); + if (!infected(firsttopface)) break; + assert(apex(firsttopface) != pa); // SELF_CHECK + } + } + toptet = firsttopface; + pa = apex(toptet); + fsymself(toptet); + // Search a subface from the top mesh. + while (1) { + esymself(toptet); // The next face in the same tet. + pc = apex(toptet); + assert(pc != pa); // We should not return to the starting point. + if (pmarktested(pc)) break; // [a,b,c] is a subface. + fsymself(toptet); // Go to the adjacent tet. + } + // Search the subface [a,b,c] in the bottom mesh. + // bottet = * (triface *) fastlookup(botfaces, 0); + if (infected(firstbotface)) { + pa = apex(firstbotface); // SELF_CHECK + while (1) { + fnextself(firstbotface); + if (!infected(firstbotface)) break; + assert(apex(firstbotface) != pa); // SELF_CHECK + } + } + bottet = firstbotface; + pa = apex(bottet); + fsymself(bottet); + while (1) { + esymself(bottet); // The next face in the same tet. + pf = apex(bottet); + assert(pf != pa); // We should not return to the starting point. + if (pf == pc) break; // Face matched. + if (pmarktested(pf)) { + mflag = false; break; // Not matched. + } + fsymself(bottet); + } if (mflag) { - // Found a pair of matched faces in 'toptet' and 'bottet'. + // Connect the two tets together. bond(toptet, bottet); // Both are interior tets. infect(toptet); @@ -16044,23 +17771,20 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, markface(toptet); midfaces->newindex((void **) &parytet); *parytet = toptet; - } else { - // No pair of 'toptet' and 'bottet'. - toptet.tet = NULL; - // Randomly split an interior edge of R. - i = randomnation(missingshs->objects - 1); - recentsh = * (face *) fastlookup(missingshs, i); } - // Find other middle faces, connect top and bottom tets. + // Match pairs of subfaces (middle faces), connect top and bottom tets. for (i = 0; i < midfaces->objects && mflag; i++) { // Get a matched middle face [a, b, c] - midface = (triface *) fastlookup(midfaces, i); + midface = * (triface *) fastlookup(midfaces, i); // The tet must be a new created tet (marktested). - assert(marktested(*midface)); // SELF_CHECK - // Check the neighbors at the edges of this face. - for (j = 0; j < 3 && mflag; j++) { - toptet = *midface; + assert(marktested(midface)); // SELF_CHECK + + // Check the neighbors at edges [b, c] and [c, a]. + for (j = 0; j < 2 && mflag; j++) { + enextself(midface); // [b, c] or [c, a]. + pg = apex(midface); + toptet = midface; bflag = false; while (1) { // Go to the next face in the same tet. @@ -16070,7 +17794,6 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, break; // Find a subface. } if (pc == dummypoint) { - assert(0); // Check this case. break; // Find a subface. } // Go to the adjacent tet. @@ -16084,19 +17807,16 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, if (!bflag) { // assert(marktested(toptet)); // SELF_CHECK if (!facemarked(toptet)) { - fsym(*midface, bottet); - spintet = bottet; + fsym(midface, bottet); while (1) { esymself(bottet); - pd = apex(bottet); - if (pd == pc) break; // Face matched. - fsymself(bottet); - if (bottet.tet == spintet.tet) { - // Not found a matched bottom face. - mflag = false; - break; + pf = apex(bottet); + if (pf == pc) break; // Face matched. + if (pmarktested(pf)) { + mflag = false; break; // Not matched } - } // while (1) + fsymself(bottet); + } if (mflag) { if (marktested(bottet)) { // Connect two tets together. @@ -16108,185 +17828,79 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, markface(toptet); midfaces->newindex((void **) &parytet); *parytet = toptet; - } - } else { // mflag == false - // Adjust 'toptet' and 'bottet' to be the crossing edges. - fsym(*midface, bottet); - spintet = bottet; - while (1) { - esymself(bottet); - pd = apex(bottet); - if (pmarktested(pd)) { - // assert(pd != pc); - // Let 'toptet' be [a,b,c,#], and 'bottet' be [b,a,d,*]. - // Adjust 'toptet' and 'bottet' to be the crossing edges. - // Test orient3d(b,c,#,d). - ori = orient3d(dest(toptet), pc, oppo(toptet), pd); - if (ori < 0) { - // Edges [a,d] and [b,c] cross each other. - enextself(toptet); // [b,c] - enextself(bottet); // [a,d] - } else if (ori > 0) { - // Edges [a,c] and [b,d] cross each other. - eprevself(toptet); // [c,a] - eprevself(bottet); // [d,b] - } else { - // b,c,#,and d are coplanar!. - assert(0); - } - break; // Not matched - } - fsymself(bottet); - assert (bottet.tet != spintet.tet); - } - } // if (!mflag) - } // if (!facemarked(toptet)) - } // if (!bflag) - enextself(*midface); - } // j - } // i - - if (mflag) { - if (b->verbose > 2) { - printf(" Found %ld middle subfaces.\n", midfaces->objects); - } - face oldsh, newsh, casout, casin, neighsh; - - oldsh = * (face *) fastlookup(missingshs, 0); - - // Create new subfaces to fill the region R. - for (i = 0; i < midfaces->objects; i++) { - // Get a matched middle face [a, b, c] - midface = (triface *) fastlookup(midfaces, i); - unmarkface(*midface); - makeshellface(subfaces, &newsh); - setsorg(newsh, org(*midface)); - setsdest(newsh, dest(*midface)); - setsapex(newsh, apex(*midface)); - // The new subface gets its markers from the old one. - setshellmark(newsh, shellmark(oldsh)); - if (checkconstraints) { - setareabound(newsh, areabound(oldsh)); - } - // Connect the new subface to adjacent tets. - tsbond(*midface, newsh); - fsym(*midface, neightet); - sesymself(newsh); - tsbond(neightet, newsh); - } - - // Connect new subfaces together and to the bdry of R. - // Delete faked segments. - for (i = 0; i < midfaces->objects; i++) { - // Get a matched middle face [a, b, c] - midface = (triface *) fastlookup(midfaces, i); - for (j = 0; j < 3; j++) { - tspivot(*midface, newsh); - spivot(newsh, casout); - if (casout.sh == NULL) { - // Search its neighbor. - fnext(*midface, searchtet); - while (1) { - // (1) First check if this side is a bdry edge of R. - tsspivot1(searchtet, checkseg); - if (checkseg.sh != NULL) { - // It's a bdry edge of R. - assert(!infected(searchtet)); // It must not be a cavity tet. - // Get the old subface. - checkseg.shver = 0; - spivot(checkseg, oldsh); - if (sinfected(checkseg)) { - // It's a faked segment. Delete it. - spintet = searchtet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - shellfacedealloc(subsegs, checkseg.sh); - ssdissolve(oldsh); - checkseg.sh = NULL; - } - spivot(oldsh, casout); - if (casout.sh != NULL) { - casin = casout; - if (checkseg.sh != NULL) { - // Make sure that the subface has the right ori at the - // segment. - checkseg.shver = 0; - if (sorg(newsh) != sorg(checkseg)) { - sesymself(newsh); - } - spivot(casin, neighsh); - while (neighsh.sh != oldsh.sh) { - casin = neighsh; - spivot(casin, neighsh); - } - } - sbond1(newsh, casout); - sbond1(casin, newsh); - } - if (checkseg.sh != NULL) { - ssbond(newsh, checkseg); - } - break; - } // if (checkseg.sh != NULL) - // (2) Second check if this side is an interior edge of R. - tspivot(searchtet, neighsh); - if (neighsh.sh != NULL) { - // Found an adjacent subface of newsh (an interior edge). - sbond(newsh, neighsh); - break; - } - fnextself(searchtet); - assert(searchtet.tet != midface->tet); - } // while (1) - } // if (casout.sh == NULL) - enextself(*midface); - } // j - } // i + } else { + // The 'bottet' is not inside the cavity! + // This case can happen when the cavity was enlarged, and the + // 'toptet' is a co-facet (sub)face adjacent to the missing + // region, and it is a boundary face of the top cavity. + // So the toptet and bottet should be bonded already through + // a temp subface. See fig/dump-cavity-case18. Check it. + fsym(toptet, neightet); + assert(neightet.tet == bottet.tet); // SELF_CHECK + assert(neightet.ver == bottet.ver); // SELF_CHECK + // Do not add this face into 'midfaces'. + } + } + } // if (!facemarked(toptet)) + } + } // j + } // i - // Delete old subfaces. - for (i = 0; i < missingshs->objects; i++) { - parysh = (face *) fastlookup(missingshs, i); - shellfacedealloc(subfaces, parysh->sh); + } // if (midfaces != NULL) + + if (mflag) { + if (midfaces != NULL) { + if (b->verbose > 2) { + printf(" Found %ld middle subfaces.\n", midfaces->objects); } - } else { - if (toptet.tet != NULL) { - // Faces at top and bottom are not matched. - // Choose a Steiner point in R. - // Split one of the crossing edges. - pa = org(toptet); - pb = dest(toptet); - pc = org(bottet); - pd = dest(bottet); - // Search an edge in R which is either [a,b] or [c,d]. - // Reminder: Subfaces in this list 'missingshs', except the first - // one, represents an interior edge of R. - for (i = 1; i < missingshs->objects; i++) { - parysh = (face *) fastlookup(missingshs, i); - if (((sorg(*parysh) == pa) && (sdest(*parysh) == pb)) || - ((sorg(*parysh) == pb) && (sdest(*parysh) == pa))) break; - if (((sorg(*parysh) == pc) && (sdest(*parysh) == pd)) || - ((sorg(*parysh) == pd) && (sdest(*parysh) == pc))) break; - } - if (i < missingshs->objects) { - // Found. Return it. - recentsh = *parysh; - } else { - assert(0); - } + if (midfaces->objects > maxregionsize) { + maxregionsize = midfaces->objects; + } + // Unmark middle faces. + for (i = 0; i < midfaces->objects; i++) { + // Get a matched middle face [a, b, c] + midface = * (triface *) fastlookup(midfaces, i); + assert(facemarked(midface)); // SELF_CHECK + unmarkface(midface); } } - - midfaces->restart(); } else { - mflag = true; - } + // Faces at top and bottom are not matched. There exists non-Delaunay + // subedges. See fig/dump-cavity-case5.lua. + pa = org(toptet); + pb = dest(toptet); + pc = apex(toptet); + pf = apex(bottet); + + pf = oppo(toptet); + pg = oppo(bottet); + // Find a subface in R which intersects the edge [f,g]. + for (i = 0; i < missingshs->objects; i++) { + parysh = (face *) fastlookup(missingshs, i); + pa = sorg(*parysh); + pb = sdest(*parysh); + pc = sapex(*parysh); + if (tri_edge_test(pa, pb, pc, pf, pg, NULL, 1, types, poss)) { + // Found a subface. + break; + } + } + + if (i < missingshs->objects) { + // Such subface exist. + recentsh = *parysh; + } else { + assert(0); // Debug this case. + } + + + // Set a tet for searching the new point. + recenttet = firsttopface; + } // Delete the temp subfaces. - for (j = 0; j < 2; j++) { - cavshells = (j == 0 ? topshells : botshells); + for (k = 0; k < 2; k++) { + cavshells = (k == 0 ? topshells : botshells); if (cavshells != NULL) { for (i = 0; i < cavshells->objects; i++) { parysh = (face *) fastlookup(cavshells, i); @@ -16299,6 +17913,9 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, if (botshells != NULL) { botshells->restart(); } + if (midfaces != NULL) { + midfaces->restart(); + } return mflag; } @@ -16313,12 +17930,10 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, arraypool *botnewtets) { arraypool *newtets; - shellface *sptr, *ssptr; triface *parytet, *pnewtet, newtet, neightet, spintet; face checksh, *parysh; face checkseg, *paryseg; - int t1ver; - int i, j; + int i, j, k; if (b->verbose > 2) { printf(" Carve cavity: %ld old tets.\n", crosstets->objects); @@ -16334,44 +17949,38 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // Collect all subfaces and segments which attached to the old tets. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); - if ((sptr = (shellface*) parytet->tet[9]) != NULL) { - for (j = 0; j < 4; j++) { - if (sptr[j]) { - sdecode(sptr[j], checksh); - if (!sinfected(checksh)) { - sinfect(checksh); - cavetetshlist->newindex((void **) &parysh); - *parysh = checksh; - } + assert(infected(*parytet)); // SELF_CHECK + for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { + tspivot(*parytet, checksh); + if (checksh.sh != NULL) { + if (!sinfected(checksh)) { + sinfect(checksh); + cavetetshlist->newindex((void **) &parysh); + *parysh = checksh; } - } // j + } } - if ((ssptr = (shellface*) parytet->tet[8]) != NULL) { - for (j = 0; j < 6; j++) { - if (ssptr[j]) { - sdecode(ssptr[j], checkseg); - // Skip a deleted segment (was a faked segment) - if (checkseg.sh[3] != NULL) { - if (!sinfected(checkseg)) { - sinfect(checkseg); - cavetetseglist->newindex((void **) &paryseg); - *paryseg = checkseg; - } - } + for (j = 0; j < 6; j++) { + parytet->ver = edge2ver[j]; + tsspivot1(*parytet, checkseg); + if (checkseg.sh != NULL) { + if (!sinfected(checkseg)) { + sinfect(checkseg); + cavetetseglist->newindex((void **) &paryseg); + *paryseg = checkseg; } - } // j + } } } // i - // Uninfect collected subfaces. for (i = 0; i < cavetetshlist->objects; i++) { - parysh = (face *) fastlookup(cavetetshlist, i); - suninfect(*parysh); + checksh = * (face *) fastlookup(cavetetshlist, i); + suninfect(checksh); } // Uninfect collected segments. for (i = 0; i < cavetetseglist->objects; i++) { - paryseg = (face *) fastlookup(cavetetseglist, i); - suninfect(*paryseg); + checkseg = * (face *) fastlookup(cavetetseglist, i); + suninfect(checkseg); } // Connect subfaces to new tets. @@ -16387,6 +17996,11 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // Does this tet lie inside the cavity. if (infected(neightet)) { checksh = *parysh; + if (b->verbose > 2) { + printf(" Found an interior subface (%d, %d, %d)\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } stdissolve(checksh); caveencshlist->newindex((void **) &parysh); *parysh = checksh; @@ -16400,7 +18014,10 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, tsbond(newtet, *parysh); } } // i - + if (b->verbose > 2) { + printf(" %ld (%ld) cavity (interior) subfaces.\n", + cavetetshlist->objects, caveencshlist->objects); + } for (i = 0; i < cavetetseglist->objects; i++) { checkseg = * (face *) fastlookup(cavetetseglist, i); @@ -16414,6 +18031,10 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } fnextself(spintet); if (spintet.tet == neightet.tet) { + if (b->verbose > 2) { + printf(" Found an interior seg (%d, %d)\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } sstdissolve1(checkseg); caveencseglist->newindex((void **) &paryseg); *paryseg = checkseg; @@ -16431,7 +18052,10 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } } } // i - + if (b->verbose > 2) { + printf(" %ld (%ld) cavity (interior) segments.\n", + cavetetseglist->objects, caveencseglist->objects); + } cavetetshlist->restart(); cavetetseglist->restart(); @@ -16439,9 +18063,6 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // Delete the old tets in cavity. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); - if (ishulltet(*parytet)) { - hullsize--; - } tetrahedrondealloc(parytet->tet); } @@ -16449,8 +18070,8 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // Collect new tets in cavity. Some new tets have already been found // (and infected) in the fillcavity(). We first collect them. - for (j = 0; j < 2; j++) { - newtets = (j == 0 ? topnewtets : botnewtets); + for (k = 0; k < 2; k++) { + newtets = (k == 0 ? topnewtets : botnewtets); if (newtets != NULL) { for (i = 0; i < newtets->objects; i++) { parytet = (triface *) fastlookup(newtets, i); @@ -16460,17 +18081,20 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } } // i } - } // j + } // k // Now we collect all new tets in cavity. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); + if (i == 0) { + recenttet = *parytet; // Remember a live handle. + } for (j = 0; j < 4; j++) { decode(parytet->tet[j], neightet); if (marktested(neightet)) { // Is it a new tet? if (!infected(neightet)) { // Find an interior tet. - //assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK + assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK infect(neightet); crosstets->newindex((void **) &pnewtet); *pnewtet = neightet; @@ -16479,12 +18103,9 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } // j } // i - parytet = (triface *) fastlookup(crosstets, 0); - recenttet = *parytet; // Remember a live handle. - // Delete outer new tets. - for (j = 0; j < 2; j++) { - newtets = (j == 0 ? topnewtets : botnewtets); + for (k = 0; k < 2; k++) { + newtets = (k == 0 ? topnewtets : botnewtets); if (newtets != NULL) { for (i = 0; i < newtets->objects; i++) { parytet = (triface *) fastlookup(newtets, i); @@ -16492,9 +18113,6 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // This is an interior tet. uninfect(*parytet); unmarktest(*parytet); - if (ishulltet(*parytet)) { - hullsize++; - } } else { // An outer tet. Delete it. tetrahedrondealloc(parytet->tet); @@ -16517,19 +18135,21 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets, arraypool *missingshbds) + arraypool *botnewtets) { - triface *parytet, neightet, spintet; - face *parysh; + triface *parytet, neightet; + face checksh; face checkseg; point *ppt; - int t1ver; int i, j; // Reconnect crossing tets to cavity boundary. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); assert(infected(*parytet)); // SELF_CHECK + if (i == 0) { + recenttet = *parytet; // Remember a live handle. + } parytet->ver = 0; for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { fsym(*parytet, neightet); @@ -16552,31 +18172,6 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, uninfect(*parytet); } - // Remember a live handle. - recenttet = * (triface *) fastlookup(crosstets, 0); - - // Delete faked segments. - for (i = 0; i < missingshbds->objects; i++) { - parysh = (face *) fastlookup(missingshbds, i); - sspivot(*parysh, checkseg); - assert(checkseg.sh != NULL); - if (checkseg.sh[3] != NULL) { - if (sinfected(checkseg)) { - // It's a faked segment. Delete it. - sstpivot1(checkseg, neightet); - spintet = neightet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == neightet.tet) break; - } - shellfacedealloc(subsegs, checkseg.sh); - ssdissolve(*parysh); - //checkseg.sh = NULL; - } - } - } // i - // Delete new tets. for (i = 0; i < topnewtets->objects; i++) { parytet = (triface *) fastlookup(topnewtets, i); @@ -16606,8 +18201,7 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, - point plane_pb, point plane_pc) +void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) { badface *parybf, *prevbf, *nextbf; triface neightet; @@ -16684,7 +18278,7 @@ void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, for (i = 0; i < 5; i++) { if (pmarktest2ed(p[i])) { // A top point has a positive weight. - w[i] = orient3dfast(plane_pa, plane_pb, plane_pc, p[i]); + w[i] = orient3d(plane_pa, plane_pb, plane_pc, p[i]); if (w[i] < 0) w[i] = -w[i]; assert(w[i] != 0); } else { @@ -16700,7 +18294,12 @@ void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, // p[1], p[0], p[2], p[3]. insph = insphere(p[1], p[0], p[2], p[3], p[4]); - ori4 = orient4d(p[1], p[0], p[2], p[3], p[4], w[1], w[0], w[2], w[3], w[4]); + + if (b->flipinsert_ori4dexact) { + ori4 = orient4dexact(p[1], p[0], p[2], p[3], p[4],w[1],w[0],w[2],w[3],w[4]); + } else { + ori4 = orient4d(p[1], p[0], p[2], p[3], p[4], w[1], w[0], w[2], w[3], w[4]); + } if (b->verbose > 2) { printf(" Heights: (%g, %g, %g, %g, %g)\n", w[0],w[1],w[2],w[3],w[4]); @@ -16733,7 +18332,7 @@ void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, // Search an item whose key is larger or equal to current key. prevbf = NULL; nextbf = *pqueue; - //if (!b->flipinsert_random) { // Default use a priority queue. + if (!b->flipinsert_random) { // Default use a priority queue. // Insert the item into priority queue. while (nextbf != NULL) { if (nextbf->key < parybf->key) { @@ -16743,7 +18342,7 @@ void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, break; } } - //} // if (!b->flipinsert_random) + } // if (!b->flipinsert_random) // -L1 // Insert the new item between prev and next items. if (prevbf == NULL) { *pqueue = parybf; @@ -16775,20 +18374,19 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, arraypool *botpoints, arraypool *midpoints) { arraypool *crossfaces, *bfacearray; - triface fliptets[6], baktets[2], fliptet, newface; + triface fliptets[5], baktets[2], fliptet, newface; triface neightet, *parytet; face checksh; face checkseg; badface *pqueue; badface *popbf, bface; - point plane_pa, plane_pb, plane_pc; point p1, p2, pd, pe; point *parypt; - flipconstraints fc; REAL ori[3]; int convcount, copcount; int flipflag, fcount; int n, i; + long f23count, f32count, f44count; long totalfcount; @@ -16836,6 +18434,9 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, if (b->verbose > 1) { printf(" Found %ld crossing faces.\n", crossfaces->objects); } + if (crossfaces->objects > maxcrossfacecount) { + maxcrossfacecount = crossfaces->objects; + } for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); @@ -16848,13 +18449,14 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, for (i = 0; i < crossfaces->objects; i++) { parytet = (triface *) fastlookup(crossfaces, i); - flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); + flipcertify(parytet, &pqueue); } crossfaces->restart(); // The list for temporarily storing unflipable faces. bfacearray = new arraypool(sizeof(triface), 4); + fliploop: fcount = 0; // Count the number of flips. @@ -16916,7 +18518,7 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, fliptets[0] = fliptet; // abcd, d may be the new vertex. fliptets[1] = neightet; // bace. - flip23(fliptets, 1, &fc); + flip23(fliptets, 1, 0, 0); // Put the link faces into check list. for (i = 0; i < 3; i++) { eprevesym(fliptets[i], newface); @@ -16943,26 +18545,16 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, assert(checkseg.sh == NULL); // Collect tets sharing at this edge. - // NOTE: This operation may collect tets which lie outside the - // cavity, e.g., when the edge lies on the boundary of the - // cavity. Do not flip if there are outside tets at this edge. - // 2012-07-27. esym(fliptet, fliptets[0]); // [b,a,d,c] n = 0; do { - p1 = apex(fliptets[n]); - if (!(pmarktested(p1) || pmarktest2ed(p1) || pmarktest3ed(p1))) { - // This apex is not on the cavity. Hence the face does not - // lie inside the cavity. Do not flip this edge. - n = 1000; break; - } fnext(fliptets[n], fliptets[n + 1]); n++; } while ((fliptets[n].tet != fliptet.tet) && (n < 5)); if (n == 3) { // Found a 3-to-2 flip. - flip32(fliptets, 1, &fc); + flip32(fliptets, 1, 0, 0); // Put the link faces into check list. for (i = 0; i < 3; i++) { esym(fliptets[0], newface); @@ -16996,7 +18588,7 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, baktets[0] = fliptets[2]; // = [b,a,e,f] baktets[1] = fliptets[3]; // = [b,a,f,d] // The flip may involve hull tets. - flip23(fliptets, 1, &fc); + flip23(fliptets, 1, 0, 0); // Put the "outer" link faces into check list. // fliptets[0] = [e,d,a,b] => will be flipped, so // [a,b,d] and [a,b,e] are not "outer" link faces. @@ -17015,7 +18607,7 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, eprevself(fliptets[0]); // = [b,a,d,c], d is the new vertex. fliptets[1] = baktets[0]; // = [b,a,e,f] fliptets[2] = baktets[1]; // = [b,a,f,d] - flip32(fliptets, 1, &fc); + flip32(fliptets, 1, 0, 0); // Put the "outer" link faces into check list. // fliptets[0] = [d,e,f,a] // fliptets[1] = [e,d,f,b] @@ -17053,22 +18645,23 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, pointmark(bface.fapex), pointmark(bface.foppo), pointmark(bface.noppo), bface.key); } + dbg_ignore_facecount++; } // if (convcount == 1) if (flipflag == 1) { // Update the priority queue. for (i = 0; i < crossfaces->objects; i++) { parytet = (triface *) fastlookup(crossfaces, i); - flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); + flipcertify(parytet, &pqueue); } crossfaces->restart(); - if (1) { // if (!b->flipinsert_random) { + if (!b->flipinsert_random) { // Insert all queued unflipped faces. for (i = 0; i < bfacearray->objects; i++) { parytet = (triface *) fastlookup(bfacearray, i); // This face may be changed. if (!isdeadtet(*parytet)) { - flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); + flipcertify(parytet, &pqueue); } } bfacearray->restart(); @@ -17090,10 +18683,22 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, printf("!! No flip is found in %ld faces.\n", bfacearray->objects); assert(0); } + if (b->flipinsert_random) { + // Insert all queued unflipped faces. + for (i = 0; i < bfacearray->objects; i++) { + parytet = (triface *) fastlookup(bfacearray, i); + // This face may be changed. + if (!isdeadtet(*parytet)) { + flipcertify(parytet, &pqueue); + } + } + bfacearray->restart(); + goto fliploop; + } } // 'bfacearray' may be not empty (for what reason ??). - //dbg_unflip_facecount += bfacearray->objects; + dbg_unflip_facecount += bfacearray->objects; assert(flippool->items == 0l); delete bfacearray; @@ -17112,6 +18717,11 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, f32count = flip32count - f32count; f44count = flip44count - f44count; totalfcount = f23count + f32count + f44count; + + if (totalfcount > maxflipsequence) { + maxflipsequence = totalfcount; + } + if (b->verbose > 2) { printf(" Total %ld flips. f23(%ld), f32(%ld), f44(%ld).\n", totalfcount, f23count, f32count, f44count); @@ -17124,85 +18734,156 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, // // // 'missingshs' contains the list of subfaces in R. Moreover, each subface // // (except the first one) in this list represents an interior edge of R. // +// Note: All subfaces in R are smarktested. // // // // Note: We assume that all vertices of R are marktested so we can detect // // new subface by checking the flag in apexes. // // // /////////////////////////////////////////////////////////////////////////////// -bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds, +bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds, arraypool* newshs) { badface *newflipface, *popface; - triface searchtet, spintet, neightet; + triface searchtet, spintet; face oldsh, newsh, opensh, *parysh; face casout, casin, neighsh, checksh; - face neighseg, checkseg; - point pc; - int success; - int t1ver; - int i, j; - + face checkseg, fakeseg; + point pc, pd, pe, pf, ppt[2]; + enum interresult dir; + REAL n[3], len; // elen[3]; + bool insideflag; + int types[2], poss[4]; + int i, j, k; - // Search the first new subface to fill the region. - for (i = 0; i < missingshbds->objects; i++) { - parysh = (face *) fastlookup(missingshbds, i); - sspivot(*parysh, neighseg); - sstpivot1(neighseg, searchtet); - j = 0; // Count the number of passes of R. + if (b->verbose > 2) { + printf(" Fill region: %ld old subfaces (%ld).\n", missingshs->objects, + fillregioncount); + } + + // Search the first constrained face of R. It is found from the set of + // faces sharing at a boundary edge [a,b]. Such face must be found. + // The search takes the following two steps: + // - First, finds a candidate face [a,b,c] where c is also a vertex of R; + // Note that [a,b,c] may not be the right face to fill R. For instance, + // when R is concave at b. + // - Second, check if [a,b,c] can fill R. This can be checked if an + // adjacent tet of [a,b,c] intersects R. This is a tetrahedron-triangle + // intersection test. It can be reduced to two triangle-edge intersect + // tests, i.e., intersect the two faces not containing the edge [a,b] in + // this tet with all interior edges of R. + + // We start from the first boundary edge of R. + oldsh = * (face *) fastlookup(missingshbds, 0); + ppt[0] = sorg(oldsh); + ppt[1] = sdest(oldsh); + point2tetorg(ppt[0], searchtet); + dir = finddirection(&searchtet, ppt[1], 0); + assert(dir == ACROSSVERT); // SELF_CHECK + + insideflag = false; + + // Each face has two adjacent tets. + for (k = 0; k < 2; k++) { + if (b->verbose > 2) { + printf(" Search an interior face from edge (%d, %d).\n", + pointmark(ppt[0]), pointmark(ppt[1])); + } spintet = searchtet; while (1) { pc = apex(spintet); if (pmarktested(pc)) { - neightet = spintet; - j++; - } + // Found a candidate face. Check if it is inside R. + if (missingshs->objects > 2l) { + // pd = oppo(spintet); + // if (pd == dummypoint) { + // Calculate an above point for this subface. + facenormal(ppt[0], ppt[1], pc, n, 1, NULL); + len = sqrt(DOT(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = DIST(ppt[0], ppt[1]); + len += DIST(ppt[1], pc); + len += DIST(pc, ppt[0]); + len /= 3.0; + dummypoint[0] = ppt[0][0] + len * n[0]; + dummypoint[1] = ppt[0][1] + len * n[1]; + dummypoint[2] = ppt[0][2] + len * n[2]; + pd = dummypoint; + // } + //if (pd != dummypoint) { + for (j = 0; j < 2 && !insideflag; j++) { + for (i = 1; i < missingshs->objects && !insideflag; i++) { + parysh = (face *) fastlookup(missingshs, i); + // Get an interior edge of R. + pe = sorg(*parysh); + pf = sdest(*parysh); + if (tri_edge_test(ppt[j],pc,pd,pe,pf,NULL,1,types,poss)) { + dir = (enum interresult) types[0]; + if (dir == ACROSSFACE) { + searchtet = spintet; + insideflag = true; + } else if (dir == ACROSSEDGE) { + searchtet = spintet; + insideflag = true; + } + } + } // i + } // j + // } + // if (pd == dummypoint) { + dummypoint[0] = 0; + dummypoint[1] = 0; + dummypoint[2] = 0; + // } + } else { + // It is a simple 2-to-2 flip. + searchtet = spintet; + insideflag = true; + } + } // if (pmarktested(pc)) + if (insideflag) break; fnextself(spintet); if (spintet.tet == searchtet.tet) break; - } - assert(j >= 1); - if (j == 1) { - // Found an interior new subface. - searchtet = neightet; - oldsh = *parysh; - break; - } - } // i - - if (i == missingshbds->objects) { - // Failed to find any interior subface. - // Need Steiner points. + } // while (1) + if (insideflag) break; + esymself(searchtet); + ppt[0] = org(searchtet); + ppt[1] = dest(searchtet); + } // k + + if (!insideflag) { + // Something strange is happening. + // Refine the missing region by adding a Steiner point. + recentsh = oldsh; + recenttet = searchtet; // For point location. return false; } + // Create a new subface at the boundary edge. + if (b->verbose > 2) { + printf(" Create a new subface (%d, %d, %d)\n", pointmark(ppt[0]), + pointmark(ppt[1]), pointmark(pc)); + } makeshellface(subfaces, &newsh); - setsorg(newsh, org(searchtet)); - setsdest(newsh, dest(searchtet)); - setsapex(newsh, apex(searchtet)); + setsorg(newsh, ppt[0]); + setsdest(newsh, ppt[1]); + setsapex(newsh, pc); // The new subface gets its markers from the old one. setshellmark(newsh, shellmark(oldsh)); if (checkconstraints) { setareabound(newsh, areabound(oldsh)); } // Connect the new subface to adjacent tets. + tspivot(searchtet, checksh); // SELF_CHECK + assert(checksh.sh == NULL); // SELF_CHECK tsbond(searchtet, newsh); fsymself(searchtet); sesymself(newsh); tsbond(searchtet, newsh); // Connect newsh to outer subfaces. sspivot(oldsh, checkseg); - if (sinfected(checkseg)) { - // It's a faked segment. Delete it. - spintet = searchtet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - shellfacedealloc(subsegs, checkseg.sh); - ssdissolve(oldsh); - checkseg.sh = NULL; - } spivot(oldsh, casout); if (casout.sh != NULL) { casin = casout; @@ -17214,498 +18895,391 @@ bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds, } spivot(casin, neighsh); while (neighsh.sh != oldsh.sh) { - casin = neighsh; - spivot(casin, neighsh); - } - } - sbond1(newsh, casout); - sbond1(casin, newsh); - } - if (checkseg.sh != NULL) { - ssbond(newsh, checkseg); - } - // Add this new subface into list. - sinfect(newsh); - newshs->newindex((void **) &parysh); - *parysh = newsh; - - // Push two "open" side of the new subface into stack. - for (i = 0; i < 2; i++) { - senextself(newsh); - newflipface = (badface *) flippool->alloc(); - newflipface->ss = newsh; - newflipface->nextitem = flipstack; - flipstack = newflipface; - } - - success = 1; - - // Loop until 'flipstack' is empty. - while ((flipstack != NULL) && success) { - // Pop an "open" side from the stack. - popface = flipstack; - opensh = popface->ss; - flipstack = popface->nextitem; // The next top item in stack. - flippool->dealloc((void *) popface); - - // opensh is either (1) an interior edge or (2) a bdry edge. - stpivot(opensh, searchtet); - tsspivot1(searchtet, checkseg); - if (checkseg.sh == NULL) { - // No segment. It is an interior edge of R. - // Search for a new face in R. - spintet = searchtet; - fnextself(spintet); // Skip the current face. - while (1) { - pc = apex(spintet); - if (pmarktested(pc)) { - // 'opensh' is an interior edge. - if (!issubface(spintet)) { - // Create a new subface. - makeshellface(subfaces, &newsh); - setsorg(newsh, org(spintet)); - setsdest(newsh, dest(spintet)); - setsapex(newsh, pc); - // The new subface gets its markers from its neighbor. - setshellmark(newsh, shellmark(opensh)); - if (checkconstraints) { - setareabound(newsh, areabound(opensh)); - } - // Connect the new subface to adjacent tets. - tsbond(spintet, newsh); - fsymself(spintet); - sesymself(newsh); - tsbond(spintet, newsh); - // Connect newsh to its adjacent subface. - sbond(newsh, opensh); - // Add this new subface into list. - sinfect(newsh); - newshs->newindex((void **) &parysh); - *parysh = newsh; - // Push two "open" side of the new subface into stack. - for (i = 0; i < 2; i++) { - senextself(newsh); - newflipface = (badface *) flippool->alloc(); - newflipface->ss = newsh; - newflipface->nextitem = flipstack; - flipstack = newflipface; - } - } else { - // Connect to another open edge. - tspivot(spintet, checksh); - sbond(opensh, checksh); - } - break; - } // if (pmarktested(pc)) - fnextself(spintet); - if (spintet.tet == searchtet.tet) { - // Not find any face to fill in R at this side. - // Suggest a point to split the edge. - success = 0; - break; - } - } // while (1) - } else { - // This side coincident with a boundary edge of R. - checkseg.shver = 0; - spivot(checkseg, oldsh); - if (sinfected(checkseg)) { - // It's a faked segment. Delete it. - spintet = searchtet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - shellfacedealloc(subsegs, checkseg.sh); - ssdissolve(oldsh); - checkseg.sh = NULL; - } - spivot(oldsh, casout); - if (casout.sh != NULL) { - casin = casout; - if (checkseg.sh != NULL) { - // Make sure that the subface has the right ori at the segment. - checkseg.shver = 0; - if (sorg(opensh) != sorg(checkseg)) { - sesymself(opensh); - } - spivot(casin, neighsh); - while (neighsh.sh != oldsh.sh) { - casin = neighsh; - spivot(casin, neighsh); - } - } - sbond1(opensh, casout); - sbond1(casin, opensh); - } - if (checkseg.sh != NULL) { - ssbond(opensh, checkseg); - } - } // if (checkseg.sh != NULL) - } // while ((flipstack != NULL) && success) - - if (success) { - // Uninfect all new subfaces. - for (i = 0; i < newshs->objects; i++) { - parysh = (face *) fastlookup(newshs, i); - suninfect(*parysh); - } - // Delete old subfaces. - for (i = 0; i < missingshs->objects; i++) { - parysh = (face *) fastlookup(missingshs, i); - shellfacedealloc(subfaces, parysh->sh); - } - fillregioncount++; - } else { - // Failed to fill the region. - // Re-connect old subfaces at boundaries of R. - // Also delete fake segments. - for (i = 0; i < missingshbds->objects; i++) { - parysh = (face *) fastlookup(missingshbds, i); - // It still connect to 'casout'. - // Re-connect 'casin' to it. - spivot(*parysh, casout); - casin = casout; - spivot(casin, neighsh); - while (1) { - if (sinfected(neighsh)) break; - if (neighsh.sh == parysh->sh) break; - casin = neighsh; - spivot(casin, neighsh); - } - if (sinfected(neighsh)) { - sbond1(casin, *parysh); - } - sspivot(*parysh, checkseg); - if (checkseg.sh != NULL) { - if (checkseg.sh[3] != NULL) { - if (sinfected(checkseg)) { - sstpivot1(checkseg, searchtet); - spintet = searchtet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - ssdissolve(*parysh); - shellfacedealloc(subsegs, checkseg.sh); - } - } - } - } - // Delete all new subfaces. - for (i = 0; i < newshs->objects; i++) { - parysh = (face *) fastlookup(newshs, i); - shellfacedealloc(subfaces, parysh->sh); - } - // Clear the flip pool. - flippool->restart(); - flipstack = NULL; - - // Choose an interior edge of R to split. - assert(missingshs->objects > 1); - // Skip the first subface in 'missingshs'. - i = randomnation(missingshs->objects - 1) + 1; - parysh = (face *) fastlookup(missingshs, i); - recentsh = *parysh; - } - - newshs->restart(); - - return success; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertpoint_cdt() Insert a new point into a CDT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::insertpoint_cdt(point newpt, triface *searchtet, face *splitsh, - face *splitseg, insertvertexflags *ivf, - arraypool *cavpoints, arraypool *cavfaces, - arraypool *cavshells, arraypool *newtets, - arraypool *crosstets, arraypool *misfaces) -{ - triface neightet, *parytet; - face checksh, *parysh, *parysh1; - face *paryseg, *paryseg1; - point *parypt; - int t1ver; - int i; - - if (b->verbose > 2) { - printf(" Insert point %d into CDT\n", pointmark(newpt)); - } - - if (!insertpoint(newpt, searchtet, NULL, NULL, ivf)) { - // Point is not inserted. Check ivf->iloc for reason. - return 0; + casin = neighsh; + spivot(casin, neighsh); + } + } + sbond1(newsh, casout); + sbond1(casin, newsh); } - - - for (i = 0; i < cavetetvertlist->objects; i++) { - cavpoints->newindex((void **) &parypt); - *parypt = * (point *) fastlookup(cavetetvertlist, i); + if (checkseg.sh != NULL) { + ssbond(newsh, checkseg); } - // Add the new point into the point list. - cavpoints->newindex((void **) &parypt); - *parypt = newpt; + // Add this new subface into list. + sinfect(newsh); + newshs->newindex((void **) &parysh); + *parysh = newsh; - for (i = 0; i < cavebdrylist->objects; i++) { - cavfaces->newindex((void **) &parytet); - *parytet = * (triface *) fastlookup(cavebdrylist, i); + // Push two "open" side of the new subface into stack. + for (i = 0; i < 2; i++) { + senextself(newsh); + newflipface = (badface *) flippool->alloc(); + newflipface->ss = newsh; + newflipface->nextitem = flipstack; + flipstack = newflipface; } - for (i = 0; i < caveoldtetlist->objects; i++) { - crosstets->newindex((void **) &parytet); - *parytet = * (triface *) fastlookup(caveoldtetlist, i); + // Every other boundary edge of R is identified as a segment. Insert a faked + // segments at the place if it is not a segment. + for (i = 1; i < missingshbds->objects; i++) { + parysh = (face *) fastlookup(missingshbds, i); + ppt[0] = sorg(*parysh); + ppt[1] = sdest(*parysh); + point2tetorg(ppt[0], searchtet); + dir = finddirection(&searchtet, ppt[1], 0); + assert(dir == ACROSSVERT); // SELF_CHECK + tsspivot1(searchtet, checkseg); + if (checkseg.sh == NULL) { + // Insert a fake segment at this tet. + if (b->verbose > 2) { + printf(" Insert a fake segment (%d, %d)\n", pointmark(ppt[0]), + pointmark(ppt[1])); + } + makeshellface(subsegs, &fakeseg); + setsorg(fakeseg, ppt[0]); + setsdest(fakeseg, ppt[1]); + sinfect(fakeseg); // Mark it as faked. + // Connect it to all tets at this edge. + spintet = searchtet; + while (1) { + tssbond1(spintet, fakeseg); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + checkseg = fakeseg; + } + // Let the segment hold the old subface. + checkseg.shver = 0; + sbond1(checkseg, *parysh); + // Remember it to free it later. + *parysh = checkseg; } - cavetetvertlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - - // Insert the point using the cavity algorithm. - delaunizecavity(cavpoints, cavfaces, cavshells, newtets, crosstets, - misfaces); - fillcavity(cavshells, NULL, NULL, NULL, NULL, NULL, NULL); - carvecavity(crosstets, newtets, NULL); - - if ((splitsh != NULL) || (splitseg != NULL)) { - // Insert the point into the surface mesh. - sinsertvertex(newpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat); + // Loop until 'flipstack' is empty. + while (flipstack != NULL) { - // Put all new subfaces into stack. - for (i = 0; i < caveshbdlist->objects; i++) { - // Get an old subface at edge [a, b]. - parysh = (face *) fastlookup(caveshbdlist, i); - spivot(*parysh, checksh); // The new subface [a, b, p]. - // Do not recover a deleted new face (degenerated). - if (checksh.sh[3] != NULL) { - subfacstack->newindex((void **) &parysh); - *parysh = checksh; - } - } + // Pop an "open" side from the stack. + popface = flipstack; + opensh = popface->ss; + flipstack = popface->nextitem; // The next top item in stack. + flippool->dealloc((void *) popface); - if (splitseg != NULL) { - // Queue two new subsegments in C(p) for recovery. - for (i = 0; i < cavesegshlist->objects; i++) { - paryseg = (face *) fastlookup(cavesegshlist, i); - subsegstack->newindex((void **) &paryseg1); - *paryseg1 = *paryseg; + // Process it if it is still open. + spivot(opensh, casout); + if (casout.sh == NULL) { + if (b->verbose > 2) { + printf(" Get an open side (%d, %d) - %d.\n", + pointmark(sorg(opensh)), pointmark(sdest(opensh)), + pointmark(sapex(opensh))); } - } // if (splitseg != NULL) - - // Delete the old subfaces in sC(p). - for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - if (checksubfaceflag) { - // It is possible that this subface still connects to adjacent - // tets which are not in C(p). If so, clear connections in the - // adjacent tets at this subface. - stpivot(*parysh, neightet); - if (neightet.tet != NULL) { - if (neightet.tet[4] != NULL) { - // Found an adjacent tet. It must be not in C(p). - assert(!infected(neightet)); - tsdissolve(neightet); - fsymself(neightet); - assert(!infected(neightet)); - tsdissolve(neightet); + // Search a neighbor to close this side. + stpivot(opensh, searchtet); + tsspivot1(searchtet, checkseg); + if (checkseg.sh == NULL) { + // No segment. It is inside R. Search for a new face to fill in R. + // Note that the face may not be found (see fig 2010-05-25-c). + spintet = searchtet; + fnextself(spintet); // Skip the current face. + while (1) { + pc = apex(spintet); + if (pmarktested(pc)) { + // Found a place for a new subface inside R -- Case (i). + tspivot(spintet, checksh); + if (checksh.sh == NULL) { + // Create a new subface. + if (b->verbose > 2) { + printf(" Create a new subface (%d, %d, %d)\n", + pointmark(org(spintet)), pointmark(dest(spintet)), + pointmark(pc)); + } + makeshellface(subfaces, &newsh); + setsorg(newsh, org(spintet)); + setsdest(newsh, dest(spintet)); + setsapex(newsh, pc); + // The new subface gets its markers from its neighbor. + setshellmark(newsh, shellmark(opensh)); + if (checkconstraints) { + setareabound(newsh, areabound(opensh)); + } + // Connect the new subface to adjacent tets. + tsbond(spintet, newsh); + fsymself(spintet); + sesymself(newsh); + tsbond(spintet, newsh); + // Connect newsh to its adjacent subface. + sbond(newsh, opensh); + // Add this new subface into list. + sinfect(newsh); + newshs->newindex((void **) &parysh); + *parysh = newsh; + // Push two "open" side of the new subface into stack. + for (i = 0; i < 2; i++) { + senextself(newsh); + newflipface = (badface *) flippool->alloc(); + newflipface->ss = newsh; + newflipface->nextitem = flipstack; + flipstack = newflipface; + } + } else { + // A new subface has already been created. + assert(sinfected(checksh)); // It must be in stack. + spivot(checksh, neighsh); // SELF_CHECK + assert(neighsh.sh == NULL); // Its side must be open. + if (b->verbose > 2) { + printf(" Connect to another open side (%d, %d, %d)\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + sbond(opensh, checksh); // Simply connect them. + } + break; // -- Case (i) + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) { + // Not find any face to fill in R at this side. + // TO DO: suggest a point to split the edge. + assert(0); + } + } // while (1) + } else { + // This side coincident with a boundary edge of R. + checkseg.shver = 0; + spivot(checkseg, oldsh); + if (sinfected(checkseg)) { + // It's a faked segment. Delete it. + if (b->verbose > 2) { + printf(" Delete a fake segment (%d, %d)\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); } + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + shellfacedealloc(subsegs, checkseg.sh); + } + if (b->verbose > 2) { + printf(" Connect to a boundary edge (%d, %d, %d)\n", + pointmark(sorg(oldsh)), pointmark(sdest(oldsh)), + pointmark(sapex(oldsh))); + } + sspivot(oldsh, checkseg); + spivot(oldsh, casout); + if (casout.sh != NULL) { + casin = casout; + if (checkseg.sh != NULL) { + // Make sure that the subface has the right ori at the segment. + checkseg.shver = 0; + if (sorg(opensh) != sorg(checkseg)) { + sesymself(opensh); + } + spivot(casin, neighsh); + while (neighsh.sh != oldsh.sh) { + casin = neighsh; + spivot(casin, neighsh); + } + } + sbond1(opensh, casout); + sbond1(casin, opensh); + } + if (checkseg.sh != NULL) { + ssbond(opensh, checkseg); } } - shellfacedealloc(subfaces, parysh->sh); - } - if (splitseg != NULL) { - // Delete the old segment in sC(p). - shellfacedealloc(subsegs, splitseg->sh); - } - // Clear working lists. - caveshlist->restart(); - caveshbdlist->restart(); - cavesegshlist->restart(); - } // if ((splitsh != NULL) || (splitseg != NULL)) - - // Put all interior subfaces into stack for recovery. - // They were collected in carvecavity(). - // Note: Some collected subfaces may be deleted by sinsertvertex(). - for (i = 0; i < caveencshlist->objects; i++) { - parysh = (face *) fastlookup(caveencshlist, i); - if (parysh->sh[3] != NULL) { - subfacstack->newindex((void **) &parysh1); - *parysh1 = *parysh; - } - } + } // if (casout.sh == NULL) - // Put all interior segments into stack for recovery. - // They were collected in carvecavity(). - // Note: Some collected segments may be deleted by sinsertvertex(). - for (i = 0; i < caveencseglist->objects; i++) { - paryseg = (face *) fastlookup(caveencseglist, i); - if (paryseg->sh[3] != NULL) { - subsegstack->newindex((void **) &paryseg1); - *paryseg1 = *paryseg; - } + } // while (flipstack != NULL) + + // Uninfect all new subfaces. + for (i = 0; i < newshs->objects; i++) { + parysh = (face *) fastlookup(newshs, i); + suninfect(*parysh); } - caveencshlist->restart(); - caveencseglist->restart(); + if (b->verbose > 2) { + printf(" Created %ld new subfaces.\n", newshs->objects); + } + fillregioncount++; - return 1; + return true; } /////////////////////////////////////////////////////////////////////////////// // // // refineregion() Refine a missing region by inserting points. // // // -// 'splitsh' represents an edge of the facet to be split. It must be not a // -// segment. -// // -// Assumption: The current mesh is a CDT and is convex. // -// // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, - arraypool *cavfaces, arraypool *cavshells, - arraypool *newtets, arraypool *crosstets, - arraypool *misfaces) +void tetgenmesh::refineregion() { - triface searchtet, spintet; - face splitseg, *paryseg; - point steinpt, pa, pb, refpt; + triface searchtet; + face splitsh; + face *paryseg, sseg; + point steinpt, pa, pb, pc; insertvertexflags ivf; - enum interresult dir; - long baknum = points->items; - int t1ver; - int i; + REAL auv[2], buv[2], newuv[2], t; + int fmark, fid, eid; + int loc; // iloc, sloc; + int s, i; - if (b->verbose > 2) { - printf(" Refining region at edge (%d, %d, %d).\n", - pointmark(sorg(splitsh)), pointmark(sdest(splitsh)), - pointmark(sapex(splitsh))); - } + // The mesh is a CDT. + assert(subsegstack->objects == 0l); // SELF_CHECK - // Add the Steiner point at the barycenter of the face. - pa = sorg(splitsh); - pb = sdest(splitsh); // Create a new point. makepoint(&steinpt, FREEFACETVERTEX); - for (i = 0; i < 3; i++) { - steinpt[i] = 0.5 * (pa[i] + pb[i]); - } - - ivf.bowywat = 1; // Use the Bowyer-Watson algorrithm. - ivf.cdtflag = 1; // Only create the initial cavity. - ivf.sloc = (int) ONEDGE; - ivf.sbowywat = 1; - ivf.assignmeshsize = b->metric; - point2tetorg(pa, searchtet); // Start location from it. - ivf.iloc = (int) OUTSIDE; + // The 'recentsh' saved an edge to be split. + splitsh = recentsh; + // Add the Steiner point at the barycenter of the face. + pa = sorg(splitsh); + pb = sdest(splitsh); + pc = sapex(splitsh); - ivf.rejflag = 1; // Reject it if it encroaches upon any segment. - if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, NULL, &ivf, cavpoints, - cavfaces, cavshells, newtets, crosstets, misfaces)) { - if (ivf.iloc == (int) ENCSEGMENT) { - pointdealloc(steinpt); - // Split an encroached segment. - assert(encseglist->objects > 0); - i = randomnation(encseglist->objects); - paryseg = (face *) fastlookup(encseglist, i); - splitseg = *paryseg; - encseglist->restart(); - - // Split the segment. - pa = sorg(splitseg); - pb = sdest(splitseg); - // Create a new point. - makepoint(&steinpt, FREESEGVERTEX); - for (i = 0; i < 3; i++) { - steinpt[i] = 0.5 * (pa[i] + pb[i]); - } - point2tetorg(pa, searchtet); - ivf.iloc = (int) OUTSIDE; - ivf.rejflag = 0; - if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, &splitseg, &ivf, - cavpoints, cavfaces, cavshells, newtets, - crosstets, misfaces)) { - assert(0); - } - st_segref_count++; - if (steinerleft > 0) steinerleft--; + if (b->psc) { + assert(in->facetmarkerlist != NULL); + fmark = shellmark(splitsh) - 1; + fid = in->facetmarkerlist[fmark]; + if (pointtype(pa) == RIDGEVERTEX) { + in->getvertexparamonface(in->geomhandle, pointmark(pa), fid, auv); + } else if (pointtype(pa) == FREESEGVERTEX) { + eid = pointgeomtag(pa); // The Edge containing this Steiner point. + t = pointgeomuv(pa, 0); // The Steiner point's parameter on Edge. + in->getedgesteinerparamonface(in->geomhandle, eid, t, fid, auv); + } else if (pointtype(pa) == FREEFACETVERTEX) { + auv[0] = pointgeomuv(pa, 0); + auv[1] = pointgeomuv(pa, 1); + } else { + assert(0); + } + if (pointtype(pb) == RIDGEVERTEX) { + in->getvertexparamonface(in->geomhandle, pointmark(pb), fid, buv); + } else if (pointtype(pb) == FREESEGVERTEX) { + eid = pointgeomtag(pb); // The Edge containing this Steiner point. + t = pointgeomuv(pb, 0); // The Steiner point's parameter on Edge. + in->getedgesteinerparamonface(in->geomhandle, eid, t, fid, buv); + } else if (pointtype(pb) == FREEFACETVERTEX) { + buv[0] = pointgeomuv(pb, 0); + buv[1] = pointgeomuv(pb, 1); } else { assert(0); } + newuv[0] = 0.5 * (auv[0] + buv[0]); + newuv[1] = 0.5 * (auv[1] + buv[1]); + in->getsteineronface(in->geomhandle, fid, newuv, steinpt); + setpointgeomuv(steinpt, 0, newuv[0]); + setpointgeomuv(steinpt, 1, newuv[1]); + setpointgeomtag(steinpt, fid); } else { - st_facref_count++; - if (steinerleft > 0) steinerleft--; + for (i = 0; i < 3; i++) { + steinpt[i] = (pa[i] + pb[i] + pc[i]) / 3.0; + } } - while (subsegstack->objects > 0l) { - // seglist is used as a stack. - subsegstack->objects--; - paryseg = (face *) fastlookup(subsegstack, subsegstack->objects); - splitseg = *paryseg; + // Start searching it from 'recentet'. + searchtet = recenttet; + // Now insert the point p. The flags are chosen as follows: + // - boywat = 2, the current T is a CDT, + // - lawson = 2, do flip after inserting p, some existing segments + // and subfaces may be flipped, they are queued and + // and will be recovered. + // - rejflag = 1, reject p if it encroaches upon at least one segment, + // queue encroached segments. + ivf.iloc = (int) OUTSIDE; + ivf.bowywat = 2; + ivf.lawson = 2; + ivf.rejflag = 1; + ivf.chkencflag = 0; + ivf.sloc = (int) ONFACE; + ivf.sbowywat = 2; + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 0; + ivf.assignmeshsize = 0; + loc = insertvertex(steinpt, &searchtet, &splitsh, NULL, &ivf); + + assert((loc != OUTSIDE) && (loc != ONVERTEX)); + if (loc == NEARVERTEX) { + // The new point is either ON or VERY CLOSE to an existing point. + pa = point2ppt(steinpt); + printf(" !! Avoid to create a short edge (length = %g)\n", + distance(steinpt, pa)); + // Indicate it may be an input problem. + printf(" Short edge length bound is: %g. Tolerance is %g.\n", + b->minedgelength, b->epsilon); + terminatetetgen(4); + } + + if (loc == ENCSEGMENT) { + // Some segments are encroached and queued. + assert(encseglist->objects > 0l); + // Randomly pick one encroached segment to split. + s = randomnation(encseglist->objects); + paryseg = (face *) fastlookup(encseglist, s); + sseg = *paryseg; + // The new point p is the midpoint of this segment. + getsteinerptonsegment(&sseg, NULL, steinpt); + setpointtype(steinpt, FREESEGVERTEX); + encseglist->restart(); // Clear the queue. - // Check if this segment has been recovered. - sstpivot1(splitseg, searchtet); - if (searchtet.tet != NULL) continue; + // Start searching from an adjacent tetrahedron (containing the segment). + sstpivot1(sseg, searchtet); + spivot(sseg, splitsh); + // Insert the point p. The flags are chosen as follows: + // - boywat = 2, the current T is a CDT, + // - lawson = 2, do flip after inserting p, some existing segments + // and subfaces may be flipped, they are queued and + // and will be recovered. + // - rejflag = 0, always insert p, even it will cause some segments + // or subfaces missing, queue missing boundaries. + ivf.iloc = (int) ONEDGE; + ivf.bowywat = 2; + ivf.lawson = 2; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONEDGE; + ivf.sbowywat = 2; + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 0; + ivf.assignmeshsize = 0; + loc = insertvertex(steinpt, &searchtet, &splitsh, &sseg, &ivf); - // Search the segment. - dir = scoutsegment(sorg(splitseg), sdest(splitseg), &searchtet, &refpt, - NULL); - if (dir == SHAREEDGE) { - // Found this segment, insert it. - if (!issubseg(searchtet)) { - // Let the segment remember an adjacent tet. - sstbond1(splitseg, searchtet); - // Bond the segment to all tets containing it. - spintet = searchtet; - do { - tssbond1(spintet, splitseg); - fnextself(spintet); - } while (spintet.tet != searchtet.tet); - } else { - // Collision! Should not happen. - assert(0); - } - } else { - if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { - // Split the segment. - // Create a new point. - makepoint(&steinpt, FREESEGVERTEX); - //setpointtype(newpt, FREESEGVERTEX); - getsteinerptonsegment(&splitseg, refpt, steinpt); - ivf.iloc = (int) OUTSIDE; - ivf.rejflag = 0; - if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, &splitseg, &ivf, - cavpoints, cavfaces, cavshells, newtets, - crosstets, misfaces)) { - assert(0); - } - st_segref_count++; - if (steinerleft > 0) steinerleft--; - } else { - // Maybe a PLC problem. - assert(0); - } + if (loc == NEARVERTEX) { + // The new point is either ON or VERY CLOSE to an existing point. + pa = point2ppt(steinpt); + printf(" !! Avoid to create a short edge (length = %g)\n", + distance(steinpt, pa)); + // Indicate it may be an input problem. + printf(" Short edge length bound is: %g. Tolerance is %g.\n", + b->minedgelength, b->epsilon); + terminatetetgen(4); } - } // while - if (b->verbose > 2) { - printf(" Added %ld Steiner points.\n", points->items - baknum); + st_segref_count++; + } else { + st_facref_count++; + } + if (steinerleft > 0) steinerleft--; + + // Do flip to recover Delaunayniess. + lawsonflip3d(steinpt, 2, 0, 0, 0); + + // Some vertices may be queued, recover them. + if (subvertstack->objects > 0l) { + assert(0); //delaunizevertices(); + } + + // Some subsegments may be queued, recover them. + if (subsegstack->objects > 0l) { + delaunizesegments(); } } /////////////////////////////////////////////////////////////////////////////// // // -// constrainedfacets() Recover constrained facets in a CDT. // -// // -// All unrecovered subfaces are queued in 'subfacestack'. // +// constrainedfacets() Recover subfaces saved in 'subfacestack'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -17716,14 +19290,16 @@ void tetgenmesh::constrainedfacets() arraypool *tg_topshells, *tg_botshells, *tg_facfaces; arraypool *tg_toppoints, *tg_botpoints; arraypool *tg_missingshs, *tg_missingshbds, *tg_missingshverts; - triface searchtet, neightet, crossedge; - face searchsh, *parysh, *parysh1; - face *paryseg; - point *parypt; + + triface searchtet, neightet; + face searchsh, neighsh, *parysh; + face checkseg, *paryseg; + point refpt, *parypt; enum interresult dir; + bool success; int facetcount; - int success; - int t1ver; + //int bakhullsize; + int crossflag; int i, j; // Initialize arrays. @@ -17741,184 +19317,245 @@ void tetgenmesh::constrainedfacets() tg_missingshs = new arraypool(sizeof(face), 10); tg_missingshbds = new arraypool(sizeof(face), 10); tg_missingshverts = new arraypool(sizeof(point), 8); + // This is a global array used by refineregion(). - encseglist = new arraypool(sizeof(face), 4); + encseglist = new arraypool(sizeof(face), 4); facetcount = 0; + // Loop until 'subfacstack' is empty. while (subfacstack->objects > 0l) { - subfacstack->objects--; parysh = (face *) fastlookup(subfacstack, subfacstack->objects); searchsh = *parysh; - if (searchsh.sh[3] == NULL) continue; // It is dead. - if (isshtet(searchsh)) continue; // It is recovered. + if (searchsh.sh[3] == NULL) continue; // Skip a dead subface. - // Collect all unrecovered subfaces which are co-facet. - smarktest(searchsh); - tg_facfaces->newindex((void **) &parysh); - *parysh = searchsh; - for (i = 0; i < tg_facfaces->objects; i++) { - parysh = (face *) fastlookup(tg_facfaces, i); - for (j = 0; j < 3; j++) { - if (!isshsubseg(*parysh)) { - spivot(*parysh, searchsh); - assert(searchsh.sh != NULL); // SELF_CHECK - if (!smarktested(searchsh)) { - if (!isshtet(searchsh)) { - smarktest(searchsh); - tg_facfaces->newindex((void **) &parysh1); - *parysh1 = searchsh; + stpivot(searchsh, neightet); + if (neightet.tet == NULL) { + // Find an unrecovered subface. + smarktest(searchsh); + tg_facfaces->newindex((void **) &parysh); + *parysh = searchsh; + // Collect all non-recovered subfaces of the same facet. + for (i = 0; i < tg_facfaces->objects; i++) { + searchsh = * (face *) fastlookup(tg_facfaces, i); + for (j = 0; j < 3; j++) { + sspivot(searchsh, checkseg); + if (checkseg.sh == NULL) { + spivot(searchsh, neighsh); + assert(neighsh.sh != NULL); // SELF_CHECK + if (!smarktested(neighsh)) { + // It may be already recovered. + stpivot(neighsh, neightet); + if (neightet.tet == NULL) { + smarktest(neighsh); + tg_facfaces->newindex((void **) &parysh); + *parysh = neighsh; + } } } - } - senextself(*parysh); - } // j - } // i - // Have found all facet subfaces. Unmark them. - for (i = 0; i < tg_facfaces->objects; i++) { - parysh = (face *) fastlookup(tg_facfaces, i); - sunmarktest(*parysh); - } - - if (b->verbose > 2) { - printf(" Recovering facet #%d: %ld subfaces.\n", facetcount + 1, - tg_facfaces->objects); - } - facetcount++; - - while (tg_facfaces->objects > 0l) { - - tg_facfaces->objects--; - parysh = (face *) fastlookup(tg_facfaces, tg_facfaces->objects); - searchsh = *parysh; - - if (searchsh.sh[3] == NULL) continue; // It is dead. - if (isshtet(searchsh)) continue; // It is recovered. - - searchtet.tet = NULL; - dir = scoutsubface(&searchsh, &searchtet); - if (dir == SHAREFACE) continue; // The subface is inserted. - - // The subface is missing. Form the missing region. - // Re-use 'tg_crosstets' for 'adjtets'. - formregion(&searchsh, tg_missingshs, tg_missingshbds, tg_missingshverts); - - if (scoutcrossedge(searchtet, tg_missingshbds, tg_missingshs)) { - // Save this crossing edge, will be used by fillcavity(). - crossedge = searchtet; - // Form a cavity of crossing tets. - success = formcavity(&searchtet, tg_missingshs, tg_crosstets, - tg_topfaces, tg_botfaces, tg_toppoints, - tg_botpoints); - if (success) { - if (!b->flipinsert) { - // Tetrahedralize the top part. Re-use 'tg_midfaces'. - delaunizecavity(tg_toppoints, tg_topfaces, tg_topshells, - tg_topnewtets, tg_crosstets, tg_midfaces); - // Tetrahedralize the bottom part. Re-use 'tg_midfaces'. - delaunizecavity(tg_botpoints, tg_botfaces, tg_botshells, - tg_botnewtets, tg_crosstets, tg_midfaces); - // Fill the cavity with new tets. - success = fillcavity(tg_topshells, tg_botshells, tg_midfaces, - tg_missingshs, tg_topnewtets, tg_botnewtets, - &crossedge); - if (success) { - // Cavity is remeshed. Delete old tets and outer new tets. - carvecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); - } else { - restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets, - tg_missingshbds); - } - } else { - // Use the flip algorithm of Shewchuk to recover the subfaces. - flipinsertfacet(tg_crosstets, tg_toppoints, tg_botpoints, - tg_missingshverts); - // Recover the missing region. - success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells); - assert(success); - // Clear working lists. - tg_crosstets->restart(); - tg_topfaces->restart(); - tg_botfaces->restart(); - tg_toppoints->restart(); - tg_botpoints->restart(); - } // b->flipinsert + senextself(searchsh); + } // j + } // i + // Have found all facet subfaces (vertices). Uninfect them. + for (i = 0; i < tg_facfaces->objects; i++) { + parysh = (face *) fastlookup(tg_facfaces, i); + sunmarktest(*parysh); + } - if (success) { - // Recover interior subfaces. - for (i = 0; i < caveencshlist->objects; i++) { - parysh = (face *) fastlookup(caveencshlist, i); - dir = scoutsubface(parysh, &searchtet); - if (dir != SHAREFACE) { - // Add this face at the end of the list, so it will be - // processed immediately. - tg_facfaces->newindex((void **) &parysh1); - *parysh1 = *parysh; - } - } - caveencshlist->restart(); - // Recover interior segments. This should always be recovered. - for (i = 0; i < caveencseglist->objects; i++) { - paryseg = (face *) fastlookup(caveencseglist, i); - dir = scoutsegment(sorg(*paryseg),sdest(*paryseg),&searchtet, - NULL, NULL); - assert(dir == SHAREEDGE); - // Insert this segment. - if (!issubseg(searchtet)) { - // Let the segment remember an adjacent tet. - sstbond1(*paryseg, searchtet); - // Bond the segment to all tets containing it. - neightet = searchtet; - do { - tssbond1(neightet, *paryseg); - fnextself(neightet); - } while (neightet.tet != searchtet.tet); + if (b->verbose > 2) { + printf(" Recover facet #%d: %ld subfaces.\n", facetcount + 1, + tg_facfaces->objects); + } + facetcount++; + + // Loop until 'tg_facfaces' is empty. + while (tg_facfaces->objects > 0l) { + // Get the last subface of this array. + tg_facfaces->objects--; + parysh = (face *) fastlookup(tg_facfaces, tg_facfaces->objects); + searchsh = *parysh; + + if (searchsh.sh[3] == NULL) continue; // Skip a dead subface. + + stpivot(searchsh, neightet); + if (neightet.tet != NULL) continue; // Not a missing subface. + + // Insert the subface. + searchtet.tet = NULL; + dir = scoutsubface(&searchsh, &searchtet); + if (dir == SHAREFACE) continue; // The subface is inserted. + if (dir == COLLISIONFACE) continue; // The subface is removed. + + // The subface is missing. Form the missing region. + // Re-use 'tg_crosstets' for 'adjtets'. + formmissingregion(&searchsh, tg_missingshs, tg_missingshbds, + tg_missingshverts, tg_crosstets); + + // Search for a crossing edge (tg_crosstets is cleared). + crossflag = scoutcrossedge(searchtet, tg_crosstets, tg_missingshs); + + if (crossflag == 1) { + // Recover subfaces by local retetrahedralization. + // Form a cavity of crossing tets. + if (formcavity(&searchtet, tg_missingshs, tg_crosstets, tg_topfaces, + tg_botfaces, tg_toppoints, tg_botpoints)) { + if (!b->flipinsert) { + // Tetrahedralize the top part. Re-use 'tg_midfaces'. + delaunizecavity(tg_toppoints, tg_topfaces, tg_topshells, + tg_topnewtets, tg_crosstets, tg_midfaces); + // Tetrahedralize the bottom part. Re-use 'tg_midfaces'. + delaunizecavity(tg_botpoints, tg_botfaces, tg_botshells, + tg_botnewtets, tg_crosstets, tg_midfaces); + // Fill the cavity with new tets. + success = fillcavity(tg_topshells, tg_botshells, tg_midfaces, + tg_missingshs); + if (success) { + // Cavity is remeshed. Delete old tets and outer new tets. + carvecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); + // Insert the missing region into cavity. + j = 0; // FOR DEBUG! Count the number of non-recovered faces. + for (i = 0; i < tg_missingshs->objects; i++) { + searchsh = * (face *) fastlookup(tg_missingshs, i); + searchtet.tet = NULL; + dir = scoutsubface(&searchsh, &searchtet); + assert(dir != COLLISIONFACE); // SELF_CHECK + if (dir != SHAREFACE) { + // A subface is missing. This is possible that the subface + // is not actually a constrained Delaunay face in T. + // Add this face at the end of the list, so it will be + // processed immediately. This is necessary because we + // have created some non-locally Delaunay face (by the + // remesh of the cavity). We have to insert the subfaces + // to make these face constrained Delaunay. + tg_facfaces->newindex((void **) &parysh); + *parysh = searchsh; + j++; // FOR DEBUG! + } + } // i + // Recover interior subfaces. + for (i = 0; i < caveencshlist->objects; i++) { + searchsh = * (face *) fastlookup(caveencshlist, i); + searchtet.tet = NULL; + dir = scoutsubface(&searchsh, &searchtet); + assert(dir != COLLISIONFACE); // SELF_CHECK + if (dir != SHAREFACE) { + // The subface is missing. This is possible that the subface + // is removed by the enlargement of the cavity. It has to + // be recovered. + // Add this face at the end of the list, so it will be + // processed immediately. We have to insert the subfaces + // to make these face constrained Delaunay. + tg_facfaces->newindex((void **) &parysh); + *parysh = searchsh; + j++; // FOR DEBUG! + } + } // i + // Recover interior segments. This should always be recovered. + for (i = 0; i < caveencseglist->objects; i++) { + paryseg = (face *) fastlookup(caveencseglist, i); + searchtet.tet = NULL; + refpt = NULL; + dir = scoutsegment(sorg(*paryseg),sdest(*paryseg),&searchtet, + &refpt, NULL); + assert(dir == SHAREEDGE); + // Insert this segment. + tsspivot1(searchtet, checkseg); // SELF_CHECK + if (checkseg.sh == NULL) { + // Let the segment remember an adjacent tet. + sstbond1(*paryseg, searchtet); + // Bond the segment to all tets containing it. + neightet = searchtet; + do { + tssbond1(neightet, *paryseg); + fnextself(neightet); + } while (neightet.tet != searchtet.tet); + } else { + // Collision! Should not happen. + assert(0); + } + } // i + caveencshlist->restart(); + caveencseglist->restart(); } else { - // Collision! Should not happen. - assert(0); + // Restore old tets and delete new tets. + restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); + // Set a handle for searching subface. + //recentsh = searchsh; } + } else { + // Use the flip algorithm of Shewchuk to recover the subfaces. + flipinsertfacet(tg_crosstets, tg_toppoints, tg_botpoints, + tg_missingshverts); + // Check the missing subfaces again. + j = 0; // FOR DEBUG! Count the number of non-recovered faces. + for (i = 0; i < tg_missingshs->objects; i++) { + searchsh = * (face *) fastlookup(tg_missingshs, i); + searchtet.tet = NULL; + dir = scoutsubface(&searchsh, &searchtet); + assert(dir != COLLISIONFACE); // SELF_CHECK + if (dir != SHAREFACE) { + // A subface is missing. This is possible that the subface + // is not actually a constrained Delaunay face in T. + // Add this face at the end of the list, so it will be + // processed immediately. This is necessary because we + // have created some non-locally Delaunay face (by the + // remesh of the cavity). We have to insert the subfaces + // to make these face constrained Delaunay. + tg_facfaces->newindex((void **) &parysh); + *parysh = searchsh; + j++; // FOR DEBUG! + } + } // i + // Clear working lists. + tg_crosstets->restart(); + tg_topfaces->restart(); + tg_botfaces->restart(); + tg_toppoints->restart(); + tg_botpoints->restart(); + success = true; + } // if (b->flipinsert) + } else { + // Formcavity failed. + success = false; + } + } else { //if (crossflag == 0) { + // Recover subfaces by retriangulate the surface mesh. + // Re-use tg_topshells for newshs. + success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells); + if (success) { + // Region is remeshed. Delete old subfaces (in tg_missingshs). + for (i = 0; i < tg_missingshs->objects; i++) { + parysh = (face *) fastlookup(tg_missingshs, i); + shellfacedealloc(subfaces, parysh->sh); } - caveencseglist->restart(); - } // success - remesh cavity - } // success - form cavity - } else { - // Recover subfaces by retriangulate the surface mesh. - // Re-use tg_topshells for newshs. - success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells); - } + tg_topshells->restart(); + } else { + // Search a handle for searching tetrahedron. + recenttet = searchtet; + } + } - // Unmarktest all points of the missing region. - for (i = 0; i < tg_missingshverts->objects; i++) { - parypt = (point *) fastlookup(tg_missingshverts, i); - punmarktest(*parypt); - } - tg_missingshverts->restart(); - tg_missingshbds->restart(); - tg_missingshs->restart(); + // Unmarktest all points of the missing region. + for (i = 0; i < tg_missingshverts->objects; i++) { + parypt = (point *) fastlookup(tg_missingshverts, i); + punmarktest(*parypt); + } + tg_missingshverts->restart(); + tg_missingshbds->restart(); + tg_missingshs->restart(); - if (!success) { - // The missing region can not be recovered. Refine it. - refineregion(recentsh, tg_toppoints, tg_topfaces, tg_topshells, - tg_topnewtets, tg_crosstets, tg_midfaces); - // Clean the current list of facet subfaces. - // tg_facfaces->restart(); - } - } // while (tg_facfaces->objects) - - } // while ((subfacstack->objects) - - // Accumulate the dynamic memory. - totalworkmemory += (tg_crosstets->totalmemory + tg_topnewtets->totalmemory + - tg_botnewtets->totalmemory + tg_topfaces->totalmemory + - tg_botfaces->totalmemory + tg_midfaces->totalmemory + - tg_toppoints->totalmemory + tg_botpoints->totalmemory + - tg_facfaces->totalmemory + tg_topshells->totalmemory + - tg_botshells->totalmemory + tg_missingshs->totalmemory + - tg_missingshbds->totalmemory + - tg_missingshverts->totalmemory + - encseglist->totalmemory); + if (!success) { + // The missing region can not be recovered. Refine it. + refineregion(); + // Clean the current list of facet subfaces. + //tg_facfaces->restart(); + } + } // while (tg_facfaces->objects > 0l) + + } // if (neightet.tet == NULL) + } // while (subfacstack->objects > 0l) // Delete arrays. delete tg_crosstets; @@ -17953,14 +19590,15 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) // Statistics. long bakfillregioncount; long bakcavitycount, bakcavityexpcount; - long bakseg_ref_count; if (!b->quiet) { printf("Constrained Delaunay...\n"); } - // Identify acute vertex for PLC inputs. - markacutevertices(); + //if (!b->psc) { + // Only identify acute vertex for PLC inputs. + markacutevertices(); + //} if (b->verbose) { printf(" Delaunizing segments.\n"); @@ -17968,25 +19606,37 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) checksubsegflag = 1; - // Put all segments into the list (in random order). - subsegs->traversalinit(); - for (i = 0; i < subsegs->items; i++) { - s = randomnation(i + 1); - // Move the s-th seg to the i-th. - subsegstack->newindex((void **) &paryseg); - *paryseg = * (face *) fastlookup(subsegstack, s); - // Put i-th seg to be the s-th. - searchseg.sh = shellfacetraverse(subsegs); - //sinfect(searchseg); // Only save it once. - paryseg = (face *) fastlookup(subsegstack, s); - *paryseg = searchseg; + // Put all segments into the list. + if (0) { //if (b->order == 4) { // '-o4' option (for debug) + // In sequential order. + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + searchseg.sh = shellfacetraverse(subsegs); + //sinfect(searchseg); // Only save it once. + subsegstack->newindex((void **) &paryseg); + *paryseg = searchseg; + } + } else { + // In random order. + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + s = randomnation(i + 1); + // Move the s-th seg to the i-th. + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(subsegstack, s); + // Put i-th seg to be the s-th. + searchseg.sh = shellfacetraverse(subsegs); + //sinfect(searchseg); // Only save it once. + paryseg = (face *) fastlookup(subsegstack, s); + *paryseg = searchseg; + } } // Recover non-Delaunay segments. delaunizesegments(); if (b->verbose) { - printf(" Inserted %ld Steiner points.\n", st_segref_count); + printf(" %ld Steiner points.\n", st_segref_count); } tv = clock(); @@ -17995,13 +19645,17 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) printf(" Constraining facets.\n"); } + if (b->flipinsert) { + // Clear the counters. + flip23count = flip32count = flip44count = 0l; + } + // Subfaces will be introduced. checksubfaceflag = 1; bakfillregioncount = fillregioncount; bakcavitycount = cavitycount; bakcavityexpcount = cavityexpcount; - bakseg_ref_count = st_segref_count; // Randomly order the subfaces. subfaces->traversalinit(); @@ -18030,10 +19684,10 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) } printf(".\n"); } - if (st_segref_count + st_facref_count - bakseg_ref_count > 0) { + if (st_segref_count + st_facref_count > 0) { printf(" Inserted %ld (%ld, %ld) refine points.\n", - st_segref_count + st_facref_count - bakseg_ref_count, - st_segref_count - bakseg_ref_count, st_facref_count); + st_segref_count + st_facref_count, st_segref_count, + st_facref_count); } } } @@ -18057,6 +19711,10 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) // the flip face, and [d,e] is the flip edge. NOTE: 'pc' may be 'dummypoint',// // other points must not be 'dummypoint'. // // // +// For avoiding mutually flipping, once a crossing face is flipped, it will // +// never be re-created again. Also, we never create a face or edge which is // +// intersecting the current recovering segment or subface. // +// // /////////////////////////////////////////////////////////////////////////////// int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, @@ -18064,23 +19722,45 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, int level, int edgepivot, flipconstraints* fc) { + int rejflag; + int i; + point tmppts[3]; + REAL normal[3], area, len; + REAL ori1, ori2; + REAL abovept[3]; + enum interresult dir; int types[2], poss[4]; int intflag; - int rejflag = 0; - int i; + + rejflag = 0; if (fc->seg[0] != NULL) { // A constraining edge is given (e.g., for edge recovery). if (fliptype == 1) { // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c]. - tmppts[0] = pa; - tmppts[1] = pb; - tmppts[2] = pc; - for (i = 0; i < 3 && !rejflag; i++) { - if (tmppts[i] != dummypoint) { - // Test if the face [e,d,#] intersects the edge. + if (pc != dummypoint) { + // Do not flip if the newly created faces intersect this edge in + // their interiors. + tmppts[0] = pa; + tmppts[1] = pb; + tmppts[2] = pc; + if (0) { + // Make sure that the three new faces are not degenerate. + for (i = 0; i < 3 && !rejflag; i++) { + facenormal(pe, pd, tmppts[i], normal, 1, &len); + area = sqrt(DOT(normal, normal)); + if (area == 0) { + rejflag = 1; // A degenerate face. + } else { + if ((area / (len * len)) < b->epsilon) { + rejflag = 1; // A nearly degenerate face. + } + } + } // i + } + for (i = 0; i < 3 && !rejflag; i++) { intflag = tri_edge_test(pe, pd, tmppts[i], fc->seg[0], fc->seg[1], NULL, 1, types, poss); if (intflag == 2) { @@ -18095,7 +19775,12 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, // Since [e,d] is the newly created edge. Reject this flip. rejflag = 1; } - } + } else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || + (dir == TOUCHFACE)) { + assert(0); // Check this case. + } + } // dir } else if (intflag == 4) { // They may intersect at either a point or a line segment. dir = (enum interresult) types[0]; @@ -18105,32 +19790,177 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, // Since [e,d] is the newly created edge. Reject this flip. rejflag = 1; } + } else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || + (dir == TOUCHFACE)) { + assert(0); // Check this case. + } } + } // if (intflag == 4) + } // i + } else { // pc == dummypoint + // Do not flip if the new hull edge [e,d] will intersect this edge + // in its interior. + // Comment: Here we actually need a 3D edge-edge test. + // We only do test if the edge in 'fc' is coplanar with the plane + // containing a,b,e,and d. + // Choose a better triangle [a,b,e] or [a,b,d]. + facenormal(pa, pb, pe, normal, 1, &len); + area = sqrt(DOT(normal, normal)); + facenormal(pa, pb, pd, normal, 1, &len); + len = sqrt(DOT(normal, normal)); // Re-use len as area. + if (area > len) { + // Choose [a,b,e] + ori1 = orient3d(pa, pb, pe, fc->seg[0]); + ori2 = orient3d(pa, pb, pe, fc->seg[1]); + } else { + // Choose [a,b,d] + ori1 = orient3d(pa, pb, pd, fc->seg[0]); + ori2 = orient3d(pa, pb, pd, fc->seg[1]); + } + if ((ori1 == 0) && (ori2 == 0)) { + calculateabovepoint4(pa, pb, pe, pd); + for (i = 0; i < 3; i++) { + abovept[i] = dummypoint[i]; } - } // if (tmppts[0] != dummypoint) - } // i + intflag = tri_edge_test(pe, pd, abovept, fc->seg[0], fc->seg[1], + NULL, 1, types, poss); + if (intflag == 2) { + dir = (enum interresult) types[0]; + assert(dir != ACROSSFACE); + if (dir == ACROSSEDGE) { + if (poss[0] == 0) { + // The interior of [e,d] intersect the segment. + // Since [e,d] is the newly created edge. Reject this flip. + rejflag = 1; + } + } else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || + (dir == TOUCHFACE)) { + assert(0); // Check this case. + } + } + } else if (intflag == 4) { + // [e,d,abovept] is coplanar with the constraining edge 'fc'. + // This is poissible if the edge in 'fc' is just the edge [e,d] + // (SHAREEDGE) or they share a common vertex (SHAREVEER) + dir = (enum interresult) types[0]; + if (dir == ACROSSEDGE) { + // This case can only happen if [e,d] is coplanar with 'fc'. + assert(0); // Not possible. + } else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || + (dir == TOUCHFACE)) { + assert(0); // Check this case. + } + } + } + } + } // if (pc == dummypoint) } else if (fliptype == 2) { // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c] if (pc != dummypoint) { - // Check if the new face [a,b,c] intersect the edge in its interior. - intflag = tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL, - 1, types, poss); - if (intflag == 2) { - // They intersect at a single point. - dir = (enum interresult) types[0]; - if (dir == ACROSSFACE) { - // The interior of [a,b,c] intersect the segment. - rejflag = 1; // Do not flip. + if (0) { + // Make sure that [a,b,c] is a valid face. + facenormal(pa, pb, pc, normal, 1, &len); + area = sqrt(DOT(normal, normal)); + if (area == 0) { + rejflag = 1; // A degenerate face. + } else { + if ((area / (len * len)) < b->epsilon) { + rejflag = 1; // A nearly degenerate face. + } } - } else if (intflag == 4) { - // [a,b,c] is coplanar with the edge. - dir = (enum interresult) types[0]; - if (dir == ACROSSEDGE) { - // The boundary of [a,b,c] intersect the segment. - rejflag = 1; // Do not flip. + } + if (!rejflag) { + // Check if the new face [a,b,c] intersect the edge in its interior. + intflag = tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL, + 1, types, poss); + if (intflag == 2) { + // They intersect at a single point. + dir = (enum interresult) types[0]; + if (dir == ACROSSFACE) { + // The interior of [a,b,c] intersect the segment. + rejflag = 1; // Do not flip. + } else if (dir == ACROSSEDGE) { + // This case is possible since we allow a previous 2-to-3 flip + // even it will create a degenerate tet at edge [a,b]. + } else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || + (dir == TOUCHFACE)) { + assert(0); // Check this case. + } + } + } else if (intflag == 4) { + // [a,b,c] is coplanar with the edge. + dir = (enum interresult) types[0]; + if (dir == ACROSSEDGE) { + // The boundary of [a,b,c] intersect the segment. + // An example is found in case 'camila.poly', during the recovery + // of segment [151, 161] (at linklevel = 2). See: 2011-06-10-a. + rejflag = 1; // Do not flip. + } } + } // if (!relflag) + } else { // pc == dummypoint + // The flip 3-to-2 will replace [e,d] with a new hull edge [a,b]. + // Only do flip if [a,b] does not intersect the edge of 'fc'. + // Comment: Here we acutually need a 3D edge-edge intersection test. + // We only do test if the edge in 'fc' is coplanar with the plane + // containing a,b,e, and d. + // Choose a better triangle [a,b,e] or [a,b,d]. + facenormal(pa, pb, pe, normal, 1, &len); + area = sqrt(DOT(normal, normal)); + facenormal(pa, pb, pd, normal, 1, &len); + len = sqrt(DOT(normal, normal)); // Re-use len as area. + if (area > len) { + // Choose [a,b,e] + ori1 = orient3d(pa, pb, pe, fc->seg[0]); + ori2 = orient3d(pa, pb, pe, fc->seg[1]); + } else { + // Choose [a,b,d] + ori1 = orient3d(pa, pb, pd, fc->seg[0]); + ori2 = orient3d(pa, pb, pd, fc->seg[1]); } - } // if (pc != dummypoint) + if ((ori1 == 0) && (ori2 == 0)) { + // The edge in 'fc' is coplanar with the plane containing [a,b,e,d]. + calculateabovepoint4(pa, pb, pe, pd); + for (i = 0; i < 3; i++) { + abovept[i] = dummypoint[i]; + } + intflag = tri_edge_test(pa, pb, abovept, fc->seg[0], fc->seg[1], + NULL, 1, types, poss); + if (intflag == 2) { + dir = (enum interresult) types[0]; + assert(dir != ACROSSFACE); + if (dir == ACROSSEDGE) { + assert(0); // Check this case. + rejflag = 1; // Do not flip. + } else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || + (dir == TOUCHFACE)) { + assert(0); // Check this case. + } + } + } else if (intflag == 4) { + // The edge 'fc' is coplanar with [a,b,abovept]. + // This is poissible if the edge in 'fc' is just the edge [a,b] + // (SHAREEDGE) or they share a common vertex (SHAREVEER) + dir = (enum interresult) types[0]; + if (dir == ACROSSEDGE) { + // This case can only happen if [a,b] is coplanar with 'fc'. + assert(0); // Not possible. + } else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || + (dir == TOUCHFACE)) { + assert(0); // Check this case. + } + } + } + } // if (ori1 == 0 && ori2 == 0) + } + } else { + assert(0); // An unknown flip type. } } // if (fc->seg[0] != NULL) @@ -18148,7 +19978,11 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, rejflag = 1; } else if (dir == ACROSSEDGE) { rejflag = 1; - } + } else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || (dir == TOUCHFACE)) { + assert(0); // Check this case. + } + } } else if (intflag == 4) { // The edge [e,d] is coplanar with the face. // There may be two intersections. @@ -18289,7 +20123,7 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, // // // removeedgebyflips() Remove an edge by flips. // // // -// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed. // +// 'flipedge' is a non-convex or flat edge [a,b,#,#]. // // // // The return value is a positive integer, it indicates whether the edge is // // removed or not. A value "2" means the edge is removed, othereise, the // @@ -18301,45 +20135,77 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) { triface *abtets, spintet; - int t1ver; + face checkseg, *paryseg; + int counter; // DEBUG int n, nn, i; + // Bakup valuses (for free spaces in flipnm_post()). + int bakunflip = fc->unflip; + int bakcollectnewtets = fc->collectnewtets; + + + if (b->verbose > 2) { + printf(" Removing edge (%d, %d)\n", pointmark(org(*flipedge)), + pointmark(dest(*flipedge))); + } + + //if (fc != NULL) { + fc->clearcounters(); + //} + if (checksubsegflag) { // Do not flip a segment. - if (issubseg(*flipedge)) { - if (fc->collectencsegflag) { - face checkseg, *paryseg; - tsspivot1(*flipedge, checkseg); - if (!sinfected(checkseg)) { - // Queue this segment in list. - sinfect(checkseg); - caveencseglist->newindex((void **) &paryseg); - *paryseg = checkseg; + tsspivot1(*flipedge, checkseg); + if (checkseg.sh != NULL) { + if (b->verbose > 2) { + printf(" Can't flip a segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + //if (fc != NULL) { + fc->encsegcount++; + if (fc->collectencsegflag) { + if (!sinfected(checkseg)) { + // Queue this segment in list. + sinfect(checkseg); + caveencseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } } - } + //} return 0; } } // Count the number of tets at edge [a,b]. n = 0; + counter = 0; // Sum of star counters; spintet = *flipedge; + i = 0; while (1) { - n++; + counter += elemcounter(spintet); + i++; fnextself(spintet); if (spintet.tet == flipedge->tet) break; } - //assert(n >= 3); - if (n < 3) { + //assert(i >= 3); + if (i < 3) { // It is only possible when the mesh contains inverted tetrahedra. assert(checkinverttetflag); // Since "return 2" means success, we return 0. return 0; } + assert(counter == 0); // SELF_CHECK + n = i; + flipstarcount++; + // Record the maximum star size. + if (n > maxflipstarsize) { + maxflipstarsize = n; + } if ((b->flipstarsize > 0) && (n > b->flipstarsize)) { - // The star size exceeds the limit. + // The star size exceeds the given limit (-YY__). + skpflipstarcount++; return 0; // Do not flip it. } @@ -18350,7 +20216,8 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) i = 0; while (1) { abtets[i] = spintet; - setelemcounter(abtets[i], 1); + //marktest(abtets[i]); // Marktest it (in Star(ab)). + setelemcounter(abtets[i], 1); i++; fnextself(spintet); if (spintet.tet == flipedge->tet) break; @@ -18361,33 +20228,45 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) nn = flipnm(abtets, n, 0, 0, fc); - if (nn > 2) { + if (nn == 2) { + // Edge is flipped. + if (b->verbose > 2) { + printf(" Edge is removed.\n"); + } + } else { // Edge is not flipped. Unmarktest the remaining tets in Star(ab). for (i = 0; i < nn; i++) { + //assert(marktested(abtets[i])); + //unmarktest(abtets[i]); + assert(elemcounter(abtets[i]) == 1); setelemcounter(abtets[i], 0); } + if (b->verbose > 2) { + printf(" Edge is not removed. n(%d), nn(%d).\n", n, nn); + } // Restore the input edge (needed by Lawson's flip). *flipedge = abtets[0]; } // Release the temporary allocated spaces. // NOTE: fc->unflip must be 0. - int bakunflip = fc->unflip; fc->unflip = 0; + fc->collectnewtets = 0; + flipnm_post(abtets, n, nn, 0, fc); + fc->unflip = bakunflip; + fc->collectnewtets = bakcollectnewtets; delete [] abtets; - return nn; + return nn; //return nn == 2; } /////////////////////////////////////////////////////////////////////////////// // // // removefacebyflips() Remove a face by flips. // // // -// Return 1 if the face is removed. Otherwise, return 0. // -// // // ASSUMPTIONS: // // - 'flipface' must not be a hull face. // // // @@ -18395,25 +20274,45 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) { + triface fliptets[3], flipedge; + face checksh; + point pa, pb, pc, pd, pe; + point pts[3]; + enum interresult dir; + int types[2], poss[4], pos; + REAL ori; + int reducflag, rejflag; + int i, j; + if (checksubfaceflag) { - if (issubface(*flipface)) { + tspivot(*flipface, checksh); + if (checksh.sh != NULL) { + if (b->verbose > 2) { + printf(" Can't flip a subface.\n"); + } return 0; } } - triface fliptets[3], flipedge; - point pa, pb, pc, pd, pe; - REAL ori; - int reducflag = 0; - fliptets[0] = *flipface; fsym(*flipface, fliptets[1]); + + assert(!ishulltet(fliptets[0])); + assert(!ishulltet(fliptets[1])); + pa = org(fliptets[0]); pb = dest(fliptets[0]); pc = apex(fliptets[0]); pd = oppo(fliptets[0]); pe = oppo(fliptets[1]); + if (b->verbose > 2) { + printf(" Removing face (%d, %d, %d) -- %d, %d\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); + } + + reducflag = 0; + ori = orient3d(pa, pb, pd, pe); if (ori > 0) { ori = orient3d(pb, pc, pd, pe); @@ -18434,12 +20333,109 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) if (reducflag) { // A 2-to-3 flip is found. - flip23(fliptets, 0, fc); - return 1; - } else { - // Try to flip the selected edge of this face. - if (removeedgebyflips(&flipedge, fc) == 2) { + rejflag = 0; + if (fc != NULL) { + //rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, fc); + } + if (!rejflag) { + flip23(fliptets, 0, 0, 0); + if (b->verbose > 2) { + printf(" Face is removed by a 2-to-3 flip.\n"); + } return 1; + } else { + if (b->verbose > 2) { + printf(" -- Reject a 2-to-3 flip at face (%d, %d, %d)\n", + pointmark(pa), pointmark(pb), pointmark(pc)); + } + if (fc != NULL) { + fc->rejf23count++; + } + } + } else { + if (0) { + // Try to flip one of the edges of this face. + pts[0] = org(flipedge); + pts[1] = dest(flipedge); + pts[2] = apex(flipedge); + // Start from the recorded locally non-convex edge 'flipedge'. + for (i = 0; i < 3; i++) { + if (removeedgebyflips(&flipedge, fc) == 2) { + if (b->verbose > 2) { + printf(" Face is removed by removing edge (%d, %d).\n", + pointmark(pts[i]), pointmark(pts[(i+1)%3])); + } + return 1; + } + // The 'flipedge' may be dead in above call. + point2tetorg(pts[i], flipedge); + finddirection(&flipedge, pts[(i+1)%3], 1); + if (dest(flipedge) != pts[(i+1)%3]) { + if (b->verbose > 2) { + printf(" Face is removed during removing edge (%d, %d).\n", + pointmark(pts[i]), pointmark(pts[(i+1)%3])); + } + return 1; + } + } // i + } else { + if (0) { //if (fc->seg[0] != NULL) { + // The face is intersecting a segment. + // Find the edge shared by three corssing faces. + // We assume that the 'flipface' is facing to 'fc->seg[0]'. It is the + // case when the function is called from 'recoveredgebyflips()'. + // DEBUG BEGIN + pa = org(*flipface); + pb = dest(*flipface); + pc = apex(*flipface); + ori = orient3d(pa, pb, pc, fc->seg[0]); + assert(ori < 0); + // DEBUG END + fsym(*flipface, flipedge); + pc = oppo(flipedge); + for (i = 0; i < 3; i++) { + pa = org(flipedge); + pb = dest(flipedge); + if (tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL, 1, + types, poss)) { + dir = (enum interresult) types[0]; + if (dir == ACROSSFACE) { + break; // Found the crossing face. + } else if (dir == ACROSSEDGE) { + // Found an edge intersects the segment. + esymself(flipedge); + pos = poss[0]; + for (j = 0; j < pos; j++) { + eprevself(flipedge); + } + // Flip this edge. + break; + } else if (dir == SHAREVERT) { + // We have reached the endpoint of the segment. + assert(pc == fc->seg[1]); + // The face is not flippable. + return 0; + } else { + assert(0); // Not possible. + } + } + enextself(flipedge); + } + assert(i < 3); + } else { + if (b->verbose > 2) { + pa = org(flipedge); + pb = dest(flipedge); + } + } + // Try to flip the selected edge of this face. + if (removeedgebyflips(&flipedge, fc) == 2) { + if (b->verbose > 2) { + printf(" Face is removed by removing edge (%d, %d).\n", + pointmark(pa), pointmark(pb)); + } + return 1; + } } } @@ -18454,7 +20450,7 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) // If the edge is recovered, 'searchtet' returns a tet containing the edge. // // // // This edge may intersect a set of faces and edges in the mesh. All these // -// faces or edges are needed to be removed. // +// faces or edges are needed to be flipped. // // // // If the parameter 'fullsearch' is set, it tries to flip any face or edge // // that intersects the recovering edge. Otherwise, only the face or edge // @@ -18465,24 +20461,38 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) int tetgenmesh::recoveredgebyflips(point startpt, point endpt, triface* searchtet, int fullsearch) { + triface neightet, spintet; // *abtets; + point pa, pb, pc, pd; + badface bakface; + enum interresult dir, dir1; flipconstraints fc; - enum interresult dir; + int types[2], poss[4], pos = 0; + int success; + //int n, endi; + int i, j; //, k; + + if (b->verbose > 2) { + printf(" Recovering edge (%d, %d)\n", pointmark(startpt), + pointmark(endpt)); + } + fc.seg[0] = startpt; fc.seg[1] = endpt; - fc.checkflipeligibility = 1; // The mainloop of the edge reocvery. while (1) { // Loop I // Search the edge from 'startpt'. point2tetorg(startpt, *searchtet); - dir = finddirection(searchtet, endpt); + assert(org(*searchtet) == startpt); // SELF_CHECK + dir = finddirection(searchtet, endpt, 1); if (dir == ACROSSVERT) { if (dest(*searchtet) == endpt) { return 1; // Edge is recovered. } else { - terminatetetgen(3); // // It may be a PLC problem. + // A PLC problem, or there is a Steiner point. + terminatetetgen(3); //assert(0); // Debug } } @@ -18501,178 +20511,175 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, continue; } } else { - terminatetetgen(3); // It may be a PLC problem. + terminatetetgen(3); //assert(0); // A PLC problem. } // The edge is missing. if (fullsearch) { - // Try to flip one of the faces/edges which intersects the edge. - triface neightet, spintet; - point pa, pb, pc, pd; - badface bakface; - enum interresult dir1; - int types[2], poss[4], pos = 0; - int success = 0; - int t1ver; - int i, j; - - // Loop through the sequence of intersecting faces/edges from - // 'startpt' to 'endpt'. - point2tetorg(startpt, *searchtet); - dir = finddirection(searchtet, endpt); - //assert(dir != ACROSSVERT); - - // Go to the face/edge intersecting the searching edge. - enextesymself(*searchtet); // Go to the opposite face. - // This face/edge has been tried in previous step. - - while (1) { // Loop I-I - - // Find the next intersecting face/edge. - fsymself(*searchtet); - if (dir == ACROSSFACE) { - neightet = *searchtet; - j = (neightet.ver & 3); // j is the current face number. - for (i = j + 1; i < j + 4; i++) { - neightet.ver = (i % 4); - pa = org(neightet); - pb = dest(neightet); - pc = apex(neightet); - pd = oppo(neightet); // The above point. - if (tri_edge_test(pa,pb,pc,startpt,endpt, pd, 1, types, poss)) { - dir = (enum interresult) types[0]; - pos = poss[0]; - break; - } else { - dir = DISJOINT; - pos = 0; - } - } // i - // There must be an intersection face/edge. - assert(dir != DISJOINT); // SELF_CHECK - } else { - assert(dir == ACROSSEDGE); - while (1) { // Loop I-I-I - // Check the two opposite faces (of the edge) in 'searchtet'. - for (i = 0; i < 2; i++) { - if (i == 0) { - enextesym(*searchtet, neightet); - } else { - eprevesym(*searchtet, neightet); - } + + if (1) { + // Try to flip one of the faces/edges which intersects the edge. + success = 0; + + // Loop through the sequence of intersecting faces/edges from + // 'startpt' to 'endpt'. + point2tetorg(startpt, *searchtet); + assert(org(*searchtet) == startpt); // SELF_CHECK + dir = finddirection(searchtet, endpt, 1); + assert(dir != ACROSSVERT); + + // Go to the face/edge intersecting the searching edge. + enextesymself(*searchtet); // Go to the opposite face. + // This face/edge has been tried in previous step. + + while (1) { // Loop I-I + + // Find the next intersecting face/edge. + fsymself(*searchtet); + if (dir == ACROSSFACE) { + neightet = *searchtet; + j = (neightet.ver & 3); // j is the current face number. + for (i = j + 1; i < j + 4; i++) { + neightet.ver = (i % 4); pa = org(neightet); pb = dest(neightet); pc = apex(neightet); pd = oppo(neightet); // The above point. - if (tri_edge_test(pa,pb,pc,startpt,endpt,pd,1, types, poss)) { + if (tri_edge_test(pa,pb,pc,startpt,endpt, pd, 1, types, poss)) { dir = (enum interresult) types[0]; pos = poss[0]; - break; // for loop + break; } else { dir = DISJOINT; pos = 0; } - } // i - if (dir != DISJOINT) { - // Find an intersection face/edge. - break; // Loop I-I-I - } - // No intersection. Rotate to the next tet at the edge. - fnextself(*searchtet); - } // while (1) // Loop I-I-I - } + } // i + // There must be an intersection face/edge. + assert(dir != DISJOINT); // SELF_CHECK + } else { + assert(dir == ACROSSEDGE); + while (1) { // Loop I-I-I + // Check the two opposite faces (of the edge) in 'searchtet'. + for (i = 0; i < 2; i++) { + if (i == 0) { + enextesym(*searchtet, neightet); + } else { + eprevesym(*searchtet, neightet); + } + pa = org(neightet); + pb = dest(neightet); + pc = apex(neightet); + pd = oppo(neightet); // The above point. + if (tri_edge_test(pa,pb,pc,startpt,endpt,pd,1, types, poss)) { + dir = (enum interresult) types[0]; + pos = poss[0]; + break; // for loop + } else { + dir = DISJOINT; + pos = 0; + } + } // i + if (dir != DISJOINT) { + // Find an intersection face/edge. + break; // Loop I-I-I + } + // No intersection. Rotate to the next tet at the edge. + fnextself(*searchtet); + } // while (1) // Loop I-I-I + } - // Adjust to the intersecting edge/vertex. - for (i = 0; i < pos; i++) { - enextself(neightet); - } + // Adjust to the intersecting edge/vertex. + for (i = 0; i < pos; i++) { + enextself(neightet); + } - if (dir == SHAREVERT) { - // Check if we have reached the 'endpt'. - pd = org(neightet); - if (pd == endpt) { - // Failed to recover the edge. - break; // Loop I-I - } else { - // We need to further check this case. It might be a PLC problem - // or a Steiner point that was added at a bad location. - assert(0); + if (dir == SHAREVERT) { + // Check if we have reached the 'endpt'. + pd = org(neightet); + if (pd == endpt) { + // Failed to recover the edge. + break; // Loop I-I + } else { + // We need to further check this case. It might be a PLC problem + // or a Steiner point that was added at a bad location. + assert(0); + } } - } - // The next to be flipped face/edge. - *searchtet = neightet; + // The next to be flipped face/edge. + *searchtet = neightet; - // Bakup this face (tetrahedron). - bakface.forg = org(*searchtet); - bakface.fdest = dest(*searchtet); - bakface.fapex = apex(*searchtet); - bakface.foppo = oppo(*searchtet); + // Bakup this face (tetrahedron). + bakface.forg = org(*searchtet); + bakface.fdest = dest(*searchtet); + bakface.fapex = apex(*searchtet); + bakface.foppo = oppo(*searchtet); - // Try to flip this intersecting face/edge. - if (dir == ACROSSFACE) { - if (removefacebyflips(searchtet, &fc)) { - success = 1; - break; // Loop I-I - } - } else if (dir == ACROSSEDGE) { - if (removeedgebyflips(searchtet, &fc) == 2) { - success = 1; - break; // Loop I-I - } - } else { - assert(0); // A PLC problem. - } - - // The face/edge is not flipped. - if ((searchtet->tet == NULL) || - (org(*searchtet) != bakface.forg) || - (dest(*searchtet) != bakface.fdest) || - (apex(*searchtet) != bakface.fapex) || - (oppo(*searchtet) != bakface.foppo)) { - // 'searchtet' was flipped. We must restore it. - point2tetorg(bakface.forg, *searchtet); - dir1 = finddirection(searchtet, bakface.fdest); - if (dir1 == ACROSSVERT) { - assert(dest(*searchtet) == bakface.fdest); - spintet = *searchtet; - while (1) { - if (apex(spintet) == bakface.fapex) { - // Found the face. - *searchtet = spintet; - break; - } - fnextself(spintet); - if (spintet.tet == searchtet->tet) { - searchtet->tet = NULL; - break; // Not find. - } - } // while (1) - if (searchtet->tet != NULL) { - if (oppo(*searchtet) != bakface.foppo) { - fsymself(*searchtet); - if (oppo(*searchtet) != bakface.foppo) { - assert(0); // Check this case. + // Try to flip this intersecting face/edge. + if (dir == ACROSSFACE) { + if (removefacebyflips(searchtet, &fc)) { + success = 1; + break; // Loop I-I + } + } else if (dir == ACROSSEDGE) { + if (removeedgebyflips(searchtet, &fc) == 2) { + success = 1; + break; // Loop I-I + } + } else { + assert(0); // A PLC problem. + } + + // The face/edge is not flipped. + if ((searchtet->tet == NULL) || + (org(*searchtet) != bakface.forg) || + (dest(*searchtet) != bakface.fdest) || + (apex(*searchtet) != bakface.fapex) || + (oppo(*searchtet) != bakface.foppo)) { + // 'searchtet' was flipped. We must restore it. + point2tetorg(bakface.forg, *searchtet); + dir1 = finddirection(searchtet, bakface.fdest, 1); + if (dir1 == ACROSSVERT) { + assert(dest(*searchtet) == bakface.fdest); + spintet = *searchtet; + while (1) { + if (apex(spintet) == bakface.fapex) { + // Found the face. + *searchtet = spintet; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) { searchtet->tet = NULL; break; // Not find. } + } // while (1) + if (searchtet->tet != NULL) { + if (oppo(*searchtet) != bakface.foppo) { + fsymself(*searchtet); + if (oppo(*searchtet) != bakface.foppo) { + assert(0); // Check this case. + searchtet->tet = NULL; + break; // Not find. + } + } } + } else { + searchtet->tet = NULL; // Not find. + } + if (searchtet->tet == NULL) { + success = 0; // This face/edge has been destroed. + break; // Loop I-I } - } else { - searchtet->tet = NULL; // Not find. - } - if (searchtet->tet == NULL) { - success = 0; // This face/edge has been destroed. - break; // Loop I-I } - } - } // while (1) // Loop I-I + } // while (1) // Loop I-I - if (success) { - // One of intersecting faces/edges is flipped. - continue; - } + if (success) { + // One of intersecting faces/edges is flipped. + continue; + } + } // if (0) } // if (fullsearch) @@ -18681,6 +20688,7 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, } // while (1) // Loop I + // The edge is not recovered. return 0; } @@ -18693,8 +20701,24 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, // tets are [a,b,p0,p1], [a,b,p1,p2], ..., [a,b,p_(n-2),p_(n-1)]. Moreover, // // the edge [p0,p_(n-1)] intersects all of the tets in 'abtets'. A special // // case is that the edge [p0,p_(n-1)] is coplanar with the edge [a,b]. // -// Such set of tets arises when we want to recover an edge from 'p0' to 'p_ // -// (n-1)', and the number of tets at [a,b] can not be reduced by any flip. // +// // +// These set of tets arises when we want to recover an edge from 'p0' to 'p_ // +// (n-1)', and the recoveredgenbyflips() routine fails. Note that the outer // +// faces of these tets defines a polyhedron P, and the set of tets gives the // +// ONLY tetrahedralization of P. If we replace the two boundary faces [a,b, // +// p0] and [a,b,p_(n-1)] by [p0,p_(n-1),a] and [p0,p_(n-1),b], and call the // +// new polyhedron P'. In this routine, we think P' is not tetrahedralizable // +// (since the routine recoveredgenbyflips() fails!! AND no flip is possible // +// on any of these edges: [a,p1], [b,p1], [a,p2], [b,p2], ..., [a,p_(n-2)], // +// and [b,p_(n-1)]). If n = 3, P' is just the famous Schoenhardt polyhedron. // +// For n > 3, we call P' the generalized Schoenhardt polyhedron, it includes // +// the Bagemihl's polyhedron as a special case. // +// // +// It is obvious that P is a Star-shaped polyhedron. The mid-point of [a,b] // +// is visible by all boundary faces of P, push it slightly inside P does not // +// change the visibilty. Indeed every interior point of [a,b] is visible by // +// the boundary faces of P. // +// // // // /////////////////////////////////////////////////////////////////////////////// @@ -18703,15 +20727,23 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, { triface worktet, *parytet; triface faketet1, faketet2; - point pc, pd, steinerpt; + point pa, pb, pc, pd; + point p1, p2, p3; + point steinerpt; insertvertexflags ivf; optparameters opm; REAL vcd[3], sampt[3], smtpt[3]; REAL maxminvol = 0.0, minvol = 0.0, ori; int success, maxidx = 0; + int loc; int it, i; + if (b->verbose > 2) { + printf(" Find a Steiner in Schoenhardt polyhedron (n=%d).\n", n); + } + pa = org(abtets[0]); + pb = dest(abtets[0]); pc = apex(abtets[0]); // pc = p0 pd = oppo(abtets[n-1]); // pd = p_(n-1) @@ -18720,10 +20752,12 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, // initialize the list of 2n boundary faces. for (i = 0; i < n; i++) { - edestoppo(abtets[i], worktet); // [p_i,p_i+1,a] + eprev(abtets[i], worktet); + esymself(worktet); // [a,p_i,p_i+1]. cavetetlist->newindex((void **) &parytet); *parytet = worktet; - eorgoppo(abtets[i], worktet); // [p_i+1,p_i,b] + enext(abtets[i], worktet); + esymself(worktet); // [p_i,b,p_i+1]. cavetetlist->newindex((void **) &parytet); *parytet = worktet; } @@ -18738,7 +20772,10 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, } for (i = 0; i < cavetetlist->objects; i++) { parytet = (triface *) fastlookup(cavetetlist, i); - ori = orient3d(dest(*parytet), org(*parytet), apex(*parytet), sampt); + p1 = org(*parytet); + p2 = dest(*parytet); + p3 = apex(*parytet); + ori = orient3d(p2, p1, p3, sampt); if (i == 0) { minvol = ori; } else { @@ -18757,6 +20794,10 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, } // it if (maxminvol <= 0) { + if (b->verbose > 2) { + printf(" Unable to find a initial point: maxminvol = %g\n", + maxminvol); + } cavetetlist->restart(); return 0; } @@ -18768,11 +20809,11 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, // Create two faked tets to hold the two non-existing boundary faces: // [d,c,a] and [c,d,b]. maketetrahedron(&faketet1); - setvertices(faketet1, pd, pc, org(abtets[0]), dummypoint); + setvertices(faketet1, pd, pc, pa, dummypoint); cavetetlist->newindex((void **) &parytet); *parytet = faketet1; maketetrahedron(&faketet2); - setvertices(faketet2, pc, pd, dest(abtets[0]), dummypoint); + setvertices(faketet2, pc, pd, pb, dummypoint); cavetetlist->newindex((void **) &parytet); *parytet = faketet2; @@ -18805,6 +20846,9 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, cavetetlist->restart(); if (!success) { + if (b->verbose > 2) { + printf(" Unable to relocate the initial point.\n"); + } return 0; } @@ -18821,23 +20865,28 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, } worktet = abtets[0]; // No need point location. ivf.iloc = (int) INSTAR; + ivf.bowywat = 0; // Do not use Bowyer-Watson algorithm. + ivf.lawson = 0; // Do not flip. + ivf.rejflag = 0; ivf.chkencflag = chkencflag; - ivf.assignmeshsize = b->metric; - if (ivf.assignmeshsize) { - // Search the tet containing 'steinerpt' for size interpolation. - locate(steinerpt, &(abtets[0]), 0); - worktet = abtets[0]; - } + ivf.sloc = 0; + ivf.sbowywat = 0; + ivf.splitbdflag = 0; + ivf.validflag = 0; + ivf.respectbdflag = 0; + ivf.assignmeshsize = 0; // Insert the new point into the tetrahedralization T. // Note that T is convex (nonconvex = 0). - if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) { + loc = insertvertex(steinerpt, &worktet, NULL, NULL, &ivf); + + if (loc == (int) INSTAR) { // The vertex has been inserted. - st_volref_count++; + st_volref_count++; //st_inpoly_count++; if (steinerleft > 0) steinerleft--; return 1; } else { - // Not inserted. + // The Steiner point is too close to an existing vertex. Reject it. pointdealloc(steinerpt); return 0; } @@ -18853,14 +20902,16 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) { triface *abtets, searchtet, spintet; face splitsh; + face checkseg; face *paryseg; point startpt, endpt; point pa, pb, pd, steinerpt, *parypt; enum interresult dir; insertvertexflags ivf; int types[2], poss[4]; + REAL ip[3], u; int n, endi, success; - int t1ver; + int loc; int i; startpt = sorg(*misseg); @@ -18872,7 +20923,12 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) // Try to recover the edge by adding Steiner points. point2tetorg(startpt, searchtet); - dir = finddirection(&searchtet, endpt); + assert(org(searchtet) == startpt); // SELF_CHECK + dir = finddirection(&searchtet, endpt, 1); + assert(dir != ACROSSVERT); + + // Get the first intersecting face/edge. + assert(!ishulltet(searchtet)); enextself(searchtet); //assert(apex(searchtet) == startpt); @@ -18880,8 +20936,13 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) // The segment is crossing at least 3 faces. Find the common edge of // the first 3 crossing faces. esymself(searchtet); + assert(oppo(searchtet) == startpt); fsym(searchtet, spintet); pd = oppo(spintet); + if (pd == endpt) { + // This should be possible. + assert(0); // Debug this case. + } for (i = 0; i < 3; i++) { pa = org(spintet); pb = dest(spintet); @@ -18897,9 +20958,8 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) } else { assert(dir == ACROSSEDGE); // PLC check. - if (issubseg(searchtet)) { - face checkseg; - tsspivot1(searchtet, checkseg); + tsspivot1(searchtet, checkseg); + if (checkseg.sh != NULL) { printf("Found two segments intersect each other.\n"); pa = farsorg(*misseg); pb = farsdest(*misseg); @@ -18936,6 +20996,8 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) abtets[i] = spintet; fnextself(spintet); } + assert(apex(abtets[0]) == startpt); + assert(apex(abtets[endi]) == endpt); success = 0; @@ -18992,15 +21054,11 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) printf(" Splitting segment (%d, %d)\n", pointmark(startpt), pointmark(endpt)); } - steinerpt = NULL; - // The following code is skipped. - if ((endi == -1) && 0) { + if (endi == -1) { // Let the missing segment be [a,b]. Let the edge [c,d] whose star contains // a and intersects [a,b]. We choose the Steiner point at the intersection // of the edge star of [c,d] and [a,b] (not a). - REAL ip[3], u; - if (dir == ACROSSFACE) { pa = org(searchtet); pb = dest(searchtet); @@ -19020,6 +21078,17 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) } assert(n >= 3); assert(endi != -1); + + // 'abtets' is only for debug purpose. + abtets = new triface[endi]; + spintet = searchtet; + for (i = 0; i < endi; i++) { + abtets[i] = spintet; + fnextself(spintet); + } + searchtet = abtets[endi - 1]; + esymself(searchtet); // The exit face of [startpt, endpt]. + delete [] abtets; } else { assert(dir == ACROSSEDGE); assert(apex(searchtet) == startpt); @@ -19062,9 +21131,11 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = b->metric; - if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { - if (ivf.iloc == (int) NEARVERTEX) { + ivf.assignmeshsize = 0; + loc = insertvertex(steinerpt, &searchtet, &splitsh, misseg, &ivf); + + if (loc != ivf.iloc) { + if (loc == (int) NEARVERTEX) { // The vertex is rejected. Too close to an existing vertex. pointdealloc(steinerpt); steinerpt = NULL; @@ -19072,7 +21143,9 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) assert(0); // Unknown case. } } - } // if ((endi == -1) && 0) + } else { // if (endi > 0) + steinerpt = NULL; + } if (steinerpt == NULL) { // Split the segment at its midpoint. @@ -19094,10 +21167,11 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = b->metric; - if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { - assert(0); - } + ivf.assignmeshsize = 0; + loc = insertvertex(steinerpt, &searchtet, &splitsh, misseg, &ivf); + + assert(loc != (int) ONVERTEX); + assert(loc != (int) NEARVERTEX); } // if (endi > 0) // Save this Steiner point (for removal). @@ -19115,11 +21189,17 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) // // // recoversegments() Recover all segments. // // // -// All segments need to be recovered are in 'subsegstack'. // +// All segments need to be recovered are in 'subsegstack' (Q). They will be // +// be recovered one by one. // +// // +// Each segment is first tried to be recovered by a sequence of flips which // +// removes faces intersecting this segment. However, it is not always possi- // +// ble to recover it by only this way. Then, Steiner points will be added to // +// help the recovery of it by flips. // // // -// This routine first tries to recover each segment by only using flips. If // -// no flip is possible, and the flag 'steinerflag' is set, it then tries to // -// insert Steiner points near or in the segment. // +// If 'steinerflag' is set, Steiner points will be added if a segment is not // +// able to recovered by flips. Otherwise, the segment is not recovered, and // +// it is returned in 'misseglist'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -19127,12 +21207,11 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, int steinerflag) { triface searchtet, spintet; - face sseg, *paryseg; + face sseg, checkseg, *paryseg; point startpt, endpt; int success; - int t1ver; - long bak_inpoly_count = st_volref_count; - long bak_segref_count = st_segref_count; + + long bak_inpoly_count = st_volref_count; //st_inpoly_count; if (b->verbose > 1) { printf(" Recover segments [%s level = %2d] #: %ld.\n", @@ -19181,6 +21260,8 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, if (success) { // Segment is recovered. Insert it. + tsspivot1(searchtet, checkseg); // SELF_CHECK + assert(checkseg.sh == NULL); // Let the segment remember an adjacent tet. sstbond1(sseg, searchtet); // Bond the segment to all tets containing it. @@ -19219,10 +21300,6 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, printf(" Add %ld Steiner points in volume.\n", st_volref_count - bak_inpoly_count); } - if (st_segref_count > bak_segref_count) { - printf(" Add %ld Steiner points in segments.\n", - st_segref_count - bak_segref_count); - } } } @@ -19242,26 +21319,33 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, face *searchsh, triface* searchtet) { triface spintet, flipedge; + face checkseg; point pd, pe; enum interresult dir; flipconstraints fc; - int types[2], poss[4], intflag; int success, success1; - int t1ver; int i, j; + int intflag; + int types[2], poss[4]; + + if (b->verbose > 2) { + printf(" Recovering face (%d, %d, %d) by flips\n", pointmark(pa), + pointmark(pb), pointmark(pc)); + } + fc.fac[0] = pa; fc.fac[1] = pb; fc.fac[2] = pc; - fc.checkflipeligibility = 1; success = 0; for (i = 0; i < 3 && !success; i++) { while (1) { // Get a tet containing the edge [a,b]. point2tetorg(fc.fac[i], *searchtet); - dir = finddirection(searchtet, fc.fac[(i+1)%3]); + assert(org(*searchtet) == fc.fac[i]); // SELF_CHECK + dir = finddirection(searchtet, fc.fac[(i+1)%3], 1); //assert(dir == ACROSSVERT); assert(dest(*searchtet) == fc.fac[(i+1)%3]); // Search the face [a,b,c] @@ -19298,13 +21382,14 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, dir = (enum interresult) types[0]; if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { // Go to the edge [d,e]. - edestoppo(spintet, flipedge); // [d,e,a,b] + eprev(spintet, flipedge); + esymself(flipedge); + enextself(flipedge); // [d,e,a,b]. if (searchsh != NULL) { // Check if [e,d] is a segment. - if (issubseg(flipedge)) { - if (!b->quiet) { - face checkseg; - tsspivot1(flipedge, checkseg); + tsspivot1(flipedge, checkseg); + if (checkseg.sh != NULL) { + if (!b->quiet) { printf("Found a segment and a subface intersect.\n"); pd = farsorg(checkseg); pe = farsdest(checkseg); @@ -19312,16 +21397,16 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, pointmark(pe), shellmark(checkseg)); printf(" 2nd: [%d,%d,%d] %d\n", pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*searchsh)); - } + } terminatetetgen(3); - } + } } // Try to flip the edge [d,e]. success1 = (removeedgebyflips(&flipedge, &fc) == 2); } else { if (dir == TOUCHFACE) { point touchpt, *parypt; - if (poss[1] == 0) { + if (poss[0] == 0) { touchpt = pd; // pd is a coplanar vertex. } else { touchpt = pe; // pe is a coplanar vertex. @@ -19329,11 +21414,17 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, if (pointtype(touchpt) == FREEVOLVERTEX) { // A volume Steiner point was added in this subface. // Split this subface by this point. + if (b->verbose > 2) { + printf(" Shift volume Steiner point %d to facet.\n", + pointmark(touchpt)); + } face checksh, *parysh; int siloc = (int) ONFACE; int sbowat = 0; // Only split this subface. - setpointtype(touchpt, FREEFACETVERTEX); + sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat); + + setpointtype(touchpt, FREEFACETVERTEX); st_volref_count--; st_facref_count++; // Queue this vertex for removal. @@ -19347,6 +21438,12 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, spivot(*parysh, checksh); // The new subface [a, b, p]. // Do not recover a deleted new face (degenerated). if (checksh.sh[3] != NULL) { + if (b->verbose > 3) { + printf(" Queue new subface (%d, %d, %d).\n", + pointmark(sorg(checksh)), pointmark(sdest(checksh)), + pointmark(sapex(checksh))); + } + //sdissolve(checksh); // It has not been connected yet. subfacstack->newindex((void **) &parysh); *parysh = checksh; } @@ -19405,13 +21502,13 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) { triface searchtet, neightet, spintet; face searchsh, neighsh, neineish, *parysh; - face bdsegs[3]; + face bdsegs[3], checkseg; point startpt, endpt, apexpt, *parypt; point steinerpt; enum interresult dir; insertvertexflags ivf; int success; - int t1ver; + int loc; int i, j; if (b->verbose > 1) { @@ -19435,7 +21532,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) if (b->verbose > 2) { - printf(" Recover subface (%d, %d, %d).\n",pointmark(sorg(searchsh)), + printf(" Recover subface (%d, %d, %d).\n", pointmark(sorg(searchsh)), pointmark(sdest(searchsh)), pointmark(sapex(searchsh))); } @@ -19455,7 +21552,8 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) startpt = sorg(searchsh); endpt = sdest(searchsh); point2tetorg(startpt, searchtet); - dir = finddirection(&searchtet, endpt); + assert(org(searchtet) == startpt); // SELF_CHECK + dir = finddirection(&searchtet, endpt, 1); if (dir == ACROSSVERT) { if (dest(searchtet) == endpt) { success = 1; @@ -19475,8 +21573,13 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) } if (success) { // Insert a temporary segment to protect this edge. + if (b->verbose > 2) { + printf(" Insert a temp segment to protect edge [%d, %d].\n", + pointmark(startpt), pointmark(endpt)); + } makeshellface(subsegs, &(bdsegs[i])); setshvertices(bdsegs[i], startpt, endpt, NULL); + //setshellmark(bdsegs[i], -2); // It's a temporary segment. smarktest2(bdsegs[i]); // It's a temporary segment. // Insert this segment into surface mesh. ssbond(searchsh, bdsegs[i]); @@ -19485,6 +21588,8 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) ssbond(neighsh, bdsegs[i]); } // Insert this segment into tetrahedralization. + tsspivot1(searchtet, checkseg); // SELF_CHECK + assert(checkseg.sh == NULL); sstbond1(bdsegs[i], searchtet); // Bond the segment to all tets containing it. spintet = searchtet; @@ -19496,7 +21601,11 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) // An edge of this subface is missing. Can't recover this subface. // Delete any temporary segment that has been created. for (j = (i - 1); j >= 0; j--) { - if (smarktest2ed(bdsegs[j])) { + if (smarktest2ed(bdsegs[j])) { // if (shellmark(bdsegs[j]) == -2) { + if (b->verbose > 2) { + printf(" Remove a temp segment (%d, %d).\n", + pointmark(sorg(bdsegs[j])), pointmark(sdest(bdsegs[j]))); + } spivot(bdsegs[j], neineish); assert(neineish.sh != NULL); //if (neineish.sh != NULL) { @@ -19508,7 +21617,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) spivotself(neighsh); // SELF_CHECK assert(neighsh.sh == neineish.sh); } - //} + //} sstpivot1(bdsegs[j], searchtet); assert(searchtet.tet != NULL); //if (searchtet.tet != NULL) { @@ -19518,7 +21627,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) fnextself(spintet); if (spintet.tet == searchtet.tet) break; } - //} + //} shellfacedealloc(subsegs, bdsegs[j].sh); } } // j @@ -19544,10 +21653,10 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = b->metric; - if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { - assert(0); - } + ivf.assignmeshsize = 0; + loc = insertvertex(steinerpt, &searchtet, &searchsh, NULL, &ivf); + assert(loc != (int) OUTSIDE); + // Save this Steiner point (for removal). // Re-use the array 'subvertstack'. subvertstack->newindex((void **) &parypt); @@ -19572,7 +21681,11 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) // Delete any temporary segment that has been created. for (j = 0; j < 3; j++) { - if (smarktest2ed(bdsegs[j])) { + if (smarktest2ed(bdsegs[j])) { //if (shellmark(bdsegs[j]) == -2) { + if (b->verbose > 2) { + printf(" Remove a temp segment (%d, %d).\n", + pointmark(sorg(bdsegs[j])), pointmark(sdest(bdsegs[j]))); + } spivot(bdsegs[j], neineish); assert(neineish.sh != NULL); //if (neineish.sh != NULL) { @@ -19584,7 +21697,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) spivotself(neighsh); // SELF_CHECK assert(neighsh.sh == neineish.sh); } - //} + //} sstpivot1(bdsegs[j], neightet); assert(neightet.tet != NULL); //if (neightet.tet != NULL) { @@ -19594,7 +21707,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) fnextself(spintet); if (spintet.tet == neightet.tet) break; } - //} + //} shellfacedealloc(subsegs, bdsegs[j].sh); } } // j @@ -19630,10 +21743,10 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = b->metric; - if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { - assert(0); - } + ivf.assignmeshsize = 0; + loc = insertvertex(steinerpt, &searchtet, &searchsh, NULL, &ivf); + assert(loc != (int) OUTSIDE); + // Save this Steiner point (for removal). // Re-use the array 'subvertstack'. subvertstack->newindex((void **) &parypt); @@ -19649,6 +21762,11 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) if (!success) { if (misshlist != NULL) { + if (b->verbose > 2) { + printf(" Subface (%d, %d, %d) is missing.\n", + pointmark(sorg(searchsh)), pointmark(sdest(searchsh)), + pointmark(sapex(searchsh))); + } // Save this subface. misshlist->newindex((void **) &parysh); *parysh = searchsh; @@ -19682,27 +21800,32 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, { triface searchtet, neightet, *parytet; face checksh, *parysh; + //face checkseg; point pt, *parypt; int collectflag; - int t1ver; int i, j; + if (b->verbose > 2) { + printf(" Form the star of vertex %d.\n", pointmark(searchpt)); + } + point2tetorg(searchpt, searchtet); // Go to the opposite face (the link face) of the vertex. - enextesymself(searchtet); + enextself(searchtet); + esymself(searchtet); //assert(oppo(searchtet) == searchpt); infect(searchtet); // Collect this tet (link face). tetlist->newindex((void **) &parytet); *parytet = searchtet; if (vertlist != NULL) { // Collect three (link) vertices. - j = (searchtet.ver & 3); // The current vertex index. - for (i = 1; i < 4; i++) { - pt = (point) searchtet.tet[4 + ((j + i) % 4)]; + for (i = 0; i < 3; i++) { + pt = org(searchtet); pinfect(pt); vertlist->newindex((void **) &parypt); *parypt = pt; + enextself(searchtet); } } @@ -19717,13 +21840,14 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, shlist->newindex((void **) &parysh); *parysh = checksh; } - } + } // if (checksh.sh != NULL) if (!fullstar) { collectflag = 0; } } if (collectflag) { fsymself(neightet); // Goto the adj tet of this face. + assert(neightet.tet != NULL); esymself(neightet); // Goto the oppo face of this vertex. // assert(oppo(neightet) == searchpt); infect(neightet); // Collect this tet (link face). @@ -19747,6 +21871,7 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, for (j = 0; j < 2; j++) { collectflag = 1; enextself(searchtet); + //fnext(searchtet, neightet); esym(searchtet, neightet); tspivot(neightet, checksh); if (checksh.sh != NULL) { @@ -19764,6 +21889,7 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, } if (collectflag) { fsymself(neightet); + assert(neightet.tet != NULL); if (!infected(neightet)) { esymself(neightet); // Go to the face opposite to 'searchpt'. infect(neightet); @@ -19783,6 +21909,16 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, } // j } // i + if (b->verbose > 2) { + printf(" Collected %ld tets", tetlist->objects); + if (vertlist != NULL) { + printf(", %ld vertices", vertlist->objects); + } + if (shlist != NULL) { + printf(", %ld subfaces", shlist->objects); + } + printf(".\n"); + } // Uninfect the list of tets and vertices. for (i = 0; i < tetlist->objects; i++) { @@ -19846,13 +21982,13 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) // Search for the edge [e1, e2]. point2tetorg(e1, *tedge); - finddirection(tedge, e2); + finddirection(tedge, e2, 1); if (dest(*tedge) == e2) { return 1; } else { // Search for the edge [e2, e1]. point2tetorg(e2, *tedge); - finddirection(tedge, e1); + finddirection(tedge, e1, 1); if (dest(*tedge) == e1) { esymself(*tedge); return 1; @@ -19862,7 +21998,8 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) // Go to the link face of e1. point2tetorg(e1, searchtet); - enextesymself(searchtet); + enextself(searchtet); + esymself(searchtet); //assert(oppo(searchtet) == e1); assert(cavetetlist->objects == 0l); // It will re-use this list. @@ -19872,7 +22009,9 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) pt = apex(searchtet); if (pt == e2) { // Found. 'searchtet' is [#,#,e2,e1]. - eorgoppo(searchtet, *tedge); // [e1,e2,#,#]. + enext(searchtet, *tedge); + esymself(*tedge); + eprevself(*tedge); // [e1,e2,#,#]. return 1; } enextself(searchtet); @@ -19885,7 +22024,9 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) pt = apex(neightet); if (pt == e2) { // Found. 'neightet' is [#,#,e2,e1]. - eorgoppo(neightet, *tedge); // [e1,e2,#,#]. + enext(neightet, *tedge); + esymself(*tedge); + eprevself(*tedge); // [e1,e2,#,#]. return 1; } @@ -19910,7 +22051,9 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) pt = apex(neightet); if (pt == e2) { // Found. 'neightet' is [#,#,e2,e1]. - eorgoppo(neightet, *tedge); + enext(neightet, *tedge); + esymself(*tedge); + eprevself(*tedge); // [e1,e2,#,#]. done = 1; } else { infect(neightet); @@ -19942,6 +22085,7 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) { triface searchtet; + face checkseg; point *pendpt, *parypt; enum interresult dir; flipconstraints fc; @@ -19949,9 +22093,13 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) int count; int n, i, j; + if (b->verbose > 2) { + printf(" Initial edge degree = %ld.\n", endptlist->objects); + } + assert(endptlist->objects >= 4l); + // Reduce the number of edges. fc.remvert = startpt; - fc.checkflipeligibility = 1; while (1) { @@ -19973,12 +22121,13 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) } } else { point2tetorg(startpt, searchtet); - dir = finddirection(&searchtet, *pendpt); + dir = finddirection(&searchtet, *pendpt, 1); } if (dir == ACROSSVERT) { if (dest(searchtet) == *pendpt) { // Do not flip a segment. - if (!issubseg(searchtet)) { + tsspivot1(searchtet, checkseg); + if (checkseg.sh == NULL) { n = removeedgebyflips(&searchtet, &fc); if (n == 2) { reduceflag = 1; @@ -20009,6 +22158,10 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) } // while (1) + if (b->verbose > 2) { + printf(" Final edge degree = %ld.\n", endptlist->objects); + } + return (int) endptlist->objects; } @@ -20016,13 +22169,28 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) // // // removevertexbyflips() Remove a vertex by flips. // // // -// This routine attempts to remove the given vertex 'rempt' (p) from the // -// tetrahedralization (T) by a sequence of flips. // +// This routine attempts to remove the given vertex 'rempt' (p) from the cur-// +// rent tetrahedralization (T) by a sequence of elementary flips. // // // // The algorithm used here is a simple edge reduce method. Suppose there are // // n edges connected at p. We try to reduce the number of edges by flipping // // any edge (not a segment) that is connecting at p. // // // +// The original location of 'p' in the tetrahedralization without 'p' is ind-// +// icated by 'iloc'. 'searchtet' (t) is a tet in the current tetrahedralizat-// +// ion which contains 'p'. Depending on 'iloc', it means the followig: // +// - INTET: the origin of 't' is 'p'; // +// - ONFACE: the origin of 't' is 'p', the face of 't' was split by 'p'; // +// - ONEDGE: the origin of 't' is 'p', the edge of 't' was split by 'p'; // +// // +// If 'parentsh' (s) is given (not NULL), it indicates that 'p' is a Steiner // +// point on a facet, and 's' was a subface created by the insertion of 'p'. // +// 'iloc' must be either ONFACE or 'OEDGE'. The origin of 's' is 'p'. // +// // +// If 'parentseg' (seg) is given (not NULL), it indicated that 'p' is a // +// Steiner point on a segment, and 'seg' was a subsegment created by 'p'. // +// 'iloc' must be ONEDGE. The original of 'seg' is 'p'. // +// // // Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' // // can be successfully removed. // // // @@ -20035,12 +22203,10 @@ int tetgenmesh::removevertexbyflips(point steinerpt) face parentsh, spinsh, checksh; face leftseg, rightseg, checkseg; point lpt = NULL, rpt = NULL, apexpt, *parypt; - flipconstraints fc; enum verttype vt; enum locateresult loc; int valence, removeflag; int slawson; - int t1ver; int n, i; vt = pointtype(steinerpt); @@ -20066,6 +22232,23 @@ int tetgenmesh::removevertexbyflips(point steinerpt) } lpt = sorg(leftseg); rpt = sdest(rightseg); + if (b->verbose > 2) { + printf(" Removing Steiner point %d in segment (%d, %d).\n", + pointmark(steinerpt), pointmark(lpt), pointmark(rpt)); + + } + } else if (vt == FREEFACETVERTEX) { + if (b->verbose > 2) { + printf(" Removing Steiner point %d in facet.\n", + pointmark(steinerpt)); + + } + } else if (vt == FREEVOLVERTEX) { + if (b->verbose > 2) { + printf(" Removing Steiner point %d in volume.\n", + pointmark(steinerpt)); + + } } else { // It is not a Steiner point. return 0; @@ -20093,7 +22276,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // Inverted elements. getvertexstar(1, steinerpt, cavetetlist, NULL, NULL); if (cavetetlist->objects == 2) { - printf("to be continued...\n"); + printf("to be continued..."); assert(0); } else { assert(0); // Unknown cases. @@ -20216,7 +22399,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) if (!removeflag) { if (vt == FREESEGVERTEX) { - // Check if the edge [lpt, rpt] exists. + // Check if the edge [lpr, rpt] exists. if (getedge(lpt, rpt, &searchtet)) { // We have recovered this edge. Shift the vertex into the volume. // We can recover this edge if the subfaces are not recovered yet. @@ -20267,6 +22450,10 @@ int tetgenmesh::removevertexbyflips(point steinerpt) } // if (!removeflag) if (!removeflag) { + if (b->verbose > 2) { + printf(" Unable to remove Steiner point %d val(%d).\n", + pointmark(steinerpt), valence); + } return 0; } @@ -20323,8 +22510,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) eprevself(fliptets[3]); esymself(fliptets[3]); // [a,b,c,p]. // Remove p by a 4-to-1 flip. - //flip41(fliptets, 1, 0, 0); - flip41(fliptets, 1, &fc); + flip41(fliptets, 1, 0, 0); //recenttet = fliptets[0]; } else if (loc == ONFACE) { // Let the original two tets be [a,b,c,d] and [b,a,c,e]. And p is in @@ -20349,12 +22535,18 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // two new tets: [a,b,c,p] and [b,a,c,e]. The new tet [a,b,c,p] is // degenerate (has zero volume). It will be deleted in the followed // 4-to-1 flip. - //flip32(&(fliptets[3]), 1, 0, 0); - flip32(&(fliptets[3]), 1, &fc); + flip32(&(fliptets[3]), 1, 0, 0); + // DEBUG BEGIN + // fliptets[3] is [a,b,c,p], check it. + assert(org(fliptets[3]) == apex(fliptets[0])); // a + assert(dest(fliptets[3]) == apex(fliptets[1])); // b + assert(apex(fliptets[3]) == apex(fliptets[2])); // c + assert(oppo(fliptets[3]) == steinerpt); + // fliptets[4] is [b,a,c,e]. + // DEBUG END // Second do a 4-to-1 flip on [p,d,a,b],[p,d,b,c],[p,d,c,a],[a,b,c,p]. // This creates a new tet [a,b,c,d]. - //flip41(fliptets, 1, 0, 0); - flip41(fliptets, 1, &fc); + flip41(fliptets, 1, 0, 0); //recenttet = fliptets[0]; } else if (loc == ONEDGE) { // Let the original edge be [e,d] and p is in [e,d]. Assume there are n @@ -20398,8 +22590,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) enextself(wrktets[1]); // [p,p_0,e,p_1] esymself(wrktets[1]); // [p_0,p,p_1,e] eprevself(wrktets[1]); // [p_1,p_0,p,e] [1] - //flip23(wrktets, 1, 0, 0); - flip23(wrktets, 1, &fc); + flip23(wrktets, 1, 0, 0); // Save the new tet [e,d,p,p_0] (degenerated). fliptets[n] = wrktets[2]; // Save the new tet [e,d,p_0,p_1]. @@ -20422,8 +22613,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) wrktets[2] = fliptets[i]; // [p,d,p_i,p_i+1] eprevself(wrktets[2]); // [p_i,p,d,p_i+1] esymself(wrktets[2]); // [p,p_i,p_i+1,d] [2] - //flip32(wrktets, 1, 0, 0); - flip32(wrktets, 1, &fc); + flip32(wrktets, 1, 0, 0); // Save the new tet [e,d,p_i,p_i+1]. // FOR DEBUG ONLY fliptets[i] = wrktets[0]; // [d,e,p_i+1,p_i] // FOR DEBUG ONLY esymself(fliptets[i]); // [e,d,p_i,p_i+1] // FOR DEBUG ONLY @@ -20448,8 +22638,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) enextself(wrktets[2]); // [p_p_n-1,e,p_0] esymself(wrktets[2]); // [p_n-1,p,p_0,e] enextself(wrktets[2]); // [p,p_0,p_n-1,e] [2] - //flip41(wrktets, 1, 0, 0); - flip41(wrktets, 1, &fc); + flip41(wrktets, 1, 0, 0); // Save the new tet [e,d,p_n-1,p_0] // FOR DEBUG ONLY fliptets[n-1] = wrktets[0]; // [e,d,p_n-1,p_0] // FOR DEBUG ONLY //recenttet = fliptets[0]; @@ -20475,7 +22664,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // Insert the new segment. point2tetorg(lpt, searchtet); - finddirection(&searchtet, rpt); + finddirection(&searchtet, rpt, 1); assert(dest(searchtet) == rpt); sstbond1(rightseg, searchtet); spintet = searchtet; @@ -20544,14 +22733,10 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // // // suppresssteinerpoint() Suppress a Steiner point. // // // -// Remove a Steiner point 'p' from the segment or facet it lies on. It is // -// replaced by a set of volume Steiner points (which are at exactly the same // -// location of 'p') in each sector at the segment or facet. The created // -// volume Steiner points is returned in 'suppsteinerptlist'. // +// Remove a Steiner point 'p' from the segment it lies on. It is replaced by // +// a set of volume Steiner points in each sector at the segment. // // // -// NOTE: After this routine, the mesh contains degenerated or even inverted // -// tets. These created volume Steiner points will be either removed or // -// smoothed later in suppresssteinerpoints(). // +// The list of volume Steiner points is returned in 'suppsteinerptlist'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -20565,8 +22750,8 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) point lpt = NULL, rpt = NULL, newpt, *parypt; point pa, pb, pc; verttype vt; + long bak_supp_steiners; int slawson; - int t1ver; int i, j, k; vt = pointtype(steinerpt); @@ -20618,12 +22803,13 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) if (vt == FREESEGVERTEX) { // Check if this edge [lpt, rpt] already exists. if (getedge(lpt, rpt, &searchtet)) { + tsspivot1(searchtet, checkseg); // SELF_CHECK + assert(checkseg.sh == NULL); return 0; } } - // Count the number of created Steiner points. - long bak_supp_steiners = suppsteinerptlist->objects; + bak_supp_steiners = suppsteinerptlist->objects; if (vt == FREESEGVERTEX) { // Get all subfaces at the left segment [lpt, steinerpt]. @@ -20680,7 +22866,6 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) // Create a new vertex 'np'. makepoint(&newpt, FREEVOLVERTEX); st_volref_count++; - if (steinerleft > 0) steinerleft--; // Init 'np' at the same location of 'p'. for (j = 0; j < 3; j++) newpt[j] = steinerpt[j]; // Within the tet, replace 'p' by 'np'. @@ -20688,9 +22873,6 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) parytet = (triface *) fastlookup(cavetetlist, j); setoppo(*parytet, newpt); } // j - // Point to a parent tet. - parytet = (triface *) fastlookup(cavetetlist, 0); - setpoint2tet(newpt, (tetrahedron) (parytet->tet)); // Save the new Steiner point in list. suppsteinerptlist->newindex((void **) &parypt); *parypt = newpt; @@ -20711,7 +22893,7 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) } // i cavesegshlist->restart(); - // Remove p from the segment (or the facet). + // Remove p from the segment. slawson = 0; // Do not do flip afterword. if (vt == FREESEGVERTEX) { spivot(rightseg, parentsh); // 'rightseg' has p as its origin. @@ -20879,7 +23061,7 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) } else { // vt == FREEFACETVERTEX st_facref_count--; } - if (steinerleft > 0) steinerleft++; // We've removed a Steiner points. + if (steinerleft > 0) steinerleft++; if (b->verbose > 2) { printf(" Duplicated %ld Steiner points.\n", @@ -20904,7 +23086,7 @@ int tetgenmesh::suppresssteinerpoints() optparameters opm; REAL ori; int bak_fliplinklevel; - int remcount, smtcount, ivcount; + int remcount, smtcount; int count, nt; int i, j; @@ -20974,8 +23156,6 @@ int tetgenmesh::suppresssteinerpoints() if (suppsteinerptlist->objects == 0l) { b->fliplinklevel = bak_fliplinklevel; - // The mesh contain no inverted (or degenerrated) tets now. - checkinverttetflag = 0; return remcount; } @@ -20989,7 +23169,6 @@ int tetgenmesh::suppresssteinerpoints() while (1) { // Try to smooth volume Steiner points. count = 0; - ivcount = 0; // Clear the inverted count. for (i = 0; i < suppsteinerptlist->objects; i++) { parypt = (point *) fastlookup(suppsteinerptlist, i); @@ -21000,7 +23179,7 @@ int tetgenmesh::suppresssteinerpoints() for (j = 0; j < cavetetlist->objects; j++) { parytet = (triface *) fastlookup(cavetetlist, j); ppt = (point *) &(parytet->tet[4]); - ori = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]); + ori = orient3d(ppt[1], ppt[0], ppt[2], ppt[3]); if (j == 0) { opm.initval = ori; } else { @@ -21010,9 +23189,6 @@ int tetgenmesh::suppresssteinerpoints() if (smoothpoint(rempt, cavetetlist, 1, &opm)) { count++; } - if (opm.imprval <= 0.0) { - ivcount++; // The mesh stll contains inverted elements. - } cavetetlist->restart(); } } // i @@ -21030,6 +23206,9 @@ int tetgenmesh::suppresssteinerpoints() } } // while + // The mesh should not contain inverted (or degenrrated) tets now. + checkinverttetflag = 0; + if (b->verbose) { if (smtcount > 0) { printf(" Smoothed %d Steiner points.\n", smtcount); @@ -21038,44 +23217,6 @@ int tetgenmesh::suppresssteinerpoints() b->fliplinklevel = bak_fliplinklevel; - if (ivcount == 0) { - // The mesh should not contain inverted (or degenrrated) tets now. - checkinverttetflag = 0; - } else { - // Still have inserted elements. - for (i = 0; i < suppsteinerptlist->objects; i++) { - parypt = (point *) fastlookup(suppsteinerptlist, i); - rempt = *parypt; - if (pointtype(rempt) == FREEVOLVERTEX) { - getvertexstar(1, rempt, cavetetlist, NULL, NULL); - // Calculate the initial smallest volume (maybe zero or negative). - for (j = 0; j < cavetetlist->objects; j++) { - parytet = (triface *) fastlookup(cavetetlist, j); - ppt = (point *) &(parytet->tet[4]); - ori = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]); - if (j == 0) { - opm.initval = ori; - } else { - if (opm.initval > ori) opm.initval = ori; - } - } - cavetetlist->restart(); - if (opm.initval > 0) { - // This Steiner point is not degenerated. remove it from list. - // Move the last entry to fill the current one. - j = (int) (suppsteinerptlist->objects - 1); - plastpt = (point *) fastlookup(suppsteinerptlist, j); - *parypt = *plastpt; - suppsteinerptlist->objects--; - i--; - } - } - } - assert(suppsteinerptlist->objects > 0l); - printf("BUG Report! Inverted tets survived!\n"); - assert(0); - } - return smtcount; } @@ -21098,11 +23239,17 @@ void tetgenmesh::recoverboundary(clock_t& tv) // Counters. long bak_segref_count, bak_facref_count, bak_volref_count; + long bak_supp_count; if (!b->quiet) { printf("Recovering boundaries...\n"); } + if (b->verbose) { + printf(" Flip link level = %d\n", b->fliplinklevel); + } + + //markacutevertices(); if (b->verbose) { printf(" Recovering segments.\n"); @@ -21114,17 +23261,29 @@ void tetgenmesh::recoverboundary(clock_t& tv) misseglist = new arraypool(sizeof(face), 8); bdrysteinerptlist = new arraypool(sizeof(point), 8); - // In random order. - subsegs->traversalinit(); - for (i = 0; i < subsegs->items; i++) { - s = randomnation(i + 1); - // Move the s-th seg to the i-th. - subsegstack->newindex((void **) &paryseg); - *paryseg = * (face *) fastlookup(subsegstack, s); - // Put i-th seg to be the s-th. - searchseg.sh = shellfacetraverse(subsegs); - paryseg = (face *) fastlookup(subsegstack, s); - *paryseg = searchseg; + if (0) { //if (b->order == 4) { // '-o4' option (for debug) + // In sequential order. + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + searchseg.sh = shellfacetraverse(subsegs); + //sinfect(searchseg); // Only save it once. + subsegstack->newindex((void **) &paryseg); + *paryseg = searchseg; + } + } else { + // In random order. + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + s = randomnation(i + 1); + // Move the s-th seg to the i-th. + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(subsegstack, s); + // Put i-th seg to be the s-th. + searchseg.sh = shellfacetraverse(subsegs); + //sinfect(searchseg); // Only save it once. + paryseg = (face *) fastlookup(subsegstack, s); + *paryseg = searchseg; + } } // The init number of missing segments. @@ -21134,8 +23293,8 @@ void tetgenmesh::recoverboundary(clock_t& tv) autofliplinklevel = 1; // Init value. } - // First, trying to recover segments by only doing flips. while (1) { + recoversegments(misseglist, 0, 0); if (misseglist->objects > 0) { @@ -21174,7 +23333,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) } if (misseglist->objects > 0) { - // Second, trying to recover segments by doing more flips (fullsearch). + // There are missing segments. Increase the fliplevel. nit = 0; while (misseglist->objects > 0) { ms = misseglist->objects; @@ -21184,6 +23343,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); + // Recover the missing segments by doing more flips. recoversegments(misseglist, 1, 0); if (misseglist->objects < ms) { @@ -21203,8 +23363,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) } if (misseglist->objects > 0) { - // Third, trying to recover segments by doing more flips (fullsearch) - // and adding Steiner points in the volume. + // There are missing segments. Add Steiner points in volume. nit = 0; while (misseglist->objects > 0) { ms = misseglist->objects; @@ -21214,6 +23373,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); + // Recover the missing segments (with Steiner points). recoversegments(misseglist, 1, 1); if (misseglist->objects < ms) { @@ -21232,8 +23392,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) } if (misseglist->objects > 0) { - // Last, trying to recover segments by doing more flips (fullsearch), - // and adding Steiner points in the volume, and splitting segments. + // There are missing segments. Add Steiner points to split them. long bak_inpoly_count = st_volref_count; //st_inpoly_count; for (i = 0; i < misseglist->objects; i++) { subsegstack->newindex((void **) &paryseg); @@ -21241,6 +23400,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); + // Recover the missing segments (with Steiner points). recoversegments(misseglist, 1, 2); if (b->verbose) { @@ -21319,7 +23479,6 @@ void tetgenmesh::recoverboundary(clock_t& tv) while (1) { recoversubfaces(misshlist, 0); - if (misshlist->objects > 0) { if (b->fliplinklevel >= 0) { break; @@ -21395,7 +23554,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) if ((bdrysteinerptlist->objects > 0) && (b->nobisect_param > 0)) { // -Y1 - long bak_supp_count = suppsteinerptlist->objects; + bak_supp_count = 0; b->fliplinklevel = 100000; // Unlimited flip levels. do { // Suppress boundary Steiner points. @@ -21403,12 +23562,9 @@ void tetgenmesh::recoverboundary(clock_t& tv) parypt = (point *) fastlookup(bdrysteinerptlist, i); rempt = *parypt; suppressssteinerpoint(rempt); + bak_supp_count++; } bdrysteinerptlist->restart(); - // The mesh may contain inverted (or degenrrated) tets now. - if (suppsteinerptlist->objects > bak_supp_count) { - checkinverttetflag = 1; - } // There may be subfaces need to be recover. if (subfacstack->objects > 0l) { recoversubfaces(NULL, 1); @@ -21416,15 +23572,13 @@ void tetgenmesh::recoverboundary(clock_t& tv) } while (bdrysteinerptlist->objects > 0); if (b->verbose) { printf(" Suppressed %ld Steiner points from boundary.\n", - suppsteinerptlist->objects - bak_supp_count); + bak_supp_count); } + // The mesh contains inverted (or degenrrated) tets now. + checkinverttetflag = 1; } // if - // Accumulate the dynamic memory. - totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory + - bdrysteinerptlist->totalmemory); - delete bdrysteinerptlist; delete misseglist; delete misshlist; @@ -21445,11 +23599,10 @@ void tetgenmesh::recoverboundary(clock_t& tv) // // /////////////////////////////////////////////////////////////////////////////// - void tetgenmesh::carveholes() { - arraypool *tetarray, *hullarray; - triface tetloop, neightet, hulltet, *parytet, *parytet1; + arraypool *tetarray; + triface tetloop, neightet, hulltet, *parytet; triface openface, casface; triface *regiontets; face checksh, casingout, casingin, *parysh; @@ -21458,15 +23611,12 @@ void tetgenmesh::carveholes() enum locateresult loc; REAL volume; long delsegcount, delvertcount, delsteinercount; - long sliver_peel_count = 0l; int regioncount; int attrnum, attr, maxattr; int remflag; - int t1ver; - int i, j, k; + int i, j; tetrahedron ptr; - shellface sptr; if (!b->quiet) { printf("Removing exterior tetrahedra ...\n"); @@ -21474,15 +23624,10 @@ void tetgenmesh::carveholes() // Initialize the pool of exterior tets. tetarray = new arraypool(sizeof(triface), 10); - hullarray = new arraypool(sizeof(triface), 10); - regiontets = NULL; - regioncount = 0; + maxattr = 0; // Choose a small number here. - //attrnum = in->numberoftetrahedronattributes; - attrnum = numelemattrib - (b->regionattrib > 0); - // Comment: The element region marker is at the end of the list of - // the element attributes. + attrnum = in->numberoftetrahedronattributes; // Mark as infected any unprotected hull tets. tetrahedrons->traversalinit(); @@ -21491,71 +23636,29 @@ void tetgenmesh::carveholes() while (tetloop.tet != (tetrahedron *) NULL) { if ((point) tetloop.tet[7] == dummypoint) { // Is this side protected by a subface? - if (!issubface(tetloop)) { + tspivot(tetloop, checksh); + if (checksh.sh == NULL) { infect(tetloop); tetarray->newindex((void **) &parytet); *parytet = tetloop; - hullsize--; - // Add the adjacent tet (not a hull tet) as well. - // tetloop's face number is 11 & 3 = 3. - decode(tetloop.tet[3], neightet); - if (!infected(neightet)) { - infect(neightet); - tetarray->newindex((void **) &parytet); - *parytet = neightet; - } } } tetloop.tet = alltetrahedrontraverse(); } + hullsize -= tetarray->objects; + if (in->numberofholes > 0) { // Mark as infected any tets inside volume holes. for (i = 0; i < 3 * in->numberofholes; i += 3) { // Search a tet containing the i-th hole point. neightet.tet = NULL; randomsample(&(in->holelist[i]), &neightet); - loc = locate(&(in->holelist[i]), &neightet, 0); + loc = locate(&(in->holelist[i]), &neightet, 0, 1); // randflag = 1; if (loc != OUTSIDE) { - // The tet 'neightet' contain this point. - if (!infected(neightet)) { - infect(neightet); - tetarray->newindex((void **) &parytet); - *parytet = neightet; - // Add its adjacent tet if it is not protected. - if (!issubface(neightet)) { - decode(neightet.tet[neightet.ver & 3], tetloop); - if (!infected(tetloop)) { - infect(tetloop); - tetarray->newindex((void **) &parytet); - *parytet = tetloop; - } - } else { - // It is protected. Check if its adjacent tet is a hull tet. - decode(neightet.tet[neightet.ver & 3], tetloop); - if (ishulltet(tetloop)) { - // It is hull tet, add it into the list. Moreover, the subface - // is dead, i.e., both sides are in exterior. - if (!infected(tetloop)) { - infect(tetloop); - tetarray->newindex((void **) &parytet); - *parytet = tetloop; - hullsize--; - } - } - if (infected(tetloop)) { - // Both sides of this subface are in exterior. - tspivot(neightet, checksh); - stdissolve(checksh); - assert(!sinfected(checksh)); - //if (!sinfected(checksh)) { - sinfect(checksh); // Only queue it once. - subfacstack->newindex((void **) &parysh); - *parysh = checksh; - //} - } - } - } // if (!infected(neightet)) + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; } else { // A hole point locates outside of the convex hull. if (!b->quiet) { @@ -21563,7 +23666,7 @@ void tetgenmesh::carveholes() printf("lies outside the convex hull.\n"); } } - } // i + } } if (b->regionattrib && (in->numberofregions > 0)) { // If has -A option. @@ -21575,7 +23678,7 @@ void tetgenmesh::carveholes() // Search a tet containing the i-th region point. neightet.tet = NULL; randomsample(&(in->regionlist[i]), &neightet); - loc = locate(&(in->regionlist[i]), &neightet, 0); + loc = locate(&(in->regionlist[i]), &neightet, 0, 1); // randflag = 1; if (loc != OUTSIDE) { regiontets[i/5] = neightet; if ((int) in->regionlist[i + 3] > maxattr) { @@ -21591,61 +23694,58 @@ void tetgenmesh::carveholes() } } - // Find and infect all exterior tets (in concave place and in holes). for (i = 0; i < tetarray->objects; i++) { parytet = (triface *) fastlookup(tetarray, i); - // Check its three neighbors if it is not a hull tet. - if ((point) parytet->tet[7] != dummypoint) { - j = (parytet->ver & 3); // j is the current face number. - // Check the neighbors of the other three faces. - for (k = 0, j++; k < 3; k++, j++) { - decode(parytet->tet[j % 4], neightet); // neightet may be a hull tet. - if (!infected(neightet)) { - // Is neightet protected by a subface. - if (!issubface(neightet)) { - // Not proected. Add it into the list. - // It should not be a hull tet. Since all unproected hull tets - // should have already been added into the list. + tetloop = *parytet; + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, neightet); + // Is this side protected by a subface? + tspivot(tetloop, checksh); + if (checksh.sh == NULL) { + // Not protected. Infect it if it is not a hull tet. + if ((point) neightet.tet[7] != dummypoint) { + if (!infected(neightet)) { infect(neightet); - tetarray->newindex((void **) &parytet1); - *parytet1 = neightet; - } else { - // It is protected. However, if neightet is a hull tet, it is - // also an exterior tet. Moverover, the subface is dead, i.e., - // both sides of it are exterior. - if ((point) neightet.tet[7] == dummypoint) { - infect(neightet); - tetarray->newindex((void **) &parytet1); - *parytet1 = neightet; - hullsize--; - // Both sides of this subface are exterior. - tspivot(neightet, checksh); - stdissolve(checksh); - // Queue this subface (to be deleted later). - assert(!sinfected(checksh)); - //if (!sinfected(checksh)) { - sinfect(checksh); // Only queue it once. - subfacstack->newindex((void **) &parysh); - *parysh = checksh; - //} - } + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } + } else { + // Its adjacent tet is protected. + if ((point) neightet.tet[7] == dummypoint) { + // A hull tet. It is dead. + assert(!infected(neightet)); + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + // Both sides of this subface are exterior. + stdissolve(checksh); + // Queue this subface (to be deleted later). + if (!sinfected(checksh)) { + sinfect(checksh); // Only queue it once. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; } + hullsize--; } else { - // Both sides of this face are in exterior. - // Check if there is a subface. - if (issubface(neightet)) { - tspivot(neightet, checksh); + if (!infected(neightet)) { + // The subface is still connect to a "live" tet - it survived. + // tsbond(neightet, checksh); + } else { + // Both sides of this subface are exterior. + stdissolve(checksh); + // Queue this subface (to be deleted later). if (!sinfected(checksh)) { sinfect(checksh); // Only queue it once. subfacstack->newindex((void **) &parysh); *parysh = checksh; - } + } } } - } // j, k + } } - } // i + } if (b->regionattrib && (in->numberofregions > 0)) { // Re-check saved region tets to see if they lie outside. @@ -21660,75 +23760,65 @@ void tetgenmesh::carveholes() } } - -if (!b->convex) { // no -c option - - // Create new hull tets. - // Update point-to-tet map, segment-to-tet map, and subface-to-tet map. - for (i = 0; i < tetarray->objects; i++) { - parytet = (triface *) fastlookup(tetarray, i); - if ((point) parytet->tet[7] != dummypoint) { - // We must check all four adjacent tets. - for (j = 0; j < 4; j++) { - decode(parytet->tet[j], tetloop); - if (!infected(tetloop)) { - // This face becomes a hull face. - tspivot(tetloop, checksh); - maketetrahedron(&hulltet); - pa = org(tetloop); - pb = dest(tetloop); - pc = apex(tetloop); - setvertices(hulltet, pb, pa, pc, dummypoint); - bond(tetloop, hulltet); - // Update the subface-to-tet map. - sesymself(checksh); - tsbond(hulltet, checksh); - // Update the segment-to-tet map. - for (k = 0; k < 3; k++) { - tsspivot1(tetloop, checkseg); - if (checkseg.sh != NULL) { - tssbond1(hulltet, checkseg); - sstbond1(checkseg, hulltet); - } - enextself(tetloop); - eprevself(hulltet); - } - // Update the point-to-tet map. - ptr = encode(tetloop); - setpoint2tet(pa, ptr); - setpoint2tet(pb, ptr); - setpoint2tet(pc, ptr); - // Save this hull tet in list. - hullarray->newindex((void **) &parytet1); - *parytet1 = hulltet; - } - } // j - } else { - // It is a hull tet. Clear the adjacent hull tets' connections to it. - // Our data structure ensures that the 3rd face opposites dummypoint. - for (j = 0; j < 3; j++) { - decode(parytet->tet[j], neightet); - if (neightet.tet != NULL) { - assert(ishulltet(neightet)); - if (!infected(neightet)) { - neightet.tet[neightet.ver & 3] = NULL; - } - } - } // j - } - } // i - - // Update the hull size. - hullsize += hullarray->objects; - // Remove all exterior tetrahedra (including infected hull tets). for (i = 0; i < tetarray->objects; i++) { parytet = (triface *) fastlookup(tetarray, i); + tetloop = *parytet; + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, neightet); + if (!infected(neightet)) { + // A "live" tet (may be a hull tet). Clear its adjacent tet. + neightet.tet[neightet.ver & 3] = NULL; + } + } tetrahedrondealloc(parytet->tet); } // i - tetarray->restart(); + tetarray->restart(); // Re-use it for new hull tets. + // Create new hull tets. + // Update point-to-tet map, segment-to-tet map, and subface-to-tet map. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + if (tetloop.tet[tetloop.ver] == NULL) { + tspivot(tetloop, checksh); + assert(checksh.sh != NULL); // SELF_CHECK + // Create a new hull tet. + maketetrahedron(&hulltet); + pa = org(tetloop); + pb = dest(tetloop); + pc = apex(tetloop); + setvertices(hulltet, pb, pa, pc, dummypoint); + bond(tetloop, hulltet); + // Update the subface-to-tet map. + sesymself(checksh); + tsbond(hulltet, checksh); + // Update the segment-to-tet map. + for (i = 0; i < 3; i++) { + tsspivot1(tetloop, checkseg); + if (checkseg.sh != NULL) { + tssbond1(hulltet, checkseg); + sstbond1(checkseg, hulltet); + } + enextself(tetloop); + eprevself(hulltet); + } + // Save this hull tet in list. + tetarray->newindex((void **) &parytet); + *parytet = hulltet; + } + } + // Update the point-to-tet map. + tetloop.ver = 0; + ptr = encode(tetloop); + ppt = (point *) tetloop.tet; + for (i = 4; i < 8; i++) { + setpoint2tet(ppt[i], ptr); + } + tetloop.tet = tetrahedrontraverse(); + } if (subfacstack->objects > 0) { // Remove all subfaces which do not attach to any tetrahedron. @@ -21793,7 +23883,6 @@ if (!b->convex) { // no -c option subfacstack->restart(); } - // Some vertices may be not belong to any tet. Mark them. delvertcount = unuverts; delsteinercount = 0l; @@ -21861,10 +23950,12 @@ if (!b->convex) { // no -c option } } + // Update the hull size. + hullsize += tetarray->objects; // Connect new hull tets. - for (i = 0; i < hullarray->objects; i++) { - parytet = (triface *) fastlookup(hullarray, i); + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); hulltet = *parytet; for (j = 0; j < 3; j++) { esym(hulltet, neightet); @@ -21889,118 +23980,13 @@ if (!b->convex) { // no -c option } } -} else { // '-c' option is set. - - - long bak_subface_count = subfaces->items; - long bak_segment_count = subsegs->items; - - // In this case, we regard every hull face/edge is a subface/segment. - for (i = 0; i < tetarray->objects; i++) { - parytet = (triface *) fastlookup(tetarray, i); - // Only need the hull tet to find convex hull faces. - if ((point) parytet->tet[7] == dummypoint) { - hulltet.tet = parytet->tet; - hulltet.ver = 3; // The hull face. - tspivot(hulltet, checksh); // SELF_CHECK - if (checksh.sh == NULL) { - // Create a subface. - makeshellface(subfaces, &checksh); - pa = org(hulltet); - pb = dest(hulltet); - pc = apex(hulltet); - setsorg(checksh, pa); - setsdest(checksh, pb); - setsapex(checksh, pc); - // Create the point-to-subface map. - sptr = sencode(checksh); - setpoint2sh(pa, sptr); - setpoint2sh(pb, sptr); - setpoint2sh(pc, sptr); - } - // Insert this subface. - // Note: Even the subface is already exist, it may have been - // disconnected from its adjacent tets. - tsbond(hulltet, checksh); - fsym(hulltet, neightet); - assert(infected(neightet)); - sesymself(checksh); - tsbond(neightet, checksh); - sesymself(checksh); - // Create three segments. - for (j = 0; j < 3; j++) { - tsspivot1(hulltet, checkseg); - if (checkseg.sh == NULL) { - // Create a segment. - makeshellface(subsegs, &checkseg); - pa = org(hulltet); - pb = dest(hulltet); - setshvertices(checkseg, pa, pb, NULL); - // Insert the segment into the mesh. - tetloop = hulltet; - pc = apex(hulltet); - checksh.sh = NULL; - while (1) { - tssbond1(tetloop, checkseg); - tspivot(tetloop, checksh); - if (checksh.sh != NULL) { - ssbond1(checksh, checkseg); - sbond1(checkseg, checksh); - } - fnextself(tetloop); - if (apex(tetloop) == pc) break; - } - sstbond1(checkseg, tetloop); - } - enextself(hulltet); - } - // Save this hull tet in list. - hullarray->newindex((void **) &parytet1); - *parytet1 = hulltet; - } - } // i - - hullsize += hullarray->objects; - - if (subfacstack->objects > 0) { - // Uninfect the collected exterior subfaces. - for (i = 0; i < subfacstack->objects; i++) { - parysh = (face *) fastlookup(subfacstack, i); - suninfect(*parysh); - } - } - + // Set region attributes (when has -A and -AA options). if (b->regionattrib) { - // Only the hull tets need to be uninfected. - for (i = 0; i < hullarray->objects; i++) { - parytet = (triface *) fastlookup(hullarray, i); - uninfect(*parytet); - } - } else { - // Uninfect all collected tets. - for (i = 0; i < tetarray->objects; i++) { - parytet = (triface *) fastlookup(tetarray, i); - uninfect(*parytet); - } - } - - tetarray->restart(); - - if (b->verbose) { - printf(" Created %ld convex hull boundary faces.\n", - subfaces->items - bak_subface_count); - printf(" Created %ld convex hull boundary edges.\n", - subsegs->items - bak_segment_count); - } - -} // if (b->convex) - - // Set region attributes (the -A option). - if (b->regionattrib) { if (!b->quiet) { printf("Spreading region attributes.\n"); } + regioncount = 0; // If has user-defined region attributes. if (in->numberofregions > 0) { @@ -22021,61 +24007,81 @@ if (!b->convex) { // no -c option if (b->varvolume) { // If has -a option. setvolumebound(tetloop.tet, volume); } - for (k = 0; k < 4; k++) { - decode(tetloop.tet[k], neightet); - // Is the adjacent already checked? - if (!infected(neightet)) { - // Is this side protected by a subface? - tspivot(neightet, checksh); - if (checksh.sh == NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, neightet); + // Is this side protected by a subface? + tspivot(tetloop, checksh); + if (checksh.sh == NULL) { + // Not protected. It must not be a hull tet. + // assert((point) neightet.tet[7] != dummypoint); + if ((point) neightet.tet[7] == dummypoint) { + assert(0); + } + if (!infected(neightet)) { infect(neightet); tetarray->newindex((void **) &parytet); *parytet = neightet; } + } else { + // Protected. Set attribute for hull tet as well. + if ((point) neightet.tet[7] == dummypoint) { + setelemattribute(neightet.tet, attrnum, attr); + if (b->varvolume) { // If has -a option. + setvolumebound(neightet.tet, volume); + } + } } - } // k + } // ver } // j regioncount++; } // if (regiontets[i/5].tet != NULL) } // i } - // Set attributes for all tetrahedra. - attr = maxattr + 1; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - if (!infected(tetloop)) { - // An unmarked region. - tetarray->restart(); // Re-use this array. - infect(tetloop); - tetarray->newindex((void **) &parytet); - *parytet = tetloop; - // Find and mark all tets. - for (j = 0; j < tetarray->objects; j++) { - parytet = (triface *) fastlookup(tetarray, j); - tetloop = *parytet; - setelemattribute(tetloop.tet, attrnum, attr); - for (k = 0; k < 4; k++) { - decode(tetloop.tet[k], neightet); - // Is the adjacent tet already checked? - if (!infected(neightet)) { + if (b->regionattrib > 1) { // If has -AA option. + // Set attributes for all tetrahedra. + attr = maxattr + 1; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + if (!infected(tetloop)) { + // An unmarked region. + tetarray->restart(); // Re-use this array. + infect(tetloop); + tetarray->newindex((void **) &parytet); + *parytet = tetloop; + // Find and mark all tets. + for (j = 0; j < tetarray->objects; j++) { + parytet = (triface *) fastlookup(tetarray, j); + tetloop = *parytet; + setelemattribute(tetloop.tet, attrnum, attr); + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, neightet); // Is this side protected by a subface? - tspivot(neightet, checksh); + tspivot(tetloop, checksh); if (checksh.sh == NULL) { - infect(neightet); - tetarray->newindex((void **) &parytet); - *parytet = neightet; + // Not protected. It must not be a hull tet. + assert((point) neightet.tet[7] != dummypoint); + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } else { + // Protected. Set attribute for hull tet as well. + if ((point) neightet.tet[7] == dummypoint) { + setelemattribute(neightet.tet, attrnum, attr); + } } - } - } // k - } // j - attr++; // Increase the attribute. - regioncount++; + } // loc + } + attr++; // Increase the attribute. + regioncount++; + } // if (!infected(tetloop)) + tetloop.tet = tetrahedrontraverse(); } - tetloop.tet = tetrahedrontraverse(); + // Until here, every tet has a region attribute. } - // Until here, every tet has a region attribute. // Uninfect processed tets. tetrahedrons->traversalinit(); @@ -22085,6 +24091,8 @@ if (!b->convex) { // no -c option tetloop.tet = tetrahedrontraverse(); } + // Mesh elements contain region attributes now. + in->numberoftetrahedronattributes++; if (b->verbose) { assert(regioncount > 0); @@ -22094,47 +24102,34 @@ if (!b->convex) { // no -c option printf(" Found 1 domain.\n"); } } + } // if (b->regionattrib) if (b->regionattrib && (in->numberofregions > 0)) { // If has -A option. delete [] regiontets; } delete tetarray; - delete hullarray; - -if (!b->convex) { // if no -c option // The mesh is non-convex now. nonconvex = 1; - // Push all hull tets into 'flipstack'. tetrahedrons->traversalinit(); tetloop.ver = 11; // The face opposite to dummypoint. tetloop.tet = alltetrahedrontraverse(); while (tetloop.tet != (tetrahedron *) NULL) { if ((point) tetloop.tet[7] == dummypoint) { - fsym(tetloop, neightet); - flippush(flipstack, &neightet); + flippush(flipstack, &tetloop); } tetloop.tet = alltetrahedrontraverse(); } - flipconstraints fc; - fc.enqflag = 2; - // Peel "slivers" off the hull. - sliver_peel_count = lawsonflip3d(&fc); + lawsonflip3d(NULL, 4, 1, 0, 0); - if (b->verbose) { - if (sliver_peel_count > 0l) { - printf(" Removed %ld hull slivers.\n", sliver_peel_count); - } + if (b->verbose && (opt_sliver_peels > 0l)) { + printf(" Peeled %ld hull slivers.\n", opt_sliver_peels); } - unflipqueue->restart(); - -} // if (!b->convex) - } /////////////////////////////////////////////////////////////////////////////// @@ -22158,7 +24153,6 @@ void tetgenmesh::reconstructmesh() REAL angtol, ang; int eextras, marker = 0; int bondflag; - int t1ver; int idx, i, j, k; if (!b->quiet) { @@ -22167,17 +24161,12 @@ void tetgenmesh::reconstructmesh() // Default assume the mesh is non-convex. nonconvex = 1; - // Create a map from indices to vertices. + // Create a map from indices to vertices. makeindex2pointmap(idx2verlist); - // 'idx2verlist' has length 'in->numberofpoints + 1'. - if (in->firstnumber == 1) { - idx2verlist[0] = dummypoint; // Let 0th-entry be dummypoint. - } // Allocate an array that maps each vertex to its adjacent tets. ver2tetarray = new tetrahedron[in->numberofpoints + 1]; - for (i = 0; i < in->numberofpoints + 1; i++) { - setpointtype(idx2verlist[i], VOLVERTEX); // initial type. + for (i = 0; i < in->numberofpoints; i++) { ver2tetarray[i] = NULL; } @@ -22187,6 +24176,7 @@ void tetgenmesh::reconstructmesh() idx = i * in->numberofcorners; for (j = 0; j < 4; j++) { p[j] = idx2verlist[in->tetrahedronlist[idx++]]; + setpointtype(p[j], VOLVERTEX); // initial type. } // Check the orientation. ori = orient3d(p[0], p[1], p[2], p[3]); @@ -22234,6 +24224,7 @@ void tetgenmesh::reconstructmesh() p[2] = apex(tetloop); // c prevchktet = tetloop; do { + assert(checktet.ver < 4); // SELF_CHECK q[0] = org(checktet); // a' q[1] = dest(checktet); // b' q[2] = apex(checktet); // c' @@ -22419,51 +24410,51 @@ void tetgenmesh::reconstructmesh() } // i } // if (in->trifacelist) - // Indentify subfaces from the mesh. - // Create subfaces for hull faces (if they're not subface yet) and - // interior faces which separate two different materials. - eextras = in->numberoftetrahedronattributes; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { - tspivot(tetloop, neighsh); - if (neighsh.sh == NULL) { - bondflag = 0; - fsym(tetloop, checktet); - if (ishulltet(checktet)) { - bondflag = 1; // A hull face. - } else { - if (eextras > 0) { - if (elemattribute(tetloop.tet, eextras - 1) != - elemattribute(checktet.tet, eextras - 1)) { - bondflag = 1; // An interior interface. + // Indentify subfaces from the mesh. + // Create subfaces for hull faces (if they're not subface yet) and + // interior faces which separate two different materials. + eextras = in->numberoftetrahedronattributes; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + tspivot(tetloop, neighsh); + if (neighsh.sh == NULL) { + bondflag = 0; + fsym(tetloop, checktet); + if (ishulltet(checktet)) { + bondflag = 1; // A hull face. + } else { + if (eextras > 0) { + if (elemattribute(tetloop.tet, eextras - 1) != + elemattribute(checktet.tet, eextras - 1)) { + bondflag = 1; // An interior interface. + } } } - } - if (bondflag) { - // Create a new subface. - makeshellface(subfaces, &subloop); - p[0] = org(tetloop); - p[1] = dest(tetloop); - p[2] = apex(tetloop); - setshvertices(subloop, p[0], p[1], p[2]); - // Create the point-to-subface map. - sptr = sencode(subloop); - for (j = 0; j < 3; j++) { - setpointtype(p[j], FACETVERTEX); // initial type. - setpoint2sh(p[j], sptr); - } - setshellmark(subloop, 0); // Default marker. - // Insert the subface into the mesh. - tsbond(tetloop, subloop); - sesymself(subloop); - tsbond(checktet, subloop); - } // if (bondflag) - } // if (neighsh.sh == NULL) + if (bondflag) { + // Create a new subface. + makeshellface(subfaces, &subloop); + p[0] = org(tetloop); + p[1] = dest(tetloop); + p[2] = apex(tetloop); + setshvertices(subloop, p[0], p[1], p[2]); + // Create the point-to-subface map. + sptr = sencode(subloop); + for (j = 0; j < 3; j++) { + setpointtype(p[j], FACETVERTEX); // initial type. + setpoint2sh(p[j], sptr); + } + setshellmark(subloop, 0); // Default marker. + // Insert the subface into the mesh. + tsbond(tetloop, subloop); + sesymself(subloop); + tsbond(checktet, subloop); + } // if (bondflag) + } // if (neighsh.sh == NULL) + } + tetloop.tet = tetrahedrontraverse(); } - tetloop.tet = tetrahedrontraverse(); - } // Connect subfaces together. subfaces->traversalinit(); @@ -22497,6 +24488,9 @@ void tetgenmesh::reconstructmesh() subloop.sh = shellfacetraverse(subfaces); } + //if (b->verbose) { + // printf(" Created %ld subfaces.\n", subfaces->items); + //} // Segments will be introudced. if (in->edgelist != NULL) { @@ -22560,82 +24554,85 @@ void tetgenmesh::reconstructmesh() } // i } // if (in->edgelist) - // Identify segments from the mesh. - // Create segments for non-manifold edges (which are shared by more - // than two subfaces), and for non-coplanar edges, i.e., two subfaces - // form an dihedral angle > 'b->facet_ang_tol' (degree). - angtol = b->facet_ang_tol / 180.0 * PI; - subfaces->traversalinit(); - subloop.shver = 0; - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != (shellface *) NULL) { - for (i = 0; i < 3; i++) { - sspivot(subloop, segloop); - if (segloop.sh == NULL) { - // Check if this edge is a segment. - bondflag = 0; - // Counter the number of subfaces at this edge. - idx = 0; - nextsh = subloop; - while (1) { - idx++; - spivotself(nextsh); - if (nextsh.sh == subloop.sh) break; - } - if (idx != 2) { - // It's a non-manifold edge. Insert a segment. - p[0] = sorg(subloop); - p[1] = sdest(subloop); - bondflag = 1; - } else { - // Check the dihedral angle formed by the two subfaces. - spivot(subloop, neighsh); - p[0] = sorg(subloop); - p[1] = sdest(subloop); - p[2] = sapex(subloop); - p[3] = sapex(neighsh); - ang = facedihedral(p[0], p[1], p[2], p[3]); - if (ang > PI) ang = 2 * PI - ang; - if (ang < angtol) { + // Identify segments from the mesh. + // Create segments for non-manifold edges (which are shared by more + // than two subfaces), and for non-coplanar edges, i.e., two subfaces + // form an dihedral angle > 'b->facet_ang_tol' (degree). + angtol = b->facet_ang_tol / 180.0 * PI; + subfaces->traversalinit(); + subloop.shver = 0; + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != (shellface *) NULL) { + for (i = 0; i < 3; i++) { + sspivot(subloop, segloop); + if (segloop.sh == NULL) { + // Check if this edge is a segment. + bondflag = 0; + // Counter the number of subfaces at this edge. + idx = 0; + nextsh = subloop; + while (1) { + idx++; + spivotself(nextsh); + if (nextsh.sh == subloop.sh) break; + } + if (idx != 2) { + // It's a non-manifold edge. Insert a segment. + p[0] = sorg(subloop); + p[1] = sdest(subloop); bondflag = 1; + } else { + // Check the dihedral angle formed by the two subfaces. + spivot(subloop, neighsh); + p[0] = sorg(subloop); + p[1] = sdest(subloop); + p[2] = sapex(subloop); + p[3] = sapex(neighsh); + ang = facedihedral(p[0], p[1], p[2], p[3]); + if (ang > PI) ang = 2 * PI - ang; + if (ang < angtol) { + bondflag = 1; + } } - } - if (bondflag) { - // Create a new subface. - makeshellface(subsegs, &segloop); - setshvertices(segloop, p[0], p[1], NULL); - // Create the point-to-segment map. - sptr = sencode(segloop); - for (j = 0; j < 2; j++) { - setpointtype(p[j], RIDGEVERTEX); // initial type. - setpoint2sh(p[j], sptr); - } - setshellmark(segloop, marker); - // Insert the subface into the mesh. - stpivot(subloop, tetloop); - q[2] = apex(tetloop); - while (1) { - tssbond1(tetloop, segloop); - tspivot(tetloop, neighsh); - if (neighsh.sh != NULL) { - ssbond1(neighsh, segloop); + if (bondflag) { + // Create a new subface. + makeshellface(subsegs, &segloop); + setshvertices(segloop, p[0], p[1], NULL); + // Create the point-to-segment map. + sptr = sencode(segloop); + for (j = 0; j < 2; j++) { + setpointtype(p[j], RIDGEVERTEX); // initial type. + setpoint2sh(p[j], sptr); } - fnextself(tetloop); - if (apex(tetloop) == q[2]) break; - } // while (1) - // Remember an adjacent tet for this segment. - sstbond1(segloop, tetloop); - sbond1(segloop, subloop); - } // if (bondflag) - } // if (neighsh.sh == NULL) - senextself(subloop); + setshellmark(segloop, marker); + // Insert the subface into the mesh. + stpivot(subloop, tetloop); + q[2] = apex(tetloop); + while (1) { + tssbond1(tetloop, segloop); + tspivot(tetloop, neighsh); + if (neighsh.sh != NULL) { + ssbond1(neighsh, segloop); + } + fnextself(tetloop); + if (apex(tetloop) == q[2]) break; + } // while (1) + // Remember an adjacent tet for this segment. + sstbond1(segloop, tetloop); + sbond1(segloop, subloop); + } // if (bondflag) + } // if (neighsh.sh == NULL) + senextself(subloop); + } + subloop.sh = shellfacetraverse(subfaces); } - subloop.sh = shellfacetraverse(subfaces); - } // Remember the number of input segments. insegments = subsegs->items; + //if (b->verbose) { + // printf(" Created %ld segments.\n", subsegs->items); + //} // Set global flags. checksubsegflag = 1; @@ -22663,32 +24660,32 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) point pa, pb, pc, pd; enum locateresult loc = OUTSIDE; REAL vol, ori1, ori2, ori3, ori4; - int t1ver; - + int iter; - // Randonmly select a good starting tet. - if (randflag) { - randomsample(searchpt, searchtet); - } else { - if (searchtet->tet == NULL) { - *searchtet = recenttet; - } + if (searchtet->tet == NULL) { + *searchtet = recenttet; } - loc = locate(searchpt, searchtet, 0); - if (loc == OUTSIDE) { - // Test if it lies nearly on the hull face. - // Reuse vol, ori1. - pa = org(*searchtet); - pb = dest(*searchtet); - pc = apex(*searchtet); - vol = triarea(pa, pb, pc); - ori1 = orient3dfast(pa, pb, pc, searchpt); - if (fabs(ori1 / vol) < b->epsilon) { - loc = ONFACE; // On face (or on edge, or on vertex). - fsymself(*searchtet); + iter = 0; + while (1) { + // Randonmly select a good starting tet. + if (randflag) { + randomsample(searchpt, searchtet); + } + loc = locate(searchpt, searchtet, 0, 1); + if (loc == OUTSIDE) { + // Not found. This happens when the mesh is not convex. + if (!randflag) break; + iter++; + if (iter > 3) { + searchtet->tet = NULL; + break; + } + } else { + // Found the point. + break; } - } + } // while (1) if (loc != OUTSIDE) { // Round the result of location. @@ -22696,11 +24693,11 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) pb = dest(*searchtet); pc = apex(*searchtet); pd = oppo(*searchtet); - vol = orient3dfast(pa, pb, pc, pd); - ori1 = orient3dfast(pa, pb, pc, searchpt); - ori2 = orient3dfast(pb, pa, pd, searchpt); - ori3 = orient3dfast(pc, pb, pd, searchpt); - ori4 = orient3dfast(pa, pc, pd, searchpt); + vol = orient3d(pa, pb, pc, pd); + ori1 = orient3d(pa, pb, pc, searchpt); + ori2 = orient3d(pb, pa, pd, searchpt); + ori3 = orient3d(pc, pb, pd, searchpt); + ori4 = orient3d(pa, pc, pd, searchpt); if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; if (fabs(ori2 / vol) < b->epsilon) ori2 = 0; if (fabs(ori3 / vol) < b->epsilon) ori3 = 0; @@ -22715,19 +24712,19 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) pc = apex(*searchtet); pd = oppo(*searchtet); - vol = orient3dfast(pa, pb, pc, pd); + vol = orient3d(pa, pb, pc, pd); assert(vol < 0); // vol != 0 - ori1 = orient3dfast(pa, pb, pc, searchpt); + ori1 = orient3d(pa, pb, pc, searchpt); if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; // Rounding. if (ori1 <= 0) { - ori2 = orient3dfast(pb, pa, pd, searchpt); + ori2 = orient3d(pb, pa, pd, searchpt); if (fabs(ori2 / vol) < b->epsilon) ori2 = 0; if (ori2 <= 0) { - ori3 = orient3dfast(pc, pb, pd, searchpt); + ori3 = orient3d(pc, pb, pd, searchpt); if (fabs(ori3 / vol) < b->epsilon) ori3 = 0; if (ori3 <= 0) { - ori4 = orient3dfast(pa, pc, pd, searchpt); + ori4 = orient3d(pa, pc, pd, searchpt); if (fabs(ori4 / vol) < b->epsilon) ori4 = 0; if (ori4 <= 0) { // Found the tet. Return its location. @@ -22737,9 +24734,8 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) } // ori2 } // ori1 - searchtet->tet = tetrahedrontraverse(); + searchtet->tet = bgm->tetrahedrontraverse(); } // while (searchtet->tet != NULL) - nonregularcount++; // Re-use this counter. } if (searchtet->tet != NULL) { @@ -22828,9 +24824,13 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) // 'iloc' indicates the location of the point w.r.t. 'searchtet'. The size // // is obtained by linear interpolation on the vertices of the tet. // // // +// If 'posflag' is set, only do interpolation when all vertices have a posi- // +// tive value. // +// // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) +REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, + int posflag) { point *pts, pa, pb, pc; REAL volume, vol[4], wei[4]; @@ -22842,15 +24842,15 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) if (iloc == (int) INTETRAHEDRON) { pts = (point *) &(searchtet->tet[4]); assert(pts[3] != dummypoint); - // Only do interpolation if all vertices have non-zero sizes. - if ((pts[0][pointmtrindex] > 0) && (pts[1][pointmtrindex] > 0) && - (pts[2][pointmtrindex] > 0) && (pts[3][pointmtrindex] > 0)) { + if (!posflag || + ((pts[0][pointmtrindex] > 0) && (pts[1][pointmtrindex] > 0) && + (pts[2][pointmtrindex] > 0) && (pts[3][pointmtrindex] > 0))) { // P1 interpolation. - volume = orient3dfast(pts[0], pts[1], pts[2], pts[3]); - vol[0] = orient3dfast(searchpt, pts[1], pts[2], pts[3]); - vol[1] = orient3dfast(pts[0], searchpt, pts[2], pts[3]); - vol[2] = orient3dfast(pts[0], pts[1], searchpt, pts[3]); - vol[3] = orient3dfast(pts[0], pts[1], pts[2], searchpt); + volume = orient3d(pts[0], pts[1], pts[2], pts[3]); + vol[0] = orient3d(searchpt, pts[1], pts[2], pts[3]); + vol[1] = orient3d(pts[0], searchpt, pts[2], pts[3]); + vol[2] = orient3d(pts[0], pts[1], searchpt, pts[3]); + vol[3] = orient3d(pts[0], pts[1], pts[2], searchpt); for (i = 0; i < 4; i++) { wei[i] = fabs(vol[i] / volume); size += (wei[i] * pts[i][pointmtrindex]); @@ -22860,8 +24860,9 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) pa = org(*searchtet); pb = dest(*searchtet); pc = apex(*searchtet); - if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) && - (pc[pointmtrindex] > 0)) { + if (!posflag || + ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) && + (pc[pointmtrindex] > 0))) { volume = triarea(pa, pb, pc); vol[0] = triarea(searchpt, pb, pc); vol[1] = triarea(pa, searchpt, pc); @@ -22873,7 +24874,7 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) } else if (iloc == (int) ONEDGE) { pa = org(*searchtet); pb = dest(*searchtet); - if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) { + if (!posflag || ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0))) { volume = distance(pa, pb); vol[0] = distance(searchpt, pb); vol[1] = distance(pa, searchpt); @@ -22882,7 +24883,7 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) } } else if (iloc == (int) ONVERTEX) { pa = org(*searchtet); - if (pa[pointmtrindex] > 0) { + if (!posflag || (pa[pointmtrindex] > 0)) { size = pa[pointmtrindex]; } } @@ -22908,11 +24909,6 @@ void tetgenmesh::interpolatemeshsize() if (!b->quiet) { printf("Interpolating mesh size ...\n"); } - - long bak_nonregularcount = nonregularcount; - nonregularcount = 0l; // Count the number of (slow) global searches. - long baksmaples = bgm->samples; - bgm->samples = 3l; count = 0; // Count the number of interpolated points. // Interpolate sizes for all points in the current mesh. @@ -22923,8 +24919,8 @@ void tetgenmesh::interpolatemeshsize() searchtet.tet = NULL; iloc = bgm->scoutpoint(ploop, &searchtet, 1); // randflag = 1 if (iloc != (int) OUTSIDE) { - // Interpolate the mesh size. - ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc); + // Interpolate the mesh size (posflag = 0) + ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc, 0); setpoint2bgmtet(ploop, bgm->encode(searchtet)); if (count == 0) { // This is the first interpolated point. @@ -22949,276 +24945,152 @@ void tetgenmesh::interpolatemeshsize() if (b->verbose) { printf(" Interoplated %d points.\n", count); - if (nonregularcount > 0l) { - printf(" Performed %ld brute-force searches.\n", nonregularcount); - } printf(" Size rangle [%.17g, %.17g].\n", minval, maxval); } - - bgm->samples = baksmaples; - nonregularcount = bak_nonregularcount; } /////////////////////////////////////////////////////////////////////////////// // // // insertconstrainedpoints() Insert a list of points into the mesh. // // // -// Assumption: The bounding box of the insert point set should be no larger // -// than the bounding box of the mesh. (Required by point sorting). // -// // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen) +void tetgenmesh::insertconstrainedpoints(tetgenio *addio) { triface searchtet, spintet; - face splitsh; - face splitseg; + face checksh, *splitsh; + face checkseg, *splitseg; + point newpt; insertvertexflags ivf; - flipconstraints fc; - int randflag = 0; - int t1ver; - int i; + REAL *attr, x, y, z, w; + int randflag; + int count, index; + int loc; + int i, j; - if (b->verbose) { - printf(" Inserting %d constrained points\n", arylen); + if (!b->quiet) { + printf("Inserting constrained points ...\n"); } - if (b->no_sort) { // -b/1 option. - if (b->verbose) { - printf(" Using the input order.\n"); - } - } else { - if (b->verbose) { - printf(" Permuting vertices.\n"); - } - point swappoint; - int randindex; - srand(arylen); - for (i = 0; i < arylen; i++) { - randindex = rand() % (i + 1); - swappoint = insertarray[i]; - insertarray[i] = insertarray[randindex]; - insertarray[randindex] = swappoint; - } - if (b->brio_hilbert) { // -b1 option - if (b->verbose) { - printf(" Sort vertices using BRIO and Hilbert curve.\n"); - } - hilbert_init(in->mesh_dim); - int ngroup = 0; - brio_multiscale_sort(insertarray, arylen, b->brio_threshold, - b->brio_ratio, &ngroup); - } else { // -b0 option. - randflag = 1; - } // if (!b->brio_hilbert) - } // if (!b->no_sort) - - long bak_nonregularcount = nonregularcount; - nonregularcount = 0l; - long baksmaples = samples; - samples = 3l; // Use at least 3 samples. Updated in randomsample(). - - long bak_seg_count = st_segref_count; - long bak_fac_count = st_facref_count; - long bak_vol_count = st_volref_count; - - // Initialize the insertion parameters. - if (b->incrflip) { // -l option - // Use incremental flip algorithm. - ivf.bowywat = 0; - ivf.lawson = 1; - ivf.validflag = 0; // No need to validate the cavity. - fc.enqflag = 2; - } else { - // Use Bowyer-Watson algorithm. - ivf.bowywat = 1; - ivf.lawson = 0; - ivf.validflag = 1; // Validate the B-W cavity. - } - ivf.rejflag = 0; // Do not check encroachment. - if (b->metric) { // -m option. - ivf.rejflag |= 4; // Reject it if it lies in some protecting balls. - } - ivf.chkencflag = 0; - ivf.sloc = (int) INSTAR; - ivf.sbowywat = 3; - ivf.splitbdflag = 1; - ivf.respectbdflag = 1; - ivf.assignmeshsize = b->metric; + randflag = 1; // Randomly select start tet for point location. + count = 0; + index = 0; - // Insert the points. - for (i = 0; i < arylen; i++) { - // Find the location of the inserted point. - // Do not use 'recenttet', since the mesh may be non-convex. - searchtet.tet = NULL; - ivf.iloc = scoutpoint(insertarray[i], &searchtet, randflag); - - // Decide the right type for this point. - setpointtype(insertarray[i], FREEVOLVERTEX); // Default. - splitsh.sh = NULL; - splitseg.sh = NULL; - if (ivf.iloc == (int) ONEDGE) { - if (issubseg(searchtet)) { - tsspivot1(searchtet, splitseg); - setpointtype(insertarray[i], FREESEGVERTEX); - //ivf.rejflag = 0; + for (i = 0; i < addio->numberofpoints; i++) { + makepoint(&newpt, VOLVERTEX); + x = newpt[0] = addio->pointlist[index++]; + y = newpt[1] = addio->pointlist[index++]; + z = newpt[2] = addio->pointlist[index++]; + if (b->weighted) { // -w option + if (addio->numberofpointattributes > 0) { + // The first point attribute is weight. + w = addio->pointattributelist[addio->numberofpointattributes * i]; } else { - // Check if it is a subface edge. - spintet = searchtet; - while (1) { - if (issubface(spintet)) { - tspivot(spintet, splitsh); - setpointtype(insertarray[i], FREEFACETVERTEX); - //ivf.rejflag |= 1; - break; - } - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } + // No given weight available. + w = 0; } - } else if (ivf.iloc == (int) ONFACE) { - if (issubface(searchtet)) { - tspivot(searchtet, splitsh); - setpointtype(insertarray[i], FREEFACETVERTEX); - //ivf.rejflag |= 1; + if (b->weighted_param == 0) { + newpt[3] = x * x + y * y + z * z - w; // Weighted DT. + } else { // -w1 option + newpt[3] = w; // Regular tetrahedralization. } + } else { + newpt[3] = 0; } - - // Now insert the point. - if (insertpoint(insertarray[i], &searchtet, &splitsh, &splitseg, &ivf)) { - if (flipstack != NULL) { - // There are queued faces. Use flips to recover Delaunayness. - lawsonflip3d(&fc); - // There may be unflippable edges. Ignore them. - unflipqueue->restart(); + // Read the add point attributes if current points have attributes. + if ((addio->numberofpointattributes > 0) && + (in->numberofpointattributes > 0)) { + attr = addio->pointattributelist + addio->numberofpointattributes * i; + for (j = 0; j < in->numberofpointattributes; j++) { + if (j < addio->numberofpointattributes) { + newpt[4 + j] = attr[j]; + } } - // Update the Steiner counters. - if (pointtype(insertarray[i]) == FREESEGVERTEX) { - st_segref_count++; - } else if (pointtype(insertarray[i]) == FREEFACETVERTEX) { - st_facref_count++; + } + // Read the point metric tensor. + //for (j = 0; j < in->numberofpointmtrs; j++) { + // pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++]; + //} + + // Find the location of the inserted point. + searchtet.tet = NULL; + ivf.iloc = scoutpoint(newpt, &searchtet, randflag); + if (ivf.iloc != (int) OUTSIDE) { + // Found the point. + // Initialize the insertion parameters. + if (b->psc) { + ivf.bowywat = 0; // Do not enlarge the initial cavity. + ivf.validflag = 0; // Do not validate the initial cavity. } else { - st_volref_count++; + ivf.bowywat = 3; // Use the "Bowyer-Watson" algorithm to form cavity. + ivf.validflag = 1; // Validate the B-W cavity. } - } else { - // Point is not inserted. - if (ivf.iloc == (int) OUTSIDE) { - if (!b->quiet) { - printf("Warning: Point #%d lies outside the domain. Ignored.\n", - pointmark(insertarray[i]) - in->numberofpoints); - } - } else if (ivf.iloc == (int) ONVERTEX) { - if (!b->quiet) { - printf("Warning: Point #%d is coincident with Point #%d. Ignored.\n", - pointmark(insertarray[i]) - in->numberofpoints, - pointmark(org(searchtet))); - } - } else if (ivf.iloc == (int) NEARVERTEX) { - if (!b->quiet) { - printf("Warning: Point #%d is too close to Point #%d. Rejected.\n", - pointmark(insertarray[i]) - in->numberofpoints, - pointmark(point2ppt(insertarray[i]))); - } - } else if (ivf.iloc == (int) ENCVERTEX) { - if (!b->quiet) { - printf("Warning: Point #%d encroaches upon Point #%d. Rejected.\n", - pointmark(insertarray[i]) - in->numberofpoints, - pointmark(point2ppt(insertarray[i]))); + ivf.lawson = 3; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = ivf.iloc; + ivf.sbowywat = ivf.bowywat; // Surface mesh options. + ivf.splitbdflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = 1; + + splitsh = NULL; + splitseg = NULL; + + // Set the right point type. + if (ivf.iloc == (int) ONEDGE) { + tsspivot1(searchtet, checkseg); + if (checkseg.sh != NULL) { + setpointtype(newpt, RIDGEVERTEX); + spivot(checkseg, checksh); + splitsh = &checksh; + splitseg = &checkseg; + } else { + // Check if it is a subface edge. + spintet = searchtet; + while (1) { + tspivot(spintet, checksh); + if (checksh.sh != NULL) { + setpointtype(newpt, FACETVERTEX); + splitsh = &checksh; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } } - } else { - if (!b->quiet) { - printf("Warning: Failed to insert Point #%d (%g,%g,%g).Ignored.\n", - i, insertarray[i][0], insertarray[i][1], insertarray[i][2]); + } else if (ivf.iloc == (int) ONFACE) { + tspivot(searchtet, checksh); + if (checksh.sh != NULL) { + setpointtype(newpt, FACETVERTEX); + splitsh = &checksh; } } - //pointdealloc(insertarray[i]); - setpointtype(insertarray[i], UNUSEDVERTEX); - unuverts++; - } - } // i - - if (b->verbose) { - printf(" Inserted %ld (%ld, %ld, %ld) vertices.\n", - st_segref_count + st_facref_count + st_volref_count - - (bak_seg_count + bak_fac_count + bak_vol_count), - st_segref_count - bak_seg_count, st_facref_count - bak_fac_count, - st_volref_count - bak_vol_count); - if (nonregularcount > 0l) { - printf(" Performed %ld brute-force searches.\n", nonregularcount); - } - } - - nonregularcount = bak_nonregularcount; - samples = baksmaples; -} - -void tetgenmesh::insertconstrainedpoints(tetgenio *addio) -{ - point *insertarray, newpt; - REAL x, y, z, w; - int index, attribindex, mtrindex; - int arylen, i, j; - - if (!b->quiet) { - printf("Inserting constrained points ...\n"); - } - insertarray = new point[addio->numberofpoints]; - arylen = 0; - index = 0; - attribindex = 0; - mtrindex = 0; + // Insert the vertex. + loc = insertvertex(newpt, &searchtet, splitsh, splitseg, &ivf); - for (i = 0; i < addio->numberofpoints; i++) { - x = addio->pointlist[index++]; - y = addio->pointlist[index++]; - z = addio->pointlist[index++]; - // Test if this point lies inside the bounding box. - if ((x < xmin) || (x > xmax) || (y < ymin) || (y > ymax) || - (z < zmin) || (z > zmax)) { - if (b->verbose) { - printf("Warning: Point #%d lies outside the bounding box. Ignored\n", - i + in->firstnumber); - } - continue; - } - makepoint(&newpt, UNUSEDVERTEX); - newpt[0] = x; - newpt[1] = y; - newpt[2] = z; - // Read the point attributes. (Including point weights.) - for (j = 0; j < addio->numberofpointattributes; j++) { - newpt[3 + j] = addio->pointattributelist[attribindex++]; - } - // Read the point metric tensor. - for (j = 0; j < addio->numberofpointmtrs; j++) { - newpt[pointmtrindex + j] = addio->pointmtrlist[mtrindex++]; - } - if (b->weighted) { // -w option - if (addio->numberofpointattributes > 0) { - // The first point attribute is its weight. - w = newpt[3]; + if (loc == ivf.iloc) { + // The point has been inserted. + lawsonflip3d(newpt, 4, 0, ivf.chkencflag, 0); + count++; } else { - // No given weight available. Default choose the maximum - // absolute value among its coordinates. - w = fabs(x); - if (w < fabs(y)) w = fabs(y); - if (w < fabs(z)) w = fabs(z); + if (!b->quiet) { + printf("Warning: Failed to insert point #%d. Ignored.\n", i); + } + pointdealloc(newpt); } - if (b->weighted_param == 0) { - newpt[3] = x * x + y * y + z * z - w; // Weighted DT. - } else { // -w1 option - newpt[3] = w; // Regular tetrahedralization. + } else { + if (!b->quiet) { + printf("Warning: Can't locate add point #%d. Ignored.\n", i); } + pointdealloc(newpt); } - insertarray[arylen] = newpt; - arylen++; } // i - // Insert the points. - insertconstrainedpoints(insertarray, arylen); - - delete [] insertarray; + if (b->verbose) { + printf(" Inserted %d of %d vertices.\n", count, addio->numberofpoints); + } } //// //// @@ -23233,6 +25105,8 @@ void tetgenmesh::insertconstrainedpoints(tetgenio *addio) // // // marksharpsegments() Mark sharp segments. // // // +// All segments are initialized as type NSHARP. // +// // // A segment is SHARP if there are two facets intersecting at it with an // // internal dihedral angle (*) less than an angle \theta. // // // @@ -23265,7 +25139,7 @@ void tetgenmesh::marksharpsegments() minfacetdihed = PI; smallang = 65.0 * PI / 180.0; // 65 degree. - exsmallang = 5.0 * PI / 180.0; // 5 degree. + exsmallang = 15.0 * PI / 180.0; // 15 degree. sharpcount = exsharpcount = 0; // A segment s may have been split into many subsegments. Operate the one @@ -23423,8 +25297,8 @@ void tetgenmesh::marksharpsegments() // (4) the square root of a maximal area constraint in a .var file; // // (5) a maximal length constraint in a .var file; // // // -// If 'b->nobisect' ('-Y' option) is set, every input vertex has a size. It // -// is used to prevent creating too close Steiner points. // +// If 'b->nobisect' ('-Y' option) is set, every input vertex has a feature // +// size. // // // // The feature size of a Steiner point is linearly interpolated from its adj-// // acent vertices which belong to the "carrier" (the boundary of the lowrest // @@ -23460,8 +25334,9 @@ void tetgenmesh::decidefeaturepointsizes() maxlen = pow(6.0 * b->maxvolume, 1.0 / 3.0); } - // First, assign a size of p if p is a feature point or an input point and - // the -Y option is used. + // First only assign a size of p if p is not a Steiner point. The size of + // a Steiner point will be interpolated later from the endpoints of the + // segment on which it lies. featurecount = 0; points->traversalinit(); ploop = pointtraverse(); @@ -23501,7 +25376,9 @@ void tetgenmesh::decidefeaturepointsizes() sdecode(point2sh(adjpt), checkseg); assert(checkseg.sh != NULL); checkseg.shver = 0; - if (sdest(checkseg) != adjpt) sesymself(checkseg); + if (sdest(checkseg) != adjpt) { + sesymself(checkseg); + } assert(sdest(checkseg) == adjpt); // It is possible that the original segment of 'adjpt' does not // have 'ploop' as an endpoint. @@ -23516,12 +25393,14 @@ void tetgenmesh::decidefeaturepointsizes() nextseg = testseg; // Adjust the direction of the nextseg. nextseg.shver = 0; - if (sorg(nextseg) != adjpt) sesymself(nextseg); + if (sorg(nextseg) != adjpt) { + sesymself(nextseg); + } assert(sorg(nextseg) == adjpt); adjpt = sdest(nextseg); } } - } else if (pointtype(adjpt) == FREEFACETVERTEX) { + } else if (pointtype(adjpt) == FREEFACETVERTEX) { // Ignore a Steiner point on facet. continue; } else if (pointtype(adjpt) == FREEVOLVERTEX) { @@ -23539,8 +25418,8 @@ void tetgenmesh::decidefeaturepointsizes() for (i = 0; i < tetlist->objects; i++) { parytet = (triface *) fastlookup(tetlist, i); for (j = 0; j < 3; j++) { - if (issubseg(*parytet)) { - tsspivot1(*parytet, checkseg); + tsspivot1(*parytet, checkseg); + if (checkseg.sh != NULL) { e1 = sorg(checkseg); e2 = sdest(checkseg); // Only do calculation if the projeciton of 'p' lies inside the @@ -23565,8 +25444,8 @@ void tetgenmesh::decidefeaturepointsizes() lfs_2 = lfs_0; for (i = 0; i < tetlist->objects; i++) { parytet = (triface *) fastlookup(tetlist, i); - if (issubface(*parytet)) { - tspivot(*parytet, checksh); + tspivot(*parytet, checksh); + if (checksh.sh != NULL) { adjpt = sorg(checksh); e1 = sdest(checksh); e2 = sapex(checksh); @@ -23630,9 +25509,9 @@ void tetgenmesh::decidefeaturepointsizes() printf(" %d feature points.\n", featurecount); } - // Second only assign sizes for all Steiner points which were inserted on - // sharp segments. The sizes are interpolated from the endpoints of - // the segments. + // Second only assign sizes for all Steiner points. A Steiner point p + // inserted on a sharp segment s is assigned a size by interpolating + // the sizes of the original endpoints of s. featurecount = 0; points->traversalinit(); ploop = pointtraverse(); @@ -23647,6 +25526,8 @@ void tetgenmesh::decidefeaturepointsizes() e1 = farsorg(checkseg); // The origin of this seg. e2 = farsdest(checkseg); // The dest of this seg. if (b->nobisect) { // '-Y' option. + assert(e1[pointmtrindex] > 0); // SELF_CHECK + assert(e2[pointmtrindex] > 0); // SELF_CHECK featureflag = 1; } else { if ((e1[pointmtrindex] > 0) && (e2[pointmtrindex] > 0)) { @@ -23660,7 +25541,67 @@ void tetgenmesh::decidefeaturepointsizes() + (lfs_0 / len) * (e2[pointmtrindex] - e1[pointmtrindex]); featurecount++; } // if (featureflag) - } + } else if (pointtype(ploop) == FREEFACETVERTEX) { + if (b->nobisect) { // -Y option. + // Collect vertices in the Star(p) which are also in the facet + // containing p. + point2shorg(ploop, parentsh); + checksh = parentsh; + while (1) { + assert(sorg(checksh) == ploop); + adjpt = sdest(checksh); + // Collect this vertex. + verlist->newindex((void **) &parypt); + *parypt = adjpt; + // Go to the next subface at p. (counterclockwise) + senext2self(checksh); + spivotself(checksh); + assert(checksh.sh != NULL); + if (checksh.sh == parentsh.sh) break; + if (sorg(checksh) != ploop) sesymself(checksh); + } + assert(verlist->objects > 0); + // Using Shepard interpolation (p=1) to interpolate the size for 'p'. + // Re-use len, lfs_0, lfs_1, lfs_2; + lfs_1 = lfs_2 = 0; + for (i = 0; i < verlist->objects; i++) { + parypt = (point *) fastlookup(verlist, i); + adjpt = *parypt; + if (adjpt[pointmtrindex] > 0) { + len = distance(adjpt, ploop); + lfs_0 = 1.0 / len; + lfs_1 += lfs_0 * adjpt[pointmtrindex]; + lfs_2 += lfs_0; + } + } + assert(lfs_2 > 0); + ploop[pointmtrindex] = lfs_1 / lfs_2; + verlist->restart(); + featurecount++; + } // if (b->nobisect) + } else if (pointtype(ploop) == FREEVOLVERTEX) { + if (b->nobisect) { // -Y option. + getvertexstar(1, ploop, tetlist, verlist, NULL); + // Using Shepard interpolation to interpolate the size for 'p'. + // Re-use len, lfs_0, lfs_1, lfs_2; + lfs_1 = lfs_2 = 0; + for (i = 0; i < verlist->objects; i++) { + parypt = (point *) fastlookup(verlist, i); + adjpt = *parypt; + if (adjpt[pointmtrindex] > 0) { + len = distance(adjpt, ploop); + lfs_0 = 1.0 / len; + lfs_1 += lfs_0 * adjpt[pointmtrindex]; + lfs_2 += lfs_0; + } + } + assert(lfs_2 > 0); + ploop[pointmtrindex] = lfs_1 / lfs_2; + tetlist->restart(); + verlist->restart(); + featurecount++; + } // if (b->nobisect) + } } // if (ploop[pointmtrindex] == 0.0) ploop = pointtraverse(); } @@ -23670,8 +25611,7 @@ void tetgenmesh::decidefeaturepointsizes() } if (checkconstraints) { - // A .var file exists. Adjust feature sizes. And make sure that every - // corner of a constraining facet get a size. + // A .var file exists. Adjust feature sizes. if (in->facetconstraintlist) { // Have facet area constrains. subfaces->traversalinit(); @@ -23687,9 +25627,6 @@ void tetgenmesh::decidefeaturepointsizes() if (ploop[pointmtrindex] > varlen) { ploop[pointmtrindex] = varlen; } - } else { - // This corner has no size yet. Set it. - ploop[pointmtrindex] = varlen; } } // j } @@ -23710,8 +25647,6 @@ void tetgenmesh::decidefeaturepointsizes() if (ploop[pointmtrindex] > varlen) { ploop[pointmtrindex] = varlen; } - } else { - ploop[pointmtrindex] = varlen; } } // j } @@ -23729,24 +25664,22 @@ void tetgenmesh::decidefeaturepointsizes() int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) { - // Check if the point lies inside the diametrical sphere of this seg. - REAL v1[3], v2[3]; + REAL ang; + REAL prjpt[3], u, v, t; - v1[0] = pa[0] - checkpt[0]; - v1[1] = pa[1] - checkpt[1]; - v1[2] = pa[2] - checkpt[2]; - v2[0] = pb[0] - checkpt[0]; - v2[1] = pb[1] - checkpt[1]; - v2[2] = pb[2] - checkpt[2]; + // Check if the point lies inside the diametrical sphere of this seg. + ang = interiorangle(checkpt, pa, pb, NULL); + ang *= 2.0; // Compare it to PI/2 (90 degree). - if (dot(v1, v2) < 0) { + if (ang > PI) { // Inside. if (b->metric || b->nobisect) { // -m or -Y option. if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) { - // The projection of 'checkpt' lies inside the segment [a,b]. - REAL prjpt[3], u, v, t; + // In this case, we're sure that the projection of 'checkpt' lies + // inside the segment [a,b]. Check if 'checkpt' lies inside the + // protecting region of this seg. projpt2edge(checkpt, pa, pb, prjpt); - // Interoplate the mesh size at the location 'prjpt'. + // Get the mesh size at the location 'prjpt'. u = distance(pa, pb); v = distance(pa, prjpt); t = v / u; @@ -23774,7 +25707,6 @@ int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) // A segment needs to be split if it is in the following case: // // (1) It is encroached by an existing vertex. // // (2) It has bad quality (too long). // -// (3) Its length is larger than the mesh sizes at its endpoints. // // // // Return 1 if it needs to be split, otherwise, return 0. 'pencpt' returns // // an encroaching point if there exists. 'qflag' returns '1' if the segment // @@ -23784,11 +25716,21 @@ int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) { - REAL ccent[3], len, r; + triface searchtet, spintet; + point forg, fdest, eapex; + REAL ccent[3], len, r, d, diff; int i; - point forg = sorg(*chkseg); - point fdest = sdest(*chkseg); + REAL ti, tj, t, midpt[3]; + REAL ang; + int eid; + + forg = sorg(*chkseg); + fdest = sdest(*chkseg); + + if (b->verbose > 2) { + printf(" Check segment (%d, %d)\n", pointmark(forg), pointmark(fdest)); + } // Initialize the return values. encpt = NULL; @@ -23803,13 +25745,21 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) // First check its quality. if (checkconstraints && (areabound(*chkseg) > 0.0)) { if (len > areabound(*chkseg)) { + if (b->verbose > 2) { + printf(" has too large size, len = %g (> %g)\n", len, + areabound(*chkseg)); + } qflag = 1; return 1; } } - if (b->fixedvolume) { + if (b->fixedvolume) { // if (b->varvolume || b->fixedvolume) { if ((len * len * len) > b->maxvolume) { + if (b->verbose > 2) { + printf(" has too large size, len^3 = %g (> %g)\n", len*len*len, + b->maxvolume); + } qflag = 1; return 1; } @@ -23824,13 +25774,33 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) } } + if (b->psc) { + // Check if it satisfies the approximation requirement. + eid = shellmark(*chkseg); + if ((pointtype(forg) == ACUTEVERTEX)||(pointtype(forg) == RIDGEVERTEX)) { + ti = in->getvertexparamonedge(in->geomhandle, pointmark(forg), eid); + } else { + ti = pointgeomuv(forg, 0); + } + if ((pointtype(fdest) == ACUTEVERTEX)||(pointtype(fdest) == RIDGEVERTEX)) { + tj = in->getvertexparamonedge(in->geomhandle, pointmark(fdest), eid); + } else { + tj = pointgeomuv(fdest, 0); + } + t = 0.5 * (ti + tj); + in->getsteineronedge(in->geomhandle, eid, t, midpt); + ang = interiorangle(midpt, forg, fdest, NULL) / PI * 180.0; + if (ang < b->facet_ang_tol) { + // Refine this segment. + if (b->verbose > 2) { + printf(" has bad approx, ang = %g\n", ang); + } + qflag = 1; + return 1; + } + } // if (b->psc) // Second check if it is encroached. - triface searchtet, spintet; - point eapex; - REAL d, diff; - int t1ver; - sstpivot1(*chkseg, searchtet); spintet = searchtet; while (1) { @@ -23850,6 +25820,9 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) } // while (1) if (encpt != NULL) { + if (b->verbose > 2) { + printf(" is encroached by %d\n", pointmark(encpt)); + } return 1; } @@ -23871,9 +25844,21 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) int tetgenmesh::splitsegment(face *splitseg, point encpt, int qflag, int chkencflag) { - point pa = sorg(*splitseg); - point pb = sdest(*splitseg); - REAL len = distance(pa, pb); + triface searchtet; + face searchsh; + point newpt, pa, pb; + insertvertexflags ivf; + REAL len; //, len1; + int loc; + //int i; + + pa = sorg(*splitseg); + pb = sdest(*splitseg); + len = distance(pa, pb); + + if (b->verbose > 2) { + printf(" Split segment (%d, %d).\n", pointmark(pa), pointmark(pb)); + } if (qflag == 0) { @@ -23882,58 +25867,77 @@ int tetgenmesh::splitsegment(face *splitseg, point encpt, int qflag, // Avoid creating too many Steiner points. return 0; } - // Quickly check if we CAN split this segment. - if (encpt == NULL) { - // Do not split this segment if the length is smaller than the mesh - // size at one of its endpoints. - if ((len < pa[pointmtrindex]) || (len < pb[pointmtrindex])) { - return 0; - } - } } - triface searchtet; - face searchsh; - point newpt; - insertvertexflags ivf; + // Quickly check if we CAN split this segment. + if ((encpt == NULL) && (qflag == 0)) { + // Do not split this segment if the length is smaller than the mesh + // size at one of its endpoints. + if ((len < pa[pointmtrindex]) || (len < pb[pointmtrindex])) { + return 0; + } + } makepoint(&newpt, FREESEGVERTEX); getsteinerptonsegment(splitseg, encpt, newpt); - // Split the segment by the Bowyer-Watson algorithm. + + // Split the segment by the "Bowyer-Watson" algorithm. + // Parameters are chosen as follows: + // - bowywat = 3, preserve subsegments and subfaces; + // - flipflag = 3, check star & link facets for flipping; + // - rejflag = 0, do insertion even if it encoraches upon + // other subsegments or subfaces. sstpivot1(*splitseg, searchtet); ivf.iloc = (int) ONEDGE; - ivf.bowywat = 3; // Use Bowyer-Watson algorithm; - ivf.validflag = 1; // Validate the B-W cavity. - ivf.lawson = b->conforming ? 2 : 0; // -D - ivf.rejflag = 0; // Do not check encroachment of new segments/facets. + if (b->psc) { + ivf.bowywat = 0; // Do not enlarge the initial cavity. + ivf.validflag = 0; // Do not validate the initial cavity. + } else { + ivf.bowywat = 3; // Use the "Bowyer-Watson" algorithm to form cavity. + ivf.validflag = 1; // Validate the B-W cavity. + } + ivf.lawson = 3; + ivf.rejflag = 0; if ((encpt == NULL) && (qflag == 0)) { - ivf.rejflag |= 4; // Do check encroachment of protecting balls. + // Do not insert the point if it lies inside some protecting balls. + ivf.rejflag |= 4; } ivf.chkencflag = chkencflag; - ivf.sloc = (int) INSTAR; // ivf.iloc; - ivf.sbowywat = 3; // ivf.bowywat; // Surface mesh options. + ivf.sloc = ivf.iloc; + ivf.sbowywat = ivf.bowywat; // Surface mesh options. ivf.splitbdflag = 1; ivf.respectbdflag = 1; ivf.assignmeshsize = 1; - - - if (insertpoint(newpt, &searchtet, &searchsh, splitseg, &ivf)) { + loc = insertvertex(newpt, &searchtet, &searchsh, splitseg, &ivf); + + // The new vertex should not too close to an existing point. + if (loc == (int) NEARVERTEX) { + outnodes(0); + outsubfaces(0); + outsubsegments(0); + assert(0); + } else if (loc == ENCVERTEX) { + // The point lies in some protecting balls. Rejected. + pointdealloc(newpt); + } else if (loc == (int) BADELEMENT) { + // Failed to create a valid sub-cavity in surface mesh. + pointdealloc(newpt); + //prob_subseg_count++; + } else if (loc == (int) ONEDGE) { + // Flip not locally Delaunay link facets by the 'Lawson's algo'. + lawsonflip3d(newpt, 4, 0, chkencflag, 0); st_segref_count++; if (steinerleft > 0) steinerleft--; - if (flipstack != NULL) { - flipconstraints fc; - fc.chkencflag = chkencflag; - fc.enqflag = 2; - lawsonflip3d(&fc); - unflipqueue->restart(); - } return 1; } else { - // Point is not inserted. - pointdealloc(newpt); - return 0; + // The vertex was not inserted. For unknown reasons. + //pointdealloc(newpt); + assert(0); } + + // Should not be here. + return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -23944,7 +25948,7 @@ int tetgenmesh::splitsegment(face *splitseg, point encpt, int qflag, void tetgenmesh::repairencsegs(int chkencflag) { - face *bface; + badface *bface; point encpt = NULL; int qflag = 0; @@ -23952,25 +25956,20 @@ void tetgenmesh::repairencsegs(int chkencflag) // if an unlimited number of Steiner points is allowed. while ((badsubsegs->items > 0) && (steinerleft != 0)) { badsubsegs->traversalinit(); - bface = (face *) badsubsegs->traverse(); + bface = badfacetraverse(badsubsegs); while ((bface != NULL) && (steinerleft != 0)) { - // Skip a deleleted element. - if (bface->shver >= 0) { - // A queued segment may have been deleted (split). - if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { - // A queued segment may have been processed. - if (smarktest2ed(*bface)) { - sunmarktest2(*bface); - if (checkseg4split(bface, encpt, qflag)) { - splitsegment(bface, encpt, qflag, chkencflag); - } + // A queued segment may have been deleted (split). + if (bface->ss.sh[3] != NULL) { + // A queued segment may have been processed. + if (smarktest2ed(bface->ss)) { + sunmarktest2(bface->ss); + if (checkseg4split(&(bface->ss), encpt, qflag)) { + splitsegment(&(bface->ss), encpt, qflag, chkencflag); } } - // Remove this entry from list. - bface->shver = -1; // Signal it as a deleted element. - badsubsegs->dealloc((void *) bface); } - bface = (face *) badsubsegs->traverse(); + badfacedealloc(badsubsegs, bface); // Remove this entry from list. + bface = badfacetraverse(badsubsegs); } } @@ -23983,37 +25982,19 @@ void tetgenmesh::repairencsegs(int chkencflag) assert(0); // Unknown case. } badsubsegs->traversalinit(); - bface = (face *) badsubsegs->traverse(); + bface = badfacetraverse(badsubsegs); while (bface != NULL) { - // Skip a deleleted element. - if (bface->shver >= 0) { - if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { - if (smarktest2ed(*bface)) { - sunmarktest2(*bface); - } + if (bface->ss.sh[3] != NULL) { + if (smarktest2ed(bface->ss)) { + sunmarktest2(bface->ss); } } - bface = (face *) badsubsegs->traverse(); + bface = badfacetraverse(badsubsegs); } badsubsegs->restart(); } } -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueuesubface() Queue a subface or a subsegment for encroachment chk. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::enqueuesubface(memorypool *pool, face *chkface) -{ - if (!smarktest2ed(*chkface)) { - smarktest2(*chkface); // Only queue it once. - face *queface = (face *) pool->alloc(); - *queface = *chkface; - } -} - /////////////////////////////////////////////////////////////////////////////// // // // checkfac4encroach() Check if a subface is encroached by a point. // @@ -24024,6 +26005,8 @@ int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, REAL* cent, REAL* r) { REAL rd, len; + REAL prjpt[3], n[3]; + REAL a, a1, a2, a3; circumsphere(pa, pb, pc, NULL, cent, &rd); assert(rd != 0); @@ -24036,8 +26019,6 @@ int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) && (pc[pointmtrindex] > 0)) { // Get the projection of 'checkpt' in the plane of pa, pb, and pc. - REAL prjpt[3], n[3]; - REAL a, a1, a2, a3; projpt2face(checkpt, pa, pb, pc, prjpt); // Get the face area of [a,b,c]. facenormal(pa, pb, pc, n, 1, NULL); @@ -24059,6 +26040,10 @@ int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, if (len < rd) { return 1; // Encroached. } + } else { + // The projection lies outside the face. + // In this case, 'p' must close to another face or a segment than + // to this one. We ignore this boundary face. } } else { return 1; // No protecting ball. Encroached. @@ -24089,10 +26074,14 @@ int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, REAL *cent) { + triface searchtet; + face checksh; // *parysh; + face checkseg; point pa, pb, pc; - REAL area, rd, len; + REAL area, rd, len, sintheta; REAL A[4][4], rhs[4], D; int indx[4]; + REAL elen[3]; int i; encpt = NULL; @@ -24102,6 +26091,11 @@ int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, pb = sdest(*chkfac); pc = sapex(*chkfac); + if (b->verbose > 2) { + printf(" Check subface (%d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc)); + } + // Compute the coefficient matrix A (3x3). A[0][0] = pb[0] - pa[0]; A[0][1] = pb[1] - pa[1]; @@ -24114,56 +26108,100 @@ int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, area = 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c]. // Compute the right hand side vector b (3x1). - rhs[0] = 0.5 * dot(A[0], A[0]); // edge [a,b] - rhs[1] = 0.5 * dot(A[1], A[1]); // edge [a,c] + elen[0] = dot(A[0], A[0]); // edge [a,b] + elen[1] = dot(A[1], A[1]); // edge [a,c] + rhs[0] = 0.5 * elen[0]; + rhs[1] = 0.5 * elen[1]; rhs[2] = 0.0; // Solve the 3 by 3 equations use LU decomposition with partial - // pivoting and backward and forward substitute. - if (!lu_decmp(A, 3, indx, &D, 0)) { - // A degenerate triangle. - return 0; - } + // pivoting and backward and forward substitute.. + if (lu_decmp(A, 3, indx, &D, 0)) { + lu_solve(A, 3, indx, rhs, 0); + cent[0] = pa[0] + rhs[0]; + cent[1] = pa[1] + rhs[1]; + cent[2] = pa[2] + rhs[2]; + rd = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); - lu_solve(A, 3, indx, rhs, 0); - cent[0] = pa[0] + rhs[0]; - cent[1] = pa[1] + rhs[1]; - cent[2] = pa[2] + rhs[2]; - rd = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); - - if (checkconstraints && (areabound(*chkfac) > 0.0)) { - // Check if the subface has too big area. - if (area > areabound(*chkfac)) { - qflag = 1; - return 1; + if (b->verbose > 2) { + printf(" circent: (%g, %g, %g)\n", cent[0], cent[1], cent[2]); + printf(" cirradi: %g\n", rd); } - } - if (b->metric) { // -m option. Check mesh size. - // Check if the ccent lies outside one of the prot.balls at vertices. - if (((pa[pointmtrindex] > 0) && (rd > pa[pointmtrindex])) || - ((pb[pointmtrindex] > 0) && (rd > pb[pointmtrindex])) || - ((pc[pointmtrindex] > 0) && (rd > pc[pointmtrindex]))) { - qflag = 1; // Enforce mesh size. - return 1; + // Check the quality (radius-edge ratio) of this subface. + // Re-use variables 'A', 'rhs', and 'D'. + A[2][0] = pb[0] - pc[0]; + A[2][1] = pb[1] - pc[1]; + A[2][2] = pb[2] - pc[2]; + elen[2] = dot(A[2], A[2]); // edge [b,c] + // Get the shortest edge length in 'D'. + D = elen[0]; // edge [a,b] + for (i = 1; i < 3; i++) { + if (D > elen[i]) D = elen[i]; + } + + + D = sqrt(D); + if (b->verbose > 2) { + printf(" shortest edge length = %g\n", D); + } + + rhs[3] = rd / D; // The radius-edge ratio. + + // Check if this subface is nearly degenerate. + sintheta = 1.0 / (2.0 * rhs[3]); + if (sintheta < sintheta_tol) { + // Do not split this subface. Save it in list. + if (b->verbose > 1) { + printf(" !! A degenerated subface, theta = %g (deg)\n", + asin(sintheta) / PI * 180.0); + } + return 0; // Do not split a degenerated subface. + } + + if (checkconstraints && (areabound(*chkfac) > 0.0)) { + // Check if the subface has too big area. + if (area > areabound(*chkfac)) { + if (b->verbose > 2) { + printf(" has too big area: %g (> %g)\n", area, + areabound(*chkfac)); + } + qflag = 1; + return 1; + } } - } - - triface searchtet; - // Check if this subface is locally encroached. - for (i = 0; i < 2; i++) { - stpivot(*chkfac, searchtet); - if (!ishulltet(searchtet)) { - len = distance(oppo(searchtet), cent); - if ((fabs(len - rd) / rd) < b->epsilon) len = rd;// Rounding. - if (len < rd) { - encpt = oppo(searchtet); + if (b->metric) { // -m option. Check mesh size. + // Check if the ccent lies outside one of the prot.balls at vertices. + if (((pa[pointmtrindex] > 0) && (rd > pa[pointmtrindex])) || + ((pb[pointmtrindex] > 0) && (rd > pb[pointmtrindex])) || + ((pc[pointmtrindex] > 0) && (rd > pc[pointmtrindex]))) { + qflag = 1; // Enforce mesh size. return 1; } } - sesymself(*chkfac); - } + + + // Check if this subface is locally encroached. + for (i = 0; i < 2; i++) { + stpivot(*chkfac, searchtet); + if (!ishulltet(searchtet)) { + len = distance(oppo(searchtet), cent); + if ((fabs(len - rd) / rd) < b->epsilon) len = rd;// Rounding. + if (len < rd) { + if (b->verbose > 2) { + printf(" is encroached by point %d\n", + pointmark(oppo(searchtet))); + } + encpt = oppo(searchtet); + return 1; + } + } + sesymself(*chkfac); + } + } else { + assert(0); + } // if (!lu_decomp) return 0; } @@ -24186,9 +26224,26 @@ int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag, REAL *ccent, int chkencflag) { - point pa = sorg(*splitfac); - point pb = sdest(*splitfac); - point pc = sapex(*splitfac); + badface *bface; + triface searchtet; + face searchsh; + face checkseg, *paryseg; + point newpt, pa, pb, pc; + insertvertexflags ivf; + REAL rd; + int splitflag; + int loc; + int i; + + + pa = sorg(*splitfac); + pb = sdest(*splitfac); + pc = sapex(*splitfac); + + if (b->verbose > 2) { + printf(" Split subface (%d, %d, %d).\n", pointmark(pa), pointmark(pb), + pointmark(pc)); + } // Quickly check if we CAN split this subface. @@ -24200,58 +26255,54 @@ int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag, } // Do not split this subface if the 'ccent' lies inside the protect balls // of one of its vertices. - REAL rd = distance(ccent, pa); + rd = distance(ccent, pa); if ((rd <= pa[pointmtrindex]) || (rd <= pb[pointmtrindex]) || (rd <= pc[pointmtrindex])) { + if (b->verbose > 2) { + printf(" Encroaching a protecting ball. Rejected.\n"); + } return 0; } } - face searchsh; - insertvertexflags ivf; - point newpt; - int i; - // Initialize the inserting point. makepoint(&newpt, FREEFACETVERTEX); - // Split the subface at its circumcenter. - for (i = 0; i < 3; i++) newpt[i] = ccent[i]; - // Search a subface which contains 'newpt'. - searchsh = *splitfac; - // Calculate an above point. It lies above the plane containing - // the subface [a,b,c], and save it in dummypoint. Moreover, - // the vector cent->dummypoint is the normal of the plane. - calculateabovepoint4(newpt, pa, pb, pc); - // Parameters: 'aflag' = 1, - above point exists. - // 'cflag' = 0, - non-convex, check co-planarity of the result. - // 'rflag' = 0, - no need to round the locating result. - ivf.iloc = (int) slocate(newpt, &searchsh, 1, 0, 0); - - if (!((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE))) { - pointdealloc(newpt); - return 0; + if (0) { + } else { + // Split the subface at its circumcenter. + for (i = 0; i < 3; i++) newpt[i] = ccent[i]; + // Search a subface which contains 'newpt'. + searchsh = *splitfac; + // Calculate an above point. It lies above the plane containing + // the subface [a,b,c], and save it in dummypoint. Moreover, + // the vector cent->dummypoint is the normal of the plane. + calculateabovepoint4(newpt, pa, pb, pc); + // Parameters: 'aflag' = 1, - above point exists. + // 'cflag' = 0, - non-convex, check co-planarity of the result. + // 'rflag' = 0, - no need to round the locating result. + ivf.iloc = (int) slocate(newpt, &searchsh, 1, 0, 0); + if ((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE)) { + // Insert this point. + } else { + pointdealloc(newpt); + return 0; + } } - - triface searchtet; - face *paryseg; - int splitflag; - // Insert the point. stpivot(searchsh, searchtet); //assert((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE)); - // Split the subface by the Bowyer-Watson algorithm. - ivf.bowywat = 3; // - //ivf.lawson = b->conforming ? 3 : 1; - ivf.lawson = b->conforming ? 2 : 0; - ivf.rejflag = 1; // Do check the encroachment of segments. + // Split the subface by the "Bowyer-Watson" algorithm. + ivf.bowywat = 3; // Form B-W cavity. + ivf.lawson = 3; // Queue faces of the cavity for flipping. + ivf.rejflag = 1; // Reject it if it encroached upon any segment. if (qflag == 0) { ivf.rejflag |= 4; // Reject it if it encroached upon any vertex. } ivf.chkencflag = chkencflag; - ivf.sloc = (int) INSTAR; // ivf.iloc; - ivf.sbowywat = 3; // ivf.bowywat; + ivf.sloc = ivf.iloc; + ivf.sbowywat = ivf.bowywat; ivf.splitbdflag = 1; ivf.validflag = 1; ivf.respectbdflag = 1; @@ -24260,45 +26311,59 @@ int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag, ivf.refineflag = 2; ivf.refinesh = searchsh; + loc = insertvertex(newpt, &searchtet, &searchsh, NULL, &ivf); - if (insertpoint(newpt, &searchtet, &searchsh, NULL, &ivf)) { - st_facref_count++; - if (steinerleft > 0) steinerleft--; - if (flipstack != NULL) { - flipconstraints fc; - fc.chkencflag = chkencflag; - fc.enqflag = 2; - lawsonflip3d(&fc); - unflipqueue->restart(); - } - return 1; - } else { - // Point was not inserted. + if (loc == (int) ENCSEGMENT) { + // The new point encroaches upon some segments. pointdealloc(newpt); - if (ivf.iloc == (int) ENCSEGMENT) { - // Select an encroached segment and split it. - splitflag = 0; - for (i = 0; i < encseglist->objects; i++) { - paryseg = (face *) fastlookup(encseglist, i); - if (splitsegment(paryseg, NULL, qflag, chkencflag | 1)) { - splitflag = 1; // A point is inserted on a segment. - break; - } + assert(encseglist->objects > 0); + // Select an encroached segment and split it. + splitflag = 0; + for (i = 0; i < encseglist->objects; i++) { + paryseg = (face *) fastlookup(encseglist, i); + if (splitsegment(paryseg, NULL, qflag, chkencflag | 1)) { + splitflag = 1; // A point is inserted on a segment. + break; } - encseglist->restart(); - if (splitflag) { - // Some segments may need to be repaired. - repairencsegs(chkencflag | 1); - // Queue this subface if it is still alive and not queued. - //if (splitfac->sh[3] != NULL) { - // enqueuesubface(badsubfacs, splitfac); - //} + } + encseglist->restart(); + if (splitflag) { + // Some segments may need to be repaired. + repairencsegs(chkencflag | 1); + // Queue this subface if it is still alive and not queued. + if (splitfac->sh[3] != NULL) { + if (!smarktest2ed(*splitfac)) { + bface = (badface *) badsubfacs->alloc(); + bface->ss = *splitfac; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(*splitfac); // An alive badface. + } } - return splitflag; - } else { - return 0; } + return splitflag; + } else if (loc == (int) ENCVERTEX) { + // The point lies inside some protecting balls. Rejected. + pointdealloc(newpt); + } else if (loc == (int) ONVERTEX) { + pointdealloc(newpt); + } else if (loc == (int) NEARVERTEX) { + pointdealloc(newpt); + } else if (loc == (int) BADELEMENT) { + // Failed to create a valid sub-cavity in surface mesh. + pointdealloc(newpt); + } else if (loc == (int) ivf.iloc) { + // Flip not locally Delaunay link facets. + lawsonflip3d(newpt, 4, 0, chkencflag, 0); + st_facref_count++; + if (steinerleft > 0) steinerleft--; + return 1; // A point is inserted on facet. + } else { + // Unknown error. + assert(0); } + + // Should not be here. + return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -24309,7 +26374,7 @@ int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag, void tetgenmesh::repairencfacs(int chkencflag) { - face *bface; + badface *bface; point encpt = NULL; int qflag = 0; REAL ccent[3]; @@ -24318,24 +26383,20 @@ void tetgenmesh::repairencfacs(int chkencflag) // if an unlimited number of Steiner points is allowed. while ((badsubfacs->items > 0) && (steinerleft != 0)) { badsubfacs->traversalinit(); - bface = (face *) badsubfacs->traverse(); + bface = badfacetraverse(badsubfacs); while ((bface != NULL) && (steinerleft != 0)) { - // Skip a deleted element. - if (bface->shver >= 0) { - // A queued subface may have been deleted (split). - if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { - // A queued subface may have been processed. - if (smarktest2ed(*bface)) { - sunmarktest2(*bface); - if (checkfac4split(bface, encpt, qflag, ccent)) { - splitsubface(bface, encpt, qflag, ccent, chkencflag); - } + // A queued subface may have been deleted (split). + if (bface->ss.sh[3] != NULL) { + // A queued subface may have been processed. + if (smarktest2ed(bface->ss)) { + sunmarktest2(bface->ss); + if (checkfac4split(&(bface->ss), encpt, qflag, ccent)) { + splitsubface(&(bface->ss), encpt, qflag, ccent, chkencflag); } } - bface->shver = -1; // Signal it as a deleted element. - badsubfacs->dealloc((void *) bface); // Remove this entry from list. } - bface = (face *) badsubfacs->traverse(); + badfacedealloc(badsubfacs, bface); // Remove this entry from list. + bface = badfacetraverse(badsubfacs); } } @@ -24348,37 +26409,19 @@ void tetgenmesh::repairencfacs(int chkencflag) assert(0); // Unknown case. } badsubfacs->traversalinit(); - bface = (face *) badsubfacs->traverse(); + bface = badfacetraverse(badsubfacs); while (bface != NULL) { - // Skip a deleted element. - if (bface->shver >= 0) { - if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { - if (smarktest2ed(*bface)) { - sunmarktest2(*bface); - } + if (bface->ss.sh[3] != NULL) { + if (smarktest2ed(bface->ss)) { + sunmarktest2(bface->ss); } } - bface = (face *) badsubfacs->traverse(); + bface = badfacetraverse(badsubfacs); } badsubfacs->restart(); } } -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueuetetrahedron() Queue a tetrahedron for quality check. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::enqueuetetrahedron(triface *chktet) -{ - if (!marktest2ed(*chktet)) { - marktest2(*chktet); // Only queue it once. - triface *quetet = (triface *) badtetrahedrons->alloc(); - *quetet = *chktet; - } -} - /////////////////////////////////////////////////////////////////////////////// // // // checktet4split() Check if the tet needs to be split. // @@ -24407,6 +26450,11 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) pb = (point) chktet->tet[5]; pc = (point) chktet->tet[6]; + if (b->verbose > 2) { + printf(" Check tet (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } + // Get the edge vectors vda: d->a, vdb: d->b, vdc: d->c. // Set the matrix A = [vda, vdb, vdc]^T. for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; @@ -24420,6 +26468,9 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) if (!lu_decmp(A, 3, indx, &D, 0)) { // A degenerated tet (vol = 0). + if (b->verbose > 2) { + printf(" Min dihed = 0 (degree)\n"); + } // Return its barycenter. for (i = 0; i < 3; i++) { ccent[i] = 0.25 * (pa[i] + pb[i] + pc[i] + pd[i]); @@ -24430,6 +26481,9 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) // Check volume if '-a#' and '-a' options are used. if (b->varvolume || b->fixedvolume) { vol = fabs(A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; + if (b->verbose > 2) { + printf(" volume = %g.\n", vol); + } if (b->fixedvolume) { if (vol > b->maxvolume) { qflag = 1; @@ -24491,6 +26545,9 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) } smlen = sqrt(smlen); D = rd / smlen; + if (b->verbose > 2) { + printf(" Ratio-edge ratio = %g, smlen = %g\n", D, smlen); + } if (D > b->minratio) { // A bad radius-edge ratio. return 1; @@ -24529,6 +26586,9 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) maxcosd = (cosd[i] > maxcosd ? cosd[i] : maxcosd); //mincosd = (cosd[i] < mincosd ? cosd[i] : maxcosd); } + if (b->verbose > 2) { + printf(" Min dihed = %g (degree)\n", acos(maxcosd) / PI * 180.0); + } if (maxcosd > cosmindihed) { // Calculate the circumcenter of this tet. // A bad dihedral angle. @@ -24576,14 +26636,22 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, int chkencflag) { - triface searchtet; - face *paryseg; - point newpt, *ppt; badface *bface; + triface searchtet; + face checkseg, *paryseg; + point newpt, pa, *ppt = NULL; insertvertexflags ivf; + REAL rd; int splitflag; + int loc; int i; + if (b->verbose > 2) { + ppt = (point *) &(splittet->tet[4]); + printf(" Split tet (%d, %d, %d, %d).\n", pointmark(ppt[0]), + pointmark(ppt[1]), pointmark(ppt[2]), pointmark(ppt[3])); + } + if (qflag == 0) { // It is a bad quality tet (not due to mesh size). @@ -24591,9 +26659,12 @@ int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, // Do a quick check if the 'ccent' lies inside the protect balls // of one of the vertices of this tet. ppt = (point *) &(splittet->tet[4]); - REAL rd = distance(ccent, ppt[0]); + rd = distance(ccent, ppt[0]); if ((rd <= ppt[0][pointmtrindex]) || (rd <= ppt[1][pointmtrindex]) || (rd <= ppt[2][pointmtrindex]) || (rd <= ppt[3][pointmtrindex])) { + if (b->verbose > 2) { + printf(" Encroaching a protecting ball. Rejected.\n"); + } return 0; } } @@ -24603,10 +26674,15 @@ int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, searchtet = *splittet; ivf.iloc = (int) OUTSIDE; - ivf.bowywat = 3; // Preserve subsegments and subfaces; - //ivf.lawson = b->conforming ? 3 : 1; - ivf.lawson = b->conforming ? 2 : 0; - ivf.rejflag = 3; // Do check for encroached segments and subfaces. + // Parameters are chosen as follows: + // - bowywat = 3, preserve subsegments and subfaces; + // - flipflag = 3, check star & link facets for flipping; + // - rejflag = 3, do not insert the point if it encroaches upon + // any segment or subface. + // - chkencflag = 4, (as input), only check tetrahedra. + ivf.bowywat = 3; + ivf.lawson = 3; + ivf.rejflag = 3; if (qflag == 0) { ivf.rejflag |= 4; // Reject it if it lies in some protecting balls. } @@ -24620,73 +26696,101 @@ int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, ivf.refineflag = 1; ivf.refinetet = *splittet; + loc = insertvertex(newpt, &searchtet, NULL, NULL, &ivf); - if (insertpoint(newpt, &searchtet, NULL, NULL, &ivf)) { - // Vertex is inserted. - st_volref_count++; - if (steinerleft > 0) steinerleft--; - if (flipstack != NULL) { - flipconstraints fc; - fc.chkencflag = chkencflag; - fc.enqflag = 2; - lawsonflip3d(&fc); - unflipqueue->restart(); + if (loc == (int) ENCSEGMENT) { + // There are encroached segments. + pointdealloc(newpt); + assert(encseglist->objects > 0); + splitflag = 0; + if (!b->nobisect) { // not -Y option + // Select an encroached segment and split it. + for (i = 0; i < encseglist->objects; i++) { + paryseg = (face *) fastlookup(encseglist, i); + if (splitsegment(paryseg, NULL, qflag, chkencflag | 3)) { + splitflag = 1; // A point is inserted on a segment. + break; + } + } + } // if (!b->nobisect) + encseglist->restart(); + if (splitflag) { + // Some segments may need to be repaired. + repairencsegs(chkencflag | 3); + // Some subfaces may need to be repaired. + repairencfacs(chkencflag | 2); + // Queue the tet if it is still alive and not queued. + if (splittet->tet[4] != NULL) { + if (!marktest2ed(*splittet)) { + bface = (badface *) badtetrahedrons->alloc(); + bface->tt = *splittet; + marktest2(bface->tt); // Only queue it once. + bface->forg = org(*splittet); // An alive badface. + } + } } - return 1; - } else { - // Point is not inserted. + return splitflag; + } else if (loc == (int) ENCSUBFACE) { + // There are encroached subfaces. pointdealloc(newpt); - // Check if there are encroached segments/subfaces. - if (ivf.iloc == (int) ENCSEGMENT) { - splitflag = 0; - if (!b->nobisect) { // not -Y option - // Select an encroached segment and split it. - for (i = 0; i < encseglist->objects; i++) { - paryseg = (face *) fastlookup(encseglist, i); - if (splitsegment(paryseg, NULL, qflag, chkencflag | 3)) { - splitflag = 1; // A point is inserted on a segment. - break; - } + assert(encshlist->objects > 0); + splitflag = 0; + if (!b->nobisect) { // not -Y option + // Select an encroached subface and split it. + for (i = 0; i < encshlist->objects; i++) { + bface = (badface *) fastlookup(encshlist, i); + if (splitsubface(&(bface->ss),NULL,qflag,bface->cent,chkencflag | 2)) { + splitflag = 1; // A point is inserted on a subface or a segment. + break; } - } // if (!b->nobisect) - encseglist->restart(); - if (splitflag) { - // Some segments may need to be repaired. - repairencsegs(chkencflag | 3); - // Some subfaces may need to be repaired. - repairencfacs(chkencflag | 2); - // Queue the tet if it is still alive and not queued. - //if (splittet->tet[4] != NULL) { - // enqueuetetrahedron(splittet); - //} - } - return splitflag; - } else if (ivf.iloc == (int) ENCSUBFACE) { - splitflag = 0; - if (!b->nobisect) { // not -Y option - // Select an encroached subface and split it. - for (i = 0; i < encshlist->objects; i++) { - bface = (badface *) fastlookup(encshlist, i); - if (splitsubface(&(bface->ss),NULL,qflag,bface->cent,chkencflag|2)){ - splitflag = 1; // A point is inserted on a subface or a segment. - break; - } + } + } // if (!b->nobisect) + encshlist->restart(); + if (splitflag) { + assert(badsubsegs->items == 0l); // repairencsegs(chkencflag | 3); + // Some subfaces may need to be repaired. + repairencfacs(chkencflag | 2); + // Queue the tet if it is still alive. + if (splittet->tet[4] != NULL) { + if (!marktest2ed(*splittet)) { + bface = (badface *) badtetrahedrons->alloc(); + bface->tt = *splittet; + marktest2(bface->tt); // Only queue it once. + bface->forg = org(*splittet); // An alive badface. } - } // if (!b->nobisect) - encshlist->restart(); - if (splitflag) { - assert(badsubsegs->items == 0l); - // Some subfaces may need to be repaired. - repairencfacs(chkencflag | 2); - // Queue the tet if it is still alive. - //if (splittet->tet[4] != NULL) { - // enqueuetetrahedron(splittet); - //} } - return splitflag; } - return 0; + return splitflag; + } else if (loc == (int) OUTSIDE) { + // There exists not boundary conforming segments/subfaces. + pointdealloc(newpt); + } else if (loc == (int) ONVERTEX) { + // Found a coincident vertex. It should be a Steiner point. + pa = org(searchtet); + assert(pointtype(pa) == FREEVOLVERTEX); + // Delete this new point. + pointdealloc(newpt); + } else if (loc == (int) NEARVERTEX) { + // The point lies very close to an existing point. + pa = point2ppt(newpt); + assert(pointtype(pa) == FREEVOLVERTEX); + // Delete this new point. + pointdealloc(newpt); + } else if (loc == (int) ENCVERTEX) { + // The new point encoraches upon some protecting balls. Rejected. + pointdealloc(newpt); + } else if (loc == (int) BADELEMENT) { + pointdealloc(newpt); + } else { + // Recover Delaunayness. + lawsonflip3d(newpt, 4, 0, chkencflag, 0); + // Vertex is inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + return 1; } + + return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -24697,7 +26801,7 @@ int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, void tetgenmesh::repairbadtets(int chkencflag) { - triface *bface; + badface *bface; REAL ccent[3]; int qflag = 0; @@ -24705,24 +26809,20 @@ void tetgenmesh::repairbadtets(int chkencflag) // if an unlimited number of Steiner points is allowed. while ((badtetrahedrons->items > 0) && (steinerleft != 0)) { badtetrahedrons->traversalinit(); - bface = (triface *) badtetrahedrons->traverse(); + bface = badfacetraverse(badtetrahedrons); while ((bface != NULL) && (steinerleft != 0)) { - // Skip a deleted element. - if (bface->ver >= 0) { - // A queued tet may have been deleted. - if (!isdeadtet(*bface)) { - // A queued tet may have been processed. - if (marktest2ed(*bface)) { - unmarktest2(*bface); - if (checktet4split(bface, qflag, ccent)) { - splittetrahedron(bface, qflag, ccent, chkencflag); - } + // A queued tet may have been deleted. + if (!isdeadtet(bface->tt)) { + // A queued tet may have been processed. + if (marktest2ed(bface->tt)) { + unmarktest2(bface->tt); + if (checktet4split(&(bface->tt), qflag, ccent)) { + splittetrahedron(&(bface->tt), qflag, ccent, chkencflag); } } - bface->ver = -1; // Signal it as a deleted element. - badtetrahedrons->dealloc((void *) bface); } - bface = (triface *) badtetrahedrons->traverse(); + badfacedealloc(badtetrahedrons, bface); + bface = badfacetraverse(badtetrahedrons); } } @@ -24736,17 +26836,14 @@ void tetgenmesh::repairbadtets(int chkencflag) } // Unmark all queued tet. badtetrahedrons->traversalinit(); - bface = (triface *) badtetrahedrons->traverse(); + bface = badfacetraverse(badtetrahedrons); while (bface != NULL) { - // Skip a deleted element. - if (bface->ver >= 0) { - if (!isdeadtet(*bface)) { - if (marktest2ed(*bface)) { - unmarktest2(*bface); - } + if (!isdeadtet(bface->tt)) { + if (marktest2ed(bface->tt)) { + unmarktest2(bface->tt); } } - bface = (triface *) badtetrahedrons->traverse(); + bface = badfacetraverse(badtetrahedrons); } // Clear the pool. badtetrahedrons->restart(); @@ -24761,6 +26858,7 @@ void tetgenmesh::repairbadtets(int chkencflag) void tetgenmesh::delaunayrefinement() { + badface *bface; triface checktet; face checksh; face checkseg; @@ -24768,16 +26866,13 @@ void tetgenmesh::delaunayrefinement() int chkencflag; long bak_segref_count, bak_facref_count, bak_volref_count; - long bak_flipcount = flip23count + flip32count + flip44count; if (!b->quiet) { printf("Refining mesh...\n"); } if (b->verbose) { - printf(" Min radiu-edge ratio = %g.\n", b->minratio); - printf(" Min dihedral angle = %g.\n", b->mindihedral); - //printf(" Min Edge length = %g.\n", b->minedgelength); + printf(" Edge length limit = %g.\n", b->minedgelength); } steinerleft = b->steinerleft; // Upperbound of # Steiner points (by -S#). @@ -24789,7 +26884,7 @@ void tetgenmesh::delaunayrefinement() } else { if (!b->quiet) { printf("\nWarning: "); - printf("The desired number of Steiner points (%d) has reached.\n\n", + printf("The desired number of Steiner points (%d) is reached.\n\n", b->steinerleft); } return; // No more Steiner points. @@ -24807,7 +26902,6 @@ void tetgenmesh::delaunayrefinement() encseglist = new arraypool(sizeof(face), 8); encshlist = new arraypool(sizeof(badface), 8); - if (!b->nobisect) { // if no '-Y' option if (b->verbose) { printf(" Splitting encroached subsegments.\n"); @@ -24817,14 +26911,17 @@ void tetgenmesh::delaunayrefinement() steinercount = points->items; // Initialize the pool of encroached subsegments. - badsubsegs = new memorypool(sizeof(face), b->shellfaceperblock, - sizeof(void *), 0); + badsubsegs = new memorypool(sizeof(badface), b->shellfaceperblock, + memorypool::POINTER, 0); // Add all segments into the pool. subsegs->traversalinit(); checkseg.sh = shellfacetraverse(subsegs); while (checkseg.sh != (shellface *) NULL) { - enqueuesubface(badsubsegs, &checkseg); + bface = (badface *) badsubsegs->alloc(); + bface->ss = checkseg; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checkseg); // An alive badface. checkseg.sh = shellfacetraverse(subsegs); } @@ -24847,14 +26944,17 @@ void tetgenmesh::delaunayrefinement() bak_facref_count = st_facref_count; // Initialize the pool of encroached subfaces. - badsubfacs = new memorypool(sizeof(face), b->shellfaceperblock, - sizeof(void *), 0); + badsubfacs = new memorypool(sizeof(badface), b->shellfaceperblock, + memorypool::POINTER, 0); // Add all subfaces into the pool. subfaces->traversalinit(); checksh.sh = shellfacetraverse(subfaces); while (checksh.sh != (shellface *) NULL) { - enqueuesubface(badsubfacs, &checksh); + bface = (badface *) badsubfacs->alloc(); + bface->ss = checksh; + smarktest2(bface->ss); // Only queue it once. + bface->forg = sorg(checksh); // An alive badface. checksh.sh = shellfacetraverse(subfaces); } @@ -24885,13 +26985,17 @@ void tetgenmesh::delaunayrefinement() cosmindihed = cos(b->mindihedral / 180.0 * PI); // Initialize the pool of bad quality tetrahedra. - badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock, - sizeof(void *), 0); + badtetrahedrons = new memorypool(sizeof(badface), b->tetrahedraperblock, + memorypool::POINTER, 0); + // Add all tetrahedra (no hull tets) into the pool. tetrahedrons->traversalinit(); checktet.tet = tetrahedrontraverse(); while (checktet.tet != NULL) { - enqueuetetrahedron(&checktet); + bface = (badface *) badtetrahedrons->alloc(); + bface->tt = checktet; + marktest2(bface->tt); // Only queue it once. + bface->forg = org(checktet); // An alive badface. checktet.tet = tetrahedrontraverse(); } @@ -24903,311 +27007,40 @@ void tetgenmesh::delaunayrefinement() points->items - steinercount, st_segref_count - bak_segref_count, st_facref_count - bak_facref_count, - st_volref_count - bak_volref_count); - } - } // if (b->reflevel > 2) - - if (b->verbose) { - if (flip23count + flip32count + flip44count > bak_flipcount) { - printf(" Performed %ld flips.\n", flip23count + flip32count + - flip44count - bak_flipcount); - } - } - - if (steinerleft == 0) { - if (!b->quiet) { - printf("\nWarnning: "); - printf("The desired number of Steiner points (%d) is reached.\n\n", - b->steinerleft); - } - } - - - delete encseglist; - delete encshlist; - - if (!b->nobisect) { - totalworkmemory += (badsubsegs->maxitems * badsubsegs->itembytes); - delete badsubsegs; - if (b->reflevel > 1) { - totalworkmemory += (badsubfacs->maxitems * badsubfacs->itembytes); - delete badsubfacs; - } - } - if (b->reflevel > 2) { - totalworkmemory += (badtetrahedrons->maxitems*badtetrahedrons->itembytes); - delete badtetrahedrons; - } -} - -//// //// -//// //// -//// refine_cxx /////////////////////////////////////////////////////////////// - -//// optimize_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// lawsonflip3d() A three-dimensional Lawson's algorithm. // -// // -/////////////////////////////////////////////////////////////////////////////// - -long tetgenmesh::lawsonflip3d(flipconstraints *fc) -{ - triface fliptets[5], neightet, hulltet; - face checksh, casingout; - badface *popface, *bface; - point pd, pe, *pts; - REAL sign, ori; - long flipcount, totalcount = 0l; - long sliver_peels = 0l; - int t1ver; - int i; - - - while (1) { - - if (b->verbose > 2) { - printf(" Lawson flip %ld faces.\n", flippool->items); - } - flipcount = 0l; - - while (flipstack != (badface *) NULL) { - // Pop a face from the stack. - popface = flipstack; - fliptets[0] = popface->tt; - flipstack = flipstack->nextitem; // The next top item in stack. - flippool->dealloc((void *) popface); - - // Skip it if it is a dead tet (destroyed by previous flips). - if (isdeadtet(fliptets[0])) continue; - // Skip it if it is not the same tet as we saved. - if (!facemarked(fliptets[0])) continue; - - unmarkface(fliptets[0]); - - if (ishulltet(fliptets[0])) continue; - - fsym(fliptets[0], fliptets[1]); - if (ishulltet(fliptets[1])) { - if (nonconvex) { - // Check if 'fliptets[0]' it is a hull sliver. - tspivot(fliptets[0], checksh); - for (i = 0; i < 3; i++) { - if (!isshsubseg(checksh)) { - spivot(checksh, casingout); - //assert(casingout.sh != NULL); - if (sorg(checksh) != sdest(casingout)) sesymself(casingout); - stpivot(casingout, neightet); - if (neightet.tet == fliptets[0].tet) { - // Found a hull sliver 'neightet'. Let it be [e,d,a,b], where - // [e,d,a] and [d,e,b] are hull faces. - edestoppo(neightet, hulltet); // [a,b,e,d] - fsymself(hulltet); // [b,a,e,#] - if (oppo(hulltet) == dummypoint) { - pe = org(neightet); - if ((pointtype(pe) == FREEFACETVERTEX) || - (pointtype(pe) == FREESEGVERTEX)) { - removevertexbyflips(pe); - } - } else { - eorgoppo(neightet, hulltet); // [b,a,d,e] - fsymself(hulltet); // [a,b,d,#] - if (oppo(hulltet) == dummypoint) { - pd = dest(neightet); - if ((pointtype(pd) == FREEFACETVERTEX) || - (pointtype(pd) == FREESEGVERTEX)) { - removevertexbyflips(pd); - } - } else { - // Perform a 3-to-2 flip to remove the sliver. - fliptets[0] = neightet; // [e,d,a,b] - fnext(fliptets[0], fliptets[1]); // [e,d,b,c] - fnext(fliptets[1], fliptets[2]); // [e,d,c,a] - flip32(fliptets, 1, fc); - // Update counters. - flip32count--; - flip22count--; - sliver_peels++; - if (fc->remove_ndelaunay_edge) { - // Update the volume (must be decreased). - //assert(fc->tetprism_vol_sum <= 0); - tetprism_vol_sum += fc->tetprism_vol_sum; - fc->tetprism_vol_sum = 0.0; // Clear it. - } - } - } - break; - } // if (neightet.tet == fliptets[0].tet) - } // if (!isshsubseg(checksh)) - senextself(checksh); - } // i - } // if (nonconvex) - continue; - } - - // Test whether the face is locally Delaunay or not. - pts = (point *) fliptets[1].tet; - sign = insphere_s(pts[4], pts[5], pts[6], pts[7], oppo(fliptets[0])); - - if (sign < 0) { - // A non-Delaunay face. Try to flip it. - pd = oppo(fliptets[0]); - pe = oppo(fliptets[1]); - - // Check the convexity of its three edges. Stop checking either a - // locally non-convex edge (ori < 0) or a flat edge (ori = 0) is - // encountered, and 'fliptet' represents that edge. - for (i = 0; i < 3; i++) { - ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe); - if (ori <= 0) break; - enextself(fliptets[0]); - } - - if (ori > 0) { - // A 2-to-3 flip is found. - if (checksubfaceflag) { - // Do not flip if it is a subface. - if (issubface(fliptets[0])) continue; - } - // [0] [a,b,c,d], - // [1] [b,a,c,e]. no dummypoint. - flip23(fliptets, 0, fc); - flipcount++; - if (fc->remove_ndelaunay_edge) { - // Update the volume (must be decreased). - //assert(fc->tetprism_vol_sum <= 0); - tetprism_vol_sum += fc->tetprism_vol_sum; - fc->tetprism_vol_sum = 0.0; // Clear it. - } - continue; - } else { // ori <= 0 - // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat, - // where the edge [a',b'] is one of [a,b], [b,c], and [c,a]. - if (checksubsegflag) { - // Do not flip if it is a segment. - if (issubseg(fliptets[0])) continue; - } - // Check if there are three or four tets sharing at this edge. - esymself(fliptets[0]); // [b,a,d,c] - for (i = 0; i < 3; i++) { - fnext(fliptets[i], fliptets[i+1]); - } - if (fliptets[3].tet == fliptets[0].tet) { - // A 3-to-2 flip is found. (No hull tet.) - flip32(fliptets, 0, fc); - flipcount++; - if (fc->remove_ndelaunay_edge) { - // Update the volume (must be decreased). - //assert(fc->tetprism_vol_sum <= 0); - tetprism_vol_sum += fc->tetprism_vol_sum; - fc->tetprism_vol_sum = 0.0; // Clear it. - } - continue; - } else { - // There are more than 3 tets at this edge. - fnext(fliptets[3], fliptets[4]); - if (fliptets[4].tet == fliptets[0].tet) { - // There are exactly 4 tets at this edge. - if (nonconvex) { - if (apex(fliptets[3]) == dummypoint) { - // This edge is locally non-convex on the hull. - // It can be removed by a 4-to-4 flip. - ori = 0; - } - } // if (nonconvex) - if (ori == 0) { - // A 4-to-4 flip is found. (Two hull tets may be involved.) - // Current tets in 'fliptets': - // [0] [b,a,d,c] (d may be newpt) - // [1] [b,a,c,e] - // [2] [b,a,e,f] (f may be dummypoint) - // [3] [b,a,f,d] - esymself(fliptets[0]); // [a,b,c,d] - // A 2-to-3 flip replaces face [a,b,c] by edge [e,d]. - // This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]). - // It will be removed by the followed 3-to-2 flip. - flip23(fliptets, 0, fc); // No hull tet. - fnext(fliptets[3], fliptets[1]); - fnext(fliptets[1], fliptets[2]); - // Current tets in 'fliptets': - // [0] [...] - // [1] [b,a,d,e] (degenerated, d may be new point). - // [2] [b,a,e,f] (f may be dummypoint) - // [3] [b,a,f,d] - // A 3-to-2 flip replaces edge [b,a] by face [d,e,f]. - // Hull tets may be involved (f may be dummypoint). - flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc); - flipcount++; - flip23count--; - flip32count--; - flip44count++; - if (fc->remove_ndelaunay_edge) { - // Update the volume (must be decreased). - //assert(fc->tetprism_vol_sum <= 0); - tetprism_vol_sum += fc->tetprism_vol_sum; - fc->tetprism_vol_sum = 0.0; // Clear it. - } - continue; - } // if (ori == 0) - } - } - } // if (ori <= 0) - - // This non-Delaunay face is unflippable. Save it. - unflipqueue->newindex((void **) &bface); - bface->tt = fliptets[0]; - bface->forg = org(fliptets[0]); - bface->fdest = dest(fliptets[0]); - bface->fapex = apex(fliptets[0]); - } // if (sign < 0) - } // while (flipstack) - - if (b->verbose > 2) { - if (flipcount > 0) { - printf(" Performed %ld flips.\n", flipcount); - } + st_volref_count - bak_volref_count); } - // Accumulate the counter of flips. - totalcount += flipcount; - - assert(flippool->items == 0l); - // Return if no unflippable faces left. - if (unflipqueue->objects == 0l) break; - // Return if no flip has been performed. - if (flipcount == 0l) break; + } // if (b->reflevel > 2) - // Try to flip the unflippable faces. - for (i = 0; i < unflipqueue->objects; i++) { - bface = (badface *) fastlookup(unflipqueue, i); - if (!isdeadtet(bface->tt) && - (org(bface->tt) == bface->forg) && - (dest(bface->tt) == bface->fdest) && - (apex(bface->tt) == bface->fapex)) { - flippush(flipstack, &(bface->tt)); - } + if (steinerleft == 0) { + if (!b->quiet) { + printf("\nWarnning: "); + printf("The desired number of Steiner points (%d) is reached.\n\n", + b->steinerleft); } - unflipqueue->restart(); + } - } // while (1) + delete encseglist; + delete encshlist; - if (b->verbose > 2) { - if (totalcount > 0) { - printf(" Performed %ld flips.\n", totalcount); - } - if (sliver_peels > 0) { - printf(" Removed %ld hull slivers.\n", sliver_peels); - } - if (unflipqueue->objects > 0l) { - printf(" %ld unflippable edges remained.\n", unflipqueue->objects); + if (!b->nobisect) { + delete badsubsegs; + if (b->reflevel > 1) { + delete badsubfacs; } } - - return totalcount + sliver_peels; + if (b->reflevel > 2) { + delete badtetrahedrons; + } } +//// //// +//// //// +//// refine_cxx /////////////////////////////////////////////////////////////// + +//// optimize_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // // recoverdelaunay() Recovery the locally Delaunay property. // @@ -25217,8 +27050,8 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) void tetgenmesh::recoverdelaunay() { arraypool *flipqueue, *nextflipqueue, *swapqueue; - triface tetloop, neightet, *parytet; badface *bface, *parybface; + triface tetloop, neightet, *parytet; point *ppt; flipconstraints fc; int i, j; @@ -25227,16 +27060,28 @@ void tetgenmesh::recoverdelaunay() printf("Recovering Delaunayness...\n"); } + if (b->verbose) { + printf(" max_flipstarsize = %d.\n", b->optmaxflipstarsize); + printf(" max_fliplinklevel = %d.\n", b->delmaxfliplevel); + } + + calc_tetprism_vol = 1; tetprism_vol_sum = 0.0; // Initialize it. + assert(flipstack == NULL); + assert(unflipqueue->objects == 0l); + // Put all interior faces of the mesh into 'flipstack'. tetrahedrons->traversalinit(); tetloop.tet = tetrahedrontraverse(); while (tetloop.tet != NULL) { for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { - decode(tetloop.tet[tetloop.ver], neightet); - if (!facemarked(neightet)) { - flippush(flipstack, &tetloop); + // Avoid queue a face twice. + fsym(tetloop, neightet); + if (!ishulltet(neightet)) { + if (!facemarked(neightet)) { + flippush(flipstack, &tetloop); + } } } ppt = (point *) &(tetloop.tet[4]); @@ -25244,10 +27089,6 @@ void tetgenmesh::recoverdelaunay() tetloop.tet = tetrahedrontraverse(); } - // Calulate a relatively lower bound for small improvement. - // Used to avoid rounding error in volume calculation. - fc.bak_tetprism_vol = tetprism_vol_sum * b->epsilon * 1e-3; - if (b->verbose) { printf(" Initial obj = %.17g\n", tetprism_vol_sum); } @@ -25255,35 +27096,37 @@ void tetgenmesh::recoverdelaunay() if (b->verbose > 1) { printf(" Recover Delaunay [Lawson] : %ld\n", flippool->items); } + assert(unflipqueue->objects == 0l); // First only use the basic Lawson's flip. - fc.remove_ndelaunay_edge = 1; - fc.enqflag = 2; - - lawsonflip3d(&fc); + lawsonflip3d(NULL, 4, 0, 0, 1); if (b->verbose > 1) { - printf(" obj (after Lawson) = %.17g\n", tetprism_vol_sum); + printf(" New obj = %.17g\n", tetprism_vol_sum); } if (unflipqueue->objects == 0l) { - return; // The mesh is Delaunay. + // The mesh is Delaunay. + return; } + // Set the common options. + fc.remove_ndelaunay_edge = 1; fc.unflip = 1; // Unflip if the edge is not flipped. - fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'. - fc.enqflag = 0; + fc.collectnewtets = 1; - autofliplinklevel = 1; // Init level. - b->fliplinklevel = -1; // No fixed level. + autofliplinklevel = 1; // Init value. + b->fliplinklevel = -1; // For efficiency reason, we limit the maximium size of the edge star. + // 'b->optmaxflipstarsize' is set by -OOOOO (5 Os), default is 10. int bakmaxflipstarsize = b->flipstarsize; - b->flipstarsize = 10; // default + b->flipstarsize = b->optmaxflipstarsize; flipqueue = new arraypool(sizeof(badface), 10); nextflipqueue = new arraypool(sizeof(badface), 10); - + + // Swap the two flip queues. swapqueue = flipqueue; flipqueue = unflipqueue; @@ -25291,61 +27134,60 @@ void tetgenmesh::recoverdelaunay() while (flipqueue->objects > 0l) { - if (b->verbose > 1) { - printf(" Recover Delaunay [level = %2d] #: %ld.\n", - autofliplinklevel, flipqueue->objects); - } - - for (i = 0; i < flipqueue->objects; i++) { - bface = (badface *) fastlookup(flipqueue, i); - if (getedge(bface->forg, bface->fdest, &bface->tt)) { - if (removeedgebyflips(&(bface->tt), &fc) == 2) { - tetprism_vol_sum += fc.tetprism_vol_sum; - fc.tetprism_vol_sum = 0.0; // Clear it. - // Queue new faces for flips. - for (j = 0; j < cavetetlist->objects; j++) { - parytet = (triface *) fastlookup(cavetetlist, j); - // A queued new tet may be dead. - if (!isdeadtet(*parytet)) { - for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { - // Avoid queue a face twice. - decode(parytet->tet[parytet->ver], neightet); - if (!facemarked(neightet)) { - flippush(flipstack, parytet); - } - } // parytet->ver + while (flipqueue->objects > 0l) { + + if (b->verbose > 1) { + printf(" Recover Delaunay [level = %2d] #: %ld.\n", + autofliplinklevel, flipqueue->objects); + } + + for (i = 0; i < flipqueue->objects; i++) { + bface = (badface *) fastlookup(flipqueue, i); + if (getedge(bface->forg, bface->fdest, &bface->tt)) { + // Remember the the objective value (volume of all tetprisms). + fc.bak_tetprism_vol = tetprism_vol_sum; + if (removeedgebyflips(&(bface->tt), &fc) == 2) { + if (b->verbose > 2) { + printf(" Decreased quantity: %.17g.\n", + fc.bak_tetprism_vol - tetprism_vol_sum); } - } // j - cavetetlist->restart(); - // Remove locally non-Delaunay faces. New non-Delaunay edges - // may be found. They are saved in 'unflipqueue'. - fc.enqflag = 2; - lawsonflip3d(&fc); - fc.enqflag = 0; - // There may be unflipable faces. Add them in flipqueue. - for (j = 0; j < unflipqueue->objects; j++) { - bface = (badface *) fastlookup(unflipqueue, j); - flipqueue->newindex((void **) &parybface); + // Queue new faces for flips. + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + // A queued new tet may be dead. + if (!isdeadtet(*parytet)) { + for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { + // Avoid queue a face twice. + fsym(*parytet, neightet); + if (!facemarked(neightet)) { + flippush(flipstack, parytet); + } + } // parytet->ver + } + } // j + cavetetlist->restart(); + // Remove locally non-Delaunay faces. New non-Delaunay edges + // may be found. They are saved in 'unflipqueue'. + lawsonflip3d(NULL, 4, 0, 0, 1); + } else { + // Unable to remove this edge. Save it. + nextflipqueue->newindex((void **) &parybface); *parybface = *bface; } - unflipqueue->restart(); - } else { - // Unable to remove this edge. Save it. - nextflipqueue->newindex((void **) &parybface); - *parybface = *bface; - // Normally, it should be zero. - //assert(fc.tetprism_vol_sum == 0.0); - // However, due to rounding errors, a tiny value may appear. - fc.tetprism_vol_sum = 0.0; } - } - } // i + } // i + + flipqueue->restart(); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while (flipqueue->objects > 0l) if (b->verbose > 1) { - printf(" obj (after level %d) = %.17g.\n", autofliplinklevel, - tetprism_vol_sum); + printf(" New obj = %.17g.\n", tetprism_vol_sum); } - flipqueue->restart(); // Swap the two flip queues. swapqueue = flipqueue; @@ -25353,13 +27195,14 @@ void tetgenmesh::recoverdelaunay() nextflipqueue = swapqueue; if (flipqueue->objects > 0l) { - // default 'b->delmaxfliplevel' is 1. + // 'b->delmaxfliplevel' is set by -OOOO, default is 1. if (autofliplinklevel >= b->delmaxfliplevel) { // For efficiency reason, we do not search too far. break; } autofliplinklevel+=b->fliplinklevelinc; } + } // while (flipqueue->objects > 0l) if (flipqueue->objects > 0l) { @@ -25368,13 +27211,16 @@ void tetgenmesh::recoverdelaunay() } } - if (b->verbose) { - printf(" Final obj = %.17g\n", tetprism_vol_sum); - } - b->flipstarsize = bakmaxflipstarsize; - delete flipqueue; + delete nextflipqueue; + delete flipqueue; + + calc_tetprism_vol = 0; + + if (b->verbose) { + printf(" Final obj = %.17g\n", tetprism_vol_sum); + } } /////////////////////////////////////////////////////////////////////////////// @@ -25387,7 +27233,11 @@ int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, triface *searchtet) { triface spintet; - int t1ver; + + if (b->verbose > 2) { + printf(" Get tet [%d,%d,%d,%d].\n", pointmark(pa), pointmark(pb), + pointmark(pc), pointmark(pd)); + } if (getedge(pa, pb, searchtet)) { spintet = *searchtet; @@ -25436,20 +27286,18 @@ long tetgenmesh::improvequalitybyflips() flipqueue = new arraypool(sizeof(badface), 10); nextflipqueue = new arraypool(sizeof(badface), 10); - // Backup flip edge options. - int bakautofliplinklevel = autofliplinklevel; - int bakfliplinklevel = b->fliplinklevel; - int bakmaxflipstarsize = b->flipstarsize; - - // Set flip edge options. - autofliplinklevel = 1; + // Flip edge options. b->fliplinklevel = -1; - b->flipstarsize = 10; // b->optmaxflipstarsize; + autofliplinklevel = 1; // Init value. + + // For efficiency reason, we limit the maximium size of the edge star. + // 'b->optmaxflipstarsize' is set by -OOOOO (5 Os), default is 10. + int bakmaxflipstarsize = b->flipstarsize; + b->flipstarsize = b->optmaxflipstarsize; fc.remove_large_angle = 1; fc.unflip = 1; fc.collectnewtets = 1; - fc.checkflipeligibility = 1; totalremcount = 0l; @@ -25472,37 +27320,40 @@ long tetgenmesh::improvequalitybyflips() bface = (badface *) fastlookup(flipqueue, k); if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, bface->foppo, &bface->tt)) { - //assert(!ishulltet(bface->tt)); // There are bad dihedral angles in this tet. if (bface->tt.ver != 11) { // The dihedral angles are permuted. // Here we simply re-compute them. Slow!!. ppt = (point *) & (bface->tt.tet[4]); tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, - &bface->key, NULL); + &maxdd, NULL); bface->forg = ppt[0]; bface->fdest = ppt[1]; bface->fapex = ppt[2]; bface->foppo = ppt[3]; bface->tt.ver = 11; } - if (bface->key == 0) { - // Re-comput the quality values. Due to smoothing operations. - ppt = (point *) & (bface->tt.tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, - &bface->key, NULL); - } cosdd = bface->cent; remflag = 0; for (i = 0; (i < 6) && !remflag; i++) { if (cosdd[i] < cosmaxdihed) { // Found a large dihedral angle. bface->tt.ver = edge2ver[i]; // Go to the edge. + if (b->verbose > 2) { + printf(" Found a large angle [%d,%d,%d,%d] (%g).\n", + pointmark(org(bface->tt)), pointmark(dest(bface->tt)), + pointmark(apex(bface->tt)), pointmark(oppo(bface->tt)), + acos(cosdd[i]) / PI * 180.0); + } fc.cosdihed_in = cosdd[i]; fc.cosdihed_out = 0.0; // 90 degree. n = removeedgebyflips(&(bface->tt), &fc); if (n == 2) { // Edge is flipped. + if (b->verbose > 2) { + printf(" Reduced a large angle to %g degree.\n", + acos(fc.cosdihed_out) / PI * 180.0); + } remflag = 1; if (fc.cosdihed_out < cosmaxdihed) { // Queue new bad tets for further improvements. @@ -25510,6 +27361,8 @@ long tetgenmesh::improvequalitybyflips() parytet = (triface *) fastlookup(cavetetlist, j); if (!isdeadtet(*parytet)) { ppt = (point *) & (parytet->tet[4]); + //if (!marktest2ed(*parytet)) { + assert(!marktest2ed(*parytet)); // SELF_CHECK // Do not test a hull tet. if (ppt[3] != dummypoint) { tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, @@ -25528,7 +27381,8 @@ long tetgenmesh::improvequalitybyflips() parybface->cent[n] = ncosdd[n]; } } - } // if (ppt[3] != dummypoint) + } // if (ppt[3] != dummypoint) { + //} } } // j } // if (fc.cosdihed_out < cosmaxdihed) @@ -25559,12 +27413,12 @@ long tetgenmesh::improvequalitybyflips() totalremcount += remcount; if (unflipqueue->objects > 0l) { - //if (autofliplinklevel >= b->optmaxfliplevel) { - if (autofliplinklevel >= b->optlevel) { + // 'b->optmaxfliplevel' is set by -OOO, default is 2. + if (autofliplinklevel >= b->optmaxfliplevel) { + // For efficiency reason, we do not search too far. break; } autofliplinklevel+=b->fliplinklevelinc; - //b->flipstarsize = 10 + (1 << (b->optlevel - 1)); } // Swap the two flip queues. @@ -25573,9 +27427,6 @@ long tetgenmesh::improvequalitybyflips() unflipqueue = swapqueue; } // while (flipqueues->objects > 0) - // Restore original flip edge options. - autofliplinklevel = bakautofliplinklevel; - b->fliplinklevel = bakfliplinklevel; b->flipstarsize = bakmaxflipstarsize; delete flipqueue; @@ -25600,8 +27451,8 @@ long tetgenmesh::improvequalitybyflips() // has two orientations, ccw or cw, with respect to 'p'. 'ccw' indicates // // the orientation is ccw (1) or not (0). // // // -// 'opm' is a structure contains the parameters of the objective function. // -// It is needed by the evaluation of the function value. // +// 'of' is a structure contains the parameters of the objective function. It // +// is needed by the evaluation of the function value. // // // // The return value indicates weather the point is smoothed or not. // // // @@ -25622,6 +27473,16 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, int numdirs, iter; int i, j, k; + if (b->verbose > 2) { + printf(" Smooth a point: %ld faces.\n", linkfacelist->objects); + if (opm->min_max_dihedangle) { + printf(" Init value = %g (degree).\n", + acos(opm->initval - 1.0) / PI * 180.0); + } else { + printf(" Init value = %g.\n", opm->initval); + } + } + // Decide the number of moving directions. numdirs = (int) linkfacelist->objects; if (numdirs > opm->numofsearchdirs) { @@ -25674,8 +27535,7 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, if (ori < 0.0) { // Calcuate the objective function value. if (opm->max_min_volume) { - //val = -ori; - val = - orient3dfast(pa, pb, pc, nextpt); + val = -ori; } else if (opm->max_min_aspectratio) { val = tetaspectratio(pa, pb, pc, nextpt); } else if (opm->min_max_dihedangle) { @@ -25688,10 +27548,8 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, } } else { // ori >= 0.0; // An invalid new tet. - // This may happen if the mesh contains inverted elements. if (opm->max_min_volume) { - //val = -ori; - val = - orient3dfast(pa, pb, pc, nextpt); + val = -ori; } else { // Discard this point. break; // j @@ -25711,6 +27569,7 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, } // j if (j == linkfacelist->objects) { // The function value has been improved. + assert(minval > opm->imprval); opm->imprval = minval; // Save the new location of the point. for (j = 0; j < 3; j++) bestpt[j] = nextpt[j]; @@ -25757,9 +27616,54 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, if (iter > 0) { // The point has been smooothed. - opm->smthiter = iter; // Remember the number of iterations. + opm->smthiter = iter; // Remember the number of iterations. + if (b->verbose > 2) { + printf(" Smoothed: %d iterations.\n", iter); + if (opm->min_max_dihedangle) { + printf(" Fina value = %g (degree).\n", + acos(opm->imprval - 1.0) / PI * 180.0); + } else { + printf(" Fina value = %g.\n", opm->imprval); + } + } // The point has been smoothed. Update it to its new position. for (i = 0; i < 3; i++) smtpt[i] = startpt[i]; + + if (opm->flipflag) { + // Push all affected faces into 'flipstack'. + triface starttet, neightet; + for (i = 0; i < linkfacelist->objects; i++) { + parytet = (triface *) fastlookup(linkfacelist, i); + starttet = *parytet; + for (starttet.ver = 0; starttet.ver < 4; starttet.ver++) { + fsym(starttet, neightet); + if (!infected(neightet)) { + flippush(flipstack, &starttet); + } + } + infect(*parytet); + } + for (i = 0; i < linkfacelist->objects; i++) { + parytet = (triface *) fastlookup(linkfacelist, i); + uninfect(*parytet); + } + } else if (opm->checkencflag) { + // Push all affected tets into pool. + badface *bface; + for (i = 0; i < linkfacelist->objects; i++) { + parytet = (triface *) fastlookup(linkfacelist, i); + if (!marktest2ed(*parytet)) { + marktest2(*parytet); // Only queue it once. + bface = (badface *) badtetrahedrons->alloc(); + bface->tt = *parytet; + bface->forg = org(bface->tt); + } + } + } + } else { + if (b->verbose > 2) { + printf(" Not smoothed.\n"); + } } return iter; @@ -25774,28 +27678,29 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, long tetgenmesh::improvequalitybysmoothing(optparameters *opm) { arraypool *flipqueue, *swapqueue; - triface *parytet; badface *bface, *parybface; point *ppt; long totalsmtcount, smtcount; int smtflag; - int iter, i, j, k; + int iter, i, k; //assert(unflipqueue->objects > 0l); flipqueue = new arraypool(sizeof(badface), 10); + totalsmtcount = 0l; + // Swap the two flip queues. swapqueue = flipqueue; flipqueue = unflipqueue; unflipqueue = swapqueue; - totalsmtcount = 0l; iter = 0; while (flipqueue->objects > 0l) { smtcount = 0l; + //while (flipqueue->objects > 0l) { if (b->verbose > 1) { printf(" Improving mesh qualiy by smoothing [%d]#: %ld.\n", iter, flipqueue->objects); @@ -25805,7 +27710,6 @@ long tetgenmesh::improvequalitybysmoothing(optparameters *opm) bface = (badface *) fastlookup(flipqueue, k); if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, bface->foppo, &bface->tt)) { - // Operate on it if it is not in 'unflipqueue'. if (!marktested(bface->tt)) { // Here we simply re-compute the quality. Since other smoothing // operation may have moved the vertices of this tet. @@ -25815,7 +27719,10 @@ long tetgenmesh::improvequalitybysmoothing(optparameters *opm) if (bface->key < cossmtdihed) { // if (maxdd < cosslidihed) { // It is a sliver. Try to smooth its vertices. smtflag = 0; - opm->initval = bface->key + 1.0; + //if (opm->min_max_dihedangle) { + opm->initval = bface->key + 1.0; + //opm->checkencflag = 4; // Queue affected tets. + //} for (i = 0; (i < 4) && !smtflag; i++) { if (pointtype(ppt[i]) == FREEVOLVERTEX) { getvertexstar(1, ppt[i], cavetetlist, NULL, NULL); @@ -25828,61 +27735,78 @@ long tetgenmesh::improvequalitybysmoothing(optparameters *opm) opm->smthiter = 0; // reset smoothpoint(ppt[i], cavetetlist, 1, opm); } - // This tet is modifed. smtcount++; - if ((opm->imprval - 1.0) < cossmtdihed) { - // There are slivers in new tets. Queue them. - for (j = 0; j < cavetetlist->objects; j++) { - parytet = (triface *) fastlookup(cavetetlist, j); - assert(!isdeadtet(*parytet)); - // Operate it if it is not in 'unflipqueue'. - if (!marktested(*parytet)) { - // Evaluate its quality. - // Re-use ppt, bface->key, bface->cent. - ppt = (point *) & (parytet->tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], - bface->cent, &bface->key, NULL); - if (bface->key < cossmtdihed) { - // A new sliver. Queue it. - marktest(*parytet); // It is in unflipqueue. - unflipqueue->newindex((void **) &parybface); - parybface->tt = *parytet; - parybface->forg = ppt[0]; - parybface->fdest = ppt[1]; - parybface->fapex = ppt[2]; - parybface->foppo = ppt[3]; - parybface->tt.ver = 11; - parybface->key = 0.0; - } - } - } // j - } // if ((opm->imprval - 1.0) < cossmtdihed) - } // if (smtflag) + } cavetetlist->restart(); - } // if (pointtype(ppt[i]) == FREEVOLVERTEX) + } } // i - if (!smtflag) { + if (smtflag) { + // This tet is modifed. + smtcount++; + if ((opm->imprval - 1.0) < cossmtdihed) { + // Queue new slivers. + badtetrahedrons->traversalinit(); + bface = badfacetraverse(badtetrahedrons); + while (bface != NULL) { + assert(!isdeadtet(bface->tt)); + assert(marktest2ed(bface->tt)); + unmarktest2(bface->tt); + if (!marktested(bface->tt)) { + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &(bface->key), NULL); + if (bface->key < cossmtdihed) { + // A new sliver. Queue it. + marktest(bface->tt); // It is in unflipqueue. + bface->forg = ppt[0]; + bface->fdest = ppt[1]; + bface->fapex = ppt[2]; + bface->foppo = ppt[3]; + bface->tt.ver = 11; + unflipqueue->newindex((void **) &parybface); + *parybface = *bface; + } + } + bface = badfacetraverse(badtetrahedrons); + } + } else { + // No new slivers. Only unmark the queued tets. + badtetrahedrons->traversalinit(); + bface = badfacetraverse(badtetrahedrons); + while (bface != NULL) { + assert(!isdeadtet(bface->tt)); + assert(marktest2ed(bface->tt)); + unmarktest2(bface->tt); + bface = badfacetraverse(badtetrahedrons); + } + } + badtetrahedrons->restart(); + } else { // Didn't smooth. Queue it again. + // Adjust the vertices for flipping. marktest(bface->tt); // It is in unflipqueue. + bface->forg = ppt[0]; + bface->fdest = ppt[1]; + bface->fapex = ppt[2]; + bface->foppo = ppt[3]; + bface->tt.ver = 11; unflipqueue->newindex((void **) &parybface); - parybface->tt = bface->tt; - parybface->forg = ppt[0]; - parybface->fdest = ppt[1]; - parybface->fapex = ppt[2]; - parybface->foppo = ppt[3]; - parybface->tt.ver = 11; - parybface->key = 0.0; + *parybface = *bface; } - } // if (maxdd < cosslidihed) + } // if (maxdd < cosslidihed) } // if (!marktested(...)) - } // if (gettetrahedron(...)) + } // gettetrahedron(...) } // k flipqueue->restart(); + // } // while + // Unmark the tets in unflipqueue. for (i = 0; i < unflipqueue->objects; i++) { bface = (badface *) fastlookup(unflipqueue, i); + assert(!isdeadtet(bface->tt)); + assert(marktested(bface->tt)); unmarktest(bface->tt); } @@ -25922,36 +27846,35 @@ int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) { triface *abtets; triface searchtet, spintet, *parytet; + face checkseg; point pa, pb, steinerpt; optparameters opm; insertvertexflags ivf; REAL smtpt[3], midpt[3]; int success; - int t1ver; + int loc; int n, i; - // 'slitet' is [c,d,a,b], where [c,d] has a big dihedral angle. + // 'slitet' is [c,d,a,b], where [c,d] has a big hihedral angle. // Go to the opposite edge [a,b]. - edestoppo(*slitet, searchtet); // [a,b,c,d]. + eprev(*slitet, searchtet); + esymself(searchtet); + enextself(searchtet); // [a,b,c,d]. // Do not split a segment. - if (issubseg(searchtet)) { + tsspivot1(searchtet, checkseg); + if (checkseg.sh != NULL) { return 0; } // Count the number of tets shared at [a,b]. - // Do not split it if it is a hull edge. spintet = searchtet; n = 0; while (1) { - if (ishulltet(spintet)) break; n++; fnextself(spintet); if (spintet.tet == searchtet.tet) break; } - if (ishulltet(spintet)) { - return 0; // It is a hull edge. - } assert(n >= 3); // Get all tets at edge [a,b]. @@ -26005,11 +27928,19 @@ int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) cavetetlist->restart(); if (!success) { + if (b->verbose > 2) { + printf(" Unable to relocate the initial point.\n"); + } delete [] abtets; return 0; } + if (steinerleft == 0) { + // The desired number of Steiner points is reached. + return 0; + } + // Insert the Steiner point. makepoint(&steinerpt, FREEVOLVERTEX); for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; @@ -26020,22 +27951,24 @@ int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) caveoldtetlist->newindex((void **) &parytet); *parytet = abtets[i]; } - searchtet = abtets[0]; // No need point location. - if (b->metric) { - locate(steinerpt, &searchtet, 0); // For size interpolation. - } - - delete [] abtets; - ivf.iloc = (int) INSTAR; + ivf.bowywat = 0; // Do not use Bowyer-Watson algorithm. + ivf.lawson = 0; // Do not flip. + ivf.rejflag = 0; ivf.chkencflag = chkencflag; - ivf.assignmeshsize = b->metric; + ivf.sloc = 0; + ivf.sbowywat = 0; + ivf.splitbdflag = 0; + ivf.validflag = 0; + ivf.respectbdflag = 0; + ivf.assignmeshsize = 0; + loc = insertvertex(steinerpt, &searchtet, NULL, NULL, &ivf); - if (insertpoint(steinerpt, &searchtet, NULL, NULL, &ivf)) { + if (loc == (int) INSTAR) { // The vertex has been inserted. - st_volref_count++; + st_volref_count++; //st_inpoly_count++; if (steinerleft > 0) steinerleft--; return 1; } else { @@ -26043,6 +27976,8 @@ int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) pointdealloc(steinerpt); return 0; } + + delete [] abtets; } /////////////////////////////////////////////////////////////////////////////// @@ -26055,24 +27990,24 @@ long tetgenmesh::removeslivers(int chkencflag) { arraypool *flipqueue, *swapqueue; badface *bface, *parybface; - triface slitet, *parytet; point *ppt; - REAL cosdd[6], maxcosd; + REAL *cosdd; long totalsptcount, sptcount; - int iter, i, j, k; + int iter, j, k; //assert(unflipqueue->objects > 0l); flipqueue = new arraypool(sizeof(badface), 10); + totalsptcount = 0l; + // Swap the two flip queues. swapqueue = flipqueue; flipqueue = unflipqueue; unflipqueue = swapqueue; - totalsptcount = 0l; iter = 0; - while ((flipqueue->objects > 0l) && (steinerleft != 0)) { + while (flipqueue->objects > 0l) { sptcount = 0l; @@ -26081,63 +28016,74 @@ long tetgenmesh::removeslivers(int chkencflag) iter, flipqueue->objects); } - for (k = 0; (k < flipqueue->objects) && (steinerleft != 0); k++) { + for (k = 0; k < flipqueue->objects; k++) { bface = (badface *) fastlookup(flipqueue, k); if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, bface->foppo, &bface->tt)) { - if ((bface->key == 0) || (bface->tt.ver != 11)) { - // Here we need to re-compute the quality. Since other smoothing + //if (!marktested(bface->tt)) { + // Here we simply re-compute the quality. Since other smoothing // operation may have moved the vertices of this tet. ppt = (point *) & (bface->tt.tet[4]); tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, &bface->key, NULL); - } - if (bface->key < cosslidihed) { - // It is a sliver. Try to split it. - slitet.tet = bface->tt.tet; - //cosdd = bface->cent; - for (j = 0; j < 6; j++) { - if (bface->cent[j] < cosslidihed) { - // Found a large dihedral angle. - slitet.ver = edge2ver[j]; // Go to the edge. - if (splitsliver(&slitet, bface->cent[j], chkencflag)) { - sptcount++; - break; + if (bface->key < cosslidihed) { + // It is a sliver. Try to split it. + cosdd = bface->cent; + for (j = 0; j < 6; j++) { + if (cosdd[j] < cosslidihed) { + // Found a large dihedral angle. + bface->tt.ver = edge2ver[j]; // Go to the edge. + if (b->verbose > 2) { + printf(" Found a bad tet [%d,%d,%d,%d] (%g).\n", + pointmark(org(bface->tt)), pointmark(dest(bface->tt)), + pointmark(apex(bface->tt)), pointmark(oppo(bface->tt)), + acos(cosdd[j]) / PI * 180.0); + } + if (splitsliver(&(bface->tt), cosdd[j], chkencflag)) { + sptcount++; + break; + } } - } - } // j - if (j < 6) { - // A sliver is split. Queue new slivers. - badtetrahedrons->traversalinit(); - parytet = (triface *) badtetrahedrons->traverse(); - while (parytet != NULL) { - unmarktest2(*parytet); - ppt = (point *) & (parytet->tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], cosdd, - &maxcosd, NULL); - if (maxcosd < cosslidihed) { - // A new sliver. Queue it. - unflipqueue->newindex((void **) &parybface); - parybface->forg = ppt[0]; - parybface->fdest = ppt[1]; - parybface->fapex = ppt[2]; - parybface->foppo = ppt[3]; - parybface->tt.tet = parytet->tet; - parybface->tt.ver = 11; - parybface->key = maxcosd; - for (i = 0; i < 6; i++) { - parybface->cent[i] = cosdd[i]; + } // j + if (j < 6) { + // A sliver is split. Queue new slivers. + badtetrahedrons->traversalinit(); + bface = badfacetraverse(badtetrahedrons); + while (bface != NULL) { + assert(!isdeadtet(bface->tt)); + assert(marktest2ed(bface->tt)); + unmarktest2(bface->tt); + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &(bface->key), NULL); + if (bface->key < cosslidihed) { + // A new sliver. Queue it. + //marktest(bface->tt); // It is in unflipqueue. + bface->forg = ppt[0]; + bface->fdest = ppt[1]; + bface->fapex = ppt[2]; + bface->foppo = ppt[3]; + bface->tt.ver = 11; + unflipqueue->newindex((void **) &parybface); + *parybface = *bface; } + bface = badfacetraverse(badtetrahedrons); } - parytet = (triface *) badtetrahedrons->traverse(); - } - badtetrahedrons->restart(); - } else { - // Didn't split. Queue it again. - unflipqueue->newindex((void **) &parybface); - *parybface = *bface; - } // if (j == 6) - } // if (bface->key < cosslidihed) + badtetrahedrons->restart(); + } else { + // Didn't split. Queue it again. + // Adjust the vertices for flipping. + //marktest(bface->tt); // It is in unflipqueue. + bface->forg = ppt[0]; + bface->fdest = ppt[1]; + bface->fapex = ppt[2]; + bface->foppo = ppt[3]; + bface->tt.ver = 11; + unflipqueue->newindex((void **) &parybface); + *parybface = *bface; + } // if (j == 6) + } // if (bface->key < cosslidihed) + // } // if (!marktested(bface->tt)) } // if (gettetrahedron(...)) } // k @@ -26175,12 +28121,11 @@ long tetgenmesh::removeslivers(int chkencflag) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::optimizemesh() +void tetgenmesh::optimizemesh(int optflag) { badface *parybface; triface checktet; point *ppt; - int optpasses; optparameters opm; REAL ncosdd[6], maxdd; long totalremcount, remcount; @@ -26194,16 +28139,18 @@ void tetgenmesh::optimizemesh() printf("Optimizing mesh...\n"); } - optpasses = ((1 << b->optlevel) - 1); - - if (b->verbose) { - printf(" Optimization level = %d.\n", b->optlevel); - printf(" Optimization scheme = %d.\n", b->optscheme); - printf(" Min_Max dihed angle = %g.\n", b->optmaxdihedral); + if (b->verbose > 1) { + printf(" min_max_dihedral = %g.\n", b->optmaxdihedral); + printf(" max_flipstarsize = %d.\n", b->optmaxflipstarsize); + printf(" max_fliplinklevel = %d.\n", b->optmaxfliplevel); + printf(" number of passes = %d.\n", b->optpasses); } - totalsmtcount = totalsptcount = totalremcount = 0l; + if (b->verbose > 1) { + printf(" Removing large angles (> %g degree).\n", b->optmaxdihedral); + } + cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI); cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI); cosslidihed = cos(b->optminslidihed / 180.0 * PI); @@ -26234,23 +28181,23 @@ void tetgenmesh::optimizemesh() totalremcount = improvequalitybyflips(); if ((unflipqueue->objects > 0l) && - ((b->optscheme & 2) || (b->optscheme & 4))) { - // The pool is only used by removeslivers(). - badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock, - sizeof(void *), 0); + ((b->optlevel & 2) || (b->optlevel & 4))) { // -O2 | -O4 + + badtetrahedrons = new memorypool(sizeof(badface), b->tetrahedraperblock, + memorypool::POINTER, 0); // Smoothing options. opm.min_max_dihedangle = 1; opm.numofsearchdirs = 10; // opm.searchstep = 0.001; opm.maxiter = 30; // Limit the maximum iterations. - //opm.checkencflag = 4; // Queue affected tets after smoothing. + opm.checkencflag = 4; // Queue affected tets after smoothing. chkencflag = 4; // Queue affected tets after splitting a sliver. iter = 0; - while (iter < optpasses) { + while (iter < b->optpasses) { smtcount = sptcount = remcount = 0l; - if (b->optscheme & 2) { + if (b->optlevel & 2) { smtcount += improvequalitybysmoothing(&opm); totalsmtcount += smtcount; if (smtcount > 0l) { @@ -26259,7 +28206,7 @@ void tetgenmesh::optimizemesh() } } if (unflipqueue->objects > 0l) { - if (b->optscheme & 4) { + if (b->optlevel & 4) { sptcount += removeslivers(chkencflag); totalsptcount += sptcount; if (sptcount > 0l) { @@ -26281,7 +28228,7 @@ void tetgenmesh::optimizemesh() delete badtetrahedrons; - } + } // // -O2 | -O4 if (unflipqueue->objects > 0l) { if (b->verbose > 1) { @@ -26292,13 +28239,13 @@ void tetgenmesh::optimizemesh() if (b->verbose) { if (totalremcount > 0l) { - printf(" Removed %ld edges.\n", totalremcount); + printf(" Removed %ld bad tets.\n", totalremcount); } if (totalsmtcount > 0l) { printf(" Smoothed %ld points.\n", totalsmtcount); } if (totalsptcount > 0l) { - printf(" Split %ld slivers.\n", totalsptcount); + printf(" Split %ld bad tets.\n", totalsptcount); } } } @@ -26311,32 +28258,6 @@ void tetgenmesh::optimizemesh() //// //// //// //// -/////////////////////////////////////////////////////////////////////////////// -// // -// printfcomma() Print a (large) number with the 'thousands separator'. // -// // -// The following code was simply copied from "stackoverflow". // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::printfcomma(unsigned long n) -{ - unsigned long n2 = 0; - int scale = 1; - while (n >= 1000) { - n2 = n2 + scale * (n % 1000); - n /= 1000; - scale *= 1000; - } - printf ("%ld", n); - while (scale != 1) { - scale /= 1000; - n = n2 / scale; - n2 = n2 % scale; - printf (",%03ld", n); - } -} - /////////////////////////////////////////////////////////////////////////////// // // // checkmesh() Test the mesh for topological consistency. // @@ -26482,12 +28403,12 @@ int tetgenmesh::checkmesh(int topoflag) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkshells() +int tetgenmesh::checkshells(/*int sub2tet*/) { triface neightet, symtet; face shloop, spinsh, nextsh; face checkseg; - point pa, pb; + point pa, pb; //, *ppt; int bakcount; int horrors, i; @@ -26515,10 +28436,10 @@ int tetgenmesh::checkshells() while ((nextsh.sh != NULL) && (nextsh.sh != shloop.sh)) { if (nextsh.sh[3] == NULL) { printf(" !! !! Wrong subface-subface connection (Dead subface).\n"); - printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" First: x%lx (%d, %d, %d).\n", (unsigned long long) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Second: x%lx (DEAD)\n", (uintptr_t) nextsh.sh); + printf(" Second: x%lx (DEAD)\n", (unsigned long long) nextsh.sh); horrors++; break; } @@ -26526,10 +28447,10 @@ int tetgenmesh::checkshells() if (!(((sorg(nextsh) == pa) && (sdest(nextsh) == pb)) || ((sorg(nextsh) == pb) && (sdest(nextsh) == pa)))) { printf(" !! !! Wrong subface-subface connection.\n"); - printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" First: x%lx (%d, %d, %d).\n", (unsigned long long) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, + printf(" Scond: x%lx (%d, %d, %d).\n", (unsigned long long) nextsh.sh, pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), pointmark(sapex(nextsh))); horrors++; @@ -26538,10 +28459,10 @@ int tetgenmesh::checkshells() // Check they should not have the same apex. if (sapex(nextsh) == sapex(spinsh)) { printf(" !! !! Existing two duplicated subfaces.\n"); - printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" First: x%lx (%d, %d, %d).\n", (unsigned long long) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, + printf(" Scond: x%lx (%d, %d, %d).\n", (unsigned long long) nextsh.sh, pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), pointmark(sapex(nextsh))); horrors++; @@ -26555,19 +28476,19 @@ int tetgenmesh::checkshells() if (checkseg.sh != NULL) { if (checkseg.sh[3] == NULL) { printf(" !! !! Wrong subface-subseg connection (Dead subseg).\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Sub: x%lx (Dead)\n", (uintptr_t) checkseg.sh); + printf(" Sub: x%lx (Dead)\n", (unsigned long long) checkseg.sh); horrors++; } else { if (!(((sorg(checkseg) == pa) && (sdest(checkseg) == pb)) || ((sorg(checkseg) == pb) && (sdest(checkseg) == pa)))) { printf(" !! !! Wrong subface-subseg connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Seg: x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, + printf(" Seg: x%lx (%d, %d).\n", (unsigned long long) checkseg.sh, pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); horrors++; } @@ -26581,20 +28502,20 @@ int tetgenmesh::checkshells() if (neightet.tet != NULL) { if (neightet.tet[4] == NULL) { printf(" !! !! Wrong sub-to-tet connection (Dead tet)\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Tet: x%lx (DEAD)\n", (uintptr_t) neightet.tet); + printf(" Tet: x%lx (DEAD)\n", (unsigned long long) neightet.tet); horrors++; } else { if (!((sorg(shloop) == org(neightet)) && (sdest(shloop) == dest(neightet)))) { printf(" !! !! Wrong sub-to-tet connection\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); printf(" Tet: x%lx (%d, %d, %d, %d).\n", - (uintptr_t) neightet.tet, pointmark(org(neightet)), + (unsigned long long) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); horrors++; @@ -26603,11 +28524,11 @@ int tetgenmesh::checkshells() if (!((sorg(spinsh) == org(neightet)) && (sdest(spinsh) == dest(neightet)))) { printf(" !! !! Wrong tet-sub connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); printf(" Tet: x%lx (%d, %d, %d, %d).\n", - (uintptr_t) neightet.tet, pointmark(org(neightet)), + (unsigned long long) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); horrors++; @@ -26618,11 +28539,11 @@ int tetgenmesh::checkshells() if (!((sorg(spinsh) == org(symtet)) && (sdest(spinsh) == dest(symtet)))) { printf(" !! !! Wrong tet-sub connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); printf(" Tet: x%lx (%d, %d, %d, %d).\n", - (uintptr_t) symtet.tet, pointmark(org(symtet)), + (unsigned long long) symtet.tet, pointmark(org(symtet)), pointmark(dest(symtet)), pointmark(apex(symtet)), pointmark(oppo(symtet))); horrors++; @@ -26677,10 +28598,8 @@ int tetgenmesh::checksegments() face sseg, checkseg; point pa, pb; int miscount; - int t1ver; int horrors, i; - if (!b->quiet) { printf(" Checking tet->seg connections...\n"); } @@ -26704,9 +28623,9 @@ int tetgenmesh::checksegments() ((org(tetloop) == pb) && (dest(tetloop) == pa)))) { printf(" !! Wrong tet-seg connection.\n"); printf(" Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", - (uintptr_t) tetloop.tet, pointmark(org(tetloop)), + (unsigned long long) tetloop.tet, pointmark(org(tetloop)), pointmark(dest(tetloop)), pointmark(apex(tetloop)), - pointmark(oppo(tetloop)), (uintptr_t) sseg.sh, + pointmark(oppo(tetloop)), (unsigned long long) sseg.sh, pointmark(pa), pointmark(pb)); horrors++; } else { @@ -26717,11 +28636,11 @@ int tetgenmesh::checksegments() if (checkseg.sh != sseg.sh) { printf(" !! Wrong tet->seg connection.\n"); printf(" Tet: x%lx (%d, %d, %d, %d) - ", - (uintptr_t) neightet.tet, pointmark(org(neightet)), + (unsigned long long) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); if (checkseg.sh != NULL) { - printf("Seg x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, + printf("Seg x%lx (%d, %d).\n", (unsigned long long) checkseg.sh, pointmark(sorg(checkseg)),pointmark(sdest(checkseg))); } else { printf("Seg: NULL.\n"); @@ -26741,9 +28660,9 @@ int tetgenmesh::checksegments() ((org(neightet) == pb) && (dest(neightet) == pa)))) { printf(" !! Wrong seg->tet connection (Wrong edge).\n"); printf(" Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", - (uintptr_t) neightet.tet, pointmark(org(neightet)), + (unsigned long long) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), - pointmark(oppo(neightet)), (uintptr_t) sseg.sh, + pointmark(oppo(neightet)), (unsigned long long) sseg.sh, pointmark(pa), pointmark(pb)); horrors++; } @@ -26760,7 +28679,7 @@ int tetgenmesh::checksegments() printf(" !! A marked edge: (%d, %d, %d, %d) -- x%lx %d.\n", pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet)), - (uintptr_t) neightet.tet, neightet.ver); + (unsigned long long) neightet.tet, neightet.ver); // Check if all tets at the edge are marked. spintet = neightet; while (1) { @@ -26769,7 +28688,7 @@ int tetgenmesh::checksegments() printf(" !! !! An unmarked edge (%d, %d, %d, %d) -- x%lx %d.\n", pointmark(org(spintet)), pointmark(dest(spintet)), pointmark(apex(spintet)), pointmark(oppo(spintet)), - (uintptr_t) spintet.tet, spintet.ver); + (unsigned long long) spintet.tet, spintet.ver); horrors++; } if (spintet.tet == neightet.tet) break; @@ -26802,7 +28721,7 @@ int tetgenmesh::checksegments() // sesymself(spinsh); // printf(" !! Wrong ori at subface (%d, %d, %d) -- x%lx %d\n", // pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), - // pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, + // pointmark(sapex(spinsh)), (unsigned long long) spinsh.sh, // spinsh.shver); // horrors++; //} @@ -26815,7 +28734,7 @@ int tetgenmesh::checksegments() printf(" !! !! No seg at tet (%d, %d, %d, %d) -- x%lx %d\n", pointmark(org(spintet)), pointmark(dest(spintet)), pointmark(apex(spintet)), pointmark(oppo(spintet)), - (uintptr_t) spintet.tet, spintet.ver); + (unsigned long long) spintet.tet, spintet.ver); horrors++; } if (checkseg.sh != sseg.sh) { @@ -26834,7 +28753,7 @@ int tetgenmesh::checksegments() } else { printf(" !! Wrong seg-subface (%d, %d, %d) -- x%lx %d connect\n", pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), - pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, + pointmark(sapex(spinsh)), (unsigned long long) spinsh.sh, spinsh.shver); horrors++; break; @@ -27115,7 +29034,6 @@ int tetgenmesh::checkconforming(int flag) REAL cent[3], radius, dist, diff, rd, len; bool enq; int encsubsegs, encsubfaces; - int t1ver; int i; REAL A[4][4], rhs[4], D; @@ -27328,7 +29246,7 @@ void tetgenmesh::qualitystatistics() for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i]; // Get the tet volume. - tetvol = orient3dfast(p[1], p[0], p[2], p[3]) / 6.0; + tetvol = orient3d(p[1], p[0], p[2], p[3]) / 6.0; total_tet_vol += tetvol; total_tetprism_vol += tetprismvol(p[0], p[1], p[2], p[3]); @@ -27632,97 +29550,6 @@ void tetgenmesh::qualitystatistics() } -/////////////////////////////////////////////////////////////////////////////// -// // -// memorystatistics() Report the memory usage. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::memorystatistics() -{ - printf("Memory usage statistics:\n\n"); - - // Count the number of blocks of tetrahedra. - int tetblocks = 0; - tetrahedrons->pathblock = tetrahedrons->firstblock; - while (tetrahedrons->pathblock != NULL) { - tetblocks++; - tetrahedrons->pathblock = (void **) *(tetrahedrons->pathblock); - } - - // Calculate the total memory (in bytes) used by storing meshes. - unsigned long totalmeshmemory = 0l, totalt2shmemory = 0l; - totalmeshmemory = points->maxitems * points->itembytes + - tetrahedrons->maxitems * tetrahedrons->itembytes; - if (b->plc || b->refine) { - totalmeshmemory += (subfaces->maxitems * subfaces->itembytes + - subsegs->maxitems * subsegs->itembytes); - totalt2shmemory = (tet2subpool->maxitems * tet2subpool->itembytes + - tet2segpool->maxitems * tet2segpool->itembytes); - } - - unsigned long totalalgomemory = 0l; - totalalgomemory = cavetetlist->totalmemory + cavebdrylist->totalmemory + - caveoldtetlist->totalmemory + - flippool->maxitems * flippool->itembytes; - if (b->plc || b->refine) { - totalalgomemory += (subsegstack->totalmemory + subfacstack->totalmemory + - subvertstack->totalmemory + - suppsteinerptlist->totalmemory + - caveshlist->totalmemory + caveshbdlist->totalmemory + - cavesegshlist->totalmemory + - cavetetshlist->totalmemory + - cavetetseglist->totalmemory + - caveencshlist->totalmemory + - caveencseglist->totalmemory + - cavetetvertlist->totalmemory + - unflipqueue->totalmemory); - } - - printf(" Maximum number of tetrahedra: %ld\n", tetrahedrons->maxitems); - printf(" Maximum number of tet blocks (blocksize = %d): %d\n", - b->tetrahedraperblock, tetblocks); - /* - if (b->plc || b->refine) { - printf(" Approximate memory for tetrahedral mesh (bytes): %ld\n", - totalmeshmemory); - - printf(" Approximate memory for extra pointers (bytes): %ld\n", - totalt2shmemory); - } else { - printf(" Approximate memory for tetrahedralization (bytes): %ld\n", - totalmeshmemory); - } - printf(" Approximate memory for algorithms (bytes): %ld\n", - totalalgomemory); - printf(" Approximate memory for working arrays (bytes): %ld\n", - totalworkmemory); - printf(" Approximate total used memory (bytes): %ld\n", - totalmeshmemory + totalt2shmemory + totalalgomemory + - totalworkmemory); - */ - if (b->plc || b->refine) { - printf(" Approximate memory for tetrahedral mesh (bytes): "); - printfcomma(totalmeshmemory); printf("\n"); - - printf(" Approximate memory for extra pointers (bytes): "); - printfcomma(totalt2shmemory); printf("\n"); - } else { - printf(" Approximate memory for tetrahedralization (bytes): "); - printfcomma(totalmeshmemory); printf("\n"); - } - printf(" Approximate memory for algorithms (bytes): "); - printfcomma(totalalgomemory); printf("\n"); - printf(" Approximate memory for working arrays (bytes): "); - printfcomma(totalworkmemory); printf("\n"); - printf(" Approximate total used memory (bytes): "); - printfcomma(totalmeshmemory + totalt2shmemory + totalalgomemory + - totalworkmemory); - printf("\n"); - - printf("\n"); -} - /////////////////////////////////////////////////////////////////////////////// // // // statistics() Print all sorts of cool facts. // @@ -27748,44 +29575,26 @@ void tetgenmesh::statistics() tetnumber = tetrahedrons->items - hullsize; facenumber = (tetnumber * 4l + hullsize) / 2l; - if (b->weighted) { // -w option - printf("\n Mesh points: %ld\n", points->items - nonregularcount); - } else { - printf("\n Mesh points: %ld\n", points->items); - } + printf("\n Mesh points: %ld\n", points->items); printf(" Mesh tetrahedra: %ld\n", tetnumber); printf(" Mesh faces: %ld\n", facenumber); - if (meshedges > 0l) { - printf(" Mesh edges: %ld\n", meshedges); - } else { - if (!nonconvex) { - long vsize = points->items - dupverts - unuverts; - if (b->weighted) vsize -= nonregularcount; - meshedges = vsize + facenumber - tetnumber - 1; - printf(" Mesh edges: %ld\n", meshedges); - } - } + printf(" Mesh edges: %ld\n", meshedges); if (b->plc || b->refine) { printf(" Mesh boundary faces: %ld\n", subfaces->items); printf(" Mesh boundary edges: %ld\n", subsegs->items); if (st_segref_count > 0l) { - printf(" Steiner points on boundary edges: %ld\n", st_segref_count); + printf(" Steiner points in boundary edges: %ld\n", st_segref_count); } if (st_facref_count > 0l) { - printf(" Steiner points on boundary faces: %ld\n", st_facref_count); + printf(" Steiner points in boundary faces: %ld\n", st_facref_count); } if (st_volref_count > 0l) { - printf(" Steiner points inside mesh domain: %ld\n", st_volref_count); + printf(" Steiner points in mesh domain: %ld\n", st_volref_count); } } else { printf(" Convex hull faces: %ld\n", hullsize); - if (meshhulledges > 0l) { - printf(" Convex hull edges: %ld\n", meshhulledges); - } - } - if (b->weighted) { // -w option - printf(" Skipped non-regular points: %ld\n", nonregularcount); + printf(" Convex hull edges: %ld\n", meshhulledges); } printf("\n"); @@ -27796,9 +29605,6 @@ void tetgenmesh::statistics() qualitystatistics(); } } - if (tetrahedrons->items > 0l) { - memorystatistics(); - } } } @@ -27841,7 +29647,7 @@ void tetgenmesh::jettisonnodes() jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || (pointtype(pointloop) == UNUSEDVERTEX); if (jetflag) { - // It is a duplicated or unused point, delete it. + // It is a duplicated point, delete it. pointdealloc(pointloop); remcount++; } else { @@ -27856,117 +29662,28 @@ void tetgenmesh::jettisonnodes() newidx++; } oldidx++; + //if (oldidx == in->numberofpoints) { + // // Update the numbe of input points (Because some were removed). + // in->numberofpoints -= remcount; + // // Remember this number for output original input nodes. + // jettisoninverts = remcount; + //} pointloop = pointtraverse(); } - if (b->verbose) { - printf(" %ld duplicated vertices are removed.\n", dupverts); - printf(" %ld unused vertices are removed.\n", unuverts); - } - dupverts = 0l; - unuverts = 0l; - - // The following line ensures that dead items in the pool of nodes cannot - // be allocated for the new created nodes. This ensures that the input - // nodes will occur earlier in the output files, and have lower indices. - points->deaditemstack = (void *) NULL; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// highorder() Create extra nodes for quadratic subparametric elements. // -// // -// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing // -// high-order nodes of each tetrahedron. This routine is used only when -o2 // -// switch is used. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::highorder() -{ - triface tetloop, worktet, spintet; - point *extralist, *adjextralist; - point torg, tdest, newpoint; - int highorderindex; - int t1ver; - int i, j; - - if (!b->quiet) { - printf("Adding vertices for second-order tetrahedra.\n"); - } - - // Initialize the 'highordertable'. - highordertable = new point[tetrahedrons->items * 6]; - if (highordertable == (point *) NULL) { - terminatetetgen(1); + if (b->verbose) { + printf(" %d duplicated vertices are removed.\n", dupverts); + printf(" %d unused vertices are removed.\n", unuverts); } - - // This will overwrite the slot for element markers. - highorderindex = 11; + dupverts = 0; + unuverts = 0; // The following line ensures that dead items in the pool of nodes cannot - // be allocated for the extra nodes associated with high order elements. - // This ensures that the primary nodes (at the corners of elements) will - // occur earlier in the output files, and have lower indices, than the - // extra nodes. + // be allocated for the new created nodes. This ensures that the input + // nodes will occur earlier in the output files, and have lower indices. points->deaditemstack = (void *) NULL; - - // Assign an entry for each tetrahedron to find its extra nodes. At the - // mean while, initialize all extra nodes be NULL. - i = 0; - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - tetloop.tet[highorderindex] = (tetrahedron) &highordertable[i]; - for (j = 0; j < 6; j++) { - highordertable[i + j] = (point) NULL; - } - i += 6; - tetloop.tet = tetrahedrontraverse(); - } - - // To create a unique node on each edge. Loop over all tetrahedra, and - // look at the six edges of each tetrahedron. If the extra node in - // the tetrahedron corresponding to this edge is NULL, create a node - // for this edge, at the same time, set the new node into the extra - // node lists of all other tetrahedra sharing this edge. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - // Get the list of extra nodes. - extralist = (point *) tetloop.tet[highorderindex]; - worktet.tet = tetloop.tet; - for (i = 0; i < 6; i++) { - if (extralist[i] == (point) NULL) { - // Go to the ith-edge. - worktet.ver = edge2ver[i]; - // Create a new point in the middle of this edge. - torg = org(worktet); - tdest = dest(worktet); - makepoint(&newpoint, FREEVOLVERTEX); - for (j = 0; j < 3 + numpointattrib; j++) { - newpoint[j] = 0.5 * (torg[j] + tdest[j]); - } - // Interpolate its metrics. - for (j = 0; j < in->numberofpointmtrs; j++) { - newpoint[pointmtrindex + j] = - 0.5 * (torg[pointmtrindex + j] + tdest[pointmtrindex + j]); - } - // Set this point into all extra node lists at this edge. - spintet = worktet; - while (1) { - if (!ishulltet(spintet)) { - adjextralist = (point *) spintet.tet[highorderindex]; - adjextralist[ver2edge[spintet.ver]] = newpoint; - } - fnextself(spintet); - if (spintet.tet == worktet.tet) break; - } - } // if (!extralist[i]) - } // i - tetloop.tet = tetrahedrontraverse(); - } } + /////////////////////////////////////////////////////////////////////////////// // // // numberedges() Count the number of edges, save in "meshedges". // @@ -27974,18 +29691,28 @@ void tetgenmesh::highorder() // This routine is called when '-p' or '-r', and '-E' options are used. The // // total number of edges depends on the genus of the input surface mesh. // // // -// NOTE: This routine must be called after outelements(). So all elements // -// have been indexed. // -// // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::numberedges() { triface worktet, spintet; + int firstindex, eindex; int ishulledge; - int t1ver; int i; + // Determine the first index (0 or 1). + firstindex = b->zeroindex ? 0 : in->firstnumber; + + // First indexing all tetrahedra. + tetrahedrons->traversalinit(); + eindex = firstindex; + worktet.tet = tetrahedrontraverse(); + while (worktet.tet != NULL) { + setelemindex(worktet.tet, eindex); + eindex++; + worktet.tet = tetrahedrontraverse(); + } + meshedges = meshhulledges = 0l; tetrahedrons->traversalinit(); @@ -28031,7 +29758,7 @@ void tetgenmesh::outnodes(tetgenio* out) char outnodefilename[FILENAMESIZE]; face parentsh; point pointloop; - int nextras, bmark, marker = 0, weightDT = 0; + int nextras, bmark, marker = 0; int coordindex, attribindex; int pointnumber, firstindex; int index, i; @@ -28049,11 +29776,7 @@ void tetgenmesh::outnodes(tetgenio* out) } } - nextras = numpointattrib; - if (b->weighted) { // -w - if (b->weighted_param == 0) weightDT = 1; // Weighted DT. - } - + nextras = in->numberofpointattributes; bmark = !b->nobound && in->pointmarkerlist; if (out == (tetgenio *) NULL) { @@ -28137,13 +29860,7 @@ void tetgenmesh::outnodes(tetgenio* out) pointloop[0], pointloop[1], pointloop[2]); for (i = 0; i < nextras; i++) { // Write an attribute. - if ((i == 0) && weightDT) { - fprintf(outfile, " %.17g", pointloop[0] * pointloop[0] + - pointloop[1] * pointloop[1] + pointloop[2] * pointloop[2] - - pointloop[3 + i]); - } else { - fprintf(outfile, " %.17g", pointloop[3 + i]); - } + fprintf(outfile, " %.17g", pointloop[4 + i]); } if (bmark) { // Write the boundary marker. @@ -28175,13 +29892,7 @@ void tetgenmesh::outnodes(tetgenio* out) // Point attributes. for (i = 0; i < nextras; i++) { // Output an attribute. - if ((i == 0) && weightDT) { - out->pointattributelist[attribindex++] = - pointloop[0] * pointloop[0] + pointloop[1] * pointloop[1] + - pointloop[2] * pointloop[2] - pointloop[3 + i]; - } else { - out->pointattributelist[attribindex++] = pointloop[3 + i]; - } + out->pointattributelist[attribindex++] = pointloop[4 + i]; } if (bmark) { // Output the boundary marker. @@ -28301,6 +30012,7 @@ void tetgenmesh::outelements(tetgenio* out) FILE *outfile = NULL; char outelefilename[FILENAMESIZE]; tetrahedron* tptr; + triface worktet, spintet; point p1, p2, p3, p4; point *extralist; REAL *talist = NULL; @@ -28308,9 +30020,10 @@ void tetgenmesh::outelements(tetgenio* out) long ntets; int firstindex, shift; int pointindex, attribindex; - int highorderindex = 11; + int highorderindex = 10; // The reserved pointer. int elementnumber; int eextras; + int ishulledge; int i; if (out == (tetgenio *) NULL) { @@ -28329,7 +30042,7 @@ void tetgenmesh::outelements(tetgenio* out) // The number of tets excluding hull tets. ntets = tetrahedrons->items - hullsize; - eextras = numelemattrib; + eextras = in->numberoftetrahedronattributes; if (out == (tetgenio *) NULL) { outfile = fopen(outelefilename, "w"); if (outfile == (FILE *) NULL) { @@ -28373,13 +30086,8 @@ void tetgenmesh::outelements(tetgenio* out) tptr = tetrahedrontraverse(); elementnumber = firstindex; // in->firstnumber; while (tptr != (tetrahedron *) NULL) { - if (!b->reversetetori) { - p1 = (point) tptr[4]; - p2 = (point) tptr[5]; - } else { - p1 = (point) tptr[5]; - p2 = (point) tptr[4]; - } + p1 = (point) tptr[4]; + p2 = (point) tptr[5]; p3 = (point) tptr[6]; p4 = (point) tptr[7]; if (out == (tetgenio *) NULL) { @@ -28387,9 +30095,9 @@ void tetgenmesh::outelements(tetgenio* out) fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, pointmark(p1) - shift, pointmark(p2) - shift, pointmark(p3) - shift, pointmark(p4) - shift); - if (b->order == 2) { + if (0) { // if (b->order == 2) { extralist = (point *) tptr[highorderindex]; - // indices for six extra points. + // Tetrahedron number, indices for four points plus six extra points. fprintf(outfile, " %5d %5d %5d %5d %5d %5d", pointmark(extralist[0]) - shift, pointmark(extralist[1]) - shift, pointmark(extralist[2]) - shift, pointmark(extralist[3]) - shift, @@ -28404,7 +30112,7 @@ void tetgenmesh::outelements(tetgenio* out) tlist[pointindex++] = pointmark(p2) - shift; tlist[pointindex++] = pointmark(p3) - shift; tlist[pointindex++] = pointmark(p4) - shift; - if (b->order == 2) { + if (0) { // if (b->order == 2) { extralist = (point *) tptr[highorderindex]; tlist[pointindex++] = pointmark(extralist[0]) - shift; tlist[pointindex++] = pointmark(extralist[1]) - shift; @@ -28417,12 +30125,44 @@ void tetgenmesh::outelements(tetgenio* out) talist[attribindex++] = elemattribute(tptr, i); } } - // Remember the index of this element (for counting edges). - setelemindex(tptr, elementnumber); + //if (b->neighout) { + // Remember the index of this element. + setelemindex(tptr, elementnumber); + //} tptr = tetrahedrontraverse(); elementnumber++; } + // Count the number of edges (# Voronoi faces). + meshedges = meshhulledges = 0l; + + tetrahedrons->traversalinit(); + tptr = tetrahedrontraverse(); + while (tptr != (tetrahedron *) NULL) { + // Count the number of Voronoi faces. Look at the six edges of this + // tet. Count an edge only if this tet's pointer is smaller than + // those of other non-hull tets which share this edge. + worktet.tet = tptr; + for (i = 0; i < 6; i++) { + worktet.ver = edge2ver[i]; + ishulledge = 0; + fnext(worktet, spintet); + do { + if (!ishulltet(spintet)) { + if (elemindex(spintet.tet) < elemindex(worktet.tet)) break; + } else { + ishulledge = 1; + } + fnextself(spintet); + } while (spintet.tet != worktet.tet); + // Count this edge if no adjacent tets are smaller than this tet. + if (spintet.tet == worktet.tet) { + meshedges++; + if (ishulledge) meshhulledges++; + } + } + tptr = tetrahedrontraverse(); + } if (out == (tetgenio *) NULL) { fprintf(outfile, "# Generated by %s\n", b->commandline); @@ -28434,6 +30174,11 @@ void tetgenmesh::outelements(tetgenio* out) // // // outfaces() Output all faces to a .face file or a tetgenio object. // // // +// The total number of faces f can be calculated as following: Let t be the // +// total number of tets. Since each tet has 4 faces, the number t * 4 counts // +// each interior face twice and each hull face once. So f = (t * 4 + h) / 2, // +// where h is the total number of hull faces (which is known). // +// // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::outfaces(tetgenio* out) @@ -28449,13 +30194,7 @@ void tetgenmesh::outfaces(tetgenio* out) int faceid, marker = 0; int firstindex, shift; int facenumber; - int index = 0; - - // For -o2 option. - triface workface; - point *extralist, pp[3] = {0,0,0}; - int highorderindex = 11; - int o2index = 0, i; + int index; if (out == (tetgenio *) NULL) { strcpy(facefilename, b->outfilename); @@ -28472,6 +30211,7 @@ void tetgenmesh::outfaces(tetgenio* out) ntets = tetrahedrons->items - hullsize; faces = (ntets * 4l + hullsize) / 2l; + //bmark = !b->nobound && in->facetmarkerlist; if (out == (tetgenio *) NULL) { outfile = fopen(facefilename, "w"); @@ -28487,9 +30227,6 @@ void tetgenmesh::outfaces(tetgenio* out) printf("Error: Out of memory.\n"); terminatetetgen(1); } - if (b->order == 2) { - out->o2facelist = new int[faces * 3]; - } // Allocate memory for 'trifacemarkerlist' if necessary. if (!b->nobound) { out->trifacemarkerlist = new int[faces]; @@ -28509,6 +30246,7 @@ void tetgenmesh::outfaces(tetgenio* out) out->numberoftrifaces = faces; elist = out->trifacelist; emlist = out->trifacemarkerlist; + index = 0; } // Determine the first index (0 or 1). @@ -28533,16 +30271,6 @@ void tetgenmesh::outfaces(tetgenio* out) torg = org(tface); tdest = dest(tface); tapex = apex(tface); - if (b->order == 2) { // -o2 - // Get the three extra vertices on edges. - extralist = (point *) (tface.tet[highorderindex]); - // The extra vertices are on edges opposite the corners. - enext(tface, workface); - for (i = 0; i < 3; i++) { - pp[i] = extralist[ver2edge[workface.ver]]; - enextself(workface); - } - } if (!b->nobound) { // Get the boundary marker of this face. if (b->plc || b->refine) { @@ -28578,10 +30306,6 @@ void tetgenmesh::outfaces(tetgenio* out) fprintf(outfile, "%5d %4d %4d %4d", facenumber, pointmark(torg) - shift, pointmark(tdest) - shift, pointmark(tapex) - shift); - if (b->order == 2) { // -o2 - fprintf(outfile, " %4d %4d %4d", pointmark(pp[0]) - shift, - pointmark(pp[1]) - shift, pointmark(pp[2]) - shift); - } if (!b->nobound) { // Output a boundary marker. fprintf(outfile, " %d", marker); @@ -28595,11 +30319,6 @@ void tetgenmesh::outfaces(tetgenio* out) elist[index++] = pointmark(torg) - shift; elist[index++] = pointmark(tdest) - shift; elist[index++] = pointmark(tapex) - shift; - if (b->order == 2) { // -o2 - out->o2facelist[o2index++] = pointmark(pp[0]) - shift; - out->o2facelist[o2index++] = pointmark(pp[1]) - shift; - out->o2facelist[o2index++] = pointmark(pp[2]) - shift; - } if (!b->nobound) { emlist[facenumber - in->firstnumber] = marker; } @@ -28735,14 +30454,6 @@ void tetgenmesh::outsubfaces(tetgenio* out) int neigh1 = 0, neigh2 = 0; int facenumber; - // For -o2 option. - triface workface; - point *extralist, pp[3] = {0,0,0}; - int highorderindex = 11; - int o2index = 0, i; - - int t1ver; // used by fsymself() - if (out == (tetgenio *) NULL) { strcpy(facefilename, b->outfilename); strcat(facefilename, ".face"); @@ -28756,6 +30467,8 @@ void tetgenmesh::outsubfaces(tetgenio* out) } } + //bmark = !b->nobound && in->facetmarkerlist; + if (out == (tetgenio *) NULL) { outfile = fopen(facefilename, "w"); if (outfile == (FILE *) NULL) { @@ -28770,9 +30483,6 @@ void tetgenmesh::outsubfaces(tetgenio* out) if (out->trifacelist == (int *) NULL) { terminatetetgen(1); } - if (b->order == 2) { - out->o2facelist = new int[subfaces->items * 3]; - } if (!b->nobound) { // Allocate memory for 'trifacemarkerlist'. out->trifacemarkerlist = new int[subfaces->items]; @@ -28804,39 +30514,18 @@ void tetgenmesh::outsubfaces(tetgenio* out) facenumber = firstindex; // in->firstnumber; while (faceloop.sh != (shellface *) NULL) { stpivot(faceloop, abuttingtet); - // If there is a tetrahedron containing this subface, orient it so - // that the normal of this face points to inside of the volume by - // right-hand rule. - if (abuttingtet.tet != NULL) { - if (ishulltet(abuttingtet)) { - fsymself(abuttingtet); - assert(!ishulltet(abuttingtet)); - } - } if (abuttingtet.tet != NULL) { + // If there is a tetrahedron containing this subface, orient it so + // that the normal of this face points to inside of the volume by + // right-hand rule. torg = org(abuttingtet); tdest = dest(abuttingtet); tapex = apex(abuttingtet); - if (b->order == 2) { // -o2 - // Get the three extra vertices on edges. - extralist = (point *) (abuttingtet.tet[highorderindex]); - workface = abuttingtet; - for (i = 0; i < 3; i++) { - pp[i] = extralist[ver2edge[workface.ver]]; - enextself(workface); - } - } } else { // This may happen when only a surface mesh be generated. torg = sorg(faceloop); tdest = sdest(faceloop); tapex = sapex(faceloop); - if (b->order == 2) { // -o2 - // There is no extra node list available. - pp[0] = torg; - pp[1] = tdest; - pp[2] = tapex; - } } if (!b->nobound) { if (in->facetmarkerlist) { @@ -28863,10 +30552,6 @@ void tetgenmesh::outsubfaces(tetgenio* out) fprintf(outfile, "%5d %4d %4d %4d", facenumber, pointmark(torg) - shift, pointmark(tdest) - shift, pointmark(tapex) - shift); - if (b->order == 2) { // -o2 - fprintf(outfile, " %4d %4d %4d", pointmark(pp[0]) - shift, - pointmark(pp[1]) - shift, pointmark(pp[2]) - shift); - } if (!b->nobound) { fprintf(outfile, " %d", marker); } @@ -28879,11 +30564,6 @@ void tetgenmesh::outsubfaces(tetgenio* out) elist[index++] = pointmark(torg) - shift; elist[index++] = pointmark(tdest) - shift; elist[index++] = pointmark(tapex) - shift; - if (b->order == 2) { // -o2 - out->o2facelist[o2index++] = pointmark(pp[0]) - shift; - out->o2facelist[o2index++] = pointmark(pp[1]) - shift; - out->o2facelist[o2index++] = pointmark(pp[2]) - shift; - } if (!b->nobound) { emlist[index1++] = marker; } @@ -28922,15 +30602,9 @@ void tetgenmesh::outedges(tetgenio* out) int ishulledge; int firstindex, shift; int edgenumber, marker; - int index = 0, index1 = 0, index2 = 0; - int t1ver; + int index, index1; int i; - // For -o2 option. - point *extralist, pp = NULL; - int highorderindex = 11; - int o2index = 0; - if (out == (tetgenio *) NULL) { strcpy(edgefilename, b->outfilename); strcat(edgefilename, ".edge"); @@ -28944,20 +30618,6 @@ void tetgenmesh::outedges(tetgenio* out) } } - if (meshedges == 0l) { - if (nonconvex) { - numberedges(); // Count the edges. - } else { - // Use Euler's characteristic to get the numbe of edges. - // It states V - E + F - C = 1, hence E = V + F - C - 1. - long tsize = tetrahedrons->items - hullsize; - long fsize = (tsize * 4l + hullsize) / 2l; - long vsize = points->items - dupverts - unuverts; - if (b->weighted) vsize -= nonregularcount; - meshedges = vsize + fsize - tsize - 1; - } - } - if (out == (tetgenio *) NULL) { outfile = fopen(edgefilename, "w"); if (outfile == (FILE *) NULL) { @@ -28973,18 +30633,14 @@ void tetgenmesh::outedges(tetgenio* out) printf("Error: Out of memory.\n"); terminatetetgen(1); } - if (b->order == 2) { // -o2 switch - out->o2edgelist = new int[meshedges]; - } if (!b->nobound) { out->edgemarkerlist = new int[meshedges]; } - if (b->neighout > 1) { // '-nn' switch. - out->edgeadjtetlist = new int[meshedges]; - } out->numberofedges = meshedges; elist = out->edgelist; emlist = out->edgemarkerlist; + index = 0; + index1 = 0; // if (!b->nobound) } // Determine the first index (0 or 1). @@ -28998,7 +30654,9 @@ void tetgenmesh::outedges(tetgenio* out) tetloop.tet = tetrahedrontraverse(); edgenumber = firstindex; // in->firstnumber; while (tetloop.tet != (tetrahedron *) NULL) { - // Count the number of Voronoi faces. + // Count the number of Voronoi faces. Look at the six edges of this + // tet. Count an edge only if this tet's pointer is smaller than + // those of other non-hull tets which share this edge. worktet.tet = tetloop.tet; for (i = 0; i < 6; i++) { worktet.ver = edge2ver[i]; @@ -29016,24 +30674,13 @@ void tetgenmesh::outedges(tetgenio* out) if (spintet.tet == worktet.tet) { torg = org(worktet); tdest = dest(worktet); - if (b->order == 2) { // -o2 - // Get the extra vertex on this edge. - extralist = (point *) worktet.tet[highorderindex]; - pp = extralist[ver2edge[worktet.ver]]; - } if (out == (tetgenio *) NULL) { fprintf(outfile, "%5d %4d %4d", edgenumber, pointmark(torg) - shift, pointmark(tdest) - shift); - if (b->order == 2) { // -o2 - fprintf(outfile, " %4d", pointmark(pp) - shift); - } } else { // Output three vertices of this face; elist[index++] = pointmark(torg) - shift; elist[index++] = pointmark(tdest) - shift; - if (b->order == 2) { // -o2 - out->o2edgelist[o2index++] = pointmark(pp) - shift; - } } if (!b->nobound) { if (b->plc || b->refine) { @@ -29057,13 +30704,6 @@ void tetgenmesh::outedges(tetgenio* out) emlist[index1++] = marker; } } - if (b->neighout > 1) { // '-nn' switch. - if (out == (tetgenio *) NULL) { - fprintf(outfile, " %d", elemindex(tetloop.tet)); - } else { - out->edgeadjtetlist[index2++] = elemindex(tetloop.tet); - } - } if (out == (tetgenio *) NULL) { fprintf(outfile, "\n"); } @@ -29097,18 +30737,6 @@ void tetgenmesh::outsubsegments(tetgenio* out) int marker; int edgenumber; - // For -o2 option. - triface workface, spintet; - point *extralist, pp = NULL; - int highorderindex = 11; - int o2index = 0; - - // For -nn option. - int neigh = -1; - int index2 = 0; - - int t1ver; // used by fsymself() - if (out == (tetgenio *) NULL) { strcpy(edgefilename, b->outfilename); strcat(edgefilename, ".edge"); @@ -29133,20 +30761,13 @@ void tetgenmesh::outsubsegments(tetgenio* out) } else { // Allocate memory for 'edgelist'. out->edgelist = new int[subsegs->items * 2]; - out->edgelist = new int[subsegs->items * (b->order == 1 ? 2 : 3)]; if (out->edgelist == (int *) NULL) { terminatetetgen(1); } - if (b->order == 2) { - out->o2edgelist = new int[subsegs->items]; - } out->edgemarkerlist = new int[subsegs->items]; if (out->edgemarkerlist == (int *) NULL) { terminatetetgen(1); } - if (b->neighout > 1) { - out->edgeadjtetlist = new int[subsegs->items]; - } out->numberofedges = subsegs->items; elist = out->edgelist; } @@ -29166,64 +30787,18 @@ void tetgenmesh::outsubsegments(tetgenio* out) while (edgeloop.sh != (shellface *) NULL) { torg = sorg(edgeloop); tdest = sdest(edgeloop); - if ((b->order == 2) || (b->neighout > 1)) { - sstpivot1(edgeloop, workface); - if (workface.tet != NULL) { - // We must find a non-hull tet. - if (ishulltet(workface)) { - spintet = workface; - while (1) { - fnextself(spintet); - if (!ishulltet(spintet)) break; - if (spintet.tet == workface.tet) break; - } - assert(!ishulltet(spintet)); - workface = spintet; - } - } - } - if (b->order == 2) { // -o2 - // Get the extra vertex on this edge. - if (workface.tet != NULL) { - extralist = (point *) workface.tet[highorderindex]; - pp = extralist[ver2edge[workface.ver]]; - } else { - pp = torg; // There is no extra node available. - } - } - if (b->neighout > 1) { // -nn - if (workface.tet != NULL) { - neigh = elemindex(workface.tet); - } else { - neigh = -1; - } - } marker = shellmark(edgeloop); if (marker == 0) { marker = 1; // Default marker of a boundary edge is 1. } if (out == (tetgenio *) NULL) { - fprintf(outfile, "%5d %4d %4d", edgenumber, - pointmark(torg) - shift, pointmark(tdest) - shift); - if (b->order == 2) { // -o2 - fprintf(outfile, " %4d", pointmark(pp) - shift); - } - fprintf(outfile, " %d", marker); - if (b->neighout > 1) { // -nn - fprintf(outfile, " %4d", neigh); - } - fprintf(outfile, "\n"); + fprintf(outfile, "%5d %4d %4d %d\n", edgenumber, + pointmark(torg) - shift, pointmark(tdest) - shift, marker); } else { // Output three vertices of this face; elist[index++] = pointmark(torg) - shift; elist[index++] = pointmark(tdest) - shift; - if (b->order == 2) { // -o2 - out->o2edgelist[o2index++] = pointmark(pp) - shift; - } out->edgemarkerlist[i++] = marker; - if (b->neighout > 1) { // -nn - out->edgeadjtetlist[index2++] = neigh; - } } edgenumber++; edgeloop.sh = shellfacetraverse(subsegs); @@ -29337,12 +30912,6 @@ void tetgenmesh::outneighbors(tetgenio* out) // internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay // // vertex belonging to the convex hull. // // // -// aunay tetrahedralization - the power diagram - of the weighted point set. // -// Note that the vertices of the power disgram are the centers of the ortho- // -// spheres of the tetrahedra. // -// // -// NOTE: This routine is only used when the input is only a set of point. // -// // // Comment: Special thanks to Victor Liu for finding and fixing few bugs. // // // /////////////////////////////////////////////////////////////////////////////// @@ -29365,8 +30934,6 @@ void tetgenmesh::outvoronoi(tetgenio* out) int index, shift, end1, end2; int i, j; - int t1ver; // used by fsymself() - // Output Voronoi vertices to .v.node file. if (out == (tetgenio *) NULL) { strcpy(outfilename, b->outfilename); @@ -29407,9 +30974,8 @@ void tetgenmesh::outvoronoi(tetgenio* out) // The number of Delaunay faces (Voronoi edges). faces = (4l * ntets + hullsize) / 2l; // The number of Delaunay edges (Voronoi faces). - long vsize = points->items - dupverts - unuverts; - if (b->weighted) vsize -= nonregularcount; - edges = vsize + faces - ntets - 1; + // edges = points->items + faces - ntets - 1; + edges = meshedges; // Counted in outelements() or numberedges(); if (out == (tetgenio *) NULL) { outfile = fopen(outfilename, "w"); @@ -29438,12 +31004,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) pt[i] = (point) tetloop.tet[4 + i]; setpoint2tet(pt[i], encode(tetloop)); } - if (b->weighted) { - orthosphere(pt[0], pt[1], pt[2], pt[3], pt[0][3], pt[1][3], pt[2][3], - pt[3][3], ccent, NULL); - } else { - circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL); - } + circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL); if (out == (tetgenio *) NULL) { fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift, ccent[0], ccent[1], ccent[2]); @@ -29716,8 +31277,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) vpointcount = 0; while (ploop != (point) NULL) { if ((pointtype(ploop) != UNUSEDVERTEX) && - (pointtype(ploop) != DUPLICATEDVERTEX) && - (pointtype(ploop) != NREGULARVERTEX)) { + (pointtype(ploop) != DUPLICATEDVERTEX)) { getvertexstar(1, ploop, tetlist, ptlist, NULL); // Mark all vertices. Check if it is a hull vertex. ishullvert = 0; @@ -29777,6 +31337,14 @@ void tetgenmesh::outvoronoi(tetgenio* out) if (out == (tetgenio *) NULL) { fprintf(outfile, "\n"); } + // DEBUG BEGIN + for (i = 0; i < ptlist->objects; i++) { + neipt = * (point *) fastlookup(ptlist, i); + if (neipt != dummypoint) { + assert(!pinfected(neipt)); + } + } + // DEBUG END tetlist->restart(); ptlist->restart(); vpointcount++; @@ -30015,18 +31583,13 @@ void tetgenmesh::outmesh2medit(char* mfilename) tetrahedrons->traversalinit(); tetptr = tetrahedrontraverse(); while (tetptr != (tetrahedron *) NULL) { - if (!b->reversetetori) { - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; - } else { - p1 = (point) tetptr[5]; - p2 = (point) tetptr[4]; - } + p1 = (point) tetptr[4]; + p2 = (point) tetptr[5]; p3 = (point) tetptr[6]; p4 = (point) tetptr[7]; fprintf(outfile, "%5d %5d %5d %5d", pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); - if (numelemattrib > 0) { + if (in->numberoftetrahedronattributes > 0) { fprintf(outfile, " %.17g", elemattribute(tetptr, 0)); } else { fprintf(outfile, " 0"); @@ -30076,7 +31639,7 @@ void tetgenmesh::outmesh2vtk(char* ofilename) { FILE *outfile; char vtkfilename[FILENAMESIZE]; - point pointloop, p1, p2, p3, p4; + point pointloop; tetrahedron* tptr; double x, y, z; int n1, n2, n3, n4; @@ -30135,15 +31698,10 @@ void tetgenmesh::outmesh2vtk(char* ofilename) return; } while (tptr != (tetrahedron *) NULL) { - if (!b->reversetetori) { - p1 = (point) tptr[4]; - p2 = (point) tptr[5]; - } else { - p1 = (point) tptr[5]; - p2 = (point) tptr[4]; - } - p3 = (point) tptr[6]; - p4 = (point) tptr[7]; + point p1 = (point) tptr[4]; + point p2 = (point) tptr[5]; + point p3 = (point) tptr[6]; + point p4 = (point) tptr[7]; n1 = pointmark(p1) - in->firstnumber; n2 = pointmark(p2) - in->firstnumber; n3 = pointmark(p3) - in->firstnumber; @@ -30198,149 +31756,186 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, tetgenio *addin, tetgenio *bgmin) { tetgenmesh m; - clock_t tv[10], ts[5]; // Timing informations (defined in time.h) - REAL cps = (REAL) CLOCKS_PER_SEC; + clock_t tv[17]; // Timing informations (defined in time.h) tv[0] = clock(); m.b = b; m.in = in; - m.addin = addin; - if ((b->metric) && (bgmin->numberofpoints > 0)) { + if ((bgmin != NULL) && + ((bgmin->numberofpoints > 0) && (bgmin->pointmtrlist != NULL))) { m.bgm = new tetgenmesh(); // Create an empty background mesh. m.bgm->b = b; m.bgm->in = bgmin; } +#ifdef INEXACT_GEOM_PRED + if (!b->quiet) { + printf("Using inexact geometric predicates.\n"); + } +#else + exactinit(); +#endif + m.initializepools(); m.transfernodes(); - exactinit(b->verbose, b->noexact, b->nostaticfilter, - m.xmax - m.xmin, m.ymax - m.ymin, m.zmax - m.zmin); - tv[1] = clock(); if (b->refine) { m.reconstructmesh(); } else { // b->plc - m.incrementaldelaunay(ts[0]); + if (!b->diagnose) { + m.incrementaldelaunay(tv[16]); + } } tv[2] = clock(); if (!b->quiet) { if (b->refine) { - printf("Mesh reconstruction seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps); + printf("Mesh reconstruction seconds: %g\n", + (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC); } else { - printf("Delaunay seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps); - if (b->verbose) { - printf(" Point sorting seconds: %g\n", ((REAL)(ts[0]-tv[1])) / cps); + if (!b->diagnose) { + printf("Delaunay seconds: %g\n", + (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC); + if (b->verbose) { + printf(" Point sorting seconds: %g\n", + (tv[16] - tv[1]) / (REAL) CLOCKS_PER_SEC); +#ifdef WITH_RUNTIME_COUNTERS + printf(" Point location seconds: %g\n", + m.t_ptloc / (REAL) CLOCKS_PER_SEC); + printf(" Point insertion seconds: %g\n", + m.t_ptinsert / (REAL) CLOCKS_PER_SEC); +#endif + } } } } - if (b->plc) { // -p + if (b->plc) { m.meshsurface(); + } - ts[0] = clock(); - - if (!b->quiet) { - printf("Surface mesh seconds: %g\n", ((REAL)(ts[0]-tv[2])) / cps); - } - - if (b->diagnose) { // -d - m.detectinterfaces(); - - ts[1] = clock(); - - if (!b->quiet) { - printf("Self-intersection seconds: %g\n", ((REAL)(ts[1]-ts[0])) / cps); - } - - // Only output when self-intersecting faces exist. - if (m.subfaces->items > 0l) { - m.outnodes(out); - m.outsubfaces(out); - } + tv[3] = clock(); - return; + if (!b->quiet) { + if (b->plc) { + printf("Surface mesh seconds: %g\n", + (tv[3] - tv[2]) / (REAL) CLOCKS_PER_SEC); } } - tv[3] = clock(); - - if ((b->metric) && (m.bgm != NULL)) { // -m - m.bgm->initializepools(); - m.bgm->transfernodes(); - m.bgm->reconstructmesh(); + if (b->plc && b->diagnose) { // -d + m.detectinterfaces(); - ts[0] = clock(); + tv[4] = clock(); if (!b->quiet) { - printf("Background mesh reconstruct seconds: %g\n", - ((REAL)(ts[0] - tv[3])) / cps); + printf("Self-intersection seconds: %g\n", + (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); } - if (b->metric) { // -m - m.interpolatemeshsize(); - - ts[1] = clock(); - - if (!b->quiet) { - printf("Size interpolating seconds: %g\n",((REAL)(ts[1]-ts[0])) / cps); - } + // Only output when self-intersecting faces exist. + if (m.subfaces->items > 0l) { + m.outnodes(out); + m.outsubfaces(out); } - } - tv[4] = clock(); + return; + } - if (b->plc) { // -p - if (b->nobisect) { // -Y - m.recoverboundary(ts[0]); + if (b->plc) { + if (b->nobisect) { // with -Y option + m.recoverboundary(tv[15]); } else { - m.constraineddelaunay(ts[0]); + m.constraineddelaunay(tv[15]); } + } - ts[1] = clock(); + tv[4] = clock(); - if (!b->quiet) { - printf("Boundary recovery seconds: %g\n", ((REAL)(ts[1]-tv[4])) / cps); + if (!b->quiet) { + if (b->plc) { + printf("Boundary recovery seconds: %g\n", + (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); if (b->verbose) { - printf(" Segment recovery seconds: %g\n",((REAL)(ts[0]-tv[4]))/ cps); - printf(" Facet recovery seconds: %g\n", ((REAL)(ts[1]-ts[0])) / cps); + printf(" Segment recovery seconds: %g\n", + (tv[15] - tv[3]) / (REAL) CLOCKS_PER_SEC); + printf(" Facet recovery seconds: %g\n", + (tv[4] - tv[15]) / (REAL) CLOCKS_PER_SEC); } } + } + if (b->plc && !b->convex) { m.carveholes(); + } - ts[2] = clock(); + tv[5] = clock(); - if (!b->quiet) { + if (!b->quiet) { + if (b->plc && !b->convex) { printf("Exterior tets removal seconds: %g\n", - ((REAL)(ts[2]-ts[1])) / cps); + (tv[5] - tv[4]) / (REAL) CLOCKS_PER_SEC); } + } - if (b->nobisect) { // -Y - m.suppresssteinerpoints(); + if (b->plc && b->nobisect) { + m.suppresssteinerpoints(); + } - ts[3] = clock(); + tv[6] = clock(); - if (!b->quiet) { - printf("Steiner suppression seconds: %g\n",((REAL)(ts[3]-ts[2]))/cps); - } + if (!b->quiet) { + if (b->plc && b->nobisect) { + printf("Steiner suppression seconds: %g\n", + (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC); + } + } - m.recoverdelaunay(); + if (b->plc && b->nobisect) { + m.recoverdelaunay(); + } - ts[4] = clock(); + tv[7] = clock(); - if (!b->quiet) { - printf("Delaunay recovery seconds: %g\n", ((REAL)(ts[4]-ts[3])) / cps); - } + if (!b->quiet) { + if (b->plc && b->nobisect) { + printf("Delaunay recovery seconds: %g\n", + (tv[7] - tv[6]) / (REAL) CLOCKS_PER_SEC); } } - tv[5] = clock(); + if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) { + m.bgm->initializepools(); + m.bgm->transfernodes(); + m.bgm->reconstructmesh(); + } + + tv[8] = clock(); + + if (!b->quiet) { + if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) { + printf("Background mesh reconstruct seconds: %g\n", + (tv[8] - tv[7]) / (REAL) CLOCKS_PER_SEC); + } + } + + if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) { + m.interpolatemeshsize(); + } + + tv[9] = clock(); + + if (!b->quiet) { + if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) { + printf("Size interpolating seconds: %g\n", + (tv[9] - tv[8]) / (REAL) CLOCKS_PER_SEC); + } + } if ((b->plc || b->refine) && b->insertaddpoints) { // -i if ((addin != NULL) && (addin->numberofpoints > 0)) { @@ -30348,38 +31943,44 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } - tv[6] = clock(); + tv[10] = clock(); if (!b->quiet) { if ((b->plc || b->refine) && b->insertaddpoints) { if ((addin != NULL) && (addin->numberofpoints > 0)) { - printf("Constrained points seconds: %g\n", ((REAL)(tv[6]-tv[5]))/cps); + printf("Constrained points seconds: %g\n", + (tv[10] - tv[9]) / (REAL) CLOCKS_PER_SEC); } } } - if (b->quality) { + tv[11] = clock(); + + + if ((b->plc || b->refine) && b->quality) { m.delaunayrefinement(); } - tv[7] = clock(); + tv[12] = clock(); if (!b->quiet) { - if (b->quality) { - printf("Refinement seconds: %g\n", ((REAL)(tv[7] - tv[6])) / cps); + if ((b->plc || b->refine) && b->quality) { + printf("Refinement seconds: %g\n", + (tv[12] - tv[11]) / (REAL) CLOCKS_PER_SEC); } } - if ((b->plc || b->refine) && (b->optlevel > 0) && !b->conforming) { - m.optimizemesh(); + if ((b->plc || b->refine) && (b->optlevel > 0)) { + m.optimizemesh(1); } - tv[8] = clock(); + tv[13] = clock(); if (!b->quiet) { - if ((b->plc || b->refine) && (b->optlevel > 0) && !b->conforming) { - printf("Optimization seconds: %g\n", ((REAL)(tv[8] - tv[7])) / cps); + if ((b->plc || b->refine) && (b->optlevel > 0)) { + printf("Optimization seconds: %g\n", + (tv[13] - tv[12]) / (REAL) CLOCKS_PER_SEC); } } @@ -30388,9 +31989,6 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.jettisonnodes(); } - if (b->order == 2) { - m.highorder(); - } if (!b->quiet) { printf("\n"); @@ -30409,10 +32007,11 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.outnodes(out); } - if (b->noelewritten) { + if (b->noelewritten == 1) { if (!b->quiet) { printf("NOT writing an .ele file.\n"); } + m.numberedges(); } else { if (m.tetrahedrons->items > 0l) { m.outelements(out); @@ -30442,17 +32041,11 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } - if (b->nofacewritten) { - if (!b->quiet) { - printf("NOT writing an .edge file.\n"); - } - } else { - if (b->edgesout) { // -e - m.outedges(out); // output all mesh edges. + if (b->edgesout) { + if (b->edgesout > 1) { + m.outedges(out); // -ee, output all mesh edges. } else { - if (b->plc || b->refine) { - m.outsubsegments(out); // output subsegments. - } + m.outsubsegments(out); // -e, only output subsegments. } } @@ -30485,11 +32078,13 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } - tv[9] = clock(); + tv[14] = clock(); if (!b->quiet) { - printf("\nOutput seconds: %g\n", ((REAL)(tv[9] - tv[8])) / cps); - printf("Total running seconds: %g\n", ((REAL)(tv[9] - tv[0])) / cps); + printf("\nOutput seconds: %g\n", + (tv[14] - tv[13]) / (REAL) CLOCKS_PER_SEC); + printf("Total running seconds: %g\n", + (tv[14] - tv[0]) / (REAL) CLOCKS_PER_SEC); } if (b->docheck) { diff --git a/contrib/Tetgen1.5/tetgen.h b/contrib/Tetgen1.5/tetgen.h index 664ddc3f779484e41de2c5d074ab7123737f3250..41634d03f49ee9d805eb18e5511c672f8d23478a 100644 --- a/contrib/Tetgen1.5/tetgen.h +++ b/contrib/Tetgen1.5/tetgen.h @@ -2,17 +2,21 @@ // // // TetGen // // // -// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // +// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // // // // Version 1.5 // -// (March 27, 2013) // +// February 21, 2012 // // // -// Copyright (C) 2002--2013 // +// PRE-RELEASE TEST CODE. // +// PLEASE DO NOT DISTRIBUTE !! // +// PLEASE HELP ME TO IMPROVE IT !! // +// // +// Copyright (C) 2002--2012 // // Hang Si // // Research Group: Numerical Mathematics and Scientific Computing // // Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // // Mohrenstr. 39, 10117 Berlin, Germany // -// si@wias-berlin.de // +// Hang.Si@wias-berlin.de // // // // TetGen is freely available through the website: http://www.tetgen.org. // // It may be copied, modified, and redistributed for non-commercial use. // @@ -24,51 +28,46 @@ #ifndef tetgenH #define tetgenH +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <time.h> +#include <assert.h> + // To compile TetGen as a library instead of an executable program, define // the TETLIBRARY symbol. // #define TETLIBRARY -// Uncomment the following line to disable assert macros. These macros were -// inserted in the code where I hoped to catch bugs. They may slow down the -// speed of TetGen. +// Uncomment the following line to disable assert macros. These macros are +// inserted in places where I hope to catch bugs. // #define NDEBUG -// TetGen default uses the double precision (64 bit) for a real number. -// Alternatively, one can use the single precision (32 bit) 'float' if the -// memory is limited. +// To insert lots of self-checks for internal errors, define the SELF_CHECK +// symbol. This will slow down the program a bit. -#define REAL double // #define REAL float +// #define SELF_CHECK -// Maximum number of characters in a file name (including the null). +// Default, TetGen uses the double precision for a real number. -#define FILENAMESIZE 1024 - -// Maximum number of chars in a line read from a file (including the null). +#define REAL double -#define INPUTLINESIZE 2048 +// The types 'intptr_t' and 'uintptr_t' are signed and unsigned integer types, +// respectively. They are guaranteed to be the same width as a pointer. +// They are defined in <stdint.h> by the C99 Standard. +// However, Microsoft Visual C++ doesn't ship with this header file yet. We +// need to define them. +// Thanks to Steven G. Johnson (MIT) for the following code. -// TetGen only uses the C standard library. +// Define the _MSC_VER symbol if you are using Microsoft Visual C++. -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <time.h> -#include <assert.h> +// #define _MSC_VER -// The types 'intptr_t' and 'uintptr_t' are signed and unsigned integer types, -// respectively. They are guaranteed to be the same width as a pointer. -// They are defined in <stdint.h> by the C99 Standard. However, Microsoft -// Visual C++ 2003 -- 2008 (Visual C++ 7.1 - 9) doesn't ship with this header -// file. In such case, we can define them by ourself. -// Update (learned from Stack Overflow): Visual Studio 2010 and Visual C++ 2010 -// Express both have stdint.h +// Define the _WIN64 symbol if you are running TetGen on Win64. -// The following piece of code was provided by Steven Johnson (MIT). Define the -// symbol _MSC_VER if you are using Microsoft Visual C++. Moreover, define -// the _WIN64 symbol if you are running TetGen on Win64 systems. +// #define _WIN64 #ifdef _MSC_VER // Microsoft Visual C++ # ifdef _WIN64 @@ -82,31 +81,23 @@ # include <stdint.h> #endif +// Maximum number of characters in a file name (including the null). + +#define FILENAMESIZE 1024 + +// Maximum number of chars in a line read from a file (including the null). + +#define INPUTLINESIZE 2048 + /////////////////////////////////////////////////////////////////////////////// // // // tetgenio // // // -// A structure for transfering data into and out of TetGen's mesh structure, // -// 'tetgenmesh' (declared below). // -// // -// The input of TetGen is either a 3D point set, or a 3D piecewise linear // -// complex (PLC), or a tetrahedral mesh. Depending on the input object and // -// the specified options, the output of TetGen is either a Delaunay (or wei- // -// ghted Delaunay) tetrahedralization, or a constrained (Delaunay) tetrahed- // -// ralization, or a quality tetrahedral mesh. // -// // -// A piecewise linear complex (PLC) represents a 3D polyhedral domain with // -// possibly internal boundaries(subdomains). It is introduced in [Miller et // -// al, 1996]. Basically it is a set of "cells", i.e., vertices, edges, poly- // -// gons, and polyhedra, and the inetrsection of any two of its cells is the // -// union of other cells of it. // -// // -// TetGen uses a set of files to describe the inputs and outputs. Each file // -// is identified from its file extension (.node, .ele, .face, .edge, etc). // +// A structure for transfering data into and out of TetGen. // // // -// The 'tetgenio' structure is a collection of arrays of data, i.e., points, // -// facets, tetrahedra, and so forth. It contains functions to read and write // -// (input and output) files of TetGen as well as other supported mesh files. // +// It holds a collection of arrays of data, i.e., points, facets, tetrahedra,// +// and so forth. It contains functions to read and write (input and output) // +// files of TetGen as well as other supported mesh files. // // // // Once an object of tetgenio is declared, no array is created. One has to // // allocate enough memory for them. On deletion of this object, the memory // @@ -123,18 +114,18 @@ class tetgenio { public: // A "polygon" describes a simple polygon (no holes). It is not necessarily - // convex. Each polygon contains a number of corners (points) and the same - // number of sides (edges). The points of the polygon must be given in - // either counterclockwise or clockwise order and they form a ring, so - // every two consective points forms an edge of the polygon. + // convex. Each polygon contains number of corners (points) and the same + // number of sides (edges). + // Note that the points of the polygon must be given in either counter- + // clockwise or clockwise order and they form a ring, so every two + // consective points forms an edge of the polygon. typedef struct { int *vertexlist; int numberofvertices; } polygon; - // A "facet" describes a polygonal region possibly with holes, edges, and - // points floating in it. Each facet consists of a list of polygons and - // a list of hole points (which lie strictly inside holes). + // A "facet" describes a facet. Each facet is a polygonal region possibly + // with holes, edges, and points in it. typedef struct { polygon *polygonlist; int numberofpolygons; @@ -166,6 +157,19 @@ public: int *elist; } vorofacet; + // The periodic boundary condition group data structure. A "pbcgroup" + // contains the definition of a pbc and the list of pbc point pairs. + // 'fmark1' and 'fmark2' are the facetmarkers of the two pbc facets f1 + // and f2, respectively. 'transmat' is the transformation matrix which + // maps a point in f1 into f2. An array of pbc point pairs are saved + // in 'pointpairlist'. The first point pair is at indices [0] and [1], + // followed by remaining pairs. Two integers per pair. + typedef struct { + int fmark1, fmark2; + REAL transmat[4][4]; + int numberofpointpairs; + int *pointpairlist; + } pbcgroup; // Additional parameters associated with an input (or mesh) vertex. // These informations are provided by CAD libraries. @@ -175,6 +179,9 @@ public: int type; // 0, 1, or 2. } pointparam; + // A callback function for mesh refinement. + typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL); + // Callback functions for meshing PSCs. typedef REAL (* GetVertexParamOnEdge)(void*, int, int); typedef void (* GetSteinerOnEdge)(void*, int, REAL, REAL*); @@ -182,9 +189,6 @@ public: typedef void (* GetEdgeSteinerParamOnFace)(void*, int, REAL, int, REAL*); typedef void (* GetSteinerOnFace)(void*, int, REAL*, REAL*); - // A callback function for mesh refinement. - typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL); - // Items are numbered starting from 'firstnumber' (0 or 1), default is 0. int firstnumber; @@ -202,108 +206,95 @@ public: // attributes occupy 'numberofpointattributes' REALs. // 'pointmtrlist': An array of metric tensors at points. Each point's // tensor occupies 'numberofpointmtr' REALs. - // 'pointmarkerlist': An array of point markers; one integer per point. + // `pointmarkerlist': An array of point markers; one integer per point. REAL *pointlist; REAL *pointattributelist; REAL *pointmtrlist; - int *pointmarkerlist; + int *pointmarkerlist; pointparam *pointparamlist; int numberofpoints; int numberofpointattributes; int numberofpointmtrs; - // 'tetrahedronlist': An array of tetrahedron corners. The first - // tetrahedron's first corner is at index [0], followed by its other - // corners, followed by six nodes on the edges of the tetrahedron if the - // second order option (-o2) is applied. Each tetrahedron occupies - // 'numberofcorners' ints. The second order nodes are ouput only. - // 'tetrahedronattributelist': An array of tetrahedron attributes. Each - // tetrahedron's attributes occupy 'numberoftetrahedronattributes' REALs. - // 'tetrahedronvolumelist': An array of constraints, i.e. tetrahedron's - // volume; one REAL per element. Input only. - // 'neighborlist': An array of tetrahedron neighbors; 4 ints per element. - // Output only. - int *tetrahedronlist; + // `elementlist': An array of element (triangle or tetrahedron) corners. + // The first element's first corner is at index [0], followed by its + // other corners in counterclockwise order, followed by any other + // nodes if the element represents a nonlinear element. Each element + // occupies `numberofcorners' ints. + // `elementattributelist': An array of element attributes. Each + // element's attributes occupy `numberofelementattributes' REALs. + // `elementconstraintlist': An array of constraints, i.e. triangle's + // area or tetrahedron's volume; one REAL per element. Input only. + // `neighborlist': An array of element neighbors; 3 or 4 ints per + // element. Output only. + int *tetrahedronlist; REAL *tetrahedronattributelist; REAL *tetrahedronvolumelist; - int *neighborlist; + int *neighborlist; int numberoftetrahedra; int numberofcorners; int numberoftetrahedronattributes; - // 'facetlist': An array of facets. Each entry is a structure of facet. - // 'facetmarkerlist': An array of facet markers; one int per facet. + // `facetlist': An array of facets. Each entry is a structure of facet. + // `facetmarkerlist': An array of facet markers; one int per facet. facet *facetlist; int *facetmarkerlist; int numberoffacets; - // 'holelist': An array of holes (in volume). Each hole is given by a - // seed (point) which lies strictly inside it. The first seed's x, y and z - // coordinates are at indices [0], [1] and [2], followed by the - // remaining seeds. Three REALs per hole. + // `holelist': An array of holes. The first hole's x, y and z + // coordinates are at indices [0], [1] and [2], followed by the + // remaining holes. Three REALs per hole. REAL *holelist; int numberofholes; - // 'regionlist': An array of regions (subdomains). Each region is given by - // a seed (point) which lies strictly inside it. The first seed's x, y and - // z coordinates are at indices [0], [1] and [2], followed by the regional - // attribute at index [3], followed by the maximum volume at index [4]. - // Five REALs per region. - // Note that each regional attribute is used only if you select the 'A' + // `regionlist': An array of regional attributes and volume constraints. + // The first constraint's x, y and z coordinates are at indices [0], + // [1] and [2], followed by the regional attribute at index [3], foll- + // owed by the maximum volume at index [4]. Five REALs per constraint. + // Note that each regional attribute is used only if you select the `A' // switch, and each volume constraint is used only if you select the - // 'a' switch (with no number following). + // `a' switch (with no number following). REAL *regionlist; int numberofregions; - // 'facetconstraintlist': An array of facet constraints. Each constraint - // specifies a maximum area bound on the subfaces of that facet. The - // first facet constraint is given by a facet marker at index [0] and its - // maximum area bound at index [1], followed by the remaining facet con- - // straints. Two REALs per facet constraint. Note: the facet marker is - // actually an integer. + // `facetconstraintlist': An array of facet maximal area constraints. + // Two REALs per constraint. The first (at index [0]) is the facet + // marker (cast it to int), the second (at index [1]) is its maximum + // area bound. REAL *facetconstraintlist; int numberoffacetconstraints; - // 'segmentconstraintlist': An array of segment constraints. Each constraint - // specifies a maximum length bound on the subsegments of that segment. - // The first constraint is given by the two endpoints of the segment at - // index [0] and [1], and the maximum length bound at index [2], followed - // by the remaining segment constraints. Three REALs per constraint. - // Note the segment endpoints are actually integers. + // `segmentconstraintlist': An array of segment max. length constraints. + // Three REALs per constraint. The first two (at indcies [0] and [1]) + // are the indices of the endpoints of the segment, the third (at index + // [2]) is its maximum length bound. REAL *segmentconstraintlist; int numberofsegmentconstraints; - - // 'trifacelist': An array of face (triangle) corners. The first face's - // three corners are at indices [0], [1] and [2], followed by the remaining - // faces. Three ints per face. - // 'trifacemarkerlist': An array of face markers; one int per face. - // 'o2facelist': An array of second order nodes (on the edges) of the face. - // It is output only if the second order option (-o2) is applied. The - // first face's three second order nodes are at [0], [1], and [2], - // followed by the remaining faces. Three ints per face. - // 'adjtetlist': An array of adjacent tetrahedra to the faces. The first - // face's two adjacent tetrahedra are at indices [0] and [1], followed by - // the remaining faces. A '-1' indicates outside (no adj. tet). This list - // is output when '-nn' switch is used. Output only. + // 'pbcgrouplist': An array of periodic boundary condition groups. + pbcgroup *pbcgrouplist; + int numberofpbcgroups; + + // `trifacelist': An array of triangular face endpoints. The first + // face's endpoints are at indices [0], [1] and [2], followed by the + // remaining faces. Three ints per face. + // `adjtetlist': An array of adjacent tetrahedra to the faces of + // trifacelist. Each face has at most two adjacent tets, the first + // face's adjacent tets are at [0], [1]. Two ints per face. A '-1' + // indicates outside (no adj. tet). This list is output when '-nn' + // switch is used. + // `trifacemarkerlist': An array of face markers; one int per face. int *trifacelist; - int *trifacemarkerlist; - int *o2facelist; int *adjtetlist; + int *trifacemarkerlist; int numberoftrifaces; - // 'edgelist': An array of edge endpoints. The first edge's endpoints - // are at indices [0] and [1], followed by the remaining edges. - // Two ints per edge. - // 'edgemarkerlist': An array of edge markers; one int per edge. - // 'o2edgelist': An array of midpoints of edges. It is output only if the - // second order option (-o2) is applied. One int per edge. - // 'edgeadjtetlist': An array of adjacent tetrahedra to the edges. One - // tetrahedron (an integer) per edge. + // `edgelist': An array of edge endpoints. The first edge's endpoints + // are at indices [0] and [1], followed by the remaining edges. Two + // ints per edge. + // `edgemarkerlist': An array of edge markers; one int per edge. int *edgelist; int *edgemarkerlist; - int *o2edgelist; - int *edgeadjtetlist; int numberofedges; // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). @@ -321,6 +312,9 @@ public: int numberofvfacets; int numberofvcells; + // A callback function. + TetSizeFunc tetunsuitable; + // Variable (and callback functions) for meshing PSCs. void *geomhandle; GetVertexParamOnEdge getvertexparamonedge; @@ -329,9 +323,6 @@ public: GetEdgeSteinerParamOnFace getedgesteinerparamonface; GetSteinerOnFace getsteineronface; - // A callback function. - TetSizeFunc tetunsuitable; - // Input & output routines. bool load_node_call(FILE* infile, int markers, int uvflag, char*); bool load_node(char*); @@ -379,8 +370,8 @@ public: // Initialize routine. void initialize() { - firstnumber = 0; - mesh_dim = 3; + firstnumber = 0; // Default item index is numbered from Zero. + mesh_dim = 3; // Default mesh dimension is 3. useindex = 1; pointlist = (REAL *) NULL; @@ -397,25 +388,22 @@ public: tetrahedronvolumelist = (REAL *) NULL; neighborlist = (int *) NULL; numberoftetrahedra = 0; - numberofcorners = 4; + numberofcorners = 4; // Default is 4 nodes per element. numberoftetrahedronattributes = 0; trifacelist = (int *) NULL; - trifacemarkerlist = (int *) NULL; - o2facelist = (int *) NULL; adjtetlist = (int *) NULL; + trifacemarkerlist = (int *) NULL; numberoftrifaces = 0; - edgelist = (int *) NULL; - edgemarkerlist = (int *) NULL; - o2edgelist = (int *) NULL; - edgeadjtetlist = (int *) NULL; - numberofedges = 0; - facetlist = (facet *) NULL; facetmarkerlist = (int *) NULL; numberoffacets = 0; + edgelist = (int *) NULL; + edgemarkerlist = (int *) NULL; + numberofedges = 0; + holelist = (REAL *) NULL; numberofholes = 0; @@ -427,6 +415,8 @@ public: segmentconstraintlist = (REAL *) NULL; numberofsegmentconstraints = 0; + pbcgrouplist = (pbcgroup *) NULL; + numberofpbcgroups = 0; vpointlist = (REAL *) NULL; vedgelist = (voroedge *) NULL; @@ -447,12 +437,17 @@ public: getsteineronface = NULL; } - // Free the memory allocated in 'tetgenio'. Note that it assumes that the - // memory was alocated by the "new" operator (C++). + // Free the memory allocated in 'tetgenio'. void deinitialize() { + facet *f; + polygon *p; + pbcgroup *pg; int i, j; + // Notince that this routine assumes that the memory was allocated by + // C++ memory allocation operator 'new'. + if (pointlist != (REAL *) NULL) { delete [] pointlist; } @@ -485,15 +480,12 @@ public: if (trifacelist != (int *) NULL) { delete [] trifacelist; } - if (trifacemarkerlist != (int *) NULL) { - delete [] trifacemarkerlist; - } - if (o2facelist != (int *) NULL) { - delete [] o2facelist; - } if (adjtetlist != (int *) NULL) { delete [] adjtetlist; } + if (trifacemarkerlist != (int *) NULL) { + delete [] trifacemarkerlist; + } if (edgelist != (int *) NULL) { delete [] edgelist; @@ -501,16 +493,8 @@ public: if (edgemarkerlist != (int *) NULL) { delete [] edgemarkerlist; } - if (o2edgelist != (int *) NULL) { - delete [] o2edgelist; - } - if (edgeadjtetlist != (int *) NULL) { - delete [] edgeadjtetlist; - } if (facetlist != (facet *) NULL) { - facet *f; - polygon *p; for (i = 0; i < numberoffacets; i++) { f = &facetlist[i]; for (j = 0; j < f->numberofpolygons; j++) { @@ -540,6 +524,15 @@ public: if (segmentconstraintlist != (REAL *) NULL) { delete [] segmentconstraintlist; } + if (pbcgrouplist != (pbcgroup *) NULL) { + for (i = 0; i < numberofpbcgroups; i++) { + pg = &(pbcgrouplist[i]); + if (pg->pointpairlist != (int *) NULL) { + delete [] pg->pointpairlist; + } + } + delete [] pbcgrouplist; + } if (vpointlist != (REAL *) NULL) { delete [] vpointlist; } @@ -570,16 +563,12 @@ public: // // // tetgenbehavior // // // -// A structure for maintaining the switches and parameters used by TetGen's // -// mesh data structure and algorithms. // +// A structure to maintain the switches and parameters of TetGen. // // // -// All switches and parameters are initialized with default values. They can // -// be set by the command line arguments (a list of strings) of TetGen. // -// // -// NOTE: Some of the switches are incompatible. While some may depend on // -// other switches. The routine parse_commandline() sets the switches from // -// the command line (a list of strings) and checks the consistency of the // -// applied switches. // +// parse_commandline() provides an simple interface to set the vaules of the // +// variables. It accepts the standard parameters (e.g., 'argc' and 'argv') // +// that pass to C/C++ main() function. Alternatively a string which contains // +// the command line options can be used as its parameter. // // // /////////////////////////////////////////////////////////////////////////////// @@ -587,94 +576,93 @@ class tetgenbehavior { public: - // Switches of TetGen. - int plc; // '-p', 0. - int psc; // '-s', 0. - int refine; // '-r', 0. - int quality; // '-q', 0. - int nobisect; // '-Y', 0. - int weighted; // '-w', 0. - int brio_hilbert; // '-b', 1. - int incrflip; // '-l', 0. - int flipinsert; // '-L', 0. - int metric; // '-m', 0. - int varvolume; // '-a', 0. - int fixedvolume; // '-a', 0. - int regionattrib; // '-A', 0. - int conforming; // '-D', 0. - int insertaddpoints; // '-i', 0. - int diagnose; // '-d', 0. - int convex; // '-c', 0. - int nomergefacet; // '-M', 0. - int nomergevertex; // '-M', 0. - int noexact; // '-X', 0. - int nostaticfilter; // '-X', 0. - int zeroindex; // '-z', 0. - int facesout; // '-f', 0. - int edgesout; // '-e', 0. - int neighout; // '-n', 0. - int voroout; // '-v', 0. - int meditview; // '-g', 0. - int vtkview; // '-k', 0. - int nobound; // '-B', 0. - int nonodewritten; // '-N', 0. - int noelewritten; // '-E', 0. - int nofacewritten; // '-F', 0. - int noiterationnum; // '-I', 0. - int nojettison; // '-J', 0. - int reversetetori; // '-R', 0. - int docheck; // '-C', 0. - int quiet; // '-Q', 0. - int verbose; // '-V', 0. - - // Parameters of TetGen. - int vertexperblock; // '-x', 4092. - int tetrahedraperblock; // '-x', 8188. - int shellfaceperblock; // '-x', 2044. - int nobisect_param; // '-Y', 1. - int weighted_param; // '-w', 0. - int fliplinklevel; // -1. - int flipstarsize; // -1. - int fliplinklevelinc; // 1. - int reflevel; // '-D', 3. - int optlevel; // '-O', 2. - int optscheme; // '-O', 7. - int delmaxfliplevel; // 1. - int order; // '-o', 1. - int steinerleft; // '-S', 0. - int no_sort; // 0. - int hilbert_order; // '-b///', 52. - int hilbert_limit; // '-b//' 8. - int brio_threshold; // '-b' 64. - REAL brio_ratio; // '-b/' 0.125. - REAL facet_ang_tol; // '-p', 179.9. - REAL maxvolume; // '-a', -1.0. - REAL minratio; // '-q', 0.0. - REAL mindihedral; // '-q', 5.0. - REAL optmaxdihedral; // 165.0. - REAL optminsmtdihed; // 179.0. - REAL optminslidihed; // 179.0. - REAL epsilon; // '-T', 1.0e-8. - REAL minedgelength; // 0.0. - - // Strings of command line arguments and input/output file names. + // Switches of TetGen. They are briefly described in the function syntax(). + // Plerase consult the user's manual for complete explanations. The last + // column indicates their initial values. + int plc; // '-p' switch, 0. + int psc; // '-s' switch, 0. + int quality; // '-q' switch, 0. + int refine; // '-r' switch, 0. + int metric; // '-m' switch, 0. + int nobisect; // '-Y' switch, 0. + int weighted; // '-w' switch, 0. + int varvolume; // '-a' switch without number, 0. + int fixedvolume; // '-a' switch with number, 0. + int incrflip; // '-l' switch, 0. + int flipinsert; // '-L' switch, 0. + int btree; // '-u' switch, 0. + int hilbertcurve; // '-U' switch, 0. + int insertaddpoints; // '-i' switch, 0. + int regionattrib; // '-A' switch, 0. + int conforming; // '-D' switch, 0. + int diagnose; // '-d' switch, 0. + int convex; // '-c' switch, 0. + int zeroindex; // '-z' switch, 0. + int facesout; // '-f' switch, 0. + int edgesout; // '-e' switch, 0. + int neighout; // '-n' switch, 0. + int voroout; // '-v',switch, 0. + int meditview; // '-g' switch, 0. + int vtkview; // '-K' switch, 0. + int nobound; // '-B' switch, 0. + int nonodewritten; // '-N' switch, 0. + int noelewritten; // '-E' switch, 0. + int nofacewritten; // '-F' switch, 0. + int noiterationnum; // '-I' switch, 0. + int nomerge; // '-M' switch, 0. + int nojettison; // '-J' switch, 0. + int docheck; // '-C' switch, 0. + int quiet; // '-Q' switch, 0. + int verbose; // '-V' switch, 0. + + // Parameters of TetGen. They are numbers specified after switches. The + // last colume indicates their initial values. + int vertexperblock; // after '-b', 4092. + int tetrahedraperblock; // after '-b', 8188. + int shellfaceperblock; // after '-b', 4092. + int nobisect_param; // after '-Y', 1. + int weighted_param; // after '-w', 0. + int flipinsert_random; // after '-L', 0. + int flipinsert_ori4dexact; // after '-L', 0. + int fliplinklevel; // after '-L', -1. + int flipstarsize; // after '-LL', -1. + int fliplinklevelinc; // after '-LLLL', 1. + int max_btreenode_size; // after '-u', 100. + int reflevel; // after '-D', 3. + int optlevel; // after '-O', 7. + int optpasses; // after '-OO', 3. + int optmaxfliplevel; // after '-OOO', 2. + int delmaxfliplevel; // after '-OOOO', 1. + int optmaxflipstarsize; // after '-OOOOO', 10. + int order; // after '-o', 1. + int steinerleft; // after '-S', 0. + REAL facet_ang_tol; // after '-p', 179.9. + REAL maxvolume; // after '-a', -1.0. + REAL minratio; // after '-q', 0.0. + REAL mindihedral; // after '-qq', 5.0. + REAL optmaxdihedral; // after '-o', 165.0. + REAL optminsmtdihed; // after '-oo', 175.0. + REAL optminslidihed; // after '-ooo', 179.0. + REAL epsilon; // after '-T', 1.0e-8. + REAL minedgelength; // The shortest length of an edge, after '-l', 0.0. + + // Variables used to save command line switches and in/out file names. char commandline[1024]; char infilename[1024]; char outfilename[1024]; char addinfilename[1024]; char bgmeshfilename[1024]; - // The input object of TetGen. They are recognized by either the input - // file extensions or by the specified options. - // Currently the following objects are supported: + // The input object type of TetGen. They are recognized by the input file + // extensions. Currently the following objects are supported: // - NODES, a list of nodes (.node); // - POLY, a piecewise linear complex (.poly or .smesh); // - OFF, a polyhedron (.off, Geomview's file format); - // - PLY, a polyhedron (.ply, file format from gatech, only ASCII); + // - PLY, a polyhedron (.ply, file format from gatech); // - STL, a surface mesh (.stl, stereolithography format); // - MEDIT, a surface mesh (.mesh, Medit's file format); // - MESH, a tetrahedral mesh (.ele). - // If no extension is available, the imposed commandline switch + // If no extension is available, the imposed commandline switch // (-p or -r) implies the object. enum objecttype {NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH} object; @@ -693,18 +681,17 @@ public: { plc = 0; psc = 0; - refine = 0; quality = 0; - nobisect = 0; + refine = 0; metric = 0; + nobisect = 0; weighted = 0; - brio_hilbert = 1; - incrflip = 0; - flipinsert = 0; varvolume = 0; fixedvolume = 0; - noexact = 0; - nostaticfilter = 0; + incrflip = 0; + flipinsert = 0; + btree = 0; + hilbertcurve = 0; insertaddpoints = 0; regionattrib = 0; conforming = 0; @@ -722,10 +709,8 @@ public: noelewritten = 0; nofacewritten = 0; noiterationnum = 0; - nomergefacet = 0; - nomergevertex = 0; + nomerge = 0; nojettison = 0; - reversetetori = 0; docheck = 0; quiet = 0; verbose = 0; @@ -735,24 +720,24 @@ public: shellfaceperblock = 4092; nobisect_param = 1; weighted_param = 0; + flipinsert_random = 0; + flipinsert_ori4dexact = 0; fliplinklevel = -1; // No limit on linklevel. flipstarsize = -1; // No limit on flip star size. fliplinklevelinc = 1; + max_btreenode_size = 100; // Default use b-tree sorting. reflevel = 3; - optscheme = 7; // 1 & 2 & 4, // min_max_dihedral. - optlevel = 2; + optlevel = 7; // 1 & 2 & 4, // min_max_dihedral. + optpasses = 3; + optmaxfliplevel = 2; delmaxfliplevel = 1; + optmaxflipstarsize = 10; order = 1; steinerleft = -1; - no_sort = 0; - hilbert_order = 52; //-1; - hilbert_limit = 8; - brio_threshold = 64; - brio_ratio = 0.125; facet_ang_tol = 179.9; maxvolume = -1.0; minratio = 2.0; - mindihedral = 5.0; + mindihedral = 0.0; // 5.0; optmaxdihedral = 165.00; // without -q, default is 179.0 optminsmtdihed = 179.00; // without -q, default is 179.999 optminslidihed = 179.00; // without -q, default is 179.999 @@ -772,42 +757,45 @@ public: /////////////////////////////////////////////////////////////////////////////// // // -// Robust Geometric predicates // -// // -// Geometric predicates are simple tests of spatial relations of a set of d- // -// dimensional points, such as the orientation test and the point-in-sphere // -// test. Each of these tests is performed by evaluating the sign of a deter- // -// minant of a matrix whose entries are the coordinates of these points. If // -// the computation is performed by using the floating-point numbers, e.g., // -// the single or double precision numbers in C/C++, roundoff error may cause // -// an incorrect result. This may either lead to a wrong result or eventually // -// lead to a failure of the program. Computing the predicates exactly will // -// avoid the error and make the program robust. // -// // -// The following routines are the robust geometric predicates for 3D orient- // -// ation test and point-in-sphere test. They were implemented by Shewchuk. // -// The source code are generously provided by him in the public domain, // -// http://www.cs.cmu.edu/~quake/robust.html. predicates.cxx is a C++ version // -// of the original C code. // -// // -// The original predicates of Shewchuk only use "dynamic filters", i.e., it // -// computes the error at run time step by step. TetGen first adds a "static // -// filter" in each predicate. It estimates the maximal possible error in all // -// cases. So it can safely and quickly answer many easy cases. // +// Geometric predicates // +// // +// Return one of the values +1, 0, and -1 on basic geometric questions such // +// as the orientation of point sets, in-circle, and in-sphere tests. They // +// are basic units for implmenting geometric algorithms. TetGen uses two 3D // +// geometric predicates: the orientation and in-sphere tests. // +// // +// Orientation test: let a, b, c be a sequence of 3 non-collinear points in // +// R^3. They defines a unique hypeplane H. Let H+ and H- be the two spaces // +// separated by H, which are defined as follows (using the left-hand rule): // +// make a fist using your left hand in such a way that your fingers follow // +// the order of a, b and c, then your thumb is pointing to H+. Given any // +// point d in R^3, the orientation test returns +1 if d lies in H+, -1 if d // +// lies in H-, or 0 if d lies on H. // +// // +// In-sphere test: let a, b, c, d be 4 non-coplanar points in R^3. They // +// defines a unique circumsphere S. Given any point e in R^3, the in-sphere // +// test returns +1 if e lies inside S, or -1 if e lies outside S, or 0 if e // +// lies on S. // +// // +// The following routines use arbitrary precision floating-point arithmetic. // +// They are provided by J. R. Schewchuk in public domain (http://www.cs.cmu. // +// edu/~quake/robust.html). The source code are in "predicates.cxx". // // // /////////////////////////////////////////////////////////////////////////////// -void exactinit(int, int, int, REAL, REAL, REAL); +REAL exactinit(); REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd); REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe); REAL orient4d(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, REAL ah, REAL bh, REAL ch, REAL dh, REAL eh); +REAL orient4dexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, + REAL ah, REAL bh, REAL ch, REAL dh, REAL eh); /////////////////////////////////////////////////////////////////////////////// // // // tetgenmesh // // // -// A structure for creating and updating tetrahedral meshes. // +// The object to generate tetrahedral meshes. // // // /////////////////////////////////////////////////////////////////////////////// @@ -815,45 +803,64 @@ class tetgenmesh { public: + // Labels that signify the type of a vertex. + enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, ACUTEVERTEX, + FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX, + FREEVOLVERTEX, HIDDENVERTEX, DEADVERTEX}; + + // Labels that signify the type of a subsegment. + enum shestype {NSHARP, SHARP, FAKESH}; + + // Labels that signify the result of triangle-triangle intersection test. + enum interresult {DISJOINT, INTERSECT, SHAREVERT, SHAREEDGE, SHAREFACE, + TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE, + COLLISIONFACE, ACROSSSEG, ACROSSSUB}; + + // Labels that signify the result of point location. + enum locateresult {OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, INSTAR, + ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX,BADELEMENT}; + /////////////////////////////////////////////////////////////////////////////// // // // Mesh data structure // // // -// A tetrahedral mesh T of a 3D piecewise linear complex (PLC) X is a 3D // -// simplicial complex whose underlying space is equal to the space of X. T // -// contains a 2D subcomplex S which is a triangular mesh of the boundary of // -// X. S contains a 1D subcomplex L which is a linear mesh of the boundary of // -// S. Faces and edges in S and L are respectively called subfaces and segme- // -// nts to distinguish them from others in T. // -// // -// TetGen stores the tetrahedra and vertices of T. The baisc structure of a // -// tetrahedron contains pointers to its vertices and adjacent tetrahedra. A // -// vertex stores its x-, y-, and z-coordinates, and a pointer to a tetrahed- // -// ron containing it. Both tetrahedra and vertices may contain user data. // -// // -// Each face of T belongs to either two tetrahedra or one tetrahedron. In // -// the latter case, the face is an exterior boundary face of T. TetGen adds // -// fictitious tetrahedra (one-to-one) at such faces, and connects them to an // -// "infinite vertex" (which has no geometric coordinates). One can imagine // -// such a vertex lies in 4D space and is visible by all exterior boundary // -// faces. The extended set of tetrahedra (including the infinite vertex) is // -// a tetrahedralization of a 3-pseudomanifold without bounday. It has the // -// property that every face is shared by exactly two tetrahedra. // -// // -// The current version of TetGen stores explicitly the subfaces and segments // -// (which are in surface mesh S and the linear mesh L), respectively. Extra // -// pointers are allocated in tetrahedra and subfaces to point each others. // +// A tetrahedral mesh of a 3D domain is a 3D simplicial complex T whose und- // +// erlying space is homeomorphic to the domain. T contains a 2D subcomplex S // +// which is a triangular mesh of the boundary of the domain. S contains a 1D // +// subcomplex L which is a linear mesh of the boundary of the surface. Faces // +// and edges in S and L are respectivly called subfaces and segments to dis- // +// tinguish them from others in T. // +// // +// The data structure to represent a tetrahedral mesh stores the tetrahedra // +// and vertices of T. Each tetrahedron is a structure including informations // +// of its vertices and adjacencies. Each vertex carries its geometric coord- // +// inates. The faces and edges of T are implicitly represented by tetrahedra.// +// This representation has a clear separation between combinatoric and geom- // +// etric data of a tetrahedral mesh. // +// // +// A hull face of T is the face on the exterior domain boundary, i.e., it is // +// contained by only one tetrahedron in T. TetGen adds fictitious tetrahedra // +// (one-to-one) at the hull faces of T, and connects them to an "infinite // +// vertex" which has no geometric coordinates. One can imagine such a vertex // +// lies in 4D space and is visible by all tetrahedra containing hull faces. // +// The extended set of tetrahedra with the infinite vertex is a tetrahedral- // +// ization of a compact 3-manifold without bounday. It has the property that // +// every face is shared by exactly two tetrahedra. // +// // +// The data structure stores explicitly the subfaces and segments (which are // +// in surface mesh S and the linear mesh L, respectively. Additional inform- // +// ations are stored in tetrahedra and subfaces to remember their relations. // // // /////////////////////////////////////////////////////////////////////////////// // The tetrahedron data structure. It includes the following fields: // - a list of four adjoining tetrahedra; // - a list of four vertices; - // - a pointer to a list of four subfaces (optional, for -p switch); - // - a pointer to a list of six segments (optional, for -p switch); + // - a list of four subfaces (optional, for -p switch); + // - a list of six segments (optional, for -p switch); // - a list of user-defined floating-point attributes (optional); // - a volume constraint (optional, for -a switch); - // - an integer of element marker (and flags); + // - an integer of element marker; // The structure of a tetrahedron is an array of pointers. Its actual size // (the length of the array) is determined at runtime. @@ -866,7 +873,8 @@ public: // - two adjoining tetrahedra; // - an area constraint (optional, for -q switch); // - an integer for boundary marker; - // - an integer for type, flags, etc. + // - an integer for type: SHARPSEGMENT, NONSHARPSEGMENT, ...; + // - an integer for pbc group (optional, if in->pbcgrouplist exists); typedef REAL **shellface; @@ -876,11 +884,11 @@ public: // - u, v coordinates (optional, for -s switch); // - a metric tensor (optional, for -q or -m switch); // - a pointer to an adjacent tetrahedron; - // - a pointer to a parent (or a duplicate) point; + // - a pointer to a parent (or a duplicate) point, or a bsp_tree node; // - a pointer to an adjacent subface or segment (optional, -p switch); // - a pointer to a tet in background mesh (optional, for -m switch); // - an integer for boundary marker (point index); - // - an integer for point type (and flags). + // - an integer for point type. // - an integer for geometry tag (optional, for -s switch). // The structure of a point is an array of REALs. Its acutal size is // determined at the runtime. @@ -889,21 +897,23 @@ public: /////////////////////////////////////////////////////////////////////////////// // // -// Handles // +// Ordered tetrahedra // // // -// Navigation and manipulation in a tetrahedralization are accomplished by // -// opertating on structures referred as ``handles". A handle is a pair (t,v),// -// where t is a pointer to a tetrahedron, and v is a 4-bit integer, in the // -// range from 0 to 11. v is called the ``version'' of a tetrahedron, it rep- // -// resents a directed edge of a specific face of the tetrahedron. // +// The four vertices of a tetrahedron can be permuted in 24 different seque- // +// nces. We call each sequence resulted by an even permutation an "ordered // +// tetrahedron". There are total 12 ordered tetrahedra. They form a group // +// which is isomorphic to the alternating group of 4 elements. Geometrically,// +// if we direct the three edges within a face of a tetrahedron by the count- // +// erclockwise order viewed from the opposite vertex of this face (using ei- // +// ther right-hand or left-hand rule). There are total twelve directed edges // +// in the tetrahedron. Each of them corresponds to an ordered tetrahedron. // // // -// There are 12 even permutations of the four vertices, each of them corres- // -// ponds to a directed edge (a version) of the tetrahedron. The 12 versions // -// can be groupped into 4 distinct ``edge rings'' in 4 ``oriented faces'' of // -// this tetrahedron. One can encode each version (a directed edge) into a // -// 4-bit integer such that the two upper bits encode the index (from 0 to 2) // -// of this edge in the edge ring, and the two lower bits encode the index ( // -// from 0 to 3) of the oriented face which contains this edge. // +// We represent an order tetrahedron by a pair (t, v), where t is a pointer // +// to the tetrahedron and v is a four-bit integer, in the range from 0 to 11,// +// identifying the ordered version of the tetrahedron. Assume the faces of // +// the tetrahedron is numbered from 0 to 3, and the edges in a face is numb- // +// ered from 0 to 2. The first two bits of v is used to identify the face of // +// the tetrahedron. The other two bits of v identify the edge in the face. // // // // The four vertices of a tetrahedron are indexed from 0 to 3 (accodring to // // their storage in the data structure). Give each face the same index as // @@ -917,10 +927,19 @@ public: // face 2 | 2 (2/3) 6 (2/1) 10 (2/0) // // face 3 | 3 (3/0) 7 (3/1) 11 (3/2) // // // -// Similarily, navigation and manipulation in a (boundary) triangulation are // -// done by using handles of triangles. Each handle is a pair (s, v), where s // -// is a pointer to a triangle, and v is a version in the range from 0 to 5. // -// Each version corresponds to a directed edge of this triangle. // +// Ordered triangles // +// // +// The three vertices of a triangle can be permuted in 6 different sequences // +// which form a group isomorphic to the symmetric group of 3 elements. Each // +// permutation of the vertices is called an ordered triangle. The first two // +// vertices of an ordered triangle defines an directed edge. There are total // +// six directed edge in the triangle. They can be divided into two groups, // +// which correspond the two orientations of the triangle, respectively. // +// // +// We represent an ordered triangle by a pair (s, v), where s is a pointer // +// to the triangle and v is a three-bit integer, in the range from 0 to 5, // +// identifying the directed edge of the triangle. Using the first bit of v // +// to identify the orientation, the other two bits of v identify the edge. // // // // Number the three vertices of a triangle from 0 to 2 (according to their // // storage in the data structure). Give each edge the same index as the node // @@ -931,9 +950,6 @@ public: // ccw orieation | 0 2 4 // // cw orieation | 1 3 5 // // // -// In the following, a 'triface' is a handle of tetrahedron, and a 'face' is // -// a handle of a triangle. // -// // /////////////////////////////////////////////////////////////////////////////// class triface { @@ -958,110 +974,12 @@ public: } }; -/////////////////////////////////////////////////////////////////////////////// -// // -// Arraypool // -// // -// A dynamic linear array. (It is written by J. Shewchuk) // -// // -// Each arraypool contains an array of pointers to a number of blocks. Each // -// block contains the same fixed number of objects. Each index of the array // -// addesses a particular object in the pool. The most significant bits add- // -// ress the index of the block containing the object. The less significant // -// bits address this object within the block. // -// // -// 'objectbytes' is the size of one object in blocks; 'log2objectsperblock' // -// is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number // -// of allocated objects; 'totalmemory' is the totoal memorypool in bytes. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class arraypool { - - public: - - int objectbytes; - int objectsperblock; - int log2objectsperblock; - int objectsperblockmark; - int toparraylen; - char **toparray; - long objects; - unsigned long totalmemory; - - void restart(); - void poolinit(int sizeofobject, int log2objperblk); - char* getblock(int objectindex); - void* lookup(int objectindex); - int newindex(void **newptr); - - arraypool(int sizeofobject, int log2objperblk); - ~arraypool(); - }; - -// fastlookup() -- A fast, unsafe operation. Return the pointer to the object -// with a given index. Note: The object's block must have been allocated, -// i.e., by the function newindex(). - -#define fastlookup(pool, index) \ - (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ - ((index) & (pool)->objectsperblockmark) * (pool)->objectbytes) - -/////////////////////////////////////////////////////////////////////////////// -// // -// Memorypool // -// // -// A structure for memory allocation. (It is written by J. Shewchuk) // -// // -// firstblock is the first block of items. nowblock is the block from which // -// items are currently being allocated. nextitem points to the next slab // -// of free memory for an item. deaditemstack is the head of a linked list // -// (stack) of deallocated items that can be recycled. unallocateditems is // -// the number of items that remain to be allocated from nowblock. // -// // -// Traversal is the process of walking through the entire list of items, and // -// is separate from allocation. Note that a traversal will visit items on // -// the "deaditemstack" stack as well as live items. pathblock points to // -// the block currently being traversed. pathitem points to the next item // -// to be traversed. pathitemsleft is the number of items that remain to // -// be traversed in pathblock. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class memorypool { - - public: - - void **firstblock, **nowblock; - void *nextitem; - void *deaditemstack; - void **pathblock; - void *pathitem; - int alignbytes; - int itembytes, itemwords; - int itemsperblock; - long items, maxitems; - int unallocateditems; - int pathitemsleft; - - memorypool(); - memorypool(int, int, int, int); - ~memorypool(); - - void poolinit(int, int, int, int); - void restart(); - void *alloc(); - void dealloc(void*); - void traversalinit(); - void *traverse(); - }; - /////////////////////////////////////////////////////////////////////////////// // // // badface // // // -// Despite of its name, a 'badface' can be used to represent one of the // -// following objects: // +// A multiple usages structure. Despite of its name, a 'badface' can be used // +// to represent the following objects: // // - a face of a tetrahedron which is (possibly) non-Delaunay; // // - an encroached subsegment or subface; // // - a bad-quality tetrahedron, i.e, has too large radius-edge ratio; // @@ -1073,12 +991,12 @@ public: class badface { public: triface tt; - face ss; + face ss; REAL key, cent[6]; // circumcenter or cos(dihedral angles) at 6 edges. point forg, fdest, fapex, foppo, noppo; - badface *nextitem; + badface *previtem, *nextitem; badface() : key(0), forg(0), fdest(0), fapex(0), foppo(0), noppo(0), - nextitem(0) {} + previtem(0), nextitem(0) {} }; /////////////////////////////////////////////////////////////////////////////// @@ -1093,12 +1011,11 @@ public: public: - int iloc; // input/output. - int bowywat, lawson; + int iloc, bowywat, lawson; + int rejflag, chkencflag; + int sloc, sbowywat; int splitbdflag, validflag, respectbdflag; - int rejflag, chkencflag, cdtflag; int assignmeshsize; - int sloc, sbowywat; // Used by Delaunay refinement. int refineflag; // 0, 1, 2, 3 @@ -1106,11 +1023,12 @@ public: face refinesh; insertvertexflags() { + // All flags are initialized as 0. iloc = bowywat = lawson = 0; + rejflag = chkencflag = 0; + sloc = sbowywat = 0; splitbdflag = validflag = respectbdflag = 0; - rejflag = chkencflag = cdtflag = 0; assignmeshsize = 0; - sloc = sbowywat = 0; refineflag = 0; refinetet.tet = NULL; @@ -1131,9 +1049,11 @@ public: public: - // Elementary flip flags. - int enqflag; // (= flipflag) - int chkencflag; + point seg[2]; // A constraining edge to be recovered. + point fac[3]; // A constraining face to be recovered. + + point remvert; // A vertex to be removed. + //point remedge[2]; // A non-Delaunay edge to be removed. // Control flags int unflip; // Undo the performed flips. @@ -1142,22 +1062,37 @@ public: // Optimization flags. int remove_ndelaunay_edge; // Remove a non-Delaunay edge. + // remedge[0] and remedge[1] store the endpoints of a + // non-Delaunay edge to be removed. REAL bak_tetprism_vol; // The value to be minimized. - REAL tetprism_vol_sum; + int remove_large_angle; // Remove a large dihedral angle at edge. REAL cosdihed_in; // The input cosine of the dihedral angle (> 0). + // Only perform a flip if new angles are less than it. REAL cosdihed_out; // The improved cosine of the dihedral angle. - // Boundary recovery flags. - int checkflipeligibility; - point seg[2]; // A constraining edge to be recovered. - point fac[3]; // A constraining face to be recovered. - point remvert; // A vertex to be removed. - + // Counters. + int maxflippedlinklevelcount; // Maximal flipped link levels. + int misfliplinklevelcount; // Number of missed flip possibilities. + int chrismastreecount; // Number of Chrismas trees (unflippable case). + int convexhulledgecount; // Number of convex hull edges (unflippable case). + int encsegcount; // Number of hitted segments. + int rejf23count, rejf32count; // Number of rejections by checkflipeligi.. + + void clearcounters() { + maxflippedlinklevelcount = 0; + misfliplinklevelcount = 0; + chrismastreecount = 0; + convexhulledgecount = 0; + encsegcount = 0; + rejf23count = rejf32count = 0; + } flipconstraints() { - enqflag = 0; - chkencflag = 0; + seg[0] = NULL; + fac[0] = NULL; + remvert = NULL; + //remedge[0] = NULL; unflip = 0; collectnewtets = 0; @@ -1165,15 +1100,12 @@ public: remove_ndelaunay_edge = 0; bak_tetprism_vol = 0.0; - tetprism_vol_sum = 0.0; + remove_large_angle = 0; cosdihed_in = 0.0; cosdihed_out = 0.0; - checkflipeligibility = 0; - seg[0] = NULL; - fac[0] = NULL; - remvert = NULL; + clearcounters(); } }; @@ -1202,6 +1134,11 @@ public: int maxiter; // Maximum smoothing iterations (disabled by -1). int smthiter; // Performed iterations. + int expstarflag; + int expstarcount; + + int flipflag; + int checkencflag; optparameters() { max_min_volume = 0; @@ -1215,43 +1152,138 @@ public: maxiter = -1; // Unlimited smoothing iterations. smthiter = 0; + expstarflag = 0; + expstarcount = 0; + + flipflag = 0; + checkencflag = 0; } }; +/////////////////////////////////////////////////////////////////////////////// +// // +// Arraypool // +// // +// A dynamic linear array. // +// (It is simply copied from Shewchuk's Starbase.c, which is provided as // +// part of Stellar, a program for improving tetrahedral meshes.) // +// // +// Each arraypool contains an array of pointers to a number of blocks. Each // +// block contains the same fixed number of objects. Each index of the array // +// addesses a particular object in the pool. The most significant bits add- // +// ress the index of the block containing the object. The less significant // +// bits address this object within the block. // +// // +// 'objectbytes' is the size of one object in blocks; 'log2objectsperblock' // +// is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number // +// of allocated objects; 'totalmemory' is the totoal memorypool in bytes. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class arraypool { + + public: + + int objectbytes; + int objectsperblock; + int log2objectsperblock; + int toparraylen; + char **toparray; + long objects; + unsigned long totalmemory; + + void restart(); + void poolinit(int sizeofobject, int log2objperblk); + char* getblock(int objectindex); + void* lookup(int objectindex); + int newindex(void **newptr); + + arraypool(int sizeofobject, int log2objperblk); + ~arraypool(); + }; + +// fastlookup() -- A fast, unsafe operation. Return the pointer to the object +// with a given index. Note: The object's block must have been allocated, +// i.e., by the function newindex(). + +#define fastlookup(pool, index) \ + (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ + ((index) & ((pool)->objectsperblock - 1)) * (pool)->objectbytes) /////////////////////////////////////////////////////////////////////////////// // // -// Labels (enumeration declarations) used by TetGen. // +// Memorypool // +// // +// A type used to allocate memory. // +// (It is simply copied from Shewchuk's triangle.c.) // +// // +// firstblock is the first block of items. nowblock is the block from which // +// items are currently being allocated. nextitem points to the next slab // +// of free memory for an item. deaditemstack is the head of a linked list // +// (stack) of deallocated items that can be recycled. unallocateditems is // +// the number of items that remain to be allocated from nowblock. // +// // +// Traversal is the process of walking through the entire list of items, and // +// is separate from allocation. Note that a traversal will visit items on // +// the "deaditemstack" stack as well as live items. pathblock points to // +// the block currently being traversed. pathitem points to the next item // +// to be traversed. pathitemsleft is the number of items that remain to // +// be traversed in pathblock. // +// // +// itemwordtype is set to POINTER or FLOATINGPOINT, and is used to suggest // +// what sort of word the record is primarily made up of. alignbytes // +// determines how new records should be aligned in memory. itembytes and // +// itemwords are the length of a record in bytes (after rounding up) and // +// words. itemsperblock is the number of items allocated at once in a // +// single block. items is the number of currently allocated items. // +// maxitems is the maximum number of items that have been allocated at // +// once; it is the current number of items plus the number of records kept // +// on deaditemstack. // // // /////////////////////////////////////////////////////////////////////////////// - // Labels that signify the type of a vertex. - enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, ACUTEVERTEX, - FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX, - FREEVOLVERTEX, NREGULARVERTEX, DEADVERTEX}; - - // Labels that signify the type of a subsegment. - enum shestype {NSHARP, SHARP}; + class memorypool { - // Labels that signify the result of triangle-triangle intersection test. - enum interresult {DISJOINT, INTERSECT, SHAREVERT, SHAREEDGE, SHAREFACE, - TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE, - COLLISIONFACE, ACROSSSEG, ACROSSSUB}; + public: - // Labels that signify the result of point location. - enum locateresult {UNKNOWN, OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, - ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX, NONREGULAR, - INSTAR, BADELEMENT}; + // Labels that signify whether a record consists primarily of pointers + // or of floating-point words. Used for data alignment. + enum wordtype {POINTER, FLOATINGPOINT}; + + void **firstblock, **nowblock; + void *nextitem; + void *deaditemstack; + void **pathblock; + void *pathitem; + wordtype itemwordtype; + int alignbytes; + int itembytes, itemwords; + int itemsperblock; + long items, maxitems; + int unallocateditems; + int pathitemsleft; + + memorypool(); + memorypool(int, int, enum wordtype, int); + ~memorypool(); + + void poolinit(int, int, enum wordtype, int); + void restart(); + void *alloc(); + void dealloc(void*); + void traversalinit(); + void *traverse(); + }; /////////////////////////////////////////////////////////////////////////////// // // -// Variables of TetGen // +// Class variables // // // /////////////////////////////////////////////////////////////////////////////// // Pointer to the input data (a set of nodes, a PLC, or a mesh). - tetgenio *in, *addin; + tetgenio *in; // Pointer to the switches and parameters. tetgenbehavior *b; @@ -1259,8 +1291,9 @@ public: // Pointer to a background mesh (contains size specification map). tetgenmesh *bgm; - // Memorypools to store mesh elements (points, tetrahedra, subfaces, and - // segments) and extra pointers between tetrahedra, subfaces, and segments. + // Memorypools to store mesh elements: tetrahedra, subfaces, segments, + // and vertices. And memorypools for storing pointers which connect + // tetrahedra and subfaces and segments. memorypool *tetrahedrons, *subfaces, *subsegs, *points; memorypool *tet2subpool, *tet2segpool; @@ -1269,8 +1302,17 @@ public: // A memorypool to store faces to be flipped. memorypool *flippool; - arraypool *unflipqueue; - badface *flipstack; + // A stack of faces to be flipped. + badface *flipstack; + // Two queues for handling unflippable edges. + arraypool *unflipqueue; //, *flipqueue; + + // Entry to find the binary tree nodes (-u option). + arraypool *btreenode_list; + // The maximum size of a btree node (number after -u option) is + int max_btreenode_size; // <= b->max_btreenode_size. + // The maximum btree depth (for bookkeeping). + int max_btree_depth; // Arrays used for point insertion (the Bowyer-Watson algorithm). arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; @@ -1282,31 +1324,46 @@ public: arraypool *subsegstack, *subfacstack, *subvertstack; arraypool *suppsteinerptlist; - // Arrays of encroached segments and subfaces (for mesh refinement). - arraypool *encseglist, *encshlist; - // The infinite vertex. point dummypoint; - // The recently visited tetrahedron, subface. + + // Two handles used for facet recovery in CDT. + triface firsttopface, firstbotface; + + // Three points define a plane (used in formcavity()). + point plane_pa, plane_pb, plane_pc; + + // Two arraies of encroached segments and subfaces (in mesh refinement). + arraypool *encseglist, *encshlist; + + // Pointer to a recently visited tetrahedron, subface. triface recenttet; face recentsh; // PI is the ratio of a circle's circumference to its diameter. static REAL PI; - // Array (size = numberoftetrahedra * 6) for storing high-order nodes of - // tetrahedra (only used when -o2 switch is selected). - point *highordertable; + // The increasement of link levels, default is 1. + int autofliplinklevel; + // The volume of tetrahedral-prisms (in 4D). + REAL tetprism_vol_sum; + int calc_tetprism_vol; - // Various variables. - int numpointattrib; // Number of point attributes. - int numelemattrib; // Number of tetrahedron attributes. + // Other variables. + REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. + REAL longest; // The longest possible edge length. + long hullsize; // Number of faces of convex hull. + long insegments; // Number of input segments. + long meshedges; // Number of output mesh edges. + long meshhulledges; // Number of hull mesh edges. + int steinerleft; // Number of Steiner points not yet used. int sizeoftensor; // Number of REALs per metric tensor. int pointmtrindex; // Index to find the metric tensor of a point. int pointparamindex; // Index to find the u,v coordinates of a point. int point2simindex; // Index to find a simplex adjacent to a point. int pointmarkindex; // Index to find boundary marker of a point. + int point2pbcptindex; // Index to find a pbc point to a point. int elemattribindex; // Index to find attributes of a tetrahedron. int volumeboundindex; // Index to find volume bound of a tetrahedron. int elemmarkerindex; // Index to find marker of a tetrahedron. @@ -1315,64 +1372,82 @@ public: int checksubsegflag; // Are there segments in the tetrahedralization yet? int checksubfaceflag; // Are there subfaces in the tetrahedralization yet? int checkinverttetflag; // Are there inverted (degenerated) tets yet? + int checkpbcs; // Are there periodic boundary conditions? int checkconstraints; // Are there variant (node, seg, facet) constraints? int nonconvex; // Is current mesh non-convex? - int autofliplinklevel; // The increasement of link levels, default is 1. + int dupverts; // Are there duplicated vertices? + int unuverts; // Are there unused vertices? long samples; // Number of random samples for point location. unsigned long randomseed; // Current random number seed. REAL cosmaxdihed, cosmindihed; // The cosine values of max/min dihedral. - REAL cossmtdihed; // The cosine value of a bad dihedral to be smoothed. - REAL cosslidihed; // The cosine value of the max dihedral of a sliver. + REAL cossmtdihed; + REAL cosslidihed; // The cosine value of max dihedral of a sliver. REAL minfaceang, minfacetdihed; // The minimum input (dihedral) angles. - REAL tetprism_vol_sum; // The total volume of tetrahedral-prisms (in 4D). - REAL longest; // The longest possible edge length. - REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. + REAL sintheta_tol; // The tolerance for sin(small angle). - // Counters. - long insegments; // Number of input segments. - long hullsize; // Number of exterior boundary faces. - long meshedges; // Number of mesh edges. - long meshhulledges; // Number of boundary mesh edges. - long steinerleft; // Number of Steiner points not yet used. - long dupverts; // Are there duplicated vertices? - long unuverts; // Are there unused vertices? - long nonregularcount; // Are there non-regular vertices? - long st_segref_count, st_facref_count, st_volref_count; // Steiner points. - long fillregioncount, cavitycount, cavityexpcount; + // Algorithm statistical counters. + long ptloc_count, ptloc_max_count; + long orient3dcount, inspherecount, insphere_sos_count; long flip14count, flip26count, flipn2ncount; - long flip23count, flip32count, flip44count, flip41count; - long flip31count, flip22count; - unsigned long totalworkmemory; // Total memory used by working arrays. - - + long flip23count, flip32count, flip44count, flip22count; + long maxbowatcavsize, totalbowatcavsize, totaldeadtets; + long triedgcount, triedgcopcount; + long across_face_count, across_edge_count, across_max_count; + long fillregioncount, missingsubfacecount, crossingtetcount; + long cavitycount, cavityexpcount, maxcavsize, maxregionsize; + long maxcrossfacecount, maxflipsequence; + long dbg_ignore_facecount, dbg_unflip_facecount; + long ccent_relocate_count; + long opt_sliver_peels; + long r1count, r2count, r3count; + long maxfliplinklevel, maxflipstarsize; + long flipstarcount, sucflipstarcount, skpflipstarcount; + long st_segref_count, st_facref_count, st_volref_count; + + long rejrefinetetcount, rejrefineshcount; + +#ifdef WITH_RUNTIME_COUNTERS + clock_t t_ptloc, t_ptinsert; // Time counters for DT operations. +#endif /////////////////////////////////////////////////////////////////////////////// // // // Mesh manipulation primitives // // // +// A serial of mesh operations such as topological maintenance, navigation, // +// local modification, etc., is accomplished through a set of mesh manipul- // +// ation primitives. These primitives are indeed very simple functions which // +// take one or two handles ('triface's and 'face's) as parameters, perform // +// basic operations such as "glue two tetrahedra at a face", "return the // +// origin of a tetrahedron", "return the subface adjoining at the face of a // +// tetrahedron", and so on. // +// // /////////////////////////////////////////////////////////////////////////////// // Fast lookup tables for mesh manipulation primitives. - static int bondtbl[12][12], fsymtbl[12][12]; - static int esymtbl[12], enexttbl[12], eprevtbl[12]; - static int enextesymtbl[12], eprevesymtbl[12]; - static int eorgoppotbl[12], edestoppotbl[12]; - static int facepivot1[12], facepivot2[12][12]; - static int orgpivot[12], destpivot[12], apexpivot[12], oppopivot[12]; - static int tsbondtbl[12][6], stbondtbl[12][6]; - static int tspivottbl[12][6], stpivottbl[12][6]; - static int ver2edge[12], edge2ver[6], epivot[12]; - static int sorgpivot [6], sdestpivot[6], sapexpivot[6]; + static int mod12[36]; + static int mod6[18]; + static int edgepivot[12]; + static int orgpivot [12]; + static int destpivot[12]; + static int apexpivot[12]; + static int oppopivot[12]; + static int ver2edge[12]; + static int edge2ver[6]; static int snextpivot[6]; - - void inittables(); + static int sorgpivot [6]; + static int sdestpivot[6]; + static int sapexpivot[6]; + static int epivot[4]; // Primitives for tetrahedra. + inline void decode(tetrahedron ptr, triface& t); inline tetrahedron encode(triface& t); inline tetrahedron encode2(tetrahedron* ptr, int ver); - inline void decode(tetrahedron ptr, triface& t); inline void bond(triface& t1, triface& t2); inline void dissolve(triface& t); + inline void fsym(triface& t1, triface& t2); + inline void fsymself(triface& t); inline void esym(triface& t1, triface& t2); inline void esymself(triface& t); inline void enext(triface& t1, triface& t2); @@ -1383,14 +1458,10 @@ public: inline void enextesymself(triface& t); inline void eprevesym(triface& t1, triface& t2); inline void eprevesymself(triface& t); - inline void eorgoppo(triface& t1, triface& t2); - inline void eorgoppoself(triface& t); - inline void edestoppo(triface& t1, triface& t2); - inline void edestoppoself(triface& t); - inline void fsym(triface& t1, triface& t2); - inline void fsymself(triface& t); inline void fnext(triface& t1, triface& t2); inline void fnextself(triface& t); + inline void fprev(triface& t1, triface& t2); + inline void fprevself(triface& t); inline point org (triface& t); inline point dest(triface& t); inline point apex(triface& t); @@ -1458,6 +1529,8 @@ public: inline void setshellmark(face& s, int value); inline enum shestype shelltype(face& s); inline void setshelltype(face& s, enum shestype value); + inline int shellpbcgroup(face& s); + inline void setshellpbcgroup(face& s, int value); inline void sinfect(face& s); inline void suninfect(face& s); inline bool sinfected(face& s); @@ -1521,6 +1594,8 @@ public: inline void setpoint2ppt(point pt, point value); inline tetrahedron point2bgmtet(point pt); inline void setpoint2bgmtet(point pt, tetrahedron value); + inline point point2pbcpt(point pt); + inline void setpoint2pbcpt(point pt, point value); // Advanced primitives. inline void point2tetorg(point pt, triface& t); @@ -1539,53 +1614,32 @@ public: tetrahedron *alltetrahedrontraverse(); void shellfacedealloc(memorypool*, shellface*); shellface *shellfacetraverse(memorypool*); + void badfacedealloc(memorypool*, badface*); + badface *badfacetraverse(memorypool*); void pointdealloc(point); point pointtraverse(); - - void makeindex2pointmap(point*&); - void makepoint2submap(memorypool*, int*&, face*&); void maketetrahedron(triface*); void makeshellface(memorypool*, face*); void makepoint(point*, enum verttype); + void makeindex2pointmap(point*&); + void makepoint2submap(memorypool*, int*&, face*&); + void initializepools(); /////////////////////////////////////////////////////////////////////////////// // // -// Advanced geometric predicates and calculations // -// // -// TetGen uses a simplified symbolic perturbation scheme from Edelsbrunner, // -// et al [*]. Hence the point-in-sphere test never returns a zero. The idea // -// is to perturb the weights of vertices in the fourth dimension. TetGen // -// uses the indices of the vertices decide the amount of perturbation. It is // -// implemented in the routine insphere_s(). -// // -// The routine tri_edge_test() determines whether or not a triangle and an // -// edge intersect in 3D. If they intersect, their intersection type is also // -// reported. This test is a combination of n 3D orientation tests (n is bet- // -// ween 3 and 9). It uses the robust orient3d() test to make the branch dec- // -// isions. The routine tri_tri_test() determines whether or not two triang- // -// les intersect in 3D. It also uses the robust orient3d() test. // -// // -// There are a number of routines to calculate geometrical quantities, e.g., // -// circumcenters, angles, dihedral angles, face normals, face areas, etc. // -// They are so far done by the default floating-point arithmetics which are // -// non-robust. They should be improved in the future. // +// Geometric predicates and calculations // // // /////////////////////////////////////////////////////////////////////////////// - // Symbolic perturbations (robust) - REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); - REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, - REAL, REAL, REAL, REAL, REAL); - - // Triangle-edge intersection test (robust) + // Triangle-edge intersection test int tri_edge_2d(point, point, point, point, point, point, int, int*, int*); - int tri_edge_tail(point, point, point, point, point, point, REAL, REAL, int, + int tri_edge_tail(point, point, point, point, point, point, REAL, REAL, int, int*, int*); int tri_edge_test(point, point, point, point, point, point, int, int*, int*); - // Triangle-triangle intersection test (robust) + // Triangle-triangle intersection test int tri_edge_inter_tail(point, point, point, point, point, REAL, REAL); int tri_tri_inter(point, point, point, point, point, point); @@ -1595,12 +1649,13 @@ public: bool lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N); void lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N); - // An embedded 2-dimensional geometric predicate (non-robust) + // Geometric predicates REAL incircle3d(point pa, point pb, point pc, point pd); + REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); + REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, + REAL, REAL, REAL, REAL, REAL); - // Geometric calculations (non-robust) - REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd); - inline REAL norm2(REAL x, REAL y, REAL z); + // Geometric calculations inline REAL distance(REAL* p1, REAL* p2); void facenormal(point pa, point pb, point pc, REAL *n, int pivot, REAL *lav); REAL shortdistance(REAL* p, REAL* e1, REAL* e2); @@ -1613,133 +1668,51 @@ public: void tetallnormal(point, point, point, point, REAL N[4][3], REAL* volume); REAL tetaspectratio(point, point, point, point); bool circumsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius); - bool orthosphere(REAL*,REAL*,REAL*,REAL*,REAL,REAL,REAL,REAL,REAL*,REAL*); void planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); REAL tetprismvol(REAL* pa, REAL* pb, REAL* pc, REAL* pd); - bool calculateabovepoint(arraypool*, point*, point*, point*); - void calculateabovepoint4(point, point, point, point); /////////////////////////////////////////////////////////////////////////////// // // // Local mesh transformations // // // -// A local transformation replaces a small set of tetrahedra with another // -// set of tetrahedra which fills the same space and the same boundaries. // -// In 3D, the most simplest local transformations are the elementary flips // -// peformed within the convex hull of five vertices: 2-to-3, 3-to-2, 1-to-4, // -// and 4-to-1 flips, where the numbers indicate the number of tetrahedra // -// before and after each flip. The 1-to-4 and 4-to-1 flip involve inserting // -// or deleting a vertex, respectively. // -// There are complex local transformations which can be decomposed as a // -// combination of elementary flips. For example,a 4-to-4 flip which replaces // -// two coplanar edges can be regarded by a 2-to-3 flip and a 3-to-2 flip. // -// Note that the first 2-to-3 flip will temporarily create a degenerate tet- // -// rahedron which is removed immediately by the followed 3-to-2 flip. More // -// generally, a n-to-m flip, where n > 3, m = (n - 2) * 2, which removes an // -// edge can be done by first performing a sequence of (n - 3) 2-to-3 flips // -// followed by a 3-to-2 flip. // -// // -// The routines flip23(), flip32(), and flip41() perform the three element- // -// ray flips. The flip14() is available inside the routine insertpoint(). // -// // -// The routines flipnm() and flipnm_post() implement a generalized edge flip // -// algorithm which uses a combination of elementary flips. // -// // -// The routine insertpoint() implements a variant of Bowyer-Watson's cavity // -// algorithm to insert a vertex. It works for arbitray tetrahedralization, // -// either Delaunay, or constrained Delaunay, or non-Delaunay. // -// // /////////////////////////////////////////////////////////////////////////////// + void flippush(badface*&, triface*); + // The elementary flips. - void flip23(triface*, int, flipconstraints* fc); - void flip32(triface*, int, flipconstraints* fc); - void flip41(triface*, int, flipconstraints* fc); + void flip23(triface*, int, int, int); + void flip32(triface*, int, int, int); + void flip41(triface*, int, int, int); // A generalized edge flip. int flipnm(triface*, int n, int level, int, flipconstraints* fc); int flipnm_post(triface*, int n, int nn, int, flipconstraints* fc); + // Incremental flips. + long lawsonflip3d(point, int flipflag, int, int, int flipedgeflag); + // Point insertion. - int insertpoint(point, triface*, face*, face*, insertvertexflags*); - void insertpoint_abort(face*, insertvertexflags*); + int insertvertex(point newpt, triface *searchtet, face *splitsh, face*, + insertvertexflags *ivf); /////////////////////////////////////////////////////////////////////////////// // // // Delaunay tetrahedralization // // // -// The routine incrementaldelaunay() implemented two incremental algorithms // -// for constructing Delaunay tetrahedralizations (DTs): the Bowyer-Watson // -// (B-W) algorithm and the incremental flip algorithm of Edelsbrunner and // -// Shah, "Incremental topological flipping works for regular triangulation," // -// Algorithmica, 15:233-241, 1996. // -// // -// The routine incrementalflip() implements the flip algorithm of [Edelsbru- // -// nner and Shah, 1996]. It flips a queue of locally non-Delaunay faces (in // -// an arbitrary order). The success is guaranteed when the Delaunay tetrah- // -// edralization is constructed incrementally by adding one vertex at a time. // -// // -// The routine locate() finds a tetrahedron contains a new point in current // -// DT. It uses a simple stochastic walk algorithm: starting from an arbitr- // -// ary tetrahedron in DT, it finds the destination by visit one tetrahedron // -// at a time, randomly chooses a tetrahedron if there are more than one // -// choices. This algorithm terminates due to Edelsbrunner's acyclic theorem. // -// Choose a good starting tetrahedron is crucial to the speed of the walk. // -// TetGen originally uses the "jump-and-walk" algorithm of Muecke, E.P., // -// Saias, I., and Zhu, B. "Fast Randomized Point Location Without Preproces- // -// sing." In Proceedings of the 12th ACM Symposium on Computational Geometry,// -// 274-283, 1996. It first randomly samples several tetrahedra in the DT // -// and then choosing the closet one to start walking. // -// The above algorithm slows download dramatically as the number of points // -// grows -- reported in Amenta, N., Choi, S. and Rote, G., "Incremental // -// construction con {BRIO}," In Proceedings of 19th ACM Symposium on // -// Computational Geometry, 211-219, 2003. On the other hand, Liu and // -// Snoeyink showed that the point location can be made in constant time if // -// the points are pre-sorted so that the nearby points in space have nearby // -// indices, then adding the points in this order. They sorted the points // -// along the 3D Hilbert curve. // -// // -// The routine hilbert_sort3() sorts a set of 3D points along the 3D Hilbert // -// curve. It recursively splits a point set according to the Hilbert indices // -// mapped to the subboxes of the bounding box of the point set. // -// The Hilbert indices is calculated by Butz's algorithm in 1971. A nice // -// exposition of this algorithm can be found in the paper of Hamilton, C., // -// "Compact Hilbert Indices", Technical Report CS-2006-07, Computer Science, // -// Dalhousie University, 2006 (the Section 2). My implementation also refer- // -// enced Steven Witham's implementation of "Hilbert walk" (hopefully, it is // -// still available at: http://www.tiac.net/~sw/2008/10/Hilbert/). // -// // -// TetGen sorts the points using the method in the paper of Boissonnat,J.-D.,// -// Devillers, O. and Hornus, S. "Incremental Construction of the Delaunay // -// Triangulation and the Delaunay Graph in Medium Dimension," In Proceedings // -// of the 25th ACM Symposium on Computational Geometry, 2009. // -// It first randomly sorts the points into subgroups using the Biased Rand-// -// omized Insertion Ordering (BRIO) of Amenta et al 2003, then sorts the // -// points in each subgroup along the 3D Hilbert curve. Inserting points in // -// this order ensures a randomized "sprinkling" of the points over the // -// domain, while sorting of each subset ensures locality. // -// // /////////////////////////////////////////////////////////////////////////////// void transfernodes(); // Point sorting. - int transgc[8][3][8], tsb1mod3[8]; - void hilbert_init(int n); - int hilbert_split(point* vertexarray, int arraysize, int gc0, int gc1, - REAL, REAL, REAL, REAL, REAL, REAL); - void hilbert_sort3(point* vertexarray, int arraysize, int e, int d, - REAL, REAL, REAL, REAL, REAL, REAL, int depth); - void brio_multiscale_sort(point*,int,int threshold,REAL ratio,int* depth); + void btree_sort(point*, int, int, REAL, REAL, REAL, REAL, REAL, REAL, int); + void btree_insert(point insertpt); + void btree_search(point searchpt, triface* searchtet); + void ordervertices(point* vertexarray, int arraysize); // Point location. unsigned long randomnation(unsigned int choices); void randomsample(point searchpt, triface *searchtet); - enum locateresult locate(point searchpt, triface *searchtet, int); - - // Incremental flips. - void flippush(badface*&, triface*); - int incrementalflip(point newpt, flipconstraints *fc); + enum locateresult locate(point searchpt, triface*, int, int); // Incremental Delaunay construction. void initialdelaunay(point pa, point pb, point pc, point pd); @@ -1751,6 +1724,9 @@ public: // // /////////////////////////////////////////////////////////////////////////////// + bool calculateabovepoint(arraypool*, point*, point*, point*); + void calculateabovepoint4(point, point, point, point); + void flipshpush(face*); void flip22(face*, int, int); void flip31(face*, int); @@ -1770,116 +1746,53 @@ public: void meshsurface(); void interecursive(shellface** subfacearray, int arraysize, int axis, - REAL, REAL, REAL, REAL, REAL, REAL, int* internum); + REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, + REAL bzmin, REAL bzmax, int* internum); void detectinterfaces(); /////////////////////////////////////////////////////////////////////////////// // // // Constrained Delaunay tetrahedralization // // // -// A constrained Delaunay tetrahedralization (CDT) is a variation of a Dela- // -// unay tetrahedralization (DT) that is constrained to respect the boundary // -// of a 3D PLC (domain). In a CDT of a 3D PLC, every vertex or edge of the // -// PLC is also a vertex or an edge of the CDT, every polygon of the PLC is a // -// union of triangles of the CDT. A crucial difference between a CDT and a // -// DT is that triangles in the PLC's polygons are not required to be locally // -// Delaunay, which frees the CDT to better respect the PLC's polygons. CDTs // -// have optimal properties similar to those of DTs. // -// // -// Steiner Points and Steiner CDTs. It is known that even a simple 3D polyh- // -// edron may not have a tetrahedralization which only uses its own vertices. // -// Some extra points, so-called "Steiner points" are needed in order to form // -// a tetrahedralization of such polyhedron. It is true for tetrahedralizing // -// a 3D PLC as well. A Steiner CDT of a 3D PLC is a CDT containing Steiner // -// points. The CDT algorithms of TetGen in general create Steiner CDTs. // -// Almost all of the Steiner points are added in the edges of the PLC. They // -// guarantee the existence of a CDT of the modified PLC. // -// // -// The routine constraineddelaunay() starts from a DT of the vertices of a // -// PLC and creates a (Steiner) CDT of the PLC (including Steiner points). It // -// is constructed by two steps, (1) segment recovery and (2) facet (polygon) // -// recovery. Each step is accomplished by its own algorithm. // -// // -// The routine delaunizesegments() implements the segment recovery algorithm // -// of Si, H. and Gaertner, K. "Meshing Piecewise Linear Complexes by Constr- // -// ained Delaunay Tetrahedralizations," In Proceedings of the 14th Internat- // -// ional Meshing Roundtable, 147--163, 2005. It adds Steiner points into // -// non-Delaunay segments until all subsegments appear together in a DT. The // -// running time of this algorithm is proportional to the number of added // -// Steiner points. // -// // -// There are two incremental facet recovery algorithms: the cavity re-trian- // -// gulation algorithm of Si, H. and Gaertner, K. "3D Boundary Recovery by // -// Constrained Delaunay Tetrahedralization," Interational Journal for Numer- // -// ical Methods in Engineering, 85:1341-1364, 2011, and the flip algorithm // -// of Shewchuk, J. "Updating and Constructing Constrained Delaunay and // -// Constrained Regular Triangulations by Flips." In Proceedings of the 19th // -// ACM Symposium on Computational Geometry, 86-95, 2003. // -// // -// It is guaranteed in theory, no Steiner point is needed in both algorithms // -// However, a facet with non-coplanar vertices might cause the additions of // -// Steiner points. It is discussed in the paper of Si, H., and Shewchuk, J.,// -// "Incrementally Constructing and Updating Constrained Delaunay // -// Tetrahedralizations with Finite Precision Coordinates." In Proceedings of // -// the 21th International Meshing Roundtable, 2012. // -// // -// Our implementation of the facet recovery algorithms recover a "missing // -// region" at a time. Each missing region is a subset of connected interiors // -// of a polygon. The routine formcavity() creates the cavity of crossing // -// tetrahedra of the missing region. // -// // -// The cavity re-triangulation algorithm is implemented by three subroutines,// -// delaunizecavity(), fillcavity(), and carvecavity(). Since it may fail due // -// to non-coplanar vertices, the subroutine restorecavity() is used to rest- // -// ore the original cavity. // -// // -// The routine flipinsertfacet() implements the flip algorithm. The subrout- // -// ine flipcertify() is used to maintain the priority queue of flips. // -// // -// The routine refineregion() is called when the facet recovery algorithm // -// fail to recover a missing region. It inserts Steiner points to refine the // -// missing region. In order to avoid inserting Steiner points very close to // -// existing segments. The classical encroachment rules of the Delaunay // -// refinement algorithm are used to choose the Steiner points. // -// // -// The routine constrainedfacets() does the facet recovery by using either // -// the cavity re-triangulation algorithm (default) or the flip algorithm. It // -// results a CDT of the (modified) PLC (including Steiner points). // -// // /////////////////////////////////////////////////////////////////////////////// void markacutevertices(); - enum interresult finddirection(triface* searchtet, point endpt); + void reportselfintersect(face *seg, face *shface); + + enum interresult finddirection(triface* searchtet, point endpt, int); enum interresult scoutsegment(point, point, triface*, point*, arraypool*); void getsteinerptonsegment(face* seg, point refpt, point steinpt); void delaunizesegments(); enum interresult scoutsubface(face* searchsh, triface* searchtet); - void formregion(face*, arraypool*, arraypool*, arraypool*); - int scoutcrossedge(triface& crosstet, arraypool*, arraypool*); - bool formcavity(triface*, arraypool*, arraypool*, arraypool*, arraypool*, - arraypool*, arraypool*); - - // Facet recovery by cavity re-triangulation [Si and Gaertner 2011]. - void delaunizecavity(arraypool*, arraypool*, arraypool*, arraypool*, - arraypool*, arraypool*); - bool fillcavity(arraypool*, arraypool*, arraypool*, arraypool*, - arraypool*, arraypool*, triface* crossedge); - void carvecavity(arraypool*, arraypool*, arraypool*); - void restorecavity(arraypool*, arraypool*, arraypool*, arraypool*); - - // Facet recovery by flips [Shewchuk 2003]. - void flipcertify(triface *chkface, badface **pqueue, point, point, point); - void flipinsertfacet(arraypool*, arraypool*, arraypool*, arraypool*); + void formmissingregion(face* missh, arraypool* missingshs, + arraypool* missingshbds, arraypool* missingshverts, + arraypool *adjtets); + int scoutcrossedge(triface& crosstet, arraypool*, arraypool* missingshs); + bool formcavity(triface* searchtet, arraypool* missingshs, + arraypool* crosstets, arraypool* topfaces, + arraypool* botfaces, arraypool* toppoints, + arraypool* botpoints); + + // Facet recovery by local re-tetrahedralization [Si and Gaertner'05,'11]. + void delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, + arraypool *cavshells, arraypool *newtets, + arraypool *crosstets, arraypool *misfaces); + bool fillcavity(arraypool* topshells, arraypool* botshells, + arraypool* midfaces, arraypool* missingshs); + void carvecavity(arraypool *crosstets, arraypool *topnewtets, + arraypool *botnewtets); + void restorecavity(arraypool *crosstets, arraypool *topnewtets, + arraypool *botnewtets); + + // Facet recovery by flips [Shewchuk'03]. + void flipcertify(triface *chkface, badface **pqueue); + void flipinsertfacet(arraypool *crosstets, arraypool *toppoints, + arraypool *botpoints, arraypool *midpoints); bool fillregion(arraypool* missingshs, arraypool*, arraypool* newshs); - - int insertpoint_cdt(point, triface*, face*, face*, insertvertexflags*, - arraypool*, arraypool*, arraypool*, arraypool*, - arraypool*, arraypool*); - void refineregion(face&, arraypool*, arraypool*, arraypool*, arraypool*, - arraypool*, arraypool*); + void refineregion(); void constrainedfacets(); @@ -1891,6 +1804,7 @@ public: // // /////////////////////////////////////////////////////////////////////////////// + // A call back function. int checkflipeligibility(int fliptype, point, point, point, point, point, int level, int edgepivot, flipconstraints* fc); @@ -1926,46 +1840,15 @@ public: void reconstructmesh(); int scoutpoint(point, triface*, int randflag); - REAL getpointmeshsize(point, triface*, int iloc); + REAL getpointmeshsize(point, triface*, int iloc, int posflag); void interpolatemeshsize(); - void insertconstrainedpoints(point *insertarray, int arylen); void insertconstrainedpoints(tetgenio *addio); /////////////////////////////////////////////////////////////////////////////// // // // Mesh refinement // // // -// The purpose of mesh refinement is to obtain a tetrahedral mesh with well- // -// -shaped tetrahedra and appropriate mesh size. It is necessary to insert // -// new Steiner points to achieve this property. The questions are (1) how to // -// choose the Steiner points? and (2) how to insert them? // -// // -// Delaunay refinement is a technique first developped by Chew [1989] and // -// Ruppert [1993, 1995] to generate quality triangular meshes in the plane. // -// It provides guarantee on the smallest angle of the triangles. Rupper's // -// algorithm guarantees that the mesh is size-optimal (to within a constant // -// factor) among all meshes with the same quality. // -// Shewchuk generalized Ruppert's algorithm into 3D in his PhD thesis // -// [Shewchuk 1997]. A short version of his algorithm appears in "Tetrahedral // -// Mesh Generation by Delaunay Refinement," In Proceedings of the 14th ACM // -// Symposium on Computational Geometry, 86-95, 1998. It guarantees that all // -// tetrahedra of the output mesh have a "radius-edge ratio" (equivalent to // -// the minimal face angle) bounded. However, it does not remove slivers, a // -// type of very flat tetrahedra which can have no small face angles but have // -// very small (and large) dihedral angles. Moreover, it may not terminate if // -// the input PLC contains "sharp features", e.g., two edges (or two facets) // -// meet at an acute angle (or dihedral angle). // -// // -// TetGen uses the basic Delaunay refinement scheme to insert Steiner points.// -// While it always maintains a constrained Delaunay mesh. The algorithm is // -// described in Si, H., "Adaptive Constrained Delaunay Mesh Generation," // -// International Journal for Numerical Methods in Engineering, 75:856-880. // -// This algorithm always terminates and sharp features are easily preserved. // -// The mesh has good quality (same as Shewchuk's Delaunay refinement algoro- // -// thm) in the bulk of the mesh domain. Moreover, it supports the generation // -// of adaptive mesh accoording to a (isotropic) mesh sizing function. // -// // /////////////////////////////////////////////////////////////////////////////// void marksharpsegments(); @@ -1976,15 +1859,14 @@ public: int splitsegment(face *splitseg, point encpt, int qflag, int chkencflag); void repairencsegs(int chkencflag); - void enqueuesubface(memorypool*, face*); int checkfac4encroach(point, point, point, point checkpt, REAL*, REAL*); int checkfac4split(face *chkfac, point& encpt, int& qflag, REAL *ccent); - int splitsubface(face *splitfac, point encpt, int qflag, REAL *ccent, int); + int splitsubface(face *splitfac, point encpt, int qflag, REAL *ccent, + int chkencflag); void repairencfacs(int chkencflag); - void enqueuetetrahedron(triface*); int checktet4split(triface *chktet, int& qflag, REAL *ccent); - int splittetrahedron(triface* splittet,int qflag,REAL *ccent, int); + int splittetrahedron(triface* splittet,int qflag,REAL *ccent,int chkencflag); void repairbadtets(int chkencflag); void delaunayrefinement(); @@ -1995,7 +1877,6 @@ public: // // /////////////////////////////////////////////////////////////////////////////// - long lawsonflip3d(flipconstraints *fc); void recoverdelaunay(); int gettetrahedron(point, point, point, point, triface *); @@ -2007,7 +1888,7 @@ public: int splitsliver(triface *, REAL, int); long removeslivers(int); - void optimizemesh(); + void optimizemesh(int optflag); /////////////////////////////////////////////////////////////////////////////// // // @@ -2024,9 +1905,7 @@ public: int checkconforming(int); // Mesh statistics. - void printfcomma(unsigned long n); void qualitystatistics(); - void memorystatistics(); void statistics(); /////////////////////////////////////////////////////////////////////////////// @@ -2053,7 +1932,6 @@ public: void outmesh2vtk(char*); - /////////////////////////////////////////////////////////////////////////////// // // // Constructor & destructor // @@ -2062,8 +1940,8 @@ public: tetgenmesh() { - in = addin = NULL; b = NULL; + in = NULL; bgm = NULL; tetrahedrons = subfaces = subsegs = points = NULL; @@ -2074,6 +1952,7 @@ public: dummypoint = NULL; flipstack = NULL; unflipqueue = NULL; + btreenode_list = NULL; cavetetlist = cavebdrylist = caveoldtetlist = NULL; cavetetshlist = cavetetseglist = cavetetvertlist = NULL; @@ -2084,15 +1963,19 @@ public: suppsteinerptlist = NULL; encseglist = encshlist = NULL; - highordertable = NULL; - + plane_pa = plane_pb = plane_pc = (point) NULL; - numpointattrib = numelemattrib = 0; - sizeoftensor = 0; + xmax = xmin = ymax = ymin = zmax = zmin = 0.0; + longest = 0.0; + hullsize = 0l; + insegments = 0l; + meshedges = meshhulledges = 0l; + steinerleft = -1; pointmtrindex = 0; pointparamindex = 0; pointmarkindex = 0; point2simindex = 0; + point2pbcptindex = 0; elemattribindex = 0; volumeboundindex = 0; shmarkindex = 0; @@ -2100,31 +1983,47 @@ public: checksubsegflag = 0; checksubfaceflag = 0; checkinverttetflag = 0; + checkpbcs = 0; checkconstraints = 0; nonconvex = 0; - autofliplinklevel = 1; + dupverts = 0; + unuverts = 0; samples = 0l; randomseed = 1l; minfaceang = minfacetdihed = PI; + sintheta_tol = sin(0.001 * PI / 180.0); + + autofliplinklevel = 1; + tetprism_vol_sum = 0.0; - longest = 0.0; - xmax = xmin = ymax = ymin = zmax = zmin = 0.0; + calc_tetprism_vol = 0; - insegments = 0l; - hullsize = 0l; - meshedges = meshhulledges = 0l; - steinerleft = -1; - dupverts = 0l; - unuverts = 0l; - nonregularcount = 0l; - st_segref_count = st_facref_count = st_volref_count = 0l; - fillregioncount = cavitycount = cavityexpcount = 0l; + ptloc_count = ptloc_max_count = 0l; + orient3dcount = 0l; + inspherecount = insphere_sos_count = 0l; flip14count = flip26count = flipn2ncount = 0l; - flip23count = flip32count = flip44count = flip41count = 0l; - flip22count = flip31count = 0l; - totalworkmemory = 0l; + flip23count = flip32count = flip44count = flip22count = 0l; + maxbowatcavsize = totalbowatcavsize = totaldeadtets = 0l; + triedgcount = triedgcopcount = 0l; + across_face_count = across_edge_count = across_max_count = 0l; + fillregioncount = missingsubfacecount = crossingtetcount = 0l; + cavitycount = cavityexpcount = 0l; + maxcavsize = maxregionsize = 0l; + maxcrossfacecount = maxflipsequence = 0l; + dbg_ignore_facecount = dbg_unflip_facecount = 0l; + ccent_relocate_count = 0l; + opt_sliver_peels = 0l; + r1count = r2count = r3count = 0l; + st_segref_count = st_facref_count = st_volref_count = 0l; + maxfliplinklevel = maxflipstarsize = 0l; + flipstarcount = sucflipstarcount = skpflipstarcount = 0l; + rejrefinetetcount = rejrefineshcount = 0l; + +#ifdef WITH_RUNTIME_COUNTERS + t_ptloc = t_ptinsert = (clock_t) 0; +#endif } // tetgenmesh() ~tetgenmesh() @@ -2133,29 +2032,31 @@ public: delete bgm; } - if (points != (memorypool *) NULL) { - delete points; - delete [] dummypoint; - } - if (tetrahedrons != (memorypool *) NULL) { delete tetrahedrons; } - if (subfaces != (memorypool *) NULL) { delete subfaces; + } + if (subsegs != (memorypool *) NULL) { delete subsegs; } - + if (points != (memorypool *) NULL) { + delete points; + } if (tet2segpool != NULL) { delete tet2segpool; + } + if (tet2subpool != NULL) { delete tet2subpool; } - if (flippool != NULL) { delete flippool; delete unflipqueue; } + if (dummypoint != (point) NULL) { + delete [] dummypoint; + } if (cavetetlist != NULL) { delete cavetetlist; @@ -2178,11 +2079,10 @@ public: delete subsegstack; delete subfacstack; delete subvertstack; - delete suppsteinerptlist; } - if (highordertable != NULL) { - delete [] highordertable; + if (suppsteinerptlist != NULL) { + delete suppsteinerptlist; } } // ~tetgenmesh() @@ -2236,7 +2136,7 @@ inline void terminatetetgen(int x) printf("Hint: use -d option to detect all self-intersections.\n"); break; case 4: - printf("A very small input feature size was detected. Program stopped.\n"); + printf("A very small input feature was size detected. Program stopped.\n"); printf("Hint: use -T option to set a smaller tolerance.\n"); break; case 5: @@ -2244,7 +2144,7 @@ inline void terminatetetgen(int x) printf("Hint: use -Y option to avoid adding Steiner points in boundary.\n"); break; case 10: - printf("An input error was detected. Program stopped.\n"); + printf("An input error was detected Program stopped.\n"); break; } // switch (x) exit(x); @@ -2253,12 +2153,24 @@ inline void terminatetetgen(int x) /////////////////////////////////////////////////////////////////////////////// // // -// Primitives for tetrahedra // +// Inline functions of mesh data structures // // // /////////////////////////////////////////////////////////////////////////////// -// encode() compress a handle into a single pointer. It relies on the -// assumption that all addresses of tetrahedra are aligned to sixteen- +// +// Begin of primitives for tetrahedra +// + +// decode() converts a pointer to an ordered tetrahedron. The version is +// extracted from the four least significant bits of the pointer. + +inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { + (t).ver = (int) ((uintptr_t) (ptr) & (uintptr_t) 15); + (t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver); +} + +// encode() compress an ordered tetrahedron into a single pointer. It +// relies on the assumption that all tetrahedra are aligned to sixteen- // byte boundaries, so that the last four significant bits are zero. inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) { @@ -2269,139 +2181,135 @@ inline tetgenmesh::tetrahedron tetgenmesh::encode2(tetrahedron* ptr, int ver) { return (tetrahedron) ((uintptr_t) (ptr) | (uintptr_t) (ver)); } -// decode() converts a pointer to a handle. The version is extracted from -// the four least significant bits of the pointer. - -inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { - (t).ver = (int) ((uintptr_t) (ptr) & (uintptr_t) 15); - (t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver); -} - -// bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must -// refer to the same face and the same edge. +// bond() connects two adjacent tetrahedra together. t1 and t2 must refer +// to the same face and the same edge. Note that the edge directions of +// t1 and t2 are reversed. +// Since an edge of t1 can be bonded to any of the three edges of t2. We +// choose to bond the edge of t2 which is symmetric to the 0-th edge of +// t1, and vice versa. Now assume t1 is at i-th edge and t2 is at j-th +// edge, where i, j in {0, 1, 2}. The edge in t2 symmetric to 0-th edge +// of t1 is mod3[i + j]. +// Since the edge number is coded in the two higher bits of the version, +// both i, j are in {0, 4, 8}. The edge in t2 symmetric to 0-th edge +// of t1 is mod12[i + j]. inline void tetgenmesh::bond(triface& t1, triface& t2) { - t1.tet[t1.ver & 3] = encode2(t2.tet, bondtbl[t1.ver][t2.ver]); - t2.tet[t2.ver & 3] = encode2(t1.tet, bondtbl[t2.ver][t1.ver]); + (t1).tet[(t1).ver & 3] = encode2((t2).tet, + ((t2).ver & 3) + mod12[((t1).ver & 12) + ((t2).ver & 12)]); + (t2).tet[(t2).ver & 3] = encode2((t1).tet, + ((t1).ver & 3) + mod12[((t1).ver & 12) + ((t2).ver & 12)]); } - // dissolve() a bond (from one side). inline void tetgenmesh::dissolve(triface& t) { t.tet[t.ver & 3] = NULL; } -// enext() finds the next edge (counterclockwise) in the same face. +// fsym() finds the adjacent tetrahedron at the same face and the same edge. + +inline void tetgenmesh::fsym(triface& t1, triface& t2) { + tetrahedron ptr = (t1).tet[(t1).ver & 3]; + int offset = 12 - ((t1).ver & 12); + decode(ptr, t2); + (t2).ver = mod12[(t2).ver + offset]; +} + +inline void tetgenmesh::fsymself(triface& t) { + tetrahedron ptr = (t).tet[(t).ver & 3]; + int offset = 12 - ((t).ver & 12); + decode(ptr, t); + (t).ver = mod12[(t).ver + offset]; +} + +// enext() finds the next edge (counterclockwise) on the same face. inline void tetgenmesh::enext(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = enexttbl[t1.ver]; + (t2).tet = (t1).tet; + (t2).ver = mod12[(t1).ver + 4]; } inline void tetgenmesh::enextself(triface& t) { - t.ver = enexttbl[t.ver]; + (t).ver = mod12[(t).ver + 4]; } -// eprev() finds the next edge (clockwise) in the same face. +// eprev() finds the next edge (clockwise) on the same face. inline void tetgenmesh::eprev(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = eprevtbl[t1.ver]; + (t2).tet = (t1).tet; + (t2).ver = mod12[(t1).ver + 8]; } inline void tetgenmesh::eprevself(triface& t) { - t.ver = eprevtbl[t.ver]; + (t).ver = mod12[(t).ver + 8]; } -// esym() finds the reversed edge. It is in the other face of the +// esym() finds the reversed edge. It is on the other face of the // same tetrahedron. inline void tetgenmesh::esym(triface& t1, triface& t2) { (t2).tet = (t1).tet; - (t2).ver = esymtbl[(t1).ver]; + (t2).ver = edgepivot[(t1).ver]; } inline void tetgenmesh::esymself(triface& t) { - (t).ver = esymtbl[(t).ver]; + (t).ver = edgepivot[(t).ver]; } -// enextesym() finds the reversed edge of the next edge. It is in the other -// face of the same tetrahedron. It is the combination esym() * enext(). +// enextesym() finds the reversed edge of the next edge. It is on the other +// face of the same tetrahedron. inline void tetgenmesh::enextesym(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = enextesymtbl[t1.ver]; + enext(t1, t2); + esymself(t2); } inline void tetgenmesh::enextesymself(triface& t) { - t.ver = enextesymtbl[t.ver]; + enextself(t); + esymself(t); } -// eprevesym() finds the reversed edge of the previous edge. +// eprevesym() finds the reversed edge of the previous edge. It is on the +// other face of the same tetrahedron. inline void tetgenmesh::eprevesym(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = eprevesymtbl[t1.ver]; + eprev(t1, t2); + esymself(t2); } inline void tetgenmesh::eprevesymself(triface& t) { - t.ver = eprevesymtbl[t.ver]; -} - -// eorgoppo() Finds the opposite face of the origin of the current edge. -// Return the opposite edge of the current edge. - -inline void tetgenmesh::eorgoppo(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = eorgoppotbl[t1.ver]; -} - -inline void tetgenmesh::eorgoppoself(triface& t) { - t.ver = eorgoppotbl[t.ver]; + eprevself(t); + esymself(t); } -// edestoppo() Finds the opposite face of the destination of the current -// edge. Return the opposite edge of the current edge. +// fnext() finds the next face while rotating about an edge according to +// a right-hand rule. The face is in the adjacent tetrahedron. It is +// equivalent to the combination: fsym() * esym(). -inline void tetgenmesh::edestoppo(triface& t1, triface& t2) { - t2.tet = t1.tet; - t2.ver = edestoppotbl[t1.ver]; +inline void tetgenmesh::fnext(triface& t1, triface& t2) { + esym(t1, t2); + fsymself(t2); } -inline void tetgenmesh::edestoppoself(triface& t) { - t.ver = edestoppotbl[t.ver]; +inline void tetgenmesh::fnextself(triface& t) { + esymself(t); + fsymself(t); } -// fsym() finds the adjacent tetrahedron at the same face and the same edge. +// fprev() finds the next face while rotating about an edge according to +// a left-hand rule. The face is in the adjacent tetrahedron. It is +// equivalent to the combination: esym() * fsym(). -inline void tetgenmesh::fsym(triface& t1, triface& t2) { - decode((t1).tet[(t1).ver & 3], t2); - t2.ver = fsymtbl[t1.ver][t2.ver]; +inline void tetgenmesh::fprev(triface& t1, triface& t2) { + fsym(t1, t2); + esymself(t2); } - -#define fsymself(t) \ - t1ver = (t).ver; \ - decode((t).tet[(t).ver & 3], (t));\ - (t).ver = fsymtbl[t1ver][(t).ver] - -// fnext() finds the next face while rotating about an edge according to -// a right-hand rule. The face is in the adjacent tetrahedron. It is -// the combination: fsym() * esym(). - -inline void tetgenmesh::fnext(triface& t1, triface& t2) { - decode(t1.tet[facepivot1[t1.ver]], t2); - t2.ver = facepivot2[t1.ver][t2.ver]; +inline void tetgenmesh::fprevself(triface& t) { + fsymself(t); + esymself(t); } - -#define fnextself(t) \ - t1ver = (t).ver; \ - decode((t).tet[facepivot1[(t).ver]], (t)); \ - (t).ver = facepivot2[t1ver][(t).ver] - - // The following primtives get or set the origin, destination, face apex, // or face opposite of an ordered tetrahedron. @@ -2500,12 +2408,17 @@ inline void tetgenmesh::uninfect(triface& t) { ((int *) (t.tet))[elemmarkerindex] &= ~1; } +// Test a tetrahedron for viral infection. + inline bool tetgenmesh::infected(triface& t) { return (((int *) (t.tet))[elemmarkerindex] & 1) != 0; } // marktest(), marktested(), unmarktest() -- primitives to flag or unflag a -// tetrahedron. Use the second lowerest bit of the element marker. +// tetrahedron. The last second bit of the element marker is marked (1) +// or unmarked (0). +// One needs them in forming Bowyer-Watson cavity, to mark a tetrahedron if +// it has been checked (for Delaunay case) so later check can be avoided. inline void tetgenmesh::marktest(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= 2; @@ -2522,6 +2435,8 @@ inline bool tetgenmesh::marktested(triface& t) { // markface(), unmarkface(), facemarked() -- primitives to flag or unflag a // face of a tetrahedron. From the last 3rd to 6th bits are used for // face markers, e.g., the last third bit corresponds to loc = 0. +// One use of the face marker is in flip algorithm. Each queued face (check +// for locally Delaunay) is marked. inline void tetgenmesh::markface(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= (4 << (t.ver & 3)); @@ -2538,7 +2453,7 @@ inline bool tetgenmesh::facemarked(triface& t) { // markedge(), unmarkedge(), edgemarked() -- primitives to flag or unflag an // edge of a tetrahedron. From the last 7th to 12th bits are used for // edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc. -// Remark: The last 7th bit is marked by 2^6 = 64. +// Remark: The last 7th bit is marked by 2^6 = 64. inline void tetgenmesh::markedge(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= (int) (64 << ver2edge[(t).ver]); @@ -2591,6 +2506,7 @@ inline void tetgenmesh::increaseelemcounter(triface& t) { inline void tetgenmesh::decreaseelemcounter(triface& t) { int c = elemcounter(t); + assert(c > 0); // Never get a negative counter. setelemcounter(t, c - 1); } @@ -2606,11 +2522,13 @@ inline bool tetgenmesh::isdeadtet(triface& t) { return ((t.tet == NULL) || (t.tet[4] == NULL)); } -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for subfaces and subsegments // -// // -/////////////////////////////////////////////////////////////////////////////// +// +// End of primitives for tetrahedra +// + +// +// Begin of primitives for subfaces/subsegments +// // Each subface contains three pointers to its neighboring subfaces, with // edge versions. To save memory, both information are kept in a single @@ -2622,7 +2540,7 @@ inline bool tetgenmesh::isdeadtet(triface& t) { inline void tetgenmesh::sdecode(shellface sptr, face& s) { s.shver = (int) ((uintptr_t) (sptr) & (uintptr_t) 7); - s.sh = (shellface *) ((uintptr_t) (sptr) ^ (uintptr_t) (s.shver)); + s.sh = (shellface *) ((uintptr_t) (sptr) & ~ (uintptr_t) 7); } inline tetgenmesh::shellface tetgenmesh::sencode(face& s) { @@ -2857,6 +2775,17 @@ inline void tetgenmesh::setshelltype(face& s, enum shestype value) ((((int *) ((s).sh))[shmarkindex + 1]) & 255); } +// These two primitives set or read the pbc group of the subface. + +inline int tetgenmesh::shellpbcgroup(face& s) +{ + return ((int *) (s.sh))[shmarkindex + 2]; +} + +inline void tetgenmesh::setshellpbcgroup(face& s, int value) +{ + ((int *) (s.sh))[shmarkindex + 2] = value; +} // sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a // subface. The last bit of ((int *) ((s).sh))[shmarkindex+1] is flaged. @@ -2881,7 +2810,8 @@ inline bool tetgenmesh::sinfected(face& s) } // smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag -// a subface.The last 2nd bit of the integer is flaged. +// a subface. +// The last 2nd bit of ((int *) ((s).sh))[shmarkindex+1] is flaged. inline void tetgenmesh::smarktest(face& s) { @@ -2901,7 +2831,8 @@ inline bool tetgenmesh::smarktested(face& s) } // smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or -// unflag a subface. The last 3rd bit of the integer is flaged. +// unflag a subface. +// The last 3rd bit of ((int *) ((s).sh))[shmarkindex+1] is flaged. inline void tetgenmesh::smarktest2(face& s) { @@ -2939,34 +2870,50 @@ inline bool tetgenmesh::smarktest3ed(face& s) return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0); } -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for interacting between tetrahedra and subfaces // -// // -/////////////////////////////////////////////////////////////////////////////// +// +// End of primitives for subfaces/subsegments +// +// +// Begin of primitives for interacting between tetrahedra and subfaces +// // tsbond() bond a tetrahedron (t) and a subface (s) together. // Note that t and s must be the same face and the same edge. Moreover, // t and s have the same orientation. // Since the edge number in t and in s can be any number in {0,1,2}. We bond // the edge in s which corresponds to t's 0th edge, and vice versa. -inline void tetgenmesh::tsbond(triface& t, face& s) +inline void tetgenmesh::tsbond(triface& t, face& s) { + int soffset, toffset, ver; + if ((t).tet[9] == NULL) { // Allocate space for this tet. (t).tet[9] = (tetrahedron) tet2subpool->alloc(); - // Initialize. + // NULL all fields in this space. for (int i = 0; i < 4; i++) { ((shellface *) (t).tet[9])[i] = NULL; } } + + assert(org(t) == sorg(s)); // FOR DEBUG + + if (((s).shver & 1) == 0) { + // t and s have the same orientation. + soffset = mod6[6 - (((t).ver & 12) >> 1)]; // {0,2,4} + toffset = mod12[12 - (((s).shver & 6) << 1)]; // {0,4,8} + } else { + // t and s have revsered orientations. + soffset = (((t).ver & 12) >> 1); // {0,2,4} + toffset = (((s).shver & 6) << 1); // {0,4,8} + } + // Bond t <== s. - ((shellface *) (t).tet[9])[(t).ver & 3] = - sencode2((s).sh, tsbondtbl[t.ver][s.shver]); + ver = ((s).shver & 1) + mod6[((s).shver & 6) + soffset]; + ((shellface *) (t).tet[9])[(t).ver & 3] = sencode2((s).sh, ver); // Bond s <== t. - s.sh[9 + ((s).shver & 1)] = - (shellface) encode2((t).tet, stbondtbl[t.ver][s.shver]); + ver = ((t).ver & 3) + mod12[((t).ver & 12) + toffset]; + s.sh[9 + ((s).shver & 1)] = (shellface) encode2((t).tet, ver); } // tspivot() finds a subface (s) abutting on the given tetrahdera (t). @@ -2976,18 +2923,24 @@ inline void tetgenmesh::tsbond(triface& t, face& s) inline void tetgenmesh::tspivot(triface& t, face& s) { + int soffset; + if ((t).tet[9] == NULL) { (s).sh = NULL; return; } + // Get the attached subface s. sdecode(((shellface *) (t).tet[9])[(t).ver & 3], (s)); - (s).shver = tspivottbl[t.ver][s.shver]; -} -// Quickly check if the handle (t, v) is a subface. -#define issubface(t) \ - ((t).tet[9] && ((t).tet[9])[(t).ver & 3]) + // Set the right edge in s. + if (((s).shver & 1) == 0) { + soffset = (((t).ver & 12) >> 1); // {0,2,4} + } else { + soffset = mod6[6 - (((t).ver & 12) >> 1)]; // {0,2,4} + } + (s).shver = ((s).shver & 1) + mod6[((s).shver & 6) + soffset]; +} // stpivot() finds a tetrahedron (t) abutting a given subface (s). // Return the t (if it exists) with the same edge and the same @@ -2995,17 +2948,21 @@ inline void tetgenmesh::tspivot(triface& t, face& s) inline void tetgenmesh::stpivot(face& s, triface& t) { + int toffset; + decode((tetrahedron) s.sh[9 + (s.shver & 1)], t); + if ((t).tet == NULL) { return; } - (t).ver = stpivottbl[t.ver][s.shver]; -} -// Quickly check if this subface is attached to a tetrahedron. - -#define isshtet(s) \ - ((s).sh[9 + ((s).shver & 1)]) + if (((s).shver & 1) == 0) { + toffset = (((s).shver & 6) << 1); // {0,4,8} + } else { + toffset = mod12[12 - (((s).shver & 6) << 1)]; // {0,4,8} + } + (t).ver = ((t).ver & 3) + mod12[((t).ver & 12) + toffset]; +} // tsdissolve() dissolve a bond (from the tetrahedron side). @@ -3024,11 +2981,13 @@ inline void tetgenmesh::stdissolve(face& s) (s).sh[10] = NULL; } -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for interacting between subfaces and segments // -// // -/////////////////////////////////////////////////////////////////////////////// +// +// End of primitives for interacting between tetrahedra and subfaces +// + +// +// Begin of primitives for interacting between subfaces and subsegs +// // ssbond() bond a subface to a subsegment. @@ -3055,26 +3014,24 @@ inline void tetgenmesh::ssdissolve(face& s) inline void tetgenmesh::sspivot(face& s, face& edge) { - sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge); + shellface sptr = (shellface) s.sh[6 + (s.shver >> 1)]; + sdecode(sptr, edge); } -// Quickly check if the edge is a subsegment. - -#define isshsubseg(s) \ - ((s).sh[6 + ((s).shver >> 1)]) +// +// End of primitives for interacting between subfaces and subsegs +// -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for interacting between tetrahedra and segments // -// // -/////////////////////////////////////////////////////////////////////////////// +// +// Begin of primitives for interacting between tet and subsegs. +// inline void tetgenmesh::tssbond1(triface& t, face& s) { if ((t).tet[8] == NULL) { // Allocate space for this tet. (t).tet[8] = (tetrahedron) tet2segpool->alloc(); - // Initialization. + // NULL all fields in this space. for (int i = 0; i < 6; i++) { ((shellface *) (t).tet[8])[i] = NULL; } @@ -3108,21 +3065,18 @@ inline void tetgenmesh::tsspivot1(triface& t, face& s) } } -// Quickly check whether 't' is a segment or not. - -#define issubseg(t) \ - ((t).tet[8] && ((t).tet[8])[ver2edge[(t).ver]]) - inline void tetgenmesh::sstpivot1(face& s, triface& t) { decode((tetrahedron) s.sh[9], t); } -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for points // -// // -/////////////////////////////////////////////////////////////////////////////// +// +// End of primitives for interacting between tet and subsegs. +// + +// +// Begin of primitives for points +// inline int tetgenmesh::pointmark(point pt) { return ((int *) (pt))[pointmarkindex]; @@ -3179,8 +3133,8 @@ inline bool tetgenmesh::pinfected(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0; } -// pmarktest(), punmarktest(), pmarktested() -- more primitives to -// flag or unflag a point. +// pmarktest(), punmarktest(), pmarktested() -- primitives to mark or unmark +// a point. inline void tetgenmesh::pmarktest(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 2; @@ -3194,6 +3148,8 @@ inline bool tetgenmesh::pmarktested(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 2) != 0; } +// pmarktest2(), ... + inline void tetgenmesh::pmarktest2(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 4; } @@ -3206,6 +3162,8 @@ inline bool tetgenmesh::pmarktest2ed(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 4) != 0; } +// pmarktest3(), ... + inline void tetgenmesh::pmarktest3(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 8; } @@ -3218,6 +3176,7 @@ inline bool tetgenmesh::pmarktest3ed(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 8) != 0; } + // These following primitives set and read a pointer to a tetrahedron // a subface/subsegment, a point, or a tet of background mesh. @@ -3254,6 +3213,15 @@ inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { ((tetrahedron *) (pt))[point2simindex + 3] = value; } +// These primitives set and read a pointer to its pbc point. + +inline tetgenmesh::point tetgenmesh::point2pbcpt(point pt) { + return (point) ((tetrahedron *) (pt))[point2pbcptindex]; +} + +inline void tetgenmesh::setpoint2pbcpt(point pt, point value) { + ((tetrahedron *) (pt))[point2pbcptindex] = (tetrahedron) value; +} // point2tetorg() Get the tetrahedron whose origin is the point. @@ -3322,19 +3290,19 @@ inline tetgenmesh::point tetgenmesh::farsdest(face& s) return sdest(travesh); } -/////////////////////////////////////////////////////////////////////////////// -// // -// Linear algebra operators. // -// // -/////////////////////////////////////////////////////////////////////////////// +// +// End of primitives for points +// // dot() returns the dot product: v1 dot v2. + inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } // cross() computes the cross product: n = v1 cross v2. + inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) { n[0] = v1[1] * v2[2] - v2[1] * v1[2]; @@ -3343,6 +3311,7 @@ inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) } // distance() computs the Euclidean distance between two points. + inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) { return sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) + @@ -3350,11 +3319,24 @@ inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) (p2[2] - p1[2]) * (p2[2] - p1[2])); } -inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z) -{ - return (x) * (x) + (y) * (y) + (z) * (z); -} +// Linear algebra operators. + +#define NORM2(x, y, z) ((x) * (x) + (y) * (y) + (z) * (z)) + +#define DIST(p1, p2) \ + sqrt(NORM2((p2)[0] - (p1)[0], (p2)[1] - (p1)[1], (p2)[2] - (p1)[2])) + +#define DOT(v1, v2) \ + ((v1)[0] * (v2)[0] + (v1)[1] * (v2)[1] + (v1)[2] * (v2)[2]) + +#define CROSS(v1, v2, n) \ + (n)[0] = (v1)[1] * (v2)[2] - (v2)[1] * (v1)[2];\ + (n)[1] = -((v1)[0] * (v2)[2] - (v2)[0] * (v1)[2]);\ + (n)[2] = (v1)[0] * (v2)[1] - (v2)[0] * (v1)[1] + +#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) +#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) #endif // #ifndef tetgenH