From 51a7e53f65e93cbdb46f5ff885ff1a68cc738fbc Mon Sep 17 00:00:00 2001 From: Christophe Geuzaine <cgeuzaine@ulg.ac.be> Date: Wed, 3 Apr 2013 05:35:30 +0000 Subject: [PATCH] --- Common/OS.cpp | 1 - contrib/Tetgen1.5/predicates.cxx | 742 +- contrib/Tetgen1.5/tetgen.cxx | 15011 +++++++++++++---------------- contrib/Tetgen1.5/tetgen.h | 1962 ++-- 4 files changed, 8019 insertions(+), 9697 deletions(-) diff --git a/Common/OS.cpp b/Common/OS.cpp index 961da73fcc..a6f3f59a71 100644 --- a/Common/OS.cpp +++ b/Common/OS.cpp @@ -27,7 +27,6 @@ #include <io.h> #include <direct.h> #include <fcntl.h> -#include <io.h> #include <iostream> #include <fstream> #endif diff --git a/contrib/Tetgen1.5/predicates.cxx b/contrib/Tetgen1.5/predicates.cxx index 4120704c35..5fe852e211 100644 --- a/contrib/Tetgen1.5/predicates.cxx +++ b/contrib/Tetgen1.5/predicates.cxx @@ -125,6 +125,13 @@ #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, */ @@ -149,8 +156,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 */ @@ -375,271 +382,144 @@ static REAL o3derrboundA, o3derrboundB, o3derrboundC; static REAL iccerrboundA, iccerrboundB, iccerrboundC; static REAL isperrboundA, isperrboundB, isperrboundC; -/*****************************************************************************/ -/* */ -/* doubleprint() Print the bit representation of a double. */ -/* */ -/* Useful for debugging exact arithmetic routines. */ -/* */ -/*****************************************************************************/ +// 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". -/* -void doubleprint(number) -double number; +double fppow2(int n) { - 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); - } + 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); } -*/ -/*****************************************************************************/ -/* */ -/* floatprint() Print the bit representation of a float. */ -/* */ -/* Useful for debugging exact arithmetic routines. */ -/* */ -/*****************************************************************************/ +#ifdef SINGLE -/* -void floatprint(number) -float number; +float fstore(float 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); - } + return (x); } -*/ -/*****************************************************************************/ -/* */ -/* expansion_print() Print the bit representation of an expansion. */ -/* */ -/* Useful for debugging exact arithmetic routines. */ -/* */ -/*****************************************************************************/ - -/* -void expansion_print(elen, e) -int elen; -REAL *e; +int test_float(int verbose) { - int i; + float x; + int pass = 1; - for (i = elen - 1; i >= 0; i--) { - REALPRINT(e[i]); - if (i > 0) { - printf(" +\n"); - } else { - printf("\n"); - } + //(void)printf("float:\n"); + + 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 } -} -*/ -/*****************************************************************************/ -/* */ -/* doublerand() Generate a double with random 53-bit significand and a */ -/* random exponent in [0, 511]. */ -/* */ -/*****************************************************************************/ + 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); -/* -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; - } + 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; } - return result; -} -*/ -/*****************************************************************************/ -/* */ -/* narrowdoublerand() Generate a double with random 53-bit significand */ -/* and a random exponent in [0, 7]. */ -/* */ -/*****************************************************************************/ - -/* -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; - } + 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; } - return result; + + return pass; } -*/ -/*****************************************************************************/ -/* */ -/* uniformdoublerand() Generate a double with random 53-bit significand. */ -/* */ -/*****************************************************************************/ +# else -/* -double uniformdoublerand() +double dstore(double x) { - double result; - long a, b; - - a = random(); - b = random(); - result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); - return result; + return (x); } -*/ - -/*****************************************************************************/ -/* */ -/* floatrand() Generate a float with random 24-bit significand and a */ -/* random exponent in [0, 63]. */ -/* */ -/*****************************************************************************/ -/* -float floatrand() +int test_double(int verbose) { - 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; - } + 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 } - return result; -} -*/ -/*****************************************************************************/ -/* */ -/* narrowfloatrand() Generate a float with random 24-bit significand and */ -/* a random exponent in [0, 7]. */ -/* */ -/*****************************************************************************/ + x = 1.0; + while (dstore(1.0 + x/2.0) != 1.0) + x /= 2.0; + if (verbose) + (void)printf(" machine epsilon = %13.5le ", x); -/* -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; - } + 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; } - return result; -} -*/ - -/*****************************************************************************/ -/* */ -/* uniformfloatrand() Generate a float with random 24-bit significand. */ -/* */ -/*****************************************************************************/ -/* -float uniformfloatrand() -{ - float result; - long a; + 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; + } - a = random(); - result = (float) ((a - 1073741824) >> 6); - return result; + return pass; } -*/ + +#endif /*****************************************************************************/ /* */ @@ -660,7 +540,8 @@ float uniformfloatrand() /* */ /*****************************************************************************/ -REAL exactinit() +void exactinit(int verbose, int noexact, int nofilter, REAL maxx, REAL maxy, + REAL maxz) { REAL half; REAL check, lastcheck; @@ -687,6 +568,24 @@ REAL exactinit() _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; @@ -722,7 +621,31 @@ REAL exactinit() isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; - return epsilon; /* Added by H. Si 30 Juli, 2004. */ + // 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); + } /*****************************************************************************/ @@ -1869,16 +1792,6 @@ 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; @@ -1916,6 +1829,7 @@ 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]); @@ -2263,27 +2177,16 @@ REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent) return finnow[finlength - 1]; } -#ifdef INEXACT_GEOM_PRED +#ifdef USE_CGAL_PREDICATES REAL orient3d(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); + 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])); } #else @@ -2293,16 +2196,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]; - 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]; + bdx = pb[0] - pd[0]; + bdy = pb[1] - pd[1]; bdz = pb[2] - pd[2]; + cdx = pc[0] - pd[0]; + cdy = pc[1] - pd[1]; cdz = pc[2] - pd[2]; bdxcdy = bdx * cdy; @@ -2318,6 +2221,19 @@ 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); @@ -2329,7 +2245,7 @@ REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd) return orient3dadapt(pa, pb, pc, pd, permanent); } -#endif // ifdef INEXACT_GEOM_PRED +#endif // #ifdef USE_CGAL_PREDICATES /*****************************************************************************/ /* */ @@ -3362,6 +3278,7 @@ 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]); @@ -3938,6 +3855,7 @@ 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]); @@ -4114,50 +4032,19 @@ REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, return insphereexact(pa, pb, pc, pd, pe); } -#ifdef INEXACT_GEOM_PRED +#ifdef USE_CGAL_PREDICATES REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) { - 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); + 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])); } + #else REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) @@ -4170,12 +4057,8 @@ 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]; @@ -4222,6 +4105,23 @@ 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); @@ -4262,7 +4162,7 @@ REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) return insphereadapt(pa, pb, pc, pd, pe, permanent); } -#endif // #ifdef INEXACT_GEOM_PRED +#endif // #ifdef USE_CGAL_PREDICATES /*****************************************************************************/ /* */ @@ -4327,6 +4227,7 @@ 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]); @@ -4540,6 +4441,7 @@ 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]); @@ -4699,108 +4601,106 @@ 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; - - //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); + 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); +} - 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 0a66de153c..df7fb43790 100644 --- a/contrib/Tetgen1.5/tetgen.cxx +++ b/contrib/Tetgen1.5/tetgen.cxx @@ -2,21 +2,17 @@ // // // TetGen // // // -// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // // // // Version 1.5 // -// February 21, 2012 // +// (March 27, 2013) // // // -// PRE-RELEASE TEST CODE. // -// PLEASE DO NOT DISTRIBUTE !! // -// PLEASE HELP ME TO IMPROVE IT !! // -// // -// Copyright (C) 2002--2012 // +// Copyright (C) 2002--2013 // // Hang Si // // Research Group: Numerical Mathematics and Scientific Computing // // Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // // Mohrenstr. 39, 10117 Berlin, Germany // -// Hang.Si@wias-berlin.de // +// 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. // @@ -335,6 +331,10 @@ 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,6 +419,12 @@ 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); @@ -627,7 +633,6 @@ 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; } @@ -740,7 +745,6 @@ bool tetgenio::load_mtr(char* filebasename) if (infile != (FILE *) NULL) { printf("Opening %s.\n", mtrfilename); } else { - // No such file. Return. return false; } @@ -1381,14 +1385,11 @@ 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); @@ -1603,14 +1604,11 @@ 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); @@ -2389,6 +2387,7 @@ 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); @@ -2415,6 +2414,7 @@ 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,6 +2422,7 @@ bool tetgenio::load_tetmesh(char* filebasename, int object) } if (success) { + // Try to load the following files (.var, .mtr). load_var(filebasename); load_mtr(filebasename); } @@ -2902,16 +2903,22 @@ char* tetgenio::findnextnumber(char *string) void tetgenbehavior::syntax() { - printf(" tetgen [-pYrq_a_AiS_T_dzfenvgKJBNEFICQVh] input_file\n"); + printf(" tetgen [-pYq_Aa_mriO_S_T_XMwcdzfenvgkJBNEFICQVh] input_file\n"); printf(" -p Tetrahedralizes a piecewise linear complex (PLC).\n"); - printf(" -Y No splitting of input boundaries (facets and segments).\n"); - printf(" -r Reconstructs a previously generated mesh.\n"); + printf(" -Y Preserves the input surface mesh (does not modify it).\n"); printf(" -q Refines mesh (to improve mesh quality).\n"); - printf(" -a Applies a maximum tetrahedron volume constraint.\n"); printf(" -A Assigns attributes to tetrahedra in different regions.\n"); - printf(" -i Inserts a list of additional points into mesh.\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(" -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"); @@ -2919,12 +2926,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 file.\n"); + printf(" -F Suppresses output of .face and .edge 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"); @@ -2943,20 +2950,18 @@ void tetgenbehavior::usage() printf("TetGen\n"); printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay "); printf("Triangulator\n"); - printf("Version 1.5 (February 21, 2012).\n"); + printf("Version 1.5\n"); + printf("(March 27, 2013)\n"); printf("\n"); - printf("Copyright (C) 2002 - 2012\n"); + printf("Copyright (C) 2002 - 2013\n"); printf("Hang Si\n"); printf("Mohrenstr. 39, 10117 Berlin, Germany\n"); - printf("Hang.Si@wias-berlin.de\n"); + printf("si@wias-berlin.de\n"); printf("\n"); printf("What Can TetGen Do?\n"); printf("\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(" TetGen generates Delaunay tetrahedralizations, constrained\n"); + printf(" Delaunay tetrahedralizations, and quality tetrahedral meshes.\n"); printf("\n"); printf("Command Line Syntax:\n"); printf("\n"); @@ -2977,19 +2982,21 @@ 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 and "); - printf("object.1.ele.\n"); + 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("\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 and "); - printf("object.1.face.\n"); + 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("\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 and object.1.face.\n"); + printf("object.1.node, object.1.ele,\n object.1.face, and object.1.edge\n"); printf("\n"); printf("Please send bugs/comments to Hang Si <si@wias-berlin.de>\n"); terminatetetgen(0); @@ -3004,19 +3011,6 @@ 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) @@ -3024,7 +3018,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) int startindex; int increment; int meshnumber; - int scount, ocount; int i, j, k; char workstring[1024]; @@ -3038,12 +3031,9 @@ 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 switches. + // Remember the command line for output. strcat(commandline, argv[i]); strcat(commandline, " "); if (startindex == 1) { @@ -3051,7 +3041,6 @@ 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; } } @@ -3072,11 +3061,17 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) facet_ang_tol = (REAL) strtod(workstring, (char **) NULL); } } else if (argv[i][j] == 's') { - psc = 1; + 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++; + } } else if (argv[i][j] == 'r') { - refine++; + refine = 1; } else if (argv[i][j] == 'q') { - quality++; + quality = 1; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3087,30 +3082,47 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - if (quality == 1) { // -q# - minratio = (REAL) strtod(workstring, (char **) NULL); - } else if (quality == 2) { // -qq# + 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'; mindihedral = (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++; + 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); } - 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; @@ -3121,8 +3133,66 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - weighted_param = (int) strtol(workstring, (char **) NULL, 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. } + 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] == '.')) { @@ -3141,71 +3211,44 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) varvolume = 1; } } else if (argv[i][j] == 'A') { - 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; + 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++; } - } 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 = 1; + facesout++; } else if (argv[i][j] == 'e') { edgesout++; } else if (argv[i][j] == 'n') { @@ -3214,10 +3257,8 @@ 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') { @@ -3226,10 +3267,6 @@ 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') { @@ -3249,53 +3286,38 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) steinerleft = (int) strtol(workstring, (char **) NULL, 0); } } else if (argv[i][j] == 'o') { - 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# + 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); - } 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') { - 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] == '+')) { + 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'); 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] == 'D') { - conforming = 1; + } else if (argv[i][j] == 'T') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3307,9 +3329,17 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - reflevel = (int) strtol(workstring, (char **) NULL, 0); + epsilon = (REAL) strtod(workstring, (char **) NULL); } - } else if (argv[i][j] == 'T') { + } 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') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3321,14 +3351,14 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - epsilon = (REAL) strtod(workstring, (char **) NULL); + tetrahedraperblock = (int) strtol(workstring, (char **) NULL, 0); + if (tetrahedraperblock > 8188) { + vertexperblock = tetrahedraperblock / 2; + shellfaceperblock = vertexperblock / 2; + } else { + tetrahedraperblock = 8188; + } } - } 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(); @@ -3395,6 +3425,10 @@ 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) { @@ -3430,18 +3464,16 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) } } } - 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; - } + // 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; } } @@ -3497,18 +3529,25 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) // Initialize fast lookup tables for mesh maniplulation primitives. -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 +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 // inversed edge (version) of it. -int tetgenmesh::edgepivot[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2}; +int tetgenmesh::esymtbl[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 @@ -3525,6 +3564,11 @@ 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. @@ -3538,11 +3582,91 @@ 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}; -// Edge versions whose apex or opposite may be dummypoint. +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + } + } -int tetgenmesh::epivot[4] = {4, 5, 2, 11}; + + 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; + + // 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); + } + } + // 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); + } + } +} /////////////////////////////////////////////////////////////////////////////// // // @@ -3576,6 +3700,7 @@ 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; @@ -3753,6 +3878,7 @@ int tetgenmesh::arraypool::newindex(void **newptr) return newindex; } + /////////////////////////////////////////////////////////////////////////////// // // // memorypool() The constructors of memorypool. // @@ -3766,7 +3892,6 @@ tetgenmesh::memorypool::memorypool() deaditemstack = (void *) NULL; pathblock = (void **) NULL; pathitem = (void *) NULL; - itemwordtype = POINTER; alignbytes = 0; itembytes = itemwords = 0; itemsperblock = 0; @@ -3775,10 +3900,10 @@ tetgenmesh::memorypool::memorypool() pathitemsleft = 0; } -tetgenmesh::memorypool:: -memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment) +tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, + int alignment) { - poolinit(bytecount, itemcount, wtype, alignment); + poolinit(bytecount, itemcount, wsize, alignment); } /////////////////////////////////////////////////////////////////////////////// @@ -3812,14 +3937,9 @@ tetgenmesh::memorypool::~memorypool() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::memorypool:: -poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) +void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize, + 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. @@ -3929,11 +4049,7 @@ void* tetgenmesh::memorypool::alloc() // Allocate a new item. newitem = nextitem; // Advance `nextitem' pointer to next free item in block. - if (itemwordtype == POINTER) { - nextitem = (void *) ((void **) nextitem + itemwords); - } else { - nextitem = (void *) ((REAL *) nextitem + itemwords); - } + nextitem = (void *) ((uintptr_t) nextitem + itembytes); unallocateditems--; maxitems++; } @@ -4017,25 +4133,18 @@ void* tetgenmesh::memorypool::traverse() } newitem = pathitem; // Find the next item in the block. - if (itemwordtype == POINTER) { - pathitem = (void *) ((void **) pathitem + itemwords); - } else { - pathitem = (void *) ((REAL *) pathitem + itemwords); - } + pathitem = (void *) ((uintptr_t) pathitem + itembytes); 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[0]'. Don't forget to minus 'in->firstnumber' when // -// to get the vertex form its index. // +// saved in 'idx2verlist[in->firstnumber]'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -4052,14 +4161,13 @@ 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. // @@ -4163,15 +4271,13 @@ void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) // dead tetrahedra when traversing the list of all tetrahedra. dyingtetrahedron[4] = (tetrahedron) NULL; - //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]); - } - //} + // 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); } @@ -4244,38 +4350,6 @@ 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; -} /////////////////////////////////////////////////////////////////////////////// // // @@ -4319,6 +4393,7 @@ 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; @@ -4332,9 +4407,9 @@ void tetgenmesh::maketetrahedron(triface *newtet) // No attached segments and sbfaces yet. newtet->tet[8] = NULL; newtet->tet[9] = NULL; - // Initialize the marker (for flags). + // Initialize the marker (clear all flags). setelemmarker(newtet->tet, 0); - for (int i = 0; i < in->numberoftetrahedronattributes; i++) { + for (int i = 0; i < numelemattrib; i++) { setelemattribute(newtet->tet, i, 0.0); } if (b->varvolume) { @@ -4355,6 +4430,7 @@ 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; @@ -4382,7 +4458,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; } @@ -4394,9 +4470,14 @@ void tetgenmesh::makeshellface(memorypool *pool, face *newface) void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) { - int ptmark, i; + int 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; @@ -4411,20 +4492,11 @@ void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) } } // Initialize the point marker (starting from in->firstnumber). - ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1); - setpointmark(*pnewpoint, ptmark); - // Initialize the point type. + setpointmark(*pnewpoint, (int) (points->items) - (!in->firstnumber)); + // Clear all flags. + ((int *) (*pnewpoint))[pointmarkindex + 1] = 0; + // Initialize (set) 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); - } } /////////////////////////////////////////////////////////////////////////////// @@ -4440,11 +4512,36 @@ void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) void tetgenmesh::initializepools() { - enum memorypool::wordtype wtype; - int pointsize, elesize, shsize; + int pointsize = 0, elesize = 0, shsize = 0; + int i; 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; @@ -4452,17 +4549,16 @@ 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) { - // For '-s' option (PSC), the u,v coordinates are provided. It is - // saved directly after the list of point attributes. - pointmtrindex = 6 + in->numberofpointattributes; + // '-s' option (PSC), the u,v coordinates are provided. + pointmtrindex = 5 + numpointattrib; } else { - pointmtrindex = 4 + in->numberofpointattributes; + pointmtrindex = 3 + numpointattrib; } // The index within each point at which its u, v coordinates are found. - pointparamindex = pointmtrindex - 2; + pointparamindex = 3 + (numpointattrib > 0); // 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. @@ -4491,19 +4587,11 @@ 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 to into the background mesh, point2bgmtet(). + // Increase one pointer 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(); @@ -4517,14 +4605,10 @@ 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, wtype, 0); + points = new memorypool(pointsize, b->vertexperblock, sizeof(REAL), 0); if (b->verbose) { printf(" Size of a point: %d bytes.\n", points->itembytes); @@ -4532,7 +4616,32 @@ void tetgenmesh::initializepools() // Initialize the infinite vertex. dummypoint = (point) new char[pointsize]; - setpointmark(dummypoint, -1); + // 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. // The number of bytes occupied by a tetrahedron is varying by the user- // specified options. The contents of the first 12 pointers are listed @@ -4558,26 +4667,28 @@ 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. Note that if the - // `b->regionattrib' flag is set, an additional attribute will be added. - volumeboundindex = elemattribindex + in->numberoftetrahedronattributes - + (b->regionattrib > 0); + // found, where the index is measured in REALs. + volumeboundindex = elemattribindex + numelemattrib; // 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 (in->numberoftetrahedronattributes + b->regionattrib > 0) { + } else if (numelemattrib > 0) { elesize = volumeboundindex * sizeof(REAL); } // Having determined the memory size of an element, initialize the pool. - tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, - memorypool::POINTER, 16); + tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void *), + 16); if (b->verbose) { printf(" Size of a tetrahedron: %d (%d) bytes.\n", elesize, @@ -4604,13 +4715,12 @@ 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 + checkpbcs) * sizeof(shellface); + shsize = (shmarkindex + 2) * 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, - memorypool::POINTER, 8); + subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); if (b->verbose) { printf(" Size of a shellface: %d (%d) bytes.\n", shsize, @@ -4619,40 +4729,37 @@ void tetgenmesh::initializepools() // Initialize the pool of subsegments. The subsegment's record is same // with subface. - subsegs = new memorypool(shsize, b->shellfaceperblock, - memorypool::POINTER, 8); + subsegs = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); // Initialize the pool for tet-subseg connections. tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock, - memorypool::POINTER, 0); + sizeof(void *), 0); // Initialize the pool for tet-subface connections. tet2subpool = new memorypool(4 * sizeof(shellface), b->shellfaceperblock, - memorypool::POINTER, 0); + sizeof(void *), 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 Bowyer-Watson algorithm. + // Initialize arraypools for surface point insertion/deletion. 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 pool for flips. - flippool = new memorypool(sizeof(badface), 1024, memorypool::POINTER, 0); + // Initialize the pools for flips. + flippool = new memorypool(sizeof(badface), 1024, sizeof(void *), 0); unflipqueue = new arraypool(sizeof(badface), 10); - // Initialize the arraypools for Bowyer-Watson algorithm. + // Initialize the arraypools for point insertion. cavetetlist = new arraypool(sizeof(triface), 10); cavebdrylist = new arraypool(sizeof(triface), 10); caveoldtetlist = new arraypool(sizeof(triface), 10); @@ -4668,9 +4775,148 @@ 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; +} + /////////////////////////////////////////////////////////////////////////////// // // // tri_edge_test() Triangle-edge intersection test. // @@ -4693,6 +4939,10 @@ REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; // // /////////////////////////////////////////////////////////////////////////////// +#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) { @@ -4709,14 +4959,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 = DIST(A, B); - len += DIST(B, C); - len += DIST(C, A); + len = distance(A, B); + len += distance(B, C); + len += distance(C, A); len /= 3.0; R = abovept; //dummypoint; R[0] = A[0] + len * n[0]; @@ -4737,7 +4987,6 @@ 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) { @@ -5321,7 +5570,6 @@ 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 @@ -5816,6 +6064,9 @@ 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) @@ -5825,19 +6076,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 = DIST(c, pd); + d = distance(c, pd); } else { // Choose [b, a, d] as the base triangle. if (area2[1] > 0) { circumsphere(pb, pa, pd, NULL, c, &r); - d = DIST(c, pc); + d = distance(c, pc); } else { // The four points are collinear. This case only happens on the boundary. return 0; // Return "not inside". @@ -5854,235 +6105,86 @@ REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// facenormal() Calculate the normal of the face. // // // -// 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. // +// 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. // // // -// 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. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) +void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, + REAL* lav) { - REAL sign; + REAL v1[3], v2[3], v3[3], *pv1, *pv2; + REAL L1, L2, L3; - inspherecount++; + 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]; - sign = insphere(pa, pb, pc, pd, pe); - if (sign != 0.0) { - return sign; + // 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). } - insphere_sos_count++; + // Calculate the face normal. + cross(pv1, pv2, n); + // Inverse the direction; + n[0] = -n[0]; + n[1] = -n[1]; + n[2] = -n[2]; +} - // 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; - - 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. // -// // -/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// 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) { @@ -6097,9 +6199,8 @@ 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; @@ -6108,7 +6209,6 @@ 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. // @@ -6132,6 +6232,27 @@ 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 // @@ -6161,9 +6282,8 @@ 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. @@ -6205,9 +6325,7 @@ 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; @@ -6249,7 +6367,6 @@ 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 // @@ -6371,6 +6488,7 @@ 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) { @@ -6559,7 +6677,59 @@ 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; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -6607,8 +6777,6 @@ void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, } } - - /////////////////////////////////////////////////////////////////////////////// // // // tetprismvol() Calculate the volume of a tetrahedral prism in 4D. // @@ -6656,44 +6824,145 @@ 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 ///////////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// 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. // +// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flippush(badface*& fstack, triface* flipface) +bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, + point *ppb, point *ppc) { - badface *newflipface; + point *ppt, pa, pb, pc; + REAL v1[3], v2[3], n[3]; + REAL lab, len, A, area; + REAL x, y, z; + int i; - if (!facemarked(*flipface)) { - newflipface = (badface *) flippool->alloc(); - newflipface->tt = *flipface; - markface(newflipface->tt); - // Push this face into stack. - newflipface->nextitem = fstack; - fstack = newflipface; - } -} + ppt = (point *) fastlookup(facpoints, 0); + pa = *ppt; // a is the first point. + pb = pc = NULL; // Avoid compiler warnings. -/////////////////////////////////////////////////////////////////////////////// -// // -// flip23() Perform a 2-to-3 flip (face-to-edge flip). // -// // -// '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. // + // 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; + } + 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. // // // // 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: // @@ -6704,27 +6973,19 @@ void tetgenmesh::flippush(badface*& fstack, triface* flipface) // rotate the three input tets counterclockwisely (right-hand rule) // // until a or b is in c's position. // // // -// 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]. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, - int chkencflag) +void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) { triface topcastets[3], botcastets[3]; triface newface, casface; - face checksh; - face checkseg; - badface *bface; // used by chkencflag point pa, pb, pc, pd, pe; - REAL volneg[2], volpos[3], vol_diff; // volumes of involved tet-prisms. + REAL attrib, volume; int dummyflag = 0; // range = {-1, 0, 1, 2}. int i; @@ -6752,16 +7013,12 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, } } - 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. @@ -6779,6 +7036,7 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, 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) { @@ -6803,6 +7061,15 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, } // 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. @@ -6836,7 +7103,8 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] * } - if (calc_tetprism_vol) { + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[2], volpos[3], vol_diff; if (pd != dummypoint) { if (pc != dummypoint) { volpos[0] = tetprismvol(pe, pd, pa, pb); @@ -6859,8 +7127,8 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, volneg[1] = tetprismvol(pb, pa, pc, pe); } vol_diff = volpos[0] + volpos[1] + volpos[2] - volneg[0] - volneg[1]; - tetprism_vol_sum += vol_diff; // Update the total sum. - } // if (check_tetprism_vol_diff) + fc->tetprism_vol_sum += vol_diff; // Update the total sum. + } // Bond three new tets together. for (i = 0; i < 3; i++) { @@ -6869,43 +7137,36 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, } // Bond to top outer boundary faces (at [a,b,c,d]). for (i = 0; i < 3; i++) { - enextesym(fliptets[i], newface); - eprevself(newface); // At edges [b,a], [c,b], [a,c]. + eorgoppo(fliptets[i], 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++) { - eprevesym(fliptets[i], newface); - enextself(newface); // At edges [a,b], [b,c], [c,a]. + edestoppo(fliptets[i], 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++) { - tsspivot1(topcastets[i], checkseg); - if (checkseg.sh != NULL) { - enextesym(fliptets[i], newface); - eprevself(newface); // At edges [b,a], [c,b], [a,c]. + for (i = 0; i < 3; i++) { + if (issubseg(topcastets[i])) { + tsspivot1(topcastets[i], checkseg); + eorgoppo(fliptets[i], newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - 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 (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } } // The top three: [d,a], [d,b], [d,c]. Two tets per edge. for (i = 0; i < 3; i++) { - eprev(topcastets[i], casface); - tsspivot1(casface, checkseg); - if (checkseg.sh != NULL) { + eprev(topcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); enext(fliptets[i], newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); @@ -6913,22 +7174,16 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, eprevself(newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - 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 (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } } // The bot three: [a,e], [b,e], [c,e]. Two tets per edge. for (i = 0; i < 3; i++) { enext(botcastets[i], casface); - tsspivot1(casface, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(casface)) { + tsspivot1(casface, checkseg); eprev(fliptets[i], newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); @@ -6936,75 +7191,53 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, enextself(newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - 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 (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } } - } + } // if (checksubsegflag) - // Bond 6 subfaces if there are. if (checksubfaceflag) { - 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]. + // 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); sesymself(checksh); tsbond(newface, 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 (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } } for (i = 0; i < 3; i++) { - tspivot(botcastets[i], checksh); - if (checksh.sh != NULL) { - eprevesym(fliptets[i], newface); - enextself(newface); // At edge [a,b], [b,c], [c,a] + if (issubface(botcastets[i])) { + tspivot(botcastets[i], checksh); + edestoppo(fliptets[i], newface); sesymself(checksh); tsbond(newface, 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 (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } } - } + } // if (checksubfaceflag) - if (chkencflag & 4) { + if (fc->chkencflag & 4) { // Put three new tets into check list. for (i = 0; i < 3; i++) { - if (!marktest2ed(fliptets[i])) { - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = fliptets[i]; - marktest2(bface->tt); - bface->forg = org(fliptets[i]); - } + enqueuetetrahedron(&(fliptets[i])); } } // Update the point-to-tet map. - 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])); + 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); if (hullflag > 0) { if (dummyflag != 0) { @@ -7037,19 +7270,15 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, } } - if (flipflag > 0) { + if (fc->enqflag > 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 (flipflag > 1) { - //pe = org(fliptets[0]); + if (fc->enqflag > 1) { for (i = 0; i < 3; i++) { enextesym(fliptets[i], newface); - //flippush(flipstack, &newface, pe); flippush(flipstack, &newface); } } @@ -7062,9 +7291,10 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, // // // flip32() Perform a 3-to-2 flip (edge-to-face flip). // // // -// '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]. // +// '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]. // // // // 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: // @@ -7075,35 +7305,36 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, // three old tets counterclockwisely (right-hand rule) until a or b // // is in c's position. // // // -// 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 '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 '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, int flipflag, - int chkencflag) +void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) { triface topcastets[3], botcastets[3]; triface newface, casface; - face checksh; + face flipshs[3]; face checkseg; - badface *bface; // used by chkencflag point pa, pb, pc, pd, pe; - REAL volneg[3], volpos[2], vol_diff; // volumes of involved tet-prisms. + REAL attrib, volume; int dummyflag = 0; // Rangle = {-1, 0, 1, 2} - int i; - - // For 2-to-2 flip (subfaces). - face flipshs[3], flipfaces[2]; - point rempt; - int spivot = -1, scount = 0; + int spivot = -1, scount = 0; // for flip22() + int t1ver; + int i, j; if (hullflag > 0) { // Check if e is 'dummypoint'. @@ -7143,36 +7374,24 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, 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++) { - enextesym(fliptets[i], casface); - eprevself(casface); + eorgoppo(fliptets[i], casface); fsym(casface, topcastets[i]); } for (i = 0; i < 3; i++) { - eprevesym(fliptets[i], casface); - enextself(casface); + edestoppo(fliptets[i], 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) { - 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]))); - } + // Found an interior subface. stdissolve(flipshs[i]); // Disconnect the sub-tet bond. scount++; } else { @@ -7208,7 +7427,28 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, 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); @@ -7232,14 +7472,15 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, esymself(fliptets[0]); // Adjust abec -> bace. esymself(fliptets[1]); - // The hullsize does not changle. + // The hullsize does not change. } } else { setvertices(fliptets[0], pa, pb, pc, pd); setvertices(fliptets[1], pb, pa, pc, pe); } - if (calc_tetprism_vol) { + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[3], volpos[2], vol_diff; if (pc != dummypoint) { if (pd != dummypoint) { volneg[0] = tetprismvol(pe, pd, pa, pb); @@ -7262,7 +7503,7 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, volpos[1] = 0.; } vol_diff = volpos[0] + volpos[1] - volneg[0] - volneg[1] - volneg[2]; - tetprism_vol_sum += vol_diff; // Update the total sum. + fc->tetprism_vol_sum += vol_diff; // Update the total sum. } // Bond abcd <==> bace. @@ -7281,133 +7522,91 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, } if (checksubsegflag) { - // Bond segments to new (flipped) tets. - for (i = 0; i < 3; i++) { - tsspivot1(topcastets[i], checkseg); - if (checkseg.sh != NULL) { + // 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); tssbond1(fliptets[0], checkseg); sstbond1(checkseg, fliptets[0]); - 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. - } + tssbond1(fliptets[1], checkseg); + sstbond1(checkseg, fliptets[1]); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } enextself(fliptets[0]); + eprevself(fliptets[1]); } // The three top edges. - for (i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) { // edges b->d, c->d, a->d. esym(fliptets[0], newface); - eprevself(newface); // edge b->d, c->d, a->d. - enext(topcastets[i], casface); - tsspivot1(casface, checkseg); - if (checkseg.sh != NULL) { + eprevself(newface); + enext(topcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - 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 (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } 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++) { + for (i = 0; i < 3; i++) { // edges b<-e, c<-e, a<-e. esym(fliptets[1], newface); - enextself(newface); // edge b<-e, c<-e, a<-e. + enextself(newface); eprev(botcastets[i], casface); - tsspivot1(casface, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(casface)) { + tsspivot1(casface, checkseg); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - 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 (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } eprevself(fliptets[1]); } - } + } // if (checksubsegflag) if (checksubfaceflag) { + face checksh; // Bond the top three casing subfaces. - 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] + 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); sesymself(checksh); tsbond(newface, 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 (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } enextself(fliptets[0]); } // Bond the bottom three casing subfaces. - 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] + 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); sesymself(checksh); tsbond(newface, 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 (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } 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, chkencflag); + flip22(flipfaces, 0, fc->chkencflag); // Connect the flipped subfaces to flipped tets. // First go to the corresponding flipping edge. // Re-use top- and botcastets[0]. @@ -7435,9 +7634,6 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, 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); @@ -7494,9 +7690,6 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, // 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); @@ -7530,26 +7723,21 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, // // 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 (chkencflag & 4) { + if (fc->chkencflag & 4) { // Put two new tets into check list. for (i = 0; i < 2; i++) { - if (!marktest2ed(fliptets[i])) { - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = fliptets[i]; - marktest2(bface->tt); - bface->forg = org(fliptets[i]); - } + enqueuetetrahedron(&(fliptets[i])); } } - 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])); + 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); if (hullflag > 0) { if (dummyflag != 0) { @@ -7572,14 +7760,14 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, } } - if (flipflag > 0) { + if (fc->enqflag > 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 (flipflag > 1) { + if (fc->enqflag > 1) { //pb = dest(fliptets[0]); eprevesym(fliptets[0], newface); flippush(flipstack, &newface); @@ -7605,28 +7793,28 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, // 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 four vertices may be 'duumypoint'. // -// The 'hullsize' may be changed. // +// 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 '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, int flipflag, - int chkencflag) +void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) { 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]); @@ -7635,11 +7823,7 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, pd = dest(fliptets[0]); pp = org(fliptets[0]); // The removing vertex. - 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++; + flip41count++; // Get the outer boundary faces. for (i = 0; i < 3; i++) { @@ -7652,8 +7836,6 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, 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]); @@ -7683,9 +7865,11 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, } } // 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) { @@ -7700,19 +7884,20 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, fliptets[0].tet[9] = NULL; } } - // Delete the other three tets. for (i = 1; i < 4; i++) { tetrahedrondealloc(fliptets[i].tet); } - // Mark the point pp as unused. - setpointtype(pp, UNUSEDVERTEX); - unuverts++; + if (pp != dummypoint) { + // Mark the point pp as unused. + setpointtype(pp, UNUSEDVERTEX); + unuverts++; + } // Create the new tet [a,b,c,d]. if (hullflag > 0) { - // One of the four vertices may be 'dummypoint'. + // One of the five vertices may be 'dummypoint'. if (pa == dummypoint) { // pa is dummypoint. setvertices(fliptets[0], pc, pb, pd, pa); @@ -7733,17 +7918,26 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, dummyflag = 4; } else { setvertices(fliptets[0], pa, pb, pc, pd); - dummyflag = 0; + if (pp == dummypoint) { + dummyflag = -1; + } else { + dummyflag = 0; + } } if (dummyflag > 0) { - // We delete 3 hull tets, and create 1 hull tet. + // We deleted 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 (calc_tetprism_vol) { + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[4], volpos[1], vol_diff; if (dummyflag > 0) { if (pa == dummypoint) { volneg[0] = 0.; @@ -7767,6 +7961,12 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, 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); @@ -7775,7 +7975,7 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, volpos[0] = tetprismvol(pa, pb, pc, pd); } vol_diff = volpos[0] - volneg[0] - volneg[1] - volneg[2] - volneg[3]; - tetprism_vol_sum += vol_diff; // Update the total sum. + fc->tetprism_vol_sum += vol_diff; // Update the total sum. } // Bond the new tet to adjacent tets. @@ -7787,40 +7987,29 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, 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]. - tsspivot1(newface, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(newface)) { + tsspivot1(newface, checkseg); esym(fliptets[0], newface); enextself(newface); // At edges [a,d], [b,d], [c,d]. tssbond1(newface, checkseg); sstbond1(checkseg, newface); - 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 (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } enextself(fliptets[0]); } for (i = 0; i < 3; i++) { - tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a]. - if (checkseg.sh != NULL) { + if (issubseg(topcastets[i])) { + tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a]. tssbond1(fliptets[0], checkseg); sstbond1(checkseg, fliptets[0]); - 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 (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } enextself(fliptets[0]); @@ -7828,40 +8017,29 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, } if (checksubfaceflag) { + face checksh; // Bond 4 subfaces (at faces of [a,b,c,d]) if there are. for (i = 0; i < 3; i++) { - tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d] - if (checksh.sh != NULL) { + if (issubface(topcastets[i])) { + tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d] esym(fliptets[0], newface); // At faces [b,a,d],[c,b,d],[a,c,d] sesymself(checksh); tsbond(newface, 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 (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } enextself(fliptets[0]); } - tspivot(botcastet, checksh); // At face [b,a,c] - if (checksh.sh != NULL) { + if (issubface(botcastet)) { + tspivot(botcastet, checksh); // At face [b,a,c] sesymself(checksh); tsbond(fliptets[0], 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 (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } - } - if (checksubfaceflag) { if (spivot >= 0) { // Perform a 3-to-1 flip in surface triangulation. // Depending on the value of 'spivot', the three subfaces are: @@ -7895,30 +8073,26 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, } // if (spivot > 0) } // if (checksubfaceflag) - 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]); - } + if (fc->chkencflag & 4) { + enqueuetetrahedron(&(fliptets[0])); } // Update the point-to-tet map. - setpoint2tet(pa, encode(fliptets[0])); - setpoint2tet(pb, encode(fliptets[0])); - setpoint2tet(pc, encode(fliptets[0])); - setpoint2tet(pd, encode(fliptets[0])); + setpoint2tet(pa, (tetrahedron) fliptets[0].tet); + setpoint2tet(pb, (tetrahedron) fliptets[0].tet); + setpoint2tet(pc, (tetrahedron) fliptets[0].tet); + setpoint2tet(pd, (tetrahedron) fliptets[0].tet); - if (flipflag > 0) { + if (fc->enqflag > 0) { // Queue faces which may be locally non-Delaunay. - for (i = 0; i < 3; i++) { - esym(fliptets[0], newface); - flippush(flipstack, &newface); - enextself(fliptets[0]); + 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]); + } } - flippush(flipstack, &(fliptets[0])); } recenttet = fliptets[0]; @@ -7926,7 +8100,7 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, /////////////////////////////////////////////////////////////////////////////// // // -// flipnm() Try to flip an edge through a sequence of elementary flips. // +// flipnm() 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.,// @@ -7951,7 +8125,6 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, // - Neither a nor b is 'dummypoint'. // // - [a,b] must not be a segment. // // // -// // /////////////////////////////////////////////////////////////////////////////// int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, @@ -7959,27 +8132,19 @@ 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; - point tmppts[3]; - REAL abovept[3]; - REAL ori, ori1, ori2; - int reducflag, rejflag; + REAL ori; int hullflag; + int reducflag, rejflag; 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; @@ -7987,9 +8152,7 @@ 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) { - // Do not flip this face if it is a constraining face. - tspivot(abtets[i], checksh); - if (checksh.sh != NULL) { + if (issubface(abtets[i])) { continue; // Skip a subface. } } @@ -7998,30 +8161,28 @@ 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)) { - // [a,b,c] is a hull face, it is not flipable. - continue; + 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. - 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; + ori = orient3d(pa, pb, pc, pd); + if (ori < 0) { + ori = orient3d(pb, pa, pc, pe); + } + if (ori >= 0) { + continue; // An invalid tet. } } // if (checkinverttetflag) - reducflag = 0; // Not reducible. + // Decide whether [a,b,c] is flippable or not. + reducflag = 0; hullflag = (pc == dummypoint); // pc may be dummypoint. if (hullflag == 0) { @@ -8072,7 +8233,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 != NULL) { + if (fc->checkflipeligibility) { // Check if the flip can be performed. rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, level, abedgepivot, fc); @@ -8081,7 +8242,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, 0, 0); + flip23(fliptets, hullflag, fc); // Shrink the array 'abtets', maintain the original order. // Two tets 'abtets[i-1] ([a,b,e,c])' and 'abtets[i] ([a,b,c,d])' @@ -8099,11 +8260,9 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // [n-2] |___________| [n-2] |___________| // [n-1] |___________| [n-1] |_[i]_2-t-3_| // - eprevself(fliptets[0]); - esymself(fliptets[0]); - enextself(fliptets[0]); // [a,b,e,d] + edestoppoself(fliptets[0]); // [a,b,e,d] // Increase the counter of this new tet (it is in Star(ab)). - increaseelemcounter(fliptets[0]); //marktest(fliptets[0]); + increaseelemcounter(fliptets[0]); abtets[(i - 1 + n) % n] = fliptets[0]; for (j = i; j < n - 1; j++) { abtets[j] = abtets[j + 1]; // Upshift @@ -8134,7 +8293,10 @@ 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) { + if (nn == 2) { + // The edge has been flipped. + return nn; + } else { // 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 @@ -8144,17 +8306,15 @@ 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] - eprevself(fliptets[0]); - esymself(fliptets[0]); - enextself(fliptets[0]); // [e,d,a,b] + edestoppoself(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, 0, 0); + flip32(fliptets, hullflag, fc); // Marktest the two restored tets in Star(ab). for (j = 0; j < 2; j++) { - increaseelemcounter(fliptets[j]); //marktest(fliptets[j]); + increaseelemcounter(fliptets[j]); } // Expand the array 'abtets', maintain the original order. for (j = n - 2; j>= i; j--) { @@ -8173,27 +8333,14 @@ 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 size (# of tets). + // further reduced. Return its current 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 @@ -8202,16 +8349,6 @@ 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. @@ -8221,13 +8358,14 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } pc = apex(abtets[i]); if (pc == dummypoint) { - continue; // [a,b,dummypoint] is a hull edge. + continue; // [a,b] 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. @@ -8265,21 +8403,15 @@ 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. - 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; - } + 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; } } continue; @@ -8294,7 +8426,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, spintet = flipedge; while (1) { n1++; - j += (elemcounter(spintet)); //if (marktested(spintet)) j++; + j += (elemcounter(spintet)); fnextself(spintet); if (spintet.tet == flipedge.tet) break; } @@ -8306,14 +8438,8 @@ 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 (-LL__). - skpflipstarcount++; + // The star size exceeds the given limit. continue; // Do not flip it. } @@ -8330,15 +8456,6 @@ 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); @@ -8359,10 +8476,8 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, esymself(spintet); eprevself(spintet); // [a,b,e,d] } // edgepivot == 2 - //assert(!marktested(spintet)); // It's a new tet. - assert(elemcounter(spintet) == 0); - //marktest(spintet); // It is in Star(ab). - increaseelemcounter(spintet); + assert(elemcounter(spintet) == 0); // It's a new tet. + increaseelemcounter(spintet); // It is in Star(ab). // Put the new tet at [i-1]-th entry. abtets[(i - 1 + n) % n] = spintet; for (j = i; j < n - 1; j++) { @@ -8389,7 +8504,10 @@ 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) { + if (nn == 2) { + // The edge has been flipped. + return nn; + } else { // if (nn > 2) { // The edge is not flipped. if (fc->unflip) { // Recover the flipped edge ([c,b] or [a,c]). @@ -8455,10 +8573,6 @@ 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). @@ -8484,70 +8598,56 @@ 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. - pc = apex(abtets[0]); - pd = apex(abtets[1]); - pe = apex(abtets[2]); - - // 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. + // 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; - if (pc == dummypoint) { - hullflag = 1; - tmppts[0] = pd; - tmppts[1] = pe; - tmppts[2] = pc; - } else if (pd == dummypoint) { + + if (apex(abtets[0]) == dummypoint) { + pc = apex(abtets[1]); + pd = apex(abtets[2]); + pe = apex(abtets[0]); hullflag = 1; - tmppts[0] = pe; - tmppts[1] = pc; - tmppts[2] = pd; - } else if (pe == dummypoint) { + } else if (apex(abtets[1]) == dummypoint) { + pc = apex(abtets[2]); + pd = apex(abtets[0]); + pe = apex(abtets[1]); hullflag = 1; - tmppts[0] = pc; - tmppts[1] = pd; - tmppts[2] = pe; } else { - tmppts[0] = pc; - tmppts[1] = pd; - tmppts[2] = pe; - } + pc = apex(abtets[0]); + pd = apex(abtets[1]); + pe = apex(abtets[2]); + hullflag = (pe == dummypoint); + } reducflag = 0; rejflag = 0; if (checkinverttetflag) { - // Only do flip if no tet is inverted (or degenerated). + // 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. 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, 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); + ori = orient3d(pa, pb, pc, pd); + if (ori > 0) { + return 3; // [a,b,c,d] is inverted. } - return 3; } } // if (checkinverttetflag) @@ -8560,97 +8660,29 @@ 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. Moreover, the tet [a,b,p0,p1] is a hull tet - // ([a,b,p0] and [a,b,p1] are two hull faces). + // [a,b] is a hull edge. // 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,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++; - //} - } + // [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; } } // if (hullflag == 1) @@ -8663,8 +8695,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // 3-to-2 flip. nn = 0; for (j = 0; j < 3; j++) { - tspivot(abtets[j], checksh); - if (checksh.sh != NULL) { + if (issubface(abtets[j])) { nn++; // Found a subface. } } @@ -8674,34 +8705,38 @@ 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 != 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, + if (!rejflag && fc->checkflipeligibility) { + // Here we must exchange '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, tmppts[0], tmppts[1], tmppts[2], - pb, pa, level, abedgepivot, fc); + rejflag = checkflipeligibility(2, pc, pd, pe, pb, pa, level, + abedgepivot, fc); } if (!rejflag) { // Do flip: [a,b] => [c,d,e] - flip32(abtets, hullflag, 0, 0); - sucflipstarcount++; + flip32(abtets, hullflag, fc); if (fc->remove_ndelaunay_edge) { if (level == 0) { - // 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); + // 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); // Increase the element counter -- They are in cavity. for (j = 0; j < 3; j++) { increaseelemcounter(abtets[j]); @@ -8731,15 +8766,7 @@ 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) @@ -8794,7 +8821,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, 0, 0); + flip23(abtets, 1, fc); if (fc->collectnewtets) { // Pop up new (flipped) tets from the stack. if (abedgepivot == 0) { @@ -8808,9 +8835,7 @@ 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++) { @@ -8836,7 +8861,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, 0, 0); + flip32(fliptets, 1, fc); // Expand the array 'abtets', maintain the original order. // The new array length is (i+1). for (j = i - 1; j >= t; j--) { @@ -8923,9 +8948,6 @@ 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 @@ -8934,916 +8956,178 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, /////////////////////////////////////////////////////////////////////////////// // // -// 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. // -// // -// 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. // +// insertpoint() Insert a point into current tetrahedralization. // // // -// 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. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::lawsonflip3d(point newpt, int flipflag, int peelsliverflag, - int chkencflag, int flipedgeflag) +int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, + face *splitseg, insertvertexflags *ivf) { - badface *popface, *bface; - triface fliptets[5], baktets[2]; - triface fliptet, neightet, *parytet; - face checksh, *parysh; + arraypool *swaplist; + triface *cavetet, spintet, neightet, neineitet, *parytet; + triface oldtet, newtet, newneitet; + face checksh, neighsh, *parysh; face checkseg, *paryseg; - point *ppt, pd, pe, pf; - long flipcount; + point *pts, pa, pb, pc, *parypt; + enum locateresult loc = OUTSIDE; REAL sign, ori; - 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; + REAL attrib, volume; + bool enqflag; + int t1ver; + int i, j, k, s; if (b->verbose > 2) { - printf(" Lawson flip %ld faces.\n", flippool->items); + printf(" Insert point %d\n", pointmark(insertpt)); } - flipcount = flip23count + flip32count + flip44count; - tetpeelcount = opt_sliver_peels; - - 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. + // Locate the point. + if (searchtet->tet != NULL) { + loc = (enum locateresult) ivf->iloc; } - while (1) { - - while (flipstack != (badface *) NULL) { - - // Pop a face from the stack. - popface = flipstack; - flipstack = flipstack->nextitem; // The next top item in stack. - fliptet = popface->tt; - flippool->dealloc((void *) popface); - - // 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 (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); + } - unmarkface(fliptet); + ivf->iloc = (int) loc; // The return value. - // FOR DEBUG - if (flipflag == 1) { - assert(oppo(fliptet) == newpt); + 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; } + } + } - 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) + // 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. - // 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 (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; - if (peelsliverflag) { - continue; // Only check hull tets. + if (ivf->splitbdflag) { + if (splitsh != NULL) { + // Create the initial sub-cavity sC(p). + smarktest(*splitsh); + caveshlist->newindex((void **) &parysh); + *parysh = *splitsh; } + } // 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) - // 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; - } - } - } // if (flipflag == 1) - - continue; // Do not flip a hull face. - } // if (ishulltet(neightet)) - - if (ishulltet(fliptet)) { - continue; // Do not flip a hull tet. + 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 ((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); + 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); @@ -9862,9 +9146,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } } // 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); @@ -9876,140 +9157,53 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, decode(cavetet->tet[j], neightet); if (!infected(neightet)) { // It's a boundary face. - neightet.ver = epivot[neightet.ver & 3]; - cavetetlist->newindex((void **) &parytet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->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 (int) loc; + return 0; } else if (loc == ENCSUBFACE) { - 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. + // Treat it as outside. + ivf->iloc = (int) OUTSIDE; + return 0; } if (ivf->assignmeshsize) { // Assign mesh size for the new point. - if (bgm != NULL) { - // Interpolate the mesh size from the background mesh. - 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] = // posflag = 1 - bgm->getpointmeshsize(insertpt, &neightet, bgmloc, 1); - setpoint2bgmtet(insertpt, bgm->encode(neightet)); - } - } else { - insertpt[pointmtrindex] = // posflag = 1 - getpointmeshsize(insertpt, searchtet, (int) loc, 1); - } - } // if (assignmeshsize) - - 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 = (triface *) fastlookup(cavetetlist, i); - // Other expansions may make this face inside C(p). - if (!infected(*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). + 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); + if (bgmloc != (int) OUTSIDE) { + insertpt[pointmtrindex] = + bgm->getpointmeshsize(insertpt, &neightet, bgmloc); + setpoint2bgmtet(insertpt, bgm->encode(neightet)); + } + } else { + insertpt[pointmtrindex] = getpointmeshsize(insertpt,searchtet,(int)loc); + } + } // if (assignmeshsize) - 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) { + if (ivf->bowywat) { + // Update the cavity C(p) using the Bowyer-Watson algorithm. + swaplist = cavetetlist; + cavetetlist = cavebdrylist; + cavebdrylist = swaplist; + 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. + enqflag = false; + if (!marktested(*cavetet)) { // Do Delaunay (in-sphere) test. pts = (point *) cavetet->tet; if (pts[7] != dummypoint) { @@ -10026,20 +9220,17 @@ int tetgenmesh::insertvertex(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. - neightet = *cavetet; - neightet.ver = 3; // The face opposite to dummypoint. - fsym(neightet, neineitet); + decode(cavetet->tet[3], neineitet); if (!infected(neineitet)) { if (!marktested(neineitet)) { // Do Delaunay test on this tet. @@ -10053,12 +9244,7 @@ int tetgenmesh::insertvertex(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. @@ -10067,15 +9253,11 @@ int tetgenmesh::insertvertex(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'. - neightet = *cavetet; - neightet.ver = 3; // The face opposite to dummypoint. - fsym(neightet, neineitet); + decode(cavetet->tet[3], neineitet); if (!infected(neineitet)) { if (!marktested(neineitet)) { // Do Delaunay test on this tet. @@ -10089,12 +9271,7 @@ int tetgenmesh::insertvertex(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. @@ -10102,200 +9279,124 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } // if (infected(neineitet)) } // if (nonconvex) } // if (pts[7] != dummypoint) - } // 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 ((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 + marktest(*cavetet); // Only test it once. + } // if (!marktested(*cavetet)) - if (b->verbose > 3) { - printf(" Initial cavity size: %ld tets, %ld faces.\n", - caveoldtetlist->objects, cavebdrylist->objects); - } + 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 (!infected(*cavetet)) + } // i + 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); - 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; + 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; + } } - } + } // j } - } + } // i // Uninfect collected segments. for (i = 0; i < cavetetseglist->objects; i++) { - checkseg = * (face *) fastlookup(cavetetseglist, i); - suninfect(checkseg); + 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; + } } } // if (checksubsegflag) if (checksubfaceflag) { // Collect all subfaces of C(p). + shellface *sptr; for (i = 0; i < caveoldtetlist->objects; i++) { cavetet = (triface *) fastlookup(caveoldtetlist, i); - 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; + 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; + } } - } + } // j } - } + } // i // Uninfect collected subfaces. for (i = 0; i < cavetetshlist->objects; i++) { - checksh = * (face *) fastlookup(cavetetshlist, i); - suninfect(checksh); - } - } // if (checksubfaceflag) - - 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; + parysh = (face *) fastlookup(cavetetshlist, i); + suninfect(*parysh); } - } // 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)); + 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; } - 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. } - 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(); + if (encshlist->objects > 0) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) ENCSUBFACE; + return 0; } - // Restore the hullsize. - hullsize = bakhullsize; - return (int) ENCSUBFACE; } - } // if (reject & 2) + } // if (checksubfaceflag) - if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { - // Update the sC(p). + if (ivf->splitbdflag) { + // The new point locates in surface mesh. 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. @@ -10304,16 +9405,15 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, assert(smarktested(*parysh)); checksh = *parysh; for (j = 0; j < 3; j++) { - sspivot(checksh, checkseg); - if (checkseg.sh == NULL) { + if (!isshsubseg(checksh)) { 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; @@ -10324,48 +9424,34 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, senextself(checksh); } // j } // i - if (b->verbose > 3) { - printf(" Initial subcavity size: %ld subfacess.\n", - caveshlist->objects); - } - } - - cutcount = 0l; + } // if (ivf->splitbdflag) - 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 (ivf->validflag) { + // Validate C(p) and update it if it is not star-shaped. + int cutcount = 0; - //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). - // It is needed by 'splitbdflag'. + // 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). 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) { - pa = org(neightet); - pb = dest(neightet); - pc = apex(neightet); - ori = orient3d(pa, pb, pc, insertpt); + ori = orient3d(org(neightet), dest(neightet), apex(neightet), + insertpt); if (ori < 0) { // A visible face, get its neighbor face. fsymself(neightet); @@ -10377,27 +9463,20 @@ int tetgenmesh::insertvertex(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 & 3]; + neightet.ver = epivot[neightet.ver]; 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 & 3]; + neineitet.ver = epivot[neineitet.ver]; cavebdrylist->newindex((void **) &parytet); *parytet = neineitet; enextself(neightet); } - // Update hullsize. - if (oppo(neightet) == dummypoint) hullsize++; } // if (ori >= 0) } } @@ -10419,10 +9498,6 @@ int tetgenmesh::insertvertex(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); @@ -10447,7 +9522,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } } } - } else { } fnextself(spintet); if (spintet.tet == neightet.tet) break; @@ -10465,42 +9539,34 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, uninfect(neightet); unmarktest(neightet); cutcount++; - neightet.ver = epivot[neightet.ver & 3]; + neightet.ver = epivot[neightet.ver]; 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 & 3]; + neineitet.ver = epivot[neineitet.ver]; cavebdrylist->newindex((void **) &parytet); *parytet = neineitet; enextself(neightet); } - // Update hullsize. - //if (oppo(neightet) == dummypoint) hullsize++; - if ((point) (neightet.tet[7]) == dummypoint) hullsize++; } } } // i - } // if (bowywat > 2) + } // if (ivf->respectbdflag) // 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. - assert(cavetet->ver == epivot[cavetet->ver & 3]); // SELF_CHECK - // It must be not inside the cavity (since we only cut tets). - assert(!infected(*cavetet)); + // 'cavetet' is an exterior tet adjacent to the cavity. // 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) { - pa = org(*cavetet); - pb = dest(*cavetet); - pc = apex(*cavetet); - ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; + ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet), + insertpt); enqflag = (ori > 0); // Comment: if ori == 0 (coplanar case), we also cut the tet. } else { @@ -10517,24 +9583,17 @@ int tetgenmesh::insertvertex(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 & 3]; + neineitet.ver = epivot[neineitet.ver]; 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); } @@ -10546,14 +9605,11 @@ int tetgenmesh::insertvertex(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. @@ -10581,12 +9637,13 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // The cavity should contain at least one tet. if (caveoldtetlist->objects == 0l) { - printf("Invalid cavity of Steiner point %d.\n", pointmark(insertpt)); - assert(0); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; } - if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { - cutshcount = 0; + if (ivf->splitbdflag) { + int cutshcount = 0; // Update the sub-cavity sC(p). for (i = 0; i < caveshlist->objects; i++) { parysh = (face *) fastlookup(caveshlist, i); @@ -10600,11 +9657,6 @@ int tetgenmesh::insertvertex(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; @@ -10652,127 +9704,140 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, if (i > 0) { // The updated sC(p) is invalid. Do not insert this vertex. - 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; + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; } } // if (cutshcount > 0) - } // if (bowywat > 2) - + } // if (ivf->splitbdflag) } // if (cutcount > 0) - } // 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->validflag) 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. - 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 (((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]; } - // Restore the original status. - for (i = 0; i < caveoldtetlist->objects; i++) { - cavetet = (triface *) fastlookup(caveoldtetlist, i); - uninfect(*cavetet); - unmarktest(*cavetet); + } 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]; } - for (i = 0; i < cavebdrylist->objects; i++) { - cavetet = (triface *) fastlookup(cavebdrylist, i); - unmarktest(*cavetet); // Unmark it. + if (pts[3] != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[3]; } - // 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); + fsym(*searchtet, spintet); + if (oppo(spintet) != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = oppo(spintet); + } + } 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); } - caveshlist->restart(); - cavesegshlist->restart(); + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; } + } - // Restore the hullsize. - hullsize = bakhullsize; - loc = BADELEMENT; - return (int) loc; - } // if (rejptflag) - } // if (ivf->refineflag) + 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; + } + } + } + cavetetvertlist->restart(); // Clear the work list. - rejptflag = (ivf->rejflag & 4); - encptflag = 0; + 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)) - if (b->weighted || b->plc || rejptflag) { + if (b->weighted || ivf->cdtflag + ) { + // There may be other vertices inside C(p). We need to find them. // 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) { @@ -10784,118 +9849,33 @@ int tetgenmesh::insertvertex(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(); - } + } - // 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; + 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; } // Before re-mesh C(p). Process the segments and subfaces which are on the @@ -10917,7 +9897,7 @@ int tetgenmesh::insertvertex(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. } @@ -10938,12 +9918,8 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, sstbond1(*paryseg, neineitet); } else { // k == j // The segment is inside C(p). - if (!ivf->splitbdflag) {//if (bowywat < 3) { // if (bowywat == 2) { + if (!ivf->splitbdflag) { 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; @@ -10958,10 +9934,6 @@ int tetgenmesh::insertvertex(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) { @@ -10991,13 +9963,8 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // This side is the outer boundary of C(p). *parysh = checksh; } else { // k == 2 - if (!ivf->splitbdflag) { //if (bowywat < 3) { // if (bowywat == 2) { + if (!ivf->splitbdflag) { 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; @@ -11012,40 +9979,26 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, sinfect(*parysh); } } // i - if (b->verbose > 3) { - printf(" %ld (%ld) cavity (interior) subfaces.\n", - cavetetshlist->objects, caveencshlist->objects); - } - } // if (checksubfaceflag) { + } // 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 (see Fig. bowyerwatson 1 or 3). + // Create a new tet in the cavity. 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 (see Fig. bowyerwatson 2). - hullsize++; + // Create a new hull tet. + hullsize++; maketetrahedron(&newtet); setorg(newtet, org(neightet)); setdest(newtet, dest(neightet)); @@ -11054,6 +10007,15 @@ int tetgenmesh::insertvertex(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. @@ -11062,12 +10024,11 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // Set a handle for speeding point location. recenttet = newtet; - setpoint2tet(insertpt, encode(newtet)); + //setpoint2tet(insertpt, encode(newtet)); + setpoint2tet(insertpt, (tetrahedron) (newtet.tet)); - if (ivf->lawson > 1) { // if (lawson == 2 || lawson == 3) { - // Re-use this list to save new interior cavity faces. - cavetetlist->restart(); - } + // Re-use this list to save new interior cavity faces. + cavetetlist->restart(); // Connect adjacent new tetrahedra together. for (i = 0; i < cavebdrylist->objects; i++) { @@ -11089,16 +10050,15 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } fsym(spintet, newneitet); esymself(newneitet); - assert(newneitet.tet[newneitet.ver & 3] == NULL); // FOR DEBUG + assert(newneitet.tet[newneitet.ver & 3] == NULL); bond(neightet, newneitet); - if (ivf->lawson > 1) { - // We are updateing a CDT. Queue the internal face. - // See also fig/dump-cavity-case13, -case21. + if (ivf->lawson > 1) { cavetetlist->newindex((void **) &parytet); *parytet = neightet; } } - setpoint2tet(org(newtet), encode(newtet)); + //setpoint2tet(org(newtet), encode(newtet)); + setpoint2tet(org(newtet), (tetrahedron) (newtet.tet)); enextself(newtet); enextself(oldtet); } @@ -11142,7 +10102,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } if (checksubfaceflag) { - if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { + if (ivf->splitbdflag) { // Recover new subfaces in C(p). for (i = 0; i < caveshbdlist->objects; i++) { // Get an old subface at edge [a, b]. @@ -11187,7 +10147,6 @@ int tetgenmesh::insertvertex(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++) { @@ -11196,12 +10155,6 @@ int tetgenmesh::insertvertex(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; } @@ -11213,11 +10166,6 @@ int tetgenmesh::insertvertex(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. @@ -11229,7 +10177,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } // if (checksubfaceflag) if (checksubsegflag) { - if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { + if (ivf->splitbdflag) { if (splitseg != NULL) { // Recover the two new subsegments in C(p). for (i = 0; i < cavesegshlist->objects; i++) { @@ -11244,11 +10192,9 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, stpivot(checksh, neightet); } else { // It's a dangling segment. - pa = sorg(checkseg); - pb = sdest(checkseg); - point2tetorg(pa, neightet); - finddirection(&neightet, pb, 1); - assert(dest(neightet) == pb); + point2tetorg(sorg(checkseg), neightet); + finddirection(&neightet, sdest(checkseg)); + assert(dest(neightet) == sdest(checkseg)); } assert(!infected(neightet)); sstbond1(checkseg, neightet); @@ -11263,17 +10209,12 @@ int tetgenmesh::insertvertex(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); @@ -11287,10 +10228,6 @@ int tetgenmesh::insertvertex(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. @@ -11304,12 +10241,10 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } } // if (checksubsegflag) - if (b->plc || b->weighted) { + if (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++) { @@ -11318,28 +10253,15 @@ int tetgenmesh::insertvertex(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 > 2) { - printf(" Point #%d is removed from the hull.\n", - pointmark(*pts)); - } - setpointtype(*pts, UNUSEDVERTEX); - } else { - if (b->verbose > 3) { - printf(" Queue a dangling vertex %d.\n", pointmark(*pts)); + if (b->verbose > 1) { + printf(" Point #%d is non-regular after the insertion of #%d.\n", + pointmark(*pts), pointmark(insertpt)); } - subvertstack->newindex((void **) &parypt); - *parypt = *pts; + setpointtype(*pts, NREGULARVERTEX); + nonregularcount++; } } } - 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) { @@ -11348,23 +10270,14 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, paryseg = (face *) fastlookup(cavetetseglist, i); // Skip if it is the split segment. if (!sinfected(*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. - } + enqueuesubface(badsubsegs, paryseg); } } if (splitseg != NULL) { // Queue the two new subsegments inside C(p). for (i = 0; i < cavesegshlist->objects; i++) { paryseg = (face *) fastlookup(cavesegshlist, i); - bface = (badface *) badsubsegs->alloc(); - bface->ss = *paryseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(*paryseg); // An alive badface. + enqueuesubface(badsubsegs, paryseg); } } } // if (chkencflag & 1) @@ -11375,15 +10288,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, parysh = (face *) fastlookup(cavetetshlist, i); // Skip if it is a split subface. if (!sinfected(*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); - } + enqueuesubface(badsubfacs, parysh); } } // Queue all new subfaces inside C(p). @@ -11393,11 +10298,7 @@ int tetgenmesh::insertvertex(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) { - //assert(!smarktest2ed(checksh)); - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface. + enqueuesubface(badsubfacs, &checksh); } } } // if (chkencflag & 2) @@ -11406,19 +10307,18 @@ int tetgenmesh::insertvertex(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); - //assert(!marktest2ed(*cavetet)); - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = *cavetet; - marktest2(bface->tt); - bface->forg = org(*cavetet); + enqueuetetrahedron(cavetet); } } - // C(p) is re-meshed successfully. + // C(p) is re-meshed successfully. - // Deleted the old tets in C(p). + // Delete 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); } @@ -11444,7 +10344,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } shellfacedealloc(subfaces, parysh->sh); } - if (splitseg != NULL) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { // Delete the old segment in sC(p). shellfacedealloc(subsegs, splitseg->sh); } @@ -11453,28 +10353,25 @@ int tetgenmesh::insertvertex(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); } } } - // 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); + // 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); + } } // Clean the working lists. @@ -11493,7 +10390,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, caveencshlist->restart(); } - if (b->plc || b->weighted) { + if (b->weighted || ivf->validflag) { cavetetvertlist->restart(); } @@ -11503,7 +10400,49 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, cavesegshlist->restart(); } - return (int) loc; + 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); + } + caveshlist->restart(); + cavesegshlist->restart(); + } } //// //// @@ -11518,10 +10457,10 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // // // transfernodes() Read the vertices from the input (tetgenio). // // // -// 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. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -11534,9 +10473,6 @@ void tetgenmesh::transfernodes() int mtrindex; int i, j; - if (b->psc) { - assert(in->pointparamlist != NULL); - } // Read the points. coordindex = 0; @@ -11547,30 +10483,32 @@ 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++]; + 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++]; + } if (b->weighted) { // -w option if (in->numberofpointattributes > 0) { - // The first point attribute is weight. - w = in->pointattributelist[in->numberofpointattributes * i]; + // The first point attribute is its weight. + //w = in->pointattributelist[in->numberofpointattributes * i]; + w = pointloop[3]; } else { - // No given weight available. - w = 0; + // 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->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) { @@ -11620,293 +10558,256 @@ void tetgenmesh::transfernodes() /////////////////////////////////////////////////////////////////////////////// // // -// btree_sort() Sort vertices using a binary space partition (bsp) tree. // +// 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::btree_sort(point* vertexarray, int arraysize, int axis, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, - REAL bzmin, REAL bzmax, int depth) +void tetgenmesh::hilbert_init(int n) { - point *leftarray, *rightarray; - point **pptary, swapvert; - REAL split; - bool lflag, rflag; - int i, j, k; // *iptr, + int gc[8], N, mask, travel_bit; + int e, d, f, k, g; + int v, c; + int i; - 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")); + 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. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::hilbert_split(point* vertexarray,int arraysize,int gc0,int gc1, + REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, + REAL bzmin, REAL bzmax) +{ + point swapvert; + int axis, d; + REAL split; + int i, j; - if (depth > max_btree_depth) { - max_btree_depth = depth; - } + // 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; + + // 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 { - // Split along z-axis. + } else { // == 2 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. - 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); + 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; } - } 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); + for (; j >= 0; j--) { + if (vertexarray[j][axis] > split) break; } - } 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; + // 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); } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// btree_insert() Add a vertex into a tree node. // -// // -/////////////////////////////////////////////////////////////////////////////// -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); + return i; } -/////////////////////////////////////////////////////////////////////////////// -// // -// btree_search() Search a near point for an inserting point. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::btree_search(point insertpt, triface* searchtet) +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) { - 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++; + 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]; } - } - - // 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; + 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); } - } - - } 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); + if (transgc[e][d][w] & 2) { // y-axis + y1 = 0.5 * (bymin + bymax); + y2 = bymax; + } else { + y1 = bymin; + y2 = 0.5 * (bymin + bymax); + } + if (transgc[e][d][w] & 4) { // z-axis + z1 = 0.5 * (bzmin + bzmax); + z2 = bzmax; + } else { + z1 = bzmin; + z2 = 0.5 * (bzmin + bzmax); + } + 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 } /////////////////////////////////////////////////////////////////////////////// // // -// ordervertices() Order the vertices for incremental inserting. // -// // -// We assume the vertices have been sorted by a binary tree. // +// brio_multiscale_sort() Sort the points using BRIO and Hilbert curve. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::ordervertices(point* vertexarray, int arraysize) +void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, + int threshold, REAL ratio, int *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); + 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. } /////////////////////////////////////////////////////////////////////////////// @@ -11938,17 +10839,14 @@ unsigned long tetgenmesh::randomnation(unsigned int choices) // // // randomsample() Randomly sample the tetrahedra for point loation. // // // -// 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. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::randomsample(point searchpt, triface *searchtet) +void tetgenmesh::randomsample(point searchpt,triface *searchtet) { tetrahedron *firsttet, *tetptr; point torg; @@ -11963,44 +10861,50 @@ void tetgenmesh::randomsample(point searchpt, triface *searchtet) pointmark(searchpt)); } - 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) { + if (!nonconvex) { + if (searchtet->tet == NULL) { + // A null tet. Choose the recenttet as the starting tet. *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))); + // 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))); + } } } + } 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. @@ -12040,11 +10944,6 @@ 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. @@ -12059,7 +10958,6 @@ 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. // @@ -12071,34 +10969,26 @@ 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 tetrahedron whose base face is visible by the search point. // +// hull face which 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, - int randflag) + tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag) { - triface neightet; - face checksh; point torg, tdest, tapex, toppo; enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; REAL ori, oriorg, oridest, oriapex; - enum locateresult loc; - int s; // i; - + enum locateresult loc = OUTSIDE; + int t1ver; + int s; 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->tet = recenttet.tet; } // Check if we are in the outside of the convex hull. @@ -12106,7 +10996,6 @@ 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. @@ -12114,22 +11003,14 @@ enum tetgenmesh::locateresult torg = org(*searchtet); tdest = dest(*searchtet); tapex = apex(*searchtet); - ori = orient3d(torg, tdest, tapex, searchpt); orient3dcount++; + ori = orient3d(torg, tdest, tapex, searchpt); if (ori < 0.0) break; } - 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. + assert(searchtet->ver != 4); // Walk through tetrahedra to locate the point. while (true) { - ptloc_count++; // Algorithimic count. - toppo = oppo(*searchtet); // Check if the vertex is we seek. @@ -12141,58 +11022,43 @@ enum tetgenmesh::locateresult break; } - // We enter from serarchtet's base face. There are three other faces in - // searchtet (all connecting to toppo), which one is the exit? + // We enter from one of serarchtet's faces, which face do we 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. Use the opposite points of thier neighbors - // to discriminate, i.e., we choose the face whose opposite point has - // the shortest distance to searchpt. + // faces are viable moves. If so, randomly choose one. if (oriorg < 0) { if (oridest < 0) { if (oriapex < 0) { - if (0) { //if (!randflag) { + // 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; } else { - // 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) + nextmove = APEXMOVE; + } } else { // Two faces, opposite to origin and destination, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = ORGMOVE; - } else { - nextmove = DESTMOVE; - } - } // if (randflag) + nextmove = DESTMOVE; + } } } else { if (oriapex < 0) { // Two faces, opposite to origin and apex, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = ORGMOVE; - } else { - nextmove = APEXMOVE; - } - } // if (randflag) + nextmove = APEXMOVE; + } } else { // Only the face opposite to origin is viable. nextmove = ORGMOVE; @@ -12202,16 +11068,12 @@ enum tetgenmesh::locateresult if (oridest < 0) { if (oriapex < 0) { // Two faces, opposite to destination and apex, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = DESTMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = DESTMOVE; - } else { - nextmove = APEXMOVE; - } - } // if (randflag) + nextmove = APEXMOVE; + } } else { // Only the face opposite to destination is viable. nextmove = DESTMOVE; @@ -12225,10 +11087,8 @@ 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. @@ -12239,7 +11099,6 @@ enum tetgenmesh::locateresult break; } if (oriapex == 0) { - //enext2self(*searchtet); enextself(*searchtet); // edge dest->oppo loc = ONEDGE; // return ONEDGE; break; @@ -12249,63 +11108,284 @@ enum tetgenmesh::locateresult } 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; + 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])); + } + enextself(fliptet); + eprevself(neightet); } - loc = INTETRAHEDRON; // return INTETRAHEDRON; - break; + // Delete the two tets. + tetrahedrondealloc(fliptet.tet); + tetrahedrondealloc(neightet.tet); + // Update the hull size. + hullsize -= 2; + flipcount++; } - } + } // ori + continue; + } // if (dummypoint) + + fsym(fliptets[0], fliptets[1]); + if ((point) fliptets[1].tet[7] == dummypoint) { + // A hull face is locally Delaunay. + continue; } - - // Move to the selected face. - if (nextmove == ORGMOVE) { - enextesymself(*searchtet); - } else if (nextmove == DESTMOVE) { - eprevesymself(*searchtet); + // 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]); } else { - esymself(*searchtet); + sign = insphere_s(pts[4], pts[5], pts[6], pts[7], newpt); } - if (chkencflag) { - // Check if we are walking across a subface. - tspivot(*searchtet, checksh); - if (checksh.sh != NULL) { - loc = ENCSUBFACE; - break; + + + 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]); } - } - // Move to the adjacent tetrahedron (maybe a hull tetrahedron). - fsymself(*searchtet); - if (oppo(*searchtet) == dummypoint) { - loc = OUTSIDE; // return OUTSIDE; - 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]; } - // Retreat the three vertices of the base face. - torg = org(*searchtet); - tdest = dest(*searchtet); - tapex = apex(*searchtet); + } // while (flipstack) - } // while (true) + // Unmark saved tetrahedra. + for (i = 0; i < cavebdrylist->objects; i++) { + parytet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*parytet); + } + cavebdrylist->restart(); - return loc; -} + return flipcount; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -12383,13 +11463,6 @@ 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)); @@ -12406,53 +11479,29 @@ 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, loc; + int randindex; + int ngroup = 0; 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->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->no_sort) { if (b->verbose) { - printf(" Sorting vertices by hilbert curve.\n"); + printf(" Using the input order.\n"); } - // To be done... for (i = 0; i < in->numberofpoints; i++) { permutarray[i] = (point) points->traverse(); } @@ -12460,23 +11509,32 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) if (b->verbose) { printf(" Permuting vertices.\n"); } + srand(in->numberofpoints); for (i = 0; i < in->numberofpoints; i++) { - randindex = randomnation(i + 1); + randindex = rand() % (i + 1); // 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 ((DIST(permutarray[0], permutarray[i]) / bboxsize) < b->epsilon) { + while ((distance(permutarray[0],permutarray[i])/bboxsize)<b->epsilon) { i++; if (i == in->numberofpoints - 1) { printf("Exception: All vertices are (nearly) identical (Tol = %g).\n", @@ -12497,8 +11555,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", @@ -12508,7 +11566,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. @@ -12519,8 +11577,8 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) // Make sure the fourth vertex is not coplanar with the first three. i = 3; - ori = orient3d(permutarray[0], permutarray[1], permutarray[2], - permutarray[i]); + ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); while ((fabs(ori) / bboxsize3) < b->epsilon) { i++; if (i == in->numberofpoints) { @@ -12528,8 +11586,8 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) b->epsilon); terminatetetgen(10); } - ori = orient3d(permutarray[0], permutarray[1], permutarray[2], - permutarray[i]); + ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); } if (i > 3) { // Swap to move the non-indetical vertex from index i to index 1. @@ -12554,57 +11612,73 @@ 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 (-l). + // Choose algorithm: Bowyer-Watson (default) or Incremental Flip 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); } - // Auto choose the starting tet for point location. - searchtet.tet = NULL; + 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; + } ivf.iloc = (int) OUTSIDE; // Insert the vertex. - 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 (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]); if (!b->quiet) { - printf("Warning: Point #%d is coincident with #%d. Ignored!\n", + printf("Warning: Point %d is replaced by point %d.\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; } @@ -12617,123 +11691,6 @@ 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. // @@ -12754,19 +11711,20 @@ void tetgenmesh::flipshpush(face* flipedge) /////////////////////////////////////////////////////////////////////////////// // // -// flip22() Remove an edge by transforming 2-to-2 subfaces. // +// flip22() Perform a 2-to-2 flip in surface mesh. // // // -// 'flipfaces' contains two faces: abc and bad. This routine removes these 2 // -// faces and replaces them by two new faces: cdb and dca. // +// '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]. // // // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) { - face bdedges[4], outfaces[4], infaces[4], bdsegs[4]; - face checkface, checkseg; + face bdedges[4], outfaces[4], infaces[4]; + face bdsegs[4]; + face checkface; point pa, pb, pc, pd; - badface *bface; int i; pa = sorg(flipfaces[0]); @@ -12778,10 +11736,6 @@ 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. @@ -12796,8 +11750,7 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) infaces[i] = outfaces[i]; sspivot(bdedges[i], bdsegs[i]); if (outfaces[i].sh != NULL) { - sspivot(bdedges[i], checkseg); - if (checkseg.sh != NULL) { + if (isshsubseg(bdedges[i])) { spivot(infaces[i], checkface); while (checkface.sh != bdedges[i].sh) { infaces[i] = checkface; @@ -12811,9 +11764,9 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) // Shellmark does not change. // area constraint does not change. - // Transform abc -> cdb. + // Transform [a,b,c] -> [c,d,b]. setshvertices(flipfaces[0], pc, pd, pb); - // Transform bad -> dca. + // Transform [b,a,d] -> [d,c,a]. setshvertices(flipfaces[1], pd, pc, pa); // Update the point-to-subface map. @@ -12849,12 +11802,7 @@ 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. - 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. - } + enqueuesubface(badsubsegs, &(bdsegs[(3 + i) % 4])); } } else { ssdissolve(bdedges[i]); @@ -12864,12 +11812,7 @@ 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++) { - 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. - } + enqueuesubface(badsubfacs, &(flipfaces[i])); } } @@ -12900,22 +11843,17 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) void tetgenmesh::flip31(face* flipfaces, int flipflag) { - face bdedges[3], outfaces[3], infaces[3], bdsegs[3]; - face checkface, checkseg; - point pa, pb, pc, delpt; - REAL area; + face bdedges[3], outfaces[3], infaces[3]; + face bdsegs[3]; + face checkface; + point pa, pb, pc; int i; - delpt = sorg(flipfaces[0]); pa = sdest(flipfaces[0]); pb = sdest(flipfaces[1]); pc = sdest(flipfaces[2]); - if (b->verbose > 3) { - printf(" flip 3-to-1: (%d, %d, %d) - %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(delpt)); - } - // flip31count++; + flip31count++; // Collect all infos at the three boundary edges. for (i = 0; i < 3; i++) { @@ -12924,8 +11862,7 @@ void tetgenmesh::flip31(face* flipfaces, int flipflag) infaces[i] = outfaces[i]; sspivot(bdedges[i], bdsegs[i]); if (outfaces[i].sh != NULL) { - sspivot(bdedges[i], checkseg); - if (checkseg.sh != NULL) { + if (isshsubseg(bdedges[i])) { spivot(infaces[i], checkface); while (checkface.sh != bdedges[i].sh) { infaces[i] = checkface; @@ -12940,8 +11877,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], area); + //area = areabound(flipfaces[0]); + setareabound(flipfaces[3], areabound(flipfaces[0])); } // Update the point-to-subface map. @@ -12998,15 +11935,13 @@ long tetgenmesh::lawsonflip() { badface *popface; face flipfaces[2]; - face checkseg; point pa, pb, pc, pd; REAL sign; - long flipcount; + long flipcount = 0; if (b->verbose > 2) { printf(" Lawson flip %ld edges.\n", flippool->items); } - flipcount = flip22count; while (flipstack != (badface *) NULL) { @@ -13023,8 +11958,7 @@ 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. - sspivot(flipfaces[0], checkseg); - if (checkseg.sh != NULL) continue; + if (isshsubseg(flipfaces[0])) continue; // Get the adjacent face. spivot(flipfaces[0], flipfaces[1]); @@ -13037,16 +11971,15 @@ long tetgenmesh::lawsonflip() if (sign < 0) { // It is non-locally Delaunay. Flip it. flip22(flipfaces, 1, 0); + flipcount++; } } if (b->verbose > 2) { - printf(" %ld edges stacked, %ld flips.\n", flippool->items, - flip22count - flipcount); + printf(" Performed %ld flips.\n", flipcount); } - assert(flippool->items == 0l); // SELF_CHECK - return flip22count - flipcount; + return flipcount; } /////////////////////////////////////////////////////////////////////////////// @@ -13058,6 +11991,19 @@ 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. // // // @@ -13066,67 +12012,47 @@ 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, pc; - enum locateresult loc; - REAL sign, ori, area; + point pa, pb; + enum locateresult loc = OUTSIDE; + REAL sign, ori; int i, j; if (b->verbose > 2) { printf(" Insert facet point %d.\n", pointmark(insertpt)); } - if (splitseg != NULL) { - // A segment is going to be split, no point location. - spivot(*splitseg, *searchsh); - loc = ONEDGE; - } else { - loc = (enum locateresult) iloc; - if (loc == OUTSIDE) { - // Do point location in surface mesh. - if (searchsh->sh == NULL) { - *searchsh = recentsh; - } - // Start searching from 'searchsh'. - loc = slocate(insertpt, searchsh, 1, 1, 0); - } + if (bowywat == 3) { + loc = INSTAR; } - 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 ((splitseg != NULL) && (splitseg->sh != NULL)) { + // A segment is going to be split, no point location. + spivot(*splitseg, *searchsh); + if (loc != INSTAR) loc = ONEDGE; + } else { + if (loc != INSTAR) 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). + loc = slocate(insertpt, searchsh, 1, 1, 0); } } -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 (b->verbose > 2) { - printf(" On edge.\n"); - } - if (splitseg != NULL) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { splitseg->shver = 0; pa = sorg(*splitseg); } else { @@ -13137,9 +12063,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { 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); @@ -13155,18 +12079,12 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } } // 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. @@ -13176,9 +12094,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { spivot(neighsh, casout); if (casout.sh == NULL) { // A convex hull edge. Is it visible by p. - pa = sorg(neighsh); - pb = sdest(neighsh); - ori = orient3d(pa, pb, dummypoint, insertpt); + ori = orient3d(sorg(neighsh), sdest(neighsh), dummypoint, insertpt); if (ori < 0) { *searchsh = neighsh; // Visible, update 'searchsh'. } else { @@ -13199,8 +12115,8 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { setshvertices(newsh, pb, pa, insertpt); setshellmark(newsh, shellmark(*searchsh)); if (checkconstraints) { - area = areabound(*searchsh); - setareabound(newsh, area); + //area = areabound(*searchsh); + setareabound(newsh, areabound(*searchsh)); } // Connect the new subface to the bottom subfaces. sbond1(newsh, *searchsh); @@ -13236,47 +12152,30 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { // 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++) { - sspivot(cavesh, checkseg); - if (checkseg.sh == NULL) { + if (!isshsubseg(cavesh)) { spivot(cavesh, neighsh); if (neighsh.sh != NULL) { // The adjacent face exists. if (!smarktested(neighsh)) { if (bowywat) { - if (bowywat > 2) { + if (loc == INSTAR) { // if (bowywat > 2) { // It must be a boundary edge. sign = 1; } else { // Check if this subface is connected to adjacent tet(s). - stpivot(neighsh, adjtet); - if (adjtet.tet == NULL) { + if (!isshtet(neighsh)) { // Check if the subface is non-Delaunay wrt. the new pt. - pa = sorg(neighsh); - pb = sdest(neighsh); - pc = sapex(neighsh); - sign = incircle3d(pa, pb, pc, insertpt); + sign = incircle3d(sorg(neighsh), sdest(neighsh), + sapex(neighsh), insertpt); } else { // It is connected to an adjacent tet. A boundary edge. sign = 1; @@ -13320,10 +12219,6 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } // 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++) { @@ -13338,8 +12233,8 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { setshellmark(newsh, shellmark(*parysh)); setshelltype(newsh, shelltype(*parysh)); if (checkconstraints) { - area = areabound(*parysh); - setareabound(newsh, area); + //area = areabound(*parysh); + setareabound(newsh, areabound(*parysh)); } // Update the point-to-subface map. if (pointtype(pa) == FREEFACETVERTEX) { @@ -13376,8 +12271,10 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { sbond1(*parysh, newsh); } - // Set a handle for searching. - recentsh = newsh; + if (newsh.sh != NULL) { + // Set a handle for searching. + recentsh = newsh; + } // Update the point-to-subface map. if (pointtype(insertpt) == FREEFACETVERTEX) { @@ -13405,8 +12302,6 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { 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 { @@ -13431,8 +12326,6 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { 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 { @@ -13442,13 +12335,15 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } } - if (loc == ONEDGE) { - + if ((loc == ONEDGE) || ((splitseg != NULL) && (splitseg->sh != NULL)) + || (cavesegshlist->objects > 0l)) { // 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); @@ -13490,7 +12385,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { sdissolve(newsh); } } - recentsh = newsh; + //recentsh = newsh; // Update the point-to-subface map. if (pointtype(insertpt) == FREEFACETVERTEX) { setpoint2sh(insertpt, sencode(newsh)); @@ -13498,18 +12393,14 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } } - if (splitseg != NULL) { - if (bowywat < 3) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + if (loc != INSTAR) { // 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); @@ -13522,7 +12413,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { setshelltype(aseg, shelltype(*splitseg)); setshelltype(bseg, shelltype(*splitseg)); if (checkconstraints) { - setareabound(bseg, areabound(*splitseg)); + setareabound(aseg, areabound(*splitseg)); setareabound(bseg, areabound(*splitseg)); } @@ -13565,11 +12456,17 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { // Let the point remember the segment it lies on. - setpoint2sh(insertpt, sencode(aseg)); + if (pointtype(insertpt) == FREESEGVERTEX) { + setpoint2sh(insertpt, sencode(aseg)); + } // Update the point-to-seg map. - setpoint2sh(pa, sencode(aseg)); - setpoint2sh(pb, sencode(bseg)); - } // if (splitseg != NULL) + if (pointtype(pa) == FREESEGVERTEX) { + setpoint2sh(pa, sencode(aseg)); + } + if (pointtype(pb) == FREESEGVERTEX) { + setpoint2sh(pb, sencode(bseg)); + } + } // if ((splitseg != NULL) && (splitseg->sh != NULL)) // Delete all degenerated new faces. for (i = 0; i < cavesegshlist->objects; i++) { @@ -13581,7 +12478,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } cavesegshlist->restart(); - if (splitseg != NULL) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { // Return the two new subsegments (for further process). // Re-use 'cavesegshlist'. cavesegshlist->newindex((void **) &parysh); @@ -13589,7 +12486,6 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { cavesegshlist->newindex((void **) &parysh); *parysh = bseg; } - } // if (loc == ONEDGE) @@ -13604,39 +12500,34 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { // 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], *parysh; - face spinsh, startsh, neighsh, nextsh, fakesh; - face abseg, prevseg, checkseg; - face adjseg1, adjseg2; + face flipfaces[4], spinsh, *parysh; point pa, pb, pc, pd; - int it, i, j; - - REAL *norm, n1[3], n2[3]; - REAL len, len1, len2; REAL ori1, ori2; + int it, i, j; 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]. @@ -13741,12 +12632,7 @@ 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); - } - assert(sorg(startsh) == pa); - assert(sdest(startsh) == pb); - assert(sapex(startsh) != delpt); + if (sorg(startsh) != pa) sesymself(startsh); sdissolve(startsh); // Connect fakesh to the segment [a,b]. ssbond(startsh, abseg); @@ -13797,9 +12683,7 @@ 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. @@ -13817,10 +12701,8 @@ 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) { @@ -13839,9 +12721,6 @@ 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. @@ -13849,35 +12728,15 @@ 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]); - // 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]; + calculateabovepoint4(pa, pb, pc, pd); // Check if a 2-to-2 flip is possible. ori1 = orient3d(pc, pd, dummypoint, pa); ori2 = orient3d(pc, pd, dummypoint, pb); @@ -13894,6 +12753,7 @@ 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. @@ -13911,6 +12771,7 @@ 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) @@ -13962,35 +12823,19 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, face* searchsh, int aflag, int cflag, int rflag) { face neighsh; - face checkseg; - point pa, pb, pc, pd, *parypt; + point pa, pb, pc; 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. - // 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(); + calculateabovepoint4(pa, pb, pc, searchpt); } // 'dummypoint' is given. Make sure it is above [a,b,c] @@ -14025,25 +12870,7 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, if (ori_bc < 0) { if (ori_ca < 0) { // (--) // Any of the edges is a viable move. - 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) { + if (randomnation(2)) { nextmove = MOVE_CA; } else { nextmove = MOVE_BC; @@ -14088,8 +12915,7 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, } if (!cflag) { // NON-convex case. Check if we will cross a boundary. - sspivot(*searchsh, checkseg); - if (checkseg.sh != NULL) { + if (isshsubseg(*searchsh)) { return ENCSEGMENT; } } @@ -14121,6 +12947,8 @@ 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); @@ -14194,17 +13022,15 @@ 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, checkseg; + face newseg; 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; @@ -14238,16 +13064,7 @@ enum tetgenmesh::interresult if (ori_ab < 0) { if (ori_ca < 0) { // (--) // Both sides are viable moves. - 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) { + if (randomnation(2)) { nextmove = MOVE_CA; } else { nextmove = MOVE_AB; @@ -14303,11 +13120,8 @@ enum tetgenmesh::interresult // Insert the segment into the triangulation. makeshellface(subsegs, &newseg); setshvertices(newseg, startpt, endpt, NULL); - // Set the actual segment marker. - if (in->facetmarkerlist != NULL) { - shmark = shellmark(*searchsh); - setshellmark(newseg, in->facetmarkerlist[shmark - 1]); - } + // Set the default segment marker. + setshellmark(newseg, 1); ssbond(*searchsh, newseg); spivot(*searchsh, neighsh); if (neighsh.sh != NULL) { @@ -14324,8 +13138,7 @@ enum tetgenmesh::interresult if (dir == ACROSSEDGE) { // Edge [b, c] intersects with the segment. senext(*searchsh, flipshs[0]); - sspivot(flipshs[0], checkseg); - if (checkseg.sh != NULL) { + if (isshsubseg(flipshs[0])) { printf("Error: Invalid PLC.\n"); pb = sorg(flipshs[0]); pc = sdest(flipshs[0]); @@ -14349,16 +13162,8 @@ enum tetgenmesh::interresult 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'. @@ -14380,7 +13185,6 @@ enum tetgenmesh::interresult void tetgenmesh::scarveholes(int holes, REAL* holelist) { face *parysh, searchsh, neighsh; - face checkseg; enum locateresult loc; int i, j; @@ -14403,8 +13207,7 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) } } else { // A hull side. Check if it is protected by a segment. - sspivot(searchsh, checkseg); - if (checkseg.sh == NULL) { + if (!isshsubseg(searchsh)) { // Not protected. Save this face. if (!sinfected(searchsh)) { sinfect(searchsh); @@ -14436,8 +13239,7 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) for (j = 0; j < 3; j++) { spivot(searchsh, neighsh); if (neighsh.sh != NULL) { - sspivot(searchsh, checkseg); - if (checkseg.sh == NULL) { + if (!isshsubseg(searchsh)) { if (!sinfected(neighsh)) { sinfect(neighsh); caveshbdlist->newindex((void **) &parysh); @@ -14469,6 +13271,9 @@ 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, @@ -14477,7 +13282,6 @@ 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; @@ -14493,57 +13297,27 @@ 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 actual segment marker. - if (in->facetmarkerlist != NULL) { - setshellmark(newseg, in->facetmarkerlist[shmark - 1]); - } + // Set the default segment marker '1'. + setshellmark(newseg, 1); } if (pointtype(pa) == VOLVERTEX) { - setpointtype(pa, RIDGEVERTEX); + setpointtype(pa, FACETVERTEX); } if (pointtype(pb) == VOLVERTEX) { - 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); - } + setpointtype(pb, FACETVERTEX); } return; - } + } + // Calulcate an above point of this facet. if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) { @@ -14566,6 +13340,34 @@ 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); @@ -14576,8 +13378,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); - loc = (enum locateresult) sinsertvertex(*ppt, &searchsh, NULL, iloc, 1); - assert(loc != ONVERTEX); // SELF_CHECK + iloc = sinsertvertex(*ppt, &searchsh, NULL, iloc, 1); + assert(iloc != (enum locateresult) ONVERTEX); // SELF_CHECK if (pointtype(*ppt) == VOLVERTEX) { setpointtype(*ppt, FACETVERTEX); } @@ -14599,8 +13401,8 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, for (i = 0; i < conlist->objects; i++) { cons = (point *) fastlookup(conlist, i); searchsh = recentsh; - loc = slocate(cons[0], &searchsh, 1, 1, 0); - assert(loc == ONVERTEX); // SELF_CHECK + iloc = (int) slocate(cons[0], &searchsh, 1, 1, 0); + assert(iloc == (enum locateresult) ONVERTEX); // SELF_CHECK // Recover the segment. Some edges may be flipped. sscoutsegment(&searchsh, cons[1]); if (flipstack != NULL) { @@ -14629,18 +13431,13 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, void tetgenmesh::unifysubfaces(face *f1, face *f2) { face casout, casin, neighsh; - face sseg, checkseg; + face sseg; 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) { @@ -14687,10 +13484,6 @@ 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) { @@ -14730,6 +13523,9 @@ 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() @@ -14852,7 +13648,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); @@ -14878,7 +13674,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); } @@ -14900,16 +13696,10 @@ 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)); - } - //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 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. @@ -14930,21 +13720,30 @@ 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); } @@ -15009,11 +13808,6 @@ 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); @@ -15033,7 +13827,6 @@ void tetgenmesh::mergefacets() lawsonflip(); // Recover Delaunayness. } - if (b->verbose > 1) { printf(" %d segments are removed.\n", remsegcount); } @@ -15063,12 +13856,13 @@ 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. @@ -15079,6 +13873,7 @@ 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++) { @@ -15102,7 +13897,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); @@ -15114,10 +13909,6 @@ 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); @@ -15129,27 +13920,30 @@ 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 (!b->psc) { - setpointtype(pa, RIDGEVERTEX); - setpointtype(pb, RIDGEVERTEX); - //} + } + + 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; + } + } + } } } // i @@ -15159,10 +13953,6 @@ 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); @@ -15326,7 +14116,7 @@ void tetgenmesh::meshsurface() unifysegments(); } - if (!b->nomerge && !b->nobisect && !b->diagnose) { + if (!b->nomergefacet && !b->nobisect && !b->diagnose) { // Merge adjacent coplanar facets. mergefacets(); } @@ -15354,6 +14144,7 @@ void tetgenmesh::meshsurface() delete conlist; } + /////////////////////////////////////////////////////////////////////////////// // // // interecursive() Recursively do intersection test on a set of triangles.// @@ -15442,9 +14233,7 @@ 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++; @@ -15619,7 +14408,7 @@ void tetgenmesh::detectinterfaces() // // // markacutevertices() Classify vertices as ACUTEVERTEXs or RIDGEVERTEXs. // // // -// Initially all segment vertices have type RIDGEVERTEX. A segment is acute // +// Initialize all segment vertices's type be RIDGEVERTEX. A segment is acute // // if there are at least two segments incident at it form an angle less than // // theta (= 60 degree). // // // @@ -15632,19 +14421,16 @@ void tetgenmesh::markacutevertices() face* segperverlist; int* idx2seglist; point pa, pb, pc; - REAL anglimit, ang; + REAL anglimit, sharpanglimit, ang; bool acuteflag; - int acutecount; + int acutecount, sharpsegcount; int idx, i, j; - REAL sharpanglimit; - int sharpsegcount; - if (b->verbose) { printf(" Marking acute vertices.\n"); } anglimit = PI / 3.0; // 60 degree. - sharpanglimit = 10.0 / 180.0 * PI; // 10 degree. + sharpanglimit = 5.0 / 180.0 * PI; // 5 degree. minfaceang = PI; // 180 degree. acutecount = sharpsegcount = 0; @@ -15658,22 +14444,15 @@ void tetgenmesh::markacutevertices() idx = pointmark(pa) - in->firstnumber; // Mark it if it is an endpoint of some segments. if (idx2seglist[idx + 1] > idx2seglist[idx]) { - if (b->psc) { - // Only test it if it is an input vertex. - if (pointtype(pa) == FREESEGVERTEX) { - pa = pointtraverse(); - continue; - } - } + // It is an endpoint of some segments. + setpointtype(pa, RIDGEVERTEX); 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; } @@ -15688,20 +14467,11 @@ 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++; } @@ -15723,34 +14493,6 @@ 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)); - -} /////////////////////////////////////////////////////////////////////////////// // // @@ -15771,21 +14513,20 @@ void tetgenmesh::reportselfintersect(face *checkseg, face *checksh) /////////////////////////////////////////////////////////////////////////////// enum tetgenmesh::interresult - tetgenmesh::finddirection(triface* searchtet, point endpt, int randflag) + tetgenmesh::finddirection(triface* searchtet, point endpt) { 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. - searchtet->ver = 11; - fsymself(*searchtet); + decode(searchtet->tet[3], *searchtet); // Reset the origin to be pa. if ((point) searchtet->tet[4] == pa) { searchtet->ver = 11; @@ -15794,7 +14535,7 @@ enum tetgenmesh::interresult } else if ((point) searchtet->tet[6] == pa) { searchtet->ver = 7; } else { - assert((point) searchtet->tet[7] == pa); // SELF_CHECK + assert((point) searchtet->tet[7] == pa); searchtet->ver = 0; } } @@ -15809,8 +14550,7 @@ enum tetgenmesh::interresult pc = apex(*searchtet); if (pc == endpt) { // pa->pc is the search edge. - eprevself(*searchtet); - esymself(*searchtet); + eprevesymself(*searchtet); return ACROSSVERT; } @@ -15818,12 +14558,6 @@ 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. @@ -15846,54 +14580,39 @@ 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 - // 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'. + // tets are viable moves. Is so, randomly choose one. if (hori > 0) { if (rori > 0) { if (lori > 0) { // Any of the three neighbors is a viable move. - if (0) { // if (!randflag) { + s = randomnation(3); + if (s == 0) { + nextmove = HMOVE; + } else if (s == 1) { + nextmove = RMOVE; } else { - // 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) + nextmove = LMOVE; + } } else { // Two tets, below horizon and below right, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); + if (randomnation(2)) { + nextmove = HMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = HMOVE; - } else { - nextmove = RMOVE; - } - } // if (randflag) + nextmove = RMOVE; + } } } else { if (lori > 0) { // Two tets, below horizon and below left, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); + if (randomnation(2)) { + nextmove = HMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = HMOVE; - } else { - nextmove = LMOVE; - } - } // if (randflag) + nextmove = LMOVE; + } } else { // The tet below horizon is chosen. nextmove = HMOVE; @@ -15903,16 +14622,12 @@ enum tetgenmesh::interresult if (rori > 0) { if (lori > 0) { // Two tets, below right and below left, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); + if (randomnation(2)) { + nextmove = RMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = RMOVE; - } else { - nextmove = LMOVE; - } - } // if (randflag) + nextmove = LMOVE; + } } else { // The tet below right is chosen. nextmove = RMOVE; @@ -15930,8 +14645,7 @@ enum tetgenmesh::interresult } if (lori == 0) { // pa->'endpt' is COLLINEAR with pa->pc. - eprevself(*searchtet); - esymself(*searchtet); // [a,c,d] + eprevesymself(*searchtet); // // [a,c,d] return ACROSSVERT; } // pa->'endpt' crosses the edge pb->pc. @@ -15951,8 +14665,7 @@ enum tetgenmesh::interresult } if (lori == 0) { // pa->'endpt' crosses the edge pc->pd. - eprevself(*searchtet); - esymself(*searchtet); // face acd + eprevesymself(*searchtet); // [a,c,d] return ACROSSEDGE; } // pa->'endpt' crosses the face bcd. @@ -15972,7 +14685,7 @@ enum tetgenmesh::interresult fsymself(*searchtet); enextself(*searchtet); } - assert(org(*searchtet) == pa); // SELF_CHECK + assert(org(*searchtet) == pa); pb = dest(*searchtet); pc = apex(*searchtet); @@ -15982,25 +14695,22 @@ enum tetgenmesh::interresult /////////////////////////////////////////////////////////////////////////////// // // -// scoutsegment() Look for a given segment in the tetrahedralization T. // +// scoutsegment() Search an edge in the tetrahedralization. // // // -// 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 found, it returns SHAREEDGE, and 'searchtet' returns the // +// edge from startpt to endpt. // // // -// 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'). // +// 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 following cases can happen when the input PLC is not valid. // -// - 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'. // +// - 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'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -16008,22 +14718,16 @@ enum tetgenmesh::interresult tetgenmesh::scoutsegment(point startpt, point endpt, triface* searchtet, point* refpt, arraypool* intfacelist) { - triface neightet, reftet; - face checkseg, checksh; - point pa, pb, pc, pd; - badface *bface; + point pd; enum interresult dir; - REAL angmax, ang; - long facecount; - int types[2], poss[4]; - int pos, i, j; + int t1ver; 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, 0); + dir = finddirection(searchtet, endpt); if (dir == ACROSSVERT) { pd = dest(*searchtet); @@ -16032,14 +14736,13 @@ enum tetgenmesh::interresult return SHAREEDGE; } else { // A point is on the path. - *refpt = pd; + // Let the origin of the searchtet be the vertex. + enextself(*searchtet); + if (refpt) *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. @@ -16047,29 +14750,28 @@ enum tetgenmesh::interresult if (dir == ACROSSEDGE) { // Check whether two segments are intersecting. - tsspivot1(*searchtet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(*searchtet)) { return ACROSSSEG; } - across_edge_count++; } else if (dir == ACROSSFACE) { if (checksubfaceflag) { // Check whether a segment and a subface are intersecting. - tspivot(*searchtet, checksh); - if (checksh.sh != NULL) { + if (issubface(*searchtet)) { return ACROSSSUB; } } } if (refpt == NULL) { + // Do not need a reference point. Return. return dir; } - if (b->verbose > 2) { - printf(" Scout a ref-point for it.\n"); - } - facecount = across_face_count; + triface neightet, reftet; + point pa, pb, pc; + REAL angmax, ang; + int types[2], poss[4]; + int pos, i, j; pa = org(*searchtet); angmax = interiorangle(pa, startpt, endpt, NULL); @@ -16091,53 +14793,10 @@ 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; @@ -16205,9 +14864,6 @@ enum tetgenmesh::interresult enextself(neightet); } pd = org(neightet); - if (b->verbose > 2) { - angmax = interiorangle(pd, startpt, endpt, NULL); - } *refpt = pd; // break; return ACROSSVERT; @@ -16222,16 +14878,13 @@ enum tetgenmesh::interresult if (dir == ACROSSEDGE) { // Check whether two segments are intersecting. - tsspivot1(*searchtet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(*searchtet)) { return ACROSSSEG; } - across_edge_count++; } else if (dir == ACROSSFACE) { if (checksubfaceflag) { // Check whether a segment and a subface are intersecting. - tspivot(*searchtet, checksh); - if (checksh.sh != NULL) { + if (issubface(*searchtet)) { return ACROSSSUB; } } @@ -16245,19 +14898,6 @@ 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; @@ -16272,175 +14912,71 @@ enum tetgenmesh::interresult void tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) { point ei, ej; - 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 (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); - } - } + REAL Li, Lj, L; + REAL t; + int i; - if (refpt != NULL) { - if (pointtype(ei) == ACUTEVERTEX) { - 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; - } + ei = sorg(*seg); + ej = sdest(*seg); - if (type == 1) { - L = distance(ei, ej); + + if (refpt != NULL) { + // Let ei be the closer one to refpt. 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; + 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 (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). for (i = 0; i < 3; i++) { steinpt[i] = ei[i] + t * (ej[i] - ei[i]); } - } - // 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; + // 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; for (i = 0; i < 3; i++) { steinpt[i] = ei[i] + t * (ej[i] - ei[i]); } } - r3count++; } else { - r2count++; + // 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]); + } + } } } else { // Split the point at the middle. - 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]); - } + t = 0.5; + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + t * (ej[i] - ei[i]); } - r1count++; - } - - if (b->psc) { - setpointgeomuv(steinpt, 0, t); - setpointgeomtag(steinpt, eid); - } + } // if (refpt == NULL) 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"); - } } + /////////////////////////////////////////////////////////////////////////////// // // // delaunizesegments() Recover segments in a DT. // @@ -16458,29 +14994,18 @@ void tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) void tetgenmesh::delaunizesegments() { triface searchtet, spintet; - face searchsh, checksh; - face sseg, checkseg, *psseg; + face searchsh; + face sseg, *psseg; point refpt, newpt; enum interresult dir; insertvertexflags ivf; - int loc; + int t1ver; - // 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. - } - searchsh.sh = NULL; + ivf.bowywat = 1; // Use Bowyer-Watson insertion. + ivf.assignmeshsize = b->metric; + ivf.sloc = (int) ONEDGE; // on 'sseg'. + ivf.sbowywat = 1; // Use Bowyer-Watson insertion. // Loop until 'subsegstack' is empty. while (subsegstack->objects > 0l) { @@ -16489,14 +15014,9 @@ 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. } @@ -16505,8 +15025,7 @@ void tetgenmesh::delaunizesegments() if (dir == SHAREEDGE) { // Found this segment, insert it. - tsspivot1(searchtet, checkseg); // SELF_CHECK - if (checkseg.sh == NULL) { + if (!issubseg(searchtet)) { // Let the segment remember an adjacent tet. sstbond1(sseg, searchtet); // Bond the segment to all tets containing it. @@ -16516,7 +15035,7 @@ void tetgenmesh::delaunizesegments() fnextself(spintet); } while (spintet.tet != searchtet.tet); } else { - // Collision! Should not happen. + // Collision! Maybe a bug. assert(0); } } else { @@ -16527,145 +15046,30 @@ void tetgenmesh::delaunizesegments() //setpointtype(newpt, FREESEGVERTEX); getsteinerptonsegment(&sseg, refpt, newpt); - // Start searching from the 'searchtet'. + // Start searching from '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). - loc = insertvertex(newpt, &searchtet, &searchsh, &sseg, &ivf); - - assert(loc != (int) ONVERTEX); - if (loc != (int) NEARVERTEX) { + if (insertpoint(newpt, &searchtet, &searchsh, &sseg, &ivf)) { // The new point has been inserted. - if (ivf.lawson > 0) { - // For CDT, use flips to reocver Delaunayness. - lawsonflip3d(newpt, ivf.lawson, 0, 0, 0); - } - st_segref_count++; //st_segpro_count++; + st_segref_count++; if (steinerleft > 0) steinerleft--; } else { - // 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); + assert (ivf.iloc == (enum locateresult) NEARVERTEX); 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() Look for a given subface in the tetrahedralization T. // +// scoutsubface() Search subface in the tetrahedralization. // // // // '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. // @@ -16681,27 +15085,25 @@ 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, 0); + dir = finddirection(searchtet, pb); if (dir == ACROSSVERT) { // Check validity of a PLC. if (dest(*searchtet) != pb) { - // A vertex lies on the search edge. Return it. + // A vertex lies on the search edge. enextself(*searchtet); + // It is possible a PLC self-intersection problem. + terminatetetgen(3); return TOUCHEDGE; } // The edge exists. Check if the face exists. @@ -16711,8 +15113,7 @@ enum tetgenmesh::interresult while (1) { if (apex(spintet) == pc) { // Found a face matching to 'searchsh'! - tspivot(spintet, checksh); - if (checksh.sh == NULL) { + if (!issubface(spintet)) { // Insert 'searchsh'. tsbond(spintet, *searchsh); fsymself(spintet); @@ -16722,6 +15123,8 @@ 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. @@ -16738,47 +15141,39 @@ enum tetgenmesh::interresult } // dir is either ACROSSEDGE or ACROSSFACE. - return dir; //ACROSSTET; + return dir; } /////////////////////////////////////////////////////////////////////////////// // // -// formmissingregion() Form the missing region of a missing subface. // +// formregion() Form the missing region of a missing subface. // // // // 'missh' is a missing subface. From it we form a missing region R which is // -// 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. // +// a connected region formed by a set of missing subfaces of a facet. // +// Comment: There should be no segment inside R. // // // -// '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. // +// '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. // // // +// 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::formmissingregion(face* missh, arraypool* missingshs, - arraypool* missingshbds, - arraypool* missingshverts, - arraypool* adjtets) +void tetgenmesh::formregion(face* missh, arraypool* missingshs, + arraypool* missingshbds, arraypool* missingshverts) { - triface searchtet, *parytet; + triface searchtet, spintet; 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; @@ -16789,43 +15184,22 @@ void tetgenmesh::formmissingregion(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); - // Search the edge [a,b]. - dir = finddirection(&searchtet, pb, 0); + dir = finddirection(&searchtet, pb); 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 (b->verbose > 3) { - printf(" Add a missing subface (%d, %d, %d)\n", - pointmark(pb), pointmark(pa), pointmark(sapex(neighsh))); - } + if (sorg(neighsh) != pb) sesymself(neighsh); smarktest(neighsh); missingshs->newindex((void **) &parysh); *parysh = neighsh; } } else { - 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); + if (dest(searchtet) != pb) { + // This might be a self-intersection problem. + terminatetetgen(3); } } // Collect the vertices of R. @@ -16838,57 +15212,86 @@ void tetgenmesh::formmissingregion(face* missh, arraypool* missingshs, } // j } // i - if (b->verbose > 2) { - printf(" Region has: %ld subfaces, %ld vertices\n", - missingshs->objects, missingshverts->objects); - } + // 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 (missingshs->objects > maxregionsize) { - maxregionsize = missingshs->objects; - } // Unmarktest collected missing subfaces. for (i = 0; i < missingshs->objects; i++) { - missh = (face *) fastlookup(missingshs, i); - sunmarktest(*missh); + parysh = (face *) fastlookup(missingshs, i); + sunmarktest(*parysh); } - - // 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* adjtets, +int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, arraypool* missingshs) { - triface *searchtet, spintet; + triface searchtet, spintet; face *parysh; - face checkseg; + face neighseg; 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 < adjtets->objects && !searchflag; j++) { - searchtet = (triface *) fastlookup(adjtets, j); + for (j = 0; j < missingshbds->objects && !searchflag; j++) { + parysh = (face *) fastlookup(missingshbds, j); + sspivot(*parysh, neighseg); + sstpivot1(neighseg, searchtet); 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); @@ -16902,7 +15305,7 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, 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. @@ -16910,13 +15313,12 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { //pos = poss[0]; // Go to the crossing edge [d,e,#,#]. - eprev(spintet, crosstet); - esymself(crosstet); - enextself(crosstet); // [d,e,#,#]. + edestoppo(spintet, crosstet); // // [d,e,#,#]. // Check if it is a segment. - tsspivot1(crosstet, checkseg); - if (checkseg.sh != NULL) { - reportselfintersect(&checkseg, parysh); + if (issubseg(crosstet)) { + //face checkseg; + //tsspivot1(crosstet, checkseg); + //reportselfintersect(&checkseg, parysh); terminatetetgen(3); } // Adjust the edge such that d lies below [a,b,c]. @@ -16925,21 +15327,7 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, 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; @@ -16951,11 +15339,10 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, 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; } @@ -16969,23 +15356,6 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, // #] 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 // @@ -16994,13 +15364,6 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, // 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. // // // @@ -17011,27 +15374,22 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, arraypool* botfaces, arraypool* toppoints, arraypool* botpoints) { - arraypool *crossedges, *testededges; + arraypool *crossedges; triface spintet, neightet, *parytet; - face checksh, *parysh = NULL; - face checkseg; // *paryseg; + face *parysh = NULL; 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 missing region.\n"); + printf(" Form the cavity of a missing region.\n"); } - missingsubfacecount += missingshs->objects; // Mark this edge to avoid testing it later. markedge(*searchtet); crossedges->newindex((void **) &parytet); @@ -17042,22 +15400,18 @@ 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); @@ -17068,11 +15422,6 @@ 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; @@ -17088,10 +15437,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) || 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. + 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. testflag = true; for (j = 0; j < 2 && testflag; j++) { if (j == 0) { @@ -17117,12 +15466,8 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, pe = dest(spintet); 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(plane_pa, plane_pb, plane_pc, pe, pa, - NULL, 1, types, poss)) { + if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), + pe, pa, NULL, 1, types, poss)) { // Found intersection. 'a' lies below R. enext(spintet, neightet); dir = (enum interresult) types[0]; @@ -17134,9 +15479,8 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, } break; } - // Test if this face intersects [a,d]. - if (tri_edge_test(plane_pa, plane_pb, plane_pc, pa, pd, - NULL, 1, types, poss)) { + if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), + pa, pd, NULL, 1, types, poss)) { // Found intersection. 'a' lies above R. eprev(spintet, neightet); dir = (enum interresult) types[0]; @@ -17152,78 +15496,56 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, if (k < missingshs->objects) { // Found a pair of triangle - edge interseciton. if (invalidflag) { - if (b->verbose > 2) { - printf(" A non-valid subface - edge intersection\n"); + if (!b->quiet) { + printf("Warning: A non-valid facet - 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(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh)), 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. - tsspivot1(neightet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(neightet)) { // Invalid PLC! - reportselfintersect(&checkseg, parysh); + //face checkseg; + //tsspivot1(neightet, checkseg); + //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(plane_pa, plane_pb, plane_pc, pd, pe, - NULL, 1, types, poss)) { + if (tri_edge_test(sorg(*parysh),sdest(*parysh),sapex(*parysh), + pd, pe, NULL, 1, types, poss)) { break; } } // k - assert(k < missingshs->objects); + 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); + } recentsh = *parysh; recenttet = spintet; // For point location. break; // the while (1) loop } // if (k == missingshs->objects) } // if (testflag) - } // if (!pmarktested(pa) || b->psc) - } + } // if (!pmarktested(pa) || b->psc) + } // if (pa != dummypoint) // Go to the next crossing tet. fnextself(spintet); if (spintet.tet == searchtet->tet) break; @@ -17238,7 +15560,6 @@ 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++) { @@ -17248,17 +15569,6 @@ 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. @@ -17278,37 +15588,13 @@ 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 @@ -17325,16 +15611,14 @@ 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]. - enextesym(*searchtet, spintet); - eprevself(spintet); // spintet is [b,a,e,d] + eorgoppo(*searchtet, spintet); fsym(spintet, neightet); // neightet is [a,b,e,#] if (!infected(neightet)) { // A top face. topfaces->newindex((void **) &parytet); *parytet = neightet; - } - eprevesym(*searchtet, spintet); - enextself(spintet); // spintet is [a,b,d,e] + } + edestoppo(*searchtet, spintet); fsym(spintet, neightet); // neightet is [b,a,d,#] if (!infected(neightet)) { // A bottom face. @@ -17399,19 +15683,17 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, arraypool *cavshells, arraypool *newtets, arraypool *crosstets, arraypool *misfaces) { - triface searchtet, neightet, spintet, *parytet, *parytet1; - face checksh, tmpsh, *parysh; - face checkseg; + triface searchtet, neightet, *parytet, *parytet1; + face tmpsh, *parysh; point pa, pb, pc, pd, pt[3], *parypt; enum interresult dir; insertvertexflags ivf; - REAL ori; //, ang, len; + REAL ori; long baknum, bakhullsize; int bakchecksubsegflag, bakchecksubfaceflag; - //int iloc; + int t1ver; int i, j; - if (b->verbose > 2) { printf(" Delaunizing cavity: %ld points, %ld faces.\n", cavpoints->objects, cavfaces->objects); @@ -17425,31 +15707,38 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, checksubsegflag = 0; checksubfaceflag = 0; b->verbose--; // Suppress informations for creating Delaunay tetra. - b->plc = 0; // Do not do unifypoint(); + b->plc = 0; // Do not check near vertices. + + ivf.bowywat = 1; // Use Bowyer-Watson algorithm. // Get four non-coplanar points (no dummypoint). - parytet = (triface *) fastlookup(cavfaces, 0); - pa = org(*parytet); - pb = dest(*parytet); - pc = apex(*parytet); + 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; + } + } pd = NULL; - for (i = 1; i < cavfaces->objects; i++) { + for (; 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. - // 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; + 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; + } } } if (pd != NULL) break; @@ -17462,11 +15751,9 @@ 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; - ivf.bowywat = 1; - insertvertex(pt[0], &searchtet, NULL, NULL, &ivf); + insertpoint(pt[0], &searchtet, NULL, NULL, &ivf); } if (b->verbose > 2) { @@ -17481,10 +15768,7 @@ 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; - // 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] + parytet->ver = epivot[parytet->ver]; pt[0] = org(*parytet); pt[1] = dest(*parytet); pt[2] = apex(*parytet); @@ -17495,38 +15779,21 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, searchtet.tet = NULL; dir = scoutsubface(&tmpsh, &searchtet); if (dir == SHAREFACE) { - // 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); - } + // 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. 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) { @@ -17566,11 +15833,7 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, if (!pinfected(pd)) { searchtet = recenttet; ivf.iloc = (int) OUTSIDE; - ivf.bowywat = 1; - insertvertex(pd, &searchtet, NULL, NULL, &ivf); - if (b->verbose > 2) { - printf(" Add point %d into list.\n", pointmark(pd)); - } + insertpoint(pd, &searchtet, NULL, NULL, &ivf); pinfect(pd); cavpoints->newindex((void **) &parypt); *parypt = pd; @@ -17580,15 +15843,9 @@ 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)) @@ -17614,8 +15871,8 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, *parytet = recenttet; for (i = 0; i < newtets->objects; i++) { searchtet = * (triface *) fastlookup(newtets, i); - for (searchtet.ver = 0; searchtet.ver < 4; searchtet.ver++) { - fsym(searchtet, neightet); + for (j = 0; j < 4; j++) { + decode(searchtet.tet[j], neightet); if (!marktested(neightet)) { marktest(neightet); newtets->newindex((void **) &parytet); @@ -17627,9 +15884,6 @@ 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++; @@ -17657,112 +15911,131 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, /////////////////////////////////////////////////////////////////////////////// bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, - arraypool* midfaces, arraypool* missingshs) + arraypool* midfaces, arraypool* missingshs, + arraypool* topnewtets, arraypool* botnewtets, + triface* crossedge) { arraypool *cavshells; - triface *parytet, bdrytet, toptet, bottet, midface; - triface neightet, spintet; - face checksh, *parysh; + triface bdrytet, neightet, *parytet; + triface searchtet, spintet; + face *parysh; face checkseg; - 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; + point pa, pb, pc; + bool mflag; + int t1ver; + int i, j; // Connect newtets to tets outside the cavity. These connections are needed // for identifying the middle faces (which belong to R). - for (k = 0; k < 2; k++) { - cavshells = (k == 0 ? topshells : botshells); + for (j = 0; j < 2; j++) { + cavshells = (j == 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. + // Get the boundary tet outside the cavity (saved in sh[0]). decode(parysh->sh[0], bdrytet); pa = org(bdrytet); pb = dest(bdrytet); pc = apex(bdrytet); - // Get the adjacent new tet. + // Get the adjacent new tet inside the cavity. stpivot(*parysh, 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. - // } + // Mark neightet as an interior tet of this cavity. + infect(neightet); + // Connect the two tets (the old connections are replaced). + bond(bdrytet, neightet); tsdissolve(neightet); // Clear the pointer to tmpsh. // Update the point-to-tets map. - setpoint2tet(pa, encode(neightet)); - setpoint2tet(pb, encode(neightet)); - setpoint2tet(pc, encode(neightet)); - // Delete the temp subface. - // shellfacedealloc(subfacepool, parysh->sh); + setpoint2tet(pa, (tetrahedron) neightet.tet); + setpoint2tet(pb, (tetrahedron) neightet.tet); + setpoint2tet(pc, (tetrahedron) neightet.tet); } // i } // if (cavshells != NULL) - } // k - - mflag = true; // Initialize it. + } // j - if (midfaces != NULL) { + 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 - // 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) { - // Connect the two tets together. + // Found a pair of matched faces in 'toptet' and 'bottet'. bond(toptet, bottet); // Both are interior tets. infect(toptet); @@ -17771,20 +16044,23 @@ 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); } - // Match pairs of subfaces (middle faces), connect top and bottom tets. + // Find other 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 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; + assert(marktested(*midface)); // SELF_CHECK + // Check the neighbors at the edges of this face. + for (j = 0; j < 3 && mflag; j++) { + toptet = *midface; bflag = false; while (1) { // Go to the next face in the same tet. @@ -17794,6 +16070,7 @@ 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. @@ -17807,16 +16084,19 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, if (!bflag) { // assert(marktested(toptet)); // SELF_CHECK if (!facemarked(toptet)) { - fsym(midface, bottet); + fsym(*midface, bottet); + spintet = bottet; while (1) { esymself(bottet); - pf = apex(bottet); - if (pf == pc) break; // Face matched. - if (pmarktested(pf)) { - mflag = false; break; // Not matched - } + 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; + } + } // while (1) if (mflag) { if (marktested(bottet)) { // Connect two tets together. @@ -17828,79 +16108,185 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, markface(toptet); midfaces->newindex((void **) &parytet); *parytet = toptet; - } 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'. } - } + } 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 (midfaces != NULL) - - if (mflag) { - if (midfaces != NULL) { + if (mflag) { if (b->verbose > 2) { printf(" Found %ld middle subfaces.\n", midfaces->objects); } - if (midfaces->objects > maxregionsize) { - maxregionsize = midfaces->objects; - } - // Unmark middle faces. + 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); - assert(facemarked(midface)); // SELF_CHECK - unmarkface(midface); - } - } - } else { - // 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; + 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); } - } - if (i < missingshs->objects) { - // Such subface exist. - recentsh = *parysh; + // 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 + + // Delete old subfaces. + for (i = 0; i < missingshs->objects; i++) { + parysh = (face *) fastlookup(missingshs, i); + shellfacedealloc(subfaces, parysh->sh); + } } else { - assert(0); // Debug this case. + 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); + } + } } - - // Set a tet for searching the new point. - recenttet = firsttopface; - } + midfaces->restart(); + } else { + mflag = true; + } // Delete the temp subfaces. - for (k = 0; k < 2; k++) { - cavshells = (k == 0 ? topshells : botshells); + for (j = 0; j < 2; j++) { + cavshells = (j == 0 ? topshells : botshells); if (cavshells != NULL) { for (i = 0; i < cavshells->objects; i++) { parysh = (face *) fastlookup(cavshells, i); @@ -17913,9 +16299,6 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, if (botshells != NULL) { botshells->restart(); } - if (midfaces != NULL) { - midfaces->restart(); - } return mflag; } @@ -17930,10 +16313,12 @@ 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 i, j, k; + int t1ver; + int i, j; if (b->verbose > 2) { printf(" Carve cavity: %ld old tets.\n", crosstets->objects); @@ -17949,38 +16334,44 @@ 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); - 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; + 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; + } } - } + } // j } - 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; + 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; + } + } } - } + } // j } } // i + // Uninfect collected subfaces. for (i = 0; i < cavetetshlist->objects; i++) { - checksh = * (face *) fastlookup(cavetetshlist, i); - suninfect(checksh); + parysh = (face *) fastlookup(cavetetshlist, i); + suninfect(*parysh); } // Uninfect collected segments. for (i = 0; i < cavetetseglist->objects; i++) { - checkseg = * (face *) fastlookup(cavetetseglist, i); - suninfect(checkseg); + paryseg = (face *) fastlookup(cavetetseglist, i); + suninfect(*paryseg); } // Connect subfaces to new tets. @@ -17996,11 +16387,6 @@ 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; @@ -18014,10 +16400,7 @@ 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); @@ -18031,10 +16414,6 @@ 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; @@ -18052,10 +16431,7 @@ 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(); @@ -18063,6 +16439,9 @@ 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); } @@ -18070,8 +16449,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 (k = 0; k < 2; k++) { - newtets = (k == 0 ? topnewtets : botnewtets); + for (j = 0; j < 2; j++) { + newtets = (j == 0 ? topnewtets : botnewtets); if (newtets != NULL) { for (i = 0; i < newtets->objects; i++) { parytet = (triface *) fastlookup(newtets, i); @@ -18081,20 +16460,17 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } } // i } - } // k + } // j // 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; @@ -18103,9 +16479,12 @@ 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 (k = 0; k < 2; k++) { - newtets = (k == 0 ? topnewtets : botnewtets); + for (j = 0; j < 2; j++) { + newtets = (j == 0 ? topnewtets : botnewtets); if (newtets != NULL) { for (i = 0; i < newtets->objects; i++) { parytet = (triface *) fastlookup(newtets, i); @@ -18113,6 +16492,9 @@ 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); @@ -18135,21 +16517,19 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets) + arraypool *botnewtets, arraypool *missingshbds) { - triface *parytet, neightet; - face checksh; + triface *parytet, neightet, spintet; + face *parysh; 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); @@ -18172,6 +16552,31 @@ 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); @@ -18201,7 +16606,8 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) +void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, + point plane_pb, point plane_pc) { badface *parybf, *prevbf, *nextbf; triface neightet; @@ -18278,7 +16684,7 @@ void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) for (i = 0; i < 5; i++) { if (pmarktest2ed(p[i])) { // A top point has a positive weight. - w[i] = orient3d(plane_pa, plane_pb, plane_pc, p[i]); + w[i] = orient3dfast(plane_pa, plane_pb, plane_pc, p[i]); if (w[i] < 0) w[i] = -w[i]; assert(w[i] != 0); } else { @@ -18294,12 +16700,7 @@ void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) // p[1], p[0], p[2], p[3]. insph = insphere(p[1], p[0], p[2], p[3], p[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]); - } + 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]); @@ -18332,7 +16733,7 @@ void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) // 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) { @@ -18342,7 +16743,7 @@ void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) break; } } - } // if (!b->flipinsert_random) // -L1 + //} // if (!b->flipinsert_random) // Insert the new item between prev and next items. if (prevbf == NULL) { *pqueue = parybf; @@ -18374,19 +16775,20 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, arraypool *botpoints, arraypool *midpoints) { arraypool *crossfaces, *bfacearray; - triface fliptets[5], baktets[2], fliptet, newface; + triface fliptets[6], 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; @@ -18434,9 +16836,6 @@ 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); @@ -18449,14 +16848,13 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, for (i = 0; i < crossfaces->objects; i++) { parytet = (triface *) fastlookup(crossfaces, i); - flipcertify(parytet, &pqueue); + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); } crossfaces->restart(); // The list for temporarily storing unflipable faces. bfacearray = new arraypool(sizeof(triface), 4); - fliploop: fcount = 0; // Count the number of flips. @@ -18518,7 +16916,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, 0, 0); + flip23(fliptets, 1, &fc); // Put the link faces into check list. for (i = 0; i < 3; i++) { eprevesym(fliptets[i], newface); @@ -18545,16 +16943,26 @@ 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, 0, 0); + flip32(fliptets, 1, &fc); // Put the link faces into check list. for (i = 0; i < 3; i++) { esym(fliptets[0], newface); @@ -18588,7 +16996,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, 0, 0); + flip23(fliptets, 1, &fc); // 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. @@ -18607,7 +17015,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, 0, 0); + flip32(fliptets, 1, &fc); // Put the "outer" link faces into check list. // fliptets[0] = [d,e,f,a] // fliptets[1] = [e,d,f,b] @@ -18645,23 +17053,22 @@ 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); + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); } crossfaces->restart(); - if (!b->flipinsert_random) { + if (1) { // 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); + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); } } bfacearray->restart(); @@ -18683,22 +17090,10 @@ 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; @@ -18717,11 +17112,6 @@ 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); @@ -18734,156 +17124,85 @@ 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; + triface searchtet, spintet, neightet; face oldsh, newsh, opensh, *parysh; face casout, casin, neighsh, checksh; - 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; + face neighseg, checkseg; + point pc; + int success; + int t1ver; + int i, j; - 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])); - } + + // 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. spintet = searchtet; while (1) { pc = apex(spintet); if (pmarktested(pc)) { - // 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; + neightet = spintet; + j++; + } fnextself(spintet); if (spintet.tet == searchtet.tet) break; - } // 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. + } + 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. 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, ppt[0]); - setsdest(newsh, ppt[1]); - setsapex(newsh, pc); + setsorg(newsh, org(searchtet)); + setsdest(newsh, dest(searchtet)); + setsapex(newsh, apex(searchtet)); // 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; @@ -18902,384 +17221,491 @@ bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds, sbond1(newsh, casout); sbond1(casin, newsh); } - if (checkseg.sh != NULL) { - ssbond(newsh, checkseg); + 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; } - // 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; + + for (i = 0; i < cavetetvertlist->objects; i++) { + cavpoints->newindex((void **) &parypt); + *parypt = * (point *) fastlookup(cavetetvertlist, i); } + // Add the new point into the point list. + cavpoints->newindex((void **) &parypt); + *parypt = newpt; - // 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; + for (i = 0; i < cavebdrylist->objects; i++) { + cavfaces->newindex((void **) &parytet); + *parytet = * (triface *) fastlookup(cavebdrylist, i); } - // Loop until 'flipstack' is empty. - while (flipstack != NULL) { + for (i = 0; i < caveoldtetlist->objects; i++) { + crosstets->newindex((void **) &parytet); + *parytet = * (triface *) fastlookup(caveoldtetlist, i); + } - // 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); + cavetetvertlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); - // 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))); + // 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); + + // 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; } - // 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); - } + } + + 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; + } + } // 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); } - 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); + } - } // if (casout.sh == NULL) - - } // while (flipstack != NULL) - - // Uninfect all new subfaces. - for (i = 0; i < newshs->objects; i++) { - parysh = (face *) fastlookup(newshs, i); - suninfect(*parysh); + // 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 (b->verbose > 2) { - printf(" Created %ld new subfaces.\n", newshs->objects); + // 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; + } } - fillregioncount++; - return true; + caveencshlist->restart(); + caveencseglist->restart(); + + return 1; } /////////////////////////////////////////////////////////////////////////////// // // // 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() +void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, + arraypool *cavfaces, arraypool *cavshells, + arraypool *newtets, arraypool *crosstets, + arraypool *misfaces) { - triface searchtet; - face splitsh; - face *paryseg, sseg; - point steinpt, pa, pb, pc; + triface searchtet, spintet; + face splitseg, *paryseg; + point steinpt, pa, pb, refpt; insertvertexflags ivf; - REAL auv[2], buv[2], newuv[2], t; - int fmark, fid, eid; - int loc; // iloc, sloc; - int s, i; - - // The mesh is a CDT. - assert(subsegstack->objects == 0l); // SELF_CHECK + enum interresult dir; + long baknum = points->items; + int t1ver; + int i; - // Create a new point. - makepoint(&steinpt, FREEFACETVERTEX); + if (b->verbose > 2) { + printf(" Refining region at edge (%d, %d, %d).\n", + pointmark(sorg(splitsh)), pointmark(sdest(splitsh)), + pointmark(sapex(splitsh))); + } - // 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); - - 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 { - for (i = 0; i < 3; i++) { - steinpt[i] = (pa[i] + pb[i] + pc[i]) / 3.0; - } + // Create a new point. + makepoint(&steinpt, FREEFACETVERTEX); + for (i = 0; i < 3; i++) { + steinpt[i] = 0.5 * (pa[i] + pb[i]); } - // 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. + 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; - // 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); + point2tetorg(pa, searchtet); // Start location from it. + ivf.iloc = (int) OUTSIDE; - 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); + 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--; + } else { + assert(0); } - - st_segref_count++; } else { st_facref_count++; + if (steinerleft > 0) steinerleft--; } - if (steinerleft > 0) steinerleft--; - // Do flip to recover Delaunayniess. - lawsonflip3d(steinpt, 2, 0, 0, 0); + while (subsegstack->objects > 0l) { + // seglist is used as a stack. + subsegstack->objects--; + paryseg = (face *) fastlookup(subsegstack, subsegstack->objects); + splitseg = *paryseg; - // Some vertices may be queued, recover them. - if (subvertstack->objects > 0l) { - assert(0); //delaunizevertices(); - } + // Check if this segment has been recovered. + sstpivot1(splitseg, searchtet); + if (searchtet.tet != NULL) continue; + + // 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); + } + } + } // while - // Some subsegments may be queued, recover them. - if (subsegstack->objects > 0l) { - delaunizesegments(); + if (b->verbose > 2) { + printf(" Added %ld Steiner points.\n", points->items - baknum); } } /////////////////////////////////////////////////////////////////////////////// // // -// constrainedfacets() Recover subfaces saved in 'subfacestack'. // +// constrainedfacets() Recover constrained facets in a CDT. // +// // +// All unrecovered subfaces are queued in 'subfacestack'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -19290,16 +17716,14 @@ 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; - face searchsh, neighsh, *parysh; - face checkseg, *paryseg; - point refpt, *parypt; + triface searchtet, neightet, crossedge; + face searchsh, *parysh, *parysh1; + face *paryseg; + point *parypt; enum interresult dir; - bool success; int facetcount; - //int bakhullsize; - int crossflag; + int success; + int t1ver; int i, j; // Initialize arrays. @@ -19317,245 +17741,184 @@ 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; // Skip a dead subface. + if (searchsh.sh[3] == NULL) continue; // It is dead. + if (isshtet(searchsh)) continue; // It is recovered. - 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; - } + // 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; } } - 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); - } + } + 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(" 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 { - // Restore old tets and delete new tets. - restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); - // Set a handle for searching subface. - //recentsh = searchsh; - } + 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 { - // 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); + restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets, + tg_missingshbds); } - tg_topshells->restart(); } else { - // Search a handle for searching tetrahedron. - recenttet = searchtet; - } - } + // 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 - // 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) { + // 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); + } else { + // Collision! Should not happen. + assert(0); + } + } + 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); + } - 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) + // 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 (neightet.tet == NULL) - } // while (subfacstack->objects > 0l) + 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); // Delete arrays. delete tg_crosstets; @@ -19590,15 +17953,14 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) // Statistics. long bakfillregioncount; long bakcavitycount, bakcavityexpcount; + long bakseg_ref_count; if (!b->quiet) { printf("Constrained Delaunay...\n"); } - //if (!b->psc) { - // Only identify acute vertex for PLC inputs. - markacutevertices(); - //} + // Identify acute vertex for PLC inputs. + markacutevertices(); if (b->verbose) { printf(" Delaunizing segments.\n"); @@ -19606,37 +17968,25 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) checksubsegflag = 1; - // 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; - } + // 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; } // Recover non-Delaunay segments. delaunizesegments(); if (b->verbose) { - printf(" %ld Steiner points.\n", st_segref_count); + printf(" Inserted %ld Steiner points.\n", st_segref_count); } tv = clock(); @@ -19645,17 +17995,13 @@ 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(); @@ -19684,10 +18030,10 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) } printf(".\n"); } - if (st_segref_count + st_facref_count > 0) { + if (st_segref_count + st_facref_count - bakseg_ref_count > 0) { printf(" Inserted %ld (%ld, %ld) refine points.\n", - st_segref_count + st_facref_count, st_segref_count, - st_facref_count); + st_segref_count + st_facref_count - bakseg_ref_count, + st_segref_count - bakseg_ref_count, st_facref_count); } } } @@ -19711,10 +18057,6 @@ 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, @@ -19722,45 +18064,23 @@ 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; - - rejflag = 0; + int rejflag = 0; + int i; 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]. - 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++) { + 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. intflag = tri_edge_test(pe, pd, tmppts[i], fc->seg[0], fc->seg[1], NULL, 1, types, poss); if (intflag == 2) { @@ -19775,12 +18095,7 @@ 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]; @@ -19790,177 +18105,32 @@ 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]; - } - 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) + } // if (tmppts[0] != dummypoint) + } // i } else if (fliptype == 2) { // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c] if (pc != dummypoint) { - 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. - } + // 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 (!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. - } + } 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 (!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 ((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 (pc != dummypoint) } } // if (fc->seg[0] != NULL) @@ -19978,11 +18148,7 @@ 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. @@ -20123,7 +18289,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,#,#]. // +// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed. // // // // 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 // @@ -20135,77 +18301,45 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) { triface *abtets, spintet; - face checkseg, *paryseg; - int counter; // DEBUG + int t1ver; 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. - 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; - } + 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; } - //} + } 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) { - counter += elemcounter(spintet); - i++; + n++; fnextself(spintet); if (spintet.tet == flipedge->tet) break; } - //assert(i >= 3); - if (i < 3) { + //assert(n >= 3); + if (n < 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 given limit (-YY__). - skpflipstarcount++; + // The star size exceeds the limit. return 0; // Do not flip it. } @@ -20216,8 +18350,7 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) i = 0; while (1) { abtets[i] = spintet; - //marktest(abtets[i]); // Marktest it (in Star(ab)). - setelemcounter(abtets[i], 1); + setelemcounter(abtets[i], 1); i++; fnextself(spintet); if (spintet.tet == flipedge->tet) break; @@ -20228,45 +18361,33 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) nn = flipnm(abtets, n, 0, 0, fc); - if (nn == 2) { - // Edge is flipped. - if (b->verbose > 2) { - printf(" Edge is removed.\n"); - } - } else { + if (nn > 2) { // 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 == 2; + return nn; } /////////////////////////////////////////////////////////////////////////////// // // // removefacebyflips() Remove a face by flips. // // // +// Return 1 if the face is removed. Otherwise, return 0. // +// // // ASSUMPTIONS: // // - 'flipface' must not be a hull face. // // // @@ -20274,45 +18395,25 @@ 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) { - tspivot(*flipface, checksh); - if (checksh.sh != NULL) { - if (b->verbose > 2) { - printf(" Can't flip a subface.\n"); - } + if (issubface(*flipface)) { 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); @@ -20333,109 +18434,12 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) if (reducflag) { // A 2-to-3 flip is found. - 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++; - } - } + flip23(fliptets, 0, fc); + return 1; } 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; - } + // Try to flip the selected edge of this face. + if (removeedgebyflips(&flipedge, fc) == 2) { + return 1; } } @@ -20450,7 +18454,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 flipped. // +// faces or edges are needed to be removed. // // // // 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 // @@ -20461,38 +18465,24 @@ 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; - 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)); - } - + enum interresult dir; 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); - assert(org(*searchtet) == startpt); // SELF_CHECK - dir = finddirection(searchtet, endpt, 1); + dir = finddirection(searchtet, endpt); if (dir == ACROSSVERT) { if (dest(*searchtet) == endpt) { return 1; // Edge is recovered. } else { - // A PLC problem, or there is a Steiner point. - terminatetetgen(3); //assert(0); // Debug + terminatetetgen(3); // // It may be a PLC problem. } } @@ -20511,175 +18501,178 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, continue; } } else { - terminatetetgen(3); //assert(0); // A PLC problem. + terminatetetgen(3); // It may be a PLC problem. } // The edge is missing. if (fullsearch) { - - 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); + // 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); + } 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; - } 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); - } - 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 + break; // for loop + } else { + dir = DISJOINT; + pos = 0; } - // No intersection. Rotate to the next tet at the edge. - fnextself(*searchtet); - } // while (1) // Loop I-I-I - } + } // 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, 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) { + // 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. 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 - - if (success) { - // One of intersecting faces/edges is flipped. - continue; } - } // if (0) + } // while (1) // Loop I-I + + if (success) { + // One of intersecting faces/edges is flipped. + continue; + } } // if (fullsearch) @@ -20688,7 +18681,6 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, } // while (1) // Loop I - // The edge is not recovered. return 0; } @@ -20701,24 +18693,8 @@ 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]. // -// // -// 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. // -// // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -20727,23 +18703,15 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, { triface worktet, *parytet; triface faketet1, faketet2; - point pa, pb, pc, pd; - point p1, p2, p3; - point steinerpt; + point pc, pd, 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) @@ -20752,12 +18720,10 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, // initialize the list of 2n boundary faces. for (i = 0; i < n; i++) { - eprev(abtets[i], worktet); - esymself(worktet); // [a,p_i,p_i+1]. + edestoppo(abtets[i], worktet); // [p_i,p_i+1,a] cavetetlist->newindex((void **) &parytet); *parytet = worktet; - enext(abtets[i], worktet); - esymself(worktet); // [p_i,b,p_i+1]. + eorgoppo(abtets[i], worktet); // [p_i+1,p_i,b] cavetetlist->newindex((void **) &parytet); *parytet = worktet; } @@ -20772,10 +18738,7 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, } for (i = 0; i < cavetetlist->objects; i++) { parytet = (triface *) fastlookup(cavetetlist, i); - p1 = org(*parytet); - p2 = dest(*parytet); - p3 = apex(*parytet); - ori = orient3d(p2, p1, p3, sampt); + ori = orient3d(dest(*parytet), org(*parytet), apex(*parytet), sampt); if (i == 0) { minvol = ori; } else { @@ -20794,10 +18757,6 @@ 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; } @@ -20809,11 +18768,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, pa, dummypoint); + setvertices(faketet1, pd, pc, org(abtets[0]), dummypoint); cavetetlist->newindex((void **) &parytet); *parytet = faketet1; maketetrahedron(&faketet2); - setvertices(faketet2, pc, pd, pb, dummypoint); + setvertices(faketet2, pc, pd, dest(abtets[0]), dummypoint); cavetetlist->newindex((void **) &parytet); *parytet = faketet2; @@ -20846,9 +18805,6 @@ 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; } @@ -20865,28 +18821,23 @@ 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.sloc = 0; - ivf.sbowywat = 0; - ivf.splitbdflag = 0; - ivf.validflag = 0; - ivf.respectbdflag = 0; - ivf.assignmeshsize = 0; + ivf.assignmeshsize = b->metric; + if (ivf.assignmeshsize) { + // Search the tet containing 'steinerpt' for size interpolation. + locate(steinerpt, &(abtets[0]), 0); + worktet = abtets[0]; + } // Insert the new point into the tetrahedralization T. // Note that T is convex (nonconvex = 0). - loc = insertvertex(steinerpt, &worktet, NULL, NULL, &ivf); - - if (loc == (int) INSTAR) { + if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) { // The vertex has been inserted. - st_volref_count++; //st_inpoly_count++; + st_volref_count++; if (steinerleft > 0) steinerleft--; return 1; } else { - // The Steiner point is too close to an existing vertex. Reject it. + // Not inserted. pointdealloc(steinerpt); return 0; } @@ -20902,16 +18853,14 @@ 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 loc; + int t1ver; int i; startpt = sorg(*misseg); @@ -20923,12 +18872,7 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) // Try to recover the edge by adding Steiner points. point2tetorg(startpt, searchtet); - assert(org(searchtet) == startpt); // SELF_CHECK - dir = finddirection(&searchtet, endpt, 1); - assert(dir != ACROSSVERT); - - // Get the first intersecting face/edge. - assert(!ishulltet(searchtet)); + dir = finddirection(&searchtet, endpt); enextself(searchtet); //assert(apex(searchtet) == startpt); @@ -20936,13 +18880,8 @@ 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); @@ -20958,8 +18897,9 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) } else { assert(dir == ACROSSEDGE); // PLC check. - tsspivot1(searchtet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(searchtet)) { + face checkseg; + tsspivot1(searchtet, checkseg); printf("Found two segments intersect each other.\n"); pa = farsorg(*misseg); pb = farsdest(*misseg); @@ -20996,8 +18936,6 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) abtets[i] = spintet; fnextself(spintet); } - assert(apex(abtets[0]) == startpt); - assert(apex(abtets[endi]) == endpt); success = 0; @@ -21054,11 +18992,15 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) printf(" Splitting segment (%d, %d)\n", pointmark(startpt), pointmark(endpt)); } + steinerpt = NULL; - if (endi == -1) { + // The following code is skipped. + if ((endi == -1) && 0) { // 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); @@ -21078,17 +19020,6 @@ 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); @@ -21131,11 +19062,9 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 0; - loc = insertvertex(steinerpt, &searchtet, &splitsh, misseg, &ivf); - - if (loc != ivf.iloc) { - if (loc == (int) NEARVERTEX) { + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { + if (ivf.iloc == (int) NEARVERTEX) { // The vertex is rejected. Too close to an existing vertex. pointdealloc(steinerpt); steinerpt = NULL; @@ -21143,9 +19072,7 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) assert(0); // Unknown case. } } - } else { // if (endi > 0) - steinerpt = NULL; - } + } // if ((endi == -1) && 0) if (steinerpt == NULL) { // Split the segment at its midpoint. @@ -21167,11 +19094,10 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 0; - loc = insertvertex(steinerpt, &searchtet, &splitsh, misseg, &ivf); - - assert(loc != (int) ONVERTEX); - assert(loc != (int) NEARVERTEX); + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { + assert(0); + } } // if (endi > 0) // Save this Steiner point (for removal). @@ -21189,17 +19115,11 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) // // // recoversegments() Recover all segments. // // // -// 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. // +// All segments need to be recovered are in 'subsegstack'. // // // -// 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'. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -21207,11 +19127,12 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, int steinerflag) { triface searchtet, spintet; - face sseg, checkseg, *paryseg; + face sseg, *paryseg; point startpt, endpt; int success; - - long bak_inpoly_count = st_volref_count; //st_inpoly_count; + int t1ver; + long bak_inpoly_count = st_volref_count; + long bak_segref_count = st_segref_count; if (b->verbose > 1) { printf(" Recover segments [%s level = %2d] #: %ld.\n", @@ -21260,8 +19181,6 @@ 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. @@ -21300,6 +19219,10 @@ 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); + } } } @@ -21319,33 +19242,26 @@ 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); - assert(org(*searchtet) == fc.fac[i]); // SELF_CHECK - dir = finddirection(searchtet, fc.fac[(i+1)%3], 1); + dir = finddirection(searchtet, fc.fac[(i+1)%3]); //assert(dir == ACROSSVERT); assert(dest(*searchtet) == fc.fac[(i+1)%3]); // Search the face [a,b,c] @@ -21382,14 +19298,13 @@ 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]. - eprev(spintet, flipedge); - esymself(flipedge); - enextself(flipedge); // [d,e,a,b]. + edestoppo(spintet, flipedge); // [d,e,a,b] if (searchsh != NULL) { // Check if [e,d] is a segment. - tsspivot1(flipedge, checkseg); - if (checkseg.sh != NULL) { - if (!b->quiet) { + if (issubseg(flipedge)) { + if (!b->quiet) { + face checkseg; + tsspivot1(flipedge, checkseg); printf("Found a segment and a subface intersect.\n"); pd = farsorg(checkseg); pe = farsdest(checkseg); @@ -21397,16 +19312,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[0] == 0) { + if (poss[1] == 0) { touchpt = pd; // pd is a coplanar vertex. } else { touchpt = pe; // pe is a coplanar vertex. @@ -21414,17 +19329,11 @@ 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. - - sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat); - setpointtype(touchpt, FREEFACETVERTEX); + sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat); st_volref_count--; st_facref_count++; // Queue this vertex for removal. @@ -21438,12 +19347,6 @@ 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; } @@ -21502,13 +19405,13 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) { triface searchtet, neightet, spintet; face searchsh, neighsh, neineish, *parysh; - face bdsegs[3], checkseg; + face bdsegs[3]; point startpt, endpt, apexpt, *parypt; point steinerpt; enum interresult dir; insertvertexflags ivf; int success; - int loc; + int t1ver; int i, j; if (b->verbose > 1) { @@ -21532,7 +19435,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))); } @@ -21552,8 +19455,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) startpt = sorg(searchsh); endpt = sdest(searchsh); point2tetorg(startpt, searchtet); - assert(org(searchtet) == startpt); // SELF_CHECK - dir = finddirection(&searchtet, endpt, 1); + dir = finddirection(&searchtet, endpt); if (dir == ACROSSVERT) { if (dest(searchtet) == endpt) { success = 1; @@ -21573,13 +19475,8 @@ 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]); @@ -21588,8 +19485,6 @@ 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; @@ -21601,11 +19496,7 @@ 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 (shellmark(bdsegs[j]) == -2) { - if (b->verbose > 2) { - printf(" Remove a temp segment (%d, %d).\n", - pointmark(sorg(bdsegs[j])), pointmark(sdest(bdsegs[j]))); - } + if (smarktest2ed(bdsegs[j])) { spivot(bdsegs[j], neineish); assert(neineish.sh != NULL); //if (neineish.sh != NULL) { @@ -21617,7 +19508,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) { @@ -21627,7 +19518,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) fnextself(spintet); if (spintet.tet == searchtet.tet) break; } - //} + //} shellfacedealloc(subsegs, bdsegs[j].sh); } } // j @@ -21653,10 +19544,10 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 0; - loc = insertvertex(steinerpt, &searchtet, &searchsh, NULL, &ivf); - assert(loc != (int) OUTSIDE); - + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { + assert(0); + } // Save this Steiner point (for removal). // Re-use the array 'subvertstack'. subvertstack->newindex((void **) &parypt); @@ -21681,11 +19572,7 @@ 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 (shellmark(bdsegs[j]) == -2) { - if (b->verbose > 2) { - printf(" Remove a temp segment (%d, %d).\n", - pointmark(sorg(bdsegs[j])), pointmark(sdest(bdsegs[j]))); - } + if (smarktest2ed(bdsegs[j])) { spivot(bdsegs[j], neineish); assert(neineish.sh != NULL); //if (neineish.sh != NULL) { @@ -21697,7 +19584,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) { @@ -21707,7 +19594,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) fnextself(spintet); if (spintet.tet == neightet.tet) break; } - //} + //} shellfacedealloc(subsegs, bdsegs[j].sh); } } // j @@ -21743,10 +19630,10 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 0; - loc = insertvertex(steinerpt, &searchtet, &searchsh, NULL, &ivf); - assert(loc != (int) OUTSIDE); - + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { + assert(0); + } // Save this Steiner point (for removal). // Re-use the array 'subvertstack'. subvertstack->newindex((void **) &parypt); @@ -21762,11 +19649,6 @@ 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; @@ -21800,32 +19682,27 @@ 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. - enextself(searchtet); - esymself(searchtet); + enextesymself(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. - for (i = 0; i < 3; i++) { - pt = org(searchtet); + j = (searchtet.ver & 3); // The current vertex index. + for (i = 1; i < 4; i++) { + pt = (point) searchtet.tet[4 + ((j + i) % 4)]; pinfect(pt); vertlist->newindex((void **) &parypt); *parypt = pt; - enextself(searchtet); } } @@ -21840,14 +19717,13 @@ 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). @@ -21871,7 +19747,6 @@ 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) { @@ -21889,7 +19764,6 @@ 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); @@ -21909,16 +19783,6 @@ 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++) { @@ -21982,13 +19846,13 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) // Search for the edge [e1, e2]. point2tetorg(e1, *tedge); - finddirection(tedge, e2, 1); + finddirection(tedge, e2); if (dest(*tedge) == e2) { return 1; } else { // Search for the edge [e2, e1]. point2tetorg(e2, *tedge); - finddirection(tedge, e1, 1); + finddirection(tedge, e1); if (dest(*tedge) == e1) { esymself(*tedge); return 1; @@ -21998,8 +19862,7 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) // Go to the link face of e1. point2tetorg(e1, searchtet); - enextself(searchtet); - esymself(searchtet); + enextesymself(searchtet); //assert(oppo(searchtet) == e1); assert(cavetetlist->objects == 0l); // It will re-use this list. @@ -22009,9 +19872,7 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) pt = apex(searchtet); if (pt == e2) { // Found. 'searchtet' is [#,#,e2,e1]. - enext(searchtet, *tedge); - esymself(*tedge); - eprevself(*tedge); // [e1,e2,#,#]. + eorgoppo(searchtet, *tedge); // [e1,e2,#,#]. return 1; } enextself(searchtet); @@ -22024,9 +19885,7 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) pt = apex(neightet); if (pt == e2) { // Found. 'neightet' is [#,#,e2,e1]. - enext(neightet, *tedge); - esymself(*tedge); - eprevself(*tedge); // [e1,e2,#,#]. + eorgoppo(neightet, *tedge); // [e1,e2,#,#]. return 1; } @@ -22051,9 +19910,7 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) pt = apex(neightet); if (pt == e2) { // Found. 'neightet' is [#,#,e2,e1]. - enext(neightet, *tedge); - esymself(*tedge); - eprevself(*tedge); // [e1,e2,#,#]. + eorgoppo(neightet, *tedge); done = 1; } else { infect(neightet); @@ -22085,7 +19942,6 @@ 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; @@ -22093,13 +19949,9 @@ 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) { @@ -22121,13 +19973,12 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) } } else { point2tetorg(startpt, searchtet); - dir = finddirection(&searchtet, *pendpt, 1); + dir = finddirection(&searchtet, *pendpt); } if (dir == ACROSSVERT) { if (dest(searchtet) == *pendpt) { // Do not flip a segment. - tsspivot1(searchtet, checkseg); - if (checkseg.sh == NULL) { + if (!issubseg(searchtet)) { n = removeedgebyflips(&searchtet, &fc); if (n == 2) { reduceflag = 1; @@ -22158,10 +20009,6 @@ 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; } @@ -22169,28 +20016,13 @@ 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 cur-// -// rent tetrahedralization (T) by a sequence of elementary flips. // +// This routine attempts to remove the given vertex 'rempt' (p) from the // +// tetrahedralization (T) by a sequence of 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. // // // @@ -22203,10 +20035,12 @@ 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); @@ -22232,23 +20066,6 @@ 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; @@ -22276,7 +20093,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // Inverted elements. getvertexstar(1, steinerpt, cavetetlist, NULL, NULL); if (cavetetlist->objects == 2) { - printf("to be continued..."); + printf("to be continued...\n"); assert(0); } else { assert(0); // Unknown cases. @@ -22399,7 +20216,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) if (!removeflag) { if (vt == FREESEGVERTEX) { - // Check if the edge [lpr, rpt] exists. + // Check if the edge [lpt, 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. @@ -22450,10 +20267,6 @@ 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; } @@ -22510,7 +20323,8 @@ 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, 0, 0); + flip41(fliptets, 1, &fc); //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 @@ -22535,18 +20349,12 @@ 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); - // 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 + //flip32(&(fliptets[3]), 1, 0, 0); + flip32(&(fliptets[3]), 1, &fc); // 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, 0, 0); + flip41(fliptets, 1, &fc); //recenttet = fliptets[0]; } else if (loc == ONEDGE) { // Let the original edge be [e,d] and p is in [e,d]. Assume there are n @@ -22590,7 +20398,8 @@ 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, 0, 0); + flip23(wrktets, 1, &fc); // Save the new tet [e,d,p,p_0] (degenerated). fliptets[n] = wrktets[2]; // Save the new tet [e,d,p_0,p_1]. @@ -22613,7 +20422,8 @@ 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, 0, 0); + flip32(wrktets, 1, &fc); // 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 @@ -22638,7 +20448,8 @@ 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, 0, 0); + flip41(wrktets, 1, &fc); // 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]; @@ -22664,7 +20475,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // Insert the new segment. point2tetorg(lpt, searchtet); - finddirection(&searchtet, rpt, 1); + finddirection(&searchtet, rpt); assert(dest(searchtet) == rpt); sstbond1(rightseg, searchtet); spintet = searchtet; @@ -22733,10 +20544,14 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // // // suppresssteinerpoint() Suppress a Steiner point. // // // -// 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. // +// 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'. // // // -// The list of volume Steiner points is returned in 'suppsteinerptlist'. // +// 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(). // // // /////////////////////////////////////////////////////////////////////////////// @@ -22750,8 +20565,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); @@ -22803,13 +20618,12 @@ 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; } } - bak_supp_steiners = suppsteinerptlist->objects; + // Count the number of created Steiner points. + long bak_supp_steiners = suppsteinerptlist->objects; if (vt == FREESEGVERTEX) { // Get all subfaces at the left segment [lpt, steinerpt]. @@ -22866,6 +20680,7 @@ 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'. @@ -22873,6 +20688,9 @@ 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; @@ -22893,7 +20711,7 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) } // i cavesegshlist->restart(); - // Remove p from the segment. + // Remove p from the segment (or the facet). slawson = 0; // Do not do flip afterword. if (vt == FREESEGVERTEX) { spivot(rightseg, parentsh); // 'rightseg' has p as its origin. @@ -23061,7 +20879,7 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) } else { // vt == FREEFACETVERTEX st_facref_count--; } - if (steinerleft > 0) steinerleft++; + if (steinerleft > 0) steinerleft++; // We've removed a Steiner points. if (b->verbose > 2) { printf(" Duplicated %ld Steiner points.\n", @@ -23086,7 +20904,7 @@ int tetgenmesh::suppresssteinerpoints() optparameters opm; REAL ori; int bak_fliplinklevel; - int remcount, smtcount; + int remcount, smtcount, ivcount; int count, nt; int i, j; @@ -23156,6 +20974,8 @@ int tetgenmesh::suppresssteinerpoints() if (suppsteinerptlist->objects == 0l) { b->fliplinklevel = bak_fliplinklevel; + // The mesh contain no inverted (or degenerrated) tets now. + checkinverttetflag = 0; return remcount; } @@ -23169,6 +20989,7 @@ 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); @@ -23179,7 +21000,7 @@ int tetgenmesh::suppresssteinerpoints() for (j = 0; j < cavetetlist->objects; j++) { parytet = (triface *) fastlookup(cavetetlist, j); ppt = (point *) &(parytet->tet[4]); - ori = orient3d(ppt[1], ppt[0], ppt[2], ppt[3]); + ori = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]); if (j == 0) { opm.initval = ori; } else { @@ -23189,6 +21010,9 @@ 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 @@ -23206,9 +21030,6 @@ 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); @@ -23217,6 +21038,44 @@ 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; } @@ -23239,17 +21098,11 @@ 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"); @@ -23261,29 +21114,17 @@ void tetgenmesh::recoverboundary(clock_t& tv) misseglist = new arraypool(sizeof(face), 8); bdrysteinerptlist = new arraypool(sizeof(point), 8); - 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; - } + // 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; } // The init number of missing segments. @@ -23293,8 +21134,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) { @@ -23333,7 +21174,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) } if (misseglist->objects > 0) { - // There are missing segments. Increase the fliplevel. + // Second, trying to recover segments by doing more flips (fullsearch). nit = 0; while (misseglist->objects > 0) { ms = misseglist->objects; @@ -23343,7 +21184,6 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); - // Recover the missing segments by doing more flips. recoversegments(misseglist, 1, 0); if (misseglist->objects < ms) { @@ -23363,7 +21203,8 @@ void tetgenmesh::recoverboundary(clock_t& tv) } if (misseglist->objects > 0) { - // There are missing segments. Add Steiner points in volume. + // Third, trying to recover segments by doing more flips (fullsearch) + // and adding Steiner points in the volume. nit = 0; while (misseglist->objects > 0) { ms = misseglist->objects; @@ -23373,7 +21214,6 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); - // Recover the missing segments (with Steiner points). recoversegments(misseglist, 1, 1); if (misseglist->objects < ms) { @@ -23392,7 +21232,8 @@ void tetgenmesh::recoverboundary(clock_t& tv) } if (misseglist->objects > 0) { - // There are missing segments. Add Steiner points to split them. + // Last, trying to recover segments by doing more flips (fullsearch), + // and adding Steiner points in the volume, and splitting segments. long bak_inpoly_count = st_volref_count; //st_inpoly_count; for (i = 0; i < misseglist->objects; i++) { subsegstack->newindex((void **) &paryseg); @@ -23400,7 +21241,6 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); - // Recover the missing segments (with Steiner points). recoversegments(misseglist, 1, 2); if (b->verbose) { @@ -23479,6 +21319,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) while (1) { recoversubfaces(misshlist, 0); + if (misshlist->objects > 0) { if (b->fliplinklevel >= 0) { break; @@ -23554,7 +21395,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) if ((bdrysteinerptlist->objects > 0) && (b->nobisect_param > 0)) { // -Y1 - bak_supp_count = 0; + long bak_supp_count = suppsteinerptlist->objects; b->fliplinklevel = 100000; // Unlimited flip levels. do { // Suppress boundary Steiner points. @@ -23562,9 +21403,12 @@ 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); @@ -23572,13 +21416,15 @@ void tetgenmesh::recoverboundary(clock_t& tv) } while (bdrysteinerptlist->objects > 0); if (b->verbose) { printf(" Suppressed %ld Steiner points from boundary.\n", - bak_supp_count); + suppsteinerptlist->objects - 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; @@ -23599,10 +21445,11 @@ void tetgenmesh::recoverboundary(clock_t& tv) // // /////////////////////////////////////////////////////////////////////////////// + void tetgenmesh::carveholes() { - arraypool *tetarray; - triface tetloop, neightet, hulltet, *parytet; + arraypool *tetarray, *hullarray; + triface tetloop, neightet, hulltet, *parytet, *parytet1; triface openface, casface; triface *regiontets; face checksh, casingout, casingin, *parysh; @@ -23611,12 +21458,15 @@ 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 i, j; + int t1ver; + int i, j, k; tetrahedron ptr; + shellface sptr; if (!b->quiet) { printf("Removing exterior tetrahedra ...\n"); @@ -23624,10 +21474,15 @@ void tetgenmesh::carveholes() // Initialize the pool of exterior tets. tetarray = new arraypool(sizeof(triface), 10); - regiontets = NULL; + hullarray = new arraypool(sizeof(triface), 10); + regiontets = NULL; + regioncount = 0; maxattr = 0; // Choose a small number here. - attrnum = in->numberoftetrahedronattributes; + //attrnum = in->numberoftetrahedronattributes; + attrnum = numelemattrib - (b->regionattrib > 0); + // Comment: The element region marker is at the end of the list of + // the element attributes. // Mark as infected any unprotected hull tets. tetrahedrons->traversalinit(); @@ -23636,29 +21491,71 @@ void tetgenmesh::carveholes() while (tetloop.tet != (tetrahedron *) NULL) { if ((point) tetloop.tet[7] == dummypoint) { // Is this side protected by a subface? - tspivot(tetloop, checksh); - if (checksh.sh == NULL) { + if (!issubface(tetloop)) { 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, 1); // randflag = 1; + loc = locate(&(in->holelist[i]), &neightet, 0); if (loc != OUTSIDE) { - infect(neightet); - tetarray->newindex((void **) &parytet); - *parytet = neightet; + // 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)) } else { // A hole point locates outside of the convex hull. if (!b->quiet) { @@ -23666,7 +21563,7 @@ void tetgenmesh::carveholes() printf("lies outside the convex hull.\n"); } } - } + } // i } if (b->regionattrib && (in->numberofregions > 0)) { // If has -A option. @@ -23678,7 +21575,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, 1); // randflag = 1; + loc = locate(&(in->regionlist[i]), &neightet, 0); if (loc != OUTSIDE) { regiontets[i/5] = neightet; if ((int) in->regionlist[i + 3] > maxattr) { @@ -23694,58 +21591,61 @@ 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); - 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)) { + // 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. infect(neightet); - 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; + 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; + //} + } } - hullsize--; } else { - 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). + // Both sides of this face are in exterior. + // Check if there is a subface. + if (issubface(neightet)) { + tspivot(neightet, checksh); 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. @@ -23760,65 +21660,75 @@ void tetgenmesh::carveholes() } } - // 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(); // Re-use it for new hull tets. +if (!b->convex) { // no -c option // 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); + 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; } - 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); + } // j } - tetloop.tet = tetrahedrontraverse(); - } + } // 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); + tetrahedrondealloc(parytet->tet); + } // i + + tetarray->restart(); + if (subfacstack->objects > 0) { // Remove all subfaces which do not attach to any tetrahedron. @@ -23883,6 +21793,7 @@ void tetgenmesh::carveholes() subfacstack->restart(); } + // Some vertices may be not belong to any tet. Mark them. delvertcount = unuverts; delsteinercount = 0l; @@ -23950,12 +21861,10 @@ void tetgenmesh::carveholes() } } - // Update the hull size. - hullsize += tetarray->objects; // Connect new hull tets. - for (i = 0; i < tetarray->objects; i++) { - parytet = (triface *) fastlookup(tetarray, i); + for (i = 0; i < hullarray->objects; i++) { + parytet = (triface *) fastlookup(hullarray, i); hulltet = *parytet; for (j = 0; j < 3; j++) { esym(hulltet, neightet); @@ -23980,13 +21889,118 @@ void tetgenmesh::carveholes() } } - // Set region attributes (when has -A and -AA options). +} 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); + } + } + 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) { @@ -24007,81 +22021,61 @@ void tetgenmesh::carveholes() if (b->varvolume) { // If has -a option. setvolumebound(tetloop.tet, volume); } - 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)) { + 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) { 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); - } - } } - } // ver + } // k } // j regioncount++; } // if (regiontets[i/5].tet != NULL) } // i } - 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); + // 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)) { // Is this side protected by a subface? - tspivot(tetloop, checksh); + tspivot(neightet, checksh); if (checksh.sh == NULL) { - // 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); - } + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; } - } // loc - } - attr++; // Increase the attribute. - regioncount++; - } // if (!infected(tetloop)) - tetloop.tet = tetrahedrontraverse(); + } + } // k + } // j + attr++; // Increase the attribute. + regioncount++; } - // Until here, every tet has a region attribute. + tetloop.tet = tetrahedrontraverse(); } + // Until here, every tet has a region attribute. // Uninfect processed tets. tetrahedrons->traversalinit(); @@ -24091,8 +22085,6 @@ void tetgenmesh::carveholes() tetloop.tet = tetrahedrontraverse(); } - // Mesh elements contain region attributes now. - in->numberoftetrahedronattributes++; if (b->verbose) { assert(regioncount > 0); @@ -24102,34 +22094,47 @@ void tetgenmesh::carveholes() 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) { - flippush(flipstack, &tetloop); + fsym(tetloop, neightet); + flippush(flipstack, &neightet); } tetloop.tet = alltetrahedrontraverse(); } + flipconstraints fc; + fc.enqflag = 2; + // Peel "slivers" off the hull. - lawsonflip3d(NULL, 4, 1, 0, 0); + sliver_peel_count = lawsonflip3d(&fc); - if (b->verbose && (opt_sliver_peels > 0l)) { - printf(" Peeled %ld hull slivers.\n", opt_sliver_peels); + if (b->verbose) { + if (sliver_peel_count > 0l) { + printf(" Removed %ld hull slivers.\n", sliver_peel_count); + } } + unflipqueue->restart(); + +} // if (!b->convex) + } /////////////////////////////////////////////////////////////////////////////// @@ -24153,6 +22158,7 @@ void tetgenmesh::reconstructmesh() REAL angtol, ang; int eextras, marker = 0; int bondflag; + int t1ver; int idx, i, j, k; if (!b->quiet) { @@ -24161,12 +22167,17 @@ 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; i++) { + for (i = 0; i < in->numberofpoints + 1; i++) { + setpointtype(idx2verlist[i], VOLVERTEX); // initial type. ver2tetarray[i] = NULL; } @@ -24176,7 +22187,6 @@ 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]); @@ -24224,7 +22234,6 @@ 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' @@ -24410,51 +22419,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) - } - tetloop.tet = tetrahedrontraverse(); + } + 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(); + } // Connect subfaces together. subfaces->traversalinit(); @@ -24488,9 +22497,6 @@ 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) { @@ -24554,85 +22560,82 @@ 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); + // 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; - } 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); + } + 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); } - 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); + 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); + } // Remember the number of input segments. insegments = subsegs->items; - //if (b->verbose) { - // printf(" Created %ld segments.\n", subsegs->items); - //} // Set global flags. checksubsegflag = 1; @@ -24660,32 +22663,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 iter; + int t1ver; - if (searchtet->tet == NULL) { - *searchtet = recenttet; - } - 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; + // Randonmly select a good starting tet. + if (randflag) { + randomsample(searchpt, searchtet); + } else { + if (searchtet->tet == NULL) { + *searchtet = recenttet; } - } // while (1) + } + 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); + } + } if (loc != OUTSIDE) { // Round the result of location. @@ -24693,11 +22696,11 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) pb = dest(*searchtet); pc = apex(*searchtet); pd = oppo(*searchtet); - 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); + 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); if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; if (fabs(ori2 / vol) < b->epsilon) ori2 = 0; if (fabs(ori3 / vol) < b->epsilon) ori3 = 0; @@ -24712,19 +22715,19 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) pc = apex(*searchtet); pd = oppo(*searchtet); - vol = orient3d(pa, pb, pc, pd); + vol = orient3dfast(pa, pb, pc, pd); assert(vol < 0); // vol != 0 - ori1 = orient3d(pa, pb, pc, searchpt); + ori1 = orient3dfast(pa, pb, pc, searchpt); if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; // Rounding. if (ori1 <= 0) { - ori2 = orient3d(pb, pa, pd, searchpt); + ori2 = orient3dfast(pb, pa, pd, searchpt); if (fabs(ori2 / vol) < b->epsilon) ori2 = 0; if (ori2 <= 0) { - ori3 = orient3d(pc, pb, pd, searchpt); + ori3 = orient3dfast(pc, pb, pd, searchpt); if (fabs(ori3 / vol) < b->epsilon) ori3 = 0; if (ori3 <= 0) { - ori4 = orient3d(pa, pc, pd, searchpt); + ori4 = orient3dfast(pa, pc, pd, searchpt); if (fabs(ori4 / vol) < b->epsilon) ori4 = 0; if (ori4 <= 0) { // Found the tet. Return its location. @@ -24734,8 +22737,9 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) } // ori2 } // ori1 - searchtet->tet = bgm->tetrahedrontraverse(); + searchtet->tet = tetrahedrontraverse(); } // while (searchtet->tet != NULL) + nonregularcount++; // Re-use this counter. } if (searchtet->tet != NULL) { @@ -24824,13 +22828,9 @@ 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, - int posflag) +REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) { point *pts, pa, pb, pc; REAL volume, vol[4], wei[4]; @@ -24842,15 +22842,15 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, if (iloc == (int) INTETRAHEDRON) { pts = (point *) &(searchtet->tet[4]); assert(pts[3] != dummypoint); - if (!posflag || - ((pts[0][pointmtrindex] > 0) && (pts[1][pointmtrindex] > 0) && - (pts[2][pointmtrindex] > 0) && (pts[3][pointmtrindex] > 0))) { + // 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)) { // P1 interpolation. - 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); + 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); for (i = 0; i < 4; i++) { wei[i] = fabs(vol[i] / volume); size += (wei[i] * pts[i][pointmtrindex]); @@ -24860,9 +22860,8 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, pa = org(*searchtet); pb = dest(*searchtet); pc = apex(*searchtet); - if (!posflag || - ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) && - (pc[pointmtrindex] > 0))) { + if ((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); @@ -24874,7 +22873,7 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, } else if (iloc == (int) ONEDGE) { pa = org(*searchtet); pb = dest(*searchtet); - if (!posflag || ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0))) { + if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) { volume = distance(pa, pb); vol[0] = distance(searchpt, pb); vol[1] = distance(pa, searchpt); @@ -24883,7 +22882,7 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, } } else if (iloc == (int) ONVERTEX) { pa = org(*searchtet); - if (!posflag || (pa[pointmtrindex] > 0)) { + if (pa[pointmtrindex] > 0) { size = pa[pointmtrindex]; } } @@ -24909,6 +22908,11 @@ 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. @@ -24919,8 +22923,8 @@ void tetgenmesh::interpolatemeshsize() searchtet.tet = NULL; iloc = bgm->scoutpoint(ploop, &searchtet, 1); // randflag = 1 if (iloc != (int) OUTSIDE) { - // Interpolate the mesh size (posflag = 0) - ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc, 0); + // Interpolate the mesh size. + ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc); setpoint2bgmtet(ploop, bgm->encode(searchtet)); if (count == 0) { // This is the first interpolated point. @@ -24945,152 +22949,276 @@ 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(tetgenio *addio) +void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen) { triface searchtet, spintet; - face checksh, *splitsh; - face checkseg, *splitseg; - point newpt; + face splitsh; + face splitseg; insertvertexflags ivf; - REAL *attr, x, y, z, w; - int randflag; - int count, index; - int loc; - int i, j; + flipconstraints fc; + int randflag = 0; + int t1ver; + int i; - if (!b->quiet) { - printf("Inserting constrained points ...\n"); + if (b->verbose) { + printf(" Inserting %d constrained points\n", arylen); } - randflag = 1; // Randomly select start tet for point location. - count = 0; - index = 0; + 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; - 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]; + // 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; } else { - // No given weight available. - w = 0; - } - 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; - } - // 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]; + // 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; } } + } else if (ivf.iloc == (int) ONFACE) { + if (issubface(searchtet)) { + tspivot(searchtet, splitsh); + setpointtype(insertarray[i], FREEFACETVERTEX); + //ivf.rejflag |= 1; + } } - // 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. + // 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(); + } + // Update the Steiner counters. + if (pointtype(insertarray[i]) == FREESEGVERTEX) { + st_segref_count++; + } else if (pointtype(insertarray[i]) == FREEFACETVERTEX) { + st_facref_count++; } else { - ivf.bowywat = 3; // Use the "Bowyer-Watson" algorithm to form cavity. - ivf.validflag = 1; // Validate the B-W cavity. + st_volref_count++; } - 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 { + // 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) ONFACE) { - tspivot(searchtet, checksh); - if (checksh.sh != NULL) { - setpointtype(newpt, FACETVERTEX); - splitsh = &checksh; + } 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]))); } - } - - // Insert the vertex. - loc = insertvertex(newpt, &searchtet, splitsh, splitseg, &ivf); - - if (loc == ivf.iloc) { - // The point has been inserted. - lawsonflip3d(newpt, 4, 0, ivf.chkencflag, 0); - count++; } else { if (!b->quiet) { - printf("Warning: Failed to insert point #%d. Ignored.\n", i); + printf("Warning: Failed to insert Point #%d (%g,%g,%g).Ignored.\n", + i, insertarray[i][0], insertarray[i][1], insertarray[i][2]); } - pointdealloc(newpt); } - } else { - if (!b->quiet) { - printf("Warning: Can't locate add point #%d. Ignored.\n", i); - } - pointdealloc(newpt); + //pointdealloc(insertarray[i]); + setpointtype(insertarray[i], UNUSEDVERTEX); + unuverts++; } } // i if (b->verbose) { - printf(" Inserted %d of %d vertices.\n", count, addio->numberofpoints); + 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; + + 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]; + } 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->weighted_param == 0) { + newpt[3] = x * x + y * y + z * z - w; // Weighted DT. + } else { // -w1 option + newpt[3] = w; // Regular tetrahedralization. + } + } + insertarray[arylen] = newpt; + arylen++; + } // i + + // Insert the points. + insertconstrainedpoints(insertarray, arylen); + + delete [] insertarray; } //// //// @@ -25105,8 +23233,6 @@ 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. // // // @@ -25139,7 +23265,7 @@ void tetgenmesh::marksharpsegments() minfacetdihed = PI; smallang = 65.0 * PI / 180.0; // 65 degree. - exsmallang = 15.0 * PI / 180.0; // 15 degree. + exsmallang = 5.0 * PI / 180.0; // 5 degree. sharpcount = exsharpcount = 0; // A segment s may have been split into many subsegments. Operate the one @@ -25297,8 +23423,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 feature // -// size. // +// If 'b->nobisect' ('-Y' option) is set, every input vertex has a size. It // +// is used to prevent creating too close Steiner points. // // // // 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 // @@ -25334,9 +23460,8 @@ void tetgenmesh::decidefeaturepointsizes() maxlen = pow(6.0 * b->maxvolume, 1.0 / 3.0); } - // 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. + // First, assign a size of p if p is a feature point or an input point and + // the -Y option is used. featurecount = 0; points->traversalinit(); ploop = pointtraverse(); @@ -25376,9 +23501,7 @@ 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. @@ -25393,14 +23516,12 @@ 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) { @@ -25418,8 +23539,8 @@ void tetgenmesh::decidefeaturepointsizes() for (i = 0; i < tetlist->objects; i++) { parytet = (triface *) fastlookup(tetlist, i); for (j = 0; j < 3; j++) { - tsspivot1(*parytet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(*parytet)) { + tsspivot1(*parytet, checkseg); e1 = sorg(checkseg); e2 = sdest(checkseg); // Only do calculation if the projeciton of 'p' lies inside the @@ -25444,8 +23565,8 @@ void tetgenmesh::decidefeaturepointsizes() lfs_2 = lfs_0; for (i = 0; i < tetlist->objects; i++) { parytet = (triface *) fastlookup(tetlist, i); - tspivot(*parytet, checksh); - if (checksh.sh != NULL) { + if (issubface(*parytet)) { + tspivot(*parytet, checksh); adjpt = sorg(checksh); e1 = sdest(checksh); e2 = sapex(checksh); @@ -25509,9 +23630,9 @@ void tetgenmesh::decidefeaturepointsizes() printf(" %d feature points.\n", featurecount); } - // 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. + // Second only assign sizes for all Steiner points which were inserted on + // sharp segments. The sizes are interpolated from the endpoints of + // the segments. featurecount = 0; points->traversalinit(); ploop = pointtraverse(); @@ -25526,8 +23647,6 @@ 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)) { @@ -25541,67 +23660,7 @@ 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(); } @@ -25611,7 +23670,8 @@ void tetgenmesh::decidefeaturepointsizes() } if (checkconstraints) { - // A .var file exists. Adjust feature sizes. + // A .var file exists. Adjust feature sizes. And make sure that every + // corner of a constraining facet get a size. if (in->facetconstraintlist) { // Have facet area constrains. subfaces->traversalinit(); @@ -25627,6 +23687,9 @@ void tetgenmesh::decidefeaturepointsizes() if (ploop[pointmtrindex] > varlen) { ploop[pointmtrindex] = varlen; } + } else { + // This corner has no size yet. Set it. + ploop[pointmtrindex] = varlen; } } // j } @@ -25647,6 +23710,8 @@ void tetgenmesh::decidefeaturepointsizes() if (ploop[pointmtrindex] > varlen) { ploop[pointmtrindex] = varlen; } + } else { + ploop[pointmtrindex] = varlen; } } // j } @@ -25664,22 +23729,24 @@ void tetgenmesh::decidefeaturepointsizes() int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) { - REAL ang; - REAL prjpt[3], u, v, t; - // 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). + REAL v1[3], v2[3]; - if (ang > PI) { + 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]; + + if (dot(v1, v2) < 0) { // Inside. if (b->metric || b->nobisect) { // -m or -Y option. if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) { - // 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. + // The projection of 'checkpt' lies inside the segment [a,b]. + REAL prjpt[3], u, v, t; projpt2edge(checkpt, pa, pb, prjpt); - // Get the mesh size at the location 'prjpt'. + // Interoplate the mesh size at the location 'prjpt'. u = distance(pa, pb); v = distance(pa, prjpt); t = v / u; @@ -25707,6 +23774,7 @@ 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 // @@ -25716,21 +23784,11 @@ int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) { - triface searchtet, spintet; - point forg, fdest, eapex; - REAL ccent[3], len, r, d, diff; + REAL ccent[3], len, r; int i; - 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)); - } + point forg = sorg(*chkseg); + point fdest = sdest(*chkseg); // Initialize the return values. encpt = NULL; @@ -25745,21 +23803,13 @@ 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->varvolume || b->fixedvolume) { + if (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; } @@ -25774,33 +23824,13 @@ 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) { @@ -25820,9 +23850,6 @@ 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; } @@ -25844,21 +23871,9 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) int tetgenmesh::splitsegment(face *splitseg, point encpt, int qflag, int chkencflag) { - 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)); - } + point pa = sorg(*splitseg); + point pb = sdest(*splitseg); + REAL len = distance(pa, pb); if (qflag == 0) { @@ -25867,77 +23882,58 @@ 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) && (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; + // 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; + makepoint(&newpt, FREESEGVERTEX); getsteinerptonsegment(splitseg, encpt, newpt); - - // 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. + // Split the segment by the Bowyer-Watson algorithm. sstpivot1(*splitseg, searchtet); ivf.iloc = (int) ONEDGE; - 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; + 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 ((encpt == NULL) && (qflag == 0)) { - // Do not insert the point if it lies inside some protecting balls. - ivf.rejflag |= 4; + ivf.rejflag |= 4; // Do check encroachment of protecting balls. } ivf.chkencflag = chkencflag; - ivf.sloc = ivf.iloc; - ivf.sbowywat = ivf.bowywat; // Surface mesh options. + ivf.sloc = (int) INSTAR; // ivf.iloc; + ivf.sbowywat = 3; // ivf.bowywat; // Surface mesh options. ivf.splitbdflag = 1; ivf.respectbdflag = 1; ivf.assignmeshsize = 1; - 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); + + + if (insertpoint(newpt, &searchtet, &searchsh, splitseg, &ivf)) { 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 { - // The vertex was not inserted. For unknown reasons. - //pointdealloc(newpt); - assert(0); + // Point is not inserted. + pointdealloc(newpt); + return 0; } - - // Should not be here. - return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -25948,7 +23944,7 @@ int tetgenmesh::splitsegment(face *splitseg, point encpt, int qflag, void tetgenmesh::repairencsegs(int chkencflag) { - badface *bface; + face *bface; point encpt = NULL; int qflag = 0; @@ -25956,20 +23952,25 @@ void tetgenmesh::repairencsegs(int chkencflag) // if an unlimited number of Steiner points is allowed. while ((badsubsegs->items > 0) && (steinerleft != 0)) { badsubsegs->traversalinit(); - bface = badfacetraverse(badsubsegs); + bface = (face *) badsubsegs->traverse(); while ((bface != NULL) && (steinerleft != 0)) { - // 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); + // 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); + } } } + // Remove this entry from list. + bface->shver = -1; // Signal it as a deleted element. + badsubsegs->dealloc((void *) bface); } - badfacedealloc(badsubsegs, bface); // Remove this entry from list. - bface = badfacetraverse(badsubsegs); + bface = (face *) badsubsegs->traverse(); } } @@ -25982,19 +23983,37 @@ void tetgenmesh::repairencsegs(int chkencflag) assert(0); // Unknown case. } badsubsegs->traversalinit(); - bface = badfacetraverse(badsubsegs); + bface = (face *) badsubsegs->traverse(); while (bface != NULL) { - if (bface->ss.sh[3] != NULL) { - if (smarktest2ed(bface->ss)) { - sunmarktest2(bface->ss); + // Skip a deleleted element. + if (bface->shver >= 0) { + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + } } } - bface = badfacetraverse(badsubsegs); + bface = (face *) badsubsegs->traverse(); } 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. // @@ -26005,8 +24024,6 @@ 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); @@ -26019,6 +24036,8 @@ 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); @@ -26040,10 +24059,6 @@ 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. @@ -26074,14 +24089,10 @@ 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, sintheta; + REAL area, rd, len; REAL A[4][4], rhs[4], D; int indx[4]; - REAL elen[3]; int i; encpt = NULL; @@ -26091,11 +24102,6 @@ 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]; @@ -26108,100 +24114,56 @@ 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). - 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[0] = 0.5 * dot(A[0], A[0]); // edge [a,b] + rhs[1] = 0.5 * dot(A[1], A[1]); // edge [a,c] 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)) { - 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 (b->verbose > 2) { - printf(" circent: (%g, %g, %g)\n", cent[0], cent[1], cent[2]); - printf(" cirradi: %g\n", rd); - } - - // 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. - } + // pivoting and backward and forward substitute. + if (!lu_decmp(A, 3, indx, &D, 0)) { + // A degenerate triangle. + return 0; + } - 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; - } + 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->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; - } + 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; } + } + 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) { - if (b->verbose > 2) { - printf(" is encroached by point %d\n", - pointmark(oppo(searchtet))); - } - encpt = oppo(searchtet); - return 1; - } + // 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); + return 1; } - sesymself(*chkfac); } - } else { - assert(0); - } // if (!lu_decomp) + sesymself(*chkfac); + } return 0; } @@ -26224,26 +24186,9 @@ int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag, REAL *ccent, int chkencflag) { - 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)); - } + point pa = sorg(*splitfac); + point pb = sdest(*splitfac); + point pc = sapex(*splitfac); // Quickly check if we CAN split this subface. @@ -26255,54 +24200,58 @@ 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. - rd = distance(ccent, pa); + REAL 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]; - 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; - } + // 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; } + + 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; // 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. + // 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. if (qflag == 0) { ivf.rejflag |= 4; // Reject it if it encroached upon any vertex. } ivf.chkencflag = chkencflag; - ivf.sloc = ivf.iloc; - ivf.sbowywat = ivf.bowywat; + ivf.sloc = (int) INSTAR; // ivf.iloc; + ivf.sbowywat = 3; // ivf.bowywat; ivf.splitbdflag = 1; ivf.validflag = 1; ivf.respectbdflag = 1; @@ -26311,59 +24260,45 @@ int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag, ivf.refineflag = 2; ivf.refinesh = searchsh; - loc = insertvertex(newpt, &searchtet, &searchsh, NULL, &ivf); - if (loc == (int) ENCSEGMENT) { - // The new point encroaches upon some segments. - pointdealloc(newpt); - 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; - } + 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(); } - 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 1; + } else { + // Point was not inserted. + 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; } } + 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); + //} + } + 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; } /////////////////////////////////////////////////////////////////////////////// @@ -26374,7 +24309,7 @@ int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag, void tetgenmesh::repairencfacs(int chkencflag) { - badface *bface; + face *bface; point encpt = NULL; int qflag = 0; REAL ccent[3]; @@ -26383,20 +24318,24 @@ void tetgenmesh::repairencfacs(int chkencflag) // if an unlimited number of Steiner points is allowed. while ((badsubfacs->items > 0) && (steinerleft != 0)) { badsubfacs->traversalinit(); - bface = badfacetraverse(badsubfacs); + bface = (face *) badsubfacs->traverse(); while ((bface != NULL) && (steinerleft != 0)) { - // 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); + // 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); + } } } + bface->shver = -1; // Signal it as a deleted element. + badsubfacs->dealloc((void *) bface); // Remove this entry from list. } - badfacedealloc(badsubfacs, bface); // Remove this entry from list. - bface = badfacetraverse(badsubfacs); + bface = (face *) badsubfacs->traverse(); } } @@ -26409,19 +24348,37 @@ void tetgenmesh::repairencfacs(int chkencflag) assert(0); // Unknown case. } badsubfacs->traversalinit(); - bface = badfacetraverse(badsubfacs); + bface = (face *) badsubfacs->traverse(); while (bface != NULL) { - if (bface->ss.sh[3] != NULL) { - if (smarktest2ed(bface->ss)) { - sunmarktest2(bface->ss); + // Skip a deleted element. + if (bface->shver >= 0) { + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + } } } - bface = badfacetraverse(badsubfacs); + bface = (face *) badsubfacs->traverse(); } 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. // @@ -26450,11 +24407,6 @@ 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]; @@ -26468,9 +24420,6 @@ 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]); @@ -26481,9 +24430,6 @@ 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; @@ -26545,9 +24491,6 @@ 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; @@ -26586,9 +24529,6 @@ 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. @@ -26636,22 +24576,14 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, int chkencflag) { - badface *bface; triface searchtet; - face checkseg, *paryseg; - point newpt, pa, *ppt = NULL; + face *paryseg; + point newpt, *ppt; + badface *bface; 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). @@ -26659,12 +24591,9 @@ 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]); - rd = distance(ccent, ppt[0]); + REAL 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; } } @@ -26674,15 +24603,10 @@ int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, searchtet = *splittet; ivf.iloc = (int) OUTSIDE; - // 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; + 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. if (qflag == 0) { ivf.rejflag |= 4; // Reject it if it lies in some protecting balls. } @@ -26696,101 +24620,73 @@ int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, ivf.refineflag = 1; ivf.refinetet = *splittet; - loc = insertvertex(newpt, &searchtet, NULL, NULL, &ivf); - 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. - } - } + 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(); } - return splitflag; - } else if (loc == (int) ENCSUBFACE) { - // There are encroached subfaces. + return 1; + } else { + // Point is not inserted. pointdealloc(newpt); - 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; + // 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; + } } - } - } // 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) + 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); + // 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 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; } - - return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -26801,7 +24697,7 @@ int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, void tetgenmesh::repairbadtets(int chkencflag) { - badface *bface; + triface *bface; REAL ccent[3]; int qflag = 0; @@ -26809,20 +24705,24 @@ void tetgenmesh::repairbadtets(int chkencflag) // if an unlimited number of Steiner points is allowed. while ((badtetrahedrons->items > 0) && (steinerleft != 0)) { badtetrahedrons->traversalinit(); - bface = badfacetraverse(badtetrahedrons); + bface = (triface *) badtetrahedrons->traverse(); while ((bface != NULL) && (steinerleft != 0)) { - // 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); + // 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); + } } } + bface->ver = -1; // Signal it as a deleted element. + badtetrahedrons->dealloc((void *) bface); } - badfacedealloc(badtetrahedrons, bface); - bface = badfacetraverse(badtetrahedrons); + bface = (triface *) badtetrahedrons->traverse(); } } @@ -26836,14 +24736,17 @@ void tetgenmesh::repairbadtets(int chkencflag) } // Unmark all queued tet. badtetrahedrons->traversalinit(); - bface = badfacetraverse(badtetrahedrons); + bface = (triface *) badtetrahedrons->traverse(); while (bface != NULL) { - if (!isdeadtet(bface->tt)) { - if (marktest2ed(bface->tt)) { - unmarktest2(bface->tt); + // Skip a deleted element. + if (bface->ver >= 0) { + if (!isdeadtet(*bface)) { + if (marktest2ed(*bface)) { + unmarktest2(*bface); + } } } - bface = badfacetraverse(badtetrahedrons); + bface = (triface *) badtetrahedrons->traverse(); } // Clear the pool. badtetrahedrons->restart(); @@ -26858,7 +24761,6 @@ void tetgenmesh::repairbadtets(int chkencflag) void tetgenmesh::delaunayrefinement() { - badface *bface; triface checktet; face checksh; face checkseg; @@ -26866,13 +24768,16 @@ 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(" Edge length limit = %g.\n", b->minedgelength); + 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); } steinerleft = b->steinerleft; // Upperbound of # Steiner points (by -S#). @@ -26884,7 +24789,7 @@ void tetgenmesh::delaunayrefinement() } else { if (!b->quiet) { printf("\nWarning: "); - printf("The desired number of Steiner points (%d) is reached.\n\n", + printf("The desired number of Steiner points (%d) has reached.\n\n", b->steinerleft); } return; // No more Steiner points. @@ -26902,6 +24807,7 @@ 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"); @@ -26911,17 +24817,14 @@ void tetgenmesh::delaunayrefinement() steinercount = points->items; // Initialize the pool of encroached subsegments. - badsubsegs = new memorypool(sizeof(badface), b->shellfaceperblock, - memorypool::POINTER, 0); + badsubsegs = new memorypool(sizeof(face), b->shellfaceperblock, + sizeof(void *), 0); // Add all segments into the pool. subsegs->traversalinit(); checkseg.sh = shellfacetraverse(subsegs); while (checkseg.sh != (shellface *) NULL) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. + enqueuesubface(badsubsegs, &checkseg); checkseg.sh = shellfacetraverse(subsegs); } @@ -26944,17 +24847,14 @@ void tetgenmesh::delaunayrefinement() bak_facref_count = st_facref_count; // Initialize the pool of encroached subfaces. - badsubfacs = new memorypool(sizeof(badface), b->shellfaceperblock, - memorypool::POINTER, 0); + badsubfacs = new memorypool(sizeof(face), b->shellfaceperblock, + sizeof(void *), 0); // Add all subfaces into the pool. subfaces->traversalinit(); checksh.sh = shellfacetraverse(subfaces); while (checksh.sh != (shellface *) NULL) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface. + enqueuesubface(badsubfacs, &checksh); checksh.sh = shellfacetraverse(subfaces); } @@ -26985,17 +24885,13 @@ void tetgenmesh::delaunayrefinement() cosmindihed = cos(b->mindihedral / 180.0 * PI); // Initialize the pool of bad quality tetrahedra. - badtetrahedrons = new memorypool(sizeof(badface), b->tetrahedraperblock, - memorypool::POINTER, 0); - + badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock, + sizeof(void *), 0); // Add all tetrahedra (no hull tets) into the pool. tetrahedrons->traversalinit(); checktet.tet = tetrahedrontraverse(); while (checktet.tet != NULL) { - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = checktet; - marktest2(bface->tt); // Only queue it once. - bface->forg = org(checktet); // An alive badface. + enqueuetetrahedron(&checktet); checktet.tet = tetrahedrontraverse(); } @@ -27009,37 +24905,308 @@ void tetgenmesh::delaunayrefinement() st_facref_count - bak_facref_count, st_volref_count - bak_volref_count); } - } // if (b->reflevel > 2) + } // 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); + } + } + // Accumulate the counter of flips. + totalcount += flipcount; - if (steinerleft == 0) { - if (!b->quiet) { - printf("\nWarnning: "); - printf("The desired number of Steiner points (%d) is reached.\n\n", - b->steinerleft); + 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; + + // 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)); + } } - } + unflipqueue->restart(); - delete encseglist; - delete encshlist; + } // while (1) - if (!b->nobisect) { - delete badsubsegs; - if (b->reflevel > 1) { - delete badsubfacs; + 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->reflevel > 2) { - delete badtetrahedrons; - } -} - -//// //// -//// //// -//// refine_cxx /////////////////////////////////////////////////////////////// -//// optimize_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// + return totalcount + sliver_peels; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -27050,8 +25217,8 @@ void tetgenmesh::delaunayrefinement() void tetgenmesh::recoverdelaunay() { arraypool *flipqueue, *nextflipqueue, *swapqueue; - badface *bface, *parybface; triface tetloop, neightet, *parytet; + badface *bface, *parybface; point *ppt; flipconstraints fc; int i, j; @@ -27060,28 +25227,16 @@ 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++) { - // Avoid queue a face twice. - fsym(tetloop, neightet); - if (!ishulltet(neightet)) { - if (!facemarked(neightet)) { - flippush(flipstack, &tetloop); - } + decode(tetloop.tet[tetloop.ver], neightet); + if (!facemarked(neightet)) { + flippush(flipstack, &tetloop); } } ppt = (point *) &(tetloop.tet[4]); @@ -27089,6 +25244,10 @@ 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); } @@ -27096,37 +25255,35 @@ 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. - lawsonflip3d(NULL, 4, 0, 0, 1); + fc.remove_ndelaunay_edge = 1; + fc.enqflag = 2; + + lawsonflip3d(&fc); if (b->verbose > 1) { - printf(" New obj = %.17g\n", tetprism_vol_sum); + printf(" obj (after Lawson) = %.17g\n", tetprism_vol_sum); } if (unflipqueue->objects == 0l) { - // The mesh is Delaunay. - return; + return; // The mesh is Delaunay. } - // Set the common options. - fc.remove_ndelaunay_edge = 1; fc.unflip = 1; // Unflip if the edge is not flipped. - fc.collectnewtets = 1; + fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'. + fc.enqflag = 0; - autofliplinklevel = 1; // Init value. - b->fliplinklevel = -1; + autofliplinklevel = 1; // Init level. + b->fliplinklevel = -1; // No fixed level. // 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; + b->flipstarsize = 10; // default flipqueue = new arraypool(sizeof(badface), 10); nextflipqueue = new arraypool(sizeof(badface), 10); - - + // Swap the two flip queues. swapqueue = flipqueue; flipqueue = unflipqueue; @@ -27134,60 +25291,61 @@ void tetgenmesh::recoverdelaunay() while (flipqueue->objects > 0l) { - 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); + 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 } - // 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); + } // 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); *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 - - flipqueue->restart(); - - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - } // while (flipqueue->objects > 0l) + } + } // i if (b->verbose > 1) { - printf(" New obj = %.17g.\n", tetprism_vol_sum); + printf(" obj (after level %d) = %.17g.\n", autofliplinklevel, + tetprism_vol_sum); } + flipqueue->restart(); // Swap the two flip queues. swapqueue = flipqueue; @@ -27195,14 +25353,13 @@ void tetgenmesh::recoverdelaunay() nextflipqueue = swapqueue; if (flipqueue->objects > 0l) { - // 'b->delmaxfliplevel' is set by -OOOO, default is 1. + // default 'b->delmaxfliplevel' 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) { @@ -27211,16 +25368,13 @@ void tetgenmesh::recoverdelaunay() } } - b->flipstarsize = bakmaxflipstarsize; - - delete nextflipqueue; - delete flipqueue; - - calc_tetprism_vol = 0; - if (b->verbose) { - printf(" Final obj = %.17g\n", tetprism_vol_sum); + printf(" Final obj = %.17g\n", tetprism_vol_sum); } + + b->flipstarsize = bakmaxflipstarsize; + delete flipqueue; + delete nextflipqueue; } /////////////////////////////////////////////////////////////////////////////// @@ -27233,11 +25387,7 @@ int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, triface *searchtet) { triface spintet; - - if (b->verbose > 2) { - printf(" Get tet [%d,%d,%d,%d].\n", pointmark(pa), pointmark(pb), - pointmark(pc), pointmark(pd)); - } + int t1ver; if (getedge(pa, pb, searchtet)) { spintet = *searchtet; @@ -27286,18 +25436,20 @@ long tetgenmesh::improvequalitybyflips() flipqueue = new arraypool(sizeof(badface), 10); nextflipqueue = new arraypool(sizeof(badface), 10); - // Flip edge options. - b->fliplinklevel = -1; - autofliplinklevel = 1; // Init value. + // Backup flip edge options. + int bakautofliplinklevel = autofliplinklevel; + int bakfliplinklevel = b->fliplinklevel; + int bakmaxflipstarsize = b->flipstarsize; - // 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; + // Set flip edge options. + autofliplinklevel = 1; + b->fliplinklevel = -1; + b->flipstarsize = 10; // b->optmaxflipstarsize; fc.remove_large_angle = 1; fc.unflip = 1; fc.collectnewtets = 1; + fc.checkflipeligibility = 1; totalremcount = 0l; @@ -27320,40 +25472,37 @@ 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, - &maxdd, NULL); + &bface->key, 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. @@ -27361,8 +25510,6 @@ 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, @@ -27381,8 +25528,7 @@ long tetgenmesh::improvequalitybyflips() parybface->cent[n] = ncosdd[n]; } } - } // if (ppt[3] != dummypoint) { - //} + } // if (ppt[3] != dummypoint) } } // j } // if (fc.cosdihed_out < cosmaxdihed) @@ -27413,12 +25559,12 @@ long tetgenmesh::improvequalitybyflips() totalremcount += remcount; if (unflipqueue->objects > 0l) { - // 'b->optmaxfliplevel' is set by -OOO, default is 2. - if (autofliplinklevel >= b->optmaxfliplevel) { - // For efficiency reason, we do not search too far. + //if (autofliplinklevel >= b->optmaxfliplevel) { + if (autofliplinklevel >= b->optlevel) { break; } autofliplinklevel+=b->fliplinklevelinc; + //b->flipstarsize = 10 + (1 << (b->optlevel - 1)); } // Swap the two flip queues. @@ -27427,6 +25573,9 @@ long tetgenmesh::improvequalitybyflips() unflipqueue = swapqueue; } // while (flipqueues->objects > 0) + // Restore original flip edge options. + autofliplinklevel = bakautofliplinklevel; + b->fliplinklevel = bakfliplinklevel; b->flipstarsize = bakmaxflipstarsize; delete flipqueue; @@ -27451,8 +25600,8 @@ long tetgenmesh::improvequalitybyflips() // has two orientations, ccw or cw, with respect to 'p'. 'ccw' indicates // // the orientation is ccw (1) or not (0). // // // -// 'of' is a structure contains the parameters of the objective function. It // -// is needed by the evaluation of the function value. // +// 'opm' 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. // // // @@ -27473,16 +25622,6 @@ 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) { @@ -27535,7 +25674,8 @@ 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 = -ori; + val = - orient3dfast(pa, pb, pc, nextpt); } else if (opm->max_min_aspectratio) { val = tetaspectratio(pa, pb, pc, nextpt); } else if (opm->min_max_dihedangle) { @@ -27548,8 +25688,10 @@ 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 = -ori; + val = - orient3dfast(pa, pb, pc, nextpt); } else { // Discard this point. break; // j @@ -27569,7 +25711,6 @@ 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]; @@ -27616,54 +25757,9 @@ 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. - 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); - } - } + opm->smthiter = iter; // Remember the number of iterations. // 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; @@ -27678,29 +25774,28 @@ 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, k; + int iter, i, j, 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); @@ -27710,6 +25805,7 @@ 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. @@ -27719,10 +25815,7 @@ long tetgenmesh::improvequalitybysmoothing(optparameters *opm) if (bface->key < cossmtdihed) { // if (maxdd < cosslidihed) { // It is a sliver. Try to smooth its vertices. smtflag = 0; - //if (opm->min_max_dihedangle) { - opm->initval = bface->key + 1.0; - //opm->checkencflag = 4; // Queue affected tets. - //} + opm->initval = bface->key + 1.0; for (i = 0; (i < 4) && !smtflag; i++) { if (pointtype(ppt[i]) == FREEVOLVERTEX) { getvertexstar(1, ppt[i], cavetetlist, NULL, NULL); @@ -27735,78 +25828,61 @@ 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) { - // 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 { + if (!smtflag) { // 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 = *bface; + 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; } - } // if (maxdd < cosslidihed) + } // if (maxdd < cosslidihed) } // if (!marktested(...)) - } // gettetrahedron(...) + } // if (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); } @@ -27846,35 +25922,36 @@ 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 loc; + int t1ver; int n, i; - // 'slitet' is [c,d,a,b], where [c,d] has a big hihedral angle. + // 'slitet' is [c,d,a,b], where [c,d] has a big dihedral angle. // Go to the opposite edge [a,b]. - eprev(*slitet, searchtet); - esymself(searchtet); - enextself(searchtet); // [a,b,c,d]. + edestoppo(*slitet, searchtet); // [a,b,c,d]. // Do not split a segment. - tsspivot1(searchtet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(searchtet)) { 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]. @@ -27928,19 +26005,11 @@ 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]; @@ -27951,24 +26020,22 @@ 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.sloc = 0; - ivf.sbowywat = 0; - ivf.splitbdflag = 0; - ivf.validflag = 0; - ivf.respectbdflag = 0; - ivf.assignmeshsize = 0; + ivf.assignmeshsize = b->metric; - loc = insertvertex(steinerpt, &searchtet, NULL, NULL, &ivf); - if (loc == (int) INSTAR) { + if (insertpoint(steinerpt, &searchtet, NULL, NULL, &ivf)) { // The vertex has been inserted. - st_volref_count++; //st_inpoly_count++; + st_volref_count++; if (steinerleft > 0) steinerleft--; return 1; } else { @@ -27976,8 +26043,6 @@ int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) pointdealloc(steinerpt); return 0; } - - delete [] abtets; } /////////////////////////////////////////////////////////////////////////////// @@ -27990,24 +26055,24 @@ long tetgenmesh::removeslivers(int chkencflag) { arraypool *flipqueue, *swapqueue; badface *bface, *parybface; + triface slitet, *parytet; point *ppt; - REAL *cosdd; + REAL cosdd[6], maxcosd; long totalsptcount, sptcount; - int iter, j, k; + int iter, i, 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) { + while ((flipqueue->objects > 0l) && (steinerleft != 0)) { sptcount = 0l; @@ -28016,74 +26081,63 @@ long tetgenmesh::removeslivers(int chkencflag) iter, flipqueue->objects); } - for (k = 0; k < flipqueue->objects; k++) { + for (k = 0; (k < flipqueue->objects) && (steinerleft != 0); k++) { bface = (badface *) fastlookup(flipqueue, k); if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, bface->foppo, &bface->tt)) { - //if (!marktested(bface->tt)) { - // Here we simply re-compute the quality. Since other smoothing + if ((bface->key == 0) || (bface->tt.ver != 11)) { + // Here we need to 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. - 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; - } + } + 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; } - } // 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; + } + } // 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]; } - bface = badfacetraverse(badtetrahedrons); } - 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)) + 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) } // if (gettetrahedron(...)) } // k @@ -28121,11 +26175,12 @@ long tetgenmesh::removeslivers(int chkencflag) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::optimizemesh(int optflag) +void tetgenmesh::optimizemesh() { badface *parybface; triface checktet; point *ppt; + int optpasses; optparameters opm; REAL ncosdd[6], maxdd; long totalremcount, remcount; @@ -28139,18 +26194,16 @@ void tetgenmesh::optimizemesh(int optflag) printf("Optimizing mesh...\n"); } - 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; + optpasses = ((1 << b->optlevel) - 1); - if (b->verbose > 1) { - printf(" Removing large angles (> %g degree).\n", b->optmaxdihedral); + 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); } + totalsmtcount = totalsptcount = totalremcount = 0l; + cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI); cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI); cosslidihed = cos(b->optminslidihed / 180.0 * PI); @@ -28181,23 +26234,23 @@ void tetgenmesh::optimizemesh(int optflag) totalremcount = improvequalitybyflips(); if ((unflipqueue->objects > 0l) && - ((b->optlevel & 2) || (b->optlevel & 4))) { // -O2 | -O4 - - badtetrahedrons = new memorypool(sizeof(badface), b->tetrahedraperblock, - memorypool::POINTER, 0); + ((b->optscheme & 2) || (b->optscheme & 4))) { + // The pool is only used by removeslivers(). + badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock, + sizeof(void *), 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 < b->optpasses) { + while (iter < optpasses) { smtcount = sptcount = remcount = 0l; - if (b->optlevel & 2) { + if (b->optscheme & 2) { smtcount += improvequalitybysmoothing(&opm); totalsmtcount += smtcount; if (smtcount > 0l) { @@ -28206,7 +26259,7 @@ void tetgenmesh::optimizemesh(int optflag) } } if (unflipqueue->objects > 0l) { - if (b->optlevel & 4) { + if (b->optscheme & 4) { sptcount += removeslivers(chkencflag); totalsptcount += sptcount; if (sptcount > 0l) { @@ -28228,7 +26281,7 @@ void tetgenmesh::optimizemesh(int optflag) delete badtetrahedrons; - } // // -O2 | -O4 + } if (unflipqueue->objects > 0l) { if (b->verbose > 1) { @@ -28239,13 +26292,13 @@ void tetgenmesh::optimizemesh(int optflag) if (b->verbose) { if (totalremcount > 0l) { - printf(" Removed %ld bad tets.\n", totalremcount); + printf(" Removed %ld edges.\n", totalremcount); } if (totalsmtcount > 0l) { printf(" Smoothed %ld points.\n", totalsmtcount); } if (totalsptcount > 0l) { - printf(" Split %ld bad tets.\n", totalsptcount); + printf(" Split %ld slivers.\n", totalsptcount); } } } @@ -28258,6 +26311,32 @@ void tetgenmesh::optimizemesh(int optflag) //// //// //// //// +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // @@ -28403,12 +26482,12 @@ int tetgenmesh::checkmesh(int topoflag) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkshells(/*int sub2tet*/) +int tetgenmesh::checkshells() { triface neightet, symtet; face shloop, spinsh, nextsh; face checkseg; - point pa, pb; //, *ppt; + point pa, pb; int bakcount; int horrors, i; @@ -28436,10 +26515,10 @@ int tetgenmesh::checkshells(/*int sub2tet*/) 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", (unsigned long long) spinsh.sh, + printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Second: x%lx (DEAD)\n", (unsigned long long) nextsh.sh); + printf(" Second: x%lx (DEAD)\n", (uintptr_t) nextsh.sh); horrors++; break; } @@ -28447,10 +26526,10 @@ int tetgenmesh::checkshells(/*int sub2tet*/) 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", (unsigned long long) spinsh.sh, + printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Scond: x%lx (%d, %d, %d).\n", (unsigned long long) nextsh.sh, + printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), pointmark(sapex(nextsh))); horrors++; @@ -28459,10 +26538,10 @@ int tetgenmesh::checkshells(/*int sub2tet*/) // 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", (unsigned long long) spinsh.sh, + printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Scond: x%lx (%d, %d, %d).\n", (unsigned long long) nextsh.sh, + printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), pointmark(sapex(nextsh))); horrors++; @@ -28476,19 +26555,19 @@ int tetgenmesh::checkshells(/*int sub2tet*/) 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", (unsigned long long) shloop.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Sub: x%lx (Dead)\n", (unsigned long long) checkseg.sh); + printf(" Sub: x%lx (Dead)\n", (uintptr_t) 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", (unsigned long long) shloop.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Seg: x%lx (%d, %d).\n", (unsigned long long) checkseg.sh, + printf(" Seg: x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); horrors++; } @@ -28502,20 +26581,20 @@ int tetgenmesh::checkshells(/*int sub2tet*/) 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", (unsigned long long) shloop.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Tet: x%lx (DEAD)\n", (unsigned long long) neightet.tet); + printf(" Tet: x%lx (DEAD)\n", (uintptr_t) 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", (unsigned long long) shloop.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); printf(" Tet: x%lx (%d, %d, %d, %d).\n", - (unsigned long long) neightet.tet, pointmark(org(neightet)), + (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); horrors++; @@ -28524,11 +26603,11 @@ int tetgenmesh::checkshells(/*int sub2tet*/) if (!((sorg(spinsh) == org(neightet)) && (sdest(spinsh) == dest(neightet)))) { printf(" !! !! Wrong tet-sub connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) spinsh.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); printf(" Tet: x%lx (%d, %d, %d, %d).\n", - (unsigned long long) neightet.tet, pointmark(org(neightet)), + (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); horrors++; @@ -28539,11 +26618,11 @@ int tetgenmesh::checkshells(/*int sub2tet*/) if (!((sorg(spinsh) == org(symtet)) && (sdest(spinsh) == dest(symtet)))) { printf(" !! !! Wrong tet-sub connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) spinsh.sh, + printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); printf(" Tet: x%lx (%d, %d, %d, %d).\n", - (unsigned long long) symtet.tet, pointmark(org(symtet)), + (uintptr_t) symtet.tet, pointmark(org(symtet)), pointmark(dest(symtet)), pointmark(apex(symtet)), pointmark(oppo(symtet))); horrors++; @@ -28598,8 +26677,10 @@ 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"); } @@ -28623,9 +26704,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", - (unsigned long long) tetloop.tet, pointmark(org(tetloop)), + (uintptr_t) tetloop.tet, pointmark(org(tetloop)), pointmark(dest(tetloop)), pointmark(apex(tetloop)), - pointmark(oppo(tetloop)), (unsigned long long) sseg.sh, + pointmark(oppo(tetloop)), (uintptr_t) sseg.sh, pointmark(pa), pointmark(pb)); horrors++; } else { @@ -28636,11 +26717,11 @@ int tetgenmesh::checksegments() if (checkseg.sh != sseg.sh) { printf(" !! Wrong tet->seg connection.\n"); printf(" Tet: x%lx (%d, %d, %d, %d) - ", - (unsigned long long) neightet.tet, pointmark(org(neightet)), + (uintptr_t) 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", (unsigned long long) checkseg.sh, + printf("Seg x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, pointmark(sorg(checkseg)),pointmark(sdest(checkseg))); } else { printf("Seg: NULL.\n"); @@ -28660,9 +26741,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", - (unsigned long long) neightet.tet, pointmark(org(neightet)), + (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), - pointmark(oppo(neightet)), (unsigned long long) sseg.sh, + pointmark(oppo(neightet)), (uintptr_t) sseg.sh, pointmark(pa), pointmark(pb)); horrors++; } @@ -28679,7 +26760,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)), - (unsigned long long) neightet.tet, neightet.ver); + (uintptr_t) neightet.tet, neightet.ver); // Check if all tets at the edge are marked. spintet = neightet; while (1) { @@ -28688,7 +26769,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)), - (unsigned long long) spintet.tet, spintet.ver); + (uintptr_t) spintet.tet, spintet.ver); horrors++; } if (spintet.tet == neightet.tet) break; @@ -28721,7 +26802,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)), (unsigned long long) spinsh.sh, + // pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, // spinsh.shver); // horrors++; //} @@ -28734,7 +26815,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)), - (unsigned long long) spintet.tet, spintet.ver); + (uintptr_t) spintet.tet, spintet.ver); horrors++; } if (checkseg.sh != sseg.sh) { @@ -28753,7 +26834,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)), (unsigned long long) spinsh.sh, + pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, spinsh.shver); horrors++; break; @@ -29034,6 +27115,7 @@ 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; @@ -29246,7 +27328,7 @@ void tetgenmesh::qualitystatistics() for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i]; // Get the tet volume. - tetvol = orient3d(p[1], p[0], p[2], p[3]) / 6.0; + tetvol = orient3dfast(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]); @@ -29550,6 +27632,97 @@ 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. // @@ -29575,26 +27748,44 @@ void tetgenmesh::statistics() tetnumber = tetrahedrons->items - hullsize; facenumber = (tetnumber * 4l + hullsize) / 2l; - printf("\n Mesh points: %ld\n", points->items); + if (b->weighted) { // -w option + printf("\n Mesh points: %ld\n", points->items - nonregularcount); + } else { + printf("\n Mesh points: %ld\n", points->items); + } printf(" Mesh tetrahedra: %ld\n", tetnumber); printf(" Mesh faces: %ld\n", facenumber); - printf(" Mesh edges: %ld\n", meshedges); + 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); + } + } 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 in boundary edges: %ld\n", st_segref_count); + printf(" Steiner points on boundary edges: %ld\n", st_segref_count); } if (st_facref_count > 0l) { - printf(" Steiner points in boundary faces: %ld\n", st_facref_count); + printf(" Steiner points on boundary faces: %ld\n", st_facref_count); } if (st_volref_count > 0l) { - printf(" Steiner points in mesh domain: %ld\n", st_volref_count); + printf(" Steiner points inside mesh domain: %ld\n", st_volref_count); } } else { printf(" Convex hull faces: %ld\n", hullsize); - printf(" Convex hull edges: %ld\n", meshhulledges); + if (meshhulledges > 0l) { + printf(" Convex hull edges: %ld\n", meshhulledges); + } + } + if (b->weighted) { // -w option + printf(" Skipped non-regular points: %ld\n", nonregularcount); } printf("\n"); @@ -29605,6 +27796,9 @@ void tetgenmesh::statistics() qualitystatistics(); } } + if (tetrahedrons->items > 0l) { + memorystatistics(); + } } } @@ -29647,7 +27841,7 @@ void tetgenmesh::jettisonnodes() jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || (pointtype(pointloop) == UNUSEDVERTEX); if (jetflag) { - // It is a duplicated point, delete it. + // It is a duplicated or unused point, delete it. pointdealloc(pointloop); remcount++; } else { @@ -29662,27 +27856,116 @@ 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(" %d duplicated vertices are removed.\n", dupverts); - printf(" %d unused vertices are removed.\n", unuverts); + 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); } - dupverts = 0; - unuverts = 0; + + // This will overwrite the slot for element markers. + highorderindex = 11; // 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. + // 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. 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(); + } +} /////////////////////////////////////////////////////////////////////////////// // // @@ -29691,28 +27974,18 @@ void tetgenmesh::jettisonnodes() // 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(); @@ -29758,7 +28031,7 @@ void tetgenmesh::outnodes(tetgenio* out) char outnodefilename[FILENAMESIZE]; face parentsh; point pointloop; - int nextras, bmark, marker = 0; + int nextras, bmark, marker = 0, weightDT = 0; int coordindex, attribindex; int pointnumber, firstindex; int index, i; @@ -29776,7 +28049,11 @@ void tetgenmesh::outnodes(tetgenio* out) } } - nextras = in->numberofpointattributes; + nextras = numpointattrib; + if (b->weighted) { // -w + if (b->weighted_param == 0) weightDT = 1; // Weighted DT. + } + bmark = !b->nobound && in->pointmarkerlist; if (out == (tetgenio *) NULL) { @@ -29860,7 +28137,13 @@ void tetgenmesh::outnodes(tetgenio* out) pointloop[0], pointloop[1], pointloop[2]); for (i = 0; i < nextras; i++) { // Write an attribute. - fprintf(outfile, " %.17g", pointloop[4 + i]); + 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]); + } } if (bmark) { // Write the boundary marker. @@ -29892,7 +28175,13 @@ void tetgenmesh::outnodes(tetgenio* out) // Point attributes. for (i = 0; i < nextras; i++) { // Output an attribute. - out->pointattributelist[attribindex++] = pointloop[4 + i]; + 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]; + } } if (bmark) { // Output the boundary marker. @@ -30012,7 +28301,6 @@ 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; @@ -30020,10 +28308,9 @@ void tetgenmesh::outelements(tetgenio* out) long ntets; int firstindex, shift; int pointindex, attribindex; - int highorderindex = 10; // The reserved pointer. + int highorderindex = 11; int elementnumber; int eextras; - int ishulledge; int i; if (out == (tetgenio *) NULL) { @@ -30042,7 +28329,7 @@ void tetgenmesh::outelements(tetgenio* out) // The number of tets excluding hull tets. ntets = tetrahedrons->items - hullsize; - eextras = in->numberoftetrahedronattributes; + eextras = numelemattrib; if (out == (tetgenio *) NULL) { outfile = fopen(outelefilename, "w"); if (outfile == (FILE *) NULL) { @@ -30086,8 +28373,13 @@ void tetgenmesh::outelements(tetgenio* out) tptr = tetrahedrontraverse(); elementnumber = firstindex; // in->firstnumber; while (tptr != (tetrahedron *) NULL) { - p1 = (point) tptr[4]; - p2 = (point) tptr[5]; + 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]; if (out == (tetgenio *) NULL) { @@ -30095,9 +28387,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 (0) { // if (b->order == 2) { + if (b->order == 2) { extralist = (point *) tptr[highorderindex]; - // Tetrahedron number, indices for four points plus six extra points. + // indices for 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, @@ -30112,7 +28404,7 @@ void tetgenmesh::outelements(tetgenio* out) tlist[pointindex++] = pointmark(p2) - shift; tlist[pointindex++] = pointmark(p3) - shift; tlist[pointindex++] = pointmark(p4) - shift; - if (0) { // if (b->order == 2) { + if (b->order == 2) { extralist = (point *) tptr[highorderindex]; tlist[pointindex++] = pointmark(extralist[0]) - shift; tlist[pointindex++] = pointmark(extralist[1]) - shift; @@ -30125,44 +28417,12 @@ void tetgenmesh::outelements(tetgenio* out) talist[attribindex++] = elemattribute(tptr, i); } } - //if (b->neighout) { - // Remember the index of this element. - setelemindex(tptr, elementnumber); - //} + // Remember the index of this element (for counting edges). + 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); @@ -30174,11 +28434,6 @@ 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) @@ -30194,7 +28449,13 @@ void tetgenmesh::outfaces(tetgenio* out) int faceid, marker = 0; int firstindex, shift; int facenumber; - int index; + int index = 0; + + // For -o2 option. + triface workface; + point *extralist, pp[3] = {0,0,0}; + int highorderindex = 11; + int o2index = 0, i; if (out == (tetgenio *) NULL) { strcpy(facefilename, b->outfilename); @@ -30211,7 +28472,6 @@ 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"); @@ -30227,6 +28487,9 @@ 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]; @@ -30246,7 +28509,6 @@ void tetgenmesh::outfaces(tetgenio* out) out->numberoftrifaces = faces; elist = out->trifacelist; emlist = out->trifacemarkerlist; - index = 0; } // Determine the first index (0 or 1). @@ -30271,6 +28533,16 @@ 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) { @@ -30306,6 +28578,10 @@ 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); @@ -30319,6 +28595,11 @@ 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; } @@ -30454,6 +28735,14 @@ 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"); @@ -30467,8 +28756,6 @@ void tetgenmesh::outsubfaces(tetgenio* out) } } - //bmark = !b->nobound && in->facetmarkerlist; - if (out == (tetgenio *) NULL) { outfile = fopen(facefilename, "w"); if (outfile == (FILE *) NULL) { @@ -30483,6 +28770,9 @@ 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]; @@ -30514,18 +28804,39 @@ 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) { @@ -30552,6 +28863,10 @@ 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); } @@ -30564,6 +28879,11 @@ 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; } @@ -30602,9 +28922,15 @@ void tetgenmesh::outedges(tetgenio* out) int ishulledge; int firstindex, shift; int edgenumber, marker; - int index, index1; + int index = 0, index1 = 0, index2 = 0; + int t1ver; 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"); @@ -30618,6 +28944,20 @@ 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) { @@ -30633,14 +28973,18 @@ 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). @@ -30654,9 +28998,7 @@ void tetgenmesh::outedges(tetgenio* out) tetloop.tet = tetrahedrontraverse(); edgenumber = firstindex; // in->firstnumber; while (tetloop.tet != (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. + // Count the number of Voronoi faces. worktet.tet = tetloop.tet; for (i = 0; i < 6; i++) { worktet.ver = edge2ver[i]; @@ -30674,13 +29016,24 @@ 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) { @@ -30704,6 +29057,13 @@ 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"); } @@ -30737,6 +29097,18 @@ 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"); @@ -30761,13 +29133,20 @@ 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; } @@ -30787,18 +29166,64 @@ 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 %d\n", edgenumber, - pointmark(torg) - shift, pointmark(tdest) - shift, marker); + 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"); } 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); @@ -30912,6 +29337,12 @@ 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -30934,6 +29365,8 @@ 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); @@ -30974,8 +29407,9 @@ void tetgenmesh::outvoronoi(tetgenio* out) // The number of Delaunay faces (Voronoi edges). faces = (4l * ntets + hullsize) / 2l; // The number of Delaunay edges (Voronoi faces). - // edges = points->items + faces - ntets - 1; - edges = meshedges; // Counted in outelements() or numberedges(); + long vsize = points->items - dupverts - unuverts; + if (b->weighted) vsize -= nonregularcount; + edges = vsize + faces - ntets - 1; if (out == (tetgenio *) NULL) { outfile = fopen(outfilename, "w"); @@ -31004,7 +29438,12 @@ void tetgenmesh::outvoronoi(tetgenio* out) pt[i] = (point) tetloop.tet[4 + i]; setpoint2tet(pt[i], encode(tetloop)); } - circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL); + 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); + } if (out == (tetgenio *) NULL) { fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift, ccent[0], ccent[1], ccent[2]); @@ -31277,7 +29716,8 @@ void tetgenmesh::outvoronoi(tetgenio* out) vpointcount = 0; while (ploop != (point) NULL) { if ((pointtype(ploop) != UNUSEDVERTEX) && - (pointtype(ploop) != DUPLICATEDVERTEX)) { + (pointtype(ploop) != DUPLICATEDVERTEX) && + (pointtype(ploop) != NREGULARVERTEX)) { getvertexstar(1, ploop, tetlist, ptlist, NULL); // Mark all vertices. Check if it is a hull vertex. ishullvert = 0; @@ -31337,14 +29777,6 @@ 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++; @@ -31583,13 +30015,18 @@ void tetgenmesh::outmesh2medit(char* mfilename) tetrahedrons->traversalinit(); tetptr = tetrahedrontraverse(); while (tetptr != (tetrahedron *) NULL) { - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; + if (!b->reversetetori) { + p1 = (point) tetptr[4]; + p2 = (point) tetptr[5]; + } else { + p1 = (point) tetptr[5]; + p2 = (point) tetptr[4]; + } p3 = (point) tetptr[6]; p4 = (point) tetptr[7]; fprintf(outfile, "%5d %5d %5d %5d", pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4)); - if (in->numberoftetrahedronattributes > 0) { + if (numelemattrib > 0) { fprintf(outfile, " %.17g", elemattribute(tetptr, 0)); } else { fprintf(outfile, " 0"); @@ -31639,7 +30076,7 @@ void tetgenmesh::outmesh2vtk(char* ofilename) { FILE *outfile; char vtkfilename[FILENAMESIZE]; - point pointloop; + point pointloop, p1, p2, p3, p4; tetrahedron* tptr; double x, y, z; int n1, n2, n3, n4; @@ -31698,10 +30135,15 @@ void tetgenmesh::outmesh2vtk(char* ofilename) return; } while (tptr != (tetrahedron *) NULL) { - point p1 = (point) tptr[4]; - point p2 = (point) tptr[5]; - point p3 = (point) tptr[6]; - point p4 = (point) tptr[7]; + 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]; n1 = pointmark(p1) - in->firstnumber; n2 = pointmark(p2) - in->firstnumber; n3 = pointmark(p3) - in->firstnumber; @@ -31756,186 +30198,149 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, tetgenio *addin, tetgenio *bgmin) { tetgenmesh m; - clock_t tv[17]; // Timing informations (defined in time.h) + clock_t tv[10], ts[5]; // Timing informations (defined in time.h) + REAL cps = (REAL) CLOCKS_PER_SEC; tv[0] = clock(); m.b = b; m.in = in; + m.addin = addin; - if ((bgmin != NULL) && - ((bgmin->numberofpoints > 0) && (bgmin->pointmtrlist != NULL))) { + if ((b->metric) && (bgmin->numberofpoints > 0)) { 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 - if (!b->diagnose) { - m.incrementaldelaunay(tv[16]); - } + m.incrementaldelaunay(ts[0]); } tv[2] = clock(); if (!b->quiet) { if (b->refine) { - printf("Mesh reconstruction seconds: %g\n", - (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC); + printf("Mesh reconstruction seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps); } else { - 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 - } + 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->plc) { + if (b->plc) { // -p m.meshsurface(); - } - tv[3] = clock(); + ts[0] = clock(); - if (!b->quiet) { - if (b->plc) { - printf("Surface mesh seconds: %g\n", - (tv[3] - tv[2]) / (REAL) CLOCKS_PER_SEC); + 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); + } + + return; } } - if (b->plc && b->diagnose) { // -d - m.detectinterfaces(); + tv[3] = clock(); + + if ((b->metric) && (m.bgm != NULL)) { // -m + m.bgm->initializepools(); + m.bgm->transfernodes(); + m.bgm->reconstructmesh(); - tv[4] = clock(); + ts[0] = clock(); if (!b->quiet) { - printf("Self-intersection seconds: %g\n", - (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); + printf("Background mesh reconstruct seconds: %g\n", + ((REAL)(ts[0] - tv[3])) / cps); } - // Only output when self-intersecting faces exist. - if (m.subfaces->items > 0l) { - m.outnodes(out); - m.outsubfaces(out); - } + if (b->metric) { // -m + m.interpolatemeshsize(); - return; - } + ts[1] = clock(); - if (b->plc) { - if (b->nobisect) { // with -Y option - m.recoverboundary(tv[15]); - } else { - m.constraineddelaunay(tv[15]); + if (!b->quiet) { + printf("Size interpolating seconds: %g\n",((REAL)(ts[1]-ts[0])) / cps); + } } } tv[4] = clock(); - if (!b->quiet) { - if (b->plc) { - printf("Boundary recovery seconds: %g\n", - (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); + if (b->plc) { // -p + if (b->nobisect) { // -Y + m.recoverboundary(ts[0]); + } else { + m.constraineddelaunay(ts[0]); + } + + ts[1] = clock(); + + if (!b->quiet) { + printf("Boundary recovery seconds: %g\n", ((REAL)(ts[1]-tv[4])) / cps); if (b->verbose) { - 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); + printf(" Segment recovery seconds: %g\n",((REAL)(ts[0]-tv[4]))/ cps); + printf(" Facet recovery seconds: %g\n", ((REAL)(ts[1]-ts[0])) / cps); } } - } - if (b->plc && !b->convex) { m.carveholes(); - } - tv[5] = clock(); + ts[2] = clock(); - if (!b->quiet) { - if (b->plc && !b->convex) { + if (!b->quiet) { printf("Exterior tets removal seconds: %g\n", - (tv[5] - tv[4]) / (REAL) CLOCKS_PER_SEC); - } - } - - if (b->plc && b->nobisect) { - m.suppresssteinerpoints(); - } - - tv[6] = clock(); - - if (!b->quiet) { - if (b->plc && b->nobisect) { - printf("Steiner suppression seconds: %g\n", - (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC); + ((REAL)(ts[2]-ts[1])) / cps); } - } - if (b->plc && b->nobisect) { - m.recoverdelaunay(); - } + if (b->nobisect) { // -Y + m.suppresssteinerpoints(); - tv[7] = clock(); + ts[3] = clock(); - if (!b->quiet) { - if (b->plc && b->nobisect) { - printf("Delaunay recovery seconds: %g\n", - (tv[7] - tv[6]) / (REAL) CLOCKS_PER_SEC); - } - } + if (!b->quiet) { + printf("Steiner suppression seconds: %g\n",((REAL)(ts[3]-ts[2]))/cps); + } - if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) { - m.bgm->initializepools(); - m.bgm->transfernodes(); - m.bgm->reconstructmesh(); - } + m.recoverdelaunay(); - tv[8] = clock(); + ts[4] = 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->quiet) { + printf("Delaunay recovery seconds: %g\n", ((REAL)(ts[4]-ts[3])) / cps); + } } } - 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); - } - } + tv[5] = clock(); if ((b->plc || b->refine) && b->insertaddpoints) { // -i if ((addin != NULL) && (addin->numberofpoints > 0)) { @@ -31943,44 +30348,38 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } - tv[10] = clock(); + tv[6] = clock(); if (!b->quiet) { if ((b->plc || b->refine) && b->insertaddpoints) { if ((addin != NULL) && (addin->numberofpoints > 0)) { - printf("Constrained points seconds: %g\n", - (tv[10] - tv[9]) / (REAL) CLOCKS_PER_SEC); + printf("Constrained points seconds: %g\n", ((REAL)(tv[6]-tv[5]))/cps); } } } - tv[11] = clock(); - - - if ((b->plc || b->refine) && b->quality) { + if (b->quality) { m.delaunayrefinement(); } - tv[12] = clock(); + tv[7] = clock(); if (!b->quiet) { - if ((b->plc || b->refine) && b->quality) { - printf("Refinement seconds: %g\n", - (tv[12] - tv[11]) / (REAL) CLOCKS_PER_SEC); + if (b->quality) { + printf("Refinement seconds: %g\n", ((REAL)(tv[7] - tv[6])) / cps); } } - if ((b->plc || b->refine) && (b->optlevel > 0)) { - m.optimizemesh(1); + if ((b->plc || b->refine) && (b->optlevel > 0) && !b->conforming) { + m.optimizemesh(); } - tv[13] = clock(); + tv[8] = clock(); if (!b->quiet) { - if ((b->plc || b->refine) && (b->optlevel > 0)) { - printf("Optimization seconds: %g\n", - (tv[13] - tv[12]) / (REAL) CLOCKS_PER_SEC); + if ((b->plc || b->refine) && (b->optlevel > 0) && !b->conforming) { + printf("Optimization seconds: %g\n", ((REAL)(tv[8] - tv[7])) / cps); } } @@ -31989,6 +30388,9 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.jettisonnodes(); } + if (b->order == 2) { + m.highorder(); + } if (!b->quiet) { printf("\n"); @@ -32007,11 +30409,10 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.outnodes(out); } - if (b->noelewritten == 1) { + if (b->noelewritten) { if (!b->quiet) { printf("NOT writing an .ele file.\n"); } - m.numberedges(); } else { if (m.tetrahedrons->items > 0l) { m.outelements(out); @@ -32041,11 +30442,17 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } - if (b->edgesout) { - if (b->edgesout > 1) { - m.outedges(out); // -ee, output all mesh edges. + 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. } else { - m.outsubsegments(out); // -e, only output subsegments. + if (b->plc || b->refine) { + m.outsubsegments(out); // output subsegments. + } } } @@ -32078,13 +30485,11 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } - tv[14] = clock(); + tv[9] = clock(); if (!b->quiet) { - 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); + printf("\nOutput seconds: %g\n", ((REAL)(tv[9] - tv[8])) / cps); + printf("Total running seconds: %g\n", ((REAL)(tv[9] - tv[0])) / cps); } if (b->docheck) { diff --git a/contrib/Tetgen1.5/tetgen.h b/contrib/Tetgen1.5/tetgen.h index 41634d03f4..664ddc3f77 100644 --- a/contrib/Tetgen1.5/tetgen.h +++ b/contrib/Tetgen1.5/tetgen.h @@ -2,21 +2,17 @@ // // // TetGen // // // -// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // // // // Version 1.5 // -// February 21, 2012 // +// (March 27, 2013) // // // -// PRE-RELEASE TEST CODE. // -// PLEASE DO NOT DISTRIBUTE !! // -// PLEASE HELP ME TO IMPROVE IT !! // -// // -// Copyright (C) 2002--2012 // +// Copyright (C) 2002--2013 // // Hang Si // // Research Group: Numerical Mathematics and Scientific Computing // // Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // // Mohrenstr. 39, 10117 Berlin, Germany // -// Hang.Si@wias-berlin.de // +// 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. // @@ -28,46 +24,51 @@ #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 are -// inserted in places where I hope to catch bugs. +// 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. // #define NDEBUG -// To insert lots of self-checks for internal errors, define the SELF_CHECK -// symbol. This will slow down the program a bit. +// 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. -// #define SELF_CHECK +#define REAL double // #define REAL float -// Default, TetGen uses the double precision for a real number. +// Maximum number of characters in a file name (including the null). -#define REAL double +#define FILENAMESIZE 1024 -// 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. +// Maximum number of chars in a line read from a file (including the null). -// Define the _MSC_VER symbol if you are using Microsoft Visual C++. +#define INPUTLINESIZE 2048 -// #define _MSC_VER +// TetGen only uses the C standard library. -// Define the _WIN64 symbol if you are running TetGen on Win64. +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <time.h> +#include <assert.h> -// #define _WIN64 +// 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 + +// 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. #ifdef _MSC_VER // Microsoft Visual C++ # ifdef _WIN64 @@ -81,23 +82,31 @@ # 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. // +// 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. // // // -// 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. // +// 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). // +// // +// 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. // // // // 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 // @@ -114,18 +123,18 @@ class tetgenio { public: // A "polygon" describes a simple polygon (no holes). It is not necessarily - // 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. + // 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. typedef struct { int *vertexlist; int numberofvertices; } polygon; - // A "facet" describes a facet. Each facet is a polygonal region possibly - // with holes, edges, and points in it. + // 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). typedef struct { polygon *polygonlist; int numberofpolygons; @@ -157,19 +166,6 @@ 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. @@ -179,9 +175,6 @@ 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*); @@ -189,6 +182,9 @@ 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; @@ -206,95 +202,108 @@ 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; - // `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; + // '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; 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. 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. + // '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. REAL *holelist; int numberofholes; - // `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' + // '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' // 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 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. + // '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. REAL *facetconstraintlist; int numberoffacetconstraints; - // `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. + // '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. REAL *segmentconstraintlist; int numberofsegmentconstraints; - // '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. + + // '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. int *trifacelist; - int *adjtetlist; int *trifacemarkerlist; + int *o2facelist; + int *adjtetlist; 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. + // '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. int *edgelist; int *edgemarkerlist; + int *o2edgelist; + int *edgeadjtetlist; int numberofedges; // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). @@ -312,9 +321,6 @@ public: int numberofvfacets; int numberofvcells; - // A callback function. - TetSizeFunc tetunsuitable; - // Variable (and callback functions) for meshing PSCs. void *geomhandle; GetVertexParamOnEdge getvertexparamonedge; @@ -323,6 +329,9 @@ 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*); @@ -370,8 +379,8 @@ public: // Initialize routine. void initialize() { - firstnumber = 0; // Default item index is numbered from Zero. - mesh_dim = 3; // Default mesh dimension is 3. + firstnumber = 0; + mesh_dim = 3; useindex = 1; pointlist = (REAL *) NULL; @@ -388,22 +397,25 @@ public: tetrahedronvolumelist = (REAL *) NULL; neighborlist = (int *) NULL; numberoftetrahedra = 0; - numberofcorners = 4; // Default is 4 nodes per element. + numberofcorners = 4; numberoftetrahedronattributes = 0; trifacelist = (int *) NULL; - adjtetlist = (int *) NULL; trifacemarkerlist = (int *) NULL; + o2facelist = (int *) NULL; + adjtetlist = (int *) NULL; numberoftrifaces = 0; - facetlist = (facet *) NULL; - facetmarkerlist = (int *) NULL; - numberoffacets = 0; - edgelist = (int *) NULL; edgemarkerlist = (int *) NULL; + o2edgelist = (int *) NULL; + edgeadjtetlist = (int *) NULL; numberofedges = 0; + facetlist = (facet *) NULL; + facetmarkerlist = (int *) NULL; + numberoffacets = 0; + holelist = (REAL *) NULL; numberofholes = 0; @@ -415,8 +427,6 @@ public: segmentconstraintlist = (REAL *) NULL; numberofsegmentconstraints = 0; - pbcgrouplist = (pbcgroup *) NULL; - numberofpbcgroups = 0; vpointlist = (REAL *) NULL; vedgelist = (voroedge *) NULL; @@ -437,17 +447,12 @@ public: getsteineronface = NULL; } - // Free the memory allocated in 'tetgenio'. + // Free the memory allocated in 'tetgenio'. Note that it assumes that the + // memory was alocated by the "new" operator (C++). 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; } @@ -480,12 +485,15 @@ public: if (trifacelist != (int *) NULL) { delete [] trifacelist; } - if (adjtetlist != (int *) NULL) { - delete [] adjtetlist; - } if (trifacemarkerlist != (int *) NULL) { delete [] trifacemarkerlist; } + if (o2facelist != (int *) NULL) { + delete [] o2facelist; + } + if (adjtetlist != (int *) NULL) { + delete [] adjtetlist; + } if (edgelist != (int *) NULL) { delete [] edgelist; @@ -493,8 +501,16 @@ 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++) { @@ -524,15 +540,6 @@ 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; } @@ -563,12 +570,16 @@ public: // // // tetgenbehavior // // // -// A structure to maintain the switches and parameters of TetGen. // +// A structure for maintaining the switches and parameters used by TetGen's // +// mesh data structure and algorithms. // // // -// 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. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -576,93 +587,94 @@ class tetgenbehavior { public: - // 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. + // 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. char commandline[1024]; char infilename[1024]; char outfilename[1024]; char addinfilename[1024]; char bgmeshfilename[1024]; - // The input object type of TetGen. They are recognized by the input file - // extensions. Currently the following objects are supported: + // 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: // - 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); + // - PLY, a polyhedron (.ply, file format from gatech, only ASCII); // - 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; @@ -681,17 +693,18 @@ public: { plc = 0; psc = 0; - quality = 0; refine = 0; - metric = 0; + quality = 0; nobisect = 0; + metric = 0; weighted = 0; - varvolume = 0; - fixedvolume = 0; + brio_hilbert = 1; incrflip = 0; flipinsert = 0; - btree = 0; - hilbertcurve = 0; + varvolume = 0; + fixedvolume = 0; + noexact = 0; + nostaticfilter = 0; insertaddpoints = 0; regionattrib = 0; conforming = 0; @@ -709,8 +722,10 @@ public: noelewritten = 0; nofacewritten = 0; noiterationnum = 0; - nomerge = 0; + nomergefacet = 0; + nomergevertex = 0; nojettison = 0; + reversetetori = 0; docheck = 0; quiet = 0; verbose = 0; @@ -720,24 +735,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; - optlevel = 7; // 1 & 2 & 4, // min_max_dihedral. - optpasses = 3; - optmaxfliplevel = 2; + optscheme = 7; // 1 & 2 & 4, // min_max_dihedral. + optlevel = 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 = 0.0; // 5.0; + mindihedral = 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 @@ -757,45 +772,42 @@ public: /////////////////////////////////////////////////////////////////////////////// // // -// 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". // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -REAL exactinit(); +void exactinit(int, int, int, REAL, REAL, REAL); 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 // // // -// The object to generate tetrahedral meshes. // +// A structure for creating and updating tetrahedral meshes. // // // /////////////////////////////////////////////////////////////////////////////// @@ -803,64 +815,45 @@ 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 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. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// // The tetrahedron data structure. It includes the following fields: // - a list of four adjoining tetrahedra; // - a list of four vertices; - // - a list of four subfaces (optional, for -p switch); - // - a list of six segments (optional, for -p switch); + // - 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 user-defined floating-point attributes (optional); // - a volume constraint (optional, for -a switch); - // - an integer of element marker; + // - an integer of element marker (and flags); // The structure of a tetrahedron is an array of pointers. Its actual size // (the length of the array) is determined at runtime. @@ -873,8 +866,7 @@ public: // - two adjoining tetrahedra; // - an area constraint (optional, for -q switch); // - an integer for boundary marker; - // - an integer for type: SHARPSEGMENT, NONSHARPSEGMENT, ...; - // - an integer for pbc group (optional, if in->pbcgrouplist exists); + // - an integer for type, flags, etc. typedef REAL **shellface; @@ -884,11 +876,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, or a bsp_tree node; + // - a pointer to a parent (or a duplicate) point; // - 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. + // - an integer for point type (and flags). // - 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. @@ -897,23 +889,21 @@ public: /////////////////////////////////////////////////////////////////////////////// // // -// Ordered tetrahedra // +// Handles // // // -// 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. // +// 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. // // // -// 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. // +// 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. // // // // 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 // @@ -927,19 +917,10 @@ public: // face 2 | 2 (2/3) 6 (2/1) 10 (2/0) // // face 3 | 3 (3/0) 7 (3/1) 11 (3/2) // // // -// 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. // +// 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. // // // // 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 // @@ -950,6 +931,9 @@ 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 { @@ -974,12 +958,110 @@ 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 // // // -// A multiple usages structure. Despite of its name, a 'badface' can be used // -// to represent the following objects: // +// Despite of its name, a 'badface' can be used to represent one of 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; // @@ -991,12 +1073,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 *previtem, *nextitem; + badface *nextitem; badface() : key(0), forg(0), fdest(0), fapex(0), foppo(0), noppo(0), - previtem(0), nextitem(0) {} + nextitem(0) {} }; /////////////////////////////////////////////////////////////////////////////// @@ -1011,11 +1093,12 @@ public: public: - int iloc, bowywat, lawson; - int rejflag, chkencflag; - int sloc, sbowywat; + int iloc; // input/output. + int bowywat, lawson; int splitbdflag, validflag, respectbdflag; + int rejflag, chkencflag, cdtflag; int assignmeshsize; + int sloc, sbowywat; // Used by Delaunay refinement. int refineflag; // 0, 1, 2, 3 @@ -1023,12 +1106,11 @@ 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; @@ -1049,11 +1131,9 @@ public: public: - 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. + // Elementary flip flags. + int enqflag; // (= flipflag) + int chkencflag; // Control flags int unflip; // Undo the performed flips. @@ -1062,37 +1142,22 @@ 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. - // 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; - } + // 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. + flipconstraints() { - seg[0] = NULL; - fac[0] = NULL; - remvert = NULL; - //remedge[0] = NULL; + enqflag = 0; + chkencflag = 0; unflip = 0; collectnewtets = 0; @@ -1100,12 +1165,15 @@ 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; - clearcounters(); + checkflipeligibility = 0; + seg[0] = NULL; + fac[0] = NULL; + remvert = NULL; } }; @@ -1134,11 +1202,6 @@ 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; @@ -1148,142 +1211,47 @@ public: initval = imprval = 0.0; numofsearchdirs = 10; - searchstep = 0.01; - 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) - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // + searchstep = 0.01; + maxiter = -1; // Unlimited smoothing iterations. + smthiter = 0; + + } + }; + + + +/////////////////////////////////////////////////////////////////////////////// // // -// 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 (enumeration declarations) used by TetGen. // // // /////////////////////////////////////////////////////////////////////////////// - class memorypool { - - public: - - // Labels that signify whether a record consists primarily of pointers - // or of floating-point words. Used for data alignment. - enum wordtype {POINTER, FLOATINGPOINT}; + // 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}; - 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; + // 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}; - 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(); - }; + // Labels that signify the result of point location. + enum locateresult {UNKNOWN, OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, + ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX, NONREGULAR, + INSTAR, BADELEMENT}; /////////////////////////////////////////////////////////////////////////////// // // -// Class variables // +// Variables of TetGen // // // /////////////////////////////////////////////////////////////////////////////// // Pointer to the input data (a set of nodes, a PLC, or a mesh). - tetgenio *in; + tetgenio *in, *addin; // Pointer to the switches and parameters. tetgenbehavior *b; @@ -1291,9 +1259,8 @@ public: // Pointer to a background mesh (contains size specification map). tetgenmesh *bgm; - // Memorypools to store mesh elements: tetrahedra, subfaces, segments, - // and vertices. And memorypools for storing pointers which connect - // tetrahedra and subfaces and segments. + // Memorypools to store mesh elements (points, tetrahedra, subfaces, and + // segments) and extra pointers between tetrahedra, subfaces, and segments. memorypool *tetrahedrons, *subfaces, *subsegs, *points; memorypool *tet2subpool, *tet2segpool; @@ -1302,17 +1269,8 @@ public: // A memorypool to store faces to be flipped. memorypool *flippool; - // 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; + arraypool *unflipqueue; + badface *flipstack; // Arrays used for point insertion (the Bowyer-Watson algorithm). arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; @@ -1324,46 +1282,31 @@ public: arraypool *subsegstack, *subfacstack, *subvertstack; arraypool *suppsteinerptlist; - // The infinite vertex. - point dummypoint; - - // 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). + // Arrays of encroached segments and subfaces (for mesh refinement). arraypool *encseglist, *encshlist; - // Pointer to a recently visited tetrahedron, subface. + // The infinite vertex. + point dummypoint; + // The recently visited tetrahedron, subface. triface recenttet; face recentsh; // PI is the ratio of a circle's circumference to its diameter. static REAL PI; - // The increasement of link levels, default is 1. - int autofliplinklevel; + // Array (size = numberoftetrahedra * 6) for storing high-order nodes of + // tetrahedra (only used when -o2 switch is selected). + point *highordertable; - // The volume of tetrahedral-prisms (in 4D). - REAL tetprism_vol_sum; - int calc_tetprism_vol; - // 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. + // Various variables. + int numpointattrib; // Number of point attributes. + int numelemattrib; // Number of tetrahedron attributes. 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. @@ -1372,82 +1315,64 @@ 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 dupverts; // Are there duplicated vertices? - int unuverts; // Are there unused vertices? + int autofliplinklevel; // The increasement of link levels, default is 1. 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; - REAL cosslidihed; // The cosine value of max dihedral of a sliver. + 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 minfaceang, minfacetdihed; // The minimum input (dihedral) angles. - REAL sintheta_tol; // The tolerance for sin(small angle). + 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. - // Algorithm statistical counters. - long ptloc_count, ptloc_max_count; - long orient3dcount, inspherecount, insphere_sos_count; + // 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; long flip14count, flip26count, flipn2ncount; - 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 + long flip23count, flip32count, flip44count, flip41count; + long flip31count, flip22count; + unsigned long totalworkmemory; // Total memory used by working arrays. + + /////////////////////////////////////////////////////////////////////////////// // // // 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 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 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 snextpivot[6]; - static int sorgpivot [6]; - static int sdestpivot[6]; - static int sapexpivot[6]; - static int epivot[4]; + + void inittables(); // 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); @@ -1458,10 +1383,14 @@ 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); @@ -1529,8 +1458,6 @@ 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); @@ -1594,8 +1521,6 @@ 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); @@ -1614,32 +1539,53 @@ public: tetrahedron *alltetrahedrontraverse(); void shellfacedealloc(memorypool*, shellface*); shellface *shellfacetraverse(memorypool*); - void badfacedealloc(memorypool*, badface*); - badface *badfacetraverse(memorypool*); void pointdealloc(point); point pointtraverse(); - void maketetrahedron(triface*); - void makeshellface(memorypool*, face*); - void makepoint(point*, enum verttype); void makeindex2pointmap(point*&); void makepoint2submap(memorypool*, int*&, face*&); + void maketetrahedron(triface*); + void makeshellface(memorypool*, face*); + void makepoint(point*, enum verttype); void initializepools(); /////////////////////////////////////////////////////////////////////////////// // // -// Geometric predicates and calculations // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// - // Triangle-edge intersection test + // 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) 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 + // Triangle-triangle intersection test (robust) int tri_edge_inter_tail(point, point, point, point, point, REAL, REAL); int tri_tri_inter(point, point, point, point, point, point); @@ -1649,13 +1595,12 @@ 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); - // Geometric predicates + // An embedded 2-dimensional geometric predicate (non-robust) 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 + // Geometric calculations (non-robust) + REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd); + inline REAL norm2(REAL x, REAL y, REAL z); 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); @@ -1668,51 +1613,133 @@ 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, int, int); - void flip32(triface*, int, int, int); - void flip41(triface*, int, int, int); + void flip23(triface*, int, flipconstraints* fc); + void flip32(triface*, int, flipconstraints* fc); + void flip41(triface*, int, flipconstraints* fc); // 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 insertvertex(point newpt, triface *searchtet, face *splitsh, face*, - insertvertexflags *ivf); + int insertpoint(point, triface*, face*, face*, insertvertexflags*); + void insertpoint_abort(face*, insertvertexflags*); /////////////////////////////////////////////////////////////////////////////// // // // 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. - 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); + 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); // Point location. unsigned long randomnation(unsigned int choices); void randomsample(point searchpt, triface *searchtet); - enum locateresult locate(point searchpt, triface*, int, int); + enum locateresult locate(point searchpt, triface *searchtet, int); + + // Incremental flips. + void flippush(badface*&, triface*); + int incrementalflip(point newpt, flipconstraints *fc); // Incremental Delaunay construction. void initialdelaunay(point pa, point pb, point pc, point pd); @@ -1724,9 +1751,6 @@ 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); @@ -1746,53 +1770,116 @@ public: void meshsurface(); void interecursive(shellface** subfacearray, int arraysize, int axis, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, - REAL bzmin, REAL bzmax, int* internum); + REAL, REAL, REAL, REAL, REAL, REAL, 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(); - void reportselfintersect(face *seg, face *shface); - - enum interresult finddirection(triface* searchtet, point endpt, int); + enum interresult finddirection(triface* searchtet, point endpt); 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 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); + 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*); bool fillregion(arraypool* missingshs, arraypool*, arraypool* newshs); - void refineregion(); + + int insertpoint_cdt(point, triface*, face*, face*, insertvertexflags*, + arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); + void refineregion(face&, arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); void constrainedfacets(); @@ -1804,7 +1891,6 @@ public: // // /////////////////////////////////////////////////////////////////////////////// - // A call back function. int checkflipeligibility(int fliptype, point, point, point, point, point, int level, int edgepivot, flipconstraints* fc); @@ -1840,15 +1926,46 @@ public: void reconstructmesh(); int scoutpoint(point, triface*, int randflag); - REAL getpointmeshsize(point, triface*, int iloc, int posflag); + REAL getpointmeshsize(point, triface*, int iloc); 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(); @@ -1859,14 +1976,15 @@ 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 chkencflag); + int splitsubface(face *splitfac, point encpt, int qflag, REAL *ccent, int); void repairencfacs(int chkencflag); + void enqueuetetrahedron(triface*); int checktet4split(triface *chktet, int& qflag, REAL *ccent); - int splittetrahedron(triface* splittet,int qflag,REAL *ccent,int chkencflag); + int splittetrahedron(triface* splittet,int qflag,REAL *ccent, int); void repairbadtets(int chkencflag); void delaunayrefinement(); @@ -1877,6 +1995,7 @@ public: // // /////////////////////////////////////////////////////////////////////////////// + long lawsonflip3d(flipconstraints *fc); void recoverdelaunay(); int gettetrahedron(point, point, point, point, triface *); @@ -1888,7 +2007,7 @@ public: int splitsliver(triface *, REAL, int); long removeslivers(int); - void optimizemesh(int optflag); + void optimizemesh(); /////////////////////////////////////////////////////////////////////////////// // // @@ -1905,7 +2024,9 @@ public: int checkconforming(int); // Mesh statistics. + void printfcomma(unsigned long n); void qualitystatistics(); + void memorystatistics(); void statistics(); /////////////////////////////////////////////////////////////////////////////// @@ -1932,6 +2053,7 @@ public: void outmesh2vtk(char*); + /////////////////////////////////////////////////////////////////////////////// // // // Constructor & destructor // @@ -1940,8 +2062,8 @@ public: tetgenmesh() { + in = addin = NULL; b = NULL; - in = NULL; bgm = NULL; tetrahedrons = subfaces = subsegs = points = NULL; @@ -1952,7 +2074,6 @@ public: dummypoint = NULL; flipstack = NULL; unflipqueue = NULL; - btreenode_list = NULL; cavetetlist = cavebdrylist = caveoldtetlist = NULL; cavetetshlist = cavetetseglist = cavetetvertlist = NULL; @@ -1963,19 +2084,15 @@ public: suppsteinerptlist = NULL; encseglist = encshlist = NULL; - plane_pa = plane_pb = plane_pc = (point) NULL; + highordertable = NULL; - xmax = xmin = ymax = ymin = zmax = zmin = 0.0; - longest = 0.0; - hullsize = 0l; - insegments = 0l; - meshedges = meshhulledges = 0l; - steinerleft = -1; + + numpointattrib = numelemattrib = 0; + sizeoftensor = 0; pointmtrindex = 0; pointparamindex = 0; pointmarkindex = 0; point2simindex = 0; - point2pbcptindex = 0; elemattribindex = 0; volumeboundindex = 0; shmarkindex = 0; @@ -1983,47 +2100,31 @@ public: checksubsegflag = 0; checksubfaceflag = 0; checkinverttetflag = 0; - checkpbcs = 0; checkconstraints = 0; nonconvex = 0; - dupverts = 0; - unuverts = 0; + autofliplinklevel = 1; samples = 0l; randomseed = 1l; minfaceang = minfacetdihed = PI; - sintheta_tol = sin(0.001 * PI / 180.0); - - autofliplinklevel = 1; - tetprism_vol_sum = 0.0; - calc_tetprism_vol = 0; + longest = 0.0; + xmax = xmin = ymax = ymin = zmax = zmin = 0.0; - ptloc_count = ptloc_max_count = 0l; - orient3dcount = 0l; - inspherecount = insphere_sos_count = 0l; - flip14count = flip26count = flipn2ncount = 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; + 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; + flip14count = flip26count = flipn2ncount = 0l; + flip23count = flip32count = flip44count = flip41count = 0l; + flip22count = flip31count = 0l; + totalworkmemory = 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() @@ -2032,31 +2133,29 @@ 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; @@ -2079,10 +2178,11 @@ public: delete subsegstack; delete subfacstack; delete subvertstack; + delete suppsteinerptlist; } - if (suppsteinerptlist != NULL) { - delete suppsteinerptlist; + if (highordertable != NULL) { + delete [] highordertable; } } // ~tetgenmesh() @@ -2136,7 +2236,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 was size detected. Program stopped.\n"); + printf("A very small input feature size was detected. Program stopped.\n"); printf("Hint: use -T option to set a smaller tolerance.\n"); break; case 5: @@ -2144,7 +2244,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); @@ -2153,24 +2253,12 @@ inline void terminatetetgen(int x) /////////////////////////////////////////////////////////////////////////////// // // -// Inline functions of mesh data structures // +// Primitives for tetrahedra // // // /////////////////////////////////////////////////////////////////////////////// -// -// 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- +// encode() compress a handle into a single pointer. It relies on the +// assumption that all addresses of tetrahedra are aligned to sixteen- // byte boundaries, so that the last four significant bits are zero. inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) { @@ -2181,135 +2269,139 @@ inline tetgenmesh::tetrahedron tetgenmesh::encode2(tetrahedron* ptr, int ver) { return (tetrahedron) ((uintptr_t) (ptr) | (uintptr_t) (ver)); } -// 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]. +// decode() converts a pointer to a handle. The version is extracted from +// the four least significant bits of the pointer. -inline void tetgenmesh::bond(triface& t1, triface& t2) { - (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)]); +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); } -// dissolve() a bond (from one side). +// bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must +// refer to the same face and the same edge. -inline void tetgenmesh::dissolve(triface& t) { - t.tet[t.ver & 3] = NULL; +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]); } -// 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]; -} +// dissolve() a bond (from one side). -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]; +inline void tetgenmesh::dissolve(triface& t) { + t.tet[t.ver & 3] = NULL; } -// enext() finds the next edge (counterclockwise) on the same face. +// enext() finds the next edge (counterclockwise) in the same face. inline void tetgenmesh::enext(triface& t1, triface& t2) { - (t2).tet = (t1).tet; - (t2).ver = mod12[(t1).ver + 4]; + t2.tet = t1.tet; + t2.ver = enexttbl[t1.ver]; } inline void tetgenmesh::enextself(triface& t) { - (t).ver = mod12[(t).ver + 4]; + t.ver = enexttbl[t.ver]; } -// eprev() finds the next edge (clockwise) on the same face. +// eprev() finds the next edge (clockwise) in the same face. inline void tetgenmesh::eprev(triface& t1, triface& t2) { - (t2).tet = (t1).tet; - (t2).ver = mod12[(t1).ver + 8]; + t2.tet = t1.tet; + t2.ver = eprevtbl[t1.ver]; } inline void tetgenmesh::eprevself(triface& t) { - (t).ver = mod12[(t).ver + 8]; + t.ver = eprevtbl[t.ver]; } -// esym() finds the reversed edge. It is on the other face of the +// esym() finds the reversed edge. It is in the other face of the // same tetrahedron. inline void tetgenmesh::esym(triface& t1, triface& t2) { (t2).tet = (t1).tet; - (t2).ver = edgepivot[(t1).ver]; + (t2).ver = esymtbl[(t1).ver]; } inline void tetgenmesh::esymself(triface& t) { - (t).ver = edgepivot[(t).ver]; + (t).ver = esymtbl[(t).ver]; } -// enextesym() finds the reversed edge of the next edge. It is on the other -// face of the same tetrahedron. +// 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(). inline void tetgenmesh::enextesym(triface& t1, triface& t2) { - enext(t1, t2); - esymself(t2); + t2.tet = t1.tet; + t2.ver = enextesymtbl[t1.ver]; } inline void tetgenmesh::enextesymself(triface& t) { - enextself(t); - esymself(t); + t.ver = enextesymtbl[t.ver]; } -// eprevesym() finds the reversed edge of the previous edge. It is on the -// other face of the same tetrahedron. +// eprevesym() finds the reversed edge of the previous edge. inline void tetgenmesh::eprevesym(triface& t1, triface& t2) { - eprev(t1, t2); - esymself(t2); + t2.tet = t1.tet; + t2.ver = eprevesymtbl[t1.ver]; } inline void tetgenmesh::eprevesymself(triface& t) { - eprevself(t); - esymself(t); + t.ver = eprevesymtbl[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 -// equivalent to the combination: fsym() * esym(). +// eorgoppo() Finds the opposite face of the origin of the current edge. +// Return the opposite edge of the current edge. -inline void tetgenmesh::fnext(triface& t1, triface& t2) { - esym(t1, t2); - fsymself(t2); +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]; +} + +// edestoppo() Finds the opposite face of the destination of the current +// edge. Return the opposite edge of the current edge. + +inline void tetgenmesh::edestoppo(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = edestoppotbl[t1.ver]; } -inline void tetgenmesh::fnextself(triface& t) { - esymself(t); - fsymself(t); +inline void tetgenmesh::edestoppoself(triface& t) { + t.ver = edestoppotbl[t.ver]; } -// 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(). +// fsym() finds the adjacent tetrahedron at the same face and the same edge. -inline void tetgenmesh::fprev(triface& t1, triface& t2) { - fsym(t1, t2); - esymself(t2); +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::fprevself(triface& t) { - fsymself(t); - esymself(t); + +#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]; } + +#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. @@ -2408,17 +2500,12 @@ 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. 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. +// tetrahedron. Use the second lowerest bit of the element marker. inline void tetgenmesh::marktest(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= 2; @@ -2435,8 +2522,6 @@ 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)); @@ -2453,7 +2538,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]); @@ -2506,7 +2591,6 @@ 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); } @@ -2522,13 +2606,11 @@ inline bool tetgenmesh::isdeadtet(triface& t) { return ((t.tet == NULL) || (t.tet[4] == NULL)); } -// -// End of primitives for tetrahedra -// - -// -// Begin of primitives for subfaces/subsegments -// +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for subfaces and subsegments // +// // +/////////////////////////////////////////////////////////////////////////////// // Each subface contains three pointers to its neighboring subfaces, with // edge versions. To save memory, both information are kept in a single @@ -2540,7 +2622,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) 7); + s.sh = (shellface *) ((uintptr_t) (sptr) ^ (uintptr_t) (s.shver)); } inline tetgenmesh::shellface tetgenmesh::sencode(face& s) { @@ -2775,17 +2857,6 @@ 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. @@ -2810,8 +2881,7 @@ inline bool tetgenmesh::sinfected(face& s) } // smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag -// a subface. -// The last 2nd bit of ((int *) ((s).sh))[shmarkindex+1] is flaged. +// a subface.The last 2nd bit of the integer is flaged. inline void tetgenmesh::smarktest(face& s) { @@ -2831,8 +2901,7 @@ inline bool tetgenmesh::smarktested(face& s) } // smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or -// unflag a subface. -// The last 3rd bit of ((int *) ((s).sh))[shmarkindex+1] is flaged. +// unflag a subface. The last 3rd bit of the integer is flaged. inline void tetgenmesh::smarktest2(face& s) { @@ -2870,50 +2939,34 @@ inline bool tetgenmesh::smarktest3ed(face& s) return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0); } -// -// End of primitives for subfaces/subsegments -// +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between tetrahedra and subfaces // +// // +/////////////////////////////////////////////////////////////////////////////// -// -// 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(); - // NULL all fields in this space. + // Initialize. 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. - ver = ((s).shver & 1) + mod6[((s).shver & 6) + soffset]; - ((shellface *) (t).tet[9])[(t).ver & 3] = sencode2((s).sh, ver); + ((shellface *) (t).tet[9])[(t).ver & 3] = + sencode2((s).sh, tsbondtbl[t.ver][s.shver]); // Bond s <== t. - ver = ((t).ver & 3) + mod12[((t).ver & 12) + toffset]; - s.sh[9 + ((s).shver & 1)] = (shellface) encode2((t).tet, ver); + s.sh[9 + ((s).shver & 1)] = + (shellface) encode2((t).tet, stbondtbl[t.ver][s.shver]); } // tspivot() finds a subface (s) abutting on the given tetrahdera (t). @@ -2923,47 +2976,37 @@ 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)); - - // 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]; + (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]) + // stpivot() finds a tetrahedron (t) abutting a given subface (s). // Return the t (if it exists) with the same edge and the same // orientation of 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; } - - 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]; + (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)]) + // tsdissolve() dissolve a bond (from the tetrahedron side). inline void tetgenmesh::tsdissolve(triface& t) @@ -2981,13 +3024,11 @@ inline void tetgenmesh::stdissolve(face& s) (s).sh[10] = NULL; } -// -// End of primitives for interacting between tetrahedra and subfaces -// - -// -// Begin of primitives for interacting between subfaces and subsegs -// +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between subfaces and segments // +// // +/////////////////////////////////////////////////////////////////////////////// // ssbond() bond a subface to a subsegment. @@ -3014,24 +3055,26 @@ inline void tetgenmesh::ssdissolve(face& s) inline void tetgenmesh::sspivot(face& s, face& edge) { - shellface sptr = (shellface) s.sh[6 + (s.shver >> 1)]; - sdecode(sptr, edge); + sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge); } -// -// End of primitives for interacting between subfaces and subsegs -// +// Quickly check if the edge is a subsegment. + +#define isshsubseg(s) \ + ((s).sh[6 + ((s).shver >> 1)]) -// -// Begin of primitives for interacting between tet and subsegs. -// +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between tetrahedra and segments // +// // +/////////////////////////////////////////////////////////////////////////////// inline void tetgenmesh::tssbond1(triface& t, face& s) { if ((t).tet[8] == NULL) { // Allocate space for this tet. (t).tet[8] = (tetrahedron) tet2segpool->alloc(); - // NULL all fields in this space. + // Initialization. for (int i = 0; i < 6; i++) { ((shellface *) (t).tet[8])[i] = NULL; } @@ -3065,18 +3108,21 @@ 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); } -// -// End of primitives for interacting between tet and subsegs. -// - -// -// Begin of primitives for points -// +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for points // +// // +/////////////////////////////////////////////////////////////////////////////// inline int tetgenmesh::pointmark(point pt) { return ((int *) (pt))[pointmarkindex]; @@ -3133,8 +3179,8 @@ inline bool tetgenmesh::pinfected(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0; } -// pmarktest(), punmarktest(), pmarktested() -- primitives to mark or unmark -// a point. +// pmarktest(), punmarktest(), pmarktested() -- more primitives to +// flag or unflag a point. inline void tetgenmesh::pmarktest(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 2; @@ -3148,8 +3194,6 @@ 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; } @@ -3162,8 +3206,6 @@ 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; } @@ -3176,7 +3218,6 @@ 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. @@ -3213,15 +3254,6 @@ 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. @@ -3290,19 +3322,19 @@ inline tetgenmesh::point tetgenmesh::farsdest(face& s) return sdest(travesh); } -// -// End of primitives for points -// +/////////////////////////////////////////////////////////////////////////////// +// // +// Linear algebra operators. // +// // +/////////////////////////////////////////////////////////////////////////////// // 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]; @@ -3311,7 +3343,6 @@ 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]) + @@ -3319,24 +3350,11 @@ inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) (p2[2] - p1[2]) * (p2[2] - p1[2])); } -// 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) +inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z) +{ + return (x) * (x) + (y) * (y) + (z) * (z); +} -#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) #endif // #ifndef tetgenH -- GitLab